[PATCH 02/45] media: qcom: camss: add CSID gen2 pixel path

From: Gjorgji . Rosikopulos . gjorgji . rosikopulos

Date: Tue Oct 06 2026 - 18:40:48 EST


From: Gjorgji Rosikopulos <gjorgji.rosikopulos@xxxxxxxxxxxxxxxx>

Add IPP register programming and interrupt handling for the full-frame
pixel output on non-lite gen2 CSID. Use stream 3 for the pixel path and
expose all four output routes.

Signed-off-by: Gjorgji Rosikopulos <gjorgji.rosikopulos@xxxxxxxxxxxxxxxx>
Signed-off-by: Atanas Filipov <atanas.filipov@xxxxxxxxxxxxxxxx>
---
.../platform/qcom/camss/camss-csid-gen2.c | 158 +++++++++++++++++-
.../media/platform/qcom/camss/camss-csid.c | 13 +-
2 files changed, 152 insertions(+), 19 deletions(-)

diff --git a/drivers/media/platform/qcom/camss/camss-csid-gen2.c b/drivers/media/platform/qcom/camss/camss-csid-gen2.c
index 3af043c38786..72a2b7fc5dd0 100644
--- a/drivers/media/platform/qcom/camss/camss-csid-gen2.c
+++ b/drivers/media/platform/qcom/camss/camss-csid-gen2.c
@@ -12,6 +12,8 @@
#include <linux/kernel.h>
#include <linux/of.h>

+#include <media/mipi-csi2.h>
+
#include "camss-csid.h"
#include "camss-csid-gen2.h"
#include "camss.h"
@@ -22,6 +24,36 @@
* alternate register layout.
*/

+#define CSID_PIX_STREAM_ID 3
+
+static bool csid_is_pix_stream(struct csid_device *csid, u8 stream_id)
+{
+ return stream_id == CSID_PIX_STREAM_ID && !csid_is_lite(csid);
+}
+
+static u8 csid_pix_dt_to_decode_format(u8 dt)
+{
+ switch (dt) {
+ case MIPI_CSI2_DT_RAW6:
+ return DECODE_FORMAT_UNCOMPRESSED_6_BIT;
+ case MIPI_CSI2_DT_YUV422_8B:
+ case MIPI_CSI2_DT_RAW8:
+ return DECODE_FORMAT_UNCOMPRESSED_8_BIT;
+ case MIPI_CSI2_DT_RAW10:
+ return DECODE_FORMAT_UNCOMPRESSED_10_BIT;
+ case MIPI_CSI2_DT_RAW12:
+ return DECODE_FORMAT_UNCOMPRESSED_12_BIT;
+ case MIPI_CSI2_DT_RAW14:
+ return DECODE_FORMAT_UNCOMPRESSED_14_BIT;
+ case MIPI_CSI2_DT_RAW16:
+ return DECODE_FORMAT_UNCOMPRESSED_16_BIT;
+ case MIPI_CSI2_DT_RAW20:
+ return DECODE_FORMAT_UNCOMPRESSED_20_BIT;
+ default:
+ return DECODE_FORMAT_PAYLOAD_ONLY;
+ }
+}
+
#define CSID_RST_STROBES 0x10
#define RST_STROBES 0

@@ -120,6 +152,33 @@
#define CSID_RDI_RPP_LINE_DROP_PERIOD(rdi) ((csid_is_lite(csid) ? 0x230 : 0x330)\
+ 0x100 * (rdi))

+#define CSID_IPP_CFG0 0x200
+#define IPP_CFG0_FMT_MEASURE_EN 0
+#define IPP_CFG0_TIMESTAMP_EN 1
+#define IPP_CFG0_DECODE_FORMAT 12
+#define IPP_CFG0_DATA_TYPE 16
+#define IPP_CFG0_VIRTUAL_CHANNEL 22
+#define IPP_CFG0_DT_ID 27
+#define IPP_CFG0_ENABLE 31
+
+#define CSID_IPP_CFG1 0x204
+#define IPP_CFG1_MIN_HBI 4
+
+#define CSID_IPP_CTRL 0x208
+#define IPP_CTRL_HALT_CMD 0
+#define IPP_CTRL_HALT_MASTER_SEL 4
+#define HALT_MASTER_SEL_IPP 3
+
+#define CSID_IPP_FRM_DROP_PATTERN 0x20c
+#define CSID_IPP_FRM_DROP_PERIOD 0x210
+#define CSID_IPP_IRQ_SUBSAMPLE_PATTERN 0x214
+#define CSID_IPP_IRQ_SUBSAMPLE_PERIOD 0x218
+#define CSID_IPP_LINE_DROP_PATTERN 0x22c
+#define CSID_IPP_LINE_DROP_PERIOD 0x230
+
+#define CSID_CSI2_IPP_IRQ_STATUS 0x30
+#define CSID_CSI2_IPP_IRQ_CLEAR 0x38
+
#define CSID_TPG_CTRL 0x600
#define TPG_CTRL_TEST_EN 0
#define TPG_CTRL_FS_PKT_EN 1
@@ -203,6 +262,64 @@ static void __csid_ctrl_rdi(struct csid_device *csid, int enable, u8 rdi)
writel_relaxed(val, csid->base + CSID_RDI_CTRL(rdi));
}

