diff mbox series

[v5,04/14] staging: media: starfive: Add a params sink pad and a scd source pad for ISP

Message ID 20240709083824.430473-5-changhuang.liang@starfivetech.com (mailing list archive)
State New
Headers show
Series Add ISP 3A for StarFive | expand

Commit Message

Changhuang Liang July 9, 2024, 8:38 a.m. UTC
StarFive ISP can use params sink pad to transmit ISP parameters and use
scd source pad to capture statistics collection data.

Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
---
 .../staging/media/starfive/camss/stf-isp.c    | 77 +++++++++++++++++--
 .../staging/media/starfive/camss/stf-isp.h    |  2 +
 2 files changed, 71 insertions(+), 8 deletions(-)

Comments

Jacopo Mondi July 10, 2024, 9:51 a.m. UTC | #1
Hello Changhuang

On Tue, Jul 09, 2024 at 01:38:14AM GMT, Changhuang Liang wrote:
> StarFive ISP can use params sink pad to transmit ISP parameters and use
> scd source pad to capture statistics collection data.
>
> Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
> ---
>  .../staging/media/starfive/camss/stf-isp.c    | 77 +++++++++++++++++--
>  .../staging/media/starfive/camss/stf-isp.h    |  2 +
>  2 files changed, 71 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/staging/media/starfive/camss/stf-isp.c b/drivers/staging/media/starfive/camss/stf-isp.c
> index 4e6e26736852..0ebffd09842a 100644
> --- a/drivers/staging/media/starfive/camss/stf-isp.c
> +++ b/drivers/staging/media/starfive/camss/stf-isp.c
> @@ -21,13 +21,23 @@ static const struct stf_isp_format isp_formats_sink[] = {
>  	{ MEDIA_BUS_FMT_SBGGR10_1X10, 10 },
>  };
>
> +static const struct stf_isp_format isp_formats_sink_params[] = {
> +	{ MEDIA_BUS_FMT_METADATA_FIXED },
> +};
> +
>  static const struct stf_isp_format isp_formats_source[] = {
>  	{ MEDIA_BUS_FMT_YUYV8_1_5X8, 8 },
>  };
>
> +static const struct stf_isp_format isp_formats_source_scd[] = {
> +	{ MEDIA_BUS_FMT_METADATA_FIXED },
> +};
> +
>  static const struct stf_isp_format_table isp_formats_st7110[] = {
>  	{ isp_formats_sink, ARRAY_SIZE(isp_formats_sink) },
> +	{ isp_formats_sink_params, ARRAY_SIZE(isp_formats_sink_params) },
>  	{ isp_formats_source, ARRAY_SIZE(isp_formats_source) },
> +	{ isp_formats_source_scd, ARRAY_SIZE(isp_formats_source_scd) },
>  };
>
>  static const struct stf_isp_format *
> @@ -93,13 +103,19 @@ static void isp_try_format(struct stf_isp_dev *isp_dev,
>
>  	formats = &isp_dev->formats[pad];
>
> -	fmt->width = clamp_t(u32, fmt->width, STFCAMSS_FRAME_MIN_WIDTH,
> -			     STFCAMSS_FRAME_MAX_WIDTH);
> -	fmt->height = clamp_t(u32, fmt->height, STFCAMSS_FRAME_MIN_HEIGHT,
> -			      STFCAMSS_FRAME_MAX_HEIGHT);
> -	fmt->height &= ~0x1;
> +	if (pad != STF_ISP_PAD_SRC_SCD && pad != STF_ISP_PAD_SINK_PARAMS) {
> +		fmt->width = clamp_t(u32, fmt->width, STFCAMSS_FRAME_MIN_WIDTH,
> +				     STFCAMSS_FRAME_MAX_WIDTH);
> +		fmt->height = clamp_t(u32, fmt->height, STFCAMSS_FRAME_MIN_HEIGHT,
> +				      STFCAMSS_FRAME_MAX_HEIGHT);
> +		fmt->height &= ~0x1;
> +		fmt->colorspace = V4L2_COLORSPACE_SRGB;
> +	} else {
> +		fmt->width = 1;
> +		fmt->height = 1;
> +	}
> +
>  	fmt->field = V4L2_FIELD_NONE;
> -	fmt->colorspace = V4L2_COLORSPACE_SRGB;
>  	fmt->flags = 0;
>
>  	if (!stf_g_fmt_by_mcode(formats, fmt->code))
> @@ -119,7 +135,7 @@ static int isp_enum_mbus_code(struct v4l2_subdev *sd,
>
>  		formats = &isp_dev->formats[code->pad];
>  		code->code = formats->fmts[code->index].code;
> -	} else {
> +	} else if (code->pad == STF_ISP_PAD_SRC) {
>  		struct v4l2_mbus_framefmt *sink_fmt;
>
>  		if (code->index >= ARRAY_SIZE(isp_formats_source))
> @@ -131,6 +147,10 @@ static int isp_enum_mbus_code(struct v4l2_subdev *sd,
>  		code->code = sink_fmt->code;
>  		if (!code->code)
>  			return -EINVAL;
> +	} else {
> +		if (code->index > 0)
> +			return -EINVAL;
> +		code->code = MEDIA_BUS_FMT_METADATA_FIXED;
>  	}
>  	code->flags = 0;
>
> @@ -151,6 +171,9 @@ static int isp_set_format(struct v4l2_subdev *sd,
>  	isp_try_format(isp_dev, state, fmt->pad, &fmt->format);
>  	*format = fmt->format;
>
> +	if (fmt->pad == STF_ISP_PAD_SRC_SCD || fmt->pad == STF_ISP_PAD_SINK_PARAMS)

nit: can easily be made < 80 cols (again, not mandatory in Linux but
enforced in v4l according to
Documentation/driver-api/media/maintainer-entry-profile.rst)

> +		return 0;
> +
>  	isp_dev->current_fmt = stf_g_fmt_by_mcode(&isp_dev->formats[fmt->pad],
>  						  fmt->format.code);
>
> @@ -202,6 +225,9 @@ static int isp_get_selection(struct v4l2_subdev *sd,
>  	struct v4l2_subdev_format fmt = { 0 };
>  	struct v4l2_rect *rect;
>
> +	if (sel->pad == STF_ISP_PAD_SRC_SCD || sel->pad == STF_ISP_PAD_SINK_PARAMS)
> +		return -EINVAL;
> +
>  	switch (sel->target) {
>  	case V4L2_SEL_TGT_CROP_BOUNDS:
>  		if (sel->pad == STF_ISP_PAD_SINK) {
> @@ -239,6 +265,9 @@ static int isp_set_selection(struct v4l2_subdev *sd,
>  	struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd);
>  	struct v4l2_rect *rect;
>
> +	if (sel->pad == STF_ISP_PAD_SRC_SCD || sel->pad == STF_ISP_PAD_SINK_PARAMS)
> +		return -EINVAL;
> +
>  	if (sel->target != V4L2_SEL_TGT_CROP)
>  		return -EINVAL;
>
> @@ -296,8 +325,38 @@ static int isp_init_formats(struct v4l2_subdev *sd,
>  			.height = 1080
>  		}
>  	};
> +	struct v4l2_subdev_format format_params = {
> +		.pad = STF_ISP_PAD_SINK_PARAMS,
> +		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
> +		.format = {
> +			.code = MEDIA_BUS_FMT_METADATA_FIXED,
> +			.width = 1,
> +			.height = 1
> +		}
> +	};
> +	struct v4l2_subdev_format format_scd = {
> +		.pad = STF_ISP_PAD_SRC_SCD,
> +		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
> +		.format = {
> +			.code = MEDIA_BUS_FMT_METADATA_FIXED,
> +			.width = 1,
> +			.height = 1
> +		}
> +	};
> +	int ret;
> +
> +	/* Init for STF_ISP_PAD_SINK and STF_ISP_PAD_SRC pad */

Does this initialize the format on STF_ISP_PAD_SRC for real ?

Anyway, was there already so
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>

Thanks
  j

> +	ret = isp_set_format(sd, sd_state, &format);
> +	if (ret < 0)
> +		return ret;
> +
> +	/* Init for STF_ISP_PAD_SINK_PARAMS pad */
> +	ret = isp_set_format(sd, sd_state, &format_params);
> +	if (ret < 0)
> +		return ret;
>
> -	return isp_set_format(sd, sd_state, &format);
> +	/* Init for STF_ISP_PAD_SRC_SCD pad */
> +	return isp_set_format(sd, sd_state, &format_scd);
>  }
>
>  static const struct v4l2_subdev_video_ops isp_video_ops = {
> @@ -338,7 +397,9 @@ int stf_isp_register(struct stf_isp_dev *isp_dev, struct v4l2_device *v4l2_dev)
>  	v4l2_set_subdevdata(sd, isp_dev);
>
>  	pads[STF_ISP_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
> +	pads[STF_ISP_PAD_SINK_PARAMS].flags = MEDIA_PAD_FL_SINK;
>  	pads[STF_ISP_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE;
> +	pads[STF_ISP_PAD_SRC_SCD].flags = MEDIA_PAD_FL_SOURCE;
>
>  	sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_ISP;
>  	sd->entity.ops = &isp_media_ops;
> diff --git a/drivers/staging/media/starfive/camss/stf-isp.h b/drivers/staging/media/starfive/camss/stf-isp.h
> index 955cbb048363..bc7e7b0736fa 100644
> --- a/drivers/staging/media/starfive/camss/stf-isp.h
> +++ b/drivers/staging/media/starfive/camss/stf-isp.h
> @@ -392,7 +392,9 @@
>  /* pad id for media framework */
>  enum stf_isp_pad_id {
>  	STF_ISP_PAD_SINK = 0,
> +	STF_ISP_PAD_SINK_PARAMS,
>  	STF_ISP_PAD_SRC,
> +	STF_ISP_PAD_SRC_SCD,
>  	STF_ISP_PAD_MAX
>  };
>
> --
> 2.25.1
>
>
Changhuang Liang July 10, 2024, 1:04 p.m. UTC | #2
Hi, Jacopo

Thanks for your comments.

[...]

> > @@ -151,6 +171,9 @@ static int isp_set_format(struct v4l2_subdev *sd,
> >  	isp_try_format(isp_dev, state, fmt->pad, &fmt->format);
> >  	*format = fmt->format;
> >
> > +	if (fmt->pad == STF_ISP_PAD_SRC_SCD || fmt->pad ==
> > +STF_ISP_PAD_SINK_PARAMS)
> 
> nit: can easily be made < 80 cols (again, not mandatory in Linux but enforced
> in v4l according to
> Documentation/driver-api/media/maintainer-entry-profile.rst)
> 

I will add --max-line-length=80 to check patchs again.

> > +		return 0;
> > +
> >  	isp_dev->current_fmt =
> stf_g_fmt_by_mcode(&isp_dev->formats[fmt->pad],
> >  						  fmt->format.code);
> >
> > @@ -202,6 +225,9 @@ static int isp_get_selection(struct v4l2_subdev *sd,
> >  	struct v4l2_subdev_format fmt = { 0 };
> >  	struct v4l2_rect *rect;
> >
> > +	if (sel->pad == STF_ISP_PAD_SRC_SCD || sel->pad ==
> STF_ISP_PAD_SINK_PARAMS)
> > +		return -EINVAL;
> > +
> >  	switch (sel->target) {
> >  	case V4L2_SEL_TGT_CROP_BOUNDS:
> >  		if (sel->pad == STF_ISP_PAD_SINK) { @@ -239,6 +265,9 @@ static
> int
> > isp_set_selection(struct v4l2_subdev *sd,
> >  	struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd);
> >  	struct v4l2_rect *rect;
> >
> > +	if (sel->pad == STF_ISP_PAD_SRC_SCD || sel->pad ==
> STF_ISP_PAD_SINK_PARAMS)
> > +		return -EINVAL;
> > +
> >  	if (sel->target != V4L2_SEL_TGT_CROP)
> >  		return -EINVAL;
> >
> > @@ -296,8 +325,38 @@ static int isp_init_formats(struct v4l2_subdev *sd,
> >  			.height = 1080
> >  		}
> >  	};
> > +	struct v4l2_subdev_format format_params = {
> > +		.pad = STF_ISP_PAD_SINK_PARAMS,
> > +		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
> > +		.format = {
> > +			.code = MEDIA_BUS_FMT_METADATA_FIXED,
> > +			.width = 1,
> > +			.height = 1
> > +		}
> > +	};
> > +	struct v4l2_subdev_format format_scd = {
> > +		.pad = STF_ISP_PAD_SRC_SCD,
> > +		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
> > +		.format = {
> > +			.code = MEDIA_BUS_FMT_METADATA_FIXED,
> > +			.width = 1,
> > +			.height = 1
> > +		}
> > +	};
> > +	int ret;
> > +
> > +	/* Init for STF_ISP_PAD_SINK and STF_ISP_PAD_SRC pad */
> 
> Does this initialize the format on STF_ISP_PAD_SRC for real ?
> 

Yes, it will initialize the PAD_SINK and PAND_SRC formats by isp_set_selection.

> Anyway, was there already so
> Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
> 
> Thanks
>   j
>
Laurent Pinchart July 22, 2024, 2:51 p.m. UTC | #3
Hello Changhuang,

Thank you for the patch.

On Tue, Jul 09, 2024 at 01:38:14AM -0700, Changhuang Liang wrote:
> StarFive ISP can use params sink pad to transmit ISP parameters and use
> scd source pad to capture statistics collection data.
> 
> Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
> ---
>  .../staging/media/starfive/camss/stf-isp.c    | 77 +++++++++++++++++--
>  .../staging/media/starfive/camss/stf-isp.h    |  2 +
>  2 files changed, 71 insertions(+), 8 deletions(-)
> 
> diff --git a/drivers/staging/media/starfive/camss/stf-isp.c b/drivers/staging/media/starfive/camss/stf-isp.c
> index 4e6e26736852..0ebffd09842a 100644
> --- a/drivers/staging/media/starfive/camss/stf-isp.c
> +++ b/drivers/staging/media/starfive/camss/stf-isp.c
> @@ -21,13 +21,23 @@ static const struct stf_isp_format isp_formats_sink[] = {
>  	{ MEDIA_BUS_FMT_SBGGR10_1X10, 10 },
>  };
>  
> +static const struct stf_isp_format isp_formats_sink_params[] = {
> +	{ MEDIA_BUS_FMT_METADATA_FIXED },
> +};
> +
>  static const struct stf_isp_format isp_formats_source[] = {
>  	{ MEDIA_BUS_FMT_YUYV8_1_5X8, 8 },
>  };
>  
> +static const struct stf_isp_format isp_formats_source_scd[] = {
> +	{ MEDIA_BUS_FMT_METADATA_FIXED },
> +};
> +
>  static const struct stf_isp_format_table isp_formats_st7110[] = {
>  	{ isp_formats_sink, ARRAY_SIZE(isp_formats_sink) },
> +	{ isp_formats_sink_params, ARRAY_SIZE(isp_formats_sink_params) },
>  	{ isp_formats_source, ARRAY_SIZE(isp_formats_source) },
> +	{ isp_formats_source_scd, ARRAY_SIZE(isp_formats_source_scd) },
>  };
>  
>  static const struct stf_isp_format *
> @@ -93,13 +103,19 @@ static void isp_try_format(struct stf_isp_dev *isp_dev,
>  
>  	formats = &isp_dev->formats[pad];
>  
> -	fmt->width = clamp_t(u32, fmt->width, STFCAMSS_FRAME_MIN_WIDTH,
> -			     STFCAMSS_FRAME_MAX_WIDTH);
> -	fmt->height = clamp_t(u32, fmt->height, STFCAMSS_FRAME_MIN_HEIGHT,
> -			      STFCAMSS_FRAME_MAX_HEIGHT);
> -	fmt->height &= ~0x1;
> +	if (pad != STF_ISP_PAD_SRC_SCD && pad != STF_ISP_PAD_SINK_PARAMS) {
> +		fmt->width = clamp_t(u32, fmt->width, STFCAMSS_FRAME_MIN_WIDTH,
> +				     STFCAMSS_FRAME_MAX_WIDTH);
> +		fmt->height = clamp_t(u32, fmt->height, STFCAMSS_FRAME_MIN_HEIGHT,
> +				      STFCAMSS_FRAME_MAX_HEIGHT);
> +		fmt->height &= ~0x1;
> +		fmt->colorspace = V4L2_COLORSPACE_SRGB;
> +	} else {
> +		fmt->width = 1;
> +		fmt->height = 1;

You should set fmt->colorspace here too. I think you can set it to 0.

> +	}
> +
>  	fmt->field = V4L2_FIELD_NONE;
> -	fmt->colorspace = V4L2_COLORSPACE_SRGB;
>  	fmt->flags = 0;
>  
>  	if (!stf_g_fmt_by_mcode(formats, fmt->code))
> @@ -119,7 +135,7 @@ static int isp_enum_mbus_code(struct v4l2_subdev *sd,
>  
>  		formats = &isp_dev->formats[code->pad];
>  		code->code = formats->fmts[code->index].code;
> -	} else {
> +	} else if (code->pad == STF_ISP_PAD_SRC) {
>  		struct v4l2_mbus_framefmt *sink_fmt;
>  
>  		if (code->index >= ARRAY_SIZE(isp_formats_source))
> @@ -131,6 +147,10 @@ static int isp_enum_mbus_code(struct v4l2_subdev *sd,
>  		code->code = sink_fmt->code;
>  		if (!code->code)
>  			return -EINVAL;
> +	} else {
> +		if (code->index > 0)
> +			return -EINVAL;
> +		code->code = MEDIA_BUS_FMT_METADATA_FIXED;
>  	}
>  	code->flags = 0;
>  
> @@ -151,6 +171,9 @@ static int isp_set_format(struct v4l2_subdev *sd,
>  	isp_try_format(isp_dev, state, fmt->pad, &fmt->format);
>  	*format = fmt->format;
>  
> +	if (fmt->pad == STF_ISP_PAD_SRC_SCD || fmt->pad == STF_ISP_PAD_SINK_PARAMS)
> +		return 0;
> +
>  	isp_dev->current_fmt = stf_g_fmt_by_mcode(&isp_dev->formats[fmt->pad],
>  						  fmt->format.code);
>  
> @@ -202,6 +225,9 @@ static int isp_get_selection(struct v4l2_subdev *sd,
>  	struct v4l2_subdev_format fmt = { 0 };
>  	struct v4l2_rect *rect;
>  
> +	if (sel->pad == STF_ISP_PAD_SRC_SCD || sel->pad == STF_ISP_PAD_SINK_PARAMS)
> +		return -EINVAL;
> +
>  	switch (sel->target) {
>  	case V4L2_SEL_TGT_CROP_BOUNDS:
>  		if (sel->pad == STF_ISP_PAD_SINK) {
> @@ -239,6 +265,9 @@ static int isp_set_selection(struct v4l2_subdev *sd,
>  	struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd);
>  	struct v4l2_rect *rect;
>  
> +	if (sel->pad == STF_ISP_PAD_SRC_SCD || sel->pad == STF_ISP_PAD_SINK_PARAMS)
> +		return -EINVAL;
> +
>  	if (sel->target != V4L2_SEL_TGT_CROP)
>  		return -EINVAL;
>  
> @@ -296,8 +325,38 @@ static int isp_init_formats(struct v4l2_subdev *sd,

While at it, you could rename the function to isp_init_state().

>  			.height = 1080
>  		}
>  	};
> +	struct v4l2_subdev_format format_params = {
> +		.pad = STF_ISP_PAD_SINK_PARAMS,
> +		.which = V4L2_SUBDEV_FORMAT_ACTIVE,

That's not quite right, as the .init_state() handler is used to
initialized both the TRY and ACTIVE states. As the "which" field is
currently ignored through the driver the code should behave correctly
(as far as I can tell), but you may want at some point to initialize the
formats and selection rectangles directly in this function instead of
calling isp_set_format().

> +		.format = {
> +			.code = MEDIA_BUS_FMT_METADATA_FIXED,
> +			.width = 1,
> +			.height = 1

According to
https://docs.kernel.org/userspace-api/media/v4l/subdev-formats.html#metadata-formats,
width and height should be set to 0 for MEDIA_BUS_FMT_METADATA_FIXED.

> +		}
> +	};
> +	struct v4l2_subdev_format format_scd = {
> +		.pad = STF_ISP_PAD_SRC_SCD,
> +		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
> +		.format = {
> +			.code = MEDIA_BUS_FMT_METADATA_FIXED,
> +			.width = 1,
> +			.height = 1
> +		}
> +	};
> +	int ret;
> +
> +	/* Init for STF_ISP_PAD_SINK and STF_ISP_PAD_SRC pad */
> +	ret = isp_set_format(sd, sd_state, &format);
> +	if (ret < 0)
> +		return ret;
> +
> +	/* Init for STF_ISP_PAD_SINK_PARAMS pad */
> +	ret = isp_set_format(sd, sd_state, &format_params);
> +	if (ret < 0)
> +		return ret;
>  
> -	return isp_set_format(sd, sd_state, &format);
> +	/* Init for STF_ISP_PAD_SRC_SCD pad */
> +	return isp_set_format(sd, sd_state, &format_scd);
>  }
>  
>  static const struct v4l2_subdev_video_ops isp_video_ops = {
> @@ -338,7 +397,9 @@ int stf_isp_register(struct stf_isp_dev *isp_dev, struct v4l2_device *v4l2_dev)
>  	v4l2_set_subdevdata(sd, isp_dev);
>  
>  	pads[STF_ISP_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
> +	pads[STF_ISP_PAD_SINK_PARAMS].flags = MEDIA_PAD_FL_SINK;
>  	pads[STF_ISP_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE;
> +	pads[STF_ISP_PAD_SRC_SCD].flags = MEDIA_PAD_FL_SOURCE;
>  
>  	sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_ISP;
>  	sd->entity.ops = &isp_media_ops;
> diff --git a/drivers/staging/media/starfive/camss/stf-isp.h b/drivers/staging/media/starfive/camss/stf-isp.h
> index 955cbb048363..bc7e7b0736fa 100644
> --- a/drivers/staging/media/starfive/camss/stf-isp.h
> +++ b/drivers/staging/media/starfive/camss/stf-isp.h
> @@ -392,7 +392,9 @@
>  /* pad id for media framework */
>  enum stf_isp_pad_id {
>  	STF_ISP_PAD_SINK = 0,
> +	STF_ISP_PAD_SINK_PARAMS,
>  	STF_ISP_PAD_SRC,
> +	STF_ISP_PAD_SRC_SCD,
>  	STF_ISP_PAD_MAX
>  };
>
diff mbox series

Patch

diff --git a/drivers/staging/media/starfive/camss/stf-isp.c b/drivers/staging/media/starfive/camss/stf-isp.c
index 4e6e26736852..0ebffd09842a 100644
--- a/drivers/staging/media/starfive/camss/stf-isp.c
+++ b/drivers/staging/media/starfive/camss/stf-isp.c
@@ -21,13 +21,23 @@  static const struct stf_isp_format isp_formats_sink[] = {
 	{ MEDIA_BUS_FMT_SBGGR10_1X10, 10 },
 };
 
+static const struct stf_isp_format isp_formats_sink_params[] = {
+	{ MEDIA_BUS_FMT_METADATA_FIXED },
+};
+
 static const struct stf_isp_format isp_formats_source[] = {
 	{ MEDIA_BUS_FMT_YUYV8_1_5X8, 8 },
 };
 
+static const struct stf_isp_format isp_formats_source_scd[] = {
+	{ MEDIA_BUS_FMT_METADATA_FIXED },
+};
+
 static const struct stf_isp_format_table isp_formats_st7110[] = {
 	{ isp_formats_sink, ARRAY_SIZE(isp_formats_sink) },
+	{ isp_formats_sink_params, ARRAY_SIZE(isp_formats_sink_params) },
 	{ isp_formats_source, ARRAY_SIZE(isp_formats_source) },
+	{ isp_formats_source_scd, ARRAY_SIZE(isp_formats_source_scd) },
 };
 
 static const struct stf_isp_format *
@@ -93,13 +103,19 @@  static void isp_try_format(struct stf_isp_dev *isp_dev,
 
 	formats = &isp_dev->formats[pad];
 
-	fmt->width = clamp_t(u32, fmt->width, STFCAMSS_FRAME_MIN_WIDTH,
-			     STFCAMSS_FRAME_MAX_WIDTH);
-	fmt->height = clamp_t(u32, fmt->height, STFCAMSS_FRAME_MIN_HEIGHT,
-			      STFCAMSS_FRAME_MAX_HEIGHT);
-	fmt->height &= ~0x1;
+	if (pad != STF_ISP_PAD_SRC_SCD && pad != STF_ISP_PAD_SINK_PARAMS) {
+		fmt->width = clamp_t(u32, fmt->width, STFCAMSS_FRAME_MIN_WIDTH,
+				     STFCAMSS_FRAME_MAX_WIDTH);
+		fmt->height = clamp_t(u32, fmt->height, STFCAMSS_FRAME_MIN_HEIGHT,
+				      STFCAMSS_FRAME_MAX_HEIGHT);
+		fmt->height &= ~0x1;
+		fmt->colorspace = V4L2_COLORSPACE_SRGB;
+	} else {
+		fmt->width = 1;
+		fmt->height = 1;
+	}
+
 	fmt->field = V4L2_FIELD_NONE;
-	fmt->colorspace = V4L2_COLORSPACE_SRGB;
 	fmt->flags = 0;
 
 	if (!stf_g_fmt_by_mcode(formats, fmt->code))
@@ -119,7 +135,7 @@  static int isp_enum_mbus_code(struct v4l2_subdev *sd,
 
 		formats = &isp_dev->formats[code->pad];
 		code->code = formats->fmts[code->index].code;
-	} else {
+	} else if (code->pad == STF_ISP_PAD_SRC) {
 		struct v4l2_mbus_framefmt *sink_fmt;
 
 		if (code->index >= ARRAY_SIZE(isp_formats_source))
@@ -131,6 +147,10 @@  static int isp_enum_mbus_code(struct v4l2_subdev *sd,
 		code->code = sink_fmt->code;
 		if (!code->code)
 			return -EINVAL;
+	} else {
+		if (code->index > 0)
+			return -EINVAL;
+		code->code = MEDIA_BUS_FMT_METADATA_FIXED;
 	}
 	code->flags = 0;
 
@@ -151,6 +171,9 @@  static int isp_set_format(struct v4l2_subdev *sd,
 	isp_try_format(isp_dev, state, fmt->pad, &fmt->format);
 	*format = fmt->format;
 
+	if (fmt->pad == STF_ISP_PAD_SRC_SCD || fmt->pad == STF_ISP_PAD_SINK_PARAMS)
+		return 0;
+
 	isp_dev->current_fmt = stf_g_fmt_by_mcode(&isp_dev->formats[fmt->pad],
 						  fmt->format.code);
 
@@ -202,6 +225,9 @@  static int isp_get_selection(struct v4l2_subdev *sd,
 	struct v4l2_subdev_format fmt = { 0 };
 	struct v4l2_rect *rect;
 
+	if (sel->pad == STF_ISP_PAD_SRC_SCD || sel->pad == STF_ISP_PAD_SINK_PARAMS)
+		return -EINVAL;
+
 	switch (sel->target) {
 	case V4L2_SEL_TGT_CROP_BOUNDS:
 		if (sel->pad == STF_ISP_PAD_SINK) {
@@ -239,6 +265,9 @@  static int isp_set_selection(struct v4l2_subdev *sd,
 	struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd);
 	struct v4l2_rect *rect;
 
+	if (sel->pad == STF_ISP_PAD_SRC_SCD || sel->pad == STF_ISP_PAD_SINK_PARAMS)
+		return -EINVAL;
+
 	if (sel->target != V4L2_SEL_TGT_CROP)
 		return -EINVAL;
 
@@ -296,8 +325,38 @@  static int isp_init_formats(struct v4l2_subdev *sd,
 			.height = 1080
 		}
 	};
+	struct v4l2_subdev_format format_params = {
+		.pad = STF_ISP_PAD_SINK_PARAMS,
+		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
+		.format = {
+			.code = MEDIA_BUS_FMT_METADATA_FIXED,
+			.width = 1,
+			.height = 1
+		}
+	};
+	struct v4l2_subdev_format format_scd = {
+		.pad = STF_ISP_PAD_SRC_SCD,
+		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
+		.format = {
+			.code = MEDIA_BUS_FMT_METADATA_FIXED,
+			.width = 1,
+			.height = 1
+		}
+	};
+	int ret;
+
+	/* Init for STF_ISP_PAD_SINK and STF_ISP_PAD_SRC pad */
+	ret = isp_set_format(sd, sd_state, &format);
+	if (ret < 0)
+		return ret;
+
+	/* Init for STF_ISP_PAD_SINK_PARAMS pad */
+	ret = isp_set_format(sd, sd_state, &format_params);
+	if (ret < 0)
+		return ret;
 
-	return isp_set_format(sd, sd_state, &format);
+	/* Init for STF_ISP_PAD_SRC_SCD pad */
+	return isp_set_format(sd, sd_state, &format_scd);
 }
 
 static const struct v4l2_subdev_video_ops isp_video_ops = {
@@ -338,7 +397,9 @@  int stf_isp_register(struct stf_isp_dev *isp_dev, struct v4l2_device *v4l2_dev)
 	v4l2_set_subdevdata(sd, isp_dev);
 
 	pads[STF_ISP_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+	pads[STF_ISP_PAD_SINK_PARAMS].flags = MEDIA_PAD_FL_SINK;
 	pads[STF_ISP_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE;
+	pads[STF_ISP_PAD_SRC_SCD].flags = MEDIA_PAD_FL_SOURCE;
 
 	sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_ISP;
 	sd->entity.ops = &isp_media_ops;
diff --git a/drivers/staging/media/starfive/camss/stf-isp.h b/drivers/staging/media/starfive/camss/stf-isp.h
index 955cbb048363..bc7e7b0736fa 100644
--- a/drivers/staging/media/starfive/camss/stf-isp.h
+++ b/drivers/staging/media/starfive/camss/stf-isp.h
@@ -392,7 +392,9 @@ 
 /* pad id for media framework */
 enum stf_isp_pad_id {
 	STF_ISP_PAD_SINK = 0,
+	STF_ISP_PAD_SINK_PARAMS,
 	STF_ISP_PAD_SRC,
+	STF_ISP_PAD_SRC_SCD,
 	STF_ISP_PAD_MAX
 };