On Thu Jul 30 19:12:39 2026 +0200, Niklas Söderlund wrote:
> Extend the RPPX1 driver to allow setting the lens shade correction
> configuration parameters. 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.
> 
> Signed-off-by: Niklas Söderlund <[email protected]>
> Co-developed-by: Jai Luthra <[email protected]>
> Signed-off-by: Jai Luthra <[email protected]>
> Co-developed-by: Jacopo Mondi <[email protected]>
> Signed-off-by: Jacopo Mondi <[email protected]>
> Reviewed-by: Jacopo Mondi <[email protected]>
> Signed-off-by: Sakari Ailus <[email protected]>

Patch committed.

Thanks,
Sakari Ailus

 .../media/platform/dreamchip/rppx1/rpp_module.h    |   1 +
 .../media/platform/dreamchip/rppx1/rpp_params.c    |   5 +
 drivers/media/platform/dreamchip/rppx1/rppx1_lsc.c | 119 +++++++++++++++++++++
 include/uapi/linux/media/dreamchip/rppx1-config.h  |  54 +++++++++-
 4 files changed, 178 insertions(+), 1 deletion(-)

---

diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_module.h 
b/drivers/media/platform/dreamchip/rppx1/rpp_module.h
index e039746ac542..48b61b5c35b4 100644
--- a/drivers/media/platform/dreamchip/rppx1/rpp_module.h
+++ b/drivers/media/platform/dreamchip/rppx1/rpp_module.h
@@ -48,6 +48,7 @@ void rpp_module_clrset(struct rpp_module *mod, u32 offset, 
u32 mask, u32 value);
 union rppx1_params_block {
        struct v4l2_isp_block_header header;
        struct rppx1_bls_params bls;
+       struct rppx1_lsc_params lsc;
        struct rppx1_awbg_params awbg;
        struct rppx1_ccor_params ccor;
        struct rppx1_hist_params hist;
diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_params.c 
b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
index a83d393d0504..8d85d0c7bff1 100644
--- a/drivers/media/platform/dreamchip/rppx1/rpp_params.c
+++ b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
@@ -19,6 +19,8 @@ static const struct v4l2_isp_params_block_type_info
 rppx1_ext_params_blocks_info[] = {
        RPPX1_PARAMS_BLOCK_INFO(BLS_PRE1, bls),
        RPPX1_PARAMS_BLOCK_INFO(BLS_PRE2, bls),
+       RPPX1_PARAMS_BLOCK_INFO(LSC_PRE1, lsc),
+       RPPX1_PARAMS_BLOCK_INFO(LSC_PRE2, lsc),
        RPPX1_PARAMS_BLOCK_INFO(AWBG_PRE1, awbg),
        RPPX1_PARAMS_BLOCK_INFO(AWBG_PRE2, awbg),
        RPPX1_PARAMS_BLOCK_INFO(CCOR_POST, ccor),
@@ -64,6 +66,9 @@ int rppx1_params(struct rppx1 *rpp, struct vb2_buffer *vb, 
size_t max_size,
                case RPPX1_PARAMS_BLOCK_TYPE_BLS_PRE1:
                        module = &rpp->pre1.bls;
                        break;
+               case RPPX1_PARAMS_BLOCK_TYPE_LSC_PRE1:
+                       module = &rpp->pre1.lsc;
+                       break;
                case RPPX1_PARAMS_BLOCK_TYPE_AWBG_PRE1:
                        module = &rpp->pre1.awbg;
                        break;
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_lsc.c 
b/drivers/media/platform/dreamchip/rppx1/rppx1_lsc.c
index be49fc17ea26..8badeca23e24 100644
--- a/drivers/media/platform/dreamchip/rppx1/rppx1_lsc.c
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_lsc.c
@@ -55,6 +55,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. */
@@ -64,6 +68,121 @@ 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_lsc_params *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[i];
+               const __u16 *gr = cfg->gr_data[i];
+               const __u16 *b = cfg->b_data[i];
+               const __u16 *gb = cfg->gb_data[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.
+        */
+
+       v = cfg->x_grad;
+       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[8], v[9]));
+       write(priv, mod->base + LSC_XGRAD_1011_REG, LSC_GRAD_VALUE(v[10], 
v[11]));
+       write(priv, mod->base + LSC_XGRAD_1213_REG, LSC_GRAD_VALUE(v[12], 
v[13]));
+       write(priv, mod->base + LSC_XGRAD_1415_REG, LSC_GRAD_VALUE(v[14], 
v[15]));
+
+       v = cfg->y_grad;
+       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[8], v[9]));
+       write(priv, mod->base + LSC_YGRAD_1011_REG, LSC_GRAD_VALUE(v[10], 
v[11]));
+       write(priv, mod->base + LSC_YGRAD_1213_REG, LSC_GRAD_VALUE(v[12], 
v[13]));
+       write(priv, mod->base + LSC_YGRAD_1415_REG, LSC_GRAD_VALUE(v[14], 
v[15]));
+
+       v = cfg->x_sect_size;
+       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[8], v[9]));
+       write(priv, mod->base + LSC_XSIZE_1011_REG, LSC_GRAD_VALUE(v[10], 
v[11]));
+       write(priv, mod->base + LSC_XSIZE_1213_REG, LSC_GRAD_VALUE(v[12], 
v[13]));
+       write(priv, mod->base + LSC_XSIZE_1415_REG, LSC_GRAD_VALUE(v[14], 
v[15]));
+
+       v = cfg->y_sect_size;
+       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[8], v[9]));
+       write(priv, mod->base + LSC_YSIZE_1011_REG, LSC_GRAD_VALUE(v[10], 
v[11]));
+       write(priv, mod->base + LSC_YSIZE_1213_REG, LSC_GRAD_VALUE(v[12], 
v[13]));
+       write(priv, mod->base + LSC_YSIZE_1415_REG, LSC_GRAD_VALUE(v[14], 
v[15]));
+
+       /* 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,
 };
diff --git a/include/uapi/linux/media/dreamchip/rppx1-config.h 
b/include/uapi/linux/media/dreamchip/rppx1-config.h
index feea30585d4c..eec8a9c50bbe 100644
--- a/include/uapi/linux/media/dreamchip/rppx1-config.h
+++ b/include/uapi/linux/media/dreamchip/rppx1-config.h
@@ -88,6 +88,8 @@ enum rppx1_meas_chan {
  * @RPPX1_PARAMS_BLOCK_TYPE_BLS_PRE1: PRE1 pipe Black Level Subtraction
  * @RPPX1_PARAMS_BLOCK_TYPE_BLS_PRE2: PRE2 pipe Black Level Subtraction
  * @RPPX1_PARAMS_BLOCK_TYPE_CCOR_POST: POST pipe Color Correction
+ * @RPPX1_PARAMS_BLOCK_TYPE_LSC_PRE1: PRE1 pipe Lens Shading Correction
+ * @RPPX1_PARAMS_BLOCK_TYPE_LSC_PRE2: PRE2 pipe Lens Shading Correction
  */
 enum rppx1_params_block_type {
        RPPX1_PARAMS_BLOCK_TYPE_WBMEAS_POST,
@@ -102,6 +104,8 @@ enum rppx1_params_block_type {
        RPPX1_PARAMS_BLOCK_TYPE_BLS_PRE1,
        RPPX1_PARAMS_BLOCK_TYPE_BLS_PRE2,
        RPPX1_PARAMS_BLOCK_TYPE_CCOR_POST,
+       RPPX1_PARAMS_BLOCK_TYPE_LSC_PRE1,
+       RPPX1_PARAMS_BLOCK_TYPE_LSC_PRE2,
 };
 
 /**
@@ -460,6 +464,52 @@ struct rppx1_ccor_params {
        __u32 offset[3];
 };
 
+/* Lens Shade Correction */
+#define RPPX1_LSC_SAMPLES_MAX 17
+#define RPPX1_LSC_NUM_SECTORS 16
+
+/**
+ * struct rppx1_lsc_params - Lens Shading Correction configuration
+ *
+ * The RPP-X1 Lens shading correction module is available on the PRE1 and PRE2
+ * pre-fusion pipes. Userspace selects which pipe to operate by setting the
+ * @header.type field to RPPX1_PARAMS_BLOCK_TYPE_LSC_PRE1 or
+ * RPPX1_PARAMS_BLOCK_TYPE_LSC_PRE2.
+ *
+ * The module applies per-color channel correction factors @r_data, @gr_data,
+ * @gb_data and @b_data as a 16x16 grid mapped on the image. The size of each
+ * grid segment is expressed by the @x_sect_size and @y_sect_size arrays.  Each
+ * segment shall be at least 8 pixels in size and the sum of all horizontal
+ * segments @x_sect_size shall match the input frame size width.
+ *
+ * The correction factors values are expressed as unsigned Q2.10 integers
+ * ranging from 1 to 3.999.
+ *
+ * Pre-calculated interpolation factors shall be provided in the @x_grad
+ * and @y_grad fields, expressed as 12 bits integer values.
+ *
+ * @header: block header (type = RPPX1_PARAMS_BLOCK_TYPE_LSC)
+ * @r_data: correction factors for the red channel in Q2.10 format
+ * @gr_data: correction factors for the green (red) channel in Q2.10 format
+ * @gb_data: correction factors for the green (blue) channel in Q2.10 format
+ * @b_data: correction factors for the blue channel in Q2.10 format
+ * @x_grad: Interpolation gradients for each horizontal sector (12 bits)
+ * @y_grad: Interpolation gradients for each vertical sector (12 bits)
+ * @x_sect_size: Horizontal sectors sizes
+ * @y_sect_size: Vertical sectors sizes
+ */
+struct rppx1_lsc_params {
+       struct v4l2_isp_params_block_header header;
+       __u16 r_data[RPPX1_LSC_SAMPLES_MAX][RPPX1_LSC_SAMPLES_MAX];
+       __u16 gr_data[RPPX1_LSC_SAMPLES_MAX][RPPX1_LSC_SAMPLES_MAX];
+       __u16 gb_data[RPPX1_LSC_SAMPLES_MAX][RPPX1_LSC_SAMPLES_MAX];
+       __u16 b_data[RPPX1_LSC_SAMPLES_MAX][RPPX1_LSC_SAMPLES_MAX];
+       __u16 x_grad[RPPX1_LSC_NUM_SECTORS];
+       __u16 y_grad[RPPX1_LSC_NUM_SECTORS];
+       __u16 x_sect_size[RPPX1_LSC_NUM_SECTORS];
+       __u16 y_sect_size[RPPX1_LSC_NUM_SECTORS];
+};
+
 /**
  * RPPX1_PARAMS_MAX_SIZE - Maximum size of all RPP-X1 parameter blocks
  *
@@ -478,7 +528,9 @@ struct rppx1_ccor_params {
        sizeof(struct rppx1_hist_params)                        +       \
        sizeof(struct rppx1_bls_params)                         +       \
        sizeof(struct rppx1_bls_params)                         +       \
-       sizeof(struct rppx1_ccor_params))
+       sizeof(struct rppx1_ccor_params)                        +       \
+       sizeof(struct rppx1_lsc_params)                         +       \
+       sizeof(struct rppx1_lsc_params))
 
 /* ---------------------------------------------------------------------------
  * Statistics Structures
_______________________________________________
linuxtv-commits mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to