The AR0144 can generate embedded data. Report it as an additional stream, with a new internal embedded data pad, and update subdev operations accordingly. Make the embedded data generation unconditional for now to simplify the implementation. It can be made configurable later if desired. Signed-off-by: Laurent Pinchart <laurent.pinchart@xxxxxxxxxxxxxxxx> --- Changes since v1: - Fix frame size enumeration --- drivers/media/i2c/ar0144.c | 147 +++++++++++++++++++++++++++++-------- 1 file changed, 118 insertions(+), 29 deletions(-) diff --git a/drivers/media/i2c/ar0144.c b/drivers/media/i2c/ar0144.c index a784d806b189..c2ef8b26a2bc 100644 --- a/drivers/media/i2c/ar0144.c +++ b/drivers/media/i2c/ar0144.c @@ -314,6 +314,9 @@ #define AR0144_PIXEL_ARRAY_ACTIVE_WIDTH 1288U #define AR0144_PIXEL_ARRAY_ACTIVE_HEIGHT 808U +/* Embedded metadata stream height */ +#define AR0144_EMBEDDED_DATA_HEIGHT 2U + /* * Documentation indicates minimum horizontal and vertical blanking of 208 * pixels and 27 lines respectively, which matches the default values for the @@ -339,11 +342,13 @@ enum ar0144_pad_ids { AR0144_PAD_SOURCE = 0, AR0144_PAD_IMAGE, + AR0144_PAD_EDATA, AR0144_NUM_PADS, }; enum ar0144_stream_ids { AR0144_STREAM_IMAGE, + AR0144_STREAM_EDATA, }; struct ar0144_model { @@ -353,6 +358,7 @@ struct ar0144_model { struct ar0144_format_info { u32 colour; u32 mono; + u32 edata; u16 bpp; u16 dt; }; @@ -373,16 +379,19 @@ static const struct ar0144_format_info ar0144_formats[] = { { .colour = MEDIA_BUS_FMT_SGRBG12_1X12, .mono = MEDIA_BUS_FMT_Y12_1X12, + .edata = MEDIA_BUS_FMT_META_12, .bpp = 12, .dt = MIPI_CSI2_DT_RAW12, }, { .colour = MEDIA_BUS_FMT_SGRBG10_1X10, .mono = MEDIA_BUS_FMT_Y10_1X10, + .edata = MEDIA_BUS_FMT_META_10, .bpp = 10, .dt = MIPI_CSI2_DT_RAW10, }, { .colour = MEDIA_BUS_FMT_SGRBG8_1X8, .mono = MEDIA_BUS_FMT_Y8_1X8, + .edata = MEDIA_BUS_FMT_META_8, .bpp = 8, .dt = MIPI_CSI2_DT_RAW8, }, @@ -574,8 +583,8 @@ static int ar0144_start_streaming(struct ar0144 *sensor, * Enable generation of embedded statistics, required for the on-chip * auto-exposure. There is no downside in enabling it unconditionally. */ - cci_write(sensor->regmap, AR0144_SMIA_TEST, AR0144_STATS_EN | 0x1802, - &ret); + cci_write(sensor->regmap, AR0144_SMIA_TEST, AR0144_EMBEDDED_DATA | + AR0144_STATS_EN | 0x1802, &ret); if (ret) goto error; @@ -1136,7 +1145,25 @@ static int ar0144_enum_mbus_code(struct v4l2_subdev *sd, return 0; } - case AR0144_PAD_SOURCE: { + case AR0144_PAD_EDATA: { + if (code->index > 0) + return -EINVAL; + + code->code = MEDIA_BUS_FMT_CCS_EMBEDDED; + return 0; + } + + case AR0144_PAD_SOURCE: + default: + break; + } + + /* + * On the source pad, the sensor supports multiple image raw formats + * with different bit depths. The embedded data format bit depth + * follows the image stream. + */ + if (code->stream == AR0144_STREAM_IMAGE) { unsigned int index = 0; unsigned int i; @@ -1155,11 +1182,18 @@ static int ar0144_enum_mbus_code(struct v4l2_subdev *sd, } return -EINVAL; + } else { + struct v4l2_mbus_framefmt *fmt; + + if (code->index > 0) + return -EINVAL; + + fmt = v4l2_subdev_state_get_format(state, AR0144_PAD_SOURCE, + AR0144_STREAM_EDATA); + code->code = fmt->code; } - default: - return -EINVAL; - } + return 0; } static int ar0144_enum_frame_size(struct v4l2_subdev *sd, @@ -1173,25 +1207,21 @@ static int ar0144_enum_frame_size(struct v4l2_subdev *sd, if (fse->index > 0) return -EINVAL; - switch (fse->pad) { - case AR0144_PAD_IMAGE: - if (fse->code != ar0144_format_code(sensor, &ar0144_formats[0])) - return -EINVAL; + fmt = v4l2_subdev_state_get_format(state, fse->pad, fse->stream); - fmt = v4l2_subdev_state_get_format(state, AR0144_PAD_IMAGE); - break; - - case AR0144_PAD_SOURCE: + /* + * Verify the media bus code. On the source image stream multiple + * options are supported, while on all other streams the requested code + * must match the current format. + */ + if (fse->pad == AR0144_PAD_SOURCE && + fse->stream == AR0144_STREAM_IMAGE) { info = ar0144_format_info(sensor, fse->code, false); if (!info) return -EINVAL; - - fmt = v4l2_subdev_state_get_format(state, AR0144_PAD_SOURCE, - AR0144_STREAM_IMAGE); - break; - - default: - return -EINVAL; + } else { + if (fse->code != fmt->code) + return -EINVAL; } fse->min_width = fmt->width; @@ -1230,6 +1260,11 @@ static int ar0144_set_fmt(struct v4l2_subdev *sd, format->format = *fmt; + /* Update the format on the source side of the embedded data stream. */ + fmt = v4l2_subdev_state_get_format(state, AR0144_PAD_SOURCE, + AR0144_STREAM_EDATA); + fmt->code = info->edata; + if (format->which != V4L2_SUBDEV_FORMAT_ACTIVE) return 0; @@ -1353,6 +1388,14 @@ static int ar0144_set_selection(struct v4l2_subdev *sd, fmt->width = compose->width; fmt->height = compose->height; + /* Update the embedded data stream width. */ + fmt = v4l2_subdev_state_get_format(state, AR0144_PAD_EDATA); + fmt->width = compose->width; + + fmt = v4l2_subdev_state_get_format(state, AR0144_PAD_SOURCE, + AR0144_STREAM_EDATA); + fmt->width = compose->width; + return 0; } @@ -1361,29 +1404,35 @@ static int ar0144_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, { struct ar0144 *sensor = to_ar0144(sd); const struct ar0144_format_info *info; - const struct v4l2_mbus_framefmt *fmt; struct v4l2_subdev_state *state; - u32 code; + u32 img_code; + u32 ed_code; if (pad != AR0144_PAD_SOURCE) return -EINVAL; state = v4l2_subdev_lock_and_get_active_state(sd); - fmt = v4l2_subdev_state_get_format(state, AR0144_PAD_SOURCE, - AR0144_STREAM_IMAGE); - code = fmt->code; + img_code = v4l2_subdev_state_get_format(state, AR0144_PAD_SOURCE, + AR0144_STREAM_IMAGE)->code; + ed_code = v4l2_subdev_state_get_format(state, AR0144_PAD_SOURCE, + AR0144_STREAM_EDATA)->code; v4l2_subdev_unlock_state(state); - info = ar0144_format_info(sensor, code, true); + info = ar0144_format_info(sensor, img_code, true); fd->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2; - fd->num_entries = 1; + fd->num_entries = 2; - fd->entry[0].pixelcode = code; + fd->entry[0].pixelcode = img_code; fd->entry[0].stream = AR0144_STREAM_IMAGE; fd->entry[0].bus.csi2.vc = 0; fd->entry[0].bus.csi2.dt = info->dt; + fd->entry[1].pixelcode = ed_code; + fd->entry[1].stream = AR0144_STREAM_EDATA; + fd->entry[1].bus.csi2.vc = 0; + fd->entry[1].bus.csi2.dt = MIPI_CSI2_DT_EMBEDDED_8B; + return 0; } @@ -1415,6 +1464,13 @@ static int ar0144_enable_streams(struct v4l2_subdev *sd, struct ar0144 *sensor = to_ar0144(sd); int ret; + /* + * The image stream controls sensor streaming, as embedded data isn't + * controllable independently. + */ + if (!(streams_mask & BIT(AR0144_STREAM_IMAGE))) + return 0; + ret = pm_runtime_resume_and_get(sensor->dev); if (ret < 0) return ret; @@ -1436,6 +1492,13 @@ static int ar0144_disable_streams(struct v4l2_subdev *sd, { struct ar0144 *sensor = to_ar0144(sd); + /* + * The image stream controls sensor streaming, as embedded data isn't + * controllable independently. + */ + if (!(streams_mask & BIT(AR0144_STREAM_IMAGE))) + return 0; + ar0144_stop_streaming(sensor); pm_runtime_mark_last_busy(sensor->dev); pm_runtime_put_autosuspend(sensor->dev); @@ -1454,6 +1517,13 @@ static int ar0144_entity_init_state(struct v4l2_subdev *sd, .source_stream = AR0144_STREAM_IMAGE, .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE | V4L2_SUBDEV_ROUTE_FL_IMMUTABLE, + }, { + .sink_pad = AR0144_PAD_EDATA, + .sink_stream = 0, + .source_pad = AR0144_PAD_SOURCE, + .source_stream = AR0144_STREAM_EDATA, + .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE | + V4L2_SUBDEV_ROUTE_FL_IMMUTABLE, }, }; struct v4l2_subdev_krouting routing = { @@ -1472,6 +1542,7 @@ static int ar0144_entity_init_state(struct v4l2_subdev *sd, if (ret) return ret; + /* Image pad. */ info = &ar0144_formats[0]; fmt = v4l2_subdev_state_get_format(state, AR0144_PAD_IMAGE); @@ -1496,6 +1567,14 @@ static int ar0144_entity_init_state(struct v4l2_subdev *sd, compose->width = AR0144_DEF_WIDTH; compose->height = AR0144_DEF_HEIGHT; + /* Embedded data pad. */ + fmt = v4l2_subdev_state_get_format(state, AR0144_PAD_EDATA); + fmt->width = AR0144_DEF_WIDTH; + fmt->height = AR0144_EMBEDDED_DATA_HEIGHT; + fmt->code = MEDIA_BUS_FMT_CCS_EMBEDDED; + fmt->field = V4L2_FIELD_NONE; + + /* Source pad, image stream. */ info = ar0144_format_info(sensor, 0, true); fmt = v4l2_subdev_state_get_format(state, AR0144_PAD_SOURCE, @@ -1509,6 +1588,14 @@ static int ar0144_entity_init_state(struct v4l2_subdev *sd, fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE; fmt->xfer_func = V4L2_XFER_FUNC_NONE; + /* Source pad, embedded data stream. */ + fmt = v4l2_subdev_state_get_format(state, AR0144_PAD_SOURCE, + AR0144_STREAM_EDATA); + fmt->width = AR0144_DEF_WIDTH; + fmt->height = AR0144_EMBEDDED_DATA_HEIGHT; + fmt->code = info->edata; + fmt->field = V4L2_FIELD_NONE; + return 0; } @@ -1557,6 +1644,8 @@ static int ar0144_init_subdev(struct ar0144 *sensor) sensor->pads[AR0144_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; sensor->pads[AR0144_PAD_IMAGE].flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_INTERNAL; + sensor->pads[AR0144_PAD_EDATA].flags = MEDIA_PAD_FL_SINK + | MEDIA_PAD_FL_INTERNAL; ret = media_entity_pads_init(&sd->entity, ARRAY_SIZE(sensor->pads), sensor->pads); -- Regards, Laurent Pinchart