[PATCH RESEND 37/45] media: qcom: camss: sa8775p: add LSC parameters
From: Gjorgji . Rosikopulos . gjorgji . rosikopulos
Date: Wed Oct 07 2026 - 05:15:26 EST
From: Gjorgji Rosikopulos <gjorgji.rosikopulos@xxxxxxxxxxxxxxxx>
Program the lens shading geometry and red and blue gain tables from
IFE parameter buffers. Validate the block counts before updating
the hardware.
Signed-off-by: Gjorgji Rosikopulos <gjorgji.rosikopulos@xxxxxxxxxxxxxxxx>
Co-developed-by: Vikram Sharma <vikram.sharma@xxxxxxxxxxxxxxxx>
Signed-off-by: Vikram Sharma <vikram.sharma@xxxxxxxxxxxxxxxx>
Co-developed-by: Prashant Shrotriya <prashant.shrotriya@xxxxxxxxxxxxxxxx>
Signed-off-by: Prashant Shrotriya <prashant.shrotriya@xxxxxxxxxxxxxxxx>
Signed-off-by: Atanas Filipov <atanas.filipov@xxxxxxxxxxxxxxxx>
---
.../platform/qcom/camss/camss-ife-mod-v41x.c | 142 ++++++++++++++++++
1 file changed, 142 insertions(+)
diff --git a/drivers/media/platform/qcom/camss/camss-ife-mod-v41x.c b/drivers/media/platform/qcom/camss/camss-ife-mod-v41x.c
index 0db88534b421..df21ea4040df 100644
--- a/drivers/media/platform/qcom/camss/camss-ife-mod-v41x.c
+++ b/drivers/media/platform/qcom/camss/camss-ife-mod-v41x.c
@@ -134,6 +134,31 @@
#define IFE_MOD_V41X_CCM_SHIFT_M_PARAM GENMASK(1, 0)
#define IFE_MOD_V41X_CCM_SHIFT_M_Q8 0x1
+/* Shared DMI port registers */
+#define IFE_MOD_V41X_DMI_LUT_CFG 0xc
+#define IFE_MOD_V41X_DMI_DATA 0x10
+
+/* LSC offset, registers and bitfields */
+#define IFE_MOD_V41X_LSC_OFFSET 0x3c80
+#define IFE_MOD_V41X_LSC_MODULE_CFG 0x60
+#define IFE_MOD_V41X_LSC_LSC_0_CFG 0x68
+#define IFE_MOD_V41X_LSC_LSC_1_CFG 0x6c
+#define IFE_MOD_V41X_LSC_LSC_2_CFG 0x70
+#define IFE_MOD_V41X_LSC_LSC_3_CFG 0x74
+#define IFE_MOD_V41X_LSC_CFG_EN BIT(0)
+#define IFE_MOD_V41X_LSC_NUM_SUBBLOCKS GENMASK(13, 12)
+#define IFE_MOD_V41X_LSC_INIT_SUBBLOCK_Y GENMASK(30, 28)
+#define IFE_MOD_V41X_LSC_NUM_BLOCKS_H GENMASK(3, 0)
+#define IFE_MOD_V41X_LSC_NUM_BLOCKS_V GENMASK(7, 4)
+#define IFE_MOD_V41X_LSC_BLOCK_WIDTH GENMASK(10, 0)
+#define IFE_MOD_V41X_LSC_BLOCK_HEIGHT GENMASK(25, 16)
+#define IFE_MOD_V41X_LSC_INV_SUBBLOCK_Q20 20
+#define IFE_MOD_V41X_LSC_INV_SUBBLOCK_WIDTH GENMASK(28, 12)
+#define IFE_MOD_V41X_LSC_INV_SUBBLOCK_HEIGHT GENMASK(28, 12)
+#define IFE_MOD_V41X_LSC_LUT_SEL_NONE 0x0
+#define IFE_MOD_V41X_LSC_LUT_SEL_RED 0x1
+#define IFE_MOD_V41X_LSC_LUT_SEL_BLUE 0x2
+
static void mod_v41x_wb_gain_config(struct ife_pp_ctx *ctx)
{
void __iomem *base = ctx->base + IFE_MOD_V41X_WB_GAIN_OFFSET;
@@ -234,6 +259,102 @@ static void mod_v41x_ccm_apply(struct ife_pp_ctx *ctx,
}
}
+static void mod_v41x_dmi_write(void __iomem *dmi_base, u32 sel,
+ const u32 *table, size_t count)
+{
+ size_t i;
+
+ writel_relaxed(sel, dmi_base + IFE_MOD_V41X_DMI_LUT_CFG);
+ for (i = 0; i < count; i++)
+ writel_relaxed(table[i], dmi_base + IFE_MOD_V41X_DMI_DATA);
+}
+
+static void mod_v41x_lsc_enable(struct ife_pp_ctx *ctx)
+{
+ void __iomem *base = ctx->base + IFE_MOD_V41X_LSC_OFFSET;
+ u32 value;
+
+ value = readl_relaxed(base + IFE_MOD_V41X_LSC_MODULE_CFG);
+ value |= IFE_MOD_V41X_LSC_CFG_EN |
+ FIELD_PREP(IFE_MOD_V41X_LSC_INIT_SUBBLOCK_Y, 0) |
+ FIELD_PREP(IFE_MOD_V41X_LSC_NUM_SUBBLOCKS, 1);
+ writel_relaxed(value, base + IFE_MOD_V41X_LSC_MODULE_CFG);
+}
+
+static void mod_v41x_lsc_disable(struct ife_pp_ctx *ctx)
+{
+ void __iomem *base = ctx->base + IFE_MOD_V41X_LSC_OFFSET;
+ u32 value;
+
+ value = readl_relaxed(base + IFE_MOD_V41X_LSC_MODULE_CFG);
+ value &= ~IFE_MOD_V41X_LSC_CFG_EN;
+ writel_relaxed(value, base + IFE_MOD_V41X_LSC_MODULE_CFG);
+}
+
+static void mod_v41x_lsc_config(struct ife_pp_ctx *ctx,
+ const struct camss_ife_params_lsc *cfg)
+{
+ void __iomem *base = ctx->base + IFE_MOD_V41X_LSC_OFFSET;
+ u32 subblock_w, subblock_h;
+ u32 num_h = cfg->num_hor_blocks;
+ u32 num_v = cfg->num_ver_blocks;
+ u32 value;
+
+ value = FIELD_PREP(IFE_MOD_V41X_LSC_NUM_BLOCKS_H, num_h - 2) |
+ FIELD_PREP(IFE_MOD_V41X_LSC_NUM_BLOCKS_V, num_v - 2);
+ writel_relaxed(value, base + IFE_MOD_V41X_LSC_LSC_0_CFG);
+
+ value = FIELD_PREP(IFE_MOD_V41X_LSC_BLOCK_WIDTH, (ctx->in_width / num_h) - 1) |
+ FIELD_PREP(IFE_MOD_V41X_LSC_BLOCK_HEIGHT, (ctx->in_height / num_v) - 1);
+ writel_relaxed(value, base + IFE_MOD_V41X_LSC_LSC_1_CFG);
+
+ subblock_w = (ctx->in_width / 4) / (num_h / 2);
+ value = subblock_w |
+ FIELD_PREP(IFE_MOD_V41X_LSC_INV_SUBBLOCK_WIDTH,
+ (1 << IFE_MOD_V41X_LSC_INV_SUBBLOCK_Q20) / (subblock_w + 1));
+ writel_relaxed(value, base + IFE_MOD_V41X_LSC_LSC_2_CFG);
+
+ subblock_h = (ctx->in_height / 4) / (num_v / 2);
+ value = subblock_h |
+ FIELD_PREP(IFE_MOD_V41X_LSC_INV_SUBBLOCK_HEIGHT,
+ (1 << IFE_MOD_V41X_LSC_INV_SUBBLOCK_Q20) / (subblock_h + 1));
+ writel_relaxed(value, base + IFE_MOD_V41X_LSC_LSC_3_CFG);
+
+ mod_v41x_dmi_write(base, IFE_MOD_V41X_LSC_LUT_SEL_RED,
+ cfg->table_r, CAMSS_IFE_MAX_LSC_BLOCKS);
+ mod_v41x_dmi_write(base, IFE_MOD_V41X_LSC_LUT_SEL_BLUE,
+ cfg->table_b, CAMSS_IFE_MAX_LSC_BLOCKS);
+
+ /*
+ * The last table's auto-incremented DMI_DATA writes only land in LUT
+ * RAM once DMI_LUT_CFG.LUT_SEL changes value again - without this,
+ * the Blue LUT silently never commits.
+ */
+ writel_relaxed(IFE_MOD_V41X_LSC_LUT_SEL_NONE, base + IFE_MOD_V41X_DMI_LUT_CFG);
+}
+
+static int mod_v41x_lsc_validate(const struct camss_ife_params_lsc *cfg)
+{
+ u32 num_h = cfg->num_hor_blocks;
+ u32 num_v = cfg->num_ver_blocks;
+
+ if (num_h < 2 || num_h > 17 || num_v < 2 || num_v > 13)
+ return -EINVAL;
+
+ return 0;
+}
+
+static void mod_v41x_lsc_apply(struct ife_pp_ctx *ctx,
+ const struct camss_ife_params_lsc *cfg, bool enable)
+{
+ if (enable) {
+ mod_v41x_lsc_config(ctx, cfg);
+ mod_v41x_lsc_enable(ctx);
+ } else {
+ mod_v41x_lsc_disable(ctx);
+ }
+}
+
static void mod_v41x_awb_bg_config(struct ife_pp_ctx *ctx,
const struct camss_pp_cfg *cfg)
{
@@ -433,14 +554,35 @@ static void mod_v41x_params_apply_ccm(void *priv, const union camss_ife_params_b
mod_v41x_ccm_apply(ctx, &block->color_correct, enable);
}
+static void mod_v41x_params_apply_lsc(void *priv, const union camss_ife_params_block *block)
+{
+ struct ife_pp_ctx *ctx = priv;
+ bool enable = block->header.flags & V4L2_ISP_PARAMS_FL_BLOCK_ENABLE;
+ int ret;
+
+ if (enable) {
+ ret = mod_v41x_lsc_validate(&block->lsc);
+ if (ret) {
+ dev_dbg(ctx->dev, "LSC validate failed: %d\n", ret);
+ return;
+ }
+ }
+
+ mod_v41x_lsc_apply(ctx, &block->lsc, enable);
+}
+
static const struct v4l2_isp_params_block_type_info mod_v41x_params_type_info[] = {
[CAMSS_IFE_PARAMS_COLOR_CORRECT] = {
.size = sizeof(struct camss_ife_params_color_correct),
},
+ [CAMSS_IFE_PARAMS_LSC] = {
+ .size = sizeof(struct camss_ife_params_lsc),
+ },
};
static const camss_ife_params_handler_fn mod_v41x_params_handlers[] = {
[CAMSS_IFE_PARAMS_COLOR_CORRECT] = mod_v41x_params_apply_ccm,
+ [CAMSS_IFE_PARAMS_LSC] = mod_v41x_params_apply_lsc,
};
static_assert(ARRAY_SIZE(mod_v41x_params_handlers) ==
--
2.34.1