From: Joe Dzado <joe.dzado@icloud.com>
To: Sakari Ailus <sakari.ailus@linux.intel.com>,
Dan Scally <dan.scally@ideasonboard.com>,
Yong Zhi <yong.zhi@intel.com>
Cc: Mauro Carvalho Chehab <mchehab@kernel.org>,
linux-media@vger.kernel.org, linux-kernel@vger.kernel.org,
Joe Dzado <joe.dzado@icloud.com>
Subject: [PATCH 1/3] media: ipu3-cio2: track the streaming queue by DMA channel
Date: Fri, 30 Oct 2026 18:23:30 -0600 [thread overview]
Message-ID: <20261031002332.33609-2-joe.dzado@icloud.com> (raw)
In-Reply-To: <20261031002332.33609-1-joe.dzado@icloud.com>
Prepare for more than one CSI-2 port streaming at once. Replace the
single cur_queue pointer with a chan_queue[] map indexed by DMA channel,
and give each queue a dma_chan it owns (cio2_queue_dma_chan() still
returns CIO2_DMA_CHAN for every port, so only one channel is used for
now). The IRQ handler, buffer-done and SOF paths dispatch via
chan_queue[] instead of assuming cur_queue, and all register accesses
use the queue's channel.
cio2->streaming becomes a count of streaming queues plus a per-queue
flag, and suspend/resume iterate the queues instead of the lone
cur_queue. No functional change: a single stream still uses channel 0.
Signed-off-by: Joe Dzado <joe.dzado@icloud.com>
---
drivers/media/pci/intel/ipu3/ipu3-cio2.c | 125 +++++++++++++++--------
drivers/media/pci/intel/ipu3/ipu3-cio2.h | 8 +-
2 files changed, 88 insertions(+), 45 deletions(-)
diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.c b/drivers/media/pci/intel/ipu3/ipu3-cio2.c
index 4d2b23129..06ce58e23 100644
--- a/drivers/media/pci/intel/ipu3/ipu3-cio2.c
+++ b/drivers/media/pci/intel/ipu3/ipu3-cio2.c
@@ -350,6 +350,12 @@ static int cio2_csi2_calc_timing(struct cio2_device *cio2, struct cio2_queue *q,
return 0;
};
+/* Each queue (CSI-2 port) owns one DMA channel. */
+static unsigned int cio2_queue_dma_chan(unsigned int port)
+{
+ return CIO2_DMA_CHAN;
+}
+
static int cio2_hw_init(struct cio2_device *cio2, struct cio2_queue *q)
{
static const int NUM_VCS = 4;
@@ -364,6 +370,7 @@ static int cio2_hw_init(struct cio2_device *cio2, struct cio2_queue *q)
void __iomem *const base = cio2->base;
u8 lanes, csi2bus = q->csi2.port;
u8 sensor_vc = SENSOR_VIR_CH_DFLT;
+ const unsigned int chan = q->dma_chan;
struct cio2_csi2_timing timing = { 0 };
int i, r;
@@ -438,8 +445,8 @@ static int cio2_hw_init(struct cio2_device *cio2, struct cio2_queue *q)
writel(0, q->csi_rx_base + CIO2_REG_IRQCTRL_LEVEL_NOT_PULSE);
writel(CIO2_INT_EN_EXT_OE_MASK, base + CIO2_REG_INT_EN_EXT_OE);
- writel(CIO2_REG_INT_EN_IRQ | CIO2_INT_IOC(CIO2_DMA_CHAN) |
- CIO2_REG_INT_EN_IOS(CIO2_DMA_CHAN),
+ writel(CIO2_REG_INT_EN_IRQ | CIO2_INT_IOC(chan) |
+ CIO2_REG_INT_EN_IOS(chan),
base + CIO2_REG_INT_EN);
writel((CIO2_PXM_PXF_FMT_CFG_BPP_10 | CIO2_PXM_PXF_FMT_CFG_PCK_64B)
@@ -451,7 +458,7 @@ static int cio2_hw_init(struct cio2_device *cio2, struct cio2_queue *q)
q->csi_rx_base + CIO2_REG_MIPIBE_LP_LUT_ENTRY(ENTRY));
writel(0, q->csi_rx_base + CIO2_REG_MIPIBE_COMP_FORMAT(sensor_vc));
writel(0, q->csi_rx_base + CIO2_REG_MIPIBE_FORCE_RAW8);
- writel(0, base + CIO2_REG_PXM_SID2BID0(csi2bus));
+ writel(chan, base + CIO2_REG_PXM_SID2BID0(csi2bus));
writel(lanes, q->csi_rx_base + CIO2_REG_CSIRX_NOF_ENABLED_LANES);
writel(CIO2_CGC_PRIM_TGE |
@@ -486,7 +493,7 @@ static int cio2_hw_init(struct cio2_device *cio2, struct cio2_queue *q)
}
/* Enable DMA */
- writel(PFN_DOWN(q->fbpt_bus_addr), base + CIO2_REG_CDMABA(CIO2_DMA_CHAN));
+ writel(PFN_DOWN(q->fbpt_bus_addr), base + CIO2_REG_CDMABA(chan));
writel(num_buffers1 << CIO2_CDMAC0_FBPT_LEN_SHIFT |
FBPT_WIDTH << CIO2_CDMAC0_FBPT_WIDTH_SHIFT |
@@ -494,10 +501,10 @@ static int cio2_hw_init(struct cio2_device *cio2, struct cio2_queue *q)
CIO2_CDMAC0_FBPT_UPDATE_FIFO_FULL |
CIO2_CDMAC0_DMA_EN |
CIO2_CDMAC0_DMA_INTR_ON_FS |
- CIO2_CDMAC0_DMA_HALTED, base + CIO2_REG_CDMAC0(CIO2_DMA_CHAN));
+ CIO2_CDMAC0_DMA_HALTED, base + CIO2_REG_CDMAC0(chan));
writel(1 << CIO2_CDMAC1_LINENUMUPDATE_SHIFT,
- base + CIO2_REG_CDMAC1(CIO2_DMA_CHAN));
+ base + CIO2_REG_CDMAC1(chan));
writel(0, base + CIO2_REG_PBM_FOPN_ABORT);
@@ -525,6 +532,7 @@ static void cio2_hw_exit(struct cio2_device *cio2, struct cio2_queue *q)
struct device *dev = &cio2->pci_dev->dev;
void __iomem *const base = cio2->base;
unsigned int i;
+ const unsigned int chan = q->dma_chan;
u32 value;
int ret;
@@ -535,12 +543,12 @@ static void cio2_hw_exit(struct cio2_device *cio2, struct cio2_queue *q)
writel(0, q->csi_rx_base + CIO2_REG_MIPIBE_ENABLE);
/* Halt DMA */
- writel(0, base + CIO2_REG_CDMAC0(CIO2_DMA_CHAN));
- ret = readl_poll_timeout(base + CIO2_REG_CDMAC0(CIO2_DMA_CHAN),
+ writel(0, base + CIO2_REG_CDMAC0(chan));
+ ret = readl_poll_timeout(base + CIO2_REG_CDMAC0(chan),
value, value & CIO2_CDMAC0_DMA_HALTED,
4000, 2000000);
if (ret)
- dev_err(dev, "DMA %i can not be halted\n", CIO2_DMA_CHAN);
+ dev_err(dev, "DMA %u can not be halted\n", chan);
for (i = 0; i < CIO2_NUM_PORTS; i++) {
writel(readl(base + CIO2_REG_PXM_FRF_CFG(i)) |
@@ -553,11 +561,11 @@ static void cio2_hw_exit(struct cio2_device *cio2, struct cio2_queue *q)
static void cio2_buffer_done(struct cio2_device *cio2, unsigned int dma_chan)
{
struct device *dev = &cio2->pci_dev->dev;
- struct cio2_queue *q = cio2->cur_queue;
+ struct cio2_queue *q = cio2->chan_queue[dma_chan];
struct cio2_fbpt_entry *entry;
u64 ns = ktime_get_ns();
- if (dma_chan >= CIO2_QUEUES) {
+ if (!q) {
dev_err(dev, "bad DMA channel %i\n", dma_chan);
return;
}
@@ -711,7 +719,8 @@ static void cio2_irq_handle_once(struct cio2_device *cio2, u32 int_status)
for (d = 0; d < CIO2_NUM_DMA_CHAN; d++)
if (int_status & CIO2_INT_IOC(d)) {
clr |= CIO2_INT_IOC(d);
- cio2_buffer_done(cio2, d);
+ if (cio2->chan_queue[d])
+ cio2_buffer_done(cio2, d);
}
int_status &= ~clr;
}
@@ -724,9 +733,9 @@ static void cio2_irq_handle_once(struct cio2_device *cio2, u32 int_status)
for (d = 0; d < CIO2_NUM_DMA_CHAN; d++)
if (int_status & CIO2_INT_IOS_IOLN(d)) {
clr |= CIO2_INT_IOS_IOLN(d);
- if (d == CIO2_DMA_CHAN)
+ if (cio2->chan_queue[d])
cio2_queue_event_sof(cio2,
- cio2->cur_queue);
+ cio2->chan_queue[d]);
}
int_status &= ~clr;
}
@@ -929,7 +938,7 @@ static void cio2_vb2_buf_queue(struct vb2_buffer *vb)
*/
local_irq_save(flags);
- fbpt_rp = (readl(cio2->base + CIO2_REG_CDMARI(CIO2_DMA_CHAN))
+ fbpt_rp = (readl(cio2->base + CIO2_REG_CDMARI(q->dma_chan))
>> CIO2_CDMARI_FBPT_RP_SHIFT)
& CIO2_CDMARI_FBPT_RP_MASK;
@@ -996,7 +1005,6 @@ static int cio2_vb2_start_streaming(struct vb2_queue *vq, unsigned int count)
struct device *dev = &cio2->pci_dev->dev;
int r;
- cio2->cur_queue = q;
atomic_set(&q->frame_sequence, 0);
r = pm_runtime_resume_and_get(dev);
@@ -1009,21 +1017,28 @@ static int cio2_vb2_start_streaming(struct vb2_queue *vq, unsigned int count)
if (r)
goto fail_pipeline;
+ cio2->chan_queue[q->dma_chan] = q;
r = cio2_hw_init(cio2, q);
- if (r)
+ if (r) {
+ cio2->chan_queue[q->dma_chan] = NULL;
goto fail_hw;
+ }
+ cio2->streaming++;
+ q->streaming = true;
/* Start streaming on sensor */
r = v4l2_subdev_call(q->sensor, video, s_stream, 1);
if (r)
goto fail_csi2_subdev;
- cio2->streaming = true;
-
return 0;
fail_csi2_subdev:
cio2_hw_exit(cio2, q);
+ synchronize_irq(cio2->pci_dev->irq);
+ cio2->chan_queue[q->dma_chan] = NULL;
+ cio2->streaming--;
+ q->streaming = false;
fail_hw:
video_device_pipeline_stop(&q->vdev);
fail_pipeline:
@@ -1045,10 +1060,12 @@ static void cio2_vb2_stop_streaming(struct vb2_queue *vq)
cio2_hw_exit(cio2, q);
synchronize_irq(cio2->pci_dev->irq);
+ cio2->chan_queue[q->dma_chan] = NULL;
+ cio2->streaming--;
+ q->streaming = false;
cio2_vb2_return_all_buffers(q, VB2_BUF_STATE_ERROR);
video_device_pipeline_stop(&q->vdev);
pm_runtime_put(dev);
- cio2->streaming = false;
}
static const struct vb2_ops cio2_vb2_ops = {
@@ -1498,6 +1515,7 @@ static int cio2_queue_init(struct cio2_device *cio2, struct cio2_queue *q)
/* Initialize miscellaneous variables */
mutex_init(&q->lock);
+ q->dma_chan = cio2_queue_dma_chan(q - cio2->queue); /* queue[] is indexed by port */
q->format.width = default_width;
q->format.height = default_height;
@@ -1895,21 +1913,26 @@ static int __maybe_unused cio2_suspend(struct device *dev)
{
struct pci_dev *pci_dev = to_pci_dev(dev);
struct cio2_device *cio2 = pci_get_drvdata(pci_dev);
- struct cio2_queue *q = cio2->cur_queue;
+ struct cio2_queue *q;
+ unsigned int i;
int r;
dev_dbg(dev, "cio2 suspend\n");
if (!cio2->streaming)
return 0;
- /* Stop stream */
- r = v4l2_subdev_call(q->sensor, video, s_stream, 0);
- if (r) {
- dev_err(dev, "failed to stop sensor streaming\n");
- return r;
+ /* Stop streams */
+ for (i = 0; i < CIO2_QUEUES; i++) {
+ q = &cio2->queue[i];
+ if (!q->streaming)
+ continue;
+ r = v4l2_subdev_call(q->sensor, video, s_stream, 0);
+ if (r) {
+ dev_err(dev, "failed to stop sensor streaming\n");
+ return r;
+ }
+ cio2_hw_exit(cio2, q);
}
-
- cio2_hw_exit(cio2, q);
synchronize_irq(pci_dev->irq);
pm_runtime_force_suspend(dev);
@@ -1918,9 +1941,14 @@ static int __maybe_unused cio2_suspend(struct device *dev)
* Upon resume, hw starts to process the fbpt entries from beginning,
* so relocate the queued buffs to the fbpt head before suspend.
*/
- cio2_fbpt_rearrange(cio2, q);
- q->bufs_first = 0;
- q->bufs_next = 0;
+ for (i = 0; i < CIO2_QUEUES; i++) {
+ q = &cio2->queue[i];
+ if (!q->streaming)
+ continue;
+ cio2_fbpt_rearrange(cio2, q);
+ q->bufs_first = 0;
+ q->bufs_next = 0;
+ }
return 0;
}
@@ -1928,30 +1956,41 @@ static int __maybe_unused cio2_suspend(struct device *dev)
static int __maybe_unused cio2_resume(struct device *dev)
{
struct cio2_device *cio2 = dev_get_drvdata(dev);
- struct cio2_queue *q = cio2->cur_queue;
+ unsigned int i, streaming = cio2->streaming;
+ struct cio2_queue *q;
int r;
dev_dbg(dev, "cio2 resume\n");
- if (!cio2->streaming)
+ if (!streaming)
return 0;
- /* Start stream */
+ /* Start streams */
r = pm_runtime_force_resume(dev);
if (r < 0) {
dev_err(dev, "failed to set power %d\n", r);
return r;
}
- r = cio2_hw_init(cio2, q);
- if (r) {
- dev_err(dev, "fail to init cio2 hw\n");
- return r;
- }
+ /* hw_init does the shared setup only for the first queue it brings up */
+ cio2->streaming = 0;
+ for (i = 0; i < CIO2_QUEUES; i++) {
+ q = &cio2->queue[i];
+ if (!q->streaming)
+ continue;
+ r = cio2_hw_init(cio2, q);
+ cio2->streaming++;
+ if (r) {
+ dev_err(dev, "fail to init cio2 hw\n");
+ break;
+ }
- r = v4l2_subdev_call(q->sensor, video, s_stream, 1);
- if (r) {
- dev_err(dev, "fail to start sensor streaming\n");
- cio2_hw_exit(cio2, q);
+ r = v4l2_subdev_call(q->sensor, video, s_stream, 1);
+ if (r) {
+ dev_err(dev, "fail to start sensor streaming\n");
+ cio2_hw_exit(cio2, q);
+ break;
+ }
}
+ cio2->streaming = streaming;
return r;
}
diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.h b/drivers/media/pci/intel/ipu3/ipu3-cio2.h
index 192581909..a6e363a4b 100644
--- a/drivers/media/pci/intel/ipu3/ipu3-cio2.h
+++ b/drivers/media/pci/intel/ipu3/ipu3-cio2.h
@@ -367,6 +367,9 @@ struct cio2_queue {
unsigned int bufs_first; /* Index of the first used entry */
unsigned int bufs_next; /* Index of the first unused entry */
atomic_t bufs_queued;
+
+ unsigned int dma_chan; /* DMA channel owned by this queue */
+ bool streaming;
};
struct cio2_device {
@@ -374,11 +377,12 @@ struct cio2_device {
void __iomem *base;
struct v4l2_device v4l2_dev;
struct cio2_queue queue[CIO2_QUEUES];
- struct cio2_queue *cur_queue;
+ /* DMA channel -> streaming queue, for the IRQ handler */
+ struct cio2_queue *chan_queue[CIO2_NUM_DMA_CHAN];
/* mutex to be used by video_device */
struct mutex lock;
- bool streaming;
+ unsigned int streaming; /* number of streaming queues */
struct v4l2_async_notifier notifier;
struct media_device media_dev;
--
2.56.0
next prev parent reply other threads:[~2026-10-31 0:23 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-31 0:23 [PATCH 0/3] media: ipu3-cio2: support concurrent CSI-2 streams Joe Dzado
2026-10-31 0:23 ` Joe Dzado [this message]
2026-10-31 0:23 ` [PATCH 2/3] media: ipu3-cio2: serialise concurrent stream start and stop Joe Dzado
2026-10-31 0:23 ` [PATCH 3/3] media: ipu3-cio2: give each CSI-2 port its own DMA channel Joe Dzado
2026-10-31 0:24 ` [PATCH 0/3] media: ipu3-cio2: support concurrent CSI-2 streams joe.dzado
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261031002332.33609-2-joe.dzado@icloud.com \
--to=joe.dzado@icloud.com \
--cc=dan.scally@ideasonboard.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-media@vger.kernel.org \
--cc=mchehab@kernel.org \
--cc=sakari.ailus@linux.intel.com \
--cc=yong.zhi@intel.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®