V4L/DVB (8671): gspca: Remove the unused field 'dev_name' of the device structure.
[safe/jmp/linux-2.6] / drivers / media / video / pxa_camera.c
index bef3c9c..388cf94 100644 (file)
 
 #include <media/v4l2-common.h>
 #include <media/v4l2-dev.h>
+#include <media/videobuf-dma-sg.h>
 #include <media/soc_camera.h>
 
 #include <linux/videodev2.h>
 
 #include <asm/dma.h>
-#include <asm/arch/pxa-regs.h>
-#include <asm/arch/camera.h>
+#include <mach/pxa-regs.h>
+#include <mach/camera.h>
 
 #define PXA_CAM_VERSION_CODE KERNEL_VERSION(0, 0, 5)
 #define PXA_CAM_DRV_NAME "pxa27x-camera"
@@ -49,6 +50,9 @@
 
 #define CICR1_DW_VAL(x)   ((x) & CICR1_DW)         /* Data bus width */
 #define CICR1_PPL_VAL(x)  (((x) << 15) & CICR1_PPL) /* Pixels per line */
+#define CICR1_COLOR_SP_VAL(x)  (((x) << 3) & CICR1_COLOR_SP)   /* color space */
+#define CICR1_RGB_BPP_VAL(x)   (((x) << 7) & CICR1_RGB_BPP)    /* bpp for rgb */
+#define CICR1_RGBT_CONV_VAL(x) (((x) << 29) & CICR1_RGBT_CONV) /* rgbt conv */
 
 #define CICR2_BLW_VAL(x)  (((x) << 24) & CICR2_BLW) /* Beginning-of-line pixel clock wait count */
 #define CICR2_ELW_VAL(x)  (((x) << 16) & CICR2_ELW) /* End-of-line pixel clock wait count */
@@ -70,6 +74,19 @@ static DEFINE_MUTEX(camera_lock);
 /*
  * Structures
  */
