#include <media/v4l2-chip-ident.h>
 #include <media/ov7670.h>
 #include <media/videobuf2-vmalloc.h>
+#include <media/videobuf2-dma-contig.h>
 
 #include "mcam-core.h"
 
+/*
+ * Basic frame stats - to be deleted shortly
+ */
+static int frames;
+static int singles;
+static int delivered;
 
 /*
  * Internal DMA buffer management.  Since the controller cannot do S/G I/O,
                "Non-zero value causes DMA buffers to be allocated when the "
                "video capture device is read, rather than at module load "
                "time.  This saves memory, but decreases the chances of "
-               "successfully getting those buffers.");
+               "successfully getting those buffers.  This parameter is "
+               "only used in the vmalloc buffer mode");
 
 static int n_dma_bufs = 3;
 module_param(n_dma_bufs, uint, 0644);
                "If set, the sensor will be instructed to flip the image "
                "vertically.");
 
+static int buffer_mode = -1;
+module_param(buffer_mode, int, 0444);
+MODULE_PARM_DESC(buffer_mode,
+               "Set the buffer mode to be used; default is to go with what "
+               "the platform driver asks for.  Set to 0 for vmalloc, 1 for "
+               "DMA contiguous.");
+
 /*
  * Status flags.  Always manipulated with bit operations.
  */
 #define CF_BUF2_VALID   2
 #define CF_DMA_ACTIVE   3      /* A frame is incoming */
 #define CF_CONFIG_NEEDED 4     /* Must configure hardware */
