diff options
Diffstat (limited to 'target/linux/bcm27xx/patches-5.15/950-0602-media-imx219-Advertise-embedded-data-node-on-media-p.patch')
-rw-r--r-- | target/linux/bcm27xx/patches-5.15/950-0602-media-imx219-Advertise-embedded-data-node-on-media-p.patch | 416 |
1 files changed, 416 insertions, 0 deletions
diff --git a/target/linux/bcm27xx/patches-5.15/950-0602-media-imx219-Advertise-embedded-data-node-on-media-p.patch b/target/linux/bcm27xx/patches-5.15/950-0602-media-imx219-Advertise-embedded-data-node-on-media-p.patch new file mode 100644 index 0000000000..79487b60cf --- /dev/null +++ b/target/linux/bcm27xx/patches-5.15/950-0602-media-imx219-Advertise-embedded-data-node-on-media-p.patch @@ -0,0 +1,416 @@ +From c144d907a9067f948706a4cb8709672b0708c4c4 Mon Sep 17 00:00:00 2001 +From: Naushir Patuck <naush@raspberrypi.com> +Date: Wed, 8 Dec 2021 13:22:48 +0100 +Subject: [PATCH] media: imx219: Advertise embedded data node on media + pad 1 + +This commit updates the imx219 driver to adverise support for embedded +data streams. This can then be used by the bcm2835-unicam driver, which +has recently been updated to expose the embedded data stream to +userland. + +The imx219 sensor subdevice overloads the media pad to differentiate +between image stream (pad 0) and embedded data stream (pad 1) when +performing the v4l2_subdev_pad_ops functions. + +Signed-off-by: Naushir Patuck <naush@raspberrypi.com> +[JMH: Adapt to the mainline 5.16 kernel] +Signed-off-by: Jean-Michel Hautbois <jeanmichel.hautbois@ideasonboard.com> +--- + drivers/media/i2c/imx219.c | 254 ++++++++++++++++++++++++------------- + 1 file changed, 169 insertions(+), 85 deletions(-) + +--- 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 ++}; ++ + struct imx219_reg { + u16 address; + u8 val; +@@ -538,7 +548,7 @@ static const struct imx219_mode supporte + + struct imx219 { + struct v4l2_subdev sd; +- struct media_pad pad; ++ struct media_pad pad[NUM_PADS]; + + struct v4l2_mbus_framefmt fmt; + +@@ -688,18 +698,26 @@ static void imx219_set_default_format(st + static int imx219_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) + { + struct imx219 *imx219 = to_imx219(sd); +- struct v4l2_mbus_framefmt *try_fmt = +- v4l2_subdev_get_try_format(sd, fh->state, 0); ++ struct v4l2_mbus_framefmt *try_fmt_img = ++ v4l2_subdev_get_try_format(sd, fh->state, IMAGE_PAD); ++ struct v4l2_mbus_framefmt *try_fmt_meta = ++ v4l2_subdev_get_try_format(sd, fh->state, METADATA_PAD); + 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_fmt for the image pad */ ++ try_fmt_img->width = supported_modes[0].width; ++ try_fmt_img->height = supported_modes[0].height; ++ try_fmt_img->code = imx219_get_format_code(imx219, ++ MEDIA_BUS_FMT_SRGGB10_1X10); ++ try_fmt_img->field = V4L2_FIELD_NONE; ++ ++ /* Initialize try_fmt for the embedded metadata pad */ ++ try_fmt_meta->width = IMX219_EMBEDDED_LINE_WIDTH; ++ try_fmt_meta->height = IMX219_NUM_EMBEDDED_LINES; ++ try_fmt_meta->code = MEDIA_BUS_FMT_SENSOR_DATA; ++ try_fmt_meta->field = V4L2_FIELD_NONE; + + /* Initialize try_crop rectangle. */ + try_crop = v4l2_subdev_get_try_crop(sd, fh->state, 0); +@@ -808,12 +826,21 @@ static int imx219_enum_mbus_code(struct + { + struct imx219 *imx219 = to_imx219(sd); + +- if (code->index >= (ARRAY_SIZE(codes) / 4)) ++ if (code->pad >= NUM_PADS) + return -EINVAL; + +- mutex_lock(&imx219->mutex); +- code->code = imx219_get_format_code(imx219, codes[code->index * 4]); +- mutex_unlock(&imx219->mutex); ++ if (code->pad == IMAGE_PAD) { ++ if (code->index >= (ARRAY_SIZE(codes) / 4)) ++ return -EINVAL; ++ ++ code->code = imx219_get_format_code(imx219, ++ codes[code->index * 4]); ++ } else { ++ if (code->index > 0) ++ return -EINVAL; ++ ++ code->code = MEDIA_BUS_FMT_SENSOR_DATA; ++ } + + return 0; + } +@@ -823,21 +850,30 @@ static int imx219_enum_frame_size(struct + struct v4l2_subdev_frame_size_enum *fse) + { + struct imx219 *imx219 = to_imx219(sd); +- u32 code; + +- if (fse->index >= ARRAY_SIZE(supported_modes)) ++ if (fse->pad >= NUM_PADS) + return -EINVAL; + +- mutex_lock(&imx219->mutex); +- code = imx219_get_format_code(imx219, fse->code); +- mutex_unlock(&imx219->mutex); +- if (fse->code != code) +- return -EINVAL; ++ if (fse->pad == IMAGE_PAD) { ++ if (fse->index >= ARRAY_SIZE(supported_modes)) ++ return -EINVAL; ++ ++ if (fse->code != imx219_get_format_code(imx219, fse->code)) ++ return -EINVAL; ++ ++ 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; ++ } else { ++ if (fse->code != MEDIA_BUS_FMT_SENSOR_DATA || fse->index > 0) ++ return -EINVAL; + +- 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; ++ 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; + } +@@ -852,9 +888,9 @@ static void imx219_reset_colorspace(stru + fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace); + } + +-static void imx219_update_pad_format(struct imx219 *imx219, +- const struct imx219_mode *mode, +- struct v4l2_subdev_format *fmt) ++static void imx219_update_image_pad_format(struct imx219 *imx219, ++ const struct imx219_mode *mode, ++ struct v4l2_subdev_format *fmt) + { + fmt->format.width = mode->width; + fmt->format.height = mode->height; +@@ -862,21 +898,39 @@ static void imx219_update_pad_format(str + imx219_reset_colorspace(&fmt->format); + } + ++static void imx219_update_metadata_pad_format(struct v4l2_subdev_format *fmt) ++{ ++ fmt->format.width = IMX219_EMBEDDED_LINE_WIDTH; ++ fmt->format.height = IMX219_NUM_EMBEDDED_LINES; ++ fmt->format.code = MEDIA_BUS_FMT_SENSOR_DATA; ++ fmt->format.field = V4L2_FIELD_NONE; ++} ++ + static int __imx219_get_pad_format(struct imx219 *imx219, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) + { ++ if (fmt->pad >= NUM_PADS) ++ return -EINVAL; ++ + 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); ++ try_fmt->code = fmt->pad == IMAGE_PAD ? ++ imx219_get_format_code(imx219, try_fmt->code) : ++ MEDIA_BUS_FMT_SENSOR_DATA; + fmt->format = *try_fmt; + } else { +- imx219_update_pad_format(imx219, imx219->mode, fmt); +- fmt->format.code = imx219_get_format_code(imx219, +- imx219->fmt.code); ++ if (fmt->pad == IMAGE_PAD) { ++ imx219_update_image_pad_format(imx219, imx219->mode, ++ fmt); ++ fmt->format.code = imx219_get_format_code(imx219, ++ imx219->fmt.code); ++ } else { ++ imx219_update_metadata_pad_format(fmt); ++ } + } + + return 0; +@@ -906,51 +960,74 @@ static int imx219_set_pad_format(struct + int exposure_max, exposure_def, hblank; + unsigned int i; + +- mutex_lock(&imx219->mutex); +- +- for (i = 0; i < ARRAY_SIZE(codes); i++) +- if (codes[i] == fmt->format.code) +- break; +- if (i >= ARRAY_SIZE(codes)) +- i = 0; ++ if (fmt->pad >= NUM_PADS) ++ return -EINVAL; + +- /* Bayer order varies with flips */ +- fmt->format.code = imx219_get_format_code(imx219, codes[i]); ++ mutex_lock(&imx219->mutex); + +- 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); ++ if (fmt->pad == IMAGE_PAD) { ++ for (i = 0; i < ARRAY_SIZE(codes); i++) ++ if (codes[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]); ++ ++ mode = v4l2_find_nearest_size(supported_modes, ++ ARRAY_SIZE(supported_modes), ++ width, height, ++ fmt->format.width, ++ fmt->format.height); ++ imx219_update_image_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); ++ } ++ } else { ++ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { ++ framefmt = v4l2_subdev_get_try_format(sd, sd_state, ++ fmt->pad); ++ *framefmt = fmt->format; ++ } else { ++ /* Only one embedded data mode is supported */ ++ imx219_update_metadata_pad_format(fmt); ++ } + } + + mutex_unlock(&imx219->mutex); +@@ -1037,9 +1114,11 @@ static int imx219_start_streaming(struct + 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; +@@ -1133,21 +1212,22 @@ err_unlock: + /* 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 +1246,8 @@ reg_off: + + 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 +1259,8 @@ static int imx219_power_off(struct devic + + 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 +1271,8 @@ static int __maybe_unused imx219_suspend + + 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; + +@@ -1525,13 +1608,14 @@ static int imx219_probe(struct i2c_clien + V4L2_SUBDEV_FL_HAS_EVENTS; + imx219->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + +- /* Initialize source pad */ +- imx219->pad.flags = MEDIA_PAD_FL_SOURCE; ++ /* Initialize source pads */ ++ imx219->pad[IMAGE_PAD].flags = MEDIA_PAD_FL_SOURCE; ++ imx219->pad[METADATA_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); ++ ret = media_entity_pads_init(&imx219->sd.entity, NUM_PADS, imx219->pad); + if (ret) { + dev_err(dev, "failed to init entity pads: %d\n", ret); + goto error_handler_free; |