+enum pxa_camera_active_dma {
+       DMA_Y = 0x1,
+       DMA_U = 0x2,
+       DMA_V = 0x4,
+};
+
+/* descriptor needed for the PXA DMA engine */
+struct pxa_cam_dma {
+       dma_addr_t              sg_dma;
+       struct pxa_dma_desc     *sg_cpu;
+       size_t                  sg_size;
+       int                     sglen;
+};
 
 /* buffer for one video frame */
 struct pxa_buffer {
@@ -78,16 +95,12 @@ struct pxa_buffer {
 
        const struct soc_camera_data_format        *fmt;
 
-       /* our descriptor list needed for the PXA DMA engine */
-       dma_addr_t              sg_dma;
-       struct pxa_dma_desc     *sg_cpu;
-       size_t                  sg_size;
+       /* our descriptor lists for Y, U and V channels */
+       struct pxa_cam_dma dmas[3];
+
        int                     inwork;
-};
 
-struct pxa_framebuffer_queue {
-       dma_addr_t              sg_last_dma;
-       struct pxa_dma_desc     *sg_last_cpu;
+       enum pxa_camera_active_dma active_dma;
 };
 
 struct pxa_camera_dev {
@@ -100,7 +113,9 @@ struct pxa_camera_dev {
 
        unsigned int            irq;
        void __iomem            *base;
-       unsigned int            dma_chan_y;
+
+       int                     channels;
+       unsigned int            dma_chans[3];
 
        struct pxacamera_platform_data *pdata;
        struct resource         *res;
@@ -112,6 +127,9 @@ struct pxa_camera_dev {
        spinlock_t              lock;
 
        struct pxa_buffer       *active;
+       struct pxa_dma_desc     *sg_tail[3];
+
+       u32                     save_cicr[5];
 };
 
 static const char *pxa_cam_driver_description = "PXA_Camera";
@@ -125,10 +143,21 @@ 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);
 
-       *size = icd->width * icd->height * ((icd->current_fmt->depth + 7) >> 3);
+       /* 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);
+       }
 
        if (0 == *count)
                *count = 32;
@@ -145,10 +174,11 @@ static void free_buffer(struct videobuf_queue *vq, struct pxa_buffer *buf)
                to_soc_camera_host(icd->dev.parent);
        struct pxa_camera_dev *pcdev = ici->priv;
        struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb);
+       int i;
 
        BUG_ON(in_interrupt());
 
-       dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __FUNCTION__,
+       dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
                &buf->vb, buf->vb.baddr, buf->vb.bsize);
 
        /* This waits until this buffer is out of danger, i.e., until it is no
@@ -157,14 +187,62 @@ static void free_buffer(struct videobuf_queue *vq, struct pxa_buffer *buf)
        videobuf_dma_unmap(vq, dma);
        videobuf_dma_free(dma);
 
-       if (buf->sg_cpu)
-               dma_free_coherent(pcdev->dev, buf->sg_size, buf->sg_cpu,
-                                 buf->sg_dma);
-       buf->sg_cpu = NULL;
+       for (i = 0; i < ARRAY_SIZE(buf->dmas); i++) {
+               if (buf->dmas[i].sg_cpu)
+                       dma_free_coherent(pcdev->dev, buf->dmas[i].sg_size,
+                                         buf->dmas[i].sg_cpu,
+                                         buf->dmas[i].sg_dma);
+               buf->dmas[i].sg_cpu = NULL;
+       }
 
        buf->vb.state = VIDEOBUF_NEEDS_INIT;
 }
 
+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)
+{
+       struct pxa_cam_dma *pxa_dma = &buf->dmas[channel];
+       int i;
+
+       if (pxa_dma->sg_cpu)
+               dma_free_coherent(pcdev->dev, pxa_dma->sg_size,
+                                 pxa_dma->sg_cpu, pxa_dma->sg_dma);
+
+       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);
+       if (!pxa_dma->sg_cpu)
+               return -ENOMEM;
+
+       pxa_dma->sglen = sglen;
+
+       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;
+
+               pxa_dma->sg_cpu[i].dsadr = pcdev->res->start + cibr;
+               pxa_dma->sg_cpu[i].dtadr = sg_dma_address(&sg[sg_i]);
+
+               /* PXA27x Developer's Manual 27.4.4.1: round up to 8 bytes */
+               xfer_len = (min(dma_len, size) + 7) & ~7;
+
+               pxa_dma->sg_cpu[i].dcmd =
+                       DCMD_FLOWSRC | DCMD_BURST8 | DCMD_INCTRGADDR | xfer_len;
+               size -= dma_len;
+               pxa_dma->sg_cpu[i].ddadr =
+                       pxa_dma->sg_dma + (i + 1) * sizeof(struct pxa_dma_desc);
+       }
+
+       pxa_dma->sg_cpu[sglen - 1].ddadr = DDADR_STOP;
+       pxa_dma->sg_cpu[sglen - 1].dcmd |= DCMD_ENDIRQEN;
+
+       return 0;
+}
+
 static int pxa_videobuf_prepare(struct videobuf_queue *vq,
                struct videobuf_buffer *vb, enum v4l2_field field)
 {
@@ -173,9 +251,11 @@ static int pxa_videobuf_prepare(struct videobuf_queue *vq,
                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);
-       int i, ret;
+       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", __FUNCTION__,
+       dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
                vb, vb->baddr, vb->bsize);
 
        /* Added list head initialization on alloc */
@@ -218,49 +298,64 @@ static int pxa_videobuf_prepare(struct videobuf_queue *vq,
                if (ret)
                        goto fail;
 
-               if (buf->sg_cpu)
-                       dma_free_coherent(pcdev->dev, buf->sg_size, buf->sg_cpu,
-                                         buf->sg_dma);
+               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);
 
