diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index 4e095af0fdb6ef..b173caacf2882a 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -126,6 +126,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; @@ -448,7 +458,7 @@ static const struct imx219_mode supported_modes[] = { struct imx219 { struct v4l2_subdev sd; - struct media_pad pad; + struct media_pad pad[NUM_PADS]; struct v4l2_mbus_framefmt fmt; @@ -598,18 +608,26 @@ static void imx219_set_default_format(struct imx219 *imx219) 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; + 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); @@ -723,12 +741,22 @@ static int imx219_enum_mbus_code(struct v4l2_subdev *sd, { 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; + + mutex_lock(&imx219->mutex); + code->code = imx219_get_format_code(imx219, codes[code->index * 4]); + mutex_unlock(&imx219->mutex); + } else { + if (code->index > 0) + return -EINVAL; + + code->code = MEDIA_BUS_FMT_SENSOR_DATA; + } return 0; } @@ -740,19 +768,32 @@ static int imx219_enum_frame_size(struct v4l2_subdev *sd, 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; + + mutex_lock(&imx219->mutex); + code = imx219_get_format_code(imx219, fse->code); + mutex_unlock(&imx219->mutex); + if (fse->code != 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; } @@ -767,9 +808,9 @@ static void imx219_reset_colorspace(struct v4l2_mbus_framefmt *fmt) 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; @@ -777,21 +818,39 @@ static void imx219_update_pad_format(struct imx219 *imx219, 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; @@ -821,59 +880,78 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd, 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) { - u32 prev_hts = imx219->mode->width + imx219->hblank->val; - - 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); - /* - * Retain PPL setting from previous mode so that the - * line time does not change on a mode change. - * Limits have to be recomputed as the controls define - * the blanking only, so PPL values need to have the - * mode width subtracted. - */ - hblank = prev_hts - mode->width; - __v4l2_ctrl_modify_range(imx219->hblank, - IMX219_PPL_MIN - mode->width, - IMX219_PPL_MAX - mode->width, - 1, - IMX219_PPL_MIN - mode->width); - __v4l2_ctrl_s_ctrl(imx219->hblank, 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) { + u32 prev_hts = imx219->mode->width + imx219->hblank->val; + + 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); + /* + * Retain PPL setting from previous mode so that the + * line time does not change on a mode change. + * Limits have to be recomputed as the controls define + * the blanking only, so PPL values need to have the + * mode width subtracted. + */ + hblank = prev_hts - mode->width; + __v4l2_ctrl_modify_range(imx219->hblank, + IMX219_PPL_MIN - mode->width, + IMX219_PPL_MAX - mode->width, + 1, + IMX219_PPL_MIN - mode->width); + __v4l2_ctrl_s_ctrl(imx219->hblank, 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); @@ -1490,12 +1568,13 @@ static int imx219_probe(struct i2c_client *client) imx219->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; /* Initialize source pad */ - imx219->pad.flags = MEDIA_PAD_FL_SOURCE; + 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;