18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci   cx231xx_vbi.c - driver for Conexant Cx23100/101/102 USB video capture devices
48c2ecf20Sopenharmony_ci
58c2ecf20Sopenharmony_ci   Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
68c2ecf20Sopenharmony_ci	Based on cx88 driver
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include "cx231xx.h"
118c2ecf20Sopenharmony_ci#include <linux/init.h>
128c2ecf20Sopenharmony_ci#include <linux/list.h>
138c2ecf20Sopenharmony_ci#include <linux/module.h>
148c2ecf20Sopenharmony_ci#include <linux/kernel.h>
158c2ecf20Sopenharmony_ci#include <linux/bitmap.h>
168c2ecf20Sopenharmony_ci#include <linux/i2c.h>
178c2ecf20Sopenharmony_ci#include <linux/mm.h>
188c2ecf20Sopenharmony_ci#include <linux/mutex.h>
198c2ecf20Sopenharmony_ci#include <linux/slab.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#include <media/v4l2-common.h>
228c2ecf20Sopenharmony_ci#include <media/v4l2-ioctl.h>
238c2ecf20Sopenharmony_ci#include <media/drv-intf/msp3400.h>
248c2ecf20Sopenharmony_ci#include <media/tuner.h>
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#include "cx231xx-vbi.h"
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_cistatic inline void print_err_status(struct cx231xx *dev, int packet, int status)
298c2ecf20Sopenharmony_ci{
308c2ecf20Sopenharmony_ci	char *errmsg = "Unknown";
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci	switch (status) {
338c2ecf20Sopenharmony_ci	case -ENOENT:
348c2ecf20Sopenharmony_ci		errmsg = "unlinked synchronously";
358c2ecf20Sopenharmony_ci		break;
368c2ecf20Sopenharmony_ci	case -ECONNRESET:
378c2ecf20Sopenharmony_ci		errmsg = "unlinked asynchronously";
388c2ecf20Sopenharmony_ci		break;
398c2ecf20Sopenharmony_ci	case -ENOSR:
408c2ecf20Sopenharmony_ci		errmsg = "Buffer error (overrun)";
418c2ecf20Sopenharmony_ci		break;
428c2ecf20Sopenharmony_ci	case -EPIPE:
438c2ecf20Sopenharmony_ci		errmsg = "Stalled (device not responding)";
448c2ecf20Sopenharmony_ci		break;
458c2ecf20Sopenharmony_ci	case -EOVERFLOW:
468c2ecf20Sopenharmony_ci		errmsg = "Babble (bad cable?)";
478c2ecf20Sopenharmony_ci		break;
488c2ecf20Sopenharmony_ci	case -EPROTO:
498c2ecf20Sopenharmony_ci		errmsg = "Bit-stuff error (bad cable?)";
508c2ecf20Sopenharmony_ci		break;
518c2ecf20Sopenharmony_ci	case -EILSEQ:
528c2ecf20Sopenharmony_ci		errmsg = "CRC/Timeout (could be anything)";
538c2ecf20Sopenharmony_ci		break;
548c2ecf20Sopenharmony_ci	case -ETIME:
558c2ecf20Sopenharmony_ci		errmsg = "Device does not respond";
568c2ecf20Sopenharmony_ci		break;
578c2ecf20Sopenharmony_ci	}
588c2ecf20Sopenharmony_ci	if (packet < 0) {
598c2ecf20Sopenharmony_ci		dev_err(dev->dev,
608c2ecf20Sopenharmony_ci			"URB status %d [%s].\n", status, errmsg);
618c2ecf20Sopenharmony_ci	} else {
628c2ecf20Sopenharmony_ci		dev_err(dev->dev,
638c2ecf20Sopenharmony_ci			"URB packet %d, status %d [%s].\n",
648c2ecf20Sopenharmony_ci			packet, status, errmsg);
658c2ecf20Sopenharmony_ci	}
668c2ecf20Sopenharmony_ci}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci/*
698c2ecf20Sopenharmony_ci * Controls the isoc copy of each urb packet
708c2ecf20Sopenharmony_ci */
718c2ecf20Sopenharmony_cistatic inline int cx231xx_isoc_vbi_copy(struct cx231xx *dev, struct urb *urb)
728c2ecf20Sopenharmony_ci{
738c2ecf20Sopenharmony_ci	struct cx231xx_dmaqueue *dma_q = urb->context;
748c2ecf20Sopenharmony_ci	int rc = 1;
758c2ecf20Sopenharmony_ci	unsigned char *p_buffer;
768c2ecf20Sopenharmony_ci	u32 bytes_parsed = 0, buffer_size = 0;
778c2ecf20Sopenharmony_ci	u8 sav_eav = 0;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	if (!dev)
808c2ecf20Sopenharmony_ci		return 0;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	if (dev->state & DEV_DISCONNECTED)
838c2ecf20Sopenharmony_ci		return 0;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	if (urb->status < 0) {
868c2ecf20Sopenharmony_ci		print_err_status(dev, -1, urb->status);
878c2ecf20Sopenharmony_ci		if (urb->status == -ENOENT)
888c2ecf20Sopenharmony_ci			return 0;
898c2ecf20Sopenharmony_ci	}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	/* get buffer pointer and length */
928c2ecf20Sopenharmony_ci	p_buffer = urb->transfer_buffer;
938c2ecf20Sopenharmony_ci	buffer_size = urb->actual_length;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	if (buffer_size > 0) {
968c2ecf20Sopenharmony_ci		bytes_parsed = 0;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci		if (dma_q->is_partial_line) {
998c2ecf20Sopenharmony_ci			/* Handle the case where we were working on a partial
1008c2ecf20Sopenharmony_ci			   line */
1018c2ecf20Sopenharmony_ci			sav_eav = dma_q->last_sav;
1028c2ecf20Sopenharmony_ci		} else {
1038c2ecf20Sopenharmony_ci			/* Check for a SAV/EAV overlapping the
1048c2ecf20Sopenharmony_ci			   buffer boundary */
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci			sav_eav = cx231xx_find_boundary_SAV_EAV(p_buffer,
1078c2ecf20Sopenharmony_ci							  dma_q->partial_buf,
1088c2ecf20Sopenharmony_ci							  &bytes_parsed);
1098c2ecf20Sopenharmony_ci		}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci		sav_eav &= 0xF0;
1128c2ecf20Sopenharmony_ci		/* Get the first line if we have some portion of an SAV/EAV from
1138c2ecf20Sopenharmony_ci		   the last buffer or a partial line */
1148c2ecf20Sopenharmony_ci		if (sav_eav) {
1158c2ecf20Sopenharmony_ci			bytes_parsed += cx231xx_get_vbi_line(dev, dma_q,
1168c2ecf20Sopenharmony_ci				sav_eav,		       /* SAV/EAV */
1178c2ecf20Sopenharmony_ci				p_buffer + bytes_parsed,       /* p_buffer */
1188c2ecf20Sopenharmony_ci				buffer_size - bytes_parsed);   /* buffer size */
1198c2ecf20Sopenharmony_ci		}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci		/* Now parse data that is completely in this buffer */
1228c2ecf20Sopenharmony_ci		dma_q->is_partial_line = 0;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci		while (bytes_parsed < buffer_size) {
1258c2ecf20Sopenharmony_ci			u32 bytes_used = 0;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci			sav_eav = cx231xx_find_next_SAV_EAV(
1288c2ecf20Sopenharmony_ci				p_buffer + bytes_parsed,	/* p_buffer */
1298c2ecf20Sopenharmony_ci				buffer_size - bytes_parsed, /* buffer size */
1308c2ecf20Sopenharmony_ci				&bytes_used);	/* bytes used to get SAV/EAV */
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci			bytes_parsed += bytes_used;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci			sav_eav &= 0xF0;
1358c2ecf20Sopenharmony_ci			if (sav_eav && (bytes_parsed < buffer_size)) {
1368c2ecf20Sopenharmony_ci				bytes_parsed += cx231xx_get_vbi_line(dev,
1378c2ecf20Sopenharmony_ci					dma_q, sav_eav,	/* SAV/EAV */
1388c2ecf20Sopenharmony_ci					p_buffer+bytes_parsed, /* p_buffer */
1398c2ecf20Sopenharmony_ci					buffer_size-bytes_parsed);/*buf size*/
1408c2ecf20Sopenharmony_ci			}
1418c2ecf20Sopenharmony_ci		}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci		/* Save the last four bytes of the buffer so we can
1448c2ecf20Sopenharmony_ci		check the buffer boundary condition next time */
1458c2ecf20Sopenharmony_ci		memcpy(dma_q->partial_buf, p_buffer + buffer_size - 4, 4);
1468c2ecf20Sopenharmony_ci		bytes_parsed = 0;
1478c2ecf20Sopenharmony_ci	}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	return rc;
1508c2ecf20Sopenharmony_ci}
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------
1538c2ecf20Sopenharmony_ci	Vbi buf operations
1548c2ecf20Sopenharmony_ci   ------------------------------------------------------------------*/
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_cistatic int vbi_queue_setup(struct vb2_queue *vq,
1578c2ecf20Sopenharmony_ci			   unsigned int *nbuffers, unsigned int *nplanes,
1588c2ecf20Sopenharmony_ci			   unsigned int sizes[], struct device *alloc_devs[])
1598c2ecf20Sopenharmony_ci{
1608c2ecf20Sopenharmony_ci	struct cx231xx *dev = vb2_get_drv_priv(vq);
1618c2ecf20Sopenharmony_ci	u32 height = 0;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	height = ((dev->norm & V4L2_STD_625_50) ?
1648c2ecf20Sopenharmony_ci		  PAL_VBI_LINES : NTSC_VBI_LINES);
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	*nplanes = 1;
1678c2ecf20Sopenharmony_ci	sizes[0] = (dev->width * height * 2 * 2);
1688c2ecf20Sopenharmony_ci	return 0;
1698c2ecf20Sopenharmony_ci}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci/* This is called *without* dev->slock held; please keep it that way */
1728c2ecf20Sopenharmony_cistatic int vbi_buf_prepare(struct vb2_buffer *vb)
1738c2ecf20Sopenharmony_ci{
1748c2ecf20Sopenharmony_ci	struct cx231xx *dev = vb2_get_drv_priv(vb->vb2_queue);
1758c2ecf20Sopenharmony_ci	u32 height = 0;
1768c2ecf20Sopenharmony_ci	u32 size;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	height = ((dev->norm & V4L2_STD_625_50) ?
1798c2ecf20Sopenharmony_ci		  PAL_VBI_LINES : NTSC_VBI_LINES);
1808c2ecf20Sopenharmony_ci	size = ((dev->width << 1) * height * 2);
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	if (vb2_plane_size(vb, 0) < size)
1838c2ecf20Sopenharmony_ci		return -EINVAL;
1848c2ecf20Sopenharmony_ci	vb2_set_plane_payload(vb, 0, size);
1858c2ecf20Sopenharmony_ci	return 0;
1868c2ecf20Sopenharmony_ci}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_cistatic void vbi_buf_queue(struct vb2_buffer *vb)
1898c2ecf20Sopenharmony_ci{
1908c2ecf20Sopenharmony_ci	struct cx231xx *dev = vb2_get_drv_priv(vb->vb2_queue);
1918c2ecf20Sopenharmony_ci	struct cx231xx_buffer *buf =
1928c2ecf20Sopenharmony_ci	    container_of(vb, struct cx231xx_buffer, vb.vb2_buf);
1938c2ecf20Sopenharmony_ci	struct cx231xx_dmaqueue *vidq = &dev->vbi_mode.vidq;
1948c2ecf20Sopenharmony_ci	unsigned long flags;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	spin_lock_irqsave(&dev->vbi_mode.slock, flags);
1978c2ecf20Sopenharmony_ci	list_add_tail(&buf->list, &vidq->active);
1988c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&dev->vbi_mode.slock, flags);
1998c2ecf20Sopenharmony_ci}
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_cistatic void return_all_buffers(struct cx231xx *dev,
2028c2ecf20Sopenharmony_ci			       enum vb2_buffer_state state)
2038c2ecf20Sopenharmony_ci{
2048c2ecf20Sopenharmony_ci	struct cx231xx_dmaqueue *vidq = &dev->vbi_mode.vidq;
2058c2ecf20Sopenharmony_ci	struct cx231xx_buffer *buf, *node;
2068c2ecf20Sopenharmony_ci	unsigned long flags;
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	spin_lock_irqsave(&dev->vbi_mode.slock, flags);
2098c2ecf20Sopenharmony_ci	dev->vbi_mode.bulk_ctl.buf = NULL;
2108c2ecf20Sopenharmony_ci	list_for_each_entry_safe(buf, node, &vidq->active, list) {
2118c2ecf20Sopenharmony_ci		list_del(&buf->list);
2128c2ecf20Sopenharmony_ci		vb2_buffer_done(&buf->vb.vb2_buf, state);
2138c2ecf20Sopenharmony_ci	}
2148c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&dev->vbi_mode.slock, flags);
2158c2ecf20Sopenharmony_ci}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_cistatic int vbi_start_streaming(struct vb2_queue *vq, unsigned int count)
2188c2ecf20Sopenharmony_ci{
2198c2ecf20Sopenharmony_ci	struct cx231xx *dev = vb2_get_drv_priv(vq);
2208c2ecf20Sopenharmony_ci	struct cx231xx_dmaqueue *vidq = &dev->vbi_mode.vidq;
2218c2ecf20Sopenharmony_ci	int ret;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	vidq->sequence = 0;
2248c2ecf20Sopenharmony_ci	ret = cx231xx_init_vbi_isoc(dev, CX231XX_NUM_VBI_PACKETS,
2258c2ecf20Sopenharmony_ci				    CX231XX_NUM_VBI_BUFS,
2268c2ecf20Sopenharmony_ci				    dev->vbi_mode.alt_max_pkt_size[0],
2278c2ecf20Sopenharmony_ci				    cx231xx_isoc_vbi_copy);
2288c2ecf20Sopenharmony_ci	if (ret)
2298c2ecf20Sopenharmony_ci		return_all_buffers(dev, VB2_BUF_STATE_QUEUED);
2308c2ecf20Sopenharmony_ci	return ret;
2318c2ecf20Sopenharmony_ci}
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_cistatic void vbi_stop_streaming(struct vb2_queue *vq)
2348c2ecf20Sopenharmony_ci{
2358c2ecf20Sopenharmony_ci	struct cx231xx *dev = vb2_get_drv_priv(vq);
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	return_all_buffers(dev, VB2_BUF_STATE_ERROR);
2388c2ecf20Sopenharmony_ci}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_cistruct vb2_ops cx231xx_vbi_qops = {
2418c2ecf20Sopenharmony_ci	.queue_setup = vbi_queue_setup,
2428c2ecf20Sopenharmony_ci	.buf_prepare = vbi_buf_prepare,
2438c2ecf20Sopenharmony_ci	.buf_queue = vbi_buf_queue,
2448c2ecf20Sopenharmony_ci	.start_streaming = vbi_start_streaming,
2458c2ecf20Sopenharmony_ci	.stop_streaming = vbi_stop_streaming,
2468c2ecf20Sopenharmony_ci	.wait_prepare = vb2_ops_wait_prepare,
2478c2ecf20Sopenharmony_ci	.wait_finish = vb2_ops_wait_finish,
2488c2ecf20Sopenharmony_ci};
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------
2518c2ecf20Sopenharmony_ci	URB control
2528c2ecf20Sopenharmony_ci   ------------------------------------------------------------------*/
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci/*
2558c2ecf20Sopenharmony_ci * IRQ callback, called by URB callback
2568c2ecf20Sopenharmony_ci */
2578c2ecf20Sopenharmony_cistatic void cx231xx_irq_vbi_callback(struct urb *urb)
2588c2ecf20Sopenharmony_ci{
2598c2ecf20Sopenharmony_ci	struct cx231xx_dmaqueue *dma_q = urb->context;
2608c2ecf20Sopenharmony_ci	struct cx231xx_video_mode *vmode =
2618c2ecf20Sopenharmony_ci	    container_of(dma_q, struct cx231xx_video_mode, vidq);
2628c2ecf20Sopenharmony_ci	struct cx231xx *dev = container_of(vmode, struct cx231xx, vbi_mode);
2638c2ecf20Sopenharmony_ci	unsigned long flags;
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	switch (urb->status) {
2668c2ecf20Sopenharmony_ci	case 0:		/* success */
2678c2ecf20Sopenharmony_ci	case -ETIMEDOUT:	/* NAK */
2688c2ecf20Sopenharmony_ci		break;
2698c2ecf20Sopenharmony_ci	case -ECONNRESET:	/* kill */
2708c2ecf20Sopenharmony_ci	case -ENOENT:
2718c2ecf20Sopenharmony_ci	case -ESHUTDOWN:
2728c2ecf20Sopenharmony_ci		return;
2738c2ecf20Sopenharmony_ci	default:		/* error */
2748c2ecf20Sopenharmony_ci		dev_err(dev->dev,
2758c2ecf20Sopenharmony_ci			"urb completion error %d.\n", urb->status);
2768c2ecf20Sopenharmony_ci		break;
2778c2ecf20Sopenharmony_ci	}
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	/* Copy data from URB */
2808c2ecf20Sopenharmony_ci	spin_lock_irqsave(&dev->vbi_mode.slock, flags);
2818c2ecf20Sopenharmony_ci	dev->vbi_mode.bulk_ctl.bulk_copy(dev, urb);
2828c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&dev->vbi_mode.slock, flags);
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	/* Reset status */
2858c2ecf20Sopenharmony_ci	urb->status = 0;
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	urb->status = usb_submit_urb(urb, GFP_ATOMIC);
2888c2ecf20Sopenharmony_ci	if (urb->status) {
2898c2ecf20Sopenharmony_ci		dev_err(dev->dev, "urb resubmit failed (error=%i)\n",
2908c2ecf20Sopenharmony_ci			urb->status);
2918c2ecf20Sopenharmony_ci	}
2928c2ecf20Sopenharmony_ci}
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci/*
2958c2ecf20Sopenharmony_ci * Stop and Deallocate URBs
2968c2ecf20Sopenharmony_ci */
2978c2ecf20Sopenharmony_civoid cx231xx_uninit_vbi_isoc(struct cx231xx *dev)
2988c2ecf20Sopenharmony_ci{
2998c2ecf20Sopenharmony_ci	struct urb *urb;
3008c2ecf20Sopenharmony_ci	int i;
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	dev_dbg(dev->dev, "called cx231xx_uninit_vbi_isoc\n");
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	dev->vbi_mode.bulk_ctl.nfields = -1;
3058c2ecf20Sopenharmony_ci	for (i = 0; i < dev->vbi_mode.bulk_ctl.num_bufs; i++) {
3068c2ecf20Sopenharmony_ci		urb = dev->vbi_mode.bulk_ctl.urb[i];
3078c2ecf20Sopenharmony_ci		if (urb) {
3088c2ecf20Sopenharmony_ci			if (!irqs_disabled())
3098c2ecf20Sopenharmony_ci				usb_kill_urb(urb);
3108c2ecf20Sopenharmony_ci			else
3118c2ecf20Sopenharmony_ci				usb_unlink_urb(urb);
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci			if (dev->vbi_mode.bulk_ctl.transfer_buffer[i]) {
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci				kfree(dev->vbi_mode.bulk_ctl.
3168c2ecf20Sopenharmony_ci				      transfer_buffer[i]);
3178c2ecf20Sopenharmony_ci				dev->vbi_mode.bulk_ctl.transfer_buffer[i] =
3188c2ecf20Sopenharmony_ci				    NULL;
3198c2ecf20Sopenharmony_ci			}
3208c2ecf20Sopenharmony_ci			usb_free_urb(urb);
3218c2ecf20Sopenharmony_ci			dev->vbi_mode.bulk_ctl.urb[i] = NULL;
3228c2ecf20Sopenharmony_ci		}
3238c2ecf20Sopenharmony_ci		dev->vbi_mode.bulk_ctl.transfer_buffer[i] = NULL;
3248c2ecf20Sopenharmony_ci	}
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	kfree(dev->vbi_mode.bulk_ctl.urb);
3278c2ecf20Sopenharmony_ci	kfree(dev->vbi_mode.bulk_ctl.transfer_buffer);
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	dev->vbi_mode.bulk_ctl.urb = NULL;
3308c2ecf20Sopenharmony_ci	dev->vbi_mode.bulk_ctl.transfer_buffer = NULL;
3318c2ecf20Sopenharmony_ci	dev->vbi_mode.bulk_ctl.num_bufs = 0;
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	cx231xx_capture_start(dev, 0, Vbi);
3348c2ecf20Sopenharmony_ci}
3358c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cx231xx_uninit_vbi_isoc);
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci/*
3388c2ecf20Sopenharmony_ci * Allocate URBs and start IRQ
3398c2ecf20Sopenharmony_ci */
3408c2ecf20Sopenharmony_ciint cx231xx_init_vbi_isoc(struct cx231xx *dev, int max_packets,
3418c2ecf20Sopenharmony_ci			  int num_bufs, int max_pkt_size,
3428c2ecf20Sopenharmony_ci			  int (*bulk_copy) (struct cx231xx *dev,
3438c2ecf20Sopenharmony_ci					    struct urb *urb))
3448c2ecf20Sopenharmony_ci{
3458c2ecf20Sopenharmony_ci	struct cx231xx_dmaqueue *dma_q = &dev->vbi_mode.vidq;
3468c2ecf20Sopenharmony_ci	int i;
3478c2ecf20Sopenharmony_ci	int sb_size, pipe;
3488c2ecf20Sopenharmony_ci	struct urb *urb;
3498c2ecf20Sopenharmony_ci	int rc;
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	dev_dbg(dev->dev, "called cx231xx_vbi_isoc\n");
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	/* De-allocates all pending stuff */
3548c2ecf20Sopenharmony_ci	cx231xx_uninit_vbi_isoc(dev);
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	/* clear if any halt */
3578c2ecf20Sopenharmony_ci	usb_clear_halt(dev->udev,
3588c2ecf20Sopenharmony_ci		       usb_rcvbulkpipe(dev->udev,
3598c2ecf20Sopenharmony_ci				       dev->vbi_mode.end_point_addr));
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	dev->vbi_mode.bulk_ctl.bulk_copy = bulk_copy;
3628c2ecf20Sopenharmony_ci	dev->vbi_mode.bulk_ctl.num_bufs = num_bufs;
3638c2ecf20Sopenharmony_ci	dma_q->pos = 0;
3648c2ecf20Sopenharmony_ci	dma_q->is_partial_line = 0;
3658c2ecf20Sopenharmony_ci	dma_q->last_sav = 0;
3668c2ecf20Sopenharmony_ci	dma_q->current_field = -1;
3678c2ecf20Sopenharmony_ci	dma_q->bytes_left_in_line = dev->width << 1;
3688c2ecf20Sopenharmony_ci	dma_q->lines_per_field = ((dev->norm & V4L2_STD_625_50) ?
3698c2ecf20Sopenharmony_ci				  PAL_VBI_LINES : NTSC_VBI_LINES);
3708c2ecf20Sopenharmony_ci	dma_q->lines_completed = 0;
3718c2ecf20Sopenharmony_ci	for (i = 0; i < 8; i++)
3728c2ecf20Sopenharmony_ci		dma_q->partial_buf[i] = 0;
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	dev->vbi_mode.bulk_ctl.urb = kcalloc(num_bufs, sizeof(void *),
3758c2ecf20Sopenharmony_ci					     GFP_KERNEL);
3768c2ecf20Sopenharmony_ci	if (!dev->vbi_mode.bulk_ctl.urb) {
3778c2ecf20Sopenharmony_ci		dev_err(dev->dev,
3788c2ecf20Sopenharmony_ci			"cannot alloc memory for usb buffers\n");
3798c2ecf20Sopenharmony_ci		return -ENOMEM;
3808c2ecf20Sopenharmony_ci	}
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	dev->vbi_mode.bulk_ctl.transfer_buffer =
3838c2ecf20Sopenharmony_ci	    kcalloc(num_bufs, sizeof(void *), GFP_KERNEL);
3848c2ecf20Sopenharmony_ci	if (!dev->vbi_mode.bulk_ctl.transfer_buffer) {
3858c2ecf20Sopenharmony_ci		dev_err(dev->dev,
3868c2ecf20Sopenharmony_ci			"cannot allocate memory for usbtransfer\n");
3878c2ecf20Sopenharmony_ci		kfree(dev->vbi_mode.bulk_ctl.urb);
3888c2ecf20Sopenharmony_ci		return -ENOMEM;
3898c2ecf20Sopenharmony_ci	}
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	dev->vbi_mode.bulk_ctl.max_pkt_size = max_pkt_size;
3928c2ecf20Sopenharmony_ci	dev->vbi_mode.bulk_ctl.buf = NULL;
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	sb_size = max_packets * dev->vbi_mode.bulk_ctl.max_pkt_size;
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	/* allocate urbs and transfer buffers */
3978c2ecf20Sopenharmony_ci	for (i = 0; i < dev->vbi_mode.bulk_ctl.num_bufs; i++) {
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci		urb = usb_alloc_urb(0, GFP_KERNEL);
4008c2ecf20Sopenharmony_ci		if (!urb) {
4018c2ecf20Sopenharmony_ci			cx231xx_uninit_vbi_isoc(dev);
4028c2ecf20Sopenharmony_ci			return -ENOMEM;
4038c2ecf20Sopenharmony_ci		}
4048c2ecf20Sopenharmony_ci		dev->vbi_mode.bulk_ctl.urb[i] = urb;
4058c2ecf20Sopenharmony_ci		urb->transfer_flags = 0;
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci		dev->vbi_mode.bulk_ctl.transfer_buffer[i] =
4088c2ecf20Sopenharmony_ci		    kzalloc(sb_size, GFP_KERNEL);
4098c2ecf20Sopenharmony_ci		if (!dev->vbi_mode.bulk_ctl.transfer_buffer[i]) {
4108c2ecf20Sopenharmony_ci			dev_err(dev->dev,
4118c2ecf20Sopenharmony_ci				"unable to allocate %i bytes for transfer buffer %i%s\n",
4128c2ecf20Sopenharmony_ci				sb_size, i,
4138c2ecf20Sopenharmony_ci				in_interrupt() ? " while in int" : "");
4148c2ecf20Sopenharmony_ci			cx231xx_uninit_vbi_isoc(dev);
4158c2ecf20Sopenharmony_ci			return -ENOMEM;
4168c2ecf20Sopenharmony_ci		}
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci		pipe = usb_rcvbulkpipe(dev->udev, dev->vbi_mode.end_point_addr);
4198c2ecf20Sopenharmony_ci		usb_fill_bulk_urb(urb, dev->udev, pipe,
4208c2ecf20Sopenharmony_ci				  dev->vbi_mode.bulk_ctl.transfer_buffer[i],
4218c2ecf20Sopenharmony_ci				  sb_size, cx231xx_irq_vbi_callback, dma_q);
4228c2ecf20Sopenharmony_ci	}
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	init_waitqueue_head(&dma_q->wq);
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	/* submit urbs and enables IRQ */
4278c2ecf20Sopenharmony_ci	for (i = 0; i < dev->vbi_mode.bulk_ctl.num_bufs; i++) {
4288c2ecf20Sopenharmony_ci		rc = usb_submit_urb(dev->vbi_mode.bulk_ctl.urb[i], GFP_ATOMIC);
4298c2ecf20Sopenharmony_ci		if (rc) {
4308c2ecf20Sopenharmony_ci			dev_err(dev->dev,
4318c2ecf20Sopenharmony_ci				"submit of urb %i failed (error=%i)\n", i, rc);
4328c2ecf20Sopenharmony_ci			cx231xx_uninit_vbi_isoc(dev);
4338c2ecf20Sopenharmony_ci			return rc;
4348c2ecf20Sopenharmony_ci		}
4358c2ecf20Sopenharmony_ci	}
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	cx231xx_capture_start(dev, 1, Vbi);
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	return 0;
4408c2ecf20Sopenharmony_ci}
4418c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cx231xx_init_vbi_isoc);
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ciu32 cx231xx_get_vbi_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
4448c2ecf20Sopenharmony_ci			 u8 sav_eav, u8 *p_buffer, u32 buffer_size)
4458c2ecf20Sopenharmony_ci{
4468c2ecf20Sopenharmony_ci	u32 bytes_copied = 0;
4478c2ecf20Sopenharmony_ci	int current_field = -1;
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	switch (sav_eav) {
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	case SAV_VBI_FIELD1:
4528c2ecf20Sopenharmony_ci		current_field = 1;
4538c2ecf20Sopenharmony_ci		break;
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	case SAV_VBI_FIELD2:
4568c2ecf20Sopenharmony_ci		current_field = 2;
4578c2ecf20Sopenharmony_ci		break;
4588c2ecf20Sopenharmony_ci	default:
4598c2ecf20Sopenharmony_ci		break;
4608c2ecf20Sopenharmony_ci	}
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	if (current_field < 0)
4638c2ecf20Sopenharmony_ci		return bytes_copied;
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	dma_q->last_sav = sav_eav;
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	bytes_copied =
4688c2ecf20Sopenharmony_ci	    cx231xx_copy_vbi_line(dev, dma_q, p_buffer, buffer_size,
4698c2ecf20Sopenharmony_ci				  current_field);
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	return bytes_copied;
4728c2ecf20Sopenharmony_ci}
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci/*
4758c2ecf20Sopenharmony_ci * Announces that a buffer were filled and request the next
4768c2ecf20Sopenharmony_ci */
4778c2ecf20Sopenharmony_cistatic inline void vbi_buffer_filled(struct cx231xx *dev,
4788c2ecf20Sopenharmony_ci				     struct cx231xx_dmaqueue *dma_q,
4798c2ecf20Sopenharmony_ci				     struct cx231xx_buffer *buf)
4808c2ecf20Sopenharmony_ci{
4818c2ecf20Sopenharmony_ci	/* Advice that buffer was filled */
4828c2ecf20Sopenharmony_ci	/* dev_dbg(dev->dev, "[%p/%d] wakeup\n", buf, buf->vb.index); */
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	buf->vb.sequence = dma_q->sequence++;
4858c2ecf20Sopenharmony_ci	buf->vb.vb2_buf.timestamp = ktime_get_ns();
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci	dev->vbi_mode.bulk_ctl.buf = NULL;
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	list_del(&buf->list);
4908c2ecf20Sopenharmony_ci	vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
4918c2ecf20Sopenharmony_ci}
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ciu32 cx231xx_copy_vbi_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
4948c2ecf20Sopenharmony_ci			  u8 *p_line, u32 length, int field_number)
4958c2ecf20Sopenharmony_ci{
4968c2ecf20Sopenharmony_ci	u32 bytes_to_copy;
4978c2ecf20Sopenharmony_ci	struct cx231xx_buffer *buf;
4988c2ecf20Sopenharmony_ci	u32 _line_size = dev->width * 2;
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci	if (dma_q->current_field == -1) {
5018c2ecf20Sopenharmony_ci		/* Just starting up */
5028c2ecf20Sopenharmony_ci		cx231xx_reset_vbi_buffer(dev, dma_q);
5038c2ecf20Sopenharmony_ci	}
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci	if (dma_q->current_field != field_number)
5068c2ecf20Sopenharmony_ci		dma_q->lines_completed = 0;
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci	/* get the buffer pointer */
5098c2ecf20Sopenharmony_ci	buf = dev->vbi_mode.bulk_ctl.buf;
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	/* Remember the field number for next time */
5128c2ecf20Sopenharmony_ci	dma_q->current_field = field_number;
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	bytes_to_copy = dma_q->bytes_left_in_line;
5158c2ecf20Sopenharmony_ci	if (bytes_to_copy > length)
5168c2ecf20Sopenharmony_ci		bytes_to_copy = length;
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	if (dma_q->lines_completed >= dma_q->lines_per_field) {
5198c2ecf20Sopenharmony_ci		dma_q->bytes_left_in_line -= bytes_to_copy;
5208c2ecf20Sopenharmony_ci		dma_q->is_partial_line =
5218c2ecf20Sopenharmony_ci		    (dma_q->bytes_left_in_line == 0) ? 0 : 1;
5228c2ecf20Sopenharmony_ci		return 0;
5238c2ecf20Sopenharmony_ci	}
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	dma_q->is_partial_line = 1;
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci	/* If we don't have a buffer, just return the number of bytes we would
5288c2ecf20Sopenharmony_ci	   have copied if we had a buffer. */
5298c2ecf20Sopenharmony_ci	if (!buf) {
5308c2ecf20Sopenharmony_ci		dma_q->bytes_left_in_line -= bytes_to_copy;
5318c2ecf20Sopenharmony_ci		dma_q->is_partial_line =
5328c2ecf20Sopenharmony_ci		    (dma_q->bytes_left_in_line == 0) ? 0 : 1;
5338c2ecf20Sopenharmony_ci		return bytes_to_copy;
5348c2ecf20Sopenharmony_ci	}
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	/* copy the data to video buffer */
5378c2ecf20Sopenharmony_ci	cx231xx_do_vbi_copy(dev, dma_q, p_line, bytes_to_copy);
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci	dma_q->pos += bytes_to_copy;
5408c2ecf20Sopenharmony_ci	dma_q->bytes_left_in_line -= bytes_to_copy;
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	if (dma_q->bytes_left_in_line == 0) {
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci		dma_q->bytes_left_in_line = _line_size;
5458c2ecf20Sopenharmony_ci		dma_q->lines_completed++;
5468c2ecf20Sopenharmony_ci		dma_q->is_partial_line = 0;
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci		if (cx231xx_is_vbi_buffer_done(dev, dma_q) && buf) {
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci			vbi_buffer_filled(dev, dma_q, buf);
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci			dma_q->pos = 0;
5538c2ecf20Sopenharmony_ci			dma_q->lines_completed = 0;
5548c2ecf20Sopenharmony_ci			cx231xx_reset_vbi_buffer(dev, dma_q);
5558c2ecf20Sopenharmony_ci		}
5568c2ecf20Sopenharmony_ci	}
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci	return bytes_to_copy;
5598c2ecf20Sopenharmony_ci}
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci/*
5628c2ecf20Sopenharmony_ci * video-buf generic routine to get the next available buffer
5638c2ecf20Sopenharmony_ci */
5648c2ecf20Sopenharmony_cistatic inline void get_next_vbi_buf(struct cx231xx_dmaqueue *dma_q,
5658c2ecf20Sopenharmony_ci				    struct cx231xx_buffer **buf)
5668c2ecf20Sopenharmony_ci{
5678c2ecf20Sopenharmony_ci	struct cx231xx_video_mode *vmode =
5688c2ecf20Sopenharmony_ci	    container_of(dma_q, struct cx231xx_video_mode, vidq);
5698c2ecf20Sopenharmony_ci	struct cx231xx *dev = container_of(vmode, struct cx231xx, vbi_mode);
5708c2ecf20Sopenharmony_ci	char *outp;
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci	if (list_empty(&dma_q->active)) {
5738c2ecf20Sopenharmony_ci		dev_err(dev->dev, "No active queue to serve\n");
5748c2ecf20Sopenharmony_ci		dev->vbi_mode.bulk_ctl.buf = NULL;
5758c2ecf20Sopenharmony_ci		*buf = NULL;
5768c2ecf20Sopenharmony_ci		return;
5778c2ecf20Sopenharmony_ci	}
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci	/* Get the next buffer */
5808c2ecf20Sopenharmony_ci	*buf = list_entry(dma_q->active.next, struct cx231xx_buffer, list);
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci	/* Cleans up buffer - Useful for testing for frame/URB loss */
5838c2ecf20Sopenharmony_ci	outp = vb2_plane_vaddr(&(*buf)->vb.vb2_buf, 0);
5848c2ecf20Sopenharmony_ci	memset(outp, 0, vb2_plane_size(&(*buf)->vb.vb2_buf, 0));
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci	dev->vbi_mode.bulk_ctl.buf = *buf;
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci	return;
5898c2ecf20Sopenharmony_ci}
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_civoid cx231xx_reset_vbi_buffer(struct cx231xx *dev,
5928c2ecf20Sopenharmony_ci			      struct cx231xx_dmaqueue *dma_q)
5938c2ecf20Sopenharmony_ci{
5948c2ecf20Sopenharmony_ci	struct cx231xx_buffer *buf;
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci	buf = dev->vbi_mode.bulk_ctl.buf;
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ci	if (buf == NULL) {
5998c2ecf20Sopenharmony_ci		/* first try to get the buffer */
6008c2ecf20Sopenharmony_ci		get_next_vbi_buf(dma_q, &buf);
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci		dma_q->pos = 0;
6038c2ecf20Sopenharmony_ci		dma_q->current_field = -1;
6048c2ecf20Sopenharmony_ci	}
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci	dma_q->bytes_left_in_line = dev->width << 1;
6078c2ecf20Sopenharmony_ci	dma_q->lines_completed = 0;
6088c2ecf20Sopenharmony_ci}
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ciint cx231xx_do_vbi_copy(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
6118c2ecf20Sopenharmony_ci			u8 *p_buffer, u32 bytes_to_copy)
6128c2ecf20Sopenharmony_ci{
6138c2ecf20Sopenharmony_ci	u8 *p_out_buffer = NULL;
6148c2ecf20Sopenharmony_ci	u32 current_line_bytes_copied = 0;
6158c2ecf20Sopenharmony_ci	struct cx231xx_buffer *buf;
6168c2ecf20Sopenharmony_ci	u32 _line_size = dev->width << 1;
6178c2ecf20Sopenharmony_ci	void *startwrite;
6188c2ecf20Sopenharmony_ci	int offset, lencopy;
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci	buf = dev->vbi_mode.bulk_ctl.buf;
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci	if (buf == NULL)
6238c2ecf20Sopenharmony_ci		return -EINVAL;
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci	p_out_buffer = vb2_plane_vaddr(&buf->vb.vb2_buf, 0);
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci	if (dma_q->bytes_left_in_line != _line_size) {
6288c2ecf20Sopenharmony_ci		current_line_bytes_copied =
6298c2ecf20Sopenharmony_ci		    _line_size - dma_q->bytes_left_in_line;
6308c2ecf20Sopenharmony_ci	}
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci	offset = (dma_q->lines_completed * _line_size) +
6338c2ecf20Sopenharmony_ci		 current_line_bytes_copied;
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci	if (dma_q->current_field == 2) {
6368c2ecf20Sopenharmony_ci		/* Populate the second half of the frame */
6378c2ecf20Sopenharmony_ci		offset += (dev->width * 2 * dma_q->lines_per_field);
6388c2ecf20Sopenharmony_ci	}
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci	/* prepare destination address */
6418c2ecf20Sopenharmony_ci	startwrite = p_out_buffer + offset;
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci	lencopy = dma_q->bytes_left_in_line > bytes_to_copy ?
6448c2ecf20Sopenharmony_ci		  bytes_to_copy : dma_q->bytes_left_in_line;
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci	memcpy(startwrite, p_buffer, lencopy);
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci	return 0;
6498c2ecf20Sopenharmony_ci}
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ciu8 cx231xx_is_vbi_buffer_done(struct cx231xx *dev,
6528c2ecf20Sopenharmony_ci			      struct cx231xx_dmaqueue *dma_q)
6538c2ecf20Sopenharmony_ci{
6548c2ecf20Sopenharmony_ci	u32 height = 0;
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci	height = ((dev->norm & V4L2_STD_625_50) ?
6578c2ecf20Sopenharmony_ci		  PAL_VBI_LINES : NTSC_VBI_LINES);
6588c2ecf20Sopenharmony_ci	if (dma_q->lines_completed == height && dma_q->current_field == 2)
6598c2ecf20Sopenharmony_ci		return 1;
6608c2ecf20Sopenharmony_ci	else
6618c2ecf20Sopenharmony_ci		return 0;
6628c2ecf20Sopenharmony_ci}
663