[PATCH v7 05/18] media: rppx1: Add support for AWB measurement parameters and statistics

From: Jai Luthra

Date: Fri Apr 10 2026 - 05:11:57 EST


From: Niklas Söderlund <niklas.soderlund+renesas@xxxxxxxxxxxx>

Extend the RPPX1 driver to parse parameter blocks configuring for the
auto white balance measurement window and parameters. As well as
producing the measurements as part of a statistics buffer.

This is the first ISP algorithm added to the RPPX1 driver and exercises
both the parameter and statistics API provided by the base driver.

It uses the parameter writing interface which allows the framework user
to specify how (and when) the configuration are applied to the RPPX1.

Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@xxxxxxxxxxxx>
Signed-off-by: Jai Luthra <jai.luthra+renesas@xxxxxxxxxxxxxxxx>
---
.../media/platform/dreamchip/rppx1/rpp_params.c | 4 +
drivers/media/platform/dreamchip/rppx1/rpp_stats.c | 4 +
.../media/platform/dreamchip/rppx1/rppx1_wbmeas.c | 118 +++++++++++++++++++++
3 files changed, 126 insertions(+)

diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_params.c b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
index 6d2a8d45056ae19c9c05b7e38c1aa013cbe2706b..868a5ce1620e185174b8fade8a9a697826b3fed5 100644
--- a/drivers/media/platform/dreamchip/rppx1/rpp_params.c
+++ b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
@@ -16,6 +16,7 @@

static const struct v4l2_isp_params_block_type_info
rppx1_ext_params_blocks_info[] = {
+ RPPX1_PARAMS_BLOCK_INFO(AWB_MEAS, awb_meas),
};

