[PATCH v7 13/18] media: rppx1: Add support for Lens Shade Correction

From: Jai Luthra

Date: Fri Apr 10 2026 - 05:09:46 EST


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

Extend the RPPX1 driver to allow setting the Lens Shade Correction (LSC)
configuration using the parameter buffer format. It uses the RPPX1
framework for parameters and its writer abstraction to allow the user
to control how (and when) configuration is applied to the RPPX1.

The parameters buffer only describes one quadrant of the lens, this is
due to the RkISP1 hardware only allowing one quadrant to be programmed
to hardware, which was copied for RPPX1. The hardware then extrapolates
this to the other three quadrants of the lens. For RPP all four
quadrants are individually programmable.

To compensate for the driver need to extrapolate all four quadrants from
the parameters buffer information.

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/rppx1_lsc.c | 126 +++++++++++++++++++++
2 files changed, 130 insertions(+)

diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_params.c b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
index 3a8848114bbba670071a7b0871955d82eec76b3f..386c36fe0e19fa52d53691d77405d31d844c5445 100644
--- a/drivers/media/platform/dreamchip/rppx1/rpp_params.c
+++ b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
@@ -19,6 +19,7 @@ rppx1_ext_params_blocks_info[] = {
RPPX1_PARAMS_BLOCK_INFO(BLS, bls),
RPPX1_PARAMS_BLOCK_INFO(AWB_GAIN, awb_gain),
RPPX1_PARAMS_BLOCK_INFO(CTK, ctk),
+ RPPX1_PARAMS_BLOCK_INFO(LSC, lsc),
RPPX1_PARAMS_BLOCK_INFO(AWB_MEAS, awb_meas),
RPPX1_PARAMS_BLOCK_INFO(HST_MEAS, hst),
RPPX1_PARAMS_BLOCK_INFO(AEC_MEAS, aec),
@@ -65,6 +66,9 @@ int rppx1_params(struct rppx1 *rpp, struct vb2_buffer *vb, size_t max_size,
case RPPX1_PARAMS_BLOCK_TYPE_CTK:
module = &rpp->post.ccor;
break;
+ case RPPX1_PARAMS_BLOCK_TYPE_LSC:
+ module = &rpp->pre1.lsc;
+ break;
case RPPX1_PARAMS_BLOCK_TYPE_AWB_MEAS:
module = &rpp->post.wbmeas;
break;
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_lsc.c b/drivers/media/platform/dreamchip/rppx1/rppx1_lsc.c
index e8acdf74495633790e2614a9985a47fa92df4eeb..4cba2d075bec6390ecc5bffb25eeba443213f52e 100644
--- a/drivers/media/platform/dreamchip/rppx1/rppx1_lsc.c
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_lsc.c
@@ -54,6 +54,10 @@
#define LSC_TABLE_SEL_REG 0x00a8
#define LSC_STATUS_REG 0x00ac

+#define LSC_R_TABLE_DATA_VALUE(v1, v2) (((v1) & 0xfff) | (((v2) & 0xfff) << 12))
+#define LSC_GRAD_VALUE(v1, v2) (((v1) & 0xfff) | (((v2) & 0xfff) << 16))
+#define LSC_SIZE_VALUE(v1, v2) (((v1) & 0x1ff) | (((v2) & 0x1ff) << 16))
+
static int rppx1_lsc_probe(struct rpp_module *mod)
{
/* Version check. */
@@ -63,6 +67,128 @@ static int rppx1_lsc_probe(struct rpp_module *mod)
return 0;
}

+static int
+rppx1_lsc_fill_params(struct rpp_module *mod,
+ const union rppx1_params_block *block,
+ rppx1_reg_write write, void *priv)
+{
+ const struct rppx1_params_lsc_config *cfg = &block->lsc;
+ const __u16 *v;
+
+ /* Always disable module as it needs be disabled before configuring. */
+ write(priv, mod->base + LSC_CTRL_REG, 0);
+ if (cfg->header.flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE)
+ return 0;
+
+ /*
+ * Program the color correction sectors.
+ *
+ * There are two tables to one can program and switch between. As the
+ * RPPX1 supports preparing a buffer of commands to be applied later
+ * only use table 0. This works as long as the ISP is not used in
+ * inline-mode.
+ *
+ * For inline-mode support using DMA for configuration is not possible
+ * so this is not an issue, but needs to be address if inline-mode
+ * support is added to the driver.
+ */
+
+ /* Start writing at beginning of table 0. */
+ write(priv, mod->base + LSC_R_TABLE_ADDR_REG, 0);
+ write(priv, mod->base + LSC_GR_TABLE_ADDR_REG, 0);
+ write(priv, mod->base + LSC_B_TABLE_ADDR_REG, 0);
+ write(priv, mod->base + LSC_GB_TABLE_ADDR_REG, 0);
+
+ /* Program data tables. */
+ for (unsigned int i = 0; i < RPPX1_LSC_SAMPLES_MAX; i++) {
+ const __u16 *r = cfg->r_data_tbl[i];
+ const __u16 *gr = cfg->gr_data_tbl[i];
+ const __u16 *b = cfg->b_data_tbl[i];
+ const __u16 *gb = cfg->gb_data_tbl[i];
+ unsigned int j;
+
+ for (j = 0; j < RPPX1_LSC_SAMPLES_MAX - 1; j += 2) {
+ write(priv, mod->base + LSC_R_TABLE_DATA_REG,
+ LSC_R_TABLE_DATA_VALUE(r[j], r[j + 1]));
+ write(priv, mod->base + LSC_GR_TABLE_DATA_REG,
+ LSC_R_TABLE_DATA_VALUE(gr[j], gr[j + 1]));
+ write(priv, mod->base + LSC_B_TABLE_DATA_REG,
+ LSC_R_TABLE_DATA_VALUE(b[j], b[j + 1]));
+ write(priv, mod->base + LSC_GB_TABLE_DATA_REG,
+ LSC_R_TABLE_DATA_VALUE(gb[j], gb[j + 1]));
+ }
+
+ write(priv, mod->base + LSC_R_TABLE_DATA_REG,
+ LSC_R_TABLE_DATA_VALUE(r[j], 0));
+ write(priv, mod->base + LSC_GR_TABLE_DATA_REG,
+ LSC_R_TABLE_DATA_VALUE(gr[j], 0));
+ write(priv, mod->base + LSC_B_TABLE_DATA_REG,
+ LSC_R_TABLE_DATA_VALUE(b[j], 0));
+ write(priv, mod->base + LSC_GB_TABLE_DATA_REG,
+ LSC_R_TABLE_DATA_VALUE(gb[j], 0));
+ }
+
+ /* Activate table 0. */
+ write(priv, mod->base + LSC_TABLE_SEL_REG, 0);
+
+ /*
+ * Program X- and Y- sizes, and gradients.
+ *
+ * The RPP ISP can describe each quarter of the lens individually, this
+ * differs from the Rk1ISP which can only describe one quarter of lens
+ * with software and then extrapolates the other three.
+ *
+ * To adjust for this extrapolate the three missing quadrants using
+ * software for the RPP ISP.
+ */
+
+ v = cfg->x_grad_tbl;
+ write(priv, mod->base + LSC_XGRAD_01_REG, LSC_GRAD_VALUE(v[0], v[1]));
+ write(priv, mod->base + LSC_XGRAD_23_REG, LSC_GRAD_VALUE(v[2], v[3]));
+ write(priv, mod->base + LSC_XGRAD_45_REG, LSC_GRAD_VALUE(v[4], v[5]));
+ write(priv, mod->base + LSC_XGRAD_67_REG, LSC_GRAD_VALUE(v[6], v[7]));
+ write(priv, mod->base + LSC_XGRAD_89_REG, LSC_GRAD_VALUE(v[7], v[6]));
+ write(priv, mod->base + LSC_XGRAD_1011_REG, LSC_GRAD_VALUE(v[5], v[4]));
+ write(priv, mod->base + LSC_XGRAD_1213_REG, LSC_GRAD_VALUE(v[3], v[2]));
+ write(priv, mod->base + LSC_XGRAD_1415_REG, LSC_GRAD_VALUE(v[1], v[0]));
+
+ v = cfg->y_grad_tbl;
+ write(priv, mod->base + LSC_YGRAD_01_REG, LSC_GRAD_VALUE(v[0], v[1]));
+ write(priv, mod->base + LSC_YGRAD_23_REG, LSC_GRAD_VALUE(v[2], v[3]));
+ write(priv, mod->base + LSC_YGRAD_45_REG, LSC_GRAD_VALUE(v[4], v[5]));
+ write(priv, mod->base + LSC_YGRAD_67_REG, LSC_GRAD_VALUE(v[6], v[7]));
+ write(priv, mod->base + LSC_YGRAD_89_REG, LSC_GRAD_VALUE(v[7], v[6]));
+ write(priv, mod->base + LSC_YGRAD_1011_REG, LSC_GRAD_VALUE(v[5], v[4]));
+ write(priv, mod->base + LSC_YGRAD_1213_REG, LSC_GRAD_VALUE(v[3], v[2]));
+ write(priv, mod->base + LSC_YGRAD_1415_REG, LSC_GRAD_VALUE(v[1], v[0]));
+
+ v = cfg->x_size_tbl;
+ write(priv, mod->base + LSC_XSIZE_01_REG, LSC_GRAD_VALUE(v[0], v[1]));
+ write(priv, mod->base + LSC_XSIZE_23_REG, LSC_GRAD_VALUE(v[2], v[3]));
+ write(priv, mod->base + LSC_XSIZE_45_REG, LSC_GRAD_VALUE(v[4], v[5]));
+ write(priv, mod->base + LSC_XSIZE_67_REG, LSC_GRAD_VALUE(v[6], v[7]));
+ write(priv, mod->base + LSC_XSIZE_89_REG, LSC_GRAD_VALUE(v[7], v[6]));
+ write(priv, mod->base + LSC_XSIZE_1011_REG, LSC_GRAD_VALUE(v[5], v[4]));
+ write(priv, mod->base + LSC_XSIZE_1213_REG, LSC_GRAD_VALUE(v[3], v[2]));
+ write(priv, mod->base + LSC_XSIZE_1415_REG, LSC_GRAD_VALUE(v[1], v[0]));
+
+ v = cfg->y_size_tbl;
+ write(priv, mod->base + LSC_YSIZE_01_REG, LSC_GRAD_VALUE(v[0], v[1]));
+ write(priv, mod->base + LSC_YSIZE_23_REG, LSC_GRAD_VALUE(v[2], v[3]));
+ write(priv, mod->base + LSC_YSIZE_45_REG, LSC_GRAD_VALUE(v[4], v[5]));
+ write(priv, mod->base + LSC_YSIZE_67_REG, LSC_GRAD_VALUE(v[6], v[7]));
+ write(priv, mod->base + LSC_YSIZE_89_REG, LSC_GRAD_VALUE(v[7], v[6]));
+ write(priv, mod->base + LSC_YSIZE_1011_REG, LSC_GRAD_VALUE(v[5], v[4]));
+ write(priv, mod->base + LSC_YSIZE_1213_REG, LSC_GRAD_VALUE(v[3], v[2]));
+ write(priv, mod->base + LSC_YSIZE_1415_REG, LSC_GRAD_VALUE(v[1], v[0]));
+
+ /* Enable module. */
+ write(priv, mod->base + LSC_CTRL_REG, LSC_CTRL_LSC_EN);
+
+ return 0;
+}
+
const struct rpp_module_ops rppx1_lsc_ops = {
.probe = rppx1_lsc_probe,
+ .fill_params = rppx1_lsc_fill_params,
};

--
2.53.0