]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/media/video/pxa_camera.c
Merge branch 'omap-pool'
[linux-2.6-omap-h63xx.git] / drivers / media / video / pxa_camera.c
index c522616ef38f500006dd83075451a575de72e91e..c639845460fff8df6a327bf1f7e21ebbb954ac34 100644 (file)
                        CICR0_PERRM | CICR0_QDM | CICR0_CDM | CICR0_SOFM | \
                        CICR0_EOFM | CICR0_FOM)
 
+/*
+ * YUV422P picture size should be a multiple of 16, so the heuristic aligns
+ * height, width on 4 byte boundaries to reach the 16 multiple for the size.
+ */
+#define YUV422P_X_Y_ALIGN 4
+#define YUV422P_SIZE_ALIGN YUV422P_X_Y_ALIGN * YUV422P_X_Y_ALIGN
+
 /*
  * Structures
  */
@@ -236,20 +243,11 @@ static int pxa_videobuf_setup(struct videobuf_queue *vq, unsigned int *count,
                              unsigned int *size)
 {
        struct soc_camera_device *icd = vq->priv_data;
-       struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
-       struct pxa_camera_dev *pcdev = ici->priv;
 
        dev_dbg(&icd->dev, "count=%d, size=%d\n", *count, *size);
 
-       /* planar capture requires Y, U and V buffers to be page aligned */
-       if (pcdev->channels == 3) {
-               *size = PAGE_ALIGN(icd->width * icd->height); /* Y pages */
-               *size += PAGE_ALIGN(icd->width * icd->height / 2); /* U pages */
-               *size += PAGE_ALIGN(icd->width * icd->height / 2); /* V pages */
-       } else {
-               *size = icd->width * icd->height *
-                       ((icd->current_fmt->depth + 7) >> 3);
-       }
+       *size = roundup(icd->width * icd->height *
+                       ((icd->current_fmt->depth + 7) >> 3), 8);
 
        if (0 == *count)
                *count = 32;
@@ -289,19 +287,63 @@ static void free_buffer(struct videobuf_queue *vq, struct pxa_buffer *buf)
        buf->vb.state = VIDEOBUF_NEEDS_INIT;
 }
 
