Re: [PATCH v5 09/12] media: microchip-isc: add SAMA7G5 hue and saturation controls
From: Eugen Hristev
Date: Fri May 29 2026 - 12:07:15 EST
On 5/27/26 14:07, Balakrishnan Sambath wrote:
> The CBHS (Contrast/Brightness/Hue/Saturation) block on SAMA7G5
> operates in YCbCr space; expose hue and saturation as V4L2 controls
> for the YUV/RGB output paths only. The SAMA5D2 has only the CBC
> block (no hue/saturation), so the controls are gated on a new
> has_cbhs flag.
>
> Saturation uses the Q4 fixed-point range 0..127 with default 16
> (1.0x) directly matching the CBHS_SAT register field. The control
> state is initialised to neutral at probe so the first config_cbc()
> write after streaming starts does not produce a grayscale image.
>
> Co-developed-by: Balamanikandan Gunasundar <balamanikandan.gunasundar@xxxxxxxxxxxxx>
> Signed-off-by: Balamanikandan Gunasundar <balamanikandan.gunasundar@xxxxxxxxxxxxx>
> Signed-off-by: Balakrishnan Sambath <balakrishnan.s@xxxxxxxxxxxxx>
> ---
> .../media/platform/microchip/microchip-isc-base.c | 75 ++++++++++++++++++++++
> .../media/platform/microchip/microchip-isc-regs.h | 11 ++--
> drivers/media/platform/microchip/microchip-isc.h | 3 +
> .../platform/microchip/microchip-sama7g5-isc.c | 6 +-
> 4 files changed, 88 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/media/platform/microchip/microchip-isc-base.c b/drivers/media/platform/microchip/microchip-isc-base.c
> index 04187127070d..cb338133d03f 100644
> --- a/drivers/media/platform/microchip/microchip-isc-base.c
> +++ b/drivers/media/platform/microchip/microchip-isc-base.c
> @@ -859,6 +859,56 @@ static int isc_try_configure_pipeline(struct isc_device *isc)
> return 0;
> }
>
> +static bool isc_format_has_chroma(u32 fourcc)
> +{
> + switch (fourcc) {
> + case V4L2_PIX_FMT_YUV420:
> + case V4L2_PIX_FMT_YUV422P:
> + case V4L2_PIX_FMT_YUYV:
> + case V4L2_PIX_FMT_UYVY:
> + case V4L2_PIX_FMT_VYUY:
> + return true;
RGB565 doesn't have chroma for example ?
> + default:
> + return false;
> + }
> +}
> +
> +/*
> + * isc_update_cbc_ctrl_activity() - Activate/deactivate CBC controls
What is the purpose of 'activity' in the name ? is a bit misleading for me.
> + *
> + * Called from isc_set_fmt(), isc_link_validate(), and isc_ctrl_init().
> + * At isc_ctrl_init() time isc->config.bits_pipeline is zero (no format
> + * has been negotiated yet), so all CBC controls are initially marked
> + * inactive. They become active once a format that includes CBHS in the
> + * pipeline is configured via VIDIOC_S_FMT or link validation.
> + */
> +static void isc_update_cbc_ctrl_activity(struct isc_device *isc)
> +{
> + struct v4l2_ctrl_handler *hdl = &isc->ctrls.handler;
> + struct v4l2_ctrl *brightness;
> + struct v4l2_ctrl *contrast;
> + struct v4l2_ctrl *hue;
> + struct v4l2_ctrl *saturation;
> + bool cbc_active = isc->config.bits_pipeline & CBHS_ENABLE;
> + bool chroma_active = cbc_active && isc_format_has_chroma(isc->config.fourcc);
> +
> + brightness = v4l2_ctrl_find(hdl, V4L2_CID_BRIGHTNESS);
> + if (brightness)
> + v4l2_ctrl_activate(brightness, cbc_active);
> +
> + contrast = v4l2_ctrl_find(hdl, V4L2_CID_CONTRAST);
> + if (contrast)
> + v4l2_ctrl_activate(contrast, cbc_active);
> +
> + hue = v4l2_ctrl_find(hdl, V4L2_CID_HUE);
> + if (hue)
> + v4l2_ctrl_activate(hue, chroma_active);
> +
> + saturation = v4l2_ctrl_find(hdl, V4L2_CID_SATURATION);
> + if (saturation)
> + v4l2_ctrl_activate(saturation, chroma_active);
Usage of v4l2_ctrl_find is a bit odd, don't you have the ctrls already
in the isc struct ? e.g. isc->ctrls.brightness, etc.
> +}
> +
> static int isc_try_fmt(struct isc_device *isc, struct v4l2_format *f)
> {
> struct v4l2_pix_format *pixfmt = &f->fmt.pix;
> @@ -902,6 +952,7 @@ static int isc_set_fmt(struct isc_device *isc, struct v4l2_format *f)
> /* make the try configuration active */
> isc->config = isc->try_config;
> isc->fmt = isc->try_fmt;
> + isc_update_cbc_ctrl_activity(isc);
>
> dev_dbg(isc->dev, "ISC set_fmt to %.4s @%dx%d\n",
> (char *)&f->fmt.pix.pixelformat,
> @@ -989,6 +1040,7 @@ static int isc_link_validate(struct media_link *link)
> return ret;
>
> isc->config = isc->try_config;
> + isc_update_cbc_ctrl_activity(isc);
>
> dev_dbg(isc->dev, "New ISC configuration in place\n");
>
> @@ -1457,6 +1509,14 @@ static int isc_s_ctrl(struct v4l2_ctrl *ctrl)
> case V4L2_CID_CONTRAST:
> ctrls->contrast = ctrl->val & ISC_CBC_CONTRAST_MASK;
> break;
> + case V4L2_CID_HUE:
> + if (isc->has_cbhs)
> + ctrls->hue = ctrl->val & ISC_CBHS_HUE_MASK;
> + break;
> + case V4L2_CID_SATURATION:
> + if (isc->has_cbhs)
> + ctrls->saturation = ctrl->val & ISC_CBHS_SAT_MASK;
> + break;
> case V4L2_CID_GAMMA:
> ctrls->gamma_index = ctrl->val;
> break;
> @@ -1464,6 +1524,7 @@ static int isc_s_ctrl(struct v4l2_ctrl *ctrl)
> return -EINVAL;
> }
>
> + /* config_cbc() flushes ctrls to hardware at stream start. */
What's the meaning of this comment here . s_ctrl configures multiple
controls, including gamma, etc, so why mention config_cbc() ?
> return 0;
> }
>
> @@ -1647,6 +1708,19 @@ static int isc_ctrl_init(struct isc_device *isc)
> ctrls->brightness = 0;
>
> v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BRIGHTNESS, -1024, 1023, 1, 0);
> + if (isc->has_cbhs) {
> + /*
> + * CBHS_HUE is a signed 9-bit value in degrees.
> + * CBHS_SAT is Q4 unsigned 7-bit, 16 = 1.0x.
> + * Initialize the kernel-side state to neutral here so the
> + * first config_cbc() call after streaming starts does not
> + * write zero (grayscale) to the hardware.
> + */
> + ctrls->hue = 0;
> + ctrls->saturation = 16;
> + v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HUE, -180, 180, 1, 0);
> + v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SATURATION, 0, 127, 1, 16);
> + }
> v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAMMA, 0, isc->gamma_max, 1,
> isc->gamma_default);
> isc->awb_ctrl = v4l2_ctrl_new_std(hdl, &isc_awb_ops,
> @@ -1665,6 +1739,7 @@ static int isc_ctrl_init(struct isc_device *isc)
> }
>
> v4l2_ctrl_activate(isc->do_wb_ctrl, false);
> + isc_update_cbc_ctrl_activity(isc);
>
> isc->r_gain_ctrl = v4l2_ctrl_new_custom(hdl, &isc_r_gain_ctrl, NULL);
> isc->b_gain_ctrl = v4l2_ctrl_new_custom(hdl, &isc_b_gain_ctrl, NULL);
> diff --git a/drivers/media/platform/microchip/microchip-isc-regs.h b/drivers/media/platform/microchip/microchip-isc-regs.h
> index e77e1d9a1db8..7f5c2e50e74b 100644
> --- a/drivers/media/platform/microchip/microchip-isc-regs.h
> +++ b/drivers/media/platform/microchip/microchip-isc-regs.h
> @@ -268,10 +268,13 @@
> #define ISC_CBC_CONTRAST 0x000003c0
> #define ISC_CBC_CONTRAST_MASK GENMASK(11, 0)
>
> -/* Hue Register */
> -#define ISC_CBCHS_HUE 0x4e0
> -/* Saturation Register */
> -#define ISC_CBCHS_SAT 0x4e4
> +/* Hue Register: signed 9-bit two's complement, covers -180 to +180 degrees */
> +#define ISC_CBHS_HUE 0x4e0
> +#define ISC_CBHS_HUE_MASK GENMASK(8, 0)
> +
> +/* Saturation Register: unsigned Q4 fixed-point (1.0 = 16, V4L2 range 0..127) */
> +#define ISC_CBHS_SAT 0x4e4
> +#define ISC_CBHS_SAT_MASK GENMASK(6, 0)
>
> /* Offset for SUB422 register specific to sama5d2 product */
> #define ISC_SAMA5D2_SUB422_OFFSET 0
> diff --git a/drivers/media/platform/microchip/microchip-isc.h b/drivers/media/platform/microchip/microchip-isc.h
> index 2282ef7dd596..36a9c0cb241f 100644
> --- a/drivers/media/platform/microchip/microchip-isc.h
> +++ b/drivers/media/platform/microchip/microchip-isc.h
> @@ -139,6 +139,8 @@ struct isc_ctrls {
>
> u32 brightness;
> u32 contrast;
> + u32 hue;
> + u32 saturation;
> u8 gamma_index;
> #define ISC_WB_NONE 0
> #define ISC_WB_AUTO 1
> @@ -343,6 +345,7 @@ struct isc_device {
> const u32 (*gamma_table)[GAMMA_ENTRIES];
> u32 gamma_max;
> u32 gamma_default;
> + bool has_cbhs;
>
> u32 max_width;
> u32 max_height;
> diff --git a/drivers/media/platform/microchip/microchip-sama7g5-isc.c b/drivers/media/platform/microchip/microchip-sama7g5-isc.c
> index 06aa801b88f9..f51c7cac25df 100644
> --- a/drivers/media/platform/microchip/microchip-sama7g5-isc.c
> +++ b/drivers/media/platform/microchip/microchip-sama7g5-isc.c
> @@ -257,9 +257,8 @@ static void isc_sama7g5_config_cbc(struct isc_device *isc)
> /* Configure what is set via v4l2 ctrls */
> regmap_write(regmap, ISC_CBC_BRIGHT + isc->offsets.cbc, isc->ctrls.brightness);
> regmap_write(regmap, ISC_CBC_CONTRAST + isc->offsets.cbc, isc->ctrls.contrast);
> - /* Configure Hue and Saturation as neutral midpoint */
> - regmap_write(regmap, ISC_CBCHS_HUE, 0);
> - regmap_write(regmap, ISC_CBCHS_SAT, (1 << 4));
> + regmap_write(regmap, ISC_CBHS_HUE, isc->ctrls.hue);
> + regmap_write(regmap, ISC_CBHS_SAT, isc->ctrls.saturation);
> }
>
> static void isc_sama7g5_config_cc(struct isc_device *isc)
> @@ -463,6 +462,7 @@ static int microchip_xisc_probe(struct platform_device *pdev)
> isc->gamma_max = 2;
> /* Index 1 in the SAMA7G5 table is gamma 1/2.2 (sRGB). */
> isc->gamma_default = 1;
> + isc->has_cbhs = true;
>
> if (of_machine_is_compatible("microchip,sam9x7")) {
> isc->max_width = ISC_SAM9X7_MAX_SUPPORT_WIDTH;
>