[PATCH 28/45] media: qcom: camss: sa8775p: add IFE scaler v2.3x
From: Gjorgji . Rosikopulos . gjorgji . rosikopulos
Date: Tue Oct 06 2026 - 18:47:10 EST
From: Atanas Filipov <atanas.filipov@xxxxxxxxxxxxxxxx>
Add the scaler helpers used by the IFE v4.1x pixel outputs.
Signed-off-by: Atanas Filipov <atanas.filipov@xxxxxxxxxxxxxxxx>
---
drivers/media/platform/qcom/camss/Makefile | 1 +
.../qcom/camss/camss-ife-scale-v23x.c | 203 ++++++++++++++++++
.../qcom/camss/camss-ife-scale-v23x.h | 19 ++
3 files changed, 223 insertions(+)
create mode 100644 drivers/media/platform/qcom/camss/camss-ife-scale-v23x.c
create mode 100644 drivers/media/platform/qcom/camss/camss-ife-scale-v23x.h
diff --git a/drivers/media/platform/qcom/camss/Makefile b/drivers/media/platform/qcom/camss/Makefile
index 2251b57ba38b..e28b7477dab1 100644
--- a/drivers/media/platform/qcom/camss/Makefile
+++ b/drivers/media/platform/qcom/camss/Makefile
@@ -22,6 +22,7 @@ qcom-camss-objs += \
camss-ife-mod-v10x.o \
camss-ife-params.o \
camss-ife-scale-v10x.o \
+ camss-ife-scale-v23x.o \
camss-ife-top-v20x.o \
camss-ife-pads.o \
camss-ispif.o \
diff --git a/drivers/media/platform/qcom/camss/camss-ife-scale-v23x.c b/drivers/media/platform/qcom/camss/camss-ife-scale-v23x.c
new file mode 100644
index 000000000000..13ce9cb93bb0
--- /dev/null
+++ b/drivers/media/platform/qcom/camss/camss-ife-scale-v23x.c
@@ -0,0 +1,203 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2026 Qualcomm Innovation Center, Inc. All rights reserved. */
+
+#include <linux/bitfield.h>
+#include <linux/math.h>
+
+#include "camss-ife.h"
+#include "camss-ife-scale-v23x.h"
+
+#define CAMIF_V23X_Q21_FORMAT 21
+
+#define CAMIF_V23X_DS_MN_MODULE_CFG 0x60
+#define CAMIF_V23X_DS_MN_CFG 0x64
+#define CAMIF_V23X_DS_MN_IMAGE_SIZE_CFG 0x68
+#define CAMIF_V23X_DS_MN_H_CFG 0x6c
+#define CAMIF_V23X_DS_MN_V_CFG 0x74
+#define CAMIF_V23X_DS_MN_CROP_LINE_CFG 0x7c
+#define CAMIF_V23X_DS_MN_CROP_PIXEL_CFG 0x80
+
+#define CAMIF_V23X_DS_MN_MODULE_CFG_CROP_EN BIT(8)
+#define CAMIF_V23X_DS_MN_MODULE_CFG_EN BIT(0)
+#define CAMIF_V23X_DS_MN_CFG_V_SCALE_EN BIT(10)
+#define CAMIF_V23X_DS_MN_CFG_H_SCALE_EN BIT(9)
+#define CAMIF_V23X_DS_MN_IMAGE_SIZE_WIDTH GENMASK(29, 16)
+#define CAMIF_V23X_DS_MN_IMAGE_SIZE_HEIGHT GENMASK(13, 0)
+#define CAMIF_V23X_DS_MN_CFG_INTERP_RES GENMASK(31, 30)
+#define CAMIF_V23X_DS_MN_CROP_FIRST GENMASK(29, 16)
+#define CAMIF_V23X_DS_MN_CROP_LAST GENMASK(13, 0)
+
+#define CAMIF_V23X_INTERP_RES_0 0
+#define CAMIF_V23X_INTERP_RES_1 1
+#define CAMIF_V23X_INTERP_RES_2 2
+#define CAMIF_V23X_INTERP_RES_3 3
+
+#define CAMIF_V23X_POST_MN_MODULE_CFG 0x60
+#define CAMIF_V23X_POST_MN_CROP_LINE_CFG 0x68
+#define CAMIF_V23X_POST_MN_CROP_PIXEL_CFG 0x6c
+#define CAMIF_V23X_POST_MN_CH0_CLAMP_MIN 0x70
+#define CAMIF_V23X_POST_MN_CH0_ROUND_CFG 0x74
+#define CAMIF_V23X_POST_MN_CH1_CLAMP_MIN 0x78
+#define CAMIF_V23X_POST_MN_CH1_ROUND_CFG 0x7c
+#define CAMIF_V23X_POST_MN_CH2_CLAMP_MIN 0x80
+#define CAMIF_V23X_POST_MN_CH2_ROUND_CFG 0x84
+#define CAMIF_V23X_POST_MN_CH0_CLAMP_MAX 0x88
+#define CAMIF_V23X_POST_MN_CH1_CLAMP_MAX 0x8c
+#define CAMIF_V23X_POST_MN_CH2_CLAMP_MAX 0x90
+
+#define CAMIF_V23X_POST_MN_EN BIT(0)
+
+static u32 camif_v23x_calc_ds_q2(u32 src_size, u32 dst_size)
+{
+ if (src_size > 64 * dst_size)
+ return CAMIF_V23X_INTERP_RES_0;
+ else if (src_size > 32 * dst_size)
+ return CAMIF_V23X_INTERP_RES_1;
+ else if (src_size > 16 * dst_size)
+ return CAMIF_V23X_INTERP_RES_2;
+
+ return CAMIF_V23X_INTERP_RES_3;
+}
+
+static u32 camif_v23x_calc_ds_q21(u32 src_size, u32 dst_size)
+{
+ u64 rq21 = (u64)src_size << CAMIF_V23X_Q21_FORMAT;
+
+ return DIV_ROUND_CLOSEST_ULL(rq21, dst_size);
+}
+
+static void camif_v23x_crop_config(void __iomem *mod_base,
+ const struct camss_ife_pipeline_cfg *cfg)
+{
+ u32 rval;
+
+ rval = FIELD_PREP(CAMIF_V23X_DS_MN_CROP_FIRST, cfg->ds_mn_crop->left) |
+ FIELD_PREP(CAMIF_V23X_DS_MN_CROP_LAST, cfg->ds_mn_crop->width - 1);
+ writel_relaxed(rval, mod_base + CAMIF_V23X_DS_MN_CROP_PIXEL_CFG);
+
+ rval = FIELD_PREP(CAMIF_V23X_DS_MN_CROP_FIRST, cfg->ds_mn_crop->top) |
+ FIELD_PREP(CAMIF_V23X_DS_MN_CROP_LAST, cfg->ds_mn_crop->height - 1);
+ writel_relaxed(rval, mod_base + CAMIF_V23X_DS_MN_CROP_LINE_CFG);
+}
+
+void camif_v23x_scale_config(void __iomem *mod_base,
+ const struct camss_ife_pipeline_cfg *cfg,
+ const struct camss_ife_wm_config *wm_cfg)
+{
+ u32 height;
+ u32 width;
+ u32 h_q21;
+ u32 v_q21;
+ u32 h_q2;
+ u32 v_q2;
+ u32 rval;
+
+ if (cfg->ds_mn_crop) {
+ camif_v23x_crop_config(mod_base, cfg);
+ width = cfg->ds_mn_crop->width;
+ height = cfg->ds_mn_crop->height;
+
+ rval = readl_relaxed(mod_base + CAMIF_V23X_DS_MN_MODULE_CFG);
+ rval |= CAMIF_V23X_DS_MN_MODULE_CFG_CROP_EN;
+ writel_relaxed(rval, mod_base + CAMIF_V23X_DS_MN_MODULE_CFG);
+ } else {
+ width = cfg->in_width;
+ height = cfg->in_height;
+
+ /*
+ * Vertical scaling misbehaves unless the crop registers are
+ * programmed to the full frame, even with crop disabled.
+ */
+ rval = FIELD_PREP(CAMIF_V23X_DS_MN_CROP_LAST, width - 1);
+ writel_relaxed(rval, mod_base + CAMIF_V23X_DS_MN_CROP_PIXEL_CFG);
+
+ rval = FIELD_PREP(CAMIF_V23X_DS_MN_CROP_LAST, height - 1);
+ writel_relaxed(rval, mod_base + CAMIF_V23X_DS_MN_CROP_LINE_CFG);
+
+ rval = readl_relaxed(mod_base + CAMIF_V23X_DS_MN_MODULE_CFG);
+ rval &= ~CAMIF_V23X_DS_MN_MODULE_CFG_CROP_EN;
+ writel_relaxed(rval, mod_base + CAMIF_V23X_DS_MN_MODULE_CFG);
+ }
+
+ rval = FIELD_PREP(CAMIF_V23X_DS_MN_IMAGE_SIZE_WIDTH, width - 1) |
+ FIELD_PREP(CAMIF_V23X_DS_MN_IMAGE_SIZE_HEIGHT, height - 1);
+ writel_relaxed(rval, mod_base + CAMIF_V23X_DS_MN_IMAGE_SIZE_CFG);
+
+ rval = readl_relaxed(mod_base + CAMIF_V23X_DS_MN_CFG);
+ if (wm_cfg->width < width) {
+ h_q2 = camif_v23x_calc_ds_q2(width, wm_cfg->width);
+ h_q21 = camif_v23x_calc_ds_q21(width, wm_cfg->width);
+
+ writel_relaxed(FIELD_PREP(CAMIF_V23X_DS_MN_CFG_INTERP_RES, h_q2) | h_q21,
+ mod_base + CAMIF_V23X_DS_MN_H_CFG);
+ rval |= CAMIF_V23X_DS_MN_CFG_H_SCALE_EN;
+ } else {
+ rval &= ~CAMIF_V23X_DS_MN_CFG_H_SCALE_EN;
+ }
+ writel_relaxed(rval, mod_base + CAMIF_V23X_DS_MN_CFG);
+
+ rval = readl_relaxed(mod_base + CAMIF_V23X_DS_MN_CFG);
+ if (wm_cfg->height < height) {
+ v_q2 = camif_v23x_calc_ds_q2(height, wm_cfg->height);
+ v_q21 = camif_v23x_calc_ds_q21(height, wm_cfg->height);
+
+ writel_relaxed(FIELD_PREP(CAMIF_V23X_DS_MN_CFG_INTERP_RES, v_q2) | v_q21,
+ mod_base + CAMIF_V23X_DS_MN_V_CFG);
+ rval |= CAMIF_V23X_DS_MN_CFG_V_SCALE_EN;
+ } else {
+ rval &= ~CAMIF_V23X_DS_MN_CFG_V_SCALE_EN;
+ }
+ writel_relaxed(rval, mod_base + CAMIF_V23X_DS_MN_CFG);
+}
+
+void camif_v23x_scale_enable(void __iomem *mod_base)
+{
+ u32 rval;
+
+ rval = readl_relaxed(mod_base + CAMIF_V23X_DS_MN_MODULE_CFG);
+ rval |= CAMIF_V23X_DS_MN_MODULE_CFG_EN;
+ writel_relaxed(rval, mod_base + CAMIF_V23X_DS_MN_MODULE_CFG);
+}
+
+void camif_v23x_scale_disable(void __iomem *mod_base)
+{
+ u32 rval;
+
+ rval = readl_relaxed(mod_base + CAMIF_V23X_DS_MN_MODULE_CFG);
+ rval &= ~CAMIF_V23X_DS_MN_MODULE_CFG_EN;
+ writel_relaxed(rval, mod_base + CAMIF_V23X_DS_MN_MODULE_CFG);
+}
+
+void camif_v23x_post_mn_config(void __iomem *base,
+ const struct camss_ife_wm_config *cfg,
+ bool chroma)
+{
+ writel_relaxed(cfg->height - 1, base + CAMIF_V23X_POST_MN_CROP_LINE_CFG);
+ writel_relaxed(cfg->width - 1, base + CAMIF_V23X_POST_MN_CROP_PIXEL_CFG);
+ writel_relaxed(0, base + CAMIF_V23X_POST_MN_CH0_CLAMP_MIN);
+ writel_relaxed(1023, base + CAMIF_V23X_POST_MN_CH0_CLAMP_MAX);
+ writel_relaxed(4 << 3, base + CAMIF_V23X_POST_MN_CH0_ROUND_CFG);
+
+ if (chroma) {
+ writel_relaxed(0, base + CAMIF_V23X_POST_MN_CH1_CLAMP_MIN);
+ writel_relaxed(1023, base + CAMIF_V23X_POST_MN_CH1_CLAMP_MAX);
+ writel_relaxed(4 << 3, base + CAMIF_V23X_POST_MN_CH1_ROUND_CFG);
+ writel_relaxed(0, base + CAMIF_V23X_POST_MN_CH2_CLAMP_MIN);
+ writel_relaxed(1023, base + CAMIF_V23X_POST_MN_CH2_CLAMP_MAX);
+ writel_relaxed(4 << 3, base + CAMIF_V23X_POST_MN_CH2_ROUND_CFG);
+ }
+}
+
+void camif_v23x_post_mn_enable(void __iomem *base)
+{
+ writel_relaxed(0, base + CAMIF_V23X_POST_MN_MODULE_CFG);
+}
+
+void camif_v23x_post_mn_disable(void __iomem *base)
+{
+ u32 rval;
+
+ rval = readl_relaxed(base + CAMIF_V23X_POST_MN_MODULE_CFG);
+ rval &= ~CAMIF_V23X_POST_MN_EN;
+ writel_relaxed(rval, base + CAMIF_V23X_POST_MN_MODULE_CFG);
+}
diff --git a/drivers/media/platform/qcom/camss/camss-ife-scale-v23x.h b/drivers/media/platform/qcom/camss/camss-ife-scale-v23x.h
new file mode 100644
index 000000000000..d3ca6420a5c7
--- /dev/null
+++ b/drivers/media/platform/qcom/camss/camss-ife-scale-v23x.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2026 Qualcomm Innovation Center, Inc. All rights reserved. */
+
+#ifndef _CAMSS_IFE_SCALE_V23X_H_
+#define _CAMSS_IFE_SCALE_V23X_H_
+
+void camif_v23x_scale_config(void __iomem *mod_base,
+ const struct camss_ife_pipeline_cfg *cfg,
+ const struct camss_ife_wm_config *wm_cfg);
+void camif_v23x_scale_enable(void __iomem *mod_base);
+void camif_v23x_scale_disable(void __iomem *mod_base);
+
+void camif_v23x_post_mn_config(void __iomem *base,
+ const struct camss_ife_wm_config *cfg,
+ bool chroma);
+void camif_v23x_post_mn_enable(void __iomem *base);
+void camif_v23x_post_mn_disable(void __iomem *base);
+
+#endif /* _CAMSS_IFE_SCALE_V23X_H_ */
--
2.34.1