int rppx1_params(struct rppx1 *rpp, struct vb2_buffer *vb, size_t max_size,
@@ -50,6 +51,9 @@ int rppx1_params(struct rppx1 *rpp, struct vb2_buffer *vb, size_t max_size,
block_offset += block->header.size;

switch (block->header.type) {
+ case RPPX1_PARAMS_BLOCK_TYPE_AWB_MEAS:
+ module = &rpp->post.wbmeas;
+ break;
default:
module = NULL;
break;
diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_stats.c b/drivers/media/platform/dreamchip/rppx1/rpp_stats.c
index b62e792b84a105baec904640ec606670b9bbbadd..aac7692d2a61e919e32d7684af86f856ab5a22bb 100644
--- a/drivers/media/platform/dreamchip/rppx1/rpp_stats.c
+++ b/drivers/media/platform/dreamchip/rppx1/rpp_stats.c
@@ -11,5 +11,9 @@ void rppx1_stats_fill_isr(struct rppx1 *rpp, u32 isc, void *buf)
struct rppx1_stat_buffer *stats = buf;

stats->meas_type = 0;
+
+ if (isc & RPPX1_IRQ_ID_POST_AWB_MEAS)
+ if (!rpp_module_call(&rpp->post.wbmeas, fill_stats, &stats->params))
+ stats->meas_type |= RPPX1_STAT_AWB;
}
EXPORT_SYMBOL_GPL(rppx1_stats_fill_isr);
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_wbmeas.c b/drivers/media/platform/dreamchip/rppx1/rppx1_wbmeas.c
index 3d197d914d0710128a61842ac3db54ddcce8c45e..e6a3b2eece1576a2daf0df4ca243d5a1e93ef662 100644
--- a/drivers/media/platform/dreamchip/rppx1/rppx1_wbmeas.c
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_wbmeas.c
@@ -56,6 +56,124 @@ static int rppx1_wbmeas_probe(struct rpp_module *mod)
return 0;
}

+static int
+rppx1_wbmeas_fill_params(struct rpp_module *mod,
+ const union rppx1_params_block *block,
+ rppx1_reg_write write, void *priv)
+{
+ const struct rppx1_params_awb_meas_config *cfg = &block->awbm;
+ /*
+ * The native params are 24-bit while the RPP can be 8, 20 or 24 bit.
+ * Figure out how much we need to adjust the input parameters.
+ */
+ const unsigned int shift = 24 - mod->info.wbmeas.colorbits;
+
+ /* If the modules is disabled, simply bypass it. */
+ if (cfg->header.flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) {
+ write(priv, mod->base + AWB_MEAS_PROP_REG, 0);
+ return 0;
+ }
+
+ /* Program measurement window. */
+ write(priv, mod->base + AWB_MEAS_H_OFFS_REG,
+ cfg->awb_wnd.h_offs);
+ write(priv, mod->base + AWB_MEAS_V_OFFS_REG,
+ cfg->awb_wnd.v_offs);
+ write(priv, mod->base + AWB_MEAS_H_SIZE_REG,
+ cfg->awb_wnd.h_size);
+ write(priv, mod->base + AWB_MEAS_V_SIZE_REG,
+ cfg->awb_wnd.v_size);
+
+ /* Set number of frames to sample. */
+ write(priv, mod->base + AWB_MEAS_FRAMES_REG, cfg->frames);
+
+ if (cfg->awb_mode == RPPX1_AWB_MODE_YCBCR) {
+ write(priv, mod->base + AWB_MEAS_REF_CB_MAX_B_REG,
+ cfg->awb_ref_cb >> shift);
+ write(priv, mod->base + AWB_MEAS_REF_CR_MAX_R_REG,
+ cfg->awb_ref_cr >> shift);
+ write(priv, mod->base + AWB_MEAS_MAX_Y_REG,
+ cfg->max_y >> shift);
+ write(priv, mod->base + AWB_MEAS_MIN_Y_MAX_G_REG,
+ cfg->min_y >> shift);
+ write(priv, mod->base + AWB_MEAS_MAX_CSUM_REG,
+ cfg->max_csum >> shift);
+ write(priv, mod->base + AWB_MEAS_MIN_C_REG,
+ cfg->min_c >> shift);
+
+ /*
+ * Match RkISP1 conversion, documented as
+ * Y = 16 + 0.2500 R + 0.5000 G + 0.1094 B
+ * Cb = 128 - 0.1406 R - 0.2969 G + 0.4375 B
+ * Cr = 128 + 0.4375 R - 0.3750 G - 0.0625 B
+ *
+ * Note map Y to G. Matrix is GBR, not RGB documented for RPPX1.
+ */
+ write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(0), 0x0800);
+ write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(1), 0x01c0);
+ write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(2), 0x0400);
+ write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(3), 0xfb40);
+ write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(4), 0x0700);
+ write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(5), 0xfdc0);
+ write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(6), 0xfa00);
+ write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(7), 0xff00);
+ write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(8), 0x0700);
+
+ write(priv, mod->base + AWB_MEAS_CCOR_OFFSET_R_REG, 0x00100000);
+ write(priv, mod->base + AWB_MEAS_CCOR_OFFSET_G_REG, 0x00800000);
+ write(priv, mod->base + AWB_MEAS_CCOR_OFFSET_B_REG, 0x00800000);
+
+ write(priv, mod->base + AWB_MEAS_PROP_REG,
+ cfg->enable_ymax_cmp ? AWB_MEAS_PROP_YMAX : 0 |
+ AWB_MEAS_PROP_AWB_MODE_ON);
+ } else {
+ write(priv, mod->base + AWB_MEAS_REF_CB_MAX_B_REG,
+ cfg->awb_ref_cb >> shift);
+ write(priv, mod->base + AWB_MEAS_REF_CR_MAX_R_REG,
+ cfg->awb_ref_cr >> shift);
+ write(priv, mod->base + AWB_MEAS_MIN_Y_MAX_G_REG,
+ cfg->min_y >> shift);
+
+ /* Values from datasheet to map G to Y, B to Cb and R to Cr. */
+ write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(0), 0x1000);
+ write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(1), 0x0000);
+ write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(2), 0x0000);
+ write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(3), 0x0000);
+ write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(4), 0x1000);
+ write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(5), 0x0000);
+ write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(6), 0x0000);
+ write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(7), 0x0000);
+ write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(8), 0x1000);
+
+ /* Values from datasheet. */
+ write(priv, mod->base + AWB_MEAS_CCOR_OFFSET_R_REG, 0x00000000);
+ write(priv, mod->base + AWB_MEAS_CCOR_OFFSET_G_REG, 0x00000000);
+ write(priv, mod->base + AWB_MEAS_CCOR_OFFSET_B_REG, 0x00000000);
+
+ write(priv, mod->base + AWB_MEAS_PROP_REG,
+ AWB_MEAS_PROP_MEAS_MODE_RGB |
+ AWB_MEAS_PROP_AWB_MODE_ON);
+ }
+
+ return 0;
+}
+
+static int rppx1_wbmeas_fill_stats(struct rpp_module *mod,
+ struct rppx1_stat *stats)
+{
+ struct rppx1_awb_meas *meas = &stats->awb.awb_mean[0];
+
+ /* Return measurements at native hardware precision. */
+ meas->cnt = rpp_module_read(mod, AWB_MEAS_WHITE_CNT_REG);
+ meas->mean_y_or_g = rpp_module_read(mod, AWB_MEAS_MEAN_Y_G_REG);
+ meas->mean_cb_or_b = rpp_module_read(mod, AWB_MEAS_MEAN_CB_B_REG);
+ meas->mean_cr_or_r = rpp_module_read(mod, AWB_MEAS_MEAN_CR_R_REG);
+
+ return 0;
+}
+
const struct rpp_module_ops rppx1_wbmeas_ops = {
.probe = rppx1_wbmeas_probe,
+ .fill_params = rppx1_wbmeas_fill_params,
+ .fill_stats = rppx1_wbmeas_fill_stats
};

--
2.53.0