-               buf->sg_size = (dma->sglen + 1) * sizeof(struct pxa_dma_desc);
-               buf->sg_cpu = dma_alloc_coherent(pcdev->dev, buf->sg_size,
-                                                &buf->sg_dma, GFP_KERNEL);
-               if (!buf->sg_cpu) {
-                       ret = -ENOMEM;
+               if (ret) {
+                       dev_err(pcdev->dev,
+                               "DMA initialization for Y/RGB failed\n");
                        goto fail;
                }
 
-               dev_dbg(&icd->dev, "nents=%d size: %d sg=0x%p\n",
-                       dma->sglen, size, dma->sglist);
-               for (i = 0; i < dma->sglen; i++) {
-                       struct scatterlist *sg = dma->sglist;
-                       unsigned int dma_len = sg_dma_len(&sg[i]), xfer_len;
-
-                       /* CIBR0 */
-                       buf->sg_cpu[i].dsadr = pcdev->res->start + 0x28;
-                       buf->sg_cpu[i].dtadr = sg_dma_address(&sg[i]);
-                       /* PXA270 Developer's Manual 27.4.4.1:
-                        * round up to 8 bytes */
-                       xfer_len = (min(dma_len, size) + 7) & ~7;
-                       if (xfer_len & 7)
-                               dev_err(&icd->dev, "Unaligned buffer: "
-                                       "dma_len %u, size %u\n", dma_len, size);
-                       buf->sg_cpu[i].dcmd = DCMD_FLOWSRC | DCMD_BURST8 |
-                               DCMD_INCTRGADDR | xfer_len;
-                       size -= dma_len;
-                       buf->sg_cpu[i].ddadr = buf->sg_dma + (i + 1) *
-                                       sizeof(struct pxa_dma_desc);
+               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;
+                       }
                }
-               buf->sg_cpu[dma->sglen - 1].ddadr = DDADR_STOP;
-               buf->sg_cpu[dma->sglen - 1].dcmd |= DCMD_ENDIRQEN;
 
                vb->state = VIDEOBUF_PREPARED;
        }
 
        buf->inwork = 0;
+       buf->active_dma = DMA_Y;
+       if (pcdev->channels == 3)
+               buf->active_dma |= DMA_U | DMA_V;
 
        return 0;
 
+fail_v:
+       dma_free_coherent(pcdev->dev, buf->dmas[1].sg_size,
+                         buf->dmas[1].sg_cpu, buf->dmas[1].sg_dma);
+fail_u:
+       dma_free_coherent(pcdev->dev, buf->dmas[0].sg_size,
+                         buf->dmas[0].sg_cpu, buf->dmas[0].sg_dma);
 fail:
        free_buffer(vq, buf);
 out:
@@ -277,11 +372,10 @@ static void pxa_videobuf_queue(struct videobuf_queue *vq,
        struct pxa_camera_dev *pcdev = ici->priv;
        struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb);
        struct pxa_buffer *active;
-       struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
-       int nents = dma->sglen;
        unsigned long flags;
+       int i;
 
-       dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __FUNCTION__,
+       dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
                vb, vb->baddr, vb->bsize);
        spin_lock_irqsave(&pcdev->lock, flags);
 
