V4L/DVB (7671): pxa-camera: fix DMA sg-list coalescing for more than 2 buffers

Currently the pxa-camera driver has a bug, visible when the user requests
more than 2 video buffers. When the third buffer is queued, it is not
appended to the DMA-descriptor list of the second buffer, but is again
appended to the first buffer. Fix it.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
diff --git a/drivers/media/video/pxa_camera.c b/drivers/media/video/pxa_camera.c
index 1dcfd91..7cc8e9b 100644
--- a/drivers/media/video/pxa_camera.c
+++ b/drivers/media/video/pxa_camera.c
@@ -102,11 +102,6 @@
 	enum pxa_camera_active_dma active_dma;
 };
 
-struct pxa_framebuffer_queue {
-	dma_addr_t		sg_last_dma;
-	struct pxa_dma_desc	*sg_last_cpu;
-};
-
 struct pxa_camera_dev {
 	struct device		*dev;
 	/* PXA27x is only supposed to handle one camera on its Quick Capture
@@ -131,6 +126,7 @@
 	spinlock_t		lock;
 
 	struct pxa_buffer	*active;
+	struct pxa_dma_desc	*sg_tail[3];
 };
 
 static const char *pxa_cam_driver_description = "PXA_Camera";
@@ -144,11 +140,14 @@
 			      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 (icd->current_fmt->fourcc == V4L2_PIX_FMT_YUV422P) {
+	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 */
@@ -296,7 +295,7 @@
 		if (ret)
 			goto fail;
 
-		if (buf->fmt->fourcc == V4L2_PIX_FMT_YUV422P) {
+		if (pcdev->channels == 3) {
 			/* FIXME the calculations should be more precise */
 			sglen_y = dma->sglen / 2;
 			sglen_u = sglen_v = dma->sglen / 4 + 1;
@@ -318,7 +317,7 @@
 			goto fail;
 		}
 
-		if (buf->fmt->fourcc == V4L2_PIX_FMT_YUV422P) {
+		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);
@@ -343,7 +342,7 @@
 
 	buf->inwork = 0;
 	buf->active_dma = DMA_Y;
-	if (buf->fmt->fourcc == V4L2_PIX_FMT_YUV422P)
+	if (pcdev->channels == 3)
 		buf->active_dma |= DMA_U | DMA_V;
 
 	return 0;
@@ -371,6 +370,7 @@
 	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);
@@ -383,15 +383,11 @@
 
 	if (!active) {
 		CIFR |= CIFR_RESET_F;
-		DDADR(pcdev->dma_chans[0]) = buf->dmas[0].sg_dma;
-		DCSR(pcdev->dma_chans[0]) = DCSR_RUN;
 
-		if (buf->fmt->fourcc == V4L2_PIX_FMT_YUV422P) {
-			DDADR(pcdev->dma_chans[1]) = buf->dmas[1].sg_dma;
-			DCSR(pcdev->dma_chans[1]) = DCSR_RUN;
-
-			DDADR(pcdev->dma_chans[2]) = buf->dmas[2].sg_dma;
-			DCSR(pcdev->dma_chans[2]) = 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;
@@ -400,7 +396,6 @@
 		struct pxa_cam_dma *buf_dma;
 		struct pxa_cam_dma *act_dma;
 		int nents;
-		int i;
 
 		for (i = 0; i < pcdev->channels; i++) {
 			buf_dma = &buf->dmas[i];
@@ -412,8 +407,8 @@
 
 			/* Add the descriptors we just initialized to
 			   the currently running chain */
-			act_dma->sg_cpu[act_dma->sglen - 1].ddadr =
-				buf_dma->sg_dma;
+			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