diff options
Diffstat (limited to 'drivers/staging/media/imx/imx7-mipi-csis.c')
-rw-r--r-- | drivers/staging/media/imx/imx7-mipi-csis.c | 582 |
1 files changed, 360 insertions, 222 deletions
diff --git a/drivers/staging/media/imx/imx7-mipi-csis.c b/drivers/staging/media/imx/imx7-mipi-csis.c index fbc1a924652a..7612993cc1d6 100644 --- a/drivers/staging/media/imx/imx7-mipi-csis.c +++ b/drivers/staging/media/imx/imx7-mipi-csis.c @@ -14,32 +14,30 @@ #include <linux/errno.h> #include <linux/interrupt.h> #include <linux/io.h> -#include <linux/irq.h> #include <linux/kernel.h> -#include <linux/mfd/syscon.h> #include <linux/module.h> -#include <linux/of_graph.h> +#include <linux/mutex.h> +#include <linux/of.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> -#include <linux/reset.h> #include <linux/regulator/consumer.h> +#include <linux/reset.h> #include <linux/spinlock.h> #include <media/v4l2-device.h> #include <media/v4l2-fwnode.h> +#include <media/v4l2-mc.h> #include <media/v4l2-subdev.h> -#include "imx-media.h" - -#define CSIS_DRIVER_NAME "imx7-mipi-csis" -#define CSIS_SUBDEV_NAME CSIS_DRIVER_NAME +#define CSIS_DRIVER_NAME "imx7-mipi-csis" +#define CSIS_SUBDEV_NAME CSIS_DRIVER_NAME -#define CSIS_PAD_SINK 0 -#define CSIS_PAD_SOURCE 1 -#define CSIS_PADS_NUM 2 +#define CSIS_PAD_SINK 0 +#define CSIS_PAD_SOURCE 1 +#define CSIS_PADS_NUM 2 -#define MIPI_CSIS_DEF_PIX_WIDTH 640 -#define MIPI_CSIS_DEF_PIX_HEIGHT 480 +#define MIPI_CSIS_DEF_PIX_WIDTH 640 +#define MIPI_CSIS_DEF_PIX_HEIGHT 480 /* Register map definition */ @@ -64,42 +62,42 @@ #define MIPI_CSIS_CLK_CTRL_WCLK_SRC BIT(0) /* CSIS Interrupt mask */ -#define MIPI_CSIS_INTMSK 0x10 -#define MIPI_CSIS_INTMSK_EVEN_BEFORE BIT(31) -#define MIPI_CSIS_INTMSK_EVEN_AFTER BIT(30) -#define MIPI_CSIS_INTMSK_ODD_BEFORE BIT(29) -#define MIPI_CSIS_INTMSK_ODD_AFTER BIT(28) -#define MIPI_CSIS_INTMSK_FRAME_START BIT(24) -#define MIPI_CSIS_INTMSK_FRAME_END BIT(20) -#define MIPI_CSIS_INTMSK_ERR_SOT_HS BIT(16) -#define MIPI_CSIS_INTMSK_ERR_LOST_FS BIT(12) -#define MIPI_CSIS_INTMSK_ERR_LOST_FE BIT(8) -#define MIPI_CSIS_INTMSK_ERR_OVER BIT(4) -#define MIPI_CSIS_INTMSK_ERR_WRONG_CFG BIT(3) -#define MIPI_CSIS_INTMSK_ERR_ECC BIT(2) -#define MIPI_CSIS_INTMSK_ERR_CRC BIT(1) -#define MIPI_CSIS_INTMSK_ERR_UNKNOWN BIT(0) +#define MIPI_CSIS_INTMSK 0x10 +#define MIPI_CSIS_INTMSK_EVEN_BEFORE BIT(31) +#define MIPI_CSIS_INTMSK_EVEN_AFTER BIT(30) +#define MIPI_CSIS_INTMSK_ODD_BEFORE BIT(29) +#define MIPI_CSIS_INTMSK_ODD_AFTER BIT(28) +#define MIPI_CSIS_INTMSK_FRAME_START BIT(24) +#define MIPI_CSIS_INTMSK_FRAME_END BIT(20) +#define MIPI_CSIS_INTMSK_ERR_SOT_HS BIT(16) +#define MIPI_CSIS_INTMSK_ERR_LOST_FS BIT(12) +#define MIPI_CSIS_INTMSK_ERR_LOST_FE BIT(8) +#define MIPI_CSIS_INTMSK_ERR_OVER BIT(4) +#define MIPI_CSIS_INTMSK_ERR_WRONG_CFG BIT(3) +#define MIPI_CSIS_INTMSK_ERR_ECC BIT(2) +#define MIPI_CSIS_INTMSK_ERR_CRC BIT(1) +#define MIPI_CSIS_INTMSK_ERR_UNKNOWN BIT(0) /* CSIS Interrupt source */ -#define MIPI_CSIS_INTSRC 0x14 -#define MIPI_CSIS_INTSRC_EVEN_BEFORE BIT(31) -#define MIPI_CSIS_INTSRC_EVEN_AFTER BIT(30) -#define MIPI_CSIS_INTSRC_EVEN BIT(30) -#define MIPI_CSIS_INTSRC_ODD_BEFORE BIT(29) -#define MIPI_CSIS_INTSRC_ODD_AFTER BIT(28) -#define MIPI_CSIS_INTSRC_ODD (0x3 << 28) -#define MIPI_CSIS_INTSRC_NON_IMAGE_DATA (0xf << 28) -#define MIPI_CSIS_INTSRC_FRAME_START BIT(24) -#define MIPI_CSIS_INTSRC_FRAME_END BIT(20) -#define MIPI_CSIS_INTSRC_ERR_SOT_HS BIT(16) -#define MIPI_CSIS_INTSRC_ERR_LOST_FS BIT(12) -#define MIPI_CSIS_INTSRC_ERR_LOST_FE BIT(8) -#define MIPI_CSIS_INTSRC_ERR_OVER BIT(4) -#define MIPI_CSIS_INTSRC_ERR_WRONG_CFG BIT(3) -#define MIPI_CSIS_INTSRC_ERR_ECC BIT(2) -#define MIPI_CSIS_INTSRC_ERR_CRC BIT(1) -#define MIPI_CSIS_INTSRC_ERR_UNKNOWN BIT(0) -#define MIPI_CSIS_INTSRC_ERRORS 0xfffff +#define MIPI_CSIS_INTSRC 0x14 +#define MIPI_CSIS_INTSRC_EVEN_BEFORE BIT(31) +#define MIPI_CSIS_INTSRC_EVEN_AFTER BIT(30) +#define MIPI_CSIS_INTSRC_EVEN BIT(30) +#define MIPI_CSIS_INTSRC_ODD_BEFORE BIT(29) +#define MIPI_CSIS_INTSRC_ODD_AFTER BIT(28) +#define MIPI_CSIS_INTSRC_ODD (0x3 << 28) +#define MIPI_CSIS_INTSRC_NON_IMAGE_DATA (0xf << 28) +#define MIPI_CSIS_INTSRC_FRAME_START BIT(24) +#define MIPI_CSIS_INTSRC_FRAME_END BIT(20) +#define MIPI_CSIS_INTSRC_ERR_SOT_HS BIT(16) +#define MIPI_CSIS_INTSRC_ERR_LOST_FS BIT(12) +#define MIPI_CSIS_INTSRC_ERR_LOST_FE BIT(8) +#define MIPI_CSIS_INTSRC_ERR_OVER BIT(4) +#define MIPI_CSIS_INTSRC_ERR_WRONG_CFG BIT(3) +#define MIPI_CSIS_INTSRC_ERR_ECC BIT(2) +#define MIPI_CSIS_INTSRC_ERR_CRC BIT(1) +#define MIPI_CSIS_INTSRC_ERR_UNKNOWN BIT(0) +#define MIPI_CSIS_INTSRC_ERRORS 0xfffff /* D-PHY status control */ #define MIPI_CSIS_DPHYSTATUS 0x20 @@ -121,19 +119,19 @@ #define MIPI_CSIS_DPHYCTRL_ENABLE (0x1f << 0) /* D-PHY Master and Slave Control register Low */ -#define MIPI_CSIS_DPHYBCTRL_L 0x30 +#define MIPI_CSIS_DPHYBCTRL_L 0x30 /* D-PHY Master and Slave Control register High */ -#define MIPI_CSIS_DPHYBCTRL_H 0x34 +#define MIPI_CSIS_DPHYBCTRL_H 0x34 /* D-PHY Slave Control register Low */ -#define MIPI_CSIS_DPHYSCTRL_L 0x38 +#define MIPI_CSIS_DPHYSCTRL_L 0x38 /* D-PHY Slave Control register High */ -#define MIPI_CSIS_DPHYSCTRL_H 0x3c +#define MIPI_CSIS_DPHYSCTRL_H 0x3c /* ISP Configuration register */ -#define MIPI_CSIS_ISPCONFIG_CH0 0x40 -#define MIPI_CSIS_ISPCONFIG_CH1 0x50 -#define MIPI_CSIS_ISPCONFIG_CH2 0x60 -#define MIPI_CSIS_ISPCONFIG_CH3 0x70 +#define MIPI_CSIS_ISPCONFIG_CH0 0x40 +#define MIPI_CSIS_ISPCONFIG_CH1 0x50 +#define MIPI_CSIS_ISPCONFIG_CH2 0x60 +#define MIPI_CSIS_ISPCONFIG_CH3 0x70 #define MIPI_CSIS_ISPCFG_MEM_FULL_GAP_MSK (0xff << 24) #define MIPI_CSIS_ISPCFG_MEM_FULL_GAP(x) ((x) << 24) @@ -143,35 +141,36 @@ #define MIPI_CSIS_ISPCFG_FMT_RAW8 (0x2a << 2) #define MIPI_CSIS_ISPCFG_FMT_RAW10 (0x2b << 2) #define MIPI_CSIS_ISPCFG_FMT_RAW12 (0x2c << 2) +#define MIPI_CSIS_ISPCFG_FMT_RAW14 (0x2d << 2) /* User defined formats, x = 1...4 */ -#define MIPI_CSIS_ISPCFG_FMT_USER(x) ((0x30 + (x) - 1) << 2) -#define MIPI_CSIS_ISPCFG_FMT_MASK (0x3f << 2) +#define MIPI_CSIS_ISPCFG_FMT_USER(x) ((0x30 + (x) - 1) << 2) +#define MIPI_CSIS_ISPCFG_FMT_MASK (0x3f << 2) /* ISP Image Resolution register */ -#define MIPI_CSIS_ISPRESOL_CH0 0x44 -#define MIPI_CSIS_ISPRESOL_CH1 0x54 -#define MIPI_CSIS_ISPRESOL_CH2 0x64 -#define MIPI_CSIS_ISPRESOL_CH3 0x74 -#define CSIS_MAX_PIX_WIDTH 0xffff -#define CSIS_MAX_PIX_HEIGHT 0xffff +#define MIPI_CSIS_ISPRESOL_CH0 0x44 +#define MIPI_CSIS_ISPRESOL_CH1 0x54 +#define MIPI_CSIS_ISPRESOL_CH2 0x64 +#define MIPI_CSIS_ISPRESOL_CH3 0x74 +#define CSIS_MAX_PIX_WIDTH 0xffff +#define CSIS_MAX_PIX_HEIGHT 0xffff /* ISP SYNC register */ -#define MIPI_CSIS_ISPSYNC_CH0 0x48 -#define MIPI_CSIS_ISPSYNC_CH1 0x58 -#define MIPI_CSIS_ISPSYNC_CH2 0x68 -#define MIPI_CSIS_ISPSYNC_CH3 0x78 +#define MIPI_CSIS_ISPSYNC_CH0 0x48 +#define MIPI_CSIS_ISPSYNC_CH1 0x58 +#define MIPI_CSIS_ISPSYNC_CH2 0x68 +#define MIPI_CSIS_ISPSYNC_CH3 0x78 #define MIPI_CSIS_ISPSYNC_HSYNC_LINTV_OFFSET 18 #define MIPI_CSIS_ISPSYNC_VSYNC_SINTV_OFFSET 12 #define MIPI_CSIS_ISPSYNC_VSYNC_EINTV_OFFSET 0 /* Non-image packet data buffers */ -#define MIPI_CSIS_PKTDATA_ODD 0x2000 -#define MIPI_CSIS_PKTDATA_EVEN 0x3000 -#define MIPI_CSIS_PKTDATA_SIZE SZ_4K +#define MIPI_CSIS_PKTDATA_ODD 0x2000 +#define MIPI_CSIS_PKTDATA_EVEN 0x3000 +#define MIPI_CSIS_PKTDATA_SIZE SZ_4K -#define DEFAULT_SCLK_CSIS_FREQ 166000000UL +#define DEFAULT_SCLK_CSIS_FREQ 166000000UL enum { ST_POWERED = 1, @@ -223,6 +222,7 @@ struct csi_state { struct device *dev; struct media_pad pads[CSIS_PADS_NUM]; struct v4l2_subdev mipi_sd; + struct v4l2_async_notifier notifier; struct v4l2_subdev *src_sd; u8 index; @@ -253,45 +253,102 @@ struct csi_state { struct csis_hw_reset hw_reset; struct regulator *mipi_phy_regulator; - bool sink_linked; }; struct csis_pix_format { - unsigned int pix_width_alignment; u32 code; u32 fmt_reg; - u8 data_alignment; + u8 width; }; static const struct csis_pix_format mipi_csis_formats[] = { + /* YUV formats. */ { - .code = MEDIA_BUS_FMT_SBGGR10_1X10, - .fmt_reg = MIPI_CSIS_ISPCFG_FMT_RAW10, - .data_alignment = 16, - }, { - .code = MEDIA_BUS_FMT_VYUY8_2X8, + .code = MEDIA_BUS_FMT_UYVY8_2X8, .fmt_reg = MIPI_CSIS_ISPCFG_FMT_YCBCR422_8BIT, - .data_alignment = 16, + .width = 8, }, { + .code = MEDIA_BUS_FMT_UYVY10_2X10, + .fmt_reg = MIPI_CSIS_ISPCFG_FMT_YCBCR422_8BIT, + .width = 10, + }, + /* RAW (Bayer and greyscale) formats. */ + { .code = MEDIA_BUS_FMT_SBGGR8_1X8, .fmt_reg = MIPI_CSIS_ISPCFG_FMT_RAW8, - .data_alignment = 8, + .width = 8, }, { - .code = MEDIA_BUS_FMT_YUYV8_2X8, - .fmt_reg = MIPI_CSIS_ISPCFG_FMT_YCBCR422_8BIT, - .data_alignment = 16, + .code = MEDIA_BUS_FMT_SGBRG8_1X8, + .fmt_reg = MIPI_CSIS_ISPCFG_FMT_RAW8, + .width = 8, + }, { + .code = MEDIA_BUS_FMT_SGRBG8_1X8, + .fmt_reg = MIPI_CSIS_ISPCFG_FMT_RAW8, + .width = 8, + }, { + .code = MEDIA_BUS_FMT_SRGGB8_1X8, + .fmt_reg = MIPI_CSIS_ISPCFG_FMT_RAW8, + .width = 8, }, { .code = MEDIA_BUS_FMT_Y8_1X8, .fmt_reg = MIPI_CSIS_ISPCFG_FMT_RAW8, - .data_alignment = 8, + .width = 8, + }, { + .code = MEDIA_BUS_FMT_SBGGR10_1X10, + .fmt_reg = MIPI_CSIS_ISPCFG_FMT_RAW10, + .width = 10, + }, { + .code = MEDIA_BUS_FMT_SGBRG10_1X10, + .fmt_reg = MIPI_CSIS_ISPCFG_FMT_RAW10, + .width = 10, + }, { + .code = MEDIA_BUS_FMT_SGRBG10_1X10, + .fmt_reg = MIPI_CSIS_ISPCFG_FMT_RAW10, + .width = 10, + }, { + .code = MEDIA_BUS_FMT_SRGGB10_1X10, + .fmt_reg = MIPI_CSIS_ISPCFG_FMT_RAW10, + .width = 10, }, { .code = MEDIA_BUS_FMT_Y10_1X10, .fmt_reg = MIPI_CSIS_ISPCFG_FMT_RAW10, - .data_alignment = 16, + .width = 10, + }, { + .code = MEDIA_BUS_FMT_SBGGR12_1X12, + .fmt_reg = MIPI_CSIS_ISPCFG_FMT_RAW12, + .width = 12, + }, { + .code = MEDIA_BUS_FMT_SGBRG12_1X12, + .fmt_reg = MIPI_CSIS_ISPCFG_FMT_RAW12, + .width = 12, + }, { + .code = MEDIA_BUS_FMT_SGRBG12_1X12, + .fmt_reg = MIPI_CSIS_ISPCFG_FMT_RAW12, + .width = 12, + }, { + .code = MEDIA_BUS_FMT_SRGGB12_1X12, + .fmt_reg = MIPI_CSIS_ISPCFG_FMT_RAW12, + .width = 12, }, { .code = MEDIA_BUS_FMT_Y12_1X12, .fmt_reg = MIPI_CSIS_ISPCFG_FMT_RAW12, - .data_alignment = 16, + .width = 12, + }, { + .code = MEDIA_BUS_FMT_SBGGR14_1X14, + .fmt_reg = MIPI_CSIS_ISPCFG_FMT_RAW14, + .width = 14, + }, { + .code = MEDIA_BUS_FMT_SGBRG14_1X14, + .fmt_reg = MIPI_CSIS_ISPCFG_FMT_RAW14, + .width = 14, + }, { + .code = MEDIA_BUS_FMT_SGRBG14_1X14, + .fmt_reg = MIPI_CSIS_ISPCFG_FMT_RAW14, + .width = 14, + }, { + .code = MEDIA_BUS_FMT_SRGGB14_1X14, + .fmt_reg = MIPI_CSIS_ISPCFG_FMT_RAW14, + .width = 14, } }; @@ -329,6 +386,12 @@ static int mipi_csis_dump_regs(struct csi_state *state) return 0; } +static struct csi_state * +mipi_notifier_to_csis_state(struct v4l2_async_notifier *n) +{ + return container_of(n, struct csi_state, notifier); +} + static struct csi_state *mipi_sd_to_csis_state(struct v4l2_subdev *sdev) { return container_of(sdev, struct csi_state, mipi_sd); @@ -405,7 +468,8 @@ static void __mipi_csis_set_format(struct csi_state *state) /* Color format */ val = mipi_csis_read(state, MIPI_CSIS_ISPCONFIG_CH0); - val = (val & ~MIPI_CSIS_ISPCFG_FMT_MASK) | state->csis_fmt->fmt_reg; + val &= ~(MIPI_CSIS_ISPCFG_ALIGN_32BIT | MIPI_CSIS_ISPCFG_FMT_MASK); + val |= state->csis_fmt->fmt_reg; mipi_csis_write(state, MIPI_CSIS_ISPCONFIG_CH0, val); /* Pixel resolution */ @@ -437,13 +501,6 @@ static void mipi_csis_set_params(struct csi_state *state) mipi_csis_set_hsync_settle(state, state->hs_settle); - val = mipi_csis_read(state, MIPI_CSIS_ISPCONFIG_CH0); - if (state->csis_fmt->data_alignment == 32) - val |= MIPI_CSIS_ISPCFG_ALIGN_32BIT; - else - val &= ~MIPI_CSIS_ISPCFG_ALIGN_32BIT; - mipi_csis_write(state, MIPI_CSIS_ISPCONFIG_CH0, val); - val = (0 << MIPI_CSIS_ISPSYNC_HSYNC_LINTV_OFFSET) | (0 << MIPI_CSIS_ISPSYNC_VSYNC_SINTV_OFFSET) | (0 << MIPI_CSIS_ISPSYNC_VSYNC_EINTV_OFFSET); @@ -622,17 +679,7 @@ static int mipi_csis_link_setup(struct media_entity *entity, mutex_lock(&state->lock); - if (local_pad->flags & MEDIA_PAD_FL_SOURCE) { - if (flags & MEDIA_LNK_FL_ENABLED) { - if (state->sink_linked) { - ret = -EBUSY; - goto out; - } - state->sink_linked = true; - } else { - state->sink_linked = false; - } - } else { + if (local_pad->flags & MEDIA_PAD_FL_SINK) { if (flags & MEDIA_LNK_FL_ENABLED) { if (state->src_sd) { ret = -EBUSY; @@ -649,48 +696,6 @@ out: return ret; } -static int mipi_csis_init_cfg(struct v4l2_subdev *mipi_sd, - struct v4l2_subdev_pad_config *cfg) -{ - struct v4l2_mbus_framefmt *mf; - unsigned int i; - int ret; - - for (i = 0; i < CSIS_PADS_NUM; i++) { - mf = v4l2_subdev_get_try_format(mipi_sd, cfg, i); - - ret = imx_media_init_mbus_fmt(mf, MIPI_CSIS_DEF_PIX_HEIGHT, - MIPI_CSIS_DEF_PIX_WIDTH, 0, - V4L2_FIELD_NONE, NULL); - if (ret < 0) - return ret; - } - - return 0; -} - -static struct csis_pix_format const * -mipi_csis_try_format(struct v4l2_subdev *mipi_sd, struct v4l2_mbus_framefmt *mf) -{ - struct csi_state *state = mipi_sd_to_csis_state(mipi_sd); - struct csis_pix_format const *csis_fmt; - - csis_fmt = find_csis_format(mf->code); - if (!csis_fmt) - csis_fmt = &mipi_csis_formats[0]; - - v4l_bound_align_image(&mf->width, 1, CSIS_MAX_PIX_WIDTH, - csis_fmt->pix_width_alignment, - &mf->height, 1, CSIS_MAX_PIX_HEIGHT, 1, - 0); - - state->format_mbus.code = csis_fmt->code; - state->format_mbus.width = mf->width; - state->format_mbus.height = mf->height; - - return csis_fmt; -} - static struct v4l2_mbus_framefmt * mipi_csis_get_format(struct csi_state *state, struct v4l2_subdev_pad_config *cfg, @@ -703,53 +708,160 @@ mipi_csis_get_format(struct csi_state *state, return &state->format_mbus; } -static int mipi_csis_set_fmt(struct v4l2_subdev *mipi_sd, +static int mipi_csis_init_cfg(struct v4l2_subdev *mipi_sd, + struct v4l2_subdev_pad_config *cfg) +{ + struct csi_state *state = mipi_sd_to_csis_state(mipi_sd); + struct v4l2_mbus_framefmt *fmt_sink; + struct v4l2_mbus_framefmt *fmt_source; + enum v4l2_subdev_format_whence which; + + which = cfg ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + fmt_sink = mipi_csis_get_format(state, cfg, which, CSIS_PAD_SINK); + + fmt_sink->code = MEDIA_BUS_FMT_UYVY8_2X8; + fmt_sink->width = MIPI_CSIS_DEF_PIX_WIDTH; + fmt_sink->height = MIPI_CSIS_DEF_PIX_HEIGHT; + fmt_sink->field = V4L2_FIELD_NONE; + + fmt_sink->colorspace = V4L2_COLORSPACE_SMPTE170M; + fmt_sink->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt_sink->colorspace); + fmt_sink->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt_sink->colorspace); + fmt_sink->quantization = + V4L2_MAP_QUANTIZATION_DEFAULT(false, fmt_sink->colorspace, + fmt_sink->ycbcr_enc); + + /* + * When called from mipi_csis_subdev_init() to initialize the active + * configuration, cfg is NULL, which indicates there's no source pad + * configuration to set. + */ + if (!cfg) + return 0; + + fmt_source = mipi_csis_get_format(state, cfg, which, CSIS_PAD_SOURCE); + *fmt_source = *fmt_sink; + + return 0; +} + +static int mipi_csis_get_fmt(struct v4l2_subdev *mipi_sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *sdformat) { struct csi_state *state = mipi_sd_to_csis_state(mipi_sd); - struct csis_pix_format const *csis_fmt; struct v4l2_mbus_framefmt *fmt; - if (sdformat->pad >= CSIS_PADS_NUM) - return -EINVAL; - + mutex_lock(&state->lock); fmt = mipi_csis_get_format(state, cfg, sdformat->which, sdformat->pad); + sdformat->format = *fmt; + mutex_unlock(&state->lock); - mutex_lock(&state->lock); - if (sdformat->pad == CSIS_PAD_SOURCE) { - sdformat->format = *fmt; - goto unlock; - } + return 0; +} + +static int mipi_csis_enum_mbus_code(struct v4l2_subdev *mipi_sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct csi_state *state = mipi_sd_to_csis_state(mipi_sd); - csis_fmt = mipi_csis_try_format(mipi_sd, &sdformat->format); + /* + * The CSIS can't transcode in any way, the source format is identical + * to the sink format. + */ + if (code->pad == CSIS_PAD_SOURCE) { + struct v4l2_mbus_framefmt *fmt; - sdformat->format = *fmt; + if (code->index > 0) + return -EINVAL; - if (csis_fmt && sdformat->which == V4L2_SUBDEV_FORMAT_ACTIVE) - state->csis_fmt = csis_fmt; - else - cfg->try_fmt = sdformat->format; + fmt = mipi_csis_get_format(state, cfg, code->which, code->pad); + code->code = fmt->code; + return 0; + } -unlock: - mutex_unlock(&state->lock); + if (code->pad != CSIS_PAD_SINK) + return -EINVAL; + + if (code->index >= ARRAY_SIZE(mipi_csis_formats)) + return -EINVAL; + + code->code = mipi_csis_formats[code->index].code; return 0; } -static int mipi_csis_get_fmt(struct v4l2_subdev *mipi_sd, +static int mipi_csis_set_fmt(struct v4l2_subdev *mipi_sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *sdformat) { struct csi_state *state = mipi_sd_to_csis_state(mipi_sd); + struct csis_pix_format const *csis_fmt; struct v4l2_mbus_framefmt *fmt; + unsigned int align; - mutex_lock(&state->lock); + /* + * The CSIS can't transcode in any way, the source format can't be + * modified. + */ + if (sdformat->pad == CSIS_PAD_SOURCE) + return mipi_csis_get_fmt(mipi_sd, cfg, sdformat); + + if (sdformat->pad != CSIS_PAD_SINK) + return -EINVAL; fmt = mipi_csis_get_format(state, cfg, sdformat->which, sdformat->pad); + mutex_lock(&state->lock); + + /* Validate the media bus code and clamp the size. */ + csis_fmt = find_csis_format(sdformat->format.code); + if (!csis_fmt) + csis_fmt = &mipi_csis_formats[0]; + + fmt->code = csis_fmt->code; + fmt->width = sdformat->format.width; + fmt->height = sdformat->format.height; + + /* + * The total number of bits per line must be a multiple of 8. We thus + * need to align the width for formats that are not multiples of 8 + * bits. + */ + switch (csis_fmt->width % 8) { + case 0: + align = 1; + break; + case 4: + align = 2; + break; + case 2: + case 6: + align = 4; + break; + case 1: + case 3: + case 5: + case 7: + align = 8; + break; + } + + v4l_bound_align_image(&fmt->width, 1, CSIS_MAX_PIX_WIDTH, align, + &fmt->height, 1, CSIS_MAX_PIX_HEIGHT, 1, 0); + sdformat->format = *fmt; + /* Propagate the format from sink to source. */ + fmt = mipi_csis_get_format(state, cfg, sdformat->which, + CSIS_PAD_SOURCE); + *fmt = sdformat->format; + + /* Store the CSIS format descriptor for active formats. */ + if (sdformat->which == V4L2_SUBDEV_FORMAT_ACTIVE) + state->csis_fmt = csis_fmt; + mutex_unlock(&state->lock); return 0; @@ -801,6 +913,7 @@ static const struct v4l2_subdev_core_ops mipi_csis_core_ops = { static const struct media_entity_operations mipi_csis_entity_ops = { .link_setup = mipi_csis_link_setup, .link_validate = v4l2_subdev_link_validate, + .get_fwnode_pad = v4l2_subdev_get_fwnode_pad_1_to_1, }; static const struct v4l2_subdev_video_ops mipi_csis_video_ops = { @@ -809,6 +922,7 @@ static const struct v4l2_subdev_video_ops mipi_csis_video_ops = { static const struct v4l2_subdev_pad_ops mipi_csis_pad_ops = { .init_cfg = mipi_csis_init_cfg, + .enum_mbus_code = mipi_csis_enum_mbus_code, .get_fmt = mipi_csis_get_fmt, .set_fmt = mipi_csis_set_fmt, }; @@ -841,33 +955,25 @@ static int mipi_csis_parse_dt(struct platform_device *pdev, static int mipi_csis_pm_resume(struct device *dev, bool runtime); -static int mipi_csis_parse_endpoint(struct device *dev, - struct v4l2_fwnode_endpoint *ep, - struct v4l2_async_subdev *asd) +static int mipi_csis_notify_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, + struct v4l2_async_subdev *asd) { - struct v4l2_subdev *mipi_sd = dev_get_drvdata(dev); - struct csi_state *state = mipi_sd_to_csis_state(mipi_sd); - - if (ep->bus_type != V4L2_MBUS_CSI2_DPHY) { - dev_err(dev, "invalid bus type, must be MIPI CSI2\n"); - return -EINVAL; - } - - state->bus = ep->bus.mipi_csi2; - - dev_dbg(state->dev, "data lanes: %d\n", state->bus.num_data_lanes); - dev_dbg(state->dev, "flags: 0x%08x\n", state->bus.flags); + struct csi_state *state = mipi_notifier_to_csis_state(notifier); + struct media_pad *sink = &state->mipi_sd.entity.pads[CSIS_PAD_SINK]; - return 0; + return v4l2_create_fwnode_links_to_pad(sd, sink); } +static const struct v4l2_async_notifier_operations mipi_csis_notify_ops = { + .bound = mipi_csis_notify_bound, +}; + static int mipi_csis_subdev_init(struct v4l2_subdev *mipi_sd, struct platform_device *pdev, const struct v4l2_subdev_ops *ops) { struct csi_state *state = mipi_sd_to_csis_state(mipi_sd); - unsigned int sink_port = 0; - int ret; v4l2_subdev_init(mipi_sd, ops); mipi_sd->owner = THIS_MODULE; @@ -883,26 +989,66 @@ static int mipi_csis_subdev_init(struct v4l2_subdev *mipi_sd, mipi_sd->dev = &pdev->dev; state->csis_fmt = &mipi_csis_formats[0]; - state->format_mbus.code = mipi_csis_formats[0].code; - state->format_mbus.width = MIPI_CSIS_DEF_PIX_WIDTH; - state->format_mbus.height = MIPI_CSIS_DEF_PIX_HEIGHT; - state->format_mbus.field = V4L2_FIELD_NONE; + mipi_csis_init_cfg(mipi_sd, NULL); v4l2_set_subdevdata(mipi_sd, &pdev->dev); state->pads[CSIS_PAD_SINK].flags = MEDIA_PAD_FL_SINK; state->pads[CSIS_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; - ret = media_entity_pads_init(&mipi_sd->entity, CSIS_PADS_NUM, - state->pads); + return media_entity_pads_init(&mipi_sd->entity, CSIS_PADS_NUM, + state->pads); +} + +static int mipi_csis_async_register(struct csi_state *state) +{ + struct v4l2_fwnode_endpoint vep = { + .bus_type = V4L2_MBUS_CSI2_DPHY, + }; + struct v4l2_async_subdev *asd = NULL; + struct fwnode_handle *ep; + int ret; + + v4l2_async_notifier_init(&state->notifier); + + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(state->dev), 0, 0, + FWNODE_GRAPH_ENDPOINT_NEXT); + if (!ep) + return -ENOTCONN; + + ret = v4l2_fwnode_endpoint_parse(ep, &vep); + if (ret) + goto err_parse; + + state->bus = vep.bus.mipi_csi2; + + dev_dbg(state->dev, "data lanes: %d\n", state->bus.num_data_lanes); + dev_dbg(state->dev, "flags: 0x%08x\n", state->bus.flags); + + asd = kzalloc(sizeof(*asd), GFP_KERNEL); + if (!asd) { + ret = -ENOMEM; + goto err_parse; + } + + ret = v4l2_async_notifier_add_fwnode_remote_subdev( + &state->notifier, ep, asd); + if (ret) + goto err_parse; + + fwnode_handle_put(ep); + + state->notifier.ops = &mipi_csis_notify_ops; + + ret = v4l2_async_subdev_notifier_register(&state->mipi_sd, + &state->notifier); if (ret) return ret; - ret = v4l2_async_register_fwnode_subdev(mipi_sd, - sizeof(struct v4l2_async_subdev), - &sink_port, 1, - mipi_csis_parse_endpoint); - if (ret < 0) - dev_err(&pdev->dev, "async fwnode register failed: %d\n", ret); + return v4l2_async_register_subdev(&state->mipi_sd); + +err_parse: + fwnode_handle_put(ep); + kfree(asd); return ret; } @@ -915,33 +1061,14 @@ static int mipi_csis_dump_regs_show(struct seq_file *m, void *private) } DEFINE_SHOW_ATTRIBUTE(mipi_csis_dump_regs); -static int mipi_csis_debugfs_init(struct csi_state *state) +static void mipi_csis_debugfs_init(struct csi_state *state) { - struct dentry *d; - - if (!debugfs_initialized()) - return -ENODEV; - state->debugfs_root = debugfs_create_dir(dev_name(state->dev), NULL); - if (!state->debugfs_root) - return -ENOMEM; - - d = debugfs_create_bool("debug_enable", 0600, state->debugfs_root, - &state->debug); - if (!d) - goto remove_debugfs; - - d = debugfs_create_file("dump_regs", 0600, state->debugfs_root, - state, &mipi_csis_dump_regs_fops); - if (!d) - goto remove_debugfs; - - return 0; -remove_debugfs: - debugfs_remove_recursive(state->debugfs_root); - - return -ENOMEM; + debugfs_create_bool("debug_enable", 0600, state->debugfs_root, + &state->debug); + debugfs_create_file("dump_regs", 0600, state->debugfs_root, state, + &mipi_csis_dump_regs_fops); } static void mipi_csis_debugfs_exit(struct csi_state *state) @@ -1009,6 +1136,12 @@ static int mipi_csis_probe(struct platform_device *pdev) if (ret < 0) goto disable_clock; + ret = mipi_csis_async_register(state); + if (ret < 0) { + dev_err(&pdev->dev, "async register failed: %d\n", ret); + goto cleanup; + } + memcpy(state->events, mipi_csis_events, sizeof(state->events)); mipi_csis_debugfs_init(state); @@ -1027,7 +1160,10 @@ static int mipi_csis_probe(struct platform_device *pdev) unregister_all: mipi_csis_debugfs_exit(state); +cleanup: media_entity_cleanup(&state->mipi_sd.entity); + v4l2_async_notifier_unregister(&state->notifier); + v4l2_async_notifier_cleanup(&state->notifier); v4l2_async_unregister_subdev(&state->mipi_sd); disable_clock: mipi_csis_clk_disable(state); @@ -1115,6 +1251,8 @@ static int mipi_csis_remove(struct platform_device *pdev) struct csi_state *state = mipi_sd_to_csis_state(mipi_sd); mipi_csis_debugfs_exit(state); + v4l2_async_notifier_unregister(&state->notifier); + v4l2_async_notifier_cleanup(&state->notifier); v4l2_async_unregister_subdev(&state->mipi_sd); pm_runtime_disable(&pdev->dev); |