[media] saa7164: allow DMA engine buffers to vary in size between analog and digital

Signed-off-by: Steven Toth <stoth@kernellabs.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
diff --git a/drivers/media/video/saa7164/saa7164-buffer.c b/drivers/media/video/saa7164/saa7164-buffer.c
index 5f45ea7..187e3f6 100644
--- a/drivers/media/video/saa7164/saa7164-buffer.c
+++ b/drivers/media/video/saa7164/saa7164-buffer.c
@@ -134,18 +134,19 @@
 	buf->crc = crc32(0, buf->cpu, buf->actual_size);
 	memset_io(buf->pt_cpu, 0xff, buf->pt_size);
 
-	dprintk(DBGLVL_BUF, "%s()   allocated buffer @ 0x%p\n",
-		__func__, buf);
+	dprintk(DBGLVL_BUF, "%s()   allocated buffer @ 0x%p (%d pageptrs)\n",
+		__func__, buf, params->numpagetables);
 	dprintk(DBGLVL_BUF, "  pci_cpu @ 0x%p    dma @ 0x%08lx len = 0x%x\n",
 		buf->cpu, (long)buf->dma, buf->pci_size);
 	dprintk(DBGLVL_BUF, "   pt_cpu @ 0x%p pt_dma @ 0x%08lx len = 0x%x\n",
 		buf->pt_cpu, (long)buf->pt_dma, buf->pt_size);
 
 	/* Format the Page Table Entries to point into the data buffer */
-//	for (i = 0 ; i < SAA7164_PT_ENTRIES; i++) {
-	for (i = 0 ; i < 10; i++) {
+	for (i = 0 ; i < params->numpagetables; i++) {
 
 		*(buf->pt_cpu + i) = buf->dma + (i * 0x1000); /* TODO */
+		dprintk(DBGLVL_BUF, "    pt[%02d] = 0x%p -> 0x%llx\n",
+			i, buf->pt_cpu, (u64)*(buf->pt_cpu));
 
 	}
 
diff --git a/drivers/media/video/saa7164/saa7164-core.c b/drivers/media/video/saa7164/saa7164-core.c
index b54b032..8879517 100644
--- a/drivers/media/video/saa7164/saa7164-core.c
+++ b/drivers/media/video/saa7164/saa7164-core.c
@@ -65,6 +65,14 @@
 module_param(print_histogram, int, 0644);
 MODULE_PARM_DESC(print_histogram, "print histogram values once");
 
+unsigned int crc_checking = 1;
+module_param(crc_checking, int, 0644);
+MODULE_PARM_DESC(crc_checking, "enable crc sanity checking on buffers");
+
+unsigned int guard_checking = 1;
+module_param(guard_checking, int, 0644);
+MODULE_PARM_DESC(guard_checking, "enable dma sanity checking for buffer overruns");
+
 static unsigned int saa7164_devcount;
 
 static DEFINE_MUTEX(devlist);
@@ -101,17 +109,24 @@
 
 	for (i = 0; i < buf->actual_size; i += 2048) {
 
-		if ( (*(p + i + 0) != 0x00) || (*(p + i + 1) != 0x00) || (*(p + i + 2) != 0x01) || (*(p + i + 3) != 0xBA) )
+		if ( (*(p + i + 0) != 0x00) || (*(p + i + 1) != 0x00) ||
+			(*(p + i + 2) != 0x01) || (*(p + i + 3) != 0xBA) ) {
 			printk(KERN_ERR "No pack at 0x%x\n", i);
+//			saa7164_dumphex16FF(buf->port->dev, (p + i), 32);
+		}
 	}
 }
 
