2024-02-22 11:33:55

by Jai Luthra

[permalink] [raw]
Subject: [PATCH RFC 00/21] media: cadence,ti: CSI2RX Multistream Support

This series adds multi-stream support for Cadence CSI2RX and TI CSI2RX
Shim drivers, along with minor improvements and bug fixes. It is a
follow up to [1] which added single-camera support for TI CSI2RX.

Posting this as an RFC as it is late in this merge window, will post a
non-RFC v2 fixing any comments after v6.9-rc1 is tagged.

Some of the patches are split to ease review, and are prefixed with
"SQUASH" in the subject. They can be squashed in subsequent revisions,
as they should ideally go as a single commit.

PATCH 1-3: Misc. fixes and improvements
PATCH 4-10: Support multiple DMA contexts/video nodes in TI CSI2RX
PATCH 11-12: Use get_frame_desc to propagate virtual channel information
across the different subdevs in the media pipeline
PATCH 13-20: Use new multi-stream APIs across the drivers to support
multiplexed cameras from sources like UB960 (FPDLink)
PATCH 21: Optimize stream on by submitting all queued buffers to DMA

[1]: https://lore.kernel.org/all/[email protected]/

Signed-off-by: Jai Luthra <[email protected]>
---
Jai Luthra (15):
media: ti: j721e-csi2rx: Fix races while restarting DMA
dt-bindings: media: ti,j721e-csi2rx-shim: Support 32 dma chans
media: ti: j721e-csi2rx: separate out device and context
media: ti: j721e-csi2rx: add a subdev for the core device
SQUASH: media: ti: j721e-csi2rx: Fix link validation for multi-stream
media: ti: j721e-csi2rx: add support for processing virtual channels
media: cadence: csi2rx: Use new enable stream APIs
media: cadence: csi2rx: Enable stream-wise routing
SQUASH: media: cadence: csi2rx: Enable per-stream controls
SQUASH: media: cadence: csi2rx: Filter using MIPI virtual channels
SQUASH: media: cadence: csi2rx: Filter streams in get_frame_desc
media: ti: j721e-csi2rx: add multistream support
SQUASH: media: ti: j721e-csi2rx: Enable per-stream controls
SQUASH: media: ti: j721e-csi2rx: Assert pixel reset before stopping last stream
media: ti: j721e-csi2rx: Submit all available buffers

Jayshri Pawar (1):
media: cadence: csi2rx: Support runtime PM

Pratyush Yadav (5):
media: cadence: csi2rx: configure DPHY before starting source stream
media: ti: j721e-csi2rx: prepare SHIM code for multiple contexts
media: ti: j721e-csi2rx: allocate DMA channel based on context index
media: ti: j721e-csi2rx: get number of contexts from device tree
media: cadence: csi2rx: add get_frame_desc wrapper

.../bindings/media/ti,j721e-csi2rx-shim.yaml | 40 +-
drivers/media/platform/cadence/cdns-csi2rx.c | 460 +++++++++--
.../media/platform/ti/j721e-csi2rx/j721e-csi2rx.c | 853 +++++++++++++++------
3 files changed, 1052 insertions(+), 301 deletions(-)
---
base-commit: d894a2a286fccd6e47cd1cac4c2d4ff5d300d7c7
change-id: 20240221-multistream-fbba6ffe47a3

Best regards,
--
Jai Luthra <[email protected]>



2024-02-22 11:33:57

by Jai Luthra

[permalink] [raw]
Subject: [PATCH RFC 03/21] media: cadence: csi2rx: Support runtime PM

From: Jayshri Pawar <[email protected]>

Use runtime power management hooks to save power when CSI-RX is not in
use. Also stop/start any in-progress streams, which might happen during
a system suspend/resume cycle.

Signed-off-by: Jayshri Pawar <[email protected]>
Co-developed-by: Jai Luthra <[email protected]>
Signed-off-by: Jai Luthra <[email protected]>
---
drivers/media/platform/cadence/cdns-csi2rx.c | 43 +++++++++++++++++++++++++++-
1 file changed, 42 insertions(+), 1 deletion(-)

diff --git a/drivers/media/platform/cadence/cdns-csi2rx.c b/drivers/media/platform/cadence/cdns-csi2rx.c
index 75e602c1d762..e19993ed351c 100644
--- a/drivers/media/platform/cadence/cdns-csi2rx.c
+++ b/drivers/media/platform/cadence/cdns-csi2rx.c
@@ -370,6 +370,12 @@ static int csi2rx_s_stream(struct v4l2_subdev *subdev, int enable)
struct csi2rx_priv *csi2rx = v4l2_subdev_to_csi2rx(subdev);
int ret = 0;

+ if (enable) {
+ ret = pm_runtime_resume_and_get(csi2rx->dev);
+ if (ret < 0)
+ return ret;
+ }
+
mutex_lock(&csi2rx->lock);

if (enable) {
@@ -379,8 +385,10 @@ static int csi2rx_s_stream(struct v4l2_subdev *subdev, int enable)
*/
if (!csi2rx->count) {
ret = csi2rx_start(csi2rx);
- if (ret)
+ if (ret) {
+ pm_runtime_put(csi2rx->dev);
goto out;
+ }
}

csi2rx->count++;
@@ -392,6 +400,8 @@ static int csi2rx_s_stream(struct v4l2_subdev *subdev, int enable)
*/
if (!csi2rx->count)
csi2rx_stop(csi2rx);
+
+ pm_runtime_put(csi2rx->dev);
}

out:
@@ -665,6 +675,29 @@ static int csi2rx_parse_dt(struct csi2rx_priv *csi2rx)
return ret;
}

+static int csi2rx_suspend(struct device *dev)
+{
+ struct csi2rx_priv *csi2rx = dev_get_drvdata(dev);
+
+ mutex_lock(&csi2rx->lock);
+ if (csi2rx->count)
+ csi2rx_stop(csi2rx);
+ mutex_unlock(&csi2rx->lock);
+
+ return 0;
+}
+
+static int csi2rx_resume(struct device *dev)
+{
+ struct csi2rx_priv *csi2rx = dev_get_drvdata(dev);
+
+ mutex_lock(&csi2rx->lock);
+ if (csi2rx->count)
+ csi2rx_start(csi2rx);
+ mutex_unlock(&csi2rx->lock);
+ return 0;
+}
+
static int csi2rx_probe(struct platform_device *pdev)
{
struct csi2rx_priv *csi2rx;
@@ -711,6 +744,7 @@ static int csi2rx_probe(struct platform_device *pdev)
if (ret)
goto err_cleanup;

+ pm_runtime_enable(csi2rx->dev);
ret = v4l2_async_register_subdev(&csi2rx->subdev);
if (ret < 0)
goto err_free_state;
@@ -725,6 +759,7 @@ static int csi2rx_probe(struct platform_device *pdev)

err_free_state:
v4l2_subdev_cleanup(&csi2rx->subdev);
+ pm_runtime_disable(csi2rx->dev);
err_cleanup:
v4l2_async_nf_unregister(&csi2rx->notifier);
v4l2_async_nf_cleanup(&csi2rx->notifier);
@@ -743,9 +778,14 @@ static void csi2rx_remove(struct platform_device *pdev)
v4l2_async_unregister_subdev(&csi2rx->subdev);
v4l2_subdev_cleanup(&csi2rx->subdev);
media_entity_cleanup(&csi2rx->subdev.entity);
+ pm_runtime_disable(csi2rx->dev);
kfree(csi2rx);
}

+static const struct dev_pm_ops csi2rx_pm_ops = {
+ SET_RUNTIME_PM_OPS(csi2rx_suspend, csi2rx_resume, NULL)
+};
+
static const struct of_device_id csi2rx_of_table[] = {
{ .compatible = "starfive,jh7110-csi2rx" },
{ .compatible = "cdns,csi2rx" },
@@ -760,6 +800,7 @@ static struct platform_driver csi2rx_driver = {
.driver = {
.name = "cdns-csi2rx",
.of_match_table = csi2rx_of_table,
+ .pm = &csi2rx_pm_ops,
},
};
module_platform_driver(csi2rx_driver);

--
2.43.0


2024-02-22 11:33:57

by Jai Luthra

[permalink] [raw]
Subject: [PATCH RFC 01/21] media: ti: j721e-csi2rx: Fix races while restarting DMA

After the frame is submitted to DMA, it may happen that the submitted
list is not updated soon enough, and the DMA callback is triggered
before that.

This can lead to kernel crashes, so move everything in a single
lock/unlock section to prevent such races.

Fixes: b4a3d877dc92 ("media: ti: Add CSI2RX support for J721E")
Signed-off-by: Jai Luthra <[email protected]>
---
drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
index be47a4a24f97..e2cac8d73d78 100644
--- a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
+++ b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
@@ -810,15 +810,14 @@ static void ti_csi2rx_buffer_queue(struct vb2_buffer *vb)
dev_warn(csi->dev,
"Failed to drain DMA. Next frame might be bogus\n");

+ spin_lock_irqsave(&dma->lock, flags);
ret = ti_csi2rx_start_dma(csi, buf);
if (ret) {
- dev_err(csi->dev, "Failed to start DMA: %d\n", ret);
- spin_lock_irqsave(&dma->lock, flags);
vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
dma->state = TI_CSI2RX_DMA_IDLE;
spin_unlock_irqrestore(&dma->lock, flags);
+ dev_err(csi->dev, "Failed to start DMA: %d\n", ret);
} else {
- spin_lock_irqsave(&dma->lock, flags);
list_add_tail(&buf->list, &dma->submitted);
spin_unlock_irqrestore(&dma->lock, flags);
}

--
2.43.0


2024-02-22 11:34:34

by Jai Luthra

[permalink] [raw]
Subject: [PATCH RFC 02/21] media: cadence: csi2rx: configure DPHY before starting source stream

From: Pratyush Yadav <[email protected]>

When the source device is operating above 1.5 Gbps per lane, it needs to
send the Skew Calibration Sequence before sending any HS data. If the
DPHY is initialized after the source stream is started, then it might
miss the sequence and not be able to receive data properly. Move the
start of source subdev to the end of the sequence to make sure
everything is ready to receive data before the source starts streaming.

Signed-off-by: Pratyush Yadav <[email protected]>
Signed-off-by: Jai Luthra <[email protected]>
---
drivers/media/platform/cadence/cdns-csi2rx.c | 26 ++++++++++++++------------
1 file changed, 14 insertions(+), 12 deletions(-)

diff --git a/drivers/media/platform/cadence/cdns-csi2rx.c b/drivers/media/platform/cadence/cdns-csi2rx.c
index 70b7f8a9e4f2..75e602c1d762 100644
--- a/drivers/media/platform/cadence/cdns-csi2rx.c
+++ b/drivers/media/platform/cadence/cdns-csi2rx.c
@@ -243,10 +243,6 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx)

writel(reg, csi2rx->base + CSI2RX_STATIC_CFG_REG);

- ret = v4l2_subdev_call(csi2rx->source_subdev, video, s_stream, true);
- if (ret)
- goto err_disable_pclk;
-
/* Enable DPHY clk and data lanes. */
if (csi2rx->dphy) {
reg = CSI2RX_DPHY_CL_EN | CSI2RX_DPHY_CL_RST;
@@ -256,6 +252,13 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx)
}

writel(reg, csi2rx->base + CSI2RX_DPHY_LANE_CTRL_REG);
+
+ ret = csi2rx_configure_ext_dphy(csi2rx);
+ if (ret) {
+ dev_err(csi2rx->dev,
+ "Failed to configure external DPHY: %d\n", ret);
+ goto err_disable_pclk;
+ }
}

