Re: [PATCH 27/45] media: qcom: camss: sa8775p: add IFE BUS v3.1x
From: Gjorgji Rosikopulos (Consultant)
Date: Wed Oct 07 2026 - 02:44:57 EST
Hello,
Appoagies somehow after my last rebase the co-develped by was removed. For all the changes
of SA8775P credits to Vikram and Prashant. This will be fixed in the next series.
Co-developed-by: Vikram Sharma <vikram.sharma@xxxxxxxxxxxxxxxx>
Signed-off-by: Vikram Sharma <vikram.sharma@xxxxxxxxxxxxxxxx>
Co-developed-by: Prashant Shrotriya <prashant.shrotriya@xxxxxxxxxxxxxxxx>
Signed-off-by: Prashant Shrotriya <prashant.shrotriya@xxxxxxxxxxxxxxxx>
~Gjorgji
On 10/7/2026 1:39 AM, "Gjorgji Rosikopulos gjorgji.rosikopulos"@oss.qualcomm.com wrote:
> 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;