[PATCH 4/6] media: qcom: camss: vfe: Add support for VFE 900
From: Wenmeng Liu
Date: Tue Sep 15 2026 - 06:03:47 EST
The VFE on the Nord platform reuses the gen4 VFE driver but places its
bus registers at a different base and does not implement the write
master CGC override register. Add a VFE 900 variant to the gen4 driver
to handle the different bus register base, the separate location of the
test bus control register, and to skip the missing CGC override
register.
In addition, the v900 ADDR_IMAGE register takes a full byte address
rather than the 256-byte-unit encoding used by the gen4 baseline, and
the full IFE write master client map places the RDI clients at index 28
(the lite instances keep RDI0 at client 0).
Signed-off-by: Wenmeng Liu <wenmeng.liu@xxxxxxxxxxxxxxxx>
---
drivers/media/platform/qcom/camss/camss-vfe-gen4.c | 102 +++++++++++++++++++--
drivers/media/platform/qcom/camss/camss.h | 1 +
2 files changed, 95 insertions(+), 8 deletions(-)
diff --git a/drivers/media/platform/qcom/camss/camss-vfe-gen4.c b/drivers/media/platform/qcom/camss/camss-vfe-gen4.c
index d73d70898710a2127c606ac8459a1d8c747a2f7c..f1429a6d2be54c415175a0129778f4b3a232564f 100644
--- a/drivers/media/platform/qcom/camss/camss-vfe-gen4.c
+++ b/drivers/media/platform/qcom/camss/camss-vfe-gen4.c
@@ -13,13 +13,77 @@
#include "camss.h"
#include "camss-vfe.h"
+struct vfe_gen4_hw_info {
+ u32 bus_reg_base;
+ u32 bus_reg_base_lite;
+ s32 test_bus_ctrl_offset; /* relative to the bus register base */
+ u8 rdi_wm_offset;
+ u8 rdi_wm_offset_lite;
+ u8 addr_shift; /* ADDR_IMAGE address unit */
+ bool has_wm_cgc_override;
+};
+
+static const struct vfe_gen4_hw_info vfe_900_hw_info = {
+ .bus_reg_base = 0xA00,
+ .bus_reg_base_lite = 0x700,
+ .test_bus_ctrl_offset = -0x1BC,
+ .rdi_wm_offset = 0x1C,
+ .rdi_wm_offset_lite = 0x0,
+ .addr_shift = 0,
+ .has_wm_cgc_override = false,
+};
+
+static const struct vfe_gen4_hw_info vfe_1080_hw_info = {
+ .bus_reg_base = 0x1000,
+ .bus_reg_base_lite = 0x800,
+ .test_bus_ctrl_offset = 0x128,
+ .rdi_wm_offset = 0x17,
+ .rdi_wm_offset_lite = 0x0,
+ .addr_shift = 8,
+ .has_wm_cgc_override = true,
+};
+
+static inline const struct vfe_gen4_hw_info *
+vfe_gen4_get_hw_info(struct vfe_device *vfe)
+{
+ switch (vfe->camss->res->version) {
+ case CAMSS_NORD:
+ return &vfe_900_hw_info;
+ default:
+ return &vfe_1080_hw_info;
+ }
+}
+
+static inline u32 vfe_gen4_bus_reg_base(struct vfe_device *vfe)
+{
+ const struct vfe_gen4_hw_info *hw_info = vfe_gen4_get_hw_info(vfe);
+
+ return vfe_is_lite(vfe) ? hw_info->bus_reg_base_lite :
+ hw_info->bus_reg_base;
+}
+
+static inline u32 vfe_gen4_test_bus_ctrl(struct vfe_device *vfe)
+{
+ const struct vfe_gen4_hw_info *hw_info = vfe_gen4_get_hw_info(vfe);
+
+ return vfe_gen4_bus_reg_base(vfe) + hw_info->test_bus_ctrl_offset;
+}
+
+static inline u8 vfe_gen4_rdi_wm(struct vfe_device *vfe, u8 rdi)
+{
+ const struct vfe_gen4_hw_info *hw_info = vfe_gen4_get_hw_info(vfe);
+
+ return rdi + (vfe_is_lite(vfe) ? hw_info->rdi_wm_offset_lite :
+ hw_info->rdi_wm_offset);
+}
+
/* VFE-gen4 Bus Register Base Addresses */
-#define BUS_REG_BASE (vfe_is_lite(vfe) ? 0x800 : 0x1000)
+#define BUS_REG_BASE (vfe_gen4_bus_reg_base(vfe))
#define VFE_BUS_WM_CGC_OVERRIDE (BUS_REG_BASE + 0x08)
#define WM_CGC_OVERRIDE_ALL (0x7FFFFFF)
-#define VFE_BUS_WM_TEST_BUS_CTRL (BUS_REG_BASE + 0x128)
+#define VFE_BUS_WM_TEST_BUS_CTRL (vfe_gen4_test_bus_ctrl(vfe))
#define VFE_BUS_WM_CFG(n) (BUS_REG_BASE + 0x500 + (n) * 0x100)
#define WM_CFG_EN BIT(0)
@@ -83,18 +147,36 @@
* RDI3 3
* GAMMA 4
* STATES_BE 5
+ *
+ * v900 full IFE write master client map: the RDI clients start at 28,
+ * after the 28 image/stats clients (0-27):
+ *
+ * MAIN C0/C1/C2/UV 0-3
+ * PIXEL_RAW 4
+ * W_IR 5
+ * AI_1 C0/C1/C2/UV 6-9
+ * AI_2 C0/C1/C2/UV 10-13
+ * HV_DS16 / HV_DS4 14-15
+ * STATS BG_IR..BLTM 16-27
+ * RDI0 28
+ * RDI1 29
+ * RDI2 30
+ * ... RDI11 39
+ *
+ * v900 IFE Lite matches the generic map above (RDI0 at client 0).
*/
-#define RDI_WM(n) ((vfe_is_lite(vfe) ? 0x0 : 0x17) + (n))
static void vfe_wm_start(struct vfe_device *vfe, u8 wm, struct vfe_line *line)
{
struct v4l2_pix_format_mplane *pix =
&line->video_out.active_fmt.fmt.pix_mp;
- wm = RDI_WM(wm);
+ wm = vfe_gen4_rdi_wm(vfe, wm);
/* no clock gating at bus input */
- writel(WM_CGC_OVERRIDE_ALL, vfe->base + VFE_BUS_WM_CGC_OVERRIDE);
+ if (vfe_gen4_get_hw_info(vfe)->has_wm_cgc_override)
+ writel(WM_CGC_OVERRIDE_ALL,
+ vfe->base + VFE_BUS_WM_CGC_OVERRIDE);
writel(0x0, vfe->base + VFE_BUS_WM_TEST_BUS_CTRL);
@@ -123,15 +205,19 @@ static void vfe_wm_start(struct vfe_device *vfe, u8 wm, struct vfe_line *line)
static void vfe_wm_stop(struct vfe_device *vfe, u8 wm)
{
- wm = RDI_WM(wm);
+ wm = vfe_gen4_rdi_wm(vfe, wm);
writel(0, vfe->base + VFE_BUS_WM_CFG(wm));
}
static void vfe_wm_update(struct vfe_device *vfe, u8 wm, u32 addr,
struct vfe_line *line)
{
- wm = RDI_WM(wm);
- writel(addr >> 8, vfe->base + VFE_BUS_WM_IMAGE_ADDR(wm));
+ const struct vfe_gen4_hw_info *hw_info = vfe_gen4_get_hw_info(vfe);
+
+ wm = vfe_gen4_rdi_wm(vfe, wm);
+
+ writel(addr >> hw_info->addr_shift,
+ vfe->base + VFE_BUS_WM_IMAGE_ADDR(wm));
dev_dbg(vfe->camss->dev, "wm:%d, image buf addr:0x%x\n", wm, addr);
}
diff --git a/drivers/media/platform/qcom/camss/camss.h b/drivers/media/platform/qcom/camss/camss.h
index 93d691c8ac63b2a47dbb234856b627d8911a1851..487b8ec0f7cdbfe2e2da0e46cb4431b1e6b4c475 100644
--- a/drivers/media/platform/qcom/camss/camss.h
+++ b/drivers/media/platform/qcom/camss/camss.h
@@ -96,6 +96,7 @@ enum camss_version {
CAMSS_8550,
CAMSS_8650,
CAMSS_8775P,
+ CAMSS_NORD,
CAMSS_X1E80100,
};
--
2.34.1