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 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 */ 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 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 */ 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 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 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 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 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 */ 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 */ 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 */ 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, 411b7085c08SMauro Carvalho Chehab "unable to allocate %i bytes for transfer buffer %i%s\n", 412ed0e3729SMauro Carvalho Chehab sb_size, i, 4130c0d06caSMauro Carvalho Chehab in_interrupt() ? " while in int" : ""); 4140c0d06caSMauro Carvalho Chehab cx231xx_uninit_vbi_isoc(dev); 4150c0d06caSMauro Carvalho Chehab return -ENOMEM; 4160c0d06caSMauro Carvalho Chehab } 4170c0d06caSMauro Carvalho Chehab 4180c0d06caSMauro Carvalho Chehab pipe = usb_rcvbulkpipe(dev->udev, dev->vbi_mode.end_point_addr); 4190c0d06caSMauro Carvalho Chehab usb_fill_bulk_urb(urb, dev->udev, pipe, 4200c0d06caSMauro Carvalho Chehab dev->vbi_mode.bulk_ctl.transfer_buffer[i], 4210c0d06caSMauro Carvalho Chehab sb_size, cx231xx_irq_vbi_callback, dma_q); 4220c0d06caSMauro Carvalho Chehab } 4230c0d06caSMauro Carvalho Chehab 4240c0d06caSMauro Carvalho Chehab init_waitqueue_head(&dma_q->wq); 4250c0d06caSMauro Carvalho Chehab 4260c0d06caSMauro Carvalho Chehab /* submit urbs and enables IRQ */ 4270c0d06caSMauro Carvalho Chehab for (i = 0; i < dev->vbi_mode.bulk_ctl.num_bufs; i++) { 4280c0d06caSMauro Carvalho Chehab rc = usb_submit_urb(dev->vbi_mode.bulk_ctl.urb[i], GFP_ATOMIC); 4290c0d06caSMauro Carvalho Chehab if (rc) { 430336fea92SMauro Carvalho Chehab dev_err(dev->dev, 431b7085c08SMauro Carvalho Chehab "submit of urb %i failed (error=%i)\n", i, rc); 4320c0d06caSMauro Carvalho Chehab cx231xx_uninit_vbi_isoc(dev); 4330c0d06caSMauro Carvalho Chehab return rc; 4340c0d06caSMauro Carvalho Chehab } 4350c0d06caSMauro Carvalho Chehab } 4360c0d06caSMauro Carvalho Chehab 4370c0d06caSMauro Carvalho Chehab cx231xx_capture_start(dev, 1, Vbi); 4380c0d06caSMauro Carvalho Chehab 4390c0d06caSMauro Carvalho Chehab return 0; 4400c0d06caSMauro Carvalho Chehab } 4410c0d06caSMauro Carvalho Chehab EXPORT_SYMBOL_GPL(cx231xx_init_vbi_isoc); 4420c0d06caSMauro Carvalho Chehab 4430c0d06caSMauro Carvalho Chehab u32 cx231xx_get_vbi_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q, 4440c0d06caSMauro Carvalho Chehab u8 sav_eav, u8 *p_buffer, u32 buffer_size) 4450c0d06caSMauro Carvalho Chehab { 4460c0d06caSMauro Carvalho Chehab u32 bytes_copied = 0; 4470c0d06caSMauro Carvalho Chehab int current_field = -1; 4480c0d06caSMauro Carvalho Chehab 4490c0d06caSMauro Carvalho Chehab switch (sav_eav) { 4500c0d06caSMauro Carvalho Chehab 4510c0d06caSMauro Carvalho Chehab case SAV_VBI_FIELD1: 4520c0d06caSMauro Carvalho Chehab current_field = 1; 4530c0d06caSMauro Carvalho Chehab break; 4540c0d06caSMauro Carvalho Chehab 4550c0d06caSMauro Carvalho Chehab case SAV_VBI_FIELD2: 4560c0d06caSMauro Carvalho Chehab current_field = 2; 4570c0d06caSMauro Carvalho Chehab break; 4580c0d06caSMauro Carvalho Chehab default: 4590c0d06caSMauro Carvalho Chehab break; 4600c0d06caSMauro Carvalho Chehab } 4610c0d06caSMauro Carvalho Chehab 4620c0d06caSMauro Carvalho Chehab if (current_field < 0) 4630c0d06caSMauro Carvalho Chehab return bytes_copied; 4640c0d06caSMauro Carvalho Chehab 4650c0d06caSMauro Carvalho Chehab dma_q->last_sav = sav_eav; 4660c0d06caSMauro Carvalho Chehab 4670c0d06caSMauro Carvalho Chehab bytes_copied = 4680c0d06caSMauro Carvalho Chehab cx231xx_copy_vbi_line(dev, dma_q, p_buffer, buffer_size, 4690c0d06caSMauro Carvalho Chehab current_field); 4700c0d06caSMauro Carvalho Chehab 4710c0d06caSMauro Carvalho Chehab return bytes_copied; 4720c0d06caSMauro Carvalho Chehab } 4730c0d06caSMauro Carvalho Chehab 4740c0d06caSMauro Carvalho Chehab /* 4750c0d06caSMauro Carvalho Chehab * Announces that a buffer were filled and request the next 4760c0d06caSMauro Carvalho Chehab */ 4770c0d06caSMauro Carvalho Chehab static inline void vbi_buffer_filled(struct cx231xx *dev, 4780c0d06caSMauro Carvalho Chehab struct cx231xx_dmaqueue *dma_q, 4790c0d06caSMauro Carvalho Chehab struct cx231xx_buffer *buf) 4800c0d06caSMauro Carvalho Chehab { 4810c0d06caSMauro Carvalho Chehab /* Advice that buffer was filled */ 4827c617138SHans Verkuil /* dev_dbg(dev->dev, "[%p/%d] wakeup\n", buf, buf->vb.index); */ 4830c0d06caSMauro Carvalho Chehab 4847c617138SHans Verkuil buf->vb.sequence = dma_q->sequence++; 4857c617138SHans Verkuil buf->vb.vb2_buf.timestamp = ktime_get_ns(); 4860c0d06caSMauro Carvalho Chehab 4870c0d06caSMauro Carvalho Chehab dev->vbi_mode.bulk_ctl.buf = NULL; 4880c0d06caSMauro Carvalho Chehab 4897c617138SHans Verkuil list_del(&buf->list); 4907c617138SHans Verkuil vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE); 4910c0d06caSMauro Carvalho Chehab } 4920c0d06caSMauro Carvalho Chehab 4930c0d06caSMauro Carvalho Chehab u32 cx231xx_copy_vbi_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q, 4940c0d06caSMauro Carvalho Chehab u8 *p_line, u32 length, int field_number) 4950c0d06caSMauro Carvalho Chehab { 4960c0d06caSMauro Carvalho Chehab u32 bytes_to_copy; 4970c0d06caSMauro Carvalho Chehab struct cx231xx_buffer *buf; 4980c0d06caSMauro Carvalho Chehab u32 _line_size = dev->width * 2; 4990c0d06caSMauro Carvalho Chehab 5000c0d06caSMauro Carvalho Chehab if (dma_q->current_field == -1) { 5010c0d06caSMauro Carvalho Chehab /* Just starting up */ 5020c0d06caSMauro Carvalho Chehab cx231xx_reset_vbi_buffer(dev, dma_q); 5030c0d06caSMauro Carvalho Chehab } 5040c0d06caSMauro Carvalho Chehab 5050c0d06caSMauro Carvalho Chehab if (dma_q->current_field != field_number) 5060c0d06caSMauro Carvalho Chehab dma_q->lines_completed = 0; 5070c0d06caSMauro Carvalho Chehab 5080c0d06caSMauro Carvalho Chehab /* get the buffer pointer */ 5090c0d06caSMauro Carvalho Chehab buf = dev->vbi_mode.bulk_ctl.buf; 5100c0d06caSMauro Carvalho Chehab 5110c0d06caSMauro Carvalho Chehab /* Remember the field number for next time */ 5120c0d06caSMauro Carvalho Chehab dma_q->current_field = field_number; 5130c0d06caSMauro Carvalho Chehab 5140c0d06caSMauro Carvalho Chehab bytes_to_copy = dma_q->bytes_left_in_line; 5150c0d06caSMauro Carvalho Chehab if (bytes_to_copy > length) 5160c0d06caSMauro Carvalho Chehab bytes_to_copy = length; 5170c0d06caSMauro Carvalho Chehab 5180c0d06caSMauro Carvalho Chehab if (dma_q->lines_completed >= dma_q->lines_per_field) { 5190c0d06caSMauro Carvalho Chehab dma_q->bytes_left_in_line -= bytes_to_copy; 5200c0d06caSMauro Carvalho Chehab dma_q->is_partial_line = 5210c0d06caSMauro Carvalho Chehab (dma_q->bytes_left_in_line == 0) ? 0 : 1; 5220c0d06caSMauro Carvalho Chehab return 0; 5230c0d06caSMauro Carvalho Chehab } 5240c0d06caSMauro Carvalho Chehab 5250c0d06caSMauro Carvalho Chehab dma_q->is_partial_line = 1; 5260c0d06caSMauro Carvalho Chehab 5270c0d06caSMauro Carvalho Chehab /* If we don't have a buffer, just return the number of bytes we would 5280c0d06caSMauro Carvalho Chehab have copied if we had a buffer. */ 5290c0d06caSMauro Carvalho Chehab if (!buf) { 5300c0d06caSMauro Carvalho Chehab dma_q->bytes_left_in_line -= bytes_to_copy; 5310c0d06caSMauro Carvalho Chehab dma_q->is_partial_line = 5320c0d06caSMauro Carvalho Chehab (dma_q->bytes_left_in_line == 0) ? 0 : 1; 5330c0d06caSMauro Carvalho Chehab return bytes_to_copy; 5340c0d06caSMauro Carvalho Chehab } 5350c0d06caSMauro Carvalho Chehab 5360c0d06caSMauro Carvalho Chehab /* copy the data to video buffer */ 5370c0d06caSMauro Carvalho Chehab cx231xx_do_vbi_copy(dev, dma_q, p_line, bytes_to_copy); 5380c0d06caSMauro Carvalho Chehab 5390c0d06caSMauro Carvalho Chehab dma_q->pos += bytes_to_copy; 5400c0d06caSMauro Carvalho Chehab dma_q->bytes_left_in_line -= bytes_to_copy; 5410c0d06caSMauro Carvalho Chehab 5420c0d06caSMauro Carvalho Chehab if (dma_q->bytes_left_in_line == 0) { 5430c0d06caSMauro Carvalho Chehab 5440c0d06caSMauro Carvalho Chehab dma_q->bytes_left_in_line = _line_size; 5450c0d06caSMauro Carvalho Chehab dma_q->lines_completed++; 5460c0d06caSMauro Carvalho Chehab dma_q->is_partial_line = 0; 5470c0d06caSMauro Carvalho Chehab 5480c0d06caSMauro Carvalho Chehab if (cx231xx_is_vbi_buffer_done(dev, dma_q) && buf) { 5490c0d06caSMauro Carvalho Chehab 5500c0d06caSMauro Carvalho Chehab vbi_buffer_filled(dev, dma_q, buf); 5510c0d06caSMauro Carvalho Chehab 5520c0d06caSMauro Carvalho Chehab dma_q->pos = 0; 5530c0d06caSMauro Carvalho Chehab dma_q->lines_completed = 0; 5540c0d06caSMauro Carvalho Chehab cx231xx_reset_vbi_buffer(dev, dma_q); 5550c0d06caSMauro Carvalho Chehab } 5560c0d06caSMauro Carvalho Chehab } 5570c0d06caSMauro Carvalho Chehab 5580c0d06caSMauro Carvalho Chehab return bytes_to_copy; 5590c0d06caSMauro Carvalho Chehab } 5600c0d06caSMauro Carvalho Chehab 5610c0d06caSMauro Carvalho Chehab /* 5620c0d06caSMauro Carvalho Chehab * video-buf generic routine to get the next available buffer 5630c0d06caSMauro Carvalho Chehab */ 5640c0d06caSMauro Carvalho Chehab static inline void get_next_vbi_buf(struct cx231xx_dmaqueue *dma_q, 5650c0d06caSMauro Carvalho Chehab struct cx231xx_buffer **buf) 5660c0d06caSMauro Carvalho Chehab { 5670c0d06caSMauro Carvalho Chehab struct cx231xx_video_mode *vmode = 5680c0d06caSMauro Carvalho Chehab container_of(dma_q, struct cx231xx_video_mode, vidq); 5690c0d06caSMauro Carvalho Chehab struct cx231xx *dev = container_of(vmode, struct cx231xx, vbi_mode); 5700c0d06caSMauro Carvalho Chehab char *outp; 5710c0d06caSMauro Carvalho Chehab 5720c0d06caSMauro Carvalho Chehab if (list_empty(&dma_q->active)) { 573336fea92SMauro Carvalho Chehab dev_err(dev->dev, "No active queue to serve\n"); 5740c0d06caSMauro Carvalho Chehab dev->vbi_mode.bulk_ctl.buf = NULL; 5750c0d06caSMauro Carvalho Chehab *buf = NULL; 5760c0d06caSMauro Carvalho Chehab return; 5770c0d06caSMauro Carvalho Chehab } 5780c0d06caSMauro Carvalho Chehab 5790c0d06caSMauro Carvalho Chehab /* Get the next buffer */ 5807c617138SHans Verkuil *buf = list_entry(dma_q->active.next, struct cx231xx_buffer, list); 5810c0d06caSMauro Carvalho Chehab 5820c0d06caSMauro Carvalho Chehab /* Cleans up buffer - Useful for testing for frame/URB loss */ 5837c617138SHans Verkuil outp = vb2_plane_vaddr(&(*buf)->vb.vb2_buf, 0); 5847c617138SHans Verkuil memset(outp, 0, vb2_plane_size(&(*buf)->vb.vb2_buf, 0)); 5850c0d06caSMauro Carvalho Chehab 5860c0d06caSMauro Carvalho Chehab dev->vbi_mode.bulk_ctl.buf = *buf; 5870c0d06caSMauro Carvalho Chehab 5880c0d06caSMauro Carvalho Chehab return; 5890c0d06caSMauro Carvalho Chehab } 5900c0d06caSMauro Carvalho Chehab 5910c0d06caSMauro Carvalho Chehab void cx231xx_reset_vbi_buffer(struct cx231xx *dev, 5920c0d06caSMauro Carvalho Chehab struct cx231xx_dmaqueue *dma_q) 5930c0d06caSMauro Carvalho Chehab { 5940c0d06caSMauro Carvalho Chehab struct cx231xx_buffer *buf; 5950c0d06caSMauro Carvalho Chehab 5960c0d06caSMauro Carvalho Chehab buf = dev->vbi_mode.bulk_ctl.buf; 5970c0d06caSMauro Carvalho Chehab 5980c0d06caSMauro Carvalho Chehab if (buf == NULL) { 5990c0d06caSMauro Carvalho Chehab /* first try to get the buffer */ 6000c0d06caSMauro Carvalho Chehab get_next_vbi_buf(dma_q, &buf); 6010c0d06caSMauro Carvalho Chehab 6020c0d06caSMauro Carvalho Chehab dma_q->pos = 0; 6030c0d06caSMauro Carvalho Chehab dma_q->current_field = -1; 6040c0d06caSMauro Carvalho Chehab } 6050c0d06caSMauro Carvalho Chehab 6060c0d06caSMauro Carvalho Chehab dma_q->bytes_left_in_line = dev->width << 1; 6070c0d06caSMauro Carvalho Chehab dma_q->lines_completed = 0; 6080c0d06caSMauro Carvalho Chehab } 6090c0d06caSMauro Carvalho Chehab 6100c0d06caSMauro Carvalho Chehab int cx231xx_do_vbi_copy(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q, 6110c0d06caSMauro Carvalho Chehab u8 *p_buffer, u32 bytes_to_copy) 6120c0d06caSMauro Carvalho Chehab { 6130c0d06caSMauro Carvalho Chehab u8 *p_out_buffer = NULL; 6140c0d06caSMauro Carvalho Chehab u32 current_line_bytes_copied = 0; 6150c0d06caSMauro Carvalho Chehab struct cx231xx_buffer *buf; 6160c0d06caSMauro Carvalho Chehab u32 _line_size = dev->width << 1; 6170c0d06caSMauro Carvalho Chehab void *startwrite; 6180c0d06caSMauro Carvalho Chehab int offset, lencopy; 6190c0d06caSMauro Carvalho Chehab 6200c0d06caSMauro Carvalho Chehab buf = dev->vbi_mode.bulk_ctl.buf; 6210c0d06caSMauro Carvalho Chehab 6220c0d06caSMauro Carvalho Chehab if (buf == NULL) 6230c0d06caSMauro Carvalho Chehab return -EINVAL; 6240c0d06caSMauro Carvalho Chehab 6257c617138SHans Verkuil p_out_buffer = vb2_plane_vaddr(&buf->vb.vb2_buf, 0); 6260c0d06caSMauro Carvalho Chehab 6270c0d06caSMauro Carvalho Chehab if (dma_q->bytes_left_in_line != _line_size) { 6280c0d06caSMauro Carvalho Chehab current_line_bytes_copied = 6290c0d06caSMauro Carvalho Chehab _line_size - dma_q->bytes_left_in_line; 6300c0d06caSMauro Carvalho Chehab } 6310c0d06caSMauro Carvalho Chehab 6320c0d06caSMauro Carvalho Chehab offset = (dma_q->lines_completed * _line_size) + 6330c0d06caSMauro Carvalho Chehab current_line_bytes_copied; 6340c0d06caSMauro Carvalho Chehab 6350c0d06caSMauro Carvalho Chehab if (dma_q->current_field == 2) { 6360c0d06caSMauro Carvalho Chehab /* Populate the second half of the frame */ 6370c0d06caSMauro Carvalho Chehab offset += (dev->width * 2 * dma_q->lines_per_field); 6380c0d06caSMauro Carvalho Chehab } 6390c0d06caSMauro Carvalho Chehab 6400c0d06caSMauro Carvalho Chehab /* prepare destination address */ 6410c0d06caSMauro Carvalho Chehab startwrite = p_out_buffer + offset; 6420c0d06caSMauro Carvalho Chehab 6430c0d06caSMauro Carvalho Chehab lencopy = dma_q->bytes_left_in_line > bytes_to_copy ? 6440c0d06caSMauro Carvalho Chehab bytes_to_copy : dma_q->bytes_left_in_line; 6450c0d06caSMauro Carvalho Chehab 6460c0d06caSMauro Carvalho Chehab memcpy(startwrite, p_buffer, lencopy); 6470c0d06caSMauro Carvalho Chehab 6480c0d06caSMauro Carvalho Chehab return 0; 6490c0d06caSMauro Carvalho Chehab } 6500c0d06caSMauro Carvalho Chehab 6510c0d06caSMauro Carvalho Chehab u8 cx231xx_is_vbi_buffer_done(struct cx231xx *dev, 6520c0d06caSMauro Carvalho Chehab struct cx231xx_dmaqueue *dma_q) 6530c0d06caSMauro Carvalho Chehab { 6540c0d06caSMauro Carvalho Chehab u32 height = 0; 6550c0d06caSMauro Carvalho Chehab 6560c0d06caSMauro Carvalho Chehab height = ((dev->norm & V4L2_STD_625_50) ? 6570c0d06caSMauro Carvalho Chehab PAL_VBI_LINES : NTSC_VBI_LINES); 6580c0d06caSMauro Carvalho Chehab if (dma_q->lines_completed == height && dma_q->current_field == 2) 6590c0d06caSMauro Carvalho Chehab return 1; 6600c0d06caSMauro Carvalho Chehab else 6610c0d06caSMauro Carvalho Chehab return 0; 6620c0d06caSMauro Carvalho Chehab } 663