174ba9207SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
20c0d06caSMauro Carvalho Chehab /*
30c0d06caSMauro Carvalho Chehab    cx231xx_vbi.c - driver for Conexant Cx23100/101/102 USB video capture devices
40c0d06caSMauro Carvalho Chehab 
50c0d06caSMauro Carvalho Chehab    Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
60c0d06caSMauro Carvalho Chehab 	Based on cx88 driver
70c0d06caSMauro Carvalho Chehab 
80c0d06caSMauro Carvalho Chehab  */
90c0d06caSMauro Carvalho Chehab 
10589dadf2SMauro Carvalho Chehab #include "cx231xx.h"
110c0d06caSMauro Carvalho Chehab #include <linux/init.h>
120c0d06caSMauro Carvalho Chehab #include <linux/list.h>
130c0d06caSMauro Carvalho Chehab #include <linux/module.h>
140c0d06caSMauro Carvalho Chehab #include <linux/kernel.h>
150c0d06caSMauro Carvalho Chehab #include <linux/bitmap.h>
160c0d06caSMauro Carvalho Chehab #include <linux/i2c.h>
170c0d06caSMauro Carvalho Chehab #include <linux/mm.h>
180c0d06caSMauro Carvalho Chehab #include <linux/mutex.h>
190c0d06caSMauro Carvalho Chehab #include <linux/slab.h>
200c0d06caSMauro Carvalho Chehab 
210c0d06caSMauro Carvalho Chehab #include <media/v4l2-common.h>
220c0d06caSMauro Carvalho Chehab #include <media/v4l2-ioctl.h>
23d647f0b7SMauro Carvalho Chehab #include <media/drv-intf/msp3400.h>
240c0d06caSMauro Carvalho Chehab #include <media/tuner.h>
250c0d06caSMauro Carvalho Chehab 
260c0d06caSMauro Carvalho Chehab #include "cx231xx-vbi.h"
270c0d06caSMauro Carvalho Chehab 
print_err_status(struct cx231xx * dev,int packet,int status)280c0d06caSMauro Carvalho Chehab static inline void print_err_status(struct cx231xx *dev, int packet, int status)
290c0d06caSMauro Carvalho Chehab {
300c0d06caSMauro Carvalho Chehab 	char *errmsg = "Unknown";
310c0d06caSMauro Carvalho Chehab 
320c0d06caSMauro Carvalho Chehab 	switch (status) {
330c0d06caSMauro Carvalho Chehab 	case -ENOENT:
34b436e26eSColin Ian King 		errmsg = "unlinked synchronously";
350c0d06caSMauro Carvalho Chehab 		break;
360c0d06caSMauro Carvalho Chehab 	case -ECONNRESET:
37b436e26eSColin Ian King 		errmsg = "unlinked asynchronously";
380c0d06caSMauro Carvalho Chehab 		break;
390c0d06caSMauro Carvalho Chehab 	case -ENOSR:
400c0d06caSMauro Carvalho Chehab 		errmsg = "Buffer error (overrun)";
410c0d06caSMauro Carvalho Chehab 		break;
420c0d06caSMauro Carvalho Chehab 	case -EPIPE:
430c0d06caSMauro Carvalho Chehab 		errmsg = "Stalled (device not responding)";
440c0d06caSMauro Carvalho Chehab 		break;
450c0d06caSMauro Carvalho Chehab 	case -EOVERFLOW:
460c0d06caSMauro Carvalho Chehab 		errmsg = "Babble (bad cable?)";
470c0d06caSMauro Carvalho Chehab 		break;
480c0d06caSMauro Carvalho Chehab 	case -EPROTO:
490c0d06caSMauro Carvalho Chehab 		errmsg = "Bit-stuff error (bad cable?)";
500c0d06caSMauro Carvalho Chehab 		break;
510c0d06caSMauro Carvalho Chehab 	case -EILSEQ:
520c0d06caSMauro Carvalho Chehab 		errmsg = "CRC/Timeout (could be anything)";
530c0d06caSMauro Carvalho Chehab 		break;
540c0d06caSMauro Carvalho Chehab 	case -ETIME:
550c0d06caSMauro Carvalho Chehab 		errmsg = "Device does not respond";
560c0d06caSMauro Carvalho Chehab 		break;
570c0d06caSMauro Carvalho Chehab 	}
580c0d06caSMauro Carvalho Chehab 	if (packet < 0) {
59336fea92SMauro Carvalho Chehab 		dev_err(dev->dev,
60b7085c08SMauro Carvalho Chehab 			"URB status %d [%s].\n", status, errmsg);
610c0d06caSMauro Carvalho Chehab 	} else {
62336fea92SMauro Carvalho Chehab 		dev_err(dev->dev,
63b7085c08SMauro Carvalho Chehab 			"URB packet %d, status %d [%s].\n",
640c0d06caSMauro Carvalho Chehab 			packet, status, errmsg);
650c0d06caSMauro Carvalho Chehab 	}
660c0d06caSMauro Carvalho Chehab }
670c0d06caSMauro Carvalho Chehab 
680c0d06caSMauro Carvalho Chehab /*
690c0d06caSMauro Carvalho Chehab  * Controls the isoc copy of each urb packet
700c0d06caSMauro Carvalho Chehab  */
cx231xx_isoc_vbi_copy(struct cx231xx * dev,struct urb * urb)710c0d06caSMauro Carvalho Chehab static inline int cx231xx_isoc_vbi_copy(struct cx231xx *dev, struct urb *urb)
720c0d06caSMauro Carvalho Chehab {
730c0d06caSMauro Carvalho Chehab 	struct cx231xx_dmaqueue *dma_q = urb->context;
740c0d06caSMauro Carvalho Chehab 	int rc = 1;
750c0d06caSMauro Carvalho Chehab 	unsigned char *p_buffer;
760c0d06caSMauro Carvalho Chehab 	u32 bytes_parsed = 0, buffer_size = 0;
770c0d06caSMauro Carvalho Chehab 	u8 sav_eav = 0;
780c0d06caSMauro Carvalho Chehab 
790c0d06caSMauro Carvalho Chehab 	if (!dev)
800c0d06caSMauro Carvalho Chehab 		return 0;
810c0d06caSMauro Carvalho Chehab 
820c0d06caSMauro Carvalho Chehab 	if (dev->state & DEV_DISCONNECTED)
830c0d06caSMauro Carvalho Chehab 		return 0;
840c0d06caSMauro Carvalho Chehab 
850c0d06caSMauro Carvalho Chehab 	if (urb->status < 0) {
860c0d06caSMauro Carvalho Chehab 		print_err_status(dev, -1, urb->status);
870c0d06caSMauro Carvalho Chehab 		if (urb->status == -ENOENT)
880c0d06caSMauro Carvalho Chehab 			return 0;
890c0d06caSMauro Carvalho Chehab 	}
900c0d06caSMauro Carvalho Chehab 
910c0d06caSMauro Carvalho Chehab 	/* get buffer pointer and length */
920c0d06caSMauro Carvalho Chehab 	p_buffer = urb->transfer_buffer;
930c0d06caSMauro Carvalho Chehab 	buffer_size = urb->actual_length;
940c0d06caSMauro Carvalho Chehab 
950c0d06caSMauro Carvalho Chehab 	if (buffer_size > 0) {
960c0d06caSMauro Carvalho Chehab 		bytes_parsed = 0;
970c0d06caSMauro Carvalho Chehab 
980c0d06caSMauro Carvalho Chehab 		if (dma_q->is_partial_line) {
990c0d06caSMauro Carvalho Chehab 			/* Handle the case where we were working on a partial
1000c0d06caSMauro Carvalho Chehab 			   line */
1010c0d06caSMauro Carvalho Chehab 			sav_eav = dma_q->last_sav;
1020c0d06caSMauro Carvalho Chehab 		} else {
1030c0d06caSMauro Carvalho Chehab 			/* Check for a SAV/EAV overlapping the
1040c0d06caSMauro Carvalho Chehab 			   buffer boundary */
1050c0d06caSMauro Carvalho Chehab 
1060c0d06caSMauro Carvalho Chehab 			sav_eav = cx231xx_find_boundary_SAV_EAV(p_buffer,
1070c0d06caSMauro Carvalho Chehab 							  dma_q->partial_buf,
1080c0d06caSMauro Carvalho Chehab 							  &bytes_parsed);
1090c0d06caSMauro Carvalho Chehab 		}
1100c0d06caSMauro Carvalho Chehab 
1110c0d06caSMauro Carvalho Chehab 		sav_eav &= 0xF0;
1120c0d06caSMauro Carvalho Chehab 		/* Get the first line if we have some portion of an SAV/EAV from
1130c0d06caSMauro Carvalho Chehab 		   the last buffer or a partial line */
1140c0d06caSMauro Carvalho Chehab 		if (sav_eav) {
1150c0d06caSMauro Carvalho Chehab 			bytes_parsed += cx231xx_get_vbi_line(dev, dma_q,
1160c0d06caSMauro Carvalho Chehab 				sav_eav,		       /* SAV/EAV */
1170c0d06caSMauro Carvalho Chehab 				p_buffer + bytes_parsed,       /* p_buffer */
1180c0d06caSMauro Carvalho Chehab 				buffer_size - bytes_parsed);   /* buffer size */
1190c0d06caSMauro Carvalho Chehab 		}
1200c0d06caSMauro Carvalho Chehab 
1210c0d06caSMauro Carvalho Chehab 		/* Now parse data that is completely in this buffer */
1220c0d06caSMauro Carvalho Chehab 		dma_q->is_partial_line = 0;
1230c0d06caSMauro Carvalho Chehab 
1240c0d06caSMauro Carvalho Chehab 		while (bytes_parsed < buffer_size) {
1250c0d06caSMauro Carvalho Chehab 			u32 bytes_used = 0;
1260c0d06caSMauro Carvalho Chehab 
1270c0d06caSMauro Carvalho Chehab 			sav_eav = cx231xx_find_next_SAV_EAV(
1280c0d06caSMauro Carvalho Chehab 				p_buffer + bytes_parsed,	/* p_buffer */
1290c0d06caSMauro Carvalho Chehab 				buffer_size - bytes_parsed, /* buffer size */
1300c0d06caSMauro Carvalho Chehab 				&bytes_used);	/* bytes used to get SAV/EAV */
1310c0d06caSMauro Carvalho Chehab 
1320c0d06caSMauro Carvalho Chehab 			bytes_parsed += bytes_used;
1330c0d06caSMauro Carvalho Chehab 
1340c0d06caSMauro Carvalho Chehab 			sav_eav &= 0xF0;
1350c0d06caSMauro Carvalho Chehab 			if (sav_eav && (bytes_parsed < buffer_size)) {
1360c0d06caSMauro Carvalho Chehab 				bytes_parsed += cx231xx_get_vbi_line(dev,
1370c0d06caSMauro Carvalho Chehab 					dma_q, sav_eav,	/* SAV/EAV */
1380c0d06caSMauro Carvalho Chehab 					p_buffer+bytes_parsed, /* p_buffer */
1390c0d06caSMauro Carvalho Chehab 					buffer_size-bytes_parsed);/*buf size*/
1400c0d06caSMauro Carvalho Chehab 			}
1410c0d06caSMauro Carvalho Chehab 		}
1420c0d06caSMauro Carvalho Chehab 
1430c0d06caSMauro Carvalho Chehab 		/* Save the last four bytes of the buffer so we can
1440c0d06caSMauro Carvalho Chehab 		check the buffer boundary condition next time */
1450c0d06caSMauro Carvalho Chehab 		memcpy(dma_q->partial_buf, p_buffer + buffer_size - 4, 4);
1460c0d06caSMauro Carvalho Chehab 		bytes_parsed = 0;
1470c0d06caSMauro Carvalho Chehab 	}
1480c0d06caSMauro Carvalho Chehab 
1490c0d06caSMauro Carvalho Chehab 	return rc;
1500c0d06caSMauro Carvalho Chehab }
1510c0d06caSMauro Carvalho Chehab 
1520c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------
1530c0d06caSMauro Carvalho Chehab 	Vbi buf operations
1540c0d06caSMauro Carvalho Chehab    ------------------------------------------------------------------*/
1550c0d06caSMauro Carvalho Chehab 
vbi_queue_setup(struct vb2_queue * vq,unsigned int * nbuffers,unsigned int * nplanes,unsigned int sizes[],struct device * alloc_devs[])1567c617138SHans Verkuil static int vbi_queue_setup(struct vb2_queue *vq,
1577c617138SHans Verkuil 			   unsigned int *nbuffers, unsigned int *nplanes,
1587c617138SHans Verkuil 			   unsigned int sizes[], struct device *alloc_devs[])
1590c0d06caSMauro Carvalho Chehab {
1607c617138SHans Verkuil 	struct cx231xx *dev = vb2_get_drv_priv(vq);
1610c0d06caSMauro Carvalho Chehab 	u32 height = 0;
1620c0d06caSMauro Carvalho Chehab 
1630c0d06caSMauro Carvalho Chehab 	height = ((dev->norm & V4L2_STD_625_50) ?
1640c0d06caSMauro Carvalho Chehab 		  PAL_VBI_LINES : NTSC_VBI_LINES);
1650c0d06caSMauro Carvalho Chehab 
1667c617138SHans Verkuil 	*nplanes = 1;
1677c617138SHans Verkuil 	sizes[0] = (dev->width * height * 2 * 2);
1680c0d06caSMauro Carvalho Chehab 	return 0;
1690c0d06caSMauro Carvalho Chehab }
1700c0d06caSMauro Carvalho Chehab 
1710c0d06caSMauro Carvalho Chehab /* This is called *without* dev->slock held; please keep it that way */
vbi_buf_prepare(struct vb2_buffer * vb)1727c617138SHans Verkuil static int vbi_buf_prepare(struct vb2_buffer *vb)
1730c0d06caSMauro Carvalho Chehab {
1747c617138SHans Verkuil 	struct cx231xx *dev = vb2_get_drv_priv(vb->vb2_queue);
1750c0d06caSMauro Carvalho Chehab 	u32 height = 0;
1767c617138SHans Verkuil 	u32 size;
1770c0d06caSMauro Carvalho Chehab 
1780c0d06caSMauro Carvalho Chehab 	height = ((dev->norm & V4L2_STD_625_50) ?
1790c0d06caSMauro Carvalho Chehab 		  PAL_VBI_LINES : NTSC_VBI_LINES);
1807c617138SHans Verkuil 	size = ((dev->width << 1) * height * 2);
1810c0d06caSMauro Carvalho Chehab 
1827c617138SHans Verkuil 	if (vb2_plane_size(vb, 0) < size)
1830c0d06caSMauro Carvalho Chehab 		return -EINVAL;
1847c617138SHans Verkuil 	vb2_set_plane_payload(vb, 0, size);
1857c617138SHans Verkuil 	return 0;
1860c0d06caSMauro Carvalho Chehab }
1870c0d06caSMauro Carvalho Chehab 
vbi_buf_queue(struct vb2_buffer * vb)1887c617138SHans Verkuil static void vbi_buf_queue(struct vb2_buffer *vb)
1897c617138SHans Verkuil {
1907c617138SHans Verkuil 	struct cx231xx *dev = vb2_get_drv_priv(vb->vb2_queue);
1917c617138SHans Verkuil 	struct cx231xx_buffer *buf =
1927c617138SHans Verkuil 	    container_of(vb, struct cx231xx_buffer, vb.vb2_buf);
1937c617138SHans Verkuil 	struct cx231xx_dmaqueue *vidq = &dev->vbi_mode.vidq;
1947c617138SHans Verkuil 	unsigned long flags;
1950c0d06caSMauro Carvalho Chehab 
1967c617138SHans Verkuil 	spin_lock_irqsave(&dev->vbi_mode.slock, flags);
1977c617138SHans Verkuil 	list_add_tail(&buf->list, &vidq->active);
1987c617138SHans Verkuil 	spin_unlock_irqrestore(&dev->vbi_mode.slock, flags);
1997c617138SHans Verkuil }
2007c617138SHans Verkuil 
return_all_buffers(struct cx231xx * dev,enum vb2_buffer_state state)2017c617138SHans Verkuil static void return_all_buffers(struct cx231xx *dev,
2027c617138SHans Verkuil 			       enum vb2_buffer_state state)
2037c617138SHans Verkuil {
2047c617138SHans Verkuil 	struct cx231xx_dmaqueue *vidq = &dev->vbi_mode.vidq;
2057c617138SHans Verkuil 	struct cx231xx_buffer *buf, *node;
2067c617138SHans Verkuil 	unsigned long flags;
2077c617138SHans Verkuil 
2087c617138SHans Verkuil 	spin_lock_irqsave(&dev->vbi_mode.slock, flags);
2097c617138SHans Verkuil 	dev->vbi_mode.bulk_ctl.buf = NULL;
2107c617138SHans Verkuil 	list_for_each_entry_safe(buf, node, &vidq->active, list) {
2117c617138SHans Verkuil 		list_del(&buf->list);
2127c617138SHans Verkuil 		vb2_buffer_done(&buf->vb.vb2_buf, state);
2137c617138SHans Verkuil 	}
2147c617138SHans Verkuil 	spin_unlock_irqrestore(&dev->vbi_mode.slock, flags);
2157c617138SHans Verkuil }
2167c617138SHans Verkuil 
vbi_start_streaming(struct vb2_queue * vq,unsigned int count)2177c617138SHans Verkuil static int vbi_start_streaming(struct vb2_queue *vq, unsigned int count)
2187c617138SHans Verkuil {
2197c617138SHans Verkuil 	struct cx231xx *dev = vb2_get_drv_priv(vq);
2207c617138SHans Verkuil 	struct cx231xx_dmaqueue *vidq = &dev->vbi_mode.vidq;
2217c617138SHans Verkuil 	int ret;
2227c617138SHans Verkuil 
2237c617138SHans Verkuil 	vidq->sequence = 0;
2247c617138SHans Verkuil 	ret = cx231xx_init_vbi_isoc(dev, CX231XX_NUM_VBI_PACKETS,
2250c0d06caSMauro Carvalho Chehab 				    CX231XX_NUM_VBI_BUFS,
2260c0d06caSMauro Carvalho Chehab 				    dev->vbi_mode.alt_max_pkt_size[0],
2270c0d06caSMauro Carvalho Chehab 				    cx231xx_isoc_vbi_copy);
2287c617138SHans Verkuil 	if (ret)
2297c617138SHans Verkuil 		return_all_buffers(dev, VB2_BUF_STATE_QUEUED);
2307c617138SHans Verkuil 	return ret;
2310c0d06caSMauro Carvalho Chehab }
2320c0d06caSMauro Carvalho Chehab 
vbi_stop_streaming(struct vb2_queue * vq)2337c617138SHans Verkuil static void vbi_stop_streaming(struct vb2_queue *vq)
2340c0d06caSMauro Carvalho Chehab {
2357c617138SHans Verkuil 	struct cx231xx *dev = vb2_get_drv_priv(vq);
2360c0d06caSMauro Carvalho Chehab 
2377c617138SHans Verkuil 	return_all_buffers(dev, VB2_BUF_STATE_ERROR);
2380c0d06caSMauro Carvalho Chehab }
2390c0d06caSMauro Carvalho Chehab 
2407c617138SHans Verkuil struct vb2_ops cx231xx_vbi_qops = {
2417c617138SHans Verkuil 	.queue_setup = vbi_queue_setup,
2427c617138SHans Verkuil 	.buf_prepare = vbi_buf_prepare,
2437c617138SHans Verkuil 	.buf_queue = vbi_buf_queue,
2447c617138SHans Verkuil 	.start_streaming = vbi_start_streaming,
2457c617138SHans Verkuil 	.stop_streaming = vbi_stop_streaming,
2467c617138SHans Verkuil 	.wait_prepare = vb2_ops_wait_prepare,
2477c617138SHans Verkuil 	.wait_finish = vb2_ops_wait_finish,
2480c0d06caSMauro Carvalho Chehab };
2490c0d06caSMauro Carvalho Chehab 
2500c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------
2510c0d06caSMauro Carvalho Chehab 	URB control
2520c0d06caSMauro Carvalho Chehab    ------------------------------------------------------------------*/
2530c0d06caSMauro Carvalho Chehab 
2540c0d06caSMauro Carvalho Chehab /*
2550c0d06caSMauro Carvalho Chehab  * IRQ callback, called by URB callback
2560c0d06caSMauro Carvalho Chehab  */
cx231xx_irq_vbi_callback(struct urb * urb)2570c0d06caSMauro Carvalho Chehab static void cx231xx_irq_vbi_callback(struct urb *urb)
2580c0d06caSMauro Carvalho Chehab {
2590c0d06caSMauro Carvalho Chehab 	struct cx231xx_dmaqueue *dma_q = urb->context;
2600c0d06caSMauro Carvalho Chehab 	struct cx231xx_video_mode *vmode =
2610c0d06caSMauro Carvalho Chehab 	    container_of(dma_q, struct cx231xx_video_mode, vidq);
2620c0d06caSMauro Carvalho Chehab 	struct cx231xx *dev = container_of(vmode, struct cx231xx, vbi_mode);
263aa53bf0bSSebastian Andrzej Siewior 	unsigned long flags;
2640c0d06caSMauro Carvalho Chehab 
2650c0d06caSMauro Carvalho Chehab 	switch (urb->status) {
2660c0d06caSMauro Carvalho Chehab 	case 0:		/* success */
2670c0d06caSMauro Carvalho Chehab 	case -ETIMEDOUT:	/* NAK */
2680c0d06caSMauro Carvalho Chehab 		break;
2690c0d06caSMauro Carvalho Chehab 	case -ECONNRESET:	/* kill */
2700c0d06caSMauro Carvalho Chehab 	case -ENOENT:
2710c0d06caSMauro Carvalho Chehab 	case -ESHUTDOWN:
2720c0d06caSMauro Carvalho Chehab 		return;
2730c0d06caSMauro Carvalho Chehab 	default:		/* error */
274336fea92SMauro Carvalho Chehab 		dev_err(dev->dev,
275854bb4ecSColin Ian King 			"urb completion error %d.\n", urb->status);
2760c0d06caSMauro Carvalho Chehab 		break;
2770c0d06caSMauro Carvalho Chehab 	}
2780c0d06caSMauro Carvalho Chehab 
2790c0d06caSMauro Carvalho Chehab 	/* Copy data from URB */
280aa53bf0bSSebastian Andrzej Siewior 	spin_lock_irqsave(&dev->vbi_mode.slock, flags);
2810c0d06caSMauro Carvalho Chehab 	dev->vbi_mode.bulk_ctl.bulk_copy(dev, urb);
282aa53bf0bSSebastian Andrzej Siewior 	spin_unlock_irqrestore(&dev->vbi_mode.slock, flags);
2830c0d06caSMauro Carvalho Chehab 
2840c0d06caSMauro Carvalho Chehab 	/* Reset status */
2850c0d06caSMauro Carvalho Chehab 	urb->status = 0;
2860c0d06caSMauro Carvalho Chehab 
2870c0d06caSMauro Carvalho Chehab 	urb->status = usb_submit_urb(urb, GFP_ATOMIC);
2880c0d06caSMauro Carvalho Chehab 	if (urb->status) {
289336fea92SMauro Carvalho Chehab 		dev_err(dev->dev, "urb resubmit failed (error=%i)\n",
2900c0d06caSMauro Carvalho Chehab 			urb->status);
2910c0d06caSMauro Carvalho Chehab 	}
2920c0d06caSMauro Carvalho Chehab }
2930c0d06caSMauro Carvalho Chehab 
2940c0d06caSMauro Carvalho Chehab /*
2950c0d06caSMauro Carvalho Chehab  * Stop and Deallocate URBs
2960c0d06caSMauro Carvalho Chehab  */
cx231xx_uninit_vbi_isoc(struct cx231xx * dev)2970c0d06caSMauro Carvalho Chehab void cx231xx_uninit_vbi_isoc(struct cx231xx *dev)
2980c0d06caSMauro Carvalho Chehab {
2990c0d06caSMauro Carvalho Chehab 	struct urb *urb;
3000c0d06caSMauro Carvalho Chehab 	int i;
3010c0d06caSMauro Carvalho Chehab 
302336fea92SMauro Carvalho Chehab 	dev_dbg(dev->dev, "called cx231xx_uninit_vbi_isoc\n");
3030c0d06caSMauro Carvalho Chehab 
3040c0d06caSMauro Carvalho Chehab 	dev->vbi_mode.bulk_ctl.nfields = -1;
3050c0d06caSMauro Carvalho Chehab 	for (i = 0; i < dev->vbi_mode.bulk_ctl.num_bufs; i++) {
3060c0d06caSMauro Carvalho Chehab 		urb = dev->vbi_mode.bulk_ctl.urb[i];
3070c0d06caSMauro Carvalho Chehab 		if (urb) {
3080c0d06caSMauro Carvalho Chehab 			if (!irqs_disabled())
3090c0d06caSMauro Carvalho Chehab 				usb_kill_urb(urb);
3100c0d06caSMauro Carvalho Chehab 			else
3110c0d06caSMauro Carvalho Chehab 				usb_unlink_urb(urb);
3120c0d06caSMauro Carvalho Chehab 
3130c0d06caSMauro Carvalho Chehab 			if (dev->vbi_mode.bulk_ctl.transfer_buffer[i]) {
3140c0d06caSMauro Carvalho Chehab 
3150c0d06caSMauro Carvalho Chehab 				kfree(dev->vbi_mode.bulk_ctl.
3160c0d06caSMauro Carvalho Chehab 				      transfer_buffer[i]);
3170c0d06caSMauro Carvalho Chehab 				dev->vbi_mode.bulk_ctl.transfer_buffer[i] =
3180c0d06caSMauro Carvalho Chehab 				    NULL;
3190c0d06caSMauro Carvalho Chehab 			}
3200c0d06caSMauro Carvalho Chehab 			usb_free_urb(urb);
3210c0d06caSMauro Carvalho Chehab 			dev->vbi_mode.bulk_ctl.urb[i] = NULL;
3220c0d06caSMauro Carvalho Chehab 		}
3230c0d06caSMauro Carvalho Chehab 		dev->vbi_mode.bulk_ctl.transfer_buffer[i] = NULL;
3240c0d06caSMauro Carvalho Chehab 	}
3250c0d06caSMauro Carvalho Chehab 
3260c0d06caSMauro Carvalho Chehab 	kfree(dev->vbi_mode.bulk_ctl.urb);
3270c0d06caSMauro Carvalho Chehab 	kfree(dev->vbi_mode.bulk_ctl.transfer_buffer);
3280c0d06caSMauro Carvalho Chehab 
3290c0d06caSMauro Carvalho Chehab 	dev->vbi_mode.bulk_ctl.urb = NULL;
3300c0d06caSMauro Carvalho Chehab 	dev->vbi_mode.bulk_ctl.transfer_buffer = NULL;
3310c0d06caSMauro Carvalho Chehab 	dev->vbi_mode.bulk_ctl.num_bufs = 0;
3320c0d06caSMauro Carvalho Chehab 
3330c0d06caSMauro Carvalho Chehab 	cx231xx_capture_start(dev, 0, Vbi);
3340c0d06caSMauro Carvalho Chehab }
3350c0d06caSMauro Carvalho Chehab EXPORT_SYMBOL_GPL(cx231xx_uninit_vbi_isoc);
3360c0d06caSMauro Carvalho Chehab 
3370c0d06caSMauro Carvalho Chehab /*
3380c0d06caSMauro Carvalho Chehab  * Allocate URBs and start IRQ
3390c0d06caSMauro Carvalho Chehab  */
cx231xx_init_vbi_isoc(struct cx231xx * dev,int max_packets,int num_bufs,int max_pkt_size,int (* bulk_copy)(struct cx231xx * dev,struct urb * urb))3400c0d06caSMauro Carvalho Chehab int cx231xx_init_vbi_isoc(struct cx231xx *dev, int max_packets,
3410c0d06caSMauro Carvalho Chehab 			  int num_bufs, int max_pkt_size,
3420c0d06caSMauro Carvalho Chehab 			  int (*bulk_copy) (struct cx231xx *dev,
3430c0d06caSMauro Carvalho Chehab 					    struct urb *urb))
3440c0d06caSMauro Carvalho Chehab {
3450c0d06caSMauro Carvalho Chehab 	struct cx231xx_dmaqueue *dma_q = &dev->vbi_mode.vidq;
3460c0d06caSMauro Carvalho Chehab 	int i;
3470c0d06caSMauro Carvalho Chehab 	int sb_size, pipe;
3480c0d06caSMauro Carvalho Chehab 	struct urb *urb;
3490c0d06caSMauro Carvalho Chehab 	int rc;
3500c0d06caSMauro Carvalho Chehab 
351336fea92SMauro Carvalho Chehab 	dev_dbg(dev->dev, "called cx231xx_vbi_isoc\n");
3520c0d06caSMauro Carvalho Chehab 
3530c0d06caSMauro Carvalho Chehab 	/* De-allocates all pending stuff */
3540c0d06caSMauro Carvalho Chehab 	cx231xx_uninit_vbi_isoc(dev);
3550c0d06caSMauro Carvalho Chehab 
3560c0d06caSMauro Carvalho Chehab 	/* clear if any halt */
3570c0d06caSMauro Carvalho Chehab 	usb_clear_halt(dev->udev,
3580c0d06caSMauro Carvalho Chehab 		       usb_rcvbulkpipe(dev->udev,
3590c0d06caSMauro Carvalho Chehab 				       dev->vbi_mode.end_point_addr));
3600c0d06caSMauro Carvalho Chehab 
3610c0d06caSMauro Carvalho Chehab 	dev->vbi_mode.bulk_ctl.bulk_copy = bulk_copy;
3620c0d06caSMauro Carvalho Chehab 	dev->vbi_mode.bulk_ctl.num_bufs = num_bufs;
3630c0d06caSMauro Carvalho Chehab 	dma_q->pos = 0;
3640c0d06caSMauro Carvalho Chehab 	dma_q->is_partial_line = 0;
3650c0d06caSMauro Carvalho Chehab 	dma_q->last_sav = 0;
3660c0d06caSMauro Carvalho Chehab 	dma_q->current_field = -1;
3670c0d06caSMauro Carvalho Chehab 	dma_q->bytes_left_in_line = dev->width << 1;
3680c0d06caSMauro Carvalho Chehab 	dma_q->lines_per_field = ((dev->norm & V4L2_STD_625_50) ?
3690c0d06caSMauro Carvalho Chehab 				  PAL_VBI_LINES : NTSC_VBI_LINES);
3700c0d06caSMauro Carvalho Chehab 	dma_q->lines_completed = 0;
3710c0d06caSMauro Carvalho Chehab 	for (i = 0; i < 8; i++)
3720c0d06caSMauro Carvalho Chehab 		dma_q->partial_buf[i] = 0;
3730c0d06caSMauro Carvalho Chehab 
3746396bb22SKees Cook 	dev->vbi_mode.bulk_ctl.urb = kcalloc(num_bufs, sizeof(void *),
3750c0d06caSMauro Carvalho Chehab 					     GFP_KERNEL);
3760c0d06caSMauro Carvalho Chehab 	if (!dev->vbi_mode.bulk_ctl.urb) {
377336fea92SMauro Carvalho Chehab 		dev_err(dev->dev,
378b7085c08SMauro Carvalho Chehab 			"cannot alloc memory for usb buffers\n");
3790c0d06caSMauro Carvalho Chehab 		return -ENOMEM;
3800c0d06caSMauro Carvalho Chehab 	}
3810c0d06caSMauro Carvalho Chehab 
3820c0d06caSMauro Carvalho Chehab 	dev->vbi_mode.bulk_ctl.transfer_buffer =
3836396bb22SKees Cook 	    kcalloc(num_bufs, sizeof(void *), GFP_KERNEL);
3840c0d06caSMauro Carvalho Chehab 	if (!dev->vbi_mode.bulk_ctl.transfer_buffer) {
385336fea92SMauro Carvalho Chehab 		dev_err(dev->dev,
386b7085c08SMauro Carvalho Chehab 			"cannot allocate memory for usbtransfer\n");
3870c0d06caSMauro Carvalho Chehab 		kfree(dev->vbi_mode.bulk_ctl.urb);
3880c0d06caSMauro Carvalho Chehab 		return -ENOMEM;
3890c0d06caSMauro Carvalho Chehab 	}
3900c0d06caSMauro Carvalho Chehab 
3910c0d06caSMauro Carvalho Chehab 	dev->vbi_mode.bulk_ctl.max_pkt_size = max_pkt_size;
3920c0d06caSMauro Carvalho Chehab 	dev->vbi_mode.bulk_ctl.buf = NULL;
3930c0d06caSMauro Carvalho Chehab 
3940c0d06caSMauro Carvalho Chehab 	sb_size = max_packets * dev->vbi_mode.bulk_ctl.max_pkt_size;
3950c0d06caSMauro Carvalho Chehab 
3960c0d06caSMauro Carvalho Chehab 	/* allocate urbs and transfer buffers */
3970c0d06caSMauro Carvalho Chehab 	for (i = 0; i < dev->vbi_mode.bulk_ctl.num_bufs; i++) {
3980c0d06caSMauro Carvalho Chehab 
3990c0d06caSMauro Carvalho Chehab 		urb = usb_alloc_urb(0, GFP_KERNEL);
4000c0d06caSMauro Carvalho Chehab 		if (!urb) {
4010c0d06caSMauro Carvalho Chehab 			cx231xx_uninit_vbi_isoc(dev);
4020c0d06caSMauro Carvalho Chehab 			return -ENOMEM;
4030c0d06caSMauro Carvalho Chehab 		}
4040c0d06caSMauro Carvalho Chehab 		dev->vbi_mode.bulk_ctl.urb[i] = urb;
4050c0d06caSMauro Carvalho Chehab 		urb->transfer_flags = 0;
4060c0d06caSMauro Carvalho Chehab 
4070c0d06caSMauro Carvalho Chehab 		dev->vbi_mode.bulk_ctl.transfer_buffer[i] =
4080c0d06caSMauro Carvalho Chehab 		    kzalloc(sb_size, GFP_KERNEL);
4090c0d06caSMauro Carvalho Chehab 		if (!dev->vbi_mode.bulk_ctl.transfer_buffer[i]) {
410336fea92SMauro Carvalho Chehab 			dev_err(dev->dev,
4118b77d1f9SThomas Gleixner 				"unable to allocate %i bytes for transfer buffer %i\n",
4128b77d1f9SThomas Gleixner 				sb_size, i);
4130c0d06caSMauro Carvalho Chehab 			cx231xx_uninit_vbi_isoc(dev);
4140c0d06caSMauro Carvalho Chehab 			return -ENOMEM;
4150c0d06caSMauro Carvalho Chehab 		}
4160c0d06caSMauro Carvalho Chehab 
4170c0d06caSMauro Carvalho Chehab 		pipe = usb_rcvbulkpipe(dev->udev, dev->vbi_mode.end_point_addr);
4180c0d06caSMauro Carvalho Chehab 		usb_fill_bulk_urb(urb, dev->udev, pipe,
4190c0d06caSMauro Carvalho Chehab 				  dev->vbi_mode.bulk_ctl.transfer_buffer[i],
4200c0d06caSMauro Carvalho Chehab 				  sb_size, cx231xx_irq_vbi_callback, dma_q);
4210c0d06caSMauro Carvalho Chehab 	}
4220c0d06caSMauro Carvalho Chehab 
4230c0d06caSMauro Carvalho Chehab 	init_waitqueue_head(&dma_q->wq);
4240c0d06caSMauro Carvalho Chehab 
4250c0d06caSMauro Carvalho Chehab 	/* submit urbs and enables IRQ */
4260c0d06caSMauro Carvalho Chehab 	for (i = 0; i < dev->vbi_mode.bulk_ctl.num_bufs; i++) {
4270c0d06caSMauro Carvalho Chehab 		rc = usb_submit_urb(dev->vbi_mode.bulk_ctl.urb[i], GFP_ATOMIC);
4280c0d06caSMauro Carvalho Chehab 		if (rc) {
429336fea92SMauro Carvalho Chehab 			dev_err(dev->dev,
430b7085c08SMauro Carvalho Chehab 				"submit of urb %i failed (error=%i)\n", i, rc);
4310c0d06caSMauro Carvalho Chehab 			cx231xx_uninit_vbi_isoc(dev);
4320c0d06caSMauro Carvalho Chehab 			return rc;
4330c0d06caSMauro Carvalho Chehab 		}
4340c0d06caSMauro Carvalho Chehab 	}
4350c0d06caSMauro Carvalho Chehab 
4360c0d06caSMauro Carvalho Chehab 	cx231xx_capture_start(dev, 1, Vbi);
4370c0d06caSMauro Carvalho Chehab 
4380c0d06caSMauro Carvalho Chehab 	return 0;
4390c0d06caSMauro Carvalho Chehab }
4400c0d06caSMauro Carvalho Chehab EXPORT_SYMBOL_GPL(cx231xx_init_vbi_isoc);
4410c0d06caSMauro Carvalho Chehab 
cx231xx_get_vbi_line(struct cx231xx * dev,struct cx231xx_dmaqueue * dma_q,u8 sav_eav,u8 * p_buffer,u32 buffer_size)4420c0d06caSMauro Carvalho Chehab u32 cx231xx_get_vbi_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
4430c0d06caSMauro Carvalho Chehab 			 u8 sav_eav, u8 *p_buffer, u32 buffer_size)
4440c0d06caSMauro Carvalho Chehab {
4450c0d06caSMauro Carvalho Chehab 	u32 bytes_copied = 0;
4460c0d06caSMauro Carvalho Chehab 	int current_field = -1;
4470c0d06caSMauro Carvalho Chehab 
4480c0d06caSMauro Carvalho Chehab 	switch (sav_eav) {
4490c0d06caSMauro Carvalho Chehab 
4500c0d06caSMauro Carvalho Chehab 	case SAV_VBI_FIELD1:
4510c0d06caSMauro Carvalho Chehab 		current_field = 1;
4520c0d06caSMauro Carvalho Chehab 		break;
4530c0d06caSMauro Carvalho Chehab 
4540c0d06caSMauro Carvalho Chehab 	case SAV_VBI_FIELD2:
4550c0d06caSMauro Carvalho Chehab 		current_field = 2;
4560c0d06caSMauro Carvalho Chehab 		break;
4570c0d06caSMauro Carvalho Chehab 	default:
4580c0d06caSMauro Carvalho Chehab 		break;
4590c0d06caSMauro Carvalho Chehab 	}
4600c0d06caSMauro Carvalho Chehab 
4610c0d06caSMauro Carvalho Chehab 	if (current_field < 0)
4620c0d06caSMauro Carvalho Chehab 		return bytes_copied;
4630c0d06caSMauro Carvalho Chehab 
4640c0d06caSMauro Carvalho Chehab 	dma_q->last_sav = sav_eav;
4650c0d06caSMauro Carvalho Chehab 
4660c0d06caSMauro Carvalho Chehab 	bytes_copied =
4670c0d06caSMauro Carvalho Chehab 	    cx231xx_copy_vbi_line(dev, dma_q, p_buffer, buffer_size,
4680c0d06caSMauro Carvalho Chehab 				  current_field);
4690c0d06caSMauro Carvalho Chehab 
4700c0d06caSMauro Carvalho Chehab 	return bytes_copied;
4710c0d06caSMauro Carvalho Chehab }
4720c0d06caSMauro Carvalho Chehab 
4730c0d06caSMauro Carvalho Chehab /*
4740c0d06caSMauro Carvalho Chehab  * Announces that a buffer were filled and request the next
4750c0d06caSMauro Carvalho Chehab  */
vbi_buffer_filled(struct cx231xx * dev,struct cx231xx_dmaqueue * dma_q,struct cx231xx_buffer * buf)4760c0d06caSMauro Carvalho Chehab static inline void vbi_buffer_filled(struct cx231xx *dev,
4770c0d06caSMauro Carvalho Chehab 				     struct cx231xx_dmaqueue *dma_q,
4780c0d06caSMauro Carvalho Chehab 				     struct cx231xx_buffer *buf)
4790c0d06caSMauro Carvalho Chehab {
4800c0d06caSMauro Carvalho Chehab 	/* Advice that buffer was filled */
4817c617138SHans Verkuil 	/* dev_dbg(dev->dev, "[%p/%d] wakeup\n", buf, buf->vb.index); */
4820c0d06caSMauro Carvalho Chehab 
4837c617138SHans Verkuil 	buf->vb.sequence = dma_q->sequence++;
4847c617138SHans Verkuil 	buf->vb.vb2_buf.timestamp = ktime_get_ns();
4850c0d06caSMauro Carvalho Chehab 
4860c0d06caSMauro Carvalho Chehab 	dev->vbi_mode.bulk_ctl.buf = NULL;
4870c0d06caSMauro Carvalho Chehab 
4887c617138SHans Verkuil 	list_del(&buf->list);
4897c617138SHans Verkuil 	vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
4900c0d06caSMauro Carvalho Chehab }
4910c0d06caSMauro Carvalho Chehab 
cx231xx_copy_vbi_line(struct cx231xx * dev,struct cx231xx_dmaqueue * dma_q,u8 * p_line,u32 length,int field_number)4920c0d06caSMauro Carvalho Chehab u32 cx231xx_copy_vbi_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
4930c0d06caSMauro Carvalho Chehab 			  u8 *p_line, u32 length, int field_number)
4940c0d06caSMauro Carvalho Chehab {
4950c0d06caSMauro Carvalho Chehab 	u32 bytes_to_copy;
4960c0d06caSMauro Carvalho Chehab 	struct cx231xx_buffer *buf;
4970c0d06caSMauro Carvalho Chehab 	u32 _line_size = dev->width * 2;
4980c0d06caSMauro Carvalho Chehab 
4990c0d06caSMauro Carvalho Chehab 	if (dma_q->current_field == -1) {
5000c0d06caSMauro Carvalho Chehab 		/* Just starting up */
5010c0d06caSMauro Carvalho Chehab 		cx231xx_reset_vbi_buffer(dev, dma_q);
5020c0d06caSMauro Carvalho Chehab 	}
5030c0d06caSMauro Carvalho Chehab 
5040c0d06caSMauro Carvalho Chehab 	if (dma_q->current_field != field_number)
5050c0d06caSMauro Carvalho Chehab 		dma_q->lines_completed = 0;
5060c0d06caSMauro Carvalho Chehab 
5070c0d06caSMauro Carvalho Chehab 	/* get the buffer pointer */
5080c0d06caSMauro Carvalho Chehab 	buf = dev->vbi_mode.bulk_ctl.buf;
5090c0d06caSMauro Carvalho Chehab 
5100c0d06caSMauro Carvalho Chehab 	/* Remember the field number for next time */
5110c0d06caSMauro Carvalho Chehab 	dma_q->current_field = field_number;
5120c0d06caSMauro Carvalho Chehab 
5130c0d06caSMauro Carvalho Chehab 	bytes_to_copy = dma_q->bytes_left_in_line;
5140c0d06caSMauro Carvalho Chehab 	if (bytes_to_copy > length)
5150c0d06caSMauro Carvalho Chehab 		bytes_to_copy = length;
5160c0d06caSMauro Carvalho Chehab 
5170c0d06caSMauro Carvalho Chehab 	if (dma_q->lines_completed >= dma_q->lines_per_field) {
5180c0d06caSMauro Carvalho Chehab 		dma_q->bytes_left_in_line -= bytes_to_copy;
5190c0d06caSMauro Carvalho Chehab 		dma_q->is_partial_line =
5200c0d06caSMauro Carvalho Chehab 		    (dma_q->bytes_left_in_line == 0) ? 0 : 1;
5210c0d06caSMauro Carvalho Chehab 		return 0;
5220c0d06caSMauro Carvalho Chehab 	}
5230c0d06caSMauro Carvalho Chehab 
5240c0d06caSMauro Carvalho Chehab 	dma_q->is_partial_line = 1;
5250c0d06caSMauro Carvalho Chehab 
5260c0d06caSMauro Carvalho Chehab 	/* If we don't have a buffer, just return the number of bytes we would
5270c0d06caSMauro Carvalho Chehab 	   have copied if we had a buffer. */
5280c0d06caSMauro Carvalho Chehab 	if (!buf) {
5290c0d06caSMauro Carvalho Chehab 		dma_q->bytes_left_in_line -= bytes_to_copy;
5300c0d06caSMauro Carvalho Chehab 		dma_q->is_partial_line =
5310c0d06caSMauro Carvalho Chehab 		    (dma_q->bytes_left_in_line == 0) ? 0 : 1;
5320c0d06caSMauro Carvalho Chehab 		return bytes_to_copy;
5330c0d06caSMauro Carvalho Chehab 	}
5340c0d06caSMauro Carvalho Chehab 
5350c0d06caSMauro Carvalho Chehab 	/* copy the data to video buffer */
5360c0d06caSMauro Carvalho Chehab 	cx231xx_do_vbi_copy(dev, dma_q, p_line, bytes_to_copy);
5370c0d06caSMauro Carvalho Chehab 
5380c0d06caSMauro Carvalho Chehab 	dma_q->pos += bytes_to_copy;
5390c0d06caSMauro Carvalho Chehab 	dma_q->bytes_left_in_line -= bytes_to_copy;
5400c0d06caSMauro Carvalho Chehab 
5410c0d06caSMauro Carvalho Chehab 	if (dma_q->bytes_left_in_line == 0) {
5420c0d06caSMauro Carvalho Chehab 
5430c0d06caSMauro Carvalho Chehab 		dma_q->bytes_left_in_line = _line_size;
5440c0d06caSMauro Carvalho Chehab 		dma_q->lines_completed++;
5450c0d06caSMauro Carvalho Chehab 		dma_q->is_partial_line = 0;
5460c0d06caSMauro Carvalho Chehab 
5470c0d06caSMauro Carvalho Chehab 		if (cx231xx_is_vbi_buffer_done(dev, dma_q) && buf) {
5480c0d06caSMauro Carvalho Chehab 
5490c0d06caSMauro Carvalho Chehab 			vbi_buffer_filled(dev, dma_q, buf);
5500c0d06caSMauro Carvalho Chehab 
5510c0d06caSMauro Carvalho Chehab 			dma_q->pos = 0;
5520c0d06caSMauro Carvalho Chehab 			dma_q->lines_completed = 0;
5530c0d06caSMauro Carvalho Chehab 			cx231xx_reset_vbi_buffer(dev, dma_q);
5540c0d06caSMauro Carvalho Chehab 		}
5550c0d06caSMauro Carvalho Chehab 	}
5560c0d06caSMauro Carvalho Chehab 
5570c0d06caSMauro Carvalho Chehab 	return bytes_to_copy;
5580c0d06caSMauro Carvalho Chehab }
5590c0d06caSMauro Carvalho Chehab 
5600c0d06caSMauro Carvalho Chehab /*
561*f068a6ceSHans Verkuil  * generic routine to get the next available buffer
5620c0d06caSMauro Carvalho Chehab  */
get_next_vbi_buf(struct cx231xx_dmaqueue * dma_q,struct cx231xx_buffer ** buf)5630c0d06caSMauro Carvalho Chehab static inline void get_next_vbi_buf(struct cx231xx_dmaqueue *dma_q,
5640c0d06caSMauro Carvalho Chehab 				    struct cx231xx_buffer **buf)
5650c0d06caSMauro Carvalho Chehab {
5660c0d06caSMauro Carvalho Chehab 	struct cx231xx_video_mode *vmode =
5670c0d06caSMauro Carvalho Chehab 	    container_of(dma_q, struct cx231xx_video_mode, vidq);
5680c0d06caSMauro Carvalho Chehab 	struct cx231xx *dev = container_of(vmode, struct cx231xx, vbi_mode);
5690c0d06caSMauro Carvalho Chehab 	char *outp;
5700c0d06caSMauro Carvalho Chehab 
5710c0d06caSMauro Carvalho Chehab 	if (list_empty(&dma_q->active)) {
572336fea92SMauro Carvalho Chehab 		dev_err(dev->dev, "No active queue to serve\n");
5730c0d06caSMauro Carvalho Chehab 		dev->vbi_mode.bulk_ctl.buf = NULL;
5740c0d06caSMauro Carvalho Chehab 		*buf = NULL;
5750c0d06caSMauro Carvalho Chehab 		return;
5760c0d06caSMauro Carvalho Chehab 	}
5770c0d06caSMauro Carvalho Chehab 
5780c0d06caSMauro Carvalho Chehab 	/* Get the next buffer */
5797c617138SHans Verkuil 	*buf = list_entry(dma_q->active.next, struct cx231xx_buffer, list);
5800c0d06caSMauro Carvalho Chehab 
5810c0d06caSMauro Carvalho Chehab 	/* Cleans up buffer - Useful for testing for frame/URB loss */
5827c617138SHans Verkuil 	outp = vb2_plane_vaddr(&(*buf)->vb.vb2_buf, 0);
5837c617138SHans Verkuil 	memset(outp, 0, vb2_plane_size(&(*buf)->vb.vb2_buf, 0));
5840c0d06caSMauro Carvalho Chehab 
5850c0d06caSMauro Carvalho Chehab 	dev->vbi_mode.bulk_ctl.buf = *buf;
5860c0d06caSMauro Carvalho Chehab 
5870c0d06caSMauro Carvalho Chehab 	return;
5880c0d06caSMauro Carvalho Chehab }
5890c0d06caSMauro Carvalho Chehab 
cx231xx_reset_vbi_buffer(struct cx231xx * dev,struct cx231xx_dmaqueue * dma_q)5900c0d06caSMauro Carvalho Chehab void cx231xx_reset_vbi_buffer(struct cx231xx *dev,
5910c0d06caSMauro Carvalho Chehab 			      struct cx231xx_dmaqueue *dma_q)
5920c0d06caSMauro Carvalho Chehab {
5930c0d06caSMauro Carvalho Chehab 	struct cx231xx_buffer *buf;
5940c0d06caSMauro Carvalho Chehab 
5950c0d06caSMauro Carvalho Chehab 	buf = dev->vbi_mode.bulk_ctl.buf;
5960c0d06caSMauro Carvalho Chehab 
5970c0d06caSMauro Carvalho Chehab 	if (buf == NULL) {
5980c0d06caSMauro Carvalho Chehab 		/* first try to get the buffer */
5990c0d06caSMauro Carvalho Chehab 		get_next_vbi_buf(dma_q, &buf);
6000c0d06caSMauro Carvalho Chehab 
6010c0d06caSMauro Carvalho Chehab 		dma_q->pos = 0;
6020c0d06caSMauro Carvalho Chehab 		dma_q->current_field = -1;
6030c0d06caSMauro Carvalho Chehab 	}
6040c0d06caSMauro Carvalho Chehab 
6050c0d06caSMauro Carvalho Chehab 	dma_q->bytes_left_in_line = dev->width << 1;
6060c0d06caSMauro Carvalho Chehab 	dma_q->lines_completed = 0;
6070c0d06caSMauro Carvalho Chehab }
6080c0d06caSMauro Carvalho Chehab 
cx231xx_do_vbi_copy(struct cx231xx * dev,struct cx231xx_dmaqueue * dma_q,u8 * p_buffer,u32 bytes_to_copy)6090c0d06caSMauro Carvalho Chehab int cx231xx_do_vbi_copy(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
6100c0d06caSMauro Carvalho Chehab 			u8 *p_buffer, u32 bytes_to_copy)
6110c0d06caSMauro Carvalho Chehab {
6120c0d06caSMauro Carvalho Chehab 	u8 *p_out_buffer = NULL;
6130c0d06caSMauro Carvalho Chehab 	u32 current_line_bytes_copied = 0;
6140c0d06caSMauro Carvalho Chehab 	struct cx231xx_buffer *buf;
6150c0d06caSMauro Carvalho Chehab 	u32 _line_size = dev->width << 1;
6160c0d06caSMauro Carvalho Chehab 	void *startwrite;
6170c0d06caSMauro Carvalho Chehab 	int offset, lencopy;
6180c0d06caSMauro Carvalho Chehab 
6190c0d06caSMauro Carvalho Chehab 	buf = dev->vbi_mode.bulk_ctl.buf;
6200c0d06caSMauro Carvalho Chehab 
6210c0d06caSMauro Carvalho Chehab 	if (buf == NULL)
6220c0d06caSMauro Carvalho Chehab 		return -EINVAL;
6230c0d06caSMauro Carvalho Chehab 
6247c617138SHans Verkuil 	p_out_buffer = vb2_plane_vaddr(&buf->vb.vb2_buf, 0);
6250c0d06caSMauro Carvalho Chehab 
6260c0d06caSMauro Carvalho Chehab 	if (dma_q->bytes_left_in_line != _line_size) {
6270c0d06caSMauro Carvalho Chehab 		current_line_bytes_copied =
6280c0d06caSMauro Carvalho Chehab 		    _line_size - dma_q->bytes_left_in_line;
6290c0d06caSMauro Carvalho Chehab 	}
6300c0d06caSMauro Carvalho Chehab 
6310c0d06caSMauro Carvalho Chehab 	offset = (dma_q->lines_completed * _line_size) +
6320c0d06caSMauro Carvalho Chehab 		 current_line_bytes_copied;
6330c0d06caSMauro Carvalho Chehab 
6340c0d06caSMauro Carvalho Chehab 	if (dma_q->current_field == 2) {
6350c0d06caSMauro Carvalho Chehab 		/* Populate the second half of the frame */
6360c0d06caSMauro Carvalho Chehab 		offset += (dev->width * 2 * dma_q->lines_per_field);
6370c0d06caSMauro Carvalho Chehab 	}
6380c0d06caSMauro Carvalho Chehab 
6390c0d06caSMauro Carvalho Chehab 	/* prepare destination address */
6400c0d06caSMauro Carvalho Chehab 	startwrite = p_out_buffer + offset;
6410c0d06caSMauro Carvalho Chehab 
6420c0d06caSMauro Carvalho Chehab 	lencopy = dma_q->bytes_left_in_line > bytes_to_copy ?
6430c0d06caSMauro Carvalho Chehab 		  bytes_to_copy : dma_q->bytes_left_in_line;
6440c0d06caSMauro Carvalho Chehab 
6450c0d06caSMauro Carvalho Chehab 	memcpy(startwrite, p_buffer, lencopy);
6460c0d06caSMauro Carvalho Chehab 
6470c0d06caSMauro Carvalho Chehab 	return 0;
6480c0d06caSMauro Carvalho Chehab }
6490c0d06caSMauro Carvalho Chehab 
cx231xx_is_vbi_buffer_done(struct cx231xx * dev,struct cx231xx_dmaqueue * dma_q)6500c0d06caSMauro Carvalho Chehab u8 cx231xx_is_vbi_buffer_done(struct cx231xx *dev,
6510c0d06caSMauro Carvalho Chehab 			      struct cx231xx_dmaqueue *dma_q)
6520c0d06caSMauro Carvalho Chehab {
6530c0d06caSMauro Carvalho Chehab 	u32 height = 0;
6540c0d06caSMauro Carvalho Chehab 
6550c0d06caSMauro Carvalho Chehab 	height = ((dev->norm & V4L2_STD_625_50) ?
6560c0d06caSMauro Carvalho Chehab 		  PAL_VBI_LINES : NTSC_VBI_LINES);
6570c0d06caSMauro Carvalho Chehab 	if (dma_q->lines_completed == height && dma_q->current_field == 2)
6580c0d06caSMauro Carvalho Chehab 		return 1;
6590c0d06caSMauro Carvalho Chehab 	else
6600c0d06caSMauro Carvalho Chehab 		return 0;
6610c0d06caSMauro Carvalho Chehab }
662