Hi Jean-Michel, Thank you for the patch. On Fri, Jan 21, 2022 at 09:18:09AM +0100, Jean-Michel Hautbois wrote: > As of now, imx219 was not supporting anything more than one stream. Add > support for embedded data, based on linux-rpi kernel, and make it work > with multiplexed streams. We have only one source pad with two streams: > stream 0 is the image, and stream 1 is the embedded data. > > Signed-off-by: Naushir Patuck <naush@xxxxxxxxxxxxxxx> > Signed-off-by: Jean-Michel Hautbois <jeanmichel.hautbois@xxxxxxxxxxxxxxxx> > > --- > in v2: modified the get_format_pad function as it was not correctly > propagating the format in case of sensor flips. > --- > drivers/media/i2c/imx219.c | 452 ++++++++++++++++++++----------------- This is too big, it bundles multiple changes together. It should be split in multiple patches. > 1 file changed, 241 insertions(+), 211 deletions(-) > > diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c > index e10af3f74b38..d73fe6b8b2fb 100644 > --- a/drivers/media/i2c/imx219.c > +++ b/drivers/media/i2c/imx219.c > @@ -118,6 +118,16 @@ > #define IMX219_PIXEL_ARRAY_WIDTH 3280U > #define IMX219_PIXEL_ARRAY_HEIGHT 2464U > > +/* Embedded metadata stream structure */ > +#define IMX219_EMBEDDED_LINE_WIDTH 16384 > +#define IMX219_NUM_EMBEDDED_LINES 1 > + > +enum pad_types { > + IMAGE_PAD, > + METADATA_PAD, > + NUM_PADS > +}; No used. > + > struct imx219_reg { > u16 address; > u8 val; > @@ -429,7 +439,7 @@ static const char * const imx219_supply_name[] = { > * - v flip > * - h&v flips > */ > -static const u32 codes[] = { > +static const u32 imx219_mbus_formats[] = { For instance renaming this array should go in a patch of its own. > MEDIA_BUS_FMT_SRGGB10_1X10, > MEDIA_BUS_FMT_SGRBG10_1X10, > MEDIA_BUS_FMT_SGBRG10_1X10, > @@ -655,62 +665,17 @@ static u32 imx219_get_format_code(struct imx219 *imx219, u32 code) > > lockdep_assert_held(&imx219->mutex); > > - for (i = 0; i < ARRAY_SIZE(codes); i++) > - if (codes[i] == code) > + for (i = 0; i < ARRAY_SIZE(imx219_mbus_formats); i++) > + if (imx219_mbus_formats[i] == code) > break; > > - if (i >= ARRAY_SIZE(codes)) > + if (i >= ARRAY_SIZE(imx219_mbus_formats)) > i = 0; > > i = (i & ~3) | (imx219->vflip->val ? 2 : 0) | > (imx219->hflip->val ? 1 : 0); > > - return codes[i]; > -} > - > -static void imx219_set_default_format(struct imx219 *imx219) > -{ > - struct v4l2_mbus_framefmt *fmt; > - > - fmt = &imx219->fmt; > - fmt->code = MEDIA_BUS_FMT_SRGGB10_1X10; > - fmt->colorspace = V4L2_COLORSPACE_SRGB; > - fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace); > - fmt->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true, > - fmt->colorspace, > - fmt->ycbcr_enc); > - fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace); > - fmt->width = supported_modes[0].width; > - fmt->height = supported_modes[0].height; > - fmt->field = V4L2_FIELD_NONE; > -} > - > -static int imx219_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) So should switching from open to init_cfg. Same thing for adding support for the V4L2 subdev active state (v4l2_subdev_init_finalize() and the corresponding changes in the get/set format handlers). This is mainline, not a BSP, patches must be reviewable. > -{ > - struct imx219 *imx219 = to_imx219(sd); > - struct v4l2_mbus_framefmt *try_fmt = > - v4l2_subdev_get_try_format(sd, fh->state, 0); > - struct v4l2_rect *try_crop; > - > - mutex_lock(&imx219->mutex); > - > - /* Initialize try_fmt */ > - try_fmt->width = supported_modes[0].width; > - try_fmt->height = supported_modes[0].height; > - try_fmt->code = imx219_get_format_code(imx219, > - MEDIA_BUS_FMT_SRGGB10_1X10); > - try_fmt->field = V4L2_FIELD_NONE; > - > - /* Initialize try_crop rectangle. */ > - try_crop = v4l2_subdev_get_try_crop(sd, fh->state, 0); > - try_crop->top = IMX219_PIXEL_ARRAY_TOP; > - try_crop->left = IMX219_PIXEL_ARRAY_LEFT; > - try_crop->width = IMX219_PIXEL_ARRAY_WIDTH; > - try_crop->height = IMX219_PIXEL_ARRAY_HEIGHT; > - > - mutex_unlock(&imx219->mutex); > - > - return 0; > + return imx219_mbus_formats[i]; > } > > static int imx219_set_ctrl(struct v4l2_ctrl *ctrl) > @@ -802,98 +767,148 @@ static const struct v4l2_ctrl_ops imx219_ctrl_ops = { > .s_ctrl = imx219_set_ctrl, > }; > > -static int imx219_enum_mbus_code(struct v4l2_subdev *sd, > - struct v4l2_subdev_state *sd_state, > - struct v4l2_subdev_mbus_code_enum *code) > +static void imx219_init_formats(struct v4l2_subdev_state *state) > { > - struct imx219 *imx219 = to_imx219(sd); > + struct v4l2_mbus_framefmt *format; > + > + format = v4l2_state_get_stream_format(state, 0, 0); > + format->code = imx219_mbus_formats[0]; > + format->width = supported_modes[0].width; > + format->height = supported_modes[0].height; > + format->field = V4L2_FIELD_NONE; > + format->colorspace = V4L2_COLORSPACE_RAW; > + > + if (state->routing.routes[1].flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE) { > + format = v4l2_state_get_stream_format(state, 0, 1); > + format->code = MEDIA_BUS_FMT_METADATA_8; > + format->width = IMX219_EMBEDDED_LINE_WIDTH; > + format->height = 1; > + format->field = V4L2_FIELD_NONE; > + format->colorspace = V4L2_COLORSPACE_DEFAULT; > + } > +} > > - if (code->index >= (ARRAY_SIZE(codes) / 4)) > - return -EINVAL; > +static int _imx219_set_routing(struct v4l2_subdev *sd, > + struct v4l2_subdev_state *state) > +{ > + struct v4l2_subdev_route routes[] = { > + { > + .source_pad = 0, > + .source_stream = 0, > + .flags = V4L2_SUBDEV_ROUTE_FL_IMMUTABLE | > + V4L2_SUBDEV_ROUTE_FL_SOURCE | > + V4L2_SUBDEV_ROUTE_FL_ACTIVE, > + }, > + { > + .source_pad = 0, > + .source_stream = 1, > + .flags = V4L2_SUBDEV_ROUTE_FL_SOURCE | > + V4L2_SUBDEV_ROUTE_FL_ACTIVE, > + } > + }; > > - mutex_lock(&imx219->mutex); > - code->code = imx219_get_format_code(imx219, codes[code->index * 4]); > - mutex_unlock(&imx219->mutex); > + struct v4l2_subdev_krouting routing = { > + .num_routes = ARRAY_SIZE(routes), > + .routes = routes, > + }; > + > + int ret; > + > + ret = v4l2_subdev_set_routing(sd, state, &routing); > + if (ret) > + return ret; > + > + imx219_init_formats(state); > > return 0; > } > > -static int imx219_enum_frame_size(struct v4l2_subdev *sd, > - struct v4l2_subdev_state *sd_state, > - struct v4l2_subdev_frame_size_enum *fse) > +static int imx219_set_routing(struct v4l2_subdev *sd, > + struct v4l2_subdev_state *state, > + enum v4l2_subdev_format_whence which, > + struct v4l2_subdev_krouting *routing) > { > - struct imx219 *imx219 = to_imx219(sd); > - u32 code; > + int ret; > > - if (fse->index >= ARRAY_SIZE(supported_modes)) > + if (routing->num_routes == 0 || routing->num_routes > 2) > return -EINVAL; > > - mutex_lock(&imx219->mutex); > - code = imx219_get_format_code(imx219, fse->code); > - mutex_unlock(&imx219->mutex); > - if (fse->code != code) > - return -EINVAL; > + v4l2_subdev_lock_state(state); > > - fse->min_width = supported_modes[fse->index].width; > - fse->max_width = fse->min_width; > - fse->min_height = supported_modes[fse->index].height; > - fse->max_height = fse->min_height; > + ret = _imx219_set_routing(sd, state); > > - return 0; > + v4l2_subdev_unlock_state(state); > + > + return ret; > } > > -static void imx219_reset_colorspace(struct v4l2_mbus_framefmt *fmt) > +static int imx219_init_cfg(struct v4l2_subdev *sd, > + struct v4l2_subdev_state *state) > { > - fmt->colorspace = V4L2_COLORSPACE_SRGB; > - fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace); > - fmt->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true, > - fmt->colorspace, > - fmt->ycbcr_enc); > - fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace); > + int ret; > + > + v4l2_subdev_lock_state(state); > + > + ret = _imx219_set_routing(sd, state); > + > + v4l2_subdev_unlock_state(state); > + > + return ret; > } > > -static void imx219_update_pad_format(struct imx219 *imx219, > - const struct imx219_mode *mode, > - struct v4l2_subdev_format *fmt) > +static int imx219_enum_mbus_code(struct v4l2_subdev *sd, > + struct v4l2_subdev_state *sd_state, > + struct v4l2_subdev_mbus_code_enum *code) > { > - fmt->format.width = mode->width; > - fmt->format.height = mode->height; > - fmt->format.field = V4L2_FIELD_NONE; > - imx219_reset_colorspace(&fmt->format); > + if (code->index >= ARRAY_SIZE(imx219_mbus_formats)) > + return -EINVAL; > + > + code->code = imx219_mbus_formats[code->index]; > + > + return 0; > } > > -static int __imx219_get_pad_format(struct imx219 *imx219, > - struct v4l2_subdev_state *sd_state, > - struct v4l2_subdev_format *fmt) > +static int imx219_enum_frame_size(struct v4l2_subdev *sd, > + struct v4l2_subdev_state *sd_state, > + struct v4l2_subdev_frame_size_enum *fse) > { > - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { > - struct v4l2_mbus_framefmt *try_fmt = > - v4l2_subdev_get_try_format(&imx219->sd, sd_state, > - fmt->pad); > - /* update the code which could change due to vflip or hflip: */ > - try_fmt->code = imx219_get_format_code(imx219, try_fmt->code); > - fmt->format = *try_fmt; > + unsigned int i; > + > + if (fse->stream == 0) { > + for (i = 0; i < ARRAY_SIZE(imx219_mbus_formats); ++i) { > + if (imx219_mbus_formats[i] == fse->code) > + break; > + } > + > + if (i == ARRAY_SIZE(imx219_mbus_formats)) > + return -EINVAL; > + > + if (fse->index >= ARRAY_SIZE(supported_modes)) > + return -EINVAL; > + > + fse->min_width = supported_modes[fse->index].width; > + fse->max_width = fse->min_width; > + fse->max_height = supported_modes[fse->index].height; > + fse->min_height = fse->max_height; > } else { > - imx219_update_pad_format(imx219, imx219->mode, fmt); > - fmt->format.code = imx219_get_format_code(imx219, > - imx219->fmt.code); > + if (fse->code != MEDIA_BUS_FMT_METADATA_8) > + return -EINVAL; > + > + fse->min_width = IMX219_EMBEDDED_LINE_WIDTH; > + fse->max_width = fse->min_width; > + fse->min_height = IMX219_NUM_EMBEDDED_LINES; > + fse->max_height = fse->min_height; > } > > return 0; > } > > -static int imx219_get_pad_format(struct v4l2_subdev *sd, > - struct v4l2_subdev_state *sd_state, > - struct v4l2_subdev_format *fmt) > +static void imx219_update_metadata_pad_format(struct v4l2_subdev_format *fmt) > { > - struct imx219 *imx219 = to_imx219(sd); > - int ret; > - > - mutex_lock(&imx219->mutex); > - ret = __imx219_get_pad_format(imx219, sd_state, fmt); > - mutex_unlock(&imx219->mutex); > - > - return ret; > + fmt->format.width = IMX219_EMBEDDED_LINE_WIDTH; > + fmt->format.height = IMX219_NUM_EMBEDDED_LINES; > + fmt->format.code = MEDIA_BUS_FMT_METADATA_8; > + fmt->format.field = V4L2_FIELD_NONE; > } > > static int imx219_set_pad_format(struct v4l2_subdev *sd, > @@ -901,82 +916,91 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd, > struct v4l2_subdev_format *fmt) > { > struct imx219 *imx219 = to_imx219(sd); > + struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd); > const struct imx219_mode *mode; > - struct v4l2_mbus_framefmt *framefmt; > - int exposure_max, exposure_def, hblank; > + struct v4l2_mbus_framefmt *format; > unsigned int i; > + int ret = 0; > + int exposure_max, exposure_def, hblank; > > - mutex_lock(&imx219->mutex); > + if (fmt->pad != 0) { > + dev_err(&client->dev, "%s Could not get pad %d\n", __func__, > + fmt->pad); > + return -EINVAL; > + } > > - for (i = 0; i < ARRAY_SIZE(codes); i++) > - if (codes[i] == fmt->format.code) > + if (fmt->stream == 1) { > + /* Only one embedded data mode is supported */ > + imx219_update_metadata_pad_format(fmt); > + return 0; > + } > + > + if (fmt->stream != 0) > + return -EINVAL; > + > + /* > + * Validate the media bus code, defaulting to the first one if the > + * requested code isn't supported. > + */ > + for (i = 0; i < ARRAY_SIZE(imx219_mbus_formats); ++i) { > + if (imx219_mbus_formats[i] == fmt->format.code) > break; > - if (i >= ARRAY_SIZE(codes)) > - i = 0; > + } > > - /* Bayer order varies with flips */ > - fmt->format.code = imx219_get_format_code(imx219, codes[i]); > + if (i >= ARRAY_SIZE(imx219_mbus_formats)) > + i = 0; > > mode = v4l2_find_nearest_size(supported_modes, > ARRAY_SIZE(supported_modes), > width, height, > - fmt->format.width, fmt->format.height); > - imx219_update_pad_format(imx219, mode, fmt); > - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { > - framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); > - *framefmt = fmt->format; > - } else if (imx219->mode != mode || > - imx219->fmt.code != fmt->format.code) { > - imx219->fmt = fmt->format; > - imx219->mode = mode; > - /* Update limits and set FPS to default */ > - __v4l2_ctrl_modify_range(imx219->vblank, IMX219_VBLANK_MIN, > - IMX219_VTS_MAX - mode->height, 1, > - mode->vts_def - mode->height); > - __v4l2_ctrl_s_ctrl(imx219->vblank, > - mode->vts_def - mode->height); > - /* Update max exposure while meeting expected vblanking */ > - exposure_max = mode->vts_def - 4; > - exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ? > - exposure_max : IMX219_EXPOSURE_DEFAULT; > - __v4l2_ctrl_modify_range(imx219->exposure, > - imx219->exposure->minimum, > - exposure_max, imx219->exposure->step, > - exposure_def); > - /* > - * Currently PPL is fixed to IMX219_PPL_DEFAULT, so hblank > - * depends on mode->width only, and is not changeble in any > - * way other than changing the mode. > - */ > - hblank = IMX219_PPL_DEFAULT - mode->width; > - __v4l2_ctrl_modify_range(imx219->hblank, hblank, hblank, 1, > - hblank); > - } > + fmt->format.width, > + fmt->format.height); > > - mutex_unlock(&imx219->mutex); > + v4l2_subdev_lock_state(sd_state); > > - return 0; > -} > + /* Update the stored format and return it. */ > + format = v4l2_state_get_stream_format(sd_state, fmt->pad, fmt->stream); > > -static int imx219_set_framefmt(struct imx219 *imx219) > -{ > - switch (imx219->fmt.code) { > - case MEDIA_BUS_FMT_SRGGB8_1X8: > - case MEDIA_BUS_FMT_SGRBG8_1X8: > - case MEDIA_BUS_FMT_SGBRG8_1X8: > - case MEDIA_BUS_FMT_SBGGR8_1X8: > - return imx219_write_regs(imx219, raw8_framefmt_regs, > - ARRAY_SIZE(raw8_framefmt_regs)); > - > - case MEDIA_BUS_FMT_SRGGB10_1X10: > - case MEDIA_BUS_FMT_SGRBG10_1X10: > - case MEDIA_BUS_FMT_SGBRG10_1X10: > - case MEDIA_BUS_FMT_SBGGR10_1X10: > - return imx219_write_regs(imx219, raw10_framefmt_regs, > - ARRAY_SIZE(raw10_framefmt_regs)); > + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE && imx219->streaming) { > + ret = -EBUSY; > + goto done; > } > > - return -EINVAL; > + /* Bayer order varies with flips */ > + fmt->format.code = imx219_get_format_code(imx219, imx219_mbus_formats[i]); > + fmt->format = *format; > + > + /* Update limits and set FPS to default */ > + __v4l2_ctrl_modify_range(imx219->vblank, > + IMX219_VBLANK_MIN, > + IMX219_VTS_MAX - mode->height, > + 1, > + mode->vts_def - mode->height); > + __v4l2_ctrl_s_ctrl(imx219->vblank, mode->vts_def - mode->height); > + /* > + * Update max exposure while meeting > + * expected vblanking > + */ > + exposure_max = mode->vts_def - 4; > + exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ? > + exposure_max : IMX219_EXPOSURE_DEFAULT; > + __v4l2_ctrl_modify_range(imx219->exposure, > + imx219->exposure->minimum, > + exposure_max, > + imx219->exposure->step, > + exposure_def); > + /* > + * Currently PPL is fixed to IMX219_PPL_DEFAULT, so > + * hblank depends on mode->width only, and is not > + * changeble in any way other than changing the mode. > + */ > + hblank = IMX219_PPL_DEFAULT - mode->width; > + __v4l2_ctrl_modify_range(imx219->hblank, hblank, hblank, 1, hblank); > + > +done: > + v4l2_subdev_unlock_state(sd_state); > + > + return ret; > } > > static const struct v4l2_rect * > @@ -1037,9 +1061,11 @@ static int imx219_start_streaming(struct imx219 *imx219) > const struct imx219_reg_list *reg_list; > int ret; > > - ret = pm_runtime_resume_and_get(&client->dev); > - if (ret < 0) > + ret = pm_runtime_get_sync(&client->dev); > + if (ret < 0) { > + pm_runtime_put_noidle(&client->dev); > return ret; > + } > > /* Apply default values of current mode */ > reg_list = &imx219->mode->reg_list; > @@ -1049,13 +1075,6 @@ static int imx219_start_streaming(struct imx219 *imx219) > goto err_rpm_put; > } > > - ret = imx219_set_framefmt(imx219); > - if (ret) { > - dev_err(&client->dev, "%s failed to set frame format: %d\n", > - __func__, ret); > - goto err_rpm_put; > - } > - > /* Apply customized values from user */ > ret = __v4l2_ctrl_handler_setup(imx219->sd.ctrl_handler); > if (ret) > @@ -1133,21 +1152,22 @@ static int imx219_set_stream(struct v4l2_subdev *sd, int enable) > /* Power/clock management functions */ > static int imx219_power_on(struct device *dev) > { > - struct v4l2_subdev *sd = dev_get_drvdata(dev); > + struct i2c_client *client = to_i2c_client(dev); > + struct v4l2_subdev *sd = i2c_get_clientdata(client); > struct imx219 *imx219 = to_imx219(sd); > int ret; > > ret = regulator_bulk_enable(IMX219_NUM_SUPPLIES, > imx219->supplies); > if (ret) { > - dev_err(dev, "%s: failed to enable regulators\n", > + dev_err(&client->dev, "%s: failed to enable regulators\n", > __func__); > return ret; > } > > ret = clk_prepare_enable(imx219->xclk); > if (ret) { > - dev_err(dev, "%s: failed to enable clock\n", > + dev_err(&client->dev, "%s: failed to enable clock\n", > __func__); > goto reg_off; > } > @@ -1166,7 +1186,8 @@ static int imx219_power_on(struct device *dev) > > static int imx219_power_off(struct device *dev) > { > - struct v4l2_subdev *sd = dev_get_drvdata(dev); > + struct i2c_client *client = to_i2c_client(dev); > + struct v4l2_subdev *sd = i2c_get_clientdata(client); > struct imx219 *imx219 = to_imx219(sd); > > gpiod_set_value_cansleep(imx219->reset_gpio, 0); > @@ -1178,7 +1199,8 @@ static int imx219_power_off(struct device *dev) > > static int __maybe_unused imx219_suspend(struct device *dev) > { > - struct v4l2_subdev *sd = dev_get_drvdata(dev); > + struct i2c_client *client = to_i2c_client(dev); > + struct v4l2_subdev *sd = i2c_get_clientdata(client); > struct imx219 *imx219 = to_imx219(sd); > > if (imx219->streaming) > @@ -1189,7 +1211,8 @@ static int __maybe_unused imx219_suspend(struct device *dev) > > static int __maybe_unused imx219_resume(struct device *dev) > { > - struct v4l2_subdev *sd = dev_get_drvdata(dev); > + struct i2c_client *client = to_i2c_client(dev); > + struct v4l2_subdev *sd = i2c_get_clientdata(client); > struct imx219 *imx219 = to_imx219(sd); > int ret; > > @@ -1255,11 +1278,13 @@ static const struct v4l2_subdev_video_ops imx219_video_ops = { > }; > > static const struct v4l2_subdev_pad_ops imx219_pad_ops = { > - .enum_mbus_code = imx219_enum_mbus_code, > - .get_fmt = imx219_get_pad_format, > - .set_fmt = imx219_set_pad_format, > - .get_selection = imx219_get_selection, > - .enum_frame_size = imx219_enum_frame_size, > + .init_cfg = imx219_init_cfg, > + .enum_mbus_code = imx219_enum_mbus_code, > + .get_fmt = v4l2_subdev_get_fmt, > + .set_fmt = imx219_set_pad_format, > + .get_selection = imx219_get_selection, > + .set_routing = imx219_set_routing, > + .enum_frame_size = imx219_enum_frame_size, > }; > > static const struct v4l2_subdev_ops imx219_subdev_ops = { > @@ -1268,10 +1293,6 @@ static const struct v4l2_subdev_ops imx219_subdev_ops = { > .pad = &imx219_pad_ops, > }; > > -static const struct v4l2_subdev_internal_ops imx219_internal_ops = { > - .open = imx219_open, > -}; > - > /* Initialize control handlers */ > static int imx219_init_controls(struct imx219 *imx219) > { > @@ -1446,6 +1467,7 @@ static int imx219_check_hwcfg(struct device *dev) > static int imx219_probe(struct i2c_client *client) > { > struct device *dev = &client->dev; > + struct v4l2_subdev *sd; > struct imx219 *imx219; > int ret; > > @@ -1453,7 +1475,8 @@ static int imx219_probe(struct i2c_client *client) > if (!imx219) > return -ENOMEM; > > - v4l2_i2c_subdev_init(&imx219->sd, client, &imx219_subdev_ops); > + sd = &imx219->sd; > + v4l2_i2c_subdev_init(sd, client, &imx219_subdev_ops); > > /* Check the hardware configuration in device tree */ > if (imx219_check_hwcfg(dev)) > @@ -1520,27 +1543,29 @@ static int imx219_probe(struct i2c_client *client) > goto error_power_off; > > /* Initialize subdev */ > - imx219->sd.internal_ops = &imx219_internal_ops; > - imx219->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | > - V4L2_SUBDEV_FL_HAS_EVENTS; > - imx219->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; > + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | > + V4L2_SUBDEV_FL_HAS_EVENTS | > + V4L2_SUBDEV_FL_MULTIPLEXED; > > - /* Initialize source pad */ > + /* Initialize the media entity. */ > imx219->pad.flags = MEDIA_PAD_FL_SOURCE; > - > - /* Initialize default format */ > - imx219_set_default_format(imx219); > - > - ret = media_entity_pads_init(&imx219->sd.entity, 1, &imx219->pad); > + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; > + ret = media_entity_pads_init(&sd->entity, 1, &imx219->pad); > if (ret) { > dev_err(dev, "failed to init entity pads: %d\n", ret); > goto error_handler_free; > } > > - ret = v4l2_async_register_subdev_sensor(&imx219->sd); > + ret = v4l2_subdev_init_finalize(sd); > + if (ret) { > + dev_err(dev, "failed to finalize sensor init: %d\n", ret); > + goto error_media_entity; > + } > + > + ret = v4l2_async_register_subdev_sensor(sd); > if (ret < 0) { > dev_err(dev, "failed to register sensor sub-device: %d\n", ret); > - goto error_media_entity; > + goto error_free_state; > } > > /* Enable runtime PM and turn off the device */ > @@ -1550,6 +1575,8 @@ static int imx219_probe(struct i2c_client *client) > > return 0; > > +error_free_state: > + v4l2_subdev_cleanup(sd); > error_media_entity: > media_entity_cleanup(&imx219->sd.entity); > > @@ -1568,6 +1595,9 @@ static int imx219_remove(struct i2c_client *client) > struct imx219 *imx219 = to_imx219(sd); > > v4l2_async_unregister_subdev(sd); > + > + v4l2_subdev_cleanup(sd); > + > media_entity_cleanup(&sd->entity); > imx219_free_controls(imx219); > -- Regards, Laurent Pinchart