+#define CF_SINGLE_BUFFER 5     /* Running with a single buffer */
 
 #define sensor_call(cam, o, f, args...) \
        v4l2_subdev_call(cam->sensor, o, f, ##args)
  */
 
 /*
- * Do everything we think we need to have the interface operating
- * according to the desired format.
+ * Set up DMA buffers when operating in vmalloc mode
  */
-static void mcam_ctlr_dma(struct mcam_camera *cam)
+static void mcam_ctlr_dma_vmalloc(struct mcam_camera *cam)
 {
        /*
         * Store the first two Y buffers (we aren't supporting
                mcam_reg_write(cam, REG_UBAR, 0); /* 32 bits only */
 }
 
+/*
+ * Set up a contiguous buffer for the given frame.  Here also is where
+ * the underrun strategy is set: if there is no buffer available, reuse
+ * the buffer from the other BAR and set the CF_SINGLE_BUFFER flag to
+ * keep the interrupt handler from giving that buffer back to user
+ * space.  In this way, we always have a buffer to DMA to and don't
+ * have to try to play games stopping and restarting the controller.
+ */
+static void mcam_set_contig_buffer(struct mcam_camera *cam, int frame)
+{
+       struct mcam_vb_buffer *buf;
+       /*
+        * If there are no available buffers, go into single mode
+        */
+       if (list_empty(&cam->buffers)) {
+               buf = cam->vb_bufs[frame ^ 0x1];
+               cam->vb_bufs[frame] = buf;
+               mcam_reg_write(cam, frame == 0 ? REG_Y0BAR : REG_Y1BAR,
+                               vb2_dma_contig_plane_paddr(&buf->vb_buf, 0));
+               set_bit(CF_SINGLE_BUFFER, &cam->flags);
+               singles++;
+               return;
+       }
+       /*
+        * OK, we have a buffer we can use.
+        */
+       buf = list_first_entry(&cam->buffers, struct mcam_vb_buffer, queue);
+       list_del_init(&buf->queue);
+       mcam_reg_write(cam, frame == 0 ? REG_Y0BAR : REG_Y1BAR,
+                       vb2_dma_contig_plane_paddr(&buf->vb_buf, 0));
+       cam->vb_bufs[frame] = buf;
+       clear_bit(CF_SINGLE_BUFFER, &cam->flags);
+}
+
+static void mcam_ctlr_dma_contig(struct mcam_camera *cam)
+{
+       mcam_reg_set_bit(cam, REG_CTRL1, C1_TWOBUFS);
+       cam->nbufs = 2;
+       mcam_set_contig_buffer(cam, 0);
+       mcam_set_contig_buffer(cam, 1);
+}
+
+
+static void mcam_ctlr_dma(struct mcam_camera *cam)
+{
+       if (cam->buffer_mode == B_DMA_contig)
+               mcam_ctlr_dma_contig(cam);
+       else
+               mcam_ctlr_dma_vmalloc(cam);
+}
+
 static void mcam_ctlr_image(struct mcam_camera *cam)
 {
        int imgsz;
 /*
  * Get everything ready, and start grabbing frames.
  */
-static int mcam_read_setup(struct mcam_camera *cam, enum mcam_state state)
+static int mcam_read_setup(struct mcam_camera *cam)
 {
        int ret;
        unsigned long flags;
         * Configuration.  If we still don't have DMA buffers,
         * make one last, desperate attempt.
         */
-       if (cam->nbufs == 0)
-               if (mcam_alloc_dma_bufs(cam, 0))
-                       return -ENOMEM;
+       if (cam->buffer_mode == B_vmalloc && cam->nbufs == 0 &&
+                       mcam_alloc_dma_bufs(cam, 0))
+               return -ENOMEM;
 
        if (mcam_needs_config(cam)) {
                mcam_cam_configure(cam);
        spin_lock_irqsave(&cam->dev_lock, flags);
        mcam_reset_buffers(cam);
        mcam_ctlr_irq_enable(cam);
-       cam->state = state;
+       cam->state = S_STREAMING;
        mcam_ctlr_start(cam);
        spin_unlock_irqrestore(&cam->dev_lock, flags);
        return 0;
 
        sizes[0] = cam->pix_format.sizeimage;
        *num_planes = 1; /* Someday we have to support planar formats... */
-       if (*nbufs < 2 || *nbufs > 32)
-               *nbufs = 6;  /* semi-arbitrary numbers */
+       if (*nbufs < 3 || *nbufs > 32)
+               *nbufs = 3;  /* semi-arbitrary numbers */
+       if (cam->buffer_mode == B_DMA_contig)
+               alloc_ctxs[0] = cam->vb_alloc_ctx;
        return 0;
 }
 
        struct mcam_vb_buffer *mvb = vb_to_mvb(vb);
        struct mcam_camera *cam = vb2_get_drv_priv(vb->vb2_queue);
        unsigned long flags;
+       int start;
 
        spin_lock_irqsave(&cam->dev_lock, flags);
-       list_add(&cam->buffers, &mvb->queue);
+       start = (cam->state == S_BUFWAIT) && !list_empty(&cam->buffers);
+       list_add(&mvb->queue, &cam->buffers);
        spin_unlock_irqrestore(&cam->dev_lock, flags);
+       if (start)
+               mcam_read_setup(cam);
 }
 
 /*
 static int mcam_vb_start_streaming(struct vb2_queue *vq)
 {
        struct mcam_camera *cam = vb2_get_drv_priv(vq);
-       int ret = -EINVAL;
 
-       if (cam->state == S_IDLE) {
-               cam->sequence = 0;
-               ret = mcam_read_setup(cam, S_STREAMING);
+       if (cam->state != S_IDLE)
+               return -EINVAL;
+       cam->sequence = 0;
+       /*
+        * Videobuf2 sneakily hoards all the buffers and won't
+        * give them to us until *after* streaming starts.  But
+        * we can't actually start streaming until we have a
+        * destination.  So go into a wait state and hope they
+        * give us buffers soon.
+        */
+       if (cam->buffer_mode != B_vmalloc && list_empty(&cam->buffers)) {
+               cam->state = S_BUFWAIT;
+               return 0;
        }
-       return ret;
+       return mcam_read_setup(cam);
 }
 
 static int mcam_vb_stop_streaming(struct vb2_queue *vq)
        struct mcam_camera *cam = vb2_get_drv_priv(vq);
        unsigned long flags;
 
+       if (cam->state == S_BUFWAIT) {
+               /* They never gave us buffers */
+               cam->state = S_IDLE;
+               return 0;
+       }
        if (cam->state != S_STREAMING)
                return -EINVAL;
        mcam_ctlr_stop_dma(cam);
        vq->io_modes = VB2_MMAP;  /* Add userptr */
        vq->drv_priv = cam;
        vq->ops = &mcam_vb2_ops;
-       vq->mem_ops = &vb2_vmalloc_memops;
+       if (cam->buffer_mode == B_DMA_contig) {
+               vq->mem_ops = &vb2_dma_contig_memops;
+               cam->vb_alloc_ctx = vb2_dma_contig_init_ctx(cam->dev);
+       } else
+               vq->mem_ops = &vb2_vmalloc_memops;
        vq->buf_struct_size = sizeof(struct mcam_vb_buffer);
 
        return vb2_queue_init(vq);
 static void mcam_cleanup_vb2(struct mcam_camera *cam)
 {
        vb2_queue_release(&cam->vb_queue);
+       if (cam->buffer_mode == B_DMA_contig)
+               vb2_dma_contig_cleanup_ctx(cam->vb_alloc_ctx);
 }
 
 static ssize_t mcam_v4l_read(struct file *filp,
 
        filp->private_data = cam;
 
+       frames = singles = delivered = 0;
        mutex_lock(&cam->s_mutex);
        if (cam->users == 0) {
                ret = mcam_setup_vb2(cam);
 {
        struct mcam_camera *cam = filp->private_data;
 
+       cam_err(cam, "Release, %d frames, %d singles, %d delivered\n", frames,
+                       singles, delivered);
        mutex_lock(&cam->s_mutex);
        (cam->users)--;
        if (filp == cam->owner) {
        if (cam->users == 0) {
                mcam_cleanup_vb2(cam);
                mcam_ctlr_power_down(cam);
-               if (alloc_bufs_at_read)
+               if (cam->buffer_mode == B_vmalloc && alloc_bufs_at_read)
                        mcam_free_dma_bufs(cam);
        }
        mutex_unlock(&cam->s_mutex);
         * Make sure we have appropriate DMA buffers.
         */
        ret = -ENOMEM;
-       if (cam->nbufs > 0 && cam->dma_buf_size < cam->pix_format.sizeimage)
-               mcam_free_dma_bufs(cam);
-       if (cam->nbufs == 0) {
-               if (mcam_alloc_dma_bufs(cam, 0))
-                       goto out;
+       if (cam->buffer_mode == B_vmalloc) {
+               if (cam->nbufs > 0 &&
+                               cam->dma_buf_size < cam->pix_format.sizeimage)
+                       mcam_free_dma_bufs(cam);
+               if (cam->nbufs == 0) {
+                       if (mcam_alloc_dma_bufs(cam, 0))
+                               goto out;
+               }
        }
-       /*
-        * It looks like this might work, so let's program the sensor.
-        */
-       ret = mcam_cam_configure(cam);
-       if (!ret)
-               ret = mcam_ctlr_configure(cam);
+       mcam_set_config_needed(cam, 1);
+       ret = 0;
 out:
        mutex_unlock(&cam->s_mutex);
        return ret;
  */
 
 
+static void mcam_buffer_done(struct mcam_camera *cam, int frame,
+               struct vb2_buffer *vbuf)
+{
+       vbuf->v4l2_buf.bytesused = cam->pix_format.sizeimage;
+       vbuf->v4l2_buf.sequence = cam->buf_seq[frame];
+       vbuf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_QUEUED;
+       vbuf->v4l2_buf.flags |= V4L2_BUF_FLAG_DONE;
+       vb2_set_plane_payload(vbuf, 0, cam->pix_format.sizeimage);
+       vb2_buffer_done(vbuf, VB2_BUF_STATE_DONE);
+}
 
+/*
+ * Copy data out to user space in the vmalloc case
+ */
 static void mcam_frame_tasklet(unsigned long data)
 {
        struct mcam_camera *cam = (struct mcam_camera *) data;
                        cam->next_buf = 0;
                if (!test_bit(bufno, &cam->flags))
                        continue;
-               if (list_empty(&cam->buffers))
+               if (list_empty(&cam->buffers)) {
+                       singles++;
                        break;  /* Leave it valid, hope for better later */
+               }
+               delivered++;
                clear_bit(bufno, &cam->flags);
                buf = list_first_entry(&cam->buffers, struct mcam_vb_buffer,
                                queue);
                spin_unlock_irqrestore(&cam->dev_lock, flags);
                memcpy(vb2_plane_vaddr(&buf->vb_buf, 0), cam->dma_bufs[bufno],
                                cam->pix_format.sizeimage);
-               buf->vb_buf.v4l2_buf.bytesused = cam->pix_format.sizeimage;
-               buf->vb_buf.v4l2_buf.sequence = cam->buf_seq[bufno];
-               buf->vb_buf.v4l2_buf.flags &= ~V4L2_BUF_FLAG_QUEUED;
-               buf->vb_buf.v4l2_buf.flags |= V4L2_BUF_FLAG_DONE;
-               vb2_set_plane_payload(&buf->vb_buf, 0,
-                               cam->pix_format.sizeimage);
-               vb2_buffer_done(&buf->vb_buf, VB2_BUF_STATE_DONE);
+               mcam_buffer_done(cam, bufno, &buf->vb_buf);
                spin_lock_irqsave(&cam->dev_lock, flags);
        }
        spin_unlock_irqrestore(&cam->dev_lock, flags);
 }
 
+/*
+ * For direct DMA, mark the buffer ready and set up another one.
+ */
+static void mcam_dma_complete(struct mcam_camera *cam, int frame)
+{
+       struct mcam_vb_buffer *buf = cam->vb_bufs[frame];
+
+       if (!test_bit(CF_SINGLE_BUFFER, &cam->flags)) {
+               delivered++;
+               mcam_buffer_done(cam, frame, &buf->vb_buf);
+       }
+       mcam_set_contig_buffer(cam, frame);
+}
 
 
 static void mcam_frame_complete(struct mcam_camera *cam, int frame)
         */
        set_bit(frame, &cam->flags);
        clear_bit(CF_DMA_ACTIVE, &cam->flags);
-       if (cam->next_buf < 0)
-               cam->next_buf = frame;
+       cam->next_buf = frame;
        cam->buf_seq[frame] = ++(cam->sequence);
+       cam->last_delivered = frame;
 
+       frames++;
        switch (cam->state) {
        /*
-        * For the streaming case, we defer the real work to the
-        * camera tasklet.
-        *
-        * FIXME: if the application is not consuming the buffers,
-        * we should eventually put things on hold and restart in
-        * vidioc_dqbuf().
+        * We're streaming and have a ready frame, hand it back
         */
        case S_STREAMING:
-               tasklet_schedule(&cam->s_tasklet);
+               if (cam->buffer_mode == B_vmalloc)
+                       tasklet_schedule(&cam->s_tasklet);
+               else
+                       mcam_dma_complete(cam, frame);
                break;
 
        default:
        INIT_LIST_HEAD(&cam->dev_list);
        INIT_LIST_HEAD(&cam->buffers);
        tasklet_init(&cam->s_tasklet, mcam_frame_tasklet, (unsigned long) cam);
-
+       /*
+        * User space may want to override the asked-for buffer mode;
+        * here's hoping they know what they're doing.
+        */
+       if (buffer_mode == 0)
+               cam->buffer_mode = B_vmalloc;
+       else if (buffer_mode == 1)
+               cam->buffer_mode = B_DMA_contig;
+       else if (buffer_mode != -1)
+               printk(KERN_ERR "marvel-cam: "
+                               "Strange module buffer mode %d - ignoring\n",
+                               buffer_mode);
        mcam_ctlr_init(cam);
 
        /*
        /*
         * If so requested, try to get our DMA buffers now.
         */
-       if (!alloc_bufs_at_read) {
+       if (cam->buffer_mode == B_vmalloc && !alloc_bufs_at_read) {
                if (mcam_alloc_dma_bufs(cam, 1))
                        cam_warn(cam, "Unable to alloc DMA buffers at load"
                                        " will try again later.");
                mcam_ctlr_power_down(cam);
        }
        vb2_queue_release(&cam->vb_queue);
-       mcam_free_dma_bufs(cam);
+       if (cam->buffer_mode == B_vmalloc)
+               mcam_free_dma_bufs(cam);
        video_unregister_device(&cam->vdev);
        v4l2_device_unregister(&cam->v4l2_dev);
 }
 
        set_bit(CF_CONFIG_NEEDED, &cam->flags);
        if (cam->state == S_STREAMING)
-               ret = mcam_read_setup(cam, cam->state);
+               ret = mcam_read_setup(cam);
        return ret;
 }
 #endif /* CONFIG_PM */