+static void __csid_ctrl_pix(struct csid_device *csid, int enable)
+{
+ u32 val;
+
+ if (enable)
+ val = HALT_CMD_RESUME_AT_FRAME_BOUNDARY << IPP_CTRL_HALT_CMD;
+ else
+ val = HALT_CMD_HALT_AT_FRAME_BOUNDARY << IPP_CTRL_HALT_CMD;
+ writel_relaxed(val, csid->base + CSID_IPP_CTRL);
+
+ if (!enable) {
+ writel_relaxed(~0, csid->base + CSID_CSI2_IPP_IRQ_CLEAR);
+ writel_relaxed(1 << IRQ_CMD_CLEAR, csid->base + CSID_IRQ_CMD);
+ }
+}
+
+static void __csid_configure_pix(struct csid_device *csid, u8 enable, u8 vc, u8 dt)
+{
+ u32 val;
+
+ val = 1 << IPP_CFG0_FMT_MEASURE_EN;
+ val |= 1 << IPP_CFG0_TIMESTAMP_EN;
+ val |= csid_pix_dt_to_decode_format(dt) << IPP_CFG0_DECODE_FORMAT;
+ val |= dt << IPP_CFG0_DATA_TYPE;
+ val |= vc << IPP_CFG0_VIRTUAL_CHANNEL;
+ val |= CSID_PIX_STREAM_ID << IPP_CFG0_DT_ID;
+ writel_relaxed(val, csid->base + CSID_IPP_CFG0);
+
+ val = 4 << IPP_CFG1_MIN_HBI;
+ writel_relaxed(val, csid->base + CSID_IPP_CFG1);
+
+ val = 1;
+ writel_relaxed(val, csid->base + CSID_IPP_FRM_DROP_PERIOD);
+
+ val = 0;
+ writel_relaxed(val, csid->base + CSID_IPP_FRM_DROP_PATTERN);
+
+ val = 1;
+ writel_relaxed(val, csid->base + CSID_IPP_IRQ_SUBSAMPLE_PERIOD);
+
+ val = 0;
+ writel_relaxed(val, csid->base + CSID_IPP_IRQ_SUBSAMPLE_PATTERN);
+
+ val = 1;
+ writel_relaxed(val, csid->base + CSID_IPP_LINE_DROP_PERIOD);
+
+ val = 0;
+ writel_relaxed(val, csid->base + CSID_IPP_LINE_DROP_PATTERN);
+
+ val = HALT_MASTER_SEL_IPP << IPP_CTRL_HALT_MASTER_SEL;
+ val |= HALT_CMD_RESUME_AT_FRAME_BOUNDARY << IPP_CTRL_HALT_CMD;
+ writel_relaxed(val, csid->base + CSID_IPP_CTRL);
+
+ val = readl_relaxed(csid->base + CSID_IPP_CFG0);
+ val |= enable << IPP_CFG0_ENABLE;
+ writel_relaxed(val, csid->base + CSID_IPP_CFG0);
+}
+
static void __csid_configure_testgen(struct csid_device *csid, u8 enable, u8 port, u8 vc)
{
struct csid_testgen_config *tg = &csid->testgen;
@@ -337,9 +454,16 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable)
if (tg->enabled)
__csid_configure_testgen(csid, enable, i, 0);

- __csid_configure_rdi_stream(csid, enable, i, 0, format->data_type);
- __csid_configure_rx(csid, &csid->phy, 0);
- __csid_ctrl_rdi(csid, enable, i);
+ if (csid_is_pix_stream(csid, i)) {
+ __csid_configure_pix(csid, enable, 0, format->data_type);
+ __csid_configure_rx(csid, &csid->phy, 0);
+ __csid_ctrl_pix(csid, enable);
+ } else {
+ __csid_configure_rdi_stream(csid, enable, i, 0,
+ format->data_type);
+ __csid_configure_rx(csid, &csid->phy, 0);
+ __csid_ctrl_rdi(csid, enable, i);
+ }
}
}

