[PATCH 27/45] media: qcom: camss: sa8775p: add IFE BUS v3.1x

From: Gjorgji . Rosikopulos . gjorgji . rosikopulos

Date: Tue Oct 06 2026 - 18:54:28 EST


From: Atanas Filipov <atanas.filipov@xxxxxxxxxxxxxxxx>

Add the IFE BUS v3.1x write engine used by the SA8775P full and lite
pixel paths.

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

diff --git a/drivers/media/platform/qcom/camss/Makefile b/drivers/media/platform/qcom/camss/Makefile
index 500b056a906c..2251b57ba38b 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.o \
camss-ife-bus-v20x.o \
+ camss-ife-bus-v31x.o \
camss-ife-camif-v10x.o \
camss-ife-lite-top-v10x.o \
camss-ife-mod-v10x.o \
diff --git a/drivers/media/platform/qcom/camss/camss-ife-bus-v31x.c b/drivers/media/platform/qcom/camss/camss-ife-bus-v31x.c
new file mode 100644
index 000000000000..c3a99bd0f75e
--- /dev/null
+++ b/drivers/media/platform/qcom/camss/camss-ife-bus-v31x.c
@@ -0,0 +1,267 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2023, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2023 Qualcomm Technologies, Inc.
+ */
+
+#include <linux/bitfield.h>
+
+#include "camss.h"
+#include "camss-ife.h"
+
+/* Block base offsets */
+#define BUSWR_V31X_OFFS 0x400
+#define BUSWR_V31X_LITE_OFFS 0x480
+
+/* Per-client register offset */
+#define BUSWR_V31X_CLIENT_OFFS(n) ((n) * 0x100)
+
+/* Interface interrupt and violation status bits */
+#define BUSWR_V31X_IIF_IRQ_CMD_CLEAR BIT(0)
+#define BUSWR_V31X_OVERFLOW_ERR_MASK GENMASK(25, 0)
+#define IS_BUSWR_V31X_OVERFLOW_ERR(s) \
+ ((s) & BUSWR_V31X_OVERFLOW_ERR_MASK)
+#define BUSWR_V31X_VIOLATION_ERR_MASK GENMASK(25, 0)
+#define IS_BUSWR_V31X_VIOLATION_ERR(s) \
+ ((s) & BUSWR_V31X_VIOLATION_ERR_MASK)
+#define BUSWR_V31X_IMG_SIZE_VIOLATION_ERR_MASK GENMASK(25, 0)
+#define IS_BUSWR_V31X_IMG_SIZE_VIOLATION_ERR(s) \
+ ((s) & BUSWR_V31X_IMG_SIZE_VIOLATION_ERR_MASK)
+#define BUSWR_V31X_IIF_IRQ_S0_IMG_SIZE_VIOLATION BIT(31)
+#define IS_BUSWR_V31X_IIF_IRQ_S0_IMG_SIZE_VIOLATION(s) \
+ ((s) & BUSWR_V31X_IIF_IRQ_S0_IMG_SIZE_VIOLATION)
+#define BUSWR_V31X_IIF_IRQ_S0_VIOLATION BIT(30)
+#define IS_BUSWR_V31X_IIF_IRQ_S0_VIOLATION(s) \
+ ((s) & BUSWR_V31X_IIF_IRQ_S0_VIOLATION)
+
+/* Client configuration field values */
+#define BUSWR_V31X_IIF_CGC_OVERRIDE GENMASK(26, 0)
+#define BUSWR_V31X_CLIENT_CFG_MODE GENMASK(17, 16)
+#define BUSWR_V31X_CLIENT_CFG_AUTO_RECOVERY_EN BIT(4)
+#define BUSWR_V31X_CLIENT_CFG_EN BIT(0)
+#define BUSWR_V31X_CLIENT_IMG_CFG0_HEIGHT GENMASK(31, 16)
+#define BUSWR_V31X_CLIENT_IMG_CFG0_WIDTH GENMASK(15, 0)
+#define BUSWR_V31X_CLIENT_IMG_CFG2_STRIDE GENMASK(23, 0)
+#define BUSWR_V31X_CLIENT_PACKER_CFG_ALIGN BIT(5)
+#define BUSWR_V31X_CLIENT_PACKER_CFG_FORMAT GENMASK(4, 0)
+#define BUSWR_V31X_PACKER_FMT_PLAIN16_10_BPP 0x5
+#define BUSWR_V31X_PACKER_FMT_PLAIN16_16_BPP 0x8
+#define BUSWR_V31X_CLIENT_IRQ_SUBSAMPLE_CFG0_PERIOD GENMASK(4, 0)
+#define BUSWR_V31X_CLIENT_IRQ_SUBSAMPLE_CFG1_PATTERN GENMASK(31, 0)
+#define BUSWR_V31X_CLIENT_FRAMEDROP_CFG0_PERIOD GENMASK(4, 0)
+#define BUSWR_V31X_CLIENT_FRAMEDROP_CFG1_PATTERN GENMASK(31, 0)
+
+/* FIFO limit and frame-based RDI defaults */
+#define BUSWR_V31X_IMG_ADDR_FIFO_MAX_DEPTH 2
+#define BUSWR_V31X_RDI_DEFAULT_WIDTH 0xffff
+#define BUSWR_V31X_RDI_DEFAULT_STRIDE 0xffff
+#define BUSWR_V31X_CLIENT_ADDR_STATUS_2_IMAGE_ADDR_FIFO_WORD_CNT GENMASK(2, 0)
+
+/* Common registers */
+#define BUSWR_V31X_IIF_CGC_OVERRIDE_REG 0x00000008
+#define BUSWR_V31X_WM_TEST_BUS_CTRL_REG 0x000000fc
+#define BUSWR_V31X_IIF_IRQ_MASK_S0 0x00000018
+#define BUSWR_V31X_IIF_IRQ_CLEAR_S0 0x00000020
+#define BUSWR_V31X_IIF_IRQ_STATUS_S0 0x00000028
+#define BUSWR_V31X_IIF_IRQ_CMD_REG 0x00000030
+#define BUSWR_V31X_STATUS_CLEAR_REG 0x00000060
+#define BUSWR_V31X_VIOLATION_STATUS_REG 0x00000064
+#define BUSWR_V31X_OVERFLOW_STATUS_REG 0x00000068
+#define BUSWR_V31X_IMG_SIZE_VIOLATION_REG 0x00000070
+
+/* Per-client registers (relative to client base = module_base + CLIENT_OFFS(n)) */
+#define BUSWR_V31X_CLIENT_CFG_REG 0x00000200
+#define BUSWR_V31X_CLIENT_ADDR_IMAGE_REG 0x00000204
+#define BUSWR_V31X_CLIENT_IMG_CFG0_REG 0x0000020c
+#define BUSWR_V31X_CLIENT_IMG_CFG2_REG 0x00000214
+#define BUSWR_V31X_CLIENT_PACKER_CFG_REG 0x00000218
+#define BUSWR_V31X_CLIENT_IRQ_SS_CFG0_REG 0x00000230
+#define BUSWR_V31X_CLIENT_IRQ_SS_CFG1_REG 0x00000234
+#define BUSWR_V31X_CLIENT_FRAMEDROP_CFG0_REG 0x00000238
+#define BUSWR_V31X_CLIENT_FRAMEDROP_CFG1_REG 0x0000023c
+#define BUSWR_V31X_CLIENT_ADDR_STATUS_0_REG 0x00000270
+#define BUSWR_V31X_CLIENT_ADDR_STATUS_2_REG 0x00000278
+
+static void ife_buswr_31x_acquire(struct ife_device *ife, void __iomem *base)
+{
+ struct ife_buswr_ctx *ctx = &ife->buswr_ctx;
+
+ if (ife->res->type == CAMSS_IFE_SUBDEV_TYPE_IFE_LITE)
+ ctx->base = base + BUSWR_V31X_LITE_OFFS;
+ else
+ ctx->base = base + BUSWR_V31X_OFFS;
+
+ ctx->dev = ife->camss->dev;
+}
+
+static void ife_buswr_31x_release(struct ife_buswr_ctx *ctx)
+{
+ ctx->base = NULL;
+}
+
+static void ife_buswr_31x_wm_config(struct ife_buswr_ctx *ctx, const u8 wm,
+ const struct camss_ife_wm_config *cfg)
+{
+ void __iomem *base = ctx->base;
+ bool align = cfg->packing_fmt >= BUSWR_V31X_PACKER_FMT_PLAIN16_10_BPP &&
+ cfg->packing_fmt <= BUSWR_V31X_PACKER_FMT_PLAIN16_16_BPP;
+ u32 pattern = 1;
+ u32 period = 0;
+ u32 bytes = cfg->bytes;
+ u32 stride = cfg->stride;
+ u32 lines = cfg->lines;
+ u32 rval;
+
+ if (cfg->mode_config == BUSWR_CLIENT_CFG_MODE_FRAME_BASED) {
+ bytes = BUSWR_V31X_RDI_DEFAULT_WIDTH;
+ lines = 0;
+ stride = BUSWR_V31X_RDI_DEFAULT_STRIDE;
+ }
+
+ writel_relaxed(BUSWR_V31X_IIF_CGC_OVERRIDE,
+ base + BUSWR_V31X_IIF_CGC_OVERRIDE_REG);
+ writel_relaxed(0, base + BUSWR_V31X_WM_TEST_BUS_CTRL_REG);
+
+ rval = FIELD_PREP(BUSWR_V31X_CLIENT_CFG_MODE, cfg->mode_config);
+ writel_relaxed(rval, base + BUSWR_V31X_CLIENT_CFG_REG + BUSWR_V31X_CLIENT_OFFS(wm));
+
+ rval = FIELD_PREP(BUSWR_V31X_CLIENT_PACKER_CFG_FORMAT, cfg->packing_fmt) |
+ FIELD_PREP(BUSWR_V31X_CLIENT_PACKER_CFG_ALIGN, align);
+ writel_relaxed(rval, base + BUSWR_V31X_CLIENT_PACKER_CFG_REG +
+ BUSWR_V31X_CLIENT_OFFS(wm));
+
+ rval = FIELD_PREP(BUSWR_V31X_CLIENT_IMG_CFG0_WIDTH, bytes) |
+ FIELD_PREP(BUSWR_V31X_CLIENT_IMG_CFG0_HEIGHT, lines);
+ writel_relaxed(rval, base + BUSWR_V31X_CLIENT_IMG_CFG0_REG +
+ BUSWR_V31X_CLIENT_OFFS(wm));
+
+ rval = FIELD_PREP(BUSWR_V31X_CLIENT_IMG_CFG2_STRIDE, stride);
+ writel_relaxed(rval, base + BUSWR_V31X_CLIENT_IMG_CFG2_REG +
+ BUSWR_V31X_CLIENT_OFFS(wm));
+
+ rval = FIELD_PREP(BUSWR_V31X_CLIENT_IRQ_SUBSAMPLE_CFG0_PERIOD, period);
+ writel_relaxed(rval, base + BUSWR_V31X_CLIENT_IRQ_SS_CFG0_REG +
+ BUSWR_V31X_CLIENT_OFFS(wm));
+
+ rval = FIELD_PREP(BUSWR_V31X_CLIENT_IRQ_SUBSAMPLE_CFG1_PATTERN, pattern);
+ writel_relaxed(rval, base + BUSWR_V31X_CLIENT_IRQ_SS_CFG1_REG +
+ BUSWR_V31X_CLIENT_OFFS(wm));
+
+ rval = FIELD_PREP(BUSWR_V31X_CLIENT_FRAMEDROP_CFG0_PERIOD, period);
+ writel_relaxed(rval, base + BUSWR_V31X_CLIENT_FRAMEDROP_CFG0_REG +
+ BUSWR_V31X_CLIENT_OFFS(wm));
+
+ rval = FIELD_PREP(BUSWR_V31X_CLIENT_FRAMEDROP_CFG1_PATTERN, pattern);
+ writel_relaxed(rval, base + BUSWR_V31X_CLIENT_FRAMEDROP_CFG1_REG +
+ BUSWR_V31X_CLIENT_OFFS(wm));
+}
+
+static void ife_buswr_31x_wm_enable(struct ife_buswr_ctx *ctx, u8 wm)
+{
+ void __iomem *base = ctx->base;
+ u32 rval;
+
+ rval = readl_relaxed(base + BUSWR_V31X_CLIENT_CFG_REG + BUSWR_V31X_CLIENT_OFFS(wm));
+ rval |= BUSWR_V31X_CLIENT_CFG_EN | BUSWR_V31X_CLIENT_CFG_AUTO_RECOVERY_EN;
+ writel_relaxed(rval, base + BUSWR_V31X_CLIENT_CFG_REG + BUSWR_V31X_CLIENT_OFFS(wm));
+}
+
+static void ife_buswr_31x_wm_disable(struct ife_buswr_ctx *ctx, u8 wm)
+{
+ void __iomem *base = ctx->base;
+ u32 rval;
+
+ rval = readl_relaxed(base + BUSWR_V31X_CLIENT_CFG_REG + BUSWR_V31X_CLIENT_OFFS(wm));
+ rval &= ~(BUSWR_V31X_CLIENT_CFG_EN | BUSWR_V31X_CLIENT_CFG_AUTO_RECOVERY_EN);
+ writel_relaxed(rval, base + BUSWR_V31X_CLIENT_CFG_REG + BUSWR_V31X_CLIENT_OFFS(wm));
+}
+
+static bool ife_buswr_31x_is_fifo_full(struct ife_buswr_ctx *ctx, u8 wm)
+{
+ u32 rval = readl_relaxed(ctx->base + BUSWR_V31X_CLIENT_OFFS(wm) +
+ BUSWR_V31X_CLIENT_ADDR_STATUS_2_REG);
+
+ rval = FIELD_GET(BUSWR_V31X_CLIENT_ADDR_STATUS_2_IMAGE_ADDR_FIFO_WORD_CNT, rval);
+
+ return rval >= BUSWR_V31X_IMG_ADDR_FIFO_MAX_DEPTH;
+}
+
+static void ife_buswr_31x_queue_buf(struct ife_buswr_ctx *ctx, const u8 num_wms, const u8 *wm,
+ dma_addr_t *addr)
+{
+ int i;
+
+ for (i = 0; i < num_wms; i++) {
+ if (addr[i])
+ writel_relaxed(addr[i], ctx->base + BUSWR_V31X_CLIENT_ADDR_IMAGE_REG +
+ BUSWR_V31X_CLIENT_OFFS(wm[i]));
+ }
+}
+
+static u32 ife_buswr_31x_consumed_addr(struct ife_buswr_ctx *ctx, u8 wm)
+{
+ return readl_relaxed(ctx->base + BUSWR_V31X_CLIENT_ADDR_STATUS_0_REG +
+ BUSWR_V31X_CLIENT_OFFS(wm));
+}
+
+static u32 ife_buswr_31x_irq_status(struct ife_buswr_ctx *ctx)
+{
+ void __iomem *base = ctx->base;
+ u32 bus_stat;
+ u32 iif_stat;
+
+ bus_stat = readl_relaxed(base + BUSWR_V31X_OVERFLOW_STATUS_REG);
+ writel_relaxed(BIT(0), base + BUSWR_V31X_STATUS_CLEAR_REG);
+ if (IS_BUSWR_V31X_OVERFLOW_ERR(bus_stat)) {
+ writel_relaxed(bus_stat, base + BUSWR_V31X_STATUS_CLEAR_REG);
+ pr_warn("BUSWR common overflow: 0x%x\n", bus_stat);
+ }
+
+ bus_stat = readl_relaxed(base + BUSWR_V31X_VIOLATION_STATUS_REG);
+ if (IS_BUSWR_V31X_VIOLATION_ERR(bus_stat))
+ pr_warn("BUSWR common violation: 0x%x\n", bus_stat);
+
+ bus_stat = readl_relaxed(base + BUSWR_V31X_IMG_SIZE_VIOLATION_REG);
+ if (IS_BUSWR_V31X_IMG_SIZE_VIOLATION_ERR(bus_stat))
+ pr_warn("BUSWR common image size violation: 0x%x\n", bus_stat);
+
+ iif_stat = readl_relaxed(base + BUSWR_V31X_IIF_IRQ_STATUS_S0);
+ writel_relaxed(iif_stat, base + BUSWR_V31X_IIF_IRQ_CLEAR_S0);
+ if (iif_stat) {
+ if (IS_BUSWR_V31X_IIF_IRQ_S0_IMG_SIZE_VIOLATION(iif_stat))
+ pr_warn("BUSWR image size violation from CCIF: 0x%x\n", iif_stat);
+ if (IS_BUSWR_V31X_IIF_IRQ_S0_VIOLATION(iif_stat))
+ pr_warn("BUSWR violation from CCIF: 0x%x\n", iif_stat);
+ }
+
+ writel_relaxed(BUSWR_V31X_IIF_IRQ_CMD_CLEAR, base + BUSWR_V31X_IIF_IRQ_CMD_REG);
+
+ return iif_stat;
+}
+
+static void ife_buswr_31x_irq_enable(struct ife_buswr_ctx *ctx)
+{
+ writel_relaxed(~0, ctx->base + BUSWR_V31X_IIF_IRQ_MASK_S0);
+}
+
+static void ife_buswr_31x_irq_disable(struct ife_buswr_ctx *ctx)
+{
+ void __iomem *base = ctx->base;
+
+ writel_relaxed(0, base + BUSWR_V31X_IIF_IRQ_MASK_S0);
+ writel_relaxed(~0, base + BUSWR_V31X_IIF_IRQ_CLEAR_S0);
+ writel_relaxed(BUSWR_V31X_IIF_IRQ_CMD_CLEAR, base + BUSWR_V31X_IIF_IRQ_CMD_REG);
+}
+
+const struct ife_hw_buswr_ops ife_buswr_31x_ops = {
+ .acquire = ife_buswr_31x_acquire,
+ .release = ife_buswr_31x_release,
+ .irq_enable = ife_buswr_31x_irq_enable,
+ .irq_disable = ife_buswr_31x_irq_disable,
+ .wm_config = ife_buswr_31x_wm_config,
+ .wm_enable = ife_buswr_31x_wm_enable,
+ .wm_disable = ife_buswr_31x_wm_disable,
+ .queue_buf = ife_buswr_31x_queue_buf,
+ .consumed_addr = ife_buswr_31x_consumed_addr,
+ .irq_status = ife_buswr_31x_irq_status,
+ .is_fifo_full = ife_buswr_31x_is_fifo_full,
+};
diff --git a/drivers/media/platform/qcom/camss/camss-ife.h b/drivers/media/platform/qcom/camss/camss-ife.h
index c163c336fcc3..efbb1e75d550 100644
--- a/drivers/media/platform/qcom/camss/camss-ife.h
+++ b/drivers/media/platform/qcom/camss/camss-ife.h
@@ -397,6 +397,7 @@ camss_ife_pipe_for_stream_id(int nstreams,
extern const struct ife_pads_descriptors ife_pads_v20x;
extern const struct ife_hw_top_ops ife_top_20x_ops;
extern const struct ife_hw_buswr_ops ife_buswr_20x_ops;
+extern const struct ife_hw_buswr_ops ife_buswr_31x_ops;
extern const struct camss_mod_ops ife_mod_v10x_ops;

extern const struct ife_pads_descriptors ife_lite_pads_v20x;
--
2.34.1