[PATCH RESEND 08/45] media: qcom: camss: sm8250: add IFE module engine v10x
From: Gjorgji . Rosikopulos . gjorgji . rosikopulos
Date: Wed Oct 07 2026 - 05:05:16 EST
From: Atanas Filipov <atanas.filipov@xxxxxxxxxxxxxxxx>
Add the demosaic, color transform and AWB statistics modules used by
the SM8250 IFE pixel path.
Signed-off-by: Atanas Filipov <atanas.filipov@xxxxxxxxxxxxxxxx>
---
drivers/media/platform/qcom/camss/Makefile | 1 +
.../platform/qcom/camss/camss-ife-mod-v10x.c | 263 ++++++++++++++++++
drivers/media/platform/qcom/camss/camss-ife.h | 1 +
3 files changed, 265 insertions(+)
create mode 100644 drivers/media/platform/qcom/camss/camss-ife-mod-v10x.c
diff --git a/drivers/media/platform/qcom/camss/Makefile b/drivers/media/platform/qcom/camss/Makefile
index dc0f307894d8..92f1938a693d 100644
--- a/drivers/media/platform/qcom/camss/Makefile
+++ b/drivers/media/platform/qcom/camss/Makefile
@@ -16,6 +16,7 @@ qcom-camss-objs += \
camss-format.o \
camss-ife-bus-v20x.o \
camss-ife-camif-v10x.o \
+ camss-ife-mod-v10x.o \
camss-ife-scale-v10x.o \
camss-ispif.o \
camss-tpg.o \
diff --git a/drivers/media/platform/qcom/camss/camss-ife-mod-v10x.c b/drivers/media/platform/qcom/camss/camss-ife-mod-v10x.c
new file mode 100644
index 000000000000..ebc97203595f
--- /dev/null
+++ b/drivers/media/platform/qcom/camss/camss-ife-mod-v10x.c
@@ -0,0 +1,263 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2023 Qualcomm Technologies, Inc.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/io.h>
+
+#include "camss-ife.h"
+
+/* Module base offsets and register offsets */
+#define IFE_MOD_V10X_DEMOSAIC_OFFSET (0x0ACB7800 - 0x0ACB4000)
+#define IFE_MOD_V10X_XFORM_OFFSET (0x0ACB8000 - 0x0ACB4000)
+#define IFE_MOD_V10X_AWB_BG_OFFSET 0x8400
+
+/* Demosaic registers */
+#define IFE_MOD_V10X_DEM_MODULE_CFG 0x00000060
+#define IFE_MOD_V10X_DEM_WB_GAIN_CFG_0 0x00000068
+#define IFE_MOD_V10X_DEM_WB_GAIN_CFG_1 0x0000006c
+
+/* Color transform register */
+#define IFE_MOD_V10X_XFORM_MODULE_CFG 0x00000060
+
+/* AWB-BG registers */
+#define IFE_MOD_V10X_AWB_BG_MODULE_CFG 0x00000060
+#define IFE_MOD_V10X_AWB_BG_HORZ_RGN_CFG_0 0x0000006c
+#define IFE_MOD_V10X_AWB_BG_HORZ_RGN_CFG_1 0x00000070
+#define IFE_MOD_V10X_AWB_BG_VERT_RGN_CFG_0 0x00000074
+#define IFE_MOD_V10X_AWB_BG_VERT_RGN_CFG_1 0x00000078
+#define IFE_MOD_V10X_AWB_BG_HI_THRESH_CFG_0 0x00000080
+#define IFE_MOD_V10X_AWB_BG_HI_THRESH_CFG_1 0x00000084
+#define IFE_MOD_V10X_AWB_BG_LO_THRESH_CFG_0 0x00000088
+#define IFE_MOD_V10X_AWB_BG_LO_THRESH_CFG_1 0x0000008c
+
+/* Demosaic bitfields */
+#define IFE_MOD_V10X_DEMOSAIC_CFG_DYN_RB_CLAMP_EN BIT(15)
+#define IFE_MOD_V10X_DEMOSAIC_CFG_DYN_G_CLAMP_EN BIT(14)
+#define IFE_MOD_V10X_DEMOSAIC_CFG_EN BIT(0)
+#define IFE_MOD_V10X_DEMOSAIC_WB_GAIN_CFG0_B_GAIN GENMASK(30, 16)
+#define IFE_MOD_V10X_DEMOSAIC_WB_GAIN_CFG0_G_GAIN GENMASK(14, 0)
+#define IFE_MOD_V10X_DEMOSAIC_WB_GAIN_CFG1_R_GAIN GENMASK(14, 0)
+
+/* Color transform bitfield */
+#define IFE_MOD_V10X_COLOR_XFORM_CFG_EN BIT(0)
+
+/* AWB-BG bitfields */
+#define IFE_MOD_V10X_AWB_BG_MODULE_EN BIT(0)
+#define IFE_MOD_V10X_AWB_BG_SAT_STATS_EN BIT(8)
+#define IFE_MOD_V10X_AWB_BG_HORZ_CFG0_H_NUM GENMASK(23, 16)
+#define IFE_MOD_V10X_AWB_BG_HORZ_CFG1_WIDTH GENMASK(8, 0)
+#define IFE_MOD_V10X_AWB_BG_VERT_CFG0_V_NUM GENMASK(22, 16)
+#define IFE_MOD_V10X_AWB_BG_VERT_CFG1_HEIGHT GENMASK(9, 0)
+#define IFE_MOD_V10X_AWB_BG_THRESH_CFG0_GR GENMASK(29, 16)
+#define IFE_MOD_V10X_AWB_BG_THRESH_CFG0_R GENMASK(13, 0)
+#define IFE_MOD_V10X_AWB_BG_THRESH_CFG1_GB GENMASK(29, 16)
+#define IFE_MOD_V10X_AWB_BG_THRESH_CFG1_B GENMASK(13, 0)
+
+/* AWB-BG default grid: 64 horizontal x 48 vertical regions */
+#define IFE_MOD_V10X_AWB_BG_MAX_H_NUM 64
+#define IFE_MOD_V10X_AWB_BG_MAX_V_NUM 48
+
+/* Default saturation threshold applied to all channels */
+#define IFE_MOD_V10X_AWB_BG_THRESHOLD 0x3C3F
+
+/* Default WB gains applied at config time (U15Q10 format) */
+#define IFE_MOD_V10X_DEMOSAIC_G_GAIN_DEF 1024 /* 1.0 U15Q10 */
+#define IFE_MOD_V10X_DEMOSAIC_B_GAIN_DEF 1664 /* 1.625 U15Q10 */
+#define IFE_MOD_V10X_DEMOSAIC_R_GAIN_DEF 2176 /* 2.125 U15Q10 */
+
+/*
+ * Default RGB-to-YUV color transform coefficients.
+ * Each entry is a { value, register-offset } pair relative to xform base.
+ */
+static const struct { u32 val; u32 offs; } mod_v10x_xform_defaults[] = {
+ { 0x00750259, 0x00000068 },
+ { 0x00000132, 0x0000006c },
+ { 0x03ff0000, 0x00000074 },
+ { 0x01fe1eae, 0x00000078 },
+ { 0x00001f54, 0x0000007c },
+ { 0x02000000, 0x00000080 },
+ { 0x03ff0000, 0x00000084 },
+ { 0x1fad1e55, 0x00000088 },
+ { 0x000001fe, 0x0000008c },
+ { 0x02000000, 0x00000090 },
+ { 0x03ff0000, 0x00000094 },
+};
+
+static void mod_v10x_demosaic_config(void __iomem *top)
+{
+ void __iomem *base = top + IFE_MOD_V10X_DEMOSAIC_OFFSET;
+ u32 rval;
+
+ rval = FIELD_PREP(IFE_MOD_V10X_DEMOSAIC_WB_GAIN_CFG0_B_GAIN,
+ IFE_MOD_V10X_DEMOSAIC_B_GAIN_DEF) |
+ FIELD_PREP(IFE_MOD_V10X_DEMOSAIC_WB_GAIN_CFG0_G_GAIN,
+ IFE_MOD_V10X_DEMOSAIC_G_GAIN_DEF);
+ writel_relaxed(rval, base + IFE_MOD_V10X_DEM_WB_GAIN_CFG_0);
+
+ rval = FIELD_PREP(IFE_MOD_V10X_DEMOSAIC_WB_GAIN_CFG1_R_GAIN,
+ IFE_MOD_V10X_DEMOSAIC_R_GAIN_DEF);
+ writel_relaxed(rval, base + IFE_MOD_V10X_DEM_WB_GAIN_CFG_1);
+}
+
+static void mod_v10x_demosaic_enable(void __iomem *top)
+{
+ void __iomem *base = top + IFE_MOD_V10X_DEMOSAIC_OFFSET;
+ u32 rval;
+
+ rval = readl_relaxed(base + IFE_MOD_V10X_DEM_MODULE_CFG);
+ rval |= IFE_MOD_V10X_DEMOSAIC_CFG_DYN_G_CLAMP_EN |
+ IFE_MOD_V10X_DEMOSAIC_CFG_DYN_RB_CLAMP_EN |
+ IFE_MOD_V10X_DEMOSAIC_CFG_EN;
+ writel_relaxed(rval, base + IFE_MOD_V10X_DEM_MODULE_CFG);
+}
+
+static void mod_v10x_demosaic_disable(void __iomem *top)
+{
+ void __iomem *base = top + IFE_MOD_V10X_DEMOSAIC_OFFSET;
+ u32 rval;
+
+ rval = readl_relaxed(base + IFE_MOD_V10X_DEM_MODULE_CFG);
+ rval &= ~(IFE_MOD_V10X_DEMOSAIC_CFG_DYN_RB_CLAMP_EN |
+ IFE_MOD_V10X_DEMOSAIC_CFG_DYN_G_CLAMP_EN |
+ IFE_MOD_V10X_DEMOSAIC_CFG_EN);
+ writel_relaxed(rval, base + IFE_MOD_V10X_DEM_MODULE_CFG);
+}
+
+static void mod_v10x_xform_config(void __iomem *top)
+{
+ void __iomem *base = top + IFE_MOD_V10X_XFORM_OFFSET;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mod_v10x_xform_defaults); i++)
+ writel_relaxed(mod_v10x_xform_defaults[i].val,
+ base + mod_v10x_xform_defaults[i].offs);
+}
+
+static void mod_v10x_xform_enable(void __iomem *top)
+{
+ void __iomem *base = top + IFE_MOD_V10X_XFORM_OFFSET;
+ u32 rval;
+
+ rval = readl_relaxed(base + IFE_MOD_V10X_XFORM_MODULE_CFG);
+ rval |= IFE_MOD_V10X_COLOR_XFORM_CFG_EN;
+ writel_relaxed(rval, base + IFE_MOD_V10X_XFORM_MODULE_CFG);
+}
+
+static void mod_v10x_xform_disable(void __iomem *top)
+{
+ void __iomem *base = top + IFE_MOD_V10X_XFORM_OFFSET;
+ u32 rval;
+
+ rval = readl_relaxed(base + IFE_MOD_V10X_XFORM_MODULE_CFG);
+ rval &= ~IFE_MOD_V10X_COLOR_XFORM_CFG_EN;
+ writel_relaxed(rval, base + IFE_MOD_V10X_XFORM_MODULE_CFG);
+}
+
+static void mod_v10x_awb_bg_config(void __iomem *base,
+ const struct camss_pp_cfg *cfg)
+{
+ void __iomem *mod_base = base + IFE_MOD_V10X_AWB_BG_OFFSET;
+ u32 width = (cfg->width / IFE_MOD_V10X_AWB_BG_MAX_H_NUM) & ~3u;
+ u32 height = (cfg->height / IFE_MOD_V10X_AWB_BG_MAX_V_NUM) & ~3u;
+ u32 val;
+
+ /* Horizontal: 64 regions, offset 0 */
+ val = FIELD_PREP(IFE_MOD_V10X_AWB_BG_HORZ_CFG0_H_NUM, IFE_MOD_V10X_AWB_BG_MAX_H_NUM - 1);
+ writel_relaxed(val, mod_base + IFE_MOD_V10X_AWB_BG_HORZ_RGN_CFG_0);
+
+ val = FIELD_PREP(IFE_MOD_V10X_AWB_BG_HORZ_CFG1_WIDTH, width - 1);
+ writel_relaxed(val, mod_base + IFE_MOD_V10X_AWB_BG_HORZ_RGN_CFG_1);
+
+ /* Vertical: 48 regions, offset 0 */
+ val = FIELD_PREP(IFE_MOD_V10X_AWB_BG_VERT_CFG0_V_NUM, IFE_MOD_V10X_AWB_BG_MAX_V_NUM - 1);
+ writel_relaxed(val, mod_base + IFE_MOD_V10X_AWB_BG_VERT_RGN_CFG_0);
+
+ val = FIELD_PREP(IFE_MOD_V10X_AWB_BG_VERT_CFG1_HEIGHT, height - 1);
+ writel_relaxed(val, mod_base + IFE_MOD_V10X_AWB_BG_VERT_RGN_CFG_1);
+
+ /* High thresholds: default saturation level for all channels */
+ val = FIELD_PREP(IFE_MOD_V10X_AWB_BG_THRESH_CFG0_GR, IFE_MOD_V10X_AWB_BG_THRESHOLD) |
+ FIELD_PREP(IFE_MOD_V10X_AWB_BG_THRESH_CFG0_R, IFE_MOD_V10X_AWB_BG_THRESHOLD);
+ writel_relaxed(val, mod_base + IFE_MOD_V10X_AWB_BG_HI_THRESH_CFG_0);
+
+ val = FIELD_PREP(IFE_MOD_V10X_AWB_BG_THRESH_CFG1_GB, IFE_MOD_V10X_AWB_BG_THRESHOLD) |
+ FIELD_PREP(IFE_MOD_V10X_AWB_BG_THRESH_CFG1_B, IFE_MOD_V10X_AWB_BG_THRESHOLD);
+ writel_relaxed(val, mod_base + IFE_MOD_V10X_AWB_BG_HI_THRESH_CFG_1);
+
+ /* Low thresholds: 0 (accept all pixels) */
+ writel_relaxed(0, mod_base + IFE_MOD_V10X_AWB_BG_LO_THRESH_CFG_0);
+ writel_relaxed(0, mod_base + IFE_MOD_V10X_AWB_BG_LO_THRESH_CFG_1);
+}
+
+static void mod_v10x_awb_bg_enable(void __iomem *base)
+{
+ void __iomem *mod_base = base + IFE_MOD_V10X_AWB_BG_OFFSET;
+ u32 val;
+
+ val = readl_relaxed(mod_base + IFE_MOD_V10X_AWB_BG_MODULE_CFG);
+ val |= IFE_MOD_V10X_AWB_BG_MODULE_EN | IFE_MOD_V10X_AWB_BG_SAT_STATS_EN;
+ writel_relaxed(val, mod_base + IFE_MOD_V10X_AWB_BG_MODULE_CFG);
+}
+
+static void mod_v10x_awb_bg_disable(void __iomem *base)
+{
+ void __iomem *mod_base = base + IFE_MOD_V10X_AWB_BG_OFFSET;
+ u32 val;
+
+ val = readl_relaxed(mod_base + IFE_MOD_V10X_AWB_BG_MODULE_CFG);
+ val &= ~(IFE_MOD_V10X_AWB_BG_MODULE_EN | IFE_MOD_V10X_AWB_BG_SAT_STATS_EN);
+ writel_relaxed(val, mod_base + IFE_MOD_V10X_AWB_BG_MODULE_CFG);
+}
+
+static void mod_v10x_modules_acquire(struct ife_device *ife,
+ struct ife_pp_ctx *ctx,
+ void __iomem *base)
+{
+ ctx->base = base;
+}
+
+static void mod_v10x_modules_release(struct ife_pp_ctx *ctx)
+{
+ ctx->base = NULL;
+}
+
+static void mod_v10x_modules_config(struct ife_pp_ctx *ctx,
+ const struct camss_pp_cfg *cfg)
+{
+ void __iomem *base = ctx->base;
+
+ ctx->in_width = cfg->width;
+ ctx->in_height = cfg->height;
+
+ mod_v10x_demosaic_config(base);
+ mod_v10x_xform_config(base);
+ mod_v10x_awb_bg_config(base, cfg);
+}
+
+static void mod_v10x_modules_enable(struct ife_pp_ctx *ctx)
+{
+ void __iomem *base = ctx->base;
+
+ mod_v10x_demosaic_enable(base);
+ mod_v10x_xform_enable(base);
+ mod_v10x_awb_bg_enable(base);
+}
+
+static void mod_v10x_modules_disable(struct ife_pp_ctx *ctx)
+{
+ void __iomem *base = ctx->base;
+
+ mod_v10x_awb_bg_disable(base);
+ mod_v10x_xform_disable(base);
+ mod_v10x_demosaic_disable(base);
+}
+
+const struct camss_mod_ops ife_mod_v10x_ops = {
+ .acquire = mod_v10x_modules_acquire,
+ .release = mod_v10x_modules_release,
+ .config = mod_v10x_modules_config,
+ .enable = mod_v10x_modules_enable,
+ .disable = mod_v10x_modules_disable,
+};
diff --git a/drivers/media/platform/qcom/camss/camss-ife.h b/drivers/media/platform/qcom/camss/camss-ife.h
index 48701cb67e1e..233b0292c107 100644
--- a/drivers/media/platform/qcom/camss/camss-ife.h
+++ b/drivers/media/platform/qcom/camss/camss-ife.h
@@ -390,5 +390,6 @@ camss_ife_pipe_for_stream_id(int nstreams,
u32 id);
extern const struct ife_hw_buswr_ops ife_buswr_20x_ops;
+extern const struct camss_mod_ops ife_mod_v10x_ops;
#endif /* CAMSS_IFE_SUB_DEV_H */
--
2.34.1