Re: [PATCH v6 12/12] intel-ipu3: Add imgu top level pci device driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Yong,

On Fri, Mar 30, 2018 at 11:15 AM Yong Zhi <yong.zhi@xxxxxxxxx> wrote:
> +/*
> + * Queue as many buffers to CSS as possible. If all buffers don't fit into
> + * CSS buffer queues, they remain unqueued and will be queued later.
> + */
> +int imgu_queue_buffers(struct imgu_device *imgu, bool initial)
> +{
> +       unsigned int node;
> +       int r = 0;
> +       struct imgu_buffer *ibuf;
> +
> +       if (!ipu3_css_is_streaming(&imgu->css))
> +               return 0;
> +
> +       mutex_lock(&imgu->lock);
> +
> +       /* Buffer set is queued to FW only when input buffer is ready */
> +       if (!imgu_queue_getbuf(imgu, IMGU_NODE_IN)) {
> +               mutex_unlock(&imgu->lock);
> +               return 0;
> +       }
> +       for (node = IMGU_NODE_IN + 1; 1; node = (node + 1) % IMGU_NODE_NUM) {

Shouldn't we make (node != IMGU_NODE_IN || imgu_queue_getbuf(imgu,
IMGU_NODE_IN)) the condition here, rather than 1?

This would also let us remove the explicit call to imgu_queue_getbuf()
above the loop.

> +               if (node == IMGU_NODE_VF &&
> +                   (imgu->css.pipe_id == IPU3_CSS_PIPE_ID_CAPTURE ||
> +                    !imgu->nodes[IMGU_NODE_VF].enabled)) {
> +                       continue;
> +               } else if (node == IMGU_NODE_PV &&
> +                          (imgu->css.pipe_id == IPU3_CSS_PIPE_ID_VIDEO ||
> +                           !imgu->nodes[IMGU_NODE_PV].enabled)) {
> +                       continue;
> +               } else if (imgu->queue_enabled[node]) {
> +                       struct ipu3_css_buffer *buf =
> +                                       imgu_queue_getbuf(imgu, node);
> +                       int dummy;
> +
> +                       if (!buf)
> +                               break;
> +
> +                       r = ipu3_css_buf_queue(&imgu->css, buf);
> +                       if (r)
> +                               break;
> +                       dummy = imgu_dummybufs_check(imgu, buf);
> +                       if (!dummy)
> +                               ibuf = container_of(buf, struct imgu_buffer,
> +                                                   css_buf);
> +                       dev_dbg(&imgu->pci_dev->dev,
> +                               "queue %s %s buffer %d to css da: 0x%08x\n",
> +                               dummy ? "dummy" : "user",
> +                               imgu_node_map[node].name,
> +                               dummy ? 0 : ibuf->vid_buf.vbb.vb2_buf.index,
> +                               (u32)buf->daddr);
> +               }
> +               if (node == IMGU_NODE_IN &&
> +                   !imgu_queue_getbuf(imgu, IMGU_NODE_IN))
> +                       break;

My suggestion to the for loop condition is based on this.

> +       }
> +       mutex_unlock(&imgu->lock);
> +
> +       if (r && r != -EBUSY)
> +               goto failed;
> +
> +       return 0;
> +
> +failed:
> +       /*
> +        * On error, mark all buffers as failed which are not
> +        * yet queued to CSS
> +        */
> +       dev_err(&imgu->pci_dev->dev,
> +               "failed to queue buffer to CSS on queue %i (%d)\n",
> +               node, r);
> +
> +       if (initial)
> +               /* If we were called from streamon(), no need to finish bufs */
> +               return r;
> +
> +       for (node = 0; node < IMGU_NODE_NUM; node++) {
> +               struct imgu_buffer *buf, *buf0;
> +
> +               if (!imgu->queue_enabled[node])
> +                       continue;       /* Skip disabled queues */
> +
> +               mutex_lock(&imgu->lock);
> +               list_for_each_entry_safe(buf, buf0, &imgu->nodes[node].buffers,
> +                                        vid_buf.list) {
> +                       if (ipu3_css_buf_state(&buf->css_buf) ==
> +                                       IPU3_CSS_BUFFER_QUEUED)
> +                               continue;       /* Was already queued, skip */
> +
> +                       ipu3_v4l2_buffer_done(&buf->vid_buf.vbb.vb2_buf,
> +                                             VB2_BUF_STATE_ERROR);
> +               }
> +               mutex_unlock(&imgu->lock);
> +       }
> +
> +       return r;
> +}

[snip]