/*
@@ -295,14 +298,9 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx)

reset_control_deassert(csi2rx->sys_rst);

- if (csi2rx->dphy) {
- ret = csi2rx_configure_ext_dphy(csi2rx);
- if (ret) {
- dev_err(csi2rx->dev,
- "Failed to configure external DPHY: %d\n", ret);
- goto err_disable_sysclk;
- }
- }
+ ret = v4l2_subdev_call(csi2rx->source_subdev, video, s_stream, true);
+ if (ret)
+ goto err_disable_sysclk;

clk_disable_unprepare(csi2rx->p_clk);

@@ -316,6 +314,10 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx)
clk_disable_unprepare(csi2rx->pixel_clk[i - 1]);
}

+ if (csi2rx->dphy) {
+ writel(0, csi2rx->base + CSI2RX_DPHY_LANE_CTRL_REG);
+ phy_power_off(csi2rx->dphy);
+ }
err_disable_pclk:
clk_disable_unprepare(csi2rx->p_clk);


--
2.43.0


2024-02-22 11:34:58

by Jai Luthra

[permalink] [raw]
Subject: [PATCH RFC 04/21] dt-bindings: media: ti,j721e-csi2rx-shim: Support 32 dma chans

The CSI2RX SHIM IP can support a maximum of 32x DMA channels.

These can be used to split incoming "streams" of data on the CSI-RX
port, distinguished by MIPI Virtual Channel (or Data Type), into
different locations in memory (/dev/videoX nodes).

Actual number of DMA channels reserved is different for each SoC
integrating this IP, but a maximum of 32x channels are always available
in this IP's register space, so set minimum as 1 and maximum as 32.

Link: https://www.ti.com/lit/pdf/spruiv7
Signed-off-by: Jai Luthra <[email protected]>
---
.../bindings/media/ti,j721e-csi2rx-shim.yaml | 40 ++++++++++++++++++++--
1 file changed, 37 insertions(+), 3 deletions(-)

diff --git a/Documentation/devicetree/bindings/media/ti,j721e-csi2rx-shim.yaml b/Documentation/devicetree/bindings/media/ti,j721e-csi2rx-shim.yaml
index f762fdc05e4d..e47dab7b959e 100644
--- a/Documentation/devicetree/bindings/media/ti,j721e-csi2rx-shim.yaml
+++ b/Documentation/devicetree/bindings/media/ti,j721e-csi2rx-shim.yaml
@@ -20,11 +20,45 @@ properties:
const: ti,j721e-csi2rx-shim

dmas:
- maxItems: 1
+ minItems: 1
+ maxItems: 32

dma-names:
+ minItems: 1
+ maxItems: 32
items:
- const: rx0
+ - const: rx1
+ - const: rx2
+ - const: rx3
+ - const: rx4
+ - const: rx5
+ - const: rx6
+ - const: rx7
+ - const: rx8
+ - const: rx9
+ - const: rx10
+ - const: rx11
+ - const: rx12
+ - const: rx13
+ - const: rx14
+ - const: rx15
+ - const: rx16
+ - const: rx17
+ - const: rx18
+ - const: rx19
+ - const: rx20
+ - const: rx21
+ - const: rx22
+ - const: rx23
+ - const: rx24
+ - const: rx25
+ - const: rx26
+ - const: rx27
+ - const: rx28
+ - const: rx29
+ - const: rx30
+ - const: rx31

reg:
maxItems: 1
@@ -62,8 +96,8 @@ examples:

ti_csi2rx0: ticsi2rx@4500000 {
compatible = "ti,j721e-csi2rx-shim";
- dmas = <&main_udmap 0x4940>;
- dma-names = "rx0";
+ dmas = <&main_udmap 0x4940>, <&main_udmap 0x4941>;
+ dma-names = "rx0", "rx1";
reg = <0x4500000 0x1000>;
power-domains = <&k3_pds 26 TI_SCI_PD_EXCLUSIVE>;
#address-cells = <1>;

--
2.43.0


2024-02-22 11:35:00

by Jai Luthra

[permalink] [raw]
Subject: [PATCH RFC 09/21] SQUASH: media: ti: j721e-csi2rx: Fix link validation for multi-stream

With the new architecture, multiple video nodes are connected to output
pads of the ticsi2rx subdev. Fix the link validation for such scenarios.

Signed-off-by: Jai Luthra <[email protected]>
---
.../media/platform/ti/j721e-csi2rx/j721e-csi2rx.c | 31 ++++++++++++++--------
1 file changed, 20 insertions(+), 11 deletions(-)

diff --git a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
index 42eb1bff21e2..1443338f4134 100644
--- a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
+++ b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
@@ -1133,45 +1133,54 @@ static int ti_csi2rx_link_validate(struct media_link *link)
struct v4l2_subdev_format source_fmt = {
.which = V4L2_SUBDEV_FORMAT_ACTIVE,
.pad = link->source->index,
+ .stream = 0,
};
+ struct v4l2_subdev_state *state;
const struct ti_csi2rx_fmt *ti_fmt;
int ret;

- ret = v4l2_subdev_call_state_active(&csi->subdev, pad,
- get_fmt, &source_fmt);
- if (ret)
- return ret;
+ state = v4l2_subdev_lock_and_get_active_state(&csi->subdev);
+ ret = v4l2_subdev_call(&csi->subdev, pad, get_fmt, state, &source_fmt);
+ v4l2_subdev_unlock_state(state);
+
+ if (ret) {
+ dev_dbg(csi->dev,
+ "Skipping validation as no format present on \"%s\":%u:0\n",
+ link->source->entity->name, link->source->index);
+ return 0;
+ }

if (source_fmt.format.width != csi_fmt->width) {
- dev_dbg(csi->dev, "Width does not match (source %u, sink %u)\n",
+ dev_err(csi->dev, "Width does not match (source %u, sink %u)\n",
source_fmt.format.width, csi_fmt->width);
return -EPIPE;
}

if (source_fmt.format.height != csi_fmt->height) {
- dev_dbg(csi->dev, "Height does not match (source %u, sink %u)\n",
+ dev_err(csi->dev, "Height does not match (source %u, sink %u)\n",
source_fmt.format.height, csi_fmt->height);
return -EPIPE;
}

if (source_fmt.format.field != csi_fmt->field &&
csi_fmt->field != V4L2_FIELD_NONE) {
- dev_dbg(csi->dev, "Field does not match (source %u, sink %u)\n",
+ dev_err(csi->dev, "Field does not match (source %u, sink %u)\n",
source_fmt.format.field, csi_fmt->field);
return -EPIPE;
}

ti_fmt = find_format_by_code(source_fmt.format.code);
if (!ti_fmt) {
- dev_dbg(csi->dev, "Media bus format 0x%x not supported\n",
+ dev_err(csi->dev, "Media bus format 0x%x not supported\n",
source_fmt.format.code);
return -EPIPE;
}

if (ti_fmt->fourcc != csi_fmt->pixelformat) {
- dev_dbg(csi->dev,
- "Cannot transform source fmt 0x%x to sink fmt 0x%x\n",
- ti_fmt->fourcc, csi_fmt->pixelformat);
+ dev_err(csi->dev,
+ "Cannot transform \"%s\":%u format %p4cc to %p4cc\n",
+ link->source->entity->name, link->source->index,
+ &ti_fmt->fourcc, &csi_fmt->pixelformat);
return -EPIPE;
}


--
2.43.0


2024-02-22 11:35:17

by Jai Luthra

[permalink] [raw]
Subject: [PATCH RFC 08/21] media: ti: j721e-csi2rx: add a subdev for the core device

With single stream capture, it was simpler to use the video device as
the media entity representing the main TI CSI2RX device. Now with multi
stream capture coming into the picture, the model has shifted to each
video device having a link to the main device's subdev. The routing
would then be set on this subdev.

Add this subdev, link each context to this subdev's entity and link the
subdev's entity to the source. Also add an array of media pads. It will
have one sink pad and source pads equal to the number of contexts.

Co-developed-by: Pratyush Yadav <[email protected]>
Signed-off-by: Pratyush Yadav <[email protected]>
Signed-off-by: Jai Luthra <[email protected]>
---
.../media/platform/ti/j721e-csi2rx/j721e-csi2rx.c | 217 +++++++++++++++++++--
1 file changed, 198 insertions(+), 19 deletions(-)

diff --git a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
index 80d7066100bf..42eb1bff21e2 100644
--- a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
+++ b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
@@ -51,6 +51,11 @@
#define MAX_WIDTH_BYTES SZ_16K
#define MAX_HEIGHT_LINES SZ_16K

+#define TI_CSI2RX_PAD_SINK 0
+#define TI_CSI2RX_PAD_FIRST_SOURCE 1
+#define TI_CSI2RX_NUM_SOURCE_PADS 1
+#define TI_CSI2RX_NUM_PADS (1 + TI_CSI2RX_NUM_SOURCE_PADS)
+
#define DRAIN_TIMEOUT_MS 50
#define DRAIN_BUFFER_SIZE SZ_32K

@@ -97,6 +102,7 @@ struct ti_csi2rx_ctx {
struct mutex mutex; /* To serialize ioctls. */
struct v4l2_format v_fmt;
struct ti_csi2rx_dma dma;
+ struct media_pad pad;
u32 sequence;
u32 idx;
};
@@ -104,12 +110,15 @@ struct ti_csi2rx_ctx {
struct ti_csi2rx_dev {
struct device *dev;
void __iomem *shim;
+ struct mutex mutex; /* To serialize ioctls. */
+ unsigned int enable_count;
struct v4l2_device v4l2_dev;
struct media_device mdev;
struct media_pipeline pipe;
- struct media_pad pad;
+ struct media_pad pads[TI_CSI2RX_NUM_PADS];
struct v4l2_async_notifier notifier;
struct v4l2_subdev *source;
+ struct v4l2_subdev subdev;
struct ti_csi2rx_ctx ctx[TI_CSI2RX_NUM_CTX];
/* Buffer to drain stale data from PSI-L endpoint */
struct {
@@ -455,6 +464,15 @@ static int csi_async_notifier_complete(struct v4l2_async_notifier *notifier)
struct ti_csi2rx_dev *csi = dev_get_drvdata(notifier->v4l2_dev->dev);
int ret, i;

+ /* Create link from source to subdev */
+ ret = v4l2_create_fwnode_links_to_pad(csi->source,
+ &csi->pads[TI_CSI2RX_PAD_SINK],
+ MEDIA_LNK_FL_IMMUTABLE |
+ MEDIA_LNK_FL_ENABLED);
+ if (ret)
+ return ret;
+
+ /* Create and link video nodes for all DMA contexts */
for (i = 0; i < TI_CSI2RX_NUM_CTX; i++) {
struct ti_csi2rx_ctx *ctx = &csi->ctx[i];
struct video_device *vdev = &ctx->vdev;
@@ -462,13 +480,17 @@ static int csi_async_notifier_complete(struct v4l2_async_notifier *notifier)
ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
if (ret)
goto unregister_dev;
- }

- ret = v4l2_create_fwnode_links_to_pad(csi->source, &csi->pad,
- MEDIA_LNK_FL_IMMUTABLE |
- MEDIA_LNK_FL_ENABLED);
- if (ret)
- goto unregister_dev;
+ ret = media_create_pad_link(&csi->subdev.entity,
+ TI_CSI2RX_PAD_FIRST_SOURCE + ctx->idx,
+ &vdev->entity, 0,
+ MEDIA_LNK_FL_IMMUTABLE |
+ MEDIA_LNK_FL_ENABLED);
+ if (ret) {
+ video_unregister_device(vdev);
+ goto unregister_dev;
+ }
+ }

ret = v4l2_device_register_subdev_nodes(&csi->v4l2_dev);
if (ret)
@@ -883,7 +905,7 @@ static int ti_csi2rx_start_streaming(struct vb2_queue *vq, unsigned int count)
dma->state = TI_CSI2RX_DMA_ACTIVE;
spin_unlock_irqrestore(&dma->lock, flags);

- ret = v4l2_subdev_call(csi->source, video, s_stream, 1);
+ ret = v4l2_subdev_call(&csi->subdev, video, s_stream, 1);
if (ret)
goto err_dma;

@@ -911,7 +933,7 @@ static void ti_csi2rx_stop_streaming(struct vb2_queue *vq)
writel(0, csi->shim + SHIM_CNTL);
writel(0, csi->shim + SHIM_DMACNTX(ctx->idx));

- ret = v4l2_subdev_call(csi->source, video, s_stream, 0);
+ ret = v4l2_subdev_call(&csi->subdev, video, s_stream, 0);
if (ret)
dev_err(csi->dev, "Failed to stop subdev stream\n");

@@ -929,6 +951,119 @@ static const struct vb2_ops csi_vb2_qops = {
.wait_finish = vb2_ops_wait_finish,
};

+static inline struct ti_csi2rx_dev *to_csi2rx_dev(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct ti_csi2rx_dev, subdev);
+}
+
+static int ti_csi2rx_sd_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *format)
+{
+ struct v4l2_mbus_framefmt *fmt;
+ int ret = 0;
+
+ /* No transcoding, don't allow setting source fmt */
+ if (format->pad >= TI_CSI2RX_PAD_FIRST_SOURCE)
+ return v4l2_subdev_get_fmt(sd, state, format);
+
+ if (!find_format_by_code(format->format.code))
+ format->format.code = ti_csi2rx_formats[0].code;
+
+ format->format.field = V4L2_FIELD_NONE;
+
+ fmt = v4l2_subdev_state_get_format(state, format->pad, format->stream);
+ if (!fmt) {
+ ret = -EINVAL;
+ goto out;
+ }
+ *fmt = format->format;
+
+ fmt = v4l2_subdev_state_get_format(state, TI_CSI2RX_PAD_FIRST_SOURCE,
+ format->stream);
+ if (!fmt) {
+ ret = -EINVAL;
+ goto out;
+ }
+ *fmt = format->format;
+
+out:
+ return ret;
+}
+
+static int ti_csi2rx_sd_init_state(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state)
+{
+ struct v4l2_subdev_format format = {
+ .pad = TI_CSI2RX_PAD_SINK,
+ .format = {
+ .width = 640,
+ .height = 480,
+ .code = MEDIA_BUS_FMT_UYVY8_1X16,
+ .field = V4L2_FIELD_NONE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .ycbcr_enc = V4L2_YCBCR_ENC_601,
+ .quantization = V4L2_QUANTIZATION_LIM_RANGE,
+ .xfer_func = V4L2_XFER_FUNC_SRGB,
+ },
+ };
+
+ return ti_csi2rx_sd_set_fmt(sd, state, &format);
+}
+
+static int ti_csi2rx_sd_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct ti_csi2rx_dev *csi = to_csi2rx_dev(sd);
+ int ret = 0;
+
+ mutex_lock(&csi->mutex);
+
+ if (enable) {
+ if (csi->enable_count > 0) {
+ csi->enable_count++;
+ goto out;
+ }
+
+ ret = v4l2_subdev_call(csi->source, video, s_stream, 1);
+ if (ret)
+ goto out;
+
+ csi->enable_count++;
+ } else {
+ if (csi->enable_count == 0) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (--csi->enable_count > 0)
+ goto out;
+
+ ret = v4l2_subdev_call(csi->source, video, s_stream, 0);
+ }
+
+out:
+ mutex_unlock(&csi->mutex);
+ return ret;
+}
+
+static const struct v4l2_subdev_pad_ops ti_csi2rx_subdev_pad_ops = {
+ .get_fmt = v4l2_subdev_get_fmt,
+ .set_fmt = ti_csi2rx_sd_set_fmt,
+};
+
+static const struct v4l2_subdev_video_ops ti_csi2rx_subdev_video_ops = {
+ .s_stream = ti_csi2rx_sd_s_stream,
+};
+
+static const struct v4l2_subdev_ops ti_csi2rx_subdev_ops = {
+ .video = &ti_csi2rx_subdev_video_ops,
+ .pad = &ti_csi2rx_subdev_pad_ops,
+};
+
+static const struct v4l2_subdev_internal_ops ti_csi2rx_internal_ops = {
+ .init_state = ti_csi2rx_sd_init_state,
+};
+
static void ti_csi2rx_cleanup_dma(struct ti_csi2rx_ctx *ctx)
{
dma_release_channel(ctx->dma.chan);
@@ -936,6 +1071,7 @@ static void ti_csi2rx_cleanup_dma(struct ti_csi2rx_ctx *ctx)

static void ti_csi2rx_cleanup_v4l2(struct ti_csi2rx_dev *csi)
{
+ v4l2_subdev_cleanup(&csi->subdev);
media_device_unregister(&csi->mdev);
v4l2_device_unregister(&csi->v4l2_dev);
media_device_cleanup(&csi->mdev);
@@ -1001,7 +1137,7 @@ static int ti_csi2rx_link_validate(struct media_link *link)
const struct ti_csi2rx_fmt *ti_fmt;
int ret;

- ret = v4l2_subdev_call_state_active(csi->source, pad,
+ ret = v4l2_subdev_call_state_active(&csi->subdev, pad,
get_fmt, &source_fmt);
if (ret)
return ret;
@@ -1046,6 +1182,10 @@ static const struct media_entity_operations ti_csi2rx_video_entity_ops = {
.link_validate = ti_csi2rx_link_validate,
};

+static const struct media_entity_operations ti_csi2rx_subdev_entity_ops = {
+ .link_validate = v4l2_subdev_link_validate,
+};
+
static int ti_csi2rx_init_dma(struct ti_csi2rx_ctx *ctx)
{
struct dma_slave_config cfg = {
@@ -1077,7 +1217,8 @@ static int ti_csi2rx_init_dma(struct ti_csi2rx_ctx *ctx)
static int ti_csi2rx_v4l2_init(struct ti_csi2rx_dev *csi)
{
struct media_device *mdev = &csi->mdev;
- int ret;
+ struct v4l2_subdev *sd = &csi->subdev;
+ int ret, i;

mdev->dev = csi->dev;
mdev->hw_revision = 1;
@@ -1089,16 +1230,50 @@ static int ti_csi2rx_v4l2_init(struct ti_csi2rx_dev *csi)

ret = v4l2_device_register(csi->dev, &csi->v4l2_dev);
if (ret)
- return ret;
+ goto cleanup_media;

ret = media_device_register(mdev);
- if (ret) {
- v4l2_device_unregister(&csi->v4l2_dev);
- media_device_cleanup(mdev);
- return ret;
- }
+ if (ret)
+ goto unregister_v4l2;
+
+ v4l2_subdev_init(sd, &ti_csi2rx_subdev_ops);
+ sd->internal_ops = &ti_csi2rx_internal_ops;
+ sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
+ sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
+ strscpy(sd->name, dev_name(csi->dev), sizeof(sd->name));
+ sd->dev = csi->dev;
+ sd->entity.ops = &ti_csi2rx_subdev_entity_ops;
+
+ csi->pads[TI_CSI2RX_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+
+ for (i = TI_CSI2RX_PAD_FIRST_SOURCE; i < TI_CSI2RX_NUM_PADS; i++)
+ csi->pads[i].flags = MEDIA_PAD_FL_SOURCE;
+
+ ret = media_entity_pads_init(&sd->entity, ARRAY_SIZE(csi->pads),
+ csi->pads);
+ if (ret)
+ goto unregister_media;
+
+ ret = v4l2_subdev_init_finalize(sd);
+ if (ret)
+ goto unregister_media;
+
+ ret = v4l2_device_register_subdev(&csi->v4l2_dev, sd);
+ if (ret)
+ goto cleanup_subdev;

return 0;
+
+cleanup_subdev:
+ v4l2_subdev_cleanup(sd);
+unregister_media:
+ media_device_unregister(mdev);
+unregister_v4l2:
+ v4l2_device_unregister(&csi->v4l2_dev);
+cleanup_media:
+ media_device_cleanup(mdev);
+
+ return ret;
}

static int ti_csi2rx_init_ctx(struct ti_csi2rx_ctx *ctx)
@@ -1125,9 +1300,9 @@ static int ti_csi2rx_init_ctx(struct ti_csi2rx_ctx *ctx)

ti_csi2rx_fill_fmt(fmt, &ctx->v_fmt);

- csi->pad.flags = MEDIA_PAD_FL_SINK;
+ ctx->pad.flags = MEDIA_PAD_FL_SINK;
vdev->entity.ops = &ti_csi2rx_video_entity_ops;
- ret = media_entity_pads_init(&ctx->vdev.entity, 1, &csi->pad);
+ ret = media_entity_pads_init(&ctx->vdev.entity, 1, &ctx->pad);
if (ret)
return ret;

@@ -1183,6 +1358,8 @@ static int ti_csi2rx_probe(struct platform_device *pdev)
if (!csi->drain.vaddr)
return -ENOMEM;

+ mutex_init(&csi->mutex);
+
ret = ti_csi2rx_v4l2_init(csi);
if (ret)
goto err_v4l2;
@@ -1215,6 +1392,7 @@ static int ti_csi2rx_probe(struct platform_device *pdev)
ti_csi2rx_cleanup_ctx(&csi->ctx[i]);

err_v4l2:
+ mutex_destroy(&csi->mutex);
dma_free_coherent(csi->dev, csi->drain.len, csi->drain.vaddr,
csi->drain.paddr);
return ret;
@@ -1235,6 +1413,7 @@ static int ti_csi2rx_remove(struct platform_device *pdev)

ti_csi2rx_cleanup_notifier(csi);
ti_csi2rx_cleanup_v4l2(csi);
+ mutex_destroy(&csi->mutex);
dma_free_coherent(csi->dev, csi->drain.len, csi->drain.vaddr,
csi->drain.paddr);


--
2.43.0


2024-02-22 11:35:44

by Jai Luthra

[permalink] [raw]
Subject: [PATCH RFC 05/21] media: ti: j721e-csi2rx: separate out device and context

The TI CSI2RX wrapper has two parts: the main device and the DMA
contexts. The driver was originally written with single camera capture
in mind, so only one DMA context was needed. For the sake of simplicity,
the context specific stuff was not modeled different to the main device.

To enable multiplexed stream capture, the contexts need to be separated
out from the main device. Create a struct ti_csi2rx_ctx that holds the
DMA context specific things. Separate out functions handling the device
and context related functionality.

Co-developed-by: Pratyush Yadav <[email protected]>
Signed-off-by: Pratyush Yadav <[email protected]>
Signed-off-by: Jai Luthra <[email protected]>
---
.../media/platform/ti/j721e-csi2rx/j721e-csi2rx.c | 422 ++++++++++++---------
1 file changed, 240 insertions(+), 182 deletions(-)

diff --git a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
index e2cac8d73d78..51fac664d01c 100644
--- a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
+++ b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
@@ -40,6 +40,8 @@
#define SHIM_PSI_CFG0_DST_TAG GENMASK(31, 16)

#define PSIL_WORD_SIZE_BYTES 16
+#define TI_CSI2RX_NUM_CTX 1
+
/*
* There are no hard limits on the width or height. The DMA engine can handle
* all sizes. The max width and height are arbitrary numbers for this driver.
@@ -64,7 +66,7 @@ struct ti_csi2rx_buffer {
/* Common v4l2 buffer. Must be first. */
struct vb2_v4l2_buffer vb;
struct list_head list;
- struct ti_csi2rx_dev *csi;
+ struct ti_csi2rx_ctx *ctx;
};

enum ti_csi2rx_dma_state {
@@ -84,29 +86,37 @@ struct ti_csi2rx_dma {
* Queue of buffers submitted to DMA engine.
*/
struct list_head submitted;
- /* Buffer to drain stale data from PSI-L endpoint */
- struct {
- void *vaddr;
- dma_addr_t paddr;
- size_t len;
- } drain;
+};
+
+struct ti_csi2rx_dev;
+
+struct ti_csi2rx_ctx {
+ struct ti_csi2rx_dev *csi;
+ struct video_device vdev;
+ struct vb2_queue vidq;
+ struct mutex mutex; /* To serialize ioctls. */
+ struct v4l2_format v_fmt;
+ struct ti_csi2rx_dma dma;
+ u32 sequence;
+ u32 idx;
};

struct ti_csi2rx_dev {
struct device *dev;
void __iomem *shim;
struct v4l2_device v4l2_dev;
- struct video_device vdev;
struct media_device mdev;
struct media_pipeline pipe;
struct media_pad pad;
struct v4l2_async_notifier notifier;
struct v4l2_subdev *source;
- struct vb2_queue vidq;
- struct mutex mutex; /* To serialize ioctls. */
- struct v4l2_format v_fmt;
- struct ti_csi2rx_dma dma;
- u32 sequence;
+ struct ti_csi2rx_ctx ctx[TI_CSI2RX_NUM_CTX];
+ /* Buffer to drain stale data from PSI-L endpoint */
+ struct {
+ void *vaddr;
+ dma_addr_t paddr;
+ size_t len;
+ } drain;
};

static const struct ti_csi2rx_fmt ti_csi2rx_formats[] = {
@@ -236,7 +246,7 @@ static const struct ti_csi2rx_fmt ti_csi2rx_formats[] = {
};

/* Forward declaration needed by ti_csi2rx_dma_callback. */
-static int ti_csi2rx_start_dma(struct ti_csi2rx_dev *csi,
+static int ti_csi2rx_start_dma(struct ti_csi2rx_ctx *ctx,
struct ti_csi2rx_buffer *buf);

static const struct ti_csi2rx_fmt *find_format_by_fourcc(u32 pixelformat)
@@ -326,7 +336,7 @@ static int ti_csi2rx_enum_fmt_vid_cap(struct file *file, void *priv,
static int ti_csi2rx_g_fmt_vid_cap(struct file *file, void *prov,
struct v4l2_format *f)
{
- struct ti_csi2rx_dev *csi = video_drvdata(file);
+ struct ti_csi2rx_ctx *csi = video_drvdata(file);

*f = csi->v_fmt;

@@ -357,7 +367,7 @@ static int ti_csi2rx_try_fmt_vid_cap(struct file *file, void *priv,
static int ti_csi2rx_s_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
- struct ti_csi2rx_dev *csi = video_drvdata(file);
+ struct ti_csi2rx_ctx *csi = video_drvdata(file);
struct vb2_queue *q = &csi->vidq;
int ret;

@@ -443,25 +453,33 @@ static int csi_async_notifier_bound(struct v4l2_async_notifier *notifier,
static int csi_async_notifier_complete(struct v4l2_async_notifier *notifier)
{
struct ti_csi2rx_dev *csi = dev_get_drvdata(notifier->v4l2_dev->dev);
- struct video_device *vdev = &csi->vdev;
- int ret;
+ int ret, i;

- ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
- if (ret)
- return ret;
+ for (i = 0; i < TI_CSI2RX_NUM_CTX; i++) {
+ struct ti_csi2rx_ctx *ctx = &csi->ctx[i];
+ struct video_device *vdev = &ctx->vdev;

- ret = v4l2_create_fwnode_links_to_pad(csi->source, &csi->pad,
- MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED);
-
- if (ret) {
- video_unregister_device(vdev);
- return ret;
+ ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
+ if (ret)
+ goto unregister_dev;
}

+ ret = v4l2_create_fwnode_links_to_pad(csi->source, &csi->pad,
+ MEDIA_LNK_FL_IMMUTABLE |
+ MEDIA_LNK_FL_ENABLED);
+ if (ret)
+ goto unregister_dev;
+
ret = v4l2_device_register_subdev_nodes(&csi->v4l2_dev);
if (ret)
- video_unregister_device(vdev);
+ goto unregister_dev;

+ return 0;
+
+unregister_dev:
+ i--;
+ for (; i >= 0; i--)
+ video_unregister_device(&csi->ctx[i].vdev);
return ret;
}

@@ -507,12 +525,13 @@ static int ti_csi2rx_notifier_register(struct ti_csi2rx_dev *csi)
return 0;
}

-static void ti_csi2rx_setup_shim(struct ti_csi2rx_dev *csi)
+static void ti_csi2rx_setup_shim(struct ti_csi2rx_ctx *ctx)
{
+ struct ti_csi2rx_dev *csi = ctx->csi;
const struct ti_csi2rx_fmt *fmt;
unsigned int reg;

- fmt = find_format_by_fourcc(csi->v_fmt.fmt.pix.pixelformat);
+ fmt = find_format_by_fourcc(ctx->v_fmt.fmt.pix.pixelformat);

/* De-assert the pixel interface reset. */
reg = SHIM_CNTL_PIX_RST;
@@ -579,8 +598,9 @@ static void ti_csi2rx_drain_callback(void *param)
* To prevent that stale data corrupting the subsequent transactions, it is
* required to issue DMA requests to drain it out.
*/
-static int ti_csi2rx_drain_dma(struct ti_csi2rx_dev *csi)
+static int ti_csi2rx_drain_dma(struct ti_csi2rx_ctx *ctx)
{
+ struct ti_csi2rx_dev *csi = ctx->csi;
struct dma_async_tx_descriptor *desc;
struct completion drain_complete;
dma_cookie_t cookie;
@@ -588,8 +608,8 @@ static int ti_csi2rx_drain_dma(struct ti_csi2rx_dev *csi)

init_completion(&drain_complete);

- desc = dmaengine_prep_slave_single(csi->dma.chan, csi->dma.drain.paddr,
- csi->dma.drain.len, DMA_DEV_TO_MEM,
+ desc = dmaengine_prep_slave_single(ctx->dma.chan, csi->drain.paddr,
+ csi->drain.len, DMA_DEV_TO_MEM,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!desc) {
ret = -EIO;
@@ -604,11 +624,11 @@ static int ti_csi2rx_drain_dma(struct ti_csi2rx_dev *csi)
if (ret)
goto out;

- dma_async_issue_pending(csi->dma.chan);
+ dma_async_issue_pending(ctx->dma.chan);

if (!wait_for_completion_timeout(&drain_complete,
msecs_to_jiffies(DRAIN_TIMEOUT_MS))) {
- dmaengine_terminate_sync(csi->dma.chan);
+ dmaengine_terminate_sync(ctx->dma.chan);
dev_dbg(csi->dev, "DMA transfer timed out for drain buffer\n");
ret = -ETIMEDOUT;
goto out;
@@ -620,8 +640,8 @@ static int ti_csi2rx_drain_dma(struct ti_csi2rx_dev *csi)
static void ti_csi2rx_dma_callback(void *param)
{
struct ti_csi2rx_buffer *buf = param;
- struct ti_csi2rx_dev *csi = buf->csi;
- struct ti_csi2rx_dma *dma = &csi->dma;
+ struct ti_csi2rx_ctx *ctx = buf->ctx;
+ struct ti_csi2rx_dma *dma = &ctx->dma;
unsigned long flags;

/*
@@ -629,7 +649,7 @@ static void ti_csi2rx_dma_callback(void *param)
* hardware monitor registers.
*/
buf->vb.vb2_buf.timestamp = ktime_get_ns();
- buf->vb.sequence = csi->sequence++;
+ buf->vb.sequence = ctx->sequence++;

spin_lock_irqsave(&dma->lock, flags);

@@ -641,8 +661,9 @@ static void ti_csi2rx_dma_callback(void *param)
while (!list_empty(&dma->queue)) {
buf = list_entry(dma->queue.next, struct ti_csi2rx_buffer, list);

- if (ti_csi2rx_start_dma(csi, buf)) {
- dev_err(csi->dev, "Failed to queue the next buffer for DMA\n");
+ if (ti_csi2rx_start_dma(ctx, buf)) {
+ dev_err(ctx->csi->dev,
+ "Failed to queue the next buffer for DMA\n");
vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
} else {
list_move_tail(&buf->list, &dma->submitted);
@@ -655,17 +676,17 @@ static void ti_csi2rx_dma_callback(void *param)
spin_unlock_irqrestore(&dma->lock, flags);
}

-static int ti_csi2rx_start_dma(struct ti_csi2rx_dev *csi,
+static int ti_csi2rx_start_dma(struct ti_csi2rx_ctx *ctx,
struct ti_csi2rx_buffer *buf)
{
unsigned long addr;
struct dma_async_tx_descriptor *desc;
- size_t len = csi->v_fmt.fmt.pix.sizeimage;
+ size_t len = ctx->v_fmt.fmt.pix.sizeimage;
dma_cookie_t cookie;
int ret = 0;

addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0);
- desc = dmaengine_prep_slave_single(csi->dma.chan, addr, len,
+ desc = dmaengine_prep_slave_single(ctx->dma.chan, addr, len,
DMA_DEV_TO_MEM,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!desc)
@@ -679,20 +700,20 @@ static int ti_csi2rx_start_dma(struct ti_csi2rx_dev *csi,
if (ret)
return ret;

- dma_async_issue_pending(csi->dma.chan);
+ dma_async_issue_pending(ctx->dma.chan);

return 0;
}

-static void ti_csi2rx_stop_dma(struct ti_csi2rx_dev *csi)
+static void ti_csi2rx_stop_dma(struct ti_csi2rx_ctx *ctx)
{
- struct ti_csi2rx_dma *dma = &csi->dma;
+ struct ti_csi2rx_dma *dma = &ctx->dma;
enum ti_csi2rx_dma_state state;
unsigned long flags;
int ret;

spin_lock_irqsave(&dma->lock, flags);
- state = csi->dma.state;
+ state = ctx->dma.state;
dma->state = TI_CSI2RX_DMA_STOPPED;
spin_unlock_irqrestore(&dma->lock, flags);

@@ -703,30 +724,30 @@ static void ti_csi2rx_stop_dma(struct ti_csi2rx_dev *csi)
* is stopped, as the module-level pixel reset cannot be
* enforced before terminating DMA.
*/
- ret = ti_csi2rx_drain_dma(csi);
+ ret = ti_csi2rx_drain_dma(ctx);
if (ret && ret != -ETIMEDOUT)
- dev_warn(csi->dev,
+ dev_warn(ctx->csi->dev,
"Failed to drain DMA. Next frame might be bogus\n");
}

- ret = dmaengine_terminate_sync(csi->dma.chan);
+ ret = dmaengine_terminate_sync(ctx->dma.chan);
if (ret)
- dev_err(csi->dev, "Failed to stop DMA: %d\n", ret);
+ dev_err(ctx->csi->dev, "Failed to stop DMA: %d\n", ret);
}

-static void ti_csi2rx_cleanup_buffers(struct ti_csi2rx_dev *csi,
+static void ti_csi2rx_cleanup_buffers(struct ti_csi2rx_ctx *ctx,
enum vb2_buffer_state state)
{
- struct ti_csi2rx_dma *dma = &csi->dma;
+ struct ti_csi2rx_dma *dma = &ctx->dma;
struct ti_csi2rx_buffer *buf, *tmp;
unsigned long flags;

spin_lock_irqsave(&dma->lock, flags);
- list_for_each_entry_safe(buf, tmp, &csi->dma.queue, list) {
+ list_for_each_entry_safe(buf, tmp, &ctx->dma.queue, list) {
list_del(&buf->list);
vb2_buffer_done(&buf->vb.vb2_buf, state);
}
- list_for_each_entry_safe(buf, tmp, &csi->dma.submitted, list) {
+ list_for_each_entry_safe(buf, tmp, &ctx->dma.submitted, list) {
list_del(&buf->list);
vb2_buffer_done(&buf->vb.vb2_buf, state);
}
@@ -737,8 +758,8 @@ static int ti_csi2rx_queue_setup(struct vb2_queue *q, unsigned int *nbuffers,
unsigned int *nplanes, unsigned int sizes[],
struct device *alloc_devs[])
{
- struct ti_csi2rx_dev *csi = vb2_get_drv_priv(q);
- unsigned int size = csi->v_fmt.fmt.pix.sizeimage;
+ struct ti_csi2rx_ctx *ctx = vb2_get_drv_priv(q);
+ unsigned int size = ctx->v_fmt.fmt.pix.sizeimage;

if (*nplanes) {
if (sizes[0] < size)
@@ -754,11 +775,11 @@ static int ti_csi2rx_queue_setup(struct vb2_queue *q, unsigned int *nbuffers,

static int ti_csi2rx_buffer_prepare(struct vb2_buffer *vb)
{
- struct ti_csi2rx_dev *csi = vb2_get_drv_priv(vb->vb2_queue);
- unsigned long size = csi->v_fmt.fmt.pix.sizeimage;
+ struct ti_csi2rx_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ unsigned long size = ctx->v_fmt.fmt.pix.sizeimage;

if (vb2_plane_size(vb, 0) < size) {
- dev_err(csi->dev, "Data will not fit into plane\n");
+ dev_err(ctx->csi->dev, "Data will not fit into plane\n");
return -EINVAL;
}

@@ -768,15 +789,15 @@ static int ti_csi2rx_buffer_prepare(struct vb2_buffer *vb)

static void ti_csi2rx_buffer_queue(struct vb2_buffer *vb)
{
- struct ti_csi2rx_dev *csi = vb2_get_drv_priv(vb->vb2_queue);
+ struct ti_csi2rx_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
struct ti_csi2rx_buffer *buf;
- struct ti_csi2rx_dma *dma = &csi->dma;
+ struct ti_csi2rx_dma *dma = &ctx->dma;
bool restart_dma = false;
unsigned long flags = 0;
int ret;

buf = container_of(vb, struct ti_csi2rx_buffer, vb.vb2_buf);
- buf->csi = csi;
+ buf->ctx = ctx;

spin_lock_irqsave(&dma->lock, flags);
/*
@@ -805,18 +826,18 @@ static void ti_csi2rx_buffer_queue(struct vb2_buffer *vb)
* the application and will only confuse it. Issue a DMA
* transaction to drain that up.
*/
- ret = ti_csi2rx_drain_dma(csi);
+ ret = ti_csi2rx_drain_dma(ctx);
if (ret && ret != -ETIMEDOUT)
- dev_warn(csi->dev,
+ dev_warn(ctx->csi->dev,
"Failed to drain DMA. Next frame might be bogus\n");

spin_lock_irqsave(&dma->lock, flags);
- ret = ti_csi2rx_start_dma(csi, buf);
+ ret = ti_csi2rx_start_dma(ctx, buf);
if (ret) {
vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
dma->state = TI_CSI2RX_DMA_IDLE;
spin_unlock_irqrestore(&dma->lock, flags);
- dev_err(csi->dev, "Failed to start DMA: %d\n", ret);
+ dev_err(ctx->csi->dev, "Failed to start DMA: %d\n", ret);
} else {
list_add_tail(&buf->list, &dma->submitted);
spin_unlock_irqrestore(&dma->lock, flags);
@@ -826,8 +847,9 @@ static void ti_csi2rx_buffer_queue(struct vb2_buffer *vb)

static int ti_csi2rx_start_streaming(struct vb2_queue *vq, unsigned int count)
{
- struct ti_csi2rx_dev *csi = vb2_get_drv_priv(vq);
- struct ti_csi2rx_dma *dma = &csi->dma;
+ struct ti_csi2rx_ctx *ctx = vb2_get_drv_priv(vq);
+ struct ti_csi2rx_dev *csi = ctx->csi;
+ struct ti_csi2rx_dma *dma = &ctx->dma;
struct ti_csi2rx_buffer *buf;
unsigned long flags;
int ret = 0;
@@ -839,18 +861,18 @@ static int ti_csi2rx_start_streaming(struct vb2_queue *vq, unsigned int count)
if (ret)
return ret;

- ret = video_device_pipeline_start(&csi->vdev, &csi->pipe);
+ ret = video_device_pipeline_start(&ctx->vdev, &csi->pipe);
if (ret)
goto err;

- ti_csi2rx_setup_shim(csi);
+ ti_csi2rx_setup_shim(ctx);

- csi->sequence = 0;
+ ctx->sequence = 0;

spin_lock_irqsave(&dma->lock, flags);
buf = list_entry(dma->queue.next, struct ti_csi2rx_buffer, list);

- ret = ti_csi2rx_start_dma(csi, buf);
+ ret = ti_csi2rx_start_dma(ctx, buf);
if (ret) {
dev_err(csi->dev, "Failed to start DMA: %d\n", ret);
spin_unlock_irqrestore(&dma->lock, flags);
@@ -868,22 +890,23 @@ static int ti_csi2rx_start_streaming(struct vb2_queue *vq, unsigned int count)
return 0;

err_dma:
- ti_csi2rx_stop_dma(csi);
+ ti_csi2rx_stop_dma(ctx);
err_pipeline:
- video_device_pipeline_stop(&csi->vdev);
+ video_device_pipeline_stop(&ctx->vdev);
writel(0, csi->shim + SHIM_CNTL);
writel(0, csi->shim + SHIM_DMACNTX);
err:
- ti_csi2rx_cleanup_buffers(csi, VB2_BUF_STATE_QUEUED);
+ ti_csi2rx_cleanup_buffers(ctx, VB2_BUF_STATE_QUEUED);
return ret;
}

static void ti_csi2rx_stop_streaming(struct vb2_queue *vq)
{
- struct ti_csi2rx_dev *csi = vb2_get_drv_priv(vq);
+ struct ti_csi2rx_ctx *ctx = vb2_get_drv_priv(vq);
+ struct ti_csi2rx_dev *csi = ctx->csi;
int ret;

- video_device_pipeline_stop(&csi->vdev);
+ video_device_pipeline_stop(&ctx->vdev);

writel(0, csi->shim + SHIM_CNTL);
writel(0, csi->shim + SHIM_DMACNTX);
@@ -892,8 +915,8 @@ static void ti_csi2rx_stop_streaming(struct vb2_queue *vq)
if (ret)
dev_err(csi->dev, "Failed to stop subdev stream\n");

- ti_csi2rx_stop_dma(csi);
- ti_csi2rx_cleanup_buffers(csi, VB2_BUF_STATE_ERROR);
+ ti_csi2rx_stop_dma(ctx);
+ ti_csi2rx_cleanup_buffers(ctx, VB2_BUF_STATE_ERROR);
}

static const struct vb2_ops csi_vb2_qops = {
@@ -906,27 +929,60 @@ static const struct vb2_ops csi_vb2_qops = {
.wait_finish = vb2_ops_wait_finish,
};

-static int ti_csi2rx_init_vb2q(struct ti_csi2rx_dev *csi)
+static void ti_csi2rx_cleanup_dma(struct ti_csi2rx_ctx *ctx)
{
- struct vb2_queue *q = &csi->vidq;
+ dma_release_channel(ctx->dma.chan);
+}
+
+static void ti_csi2rx_cleanup_v4l2(struct ti_csi2rx_dev *csi)
+{
+ media_device_unregister(&csi->mdev);
+ v4l2_device_unregister(&csi->v4l2_dev);
+ media_device_cleanup(&csi->mdev);
+}
+
+static void ti_csi2rx_cleanup_notifier(struct ti_csi2rx_dev *csi)
+{
+ v4l2_async_nf_unregister(&csi->notifier);
+ v4l2_async_nf_cleanup(&csi->notifier);
+}
+
+static void ti_csi2rx_cleanup_vb2q(struct ti_csi2rx_ctx *ctx)
+{
+ vb2_queue_release(&ctx->vidq);
+}
+
+static void ti_csi2rx_cleanup_ctx(struct ti_csi2rx_ctx *ctx)
+{
+ ti_csi2rx_cleanup_dma(ctx);
+ ti_csi2rx_cleanup_vb2q(ctx);
+
+ video_unregister_device(&ctx->vdev);
+
+ mutex_destroy(&ctx->mutex);
+}
+
+static int ti_csi2rx_init_vb2q(struct ti_csi2rx_ctx *ctx)
+{
+ struct vb2_queue *q = &ctx->vidq;
int ret;

q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
q->io_modes = VB2_MMAP | VB2_DMABUF;
- q->drv_priv = csi;
+ q->drv_priv = ctx;
q->buf_struct_size = sizeof(struct ti_csi2rx_buffer);
q->ops = &csi_vb2_qops;
q->mem_ops = &vb2_dma_contig_memops;
q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
- q->dev = dmaengine_get_dma_device(csi->dma.chan);
- q->lock = &csi->mutex;
+ q->dev = dmaengine_get_dma_device(ctx->dma.chan);
+ q->lock = &ctx->mutex;
q->min_queued_buffers = 1;

ret = vb2_queue_init(q);
if (ret)
return ret;

- csi->vdev.queue = q;
+ ctx->vdev.queue = q;

return 0;
}
@@ -935,8 +991,9 @@ static int ti_csi2rx_link_validate(struct media_link *link)
{
struct media_entity *entity = link->sink->entity;
struct video_device *vdev = media_entity_to_video_device(entity);
- struct ti_csi2rx_dev *csi = container_of(vdev, struct ti_csi2rx_dev, vdev);
- struct v4l2_pix_format *csi_fmt = &csi->v_fmt.fmt.pix;
+ struct ti_csi2rx_ctx *ctx = container_of(vdev, struct ti_csi2rx_ctx, vdev);
+ struct ti_csi2rx_dev *csi = ctx->csi;
+ struct v4l2_pix_format *csi_fmt = &ctx->v_fmt.fmt.pix;
struct v4l2_subdev_format source_fmt = {
.which = V4L2_SUBDEV_FORMAT_ACTIVE,
.pad = link->source->index,
@@ -989,47 +1046,69 @@ static const struct media_entity_operations ti_csi2rx_video_entity_ops = {
.link_validate = ti_csi2rx_link_validate,
};

-static int ti_csi2rx_init_dma(struct ti_csi2rx_dev *csi)
+static int ti_csi2rx_init_dma(struct ti_csi2rx_ctx *ctx)
{
struct dma_slave_config cfg = {
.src_addr_width = DMA_SLAVE_BUSWIDTH_16_BYTES,
};
int ret;

- INIT_LIST_HEAD(&csi->dma.queue);
- INIT_LIST_HEAD(&csi->dma.submitted);
- spin_lock_init(&csi->dma.lock);
+ INIT_LIST_HEAD(&ctx->dma.queue);
+ INIT_LIST_HEAD(&ctx->dma.submitted);
+ spin_lock_init(&ctx->dma.lock);

- csi->dma.state = TI_CSI2RX_DMA_STOPPED;
+ ctx->dma.state = TI_CSI2RX_DMA_STOPPED;

- csi->dma.chan = dma_request_chan(csi->dev, "rx0");
- if (IS_ERR(csi->dma.chan))
- return PTR_ERR(csi->dma.chan);
+ ctx->dma.chan = dma_request_chan(ctx->csi->dev, "rx0");
+ if (IS_ERR(ctx->dma.chan))
+ return PTR_ERR(ctx->dma.chan);

- ret = dmaengine_slave_config(csi->dma.chan, &cfg);
+ ret = dmaengine_slave_config(ctx->dma.chan, &cfg);
if (ret) {
- dma_release_channel(csi->dma.chan);
+ dma_release_channel(ctx->dma.chan);
return ret;
}

- csi->dma.drain.len = DRAIN_BUFFER_SIZE;
- csi->dma.drain.vaddr = dma_alloc_coherent(csi->dev, csi->dma.drain.len,
- &csi->dma.drain.paddr,
- GFP_KERNEL);
- if (!csi->dma.drain.vaddr)
- return -ENOMEM;
-
return 0;
}

static int ti_csi2rx_v4l2_init(struct ti_csi2rx_dev *csi)
{
struct media_device *mdev = &csi->mdev;
- struct video_device *vdev = &csi->vdev;
+ int ret;
+
+ mdev->dev = csi->dev;
+ mdev->hw_revision = 1;
+ strscpy(mdev->model, "TI-CSI2RX", sizeof(mdev->model));
+
+ media_device_init(mdev);
+
+ csi->v4l2_dev.mdev = mdev;
+
+ ret = v4l2_device_register(csi->dev, &csi->v4l2_dev);
+ if (ret)
+ return ret;
+
+ ret = media_device_register(mdev);
+ if (ret) {
+ v4l2_device_unregister(&csi->v4l2_dev);
+ media_device_cleanup(mdev);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ti_csi2rx_init_ctx(struct ti_csi2rx_ctx *ctx)
+{
+ struct ti_csi2rx_dev *csi = ctx->csi;
+ struct video_device *vdev = &ctx->vdev;
const struct ti_csi2rx_fmt *fmt;
- struct v4l2_pix_format *pix_fmt = &csi->v_fmt.fmt.pix;
+ struct v4l2_pix_format *pix_fmt = &ctx->v_fmt.fmt.pix;
int ret;

+ mutex_init(&ctx->mutex);
+
fmt = find_format_by_fourcc(V4L2_PIX_FMT_UYVY);
if (!fmt)
return -EINVAL;
@@ -1042,15 +1121,16 @@ static int ti_csi2rx_v4l2_init(struct ti_csi2rx_dev *csi)
pix_fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE,
pix_fmt->xfer_func = V4L2_XFER_FUNC_SRGB,

- ti_csi2rx_fill_fmt(fmt, &csi->v_fmt);
+ ti_csi2rx_fill_fmt(fmt, &ctx->v_fmt);

- mdev->dev = csi->dev;
- mdev->hw_revision = 1;
- strscpy(mdev->model, "TI-CSI2RX", sizeof(mdev->model));
-
- media_device_init(mdev);
+ csi->pad.flags = MEDIA_PAD_FL_SINK;
+ vdev->entity.ops = &ti_csi2rx_video_entity_ops;
+ ret = media_entity_pads_init(&ctx->vdev.entity, 1, &csi->pad);
+ if (ret)
+ return ret;

- strscpy(vdev->name, TI_CSI2RX_MODULE_NAME, sizeof(vdev->name));
+ snprintf(vdev->name, sizeof(vdev->name), "%s context %u",
+ dev_name(csi->dev), ctx->idx);
vdev->v4l2_dev = &csi->v4l2_dev;
vdev->vfl_dir = VFL_DIR_RX;
vdev->fops = &csi_fops;
@@ -1058,61 +1138,28 @@ static int ti_csi2rx_v4l2_init(struct ti_csi2rx_dev *csi)
vdev->release = video_device_release_empty;
vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
V4L2_CAP_IO_MC;
- vdev->lock = &csi->mutex;
- video_set_drvdata(vdev, csi);
+ vdev->lock = &ctx->mutex;
+ video_set_drvdata(vdev, ctx);

- csi->pad.flags = MEDIA_PAD_FL_SINK;
- vdev->entity.ops = &ti_csi2rx_video_entity_ops;
- ret = media_entity_pads_init(&csi->vdev.entity, 1, &csi->pad);
+ ret = ti_csi2rx_init_dma(ctx);
if (ret)
return ret;

- csi->v4l2_dev.mdev = mdev;
-
- ret = v4l2_device_register(csi->dev, &csi->v4l2_dev);
+ ret = ti_csi2rx_init_vb2q(ctx);
if (ret)
- return ret;
-
- ret = media_device_register(mdev);
- if (ret) {
- v4l2_device_unregister(&csi->v4l2_dev);
- media_device_cleanup(mdev);
- return ret;
- }
+ goto cleanup_dma;

return 0;
-}

-static void ti_csi2rx_cleanup_dma(struct ti_csi2rx_dev *csi)
-{
- dma_free_coherent(csi->dev, csi->dma.drain.len,
- csi->dma.drain.vaddr, csi->dma.drain.paddr);
- csi->dma.drain.vaddr = NULL;
- dma_release_channel(csi->dma.chan);
-}
-
-static void ti_csi2rx_cleanup_v4l2(struct ti_csi2rx_dev *csi)
-{
- media_device_unregister(&csi->mdev);
- v4l2_device_unregister(&csi->v4l2_dev);
- media_device_cleanup(&csi->mdev);
-}
-
-static void ti_csi2rx_cleanup_subdev(struct ti_csi2rx_dev *csi)
-{
- v4l2_async_nf_unregister(&csi->notifier);
- v4l2_async_nf_cleanup(&csi->notifier);
-}
-
-static void ti_csi2rx_cleanup_vb2q(struct ti_csi2rx_dev *csi)
-{
- vb2_queue_release(&csi->vidq);
+cleanup_dma:
+ ti_csi2rx_cleanup_dma(ctx);
+ return ret;
}

static int ti_csi2rx_probe(struct platform_device *pdev)
{
struct ti_csi2rx_dev *csi;
- int ret;
+ int ret, i;

csi = devm_kzalloc(&pdev->dev, sizeof(*csi), GFP_KERNEL);
if (!csi)
@@ -1121,62 +1168,73 @@ static int ti_csi2rx_probe(struct platform_device *pdev)
csi->dev = &pdev->dev;
platform_set_drvdata(pdev, csi);

- mutex_init(&csi->mutex);
csi->shim = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(csi->shim)) {
ret = PTR_ERR(csi->shim);
- goto err_mutex;
+ return ret;
}

- ret = ti_csi2rx_init_dma(csi);
- if (ret)
- goto err_mutex;
+ csi->drain.len = DRAIN_BUFFER_SIZE;
+ csi->drain.vaddr = dma_alloc_coherent(csi->dev, csi->drain.len,
+ &csi->drain.paddr,
+ GFP_KERNEL);
+ if (!csi->drain.vaddr)
+ return -ENOMEM;

ret = ti_csi2rx_v4l2_init(csi);
- if (ret)
- goto err_dma;
-
- ret = ti_csi2rx_init_vb2q(csi);
if (ret)
goto err_v4l2;

+ for (i = 0; i < TI_CSI2RX_NUM_CTX; i++) {
+ csi->ctx[i].idx = i;
+ csi->ctx[i].csi = csi;
+ ret = ti_csi2rx_init_ctx(&csi->ctx[i]);
+ if (ret)
+ goto err_ctx;
+ }
+
ret = ti_csi2rx_notifier_register(csi);
if (ret)
- goto err_vb2q;
+ goto err_ctx;

ret = of_platform_populate(csi->dev->of_node, NULL, NULL, csi->dev);
if (ret) {
dev_err(csi->dev, "Failed to create children: %d\n", ret);
- goto err_subdev;
+ goto err_notifier;
}

return 0;

-err_subdev:
- ti_csi2rx_cleanup_subdev(csi);
-err_vb2q:
- ti_csi2rx_cleanup_vb2q(csi);
+err_notifier:
+ ti_csi2rx_cleanup_notifier(csi);
+err_ctx:
+ i--;
+ for (; i >= 0; i--)
+ ti_csi2rx_cleanup_ctx(&csi->ctx[i]);
+
err_v4l2:
- ti_csi2rx_cleanup_v4l2(csi);
-err_dma:
- ti_csi2rx_cleanup_dma(csi);
-err_mutex:
- mutex_destroy(&csi->mutex);
+ dma_free_coherent(csi->dev, csi->drain.len, csi->drain.vaddr,
+ csi->drain.paddr);
return ret;
}

static int ti_csi2rx_remove(struct platform_device *pdev)
{
struct ti_csi2rx_dev *csi = platform_get_drvdata(pdev);
+ int i;
+
+ for (i = 0; i < TI_CSI2RX_NUM_CTX; i++) {
+ if (vb2_is_busy(&csi->ctx[i].vidq))
+ return -EBUSY;
+ }

- video_unregister_device(&csi->vdev);
+ for (i = 0; i < TI_CSI2RX_NUM_CTX; i++)
+ ti_csi2rx_cleanup_ctx(&csi->ctx[i]);

- ti_csi2rx_cleanup_vb2q(csi);
- ti_csi2rx_cleanup_subdev(csi);
+ ti_csi2rx_cleanup_notifier(csi);
ti_csi2rx_cleanup_v4l2(csi);
- ti_csi2rx_cleanup_dma(csi);
-
- mutex_destroy(&csi->mutex);
+ dma_free_coherent(csi->dev, csi->drain.len, csi->drain.vaddr,
+ csi->drain.paddr);

return 0;
}

--
2.43.0


2024-02-22 11:35:47

by Jai Luthra

[permalink] [raw]
Subject: [PATCH RFC 07/21] media: ti: j721e-csi2rx: allocate DMA channel based on context index

From: Pratyush Yadav <[email protected]>

With multiple contexts, there needs to be a different DMA channel for
each context. Earlier, the DMA channel name was hard coded to "rx0" for
the sake of simplicity. Generate the DMA channel name based on its index
and get the channel corresponding to the context.

Signed-off-by: Pratyush Yadav <[email protected]>
Signed-off-by: Jai Luthra <[email protected]>
---
drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
index e42941d8c457..80d7066100bf 100644
--- a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
+++ b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
@@ -1051,6 +1051,7 @@ static int ti_csi2rx_init_dma(struct ti_csi2rx_ctx *ctx)
struct dma_slave_config cfg = {
.src_addr_width = DMA_SLAVE_BUSWIDTH_16_BYTES,
};
+ char name[32];
int ret;

INIT_LIST_HEAD(&ctx->dma.queue);
@@ -1059,7 +1060,8 @@ static int ti_csi2rx_init_dma(struct ti_csi2rx_ctx *ctx)

ctx->dma.state = TI_CSI2RX_DMA_STOPPED;

- ctx->dma.chan = dma_request_chan(ctx->csi->dev, "rx0");
+ snprintf(name, sizeof(name), "rx%u", ctx->idx);
+ ctx->dma.chan = dma_request_chan(ctx->csi->dev, name);
if (IS_ERR(ctx->dma.chan))
return PTR_ERR(ctx->dma.chan);


--
2.43.0


2024-02-22 11:36:14

by Jai Luthra

[permalink] [raw]
Subject: [PATCH RFC 11/21] media: cadence: csi2rx: add get_frame_desc wrapper

From: Pratyush Yadav <[email protected]>

J721E wrapper CSI2RX driver needs to get the frame descriptor from the
source to find out info about virtual channel. This driver itself does
not touch the routing or virtual channels in any way. So simply pass the
descriptor through from the source.

Signed-off-by: Pratyush Yadav <[email protected]>
Signed-off-by: Jai Luthra <[email protected]>
---
drivers/media/platform/cadence/cdns-csi2rx.c | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)

diff --git a/drivers/media/platform/cadence/cdns-csi2rx.c b/drivers/media/platform/cadence/cdns-csi2rx.c
index e19993ed351c..989924dba9a9 100644
--- a/drivers/media/platform/cadence/cdns-csi2rx.c
+++ b/drivers/media/platform/cadence/cdns-csi2rx.c
@@ -139,6 +139,21 @@ static const struct csi2rx_fmt *csi2rx_get_fmt_by_code(u32 code)
return NULL;
}

+static int csi2rx_get_frame_desc_from_source(struct csi2rx_priv *csi2rx,
+ struct v4l2_mbus_frame_desc *fd)
+{
+ struct media_pad *remote_pad;
+
+ remote_pad = media_entity_remote_source_pad_unique(&csi2rx->subdev.entity);
+ if (!remote_pad) {
+ dev_err(csi2rx->dev, "No remote pad found for sink\n");
+ return -ENODEV;
+ }
+
+ return v4l2_subdev_call(csi2rx->source_subdev, pad, get_frame_desc,
+ remote_pad->index, fd);
+}
+
static inline
struct csi2rx_priv *v4l2_subdev_to_csi2rx(struct v4l2_subdev *subdev)
{
@@ -470,10 +485,19 @@ static int csi2rx_init_state(struct v4l2_subdev *subdev,
return csi2rx_set_fmt(subdev, state, &format);
}

+static int csi2rx_get_frame_desc(struct v4l2_subdev *subdev, unsigned int pad,
+ struct v4l2_mbus_frame_desc *fd)
+{
+ struct csi2rx_priv *csi2rx = v4l2_subdev_to_csi2rx(subdev);
+
+ return csi2rx_get_frame_desc_from_source(csi2rx, fd);
+}
+
static const struct v4l2_subdev_pad_ops csi2rx_pad_ops = {
.enum_mbus_code = csi2rx_enum_mbus_code,
.get_fmt = v4l2_subdev_get_fmt,
.set_fmt = csi2rx_set_fmt,
+ .get_frame_desc = csi2rx_get_frame_desc,
};

static const struct v4l2_subdev_video_ops csi2rx_video_ops = {

--
2.43.0


2024-02-22 11:36:16

by Jai Luthra

[permalink] [raw]
Subject: [PATCH RFC 10/21] media: ti: j721e-csi2rx: get number of contexts from device tree

From: Pratyush Yadav <[email protected]>

Different platforms that use this driver might have different number of
DMA channels allocated for CSI. So only as many DMA contexts can be used
as the number of DMA channels available. Get the number of channels
provided via device tree and only configure that many contexts, and
hence only that many pads.

Signed-off-by: Pratyush Yadav <[email protected]>
Co-developed-by: Jai Luthra <[email protected]>
Signed-off-by: Jai Luthra <[email protected]>
---
.../media/platform/ti/j721e-csi2rx/j721e-csi2rx.c | 44 ++++++++++++++++------
1 file changed, 32 insertions(+), 12 deletions(-)

diff --git a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
index 1443338f4134..f6045888eed0 100644
--- a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
+++ b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
@@ -40,7 +40,7 @@
#define SHIM_PSI_CFG0_DST_TAG GENMASK(31, 16)

#define PSIL_WORD_SIZE_BYTES 16
-#define TI_CSI2RX_NUM_CTX 1
+#define TI_CSI2RX_MAX_CTX 32

/*
* There are no hard limits on the width or height. The DMA engine can handle
@@ -53,8 +53,8 @@

#define TI_CSI2RX_PAD_SINK 0
#define TI_CSI2RX_PAD_FIRST_SOURCE 1
-#define TI_CSI2RX_NUM_SOURCE_PADS 1
-#define TI_CSI2RX_NUM_PADS (1 + TI_CSI2RX_NUM_SOURCE_PADS)
+#define TI_CSI2RX_MAX_SOURCE_PADS TI_CSI2RX_MAX_CTX
+#define TI_CSI2RX_MAX_PADS (1 + TI_CSI2RX_MAX_SOURCE_PADS)

#define DRAIN_TIMEOUT_MS 50
#define DRAIN_BUFFER_SIZE SZ_32K
@@ -112,14 +112,15 @@ struct ti_csi2rx_dev {
void __iomem *shim;
struct mutex mutex; /* To serialize ioctls. */
unsigned int enable_count;
+ unsigned int num_ctx;
struct v4l2_device v4l2_dev;
struct media_device mdev;
struct media_pipeline pipe;
- struct media_pad pads[TI_CSI2RX_NUM_PADS];
+ struct media_pad pads[TI_CSI2RX_MAX_PADS];
struct v4l2_async_notifier notifier;
struct v4l2_subdev *source;
struct v4l2_subdev subdev;
- struct ti_csi2rx_ctx ctx[TI_CSI2RX_NUM_CTX];
+ struct ti_csi2rx_ctx ctx[TI_CSI2RX_MAX_CTX];
/* Buffer to drain stale data from PSI-L endpoint */
struct {
void *vaddr;
@@ -473,7 +474,7 @@ static int csi_async_notifier_complete(struct v4l2_async_notifier *notifier)
return ret;

/* Create and link video nodes for all DMA contexts */
- for (i = 0; i < TI_CSI2RX_NUM_CTX; i++) {
+ for (i = 0; i < csi->num_ctx; i++) {
struct ti_csi2rx_ctx *ctx = &csi->ctx[i];
struct video_device *vdev = &ctx->vdev;

@@ -1255,10 +1256,12 @@ static int ti_csi2rx_v4l2_init(struct ti_csi2rx_dev *csi)

csi->pads[TI_CSI2RX_PAD_SINK].flags = MEDIA_PAD_FL_SINK;

- for (i = TI_CSI2RX_PAD_FIRST_SOURCE; i < TI_CSI2RX_NUM_PADS; i++)
+ for (i = TI_CSI2RX_PAD_FIRST_SOURCE;
+ i < TI_CSI2RX_PAD_FIRST_SOURCE + csi->num_ctx; i++)
csi->pads[i].flags = MEDIA_PAD_FL_SOURCE;

- ret = media_entity_pads_init(&sd->entity, ARRAY_SIZE(csi->pads),
+ ret = media_entity_pads_init(&sd->entity,
+ TI_CSI2RX_PAD_FIRST_SOURCE + csi->num_ctx,
csi->pads);
if (ret)
goto unregister_media;
@@ -1344,8 +1347,9 @@ static int ti_csi2rx_init_ctx(struct ti_csi2rx_ctx *ctx)

static int ti_csi2rx_probe(struct platform_device *pdev)
{
+ struct device_node *np = pdev->dev.of_node;
struct ti_csi2rx_dev *csi;
- int ret, i;
+ int ret, i, count;

csi = devm_kzalloc(&pdev->dev, sizeof(*csi), GFP_KERNEL);
if (!csi)
@@ -1367,13 +1371,29 @@ static int ti_csi2rx_probe(struct platform_device *pdev)
if (!csi->drain.vaddr)
return -ENOMEM;

+ /* Only use as many contexts as the number of DMA channels allocated. */
+ count = of_property_count_strings(np, "dma-names");
+ if (count < 0) {
+ dev_err(csi->dev, "Failed to get DMA channel count: %d\n",
+ count);
+ return count;
+ }
+
+ csi->num_ctx = count;
+ if (csi->num_ctx > TI_CSI2RX_MAX_CTX) {
+ dev_warn(csi->dev,
+ "%u DMA channels passed. Maximum is %u. Ignoring the rest.\n",
+ csi->num_ctx, TI_CSI2RX_MAX_CTX);
+ csi->num_ctx = TI_CSI2RX_MAX_CTX;
+ }
+
mutex_init(&csi->mutex);

ret = ti_csi2rx_v4l2_init(csi);
if (ret)
goto err_v4l2;

- for (i = 0; i < TI_CSI2RX_NUM_CTX; i++) {
+ for (i = 0; i < csi->num_ctx; i++) {
csi->ctx[i].idx = i;
csi->ctx[i].csi = csi;
ret = ti_csi2rx_init_ctx(&csi->ctx[i]);
@@ -1412,12 +1432,12 @@ static int ti_csi2rx_remove(struct platform_device *pdev)
struct ti_csi2rx_dev *csi = platform_get_drvdata(pdev);
int i;

- for (i = 0; i < TI_CSI2RX_NUM_CTX; i++) {
+ for (i = 0; i < csi->num_ctx; i++) {
if (vb2_is_busy(&csi->ctx[i].vidq))
return -EBUSY;
}

- for (i = 0; i < TI_CSI2RX_NUM_CTX; i++)
+ for (i = 0; i < csi->num_ctx; i++)
ti_csi2rx_cleanup_ctx(&csi->ctx[i]);

ti_csi2rx_cleanup_notifier(csi);

--
2.43.0


2024-02-22 11:37:17

by Jai Luthra

[permalink] [raw]
Subject: [PATCH RFC 12/21] media: ti: j721e-csi2rx: add support for processing virtual channels

Use get_frame_desc() to get the frame desc from the connected source,
and use the provided virtual channel instead of hardcoded one.

get_frame_desc() works per stream, but as we don't support multiple
streams yet, we will just always use stream 0. If the source doesn't
support get_frame_desc(), fall back to the previous method of always
capturing virtual channel 0.

Co-developed-by: Pratyush Yadav <[email protected]>
Signed-off-by: Pratyush Yadav <[email protected]>
Signed-off-by: Jai Luthra <[email protected]>
---
.../media/platform/ti/j721e-csi2rx/j721e-csi2rx.c | 39 ++++++++++++++++++++++
1 file changed, 39 insertions(+)

diff --git a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
index f6045888eed0..4be8a306ac1f 100644
--- a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
+++ b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
@@ -29,6 +29,7 @@
#define SHIM_DMACNTX_EN BIT(31)
#define SHIM_DMACNTX_YUV422 GENMASK(27, 26)
#define SHIM_DMACNTX_SIZE GENMASK(21, 20)
+#define SHIM_DMACNTX_VC GENMASK(9, 6)
#define SHIM_DMACNTX_FMT GENMASK(5, 0)
#define SHIM_DMACNTX_YUV422_MODE_11 3
#define SHIM_DMACNTX_SIZE_8 0
@@ -105,6 +106,8 @@ struct ti_csi2rx_ctx {
struct media_pad pad;
u32 sequence;
u32 idx;
+ u32 vc;
+ u32 stream;
};

struct ti_csi2rx_dev {
@@ -595,6 +598,7 @@ static void ti_csi2rx_setup_shim(struct ti_csi2rx_ctx *ctx)
}

reg |= FIELD_PREP(SHIM_DMACNTX_SIZE, fmt->size);
+ reg |= FIELD_PREP(SHIM_DMACNTX_VC, ctx->vc);

writel(reg, csi->shim + SHIM_DMACNTX(ctx->idx));

@@ -868,6 +872,33 @@ static void ti_csi2rx_buffer_queue(struct vb2_buffer *vb)
}
}

+static int ti_csi2rx_get_vc(struct ti_csi2rx_ctx *ctx)
+{
+ struct ti_csi2rx_dev *csi = ctx->csi;
+ struct v4l2_mbus_frame_desc fd;
+ struct media_pad *pad;
+ int ret, i;
+
+ pad = media_entity_remote_pad_unique(&csi->subdev.entity, MEDIA_PAD_FL_SOURCE);
+ if (!pad)
+ return -ENODEV;
+
+ ret = v4l2_subdev_call(csi->source, pad, get_frame_desc, pad->index,
+ &fd);
+ if (ret)
+ return ret;
+
+ if (fd.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2)
+ return -EINVAL;
+
+ for (i = 0; i < fd.num_entries; i++) {
+ if (ctx->stream == fd.entry[i].stream)
+ return fd.entry[i].bus.csi2.vc;
+ }
+
+ return -ENODEV;
+}
+
static int ti_csi2rx_start_streaming(struct vb2_queue *vq, unsigned int count)
{
struct ti_csi2rx_ctx *ctx = vb2_get_drv_priv(vq);
@@ -888,6 +919,14 @@ static int ti_csi2rx_start_streaming(struct vb2_queue *vq, unsigned int count)
if (ret)
goto err;

+ ret = ti_csi2rx_get_vc(ctx);
+ if (ret == -ENOIOCTLCMD)
+ ctx->vc = 0;
+ else if (ret < 0)
+ goto err;
+ else
+ ctx->vc = ret;
+
ti_csi2rx_setup_shim(ctx);

ctx->sequence = 0;

--
2.43.0


2024-02-22 11:37:38

by Jai Luthra

[permalink] [raw]
Subject: [PATCH RFC 13/21] media: cadence: csi2rx: Use new enable stream APIs

The enable_streams() API in v4l2 supports passing a bitmask to enable
each pad/stream combination individually on any media subdev instead of
doing s_stream(1) to start all streams on the subdev at once.

This API is implemented by ds90ub960 driver (FPDLink deser) and thus the
caller (cdns-csi2x) is required to use it. For now we only enable
stream0.

Signed-off-by: Jai Luthra <[email protected]>
---
drivers/media/platform/cadence/cdns-csi2rx.c | 18 ++++++++++++++++--
1 file changed, 16 insertions(+), 2 deletions(-)

diff --git a/drivers/media/platform/cadence/cdns-csi2rx.c b/drivers/media/platform/cadence/cdns-csi2rx.c
index 989924dba9a9..bac341881e41 100644
--- a/drivers/media/platform/cadence/cdns-csi2rx.c
+++ b/drivers/media/platform/cadence/cdns-csi2rx.c
@@ -226,10 +226,18 @@ static int csi2rx_configure_ext_dphy(struct csi2rx_priv *csi2rx)
static int csi2rx_start(struct csi2rx_priv *csi2rx)
{
unsigned int i;
+ struct media_pad *remote_pad;
unsigned long lanes_used = 0;
u32 reg;
int ret;

+ remote_pad = media_pad_remote_pad_first(&csi2rx->pads[CSI2RX_PAD_SINK]);
+ if (!remote_pad) {
+ dev_err(csi2rx->dev,
+ "Failed to find connected source\n");
+ return -ENODEV;
+ }
+
ret = clk_prepare_enable(csi2rx->p_clk);
if (ret)
return ret;
@@ -313,7 +321,8 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx)

reset_control_deassert(csi2rx->sys_rst);

- ret = v4l2_subdev_call(csi2rx->source_subdev, video, s_stream, true);
+ ret = v4l2_subdev_enable_streams(csi2rx->source_subdev,
+ remote_pad->index, BIT(0));
if (ret)
goto err_disable_sysclk;

@@ -341,6 +350,7 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx)

static void csi2rx_stop(struct csi2rx_priv *csi2rx)
{
+ struct media_pad *remote_pad;
unsigned int i;
u32 val;
int ret;
@@ -369,8 +379,12 @@ static void csi2rx_stop(struct csi2rx_priv *csi2rx)
reset_control_assert(csi2rx->p_rst);
clk_disable_unprepare(csi2rx->p_clk);

- if (v4l2_subdev_call(csi2rx->source_subdev, video, s_stream, false))
+ remote_pad = media_pad_remote_pad_first(&csi2rx->pads[CSI2RX_PAD_SINK]);
+ if (!remote_pad ||
+ v4l2_subdev_disable_streams(csi2rx->source_subdev,
+ remote_pad->index, BIT(0))) {
dev_warn(csi2rx->dev, "Couldn't disable our subdev\n");
+ }

if (csi2rx->dphy) {
writel(0, csi2rx->base + CSI2RX_DPHY_LANE_CTRL_REG);

--
2.43.0


2024-02-22 11:37:52

by Jai Luthra

[permalink] [raw]
Subject: [PATCH RFC 15/21] SQUASH: media: cadence: csi2rx: Enable per-stream controls

With stream routing now supported in the driver, implement the
enable_stream and disable_stream hooks in place of the stream-unaware
s_stream hook.

This allows consumer devices like a DMA bridge or ISP, to enable
particular streams on a source pad, which in turn can be used to enable
only particular streams on the CSI-TX device connected on the sink pad.

Implement a fallback s_stream hook that internally calls enable_stream
on each source pad, for consumer drivers that don't use multi-stream
APIs to still work.

Signed-off-by: Jai Luthra <[email protected]>
---
drivers/media/platform/cadence/cdns-csi2rx.c | 211 ++++++++++++++++++---------
1 file changed, 142 insertions(+), 69 deletions(-)

diff --git a/drivers/media/platform/cadence/cdns-csi2rx.c b/drivers/media/platform/cadence/cdns-csi2rx.c
index 1b76610ff617..f08d3b845dc9 100644
--- a/drivers/media/platform/cadence/cdns-csi2rx.c
+++ b/drivers/media/platform/cadence/cdns-csi2rx.c
@@ -183,27 +183,43 @@ static void csi2rx_reset(struct csi2rx_priv *csi2rx)

static int csi2rx_configure_ext_dphy(struct csi2rx_priv *csi2rx)
{
+ struct v4l2_ctrl_handler *handler = csi2rx->source_subdev->ctrl_handler;
union phy_configure_opts opts = { };
struct phy_configure_opts_mipi_dphy *cfg = &opts.mipi_dphy;
- struct v4l2_subdev_format sd_fmt = {
- .which = V4L2_SUBDEV_FORMAT_ACTIVE,
- .pad = CSI2RX_PAD_SINK,
- };
+ struct v4l2_mbus_framefmt *framefmt;
+ struct v4l2_subdev_state *state;
const struct csi2rx_fmt *fmt;
s64 link_freq;
int ret;

- ret = v4l2_subdev_call_state_active(&csi2rx->subdev, pad, get_fmt,
- &sd_fmt);
- if (ret < 0)
- return ret;
+ if (v4l2_ctrl_find(handler, V4L2_CID_LINK_FREQ)) {
+ link_freq = v4l2_get_link_freq(handler, 0, 0);
+ } else {
+ state = v4l2_subdev_get_locked_active_state(&csi2rx->subdev);
+ framefmt = v4l2_subdev_state_get_format(state, CSI2RX_PAD_SINK,
+ 0);

- fmt = csi2rx_get_fmt_by_code(sd_fmt.format.code);
+ if (framefmt) {
+ fmt = csi2rx_get_fmt_by_code(framefmt->code);
+ } else {
+ dev_err(csi2rx->dev,
+ "Did not find active sink format\n");
+ return -EINVAL;
+ }

- link_freq = v4l2_get_link_freq(csi2rx->source_subdev->ctrl_handler,
- fmt->bpp, 2 * csi2rx->num_lanes);
- if (link_freq < 0)
+ link_freq = v4l2_get_link_freq(handler, fmt->bpp,
+ 2 * csi2rx->num_lanes);
+
+ dev_warn(csi2rx->dev,
+ "Guessing link frequency using bitdepth of stream 0.\n");
+ dev_warn(csi2rx->dev,
+ "V4L2_CID_LINK_FREQ control is required for multi format sources.\n");
+ }
+
+ if (link_freq < 0) {
+ dev_err(csi2rx->dev, "Unable to calculate link frequency\n");
return link_freq;
+ }

ret = phy_mipi_dphy_get_default_config_for_hsclk(link_freq,
csi2rx->num_lanes, cfg);
@@ -226,18 +242,10 @@ static int csi2rx_configure_ext_dphy(struct csi2rx_priv *csi2rx)
static int csi2rx_start(struct csi2rx_priv *csi2rx)
{
unsigned int i;
- struct media_pad *remote_pad;
unsigned long lanes_used = 0;
u32 reg;
int ret;

- remote_pad = media_pad_remote_pad_first(&csi2rx->pads[CSI2RX_PAD_SINK]);
- if (!remote_pad) {
- dev_err(csi2rx->dev,
- "Failed to find connected source\n");
- return -ENODEV;
- }
-
ret = clk_prepare_enable(csi2rx->p_clk);
if (ret)
return ret;
@@ -321,17 +329,10 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx)

reset_control_deassert(csi2rx->sys_rst);

- ret = v4l2_subdev_enable_streams(csi2rx->source_subdev,
- remote_pad->index, BIT(0));
- if (ret)
- goto err_disable_sysclk;
-
clk_disable_unprepare(csi2rx->p_clk);

return 0;

-err_disable_sysclk:
- clk_disable_unprepare(csi2rx->sys_clk);
err_disable_pixclk:
for (; i > 0; i--) {
reset_control_assert(csi2rx->pixel_rst[i - 1]);
@@ -350,7 +351,6 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx)

static void csi2rx_stop(struct csi2rx_priv *csi2rx)
{
- struct media_pad *remote_pad;
unsigned int i;
u32 val;
int ret;
@@ -379,13 +379,6 @@ static void csi2rx_stop(struct csi2rx_priv *csi2rx)
reset_control_assert(csi2rx->p_rst);
clk_disable_unprepare(csi2rx->p_clk);

- remote_pad = media_pad_remote_pad_first(&csi2rx->pads[CSI2RX_PAD_SINK]);
- if (!remote_pad ||
- v4l2_subdev_disable_streams(csi2rx->source_subdev,
- remote_pad->index, BIT(0))) {
- dev_warn(csi2rx->dev, "Couldn't disable our subdev\n");
- }
-
if (csi2rx->dphy) {
writel(0, csi2rx->base + CSI2RX_DPHY_LANE_CTRL_REG);

@@ -394,47 +387,125 @@ static void csi2rx_stop(struct csi2rx_priv *csi2rx)
}
}

-static int csi2rx_s_stream(struct v4l2_subdev *subdev, int enable)
+static int csi2rx_enable_streams(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_state *state, u32 pad,
+ u64 streams_mask)
{
struct csi2rx_priv *csi2rx = v4l2_subdev_to_csi2rx(subdev);
- int ret = 0;
+ struct media_pad *remote_pad;
+ u64 sink_streams;
+ int ret;

- if (enable) {
- ret = pm_runtime_resume_and_get(csi2rx->dev);
- if (ret < 0)
- return ret;
+ remote_pad = media_pad_remote_pad_first(&csi2rx->pads[CSI2RX_PAD_SINK]);
+ if (!remote_pad) {
+ dev_err(csi2rx->dev,
+ "Failed to find connected source\n");
+ return -ENODEV;
}

+ ret = pm_runtime_resume_and_get(csi2rx->dev);
+ if (ret < 0)
+ return ret;
+
+ sink_streams = v4l2_subdev_state_xlate_streams(state, pad,
+ CSI2RX_PAD_SINK,
+ &streams_mask);
+
mutex_lock(&csi2rx->lock);
+ /*
+ * If we're not the first users, there's no need to
+ * enable the whole controller.
+ */
+ if (!csi2rx->count) {
+ ret = csi2rx_start(csi2rx);
+ if (ret)
+ goto err_stream_start;
+ }

- if (enable) {
- /*
- * If we're not the first users, there's no need to
- * enable the whole controller.
- */
- if (!csi2rx->count) {
- ret = csi2rx_start(csi2rx);
- if (ret) {
- pm_runtime_put(csi2rx->dev);
- goto out;
- }
- }
+ /* Start streaming on the source */
+ ret = v4l2_subdev_enable_streams(csi2rx->source_subdev, remote_pad->index,
+ sink_streams);
+ if (ret) {
+ dev_err(csi2rx->dev,
+ "Failed to start streams %#llx on subdev\n",
+ sink_streams);
+ goto err_subdev_enable;
+ }

- csi2rx->count++;
- } else {
- csi2rx->count--;
+ csi2rx->count++;
+ mutex_unlock(&csi2rx->lock);

- /*
- * Let the last user turn off the lights.
- */
- if (!csi2rx->count)
- csi2rx_stop(csi2rx);
+ return 0;
+
+err_subdev_enable:
+ if (!csi2rx->count)
+ csi2rx_stop(csi2rx);
+err_stream_start:
+ mutex_unlock(&csi2rx->lock);
+ pm_runtime_put(csi2rx->dev);
+ return ret;
+}
+
+static int csi2rx_disable_streams(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_state *state, u32 pad,
+ u64 streams_mask)
+{
+ struct csi2rx_priv *csi2rx = v4l2_subdev_to_csi2rx(subdev);
+ struct media_pad *remote_pad;
+ u64 sink_streams;

- pm_runtime_put(csi2rx->dev);
+ sink_streams = v4l2_subdev_state_xlate_streams(state, pad,
+ CSI2RX_PAD_SINK,
+ &streams_mask);
+
+ remote_pad = media_pad_remote_pad_first(&csi2rx->pads[CSI2RX_PAD_SINK]);
+ if (!remote_pad ||
+ v4l2_subdev_disable_streams(csi2rx->source_subdev,
+ remote_pad->index, sink_streams)) {
+ dev_err(csi2rx->dev, "Couldn't disable our subdev\n");
}

-out:
+ mutex_lock(&csi2rx->lock);
+ csi2rx->count--;
+ /*
+ * Let the last user turn off the lights.
+ */
+ if (!csi2rx->count)
+ csi2rx_stop(csi2rx);
mutex_unlock(&csi2rx->lock);
+
+ pm_runtime_put(csi2rx->dev);
+
+ return 0;
+}
+
+static int csi2rx_s_stream_fallback(struct v4l2_subdev *sd, int enable)
+{
+ struct v4l2_subdev_state *state;
+ struct v4l2_subdev_route *route;
+ u64 mask[CSI2RX_PAD_MAX] = {0};
+ int i, ret;
+
+ /* Find the stream mask on all source pads */
+ state = v4l2_subdev_lock_and_get_active_state(sd);
+ for (i = CSI2RX_PAD_SOURCE_STREAM0; i < CSI2RX_PAD_MAX; i++) {
+ for_each_active_route(&state->routing, route) {
+ if (route->source_pad == i)
+ mask[i] |= BIT_ULL(route->source_stream);
+ }
+ }
+ v4l2_subdev_unlock_state(state);
+
+ /* Start streaming on each pad */
+ for (i = CSI2RX_PAD_SOURCE_STREAM0; i < CSI2RX_PAD_MAX; i++) {
+ if (enable)
+ ret = v4l2_subdev_enable_streams(sd, i, mask[i]);
+ else
+ ret = v4l2_subdev_disable_streams(sd, i, mask[i]);
+ if (ret)
+ return ret;
+ }
+
return ret;
}

@@ -557,15 +628,17 @@ static int csi2rx_get_frame_desc(struct v4l2_subdev *subdev, unsigned int pad,
}

static const struct v4l2_subdev_pad_ops csi2rx_pad_ops = {
- .enum_mbus_code = csi2rx_enum_mbus_code,
- .get_fmt = v4l2_subdev_get_fmt,
- .set_fmt = csi2rx_set_fmt,
- .get_frame_desc = csi2rx_get_frame_desc,
- .set_routing = csi2rx_set_routing,
+ .enum_mbus_code = csi2rx_enum_mbus_code,
+ .get_fmt = v4l2_subdev_get_fmt,
+ .set_fmt = csi2rx_set_fmt,
+ .get_frame_desc = csi2rx_get_frame_desc,
+ .set_routing = csi2rx_set_routing,
+ .enable_streams = csi2rx_enable_streams,
+ .disable_streams = csi2rx_disable_streams,
};

static const struct v4l2_subdev_video_ops csi2rx_video_ops = {
- .s_stream = csi2rx_s_stream,
+ .s_stream = csi2rx_s_stream_fallback,
};

static const struct v4l2_subdev_ops csi2rx_subdev_ops = {

--
2.43.0


2024-02-22 11:38:03

by Jai Luthra

[permalink] [raw]
Subject: [PATCH RFC 16/21] SQUASH: media: cadence: csi2rx: Filter using MIPI virtual channels

With stream routing now supported by the driver, we can check if the
CSI-TX source provides extra bus information through frame descriptor
entries, and use it to filter for particular VCs on the source pads.

Signed-off-by: Jai Luthra <[email protected]>
---
drivers/media/platform/cadence/cdns-csi2rx.c | 59 +++++++++++++++++++++++++---
1 file changed, 53 insertions(+), 6 deletions(-)

diff --git a/drivers/media/platform/cadence/cdns-csi2rx.c b/drivers/media/platform/cadence/cdns-csi2rx.c
index f08d3b845dc9..f96638b9f252 100644
--- a/drivers/media/platform/cadence/cdns-csi2rx.c
+++ b/drivers/media/platform/cadence/cdns-csi2rx.c
@@ -90,6 +90,7 @@ struct csi2rx_priv {
struct reset_control *pixel_rst[CSI2RX_STREAMS_MAX];
struct phy *dphy;

+ u32 vc_select[CSI2RX_STREAMS_MAX];
u8 lanes[CSI2RX_LANES_MAX];
u8 num_lanes;
u8 max_lanes;
@@ -312,11 +313,7 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx)
writel(CSI2RX_STREAM_CFG_FIFO_MODE_LARGE_BUF,
csi2rx->base + CSI2RX_STREAM_CFG_REG(i));

- /*
- * Enable one virtual channel. When multiple virtual channels
- * are supported this will have to be changed.
- */
- writel(CSI2RX_STREAM_DATA_CFG_VC_SELECT(0),
+ writel(csi2rx->vc_select[i],
csi2rx->base + CSI2RX_STREAM_DATA_CFG_REG(i));

writel(CSI2RX_STREAM_CTRL_START,
@@ -387,6 +384,48 @@ static void csi2rx_stop(struct csi2rx_priv *csi2rx)
}
}

+static void csi2rx_update_vc_select(struct csi2rx_priv *csi2rx,
+ struct v4l2_subdev_state *state)
+{
+ struct v4l2_mbus_frame_desc fd = {0};
+ struct v4l2_subdev_route *route;
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < CSI2RX_STREAMS_MAX; i++)
+ csi2rx->vc_select[i] = 0;
+
+ ret = csi2rx_get_frame_desc_from_source(csi2rx, &fd);
+ if (ret || fd.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) {
+ dev_dbg(csi2rx->dev,
+ "Failed to get source frame desc, allowing only VC=0\n");
+ goto err_no_fd;
+ }
+
+ /* If source provides per-stream VC info, use it to filter by VC */
+ for_each_active_route(&state->routing, route) {
+ int cdns_stream = route->source_pad - CSI2RX_PAD_SOURCE_STREAM0;
+ u8 used_vc;
+
+ for (i = 0; i < fd.num_entries; i++) {
+ if (fd.entry[i].stream == route->sink_stream) {
+ used_vc = fd.entry[i].bus.csi2.vc;
+ break;
+ }
+ }
+ csi2rx->vc_select[cdns_stream] |=
+ CSI2RX_STREAM_DATA_CFG_VC_SELECT(used_vc);
+ }
+
+err_no_fd:
+ for (i = 0; i < CSI2RX_STREAMS_MAX; i++) {
+ if (!csi2rx->vc_select[i]) {
+ csi2rx->vc_select[i] =
+ CSI2RX_STREAM_DATA_CFG_VC_SELECT(0);
+ }
+ }
+}
+
static int csi2rx_enable_streams(struct v4l2_subdev *subdev,
struct v4l2_subdev_state *state, u32 pad,
u64 streams_mask)
@@ -558,11 +597,19 @@ static int csi2rx_set_routing(struct v4l2_subdev *subdev,
struct v4l2_subdev_krouting *routing)
{
struct csi2rx_priv *csi2rx = v4l2_subdev_to_csi2rx(subdev);
+ int ret;

if (which == V4L2_SUBDEV_FORMAT_ACTIVE && csi2rx->count)
return -EBUSY;

- return _csi2rx_set_routing(subdev, state, routing);
+ ret = _csi2rx_set_routing(subdev, state, routing);
+
+ if (ret)
+ return ret;
+
+ csi2rx_update_vc_select(csi2rx, state);
+
+ return 0;
}

static int csi2rx_set_fmt(struct v4l2_subdev *subdev,

--
2.43.0


2024-02-22 11:38:27

by Jai Luthra

[permalink] [raw]
Subject: [PATCH RFC 17/21] SQUASH: media: cadence: csi2rx: Filter streams in get_frame_desc

With routing and stream-aware APIs support in place, make sure to
propagate frame descriptor information for only the subset of streams
that are routed to the particular source pad.

Signed-off-by: Jai Luthra <[email protected]>
---
drivers/media/platform/cadence/cdns-csi2rx.c | 50 +++++++++++++++++++++++++++-
1 file changed, 49 insertions(+), 1 deletion(-)

diff --git a/drivers/media/platform/cadence/cdns-csi2rx.c b/drivers/media/platform/cadence/cdns-csi2rx.c
index f96638b9f252..a0ccdf72a3b2 100644
--- a/drivers/media/platform/cadence/cdns-csi2rx.c
+++ b/drivers/media/platform/cadence/cdns-csi2rx.c
@@ -670,8 +670,56 @@ static int csi2rx_get_frame_desc(struct v4l2_subdev *subdev, unsigned int pad,
struct v4l2_mbus_frame_desc *fd)
{
struct csi2rx_priv *csi2rx = v4l2_subdev_to_csi2rx(subdev);
+ struct v4l2_mbus_frame_desc source_fd = {0};
+ struct v4l2_subdev_route *route;
+ struct v4l2_subdev_state *state;
+ int ret;
+
+ ret = csi2rx_get_frame_desc_from_source(csi2rx, &source_fd);
+ if (ret)
+ return ret;
+
+ fd->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2;
+
+ state = v4l2_subdev_lock_and_get_active_state(subdev);
+
+ for_each_active_route(&state->routing, route) {
+ struct v4l2_mbus_frame_desc_entry *source_entry = NULL;
+ unsigned int i;
+
+ if (route->source_pad != pad)
+ continue;
+
+ for (i = 0; i < source_fd.num_entries; i++) {
+ if (source_fd.entry[i].stream == route->sink_stream) {
+ source_entry = &source_fd.entry[i];
+ break;
+ }
+ }
+
+ if (!source_entry) {
+ dev_err(csi2rx->dev,
+ "Failed to find stream from source frame desc\n");
+ ret = -EPIPE;
+ goto err_missing_stream;
+ }

- return csi2rx_get_frame_desc_from_source(csi2rx, fd);
+ fd->entry[fd->num_entries].stream = route->source_stream;
+ fd->entry[fd->num_entries].flags = source_entry->flags;
+ fd->entry[fd->num_entries].length = source_entry->length;
+ fd->entry[fd->num_entries].pixelcode = source_entry->pixelcode;
+ fd->entry[fd->num_entries].bus.csi2.vc =
+ source_entry->bus.csi2.vc;
+ fd->entry[fd->num_entries].bus.csi2.dt =
+ source_entry->bus.csi2.dt;
+
+ fd->num_entries++;
+ }
+
+err_missing_stream:
+ v4l2_subdev_unlock_state(state);
+
+ return ret;
}

static const struct v4l2_subdev_pad_ops csi2rx_pad_ops = {

--
2.43.0


2024-02-22 11:38:47

by Jai Luthra

[permalink] [raw]
Subject: [PATCH RFC 18/21] media: ti: j721e-csi2rx: add multistream support

Each CSI2 stream can be multiplexed into 4 independent streams, each
identified by its virtual channel number. To capture this multiplexed
stream, the application needs to tell the driver how it wants to route
the data. It needs to specify which context should process which stream.
This is done via the new routing APIs.

Add ioctls to accept routing information from the application and save
that in the driver. This can be used when starting streaming on a
context to determine which route and consequently which virtual channel
it should process.

Co-developed-by: Pratyush Yadav <[email protected]>
Signed-off-by: Pratyush Yadav <[email protected]>
Signed-off-by: Jai Luthra <[email protected]>
---
.../media/platform/ti/j721e-csi2rx/j721e-csi2rx.c | 110 +++++++++++++++++----
1 file changed, 93 insertions(+), 17 deletions(-)

diff --git a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
index 4be8a306ac1f..b33681e1e2db 100644
--- a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
+++ b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
@@ -905,8 +905,12 @@ static int ti_csi2rx_start_streaming(struct vb2_queue *vq, unsigned int count)
struct ti_csi2rx_dev *csi = ctx->csi;
struct ti_csi2rx_dma *dma = &ctx->dma;
struct ti_csi2rx_buffer *buf;
+ struct v4l2_subdev_krouting *routing;
+ struct v4l2_subdev_route *route = NULL;
+ struct media_pad *remote_pad;
unsigned long flags;
- int ret = 0;
+ int ret = 0, i;
+ struct v4l2_subdev_state *state;

spin_lock_irqsave(&dma->lock, flags);
if (list_empty(&dma->queue))
@@ -919,6 +923,40 @@ static int ti_csi2rx_start_streaming(struct vb2_queue *vq, unsigned int count)
if (ret)
goto err;

+ remote_pad = media_entity_remote_source_pad_unique(ctx->pad.entity);
+ if (!remote_pad) {
+ ret = -ENODEV;
+ goto err;
+ }
+
+ state = v4l2_subdev_lock_and_get_active_state(&csi->subdev);
+
+ routing = &state->routing;
+
+ /* Find the stream to process. */
+ for (i = 0; i < routing->num_routes; i++) {
+ struct v4l2_subdev_route *r = &routing->routes[i];
+
+ if (!(r->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE))
+ continue;
+
+ if (r->source_pad != remote_pad->index)
+ continue;
+
+ route = r;
+ break;
+ }
+
+ if (!route) {
+ ret = -ENODEV;
+ v4l2_subdev_unlock_state(state);
+ goto err;
+ }
+
+ ctx->stream = route->sink_stream;
+
+ v4l2_subdev_unlock_state(state);
+
ret = ti_csi2rx_get_vc(ctx);
if (ret == -ENOIOCTLCMD)
ctx->vc = 0;
@@ -1019,8 +1057,8 @@ static int ti_csi2rx_sd_set_fmt(struct v4l2_subdev *sd,
}
*fmt = format->format;

- fmt = v4l2_subdev_state_get_format(state, TI_CSI2RX_PAD_FIRST_SOURCE,
- format->stream);
+ fmt = v4l2_subdev_state_get_opposite_stream_format(state, format->pad,
+ format->stream);
if (!fmt) {
ret = -EINVAL;
goto out;
@@ -1031,24 +1069,61 @@ static int ti_csi2rx_sd_set_fmt(struct v4l2_subdev *sd,
return ret;
}

+static int _ti_csi2rx_sd_set_routing(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_krouting *routing)
+{
+ int ret;
+
+ const struct v4l2_mbus_framefmt format = {
+ .width = 640,
+ .height = 480,
+ .code = MEDIA_BUS_FMT_UYVY8_1X16,
+ .field = V4L2_FIELD_NONE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .ycbcr_enc = V4L2_YCBCR_ENC_601,
+ .quantization = V4L2_QUANTIZATION_LIM_RANGE,
+ .xfer_func = V4L2_XFER_FUNC_SRGB,
+ };
+
+ ret = v4l2_subdev_routing_validate(sd, routing,
+ V4L2_SUBDEV_ROUTING_ONLY_1_TO_1 |
+ V4L2_SUBDEV_ROUTING_NO_SOURCE_MULTIPLEXING);
+
+ if (ret)
+ return ret;
+
+ ret = v4l2_subdev_set_routing_with_fmt(sd, state, routing, &format);
+
+ return ret;
+}
+
+static int ti_csi2rx_sd_set_routing(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ enum v4l2_subdev_format_whence which,
+ struct v4l2_subdev_krouting *routing)
+{
+ return _ti_csi2rx_sd_set_routing(sd, state, routing);
+}
+
static int ti_csi2rx_sd_init_state(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state)
{
- struct v4l2_subdev_format format = {
- .pad = TI_CSI2RX_PAD_SINK,
- .format = {
- .width = 640,
- .height = 480,
- .code = MEDIA_BUS_FMT_UYVY8_1X16,
- .field = V4L2_FIELD_NONE,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .ycbcr_enc = V4L2_YCBCR_ENC_601,
- .quantization = V4L2_QUANTIZATION_LIM_RANGE,
- .xfer_func = V4L2_XFER_FUNC_SRGB,
- },
+ struct v4l2_subdev_route routes[] = { {
+ .sink_pad = 0,
+ .sink_stream = 0,
+ .source_pad = TI_CSI2RX_PAD_FIRST_SOURCE,
+ .source_stream = 0,
+ .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE,
+ } };
+
+ struct v4l2_subdev_krouting routing = {
+ .num_routes = 1,
+ .routes = routes,
};

- return ti_csi2rx_sd_set_fmt(sd, state, &format);
+ /* Initialize routing to single route to the fist source pad */
+ return _ti_csi2rx_sd_set_routing(sd, state, &routing);
}

static int ti_csi2rx_sd_s_stream(struct v4l2_subdev *sd, int enable)
@@ -1087,6 +1162,7 @@ static int ti_csi2rx_sd_s_stream(struct v4l2_subdev *sd, int enable)
}

static const struct v4l2_subdev_pad_ops ti_csi2rx_subdev_pad_ops = {
+ .set_routing = ti_csi2rx_sd_set_routing,
.get_fmt = v4l2_subdev_get_fmt,
.set_fmt = ti_csi2rx_sd_set_fmt,
};
@@ -1288,7 +1364,7 @@ static int ti_csi2rx_v4l2_init(struct ti_csi2rx_dev *csi)
v4l2_subdev_init(sd, &ti_csi2rx_subdev_ops);
sd->internal_ops = &ti_csi2rx_internal_ops;
sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
- sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
+ sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS;
strscpy(sd->name, dev_name(csi->dev), sizeof(sd->name));
sd->dev = csi->dev;
sd->entity.ops = &ti_csi2rx_subdev_entity_ops;

--
2.43.0


2024-02-22 11:39:07

by Jai Luthra

[permalink] [raw]
Subject: [PATCH RFC 20/21] SQUASH: media: ti: j721e-csi2rx: Assert pixel reset before stopping last stream

With multi-stream support, make sure pixel reset is only asserted when
last-stream is stopped.

We assert this before stopping the stream to prevent issues with module
unload/reload. As the device's power domain cannot be cleanly switched
off by the firmware if stale data is present on the PSI-L endpoint,
which can happen if source was streaming but we pulling data out through
DMA.

Signed-off-by: Jai Luthra <[email protected]>
---
drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
index c2d47507b3a7..0b9e0852da9f 100644
--- a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
+++ b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
@@ -560,8 +560,10 @@ static void ti_csi2rx_setup_shim(struct ti_csi2rx_ctx *ctx)
fmt = find_format_by_fourcc(ctx->v_fmt.fmt.pix.pixelformat);

/* De-assert the pixel interface reset. */
- reg = SHIM_CNTL_PIX_RST;
- writel(reg, csi->shim + SHIM_CNTL);
+ if (!csi->enable_count) {
+ reg = SHIM_CNTL_PIX_RST;
+ writel(reg, csi->shim + SHIM_CNTL);
+ }

reg = SHIM_DMACNTX_EN;
reg |= FIELD_PREP(SHIM_DMACNTX_FMT, fmt->csi_dt);
@@ -1008,9 +1010,11 @@ static void ti_csi2rx_stop_streaming(struct vb2_queue *vq)
struct ti_csi2rx_dev *csi = ctx->csi;
int ret;

- video_device_pipeline_stop(&ctx->vdev);
+ /* assert pixel reset to prevent stale data on stopping last stream */
+ if (csi->enable_count == 1)
+ writel(0, csi->shim + SHIM_CNTL);

- writel(0, csi->shim + SHIM_CNTL);
+ video_device_pipeline_stop(&ctx->vdev);
writel(0, csi->shim + SHIM_DMACNTX(ctx->idx));

ret = v4l2_subdev_disable_streams(&csi->subdev,

--
2.43.0


2024-02-22 11:39:16

by Jai Luthra

[permalink] [raw]
Subject: [PATCH RFC 19/21] SQUASH: media: ti: j721e-csi2rx: Enable per-stream controls

Use the new enable_streams() APIs to start/stop individual streams on
the connected subdev when capture is initiated on the corresponding v4l2
device.

Signed-off-by: Jai Luthra <[email protected]>
---
.../media/platform/ti/j721e-csi2rx/j721e-csi2rx.c | 78 ++++++++++++++--------
1 file changed, 52 insertions(+), 26 deletions(-)

diff --git a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
index b33681e1e2db..c2d47507b3a7 100644
--- a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
+++ b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
@@ -983,7 +983,9 @@ static int ti_csi2rx_start_streaming(struct vb2_queue *vq, unsigned int count)
dma->state = TI_CSI2RX_DMA_ACTIVE;
spin_unlock_irqrestore(&dma->lock, flags);

- ret = v4l2_subdev_call(&csi->subdev, video, s_stream, 1);
+ ret = v4l2_subdev_enable_streams(&csi->subdev,
+ TI_CSI2RX_PAD_FIRST_SOURCE + ctx->idx,
+ BIT(0));
if (ret)
goto err_dma;

@@ -1011,7 +1013,10 @@ static void ti_csi2rx_stop_streaming(struct vb2_queue *vq)
writel(0, csi->shim + SHIM_CNTL);
writel(0, csi->shim + SHIM_DMACNTX(ctx->idx));

- ret = v4l2_subdev_call(&csi->subdev, video, s_stream, 0);
+ ret = v4l2_subdev_disable_streams(&csi->subdev,
+ TI_CSI2RX_PAD_FIRST_SOURCE + ctx->idx,
+ BIT(0));
+
if (ret)
dev_err(csi->dev, "Failed to stop subdev stream\n");

@@ -1126,36 +1131,60 @@ static int ti_csi2rx_sd_init_state(struct v4l2_subdev *sd,
return _ti_csi2rx_sd_set_routing(sd, state, &routing);
}

-static int ti_csi2rx_sd_s_stream(struct v4l2_subdev *sd, int enable)
+static int ti_csi2rx_sd_enable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ u32 pad, u64 streams_mask)
{
struct ti_csi2rx_dev *csi = to_csi2rx_dev(sd);
+ struct media_pad *remote_pad;
+ u64 sink_streams;
int ret = 0;

- mutex_lock(&csi->mutex);
+ remote_pad = media_entity_remote_source_pad_unique(&csi->subdev.entity);
+ if (!remote_pad)
+ return -ENODEV;
+ sink_streams = v4l2_subdev_state_xlate_streams(state, pad,
+ TI_CSI2RX_PAD_SINK,
+ &streams_mask);

- if (enable) {
- if (csi->enable_count > 0) {
- csi->enable_count++;
- goto out;
- }
+ ret = v4l2_subdev_enable_streams(csi->source, remote_pad->index,
+ sink_streams);
+ if (ret)
+ return ret;

- ret = v4l2_subdev_call(csi->source, video, s_stream, 1);
- if (ret)
- goto out;
+ mutex_lock(&csi->mutex);
+ csi->enable_count++;
+ mutex_unlock(&csi->mutex);

- csi->enable_count++;
- } else {
- if (csi->enable_count == 0) {
- ret = -EINVAL;
- goto out;
- }
+ return 0;
+}
+
+static int ti_csi2rx_sd_disable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ u32 pad, u64 streams_mask)
+{
+ struct ti_csi2rx_dev *csi = to_csi2rx_dev(sd);
+ struct media_pad *remote_pad;
+ u64 sink_streams;
+ int ret = 0;

- if (--csi->enable_count > 0)
- goto out;
+ remote_pad = media_entity_remote_source_pad_unique(&csi->subdev.entity);
+ if (!remote_pad)
+ return -ENODEV;
+ sink_streams = v4l2_subdev_state_xlate_streams(state, pad,
+ TI_CSI2RX_PAD_SINK,
+ &streams_mask);

- ret = v4l2_subdev_call(csi->source, video, s_stream, 0);
+ mutex_lock(&csi->mutex);
+ if (csi->enable_count == 0) {
+ ret = -EINVAL;
+ goto out;
}

+ ret = v4l2_subdev_disable_streams(csi->source, remote_pad->index,
+ sink_streams);
+ if (!ret)
+ --csi->enable_count;
out:
mutex_unlock(&csi->mutex);
return ret;
@@ -1165,14 +1194,11 @@ static const struct v4l2_subdev_pad_ops ti_csi2rx_subdev_pad_ops = {
.set_routing = ti_csi2rx_sd_set_routing,
.get_fmt = v4l2_subdev_get_fmt,
.set_fmt = ti_csi2rx_sd_set_fmt,
-};
-
-static const struct v4l2_subdev_video_ops ti_csi2rx_subdev_video_ops = {
- .s_stream = ti_csi2rx_sd_s_stream,
+ .enable_streams = ti_csi2rx_sd_enable_streams,
+ .disable_streams = ti_csi2rx_sd_disable_streams,
};

static const struct v4l2_subdev_ops ti_csi2rx_subdev_ops = {
- .video = &ti_csi2rx_subdev_video_ops,
.pad = &ti_csi2rx_subdev_pad_ops,
};


--
2.43.0


2024-02-22 11:39:22

by Jai Luthra

[permalink] [raw]
Subject: [PATCH RFC 21/21] media: ti: j721e-csi2rx: Submit all available buffers

We already make sure to submit all available buffers to DMA in each DMA
completion callback.

Move that logic in a separate function, and use it during stream start
as well, as most application queue all their buffers before stream on.

Signed-off-by: Jai Luthra <[email protected]>
---
.../media/platform/ti/j721e-csi2rx/j721e-csi2rx.c | 43 ++++++++++++----------
1 file changed, 24 insertions(+), 19 deletions(-)

diff --git a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
index 0b9e0852da9f..a682db099e6c 100644
--- a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
+++ b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
@@ -666,6 +666,27 @@ static int ti_csi2rx_drain_dma(struct ti_csi2rx_ctx *ctx)
return ret;
}

+static int ti_csi2rx_dma_submit_pending(struct ti_csi2rx_ctx *ctx)
+{
+ struct ti_csi2rx_dma *dma = &ctx->dma;
+ struct ti_csi2rx_buffer *buf;
+ int ret = 0;
+
+ /* If there are more buffers to process then start their transfer. */
+ while (!list_empty(&dma->queue)) {
+ buf = list_entry(dma->queue.next, struct ti_csi2rx_buffer, list);
+ ret = ti_csi2rx_start_dma(ctx, buf);
+ if (ret) {
+ dev_err(ctx->csi->dev,
+ "Failed to queue the next buffer for DMA\n");
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+ break;
+ }
+ list_move_tail(&buf->list, &dma->submitted);
+ }
+ return ret;
+}
+
static void ti_csi2rx_dma_callback(void *param)
{
struct ti_csi2rx_buffer *buf = param;
@@ -686,18 +707,7 @@ static void ti_csi2rx_dma_callback(void *param)
vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
list_del(&buf->list);

- /* If there are more buffers to process then start their transfer. */
- while (!list_empty(&dma->queue)) {
- buf = list_entry(dma->queue.next, struct ti_csi2rx_buffer, list);
-
- if (ti_csi2rx_start_dma(ctx, buf)) {
- dev_err(ctx->csi->dev,
- "Failed to queue the next buffer for DMA\n");
- vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
- } else {
- list_move_tail(&buf->list, &dma->submitted);
- }
- }
+ ti_csi2rx_dma_submit_pending(ctx);

if (list_empty(&dma->submitted))
dma->state = TI_CSI2RX_DMA_IDLE;
@@ -906,7 +916,6 @@ static int ti_csi2rx_start_streaming(struct vb2_queue *vq, unsigned int count)
struct ti_csi2rx_ctx *ctx = vb2_get_drv_priv(vq);
struct ti_csi2rx_dev *csi = ctx->csi;
struct ti_csi2rx_dma *dma = &ctx->dma;
- struct ti_csi2rx_buffer *buf;
struct v4l2_subdev_krouting *routing;
struct v4l2_subdev_route *route = NULL;
struct media_pad *remote_pad;
@@ -972,16 +981,13 @@ static int ti_csi2rx_start_streaming(struct vb2_queue *vq, unsigned int count)
ctx->sequence = 0;

spin_lock_irqsave(&dma->lock, flags);
- buf = list_entry(dma->queue.next, struct ti_csi2rx_buffer, list);

- ret = ti_csi2rx_start_dma(ctx, buf);
+ ret = ti_csi2rx_dma_submit_pending(ctx);
if (ret) {
- dev_err(csi->dev, "Failed to start DMA: %d\n", ret);
spin_unlock_irqrestore(&dma->lock, flags);
- goto err_pipeline;
+ goto err_dma;
}

- list_move_tail(&buf->list, &dma->submitted);
dma->state = TI_CSI2RX_DMA_ACTIVE;
spin_unlock_irqrestore(&dma->lock, flags);

@@ -995,7 +1001,6 @@ static int ti_csi2rx_start_streaming(struct vb2_queue *vq, unsigned int count)

err_dma:
ti_csi2rx_stop_dma(ctx);
-err_pipeline:
video_device_pipeline_stop(&ctx->vdev);
writel(0, csi->shim + SHIM_CNTL);
writel(0, csi->shim + SHIM_DMACNTX(ctx->idx));

--
2.43.0


2024-02-22 11:41:43

by Jai Luthra

[permalink] [raw]
Subject: [PATCH RFC 06/21] media: ti: j721e-csi2rx: prepare SHIM code for multiple contexts

From: Pratyush Yadav <[email protected]>

Currently the SHIM code to configure the context only touches the first
context. Add support for writing to the context's registers based on the
context index.

Signed-off-by: Pratyush Yadav <[email protected]>
Signed-off-by: Jai Luthra <[email protected]>
---
drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
index 51fac664d01c..e42941d8c457 100644
--- a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
+++ b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
@@ -25,7 +25,7 @@
#define SHIM_CNTL 0x10
#define SHIM_CNTL_PIX_RST BIT(0)

-#define SHIM_DMACNTX 0x20
+#define SHIM_DMACNTX(i) (0x20 + ((i) * 0x20))
#define SHIM_DMACNTX_EN BIT(31)
#define SHIM_DMACNTX_YUV422 GENMASK(27, 26)
#define SHIM_DMACNTX_SIZE GENMASK(21, 20)
@@ -35,7 +35,7 @@
#define SHIM_DMACNTX_SIZE_16 1
#define SHIM_DMACNTX_SIZE_32 2

-#define SHIM_PSI_CFG0 0x24
+#define SHIM_PSI_CFG0(i) (0x24 + ((i) * 0x20))
#define SHIM_PSI_CFG0_SRC_TAG GENMASK(15, 0)
#define SHIM_PSI_CFG0_DST_TAG GENMASK(31, 16)

@@ -573,11 +573,11 @@ static void ti_csi2rx_setup_shim(struct ti_csi2rx_ctx *ctx)

reg |= FIELD_PREP(SHIM_DMACNTX_SIZE, fmt->size);

- writel(reg, csi->shim + SHIM_DMACNTX);
+ writel(reg, csi->shim + SHIM_DMACNTX(ctx->idx));

reg = FIELD_PREP(SHIM_PSI_CFG0_SRC_TAG, 0) |
FIELD_PREP(SHIM_PSI_CFG0_DST_TAG, 0);
- writel(reg, csi->shim + SHIM_PSI_CFG0);
+ writel(reg, csi->shim + SHIM_PSI_CFG0(ctx->idx));
}

static void ti_csi2rx_drain_callback(void *param)
@@ -894,7 +894,7 @@ static int ti_csi2rx_start_streaming(struct vb2_queue *vq, unsigned int count)
err_pipeline:
video_device_pipeline_stop(&ctx->vdev);
writel(0, csi->shim + SHIM_CNTL);
- writel(0, csi->shim + SHIM_DMACNTX);
+ writel(0, csi->shim + SHIM_DMACNTX(ctx->idx));
err:
ti_csi2rx_cleanup_buffers(ctx, VB2_BUF_STATE_QUEUED);
return ret;
@@ -909,7 +909,7 @@ static void ti_csi2rx_stop_streaming(struct vb2_queue *vq)
video_device_pipeline_stop(&ctx->vdev);

writel(0, csi->shim + SHIM_CNTL);
- writel(0, csi->shim + SHIM_DMACNTX);
+ writel(0, csi->shim + SHIM_DMACNTX(ctx->idx));

ret = v4l2_subdev_call(csi->source, video, s_stream, 0);
if (ret)

--
2.43.0


2024-02-22 11:48:19

by Jai Luthra

[permalink] [raw]
Subject: [PATCH RFC 14/21] media: cadence: csi2rx: Enable stream-wise routing

Cadence CSI-2 bridge IP supports capturing multiple virtual "streams"
of data over the same physical interface using MIPI Virtual Channels.

The V4L2 subdev APIs should reflect this capability and allow per-stream
routing and controls.

While the hardware IP supports usecases where streams coming in the sink
pad can be broadcasted to multiple source pads, the driver will need
significant re-architecture to make that possible. The two users of this
IP in mainline linux are TI Shim and StarFive JH7110 CAMSS, and both
have only integrated the first source pad i.e stream0 of this IP. So for
now keep it simple and only allow 1-to-1 mapping of streams from sink to
source, without any broadcasting.

This commit only adds support for subdev routing APIs, without actually
setting the registers in the Cadence IP to filter for particular virtual
channels. Subsequent commits will add that support.

Signed-off-by: Jai Luthra <[email protected]>
---
drivers/media/platform/cadence/cdns-csi2rx.c | 91 ++++++++++++++++++++++------
1 file changed, 71 insertions(+), 20 deletions(-)

diff --git a/drivers/media/platform/cadence/cdns-csi2rx.c b/drivers/media/platform/cadence/cdns-csi2rx.c
index bac341881e41..1b76610ff617 100644
--- a/drivers/media/platform/cadence/cdns-csi2rx.c
+++ b/drivers/media/platform/cadence/cdns-csi2rx.c
@@ -450,12 +450,55 @@ static int csi2rx_enum_mbus_code(struct v4l2_subdev *subdev,
return 0;
}

+static int _csi2rx_set_routing(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_krouting *routing)
+{
+ static const struct v4l2_mbus_framefmt format = {
+ .width = 640,
+ .height = 480,
+ .code = MEDIA_BUS_FMT_UYVY8_1X16,
+ .field = V4L2_FIELD_NONE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .ycbcr_enc = V4L2_YCBCR_ENC_601,
+ .quantization = V4L2_QUANTIZATION_LIM_RANGE,
+ .xfer_func = V4L2_XFER_FUNC_SRGB,
+ };
+ int ret;
+
+ if (routing->num_routes > V4L2_FRAME_DESC_ENTRY_MAX)
+ return -EINVAL;
+
+ ret = v4l2_subdev_routing_validate(subdev, routing,
+ V4L2_SUBDEV_ROUTING_ONLY_1_TO_1);
+ if (ret)
+ return ret;
+
+ ret = v4l2_subdev_set_routing_with_fmt(subdev, state, routing, &format);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int csi2rx_set_routing(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_state *state,
+ enum v4l2_subdev_format_whence which,
+ struct v4l2_subdev_krouting *routing)
+{
+ struct csi2rx_priv *csi2rx = v4l2_subdev_to_csi2rx(subdev);
+
+ if (which == V4L2_SUBDEV_FORMAT_ACTIVE && csi2rx->count)
+ return -EBUSY;
+
+ return _csi2rx_set_routing(subdev, state, routing);
+}
+
static int csi2rx_set_fmt(struct v4l2_subdev *subdev,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *format)
{
struct v4l2_mbus_framefmt *fmt;
- unsigned int i;

/* No transcoding, source and sink formats must match. */
if (format->pad != CSI2RX_PAD_SINK)
@@ -467,14 +510,19 @@ static int csi2rx_set_fmt(struct v4l2_subdev *subdev,
format->format.field = V4L2_FIELD_NONE;

/* Set sink format */
- fmt = v4l2_subdev_state_get_format(state, format->pad);
+ fmt = v4l2_subdev_state_get_format(state, format->pad, format->stream);
+ if (!fmt)
+ return -EINVAL;
+
*fmt = format->format;

- /* Propagate to source formats */
- for (i = CSI2RX_PAD_SOURCE_STREAM0; i < CSI2RX_PAD_MAX; i++) {
- fmt = v4l2_subdev_state_get_format(state, i);
- *fmt = format->format;
- }
+ /* Propagate to source format */
+ fmt = v4l2_subdev_state_get_opposite_stream_format(state, format->pad,
+ format->stream);
+ if (!fmt)
+ return -EINVAL;
+
+ *fmt = format->format;

return 0;
}
@@ -482,21 +530,22 @@ static int csi2rx_set_fmt(struct v4l2_subdev *subdev,
static int csi2rx_init_state(struct v4l2_subdev *subdev,
struct v4l2_subdev_state *state)
{
- struct v4l2_subdev_format format = {
- .pad = CSI2RX_PAD_SINK,
- .format = {
- .width = 640,
- .height = 480,
- .code = MEDIA_BUS_FMT_UYVY8_1X16,
- .field = V4L2_FIELD_NONE,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .ycbcr_enc = V4L2_YCBCR_ENC_601,
- .quantization = V4L2_QUANTIZATION_LIM_RANGE,
- .xfer_func = V4L2_XFER_FUNC_SRGB,
+ struct v4l2_subdev_route routes[] = {
+ {
+ .sink_pad = CSI2RX_PAD_SINK,
+ .sink_stream = 0,
+ .source_pad = CSI2RX_PAD_SOURCE_STREAM0,
+ .source_stream = 0,
+ .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE,
},
};

- return csi2rx_set_fmt(subdev, state, &format);
+ struct v4l2_subdev_krouting routing = {
+ .num_routes = ARRAY_SIZE(routes),
+ .routes = routes,
+ };
+
+ return _csi2rx_set_routing(subdev, state, &routing);
}

static int csi2rx_get_frame_desc(struct v4l2_subdev *subdev, unsigned int pad,
@@ -512,6 +561,7 @@ static const struct v4l2_subdev_pad_ops csi2rx_pad_ops = {
.get_fmt = v4l2_subdev_get_fmt,
.set_fmt = csi2rx_set_fmt,
.get_frame_desc = csi2rx_get_frame_desc,
+ .set_routing = csi2rx_set_routing,
};

static const struct v4l2_subdev_video_ops csi2rx_video_ops = {
@@ -770,7 +820,8 @@ static int csi2rx_probe(struct platform_device *pdev)
csi2rx->pads[CSI2RX_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
for (i = CSI2RX_PAD_SOURCE_STREAM0; i < CSI2RX_PAD_MAX; i++)
csi2rx->pads[i].flags = MEDIA_PAD_FL_SOURCE;
- csi2rx->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ csi2rx->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
+ V4L2_SUBDEV_FL_STREAMS;
csi2rx->subdev.entity.ops = &csi2rx_media_ops;

ret = media_entity_pads_init(&csi2rx->subdev.entity, CSI2RX_PAD_MAX,

--
2.43.0


2024-02-22 12:25:46

by Rob Herring (Arm)

[permalink] [raw]
Subject: Re: [PATCH RFC 04/21] dt-bindings: media: ti,j721e-csi2rx-shim: Support 32 dma chans


On Thu, 22 Feb 2024 17:01:20 +0530, Jai Luthra wrote:
> The CSI2RX SHIM IP can support a maximum of 32x DMA channels.
>
> These can be used to split incoming "streams" of data on the CSI-RX
> port, distinguished by MIPI Virtual Channel (or Data Type), into
> different locations in memory (/dev/videoX nodes).
>
> Actual number of DMA channels reserved is different for each SoC
> integrating this IP, but a maximum of 32x channels are always available
> in this IP's register space, so set minimum as 1 and maximum as 32.
>
> Link: https://www.ti.com/lit/pdf/spruiv7
> Signed-off-by: Jai Luthra <[email protected]>
> ---
> .../bindings/media/ti,j721e-csi2rx-shim.yaml | 40 ++++++++++++++++++++--
> 1 file changed, 37 insertions(+), 3 deletions(-)
>

My bot found errors running 'make DT_CHECKER_FLAGS=-m dt_binding_check'
on your patch (DT_CHECKER_FLAGS is new in v5.13):

yamllint warnings/errors:

dtschema/dtc warnings/errors:
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/media/ti,j721e-csi2rx-shim.yaml: properties:dma-names: {'minItems': 1, 'maxItems': 32, 'items': [{'const': 'rx0'}, {'const': 'rx1'}, {'const': 'rx2'}, {'const': 'rx3'}, {'const': 'rx4'}, {'const': 'rx5'}, {'const': 'rx6'}, {'const': 'rx7'}, {'const': 'rx8'}, {'const': 'rx9'}, {'const': 'rx10'}, {'const': 'rx11'}, {'const': 'rx12'}, {'const': 'rx13'}, {'const': 'rx14'}, {'const': 'rx15'}, {'const': 'rx16'}, {'const': 'rx17'}, {'const': 'rx18'}, {'const': 'rx19'}, {'const': 'rx20'}, {'const': 'rx21'}, {'const': 'rx22'}, {'const': 'rx23'}, {'const': 'rx24'}, {'const': 'rx25'}, {'const': 'rx26'}, {'const': 'rx27'}, {'const': 'rx28'}, {'const': 'rx29'}, {'const': 'rx30'}, {'const': 'rx31'}]} should not be valid under {'required': ['maxItems']}
hint: "maxItems" is not needed with an "items" list
from schema $id: http://devicetree.org/meta-schemas/items.yaml#

doc reference errors (make refcheckdocs):

See https://patchwork.ozlabs.org/project/devicetree-bindings/patch/[email protected]

The base for the series is generally the latest rc1. A different dependency
should be noted in *this* patch.

If you already ran 'make dt_binding_check' and didn't see the above
error(s), then make sure 'yamllint' is installed and dt-schema is up to
date:

pip3 install dtschema --upgrade

Please check and re-submit after running the above command yourself. Note
that DT_SCHEMA_FILES can be set to your schema file to speed up checking
your schema. However, it must be unset to test all examples with your schema.


2024-02-22 14:30:44

by Julien Massot

[permalink] [raw]
Subject: Re: [PATCH RFC 02/21] media: cadence: csi2rx: configure DPHY before starting source stream

Hi Jai,

On 2/22/24 12:31, Jai Luthra wrote:
> From: Pratyush Yadav <[email protected]>
>
> When the source device is operating above 1.5 Gbps per lane, it needs to
> send the Skew Calibration Sequence before sending any HS data. If the
> DPHY is initialized after the source stream is started, then it might
> miss the sequence and not be able to receive data properly. Move the
> start of source subdev to the end of the sequence to make sure
> everything is ready to receive data before the source starts streaming.
>
> Signed-off-by: Pratyush Yadav <[email protected]>
> Signed-off-by: Jai Luthra <[email protected]>

Thanks for your patch!
This patch is useful even at lower data rate than 1.5 Gbps.
I'm having the issue at 800Mbps with max96714F deserializer.

Should this patch deserve a Fixes tag?
Fixes: 3295cf1241d3 ("media: cadence: Add support for external dphy")

Reviewed-by: Julien Massot <[email protected]>
Tested-by: Julien Massot <[email protected]>

> ---
> drivers/media/platform/cadence/cdns-csi2rx.c | 26 ++++++++++++++------------
> 1 file changed, 14 insertions(+), 12 deletions(-)
>
> diff --git a/drivers/media/platform/cadence/cdns-csi2rx.c b/drivers/media/platform/cadence/cdns-csi2rx.c
> index 70b7f8a9e4f2..75e602c1d762 100644
> --- a/drivers/media/platform/cadence/cdns-csi2rx.c
> +++ b/drivers/media/platform/cadence/cdns-csi2rx.c
> @@ -243,10 +243,6 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx)
>
> writel(reg, csi2rx->base + CSI2RX_STATIC_CFG_REG);
>
> - ret = v4l2_subdev_call(csi2rx->source_subdev, video, s_stream, true);
> - if (ret)
> - goto err_disable_pclk;
> -
> /* Enable DPHY clk and data lanes. */
> if (csi2rx->dphy) {
> reg = CSI2RX_DPHY_CL_EN | CSI2RX_DPHY_CL_RST;
> @@ -256,6 +252,13 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx)
> }
>
> writel(reg, csi2rx->base + CSI2RX_DPHY_LANE_CTRL_REG);
> +
> + ret = csi2rx_configure_ext_dphy(csi2rx);
> + if (ret) {
> + dev_err(csi2rx->dev,
> + "Failed to configure external DPHY: %d\n", ret);
> + goto err_disable_pclk;
> + }
> }
>
> /*
> @@ -295,14 +298,9 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx)
>
> reset_control_deassert(csi2rx->sys_rst);
>
> - if (csi2rx->dphy) {
> - ret = csi2rx_configure_ext_dphy(csi2rx);
> - if (ret) {
> - dev_err(csi2rx->dev,
> - "Failed to configure external DPHY: %d\n", ret);
> - goto err_disable_sysclk;
> - }
> - }
> + ret = v4l2_subdev_call(csi2rx->source_subdev, video, s_stream, true);
> + if (ret)
> + goto err_disable_sysclk;
>
> clk_disable_unprepare(csi2rx->p_clk);
>
> @@ -316,6 +314,10 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx)
> clk_disable_unprepare(csi2rx->pixel_clk[i - 1]);
> }
>
> + if (csi2rx->dphy) {
> + writel(0, csi2rx->base + CSI2RX_DPHY_LANE_CTRL_REG);
> + phy_power_off(csi2rx->dphy);
> + }
> err_disable_pclk:
> clk_disable_unprepare(csi2rx->p_clk);
>
>

--
Julien Massot
Senior Software Engineer
Collabora Ltd.
Platinum Building, St John's Innovation Park, Cambridge CB4 0DS, UK
Registered in England & Wales, no. 5513718

2024-02-23 02:46:51

by Changhuang Liang

[permalink] [raw]
Subject: 回复: [PATCH RFC 02/21] media: cadence: csi 2rx: configure DPHY before starting source st ream

Hi, Jai

> [PATCH RFC 02/21] media: cadence: csi2rx: configure DPHY before
> starting source stream
>
> From: Pratyush Yadav <[email protected]>
>
> When the source device is operating above 1.5 Gbps per lane, it needs to send
> the Skew Calibration Sequence before sending any HS data. If the DPHY is
> initialized after the source stream is started, then it might miss the sequence
> and not be able to receive data properly. Move the start of source subdev to
> the end of the sequence to make sure everything is ready to receive data
> before the source starts streaming.
>
> Signed-off-by: Pratyush Yadav <[email protected]>
> Signed-off-by: Jai Luthra <[email protected]>
> ---

I also tested this patch on StarFive JH7100 CAMSS and it worked fine.

Reviewed-by: Changhuang Liang <[email protected]>
Tested-by: Changhuang Liang <[email protected]>

regards
Changhuang

2024-02-23 03:27:47

by Changhuang Liang

[permalink] [raw]
Subject: 回复: [PATCH RFC 00/21] media: cadence,ti: CSI2RX Multistream Support

Hi Jai,

> [PATCH RFC 00/21] media: cadence,ti: CSI2RX Multistream Support
[...]
> .../bindings/media/ti,j721e-csi2rx-shim.yaml | 40 +-
> drivers/media/platform/cadence/cdns-csi2rx.c | 460 +++++++++--
> .../media/platform/ti/j721e-csi2rx/j721e-csi2rx.c | 853
> +++++++++++++++------
> 3 files changed, 1052 insertions(+), 301 deletions(-)
> ---
> base-commit: d894a2a286fccd6e47cd1cac4c2d4ff5d300d7c7

Can you provide me with where this commit comes from, I can only
apply partial patchs to test.

Thanks,
Changhuang

2024-02-23 07:24:32

by Jai Luthra

[permalink] [raw]
Subject: Re: Re: [PATCH RFC 02/21] media: cadence: csi2rx: configure DPHY before starting source stream

Hi Julien,

On Feb 22, 2024 at 15:29:54 +0100, Julien Massot wrote:
> Hi Jai,
>
> On 2/22/24 12:31, Jai Luthra wrote:
> > From: Pratyush Yadav <[email protected]>
> >
> > When the source device is operating above 1.5 Gbps per lane, it needs to
> > send the Skew Calibration Sequence before sending any HS data. If the
> > DPHY is initialized after the source stream is started, then it might
> > miss the sequence and not be able to receive data properly. Move the
> > start of source subdev to the end of the sequence to make sure
> > everything is ready to receive data before the source starts streaming.
> >
> > Signed-off-by: Pratyush Yadav <[email protected]>
> > Signed-off-by: Jai Luthra <[email protected]>
>
> Thanks for your patch!
> This patch is useful even at lower data rate than 1.5 Gbps.
> I'm having the issue at 800Mbps with max96714F deserializer.
>
> Should this patch deserve a Fixes tag?

Thanks, yes in that case I will add the fixes tag and repost this fix
separately so it can be picked sooner.

> Fixes: 3295cf1241d3 ("media: cadence: Add support for external dphy")
>
> Reviewed-by: Julien Massot <[email protected]>
> Tested-by: Julien Massot <[email protected]>
>
> > ---
> > drivers/media/platform/cadence/cdns-csi2rx.c | 26 ++++++++++++++------------
> > 1 file changed, 14 insertions(+), 12 deletions(-)
> >
> > diff --git a/drivers/media/platform/cadence/cdns-csi2rx.c b/drivers/media/platform/cadence/cdns-csi2rx.c
> > index 70b7f8a9e4f2..75e602c1d762 100644
> > --- a/drivers/media/platform/cadence/cdns-csi2rx.c
> > +++ b/drivers/media/platform/cadence/cdns-csi2rx.c
> > @@ -243,10 +243,6 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx)
> > writel(reg, csi2rx->base + CSI2RX_STATIC_CFG_REG);
> > - ret = v4l2_subdev_call(csi2rx->source_subdev, video, s_stream, true);
> > - if (ret)
> > - goto err_disable_pclk;
> > -
> > /* Enable DPHY clk and data lanes. */
> > if (csi2rx->dphy) {
> > reg = CSI2RX_DPHY_CL_EN | CSI2RX_DPHY_CL_RST;
> > @@ -256,6 +252,13 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx)
> > }
> > writel(reg, csi2rx->base + CSI2RX_DPHY_LANE_CTRL_REG);
> > +
> > + ret = csi2rx_configure_ext_dphy(csi2rx);
> > + if (ret) {
> > + dev_err(csi2rx->dev,
> > + "Failed to configure external DPHY: %d\n", ret);
> > + goto err_disable_pclk;
> > + }
> > }
> > /*
> > @@ -295,14 +298,9 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx)
> > reset_control_deassert(csi2rx->sys_rst);
> > - if (csi2rx->dphy) {
> > - ret = csi2rx_configure_ext_dphy(csi2rx);
> > - if (ret) {
> > - dev_err(csi2rx->dev,
> > - "Failed to configure external DPHY: %d\n", ret);
> > - goto err_disable_sysclk;
> > - }
> > - }
> > + ret = v4l2_subdev_call(csi2rx->source_subdev, video, s_stream, true);
> > + if (ret)
> > + goto err_disable_sysclk;
> > clk_disable_unprepare(csi2rx->p_clk);
> > @@ -316,6 +314,10 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx)
> > clk_disable_unprepare(csi2rx->pixel_clk[i - 1]);
> > }
> > + if (csi2rx->dphy) {
> > + writel(0, csi2rx->base + CSI2RX_DPHY_LANE_CTRL_REG);
> > + phy_power_off(csi2rx->dphy);
> > + }
> > err_disable_pclk:
> > clk_disable_unprepare(csi2rx->p_clk);
> >
>
> --
> Julien Massot
> Senior Software Engineer
> Collabora Ltd.
> Platinum Building, St John's Innovation Park, Cambridge CB4 0DS, UK
> Registered in England & Wales, no. 5513718

--
Thanks,
Jai

GPG Fingerprint: 4DE0 D818 E5D5 75E8 D45A AFC5 43DE 91F9 249A 7145


Attachments:
(No filename) (3.54 kB)
signature.asc (849.00 B)
Download all attachments

2024-02-23 07:45:17

by Jai Luthra

[permalink] [raw]
Subject: Re: Re: [PATCH RFC 04/21] dt-bindings: media: ti,j721e-csi2rx-shim: Support 32 dma chans

Hi Rob,

On Feb 22, 2024 at 05:25:13 -0700, Rob Herring wrote:
>
> On Thu, 22 Feb 2024 17:01:20 +0530, Jai Luthra wrote:
> > The CSI2RX SHIM IP can support a maximum of 32x DMA channels.
> >
> > These can be used to split incoming "streams" of data on the CSI-RX
> > port, distinguished by MIPI Virtual Channel (or Data Type), into
> > different locations in memory (/dev/videoX nodes).
> >
> > Actual number of DMA channels reserved is different for each SoC
> > integrating this IP, but a maximum of 32x channels are always available
> > in this IP's register space, so set minimum as 1 and maximum as 32.
> >
> > Link: https://www.ti.com/lit/pdf/spruiv7
> > Signed-off-by: Jai Luthra <[email protected]>
> > ---
> > .../bindings/media/ti,j721e-csi2rx-shim.yaml | 40 ++++++++++++++++++++--
> > 1 file changed, 37 insertions(+), 3 deletions(-)
> >
>
> My bot found errors running 'make DT_CHECKER_FLAGS=-m dt_binding_check'
> on your patch (DT_CHECKER_FLAGS is new in v5.13):
>
> yamllint warnings/errors:
>
> dtschema/dtc warnings/errors:
> /builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/media/ti,j721e-csi2rx-shim.yaml: properties:dma-names: {'minItems': 1, 'maxItems': 32, 'items': [{'const': 'rx0'}, {'const': 'rx1'}, {'const': 'rx2'}, {'const': 'rx3'}, {'const': 'rx4'}, {'const': 'rx5'}, {'const': 'rx6'}, {'const': 'rx7'}, {'const': 'rx8'}, {'const': 'rx9'}, {'const': 'rx10'}, {'const': 'rx11'}, {'const': 'rx12'}, {'const': 'rx13'}, {'const': 'rx14'}, {'const': 'rx15'}, {'const': 'rx16'}, {'const': 'rx17'}, {'const': 'rx18'}, {'const': 'rx19'}, {'const': 'rx20'}, {'const': 'rx21'}, {'const': 'rx22'}, {'const': 'rx23'}, {'const': 'rx24'}, {'const': 'rx25'}, {'const': 'rx26'}, {'const': 'rx27'}, {'const': 'rx28'}, {'const': 'rx29'}, {'const': 'rx30'}, {'const': 'rx31'}]} should not be valid under {'required': ['maxItems']}
> hint: "maxItems" is not needed with an "items" list

Thanks will fix in next revision.

> from schema $id: http://devicetree.org/meta-schemas/items.yaml#
>
> doc reference errors (make refcheckdocs):
>
> See https://patchwork.ozlabs.org/project/devicetree-bindings/patch/[email protected]
>
> The base for the series is generally the latest rc1. A different dependency
> should be noted in *this* patch.
>
> If you already ran 'make dt_binding_check' and didn't see the above
> error(s), then make sure 'yamllint' is installed and dt-schema is up to
> date:
>
> pip3 install dtschema --upgrade
>
> Please check and re-submit after running the above command yourself. Note
> that DT_SCHEMA_FILES can be set to your schema file to speed up checking
> your schema. However, it must be unset to test all examples with your schema.
>

--
Thanks,
Jai

GPG Fingerprint: 4DE0 D818 E5D5 75E8 D45A AFC5 43DE 91F9 249A 7145


Attachments:
(No filename) (2.82 kB)
signature.asc (849.00 B)
Download all attachments

2024-02-23 07:52:38

by Jai Luthra

[permalink] [raw]
Subject: Re: 回复: [PATC H RFC 00/21] media: cadence,ti: CSI2RX Multistream Support

Hi Changhuang,

On Feb 23, 2024 at 02:53:36 +0000, Changhuang Liang wrote:
> Hi Jai,
>
> > [PATCH RFC 00/21] media: cadence,ti: CSI2RX Multistream Support
> [...]
> > .../bindings/media/ti,j721e-csi2rx-shim.yaml | 40 +-
> > drivers/media/platform/cadence/cdns-csi2rx.c | 460 +++++++++--
> > .../media/platform/ti/j721e-csi2rx/j721e-csi2rx.c | 853
> > +++++++++++++++------
> > 3 files changed, 1052 insertions(+), 301 deletions(-)
> > ---
> > base-commit: d894a2a286fccd6e47cd1cac4c2d4ff5d300d7c7
>
> Can you provide me with where this commit comes from, I can only
> apply partial patchs to test.

My bad, this series is based on next-20240219 but I had a few DT commits
to test that I forgot to remove before sending it out.

You can apply this on top of
https://gitlab.com/linux-kernel/linux-next/-/tree/next-20240219

>
> Thanks,
> Changhuang

--
Thanks,
Jai

GPG Fingerprint: 4DE0 D818 E5D5 75E8 D45A AFC5 43DE 91F9 249A 7145


Attachments:
(No filename) (989.00 B)
signature.asc (849.00 B)
Download all attachments

2024-03-22 10:00:22

by Changhuang Liang

[permalink] [raw]
Subject: 回复: [PATCH RFC 03/21] media: cadence: csi 2rx: Support runtime PM

Hi, Jai

> [PATCH RFC 03/21] media: cadence: csi2rx: Support runtime PM
>
> From: Jayshri Pawar <[email protected]>
>
> Use runtime power management hooks to save power when CSI-RX is not in
> use. Also stop/start any in-progress streams, which might happen during a
> system suspend/resume cycle.
>
> Signed-off-by: Jayshri Pawar <[email protected]>
> Co-developed-by: Jai Luthra <[email protected]>
> Signed-off-by: Jai Luthra <[email protected]>

I want to send a series containing this patch, So I want to help you send this patch,
What about your opinion?

Regards,
Changhuang

2024-03-22 11:14:41

by Jai Luthra

[permalink] [raw]
Subject: Re: 回复: [PATC H RFC 03/21] media: cadence: csi2rx: Support runtime PM

Hi Changhuang,

On Mar 22, 2024 at 09:26:22 +0000, Changhuang Liang wrote:
> Hi, Jai
>
> > [PATCH RFC 03/21] media: cadence: csi2rx: Support runtime PM
> >
> > From: Jayshri Pawar <[email protected]>
> >
> > Use runtime power management hooks to save power when CSI-RX is not in
> > use. Also stop/start any in-progress streams, which might happen during a
> > system suspend/resume cycle.
> >
> > Signed-off-by: Jayshri Pawar <[email protected]>
> > Co-developed-by: Jai Luthra <[email protected]>
> > Signed-off-by: Jai Luthra <[email protected]>
>
> I want to send a series containing this patch, So I want to help you send this patch,
> What about your opinion?

Not sure I understood exactly - but yes please feel free to post this
patch as part of your series.

That will be appreciated in fact, as this change is not related to the
multi-stream support anyway.

>
> Regards,
> Changhuang

--
Thanks,
Jai

GPG Fingerprint: 4DE0 D818 E5D5 75E8 D45A AFC5 43DE 91F9 249A 7145


Attachments:
(No filename) (1.00 kB)
signature.asc (849.00 B)
Download all attachments