[media] au0828: continue video streaming even when no ITU-656 coming in

We need the au0828 to continue delivering frames even when the device is not
delivering video, or else applications such as tvtime will block indefinitely.

Unfortunately, the au8522 doesn't have any sort of free-running mode or "blue
screen on no video" like some other decoders.

This work was sponsored by GetWellNetwork Inc.

Signed-off-by: Devin Heitmueller <dheitmueller@kernellabs.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
diff --git a/drivers/media/video/au0828/au0828-video.c b/drivers/media/video/au0828/au0828-video.c
index 20ba591..8d11e544c 100644
--- a/drivers/media/video/au0828/au0828-video.c
+++ b/drivers/media/video/au0828/au0828-video.c
@@ -312,6 +312,9 @@
 
 	list_del(&buf->vb.queue);
 	wake_up(&buf->vb.done);
+
+	/* Reset the timer for "no video condition" */
+	mod_timer(&dev->vid_timeout, jiffies + (HZ / 10));
 }
 
 static inline void vbi_buffer_filled(struct au0828_dev *dev,
@@ -329,6 +332,9 @@
 
 	list_del(&buf->vb.queue);
 	wake_up(&buf->vb.done);
+
+	/* Reset the timer for "no video condition" */
+	mod_timer(&dev->vbi_timeout, jiffies + (HZ / 10));
 }
 
 /*
@@ -907,6 +913,50 @@
 	}
 }
 
+/* This function ensures that video frames continue to be delivered even if
+   the ITU-656 input isn't receiving any data (thereby preventing applications
+   such as tvtime from hanging) */
+void au0828_vid_buffer_timeout(unsigned long data)
+{
+	struct au0828_dev *dev = (struct au0828_dev *) data;
+	struct au0828_dmaqueue *dma_q = &dev->vidq;
+	struct au0828_buffer *buf;
+	unsigned char *vid_data;
+
+	spin_lock(&dev->slock);
+
+	buf = dev->isoc_ctl.buf;
+	if (buf != NULL) {
+		vid_data = videobuf_to_vmalloc(&buf->vb);
+		memset(vid_data, 0x00, buf->vb.size); /* Blank green frame */
+		buffer_filled(dev, dma_q, buf);
+		get_next_buf(dma_q, &buf);
+	}
+
+	spin_unlock(&dev->slock);
+}
+
+void au0828_vbi_buffer_timeout(unsigned long data)
+{
+	struct au0828_dev *dev = (struct au0828_dev *) data;
+	struct au0828_dmaqueue *dma_q = &dev->vbiq;
+	struct au0828_buffer *buf;
+	unsigned char *vbi_data;
+
+	spin_lock(&dev->slock);
+
+	buf = dev->isoc_ctl.vbi_buf;
+	if (buf != NULL) {
+		vbi_data = videobuf_to_vmalloc(&buf->vb);
+		memset(vbi_data, 0x00, buf->vb.size);
+		vbi_buffer_filled(dev, dma_q, buf);
+		vbi_get_next_buf(dma_q, &buf);
+	}
+
+	spin_unlock(&dev->slock);
+}
+
+
 static int au0828_v4l2_open(struct file *filp)
 {
 	int ret = 0;
@@ -976,6 +1026,15 @@
 				    V4L2_FIELD_SEQ_TB,
 				    sizeof(struct au0828_buffer), fh, NULL);
 
+	if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+		dev->vid_timeout.function = au0828_vid_buffer_timeout;
+		dev->vid_timeout.data = (unsigned long) dev;
+		init_timer(&dev->vid_timeout);
+	} else {
+		dev->vbi_timeout.function = au0828_vbi_buffer_timeout;
+		dev->vbi_timeout.data = (unsigned long) dev;
+		init_timer(&dev->vbi_timeout);
+	}
 
 	return ret;
 }
@@ -987,11 +1046,13 @@
 	struct au0828_dev *dev = fh->dev;
 
 	if (res_check(fh, AU0828_RESOURCE_VIDEO)) {
+		del_timer(&dev->vid_timeout);
 		videobuf_stop(&fh->vb_vidq);
 		res_free(fh, AU0828_RESOURCE_VIDEO);
 	}
 
 	if (res_check(fh, AU0828_RESOURCE_VBI)) {
+		del_timer(&dev->vbi_timeout);
 		videobuf_stop(&fh->vb_vbiq);
 		res_free(fh, AU0828_RESOURCE_VBI);
 	}