[PATCH 09/45] media: qcom: camss: sm8250: add IFE scaler v10x

From: Gjorgji . Rosikopulos . gjorgji . rosikopulos

Date: Tue Oct 06 2026 - 18:47:47 EST


From: Atanas Filipov <atanas.filipov@xxxxxxxxxxxxxxxx>

Add the M/N downscaler used by the SM8250 IFE pixel path.

Signed-off-by: Atanas Filipov <atanas.filipov@xxxxxxxxxxxxxxxx>
---
drivers/media/platform/qcom/camss/Makefile | 1 +
.../qcom/camss/camss-ife-scale-v10x.c | 148 ++++++++++++++++++
.../qcom/camss/camss-ife-scale-v10x.h | 15 ++
3 files changed, 164 insertions(+)
create mode 100644 drivers/media/platform/qcom/camss/camss-ife-scale-v10x.c
create mode 100644 drivers/media/platform/qcom/camss/camss-ife-scale-v10x.h

diff --git a/drivers/media/platform/qcom/camss/Makefile b/drivers/media/platform/qcom/camss/Makefile
index 2da998444554..dc0f307894d8 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-scale-v10x.o \
camss-ispif.o \
camss-tpg.o \
camss-tpg-gen1.o \
diff --git a/drivers/media/platform/qcom/camss/camss-ife-scale-v10x.c b/drivers/media/platform/qcom/camss/camss-ife-scale-v10x.c
new file mode 100644
index 000000000000..a8939114cb4d
--- /dev/null
+++ b/drivers/media/platform/qcom/camss/camss-ife-scale-v10x.c
@@ -0,0 +1,148 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2022, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/math.h>
+
+#include "camss-ife.h"
+#include "camss-ife-scale-v10x.h"
+
+/* DS M/N scaler register offsets */
+#define CAMIF_V10X_DS_MN_MODULE_CFG 0x00000060
+#define CAMIF_V10X_DS_MN_CFG 0x00000064
+#define CAMIF_V10X_DS_MN_IMAGE_SIZE_CFG 0x00000068
+#define CAMIF_V10X_DS_MN_H_CFG 0x0000006c
+#define CAMIF_V10X_DS_MN_V_CFG 0x00000074
+#define CAMIF_V10X_DS_MN_CROP_LINE_CFG 0x0000007c
+#define CAMIF_V10X_DS_MN_CROP_PIXEL_CFG 0x00000080
+
+#define PP_V10X_Q21_FORMAT (21)
+
+#define PP_V10X_MODULE_DS_MN_CFG_CROP_EN BIT(8)
+#define PP_V10X_MODULE_DS_MN_CFG_EN BIT(0)
+#define PP_V10X_DS_MN_CFG_VERTICAL_SCALE_EN BIT(10)
+#define PP_V10X_DS_MN_CFG_HORIZONTAL_SCALE_EN BIT(9)
+#define PP_V10X_DS_MN_IMAGE_SIZE_CFG_INPUT_WIDTH GENMASK(29, 16)
+#define PP_V10X_DS_MN_IMAGE_SIZE_CFG_INPUT_HEIGHT GENMASK(13, 0)
+#define PP_V10X_DS_MN_CROP_LINE_CFG_FIRST_LINE GENMASK(29, 16)
+#define PP_V10X_DS_MN_CROP_LINE_CFG_LAST_LINE GENMASK(13, 0)
+#define PP_V10X_DS_MN_CROP_PIXEL_CFG_FIRST_PIXEL GENMASK(29, 16)
+#define PP_V10X_DS_MN_CROP_PIXEL_CFG_LAST_PIXEL GENMASK(13, 0)
+
+#define PP_V10X_DS_MN_H_CFG_INTERP_RESO GENMASK(31, 30)
+#define PP_V10X_DS_MN_V_CFG_INTERP_RESO GENMASK(31, 30)
+
+#define INTERP_RES_Q2_0 (0) /* 1/128 <= M/N <= 1/16 */
+#define INTERP_RES_Q2_1 (1) /* 1/16 <= M/N <= 1/8 */
+#define INTERP_RES_Q2_2 (2) /* 1/8 <= M/N <= 1/4 */
+#define INTERP_RES_Q2_3 (3) /* 1/4 <= M/N <= 1 */
+
+static u32 camif_v10x_calc_ds_q2(u32 src_size, u32 dst_size)
+{
+ if ((u64)src_size >= 16 * (u64)dst_size)
+ return INTERP_RES_Q2_0;
+ else if ((u64)src_size >= 8 * (u64)dst_size)
+ return INTERP_RES_Q2_1;
+ else if ((u64)src_size >= 4 * (u64)dst_size)
+ return INTERP_RES_Q2_2;
+
+ return INTERP_RES_Q2_3;
+}
+
+static u32 camif_v10x_calc_ds_q21(u32 src_size, u32 dst_size)
+{
+ u64 rq21;
+
+ rq21 = (u64)src_size << PP_V10X_Q21_FORMAT;
+ do_div(rq21, dst_size);
+
+ return rq21;
+}
+
+static void camif_v10x_crop_config(void __iomem *mod_base, const struct camss_ife_pipeline_cfg *cfg)
+{
+ u32 rval;
+
+ rval = FIELD_PREP(PP_V10X_DS_MN_CROP_PIXEL_CFG_FIRST_PIXEL, cfg->ds_mn_crop->left) |
+ FIELD_PREP(PP_V10X_DS_MN_CROP_PIXEL_CFG_LAST_PIXEL, cfg->ds_mn_crop->width - 1);
+ writel_relaxed(rval, mod_base + CAMIF_V10X_DS_MN_CROP_PIXEL_CFG);
+
+ rval = FIELD_PREP(PP_V10X_DS_MN_CROP_LINE_CFG_FIRST_LINE, cfg->ds_mn_crop->top) |
+ FIELD_PREP(PP_V10X_DS_MN_CROP_LINE_CFG_LAST_LINE, cfg->ds_mn_crop->height - 1);
+ writel_relaxed(rval, mod_base + CAMIF_V10X_DS_MN_CROP_LINE_CFG);
+}
+
+void camif_v10x_scale_config(void __iomem *mod_base, const struct camss_ife_pipeline_cfg *cfg,
+ const struct camss_ife_wm_config *wm_cfg)
+{
+ u32 rval, h_q21, v_q21, h_q2, v_q2, width, height;
+
+ if (cfg->ds_mn_crop) {
+ camif_v10x_crop_config(mod_base, cfg);
+ width = cfg->ds_mn_crop->width;
+ height = cfg->ds_mn_crop->height;
+
+ rval = readl_relaxed(mod_base + CAMIF_V10X_DS_MN_MODULE_CFG);
+ rval |= PP_V10X_MODULE_DS_MN_CFG_CROP_EN;
+ writel_relaxed(rval, mod_base + CAMIF_V10X_DS_MN_MODULE_CFG);
+
+ } else {
+ width = cfg->in_width;
+ height = cfg->in_height;
+
+ rval = readl_relaxed(mod_base + CAMIF_V10X_DS_MN_MODULE_CFG);
+ rval &= ~PP_V10X_MODULE_DS_MN_CFG_CROP_EN;
+ writel_relaxed(rval, mod_base + CAMIF_V10X_DS_MN_MODULE_CFG);
+ }
+
+ rval = FIELD_PREP(PP_V10X_DS_MN_IMAGE_SIZE_CFG_INPUT_WIDTH, width - 1) |
+ FIELD_PREP(PP_V10X_DS_MN_IMAGE_SIZE_CFG_INPUT_HEIGHT, height - 1);
+ writel_relaxed(rval, mod_base + CAMIF_V10X_DS_MN_IMAGE_SIZE_CFG);
+
+ h_q2 = camif_v10x_calc_ds_q2(width, wm_cfg->width);
+ h_q21 = camif_v10x_calc_ds_q21(width, wm_cfg->width);
+ rval = FIELD_PREP(PP_V10X_DS_MN_H_CFG_INTERP_RESO, h_q2) | h_q21;
+ writel_relaxed(rval, mod_base + CAMIF_V10X_DS_MN_H_CFG);
+
+ rval = readl_relaxed(mod_base + CAMIF_V10X_DS_MN_CFG);
+ if (wm_cfg->width < width)
+ rval |= PP_V10X_DS_MN_CFG_HORIZONTAL_SCALE_EN;
+ else
+ rval &= ~PP_V10X_DS_MN_CFG_HORIZONTAL_SCALE_EN;
+ writel_relaxed(rval, mod_base + CAMIF_V10X_DS_MN_CFG);
+
+ v_q2 = camif_v10x_calc_ds_q2(height, wm_cfg->height);
+ v_q21 = camif_v10x_calc_ds_q21(height, wm_cfg->height);
+ rval = FIELD_PREP(PP_V10X_DS_MN_V_CFG_INTERP_RESO, v_q2) | v_q21;
+ writel_relaxed(rval, mod_base + CAMIF_V10X_DS_MN_V_CFG);
+
+ rval = readl_relaxed(mod_base + CAMIF_V10X_DS_MN_CFG);
+ if (wm_cfg->height < height)
+ rval |= PP_V10X_DS_MN_CFG_VERTICAL_SCALE_EN;
+ else
+ rval &= ~PP_V10X_DS_MN_CFG_VERTICAL_SCALE_EN;
+ writel_relaxed(rval, mod_base + CAMIF_V10X_DS_MN_CFG);
+}
+
+int camif_v10x_scale_enable(void __iomem *mod_base)
+{
+ u32 rval;
+
+ rval = readl_relaxed(mod_base + CAMIF_V10X_DS_MN_MODULE_CFG);
+ rval |= PP_V10X_MODULE_DS_MN_CFG_EN;
+ writel_relaxed(rval, mod_base + CAMIF_V10X_DS_MN_MODULE_CFG);
+
+ return 0;
+}
+
+int camif_v10x_scale_disable(void __iomem *mod_base)
+{
+ u32 rval;
+
+ rval = readl_relaxed(mod_base + CAMIF_V10X_DS_MN_MODULE_CFG);
+ rval &= ~PP_V10X_MODULE_DS_MN_CFG_EN;
+ writel_relaxed(rval, mod_base + CAMIF_V10X_DS_MN_MODULE_CFG);
+
+ return 0;
+}
diff --git a/drivers/media/platform/qcom/camss/camss-ife-scale-v10x.h b/drivers/media/platform/qcom/camss/camss-ife-scale-v10x.h
new file mode 100644
index 000000000000..cb548e69f321
--- /dev/null
+++ b/drivers/media/platform/qcom/camss/camss-ife-scale-v10x.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2022, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef _CAMSS_PP_V10X_SCALE_H_
+#define _CAMSS_PP_V10X_SCALE_H_
+
+void camif_v10x_scale_config(void __iomem *mod_base, const struct camss_ife_pipeline_cfg *cfg,
+ const struct camss_ife_wm_config *wm_cfg);
+
+int camif_v10x_scale_enable(void __iomem *top);
+
+int camif_v10x_scale_disable(void __iomem *top);
+
+#endif /* _CAMSS_PP_V10X_SCALE_H_ */
--
2.34.1