+static int calculate_dma_sglen(struct scatterlist *sglist, int sglen,
+                              int sg_first_ofs, int size)
+{
+       int i, offset, dma_len, xfer_len;
+       struct scatterlist *sg;
+
+       offset = sg_first_ofs;
+       for_each_sg(sglist, sg, sglen, i) {
+               dma_len = sg_dma_len(sg);
+
+               /* PXA27x Developer's Manual 27.4.4.1: round up to 8 bytes */
+               xfer_len = roundup(min(dma_len - offset, size), 8);
+
+               size = max(0, size - xfer_len);
+               offset = 0;
+               if (size == 0)
+                       break;
+       }
+
+       BUG_ON(size != 0);
+       return i + 1;
+}
+
+/**
+ * pxa_init_dma_channel - init dma descriptors
+ * @pcdev: pxa camera device
+ * @buf: pxa buffer to find pxa dma channel
+ * @dma: dma video buffer
+ * @channel: dma channel (0 => 'Y', 1 => 'U', 2 => 'V')
+ * @cibr: camera Receive Buffer Register
+ * @size: bytes to transfer
+ * @sg_first: first element of sg_list
+ * @sg_first_ofs: offset in first element of sg_list
+ *
+ * Prepares the pxa dma descriptors to transfer one camera channel.
+ * Beware sg_first and sg_first_ofs are both input and output parameters.
+ *
+ * Returns 0 or -ENOMEM if no coherent memory is available
+ */
 static int pxa_init_dma_channel(struct pxa_camera_dev *pcdev,
                                struct pxa_buffer *buf,
                                struct videobuf_dmabuf *dma, int channel,
-                               int sglen, int sg_start, int cibr,
-                               unsigned int size)
+                               int cibr, int size,
+                               struct scatterlist **sg_first, int *sg_first_ofs)
 {
        struct pxa_cam_dma *pxa_dma = &buf->dmas[channel];
-       int i;
+       struct scatterlist *sg;
+       int i, offset, sglen;
+       int dma_len = 0, xfer_len = 0;
 
        if (pxa_dma->sg_cpu)
                dma_free_coherent(pcdev->dev, pxa_dma->sg_size,
                                  pxa_dma->sg_cpu, pxa_dma->sg_dma);
 
+       sglen = calculate_dma_sglen(*sg_first, dma->sglen,
+                                   *sg_first_ofs, size);
+
        pxa_dma->sg_size = (sglen + 1) * sizeof(struct pxa_dma_desc);
        pxa_dma->sg_cpu = dma_alloc_coherent(pcdev->dev, pxa_dma->sg_size,
                                             &pxa_dma->sg_dma, GFP_KERNEL);
@@ -309,31 +351,75 @@ static int pxa_init_dma_channel(struct pxa_camera_dev *pcdev,
                return -ENOMEM;
 
        pxa_dma->sglen = sglen;
+       offset = *sg_first_ofs;
 
-       for (i = 0; i < sglen; i++) {
-               int sg_i = sg_start + i;
-               struct scatterlist *sg = dma->sglist;
-               unsigned int dma_len = sg_dma_len(&sg[sg_i]), xfer_len;
+       dev_dbg(pcdev->dev, "DMA: sg_first=%p, sglen=%d, ofs=%d, dma.desc=%x\n",
+               *sg_first, sglen, *sg_first_ofs, pxa_dma->sg_dma);
 
-               pxa_dma->sg_cpu[i].dsadr = pcdev->res->start + cibr;
-               pxa_dma->sg_cpu[i].dtadr = sg_dma_address(&sg[sg_i]);
+
+       for_each_sg(*sg_first, sg, sglen, i) {
+               dma_len = sg_dma_len(sg);
 
                /* PXA27x Developer's Manual 27.4.4.1: round up to 8 bytes */
-               xfer_len = (min(dma_len, size) + 7) & ~7;
+               xfer_len = roundup(min(dma_len - offset, size), 8);
 
+               size = max(0, size - xfer_len);
+
+               pxa_dma->sg_cpu[i].dsadr = pcdev->res->start + cibr;
+               pxa_dma->sg_cpu[i].dtadr = sg_dma_address(sg) + offset;
                pxa_dma->sg_cpu[i].dcmd =
                        DCMD_FLOWSRC | DCMD_BURST8 | DCMD_INCTRGADDR | xfer_len;
-               size -= dma_len;
+#ifdef DEBUG
+               if (!i)
+                       pxa_dma->sg_cpu[i].dcmd |= DCMD_STARTIRQEN;
+#endif
                pxa_dma->sg_cpu[i].ddadr =
                        pxa_dma->sg_dma + (i + 1) * sizeof(struct pxa_dma_desc);
+
+               dev_vdbg(pcdev->dev, "DMA: desc.%08x->@phys=0x%08x, len=%d\n",
+                        pxa_dma->sg_dma + i * sizeof(struct pxa_dma_desc),
+                        sg_dma_address(sg) + offset, xfer_len);
+               offset = 0;
+
+               if (size == 0)
+                       break;
        }
 
-       pxa_dma->sg_cpu[sglen - 1].ddadr = DDADR_STOP;
-       pxa_dma->sg_cpu[sglen - 1].dcmd |= DCMD_ENDIRQEN;
+       pxa_dma->sg_cpu[sglen].ddadr = DDADR_STOP;
+       pxa_dma->sg_cpu[sglen].dcmd  = DCMD_FLOWSRC | DCMD_BURST8 | DCMD_ENDIRQEN;
+
+       /*
+        * Handle 1 special case :
+        *  - in 3 planes (YUV422P format), we might finish with xfer_len equal
+        *    to dma_len (end on PAGE boundary). In this case, the sg element
+        *    for next plane should be the next after the last used to store the
+        *    last scatter gather RAM page
+        */
+       if (xfer_len >= dma_len) {
+               *sg_first_ofs = xfer_len - dma_len;
+               *sg_first = sg_next(sg);
+       } else {
+               *sg_first_ofs = xfer_len;
+               *sg_first = sg;
+       }
 
        return 0;
 }
 
+static void pxa_videobuf_set_actdma(struct pxa_camera_dev *pcdev,
+                                   struct pxa_buffer *buf)
+{
+       buf->active_dma = DMA_Y;
+       if (pcdev->channels == 3)
+               buf->active_dma |= DMA_U | DMA_V;
+}
+
+/*
+ * Please check the DMA prepared buffer structure in :
+ *   Documentation/video4linux/pxa_camera.txt
+ * Please check also in pxa_camera_check_link_miss() to understand why DMA chain
+ * modification while DMA chain is running will work anyway.
+ */
 static int pxa_videobuf_prepare(struct videobuf_queue *vq,
                struct videobuf_buffer *vb, enum v4l2_field field)
 {
@@ -342,7 +428,6 @@ static int pxa_videobuf_prepare(struct videobuf_queue *vq,
        struct pxa_camera_dev *pcdev = ici->priv;
        struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb);
        int ret;
-       int sglen_y,  sglen_yu = 0, sglen_u = 0, sglen_v = 0;
        int size_y, size_u = 0, size_v = 0;
 
        dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
@@ -381,62 +466,58 @@ static int pxa_videobuf_prepare(struct videobuf_queue *vq,
        }
 
        if (vb->state == VIDEOBUF_NEEDS_INIT) {
-               unsigned int size = vb->size;
+               int size = vb->size;
+               int next_ofs = 0;
                struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
+               struct scatterlist *sg;
 
                ret = videobuf_iolock(vq, vb, NULL);
                if (ret)
                        goto fail;
 
                if (pcdev->channels == 3) {
-                       /* FIXME the calculations should be more precise */
-                       sglen_y = dma->sglen / 2;
-                       sglen_u = sglen_v = dma->sglen / 4 + 1;
-                       sglen_yu = sglen_y + sglen_u;
                        size_y = size / 2;
                        size_u = size_v = size / 4;
                } else {
-                       sglen_y = dma->sglen;
                        size_y = size;
                }
 
-               /* init DMA for Y channel */
-               ret = pxa_init_dma_channel(pcdev, buf, dma, 0, sglen_y,
-                                          0, 0x28, size_y);
+               sg = dma->sglist;
 
+               /* init DMA for Y channel */
+               ret = pxa_init_dma_channel(pcdev, buf, dma, 0, CIBR0, size_y,
+                                          &sg, &next_ofs);
                if (ret) {
                        dev_err(pcdev->dev,
                                "DMA initialization for Y/RGB failed\n");
                        goto fail;
                }
 
-               if (pcdev->channels == 3) {
-                       /* init DMA for U channel */
-                       ret = pxa_init_dma_channel(pcdev, buf, dma, 1, sglen_u,
-                                                  sglen_y, 0x30, size_u);
-                       if (ret) {
-                               dev_err(pcdev->dev,
-                                       "DMA initialization for U failed\n");
-                               goto fail_u;
-                       }
-
-                       /* init DMA for V channel */
-                       ret = pxa_init_dma_channel(pcdev, buf, dma, 2, sglen_v,
-                                                  sglen_yu, 0x38, size_v);
-                       if (ret) {
-                               dev_err(pcdev->dev,
-                                       "DMA initialization for V failed\n");
-                               goto fail_v;
-                       }
+               /* init DMA for U channel */
+               if (size_u)
+                       ret = pxa_init_dma_channel(pcdev, buf, dma, 1, CIBR1,
+                                                  size_u, &sg, &next_ofs);
+               if (ret) {
+                       dev_err(pcdev->dev,
+                               "DMA initialization for U failed\n");
+                       goto fail_u;
+               }
+
+               /* init DMA for V channel */
+               if (size_v)
+                       ret = pxa_init_dma_channel(pcdev, buf, dma, 2, CIBR2,
+                                                  size_v, &sg, &next_ofs);
+               if (ret) {
+                       dev_err(pcdev->dev,
+                               "DMA initialization for V failed\n");
+                       goto fail_v;
                }
 
                vb->state = VIDEOBUF_PREPARED;
        }
 
        buf->inwork = 0;
-       buf->active_dma = DMA_Y;
-       if (pcdev->channels == 3)
-               buf->active_dma |= DMA_U | DMA_V;
+       pxa_videobuf_set_actdma(pcdev, buf);
 
        return 0;
 
@@ -453,6 +534,92 @@ out:
        return ret;
 }
 
+/**
+ * pxa_dma_start_channels - start DMA channel for active buffer
+ * @pcdev: pxa camera device
+ *
+ * Initialize DMA channels to the beginning of the active video buffer, and
+ * start these channels.
+ */
+static void pxa_dma_start_channels(struct pxa_camera_dev *pcdev)
+{
+       int i;
+       struct pxa_buffer *active;
+
+       active = pcdev->active;
+
+       for (i = 0; i < pcdev->channels; i++) {
+               dev_dbg(pcdev->dev, "%s (channel=%d) ddadr=%08x\n", __func__,
+                       i, active->dmas[i].sg_dma);
+               DDADR(pcdev->dma_chans[i]) = active->dmas[i].sg_dma;
+               DCSR(pcdev->dma_chans[i]) = DCSR_RUN;
+       }
+}
+
+static void pxa_dma_stop_channels(struct pxa_camera_dev *pcdev)
+{
+       int i;
+
+       for (i = 0; i < pcdev->channels; i++) {
+               dev_dbg(pcdev->dev, "%s (channel=%d)\n", __func__, i);
+               DCSR(pcdev->dma_chans[i]) = 0;
+       }
+}
+
+static void pxa_dma_add_tail_buf(struct pxa_camera_dev *pcdev,
+                                struct pxa_buffer *buf)
+{
+       int i;
+       struct pxa_dma_desc *buf_last_desc;
+
+       for (i = 0; i < pcdev->channels; i++) {
+               buf_last_desc = buf->dmas[i].sg_cpu + buf->dmas[i].sglen;
+               buf_last_desc->ddadr = DDADR_STOP;
+
+               if (pcdev->sg_tail[i])
+                       /* Link the new buffer to the old tail */
+                       pcdev->sg_tail[i]->ddadr = buf->dmas[i].sg_dma;
+
+               /* Update the channel tail */
+               pcdev->sg_tail[i] = buf_last_desc;
+       }
+}
+
+/**
+ * pxa_camera_start_capture - start video capturing
+ * @pcdev: camera device
+ *
+ * Launch capturing. DMA channels should not be active yet. They should get
+ * activated at the end of frame interrupt, to capture only whole frames, and
+ * never begin the capture of a partial frame.
+ */
+static void pxa_camera_start_capture(struct pxa_camera_dev *pcdev)
+{
+       unsigned long cicr0, cifr;
+
+       dev_dbg(pcdev->dev, "%s\n", __func__);
+       /* Reset the FIFOs */
+       cifr = __raw_readl(pcdev->base + CIFR) | CIFR_RESET_F;
+       __raw_writel(cifr, pcdev->base + CIFR);
+       /* Enable End-Of-Frame Interrupt */
+       cicr0 = __raw_readl(pcdev->base + CICR0) | CICR0_ENB;
+       cicr0 &= ~CICR0_EOFM;
+       __raw_writel(cicr0, pcdev->base + CICR0);
+}
+
+static void pxa_camera_stop_capture(struct pxa_camera_dev *pcdev)
+{
+       unsigned long cicr0;
+
+       pxa_dma_stop_channels(pcdev);
+
+       cicr0 = __raw_readl(pcdev->base + CICR0) & ~CICR0_ENB;
+       __raw_writel(cicr0, pcdev->base + CICR0);
+
+       pcdev->active = NULL;
+       dev_dbg(pcdev->dev, "%s\n", __func__);
+}
+
 static void pxa_videobuf_queue(struct videobuf_queue *vq,
                               struct videobuf_buffer *vb)
 {
@@ -460,81 +627,20 @@ static void pxa_videobuf_queue(struct videobuf_queue *vq,
        struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
        struct pxa_camera_dev *pcdev = ici->priv;
        struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb);
-       struct pxa_buffer *active;
        unsigned long flags;
-       int i;
 
-       dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
-               vb, vb->baddr, vb->bsize);
+       dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d active=%p\n", __func__,
+               vb, vb->baddr, vb->bsize, pcdev->active);
+
        spin_lock_irqsave(&pcdev->lock, flags);
 
        list_add_tail(&vb->queue, &pcdev->capture);
 
        vb->state = VIDEOBUF_ACTIVE;
-       active = pcdev->active;
+       pxa_dma_add_tail_buf(pcdev, buf);
 
-       if (!active) {
-               unsigned long cifr, cicr0;
-
-               cifr = __raw_readl(pcdev->base + CIFR) | CIFR_RESET_F;
-               __raw_writel(cifr, pcdev->base + CIFR);
-
-               for (i = 0; i < pcdev->channels; i++) {
-                       DDADR(pcdev->dma_chans[i]) = buf->dmas[i].sg_dma;
-                       DCSR(pcdev->dma_chans[i]) = DCSR_RUN;
-                       pcdev->sg_tail[i] = buf->dmas[i].sg_cpu + buf->dmas[i].sglen - 1;
-               }
-
-               pcdev->active = buf;
-
-               cicr0 = __raw_readl(pcdev->base + CICR0) | CICR0_ENB;
-               __raw_writel(cicr0, pcdev->base + CICR0);
-       } else {
-               struct pxa_cam_dma *buf_dma;
-               struct pxa_cam_dma *act_dma;
-               int nents;
-
-               for (i = 0; i < pcdev->channels; i++) {
-                       buf_dma = &buf->dmas[i];
-                       act_dma = &active->dmas[i];
-                       nents = buf_dma->sglen;
-
-                       /* Stop DMA engine */
-                       DCSR(pcdev->dma_chans[i]) = 0;
-
-                       /* Add the descriptors we just initialized to
-                          the currently running chain */
-                       pcdev->sg_tail[i]->ddadr = buf_dma->sg_dma;
-                       pcdev->sg_tail[i] = buf_dma->sg_cpu + buf_dma->sglen - 1;
-
-                       /* Setup a dummy descriptor with the DMA engines current
-                        * state
-                        */
-                       buf_dma->sg_cpu[nents].dsadr =
-                               pcdev->res->start + 0x28 + i*8; /* CIBRx */
-                       buf_dma->sg_cpu[nents].dtadr =
-                               DTADR(pcdev->dma_chans[i]);
-                       buf_dma->sg_cpu[nents].dcmd =
-                               DCMD(pcdev->dma_chans[i]);
-
-                       if (DDADR(pcdev->dma_chans[i]) == DDADR_STOP) {
-                               /* The DMA engine is on the last
-                                  descriptor, set the next descriptors
-                                  address to the descriptors we just
-                                  initialized */
-                               buf_dma->sg_cpu[nents].ddadr = buf_dma->sg_dma;
-                       } else {
-                               buf_dma->sg_cpu[nents].ddadr =
-                                       DDADR(pcdev->dma_chans[i]);
-                       }
-
-                       /* The next descriptor is the dummy descriptor */
-                       DDADR(pcdev->dma_chans[i]) = buf_dma->sg_dma + nents *
-                               sizeof(struct pxa_dma_desc);
-
-                       DCSR(pcdev->dma_chans[i]) = DCSR_RUN;
-               }
-       }
+       if (!pcdev->active)
+               pxa_camera_start_capture(pcdev);
 
        spin_unlock_irqrestore(&pcdev->lock, flags);
 }
@@ -572,7 +678,7 @@ static void pxa_camera_wakeup(struct pxa_camera_dev *pcdev,
                              struct videobuf_buffer *vb,
                              struct pxa_buffer *buf)
 {
-       unsigned long cicr0;
+       int i;
 
        /* _init is used to debug races, see comment in pxa_camera_reqbufs() */
        list_del_init(&vb->queue);
@@ -580,15 +686,12 @@ static void pxa_camera_wakeup(struct pxa_camera_dev *pcdev,
        do_gettimeofday(&vb->ts);
        vb->field_count++;
        wake_up(&vb->done);
+       dev_dbg(pcdev->dev, "%s dequeud buffer (vb=0x%p)\n", __func__, vb);
 
        if (list_empty(&pcdev->capture)) {
-               pcdev->active = NULL;
-               DCSR(pcdev->dma_chans[0]) = 0;
-               DCSR(pcdev->dma_chans[1]) = 0;
-               DCSR(pcdev->dma_chans[2]) = 0;
-
-               cicr0 = __raw_readl(pcdev->base + CICR0) & ~CICR0_ENB;
-               __raw_writel(cicr0, pcdev->base + CICR0);
+               pxa_camera_stop_capture(pcdev);
+               for (i = 0; i < pcdev->channels; i++)
+                       pcdev->sg_tail[i] = NULL;
                return;
        }
 
@@ -596,6 +699,35 @@ static void pxa_camera_wakeup(struct pxa_camera_dev *pcdev,
                                   struct pxa_buffer, vb.queue);
 }
 
+/**
+ * pxa_camera_check_link_miss - check missed DMA linking
+ * @pcdev: camera device
+ *
+ * The DMA chaining is done with DMA running. This means a tiny temporal window
+ * remains, where a buffer is queued on the chain, while the chain is already
+ * stopped. This means the tailed buffer would never be transfered by DMA.
+ * This function restarts the capture for this corner case, where :
+ *  - DADR() == DADDR_STOP
+ *  - a videobuffer is queued on the pcdev->capture list
+ *
+ * Please check the "DMA hot chaining timeslice issue" in
+ *   Documentation/video4linux/pxa_camera.txt
+ *
+ * Context: should only be called within the dma irq handler
+ */
+static void pxa_camera_check_link_miss(struct pxa_camera_dev *pcdev)
+{
+       int i, is_dma_stopped = 1;
+
+       for (i = 0; i < pcdev->channels; i++)
+               if (DDADR(pcdev->dma_chans[i]) != DDADR_STOP)
+                       is_dma_stopped = 0;
+       dev_dbg(pcdev->dev, "%s : top queued buffer=%p, dma_stopped=%d\n",
+               __func__, pcdev->active, is_dma_stopped);
+       if (pcdev->active && is_dma_stopped)
+               pxa_camera_start_capture(pcdev);
+}
+
 static void pxa_camera_dma_irq(int channel, struct pxa_camera_dev *pcdev,
                               enum pxa_camera_active_dma act_dma)
 {
@@ -603,61 +735,70 @@ static void pxa_camera_dma_irq(int channel, struct pxa_camera_dev *pcdev,
        unsigned long flags;
        u32 status, camera_status, overrun;
        struct videobuf_buffer *vb;
-       unsigned long cifr, cicr0;
 
        spin_lock_irqsave(&pcdev->lock, flags);
 
        status = DCSR(channel);
-       DCSR(channel) = status | DCSR_ENDINTR;
+       DCSR(channel) = status;
+
+       camera_status = __raw_readl(pcdev->base + CISR);
+       overrun = CISR_IFO_0;
+       if (pcdev->channels == 3)
+               overrun |= CISR_IFO_1 | CISR_IFO_2;
 
        if (status & DCSR_BUSERR) {
                dev_err(pcdev->dev, "DMA Bus Error IRQ!\n");
                goto out;
        }
 
-       if (!(status & DCSR_ENDINTR)) {
+       if (!(status & (DCSR_ENDINTR | DCSR_STARTINTR))) {
                dev_err(pcdev->dev, "Unknown DMA IRQ source, "
                        "status: 0x%08x\n", status);
                goto out;
        }
 
-       if (!pcdev->active) {
-               dev_err(pcdev->dev, "DMA End IRQ with no active buffer!\n");
-               goto out;
-       }
-
-       camera_status = __raw_readl(pcdev->base + CISR);
-       overrun = CISR_IFO_0;
-       if (pcdev->channels == 3)
-               overrun |= CISR_IFO_1 | CISR_IFO_2;
-       if (camera_status & overrun) {
-               dev_dbg(pcdev->dev, "FIFO overrun! CISR: %x\n", camera_status);
-               /* Stop the Capture Interface */
-               cicr0 = __raw_readl(pcdev->base + CICR0) & ~CICR0_ENB;
-               __raw_writel(cicr0, pcdev->base + CICR0);
-
-               /* Stop DMA */
-               DCSR(channel) = 0;
-               /* Reset the FIFOs */
-               cifr = __raw_readl(pcdev->base + CIFR) | CIFR_RESET_F;
-               __raw_writel(cifr, pcdev->base + CIFR);
-               /* Enable End-Of-Frame Interrupt */
-               cicr0 &= ~CICR0_EOFM;
-               __raw_writel(cicr0, pcdev->base + CICR0);
-               /* Restart the Capture Interface */
-               __raw_writel(cicr0 | CICR0_ENB, pcdev->base + CICR0);
+       /*
+        * pcdev->active should not be NULL in DMA irq handler.
+        *
+        * But there is one corner case : if capture was stopped due to an
+        * overrun of channel 1, and at that same channel 2 was completed.
+        *
+        * When handling the overrun in DMA irq for channel 1, we'll stop the
+        * capture and restart it (and thus set pcdev->active to NULL). But the
+        * DMA irq handler will already be pending for channel 2. So on entering
+        * the DMA irq handler for channel 2 there will be no active buffer, yet
+        * that is normal.
+        */
+       if (!pcdev->active)
                goto out;
-       }
 
        vb = &pcdev->active->vb;
        buf = container_of(vb, struct pxa_buffer, vb);
        WARN_ON(buf->inwork || list_empty(&vb->queue));
-       dev_dbg(pcdev->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
-               vb, vb->baddr, vb->bsize);
 
-       buf->active_dma &= ~act_dma;
-       if (!buf->active_dma)
-               pxa_camera_wakeup(pcdev, vb, buf);
+       dev_dbg(pcdev->dev, "%s channel=%d %s%s(vb=0x%p) dma.desc=%x\n",
+               __func__, channel, status & DCSR_STARTINTR ? "SOF " : "",
+               status & DCSR_ENDINTR ? "EOF " : "", vb, DDADR(channel));
+
+       if (status & DCSR_ENDINTR) {
+               /*
+                * It's normal if the last frame creates an overrun, as there
+                * are no more DMA descriptors to fetch from QCI fifos
+                */
+               if (camera_status & overrun &&
+                   !list_is_last(pcdev->capture.next, &pcdev->capture)) {
+                       dev_dbg(pcdev->dev, "FIFO overrun! CISR: %x\n",
+                               camera_status);
+                       pxa_camera_stop_capture(pcdev);
+                       pxa_camera_start_capture(pcdev);
+                       goto out;
+               }
+               buf->active_dma &= ~act_dma;
+               if (!buf->active_dma) {
+                       pxa_camera_wakeup(pcdev, vb, buf);
+                       pxa_camera_check_link_miss(pcdev);
+               }
+       }
 
 out:
        spin_unlock_irqrestore(&pcdev->lock, flags);
@@ -786,6 +927,8 @@ static irqreturn_t pxa_camera_irq(int irq, void *data)
 {
        struct pxa_camera_dev *pcdev = data;
        unsigned long status, cicr0;
+       struct pxa_buffer *buf;
+       struct videobuf_buffer *vb;
 
        status = __raw_readl(pcdev->base + CISR);
        dev_dbg(pcdev->dev, "Camera interrupt status 0x%lx\n", status);
@@ -796,12 +939,14 @@ static irqreturn_t pxa_camera_irq(int irq, void *data)
        __raw_writel(status, pcdev->base + CISR);
 
        if (status & CISR_EOF) {
-               int i;
-               for (i = 0; i < pcdev->channels; i++) {
-                       DDADR(pcdev->dma_chans[i]) =
-                               pcdev->active->dmas[i].sg_dma;
-                       DCSR(pcdev->dma_chans[i]) = DCSR_RUN;
-               }
+               pcdev->active = list_first_entry(&pcdev->capture,
+                                          struct pxa_buffer, vb.queue);
+               vb = &pcdev->active->vb;
+               buf = container_of(vb, struct pxa_buffer, vb);
+               pxa_videobuf_set_actdma(pcdev, buf);
+
+               pxa_dma_start_channels(pcdev);
+
                cicr0 = __raw_readl(pcdev->base + CICR0) | CICR0_EOFM;
                __raw_writel(cicr0, pcdev->base + CICR0);
        }
@@ -1265,6 +1410,18 @@ static int pxa_camera_try_fmt(struct soc_camera_device *icd,
                pix->width = 2048;
        pix->width &= ~0x01;
 
+       /*
+        * YUV422P planar format requires images size to be a 16 bytes
+        * multiple. If not, zeros will be inserted between Y and U planes, and
+        * U and V planes, and YUV422P standard would be violated.
+        */
+       if (xlate->host_fmt->fourcc == V4L2_PIX_FMT_YUV422P) {
+               if (!IS_ALIGNED(pix->width * pix->height, YUV422P_SIZE_ALIGN))
+                       pix->height = ALIGN(pix->height, YUV422P_X_Y_ALIGN);
+               if (!IS_ALIGNED(pix->width * pix->height, YUV422P_SIZE_ALIGN))
+                       pix->width = ALIGN(pix->width, YUV422P_X_Y_ALIGN);
+       }
+
        pix->bytesperline = pix->width *
                DIV_ROUND_UP(xlate->host_fmt->depth, 8);
        pix->sizeimage = pix->height * pix->bytesperline;
@@ -1372,18 +1529,8 @@ static int pxa_camera_resume(struct soc_camera_device *icd)
                ret = pcdev->icd->ops->resume(pcdev->icd);
 
        /* Restart frame capture if active buffer exists */
-       if (!ret && pcdev->active) {
-               unsigned long cifr, cicr0;
-
-               /* Reset the FIFOs */
-               cifr = __raw_readl(pcdev->base + CIFR) | CIFR_RESET_F;
-               __raw_writel(cifr, pcdev->base + CIFR);
-
-               cicr0 = __raw_readl(pcdev->base + CICR0);
-               cicr0 &= ~CICR0_EOFM;   /* Enable End-Of-Frame Interrupt */
-               cicr0 |= CICR0_ENB;     /* Restart the Capture Interface */
-               __raw_writel(cicr0, pcdev->base + CICR0);
-       }
+       if (!ret && pcdev->active)
+               pxa_camera_start_capture(pcdev);
 
        return ret;
 }