@@ -292,59 +386,63 @@ static void pxa_videobuf_queue(struct videobuf_queue *vq,
 
        if (!active) {
                CIFR |= CIFR_RESET_F;
-               DDADR(pcdev->dma_chan_y) = buf->sg_dma;
-               DCSR(pcdev->dma_chan_y) = DCSR_RUN;
+
+               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 |= CICR0_ENB;
        } else {
-               struct videobuf_dmabuf *active_dma =
-                       videobuf_to_dma(&active->vb);
-               /* Stop DMA engine */
-               DCSR(pcdev->dma_chan_y) = 0;
-
-               /* Add the descriptors we just initialized to the currently
-                * running chain
-                */
-               active->sg_cpu[active_dma->sglen - 1].ddadr = buf->sg_dma;
-
-               /* Setup a dummy descriptor with the DMA engines current
-                * state
-                */
-               /* CIBR0 */
-               buf->sg_cpu[nents].dsadr = pcdev->res->start + 0x28;
-               buf->sg_cpu[nents].dtadr = DTADR(pcdev->dma_chan_y);
-               buf->sg_cpu[nents].dcmd = DCMD(pcdev->dma_chan_y);
-
-               if (DDADR(pcdev->dma_chan_y) == DDADR_STOP) {
-                       /* The DMA engine is on the last descriptor, set the
-                        * next descriptors address to the descriptors
-                        * we just initialized
-                        */
-                       buf->sg_cpu[nents].ddadr = buf->sg_dma;
-               } else {
-                       buf->sg_cpu[nents].ddadr = DDADR(pcdev->dma_chan_y);
-               }
+               struct pxa_cam_dma *buf_dma;
+               struct pxa_cam_dma *act_dma;
+               int nents;
 
-               /* The next descriptor is the dummy descriptor */
-               DDADR(pcdev->dma_chan_y) = buf->sg_dma + nents *
-                       sizeof(struct pxa_dma_desc);
+               for (i = 0; i < pcdev->channels; i++) {
+                       buf_dma = &buf->dmas[i];
+                       act_dma = &active->dmas[i];
+                       nents = buf_dma->sglen;
 
-#ifdef DEBUG
-               if (CISR & CISR_IFO_0) {
-                       dev_warn(pcdev->dev, "FIFO overrun\n");
-                       DDADR(pcdev->dma_chan_y) = pcdev->active->sg_dma;
-
-                       CICR0 &= ~CICR0_ENB;
-                       CIFR |= CIFR_RESET_F;
-                       DCSR(pcdev->dma_chan_y) = DCSR_RUN;
-                       CICR0 |= CICR0_ENB;
-               } else
-#endif
-                       DCSR(pcdev->dma_chan_y) = DCSR_RUN;
+                       /* 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;
+               }
        }
 
        spin_unlock_irqrestore(&pcdev->lock, flags);
-
 }
 
 static void pxa_videobuf_release(struct videobuf_queue *vq,
@@ -354,21 +452,21 @@ static void pxa_videobuf_release(struct videobuf_queue *vq,
 #ifdef DEBUG
        struct soc_camera_device *icd = vq->priv_data;
 
-       dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __FUNCTION__,
+       dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
                vb, vb->baddr, vb->bsize);
 
        switch (vb->state) {
        case VIDEOBUF_ACTIVE:
-               dev_dbg(&icd->dev, "%s (active)\n", __FUNCTION__);
+               dev_dbg(&icd->dev, "%s (active)\n", __func__);
                break;
        case VIDEOBUF_QUEUED:
-               dev_dbg(&icd->dev, "%s (queued)\n", __FUNCTION__);
+               dev_dbg(&icd->dev, "%s (queued)\n", __func__);
                break;
        case VIDEOBUF_PREPARED:
-               dev_dbg(&icd->dev, "%s (prepared)\n", __FUNCTION__);
+               dev_dbg(&icd->dev, "%s (prepared)\n", __func__);
                break;
        default:
-               dev_dbg(&icd->dev, "%s (unknown)\n", __FUNCTION__);
+               dev_dbg(&icd->dev, "%s (unknown)\n", __func__);
                break;
        }
 #endif
@@ -376,18 +474,42 @@ static void pxa_videobuf_release(struct videobuf_queue *vq,
        free_buffer(vq, buf);
 }
 
-static void pxa_camera_dma_irq_y(int channel, void *data)
+static void pxa_camera_wakeup(struct pxa_camera_dev *pcdev,
+                             struct videobuf_buffer *vb,
+                             struct pxa_buffer *buf)
+{
+       /* _init is used to debug races, see comment in pxa_camera_reqbufs() */
+       list_del_init(&vb->queue);
+       vb->state = VIDEOBUF_DONE;
+       do_gettimeofday(&vb->ts);
+       vb->field_count++;
+       wake_up(&vb->done);
+
+       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 &= ~CICR0_ENB;
+               return;
+       }
+
+       pcdev->active = list_entry(pcdev->capture.next,
+                                  struct pxa_buffer, vb.queue);
+}
+
+static void pxa_camera_dma_irq(int channel, struct pxa_camera_dev *pcdev,
+                              enum pxa_camera_active_dma act_dma)
 {
-       struct pxa_camera_dev *pcdev = data;
        struct pxa_buffer *buf;
        unsigned long flags;
-       unsigned int status;
+       u32 status, camera_status, overrun;
        struct videobuf_buffer *vb;
 
        spin_lock_irqsave(&pcdev->lock, flags);
 
-       status = DCSR(pcdev->dma_chan_y);
-       DCSR(pcdev->dma_chan_y) = status;
+       status = DCSR(channel);
+       DCSR(channel) = status | DCSR_ENDINTR;
 
        if (status & DCSR_BUSERR) {
                dev_err(pcdev->dev, "DMA Bus Error IRQ!\n");
@@ -405,33 +527,57 @@ static void pxa_camera_dma_irq_y(int channel, void *data)
                goto out;
        }
 
+       camera_status = 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 &= ~CICR0_ENB;
+               /* Stop DMA */
+               DCSR(channel) = 0;
+               /* Reset the FIFOs */
+               CIFR |= CIFR_RESET_F;
+               /* Enable End-Of-Frame Interrupt */
+               CICR0 &= ~CICR0_EOFM;
+               /* Restart the Capture Interface */
+               CICR0 |= CICR0_ENB;
+               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", __FUNCTION__,
+       dev_dbg(pcdev->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
                vb, vb->baddr, vb->bsize);
 
-       /* _init is used to debug races, see comment in pxa_camera_reqbufs() */
-       list_del_init(&vb->queue);
-       vb->state = VIDEOBUF_DONE;
-       do_gettimeofday(&vb->ts);
-       vb->field_count++;
-       wake_up(&vb->done);
-
-       if (list_empty(&pcdev->capture)) {
-               pcdev->active = NULL;
-               DCSR(pcdev->dma_chan_y) = 0;
-               CICR0 &= ~CICR0_ENB;
-               goto out;
-       }
-
-       pcdev->active = list_entry(pcdev->capture.next, struct pxa_buffer,
-                                  vb.queue);
+       buf->active_dma &= ~act_dma;
+       if (!buf->active_dma)
+               pxa_camera_wakeup(pcdev, vb, buf);
 
 out:
        spin_unlock_irqrestore(&pcdev->lock, flags);
 }
 
+static void pxa_camera_dma_irq_y(int channel, void *data)
+{
+       struct pxa_camera_dev *pcdev = data;
+       pxa_camera_dma_irq(channel, pcdev, DMA_Y);
+}
+
+static void pxa_camera_dma_irq_u(int channel, void *data)
+{
+       struct pxa_camera_dev *pcdev = data;
+       pxa_camera_dma_irq(channel, pcdev, DMA_U);
+}
+
+static void pxa_camera_dma_irq_v(int channel, void *data)
+{
+       struct pxa_camera_dev *pcdev = data;
+       pxa_camera_dma_irq(channel, pcdev, DMA_V);
+}
+
 static struct videobuf_queue_ops pxa_videobuf_ops = {
        .buf_setup      = pxa_videobuf_setup,
        .buf_prepare    = pxa_videobuf_prepare,
@@ -439,6 +585,19 @@ static struct videobuf_queue_ops pxa_videobuf_ops = {
        .buf_release    = pxa_videobuf_release,
 };
 
+static void pxa_camera_init_videobuf(struct videobuf_queue *q,
+                             struct soc_camera_device *icd)
+{
+       struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+       struct pxa_camera_dev *pcdev = ici->priv;
+
+       /* We must pass NULL as dev pointer, then all pci_* dma operations
+        * transform to normal dma_* ones. */
+       videobuf_queue_sg_init(q, &pxa_videobuf_ops, NULL, &pcdev->lock,
+                               V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE,
+                               sizeof(struct pxa_buffer), icd);
+}
+
 static int mclk_get_divisor(struct pxa_camera_dev *pcdev)
 {
        unsigned int mclk_10khz = pcdev->platform_mclk_10khz;
@@ -466,18 +625,18 @@ static void pxa_camera_activate(struct pxa_camera_dev *pcdev)
                pcdev, pdata);
 
        if (pdata && pdata->init) {
-               dev_dbg(pcdev->dev, "%s: Init gpios\n", __FUNCTION__);
+               dev_dbg(pcdev->dev, "%s: Init gpios\n", __func__);
                pdata->init(pcdev->dev);
        }
 
        if (pdata && pdata->power) {
-               dev_dbg(pcdev->dev, "%s: Power on camera\n", __FUNCTION__);
+               dev_dbg(pcdev->dev, "%s: Power on camera\n", __func__);
                pdata->power(pcdev->dev, 1);
        }
 
        if (pdata && pdata->reset) {
                dev_dbg(pcdev->dev, "%s: Releasing camera reset\n",
-                       __FUNCTION__);
+                       __func__);
                pdata->reset(pcdev->dev, 1);
        }
 
@@ -507,12 +666,12 @@ static void pxa_camera_deactivate(struct pxa_camera_dev *pcdev)
 
        if (board && board->reset) {
                dev_dbg(pcdev->dev, "%s: Asserting camera reset\n",
-                       __FUNCTION__);
+                       __func__);
                board->reset(pcdev->dev, 0);
        }
 
        if (board && board->power) {
-               dev_dbg(pcdev->dev, "%s: Power off camera\n", __FUNCTION__);
+               dev_dbg(pcdev->dev, "%s: Power off camera\n", __func__);
                board->power(pcdev->dev, 0);
        }
 }
