[PATCH v2 09/15] media: microchip-isc: add SAMA7G5 hue and saturation controls
From: Balakrishnan Sambath
Date: Tue May 12 2026 - 11:52:07 EST
SAMA7G5 extends CBC with hue and saturation. Add V4L2_CID_HUE and
V4L2_CID_SATURATION controls. Disable CBHS for RGB output since it
operates in YCbCr domain.
Signed-off-by: Balamanikandan Gunasundar <balamanikandan.gunasundar@xxxxxxxxxxxxx>
Signed-off-by: Balakrishnan Sambath <balakrishnan.s@xxxxxxxxxxxxx>
---
.../platform/microchip/microchip-isc-base.c | 86 ++++++++++++++++++-
.../platform/microchip/microchip-isc-regs.h | 11 ++-
.../media/platform/microchip/microchip-isc.h | 5 +-
.../microchip/microchip-sama5d2-isc.c | 2 +-
.../microchip/microchip-sama7g5-isc.c | 8 +-
5 files changed, 98 insertions(+), 14 deletions(-)
diff --git a/drivers/media/platform/microchip/microchip-isc-base.c b/drivers/media/platform/microchip/microchip-isc-base.c
index ae2a0c6ba566..7e140af51912 100644
--- a/drivers/media/platform/microchip/microchip-isc-base.c
+++ b/drivers/media/platform/microchip/microchip-isc-base.c
@@ -810,7 +810,7 @@ static int isc_try_configure_pipeline(struct isc_device *isc)
if (ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code)) {
isc->try_config.bits_pipeline = CFA_ENABLE |
CSC_ENABLE | GAM_ENABLES | WB_ENABLE |
- SUB420_ENABLE | SUB422_ENABLE | CBC_ENABLE |
+ SUB420_ENABLE | SUB422_ENABLE | CBHS_ENABLE |
DPC_BLCENABLE;
} else {
isc->try_config.bits_pipeline = 0x0;
@@ -821,7 +821,7 @@ static int isc_try_configure_pipeline(struct isc_device *isc)
if (ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code)) {
isc->try_config.bits_pipeline = CFA_ENABLE |
CSC_ENABLE | WB_ENABLE | GAM_ENABLES |
- SUB422_ENABLE | CBC_ENABLE | DPC_BLCENABLE;
+ SUB422_ENABLE | CBHS_ENABLE | DPC_BLCENABLE;
} else {
isc->try_config.bits_pipeline = 0x0;
}
@@ -833,7 +833,7 @@ static int isc_try_configure_pipeline(struct isc_device *isc)
if (ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code)) {
isc->try_config.bits_pipeline = CFA_ENABLE |
CSC_ENABLE | WB_ENABLE | GAM_ENABLES |
- SUB422_ENABLE | CBC_ENABLE | DPC_BLCENABLE;
+ SUB422_ENABLE | CBHS_ENABLE | DPC_BLCENABLE;
} else {
isc->try_config.bits_pipeline = 0x0;
}
@@ -844,7 +844,7 @@ static int isc_try_configure_pipeline(struct isc_device *isc)
if (ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code)) {
isc->try_config.bits_pipeline = CFA_ENABLE |
CSC_ENABLE | WB_ENABLE | GAM_ENABLES |
- CBC_ENABLE | DPC_BLCENABLE;
+ CBHS_ENABLE | DPC_BLCENABLE;
} else {
isc->try_config.bits_pipeline = 0x0;
}
@@ -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;
+ default:
+ return false;
+ }
+}
+
+/*
+ * isc_update_cbc_ctrl_activity() - Activate/deactivate CBC controls
+ *
+ * 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);
+}
+
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");
@@ -1453,9 +1505,30 @@ static int isc_s_ctrl(struct v4l2_ctrl *ctrl)
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
ctrls->brightness = ctrl->val & ISC_CBC_BRIGHT_MASK;
+ regmap_write(regmap, ISC_CBC_BRIGHT + isc->offsets.cbc, ctrls->brightness);
break;
case V4L2_CID_CONTRAST:
ctrls->contrast = ctrl->val & ISC_CBC_CONTRAST_MASK;
+ regmap_write(regmap, ISC_CBC_CONTRAST + isc->offsets.cbc, ctrls->contrast);
+ break;
+ case V4L2_CID_HUE:
+ if (isc->has_cbhs) {
+ ctrls->hue = ctrl->val & ISC_CBHS_HUE_MASK;
+ regmap_write(regmap, ISC_CBHS_HUE, ctrls->hue);
+ }
+ break;
+ case V4L2_CID_SATURATION:
+ if (isc->has_cbhs) {
+ /*
+ * The ISC CBHS SAT register holds a Q4 fixed-point
+ * coefficient: 0 = grayscale, 16 = 1.0 (no change),
+ * values above 16 boost saturation. The V4L2 range
+ * 0-100 (default 16) maps directly to this hardware
+ * value; no unit conversion is applied.
+ */
+ ctrls->saturation = ctrl->val & ISC_CBHS_SAT_MASK;
+ regmap_write(regmap, ISC_CBHS_SAT, ctrls->saturation);
+ }
break;
case V4L2_CID_GAMMA:
ctrls->gamma_index = ctrl->val;
@@ -1647,6 +1720,10 @@ 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) {
+ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HUE, -180, 180, 1, 0);
+ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SATURATION, 0, 100, 1, 16);
+ }
v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAMMA, 0, isc->gamma_max, 1, 1);
isc->awb_ctrl = v4l2_ctrl_new_std(hdl, &isc_awb_ops,
V4L2_CID_AUTO_WHITE_BALANCE,
@@ -1664,6 +1741,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..2fd8916abf21 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-100) */
+#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 ad4e98a1dd8f..2c8bcaaa26ea 100644
--- a/drivers/media/platform/microchip/microchip-isc.h
+++ b/drivers/media/platform/microchip/microchip-isc.h
@@ -88,7 +88,7 @@ struct isc_format {
#define GAM_RENABLE BIT(9)
#define VHXS_ENABLE BIT(10)
#define CSC_ENABLE BIT(11)
-#define CBC_ENABLE BIT(12)
+#define CBHS_ENABLE BIT(12)
#define SUB422_ENABLE BIT(13)
#define SUB420_ENABLE BIT(14)
@@ -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
@@ -342,6 +344,7 @@ struct isc_device {
/* pointer to the defined gamma table */
const u32 (*gamma_table)[GAMMA_ENTRIES];
u32 gamma_max;
+ bool has_cbhs;
u32 max_width;
u32 max_height;
diff --git a/drivers/media/platform/microchip/microchip-sama5d2-isc.c b/drivers/media/platform/microchip/microchip-sama5d2-isc.c
index 66d3d7891991..239aac170472 100644
--- a/drivers/media/platform/microchip/microchip-sama5d2-isc.c
+++ b/drivers/media/platform/microchip/microchip-sama5d2-isc.c
@@ -54,7 +54,7 @@
#define ISC_SAMA5D2_PIPELINE \
(WB_ENABLE | CFA_ENABLE | CC_ENABLE | GAM_ENABLES | CSC_ENABLE | \
- CBC_ENABLE | SUB422_ENABLE | SUB420_ENABLE)
+ CBHS_ENABLE | SUB422_ENABLE | SUB420_ENABLE)
/* This is a list of the formats that the ISC can *output* */
static const struct isc_format sama5d2_controller_formats[] = {
diff --git a/drivers/media/platform/microchip/microchip-sama7g5-isc.c b/drivers/media/platform/microchip/microchip-sama7g5-isc.c
index 8b73b625d92b..6705011edc2a 100644
--- a/drivers/media/platform/microchip/microchip-sama7g5-isc.c
+++ b/drivers/media/platform/microchip/microchip-sama7g5-isc.c
@@ -61,7 +61,7 @@
#define ISC_SAMA7G5_PIPELINE \
(DPC_DPCENABLE | DPC_GDCENABLE | DPC_BLCENABLE | \
WB_ENABLE | CFA_ENABLE | CC_ENABLE | GAM_ENABLES | CSC_ENABLE | \
- CBC_ENABLE | SUB422_ENABLE | SUB420_ENABLE)
+ CBHS_ENABLE | SUB422_ENABLE | SUB420_ENABLE)
/* This is a list of the formats that the ISC can *output* */
static const struct isc_format sama7g5_controller_formats[] = {
@@ -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)
@@ -461,6 +460,7 @@ static int microchip_xisc_probe(struct platform_device *pdev)
isc->gamma_table = isc_sama7g5_gamma_table;
isc->gamma_max = 2;
+ isc->has_cbhs = true;
if (of_machine_is_compatible("microchip,sam9x7")) {
isc->max_width = ISC_SAM9X7_MAX_SUPPORT_WIDTH;
--
2.34.1