162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// DVB USB compliant linux driver for Conexant USB reference design - 462306a36Sopenharmony_ci// (analog part). 562306a36Sopenharmony_ci// 662306a36Sopenharmony_ci// Copyright (C) 2011, 2017, 2018 762306a36Sopenharmony_ci// Maciej S. Szmigiero (mail@maciej.szmigiero.name) 862306a36Sopenharmony_ci// 962306a36Sopenharmony_ci// In case there are new analog / DVB-T hybrid devices released in the market 1062306a36Sopenharmony_ci// using the same general design as Medion MD95700: a CX25840 video decoder 1162306a36Sopenharmony_ci// outputting a BT.656 stream to a USB bridge chip which then forwards it to 1262306a36Sopenharmony_ci// the host in isochronous USB packets this code should be made generic, with 1362306a36Sopenharmony_ci// board specific bits implemented via separate card structures. 1462306a36Sopenharmony_ci// 1562306a36Sopenharmony_ci// This is, however, unlikely as the Medion model was released 1662306a36Sopenharmony_ci// years ago (in 2005). 1762306a36Sopenharmony_ci// 1862306a36Sopenharmony_ci// TODO: 1962306a36Sopenharmony_ci// * audio support, 2062306a36Sopenharmony_ci// * finish radio support (requires audio of course), 2162306a36Sopenharmony_ci// * VBI support, 2262306a36Sopenharmony_ci// * controls support 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include <linux/bitops.h> 2562306a36Sopenharmony_ci#include <linux/device.h> 2662306a36Sopenharmony_ci#include <linux/slab.h> 2762306a36Sopenharmony_ci#include <linux/string.h> 2862306a36Sopenharmony_ci#include <linux/ktime.h> 2962306a36Sopenharmony_ci#include <linux/vmalloc.h> 3062306a36Sopenharmony_ci#include <media/drv-intf/cx25840.h> 3162306a36Sopenharmony_ci#include <media/tuner.h> 3262306a36Sopenharmony_ci#include <media/v4l2-fh.h> 3362306a36Sopenharmony_ci#include <media/v4l2-ioctl.h> 3462306a36Sopenharmony_ci#include <media/v4l2-subdev.h> 3562306a36Sopenharmony_ci#include <media/videobuf2-vmalloc.h> 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#include "cxusb.h" 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic int cxusb_medion_v_queue_setup(struct vb2_queue *q, 4062306a36Sopenharmony_ci unsigned int *num_buffers, 4162306a36Sopenharmony_ci unsigned int *num_planes, 4262306a36Sopenharmony_ci unsigned int sizes[], 4362306a36Sopenharmony_ci struct device *alloc_devs[]) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci struct dvb_usb_device *dvbdev = vb2_get_drv_priv(q); 4662306a36Sopenharmony_ci struct cxusb_medion_dev *cxdev = dvbdev->priv; 4762306a36Sopenharmony_ci unsigned int size = cxdev->width * cxdev->height * 2; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci if (*num_planes > 0) { 5062306a36Sopenharmony_ci if (*num_planes != 1) 5162306a36Sopenharmony_ci return -EINVAL; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci if (sizes[0] < size) 5462306a36Sopenharmony_ci return -EINVAL; 5562306a36Sopenharmony_ci } else { 5662306a36Sopenharmony_ci *num_planes = 1; 5762306a36Sopenharmony_ci sizes[0] = size; 5862306a36Sopenharmony_ci } 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci return 0; 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic int cxusb_medion_v_buf_init(struct vb2_buffer *vb) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci struct dvb_usb_device *dvbdev = vb2_get_drv_priv(vb->vb2_queue); 6662306a36Sopenharmony_ci struct cxusb_medion_dev *cxdev = dvbdev->priv; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci cxusb_vprintk(dvbdev, OPS, "buffer init\n"); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci if (vb2_plane_size(vb, 0) < cxdev->width * cxdev->height * 2) 7162306a36Sopenharmony_ci return -ENOMEM; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci cxusb_vprintk(dvbdev, OPS, "buffer OK\n"); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci return 0; 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic void cxusb_auxbuf_init(struct dvb_usb_device *dvbdev, 7962306a36Sopenharmony_ci struct cxusb_medion_auxbuf *auxbuf, 8062306a36Sopenharmony_ci u8 *buf, unsigned int len) 8162306a36Sopenharmony_ci{ 8262306a36Sopenharmony_ci cxusb_vprintk(dvbdev, AUXB, "initializing auxbuf of len %u\n", len); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci auxbuf->buf = buf; 8562306a36Sopenharmony_ci auxbuf->len = len; 8662306a36Sopenharmony_ci auxbuf->paylen = 0; 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic void cxusb_auxbuf_head_trim(struct dvb_usb_device *dvbdev, 9062306a36Sopenharmony_ci struct cxusb_medion_auxbuf *auxbuf, 9162306a36Sopenharmony_ci unsigned int pos) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci if (pos == 0) 9462306a36Sopenharmony_ci return; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci if (WARN_ON(pos > auxbuf->paylen)) 9762306a36Sopenharmony_ci return; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci cxusb_vprintk(dvbdev, AUXB, 10062306a36Sopenharmony_ci "trimming auxbuf len by %u to %u\n", 10162306a36Sopenharmony_ci pos, auxbuf->paylen - pos); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci memmove(auxbuf->buf, auxbuf->buf + pos, auxbuf->paylen - pos); 10462306a36Sopenharmony_ci auxbuf->paylen -= pos; 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic unsigned int cxusb_auxbuf_paylen(struct cxusb_medion_auxbuf *auxbuf) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci return auxbuf->paylen; 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic bool cxusb_auxbuf_make_space(struct dvb_usb_device *dvbdev, 11362306a36Sopenharmony_ci struct cxusb_medion_auxbuf *auxbuf, 11462306a36Sopenharmony_ci unsigned int howmuch) 11562306a36Sopenharmony_ci{ 11662306a36Sopenharmony_ci unsigned int freespace; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci if (WARN_ON(howmuch >= auxbuf->len)) 11962306a36Sopenharmony_ci howmuch = auxbuf->len - 1; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci freespace = auxbuf->len - cxusb_auxbuf_paylen(auxbuf); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci cxusb_vprintk(dvbdev, AUXB, "freespace is %u\n", freespace); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci if (freespace >= howmuch) 12662306a36Sopenharmony_ci return true; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci howmuch -= freespace; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci cxusb_vprintk(dvbdev, AUXB, "will overwrite %u bytes of buffer\n", 13162306a36Sopenharmony_ci howmuch); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci cxusb_auxbuf_head_trim(dvbdev, auxbuf, howmuch); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci return false; 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci/* returns false if some data was overwritten */ 13962306a36Sopenharmony_cistatic bool cxusb_auxbuf_append_urb(struct dvb_usb_device *dvbdev, 14062306a36Sopenharmony_ci struct cxusb_medion_auxbuf *auxbuf, 14162306a36Sopenharmony_ci struct urb *urb) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci unsigned long len; 14462306a36Sopenharmony_ci int i; 14562306a36Sopenharmony_ci bool ret; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci for (i = 0, len = 0; i < urb->number_of_packets; i++) 14862306a36Sopenharmony_ci len += urb->iso_frame_desc[i].actual_length; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci ret = cxusb_auxbuf_make_space(dvbdev, auxbuf, len); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci for (i = 0; i < urb->number_of_packets; i++) { 15362306a36Sopenharmony_ci unsigned int to_copy; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci to_copy = urb->iso_frame_desc[i].actual_length; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci memcpy(auxbuf->buf + auxbuf->paylen, urb->transfer_buffer + 15862306a36Sopenharmony_ci urb->iso_frame_desc[i].offset, to_copy); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci auxbuf->paylen += to_copy; 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci return ret; 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic bool cxusb_auxbuf_copy(struct cxusb_medion_auxbuf *auxbuf, 16762306a36Sopenharmony_ci unsigned int pos, unsigned char *dest, 16862306a36Sopenharmony_ci unsigned int len) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci if (pos + len > auxbuf->paylen) 17162306a36Sopenharmony_ci return false; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci memcpy(dest, auxbuf->buf + pos, len); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci return true; 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic bool cxusb_medion_cf_refc_fld_chg(struct dvb_usb_device *dvbdev, 17962306a36Sopenharmony_ci struct cxusb_bt656_params *bt656, 18062306a36Sopenharmony_ci bool firstfield, 18162306a36Sopenharmony_ci unsigned int maxlines, 18262306a36Sopenharmony_ci unsigned int maxlinesamples, 18362306a36Sopenharmony_ci unsigned char buf[4]) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci bool firstfield_code = (buf[3] & CXUSB_BT656_FIELD_MASK) == 18662306a36Sopenharmony_ci CXUSB_BT656_FIELD_1; 18762306a36Sopenharmony_ci unsigned int remlines; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci if (bt656->line == 0 || firstfield == firstfield_code) 19062306a36Sopenharmony_ci return false; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci if (bt656->fmode == LINE_SAMPLES) { 19362306a36Sopenharmony_ci unsigned int remsamples = maxlinesamples - 19462306a36Sopenharmony_ci bt656->linesamples; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci cxusb_vprintk(dvbdev, BT656, 19762306a36Sopenharmony_ci "field %c after line %u field change\n", 19862306a36Sopenharmony_ci firstfield ? '1' : '2', bt656->line); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci if (bt656->buf && remsamples > 0) { 20162306a36Sopenharmony_ci memset(bt656->buf, 0, remsamples); 20262306a36Sopenharmony_ci bt656->buf += remsamples; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci cxusb_vprintk(dvbdev, BT656, 20562306a36Sopenharmony_ci "field %c line %u %u samples still remaining (of %u)\n", 20662306a36Sopenharmony_ci firstfield ? '1' : '2', 20762306a36Sopenharmony_ci bt656->line, remsamples, 20862306a36Sopenharmony_ci maxlinesamples); 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci bt656->line++; 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci remlines = maxlines - bt656->line; 21562306a36Sopenharmony_ci if (bt656->buf && remlines > 0) { 21662306a36Sopenharmony_ci memset(bt656->buf, 0, remlines * maxlinesamples); 21762306a36Sopenharmony_ci bt656->buf += remlines * maxlinesamples; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci cxusb_vprintk(dvbdev, BT656, 22062306a36Sopenharmony_ci "field %c %u lines still remaining (of %u)\n", 22162306a36Sopenharmony_ci firstfield ? '1' : '2', remlines, 22262306a36Sopenharmony_ci maxlines); 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci return true; 22662306a36Sopenharmony_ci} 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_cistatic void cxusb_medion_cf_refc_start_sch(struct dvb_usb_device *dvbdev, 22962306a36Sopenharmony_ci struct cxusb_bt656_params *bt656, 23062306a36Sopenharmony_ci bool firstfield, 23162306a36Sopenharmony_ci unsigned char buf[4]) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci bool firstfield_code = (buf[3] & CXUSB_BT656_FIELD_MASK) == 23462306a36Sopenharmony_ci CXUSB_BT656_FIELD_1; 23562306a36Sopenharmony_ci bool sav_code = (buf[3] & CXUSB_BT656_SEAV_MASK) == 23662306a36Sopenharmony_ci CXUSB_BT656_SEAV_SAV; 23762306a36Sopenharmony_ci bool vbi_code = (buf[3] & CXUSB_BT656_VBI_MASK) == 23862306a36Sopenharmony_ci CXUSB_BT656_VBI_ON; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci if (!sav_code || firstfield != firstfield_code) 24162306a36Sopenharmony_ci return; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci if (!vbi_code) { 24462306a36Sopenharmony_ci cxusb_vprintk(dvbdev, BT656, "line start @ pos %u\n", 24562306a36Sopenharmony_ci bt656->pos); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci bt656->linesamples = 0; 24862306a36Sopenharmony_ci bt656->fmode = LINE_SAMPLES; 24962306a36Sopenharmony_ci } else { 25062306a36Sopenharmony_ci cxusb_vprintk(dvbdev, BT656, "VBI start @ pos %u\n", 25162306a36Sopenharmony_ci bt656->pos); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci bt656->fmode = VBI_SAMPLES; 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_cistatic void cxusb_medion_cf_refc_line_smpl(struct dvb_usb_device *dvbdev, 25862306a36Sopenharmony_ci struct cxusb_bt656_params *bt656, 25962306a36Sopenharmony_ci bool firstfield, 26062306a36Sopenharmony_ci unsigned int maxlinesamples, 26162306a36Sopenharmony_ci unsigned char buf[4]) 26262306a36Sopenharmony_ci{ 26362306a36Sopenharmony_ci bool sav_code = (buf[3] & CXUSB_BT656_SEAV_MASK) == 26462306a36Sopenharmony_ci CXUSB_BT656_SEAV_SAV; 26562306a36Sopenharmony_ci unsigned int remsamples; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci if (sav_code) 26862306a36Sopenharmony_ci cxusb_vprintk(dvbdev, BT656, 26962306a36Sopenharmony_ci "SAV in line samples @ line %u, pos %u\n", 27062306a36Sopenharmony_ci bt656->line, bt656->pos); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci remsamples = maxlinesamples - bt656->linesamples; 27362306a36Sopenharmony_ci if (bt656->buf && remsamples > 0) { 27462306a36Sopenharmony_ci memset(bt656->buf, 0, remsamples); 27562306a36Sopenharmony_ci bt656->buf += remsamples; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci cxusb_vprintk(dvbdev, BT656, 27862306a36Sopenharmony_ci "field %c line %u %u samples still remaining (of %u)\n", 27962306a36Sopenharmony_ci firstfield ? '1' : '2', bt656->line, remsamples, 28062306a36Sopenharmony_ci maxlinesamples); 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci bt656->fmode = START_SEARCH; 28462306a36Sopenharmony_ci bt656->line++; 28562306a36Sopenharmony_ci} 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_cistatic void cxusb_medion_cf_refc_vbi_smpl(struct dvb_usb_device *dvbdev, 28862306a36Sopenharmony_ci struct cxusb_bt656_params *bt656, 28962306a36Sopenharmony_ci unsigned char buf[4]) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci bool sav_code = (buf[3] & CXUSB_BT656_SEAV_MASK) == 29262306a36Sopenharmony_ci CXUSB_BT656_SEAV_SAV; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci if (sav_code) 29562306a36Sopenharmony_ci cxusb_vprintk(dvbdev, BT656, "SAV in VBI samples @ pos %u\n", 29662306a36Sopenharmony_ci bt656->pos); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci bt656->fmode = START_SEARCH; 29962306a36Sopenharmony_ci} 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci/* returns whether the whole 4-byte code should be skipped in the buffer */ 30262306a36Sopenharmony_cistatic bool cxusb_medion_cf_ref_code(struct dvb_usb_device *dvbdev, 30362306a36Sopenharmony_ci struct cxusb_bt656_params *bt656, 30462306a36Sopenharmony_ci bool firstfield, 30562306a36Sopenharmony_ci unsigned int maxlines, 30662306a36Sopenharmony_ci unsigned int maxlinesamples, 30762306a36Sopenharmony_ci unsigned char buf[4]) 30862306a36Sopenharmony_ci{ 30962306a36Sopenharmony_ci if (bt656->fmode == START_SEARCH) { 31062306a36Sopenharmony_ci cxusb_medion_cf_refc_start_sch(dvbdev, bt656, firstfield, buf); 31162306a36Sopenharmony_ci } else if (bt656->fmode == LINE_SAMPLES) { 31262306a36Sopenharmony_ci cxusb_medion_cf_refc_line_smpl(dvbdev, bt656, firstfield, 31362306a36Sopenharmony_ci maxlinesamples, buf); 31462306a36Sopenharmony_ci return false; 31562306a36Sopenharmony_ci } else if (bt656->fmode == VBI_SAMPLES) { 31662306a36Sopenharmony_ci cxusb_medion_cf_refc_vbi_smpl(dvbdev, bt656, buf); 31762306a36Sopenharmony_ci return false; 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci return true; 32162306a36Sopenharmony_ci} 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_cistatic bool cxusb_medion_cs_start_sch(struct dvb_usb_device *dvbdev, 32462306a36Sopenharmony_ci struct cxusb_medion_auxbuf *auxbuf, 32562306a36Sopenharmony_ci struct cxusb_bt656_params *bt656, 32662306a36Sopenharmony_ci unsigned int maxlinesamples) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci unsigned char buf[64]; 32962306a36Sopenharmony_ci unsigned int idx; 33062306a36Sopenharmony_ci unsigned int tocheck = clamp_t(size_t, maxlinesamples / 4, 3, 33162306a36Sopenharmony_ci sizeof(buf)); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci if (!cxusb_auxbuf_copy(auxbuf, bt656->pos + 1, buf, tocheck)) 33462306a36Sopenharmony_ci return false; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci for (idx = 0; idx <= tocheck - 3; idx++) 33762306a36Sopenharmony_ci if (memcmp(buf + idx, CXUSB_BT656_PREAMBLE, 3) == 0) { 33862306a36Sopenharmony_ci bt656->pos += (1 + idx); 33962306a36Sopenharmony_ci return true; 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci cxusb_vprintk(dvbdev, BT656, "line %u early start, pos %u\n", 34362306a36Sopenharmony_ci bt656->line, bt656->pos); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci bt656->linesamples = 0; 34662306a36Sopenharmony_ci bt656->fmode = LINE_SAMPLES; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci return true; 34962306a36Sopenharmony_ci} 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_cistatic void cxusb_medion_cs_line_smpl(struct cxusb_bt656_params *bt656, 35262306a36Sopenharmony_ci unsigned int maxlinesamples, 35362306a36Sopenharmony_ci unsigned char val) 35462306a36Sopenharmony_ci{ 35562306a36Sopenharmony_ci if (bt656->buf) 35662306a36Sopenharmony_ci *(bt656->buf++) = val; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci bt656->linesamples++; 35962306a36Sopenharmony_ci bt656->pos++; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci if (bt656->linesamples >= maxlinesamples) { 36262306a36Sopenharmony_ci bt656->fmode = START_SEARCH; 36362306a36Sopenharmony_ci bt656->line++; 36462306a36Sopenharmony_ci } 36562306a36Sopenharmony_ci} 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_cistatic bool cxusb_medion_copy_samples(struct dvb_usb_device *dvbdev, 36862306a36Sopenharmony_ci struct cxusb_medion_auxbuf *auxbuf, 36962306a36Sopenharmony_ci struct cxusb_bt656_params *bt656, 37062306a36Sopenharmony_ci unsigned int maxlinesamples, 37162306a36Sopenharmony_ci unsigned char val) 37262306a36Sopenharmony_ci{ 37362306a36Sopenharmony_ci if (bt656->fmode == START_SEARCH && bt656->line > 0) 37462306a36Sopenharmony_ci return cxusb_medion_cs_start_sch(dvbdev, auxbuf, bt656, 37562306a36Sopenharmony_ci maxlinesamples); 37662306a36Sopenharmony_ci else if (bt656->fmode == LINE_SAMPLES) 37762306a36Sopenharmony_ci cxusb_medion_cs_line_smpl(bt656, maxlinesamples, val); 37862306a36Sopenharmony_ci else /* TODO: copy VBI samples */ 37962306a36Sopenharmony_ci bt656->pos++; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci return true; 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_cistatic bool cxusb_medion_copy_field(struct dvb_usb_device *dvbdev, 38562306a36Sopenharmony_ci struct cxusb_medion_auxbuf *auxbuf, 38662306a36Sopenharmony_ci struct cxusb_bt656_params *bt656, 38762306a36Sopenharmony_ci bool firstfield, 38862306a36Sopenharmony_ci unsigned int maxlines, 38962306a36Sopenharmony_ci unsigned int maxlinesmpls) 39062306a36Sopenharmony_ci{ 39162306a36Sopenharmony_ci while (bt656->line < maxlines) { 39262306a36Sopenharmony_ci unsigned char val; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci if (!cxusb_auxbuf_copy(auxbuf, bt656->pos, &val, 1)) 39562306a36Sopenharmony_ci break; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci if (val == CXUSB_BT656_PREAMBLE[0]) { 39862306a36Sopenharmony_ci unsigned char buf[4]; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci buf[0] = val; 40162306a36Sopenharmony_ci if (!cxusb_auxbuf_copy(auxbuf, bt656->pos + 1, 40262306a36Sopenharmony_ci buf + 1, 3)) 40362306a36Sopenharmony_ci break; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci if (buf[1] == CXUSB_BT656_PREAMBLE[1] && 40662306a36Sopenharmony_ci buf[2] == CXUSB_BT656_PREAMBLE[2]) { 40762306a36Sopenharmony_ci /* 40862306a36Sopenharmony_ci * is this a field change? 40962306a36Sopenharmony_ci * if so, terminate copying the current field 41062306a36Sopenharmony_ci */ 41162306a36Sopenharmony_ci if (cxusb_medion_cf_refc_fld_chg(dvbdev, 41262306a36Sopenharmony_ci bt656, 41362306a36Sopenharmony_ci firstfield, 41462306a36Sopenharmony_ci maxlines, 41562306a36Sopenharmony_ci maxlinesmpls, 41662306a36Sopenharmony_ci buf)) 41762306a36Sopenharmony_ci return true; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci if (cxusb_medion_cf_ref_code(dvbdev, bt656, 42062306a36Sopenharmony_ci firstfield, 42162306a36Sopenharmony_ci maxlines, 42262306a36Sopenharmony_ci maxlinesmpls, 42362306a36Sopenharmony_ci buf)) 42462306a36Sopenharmony_ci bt656->pos += 4; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci continue; 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci } 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci if (!cxusb_medion_copy_samples(dvbdev, auxbuf, bt656, 43162306a36Sopenharmony_ci maxlinesmpls, val)) 43262306a36Sopenharmony_ci break; 43362306a36Sopenharmony_ci } 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci if (bt656->line < maxlines) { 43662306a36Sopenharmony_ci cxusb_vprintk(dvbdev, BT656, 43762306a36Sopenharmony_ci "end of buffer pos = %u, line = %u\n", 43862306a36Sopenharmony_ci bt656->pos, bt656->line); 43962306a36Sopenharmony_ci return false; 44062306a36Sopenharmony_ci } 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci return true; 44362306a36Sopenharmony_ci} 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_cistatic bool cxusb_medion_v_process_auxbuf(struct cxusb_medion_dev *cxdev, 44662306a36Sopenharmony_ci bool reset) 44762306a36Sopenharmony_ci{ 44862306a36Sopenharmony_ci struct dvb_usb_device *dvbdev = cxdev->dvbdev; 44962306a36Sopenharmony_ci struct cxusb_bt656_params *bt656 = &cxdev->bt656; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci /* 45262306a36Sopenharmony_ci * if this is a new frame 45362306a36Sopenharmony_ci * fetch a buffer from list 45462306a36Sopenharmony_ci */ 45562306a36Sopenharmony_ci if (bt656->mode == NEW_FRAME) { 45662306a36Sopenharmony_ci if (!list_empty(&cxdev->buflist)) { 45762306a36Sopenharmony_ci cxdev->vbuf = 45862306a36Sopenharmony_ci list_first_entry(&cxdev->buflist, 45962306a36Sopenharmony_ci struct cxusb_medion_vbuffer, 46062306a36Sopenharmony_ci list); 46162306a36Sopenharmony_ci list_del(&cxdev->vbuf->list); 46262306a36Sopenharmony_ci } else { 46362306a36Sopenharmony_ci dev_warn(&dvbdev->udev->dev, "no free buffers\n"); 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci } 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci if (bt656->mode == NEW_FRAME || reset) { 46862306a36Sopenharmony_ci cxusb_vprintk(dvbdev, URB, "will copy field 1\n"); 46962306a36Sopenharmony_ci bt656->pos = 0; 47062306a36Sopenharmony_ci bt656->mode = FIRST_FIELD; 47162306a36Sopenharmony_ci bt656->fmode = START_SEARCH; 47262306a36Sopenharmony_ci bt656->line = 0; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci if (cxdev->vbuf) { 47562306a36Sopenharmony_ci cxdev->vbuf->vb2.vb2_buf.timestamp = ktime_get_ns(); 47662306a36Sopenharmony_ci bt656->buf = vb2_plane_vaddr(&cxdev->vbuf->vb2.vb2_buf, 47762306a36Sopenharmony_ci 0); 47862306a36Sopenharmony_ci } 47962306a36Sopenharmony_ci } 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci if (bt656->mode == FIRST_FIELD) { 48262306a36Sopenharmony_ci if (!cxusb_medion_copy_field(dvbdev, &cxdev->auxbuf, bt656, 48362306a36Sopenharmony_ci true, cxdev->height / 2, 48462306a36Sopenharmony_ci cxdev->width * 2)) 48562306a36Sopenharmony_ci return false; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci /* 48862306a36Sopenharmony_ci * do not trim buffer there in case 48962306a36Sopenharmony_ci * we need to reset the search later 49062306a36Sopenharmony_ci */ 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci cxusb_vprintk(dvbdev, URB, "will copy field 2\n"); 49362306a36Sopenharmony_ci bt656->mode = SECOND_FIELD; 49462306a36Sopenharmony_ci bt656->fmode = START_SEARCH; 49562306a36Sopenharmony_ci bt656->line = 0; 49662306a36Sopenharmony_ci } 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci if (bt656->mode == SECOND_FIELD) { 49962306a36Sopenharmony_ci if (!cxusb_medion_copy_field(dvbdev, &cxdev->auxbuf, bt656, 50062306a36Sopenharmony_ci false, cxdev->height / 2, 50162306a36Sopenharmony_ci cxdev->width * 2)) 50262306a36Sopenharmony_ci return false; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci cxusb_auxbuf_head_trim(dvbdev, &cxdev->auxbuf, bt656->pos); 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci bt656->mode = NEW_FRAME; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci if (cxdev->vbuf) { 50962306a36Sopenharmony_ci vb2_set_plane_payload(&cxdev->vbuf->vb2.vb2_buf, 0, 51062306a36Sopenharmony_ci cxdev->width * cxdev->height * 2); 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci cxdev->vbuf->vb2.field = cxdev->field_order; 51362306a36Sopenharmony_ci cxdev->vbuf->vb2.sequence = cxdev->vbuf_sequence++; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci vb2_buffer_done(&cxdev->vbuf->vb2.vb2_buf, 51662306a36Sopenharmony_ci VB2_BUF_STATE_DONE); 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci cxdev->vbuf = NULL; 51962306a36Sopenharmony_ci cxdev->bt656.buf = NULL; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci cxusb_vprintk(dvbdev, URB, "frame done\n"); 52262306a36Sopenharmony_ci } else { 52362306a36Sopenharmony_ci cxusb_vprintk(dvbdev, URB, "frame skipped\n"); 52462306a36Sopenharmony_ci cxdev->vbuf_sequence++; 52562306a36Sopenharmony_ci } 52662306a36Sopenharmony_ci } 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci return true; 52962306a36Sopenharmony_ci} 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_cistatic bool cxusb_medion_v_complete_handle_urb(struct cxusb_medion_dev *cxdev, 53262306a36Sopenharmony_ci bool *auxbuf_reset) 53362306a36Sopenharmony_ci{ 53462306a36Sopenharmony_ci struct dvb_usb_device *dvbdev = cxdev->dvbdev; 53562306a36Sopenharmony_ci unsigned int urbn; 53662306a36Sopenharmony_ci struct urb *urb; 53762306a36Sopenharmony_ci int ret; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci *auxbuf_reset = false; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci urbn = cxdev->nexturb; 54262306a36Sopenharmony_ci if (!test_bit(urbn, &cxdev->urbcomplete)) 54362306a36Sopenharmony_ci return false; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci clear_bit(urbn, &cxdev->urbcomplete); 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci do { 54862306a36Sopenharmony_ci cxdev->nexturb++; 54962306a36Sopenharmony_ci cxdev->nexturb %= CXUSB_VIDEO_URBS; 55062306a36Sopenharmony_ci urb = cxdev->streamurbs[cxdev->nexturb]; 55162306a36Sopenharmony_ci } while (!urb); 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci urb = cxdev->streamurbs[urbn]; 55462306a36Sopenharmony_ci cxusb_vprintk(dvbdev, URB, "URB %u status = %d\n", urbn, urb->status); 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci if (urb->status == 0 || urb->status == -EXDEV) { 55762306a36Sopenharmony_ci int i; 55862306a36Sopenharmony_ci unsigned long len; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci for (i = 0, len = 0; i < urb->number_of_packets; i++) 56162306a36Sopenharmony_ci len += urb->iso_frame_desc[i].actual_length; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci cxusb_vprintk(dvbdev, URB, "URB %u data len = %lu\n", urbn, 56462306a36Sopenharmony_ci len); 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci if (len > 0) { 56762306a36Sopenharmony_ci cxusb_vprintk(dvbdev, URB, "appending URB\n"); 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci /* 57062306a36Sopenharmony_ci * append new data to auxbuf while 57162306a36Sopenharmony_ci * overwriting old data if necessary 57262306a36Sopenharmony_ci * 57362306a36Sopenharmony_ci * if any overwrite happens then we can no 57462306a36Sopenharmony_ci * longer rely on consistency of the whole 57562306a36Sopenharmony_ci * data so let's start again the current 57662306a36Sopenharmony_ci * auxbuf frame assembling process from 57762306a36Sopenharmony_ci * the beginning 57862306a36Sopenharmony_ci */ 57962306a36Sopenharmony_ci *auxbuf_reset = 58062306a36Sopenharmony_ci !cxusb_auxbuf_append_urb(dvbdev, 58162306a36Sopenharmony_ci &cxdev->auxbuf, 58262306a36Sopenharmony_ci urb); 58362306a36Sopenharmony_ci } 58462306a36Sopenharmony_ci } 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci cxusb_vprintk(dvbdev, URB, "URB %u resubmit\n", urbn); 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci ret = usb_submit_urb(urb, GFP_KERNEL); 58962306a36Sopenharmony_ci if (ret != 0) 59062306a36Sopenharmony_ci dev_err(&dvbdev->udev->dev, 59162306a36Sopenharmony_ci "unable to resubmit URB %u (%d), you'll have to restart streaming\n", 59262306a36Sopenharmony_ci urbn, ret); 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci /* next URB is complete already? reschedule us then to handle it */ 59562306a36Sopenharmony_ci return test_bit(cxdev->nexturb, &cxdev->urbcomplete); 59662306a36Sopenharmony_ci} 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_cistatic void cxusb_medion_v_complete_work(struct work_struct *work) 59962306a36Sopenharmony_ci{ 60062306a36Sopenharmony_ci struct cxusb_medion_dev *cxdev = container_of(work, 60162306a36Sopenharmony_ci struct cxusb_medion_dev, 60262306a36Sopenharmony_ci urbwork); 60362306a36Sopenharmony_ci struct dvb_usb_device *dvbdev = cxdev->dvbdev; 60462306a36Sopenharmony_ci bool auxbuf_reset; 60562306a36Sopenharmony_ci bool reschedule; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci mutex_lock(cxdev->videodev->lock); 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci cxusb_vprintk(dvbdev, URB, "worker called, stop_streaming = %d\n", 61062306a36Sopenharmony_ci (int)cxdev->stop_streaming); 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci if (cxdev->stop_streaming) 61362306a36Sopenharmony_ci goto unlock; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci reschedule = cxusb_medion_v_complete_handle_urb(cxdev, &auxbuf_reset); 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci if (cxusb_medion_v_process_auxbuf(cxdev, auxbuf_reset)) 61862306a36Sopenharmony_ci /* reschedule us until auxbuf no longer can produce any frame */ 61962306a36Sopenharmony_ci reschedule = true; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci if (reschedule) { 62262306a36Sopenharmony_ci cxusb_vprintk(dvbdev, URB, "rescheduling worker\n"); 62362306a36Sopenharmony_ci schedule_work(&cxdev->urbwork); 62462306a36Sopenharmony_ci } 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ciunlock: 62762306a36Sopenharmony_ci mutex_unlock(cxdev->videodev->lock); 62862306a36Sopenharmony_ci} 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_cistatic void cxusb_medion_v_complete(struct urb *u) 63162306a36Sopenharmony_ci{ 63262306a36Sopenharmony_ci struct dvb_usb_device *dvbdev = u->context; 63362306a36Sopenharmony_ci struct cxusb_medion_dev *cxdev = dvbdev->priv; 63462306a36Sopenharmony_ci unsigned int i; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci for (i = 0; i < CXUSB_VIDEO_URBS; i++) 63762306a36Sopenharmony_ci if (cxdev->streamurbs[i] == u) 63862306a36Sopenharmony_ci break; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci if (i >= CXUSB_VIDEO_URBS) { 64162306a36Sopenharmony_ci dev_err(&dvbdev->udev->dev, 64262306a36Sopenharmony_ci "complete on unknown URB\n"); 64362306a36Sopenharmony_ci return; 64462306a36Sopenharmony_ci } 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci cxusb_vprintk(dvbdev, URB, "URB %u complete\n", i); 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci set_bit(i, &cxdev->urbcomplete); 64962306a36Sopenharmony_ci schedule_work(&cxdev->urbwork); 65062306a36Sopenharmony_ci} 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_cistatic void cxusb_medion_urbs_free(struct cxusb_medion_dev *cxdev) 65362306a36Sopenharmony_ci{ 65462306a36Sopenharmony_ci unsigned int i; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci for (i = 0; i < CXUSB_VIDEO_URBS; i++) 65762306a36Sopenharmony_ci if (cxdev->streamurbs[i]) { 65862306a36Sopenharmony_ci kfree(cxdev->streamurbs[i]->transfer_buffer); 65962306a36Sopenharmony_ci usb_free_urb(cxdev->streamurbs[i]); 66062306a36Sopenharmony_ci cxdev->streamurbs[i] = NULL; 66162306a36Sopenharmony_ci } 66262306a36Sopenharmony_ci} 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_cistatic void cxusb_medion_return_buffers(struct cxusb_medion_dev *cxdev, 66562306a36Sopenharmony_ci bool requeue) 66662306a36Sopenharmony_ci{ 66762306a36Sopenharmony_ci struct cxusb_medion_vbuffer *vbuf, *vbuf_tmp; 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci list_for_each_entry_safe(vbuf, vbuf_tmp, &cxdev->buflist, 67062306a36Sopenharmony_ci list) { 67162306a36Sopenharmony_ci list_del(&vbuf->list); 67262306a36Sopenharmony_ci vb2_buffer_done(&vbuf->vb2.vb2_buf, 67362306a36Sopenharmony_ci requeue ? VB2_BUF_STATE_QUEUED : 67462306a36Sopenharmony_ci VB2_BUF_STATE_ERROR); 67562306a36Sopenharmony_ci } 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci if (cxdev->vbuf) { 67862306a36Sopenharmony_ci vb2_buffer_done(&cxdev->vbuf->vb2.vb2_buf, 67962306a36Sopenharmony_ci requeue ? VB2_BUF_STATE_QUEUED : 68062306a36Sopenharmony_ci VB2_BUF_STATE_ERROR); 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci cxdev->vbuf = NULL; 68362306a36Sopenharmony_ci cxdev->bt656.buf = NULL; 68462306a36Sopenharmony_ci } 68562306a36Sopenharmony_ci} 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_cistatic int cxusb_medion_v_ss_auxbuf_alloc(struct cxusb_medion_dev *cxdev, 68862306a36Sopenharmony_ci int *npackets) 68962306a36Sopenharmony_ci{ 69062306a36Sopenharmony_ci struct dvb_usb_device *dvbdev = cxdev->dvbdev; 69162306a36Sopenharmony_ci u8 *buf; 69262306a36Sopenharmony_ci unsigned int framelen, urblen, auxbuflen; 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci framelen = (cxdev->width * 2 + 4 + 4) * 69562306a36Sopenharmony_ci (cxdev->height + 50 /* VBI lines */); 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci /* 69862306a36Sopenharmony_ci * try to fit a whole frame into each URB, as long as doing so 69962306a36Sopenharmony_ci * does not require very high order memory allocations 70062306a36Sopenharmony_ci */ 70162306a36Sopenharmony_ci BUILD_BUG_ON(CXUSB_VIDEO_URB_MAX_SIZE / CXUSB_VIDEO_PKT_SIZE > 70262306a36Sopenharmony_ci CXUSB_VIDEO_MAX_FRAME_PKTS); 70362306a36Sopenharmony_ci *npackets = min_t(int, (framelen + CXUSB_VIDEO_PKT_SIZE - 1) / 70462306a36Sopenharmony_ci CXUSB_VIDEO_PKT_SIZE, 70562306a36Sopenharmony_ci CXUSB_VIDEO_URB_MAX_SIZE / CXUSB_VIDEO_PKT_SIZE); 70662306a36Sopenharmony_ci urblen = *npackets * CXUSB_VIDEO_PKT_SIZE; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci cxusb_vprintk(dvbdev, URB, 70962306a36Sopenharmony_ci "each URB will have %d packets for total of %u bytes (%u x %u @ %u)\n", 71062306a36Sopenharmony_ci *npackets, urblen, (unsigned int)cxdev->width, 71162306a36Sopenharmony_ci (unsigned int)cxdev->height, framelen); 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci auxbuflen = framelen + urblen; 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci buf = vmalloc(auxbuflen); 71662306a36Sopenharmony_ci if (!buf) 71762306a36Sopenharmony_ci return -ENOMEM; 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci cxusb_auxbuf_init(dvbdev, &cxdev->auxbuf, buf, auxbuflen); 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci return 0; 72262306a36Sopenharmony_ci} 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_cistatic u32 cxusb_medion_norm2field_order(v4l2_std_id norm) 72562306a36Sopenharmony_ci{ 72662306a36Sopenharmony_ci bool is625 = norm & V4L2_STD_625_50; 72762306a36Sopenharmony_ci bool is525 = norm & V4L2_STD_525_60; 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci if (!is625 && !is525) 73062306a36Sopenharmony_ci return V4L2_FIELD_NONE; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci if (is625 && is525) 73362306a36Sopenharmony_ci return V4L2_FIELD_NONE; 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci if (is625) 73662306a36Sopenharmony_ci return V4L2_FIELD_SEQ_TB; 73762306a36Sopenharmony_ci else /* is525 */ 73862306a36Sopenharmony_ci return V4L2_FIELD_SEQ_BT; 73962306a36Sopenharmony_ci} 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_cistatic u32 cxusb_medion_field_order(struct cxusb_medion_dev *cxdev) 74262306a36Sopenharmony_ci{ 74362306a36Sopenharmony_ci struct dvb_usb_device *dvbdev = cxdev->dvbdev; 74462306a36Sopenharmony_ci u32 field; 74562306a36Sopenharmony_ci int ret; 74662306a36Sopenharmony_ci v4l2_std_id norm; 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci /* TV tuner is PAL-only so it is always TB */ 74962306a36Sopenharmony_ci if (cxdev->input == 0) 75062306a36Sopenharmony_ci return V4L2_FIELD_SEQ_TB; 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci field = cxusb_medion_norm2field_order(cxdev->norm); 75362306a36Sopenharmony_ci if (field != V4L2_FIELD_NONE) 75462306a36Sopenharmony_ci return field; 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci ret = v4l2_subdev_call(cxdev->cx25840, video, g_std, &norm); 75762306a36Sopenharmony_ci if (ret != 0) { 75862306a36Sopenharmony_ci cxusb_vprintk(dvbdev, OPS, 75962306a36Sopenharmony_ci "cannot get current standard for input %u\n", 76062306a36Sopenharmony_ci (unsigned int)cxdev->input); 76162306a36Sopenharmony_ci } else { 76262306a36Sopenharmony_ci field = cxusb_medion_norm2field_order(norm); 76362306a36Sopenharmony_ci if (field != V4L2_FIELD_NONE) 76462306a36Sopenharmony_ci return field; 76562306a36Sopenharmony_ci } 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci dev_warn(&dvbdev->udev->dev, 76862306a36Sopenharmony_ci "cannot determine field order for the current standard setup and received signal, using TB\n"); 76962306a36Sopenharmony_ci return V4L2_FIELD_SEQ_TB; 77062306a36Sopenharmony_ci} 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_cistatic int cxusb_medion_v_start_streaming(struct vb2_queue *q, 77362306a36Sopenharmony_ci unsigned int count) 77462306a36Sopenharmony_ci{ 77562306a36Sopenharmony_ci struct dvb_usb_device *dvbdev = vb2_get_drv_priv(q); 77662306a36Sopenharmony_ci struct cxusb_medion_dev *cxdev = dvbdev->priv; 77762306a36Sopenharmony_ci u8 streamon_params[2] = { 0x03, 0x00 }; 77862306a36Sopenharmony_ci int npackets, i; 77962306a36Sopenharmony_ci int ret; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci cxusb_vprintk(dvbdev, OPS, "should start streaming\n"); 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci if (cxdev->stop_streaming) { 78462306a36Sopenharmony_ci /* stream is being stopped */ 78562306a36Sopenharmony_ci ret = -EBUSY; 78662306a36Sopenharmony_ci goto ret_retbufs; 78762306a36Sopenharmony_ci } 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci cxdev->field_order = cxusb_medion_field_order(cxdev); 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci ret = v4l2_subdev_call(cxdev->cx25840, video, s_stream, 1); 79262306a36Sopenharmony_ci if (ret != 0) { 79362306a36Sopenharmony_ci dev_err(&dvbdev->udev->dev, 79462306a36Sopenharmony_ci "unable to start stream (%d)\n", ret); 79562306a36Sopenharmony_ci goto ret_retbufs; 79662306a36Sopenharmony_ci } 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci ret = cxusb_ctrl_msg(dvbdev, CMD_STREAMING_ON, streamon_params, 2, 79962306a36Sopenharmony_ci NULL, 0); 80062306a36Sopenharmony_ci if (ret != 0) { 80162306a36Sopenharmony_ci dev_err(&dvbdev->udev->dev, 80262306a36Sopenharmony_ci "unable to start streaming (%d)\n", ret); 80362306a36Sopenharmony_ci goto ret_unstream_cx; 80462306a36Sopenharmony_ci } 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci ret = cxusb_medion_v_ss_auxbuf_alloc(cxdev, &npackets); 80762306a36Sopenharmony_ci if (ret != 0) 80862306a36Sopenharmony_ci goto ret_unstream_md; 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci for (i = 0; i < CXUSB_VIDEO_URBS; i++) { 81162306a36Sopenharmony_ci int framen; 81262306a36Sopenharmony_ci u8 *streambuf; 81362306a36Sopenharmony_ci struct urb *surb; 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci /* 81662306a36Sopenharmony_ci * TODO: change this to an array of single pages to avoid 81762306a36Sopenharmony_ci * doing a large continuous allocation when (if) 81862306a36Sopenharmony_ci * s-g isochronous USB transfers are supported 81962306a36Sopenharmony_ci */ 82062306a36Sopenharmony_ci streambuf = kmalloc(npackets * CXUSB_VIDEO_PKT_SIZE, 82162306a36Sopenharmony_ci GFP_KERNEL); 82262306a36Sopenharmony_ci if (!streambuf) { 82362306a36Sopenharmony_ci if (i < 2) { 82462306a36Sopenharmony_ci ret = -ENOMEM; 82562306a36Sopenharmony_ci goto ret_freeab; 82662306a36Sopenharmony_ci } 82762306a36Sopenharmony_ci break; 82862306a36Sopenharmony_ci } 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci surb = usb_alloc_urb(npackets, GFP_KERNEL); 83162306a36Sopenharmony_ci if (!surb) { 83262306a36Sopenharmony_ci kfree(streambuf); 83362306a36Sopenharmony_ci ret = -ENOMEM; 83462306a36Sopenharmony_ci goto ret_freeu; 83562306a36Sopenharmony_ci } 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci cxdev->streamurbs[i] = surb; 83862306a36Sopenharmony_ci surb->dev = dvbdev->udev; 83962306a36Sopenharmony_ci surb->context = dvbdev; 84062306a36Sopenharmony_ci surb->pipe = usb_rcvisocpipe(dvbdev->udev, 2); 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci surb->interval = 1; 84362306a36Sopenharmony_ci surb->transfer_flags = URB_ISO_ASAP; 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci surb->transfer_buffer = streambuf; 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci surb->complete = cxusb_medion_v_complete; 84862306a36Sopenharmony_ci surb->number_of_packets = npackets; 84962306a36Sopenharmony_ci surb->transfer_buffer_length = npackets * CXUSB_VIDEO_PKT_SIZE; 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci for (framen = 0; framen < npackets; framen++) { 85262306a36Sopenharmony_ci surb->iso_frame_desc[framen].offset = 85362306a36Sopenharmony_ci CXUSB_VIDEO_PKT_SIZE * framen; 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci surb->iso_frame_desc[framen].length = 85662306a36Sopenharmony_ci CXUSB_VIDEO_PKT_SIZE; 85762306a36Sopenharmony_ci } 85862306a36Sopenharmony_ci } 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci cxdev->urbcomplete = 0; 86162306a36Sopenharmony_ci cxdev->nexturb = 0; 86262306a36Sopenharmony_ci cxdev->vbuf_sequence = 0; 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci cxdev->vbuf = NULL; 86562306a36Sopenharmony_ci cxdev->bt656.mode = NEW_FRAME; 86662306a36Sopenharmony_ci cxdev->bt656.buf = NULL; 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci for (i = 0; i < CXUSB_VIDEO_URBS; i++) 86962306a36Sopenharmony_ci if (cxdev->streamurbs[i]) { 87062306a36Sopenharmony_ci ret = usb_submit_urb(cxdev->streamurbs[i], 87162306a36Sopenharmony_ci GFP_KERNEL); 87262306a36Sopenharmony_ci if (ret != 0) 87362306a36Sopenharmony_ci dev_err(&dvbdev->udev->dev, 87462306a36Sopenharmony_ci "URB %d submission failed (%d)\n", i, 87562306a36Sopenharmony_ci ret); 87662306a36Sopenharmony_ci } 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci return 0; 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ciret_freeu: 88162306a36Sopenharmony_ci cxusb_medion_urbs_free(cxdev); 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ciret_freeab: 88462306a36Sopenharmony_ci vfree(cxdev->auxbuf.buf); 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ciret_unstream_md: 88762306a36Sopenharmony_ci cxusb_ctrl_msg(dvbdev, CMD_STREAMING_OFF, NULL, 0, NULL, 0); 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ciret_unstream_cx: 89062306a36Sopenharmony_ci v4l2_subdev_call(cxdev->cx25840, video, s_stream, 0); 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ciret_retbufs: 89362306a36Sopenharmony_ci cxusb_medion_return_buffers(cxdev, true); 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci return ret; 89662306a36Sopenharmony_ci} 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_cistatic void cxusb_medion_v_stop_streaming(struct vb2_queue *q) 89962306a36Sopenharmony_ci{ 90062306a36Sopenharmony_ci struct dvb_usb_device *dvbdev = vb2_get_drv_priv(q); 90162306a36Sopenharmony_ci struct cxusb_medion_dev *cxdev = dvbdev->priv; 90262306a36Sopenharmony_ci int ret; 90362306a36Sopenharmony_ci unsigned int i; 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci cxusb_vprintk(dvbdev, OPS, "should stop streaming\n"); 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci if (WARN_ON(cxdev->stop_streaming)) 90862306a36Sopenharmony_ci return; 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci cxdev->stop_streaming = true; 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci cxusb_ctrl_msg(dvbdev, CMD_STREAMING_OFF, NULL, 0, NULL, 0); 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci ret = v4l2_subdev_call(cxdev->cx25840, video, s_stream, 0); 91562306a36Sopenharmony_ci if (ret != 0) 91662306a36Sopenharmony_ci dev_err(&dvbdev->udev->dev, "unable to stop stream (%d)\n", 91762306a36Sopenharmony_ci ret); 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci /* let URB completion run */ 92062306a36Sopenharmony_ci mutex_unlock(cxdev->videodev->lock); 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci for (i = 0; i < CXUSB_VIDEO_URBS; i++) 92362306a36Sopenharmony_ci if (cxdev->streamurbs[i]) 92462306a36Sopenharmony_ci usb_kill_urb(cxdev->streamurbs[i]); 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci flush_work(&cxdev->urbwork); 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci mutex_lock(cxdev->videodev->lock); 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci /* free transfer buffer and URB */ 93162306a36Sopenharmony_ci vfree(cxdev->auxbuf.buf); 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci cxusb_medion_urbs_free(cxdev); 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci cxusb_medion_return_buffers(cxdev, false); 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci cxdev->stop_streaming = false; 93862306a36Sopenharmony_ci} 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_cistatic void cxusub_medion_v_buf_queue(struct vb2_buffer *vb) 94162306a36Sopenharmony_ci{ 94262306a36Sopenharmony_ci struct vb2_v4l2_buffer *v4l2buf = to_vb2_v4l2_buffer(vb); 94362306a36Sopenharmony_ci struct cxusb_medion_vbuffer *vbuf = 94462306a36Sopenharmony_ci container_of(v4l2buf, struct cxusb_medion_vbuffer, vb2); 94562306a36Sopenharmony_ci struct dvb_usb_device *dvbdev = vb2_get_drv_priv(vb->vb2_queue); 94662306a36Sopenharmony_ci struct cxusb_medion_dev *cxdev = dvbdev->priv; 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci /* cxusb_vprintk(dvbdev, OPS, "mmmm.. a fresh buffer...\n"); */ 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci list_add_tail(&vbuf->list, &cxdev->buflist); 95162306a36Sopenharmony_ci} 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_cistatic const struct vb2_ops cxdev_video_qops = { 95462306a36Sopenharmony_ci .queue_setup = cxusb_medion_v_queue_setup, 95562306a36Sopenharmony_ci .buf_init = cxusb_medion_v_buf_init, 95662306a36Sopenharmony_ci .start_streaming = cxusb_medion_v_start_streaming, 95762306a36Sopenharmony_ci .stop_streaming = cxusb_medion_v_stop_streaming, 95862306a36Sopenharmony_ci .buf_queue = cxusub_medion_v_buf_queue, 95962306a36Sopenharmony_ci .wait_prepare = vb2_ops_wait_prepare, 96062306a36Sopenharmony_ci .wait_finish = vb2_ops_wait_finish 96162306a36Sopenharmony_ci}; 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_cistatic const __u32 videocaps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | 96462306a36Sopenharmony_ci V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; 96562306a36Sopenharmony_cistatic const __u32 radiocaps = V4L2_CAP_TUNER | V4L2_CAP_RADIO; 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_cistatic int cxusb_medion_v_querycap(struct file *file, void *fh, 96862306a36Sopenharmony_ci struct v4l2_capability *cap) 96962306a36Sopenharmony_ci{ 97062306a36Sopenharmony_ci struct dvb_usb_device *dvbdev = video_drvdata(file); 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci strscpy(cap->driver, dvbdev->udev->dev.driver->name, 97362306a36Sopenharmony_ci sizeof(cap->driver)); 97462306a36Sopenharmony_ci strscpy(cap->card, "Medion 95700", sizeof(cap->card)); 97562306a36Sopenharmony_ci usb_make_path(dvbdev->udev, cap->bus_info, sizeof(cap->bus_info)); 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci cap->capabilities = videocaps | radiocaps | V4L2_CAP_DEVICE_CAPS; 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci return 0; 98062306a36Sopenharmony_ci} 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_cistatic int cxusb_medion_v_enum_fmt_vid_cap(struct file *file, void *fh, 98362306a36Sopenharmony_ci struct v4l2_fmtdesc *f) 98462306a36Sopenharmony_ci{ 98562306a36Sopenharmony_ci if (f->index != 0) 98662306a36Sopenharmony_ci return -EINVAL; 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci f->pixelformat = V4L2_PIX_FMT_UYVY; 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci return 0; 99162306a36Sopenharmony_ci} 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_cistatic int cxusb_medion_g_fmt_vid_cap(struct file *file, void *fh, 99462306a36Sopenharmony_ci struct v4l2_format *f) 99562306a36Sopenharmony_ci{ 99662306a36Sopenharmony_ci struct dvb_usb_device *dvbdev = video_drvdata(file); 99762306a36Sopenharmony_ci struct cxusb_medion_dev *cxdev = dvbdev->priv; 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci f->fmt.pix.width = cxdev->width; 100062306a36Sopenharmony_ci f->fmt.pix.height = cxdev->height; 100162306a36Sopenharmony_ci f->fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY; 100262306a36Sopenharmony_ci f->fmt.pix.field = vb2_start_streaming_called(&cxdev->videoqueue) ? 100362306a36Sopenharmony_ci cxdev->field_order : cxusb_medion_field_order(cxdev); 100462306a36Sopenharmony_ci f->fmt.pix.bytesperline = cxdev->width * 2; 100562306a36Sopenharmony_ci f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; 100662306a36Sopenharmony_ci f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height; 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci return 0; 100962306a36Sopenharmony_ci} 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_cistatic int cxusb_medion_try_s_fmt_vid_cap(struct file *file, 101262306a36Sopenharmony_ci struct v4l2_format *f, 101362306a36Sopenharmony_ci bool isset) 101462306a36Sopenharmony_ci{ 101562306a36Sopenharmony_ci struct dvb_usb_device *dvbdev = video_drvdata(file); 101662306a36Sopenharmony_ci struct cxusb_medion_dev *cxdev = dvbdev->priv; 101762306a36Sopenharmony_ci struct v4l2_subdev_format subfmt = { 101862306a36Sopenharmony_ci .which = isset ? V4L2_SUBDEV_FORMAT_ACTIVE : 101962306a36Sopenharmony_ci V4L2_SUBDEV_FORMAT_TRY, 102062306a36Sopenharmony_ci }; 102162306a36Sopenharmony_ci u32 field; 102262306a36Sopenharmony_ci int ret; 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci if (isset && vb2_is_busy(&cxdev->videoqueue)) 102562306a36Sopenharmony_ci return -EBUSY; 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci field = vb2_start_streaming_called(&cxdev->videoqueue) ? 102862306a36Sopenharmony_ci cxdev->field_order : cxusb_medion_field_order(cxdev); 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci subfmt.format.width = f->fmt.pix.width & ~1; 103162306a36Sopenharmony_ci subfmt.format.height = f->fmt.pix.height & ~1; 103262306a36Sopenharmony_ci subfmt.format.code = MEDIA_BUS_FMT_FIXED; 103362306a36Sopenharmony_ci subfmt.format.field = field; 103462306a36Sopenharmony_ci subfmt.format.colorspace = V4L2_COLORSPACE_SMPTE170M; 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci ret = v4l2_subdev_call(cxdev->cx25840, pad, set_fmt, NULL, &subfmt); 103762306a36Sopenharmony_ci if (ret != 0) 103862306a36Sopenharmony_ci return ret; 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci f->fmt.pix.width = subfmt.format.width; 104162306a36Sopenharmony_ci f->fmt.pix.height = subfmt.format.height; 104262306a36Sopenharmony_ci f->fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY; 104362306a36Sopenharmony_ci f->fmt.pix.field = field; 104462306a36Sopenharmony_ci f->fmt.pix.bytesperline = f->fmt.pix.width * 2; 104562306a36Sopenharmony_ci f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height; 104662306a36Sopenharmony_ci f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci if (isset) { 104962306a36Sopenharmony_ci cxdev->width = f->fmt.pix.width; 105062306a36Sopenharmony_ci cxdev->height = f->fmt.pix.height; 105162306a36Sopenharmony_ci } 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci return 0; 105462306a36Sopenharmony_ci} 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_cistatic int cxusb_medion_try_fmt_vid_cap(struct file *file, void *fh, 105762306a36Sopenharmony_ci struct v4l2_format *f) 105862306a36Sopenharmony_ci{ 105962306a36Sopenharmony_ci return cxusb_medion_try_s_fmt_vid_cap(file, f, false); 106062306a36Sopenharmony_ci} 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_cistatic int cxusb_medion_s_fmt_vid_cap(struct file *file, void *fh, 106362306a36Sopenharmony_ci struct v4l2_format *f) 106462306a36Sopenharmony_ci{ 106562306a36Sopenharmony_ci return cxusb_medion_try_s_fmt_vid_cap(file, f, true); 106662306a36Sopenharmony_ci} 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_cistatic const struct { 106962306a36Sopenharmony_ci struct v4l2_input input; 107062306a36Sopenharmony_ci u32 inputcfg; 107162306a36Sopenharmony_ci} cxusb_medion_inputs[] = { 107262306a36Sopenharmony_ci { .input = { .name = "TV tuner", .type = V4L2_INPUT_TYPE_TUNER, 107362306a36Sopenharmony_ci .tuner = 0, .std = V4L2_STD_PAL }, 107462306a36Sopenharmony_ci .inputcfg = CX25840_COMPOSITE2, }, 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_ci { .input = { .name = "Composite", .type = V4L2_INPUT_TYPE_CAMERA, 107762306a36Sopenharmony_ci .std = V4L2_STD_ALL }, 107862306a36Sopenharmony_ci .inputcfg = CX25840_COMPOSITE1, }, 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci { .input = { .name = "S-Video", .type = V4L2_INPUT_TYPE_CAMERA, 108162306a36Sopenharmony_ci .std = V4L2_STD_ALL }, 108262306a36Sopenharmony_ci .inputcfg = CX25840_SVIDEO_LUMA3 | CX25840_SVIDEO_CHROMA4 } 108362306a36Sopenharmony_ci}; 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci#define CXUSB_INPUT_CNT ARRAY_SIZE(cxusb_medion_inputs) 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_cistatic int cxusb_medion_enum_input(struct file *file, void *fh, 108862306a36Sopenharmony_ci struct v4l2_input *inp) 108962306a36Sopenharmony_ci{ 109062306a36Sopenharmony_ci struct dvb_usb_device *dvbdev = video_drvdata(file); 109162306a36Sopenharmony_ci struct cxusb_medion_dev *cxdev = dvbdev->priv; 109262306a36Sopenharmony_ci u32 index = inp->index; 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ci if (index >= CXUSB_INPUT_CNT) 109562306a36Sopenharmony_ci return -EINVAL; 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci *inp = cxusb_medion_inputs[index].input; 109862306a36Sopenharmony_ci inp->index = index; 109962306a36Sopenharmony_ci inp->capabilities |= V4L2_IN_CAP_STD; 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci if (index == cxdev->input) { 110262306a36Sopenharmony_ci int ret; 110362306a36Sopenharmony_ci u32 status = 0; 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_ci ret = v4l2_subdev_call(cxdev->cx25840, video, g_input_status, 110662306a36Sopenharmony_ci &status); 110762306a36Sopenharmony_ci if (ret != 0) 110862306a36Sopenharmony_ci dev_warn(&dvbdev->udev->dev, 110962306a36Sopenharmony_ci "cx25840 input status query failed (%d)\n", 111062306a36Sopenharmony_ci ret); 111162306a36Sopenharmony_ci else 111262306a36Sopenharmony_ci inp->status = status; 111362306a36Sopenharmony_ci } 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ci return 0; 111662306a36Sopenharmony_ci} 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_cistatic int cxusb_medion_g_input(struct file *file, void *fh, 111962306a36Sopenharmony_ci unsigned int *i) 112062306a36Sopenharmony_ci{ 112162306a36Sopenharmony_ci struct dvb_usb_device *dvbdev = video_drvdata(file); 112262306a36Sopenharmony_ci struct cxusb_medion_dev *cxdev = dvbdev->priv; 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_ci *i = cxdev->input; 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci return 0; 112762306a36Sopenharmony_ci} 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_cistatic int cxusb_medion_set_norm(struct cxusb_medion_dev *cxdev, 113062306a36Sopenharmony_ci v4l2_std_id norm) 113162306a36Sopenharmony_ci{ 113262306a36Sopenharmony_ci struct dvb_usb_device *dvbdev = cxdev->dvbdev; 113362306a36Sopenharmony_ci int ret; 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_ci cxusb_vprintk(dvbdev, OPS, 113662306a36Sopenharmony_ci "trying to set standard for input %u to %lx\n", 113762306a36Sopenharmony_ci (unsigned int)cxdev->input, 113862306a36Sopenharmony_ci (unsigned long)norm); 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci /* no autodetection support */ 114162306a36Sopenharmony_ci if (norm == V4L2_STD_UNKNOWN) 114262306a36Sopenharmony_ci return -EINVAL; 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci /* on composite or S-Video any std is acceptable */ 114562306a36Sopenharmony_ci if (cxdev->input != 0) { 114662306a36Sopenharmony_ci ret = v4l2_subdev_call(cxdev->cx25840, video, s_std, norm); 114762306a36Sopenharmony_ci if (ret) 114862306a36Sopenharmony_ci return ret; 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ci goto ret_savenorm; 115162306a36Sopenharmony_ci } 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_ci /* TV tuner is only able to demodulate PAL */ 115462306a36Sopenharmony_ci if ((norm & ~V4L2_STD_PAL) != 0) 115562306a36Sopenharmony_ci return -EINVAL; 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci ret = v4l2_subdev_call(cxdev->tda9887, video, s_std, norm); 115862306a36Sopenharmony_ci if (ret != 0) { 115962306a36Sopenharmony_ci dev_err(&dvbdev->udev->dev, 116062306a36Sopenharmony_ci "tda9887 norm setup failed (%d)\n", 116162306a36Sopenharmony_ci ret); 116262306a36Sopenharmony_ci return ret; 116362306a36Sopenharmony_ci } 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_ci ret = v4l2_subdev_call(cxdev->tuner, video, s_std, norm); 116662306a36Sopenharmony_ci if (ret != 0) { 116762306a36Sopenharmony_ci dev_err(&dvbdev->udev->dev, 116862306a36Sopenharmony_ci "tuner norm setup failed (%d)\n", 116962306a36Sopenharmony_ci ret); 117062306a36Sopenharmony_ci return ret; 117162306a36Sopenharmony_ci } 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci ret = v4l2_subdev_call(cxdev->cx25840, video, s_std, norm); 117462306a36Sopenharmony_ci if (ret != 0) { 117562306a36Sopenharmony_ci dev_err(&dvbdev->udev->dev, 117662306a36Sopenharmony_ci "cx25840 norm setup failed (%d)\n", 117762306a36Sopenharmony_ci ret); 117862306a36Sopenharmony_ci return ret; 117962306a36Sopenharmony_ci } 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ciret_savenorm: 118262306a36Sopenharmony_ci cxdev->norm = norm; 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci return 0; 118562306a36Sopenharmony_ci} 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_cistatic int cxusb_medion_s_input(struct file *file, void *fh, 118862306a36Sopenharmony_ci unsigned int i) 118962306a36Sopenharmony_ci{ 119062306a36Sopenharmony_ci struct dvb_usb_device *dvbdev = video_drvdata(file); 119162306a36Sopenharmony_ci struct cxusb_medion_dev *cxdev = dvbdev->priv; 119262306a36Sopenharmony_ci int ret; 119362306a36Sopenharmony_ci v4l2_std_id norm; 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci if (i >= CXUSB_INPUT_CNT) 119662306a36Sopenharmony_ci return -EINVAL; 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_ci ret = v4l2_subdev_call(cxdev->cx25840, video, s_routing, 119962306a36Sopenharmony_ci cxusb_medion_inputs[i].inputcfg, 0, 0); 120062306a36Sopenharmony_ci if (ret != 0) 120162306a36Sopenharmony_ci return ret; 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_ci cxdev->input = i; 120462306a36Sopenharmony_ci cxdev->videodev->tvnorms = cxusb_medion_inputs[i].input.std; 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_ci norm = cxdev->norm & cxusb_medion_inputs[i].input.std; 120762306a36Sopenharmony_ci if (norm == 0) 120862306a36Sopenharmony_ci norm = cxusb_medion_inputs[i].input.std; 120962306a36Sopenharmony_ci 121062306a36Sopenharmony_ci cxusb_medion_set_norm(cxdev, norm); 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci return 0; 121362306a36Sopenharmony_ci} 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_cistatic int cxusb_medion_g_tuner(struct file *file, void *fh, 121662306a36Sopenharmony_ci struct v4l2_tuner *tuner) 121762306a36Sopenharmony_ci{ 121862306a36Sopenharmony_ci struct dvb_usb_device *dvbdev = video_drvdata(file); 121962306a36Sopenharmony_ci struct cxusb_medion_dev *cxdev = dvbdev->priv; 122062306a36Sopenharmony_ci struct video_device *vdev = video_devdata(file); 122162306a36Sopenharmony_ci int ret; 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_ci if (tuner->index != 0) 122462306a36Sopenharmony_ci return -EINVAL; 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_ci if (vdev->vfl_type == VFL_TYPE_VIDEO) 122762306a36Sopenharmony_ci tuner->type = V4L2_TUNER_ANALOG_TV; 122862306a36Sopenharmony_ci else 122962306a36Sopenharmony_ci tuner->type = V4L2_TUNER_RADIO; 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci tuner->capability = 0; 123262306a36Sopenharmony_ci tuner->afc = 0; 123362306a36Sopenharmony_ci 123462306a36Sopenharmony_ci /* 123562306a36Sopenharmony_ci * fills: 123662306a36Sopenharmony_ci * always: capability (static), rangelow (static), rangehigh (static) 123762306a36Sopenharmony_ci * radio mode: afc (may fail silently), rxsubchans (static), audmode 123862306a36Sopenharmony_ci */ 123962306a36Sopenharmony_ci ret = v4l2_subdev_call(cxdev->tda9887, tuner, g_tuner, tuner); 124062306a36Sopenharmony_ci if (ret != 0) 124162306a36Sopenharmony_ci return ret; 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci /* 124462306a36Sopenharmony_ci * fills: 124562306a36Sopenharmony_ci * always: capability (static), rangelow (static), rangehigh (static) 124662306a36Sopenharmony_ci * radio mode: rxsubchans (always stereo), audmode, 124762306a36Sopenharmony_ci * signal (might be wrong) 124862306a36Sopenharmony_ci */ 124962306a36Sopenharmony_ci ret = v4l2_subdev_call(cxdev->tuner, tuner, g_tuner, tuner); 125062306a36Sopenharmony_ci if (ret != 0) 125162306a36Sopenharmony_ci return ret; 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_ci tuner->signal = 0; 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_ci /* 125662306a36Sopenharmony_ci * fills: TV mode: capability, rxsubchans, audmode, signal 125762306a36Sopenharmony_ci */ 125862306a36Sopenharmony_ci ret = v4l2_subdev_call(cxdev->cx25840, tuner, g_tuner, tuner); 125962306a36Sopenharmony_ci if (ret != 0) 126062306a36Sopenharmony_ci return ret; 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_ci if (vdev->vfl_type == VFL_TYPE_VIDEO) 126362306a36Sopenharmony_ci strscpy(tuner->name, "TV tuner", sizeof(tuner->name)); 126462306a36Sopenharmony_ci else 126562306a36Sopenharmony_ci strscpy(tuner->name, "Radio tuner", sizeof(tuner->name)); 126662306a36Sopenharmony_ci 126762306a36Sopenharmony_ci memset(tuner->reserved, 0, sizeof(tuner->reserved)); 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_ci return 0; 127062306a36Sopenharmony_ci} 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_cistatic int cxusb_medion_s_tuner(struct file *file, void *fh, 127362306a36Sopenharmony_ci const struct v4l2_tuner *tuner) 127462306a36Sopenharmony_ci{ 127562306a36Sopenharmony_ci struct dvb_usb_device *dvbdev = video_drvdata(file); 127662306a36Sopenharmony_ci struct cxusb_medion_dev *cxdev = dvbdev->priv; 127762306a36Sopenharmony_ci struct video_device *vdev = video_devdata(file); 127862306a36Sopenharmony_ci int ret; 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_ci if (tuner->index != 0) 128162306a36Sopenharmony_ci return -EINVAL; 128262306a36Sopenharmony_ci 128362306a36Sopenharmony_ci ret = v4l2_subdev_call(cxdev->tda9887, tuner, s_tuner, tuner); 128462306a36Sopenharmony_ci if (ret != 0) 128562306a36Sopenharmony_ci return ret; 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_ci ret = v4l2_subdev_call(cxdev->tuner, tuner, s_tuner, tuner); 128862306a36Sopenharmony_ci if (ret != 0) 128962306a36Sopenharmony_ci return ret; 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_ci /* 129262306a36Sopenharmony_ci * make sure that cx25840 is in a correct TV / radio mode, 129362306a36Sopenharmony_ci * since calls above may have changed it for tuner / IF demod 129462306a36Sopenharmony_ci */ 129562306a36Sopenharmony_ci if (vdev->vfl_type == VFL_TYPE_VIDEO) 129662306a36Sopenharmony_ci v4l2_subdev_call(cxdev->cx25840, video, s_std, cxdev->norm); 129762306a36Sopenharmony_ci else 129862306a36Sopenharmony_ci v4l2_subdev_call(cxdev->cx25840, tuner, s_radio); 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_ci return v4l2_subdev_call(cxdev->cx25840, tuner, s_tuner, tuner); 130162306a36Sopenharmony_ci} 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_cistatic int cxusb_medion_g_frequency(struct file *file, void *fh, 130462306a36Sopenharmony_ci struct v4l2_frequency *freq) 130562306a36Sopenharmony_ci{ 130662306a36Sopenharmony_ci struct dvb_usb_device *dvbdev = video_drvdata(file); 130762306a36Sopenharmony_ci struct cxusb_medion_dev *cxdev = dvbdev->priv; 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ci if (freq->tuner != 0) 131062306a36Sopenharmony_ci return -EINVAL; 131162306a36Sopenharmony_ci 131262306a36Sopenharmony_ci return v4l2_subdev_call(cxdev->tuner, tuner, g_frequency, freq); 131362306a36Sopenharmony_ci} 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_cistatic int cxusb_medion_s_frequency(struct file *file, void *fh, 131662306a36Sopenharmony_ci const struct v4l2_frequency *freq) 131762306a36Sopenharmony_ci{ 131862306a36Sopenharmony_ci struct dvb_usb_device *dvbdev = video_drvdata(file); 131962306a36Sopenharmony_ci struct cxusb_medion_dev *cxdev = dvbdev->priv; 132062306a36Sopenharmony_ci struct video_device *vdev = video_devdata(file); 132162306a36Sopenharmony_ci int ret; 132262306a36Sopenharmony_ci 132362306a36Sopenharmony_ci if (freq->tuner != 0) 132462306a36Sopenharmony_ci return -EINVAL; 132562306a36Sopenharmony_ci 132662306a36Sopenharmony_ci ret = v4l2_subdev_call(cxdev->tda9887, tuner, s_frequency, freq); 132762306a36Sopenharmony_ci if (ret != 0) 132862306a36Sopenharmony_ci return ret; 132962306a36Sopenharmony_ci 133062306a36Sopenharmony_ci ret = v4l2_subdev_call(cxdev->tuner, tuner, s_frequency, freq); 133162306a36Sopenharmony_ci if (ret != 0) 133262306a36Sopenharmony_ci return ret; 133362306a36Sopenharmony_ci 133462306a36Sopenharmony_ci /* 133562306a36Sopenharmony_ci * make sure that cx25840 is in a correct TV / radio mode, 133662306a36Sopenharmony_ci * since calls above may have changed it for tuner / IF demod 133762306a36Sopenharmony_ci */ 133862306a36Sopenharmony_ci if (vdev->vfl_type == VFL_TYPE_VIDEO) 133962306a36Sopenharmony_ci v4l2_subdev_call(cxdev->cx25840, video, s_std, cxdev->norm); 134062306a36Sopenharmony_ci else 134162306a36Sopenharmony_ci v4l2_subdev_call(cxdev->cx25840, tuner, s_radio); 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_ci return v4l2_subdev_call(cxdev->cx25840, tuner, s_frequency, freq); 134462306a36Sopenharmony_ci} 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_cistatic int cxusb_medion_g_std(struct file *file, void *fh, 134762306a36Sopenharmony_ci v4l2_std_id *norm) 134862306a36Sopenharmony_ci{ 134962306a36Sopenharmony_ci struct dvb_usb_device *dvbdev = video_drvdata(file); 135062306a36Sopenharmony_ci struct cxusb_medion_dev *cxdev = dvbdev->priv; 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_ci *norm = cxdev->norm; 135362306a36Sopenharmony_ci 135462306a36Sopenharmony_ci if (*norm == V4L2_STD_UNKNOWN) 135562306a36Sopenharmony_ci return -ENODATA; 135662306a36Sopenharmony_ci 135762306a36Sopenharmony_ci return 0; 135862306a36Sopenharmony_ci} 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_cistatic int cxusb_medion_s_std(struct file *file, void *fh, 136162306a36Sopenharmony_ci v4l2_std_id norm) 136262306a36Sopenharmony_ci{ 136362306a36Sopenharmony_ci struct dvb_usb_device *dvbdev = video_drvdata(file); 136462306a36Sopenharmony_ci struct cxusb_medion_dev *cxdev = dvbdev->priv; 136562306a36Sopenharmony_ci 136662306a36Sopenharmony_ci return cxusb_medion_set_norm(cxdev, norm); 136762306a36Sopenharmony_ci} 136862306a36Sopenharmony_ci 136962306a36Sopenharmony_cistatic int cxusb_medion_querystd(struct file *file, void *fh, 137062306a36Sopenharmony_ci v4l2_std_id *norm) 137162306a36Sopenharmony_ci{ 137262306a36Sopenharmony_ci struct dvb_usb_device *dvbdev = video_drvdata(file); 137362306a36Sopenharmony_ci struct cxusb_medion_dev *cxdev = dvbdev->priv; 137462306a36Sopenharmony_ci v4l2_std_id norm_mask; 137562306a36Sopenharmony_ci int ret; 137662306a36Sopenharmony_ci 137762306a36Sopenharmony_ci /* 137862306a36Sopenharmony_ci * make sure we don't have improper std bits set for the TV tuner 137962306a36Sopenharmony_ci * (could happen when no signal was present yet after reset) 138062306a36Sopenharmony_ci */ 138162306a36Sopenharmony_ci if (cxdev->input == 0) 138262306a36Sopenharmony_ci norm_mask = V4L2_STD_PAL; 138362306a36Sopenharmony_ci else 138462306a36Sopenharmony_ci norm_mask = V4L2_STD_ALL; 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_ci ret = v4l2_subdev_call(cxdev->cx25840, video, querystd, norm); 138762306a36Sopenharmony_ci if (ret != 0) { 138862306a36Sopenharmony_ci cxusb_vprintk(dvbdev, OPS, 138962306a36Sopenharmony_ci "cannot get detected standard for input %u\n", 139062306a36Sopenharmony_ci (unsigned int)cxdev->input); 139162306a36Sopenharmony_ci return ret; 139262306a36Sopenharmony_ci } 139362306a36Sopenharmony_ci 139462306a36Sopenharmony_ci cxusb_vprintk(dvbdev, OPS, "input %u detected standard is %lx\n", 139562306a36Sopenharmony_ci (unsigned int)cxdev->input, (unsigned long)*norm); 139662306a36Sopenharmony_ci *norm &= norm_mask; 139762306a36Sopenharmony_ci 139862306a36Sopenharmony_ci return 0; 139962306a36Sopenharmony_ci} 140062306a36Sopenharmony_ci 140162306a36Sopenharmony_cistatic int cxusb_medion_log_status(struct file *file, void *fh) 140262306a36Sopenharmony_ci{ 140362306a36Sopenharmony_ci struct dvb_usb_device *dvbdev = video_drvdata(file); 140462306a36Sopenharmony_ci struct cxusb_medion_dev *cxdev = dvbdev->priv; 140562306a36Sopenharmony_ci 140662306a36Sopenharmony_ci v4l2_device_call_all(&cxdev->v4l2dev, 0, core, log_status); 140762306a36Sopenharmony_ci 140862306a36Sopenharmony_ci return 0; 140962306a36Sopenharmony_ci} 141062306a36Sopenharmony_ci 141162306a36Sopenharmony_cistatic const struct v4l2_ioctl_ops cxusb_video_ioctl = { 141262306a36Sopenharmony_ci .vidioc_querycap = cxusb_medion_v_querycap, 141362306a36Sopenharmony_ci .vidioc_enum_fmt_vid_cap = cxusb_medion_v_enum_fmt_vid_cap, 141462306a36Sopenharmony_ci .vidioc_g_fmt_vid_cap = cxusb_medion_g_fmt_vid_cap, 141562306a36Sopenharmony_ci .vidioc_s_fmt_vid_cap = cxusb_medion_s_fmt_vid_cap, 141662306a36Sopenharmony_ci .vidioc_try_fmt_vid_cap = cxusb_medion_try_fmt_vid_cap, 141762306a36Sopenharmony_ci .vidioc_enum_input = cxusb_medion_enum_input, 141862306a36Sopenharmony_ci .vidioc_g_input = cxusb_medion_g_input, 141962306a36Sopenharmony_ci .vidioc_s_input = cxusb_medion_s_input, 142062306a36Sopenharmony_ci .vidioc_g_tuner = cxusb_medion_g_tuner, 142162306a36Sopenharmony_ci .vidioc_s_tuner = cxusb_medion_s_tuner, 142262306a36Sopenharmony_ci .vidioc_g_frequency = cxusb_medion_g_frequency, 142362306a36Sopenharmony_ci .vidioc_s_frequency = cxusb_medion_s_frequency, 142462306a36Sopenharmony_ci .vidioc_g_std = cxusb_medion_g_std, 142562306a36Sopenharmony_ci .vidioc_s_std = cxusb_medion_s_std, 142662306a36Sopenharmony_ci .vidioc_querystd = cxusb_medion_querystd, 142762306a36Sopenharmony_ci .vidioc_log_status = cxusb_medion_log_status, 142862306a36Sopenharmony_ci .vidioc_reqbufs = vb2_ioctl_reqbufs, 142962306a36Sopenharmony_ci .vidioc_querybuf = vb2_ioctl_querybuf, 143062306a36Sopenharmony_ci .vidioc_qbuf = vb2_ioctl_qbuf, 143162306a36Sopenharmony_ci .vidioc_dqbuf = vb2_ioctl_dqbuf, 143262306a36Sopenharmony_ci .vidioc_create_bufs = vb2_ioctl_create_bufs, 143362306a36Sopenharmony_ci .vidioc_prepare_buf = vb2_ioctl_prepare_buf, 143462306a36Sopenharmony_ci .vidioc_streamon = vb2_ioctl_streamon, 143562306a36Sopenharmony_ci .vidioc_streamoff = vb2_ioctl_streamoff 143662306a36Sopenharmony_ci}; 143762306a36Sopenharmony_ci 143862306a36Sopenharmony_cistatic const struct v4l2_ioctl_ops cxusb_radio_ioctl = { 143962306a36Sopenharmony_ci .vidioc_querycap = cxusb_medion_v_querycap, 144062306a36Sopenharmony_ci .vidioc_g_tuner = cxusb_medion_g_tuner, 144162306a36Sopenharmony_ci .vidioc_s_tuner = cxusb_medion_s_tuner, 144262306a36Sopenharmony_ci .vidioc_g_frequency = cxusb_medion_g_frequency, 144362306a36Sopenharmony_ci .vidioc_s_frequency = cxusb_medion_s_frequency, 144462306a36Sopenharmony_ci .vidioc_log_status = cxusb_medion_log_status 144562306a36Sopenharmony_ci}; 144662306a36Sopenharmony_ci 144762306a36Sopenharmony_ci/* 144862306a36Sopenharmony_ci * in principle, this should be const, but s_io_pin_config is declared 144962306a36Sopenharmony_ci * to take non-const, and gcc complains 145062306a36Sopenharmony_ci */ 145162306a36Sopenharmony_cistatic struct v4l2_subdev_io_pin_config cxusub_medion_pin_config[] = { 145262306a36Sopenharmony_ci { .pin = CX25840_PIN_DVALID_PRGM0, .function = CX25840_PAD_DEFAULT, 145362306a36Sopenharmony_ci .strength = CX25840_PIN_DRIVE_MEDIUM }, 145462306a36Sopenharmony_ci { .pin = CX25840_PIN_PLL_CLK_PRGM7, .function = CX25840_PAD_AUX_PLL }, 145562306a36Sopenharmony_ci { .pin = CX25840_PIN_HRESET_PRGM2, .function = CX25840_PAD_ACTIVE, 145662306a36Sopenharmony_ci .strength = CX25840_PIN_DRIVE_MEDIUM } 145762306a36Sopenharmony_ci}; 145862306a36Sopenharmony_ci 145962306a36Sopenharmony_ciint cxusb_medion_analog_init(struct dvb_usb_device *dvbdev) 146062306a36Sopenharmony_ci{ 146162306a36Sopenharmony_ci struct cxusb_medion_dev *cxdev = dvbdev->priv; 146262306a36Sopenharmony_ci u8 tuner_analog_msg_data[] = { 0x9c, 0x60, 0x85, 0x54 }; 146362306a36Sopenharmony_ci struct i2c_msg tuner_analog_msg = { .addr = 0x61, .flags = 0, 146462306a36Sopenharmony_ci .buf = tuner_analog_msg_data, 146562306a36Sopenharmony_ci .len = 146662306a36Sopenharmony_ci sizeof(tuner_analog_msg_data) }; 146762306a36Sopenharmony_ci struct v4l2_subdev_format subfmt = { 146862306a36Sopenharmony_ci .which = V4L2_SUBDEV_FORMAT_ACTIVE, 146962306a36Sopenharmony_ci }; 147062306a36Sopenharmony_ci int ret; 147162306a36Sopenharmony_ci 147262306a36Sopenharmony_ci /* switch tuner to analog mode so IF demod will become accessible */ 147362306a36Sopenharmony_ci ret = i2c_transfer(&dvbdev->i2c_adap, &tuner_analog_msg, 1); 147462306a36Sopenharmony_ci if (ret != 1) 147562306a36Sopenharmony_ci dev_warn(&dvbdev->udev->dev, 147662306a36Sopenharmony_ci "tuner analog switch failed (%d)\n", ret); 147762306a36Sopenharmony_ci 147862306a36Sopenharmony_ci /* 147962306a36Sopenharmony_ci * cx25840 might have lost power during mode switching so we need 148062306a36Sopenharmony_ci * to set it again 148162306a36Sopenharmony_ci */ 148262306a36Sopenharmony_ci ret = v4l2_subdev_call(cxdev->cx25840, core, reset, 0); 148362306a36Sopenharmony_ci if (ret != 0) 148462306a36Sopenharmony_ci dev_warn(&dvbdev->udev->dev, 148562306a36Sopenharmony_ci "cx25840 reset failed (%d)\n", ret); 148662306a36Sopenharmony_ci 148762306a36Sopenharmony_ci ret = v4l2_subdev_call(cxdev->cx25840, video, s_routing, 148862306a36Sopenharmony_ci CX25840_COMPOSITE1, 0, 0); 148962306a36Sopenharmony_ci if (ret != 0) 149062306a36Sopenharmony_ci dev_warn(&dvbdev->udev->dev, 149162306a36Sopenharmony_ci "cx25840 initial input setting failed (%d)\n", ret); 149262306a36Sopenharmony_ci 149362306a36Sopenharmony_ci /* composite */ 149462306a36Sopenharmony_ci cxdev->input = 1; 149562306a36Sopenharmony_ci cxdev->videodev->tvnorms = V4L2_STD_ALL; 149662306a36Sopenharmony_ci cxdev->norm = V4L2_STD_PAL; 149762306a36Sopenharmony_ci 149862306a36Sopenharmony_ci /* TODO: setup audio samples insertion */ 149962306a36Sopenharmony_ci 150062306a36Sopenharmony_ci ret = v4l2_subdev_call(cxdev->cx25840, core, s_io_pin_config, 150162306a36Sopenharmony_ci ARRAY_SIZE(cxusub_medion_pin_config), 150262306a36Sopenharmony_ci cxusub_medion_pin_config); 150362306a36Sopenharmony_ci if (ret != 0) 150462306a36Sopenharmony_ci dev_warn(&dvbdev->udev->dev, 150562306a36Sopenharmony_ci "cx25840 pin config failed (%d)\n", ret); 150662306a36Sopenharmony_ci 150762306a36Sopenharmony_ci /* make sure that we aren't in radio mode */ 150862306a36Sopenharmony_ci v4l2_subdev_call(cxdev->tda9887, video, s_std, cxdev->norm); 150962306a36Sopenharmony_ci v4l2_subdev_call(cxdev->tuner, video, s_std, cxdev->norm); 151062306a36Sopenharmony_ci v4l2_subdev_call(cxdev->cx25840, video, s_std, cxdev->norm); 151162306a36Sopenharmony_ci 151262306a36Sopenharmony_ci subfmt.format.width = cxdev->width; 151362306a36Sopenharmony_ci subfmt.format.height = cxdev->height; 151462306a36Sopenharmony_ci subfmt.format.code = MEDIA_BUS_FMT_FIXED; 151562306a36Sopenharmony_ci subfmt.format.field = V4L2_FIELD_SEQ_TB; 151662306a36Sopenharmony_ci subfmt.format.colorspace = V4L2_COLORSPACE_SMPTE170M; 151762306a36Sopenharmony_ci 151862306a36Sopenharmony_ci ret = v4l2_subdev_call(cxdev->cx25840, pad, set_fmt, NULL, &subfmt); 151962306a36Sopenharmony_ci if (ret != 0) 152062306a36Sopenharmony_ci dev_warn(&dvbdev->udev->dev, 152162306a36Sopenharmony_ci "cx25840 format set failed (%d)\n", ret); 152262306a36Sopenharmony_ci 152362306a36Sopenharmony_ci if (ret == 0) { 152462306a36Sopenharmony_ci cxdev->width = subfmt.format.width; 152562306a36Sopenharmony_ci cxdev->height = subfmt.format.height; 152662306a36Sopenharmony_ci } 152762306a36Sopenharmony_ci 152862306a36Sopenharmony_ci return 0; 152962306a36Sopenharmony_ci} 153062306a36Sopenharmony_ci 153162306a36Sopenharmony_cistatic int cxusb_videoradio_open(struct file *f) 153262306a36Sopenharmony_ci{ 153362306a36Sopenharmony_ci struct dvb_usb_device *dvbdev = video_drvdata(f); 153462306a36Sopenharmony_ci int ret; 153562306a36Sopenharmony_ci 153662306a36Sopenharmony_ci /* 153762306a36Sopenharmony_ci * no locking needed since this call only modifies analog 153862306a36Sopenharmony_ci * state if there are no other analog handles currenly 153962306a36Sopenharmony_ci * opened so ops done via them cannot create a conflict 154062306a36Sopenharmony_ci */ 154162306a36Sopenharmony_ci ret = cxusb_medion_get(dvbdev, CXUSB_OPEN_ANALOG); 154262306a36Sopenharmony_ci if (ret != 0) 154362306a36Sopenharmony_ci return ret; 154462306a36Sopenharmony_ci 154562306a36Sopenharmony_ci ret = v4l2_fh_open(f); 154662306a36Sopenharmony_ci if (ret != 0) 154762306a36Sopenharmony_ci goto ret_release; 154862306a36Sopenharmony_ci 154962306a36Sopenharmony_ci cxusb_vprintk(dvbdev, OPS, "got open\n"); 155062306a36Sopenharmony_ci 155162306a36Sopenharmony_ci return 0; 155262306a36Sopenharmony_ci 155362306a36Sopenharmony_ciret_release: 155462306a36Sopenharmony_ci cxusb_medion_put(dvbdev); 155562306a36Sopenharmony_ci 155662306a36Sopenharmony_ci return ret; 155762306a36Sopenharmony_ci} 155862306a36Sopenharmony_ci 155962306a36Sopenharmony_cistatic int cxusb_videoradio_release(struct file *f) 156062306a36Sopenharmony_ci{ 156162306a36Sopenharmony_ci struct video_device *vdev = video_devdata(f); 156262306a36Sopenharmony_ci struct dvb_usb_device *dvbdev = video_drvdata(f); 156362306a36Sopenharmony_ci int ret; 156462306a36Sopenharmony_ci 156562306a36Sopenharmony_ci cxusb_vprintk(dvbdev, OPS, "got release\n"); 156662306a36Sopenharmony_ci 156762306a36Sopenharmony_ci if (vdev->vfl_type == VFL_TYPE_VIDEO) 156862306a36Sopenharmony_ci ret = vb2_fop_release(f); 156962306a36Sopenharmony_ci else 157062306a36Sopenharmony_ci ret = v4l2_fh_release(f); 157162306a36Sopenharmony_ci 157262306a36Sopenharmony_ci cxusb_medion_put(dvbdev); 157362306a36Sopenharmony_ci 157462306a36Sopenharmony_ci return ret; 157562306a36Sopenharmony_ci} 157662306a36Sopenharmony_ci 157762306a36Sopenharmony_cistatic const struct v4l2_file_operations cxusb_video_fops = { 157862306a36Sopenharmony_ci .owner = THIS_MODULE, 157962306a36Sopenharmony_ci .read = vb2_fop_read, 158062306a36Sopenharmony_ci .poll = vb2_fop_poll, 158162306a36Sopenharmony_ci .unlocked_ioctl = video_ioctl2, 158262306a36Sopenharmony_ci .mmap = vb2_fop_mmap, 158362306a36Sopenharmony_ci .open = cxusb_videoradio_open, 158462306a36Sopenharmony_ci .release = cxusb_videoradio_release 158562306a36Sopenharmony_ci}; 158662306a36Sopenharmony_ci 158762306a36Sopenharmony_cistatic const struct v4l2_file_operations cxusb_radio_fops = { 158862306a36Sopenharmony_ci .owner = THIS_MODULE, 158962306a36Sopenharmony_ci .unlocked_ioctl = video_ioctl2, 159062306a36Sopenharmony_ci .open = cxusb_videoradio_open, 159162306a36Sopenharmony_ci .release = cxusb_videoradio_release 159262306a36Sopenharmony_ci}; 159362306a36Sopenharmony_ci 159462306a36Sopenharmony_cistatic void cxusb_medion_v4l2_release(struct v4l2_device *v4l2_dev) 159562306a36Sopenharmony_ci{ 159662306a36Sopenharmony_ci struct cxusb_medion_dev *cxdev = 159762306a36Sopenharmony_ci container_of(v4l2_dev, struct cxusb_medion_dev, v4l2dev); 159862306a36Sopenharmony_ci struct dvb_usb_device *dvbdev = cxdev->dvbdev; 159962306a36Sopenharmony_ci 160062306a36Sopenharmony_ci cxusb_vprintk(dvbdev, OPS, "v4l2 device release\n"); 160162306a36Sopenharmony_ci 160262306a36Sopenharmony_ci v4l2_device_unregister(&cxdev->v4l2dev); 160362306a36Sopenharmony_ci 160462306a36Sopenharmony_ci mutex_destroy(&cxdev->dev_lock); 160562306a36Sopenharmony_ci 160662306a36Sopenharmony_ci while (completion_done(&cxdev->v4l2_release)) 160762306a36Sopenharmony_ci schedule(); 160862306a36Sopenharmony_ci 160962306a36Sopenharmony_ci complete(&cxdev->v4l2_release); 161062306a36Sopenharmony_ci} 161162306a36Sopenharmony_ci 161262306a36Sopenharmony_cistatic void cxusb_medion_videodev_release(struct video_device *vdev) 161362306a36Sopenharmony_ci{ 161462306a36Sopenharmony_ci struct dvb_usb_device *dvbdev = video_get_drvdata(vdev); 161562306a36Sopenharmony_ci 161662306a36Sopenharmony_ci cxusb_vprintk(dvbdev, OPS, "video device release\n"); 161762306a36Sopenharmony_ci 161862306a36Sopenharmony_ci video_device_release(vdev); 161962306a36Sopenharmony_ci} 162062306a36Sopenharmony_ci 162162306a36Sopenharmony_cistatic int cxusb_medion_register_analog_video(struct dvb_usb_device *dvbdev) 162262306a36Sopenharmony_ci{ 162362306a36Sopenharmony_ci struct cxusb_medion_dev *cxdev = dvbdev->priv; 162462306a36Sopenharmony_ci int ret; 162562306a36Sopenharmony_ci 162662306a36Sopenharmony_ci cxdev->videoqueue.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 162762306a36Sopenharmony_ci cxdev->videoqueue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ | 162862306a36Sopenharmony_ci VB2_DMABUF; 162962306a36Sopenharmony_ci cxdev->videoqueue.ops = &cxdev_video_qops; 163062306a36Sopenharmony_ci cxdev->videoqueue.mem_ops = &vb2_vmalloc_memops; 163162306a36Sopenharmony_ci cxdev->videoqueue.drv_priv = dvbdev; 163262306a36Sopenharmony_ci cxdev->videoqueue.buf_struct_size = 163362306a36Sopenharmony_ci sizeof(struct cxusb_medion_vbuffer); 163462306a36Sopenharmony_ci cxdev->videoqueue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; 163562306a36Sopenharmony_ci cxdev->videoqueue.min_buffers_needed = 6; 163662306a36Sopenharmony_ci cxdev->videoqueue.lock = &cxdev->dev_lock; 163762306a36Sopenharmony_ci 163862306a36Sopenharmony_ci ret = vb2_queue_init(&cxdev->videoqueue); 163962306a36Sopenharmony_ci if (ret) { 164062306a36Sopenharmony_ci dev_err(&dvbdev->udev->dev, 164162306a36Sopenharmony_ci "video queue init failed, ret = %d\n", ret); 164262306a36Sopenharmony_ci return ret; 164362306a36Sopenharmony_ci } 164462306a36Sopenharmony_ci 164562306a36Sopenharmony_ci cxdev->videodev = video_device_alloc(); 164662306a36Sopenharmony_ci if (!cxdev->videodev) { 164762306a36Sopenharmony_ci dev_err(&dvbdev->udev->dev, "video device alloc failed\n"); 164862306a36Sopenharmony_ci return -ENOMEM; 164962306a36Sopenharmony_ci } 165062306a36Sopenharmony_ci 165162306a36Sopenharmony_ci cxdev->videodev->device_caps = videocaps; 165262306a36Sopenharmony_ci cxdev->videodev->fops = &cxusb_video_fops; 165362306a36Sopenharmony_ci cxdev->videodev->v4l2_dev = &cxdev->v4l2dev; 165462306a36Sopenharmony_ci cxdev->videodev->queue = &cxdev->videoqueue; 165562306a36Sopenharmony_ci strscpy(cxdev->videodev->name, "cxusb", sizeof(cxdev->videodev->name)); 165662306a36Sopenharmony_ci cxdev->videodev->vfl_dir = VFL_DIR_RX; 165762306a36Sopenharmony_ci cxdev->videodev->ioctl_ops = &cxusb_video_ioctl; 165862306a36Sopenharmony_ci cxdev->videodev->tvnorms = V4L2_STD_ALL; 165962306a36Sopenharmony_ci cxdev->videodev->release = cxusb_medion_videodev_release; 166062306a36Sopenharmony_ci cxdev->videodev->lock = &cxdev->dev_lock; 166162306a36Sopenharmony_ci video_set_drvdata(cxdev->videodev, dvbdev); 166262306a36Sopenharmony_ci 166362306a36Sopenharmony_ci ret = video_register_device(cxdev->videodev, VFL_TYPE_VIDEO, -1); 166462306a36Sopenharmony_ci if (ret) { 166562306a36Sopenharmony_ci dev_err(&dvbdev->udev->dev, 166662306a36Sopenharmony_ci "video device register failed, ret = %d\n", ret); 166762306a36Sopenharmony_ci goto ret_vrelease; 166862306a36Sopenharmony_ci } 166962306a36Sopenharmony_ci 167062306a36Sopenharmony_ci return 0; 167162306a36Sopenharmony_ci 167262306a36Sopenharmony_ciret_vrelease: 167362306a36Sopenharmony_ci video_device_release(cxdev->videodev); 167462306a36Sopenharmony_ci return ret; 167562306a36Sopenharmony_ci} 167662306a36Sopenharmony_ci 167762306a36Sopenharmony_cistatic int cxusb_medion_register_analog_radio(struct dvb_usb_device *dvbdev) 167862306a36Sopenharmony_ci{ 167962306a36Sopenharmony_ci struct cxusb_medion_dev *cxdev = dvbdev->priv; 168062306a36Sopenharmony_ci int ret; 168162306a36Sopenharmony_ci 168262306a36Sopenharmony_ci cxdev->radiodev = video_device_alloc(); 168362306a36Sopenharmony_ci if (!cxdev->radiodev) { 168462306a36Sopenharmony_ci dev_err(&dvbdev->udev->dev, "radio device alloc failed\n"); 168562306a36Sopenharmony_ci return -ENOMEM; 168662306a36Sopenharmony_ci } 168762306a36Sopenharmony_ci 168862306a36Sopenharmony_ci cxdev->radiodev->device_caps = radiocaps; 168962306a36Sopenharmony_ci cxdev->radiodev->fops = &cxusb_radio_fops; 169062306a36Sopenharmony_ci cxdev->radiodev->v4l2_dev = &cxdev->v4l2dev; 169162306a36Sopenharmony_ci strscpy(cxdev->radiodev->name, "cxusb", sizeof(cxdev->radiodev->name)); 169262306a36Sopenharmony_ci cxdev->radiodev->vfl_dir = VFL_DIR_RX; 169362306a36Sopenharmony_ci cxdev->radiodev->ioctl_ops = &cxusb_radio_ioctl; 169462306a36Sopenharmony_ci cxdev->radiodev->release = video_device_release; 169562306a36Sopenharmony_ci cxdev->radiodev->lock = &cxdev->dev_lock; 169662306a36Sopenharmony_ci video_set_drvdata(cxdev->radiodev, dvbdev); 169762306a36Sopenharmony_ci 169862306a36Sopenharmony_ci ret = video_register_device(cxdev->radiodev, VFL_TYPE_RADIO, -1); 169962306a36Sopenharmony_ci if (ret) { 170062306a36Sopenharmony_ci dev_err(&dvbdev->udev->dev, 170162306a36Sopenharmony_ci "radio device register failed, ret = %d\n", ret); 170262306a36Sopenharmony_ci video_device_release(cxdev->radiodev); 170362306a36Sopenharmony_ci return ret; 170462306a36Sopenharmony_ci } 170562306a36Sopenharmony_ci 170662306a36Sopenharmony_ci return 0; 170762306a36Sopenharmony_ci} 170862306a36Sopenharmony_ci 170962306a36Sopenharmony_cistatic int cxusb_medion_register_analog_subdevs(struct dvb_usb_device *dvbdev) 171062306a36Sopenharmony_ci{ 171162306a36Sopenharmony_ci struct cxusb_medion_dev *cxdev = dvbdev->priv; 171262306a36Sopenharmony_ci int ret; 171362306a36Sopenharmony_ci struct tuner_setup tun_setup; 171462306a36Sopenharmony_ci 171562306a36Sopenharmony_ci /* attach cx25840 capture chip */ 171662306a36Sopenharmony_ci cxdev->cx25840 = v4l2_i2c_new_subdev(&cxdev->v4l2dev, 171762306a36Sopenharmony_ci &dvbdev->i2c_adap, 171862306a36Sopenharmony_ci "cx25840", 0x44, NULL); 171962306a36Sopenharmony_ci if (!cxdev->cx25840) { 172062306a36Sopenharmony_ci dev_err(&dvbdev->udev->dev, "cx25840 not found\n"); 172162306a36Sopenharmony_ci return -ENODEV; 172262306a36Sopenharmony_ci } 172362306a36Sopenharmony_ci 172462306a36Sopenharmony_ci /* 172562306a36Sopenharmony_ci * Initialize cx25840 chip by calling its subdevice init core op. 172662306a36Sopenharmony_ci * 172762306a36Sopenharmony_ci * This switches it into the generic mode that disables some of 172862306a36Sopenharmony_ci * ivtv-related hacks in the cx25840 driver while allowing setting 172962306a36Sopenharmony_ci * of the chip video output configuration (passed in the call below 173062306a36Sopenharmony_ci * as the last argument). 173162306a36Sopenharmony_ci */ 173262306a36Sopenharmony_ci ret = v4l2_subdev_call(cxdev->cx25840, core, init, 173362306a36Sopenharmony_ci CX25840_VCONFIG_FMT_BT656 | 173462306a36Sopenharmony_ci CX25840_VCONFIG_RES_8BIT | 173562306a36Sopenharmony_ci CX25840_VCONFIG_VBIRAW_DISABLED | 173662306a36Sopenharmony_ci CX25840_VCONFIG_ANCDATA_DISABLED | 173762306a36Sopenharmony_ci CX25840_VCONFIG_ACTIVE_COMPOSITE | 173862306a36Sopenharmony_ci CX25840_VCONFIG_VALID_ANDACTIVE | 173962306a36Sopenharmony_ci CX25840_VCONFIG_HRESETW_NORMAL | 174062306a36Sopenharmony_ci CX25840_VCONFIG_CLKGATE_NONE | 174162306a36Sopenharmony_ci CX25840_VCONFIG_DCMODE_DWORDS); 174262306a36Sopenharmony_ci if (ret != 0) { 174362306a36Sopenharmony_ci dev_err(&dvbdev->udev->dev, 174462306a36Sopenharmony_ci "cx25840 init failed (%d)\n", ret); 174562306a36Sopenharmony_ci return ret; 174662306a36Sopenharmony_ci } 174762306a36Sopenharmony_ci 174862306a36Sopenharmony_ci /* attach analog tuner */ 174962306a36Sopenharmony_ci cxdev->tuner = v4l2_i2c_new_subdev(&cxdev->v4l2dev, 175062306a36Sopenharmony_ci &dvbdev->i2c_adap, 175162306a36Sopenharmony_ci "tuner", 0x61, NULL); 175262306a36Sopenharmony_ci if (!cxdev->tuner) { 175362306a36Sopenharmony_ci dev_err(&dvbdev->udev->dev, "tuner not found\n"); 175462306a36Sopenharmony_ci return -ENODEV; 175562306a36Sopenharmony_ci } 175662306a36Sopenharmony_ci 175762306a36Sopenharmony_ci /* configure it */ 175862306a36Sopenharmony_ci memset(&tun_setup, 0, sizeof(tun_setup)); 175962306a36Sopenharmony_ci tun_setup.addr = 0x61; 176062306a36Sopenharmony_ci tun_setup.type = TUNER_PHILIPS_FMD1216ME_MK3; 176162306a36Sopenharmony_ci tun_setup.mode_mask = T_RADIO | T_ANALOG_TV; 176262306a36Sopenharmony_ci v4l2_subdev_call(cxdev->tuner, tuner, s_type_addr, &tun_setup); 176362306a36Sopenharmony_ci 176462306a36Sopenharmony_ci /* attach IF demod */ 176562306a36Sopenharmony_ci cxdev->tda9887 = v4l2_i2c_new_subdev(&cxdev->v4l2dev, 176662306a36Sopenharmony_ci &dvbdev->i2c_adap, 176762306a36Sopenharmony_ci "tuner", 0x43, NULL); 176862306a36Sopenharmony_ci if (!cxdev->tda9887) { 176962306a36Sopenharmony_ci dev_err(&dvbdev->udev->dev, "tda9887 not found\n"); 177062306a36Sopenharmony_ci return -ENODEV; 177162306a36Sopenharmony_ci } 177262306a36Sopenharmony_ci 177362306a36Sopenharmony_ci return 0; 177462306a36Sopenharmony_ci} 177562306a36Sopenharmony_ci 177662306a36Sopenharmony_ciint cxusb_medion_register_analog(struct dvb_usb_device *dvbdev) 177762306a36Sopenharmony_ci{ 177862306a36Sopenharmony_ci struct cxusb_medion_dev *cxdev = dvbdev->priv; 177962306a36Sopenharmony_ci int ret; 178062306a36Sopenharmony_ci 178162306a36Sopenharmony_ci mutex_init(&cxdev->dev_lock); 178262306a36Sopenharmony_ci 178362306a36Sopenharmony_ci init_completion(&cxdev->v4l2_release); 178462306a36Sopenharmony_ci 178562306a36Sopenharmony_ci cxdev->v4l2dev.release = cxusb_medion_v4l2_release; 178662306a36Sopenharmony_ci 178762306a36Sopenharmony_ci ret = v4l2_device_register(&dvbdev->udev->dev, &cxdev->v4l2dev); 178862306a36Sopenharmony_ci if (ret != 0) { 178962306a36Sopenharmony_ci dev_err(&dvbdev->udev->dev, 179062306a36Sopenharmony_ci "V4L2 device registration failed, ret = %d\n", ret); 179162306a36Sopenharmony_ci mutex_destroy(&cxdev->dev_lock); 179262306a36Sopenharmony_ci return ret; 179362306a36Sopenharmony_ci } 179462306a36Sopenharmony_ci 179562306a36Sopenharmony_ci ret = cxusb_medion_register_analog_subdevs(dvbdev); 179662306a36Sopenharmony_ci if (ret) 179762306a36Sopenharmony_ci goto ret_unregister; 179862306a36Sopenharmony_ci 179962306a36Sopenharmony_ci INIT_WORK(&cxdev->urbwork, cxusb_medion_v_complete_work); 180062306a36Sopenharmony_ci INIT_LIST_HEAD(&cxdev->buflist); 180162306a36Sopenharmony_ci 180262306a36Sopenharmony_ci cxdev->width = 320; 180362306a36Sopenharmony_ci cxdev->height = 240; 180462306a36Sopenharmony_ci 180562306a36Sopenharmony_ci ret = cxusb_medion_register_analog_video(dvbdev); 180662306a36Sopenharmony_ci if (ret) 180762306a36Sopenharmony_ci goto ret_unregister; 180862306a36Sopenharmony_ci 180962306a36Sopenharmony_ci ret = cxusb_medion_register_analog_radio(dvbdev); 181062306a36Sopenharmony_ci if (ret) 181162306a36Sopenharmony_ci goto ret_vunreg; 181262306a36Sopenharmony_ci 181362306a36Sopenharmony_ci return 0; 181462306a36Sopenharmony_ci 181562306a36Sopenharmony_ciret_vunreg: 181662306a36Sopenharmony_ci vb2_video_unregister_device(cxdev->videodev); 181762306a36Sopenharmony_ci 181862306a36Sopenharmony_ciret_unregister: 181962306a36Sopenharmony_ci v4l2_device_put(&cxdev->v4l2dev); 182062306a36Sopenharmony_ci wait_for_completion(&cxdev->v4l2_release); 182162306a36Sopenharmony_ci 182262306a36Sopenharmony_ci return ret; 182362306a36Sopenharmony_ci} 182462306a36Sopenharmony_ci 182562306a36Sopenharmony_civoid cxusb_medion_unregister_analog(struct dvb_usb_device *dvbdev) 182662306a36Sopenharmony_ci{ 182762306a36Sopenharmony_ci struct cxusb_medion_dev *cxdev = dvbdev->priv; 182862306a36Sopenharmony_ci 182962306a36Sopenharmony_ci cxusb_vprintk(dvbdev, OPS, "unregistering analog\n"); 183062306a36Sopenharmony_ci 183162306a36Sopenharmony_ci video_unregister_device(cxdev->radiodev); 183262306a36Sopenharmony_ci vb2_video_unregister_device(cxdev->videodev); 183362306a36Sopenharmony_ci 183462306a36Sopenharmony_ci v4l2_device_put(&cxdev->v4l2dev); 183562306a36Sopenharmony_ci wait_for_completion(&cxdev->v4l2_release); 183662306a36Sopenharmony_ci 183762306a36Sopenharmony_ci cxusb_vprintk(dvbdev, OPS, "analog unregistered\n"); 183862306a36Sopenharmony_ci} 1839