+#define FIXED_VIDEO_PID 0xf1
+#define FIXED_AUDIO_PID 0xf2
+
 static void saa7164_ts_verifier(struct saa7164_buffer *buf)
 {
 	struct saa7164_port *port = buf->port;
 	u32 i;
-	u8 tmp, cc, a;
-	u8 *bufcpu = (u8 *)buf->cpu;
+	u8 cc, a;
+	u16 pid;
+	u8 __iomem *bufcpu = (u8 *)buf->cpu;
 
 	port->sync_errors = 0;
 	port->v_cc_errors = 0;
@@ -121,23 +136,25 @@
 		if (*(bufcpu + i) != 0x47)
 			port->sync_errors++;
 
-		/* Query pid lower 8 bits */
-		tmp = *(bufcpu + i + 2);
+		/* TODO: Query pid lower 8 bits, ignoring upper bits intensionally */
+		pid = ((*(bufcpu + i + 1) & 0x1f) << 8) | *(bufcpu + i + 2);
 		cc = *(bufcpu + i + 3) & 0x0f;
 
-		if (tmp == 0xf1) {
+		if (pid == FIXED_VIDEO_PID) {
 			a = ((port->last_v_cc + 1) & 0x0f);
 			if (a != cc) {
-				printk(KERN_ERR "video cc last = %x current = %x i = %d\n", port->last_v_cc, cc, i);
+				printk(KERN_ERR "video cc last = %x current = %x i = %d\n",
+					port->last_v_cc, cc, i);
 				port->v_cc_errors++;
 			}
 
 			port->last_v_cc = cc;
 		} else
-		if (tmp == 0xf2) {
+		if (pid == FIXED_AUDIO_PID) {
 			a = ((port->last_a_cc + 1) & 0x0f);
 			if (a != cc) {
-				printk(KERN_ERR "audio cc last = %x current = %x i = %d\n", port->last_a_cc, cc, i);
+				printk(KERN_ERR "audio cc last = %x current = %x i = %d\n",
+					port->last_a_cc, cc, i);
 				port->a_cc_errors++;
 			}
 
@@ -246,6 +263,7 @@
 	struct saa7164_user_buffer *ubuf = 0;
 	struct list_head *c, *n;
 	int i = 0;
+	u8 __iomem *p;
 
 	mutex_lock(&port->dmaqueue_lock);
 	list_for_each_safe(c, n, &port->dmaqueue.list) {
@@ -260,12 +278,33 @@
 		if (buf->idx == bufnr) {
 
 			/* Found the buffer, deal with it */
-			dprintk(DBGLVL_IRQ, "%s() rp: %d\n", __func__, bufnr);
+			dprintk(DBGLVL_IRQ, "%s() bufnr: %d\n", __func__, bufnr);
+
+			if (crc_checking) {
+				/* Throw a new checksum on the dma buffer */
+				buf->crc = crc32(0, buf->cpu, buf->actual_size);
+			}
+
+			if (guard_checking) {
+				p = (u8 *)buf->cpu;
+				if ( (*(p + buf->actual_size + 0) != 0xff) ||
+					(*(p + buf->actual_size + 1) != 0xff) ||
+					(*(p + buf->actual_size + 2) != 0xff) ||
+					(*(p + buf->actual_size + 3) != 0xff) ||
+					(*(p + buf->actual_size + 0x10) != 0xff) ||
+					(*(p + buf->actual_size + 0x11) != 0xff) ||
+					(*(p + buf->actual_size + 0x12) != 0xff) ||
+					(*(p + buf->actual_size + 0x13) != 0xff) ) {
+						printk(KERN_ERR "%s() buf %p guard buffer breach\n",
+							__func__, buf);
+//						saa7164_dumphex16FF(dev, (p + buf->actual_size) - 32 , 64);
+				}
+			}
 
 			/* Validate the incoming buffer content */
 			if (port->encoder_params.stream_type == V4L2_MPEG_STREAM_TYPE_MPEG2_TS)
 				saa7164_ts_verifier(buf);
-			if (port->encoder_params.stream_type == V4L2_MPEG_STREAM_TYPE_MPEG2_PS)
+			else if (port->encoder_params.stream_type == V4L2_MPEG_STREAM_TYPE_MPEG2_PS)
 				saa7164_pack_verifier(buf);
 
 			/* find a free user buffer and clone to it */
@@ -280,8 +319,10 @@
 					memcpy_fromio(ubuf->data, buf->cpu,
 						ubuf->actual_size);
 
-					/* Throw a new checksum on the read buffer */
-					ubuf->crc = crc32(0, ubuf->data, ubuf->actual_size);
+					if (crc_checking) {
+						/* Throw a new checksum on the read buffer */
+						ubuf->crc = crc32(0, ubuf->data, ubuf->actual_size);
+					}
 
 					/* Requeue the buffer on the free list */
 					ubuf->pos = 0;
@@ -304,6 +345,10 @@
 			 * in time. */
 			saa7164_buffer_zero_offsets(port, bufnr);
 			memset_io(buf->cpu, 0xff, buf->pci_size);
+			if (crc_checking) {
+				/* Throw yet aanother new checksum on the dma buffer */
+				buf->crc = crc32(0, buf->cpu, buf->actual_size);
+			}
 
 			break;
 		}
@@ -352,17 +397,22 @@
 
 	/* Most current complete buffer */
 	if (wp == 0)
-		mcb = 7;
+		mcb = (port->hwcfg.buffercount - 1);
 	else
 		mcb = wp - 1;
 
 	while (1) {
-		rp = (port->last_svc_rp + 1) % 8;
+		if (port->done_first_interrupt == 0) {
+			port->done_first_interrupt++;
+			rp = mcb;
+		} else
+			rp = (port->last_svc_rp + 1) % 8;
 
-		if ((rp < 0) || (rp > 7)) {
+		if ((rp < 0) || (rp > (port->hwcfg.buffercount - 1))) {
 			printk(KERN_ERR "%s() illegal rp count %d\n", __func__, rp);
 			break;
 		}
+
 		saa7164_work_enchandler_helper(port, rp);
 		port->last_svc_rp = rp;
 		cnt++;
@@ -371,6 +421,7 @@
 			break;
 	}
 
+	/* TODO: Convert this into a /proc/saa7164 style readable file */
 	if (print_histogram == port->nr) {
 		saa7164_histogram_print(port, &port->irq_interval);
 		saa7164_histogram_print(port, &port->svc_interval);
@@ -438,7 +489,7 @@
 
 	/* Find the previous buffer to the current write point */
 	if (wp == 0)
-		rp = 7;
+		rp = (port->hwcfg.buffercount - 1);
 	else
 		rp = wp - 1;
 
diff --git a/drivers/media/video/saa7164/saa7164-encoder.c b/drivers/media/video/saa7164/saa7164-encoder.c
index 08b62e4..0859448 100644
--- a/drivers/media/video/saa7164/saa7164-encoder.c
+++ b/drivers/media/video/saa7164/saa7164-encoder.c
@@ -76,15 +76,131 @@
 	saa7164_api_set_audio_std(port);
 }
 
-/* One time configuration at registration time */
-static int saa7164_encoder_initialize(struct saa7164_port *port)
+static int saa7164_encoder_buffers_dealloc(struct saa7164_port *port)
+{
+	struct list_head *c, *n, *p, *q, *l, *v;
+	struct saa7164_dev *dev = port->dev;
+	struct saa7164_buffer *buf;
+	struct saa7164_user_buffer *ubuf;
+
+	/* Remove any allocated buffers */
+	mutex_lock(&port->dmaqueue_lock);
+
+	dprintk(DBGLVL_ENC, "%s(port=%d) dmaqueue\n", __func__, port->nr);
+	list_for_each_safe(c, n, &port->dmaqueue.list) {
+		buf = list_entry(c, struct saa7164_buffer, list);
+		list_del(c);
+		saa7164_buffer_dealloc(buf);
+	}
+
+	dprintk(DBGLVL_ENC, "%s(port=%d) used\n", __func__, port->nr);
+	list_for_each_safe(p, q, &port->list_buf_used.list) {
+		ubuf = list_entry(p, struct saa7164_user_buffer, list);
+		list_del(p);
+		saa7164_buffer_dealloc_user(ubuf);
+	}
+
+	dprintk(DBGLVL_ENC, "%s(port=%d) free\n", __func__, port->nr);
+	list_for_each_safe(l, v, &port->list_buf_free.list) {
+		ubuf = list_entry(l, struct saa7164_user_buffer, list);
+		list_del(l);
+		saa7164_buffer_dealloc_user(ubuf);
+	}
+
+	mutex_unlock(&port->dmaqueue_lock);
+	dprintk(DBGLVL_ENC, "%s(port=%d) done\n", __func__, port->nr);
+
+	return 0;
+}
+
+/* Dynamic buffer switch at encoder start time */
+static int saa7164_encoder_buffers_alloc(struct saa7164_port *port)
 {
 	struct saa7164_dev *dev = port->dev;
+	struct saa7164_buffer *buf;
+	struct saa7164_user_buffer *ubuf;
+	tmHWStreamParameters_t *params = &port->hw_streamingparams;
+	int result = -ENODEV, i;
+	int len = 0;
 
 	dprintk(DBGLVL_ENC, "%s()\n", __func__);
 
-	saa7164_encoder_configure(port);
+	if (port->encoder_params.stream_type == V4L2_MPEG_STREAM_TYPE_MPEG2_PS) {
+		dprintk(DBGLVL_ENC, "%s() type=V4L2_MPEG_STREAM_TYPE_MPEG2_PS\n", __func__);
+		params->samplesperline = 128;
+		params->numberoflines = 256;
+		params->pitch = 128;
+		params->numpagetables = 2 +
+			((SAA7164_PS_NUMBER_OF_LINES * 128) / PAGE_SIZE);
+	} else
+	if (port->encoder_params.stream_type == V4L2_MPEG_STREAM_TYPE_MPEG2_TS) {
+		dprintk(DBGLVL_ENC, "%s() type=V4L2_MPEG_STREAM_TYPE_MPEG2_TS\n", __func__);
+		params->samplesperline = 188;
+		params->numberoflines = 312;
+		params->pitch = 188;
+		params->numpagetables = 2 +
+			((SAA7164_TS_NUMBER_OF_LINES * 188) / PAGE_SIZE);
+	} else
+		BUG();
 
+	/* Init and establish defaults */
+	params->bitspersample = 8;
+	params->linethreshold = 0;
+	params->pagetablelistvirt = 0;
+	params->pagetablelistphys = 0;
+	params->numpagetableentries = port->hwcfg.buffercount;
+
+	/* Allocate the PCI resources, buffers (hard) */
+	for (i = 0; i < port->hwcfg.buffercount; i++) {
+		buf = saa7164_buffer_alloc(port,
+			params->numberoflines *
+			params->pitch);
+
+		if (!buf) {
+			printk(KERN_ERR "%s() failed "
+			       "(errno = %d), unable to allocate buffer\n",
+				__func__, result);
+			result = -ENOMEM;
+			goto failed;
+		} else {
+
+			mutex_lock(&port->dmaqueue_lock);
+			list_add_tail(&buf->list, &port->dmaqueue.list);
+			mutex_unlock(&port->dmaqueue_lock);
+
+		}
+	}
+
+	/* Allocate some kenrel kernel buffers for copying
+	 * to userpsace.
+	 */
+	len = params->numberoflines * params->pitch;
+
+	if (encoder_buffers < 16)
+		encoder_buffers = 16;
+	if (encoder_buffers > 512)
+		encoder_buffers = 512;
+
+	for (i = 0; i < encoder_buffers; i++) {
+
+		ubuf = saa7164_buffer_alloc_user(dev, len);
+		if (ubuf) {
+			mutex_lock(&port->dmaqueue_lock);
+			list_add_tail(&ubuf->list, &port->list_buf_free.list);
+			mutex_unlock(&port->dmaqueue_lock);
+		}
+
+	}
+
+	result = 0;
+
+failed:
+	return result;
+}
+
+static int saa7164_encoder_initialize(struct saa7164_port *port)
+{
+	saa7164_encoder_configure(port);
 	return 0;
 }
 
@@ -835,6 +951,7 @@
 	dprintk(DBGLVL_ENC, "%s(port=%d) Hardware stopped\n", __func__,
 		port->nr);
 
+	/* Reset the state of any allocated buffer resources */
 	mutex_lock(&port->dmaqueue_lock);
 
 	/* Reset the hard and soft buffer state */
@@ -851,6 +968,10 @@
 	}
 
 	mutex_unlock(&port->dmaqueue_lock);
+
+	/* Free any allocated resources */
+	saa7164_encoder_buffers_dealloc(port);
+
 	dprintk(DBGLVL_ENC, "%s(port=%d) Released\n", __func__, port->nr);
 
 	return ret;
@@ -863,10 +984,19 @@
 
 	dprintk(DBGLVL_ENC, "%s(port=%d)\n", __func__, port->nr);
 
+	port->done_first_interrupt = 0;
+
+	/* allocate all of the PCIe DMA buffer resources on the fly,
+	 * allowing switching between TS and PS payloads without
+	 * requiring a complete driver reload.
+	 */
+	saa7164_encoder_buffers_alloc(port);
+
 	/* Configure the encoder with any cache values */
 	saa7164_api_set_encoder(port);
 	saa7164_api_get_encoder(port);
 
+	/* Place the empty buffers on the hardware */
 	saa7164_buffer_cfg_port(port);
 
 	/* Acquire the hardware */
@@ -1005,27 +1135,29 @@
 
 struct saa7164_user_buffer *saa7164_enc_next_buf(struct saa7164_port *port)
 {
-	struct saa7164_user_buffer *buf = 0;
+	struct saa7164_user_buffer *ubuf = 0;
 	struct saa7164_dev *dev = port->dev;
 	u32 crc;
 
 	mutex_lock(&port->dmaqueue_lock);
 	if (!list_empty(&port->list_buf_used.list)) {
-		buf = list_first_entry(&port->list_buf_used.list,
+		ubuf = list_first_entry(&port->list_buf_used.list,
 			struct saa7164_user_buffer, list);
 
-		crc = crc32(0, buf->data, buf->actual_size);
-		if (crc != buf->crc) {
-			printk(KERN_ERR "%s() buf %p crc became invalid, was 0x%x became 0x%x\n", __func__,
-				buf, buf->crc, crc);
+		if (crc_checking) {
+			crc = crc32(0, ubuf->data, ubuf->actual_size);
+			if (crc != ubuf->crc) {
+				printk(KERN_ERR "%s() ubuf %p crc became invalid, was 0x%x became 0x%x\n", __func__,
+					ubuf, ubuf->crc, crc);
+			}
 		}
 
 	}
 	mutex_unlock(&port->dmaqueue_lock);
 
-	dprintk(DBGLVL_ENC, "%s() returns %p\n", __func__, buf);
+	dprintk(DBGLVL_ENC, "%s() returns %p\n", __func__, ubuf);
 
-	return buf;
+	return ubuf;
 }
 
 static ssize_t fops_read(struct file *file, char __user *buffer,
@@ -1292,10 +1424,7 @@
 int saa7164_encoder_register(struct saa7164_port *port)
 {
 	struct saa7164_dev *dev = port->dev;
-	struct saa7164_buffer *buf;
-	struct saa7164_user_buffer *ubuf;
-	int result = -ENODEV, i;
-	int len = 0;
+	int result = -ENODEV;
 
 	dprintk(DBGLVL_ENC, "%s()\n", __func__);
 
@@ -1311,64 +1440,6 @@
 		goto failed;
 	}
 
-	/* Init and establish defaults */
-	/* TODO: Check the umber of lines for PS */
-	port->hw_streamingparams.bitspersample = 8;
-	port->hw_streamingparams.samplesperline = 128;
-	port->hw_streamingparams.numberoflines = 256;
-
-	port->hw_streamingparams.pitch = 128;
-	port->hw_streamingparams.linethreshold = 0;
-	port->hw_streamingparams.pagetablelistvirt = 0;
-	port->hw_streamingparams.pagetablelistphys = 0;
-	port->hw_streamingparams.numpagetables = 2 +
-		((SAA7164_PS_NUMBER_OF_LINES * 128) / PAGE_SIZE);
-
-	port->hw_streamingparams.numpagetableentries = port->hwcfg.buffercount;
-
-	/* Allocate the PCI resources, buffers (hard) */
-	for (i = 0; i < port->hwcfg.buffercount; i++) {
-		buf = saa7164_buffer_alloc(port,
-			port->hw_streamingparams.numberoflines *
-			port->hw_streamingparams.pitch);
-
-		if (!buf) {
-			printk(KERN_ERR "%s() failed "
-			       "(errno = %d), unable to allocate buffer\n",
-				__func__, result);
-			result = -ENOMEM;
-			goto failed;
-		} else {
-
-			mutex_lock(&port->dmaqueue_lock);
-			list_add_tail(&buf->list, &port->dmaqueue.list);
-			mutex_unlock(&port->dmaqueue_lock);
-
-		}
-	}
-
-	/* Allocate some kenrel kernel buffers for copying
-	 * to userpsace.
-	 */
-	len = port->hw_streamingparams.numberoflines *
-		port->hw_streamingparams.pitch;
-
-	if (encoder_buffers < 16)
-		encoder_buffers = 16;
-	if (encoder_buffers > 512)
-		encoder_buffers = 512;
-
-	for (i = 0; i < encoder_buffers; i++) {
-
-		ubuf = saa7164_buffer_alloc_user(dev, len);
-		if (ubuf) {
-			mutex_lock(&port->dmaqueue_lock);
-			list_add_tail(&ubuf->list, &port->list_buf_free.list);
-			mutex_unlock(&port->dmaqueue_lock);
-		}
-
-	}
-
 	/* Establish encoder defaults here */
 	/* Set default TV standard */
 	port->encodernorm = saa7164_tvnorms[0];
@@ -1446,9 +1517,6 @@
 void saa7164_encoder_unregister(struct saa7164_port *port)
 {
 	struct saa7164_dev *dev = port->dev;
-	struct saa7164_buffer *buf;
-	struct saa7164_user_buffer *ubuf;
-	struct list_head *c, *n, *p, *q, *l, *v;
 
 	dprintk(DBGLVL_ENC, "%s(port=%d)\n", __func__, port->nr);
 
@@ -1464,31 +1532,6 @@
 		port->v4l_device = NULL;
 	}
 
-	/* Remove any allocated buffers */
-	mutex_lock(&port->dmaqueue_lock);
-
-	dprintk(DBGLVL_ENC, "%s(port=%d) dmaqueue\n", __func__, port->nr);
-	list_for_each_safe(c, n, &port->dmaqueue.list) {
-		buf = list_entry(c, struct saa7164_buffer, list);
-		list_del(c);
-		saa7164_buffer_dealloc(buf);
-	}
-
-	dprintk(DBGLVL_ENC, "%s(port=%d) used\n", __func__, port->nr);
-	list_for_each_safe(p, q, &port->list_buf_used.list) {
-		ubuf = list_entry(p, struct saa7164_user_buffer, list);
-		list_del(p);
-		saa7164_buffer_dealloc_user(ubuf);
-	}
-
-	dprintk(DBGLVL_ENC, "%s(port=%d) free\n", __func__, port->nr);
-	list_for_each_safe(l, v, &port->list_buf_free.list) {
-		ubuf = list_entry(l, struct saa7164_user_buffer, list);
-		list_del(l);
-		saa7164_buffer_dealloc_user(ubuf);
-	}
-
-	mutex_unlock(&port->dmaqueue_lock);
 	dprintk(DBGLVL_ENC, "%s(port=%d) done\n", __func__, port->nr);
 }
 
diff --git a/drivers/media/video/saa7164/saa7164.h b/drivers/media/video/saa7164/saa7164.h
index 71e3853..0cafe8a 100644
--- a/drivers/media/video/saa7164/saa7164.h
+++ b/drivers/media/video/saa7164/saa7164.h
@@ -389,6 +389,7 @@
 	u32 a_cc_errors;
 	u8 last_v_cc;
 	u8 last_a_cc;
+	u32 done_first_interrupt;
 };
 
 struct saa7164_dev {
@@ -546,6 +547,8 @@
 
 /* ----------------------------------------------------------- */
 
+extern unsigned int crc_checking;
+
 extern unsigned int saa_debug;
 #define dprintk(level, fmt, arg...)\
 	do { if (saa_debug & level)\