@@ -355,20 +479,31 @@ static void csid_enable_stream(struct csid_device *csid, u32 stream_id, u8 vc, u
if (tg->enabled)
__csid_configure_testgen(csid, 1, stream_id, vc);

- __csid_configure_rdi_stream(csid, 1, stream_id, vc, dt);
- __csid_ctrl_rdi(csid, 1, stream_id);
+ if (csid_is_pix_stream(csid, stream_id)) {
+ __csid_configure_pix(csid, 1, vc, dt);
+ __csid_ctrl_pix(csid, 1);
+ } else {
+ __csid_configure_rdi_stream(csid, 1, stream_id, vc, dt);
+ __csid_ctrl_rdi(csid, 1, stream_id);
+ }
}

static void csid_disable_stream(struct csid_device *csid, u32 stream_id)
{
struct csid_testgen_config *tg = &csid->testgen;

- __csid_ctrl_rdi(csid, 0, stream_id);
+ if (csid_is_pix_stream(csid, stream_id)) {
+ __csid_configure_pix(csid, 0, 0, 0);
+ __csid_ctrl_pix(csid, 0);
+ } else {
+ __csid_ctrl_rdi(csid, 0, stream_id);
+ }

if (tg->enabled)
__csid_configure_testgen(csid, 0, stream_id, 0);

- __csid_configure_rdi_stream(csid, 0, stream_id, 0, 0);
+ if (!csid_is_pix_stream(csid, stream_id))
+ __csid_configure_rdi_stream(csid, 0, stream_id, 0, 0);
}

static int csid_configure_testgen_pattern(struct csid_device *csid, s32 val)
@@ -403,8 +538,13 @@ static irqreturn_t csid_isr(int irq, void *dev)
/* Read and clear IRQ status for each enabled RDI channel */
for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++)
if (csid->phy.en_vc & BIT(i)) {
- val = readl_relaxed(csid->base + CSID_CSI2_RDIN_IRQ_STATUS(i));
- writel_relaxed(val, csid->base + CSID_CSI2_RDIN_IRQ_CLEAR(i));
+ if (csid_is_pix_stream(csid, i)) {
+ val = readl_relaxed(csid->base + CSID_CSI2_IPP_IRQ_STATUS);
+ writel_relaxed(val, csid->base + CSID_CSI2_IPP_IRQ_CLEAR);
+ } else {
+ val = readl_relaxed(csid->base + CSID_CSI2_RDIN_IRQ_STATUS(i));
+ writel_relaxed(val, csid->base + CSID_CSI2_RDIN_IRQ_CLEAR(i));
+ }
}

val = 1 << IRQ_CMD_CLEAR;
diff --git a/drivers/media/platform/qcom/camss/camss-csid.c b/drivers/media/platform/qcom/camss/camss-csid.c
index bdac29b7af86..b7e6bfc58bcf 100644
--- a/drivers/media/platform/qcom/camss/camss-csid.c
+++ b/drivers/media/platform/qcom/camss/camss-csid.c
@@ -1784,16 +1784,9 @@ static int csid_init_state(struct v4l2_subdev *sd, struct v4l2_subdev_state *sta
struct csid_device *csid = v4l2_get_subdevdata(sd);
struct v4l2_subdev_route routes[MSM_CSID_MAX_SRC_STREAMS];
struct v4l2_subdev_krouting routing = { };
- unsigned int num_routes;
int i, ret;

- /* The full IFE has only 3 rdi's and pix output is not functional */
- if (csid_is_lite(csid))
- num_routes = MSM_CSID_MAX_SRC_STREAMS;
- else
- num_routes = MSM_CSID_MAX_SRC_STREAMS - 1;
-
- for (i = 0; i < num_routes; i++) {
+ for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++) {
routes[i].sink_pad = MSM_CSID_PAD_SINK;
routes[i].sink_stream = 0;
if (csid->res->source_streams_mode == CSID_STREAMS_SINGLE_PAD) {
@@ -1806,7 +1799,7 @@ static int csid_init_state(struct v4l2_subdev *sd, struct v4l2_subdev_state *sta
routes[i].flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE;
}

- routing.num_routes = num_routes;
+ routing.num_routes = MSM_CSID_MAX_SRC_STREAMS;
routing.routes = routes;
ret = v4l2_subdev_set_routing_with_fmt(sd, state, &routing, &csid_default_format);
if (ret)
@@ -1849,7 +1842,7 @@ int msm_csid_register_entity(struct csid_device *csid,
if (csid->res->source_streams_mode == CSID_STREAMS_SINGLE_PAD)
num_pads = MSM_CSID_PAD_FIRST_SRC + 1;
else
- num_pads = csid_is_lite(csid) ? MSM_CSID_PADS_NUM : MSM_CSID_PADS_NUM - 1;
+ num_pads = MSM_CSID_PADS_NUM;

v4l2_subdev_init(sd, streams_api ? &csid_streams_v4l2_ops : &csid_v4l2_ops);
sd->internal_ops = streams_api ? &csid_streams_internal_ops
--
2.34.1