18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// DVB USB compliant linux driver for Conexant USB reference design - 48c2ecf20Sopenharmony_ci// (analog part). 58c2ecf20Sopenharmony_ci// 68c2ecf20Sopenharmony_ci// Copyright (C) 2011, 2017, 2018 78c2ecf20Sopenharmony_ci// Maciej S. Szmigiero (mail@maciej.szmigiero.name) 88c2ecf20Sopenharmony_ci// 98c2ecf20Sopenharmony_ci// In case there are new analog / DVB-T hybrid devices released in the market 108c2ecf20Sopenharmony_ci// using the same general design as Medion MD95700: a CX25840 video decoder 118c2ecf20Sopenharmony_ci// outputting a BT.656 stream to a USB bridge chip which then forwards it to 128c2ecf20Sopenharmony_ci// the host in isochronous USB packets this code should be made generic, with 138c2ecf20Sopenharmony_ci// board specific bits implemented via separate card structures. 148c2ecf20Sopenharmony_ci// 158c2ecf20Sopenharmony_ci// This is, however, unlikely as the Medion model was released 168c2ecf20Sopenharmony_ci// years ago (in 2005). 178c2ecf20Sopenharmony_ci// 188c2ecf20Sopenharmony_ci// TODO: 198c2ecf20Sopenharmony_ci// * audio support, 208c2ecf20Sopenharmony_ci// * finish radio support (requires audio of course), 218c2ecf20Sopenharmony_ci// * VBI support, 228c2ecf20Sopenharmony_ci// * controls support 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include <linux/bitops.h> 258c2ecf20Sopenharmony_ci#include <linux/device.h> 268c2ecf20Sopenharmony_ci#include <linux/slab.h> 278c2ecf20Sopenharmony_ci#include <linux/string.h> 288c2ecf20Sopenharmony_ci#include <linux/ktime.h> 298c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 308c2ecf20Sopenharmony_ci#include <media/drv-intf/cx25840.h> 318c2ecf20Sopenharmony_ci#include <media/tuner.h> 328c2ecf20Sopenharmony_ci#include <media/v4l2-fh.h> 338c2ecf20Sopenharmony_ci#include <media/v4l2-ioctl.h> 348c2ecf20Sopenharmony_ci#include <media/v4l2-subdev.h> 358c2ecf20Sopenharmony_ci#include <media/videobuf2-vmalloc.h> 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#include "cxusb.h" 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic int cxusb_medion_v_queue_setup(struct vb2_queue *q, 408c2ecf20Sopenharmony_ci unsigned int *num_buffers, 418c2ecf20Sopenharmony_ci unsigned int *num_planes, 428c2ecf20Sopenharmony_ci unsigned int sizes[], 438c2ecf20Sopenharmony_ci struct device *alloc_devs[]) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci struct dvb_usb_device *dvbdev = vb2_get_drv_priv(q); 468c2ecf20Sopenharmony_ci struct cxusb_medion_dev *cxdev = dvbdev->priv; 478c2ecf20Sopenharmony_ci unsigned int size = cxdev->width * cxdev->height * 2; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci if (*num_planes > 0) { 508c2ecf20Sopenharmony_ci if (*num_planes != 1) 518c2ecf20Sopenharmony_ci return -EINVAL; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci if (sizes[0] < size) 548c2ecf20Sopenharmony_ci return -EINVAL; 558c2ecf20Sopenharmony_ci } else { 568c2ecf20Sopenharmony_ci *num_planes = 1; 578c2ecf20Sopenharmony_ci sizes[0] = size; 588c2ecf20Sopenharmony_ci } 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci return 0; 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic int cxusb_medion_v_buf_init(struct vb2_buffer *vb) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci struct dvb_usb_device *dvbdev = vb2_get_drv_priv(vb->vb2_queue); 668c2ecf20Sopenharmony_ci struct cxusb_medion_dev *cxdev = dvbdev->priv; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci cxusb_vprintk(dvbdev, OPS, "buffer init\n"); 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci if (vb2_plane_size(vb, 0) < cxdev->width * cxdev->height * 2) 718c2ecf20Sopenharmony_ci return -ENOMEM; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci cxusb_vprintk(dvbdev, OPS, "buffer OK\n"); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci return 0; 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic void cxusb_auxbuf_init(struct dvb_usb_device *dvbdev, 798c2ecf20Sopenharmony_ci struct cxusb_medion_auxbuf *auxbuf, 808c2ecf20Sopenharmony_ci u8 *buf, unsigned int len) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci cxusb_vprintk(dvbdev, AUXB, "initializing auxbuf of len %u\n", len); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci auxbuf->buf = buf; 858c2ecf20Sopenharmony_ci auxbuf->len = len; 868c2ecf20Sopenharmony_ci auxbuf->paylen = 0; 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic void cxusb_auxbuf_head_trim(struct dvb_usb_device *dvbdev, 908c2ecf20Sopenharmony_ci struct cxusb_medion_auxbuf *auxbuf, 918c2ecf20Sopenharmony_ci unsigned int pos) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci if (pos == 0) 948c2ecf20Sopenharmony_ci return; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci if (WARN_ON(pos > auxbuf->paylen)) 978c2ecf20Sopenharmony_ci return; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci cxusb_vprintk(dvbdev, AUXB, 1008c2ecf20Sopenharmony_ci "trimming auxbuf len by %u to %u\n", 1018c2ecf20Sopenharmony_ci pos, auxbuf->paylen - pos); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci memmove(auxbuf->buf, auxbuf->buf + pos, auxbuf->paylen - pos); 1048c2ecf20Sopenharmony_ci auxbuf->paylen -= pos; 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic unsigned int cxusb_auxbuf_paylen(struct cxusb_medion_auxbuf *auxbuf) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci return auxbuf->paylen; 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic bool cxusb_auxbuf_make_space(struct dvb_usb_device *dvbdev, 1138c2ecf20Sopenharmony_ci struct cxusb_medion_auxbuf *auxbuf, 1148c2ecf20Sopenharmony_ci unsigned int howmuch) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci unsigned int freespace; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci if (WARN_ON(howmuch >= auxbuf->len)) 1198c2ecf20Sopenharmony_ci howmuch = auxbuf->len - 1; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci freespace = auxbuf->len - cxusb_auxbuf_paylen(auxbuf); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci cxusb_vprintk(dvbdev, AUXB, "freespace is %u\n", freespace); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci if (freespace >= howmuch) 1268c2ecf20Sopenharmony_ci return true; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci howmuch -= freespace; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci cxusb_vprintk(dvbdev, AUXB, "will overwrite %u bytes of buffer\n", 1318c2ecf20Sopenharmony_ci howmuch); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci cxusb_auxbuf_head_trim(dvbdev, auxbuf, howmuch); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci return false; 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci/* returns false if some data was overwritten */ 1398c2ecf20Sopenharmony_cistatic bool cxusb_auxbuf_append_urb(struct dvb_usb_device *dvbdev, 1408c2ecf20Sopenharmony_ci struct cxusb_medion_auxbuf *auxbuf, 1418c2ecf20Sopenharmony_ci struct urb *urb) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci unsigned long len; 1448c2ecf20Sopenharmony_ci int i; 1458c2ecf20Sopenharmony_ci bool ret; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci for (i = 0, len = 0; i < urb->number_of_packets; i++) 1488c2ecf20Sopenharmony_ci len += urb->iso_frame_desc[i].actual_length; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci ret = cxusb_auxbuf_make_space(dvbdev, auxbuf, len); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci for (i = 0; i < urb->number_of_packets; i++) { 1538c2ecf20Sopenharmony_ci unsigned int to_copy; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci to_copy = urb->iso_frame_desc[i].actual_length; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci memcpy(auxbuf->buf + auxbuf->paylen, urb->transfer_buffer + 1588c2ecf20Sopenharmony_ci urb->iso_frame_desc[i].offset, to_copy); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci auxbuf->paylen += to_copy; 1618c2ecf20Sopenharmony_ci } 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci return ret; 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic bool cxusb_auxbuf_copy(struct cxusb_medion_auxbuf *auxbuf, 1678c2ecf20Sopenharmony_ci unsigned int pos, unsigned char *dest, 1688c2ecf20Sopenharmony_ci unsigned int len) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci if (pos + len > auxbuf->paylen) 1718c2ecf20Sopenharmony_ci return false; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci memcpy(dest, auxbuf->buf + pos, len); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci return true; 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic bool cxusb_medion_cf_refc_fld_chg(struct dvb_usb_device *dvbdev, 1798c2ecf20Sopenharmony_ci struct cxusb_bt656_params *bt656, 1808c2ecf20Sopenharmony_ci bool firstfield, 1818c2ecf20Sopenharmony_ci unsigned int maxlines, 1828c2ecf20Sopenharmony_ci unsigned int maxlinesamples, 1838c2ecf20Sopenharmony_ci unsigned char buf[4]) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci bool firstfield_code = (buf[3] & CXUSB_BT656_FIELD_MASK) == 1868c2ecf20Sopenharmony_ci CXUSB_BT656_FIELD_1; 1878c2ecf20Sopenharmony_ci unsigned int remlines; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci if (bt656->line == 0 || firstfield == firstfield_code) 1908c2ecf20Sopenharmony_ci return false; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci if (bt656->fmode == LINE_SAMPLES) { 1938c2ecf20Sopenharmony_ci unsigned int remsamples = maxlinesamples - 1948c2ecf20Sopenharmony_ci bt656->linesamples; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci cxusb_vprintk(dvbdev, BT656, 1978c2ecf20Sopenharmony_ci "field %c after line %u field change\n", 1988c2ecf20Sopenharmony_ci firstfield ? '1' : '2', bt656->line); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci if (bt656->buf && remsamples > 0) { 2018c2ecf20Sopenharmony_ci memset(bt656->buf, 0, remsamples); 2028c2ecf20Sopenharmony_ci bt656->buf += remsamples; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci cxusb_vprintk(dvbdev, BT656, 2058c2ecf20Sopenharmony_ci "field %c line %u %u samples still remaining (of %u)\n", 2068c2ecf20Sopenharmony_ci firstfield ? '1' : '2', 2078c2ecf20Sopenharmony_ci bt656->line, remsamples, 2088c2ecf20Sopenharmony_ci maxlinesamples); 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci bt656->line++; 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci remlines = maxlines - bt656->line; 2158c2ecf20Sopenharmony_ci if (bt656->buf && remlines > 0) { 2168c2ecf20Sopenharmony_ci memset(bt656->buf, 0, remlines * maxlinesamples); 2178c2ecf20Sopenharmony_ci bt656->buf += remlines * maxlinesamples; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci cxusb_vprintk(dvbdev, BT656, 2208c2ecf20Sopenharmony_ci "field %c %u lines still remaining (of %u)\n", 2218c2ecf20Sopenharmony_ci firstfield ? '1' : '2', remlines, 2228c2ecf20Sopenharmony_ci maxlines); 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci return true; 2268c2ecf20Sopenharmony_ci} 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_cistatic void cxusb_medion_cf_refc_start_sch(struct dvb_usb_device *dvbdev, 2298c2ecf20Sopenharmony_ci struct cxusb_bt656_params *bt656, 2308c2ecf20Sopenharmony_ci bool firstfield, 2318c2ecf20Sopenharmony_ci unsigned char buf[4]) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci bool firstfield_code = (buf[3] & CXUSB_BT656_FIELD_MASK) == 2348c2ecf20Sopenharmony_ci CXUSB_BT656_FIELD_1; 2358c2ecf20Sopenharmony_ci bool sav_code = (buf[3] & CXUSB_BT656_SEAV_MASK) == 2368c2ecf20Sopenharmony_ci CXUSB_BT656_SEAV_SAV; 2378c2ecf20Sopenharmony_ci bool vbi_code = (buf[3] & CXUSB_BT656_VBI_MASK) == 2388c2ecf20Sopenharmony_ci CXUSB_BT656_VBI_ON; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci if (!sav_code || firstfield != firstfield_code) 2418c2ecf20Sopenharmony_ci return; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci if (!vbi_code) { 2448c2ecf20Sopenharmony_ci cxusb_vprintk(dvbdev, BT656, "line start @ pos %u\n", 2458c2ecf20Sopenharmony_ci bt656->pos); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci bt656->linesamples = 0; 2488c2ecf20Sopenharmony_ci bt656->fmode = LINE_SAMPLES; 2498c2ecf20Sopenharmony_ci } else { 2508c2ecf20Sopenharmony_ci cxusb_vprintk(dvbdev, BT656, "VBI start @ pos %u\n", 2518c2ecf20Sopenharmony_ci bt656->pos); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci bt656->fmode = VBI_SAMPLES; 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistatic void cxusb_medion_cf_refc_line_smpl(struct dvb_usb_device *dvbdev, 2588c2ecf20Sopenharmony_ci struct cxusb_bt656_params *bt656, 2598c2ecf20Sopenharmony_ci bool firstfield, 2608c2ecf20Sopenharmony_ci unsigned int maxlinesamples, 2618c2ecf20Sopenharmony_ci unsigned char buf[4]) 2628c2ecf20Sopenharmony_ci{ 2638c2ecf20Sopenharmony_ci bool sav_code = (buf[3] & CXUSB_BT656_SEAV_MASK) == 2648c2ecf20Sopenharmony_ci CXUSB_BT656_SEAV_SAV; 2658c2ecf20Sopenharmony_ci unsigned int remsamples; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci if (sav_code) 2688c2ecf20Sopenharmony_ci cxusb_vprintk(dvbdev, BT656, 2698c2ecf20Sopenharmony_ci "SAV in line samples @ line %u, pos %u\n", 2708c2ecf20Sopenharmony_ci bt656->line, bt656->pos); 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci remsamples = maxlinesamples - bt656->linesamples; 2738c2ecf20Sopenharmony_ci if (bt656->buf && remsamples > 0) { 2748c2ecf20Sopenharmony_ci memset(bt656->buf, 0, remsamples); 2758c2ecf20Sopenharmony_ci bt656->buf += remsamples; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci cxusb_vprintk(dvbdev, BT656, 2788c2ecf20Sopenharmony_ci "field %c line %u %u samples still remaining (of %u)\n", 2798c2ecf20Sopenharmony_ci firstfield ? '1' : '2', bt656->line, remsamples, 2808c2ecf20Sopenharmony_ci maxlinesamples); 2818c2ecf20Sopenharmony_ci } 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci bt656->fmode = START_SEARCH; 2848c2ecf20Sopenharmony_ci bt656->line++; 2858c2ecf20Sopenharmony_ci} 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_cistatic void cxusb_medion_cf_refc_vbi_smpl(struct dvb_usb_device *dvbdev, 2888c2ecf20Sopenharmony_ci struct cxusb_bt656_params *bt656, 2898c2ecf20Sopenharmony_ci unsigned char buf[4]) 2908c2ecf20Sopenharmony_ci{ 2918c2ecf20Sopenharmony_ci bool sav_code = (buf[3] & CXUSB_BT656_SEAV_MASK) == 2928c2ecf20Sopenharmony_ci CXUSB_BT656_SEAV_SAV; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci if (sav_code) 2958c2ecf20Sopenharmony_ci cxusb_vprintk(dvbdev, BT656, "SAV in VBI samples @ pos %u\n", 2968c2ecf20Sopenharmony_ci bt656->pos); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci bt656->fmode = START_SEARCH; 2998c2ecf20Sopenharmony_ci} 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci/* returns whether the whole 4-byte code should be skipped in the buffer */ 3028c2ecf20Sopenharmony_cistatic bool cxusb_medion_cf_ref_code(struct dvb_usb_device *dvbdev, 3038c2ecf20Sopenharmony_ci struct cxusb_bt656_params *bt656, 3048c2ecf20Sopenharmony_ci bool firstfield, 3058c2ecf20Sopenharmony_ci unsigned int maxlines, 3068c2ecf20Sopenharmony_ci unsigned int maxlinesamples, 3078c2ecf20Sopenharmony_ci unsigned char buf[4]) 3088c2ecf20Sopenharmony_ci{ 3098c2ecf20Sopenharmony_ci if (bt656->fmode == START_SEARCH) { 3108c2ecf20Sopenharmony_ci cxusb_medion_cf_refc_start_sch(dvbdev, bt656, firstfield, buf); 3118c2ecf20Sopenharmony_ci } else if (bt656->fmode == LINE_SAMPLES) { 3128c2ecf20Sopenharmony_ci cxusb_medion_cf_refc_line_smpl(dvbdev, bt656, firstfield, 3138c2ecf20Sopenharmony_ci maxlinesamples, buf); 3148c2ecf20Sopenharmony_ci return false; 3158c2ecf20Sopenharmony_ci } else if (bt656->fmode == VBI_SAMPLES) { 3168c2ecf20Sopenharmony_ci cxusb_medion_cf_refc_vbi_smpl(dvbdev, bt656, buf); 3178c2ecf20Sopenharmony_ci return false; 3188c2ecf20Sopenharmony_ci } 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci return true; 3218c2ecf20Sopenharmony_ci} 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_cistatic bool cxusb_medion_cs_start_sch(struct dvb_usb_device *dvbdev, 3248c2ecf20Sopenharmony_ci struct cxusb_medion_auxbuf *auxbuf, 3258c2ecf20Sopenharmony_ci struct cxusb_bt656_params *bt656, 3268c2ecf20Sopenharmony_ci unsigned int maxlinesamples) 3278c2ecf20Sopenharmony_ci{ 3288c2ecf20Sopenharmony_ci unsigned char buf[64]; 3298c2ecf20Sopenharmony_ci unsigned int idx; 3308c2ecf20Sopenharmony_ci unsigned int tocheck = clamp_t(size_t, maxlinesamples / 4, 3, 3318c2ecf20Sopenharmony_ci sizeof(buf)); 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci if (!cxusb_auxbuf_copy(auxbuf, bt656->pos + 1, buf, tocheck)) 3348c2ecf20Sopenharmony_ci return false; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci for (idx = 0; idx <= tocheck - 3; idx++) 3378c2ecf20Sopenharmony_ci if (memcmp(buf + idx, CXUSB_BT656_PREAMBLE, 3) == 0) { 3388c2ecf20Sopenharmony_ci bt656->pos += (1 + idx); 3398c2ecf20Sopenharmony_ci return true; 3408c2ecf20Sopenharmony_ci } 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci cxusb_vprintk(dvbdev, BT656, "line %u early start, pos %u\n", 3438c2ecf20Sopenharmony_ci bt656->line, bt656->pos); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci bt656->linesamples = 0; 3468c2ecf20Sopenharmony_ci bt656->fmode = LINE_SAMPLES; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci return true; 3498c2ecf20Sopenharmony_ci} 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_cistatic void cxusb_medion_cs_line_smpl(struct cxusb_bt656_params *bt656, 3528c2ecf20Sopenharmony_ci unsigned int maxlinesamples, 3538c2ecf20Sopenharmony_ci unsigned char val) 3548c2ecf20Sopenharmony_ci{ 3558c2ecf20Sopenharmony_ci if (bt656->buf) 3568c2ecf20Sopenharmony_ci *(bt656->buf++) = val; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci bt656->linesamples++; 3598c2ecf20Sopenharmony_ci bt656->pos++; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci if (bt656->linesamples >= maxlinesamples) { 3628c2ecf20Sopenharmony_ci bt656->fmode = START_SEARCH; 3638c2ecf20Sopenharmony_ci bt656->line++; 3648c2ecf20Sopenharmony_ci } 3658c2ecf20Sopenharmony_ci} 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_cistatic bool cxusb_medion_copy_samples(struct dvb_usb_device *dvbdev, 3688c2ecf20Sopenharmony_ci struct cxusb_medion_auxbuf *auxbuf, 3698c2ecf20Sopenharmony_ci struct cxusb_bt656_params *bt656, 3708c2ecf20Sopenharmony_ci unsigned int maxlinesamples, 3718c2ecf20Sopenharmony_ci unsigned char val) 3728c2ecf20Sopenharmony_ci{ 3738c2ecf20Sopenharmony_ci if (bt656->fmode == START_SEARCH && bt656->line > 0) 3748c2ecf20Sopenharmony_ci return cxusb_medion_cs_start_sch(dvbdev, auxbuf, bt656, 3758c2ecf20Sopenharmony_ci maxlinesamples); 3768c2ecf20Sopenharmony_ci else if (bt656->fmode == LINE_SAMPLES) 3778c2ecf20Sopenharmony_ci cxusb_medion_cs_line_smpl(bt656, maxlinesamples, val); 3788c2ecf20Sopenharmony_ci else /* TODO: copy VBI samples */ 3798c2ecf20Sopenharmony_ci bt656->pos++; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci return true; 3828c2ecf20Sopenharmony_ci} 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_cistatic bool cxusb_medion_copy_field(struct dvb_usb_device *dvbdev, 3858c2ecf20Sopenharmony_ci struct cxusb_medion_auxbuf *auxbuf, 3868c2ecf20Sopenharmony_ci struct cxusb_bt656_params *bt656, 3878c2ecf20Sopenharmony_ci bool firstfield, 3888c2ecf20Sopenharmony_ci unsigned int maxlines, 3898c2ecf20Sopenharmony_ci unsigned int maxlinesmpls) 3908c2ecf20Sopenharmony_ci{ 3918c2ecf20Sopenharmony_ci while (bt656->line < maxlines) { 3928c2ecf20Sopenharmony_ci unsigned char val; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci if (!cxusb_auxbuf_copy(auxbuf, bt656->pos, &val, 1)) 3958c2ecf20Sopenharmony_ci break; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci if (val == CXUSB_BT656_PREAMBLE[0]) { 3988c2ecf20Sopenharmony_ci unsigned char buf[4]; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci buf[0] = val; 4018c2ecf20Sopenharmony_ci if (!cxusb_auxbuf_copy(auxbuf, bt656->pos + 1, 4028c2ecf20Sopenharmony_ci buf + 1, 3)) 4038c2ecf20Sopenharmony_ci break; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci if (buf[1] == CXUSB_BT656_PREAMBLE[1] && 4068c2ecf20Sopenharmony_ci buf[2] == CXUSB_BT656_PREAMBLE[2]) { 4078c2ecf20Sopenharmony_ci /* 4088c2ecf20Sopenharmony_ci * is this a field change? 4098c2ecf20Sopenharmony_ci * if so, terminate copying the current field 4108c2ecf20Sopenharmony_ci */ 4118c2ecf20Sopenharmony_ci if (cxusb_medion_cf_refc_fld_chg(dvbdev, 4128c2ecf20Sopenharmony_ci bt656, 4138c2ecf20Sopenharmony_ci firstfield, 4148c2ecf20Sopenharmony_ci maxlines, 4158c2ecf20Sopenharmony_ci maxlinesmpls, 4168c2ecf20Sopenharmony_ci buf)) 4178c2ecf20Sopenharmony_ci return true; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci if (cxusb_medion_cf_ref_code(dvbdev, bt656, 4208c2ecf20Sopenharmony_ci firstfield, 4218c2ecf20Sopenharmony_ci maxlines, 4228c2ecf20Sopenharmony_ci maxlinesmpls, 4238c2ecf20Sopenharmony_ci buf)) 4248c2ecf20Sopenharmony_ci bt656->pos += 4; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci continue; 4278c2ecf20Sopenharmony_ci } 4288c2ecf20Sopenharmony_ci } 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci if (!cxusb_medion_copy_samples(dvbdev, auxbuf, bt656, 4318c2ecf20Sopenharmony_ci maxlinesmpls, val)) 4328c2ecf20Sopenharmony_ci break; 4338c2ecf20Sopenharmony_ci } 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci if (bt656->line < maxlines) { 4368c2ecf20Sopenharmony_ci cxusb_vprintk(dvbdev, BT656, 4378c2ecf20Sopenharmony_ci "end of buffer pos = %u, line = %u\n", 4388c2ecf20Sopenharmony_ci bt656->pos, bt656->line); 4398c2ecf20Sopenharmony_ci return false; 4408c2ecf20Sopenharmony_ci } 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci return true; 4438c2ecf20Sopenharmony_ci} 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_cistatic bool cxusb_medion_v_process_auxbuf(struct cxusb_medion_dev *cxdev, 4468c2ecf20Sopenharmony_ci bool reset) 4478c2ecf20Sopenharmony_ci{ 4488c2ecf20Sopenharmony_ci struct dvb_usb_device *dvbdev = cxdev->dvbdev; 4498c2ecf20Sopenharmony_ci struct cxusb_bt656_params *bt656 = &cxdev->bt656; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci /* 4528c2ecf20Sopenharmony_ci * if this is a new frame 4538c2ecf20Sopenharmony_ci * fetch a buffer from list 4548c2ecf20Sopenharmony_ci */ 4558c2ecf20Sopenharmony_ci if (bt656->mode == NEW_FRAME) { 4568c2ecf20Sopenharmony_ci if (!list_empty(&cxdev->buflist)) { 4578c2ecf20Sopenharmony_ci cxdev->vbuf = 4588c2ecf20Sopenharmony_ci list_first_entry(&cxdev->buflist, 4598c2ecf20Sopenharmony_ci struct cxusb_medion_vbuffer, 4608c2ecf20Sopenharmony_ci list); 4618c2ecf20Sopenharmony_ci list_del(&cxdev->vbuf->list); 4628c2ecf20Sopenharmony_ci } else { 4638c2ecf20Sopenharmony_ci dev_warn(&dvbdev->udev->dev, "no free buffers\n"); 4648c2ecf20Sopenharmony_ci } 4658c2ecf20Sopenharmony_ci } 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci if (bt656->mode == NEW_FRAME || reset) { 4688c2ecf20Sopenharmony_ci cxusb_vprintk(dvbdev, URB, "will copy field 1\n"); 4698c2ecf20Sopenharmony_ci bt656->pos = 0; 4708c2ecf20Sopenharmony_ci bt656->mode = FIRST_FIELD; 4718c2ecf20Sopenharmony_ci bt656->fmode = START_SEARCH; 4728c2ecf20Sopenharmony_ci bt656->line = 0; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci if (cxdev->vbuf) { 4758c2ecf20Sopenharmony_ci cxdev->vbuf->vb2.vb2_buf.timestamp = ktime_get_ns(); 4768c2ecf20Sopenharmony_ci bt656->buf = vb2_plane_vaddr(&cxdev->vbuf->vb2.vb2_buf, 4778c2ecf20Sopenharmony_ci 0); 4788c2ecf20Sopenharmony_ci } 4798c2ecf20Sopenharmony_ci } 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci if (bt656->mode == FIRST_FIELD) { 4828c2ecf20Sopenharmony_ci if (!cxusb_medion_copy_field(dvbdev, &cxdev->auxbuf, bt656, 4838c2ecf20Sopenharmony_ci true, cxdev->height / 2, 4848c2ecf20Sopenharmony_ci cxdev->width * 2)) 4858c2ecf20Sopenharmony_ci return false; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci /* 4888c2ecf20Sopenharmony_ci * do not trim buffer there in case 4898c2ecf20Sopenharmony_ci * we need to reset the search later 4908c2ecf20Sopenharmony_ci */ 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci cxusb_vprintk(dvbdev, URB, "will copy field 2\n"); 4938c2ecf20Sopenharmony_ci bt656->mode = SECOND_FIELD; 4948c2ecf20Sopenharmony_ci bt656->fmode = START_SEARCH; 4958c2ecf20Sopenharmony_ci bt656->line = 0; 4968c2ecf20Sopenharmony_ci } 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci if (bt656->mode == SECOND_FIELD) { 4998c2ecf20Sopenharmony_ci if (!cxusb_medion_copy_field(dvbdev, &cxdev->auxbuf, bt656, 5008c2ecf20Sopenharmony_ci false, cxdev->height / 2, 5018c2ecf20Sopenharmony_ci cxdev->width * 2)) 5028c2ecf20Sopenharmony_ci return false; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci cxusb_auxbuf_head_trim(dvbdev, &cxdev->auxbuf, bt656->pos); 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci bt656->mode = NEW_FRAME; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci if (cxdev->vbuf) { 5098c2ecf20Sopenharmony_ci vb2_set_plane_payload(&cxdev->vbuf->vb2.vb2_buf, 0, 5108c2ecf20Sopenharmony_ci cxdev->width * cxdev->height * 2); 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci cxdev->vbuf->vb2.field = cxdev->field_order; 5138c2ecf20Sopenharmony_ci cxdev->vbuf->vb2.sequence = cxdev->vbuf_sequence++; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci vb2_buffer_done(&cxdev->vbuf->vb2.vb2_buf, 5168c2ecf20Sopenharmony_ci VB2_BUF_STATE_DONE); 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci cxdev->vbuf = NULL; 5198c2ecf20Sopenharmony_ci cxdev->bt656.buf = NULL; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci cxusb_vprintk(dvbdev, URB, "frame done\n"); 5228c2ecf20Sopenharmony_ci } else { 5238c2ecf20Sopenharmony_ci cxusb_vprintk(dvbdev, URB, "frame skipped\n"); 5248c2ecf20Sopenharmony_ci cxdev->vbuf_sequence++; 5258c2ecf20Sopenharmony_ci } 5268c2ecf20Sopenharmony_ci } 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci return true; 5298c2ecf20Sopenharmony_ci} 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_cistatic bool cxusb_medion_v_complete_handle_urb(struct cxusb_medion_dev *cxdev, 5328c2ecf20Sopenharmony_ci bool *auxbuf_reset) 5338c2ecf20Sopenharmony_ci{ 5348c2ecf20Sopenharmony_ci struct dvb_usb_device *dvbdev = cxdev->dvbdev; 5358c2ecf20Sopenharmony_ci unsigned int urbn; 5368c2ecf20Sopenharmony_ci struct urb *urb; 5378c2ecf20Sopenharmony_ci int ret; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci *auxbuf_reset = false; 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci urbn = cxdev->nexturb; 5428c2ecf20Sopenharmony_ci if (!test_bit(urbn, &cxdev->urbcomplete)) 5438c2ecf20Sopenharmony_ci return false; 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci clear_bit(urbn, &cxdev->urbcomplete); 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci do { 5488c2ecf20Sopenharmony_ci cxdev->nexturb++; 5498c2ecf20Sopenharmony_ci cxdev->nexturb %= CXUSB_VIDEO_URBS; 5508c2ecf20Sopenharmony_ci urb = cxdev->streamurbs[cxdev->nexturb]; 5518c2ecf20Sopenharmony_ci } while (!urb); 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci urb = cxdev->streamurbs[urbn]; 5548c2ecf20Sopenharmony_ci cxusb_vprintk(dvbdev, URB, "URB %u status = %d\n", urbn, urb->status); 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci if (urb->status == 0 || urb->status == -EXDEV) { 5578c2ecf20Sopenharmony_ci int i; 5588c2ecf20Sopenharmony_ci unsigned long len; 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci for (i = 0, len = 0; i < urb->number_of_packets; i++) 5618c2ecf20Sopenharmony_ci len += urb->iso_frame_desc[i].actual_length; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci cxusb_vprintk(dvbdev, URB, "URB %u data len = %lu\n", urbn, 5648c2ecf20Sopenharmony_ci len); 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci if (len > 0) { 5678c2ecf20Sopenharmony_ci cxusb_vprintk(dvbdev, URB, "appending URB\n"); 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci /* 5708c2ecf20Sopenharmony_ci * append new data to auxbuf while 5718c2ecf20Sopenharmony_ci * overwriting old data if necessary 5728c2ecf20Sopenharmony_ci * 5738c2ecf20Sopenharmony_ci * if any overwrite happens then we can no 5748c2ecf20Sopenharmony_ci * longer rely on consistency of the whole 5758c2ecf20Sopenharmony_ci * data so let's start again the current 5768c2ecf20Sopenharmony_ci * auxbuf frame assembling process from 5778c2ecf20Sopenharmony_ci * the beginning 5788c2ecf20Sopenharmony_ci */ 5798c2ecf20Sopenharmony_ci *auxbuf_reset = 5808c2ecf20Sopenharmony_ci !cxusb_auxbuf_append_urb(dvbdev, 5818c2ecf20Sopenharmony_ci &cxdev->auxbuf, 5828c2ecf20Sopenharmony_ci urb); 5838c2ecf20Sopenharmony_ci } 5848c2ecf20Sopenharmony_ci } 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci cxusb_vprintk(dvbdev, URB, "URB %u resubmit\n", urbn); 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci ret = usb_submit_urb(urb, GFP_KERNEL); 5898c2ecf20Sopenharmony_ci if (ret != 0) 5908c2ecf20Sopenharmony_ci dev_err(&dvbdev->udev->dev, 5918c2ecf20Sopenharmony_ci "unable to resubmit URB %u (%d), you'll have to restart streaming\n", 5928c2ecf20Sopenharmony_ci urbn, ret); 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci /* next URB is complete already? reschedule us then to handle it */ 5958c2ecf20Sopenharmony_ci return test_bit(cxdev->nexturb, &cxdev->urbcomplete); 5968c2ecf20Sopenharmony_ci} 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_cistatic void cxusb_medion_v_complete_work(struct work_struct *work) 5998c2ecf20Sopenharmony_ci{ 6008c2ecf20Sopenharmony_ci struct cxusb_medion_dev *cxdev = container_of(work, 6018c2ecf20Sopenharmony_ci struct cxusb_medion_dev, 6028c2ecf20Sopenharmony_ci urbwork); 6038c2ecf20Sopenharmony_ci struct dvb_usb_device *dvbdev = cxdev->dvbdev; 6048c2ecf20Sopenharmony_ci bool auxbuf_reset; 6058c2ecf20Sopenharmony_ci bool reschedule; 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci mutex_lock(cxdev->videodev->lock); 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci cxusb_vprintk(dvbdev, URB, "worker called, stop_streaming = %d\n", 6108c2ecf20Sopenharmony_ci (int)cxdev->stop_streaming); 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci if (cxdev->stop_streaming) 6138c2ecf20Sopenharmony_ci goto unlock; 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci reschedule = cxusb_medion_v_complete_handle_urb(cxdev, &auxbuf_reset); 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci if (cxusb_medion_v_process_auxbuf(cxdev, auxbuf_reset)) 6188c2ecf20Sopenharmony_ci /* reschedule us until auxbuf no longer can produce any frame */ 6198c2ecf20Sopenharmony_ci reschedule = true; 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci if (reschedule) { 6228c2ecf20Sopenharmony_ci cxusb_vprintk(dvbdev, URB, "rescheduling worker\n"); 6238c2ecf20Sopenharmony_ci schedule_work(&cxdev->urbwork); 6248c2ecf20Sopenharmony_ci } 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ciunlock: 6278c2ecf20Sopenharmony_ci mutex_unlock(cxdev->videodev->lock); 6288c2ecf20Sopenharmony_ci} 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_cistatic void cxusb_medion_v_complete(struct urb *u) 6318c2ecf20Sopenharmony_ci{ 6328c2ecf20Sopenharmony_ci struct dvb_usb_device *dvbdev = u->context; 6338c2ecf20Sopenharmony_ci struct cxusb_medion_dev *cxdev = dvbdev->priv; 6348c2ecf20Sopenharmony_ci unsigned int i; 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci for (i = 0; i < CXUSB_VIDEO_URBS; i++) 6378c2ecf20Sopenharmony_ci if (cxdev->streamurbs[i] == u) 6388c2ecf20Sopenharmony_ci break; 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci if (i >= CXUSB_VIDEO_URBS) { 6418c2ecf20Sopenharmony_ci dev_err(&dvbdev->udev->dev, 6428c2ecf20Sopenharmony_ci "complete on unknown URB\n"); 6438c2ecf20Sopenharmony_ci return; 6448c2ecf20Sopenharmony_ci } 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci cxusb_vprintk(dvbdev, URB, "URB %u complete\n", i); 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci set_bit(i, &cxdev->urbcomplete); 6498c2ecf20Sopenharmony_ci schedule_work(&cxdev->urbwork); 6508c2ecf20Sopenharmony_ci} 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_cistatic void cxusb_medion_urbs_free(struct cxusb_medion_dev *cxdev) 6538c2ecf20Sopenharmony_ci{ 6548c2ecf20Sopenharmony_ci unsigned int i; 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci for (i = 0; i < CXUSB_VIDEO_URBS; i++) 6578c2ecf20Sopenharmony_ci if (cxdev->streamurbs[i]) { 6588c2ecf20Sopenharmony_ci kfree(cxdev->streamurbs[i]->transfer_buffer); 6598c2ecf20Sopenharmony_ci usb_free_urb(cxdev->streamurbs[i]); 6608c2ecf20Sopenharmony_ci cxdev->streamurbs[i] = NULL; 6618c2ecf20Sopenharmony_ci } 6628c2ecf20Sopenharmony_ci} 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_cistatic void cxusb_medion_return_buffers(struct cxusb_medion_dev *cxdev, 6658c2ecf20Sopenharmony_ci bool requeue) 6668c2ecf20Sopenharmony_ci{ 6678c2ecf20Sopenharmony_ci struct cxusb_medion_vbuffer *vbuf, *vbuf_tmp; 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci list_for_each_entry_safe(vbuf, vbuf_tmp, &cxdev->buflist, 6708c2ecf20Sopenharmony_ci list) { 6718c2ecf20Sopenharmony_ci list_del(&vbuf->list); 6728c2ecf20Sopenharmony_ci vb2_buffer_done(&vbuf->vb2.vb2_buf, 6738c2ecf20Sopenharmony_ci requeue ? VB2_BUF_STATE_QUEUED : 6748c2ecf20Sopenharmony_ci VB2_BUF_STATE_ERROR); 6758c2ecf20Sopenharmony_ci } 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci if (cxdev->vbuf) { 6788c2ecf20Sopenharmony_ci vb2_buffer_done(&cxdev->vbuf->vb2.vb2_buf, 6798c2ecf20Sopenharmony_ci requeue ? VB2_BUF_STATE_QUEUED : 6808c2ecf20Sopenharmony_ci VB2_BUF_STATE_ERROR); 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci cxdev->vbuf = NULL; 6838c2ecf20Sopenharmony_ci cxdev->bt656.buf = NULL; 6848c2ecf20Sopenharmony_ci } 6858c2ecf20Sopenharmony_ci} 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_cistatic int cxusb_medion_v_ss_auxbuf_alloc(struct cxusb_medion_dev *cxdev, 6888c2ecf20Sopenharmony_ci int *npackets) 6898c2ecf20Sopenharmony_ci{ 6908c2ecf20Sopenharmony_ci struct dvb_usb_device *dvbdev = cxdev->dvbdev; 6918c2ecf20Sopenharmony_ci u8 *buf; 6928c2ecf20Sopenharmony_ci unsigned int framelen, urblen, auxbuflen; 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci framelen = (cxdev->width * 2 + 4 + 4) * 6958c2ecf20Sopenharmony_ci (cxdev->height + 50 /* VBI lines */); 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci /* 6988c2ecf20Sopenharmony_ci * try to fit a whole frame into each URB, as long as doing so 6998c2ecf20Sopenharmony_ci * does not require very high order memory allocations 7008c2ecf20Sopenharmony_ci */ 7018c2ecf20Sopenharmony_ci BUILD_BUG_ON(CXUSB_VIDEO_URB_MAX_SIZE / CXUSB_VIDEO_PKT_SIZE > 7028c2ecf20Sopenharmony_ci CXUSB_VIDEO_MAX_FRAME_PKTS); 7038c2ecf20Sopenharmony_ci *npackets = min_t(int, (framelen + CXUSB_VIDEO_PKT_SIZE - 1) / 7048c2ecf20Sopenharmony_ci CXUSB_VIDEO_PKT_SIZE, 7058c2ecf20Sopenharmony_ci CXUSB_VIDEO_URB_MAX_SIZE / CXUSB_VIDEO_PKT_SIZE); 7068c2ecf20Sopenharmony_ci urblen = *npackets * CXUSB_VIDEO_PKT_SIZE; 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci cxusb_vprintk(dvbdev, URB, 7098c2ecf20Sopenharmony_ci "each URB will have %d packets for total of %u bytes (%u x %u @ %u)\n", 7108c2ecf20Sopenharmony_ci *npackets, urblen, (unsigned int)cxdev->width, 7118c2ecf20Sopenharmony_ci (unsigned int)cxdev->height, framelen); 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci auxbuflen = framelen + urblen; 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci buf = vmalloc(auxbuflen); 7168c2ecf20Sopenharmony_ci if (!buf) 7178c2ecf20Sopenharmony_ci return -ENOMEM; 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci cxusb_auxbuf_init(dvbdev, &cxdev->auxbuf, buf, auxbuflen); 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci return 0; 7228c2ecf20Sopenharmony_ci} 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_cistatic u32 cxusb_medion_norm2field_order(v4l2_std_id norm) 7258c2ecf20Sopenharmony_ci{ 7268c2ecf20Sopenharmony_ci bool is625 = norm & V4L2_STD_625_50; 7278c2ecf20Sopenharmony_ci bool is525 = norm & V4L2_STD_525_60; 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci if (!is625 && !is525) 7308c2ecf20Sopenharmony_ci return V4L2_FIELD_NONE; 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci if (is625 && is525) 7338c2ecf20Sopenharmony_ci return V4L2_FIELD_NONE; 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci if (is625) 7368c2ecf20Sopenharmony_ci return V4L2_FIELD_SEQ_TB; 7378c2ecf20Sopenharmony_ci else /* is525 */ 7388c2ecf20Sopenharmony_ci return V4L2_FIELD_SEQ_BT; 7398c2ecf20Sopenharmony_ci} 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_cistatic u32 cxusb_medion_field_order(struct cxusb_medion_dev *cxdev) 7428c2ecf20Sopenharmony_ci{ 7438c2ecf20Sopenharmony_ci struct dvb_usb_device *dvbdev = cxdev->dvbdev; 7448c2ecf20Sopenharmony_ci u32 field; 7458c2ecf20Sopenharmony_ci int ret; 7468c2ecf20Sopenharmony_ci v4l2_std_id norm; 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci /* TV tuner is PAL-only so it is always TB */ 7498c2ecf20Sopenharmony_ci if (cxdev->input == 0) 7508c2ecf20Sopenharmony_ci return V4L2_FIELD_SEQ_TB; 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci field = cxusb_medion_norm2field_order(cxdev->norm); 7538c2ecf20Sopenharmony_ci if (field != V4L2_FIELD_NONE) 7548c2ecf20Sopenharmony_ci return field; 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci ret = v4l2_subdev_call(cxdev->cx25840, video, g_std, &norm); 7578c2ecf20Sopenharmony_ci if (ret != 0) { 7588c2ecf20Sopenharmony_ci cxusb_vprintk(dvbdev, OPS, 7598c2ecf20Sopenharmony_ci "cannot get current standard for input %u\n", 7608c2ecf20Sopenharmony_ci (unsigned int)cxdev->input); 7618c2ecf20Sopenharmony_ci } else { 7628c2ecf20Sopenharmony_ci field = cxusb_medion_norm2field_order(norm); 7638c2ecf20Sopenharmony_ci if (field != V4L2_FIELD_NONE) 7648c2ecf20Sopenharmony_ci return field; 7658c2ecf20Sopenharmony_ci } 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci dev_warn(&dvbdev->udev->dev, 7688c2ecf20Sopenharmony_ci "cannot determine field order for the current standard setup and received signal, using TB\n"); 7698c2ecf20Sopenharmony_ci return V4L2_FIELD_SEQ_TB; 7708c2ecf20Sopenharmony_ci} 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_cistatic int cxusb_medion_v_start_streaming(struct vb2_queue *q, 7738c2ecf20Sopenharmony_ci unsigned int count) 7748c2ecf20Sopenharmony_ci{ 7758c2ecf20Sopenharmony_ci struct dvb_usb_device *dvbdev = vb2_get_drv_priv(q); 7768c2ecf20Sopenharmony_ci struct cxusb_medion_dev *cxdev = dvbdev->priv; 7778c2ecf20Sopenharmony_ci u8 streamon_params[2] = { 0x03, 0x00 }; 7788c2ecf20Sopenharmony_ci int npackets, i; 7798c2ecf20Sopenharmony_ci int ret; 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci cxusb_vprintk(dvbdev, OPS, "should start streaming\n"); 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci if (cxdev->stop_streaming) { 7848c2ecf20Sopenharmony_ci /* stream is being stopped */ 7858c2ecf20Sopenharmony_ci ret = -EBUSY; 7868c2ecf20Sopenharmony_ci goto ret_retbufs; 7878c2ecf20Sopenharmony_ci } 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci cxdev->field_order = cxusb_medion_field_order(cxdev); 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci ret = v4l2_subdev_call(cxdev->cx25840, video, s_stream, 1); 7928c2ecf20Sopenharmony_ci if (ret != 0) { 7938c2ecf20Sopenharmony_ci dev_err(&dvbdev->udev->dev, 7948c2ecf20Sopenharmony_ci "unable to start stream (%d)\n", ret); 7958c2ecf20Sopenharmony_ci goto ret_retbufs; 7968c2ecf20Sopenharmony_ci } 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci ret = cxusb_ctrl_msg(dvbdev, CMD_STREAMING_ON, streamon_params, 2, 7998c2ecf20Sopenharmony_ci NULL, 0); 8008c2ecf20Sopenharmony_ci if (ret != 0) { 8018c2ecf20Sopenharmony_ci dev_err(&dvbdev->udev->dev, 8028c2ecf20Sopenharmony_ci "unable to start streaming (%d)\n", ret); 8038c2ecf20Sopenharmony_ci goto ret_unstream_cx; 8048c2ecf20Sopenharmony_ci } 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci ret = cxusb_medion_v_ss_auxbuf_alloc(cxdev, &npackets); 8078c2ecf20Sopenharmony_ci if (ret != 0) 8088c2ecf20Sopenharmony_ci goto ret_unstream_md; 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci for (i = 0; i < CXUSB_VIDEO_URBS; i++) { 8118c2ecf20Sopenharmony_ci int framen; 8128c2ecf20Sopenharmony_ci u8 *streambuf; 8138c2ecf20Sopenharmony_ci struct urb *surb; 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci /* 8168c2ecf20Sopenharmony_ci * TODO: change this to an array of single pages to avoid 8178c2ecf20Sopenharmony_ci * doing a large continuous allocation when (if) 8188c2ecf20Sopenharmony_ci * s-g isochronous USB transfers are supported 8198c2ecf20Sopenharmony_ci */ 8208c2ecf20Sopenharmony_ci streambuf = kmalloc(npackets * CXUSB_VIDEO_PKT_SIZE, 8218c2ecf20Sopenharmony_ci GFP_KERNEL); 8228c2ecf20Sopenharmony_ci if (!streambuf) { 8238c2ecf20Sopenharmony_ci if (i < 2) { 8248c2ecf20Sopenharmony_ci ret = -ENOMEM; 8258c2ecf20Sopenharmony_ci goto ret_freeab; 8268c2ecf20Sopenharmony_ci } 8278c2ecf20Sopenharmony_ci break; 8288c2ecf20Sopenharmony_ci } 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci surb = usb_alloc_urb(npackets, GFP_KERNEL); 8318c2ecf20Sopenharmony_ci if (!surb) { 8328c2ecf20Sopenharmony_ci kfree(streambuf); 8338c2ecf20Sopenharmony_ci ret = -ENOMEM; 8348c2ecf20Sopenharmony_ci goto ret_freeu; 8358c2ecf20Sopenharmony_ci } 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci cxdev->streamurbs[i] = surb; 8388c2ecf20Sopenharmony_ci surb->dev = dvbdev->udev; 8398c2ecf20Sopenharmony_ci surb->context = dvbdev; 8408c2ecf20Sopenharmony_ci surb->pipe = usb_rcvisocpipe(dvbdev->udev, 2); 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci surb->interval = 1; 8438c2ecf20Sopenharmony_ci surb->transfer_flags = URB_ISO_ASAP; 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci surb->transfer_buffer = streambuf; 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci surb->complete = cxusb_medion_v_complete; 8488c2ecf20Sopenharmony_ci surb->number_of_packets = npackets; 8498c2ecf20Sopenharmony_ci surb->transfer_buffer_length = npackets * CXUSB_VIDEO_PKT_SIZE; 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci for (framen = 0; framen < npackets; framen++) { 8528c2ecf20Sopenharmony_ci surb->iso_frame_desc[framen].offset = 8538c2ecf20Sopenharmony_ci CXUSB_VIDEO_PKT_SIZE * framen; 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci surb->iso_frame_desc[framen].length = 8568c2ecf20Sopenharmony_ci CXUSB_VIDEO_PKT_SIZE; 8578c2ecf20Sopenharmony_ci } 8588c2ecf20Sopenharmony_ci } 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci cxdev->urbcomplete = 0; 8618c2ecf20Sopenharmony_ci cxdev->nexturb = 0; 8628c2ecf20Sopenharmony_ci cxdev->vbuf_sequence = 0; 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci cxdev->vbuf = NULL; 8658c2ecf20Sopenharmony_ci cxdev->bt656.mode = NEW_FRAME; 8668c2ecf20Sopenharmony_ci cxdev->bt656.buf = NULL; 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci for (i = 0; i < CXUSB_VIDEO_URBS; i++) 8698c2ecf20Sopenharmony_ci if (cxdev->streamurbs[i]) { 8708c2ecf20Sopenharmony_ci ret = usb_submit_urb(cxdev->streamurbs[i], 8718c2ecf20Sopenharmony_ci GFP_KERNEL); 8728c2ecf20Sopenharmony_ci if (ret != 0) 8738c2ecf20Sopenharmony_ci dev_err(&dvbdev->udev->dev, 8748c2ecf20Sopenharmony_ci "URB %d submission failed (%d)\n", i, 8758c2ecf20Sopenharmony_ci ret); 8768c2ecf20Sopenharmony_ci } 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci return 0; 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ciret_freeu: 8818c2ecf20Sopenharmony_ci cxusb_medion_urbs_free(cxdev); 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ciret_freeab: 8848c2ecf20Sopenharmony_ci vfree(cxdev->auxbuf.buf); 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ciret_unstream_md: 8878c2ecf20Sopenharmony_ci cxusb_ctrl_msg(dvbdev, CMD_STREAMING_OFF, NULL, 0, NULL, 0); 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ciret_unstream_cx: 8908c2ecf20Sopenharmony_ci v4l2_subdev_call(cxdev->cx25840, video, s_stream, 0); 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ciret_retbufs: 8938c2ecf20Sopenharmony_ci cxusb_medion_return_buffers(cxdev, true); 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci return ret; 8968c2ecf20Sopenharmony_ci} 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_cistatic void cxusb_medion_v_stop_streaming(struct vb2_queue *q) 8998c2ecf20Sopenharmony_ci{ 9008c2ecf20Sopenharmony_ci struct dvb_usb_device *dvbdev = vb2_get_drv_priv(q); 9018c2ecf20Sopenharmony_ci struct cxusb_medion_dev *cxdev = dvbdev->priv; 9028c2ecf20Sopenharmony_ci int ret; 9038c2ecf20Sopenharmony_ci unsigned int i; 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci cxusb_vprintk(dvbdev, OPS, "should stop streaming\n"); 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci if (WARN_ON(cxdev->stop_streaming)) 9088c2ecf20Sopenharmony_ci return; 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci cxdev->stop_streaming = true; 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci cxusb_ctrl_msg(dvbdev, CMD_STREAMING_OFF, NULL, 0, NULL, 0); 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci ret = v4l2_subdev_call(cxdev->cx25840, video, s_stream, 0); 9158c2ecf20Sopenharmony_ci if (ret != 0) 9168c2ecf20Sopenharmony_ci dev_err(&dvbdev->udev->dev, "unable to stop stream (%d)\n", 9178c2ecf20Sopenharmony_ci ret); 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci /* let URB completion run */ 9208c2ecf20Sopenharmony_ci mutex_unlock(cxdev->videodev->lock); 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci for (i = 0; i < CXUSB_VIDEO_URBS; i++) 9238c2ecf20Sopenharmony_ci if (cxdev->streamurbs[i]) 9248c2ecf20Sopenharmony_ci usb_kill_urb(cxdev->streamurbs[i]); 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci flush_work(&cxdev->urbwork); 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci mutex_lock(cxdev->videodev->lock); 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_ci /* free transfer buffer and URB */ 9318c2ecf20Sopenharmony_ci vfree(cxdev->auxbuf.buf); 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci cxusb_medion_urbs_free(cxdev); 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci cxusb_medion_return_buffers(cxdev, false); 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci cxdev->stop_streaming = false; 9388c2ecf20Sopenharmony_ci} 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_cistatic void cxusub_medion_v_buf_queue(struct vb2_buffer *vb) 9418c2ecf20Sopenharmony_ci{ 9428c2ecf20Sopenharmony_ci struct vb2_v4l2_buffer *v4l2buf = to_vb2_v4l2_buffer(vb); 9438c2ecf20Sopenharmony_ci struct cxusb_medion_vbuffer *vbuf = 9448c2ecf20Sopenharmony_ci container_of(v4l2buf, struct cxusb_medion_vbuffer, vb2); 9458c2ecf20Sopenharmony_ci struct dvb_usb_device *dvbdev = vb2_get_drv_priv(vb->vb2_queue); 9468c2ecf20Sopenharmony_ci struct cxusb_medion_dev *cxdev = dvbdev->priv; 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci /* cxusb_vprintk(dvbdev, OPS, "mmmm.. a fresh buffer...\n"); */ 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci list_add_tail(&vbuf->list, &cxdev->buflist); 9518c2ecf20Sopenharmony_ci} 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_cistatic const struct vb2_ops cxdev_video_qops = { 9548c2ecf20Sopenharmony_ci .queue_setup = cxusb_medion_v_queue_setup, 9558c2ecf20Sopenharmony_ci .buf_init = cxusb_medion_v_buf_init, 9568c2ecf20Sopenharmony_ci .start_streaming = cxusb_medion_v_start_streaming, 9578c2ecf20Sopenharmony_ci .stop_streaming = cxusb_medion_v_stop_streaming, 9588c2ecf20Sopenharmony_ci .buf_queue = cxusub_medion_v_buf_queue, 9598c2ecf20Sopenharmony_ci .wait_prepare = vb2_ops_wait_prepare, 9608c2ecf20Sopenharmony_ci .wait_finish = vb2_ops_wait_finish 9618c2ecf20Sopenharmony_ci}; 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_cistatic const __u32 videocaps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | 9648c2ecf20Sopenharmony_ci V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; 9658c2ecf20Sopenharmony_cistatic const __u32 radiocaps = V4L2_CAP_TUNER | V4L2_CAP_RADIO; 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_cistatic int cxusb_medion_v_querycap(struct file *file, void *fh, 9688c2ecf20Sopenharmony_ci struct v4l2_capability *cap) 9698c2ecf20Sopenharmony_ci{ 9708c2ecf20Sopenharmony_ci struct dvb_usb_device *dvbdev = video_drvdata(file); 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci strscpy(cap->driver, dvbdev->udev->dev.driver->name, 9738c2ecf20Sopenharmony_ci sizeof(cap->driver)); 9748c2ecf20Sopenharmony_ci strscpy(cap->card, "Medion 95700", sizeof(cap->card)); 9758c2ecf20Sopenharmony_ci usb_make_path(dvbdev->udev, cap->bus_info, sizeof(cap->bus_info)); 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci cap->capabilities = videocaps | radiocaps | V4L2_CAP_DEVICE_CAPS; 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_ci return 0; 9808c2ecf20Sopenharmony_ci} 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_cistatic int cxusb_medion_v_enum_fmt_vid_cap(struct file *file, void *fh, 9838c2ecf20Sopenharmony_ci struct v4l2_fmtdesc *f) 9848c2ecf20Sopenharmony_ci{ 9858c2ecf20Sopenharmony_ci if (f->index != 0) 9868c2ecf20Sopenharmony_ci return -EINVAL; 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci f->pixelformat = V4L2_PIX_FMT_UYVY; 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci return 0; 9918c2ecf20Sopenharmony_ci} 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_cistatic int cxusb_medion_g_fmt_vid_cap(struct file *file, void *fh, 9948c2ecf20Sopenharmony_ci struct v4l2_format *f) 9958c2ecf20Sopenharmony_ci{ 9968c2ecf20Sopenharmony_ci struct dvb_usb_device *dvbdev = video_drvdata(file); 9978c2ecf20Sopenharmony_ci struct cxusb_medion_dev *cxdev = dvbdev->priv; 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci f->fmt.pix.width = cxdev->width; 10008c2ecf20Sopenharmony_ci f->fmt.pix.height = cxdev->height; 10018c2ecf20Sopenharmony_ci f->fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY; 10028c2ecf20Sopenharmony_ci f->fmt.pix.field = vb2_start_streaming_called(&cxdev->videoqueue) ? 10038c2ecf20Sopenharmony_ci cxdev->field_order : cxusb_medion_field_order(cxdev); 10048c2ecf20Sopenharmony_ci f->fmt.pix.bytesperline = cxdev->width * 2; 10058c2ecf20Sopenharmony_ci f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; 10068c2ecf20Sopenharmony_ci f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height; 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci return 0; 10098c2ecf20Sopenharmony_ci} 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_cistatic int cxusb_medion_try_s_fmt_vid_cap(struct file *file, 10128c2ecf20Sopenharmony_ci struct v4l2_format *f, 10138c2ecf20Sopenharmony_ci bool isset) 10148c2ecf20Sopenharmony_ci{ 10158c2ecf20Sopenharmony_ci struct dvb_usb_device *dvbdev = video_drvdata(file); 10168c2ecf20Sopenharmony_ci struct cxusb_medion_dev *cxdev = dvbdev->priv; 10178c2ecf20Sopenharmony_ci struct v4l2_subdev_format subfmt; 10188c2ecf20Sopenharmony_ci u32 field; 10198c2ecf20Sopenharmony_ci int ret; 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_ci if (isset && vb2_is_busy(&cxdev->videoqueue)) 10228c2ecf20Sopenharmony_ci return -EBUSY; 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci field = vb2_start_streaming_called(&cxdev->videoqueue) ? 10258c2ecf20Sopenharmony_ci cxdev->field_order : cxusb_medion_field_order(cxdev); 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci memset(&subfmt, 0, sizeof(subfmt)); 10288c2ecf20Sopenharmony_ci subfmt.which = isset ? V4L2_SUBDEV_FORMAT_ACTIVE : 10298c2ecf20Sopenharmony_ci V4L2_SUBDEV_FORMAT_TRY; 10308c2ecf20Sopenharmony_ci subfmt.format.width = f->fmt.pix.width & ~1; 10318c2ecf20Sopenharmony_ci subfmt.format.height = f->fmt.pix.height & ~1; 10328c2ecf20Sopenharmony_ci subfmt.format.code = MEDIA_BUS_FMT_FIXED; 10338c2ecf20Sopenharmony_ci subfmt.format.field = field; 10348c2ecf20Sopenharmony_ci subfmt.format.colorspace = V4L2_COLORSPACE_SMPTE170M; 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci ret = v4l2_subdev_call(cxdev->cx25840, pad, set_fmt, NULL, &subfmt); 10378c2ecf20Sopenharmony_ci if (ret != 0) 10388c2ecf20Sopenharmony_ci return ret; 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_ci f->fmt.pix.width = subfmt.format.width; 10418c2ecf20Sopenharmony_ci f->fmt.pix.height = subfmt.format.height; 10428c2ecf20Sopenharmony_ci f->fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY; 10438c2ecf20Sopenharmony_ci f->fmt.pix.field = field; 10448c2ecf20Sopenharmony_ci f->fmt.pix.bytesperline = f->fmt.pix.width * 2; 10458c2ecf20Sopenharmony_ci f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height; 10468c2ecf20Sopenharmony_ci f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci if (isset) { 10498c2ecf20Sopenharmony_ci cxdev->width = f->fmt.pix.width; 10508c2ecf20Sopenharmony_ci cxdev->height = f->fmt.pix.height; 10518c2ecf20Sopenharmony_ci } 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci return 0; 10548c2ecf20Sopenharmony_ci} 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_cistatic int cxusb_medion_try_fmt_vid_cap(struct file *file, void *fh, 10578c2ecf20Sopenharmony_ci struct v4l2_format *f) 10588c2ecf20Sopenharmony_ci{ 10598c2ecf20Sopenharmony_ci return cxusb_medion_try_s_fmt_vid_cap(file, f, false); 10608c2ecf20Sopenharmony_ci} 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_cistatic int cxusb_medion_s_fmt_vid_cap(struct file *file, void *fh, 10638c2ecf20Sopenharmony_ci struct v4l2_format *f) 10648c2ecf20Sopenharmony_ci{ 10658c2ecf20Sopenharmony_ci return cxusb_medion_try_s_fmt_vid_cap(file, f, true); 10668c2ecf20Sopenharmony_ci} 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_cistatic const struct { 10698c2ecf20Sopenharmony_ci struct v4l2_input input; 10708c2ecf20Sopenharmony_ci u32 inputcfg; 10718c2ecf20Sopenharmony_ci} cxusb_medion_inputs[] = { 10728c2ecf20Sopenharmony_ci { .input = { .name = "TV tuner", .type = V4L2_INPUT_TYPE_TUNER, 10738c2ecf20Sopenharmony_ci .tuner = 0, .std = V4L2_STD_PAL }, 10748c2ecf20Sopenharmony_ci .inputcfg = CX25840_COMPOSITE2, }, 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_ci { .input = { .name = "Composite", .type = V4L2_INPUT_TYPE_CAMERA, 10778c2ecf20Sopenharmony_ci .std = V4L2_STD_ALL }, 10788c2ecf20Sopenharmony_ci .inputcfg = CX25840_COMPOSITE1, }, 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_ci { .input = { .name = "S-Video", .type = V4L2_INPUT_TYPE_CAMERA, 10818c2ecf20Sopenharmony_ci .std = V4L2_STD_ALL }, 10828c2ecf20Sopenharmony_ci .inputcfg = CX25840_SVIDEO_LUMA3 | CX25840_SVIDEO_CHROMA4 } 10838c2ecf20Sopenharmony_ci}; 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci#define CXUSB_INPUT_CNT ARRAY_SIZE(cxusb_medion_inputs) 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_cistatic int cxusb_medion_enum_input(struct file *file, void *fh, 10888c2ecf20Sopenharmony_ci struct v4l2_input *inp) 10898c2ecf20Sopenharmony_ci{ 10908c2ecf20Sopenharmony_ci struct dvb_usb_device *dvbdev = video_drvdata(file); 10918c2ecf20Sopenharmony_ci struct cxusb_medion_dev *cxdev = dvbdev->priv; 10928c2ecf20Sopenharmony_ci u32 index = inp->index; 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci if (index >= CXUSB_INPUT_CNT) 10958c2ecf20Sopenharmony_ci return -EINVAL; 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci *inp = cxusb_medion_inputs[index].input; 10988c2ecf20Sopenharmony_ci inp->index = index; 10998c2ecf20Sopenharmony_ci inp->capabilities |= V4L2_IN_CAP_STD; 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci if (index == cxdev->input) { 11028c2ecf20Sopenharmony_ci int ret; 11038c2ecf20Sopenharmony_ci u32 status = 0; 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci ret = v4l2_subdev_call(cxdev->cx25840, video, g_input_status, 11068c2ecf20Sopenharmony_ci &status); 11078c2ecf20Sopenharmony_ci if (ret != 0) 11088c2ecf20Sopenharmony_ci dev_warn(&dvbdev->udev->dev, 11098c2ecf20Sopenharmony_ci "cx25840 input status query failed (%d)\n", 11108c2ecf20Sopenharmony_ci ret); 11118c2ecf20Sopenharmony_ci else 11128c2ecf20Sopenharmony_ci inp->status = status; 11138c2ecf20Sopenharmony_ci } 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_ci return 0; 11168c2ecf20Sopenharmony_ci} 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_cistatic int cxusb_medion_g_input(struct file *file, void *fh, 11198c2ecf20Sopenharmony_ci unsigned int *i) 11208c2ecf20Sopenharmony_ci{ 11218c2ecf20Sopenharmony_ci struct dvb_usb_device *dvbdev = video_drvdata(file); 11228c2ecf20Sopenharmony_ci struct cxusb_medion_dev *cxdev = dvbdev->priv; 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci *i = cxdev->input; 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci return 0; 11278c2ecf20Sopenharmony_ci} 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_cistatic int cxusb_medion_set_norm(struct cxusb_medion_dev *cxdev, 11308c2ecf20Sopenharmony_ci v4l2_std_id norm) 11318c2ecf20Sopenharmony_ci{ 11328c2ecf20Sopenharmony_ci struct dvb_usb_device *dvbdev = cxdev->dvbdev; 11338c2ecf20Sopenharmony_ci int ret; 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci cxusb_vprintk(dvbdev, OPS, 11368c2ecf20Sopenharmony_ci "trying to set standard for input %u to %lx\n", 11378c2ecf20Sopenharmony_ci (unsigned int)cxdev->input, 11388c2ecf20Sopenharmony_ci (unsigned long)norm); 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci /* no autodetection support */ 11418c2ecf20Sopenharmony_ci if (norm == V4L2_STD_UNKNOWN) 11428c2ecf20Sopenharmony_ci return -EINVAL; 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci /* on composite or S-Video any std is acceptable */ 11458c2ecf20Sopenharmony_ci if (cxdev->input != 0) { 11468c2ecf20Sopenharmony_ci ret = v4l2_subdev_call(cxdev->cx25840, video, s_std, norm); 11478c2ecf20Sopenharmony_ci if (ret) 11488c2ecf20Sopenharmony_ci return ret; 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_ci goto ret_savenorm; 11518c2ecf20Sopenharmony_ci } 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_ci /* TV tuner is only able to demodulate PAL */ 11548c2ecf20Sopenharmony_ci if ((norm & ~V4L2_STD_PAL) != 0) 11558c2ecf20Sopenharmony_ci return -EINVAL; 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_ci ret = v4l2_subdev_call(cxdev->tda9887, video, s_std, norm); 11588c2ecf20Sopenharmony_ci if (ret != 0) { 11598c2ecf20Sopenharmony_ci dev_err(&dvbdev->udev->dev, 11608c2ecf20Sopenharmony_ci "tda9887 norm setup failed (%d)\n", 11618c2ecf20Sopenharmony_ci ret); 11628c2ecf20Sopenharmony_ci return ret; 11638c2ecf20Sopenharmony_ci } 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_ci ret = v4l2_subdev_call(cxdev->tuner, video, s_std, norm); 11668c2ecf20Sopenharmony_ci if (ret != 0) { 11678c2ecf20Sopenharmony_ci dev_err(&dvbdev->udev->dev, 11688c2ecf20Sopenharmony_ci "tuner norm setup failed (%d)\n", 11698c2ecf20Sopenharmony_ci ret); 11708c2ecf20Sopenharmony_ci return ret; 11718c2ecf20Sopenharmony_ci } 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_ci ret = v4l2_subdev_call(cxdev->cx25840, video, s_std, norm); 11748c2ecf20Sopenharmony_ci if (ret != 0) { 11758c2ecf20Sopenharmony_ci dev_err(&dvbdev->udev->dev, 11768c2ecf20Sopenharmony_ci "cx25840 norm setup failed (%d)\n", 11778c2ecf20Sopenharmony_ci ret); 11788c2ecf20Sopenharmony_ci return ret; 11798c2ecf20Sopenharmony_ci } 11808c2ecf20Sopenharmony_ci 11818c2ecf20Sopenharmony_ciret_savenorm: 11828c2ecf20Sopenharmony_ci cxdev->norm = norm; 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_ci return 0; 11858c2ecf20Sopenharmony_ci} 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_cistatic int cxusb_medion_s_input(struct file *file, void *fh, 11888c2ecf20Sopenharmony_ci unsigned int i) 11898c2ecf20Sopenharmony_ci{ 11908c2ecf20Sopenharmony_ci struct dvb_usb_device *dvbdev = video_drvdata(file); 11918c2ecf20Sopenharmony_ci struct cxusb_medion_dev *cxdev = dvbdev->priv; 11928c2ecf20Sopenharmony_ci int ret; 11938c2ecf20Sopenharmony_ci v4l2_std_id norm; 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_ci if (i >= CXUSB_INPUT_CNT) 11968c2ecf20Sopenharmony_ci return -EINVAL; 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_ci ret = v4l2_subdev_call(cxdev->cx25840, video, s_routing, 11998c2ecf20Sopenharmony_ci cxusb_medion_inputs[i].inputcfg, 0, 0); 12008c2ecf20Sopenharmony_ci if (ret != 0) 12018c2ecf20Sopenharmony_ci return ret; 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci cxdev->input = i; 12048c2ecf20Sopenharmony_ci cxdev->videodev->tvnorms = cxusb_medion_inputs[i].input.std; 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_ci norm = cxdev->norm & cxusb_medion_inputs[i].input.std; 12078c2ecf20Sopenharmony_ci if (norm == 0) 12088c2ecf20Sopenharmony_ci norm = cxusb_medion_inputs[i].input.std; 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci cxusb_medion_set_norm(cxdev, norm); 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_ci return 0; 12138c2ecf20Sopenharmony_ci} 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_cistatic int cxusb_medion_g_tuner(struct file *file, void *fh, 12168c2ecf20Sopenharmony_ci struct v4l2_tuner *tuner) 12178c2ecf20Sopenharmony_ci{ 12188c2ecf20Sopenharmony_ci struct dvb_usb_device *dvbdev = video_drvdata(file); 12198c2ecf20Sopenharmony_ci struct cxusb_medion_dev *cxdev = dvbdev->priv; 12208c2ecf20Sopenharmony_ci struct video_device *vdev = video_devdata(file); 12218c2ecf20Sopenharmony_ci int ret; 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ci if (tuner->index != 0) 12248c2ecf20Sopenharmony_ci return -EINVAL; 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_ci if (vdev->vfl_type == VFL_TYPE_VIDEO) 12278c2ecf20Sopenharmony_ci tuner->type = V4L2_TUNER_ANALOG_TV; 12288c2ecf20Sopenharmony_ci else 12298c2ecf20Sopenharmony_ci tuner->type = V4L2_TUNER_RADIO; 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_ci tuner->capability = 0; 12328c2ecf20Sopenharmony_ci tuner->afc = 0; 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_ci /* 12358c2ecf20Sopenharmony_ci * fills: 12368c2ecf20Sopenharmony_ci * always: capability (static), rangelow (static), rangehigh (static) 12378c2ecf20Sopenharmony_ci * radio mode: afc (may fail silently), rxsubchans (static), audmode 12388c2ecf20Sopenharmony_ci */ 12398c2ecf20Sopenharmony_ci ret = v4l2_subdev_call(cxdev->tda9887, tuner, g_tuner, tuner); 12408c2ecf20Sopenharmony_ci if (ret != 0) 12418c2ecf20Sopenharmony_ci return ret; 12428c2ecf20Sopenharmony_ci 12438c2ecf20Sopenharmony_ci /* 12448c2ecf20Sopenharmony_ci * fills: 12458c2ecf20Sopenharmony_ci * always: capability (static), rangelow (static), rangehigh (static) 12468c2ecf20Sopenharmony_ci * radio mode: rxsubchans (always stereo), audmode, 12478c2ecf20Sopenharmony_ci * signal (might be wrong) 12488c2ecf20Sopenharmony_ci */ 12498c2ecf20Sopenharmony_ci ret = v4l2_subdev_call(cxdev->tuner, tuner, g_tuner, tuner); 12508c2ecf20Sopenharmony_ci if (ret != 0) 12518c2ecf20Sopenharmony_ci return ret; 12528c2ecf20Sopenharmony_ci 12538c2ecf20Sopenharmony_ci tuner->signal = 0; 12548c2ecf20Sopenharmony_ci 12558c2ecf20Sopenharmony_ci /* 12568c2ecf20Sopenharmony_ci * fills: TV mode: capability, rxsubchans, audmode, signal 12578c2ecf20Sopenharmony_ci */ 12588c2ecf20Sopenharmony_ci ret = v4l2_subdev_call(cxdev->cx25840, tuner, g_tuner, tuner); 12598c2ecf20Sopenharmony_ci if (ret != 0) 12608c2ecf20Sopenharmony_ci return ret; 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ci if (vdev->vfl_type == VFL_TYPE_VIDEO) 12638c2ecf20Sopenharmony_ci strscpy(tuner->name, "TV tuner", sizeof(tuner->name)); 12648c2ecf20Sopenharmony_ci else 12658c2ecf20Sopenharmony_ci strscpy(tuner->name, "Radio tuner", sizeof(tuner->name)); 12668c2ecf20Sopenharmony_ci 12678c2ecf20Sopenharmony_ci memset(tuner->reserved, 0, sizeof(tuner->reserved)); 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_ci return 0; 12708c2ecf20Sopenharmony_ci} 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_cistatic int cxusb_medion_s_tuner(struct file *file, void *fh, 12738c2ecf20Sopenharmony_ci const struct v4l2_tuner *tuner) 12748c2ecf20Sopenharmony_ci{ 12758c2ecf20Sopenharmony_ci struct dvb_usb_device *dvbdev = video_drvdata(file); 12768c2ecf20Sopenharmony_ci struct cxusb_medion_dev *cxdev = dvbdev->priv; 12778c2ecf20Sopenharmony_ci struct video_device *vdev = video_devdata(file); 12788c2ecf20Sopenharmony_ci int ret; 12798c2ecf20Sopenharmony_ci 12808c2ecf20Sopenharmony_ci if (tuner->index != 0) 12818c2ecf20Sopenharmony_ci return -EINVAL; 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_ci ret = v4l2_subdev_call(cxdev->tda9887, tuner, s_tuner, tuner); 12848c2ecf20Sopenharmony_ci if (ret != 0) 12858c2ecf20Sopenharmony_ci return ret; 12868c2ecf20Sopenharmony_ci 12878c2ecf20Sopenharmony_ci ret = v4l2_subdev_call(cxdev->tuner, tuner, s_tuner, tuner); 12888c2ecf20Sopenharmony_ci if (ret != 0) 12898c2ecf20Sopenharmony_ci return ret; 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_ci /* 12928c2ecf20Sopenharmony_ci * make sure that cx25840 is in a correct TV / radio mode, 12938c2ecf20Sopenharmony_ci * since calls above may have changed it for tuner / IF demod 12948c2ecf20Sopenharmony_ci */ 12958c2ecf20Sopenharmony_ci if (vdev->vfl_type == VFL_TYPE_VIDEO) 12968c2ecf20Sopenharmony_ci v4l2_subdev_call(cxdev->cx25840, video, s_std, cxdev->norm); 12978c2ecf20Sopenharmony_ci else 12988c2ecf20Sopenharmony_ci v4l2_subdev_call(cxdev->cx25840, tuner, s_radio); 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_ci return v4l2_subdev_call(cxdev->cx25840, tuner, s_tuner, tuner); 13018c2ecf20Sopenharmony_ci} 13028c2ecf20Sopenharmony_ci 13038c2ecf20Sopenharmony_cistatic int cxusb_medion_g_frequency(struct file *file, void *fh, 13048c2ecf20Sopenharmony_ci struct v4l2_frequency *freq) 13058c2ecf20Sopenharmony_ci{ 13068c2ecf20Sopenharmony_ci struct dvb_usb_device *dvbdev = video_drvdata(file); 13078c2ecf20Sopenharmony_ci struct cxusb_medion_dev *cxdev = dvbdev->priv; 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_ci if (freq->tuner != 0) 13108c2ecf20Sopenharmony_ci return -EINVAL; 13118c2ecf20Sopenharmony_ci 13128c2ecf20Sopenharmony_ci return v4l2_subdev_call(cxdev->tuner, tuner, g_frequency, freq); 13138c2ecf20Sopenharmony_ci} 13148c2ecf20Sopenharmony_ci 13158c2ecf20Sopenharmony_cistatic int cxusb_medion_s_frequency(struct file *file, void *fh, 13168c2ecf20Sopenharmony_ci const struct v4l2_frequency *freq) 13178c2ecf20Sopenharmony_ci{ 13188c2ecf20Sopenharmony_ci struct dvb_usb_device *dvbdev = video_drvdata(file); 13198c2ecf20Sopenharmony_ci struct cxusb_medion_dev *cxdev = dvbdev->priv; 13208c2ecf20Sopenharmony_ci struct video_device *vdev = video_devdata(file); 13218c2ecf20Sopenharmony_ci int ret; 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_ci if (freq->tuner != 0) 13248c2ecf20Sopenharmony_ci return -EINVAL; 13258c2ecf20Sopenharmony_ci 13268c2ecf20Sopenharmony_ci ret = v4l2_subdev_call(cxdev->tda9887, tuner, s_frequency, freq); 13278c2ecf20Sopenharmony_ci if (ret != 0) 13288c2ecf20Sopenharmony_ci return ret; 13298c2ecf20Sopenharmony_ci 13308c2ecf20Sopenharmony_ci ret = v4l2_subdev_call(cxdev->tuner, tuner, s_frequency, freq); 13318c2ecf20Sopenharmony_ci if (ret != 0) 13328c2ecf20Sopenharmony_ci return ret; 13338c2ecf20Sopenharmony_ci 13348c2ecf20Sopenharmony_ci /* 13358c2ecf20Sopenharmony_ci * make sure that cx25840 is in a correct TV / radio mode, 13368c2ecf20Sopenharmony_ci * since calls above may have changed it for tuner / IF demod 13378c2ecf20Sopenharmony_ci */ 13388c2ecf20Sopenharmony_ci if (vdev->vfl_type == VFL_TYPE_VIDEO) 13398c2ecf20Sopenharmony_ci v4l2_subdev_call(cxdev->cx25840, video, s_std, cxdev->norm); 13408c2ecf20Sopenharmony_ci else 13418c2ecf20Sopenharmony_ci v4l2_subdev_call(cxdev->cx25840, tuner, s_radio); 13428c2ecf20Sopenharmony_ci 13438c2ecf20Sopenharmony_ci return v4l2_subdev_call(cxdev->cx25840, tuner, s_frequency, freq); 13448c2ecf20Sopenharmony_ci} 13458c2ecf20Sopenharmony_ci 13468c2ecf20Sopenharmony_cistatic int cxusb_medion_g_std(struct file *file, void *fh, 13478c2ecf20Sopenharmony_ci v4l2_std_id *norm) 13488c2ecf20Sopenharmony_ci{ 13498c2ecf20Sopenharmony_ci struct dvb_usb_device *dvbdev = video_drvdata(file); 13508c2ecf20Sopenharmony_ci struct cxusb_medion_dev *cxdev = dvbdev->priv; 13518c2ecf20Sopenharmony_ci 13528c2ecf20Sopenharmony_ci *norm = cxdev->norm; 13538c2ecf20Sopenharmony_ci 13548c2ecf20Sopenharmony_ci if (*norm == V4L2_STD_UNKNOWN) 13558c2ecf20Sopenharmony_ci return -ENODATA; 13568c2ecf20Sopenharmony_ci 13578c2ecf20Sopenharmony_ci return 0; 13588c2ecf20Sopenharmony_ci} 13598c2ecf20Sopenharmony_ci 13608c2ecf20Sopenharmony_cistatic int cxusb_medion_s_std(struct file *file, void *fh, 13618c2ecf20Sopenharmony_ci v4l2_std_id norm) 13628c2ecf20Sopenharmony_ci{ 13638c2ecf20Sopenharmony_ci struct dvb_usb_device *dvbdev = video_drvdata(file); 13648c2ecf20Sopenharmony_ci struct cxusb_medion_dev *cxdev = dvbdev->priv; 13658c2ecf20Sopenharmony_ci 13668c2ecf20Sopenharmony_ci return cxusb_medion_set_norm(cxdev, norm); 13678c2ecf20Sopenharmony_ci} 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_cistatic int cxusb_medion_querystd(struct file *file, void *fh, 13708c2ecf20Sopenharmony_ci v4l2_std_id *norm) 13718c2ecf20Sopenharmony_ci{ 13728c2ecf20Sopenharmony_ci struct dvb_usb_device *dvbdev = video_drvdata(file); 13738c2ecf20Sopenharmony_ci struct cxusb_medion_dev *cxdev = dvbdev->priv; 13748c2ecf20Sopenharmony_ci v4l2_std_id norm_mask; 13758c2ecf20Sopenharmony_ci int ret; 13768c2ecf20Sopenharmony_ci 13778c2ecf20Sopenharmony_ci /* 13788c2ecf20Sopenharmony_ci * make sure we don't have improper std bits set for the TV tuner 13798c2ecf20Sopenharmony_ci * (could happen when no signal was present yet after reset) 13808c2ecf20Sopenharmony_ci */ 13818c2ecf20Sopenharmony_ci if (cxdev->input == 0) 13828c2ecf20Sopenharmony_ci norm_mask = V4L2_STD_PAL; 13838c2ecf20Sopenharmony_ci else 13848c2ecf20Sopenharmony_ci norm_mask = V4L2_STD_ALL; 13858c2ecf20Sopenharmony_ci 13868c2ecf20Sopenharmony_ci ret = v4l2_subdev_call(cxdev->cx25840, video, querystd, norm); 13878c2ecf20Sopenharmony_ci if (ret != 0) { 13888c2ecf20Sopenharmony_ci cxusb_vprintk(dvbdev, OPS, 13898c2ecf20Sopenharmony_ci "cannot get detected standard for input %u\n", 13908c2ecf20Sopenharmony_ci (unsigned int)cxdev->input); 13918c2ecf20Sopenharmony_ci return ret; 13928c2ecf20Sopenharmony_ci } 13938c2ecf20Sopenharmony_ci 13948c2ecf20Sopenharmony_ci cxusb_vprintk(dvbdev, OPS, "input %u detected standard is %lx\n", 13958c2ecf20Sopenharmony_ci (unsigned int)cxdev->input, (unsigned long)*norm); 13968c2ecf20Sopenharmony_ci *norm &= norm_mask; 13978c2ecf20Sopenharmony_ci 13988c2ecf20Sopenharmony_ci return 0; 13998c2ecf20Sopenharmony_ci} 14008c2ecf20Sopenharmony_ci 14018c2ecf20Sopenharmony_cistatic int cxusb_medion_log_status(struct file *file, void *fh) 14028c2ecf20Sopenharmony_ci{ 14038c2ecf20Sopenharmony_ci struct dvb_usb_device *dvbdev = video_drvdata(file); 14048c2ecf20Sopenharmony_ci struct cxusb_medion_dev *cxdev = dvbdev->priv; 14058c2ecf20Sopenharmony_ci 14068c2ecf20Sopenharmony_ci v4l2_device_call_all(&cxdev->v4l2dev, 0, core, log_status); 14078c2ecf20Sopenharmony_ci 14088c2ecf20Sopenharmony_ci return 0; 14098c2ecf20Sopenharmony_ci} 14108c2ecf20Sopenharmony_ci 14118c2ecf20Sopenharmony_cistatic const struct v4l2_ioctl_ops cxusb_video_ioctl = { 14128c2ecf20Sopenharmony_ci .vidioc_querycap = cxusb_medion_v_querycap, 14138c2ecf20Sopenharmony_ci .vidioc_enum_fmt_vid_cap = cxusb_medion_v_enum_fmt_vid_cap, 14148c2ecf20Sopenharmony_ci .vidioc_g_fmt_vid_cap = cxusb_medion_g_fmt_vid_cap, 14158c2ecf20Sopenharmony_ci .vidioc_s_fmt_vid_cap = cxusb_medion_s_fmt_vid_cap, 14168c2ecf20Sopenharmony_ci .vidioc_try_fmt_vid_cap = cxusb_medion_try_fmt_vid_cap, 14178c2ecf20Sopenharmony_ci .vidioc_enum_input = cxusb_medion_enum_input, 14188c2ecf20Sopenharmony_ci .vidioc_g_input = cxusb_medion_g_input, 14198c2ecf20Sopenharmony_ci .vidioc_s_input = cxusb_medion_s_input, 14208c2ecf20Sopenharmony_ci .vidioc_g_tuner = cxusb_medion_g_tuner, 14218c2ecf20Sopenharmony_ci .vidioc_s_tuner = cxusb_medion_s_tuner, 14228c2ecf20Sopenharmony_ci .vidioc_g_frequency = cxusb_medion_g_frequency, 14238c2ecf20Sopenharmony_ci .vidioc_s_frequency = cxusb_medion_s_frequency, 14248c2ecf20Sopenharmony_ci .vidioc_g_std = cxusb_medion_g_std, 14258c2ecf20Sopenharmony_ci .vidioc_s_std = cxusb_medion_s_std, 14268c2ecf20Sopenharmony_ci .vidioc_querystd = cxusb_medion_querystd, 14278c2ecf20Sopenharmony_ci .vidioc_log_status = cxusb_medion_log_status, 14288c2ecf20Sopenharmony_ci .vidioc_reqbufs = vb2_ioctl_reqbufs, 14298c2ecf20Sopenharmony_ci .vidioc_querybuf = vb2_ioctl_querybuf, 14308c2ecf20Sopenharmony_ci .vidioc_qbuf = vb2_ioctl_qbuf, 14318c2ecf20Sopenharmony_ci .vidioc_dqbuf = vb2_ioctl_dqbuf, 14328c2ecf20Sopenharmony_ci .vidioc_create_bufs = vb2_ioctl_create_bufs, 14338c2ecf20Sopenharmony_ci .vidioc_prepare_buf = vb2_ioctl_prepare_buf, 14348c2ecf20Sopenharmony_ci .vidioc_streamon = vb2_ioctl_streamon, 14358c2ecf20Sopenharmony_ci .vidioc_streamoff = vb2_ioctl_streamoff 14368c2ecf20Sopenharmony_ci}; 14378c2ecf20Sopenharmony_ci 14388c2ecf20Sopenharmony_cistatic const struct v4l2_ioctl_ops cxusb_radio_ioctl = { 14398c2ecf20Sopenharmony_ci .vidioc_querycap = cxusb_medion_v_querycap, 14408c2ecf20Sopenharmony_ci .vidioc_g_tuner = cxusb_medion_g_tuner, 14418c2ecf20Sopenharmony_ci .vidioc_s_tuner = cxusb_medion_s_tuner, 14428c2ecf20Sopenharmony_ci .vidioc_g_frequency = cxusb_medion_g_frequency, 14438c2ecf20Sopenharmony_ci .vidioc_s_frequency = cxusb_medion_s_frequency, 14448c2ecf20Sopenharmony_ci .vidioc_log_status = cxusb_medion_log_status 14458c2ecf20Sopenharmony_ci}; 14468c2ecf20Sopenharmony_ci 14478c2ecf20Sopenharmony_ci/* 14488c2ecf20Sopenharmony_ci * in principle, this should be const, but s_io_pin_config is declared 14498c2ecf20Sopenharmony_ci * to take non-const, and gcc complains 14508c2ecf20Sopenharmony_ci */ 14518c2ecf20Sopenharmony_cistatic struct v4l2_subdev_io_pin_config cxusub_medion_pin_config[] = { 14528c2ecf20Sopenharmony_ci { .pin = CX25840_PIN_DVALID_PRGM0, .function = CX25840_PAD_DEFAULT, 14538c2ecf20Sopenharmony_ci .strength = CX25840_PIN_DRIVE_MEDIUM }, 14548c2ecf20Sopenharmony_ci { .pin = CX25840_PIN_PLL_CLK_PRGM7, .function = CX25840_PAD_AUX_PLL }, 14558c2ecf20Sopenharmony_ci { .pin = CX25840_PIN_HRESET_PRGM2, .function = CX25840_PAD_ACTIVE, 14568c2ecf20Sopenharmony_ci .strength = CX25840_PIN_DRIVE_MEDIUM } 14578c2ecf20Sopenharmony_ci}; 14588c2ecf20Sopenharmony_ci 14598c2ecf20Sopenharmony_ciint cxusb_medion_analog_init(struct dvb_usb_device *dvbdev) 14608c2ecf20Sopenharmony_ci{ 14618c2ecf20Sopenharmony_ci struct cxusb_medion_dev *cxdev = dvbdev->priv; 14628c2ecf20Sopenharmony_ci u8 tuner_analog_msg_data[] = { 0x9c, 0x60, 0x85, 0x54 }; 14638c2ecf20Sopenharmony_ci struct i2c_msg tuner_analog_msg = { .addr = 0x61, .flags = 0, 14648c2ecf20Sopenharmony_ci .buf = tuner_analog_msg_data, 14658c2ecf20Sopenharmony_ci .len = 14668c2ecf20Sopenharmony_ci sizeof(tuner_analog_msg_data) }; 14678c2ecf20Sopenharmony_ci struct v4l2_subdev_format subfmt; 14688c2ecf20Sopenharmony_ci int ret; 14698c2ecf20Sopenharmony_ci 14708c2ecf20Sopenharmony_ci /* switch tuner to analog mode so IF demod will become accessible */ 14718c2ecf20Sopenharmony_ci ret = i2c_transfer(&dvbdev->i2c_adap, &tuner_analog_msg, 1); 14728c2ecf20Sopenharmony_ci if (ret != 1) 14738c2ecf20Sopenharmony_ci dev_warn(&dvbdev->udev->dev, 14748c2ecf20Sopenharmony_ci "tuner analog switch failed (%d)\n", ret); 14758c2ecf20Sopenharmony_ci 14768c2ecf20Sopenharmony_ci /* 14778c2ecf20Sopenharmony_ci * cx25840 might have lost power during mode switching so we need 14788c2ecf20Sopenharmony_ci * to set it again 14798c2ecf20Sopenharmony_ci */ 14808c2ecf20Sopenharmony_ci ret = v4l2_subdev_call(cxdev->cx25840, core, reset, 0); 14818c2ecf20Sopenharmony_ci if (ret != 0) 14828c2ecf20Sopenharmony_ci dev_warn(&dvbdev->udev->dev, 14838c2ecf20Sopenharmony_ci "cx25840 reset failed (%d)\n", ret); 14848c2ecf20Sopenharmony_ci 14858c2ecf20Sopenharmony_ci ret = v4l2_subdev_call(cxdev->cx25840, video, s_routing, 14868c2ecf20Sopenharmony_ci CX25840_COMPOSITE1, 0, 0); 14878c2ecf20Sopenharmony_ci if (ret != 0) 14888c2ecf20Sopenharmony_ci dev_warn(&dvbdev->udev->dev, 14898c2ecf20Sopenharmony_ci "cx25840 initial input setting failed (%d)\n", ret); 14908c2ecf20Sopenharmony_ci 14918c2ecf20Sopenharmony_ci /* composite */ 14928c2ecf20Sopenharmony_ci cxdev->input = 1; 14938c2ecf20Sopenharmony_ci cxdev->videodev->tvnorms = V4L2_STD_ALL; 14948c2ecf20Sopenharmony_ci cxdev->norm = V4L2_STD_PAL; 14958c2ecf20Sopenharmony_ci 14968c2ecf20Sopenharmony_ci /* TODO: setup audio samples insertion */ 14978c2ecf20Sopenharmony_ci 14988c2ecf20Sopenharmony_ci ret = v4l2_subdev_call(cxdev->cx25840, core, s_io_pin_config, 14998c2ecf20Sopenharmony_ci ARRAY_SIZE(cxusub_medion_pin_config), 15008c2ecf20Sopenharmony_ci cxusub_medion_pin_config); 15018c2ecf20Sopenharmony_ci if (ret != 0) 15028c2ecf20Sopenharmony_ci dev_warn(&dvbdev->udev->dev, 15038c2ecf20Sopenharmony_ci "cx25840 pin config failed (%d)\n", ret); 15048c2ecf20Sopenharmony_ci 15058c2ecf20Sopenharmony_ci /* make sure that we aren't in radio mode */ 15068c2ecf20Sopenharmony_ci v4l2_subdev_call(cxdev->tda9887, video, s_std, cxdev->norm); 15078c2ecf20Sopenharmony_ci v4l2_subdev_call(cxdev->tuner, video, s_std, cxdev->norm); 15088c2ecf20Sopenharmony_ci v4l2_subdev_call(cxdev->cx25840, video, s_std, cxdev->norm); 15098c2ecf20Sopenharmony_ci 15108c2ecf20Sopenharmony_ci memset(&subfmt, 0, sizeof(subfmt)); 15118c2ecf20Sopenharmony_ci subfmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; 15128c2ecf20Sopenharmony_ci subfmt.format.width = cxdev->width; 15138c2ecf20Sopenharmony_ci subfmt.format.height = cxdev->height; 15148c2ecf20Sopenharmony_ci subfmt.format.code = MEDIA_BUS_FMT_FIXED; 15158c2ecf20Sopenharmony_ci subfmt.format.field = V4L2_FIELD_SEQ_TB; 15168c2ecf20Sopenharmony_ci subfmt.format.colorspace = V4L2_COLORSPACE_SMPTE170M; 15178c2ecf20Sopenharmony_ci 15188c2ecf20Sopenharmony_ci ret = v4l2_subdev_call(cxdev->cx25840, pad, set_fmt, NULL, &subfmt); 15198c2ecf20Sopenharmony_ci if (ret != 0) 15208c2ecf20Sopenharmony_ci dev_warn(&dvbdev->udev->dev, 15218c2ecf20Sopenharmony_ci "cx25840 format set failed (%d)\n", ret); 15228c2ecf20Sopenharmony_ci 15238c2ecf20Sopenharmony_ci if (ret == 0) { 15248c2ecf20Sopenharmony_ci cxdev->width = subfmt.format.width; 15258c2ecf20Sopenharmony_ci cxdev->height = subfmt.format.height; 15268c2ecf20Sopenharmony_ci } 15278c2ecf20Sopenharmony_ci 15288c2ecf20Sopenharmony_ci return 0; 15298c2ecf20Sopenharmony_ci} 15308c2ecf20Sopenharmony_ci 15318c2ecf20Sopenharmony_cistatic int cxusb_videoradio_open(struct file *f) 15328c2ecf20Sopenharmony_ci{ 15338c2ecf20Sopenharmony_ci struct dvb_usb_device *dvbdev = video_drvdata(f); 15348c2ecf20Sopenharmony_ci int ret; 15358c2ecf20Sopenharmony_ci 15368c2ecf20Sopenharmony_ci /* 15378c2ecf20Sopenharmony_ci * no locking needed since this call only modifies analog 15388c2ecf20Sopenharmony_ci * state if there are no other analog handles currenly 15398c2ecf20Sopenharmony_ci * opened so ops done via them cannot create a conflict 15408c2ecf20Sopenharmony_ci */ 15418c2ecf20Sopenharmony_ci ret = cxusb_medion_get(dvbdev, CXUSB_OPEN_ANALOG); 15428c2ecf20Sopenharmony_ci if (ret != 0) 15438c2ecf20Sopenharmony_ci return ret; 15448c2ecf20Sopenharmony_ci 15458c2ecf20Sopenharmony_ci ret = v4l2_fh_open(f); 15468c2ecf20Sopenharmony_ci if (ret != 0) 15478c2ecf20Sopenharmony_ci goto ret_release; 15488c2ecf20Sopenharmony_ci 15498c2ecf20Sopenharmony_ci cxusb_vprintk(dvbdev, OPS, "got open\n"); 15508c2ecf20Sopenharmony_ci 15518c2ecf20Sopenharmony_ci return 0; 15528c2ecf20Sopenharmony_ci 15538c2ecf20Sopenharmony_ciret_release: 15548c2ecf20Sopenharmony_ci cxusb_medion_put(dvbdev); 15558c2ecf20Sopenharmony_ci 15568c2ecf20Sopenharmony_ci return ret; 15578c2ecf20Sopenharmony_ci} 15588c2ecf20Sopenharmony_ci 15598c2ecf20Sopenharmony_cistatic int cxusb_videoradio_release(struct file *f) 15608c2ecf20Sopenharmony_ci{ 15618c2ecf20Sopenharmony_ci struct video_device *vdev = video_devdata(f); 15628c2ecf20Sopenharmony_ci struct dvb_usb_device *dvbdev = video_drvdata(f); 15638c2ecf20Sopenharmony_ci int ret; 15648c2ecf20Sopenharmony_ci 15658c2ecf20Sopenharmony_ci cxusb_vprintk(dvbdev, OPS, "got release\n"); 15668c2ecf20Sopenharmony_ci 15678c2ecf20Sopenharmony_ci if (vdev->vfl_type == VFL_TYPE_VIDEO) 15688c2ecf20Sopenharmony_ci ret = vb2_fop_release(f); 15698c2ecf20Sopenharmony_ci else 15708c2ecf20Sopenharmony_ci ret = v4l2_fh_release(f); 15718c2ecf20Sopenharmony_ci 15728c2ecf20Sopenharmony_ci cxusb_medion_put(dvbdev); 15738c2ecf20Sopenharmony_ci 15748c2ecf20Sopenharmony_ci return ret; 15758c2ecf20Sopenharmony_ci} 15768c2ecf20Sopenharmony_ci 15778c2ecf20Sopenharmony_cistatic const struct v4l2_file_operations cxusb_video_fops = { 15788c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 15798c2ecf20Sopenharmony_ci .read = vb2_fop_read, 15808c2ecf20Sopenharmony_ci .poll = vb2_fop_poll, 15818c2ecf20Sopenharmony_ci .unlocked_ioctl = video_ioctl2, 15828c2ecf20Sopenharmony_ci .mmap = vb2_fop_mmap, 15838c2ecf20Sopenharmony_ci .open = cxusb_videoradio_open, 15848c2ecf20Sopenharmony_ci .release = cxusb_videoradio_release 15858c2ecf20Sopenharmony_ci}; 15868c2ecf20Sopenharmony_ci 15878c2ecf20Sopenharmony_cistatic const struct v4l2_file_operations cxusb_radio_fops = { 15888c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 15898c2ecf20Sopenharmony_ci .unlocked_ioctl = video_ioctl2, 15908c2ecf20Sopenharmony_ci .open = cxusb_videoradio_open, 15918c2ecf20Sopenharmony_ci .release = cxusb_videoradio_release 15928c2ecf20Sopenharmony_ci}; 15938c2ecf20Sopenharmony_ci 15948c2ecf20Sopenharmony_cistatic void cxusb_medion_v4l2_release(struct v4l2_device *v4l2_dev) 15958c2ecf20Sopenharmony_ci{ 15968c2ecf20Sopenharmony_ci struct cxusb_medion_dev *cxdev = 15978c2ecf20Sopenharmony_ci container_of(v4l2_dev, struct cxusb_medion_dev, v4l2dev); 15988c2ecf20Sopenharmony_ci struct dvb_usb_device *dvbdev = cxdev->dvbdev; 15998c2ecf20Sopenharmony_ci 16008c2ecf20Sopenharmony_ci cxusb_vprintk(dvbdev, OPS, "v4l2 device release\n"); 16018c2ecf20Sopenharmony_ci 16028c2ecf20Sopenharmony_ci v4l2_device_unregister(&cxdev->v4l2dev); 16038c2ecf20Sopenharmony_ci 16048c2ecf20Sopenharmony_ci mutex_destroy(&cxdev->dev_lock); 16058c2ecf20Sopenharmony_ci 16068c2ecf20Sopenharmony_ci while (completion_done(&cxdev->v4l2_release)) 16078c2ecf20Sopenharmony_ci schedule(); 16088c2ecf20Sopenharmony_ci 16098c2ecf20Sopenharmony_ci complete(&cxdev->v4l2_release); 16108c2ecf20Sopenharmony_ci} 16118c2ecf20Sopenharmony_ci 16128c2ecf20Sopenharmony_cistatic void cxusb_medion_videodev_release(struct video_device *vdev) 16138c2ecf20Sopenharmony_ci{ 16148c2ecf20Sopenharmony_ci struct dvb_usb_device *dvbdev = video_get_drvdata(vdev); 16158c2ecf20Sopenharmony_ci 16168c2ecf20Sopenharmony_ci cxusb_vprintk(dvbdev, OPS, "video device release\n"); 16178c2ecf20Sopenharmony_ci 16188c2ecf20Sopenharmony_ci video_device_release(vdev); 16198c2ecf20Sopenharmony_ci} 16208c2ecf20Sopenharmony_ci 16218c2ecf20Sopenharmony_cistatic int cxusb_medion_register_analog_video(struct dvb_usb_device *dvbdev) 16228c2ecf20Sopenharmony_ci{ 16238c2ecf20Sopenharmony_ci struct cxusb_medion_dev *cxdev = dvbdev->priv; 16248c2ecf20Sopenharmony_ci int ret; 16258c2ecf20Sopenharmony_ci 16268c2ecf20Sopenharmony_ci cxdev->videoqueue.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 16278c2ecf20Sopenharmony_ci cxdev->videoqueue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ | 16288c2ecf20Sopenharmony_ci VB2_DMABUF; 16298c2ecf20Sopenharmony_ci cxdev->videoqueue.ops = &cxdev_video_qops; 16308c2ecf20Sopenharmony_ci cxdev->videoqueue.mem_ops = &vb2_vmalloc_memops; 16318c2ecf20Sopenharmony_ci cxdev->videoqueue.drv_priv = dvbdev; 16328c2ecf20Sopenharmony_ci cxdev->videoqueue.buf_struct_size = 16338c2ecf20Sopenharmony_ci sizeof(struct cxusb_medion_vbuffer); 16348c2ecf20Sopenharmony_ci cxdev->videoqueue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; 16358c2ecf20Sopenharmony_ci cxdev->videoqueue.min_buffers_needed = 6; 16368c2ecf20Sopenharmony_ci cxdev->videoqueue.lock = &cxdev->dev_lock; 16378c2ecf20Sopenharmony_ci 16388c2ecf20Sopenharmony_ci ret = vb2_queue_init(&cxdev->videoqueue); 16398c2ecf20Sopenharmony_ci if (ret) { 16408c2ecf20Sopenharmony_ci dev_err(&dvbdev->udev->dev, 16418c2ecf20Sopenharmony_ci "video queue init failed, ret = %d\n", ret); 16428c2ecf20Sopenharmony_ci return ret; 16438c2ecf20Sopenharmony_ci } 16448c2ecf20Sopenharmony_ci 16458c2ecf20Sopenharmony_ci cxdev->videodev = video_device_alloc(); 16468c2ecf20Sopenharmony_ci if (!cxdev->videodev) { 16478c2ecf20Sopenharmony_ci dev_err(&dvbdev->udev->dev, "video device alloc failed\n"); 16488c2ecf20Sopenharmony_ci return -ENOMEM; 16498c2ecf20Sopenharmony_ci } 16508c2ecf20Sopenharmony_ci 16518c2ecf20Sopenharmony_ci cxdev->videodev->device_caps = videocaps; 16528c2ecf20Sopenharmony_ci cxdev->videodev->fops = &cxusb_video_fops; 16538c2ecf20Sopenharmony_ci cxdev->videodev->v4l2_dev = &cxdev->v4l2dev; 16548c2ecf20Sopenharmony_ci cxdev->videodev->queue = &cxdev->videoqueue; 16558c2ecf20Sopenharmony_ci strscpy(cxdev->videodev->name, "cxusb", sizeof(cxdev->videodev->name)); 16568c2ecf20Sopenharmony_ci cxdev->videodev->vfl_dir = VFL_DIR_RX; 16578c2ecf20Sopenharmony_ci cxdev->videodev->ioctl_ops = &cxusb_video_ioctl; 16588c2ecf20Sopenharmony_ci cxdev->videodev->tvnorms = V4L2_STD_ALL; 16598c2ecf20Sopenharmony_ci cxdev->videodev->release = cxusb_medion_videodev_release; 16608c2ecf20Sopenharmony_ci cxdev->videodev->lock = &cxdev->dev_lock; 16618c2ecf20Sopenharmony_ci video_set_drvdata(cxdev->videodev, dvbdev); 16628c2ecf20Sopenharmony_ci 16638c2ecf20Sopenharmony_ci ret = video_register_device(cxdev->videodev, VFL_TYPE_VIDEO, -1); 16648c2ecf20Sopenharmony_ci if (ret) { 16658c2ecf20Sopenharmony_ci dev_err(&dvbdev->udev->dev, 16668c2ecf20Sopenharmony_ci "video device register failed, ret = %d\n", ret); 16678c2ecf20Sopenharmony_ci goto ret_vrelease; 16688c2ecf20Sopenharmony_ci } 16698c2ecf20Sopenharmony_ci 16708c2ecf20Sopenharmony_ci return 0; 16718c2ecf20Sopenharmony_ci 16728c2ecf20Sopenharmony_ciret_vrelease: 16738c2ecf20Sopenharmony_ci video_device_release(cxdev->videodev); 16748c2ecf20Sopenharmony_ci return ret; 16758c2ecf20Sopenharmony_ci} 16768c2ecf20Sopenharmony_ci 16778c2ecf20Sopenharmony_cistatic int cxusb_medion_register_analog_radio(struct dvb_usb_device *dvbdev) 16788c2ecf20Sopenharmony_ci{ 16798c2ecf20Sopenharmony_ci struct cxusb_medion_dev *cxdev = dvbdev->priv; 16808c2ecf20Sopenharmony_ci int ret; 16818c2ecf20Sopenharmony_ci 16828c2ecf20Sopenharmony_ci cxdev->radiodev = video_device_alloc(); 16838c2ecf20Sopenharmony_ci if (!cxdev->radiodev) { 16848c2ecf20Sopenharmony_ci dev_err(&dvbdev->udev->dev, "radio device alloc failed\n"); 16858c2ecf20Sopenharmony_ci return -ENOMEM; 16868c2ecf20Sopenharmony_ci } 16878c2ecf20Sopenharmony_ci 16888c2ecf20Sopenharmony_ci cxdev->radiodev->device_caps = radiocaps; 16898c2ecf20Sopenharmony_ci cxdev->radiodev->fops = &cxusb_radio_fops; 16908c2ecf20Sopenharmony_ci cxdev->radiodev->v4l2_dev = &cxdev->v4l2dev; 16918c2ecf20Sopenharmony_ci strscpy(cxdev->radiodev->name, "cxusb", sizeof(cxdev->radiodev->name)); 16928c2ecf20Sopenharmony_ci cxdev->radiodev->vfl_dir = VFL_DIR_RX; 16938c2ecf20Sopenharmony_ci cxdev->radiodev->ioctl_ops = &cxusb_radio_ioctl; 16948c2ecf20Sopenharmony_ci cxdev->radiodev->release = video_device_release; 16958c2ecf20Sopenharmony_ci cxdev->radiodev->lock = &cxdev->dev_lock; 16968c2ecf20Sopenharmony_ci video_set_drvdata(cxdev->radiodev, dvbdev); 16978c2ecf20Sopenharmony_ci 16988c2ecf20Sopenharmony_ci ret = video_register_device(cxdev->radiodev, VFL_TYPE_RADIO, -1); 16998c2ecf20Sopenharmony_ci if (ret) { 17008c2ecf20Sopenharmony_ci dev_err(&dvbdev->udev->dev, 17018c2ecf20Sopenharmony_ci "radio device register failed, ret = %d\n", ret); 17028c2ecf20Sopenharmony_ci video_device_release(cxdev->radiodev); 17038c2ecf20Sopenharmony_ci return ret; 17048c2ecf20Sopenharmony_ci } 17058c2ecf20Sopenharmony_ci 17068c2ecf20Sopenharmony_ci return 0; 17078c2ecf20Sopenharmony_ci} 17088c2ecf20Sopenharmony_ci 17098c2ecf20Sopenharmony_cistatic int cxusb_medion_register_analog_subdevs(struct dvb_usb_device *dvbdev) 17108c2ecf20Sopenharmony_ci{ 17118c2ecf20Sopenharmony_ci struct cxusb_medion_dev *cxdev = dvbdev->priv; 17128c2ecf20Sopenharmony_ci int ret; 17138c2ecf20Sopenharmony_ci struct tuner_setup tun_setup; 17148c2ecf20Sopenharmony_ci 17158c2ecf20Sopenharmony_ci /* attach cx25840 capture chip */ 17168c2ecf20Sopenharmony_ci cxdev->cx25840 = v4l2_i2c_new_subdev(&cxdev->v4l2dev, 17178c2ecf20Sopenharmony_ci &dvbdev->i2c_adap, 17188c2ecf20Sopenharmony_ci "cx25840", 0x44, NULL); 17198c2ecf20Sopenharmony_ci if (!cxdev->cx25840) { 17208c2ecf20Sopenharmony_ci dev_err(&dvbdev->udev->dev, "cx25840 not found\n"); 17218c2ecf20Sopenharmony_ci return -ENODEV; 17228c2ecf20Sopenharmony_ci } 17238c2ecf20Sopenharmony_ci 17248c2ecf20Sopenharmony_ci /* 17258c2ecf20Sopenharmony_ci * Initialize cx25840 chip by calling its subdevice init core op. 17268c2ecf20Sopenharmony_ci * 17278c2ecf20Sopenharmony_ci * This switches it into the generic mode that disables some of 17288c2ecf20Sopenharmony_ci * ivtv-related hacks in the cx25840 driver while allowing setting 17298c2ecf20Sopenharmony_ci * of the chip video output configuration (passed in the call below 17308c2ecf20Sopenharmony_ci * as the last argument). 17318c2ecf20Sopenharmony_ci */ 17328c2ecf20Sopenharmony_ci ret = v4l2_subdev_call(cxdev->cx25840, core, init, 17338c2ecf20Sopenharmony_ci CX25840_VCONFIG_FMT_BT656 | 17348c2ecf20Sopenharmony_ci CX25840_VCONFIG_RES_8BIT | 17358c2ecf20Sopenharmony_ci CX25840_VCONFIG_VBIRAW_DISABLED | 17368c2ecf20Sopenharmony_ci CX25840_VCONFIG_ANCDATA_DISABLED | 17378c2ecf20Sopenharmony_ci CX25840_VCONFIG_ACTIVE_COMPOSITE | 17388c2ecf20Sopenharmony_ci CX25840_VCONFIG_VALID_ANDACTIVE | 17398c2ecf20Sopenharmony_ci CX25840_VCONFIG_HRESETW_NORMAL | 17408c2ecf20Sopenharmony_ci CX25840_VCONFIG_CLKGATE_NONE | 17418c2ecf20Sopenharmony_ci CX25840_VCONFIG_DCMODE_DWORDS); 17428c2ecf20Sopenharmony_ci if (ret != 0) { 17438c2ecf20Sopenharmony_ci dev_err(&dvbdev->udev->dev, 17448c2ecf20Sopenharmony_ci "cx25840 init failed (%d)\n", ret); 17458c2ecf20Sopenharmony_ci return ret; 17468c2ecf20Sopenharmony_ci } 17478c2ecf20Sopenharmony_ci 17488c2ecf20Sopenharmony_ci /* attach analog tuner */ 17498c2ecf20Sopenharmony_ci cxdev->tuner = v4l2_i2c_new_subdev(&cxdev->v4l2dev, 17508c2ecf20Sopenharmony_ci &dvbdev->i2c_adap, 17518c2ecf20Sopenharmony_ci "tuner", 0x61, NULL); 17528c2ecf20Sopenharmony_ci if (!cxdev->tuner) { 17538c2ecf20Sopenharmony_ci dev_err(&dvbdev->udev->dev, "tuner not found\n"); 17548c2ecf20Sopenharmony_ci return -ENODEV; 17558c2ecf20Sopenharmony_ci } 17568c2ecf20Sopenharmony_ci 17578c2ecf20Sopenharmony_ci /* configure it */ 17588c2ecf20Sopenharmony_ci memset(&tun_setup, 0, sizeof(tun_setup)); 17598c2ecf20Sopenharmony_ci tun_setup.addr = 0x61; 17608c2ecf20Sopenharmony_ci tun_setup.type = TUNER_PHILIPS_FMD1216ME_MK3; 17618c2ecf20Sopenharmony_ci tun_setup.mode_mask = T_RADIO | T_ANALOG_TV; 17628c2ecf20Sopenharmony_ci v4l2_subdev_call(cxdev->tuner, tuner, s_type_addr, &tun_setup); 17638c2ecf20Sopenharmony_ci 17648c2ecf20Sopenharmony_ci /* attach IF demod */ 17658c2ecf20Sopenharmony_ci cxdev->tda9887 = v4l2_i2c_new_subdev(&cxdev->v4l2dev, 17668c2ecf20Sopenharmony_ci &dvbdev->i2c_adap, 17678c2ecf20Sopenharmony_ci "tuner", 0x43, NULL); 17688c2ecf20Sopenharmony_ci if (!cxdev->tda9887) { 17698c2ecf20Sopenharmony_ci dev_err(&dvbdev->udev->dev, "tda9887 not found\n"); 17708c2ecf20Sopenharmony_ci return -ENODEV; 17718c2ecf20Sopenharmony_ci } 17728c2ecf20Sopenharmony_ci 17738c2ecf20Sopenharmony_ci return 0; 17748c2ecf20Sopenharmony_ci} 17758c2ecf20Sopenharmony_ci 17768c2ecf20Sopenharmony_ciint cxusb_medion_register_analog(struct dvb_usb_device *dvbdev) 17778c2ecf20Sopenharmony_ci{ 17788c2ecf20Sopenharmony_ci struct cxusb_medion_dev *cxdev = dvbdev->priv; 17798c2ecf20Sopenharmony_ci int ret; 17808c2ecf20Sopenharmony_ci 17818c2ecf20Sopenharmony_ci mutex_init(&cxdev->dev_lock); 17828c2ecf20Sopenharmony_ci 17838c2ecf20Sopenharmony_ci init_completion(&cxdev->v4l2_release); 17848c2ecf20Sopenharmony_ci 17858c2ecf20Sopenharmony_ci cxdev->v4l2dev.release = cxusb_medion_v4l2_release; 17868c2ecf20Sopenharmony_ci 17878c2ecf20Sopenharmony_ci ret = v4l2_device_register(&dvbdev->udev->dev, &cxdev->v4l2dev); 17888c2ecf20Sopenharmony_ci if (ret != 0) { 17898c2ecf20Sopenharmony_ci dev_err(&dvbdev->udev->dev, 17908c2ecf20Sopenharmony_ci "V4L2 device registration failed, ret = %d\n", ret); 17918c2ecf20Sopenharmony_ci mutex_destroy(&cxdev->dev_lock); 17928c2ecf20Sopenharmony_ci return ret; 17938c2ecf20Sopenharmony_ci } 17948c2ecf20Sopenharmony_ci 17958c2ecf20Sopenharmony_ci ret = cxusb_medion_register_analog_subdevs(dvbdev); 17968c2ecf20Sopenharmony_ci if (ret) 17978c2ecf20Sopenharmony_ci goto ret_unregister; 17988c2ecf20Sopenharmony_ci 17998c2ecf20Sopenharmony_ci INIT_WORK(&cxdev->urbwork, cxusb_medion_v_complete_work); 18008c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&cxdev->buflist); 18018c2ecf20Sopenharmony_ci 18028c2ecf20Sopenharmony_ci cxdev->width = 320; 18038c2ecf20Sopenharmony_ci cxdev->height = 240; 18048c2ecf20Sopenharmony_ci 18058c2ecf20Sopenharmony_ci ret = cxusb_medion_register_analog_video(dvbdev); 18068c2ecf20Sopenharmony_ci if (ret) 18078c2ecf20Sopenharmony_ci goto ret_unregister; 18088c2ecf20Sopenharmony_ci 18098c2ecf20Sopenharmony_ci ret = cxusb_medion_register_analog_radio(dvbdev); 18108c2ecf20Sopenharmony_ci if (ret) 18118c2ecf20Sopenharmony_ci goto ret_vunreg; 18128c2ecf20Sopenharmony_ci 18138c2ecf20Sopenharmony_ci return 0; 18148c2ecf20Sopenharmony_ci 18158c2ecf20Sopenharmony_ciret_vunreg: 18168c2ecf20Sopenharmony_ci vb2_video_unregister_device(cxdev->videodev); 18178c2ecf20Sopenharmony_ci 18188c2ecf20Sopenharmony_ciret_unregister: 18198c2ecf20Sopenharmony_ci v4l2_device_put(&cxdev->v4l2dev); 18208c2ecf20Sopenharmony_ci wait_for_completion(&cxdev->v4l2_release); 18218c2ecf20Sopenharmony_ci 18228c2ecf20Sopenharmony_ci return ret; 18238c2ecf20Sopenharmony_ci} 18248c2ecf20Sopenharmony_ci 18258c2ecf20Sopenharmony_civoid cxusb_medion_unregister_analog(struct dvb_usb_device *dvbdev) 18268c2ecf20Sopenharmony_ci{ 18278c2ecf20Sopenharmony_ci struct cxusb_medion_dev *cxdev = dvbdev->priv; 18288c2ecf20Sopenharmony_ci 18298c2ecf20Sopenharmony_ci cxusb_vprintk(dvbdev, OPS, "unregistering analog\n"); 18308c2ecf20Sopenharmony_ci 18318c2ecf20Sopenharmony_ci video_unregister_device(cxdev->radiodev); 18328c2ecf20Sopenharmony_ci vb2_video_unregister_device(cxdev->videodev); 18338c2ecf20Sopenharmony_ci 18348c2ecf20Sopenharmony_ci v4l2_device_put(&cxdev->v4l2dev); 18358c2ecf20Sopenharmony_ci wait_for_completion(&cxdev->v4l2_release); 18368c2ecf20Sopenharmony_ci 18378c2ecf20Sopenharmony_ci cxusb_vprintk(dvbdev, OPS, "analog unregistered\n"); 18388c2ecf20Sopenharmony_ci} 1839