@@ -524,8 +683,21 @@ static irqreturn_t pxa_camera_irq(int irq, void *data)
 
        dev_dbg(pcdev->dev, "Camera interrupt status 0x%x\n", status);
 
+       if (!status)
+               return IRQ_NONE;
+
        CISR = status;
 
+       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;
+               }
+               CICR0 |= CICR0_EOFM;
+       }
+
        return IRQ_HANDLED;
 }
 
@@ -571,8 +743,11 @@ static void pxa_camera_remove_device(struct soc_camera_device *icd)
 
        /* disable capture, disable interrupts */
        CICR0 = 0x3ff;
+
        /* Stop DMA engine */
-       DCSR(pcdev->dma_chan_y) = 0;
+       DCSR(pcdev->dma_chans[0]) = 0;
+       DCSR(pcdev->dma_chans[1]) = 0;
+       DCSR(pcdev->dma_chans[2]) = 0;
 
        icd->ops->release(icd);
 
@@ -625,7 +800,7 @@ static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
                to_soc_camera_host(icd->dev.parent);
        struct pxa_camera_dev *pcdev = ici->priv;
        unsigned long dw, bpp, bus_flags, camera_flags, common_flags;
-       u32 cicr0, cicr4 = 0;
+       u32 cicr0, cicr1, cicr4 = 0;
        int ret = test_platform_param(pcdev, icd->buswidth, &bus_flags);
 
        if (ret < 0)