> +static irqreturn_t imgu_isr_threaded(int irq, void *imgu_ptr)
> +{
> +       struct imgu_device *imgu = imgu_ptr;
> +
> +       /* Dequeue / queue buffers */
> +       do {
> +               u64 ns = ktime_get_ns();
> +               struct ipu3_css_buffer *b;
> +               struct imgu_buffer *buf;
> +               unsigned int node;
> +               bool dummy;
> +
> +               do {
> +                       mutex_lock(&imgu->lock);
> +                       b = ipu3_css_buf_dequeue(&imgu->css);
> +                       mutex_unlock(&imgu->lock);
> +               } while (PTR_ERR(b) == -EAGAIN);
> +
> +               if (IS_ERR_OR_NULL(b)) {
> +                       if (!b || PTR_ERR(b) == -EBUSY) /* All done */
> +                               break;
> +                       dev_err(&imgu->pci_dev->dev,
> +                               "failed to dequeue buffers (%ld)\n",
> +                               PTR_ERR(b));
> +                       break;
> +               }
> +
> +               node = imgu_map_node(imgu, b->queue);
> +               dummy = imgu_dummybufs_check(imgu, b);
> +               if (!dummy)
> +                       buf = container_of(b, struct imgu_buffer, css_buf);
> +               dev_dbg(&imgu->pci_dev->dev,
> +                       "dequeue %s %s buffer %d from css\n",
> +                       dummy ? "dummy" : "user",
> +                       imgu_node_map[node].name,
> +                       dummy ? 0 : buf->vid_buf.vbb.vb2_buf.index);
> +
> +               if (dummy)
> +                       /* It was a dummy buffer, skip it */
> +                       continue;
> +
> +               /* Fill vb2 buffer entries and tell it's ready */
> +               if (!imgu->nodes[node].output) {
> +                       buf->vid_buf.vbb.vb2_buf.timestamp = ns;
> +                       buf->vid_buf.vbb.field = V4L2_FIELD_NONE;
> +                       buf->vid_buf.vbb.sequence =
> +                               atomic_inc_return(&imgu->nodes[node].sequence);
> +               }
> +               imgu_buffer_done(imgu, &buf->vid_buf.vbb.vb2_buf,
> +                                ipu3_css_buf_state(&buf->css_buf) ==
> +                                                   IPU3_CSS_BUFFER_DONE ?
> +                                                   VB2_BUF_STATE_DONE :
> +                                                   VB2_BUF_STATE_ERROR);
> +       } while (1);
> +
> +       /*
> +        * Try to queue more buffers for CSS.
> +        * qbuf_barrier is used to disable new buffers
> +        * to be queued to CSS.
> +        */
> +       if (!atomic_read(&imgu->qbuf_barrier))
> +               imgu_queue_buffers(imgu, false);
> +
> +       return IRQ_NONE;

This is a serious bug. An interrupt handler must not return IRQ_NONE
unless it's really sure that the device it handles did not generate
any interrupt. This threaded handler is called as a result of the
hardirq handler actually finding an interrupt to be handled, so we
must always return IRQ_HANDLED here.

[snip]

> +static int __maybe_unused imgu_suspend(struct device *dev)
> +{
> +       struct pci_dev *pci_dev = to_pci_dev(dev);
> +       struct imgu_device *imgu = pci_get_drvdata(pci_dev);
> +       unsigned long expire;
> +
> +       dev_dbg(dev, "enter %s\n", __func__);
> +       imgu->suspend_in_stream = ipu3_css_is_streaming(&imgu->css);
> +       if (!imgu->suspend_in_stream)
> +               goto out;
> +       /* Block new buffers to be queued to CSS. */
> +       atomic_set(&imgu->qbuf_barrier, 1);
> +       /*
> +        * Wait for currently running irq handler to be done so that
> +        * no new buffers will be queued to fw later.
> +        */
> +       synchronize_irq(pci_dev->irq);
> +       /* Wait until all buffers in CSS are done. */
> +       expire = jiffies + msecs_to_jiffies(1000);
> +       while (!ipu3_css_queue_empty(&imgu->css)) {
> +               if (time_is_before_jiffies(expire)) {
> +                       dev_err(dev, "wait buffer drain timeout.\n");
> +                       break;
> +               }
> +       }

Uhm. We struggle to save some power by suspending the device only to
end up with an ugly busy wait that could take even a second here. This
doesn't make any sense.

We had a working solution using a wait queue in previous revision [1].
What happened to it?

[1] https://chromium-review.googlesource.com/c/chromiumos/third_party/kernel/+/1029594/2/drivers/media/pci/intel/ipu3/ipu3.c#b913
(see the left side)

> +       ipu3_css_stop_streaming(&imgu->css);
> +       atomic_set(&imgu->qbuf_barrier, 0);
> +       imgu_powerdown(imgu);
> +       pm_runtime_force_suspend(dev);
> +out:
> +       dev_dbg(dev, "leave %s\n", __func__);
> +       return 0;
> +}

Best regards,
Tomasz



[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]

  Powered by Linux