[PATCH 36/45] media: qcom: camss: sa8775p: add CCM parameters
From: Gjorgji . Rosikopulos . gjorgji . rosikopulos
Date: Tue Oct 06 2026 - 19:16:30 EST
From: Gjorgji Rosikopulos <gjorgji.rosikopulos@xxxxxxxxxxxxxxxx>
Program the color correction matrix, offsets, and Q8 shift from IFE
parameter buffers. Enable or disable the block as requested.
Signed-off-by: Gjorgji Rosikopulos <gjorgji.rosikopulos@xxxxxxxxxxxxxxxx>
Signed-off-by: Atanas Filipov <atanas.filipov@xxxxxxxxxxxxxxxx>
---
.../platform/qcom/camss/camss-ife-mod-v41x.c | 139 ++++++++++++++++++
1 file changed, 139 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 9f4c8ae9667e..0db88534b421 100644
--- a/drivers/media/platform/qcom/camss/camss-ife-mod-v41x.c
+++ b/drivers/media/platform/qcom/camss/camss-ife-mod-v41x.c
@@ -7,8 +7,14 @@
#include <linux/bitfield.h>
#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/qcom-camss-config.h>
+#include <media/v4l2-isp.h>
+
+#include "camss.h"
#include "camss-ife.h"
+#include "camss-ife-params.h"
/* Module base offsets */
#define IFE_MOD_V41X_WB_GAIN_OFFSET 0x4000
@@ -108,6 +114,26 @@
#define IFE_MOD_V41X_AWB_BG_THRESHOLD 0x3ffff
#define IFE_MOD_V41X_AWB_BG_NUM_THRESHOLDS 5
+/* CCM offset, registers and bitfields */
+#define IFE_MOD_V41X_CCM_OFFSET 0x5800
+#define IFE_MOD_V41X_CCM_MODULE_CFG 0x60
+#define IFE_MOD_V41X_CCM_COEFF_A_CFG_0 0x68
+#define IFE_MOD_V41X_CCM_COEFF_A_CFG_1 0x6c
+#define IFE_MOD_V41X_CCM_COEFF_B_CFG_0 0x70
+#define IFE_MOD_V41X_CCM_COEFF_B_CFG_1 0x74
+#define IFE_MOD_V41X_CCM_COEFF_C_CFG_0 0x78
+#define IFE_MOD_V41X_CCM_COEFF_C_CFG_1 0x7c
+#define IFE_MOD_V41X_CCM_OFFSET_K_CFG_0 0x80
+#define IFE_MOD_V41X_CCM_OFFSET_K_CFG_1 0x84
+#define IFE_MOD_V41X_CCM_OFFSET_K_CFG_2 0x88
+#define IFE_MOD_V41X_CCM_SHIFT_M_CFG 0x8c
+#define IFE_MOD_V41X_CCM_CFG_EN BIT(0)
+#define IFE_MOD_V41X_CCM_MATRIX_0 GENMASK(11, 0)
+#define IFE_MOD_V41X_CCM_MATRIX_1 GENMASK(27, 16)
+#define IFE_MOD_V41X_CCM_OFFSET_VALUE GENMASK(31, 17)
+#define IFE_MOD_V41X_CCM_SHIFT_M_PARAM GENMASK(1, 0)
+#define IFE_MOD_V41X_CCM_SHIFT_M_Q8 0x1
+
static void mod_v41x_wb_gain_config(struct ife_pp_ctx *ctx)
{
void __iomem *base = ctx->base + IFE_MOD_V41X_WB_GAIN_OFFSET;
@@ -133,6 +159,81 @@ static void mod_v41x_demosaic_config(struct ife_pp_ctx *ctx)
base + IFE_MOD_V41X_DEMOSAIC_INTERP_CLASSIFIER_CFG_1);
}
+static void mod_v41x_ccm_enable(struct ife_pp_ctx *ctx)
+{
+ void __iomem *base = ctx->base + IFE_MOD_V41X_CCM_OFFSET;
+ u32 value;
+
+ value = readl_relaxed(base + IFE_MOD_V41X_CCM_MODULE_CFG);
+ value |= IFE_MOD_V41X_CCM_CFG_EN;
+ writel_relaxed(value, base + IFE_MOD_V41X_CCM_MODULE_CFG);
+}
+
+static void mod_v41x_ccm_disable(struct ife_pp_ctx *ctx)
+{
+ void __iomem *base = ctx->base + IFE_MOD_V41X_CCM_OFFSET;
+ u32 value;
+
+ value = readl_relaxed(base + IFE_MOD_V41X_CCM_MODULE_CFG);
+ value &= ~IFE_MOD_V41X_CCM_CFG_EN;
+ writel_relaxed(value, base + IFE_MOD_V41X_CCM_MODULE_CFG);
+}
+
+static void mod_v41x_ccm_config(struct ife_pp_ctx *ctx,
+ const struct camss_ife_params_color_correct *cfg)
+{
+ void __iomem *base = ctx->base + IFE_MOD_V41X_CCM_OFFSET;
+ u32 value;
+
+ writel_relaxed(FIELD_PREP(IFE_MOD_V41X_CCM_SHIFT_M_PARAM,
+ IFE_MOD_V41X_CCM_SHIFT_M_Q8),
+ base + IFE_MOD_V41X_CCM_SHIFT_M_CFG);
+
+ /*
+ * HW banks A/B/C each multiply one fixed input channel (A=G_in,
+ * B=B_in, C=R_in), and HW output indices 0/1/2 are also G/B/R, not
+ * R/G/B, same as IFE_MOD_V10X_CCM. The UAPI's coeff[out][in]/
+ * offset[out] are R,G,B-indexed on both axes (0=R,1=G,2=B); remap
+ * accordingly: bank X's slot N takes UAPI row (N+1)%3 (G,B,R output
+ * order) and the column matching that bank's input channel.
+ */
+ value = FIELD_PREP(IFE_MOD_V41X_CCM_MATRIX_0, cfg->coeff[1][1]) |
+ FIELD_PREP(IFE_MOD_V41X_CCM_MATRIX_1, cfg->coeff[2][1]);
+ writel_relaxed(value, base + IFE_MOD_V41X_CCM_COEFF_A_CFG_0);
+ writel_relaxed(FIELD_PREP(IFE_MOD_V41X_CCM_MATRIX_0, cfg->coeff[0][1]),
+ base + IFE_MOD_V41X_CCM_COEFF_A_CFG_1);
+
+ value = FIELD_PREP(IFE_MOD_V41X_CCM_MATRIX_0, cfg->coeff[1][2]) |
+ FIELD_PREP(IFE_MOD_V41X_CCM_MATRIX_1, cfg->coeff[2][2]);
+ writel_relaxed(value, base + IFE_MOD_V41X_CCM_COEFF_B_CFG_0);
+ writel_relaxed(FIELD_PREP(IFE_MOD_V41X_CCM_MATRIX_0, cfg->coeff[0][2]),
+ base + IFE_MOD_V41X_CCM_COEFF_B_CFG_1);
+
+ value = FIELD_PREP(IFE_MOD_V41X_CCM_MATRIX_0, cfg->coeff[1][0]) |
+ FIELD_PREP(IFE_MOD_V41X_CCM_MATRIX_1, cfg->coeff[2][0]);
+ writel_relaxed(value, base + IFE_MOD_V41X_CCM_COEFF_C_CFG_0);
+ writel_relaxed(FIELD_PREP(IFE_MOD_V41X_CCM_MATRIX_0, cfg->coeff[0][0]),
+ base + IFE_MOD_V41X_CCM_COEFF_C_CFG_1);
+
+ writel_relaxed(FIELD_PREP(IFE_MOD_V41X_CCM_OFFSET_VALUE, cfg->offset[1]),
+ base + IFE_MOD_V41X_CCM_OFFSET_K_CFG_0);
+ writel_relaxed(FIELD_PREP(IFE_MOD_V41X_CCM_OFFSET_VALUE, cfg->offset[2]),
+ base + IFE_MOD_V41X_CCM_OFFSET_K_CFG_1);
+ writel_relaxed(FIELD_PREP(IFE_MOD_V41X_CCM_OFFSET_VALUE, cfg->offset[0]),
+ base + IFE_MOD_V41X_CCM_OFFSET_K_CFG_2);
+}
+
+static void mod_v41x_ccm_apply(struct ife_pp_ctx *ctx,
+ const struct camss_ife_params_color_correct *cfg, bool enable)
+{
+ if (enable) {
+ mod_v41x_ccm_config(ctx, cfg);
+ mod_v41x_ccm_enable(ctx);
+ } else {
+ mod_v41x_ccm_disable(ctx);
+ }
+}
+
static void mod_v41x_awb_bg_config(struct ife_pp_ctx *ctx,
const struct camss_pp_cfg *cfg)
{
@@ -226,12 +327,17 @@ static void mod_v41x_xform_write_channel(void __iomem *base, u32 coeff0,
static void mod_v41x_modules_acquire(struct ife_device *ife, struct ife_pp_ctx *ctx,
void __iomem *base)
{
+ ctx->dev = ife->camss->dev;
ctx->base = base;
+ ctx->params_scratch = devm_kzalloc(ctx->dev,
+ v4l2_isp_buffer_size(CAMSS_IFE_PARAMS_MAX_PAYLOAD),
+ GFP_KERNEL);
}
static void mod_v41x_modules_release(struct ife_pp_ctx *ctx)
{
ctx->base = NULL;
+ ctx->params_scratch = NULL;
}
static void mod_v41x_modules_config(struct ife_pp_ctx *ctx,
@@ -319,10 +425,43 @@ static void mod_v41x_modules_disable(struct ife_pp_ctx *ctx)
writel_relaxed(value, base + IFE_MOD_V41X_XFORM_MODULE_CFG);
}
+static void mod_v41x_params_apply_ccm(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;
+
+ mod_v41x_ccm_apply(ctx, &block->color_correct, 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),
+ },
+};
+
+static const camss_ife_params_handler_fn mod_v41x_params_handlers[] = {
+ [CAMSS_IFE_PARAMS_COLOR_CORRECT] = mod_v41x_params_apply_ccm,
+};
+
+static_assert(ARRAY_SIZE(mod_v41x_params_handlers) ==
+ ARRAY_SIZE(mod_v41x_params_type_info));
+
+static int mod_v41x_modules_apply_params(struct ife_pp_ctx *ctx,
+ struct vb2_buffer *vb)
+{
+ return camss_ife_params_apply(ctx->dev, vb, ctx->params_scratch,
+ CAMSS_IFE_PARAMS_MAX_PAYLOAD,
+ mod_v41x_params_type_info,
+ mod_v41x_params_handlers,
+ ARRAY_SIZE(mod_v41x_params_handlers),
+ ctx);
+}
+
const struct camss_mod_ops ife_mod_v41x_ops = {
.acquire = mod_v41x_modules_acquire,
.release = mod_v41x_modules_release,
.config = mod_v41x_modules_config,
.enable = mod_v41x_modules_enable,
.disable = mod_v41x_modules_disable,
+ .apply_params = mod_v41x_modules_apply_params,
};
--
2.34.1