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]