Re: [PATCH 2/4] media: i2c: st-vgxy61: Add support for embedded data

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Julien,

On 3/15/24 09:51, Julien Massot wrote:
> Add support for embedded data. This introduces two internal pads for pixel
> and embedded data streams. The sensor can send ISL data at the begginning

Typo 'beginning'.

> of each frame.
> 
> The ISL data contains information related to the current frame such as:
> ROI, cropping and orientation, gains, thermal sensors values,
> frame counter..
> 
> The Intelligent Status Line follows the CCS embedded data format definition
> regarding the tagged data but not for the registers address, therefore the
> format code is MEDIA_BUS_FMT_META_8 and not MEDIA_BUS_FMT_CCS_EMBEDDED.
> 
> Signed-off-by: Julien Massot <julien.massot@xxxxxxxxxxxxx>
> ---
>  drivers/media/i2c/st-vgxy61.c | 172 +++++++++++++++++++++++++++++++---
>  1 file changed, 160 insertions(+), 12 deletions(-)
> 
> diff --git a/drivers/media/i2c/st-vgxy61.c b/drivers/media/i2c/st-vgxy61.c
> index 733713f909cf..e8302456a8d9 100644
> --- a/drivers/media/i2c/st-vgxy61.c
> +++ b/drivers/media/i2c/st-vgxy61.c
> @@ -88,11 +88,16 @@
>  #define VGXY61_REG_PATGEN_SHORT_DATA_B			CCI_REG16_LE(0x0954)
>  #define VGXY61_REG_PATGEN_SHORT_DATA_GB			CCI_REG16_LE(0x0956)
>  #define VGXY61_REG_BYPASS_CTRL				CCI_REG8(0x0a60)
> +#define VGXY61_ISL_BYPASS				BIT(3)
> +#define VGXY61_ASIL_BYPASS				BIT(2)
>  
>  #define VGX661_WIDTH					1464
>  #define VGX661_HEIGHT					1104
>  #define VGX761_WIDTH					1944
>  #define VGX761_HEIGHT					1204
> +/* two status lines (ISL), of 256 bytes each */
> +#define VGXY61_META_WIDTH				256
> +#define VGXY61_META_HEIGHT				2
>  #define VGX661_DEFAULT_MODE				1
>  #define VGX761_DEFAULT_MODE				1
>  #define VGX661_SHORT_ROT_TERM				93
> @@ -112,6 +117,18 @@
>  #define VGXY61_FWPATCH_REVISION_MINOR			0
>  #define VGXY61_FWPATCH_REVISION_MICRO			5
>  
> +enum {
> +	VGXY61_PAD_SOURCE,
> +	VGXY61_PAD_PIXEL,
> +	VGXY61_PAD_META,
> +	VGXY61_NUM_PADS,
> +};
> +
> +enum {
> +	VGXY61_STREAM_PIXEL,
> +	VGXY61_STREAM_META,
> +};
> +
>  static const u8 patch_array[] = {
>  	0xbf, 0x00, 0x05, 0x20, 0x06, 0x01, 0xe0, 0xe0, 0x04, 0x80, 0xe6, 0x45,
>  	0xed, 0x6f, 0xfe, 0xff, 0x14, 0x80, 0x1f, 0x84, 0x10, 0x42, 0x05, 0x7c,
> @@ -382,7 +399,7 @@ struct vgxy61_dev {
>  	struct i2c_client *i2c_client;
>  	struct regmap *regmap;
>  	struct v4l2_subdev sd;
> -	struct media_pad pad;
> +	struct media_pad pads[VGXY61_NUM_PADS];
>  	struct regulator_bulk_data supplies[ARRAY_SIZE(vgxy61_supply_name)];
>  	struct gpio_desc *reset_gpio;
>  	struct clk *xclk;
> @@ -655,6 +672,13 @@ static int vgxy61_get_selection(struct v4l2_subdev *sd,
>  {
>  	struct vgxy61_dev *sensor = to_vgxy61_dev(sd);
>  
> +	/*
> +	 * The embedded data stream doesn't support selection rectangles,
> +	 * neither on the embedded data pad nor on the source pad.
> +	 */
> +	if (sel->pad == VGXY61_PAD_META)
> +		return -EINVAL;
> +
>  	switch (sel->target) {
>  	case V4L2_SEL_TGT_CROP:
>  		sel->r = sensor->current_mode->crop;
> @@ -676,6 +700,16 @@ static int vgxy61_enum_mbus_code(struct v4l2_subdev *sd,
>  				 struct v4l2_subdev_state *sd_state,
>  				 struct v4l2_subdev_mbus_code_enum *code)
>  {
> +	if (code->pad == VGXY61_PAD_META ||
> +	    (code->pad == VGXY61_PAD_SOURCE &&
> +	     code->stream == VGXY61_STREAM_META)) {
> +		if (code->index > 0)
> +			return -EINVAL;
> +
> +		code->code = MEDIA_BUS_FMT_META_8;
> +		return 0;
> +	}
> +
>  	if (code->index >= ARRAY_SIZE(vgxy61_supported_codes))
>  		return -EINVAL;
>  
> @@ -703,6 +737,19 @@ static int vgxy61_enum_frame_size(struct v4l2_subdev *sd,
>  {
>  	struct vgxy61_dev *sensor = to_vgxy61_dev(sd);
>  
> +	if (fse->pad == VGXY61_PAD_META ||
> +	    (fse->pad == VGXY61_PAD_SOURCE &&
> +	     fse->stream == VGXY61_STREAM_META)) {
> +		if (fse->index > 0)
> +			return -EINVAL;
> +
> +		fse->min_width = VGXY61_META_WIDTH;
> +		fse->max_width = VGXY61_META_WIDTH;
> +		fse->min_height = VGXY61_META_HEIGHT;
> +		fse->max_height = VGXY61_META_HEIGHT;
> +		return 0;
> +	}
> +
>  	if (fse->index >= sensor->sensor_modes_nb)
>  		return -EINVAL;
>  
> @@ -1159,24 +1206,54 @@ static int vgxy61_s_stream(struct v4l2_subdev *sd, int enable)
>  	return ret;
>  }
>  
> -static int vgxy61_set_fmt(struct v4l2_subdev *sd,
> -			  struct v4l2_subdev_state *sd_state,
> -			  struct v4l2_subdev_format *format)
> +static int __vgxy61_set_fmt(struct v4l2_subdev *sd,
> +			    struct v4l2_subdev_state *sd_state,
> +			    struct v4l2_mbus_framefmt *format,
> +			    enum v4l2_subdev_format_whence which,
> +			    unsigned int pad, unsigned int stream)
>  {
>  	struct vgxy61_dev *sensor = to_vgxy61_dev(sd);
> +	struct v4l2_mbus_framefmt *src_pix_fmt, *src_meta_fmt, *pix_fmt,
> +		*meta_fmt;
>  	const struct vgxy61_mode_info *new_mode;
>  	int ret;
>  
>  	if (sensor->streaming)
>  		return -EBUSY;
>  
> -	ret = vgxy61_try_fmt_internal(sd, &format->format, &new_mode);
> +	/*
> +	 * Allow setting format on internal pixel pad as well as the source
> +	 * pad's pixel stream (for compatibility).
> +	 */
> +	if ((pad == VGXY61_PAD_SOURCE && stream == VGXY61_STREAM_META) ||
> +	    pad == VGXY61_PAD_META) {
> +		*format = *v4l2_subdev_state_get_format(sd_state, pad, stream);
> +		return 0;
> +	}
> +
> +	pix_fmt = v4l2_subdev_state_get_format(sd_state, VGXY61_PAD_PIXEL, 0);
> +	meta_fmt = v4l2_subdev_state_get_format(sd_state, VGXY61_PAD_META, 0);
> +	src_pix_fmt = v4l2_subdev_state_get_format(sd_state, VGXY61_PAD_SOURCE,
> +						   VGXY61_STREAM_PIXEL);
> +	src_meta_fmt = v4l2_subdev_state_get_format(sd_state, VGXY61_PAD_SOURCE,
> +						    VGXY61_STREAM_META);
> +
> +	ret = vgxy61_try_fmt_internal(sd, format, &new_mode);
>  	if (ret)
>  		return ret;
>  
> -	*v4l2_subdev_state_get_format(sd_state, format->pad) = format->format;
> +	pix_fmt->width = format->width;
> +	pix_fmt->height = format->height;
> +	pix_fmt->code = format->code;
> +	pix_fmt->field = V4L2_FIELD_NONE;
> +
> +	*format = *src_pix_fmt = *pix_fmt;
> +	meta_fmt->code = MEDIA_BUS_FMT_META_8;
> +	meta_fmt->width = VGXY61_META_WIDTH;
> +	meta_fmt->height = VGXY61_META_HEIGHT;
> +	*src_meta_fmt = *meta_fmt;
>  
> -	if (format->which == V4L2_SUBDEV_FORMAT_TRY)
> +	if (which == V4L2_SUBDEV_FORMAT_TRY)
>  		return 0;
>  
>  	sensor->current_mode = new_mode;
> @@ -1202,16 +1279,78 @@ static int vgxy61_set_fmt(struct v4l2_subdev *sd,
>  	return ret;
>  }
>  
> +static int vgxy61_set_fmt(struct v4l2_subdev *sd,
> +			  struct v4l2_subdev_state *sd_state,
> +			  struct v4l2_subdev_format *fmt)
> +{
> +	return __vgxy61_set_fmt(sd, sd_state, &fmt->format, fmt->which,
> +				fmt->pad, fmt->stream);
> +}
> +
> +static int vgxy61_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad,
> +				 struct v4l2_mbus_frame_desc *desc)
> +{
> +	struct v4l2_subdev_state *sd_state;
> +	struct v4l2_mbus_framefmt *fmt;
> +
> +	desc->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2;
> +	desc->num_entries = 2;
> +
> +	sd_state = v4l2_subdev_lock_and_get_active_state(sd);
> +	fmt = v4l2_subdev_state_get_format(sd_state, VGXY61_PAD_SOURCE,
> +					   VGXY61_STREAM_PIXEL);
> +	v4l2_subdev_unlock_state(sd_state);
> +
> +	desc->entry[0].stream = VGXY61_STREAM_PIXEL;
> +	desc->entry[0].pixelcode = fmt->code;
> +	desc->entry[0].bus.csi2.dt = get_data_type_by_code(fmt->code);
> +
> +	desc->entry[1].stream = VGXY61_STREAM_META;
> +	desc->entry[1].pixelcode = MEDIA_BUS_FMT_META_8;
> +	desc->entry[1].bus.csi2.dt = MIPI_CSI2_DT_EMBEDDED_8B;
> +
> +	return 0;
> +}
> +
>  static int vgxy61_init_state(struct v4l2_subdev *sd,
>  			     struct v4l2_subdev_state *sd_state)
>  {
>  	struct vgxy61_dev *sensor = to_vgxy61_dev(sd);
>  	struct v4l2_subdev_format fmt = { 0 };
> +	struct v4l2_subdev_route routes[] = {
> +		{
> +			.sink_pad = VGXY61_PAD_PIXEL,
> +			.source_pad = VGXY61_PAD_SOURCE,
> +			.source_stream = VGXY61_STREAM_PIXEL,
> +			.flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE |
> +				 V4L2_SUBDEV_ROUTE_FL_IMMUTABLE,
> +		}, {
> +			.sink_pad = VGXY61_PAD_META,
> +			.source_pad = VGXY61_PAD_SOURCE,
> +			.source_stream = VGXY61_STREAM_META,
> +			.flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE,
> +		},
> +	};
> +	struct v4l2_subdev_krouting routing = {
> +		.routes = routes,
> +		.num_routes = ARRAY_SIZE(routes),
> +	};
> +	struct v4l2_subdev_state *active_state;
> +	int ret;
> +
> +	ret = v4l2_subdev_set_routing(sd, sd_state, &routing);
> +	if (ret)
> +		return ret;
> +
> +	active_state = v4l2_subdev_get_locked_active_state(sd);
>  
>  	vgxy61_fill_framefmt(sensor, sensor->current_mode, &fmt.format,
>  			     VGXY61_MEDIA_BUS_FMT_DEF);
>  
> -	return vgxy61_set_fmt(sd, sd_state, &fmt);
> +	return __vgxy61_set_fmt(sd, sd_state, &fmt.format,
> +				active_state == sd_state ?
> +				V4L2_SUBDEV_FORMAT_ACTIVE :
> +				V4L2_SUBDEV_FORMAT_TRY, VGXY61_PAD_PIXEL, 0);
>  }
>  
>  static int vgxy61_s_ctrl(struct v4l2_ctrl *ctrl)
> @@ -1364,6 +1503,7 @@ static const struct v4l2_subdev_pad_ops vgxy61_pad_ops = {
>  	.enum_mbus_code = vgxy61_enum_mbus_code,
>  	.get_fmt = v4l2_subdev_get_fmt,
>  	.set_fmt = vgxy61_set_fmt,
> +	.get_frame_desc = vgxy61_get_frame_desc,
>  	.get_selection = vgxy61_get_selection,
>  	.enum_frame_size = vgxy61_enum_frame_size,
>  };
> @@ -1478,7 +1618,8 @@ static int vgxy61_configure(struct vgxy61_dev *sensor)
>  	cci_write(sensor->regmap, VGXY61_REG_CLK_SYS_PLL_MULT, mult, &ret);
>  	cci_write(sensor->regmap, VGXY61_REG_OIF_CTRL, sensor->oif_ctrl, &ret);
>  	cci_write(sensor->regmap, VGXY61_REG_FRAME_CONTENT_CTRL, 0, &ret);
> -	cci_write(sensor->regmap, VGXY61_REG_BYPASS_CTRL, 4, &ret);
> +	cci_write(sensor->regmap, VGXY61_REG_BYPASS_CTRL, VGXY61_ASIL_BYPASS,
> +		  &ret);
>  	if (ret)
>  		return ret;
>  	vgxy61_update_gpios_strobe_polarity(sensor, sensor->gpios_polarity);
> @@ -1743,8 +1884,14 @@ static int vgxy61_probe(struct i2c_client *client)
>  	v4l2_i2c_subdev_init(&sensor->sd, client, &vgxy61_subdev_ops);
>  	sensor->sd.internal_ops = &vgxy61_internal_ops;
>  	sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
> -			    V4L2_SUBDEV_FL_HAS_EVENTS;
> -	sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
> +			    V4L2_SUBDEV_FL_HAS_EVENTS |
> +			    V4L2_SUBDEV_FL_STREAMS;
> +	sensor->pads[VGXY61_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
> +	sensor->pads[VGXY61_PAD_PIXEL].flags =
> +		MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_INTERNAL;
> +	sensor->pads[VGXY61_PAD_META].flags =
> +		MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_INTERNAL;
> +
>  	sensor->sd.entity.ops = &vgxy61_subdev_entity_ops;
>  	sensor->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
>  
> @@ -1778,7 +1925,8 @@ static int vgxy61_probe(struct i2c_client *client)
>  		goto error_power_off;
>  	}
>  
> -	ret = media_entity_pads_init(&sensor->sd.entity, 1, &sensor->pad);
> +	ret = media_entity_pads_init(&sensor->sd.entity,
> +				     ARRAY_SIZE(sensor->pads), sensor->pads);
>  	if (ret) {
>  		dev_err(&client->dev, "pads init failed %d\n", ret);
>  		goto error_handler_free;

-- 
Regards,

Benjamin




[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]

  Powered by Linux