@@ -637,6 +812,8 @@ static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
        if (!common_flags)
                return -EINVAL;
 
+       pcdev->channels = 1;
+
        /* Make choises, based on platform preferences */
        if ((common_flags & SOCAM_HSYNC_ACTIVE_HIGH) &&
            (common_flags & SOCAM_HSYNC_ACTIVE_LOW)) {
@@ -702,7 +879,26 @@ static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
        cicr0 = CICR0;
        if (cicr0 & CICR0_ENB)
                CICR0 = cicr0 & ~CICR0_ENB;
-       CICR1 = CICR1_PPL_VAL(icd->width - 1) | bpp | dw;
+
+       cicr1 = CICR1_PPL_VAL(icd->width - 1) | bpp | dw;
+
+       switch (pixfmt) {
+       case V4L2_PIX_FMT_YUV422P:
+               pcdev->channels = 3;
+               cicr1 |= CICR1_YCBCR_F;
+       case V4L2_PIX_FMT_YUYV:
+               cicr1 |= CICR1_COLOR_SP_VAL(2);
+               break;
+       case V4L2_PIX_FMT_RGB555:
+               cicr1 |= CICR1_RGB_BPP_VAL(1) | CICR1_RGBT_CONV_VAL(2) |
+                       CICR1_TBIT | CICR1_COLOR_SP_VAL(1);
+               break;
+       case V4L2_PIX_FMT_RGB565:
+               cicr1 |= CICR1_COLOR_SP_VAL(1) | CICR1_RGB_BPP_VAL(2);
+               break;
+       }
+
+       CICR1 = cicr1;
        CICR2 = 0;
        CICR3 = CICR3_LPF_VAL(icd->height - 1) |
                CICR3_BFW_VAL(min((unsigned short)255, icd->y_skip_top));
@@ -803,34 +999,77 @@ static int pxa_camera_querycap(struct soc_camera_host *ici,
        return 0;
 }
 
-static spinlock_t *pxa_camera_spinlock_alloc(struct soc_camera_file *icf)
+static int pxa_camera_suspend(struct soc_camera_device *icd, pm_message_t state)
+{
+       struct soc_camera_host *ici =
+               to_soc_camera_host(icd->dev.parent);
+       struct pxa_camera_dev *pcdev = ici->priv;
+       int i = 0, ret = 0;
+
+       pcdev->save_cicr[i++] = CICR0;
+       pcdev->save_cicr[i++] = CICR1;
+       pcdev->save_cicr[i++] = CICR2;
+       pcdev->save_cicr[i++] = CICR3;
+       pcdev->save_cicr[i++] = CICR4;
+
+       if ((pcdev->icd) && (pcdev->icd->ops->suspend))
+               ret = pcdev->icd->ops->suspend(pcdev->icd, state);
+
+       return ret;
+}
+
+static int pxa_camera_resume(struct soc_camera_device *icd)
 {
        struct soc_camera_host *ici =
-               to_soc_camera_host(icf->icd->dev.parent);
+               to_soc_camera_host(icd->dev.parent);
        struct pxa_camera_dev *pcdev = ici->priv;
+       int i = 0, ret = 0;
+
+       DRCMR68 = pcdev->dma_chans[0] | DRCMR_MAPVLD;
+       DRCMR69 = pcdev->dma_chans[1] | DRCMR_MAPVLD;
+       DRCMR70 = pcdev->dma_chans[2] | DRCMR_MAPVLD;
+
+       CICR0 = pcdev->save_cicr[i++] & ~CICR0_ENB;
+       CICR1 = pcdev->save_cicr[i++];
+       CICR2 = pcdev->save_cicr[i++];
+       CICR3 = pcdev->save_cicr[i++];
+       CICR4 = pcdev->save_cicr[i++];
+
+       if ((pcdev->icd) && (pcdev->icd->ops->resume))
+               ret = pcdev->icd->ops->resume(pcdev->icd);
+
+       /* Restart frame capture if active buffer exists */
+       if (!ret && pcdev->active) {
+               /* Reset the FIFOs */
+               CIFR |= CIFR_RESET_F;
+               /* Enable End-Of-Frame Interrupt */
+               CICR0 &= ~CICR0_EOFM;
+               /* Restart the Capture Interface */
+               CICR0 |= CICR0_ENB;
+       }
 
-       return &pcdev->lock;
+       return ret;
 }
 
 static struct soc_camera_host_ops pxa_soc_camera_host_ops = {
        .owner          = THIS_MODULE,
        .add            = pxa_camera_add_device,
        .remove         = pxa_camera_remove_device,
+       .suspend        = pxa_camera_suspend,
+       .resume         = pxa_camera_resume,
        .set_fmt_cap    = pxa_camera_set_fmt_cap,
        .try_fmt_cap    = pxa_camera_try_fmt_cap,
+       .init_videobuf  = pxa_camera_init_videobuf,
        .reqbufs        = pxa_camera_reqbufs,
        .poll           = pxa_camera_poll,
        .querycap       = pxa_camera_querycap,
        .try_bus_param  = pxa_camera_try_bus_param,
        .set_bus_param  = pxa_camera_set_bus_param,
-       .spinlock_alloc = pxa_camera_spinlock_alloc,
 };
 
 /* Should be allocated dynamically too, but we have only one. */
 static struct soc_camera_host pxa_soc_camera_host = {
        .drv_name               = PXA_CAM_DRV_NAME,
-       .vbq_ops                = &pxa_videobuf_ops,
-       .msize                  = sizeof(struct pxa_buffer),
        .ops                    = &pxa_soc_camera_host_ops,
 };
 
@@ -839,12 +1078,12 @@ static int pxa_camera_probe(struct platform_device *pdev)
        struct pxa_camera_dev *pcdev;
        struct resource *res;
        void __iomem *base;
-       unsigned int irq;
+       int irq;
        int err = 0;
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        irq = platform_get_irq(pdev, 0);
-       if (!res || !irq) {
+       if (!res || irq < 0) {
                err = -ENODEV;
                goto exit;
        }
@@ -905,16 +1144,36 @@ static int pxa_camera_probe(struct platform_device *pdev)
        pcdev->dev = &pdev->dev;
 
        /* request dma */
-       pcdev->dma_chan_y = pxa_request_dma("CI_Y", DMA_PRIO_HIGH,
-                                           pxa_camera_dma_irq_y, pcdev);
-       if (pcdev->dma_chan_y < 0) {
+       pcdev->dma_chans[0] = pxa_request_dma("CI_Y", DMA_PRIO_HIGH,
+                                             pxa_camera_dma_irq_y, pcdev);
+       if (pcdev->dma_chans[0] < 0) {
                dev_err(pcdev->dev, "Can't request DMA for Y\n");
                err = -ENOMEM;
                goto exit_iounmap;
        }
-       dev_dbg(pcdev->dev, "got DMA channel %d\n", pcdev->dma_chan_y);
+       dev_dbg(pcdev->dev, "got DMA channel %d\n", pcdev->dma_chans[0]);
+
+       pcdev->dma_chans[1] = pxa_request_dma("CI_U", DMA_PRIO_HIGH,
+                                             pxa_camera_dma_irq_u, pcdev);
+       if (pcdev->dma_chans[1] < 0) {
+               dev_err(pcdev->dev, "Can't request DMA for U\n");
+               err = -ENOMEM;
+               goto exit_free_dma_y;
+       }
+       dev_dbg(pcdev->dev, "got DMA channel (U) %d\n", pcdev->dma_chans[1]);
+
+       pcdev->dma_chans[2] = pxa_request_dma("CI_V", DMA_PRIO_HIGH,
+                                             pxa_camera_dma_irq_v, pcdev);
+       if (pcdev->dma_chans[0] < 0) {
+               dev_err(pcdev->dev, "Can't request DMA for V\n");
+               err = -ENOMEM;
+               goto exit_free_dma_u;
+       }
+       dev_dbg(pcdev->dev, "got DMA channel (V) %d\n", pcdev->dma_chans[2]);
 
-       DRCMR68 = pcdev->dma_chan_y  | DRCMR_MAPVLD;
+       DRCMR68 = pcdev->dma_chans[0] | DRCMR_MAPVLD;
+       DRCMR69 = pcdev->dma_chans[1] | DRCMR_MAPVLD;
+       DRCMR70 = pcdev->dma_chans[2] | DRCMR_MAPVLD;
 
        /* request irq */
        err = request_irq(pcdev->irq, pxa_camera_irq, 0, PXA_CAM_DRV_NAME,
@@ -936,7 +1195,11 @@ static int pxa_camera_probe(struct platform_device *pdev)
 exit_free_irq:
        free_irq(pcdev->irq, pcdev);
 exit_free_dma:
-       pxa_free_dma(pcdev->dma_chan_y);
+       pxa_free_dma(pcdev->dma_chans[2]);
+exit_free_dma_u:
+       pxa_free_dma(pcdev->dma_chans[1]);
+exit_free_dma_y:
+       pxa_free_dma(pcdev->dma_chans[0]);
 exit_iounmap:
        iounmap(base);
 exit_release:
@@ -956,7 +1219,9 @@ static int __devexit pxa_camera_remove(struct platform_device *pdev)
 
        clk_put(pcdev->clk);
 
-       pxa_free_dma(pcdev->dma_chan_y);
+       pxa_free_dma(pcdev->dma_chans[0]);
+       pxa_free_dma(pcdev->dma_chans[1]);
+       pxa_free_dma(pcdev->dma_chans[2]);
        free_irq(pcdev->irq, pcdev);
 
        soc_camera_host_unregister(&pxa_soc_camera_host);
@@ -989,7 +1254,7 @@ static int __devinit pxa_camera_init(void)
 
 static void __exit pxa_camera_exit(void)
 {
-       return platform_driver_unregister(&pxa_camera_driver);
+       platform_driver_unregister(&pxa_camera_driver);
 }
 
 module_init(pxa_camera_init);