diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2019-07-09 09:47:22 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2019-07-09 09:47:22 -0700 |
commit | ed63b9c873601ca113da5c7b1745e3946493e9f3 (patch) | |
tree | 94e96db2b79a8d123c0645dd64b3830bc4d20bfe /drivers/media/usb | |
parent | 947fbd4ca9fb38f320b076e68cfccab977c5ea01 (diff) | |
parent | f81cbfc4f82a75ca0a2dc181a9c93b88f0e6509d (diff) |
Merge tag 'media/v5.3-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media
Pull media updates from Mauro Carvalho Chehab:
- new Atmel microship ISC driver
- coda has gained support for mpeg2 and mpeg4
- cxusb gained support for analog TV
- rockchip staging driver was split into two separate staging drivers
- added a new staging driver for Allegro DVT video IP core
- added a new staging driver for Amlogic Meson video decoder
- lots of improvements and cleanups
* tag 'media/v5.3-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (398 commits)
media: allegro: use new v4l2_m2m_ioctl_try_encoder_cmd funcs
media: doc-rst: Fix typos
media: radio-raremono: change devm_k*alloc to k*alloc
media: stv0297: fix frequency range limit
media: rc: Prefer KEY_NUMERIC_* for number buttons on remotes
media: dvb_frontend: split dvb_frontend_handle_ioctl function
media: mceusb: disable "nonsensical irdata" messages
media: rc: remove redundant dev_err message
media: cec-notifier: add new notifier functions
media: cec: add struct cec_connector_info support
media: cec-notifier: rename variables, check kstrdup and n->conn_name
media: MAINTAINERS: Add maintainers for Media Controller
media: staging: media: tegra-vde: Defer dmabuf's unmapping
media: staging: media: tegra-vde: Add IOMMU support
media: hdpvr: fix locking and a missing msleep
media: v4l2: Test type instead of cfg->type in v4l2_ctrl_new_custom()
media: atmel: atmel-isc: fix i386 build error
media: v4l2-ctrl: Move compound control initialization
media: hantro: Use vb2_get_buffer
media: pci: cx88: Change the type of 'missed' to u64
...
Diffstat (limited to 'drivers/media/usb')
51 files changed, 3142 insertions, 461 deletions
diff --git a/drivers/media/usb/airspy/airspy.c b/drivers/media/usb/airspy/airspy.c index 3329de6671ce..b35231ffe503 100644 --- a/drivers/media/usb/airspy/airspy.c +++ b/drivers/media/usb/airspy/airspy.c @@ -613,10 +613,6 @@ static int airspy_querycap(struct file *file, void *fh, strscpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver)); strscpy(cap->card, s->vdev.name, sizeof(cap->card)); usb_make_path(s->udev, cap->bus_info, sizeof(cap->bus_info)); - cap->device_caps = V4L2_CAP_SDR_CAPTURE | V4L2_CAP_STREAMING | - V4L2_CAP_READWRITE | V4L2_CAP_TUNER; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; - return 0; } @@ -1057,6 +1053,8 @@ static int airspy_probe(struct usb_interface *intf, s->v4l2_dev.ctrl_handler = &s->hdl; s->vdev.v4l2_dev = &s->v4l2_dev; s->vdev.lock = &s->v4l2_lock; + s->vdev.device_caps = V4L2_CAP_SDR_CAPTURE | V4L2_CAP_STREAMING | + V4L2_CAP_READWRITE | V4L2_CAP_TUNER; ret = video_register_device(&s->vdev, VFL_TYPE_SDR, -1); if (ret) { diff --git a/drivers/media/usb/au0828/au0828-core.c b/drivers/media/usb/au0828/au0828-core.c index f746f6e2f686..a8a72d5fbd12 100644 --- a/drivers/media/usb/au0828/au0828-core.c +++ b/drivers/media/usb/au0828/au0828-core.c @@ -719,6 +719,12 @@ static int au0828_usb_probe(struct usb_interface *interface, /* Setup */ au0828_card_setup(dev); + /* + * Store the pointer to the au0828_dev so it can be accessed in + * au0828_usb_disconnect + */ + usb_set_intfdata(interface, dev); + /* Analog TV */ retval = au0828_analog_register(dev, interface); if (retval) { @@ -737,12 +743,6 @@ static int au0828_usb_probe(struct usb_interface *interface, /* Remote controller */ au0828_rc_register(dev); - /* - * Store the pointer to the au0828_dev so it can be accessed in - * au0828_usb_disconnect - */ - usb_set_intfdata(interface, dev); - pr_info("Registered device AU0828 [%s]\n", dev->board.name == NULL ? "Unset" : dev->board.name); diff --git a/drivers/media/usb/au0828/au0828-video.c b/drivers/media/usb/au0828/au0828-video.c index a414a25e48a8..5e00019bce8a 100644 --- a/drivers/media/usb/au0828/au0828-video.c +++ b/drivers/media/usb/au0828/au0828-video.c @@ -1182,7 +1182,6 @@ static int au0828_set_format(struct au0828_dev *dev, unsigned int cmd, static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { - struct video_device *vdev = video_devdata(file); struct au0828_dev *dev = video_drvdata(file); dprintk(1, "%s called std_set %d dev_state %ld\n", __func__, @@ -1193,16 +1192,10 @@ static int vidioc_querycap(struct file *file, void *priv, usb_make_path(dev->usbdev, cap->bus_info, sizeof(cap->bus_info)); /* set the device capabilities */ - cap->device_caps = V4L2_CAP_AUDIO | - V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING | - V4L2_CAP_TUNER; - if (vdev->vfl_type == VFL_TYPE_GRABBER) - cap->device_caps |= V4L2_CAP_VIDEO_CAPTURE; - else - cap->device_caps |= V4L2_CAP_VBI_CAPTURE; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS | - V4L2_CAP_VBI_CAPTURE | V4L2_CAP_VIDEO_CAPTURE; + cap->capabilities = + V4L2_CAP_AUDIO | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING | + V4L2_CAP_TUNER | V4L2_CAP_VBI_CAPTURE | V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_DEVICE_CAPS; return 0; } @@ -1991,6 +1984,9 @@ int au0828_analog_register(struct au0828_dev *dev, dev->vdev.lock = &dev->lock; dev->vdev.queue = &dev->vb_vidq; dev->vdev.queue->lock = &dev->vb_queue_lock; + dev->vdev.device_caps = + V4L2_CAP_AUDIO | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING | + V4L2_CAP_TUNER | V4L2_CAP_VIDEO_CAPTURE; strscpy(dev->vdev.name, "au0828a video", sizeof(dev->vdev.name)); /* Setup the VBI device */ @@ -1999,6 +1995,9 @@ int au0828_analog_register(struct au0828_dev *dev, dev->vbi_dev.lock = &dev->lock; dev->vbi_dev.queue = &dev->vb_vbiq; dev->vbi_dev.queue->lock = &dev->vb_vbi_queue_lock; + dev->vbi_dev.device_caps = + V4L2_CAP_AUDIO | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING | + V4L2_CAP_TUNER | V4L2_CAP_VBI_CAPTURE; strscpy(dev->vbi_dev.name, "au0828a vbi", sizeof(dev->vbi_dev.name)); /* Init entities at the Media Controller */ diff --git a/drivers/media/usb/cpia2/cpia2_usb.c b/drivers/media/usb/cpia2/cpia2_usb.c index b2268981c963..17468f7d78ed 100644 --- a/drivers/media/usb/cpia2/cpia2_usb.c +++ b/drivers/media/usb/cpia2/cpia2_usb.c @@ -893,7 +893,6 @@ static void cpia2_usb_disconnect(struct usb_interface *intf) cpia2_unregister_camera(cam); v4l2_device_disconnect(&cam->v4l2_dev); mutex_unlock(&cam->v4l2_lock); - v4l2_device_put(&cam->v4l2_dev); if(cam->buffers) { DBG("Wakeup waiting processes\n"); @@ -902,6 +901,8 @@ static void cpia2_usb_disconnect(struct usb_interface *intf) wake_up_interruptible(&cam->wq_stream); } + v4l2_device_put(&cam->v4l2_dev); + LOG("CPiA2 camera disconnected.\n"); } diff --git a/drivers/media/usb/cpia2/cpia2_v4l.c b/drivers/media/usb/cpia2/cpia2_v4l.c index da6a5b2f86d1..0feae825cebb 100644 --- a/drivers/media/usb/cpia2/cpia2_v4l.c +++ b/drivers/media/usb/cpia2/cpia2_v4l.c @@ -250,13 +250,6 @@ static int cpia2_querycap(struct file *file, void *fh, struct v4l2_capability *v if (usb_make_path(cam->dev, vc->bus_info, sizeof(vc->bus_info)) <0) memset(vc->bus_info,0, sizeof(vc->bus_info)); - - vc->device_caps = V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING; - vc->capabilities = vc->device_caps | - V4L2_CAP_DEVICE_CAPS; - return 0; } @@ -1152,6 +1145,8 @@ int cpia2_register_camera(struct camera_data *cam) cam->vdev.lock = &cam->v4l2_lock; cam->vdev.ctrl_handler = hdl; cam->vdev.v4l2_dev = &cam->v4l2_dev; + cam->vdev.device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING; reset_camera_struct_v4l(cam); diff --git a/drivers/media/usb/cx231xx/cx231xx-cards.c b/drivers/media/usb/cx231xx/cx231xx-cards.c index 26b05df698f0..e0d98ba8fdbf 100644 --- a/drivers/media/usb/cx231xx/cx231xx-cards.c +++ b/drivers/media/usb/cx231xx/cx231xx-cards.c @@ -1023,6 +1023,8 @@ struct usb_device_id cx231xx_id_table[] = { .driver_info = CX231XX_BOARD_HAUPPAUGE_EXETER}, {USB_DEVICE(0x2040, 0xb123), .driver_info = CX231XX_BOARD_HAUPPAUGE_955Q}, + {USB_DEVICE(0x2040, 0xb124), + .driver_info = CX231XX_BOARD_HAUPPAUGE_955Q}, {USB_DEVICE(0x2040, 0xb151), .driver_info = CX231XX_BOARD_HAUPPAUGE_935C}, {USB_DEVICE(0x2040, 0xb150), diff --git a/drivers/media/usb/cx231xx/cx231xx-dvb.c b/drivers/media/usb/cx231xx/cx231xx-dvb.c index 8fbb9523c88d..e205f7f0a56a 100644 --- a/drivers/media/usb/cx231xx/cx231xx-dvb.c +++ b/drivers/media/usb/cx231xx/cx231xx-dvb.c @@ -1147,6 +1147,7 @@ static int dvb_fini(struct cx231xx *dev) if (dev->dvb) { unregister_dvb(dev->dvb); + kfree(dev->dvb); dev->dvb = NULL; } diff --git a/drivers/media/usb/cx231xx/cx231xx-video.c b/drivers/media/usb/cx231xx/cx231xx-video.c index f8820478d46b..b651ac7713ea 100644 --- a/drivers/media/usb/cx231xx/cx231xx-video.c +++ b/drivers/media/usb/cx231xx/cx231xx-video.c @@ -1555,30 +1555,19 @@ static int vidioc_streamoff(struct file *file, void *priv, int cx231xx_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { - struct video_device *vdev = video_devdata(file); struct cx231xx_fh *fh = priv; struct cx231xx *dev = fh->dev; strscpy(cap->driver, "cx231xx", sizeof(cap->driver)); strscpy(cap->card, cx231xx_boards[dev->model].name, sizeof(cap->card)); usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); - - if (vdev->vfl_type == VFL_TYPE_RADIO) - cap->device_caps = V4L2_CAP_RADIO; - else { - cap->device_caps = V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; - if (vdev->vfl_type == VFL_TYPE_VBI) - cap->device_caps |= V4L2_CAP_VBI_CAPTURE; - else - cap->device_caps |= V4L2_CAP_VIDEO_CAPTURE; - } - if (dev->tuner_type != TUNER_ABSENT) - cap->device_caps |= V4L2_CAP_TUNER; - cap->capabilities = cap->device_caps | V4L2_CAP_READWRITE | + cap->capabilities = V4L2_CAP_READWRITE | V4L2_CAP_VBI_CAPTURE | V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | V4L2_CAP_DEVICE_CAPS; if (video_is_registered(&dev->radio_dev)) cap->capabilities |= V4L2_CAP_RADIO; + if (dev->tuner_type != TUNER_ABSENT) + cap->capabilities |= V4L2_CAP_TUNER; return 0; } @@ -2234,6 +2223,11 @@ int cx231xx_register_analog_devices(struct cx231xx *dev) dev_err(dev->dev, "failed to initialize video media entity!\n"); #endif dev->vdev.ctrl_handler = &dev->ctrl_handler; + dev->vdev.device_caps = V4L2_CAP_READWRITE | V4L2_CAP_STREAMING | + V4L2_CAP_VIDEO_CAPTURE; + if (dev->tuner_type != TUNER_ABSENT) + dev->vdev.device_caps |= V4L2_CAP_TUNER; + /* register v4l2 video video_device */ ret = video_register_device(&dev->vdev, VFL_TYPE_GRABBER, video_nr[dev->devno]); @@ -2262,6 +2256,11 @@ int cx231xx_register_analog_devices(struct cx231xx *dev) dev_err(dev->dev, "failed to initialize vbi media entity!\n"); #endif dev->vbi_dev.ctrl_handler = &dev->ctrl_handler; + dev->vbi_dev.device_caps = V4L2_CAP_READWRITE | V4L2_CAP_STREAMING | + V4L2_CAP_VBI_CAPTURE; + if (dev->tuner_type != TUNER_ABSENT) + dev->vbi_dev.device_caps |= V4L2_CAP_TUNER; + /* register v4l2 vbi video_device */ ret = video_register_device(&dev->vbi_dev, VFL_TYPE_VBI, vbi_nr[dev->devno]); @@ -2277,6 +2276,7 @@ int cx231xx_register_analog_devices(struct cx231xx *dev) cx231xx_vdev_init(dev, &dev->radio_dev, &cx231xx_radio_template, "radio"); dev->radio_dev.ctrl_handler = &dev->radio_ctrl_handler; + dev->radio_dev.device_caps = V4L2_CAP_RADIO | V4L2_CAP_TUNER; ret = video_register_device(&dev->radio_dev, VFL_TYPE_RADIO, radio_nr[dev->devno]); if (ret < 0) { diff --git a/drivers/media/usb/dvb-usb-v2/af9035.c b/drivers/media/usb/dvb-usb-v2/af9035.c index de52309eaaab..3afd18733614 100644 --- a/drivers/media/usb/dvb-usb-v2/af9035.c +++ b/drivers/media/usb/dvb-usb-v2/af9035.c @@ -107,8 +107,6 @@ static int af9035_ctrl_msg(struct dvb_usb_device *d, struct usb_req *req) memcpy(req->rbuf, &state->buf[ACK_HDR_LEN], req->rlen); exit: mutex_unlock(&d->usb_mutex); - if (ret < 0) - dev_dbg(&intf->dev, "failed=%d\n", ret); return ret; } diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb_urb.c b/drivers/media/usb/dvb-usb-v2/dvb_usb_urb.c index 91729a39a306..7e817ea506c6 100644 --- a/drivers/media/usb/dvb-usb-v2/dvb_usb_urb.c +++ b/drivers/media/usb/dvb-usb-v2/dvb_usb_urb.c @@ -24,14 +24,19 @@ static int dvb_usb_v2_generic_io(struct dvb_usb_device *d, ret = usb_bulk_msg(d->udev, usb_sndbulkpipe(d->udev, d->props->generic_bulk_ctrl_endpoint), wbuf, wlen, &actual_length, 2000); - if (ret < 0) + if (ret) { dev_err(&d->udev->dev, "%s: usb_bulk_msg() failed=%d\n", KBUILD_MODNAME, ret); - else - ret = actual_length != wlen ? -EIO : 0; + return ret; + } + if (actual_length != wlen) { + dev_err(&d->udev->dev, "%s: usb_bulk_msg() write length=%d, actual=%d\n", + KBUILD_MODNAME, wlen, actual_length); + return -EIO; + } - /* an answer is expected, and no error before */ - if (!ret && rbuf && rlen) { + /* an answer is expected */ + if (rbuf && rlen) { if (d->props->generic_bulk_ctrl_delay) usleep_range(d->props->generic_bulk_ctrl_delay, d->props->generic_bulk_ctrl_delay diff --git a/drivers/media/usb/dvb-usb-v2/dvbsky.c b/drivers/media/usb/dvb-usb-v2/dvbsky.c index c41e10bd6ef7..8610487f2d72 100644 --- a/drivers/media/usb/dvb-usb-v2/dvbsky.c +++ b/drivers/media/usb/dvb-usb-v2/dvbsky.c @@ -91,8 +91,6 @@ static int dvbsky_gpio_ctrl(struct dvb_usb_device *d, u8 gport, u8 value) obuf[1] = gport; obuf[2] = value; ret = dvbsky_usb_generic_rw(d, obuf, 3, ibuf, 1); - if (ret) - dev_err(&d->udev->dev, "failed=%d\n", ret); return ret; } @@ -130,8 +128,6 @@ static int dvbsky_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], obuf[3] = msg[0].addr; ret = dvbsky_usb_generic_rw(d, obuf, 4, ibuf, msg[0].len + 1); - if (ret) - dev_err(&d->udev->dev, "failed=%d\n", ret); if (!ret) memcpy(msg[0].buf, &ibuf[1], msg[0].len); } else { @@ -142,8 +138,6 @@ static int dvbsky_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], memcpy(&obuf[3], msg[0].buf, msg[0].len); ret = dvbsky_usb_generic_rw(d, obuf, msg[0].len + 3, ibuf, 1); - if (ret) - dev_err(&d->udev->dev, "failed=%d\n", ret); } } else { if ((msg[0].len > 60) || (msg[1].len > 60)) { @@ -161,9 +155,6 @@ static int dvbsky_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], memcpy(&obuf[4], msg[0].buf, msg[0].len); ret = dvbsky_usb_generic_rw(d, obuf, msg[0].len + 4, ibuf, msg[1].len + 1); - if (ret) - dev_err(&d->udev->dev, "failed=%d\n", ret); - if (!ret) memcpy(msg[1].buf, &ibuf[1], msg[1].len); } @@ -192,8 +183,6 @@ static int dvbsky_rc_query(struct dvb_usb_device *d) obuf[0] = 0x10; ret = dvbsky_usb_generic_rw(d, obuf, 1, ibuf, 2); - if (ret) - dev_err(&d->udev->dev, "failed=%d\n", ret); if (ret == 0) code = (ibuf[0] << 8) | ibuf[1]; if (code != 0xffff) { diff --git a/drivers/media/usb/dvb-usb/Kconfig b/drivers/media/usb/dvb-usb/Kconfig index 87dbae875177..1a3e5f965ae4 100644 --- a/drivers/media/usb/dvb-usb/Kconfig +++ b/drivers/media/usb/dvb-usb/Kconfig @@ -139,12 +139,24 @@ config DVB_USB_CXUSB select MEDIA_TUNER_SI2157 if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the Conexant USB2.0 hybrid reference design. - Currently, only DVB and ATSC modes are supported, analog mode - shall be added in the future. Devices that require this module: + DVB and ATSC modes are supported, for a basic analog mode support + see the next option ("Analog support for the Conexant USB2.0 hybrid + reference design"). + Devices that require this module: Medion MD95700 hybrid USB2.0 device. DViCO FusionHDTV (Bluebird) USB2.0 devices +config DVB_USB_CXUSB_ANALOG + bool "Analog support for the Conexant USB2.0 hybrid reference design" + depends on DVB_USB_CXUSB && VIDEO_V4L2 + select VIDEO_CX25840 + select VIDEOBUF2_VMALLOC + help + Say Y here to enable basic analog mode support for the Conexant + USB2.0 hybrid reference design. + Currently this mode is supported only on a Medion MD95700 device. + config DVB_USB_M920X tristate "Uli m920x DVB-T USB2.0 support" depends on DVB_USB diff --git a/drivers/media/usb/dvb-usb/Makefile b/drivers/media/usb/dvb-usb/Makefile index 407d90ca8be0..28e4806a87cd 100644 --- a/drivers/media/usb/dvb-usb/Makefile +++ b/drivers/media/usb/dvb-usb/Makefile @@ -42,6 +42,9 @@ dvb-usb-digitv-objs := digitv.o obj-$(CONFIG_DVB_USB_DIGITV) += dvb-usb-digitv.o dvb-usb-cxusb-objs := cxusb.o +ifeq ($(CONFIG_DVB_USB_CXUSB_ANALOG),y) +dvb-usb-cxusb-objs += cxusb-analog.o +endif obj-$(CONFIG_DVB_USB_CXUSB) += dvb-usb-cxusb.o dvb-usb-ttusb2-objs := ttusb2.o diff --git a/drivers/media/usb/dvb-usb/cxusb-analog.c b/drivers/media/usb/dvb-usb/cxusb-analog.c new file mode 100644 index 000000000000..0699f718d052 --- /dev/null +++ b/drivers/media/usb/dvb-usb/cxusb-analog.c @@ -0,0 +1,1845 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// DVB USB compliant linux driver for Conexant USB reference design - +// (analog part). +// +// Copyright (C) 2011, 2017, 2018 +// Maciej S. Szmigiero (mail@maciej.szmigiero.name) +// +// In case there are new analog / DVB-T hybrid devices released in the market +// using the same general design as Medion MD95700: a CX25840 video decoder +// outputting a BT.656 stream to a USB bridge chip which then forwards it to +// the host in isochronous USB packets this code should be made generic, with +// board specific bits implemented via separate card structures. +// +// This is, however, unlikely as the Medion model was released +// years ago (in 2005). +// +// TODO: +// * audio support, +// * finish radio support (requires audio of course), +// * VBI support, +// * controls support + +#include <linux/bitops.h> +#include <linux/device.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/ktime.h> +#include <linux/vmalloc.h> +#include <media/drv-intf/cx25840.h> +#include <media/tuner.h> +#include <media/v4l2-fh.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-subdev.h> +#include <media/videobuf2-vmalloc.h> + +#include "cxusb.h" + +static int cxusb_medion_v_queue_setup(struct vb2_queue *q, + unsigned int *num_buffers, + unsigned int *num_planes, + unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct dvb_usb_device *dvbdev = vb2_get_drv_priv(q); + struct cxusb_medion_dev *cxdev = dvbdev->priv; + unsigned int size = cxdev->width * cxdev->height * 2; + + if (*num_planes > 0) { + if (*num_planes != 1) + return -EINVAL; + + if (sizes[0] < size) + return -EINVAL; + } else { + *num_planes = 1; + sizes[0] = size; + } + + return 0; +} + +static int cxusb_medion_v_buf_init(struct vb2_buffer *vb) +{ + struct dvb_usb_device *dvbdev = vb2_get_drv_priv(vb->vb2_queue); + struct cxusb_medion_dev *cxdev = dvbdev->priv; + + cxusb_vprintk(dvbdev, OPS, "buffer init\n"); + + if (vb2_plane_size(vb, 0) < cxdev->width * cxdev->height * 2) + return -ENOMEM; + + cxusb_vprintk(dvbdev, OPS, "buffer OK\n"); + + return 0; +} + +static void cxusb_auxbuf_init(struct dvb_usb_device *dvbdev, + struct cxusb_medion_auxbuf *auxbuf, + u8 *buf, unsigned int len) +{ + cxusb_vprintk(dvbdev, AUXB, "initializing auxbuf of len %u\n", len); + + auxbuf->buf = buf; + auxbuf->len = len; + auxbuf->paylen = 0; +} + +static void cxusb_auxbuf_head_trim(struct dvb_usb_device *dvbdev, + struct cxusb_medion_auxbuf *auxbuf, + unsigned int pos) +{ + if (pos == 0) + return; + + if (WARN_ON(pos > auxbuf->paylen)) + return; + + cxusb_vprintk(dvbdev, AUXB, + "trimming auxbuf len by %u to %u\n", + pos, auxbuf->paylen - pos); + + memmove(auxbuf->buf, auxbuf->buf + pos, auxbuf->paylen - pos); + auxbuf->paylen -= pos; +} + +static unsigned int cxusb_auxbuf_paylen(struct cxusb_medion_auxbuf *auxbuf) +{ + return auxbuf->paylen; +} + +static bool cxusb_auxbuf_make_space(struct dvb_usb_device *dvbdev, + struct cxusb_medion_auxbuf *auxbuf, + unsigned int howmuch) +{ + unsigned int freespace; + + if (WARN_ON(howmuch >= auxbuf->len)) + howmuch = auxbuf->len - 1; + + freespace = auxbuf->len - cxusb_auxbuf_paylen(auxbuf); + + cxusb_vprintk(dvbdev, AUXB, "freespace is %u\n", freespace); + + if (freespace >= howmuch) + return true; + + howmuch -= freespace; + + cxusb_vprintk(dvbdev, AUXB, "will overwrite %u bytes of buffer\n", + howmuch); + + cxusb_auxbuf_head_trim(dvbdev, auxbuf, howmuch); + + return false; +} + +/* returns false if some data was overwritten */ +static bool cxusb_auxbuf_append_urb(struct dvb_usb_device *dvbdev, + struct cxusb_medion_auxbuf *auxbuf, + struct urb *urb) +{ + unsigned long len; + int i; + bool ret; + + for (i = 0, len = 0; i < urb->number_of_packets; i++) + len += urb->iso_frame_desc[i].actual_length; + + ret = cxusb_auxbuf_make_space(dvbdev, auxbuf, len); + + for (i = 0; i < urb->number_of_packets; i++) { + unsigned int to_copy; + + to_copy = urb->iso_frame_desc[i].actual_length; + + memcpy(auxbuf->buf + auxbuf->paylen, urb->transfer_buffer + + urb->iso_frame_desc[i].offset, to_copy); + + auxbuf->paylen += to_copy; + } + + return ret; +} + +static bool cxusb_auxbuf_copy(struct cxusb_medion_auxbuf *auxbuf, + unsigned int pos, unsigned char *dest, + unsigned int len) +{ + if (pos + len > auxbuf->paylen) + return false; + + memcpy(dest, auxbuf->buf + pos, len); + + return true; +} + +static bool cxusb_medion_cf_refc_fld_chg(struct dvb_usb_device *dvbdev, + struct cxusb_bt656_params *bt656, + bool firstfield, + unsigned int maxlines, + unsigned int maxlinesamples, + unsigned char buf[4]) +{ + bool firstfield_code = (buf[3] & CXUSB_BT656_FIELD_MASK) == + CXUSB_BT656_FIELD_1; + unsigned int remlines; + + if (bt656->line == 0 || firstfield == firstfield_code) + return false; + + if (bt656->fmode == LINE_SAMPLES) { + unsigned int remsamples = maxlinesamples - + bt656->linesamples; + + cxusb_vprintk(dvbdev, BT656, + "field %c after line %u field change\n", + firstfield ? '1' : '2', bt656->line); + + if (bt656->buf && remsamples > 0) { + memset(bt656->buf, 0, remsamples); + bt656->buf += remsamples; + + cxusb_vprintk(dvbdev, BT656, + "field %c line %u %u samples still remaining (of %u)\n", + firstfield ? '1' : '2', + bt656->line, remsamples, + maxlinesamples); + } + + bt656->line++; + } + + remlines = maxlines - bt656->line; + if (bt656->buf && remlines > 0) { + memset(bt656->buf, 0, remlines * maxlinesamples); + bt656->buf += remlines * maxlinesamples; + + cxusb_vprintk(dvbdev, BT656, + "field %c %u lines still remaining (of %u)\n", + firstfield ? '1' : '2', remlines, + maxlines); + } + + return true; +} + +static void cxusb_medion_cf_refc_start_sch(struct dvb_usb_device *dvbdev, + struct cxusb_bt656_params *bt656, + bool firstfield, + unsigned char buf[4]) +{ + bool firstfield_code = (buf[3] & CXUSB_BT656_FIELD_MASK) == + CXUSB_BT656_FIELD_1; + bool sav_code = (buf[3] & CXUSB_BT656_SEAV_MASK) == + CXUSB_BT656_SEAV_SAV; + bool vbi_code = (buf[3] & CXUSB_BT656_VBI_MASK) == + CXUSB_BT656_VBI_ON; + + if (!sav_code || firstfield != firstfield_code) + return; + + if (!vbi_code) { + cxusb_vprintk(dvbdev, BT656, "line start @ pos %u\n", + bt656->pos); + + bt656->linesamples = 0; + bt656->fmode = LINE_SAMPLES; + } else { + cxusb_vprintk(dvbdev, BT656, "VBI start @ pos %u\n", + bt656->pos); + + bt656->fmode = VBI_SAMPLES; + } +} + +static void cxusb_medion_cf_refc_line_smpl(struct dvb_usb_device *dvbdev, + struct cxusb_bt656_params *bt656, + bool firstfield, + unsigned int maxlinesamples, + unsigned char buf[4]) +{ + bool sav_code = (buf[3] & CXUSB_BT656_SEAV_MASK) == + CXUSB_BT656_SEAV_SAV; + unsigned int remsamples; + + if (sav_code) + cxusb_vprintk(dvbdev, BT656, + "SAV in line samples @ line %u, pos %u\n", + bt656->line, bt656->pos); + + remsamples = maxlinesamples - bt656->linesamples; + if (bt656->buf && remsamples > 0) { + memset(bt656->buf, 0, remsamples); + bt656->buf += remsamples; + + cxusb_vprintk(dvbdev, BT656, + "field %c line %u %u samples still remaining (of %u)\n", + firstfield ? '1' : '2', bt656->line, remsamples, + maxlinesamples); + } + + bt656->fmode = START_SEARCH; + bt656->line++; +} + +static void cxusb_medion_cf_refc_vbi_smpl(struct dvb_usb_device *dvbdev, + struct cxusb_bt656_params *bt656, + unsigned char buf[4]) +{ + bool sav_code = (buf[3] & CXUSB_BT656_SEAV_MASK) == + CXUSB_BT656_SEAV_SAV; + + if (sav_code) + cxusb_vprintk(dvbdev, BT656, "SAV in VBI samples @ pos %u\n", + bt656->pos); + + bt656->fmode = START_SEARCH; +} + +/* returns whether the whole 4-byte code should be skipped in the buffer */ +static bool cxusb_medion_cf_ref_code(struct dvb_usb_device *dvbdev, + struct cxusb_bt656_params *bt656, + bool firstfield, + unsigned int maxlines, + unsigned int maxlinesamples, + unsigned char buf[4]) +{ + if (bt656->fmode == START_SEARCH) { + cxusb_medion_cf_refc_start_sch(dvbdev, bt656, firstfield, buf); + } else if (bt656->fmode == LINE_SAMPLES) { + cxusb_medion_cf_refc_line_smpl(dvbdev, bt656, firstfield, + maxlinesamples, buf); + return false; + } else if (bt656->fmode == VBI_SAMPLES) { + cxusb_medion_cf_refc_vbi_smpl(dvbdev, bt656, buf); + return false; + } + + return true; +} + +static bool cxusb_medion_cs_start_sch(struct dvb_usb_device *dvbdev, + struct cxusb_medion_auxbuf *auxbuf, + struct cxusb_bt656_params *bt656, + unsigned int maxlinesamples) +{ + unsigned char buf[64]; + unsigned int idx; + unsigned int tocheck = clamp_t(size_t, maxlinesamples / 4, 3, + sizeof(buf)); + + if (!cxusb_auxbuf_copy(auxbuf, bt656->pos + 1, buf, tocheck)) + return false; + + for (idx = 0; idx <= tocheck - 3; idx++) + if (memcmp(buf + idx, CXUSB_BT656_PREAMBLE, 3) == 0) { + bt656->pos += (1 + idx); + return true; + } + + cxusb_vprintk(dvbdev, BT656, "line %u early start, pos %u\n", + bt656->line, bt656->pos); + + bt656->linesamples = 0; + bt656->fmode = LINE_SAMPLES; + + return true; +} + +static void cxusb_medion_cs_line_smpl(struct cxusb_bt656_params *bt656, + unsigned int maxlinesamples, + unsigned char val) +{ + if (bt656->buf) + *(bt656->buf++) = val; + + bt656->linesamples++; + bt656->pos++; + + if (bt656->linesamples >= maxlinesamples) { + bt656->fmode = START_SEARCH; + bt656->line++; + } +} + +static bool cxusb_medion_copy_samples(struct dvb_usb_device *dvbdev, + struct cxusb_medion_auxbuf *auxbuf, + struct cxusb_bt656_params *bt656, + unsigned int maxlinesamples, + unsigned char val) +{ + if (bt656->fmode == START_SEARCH && bt656->line > 0) + return cxusb_medion_cs_start_sch(dvbdev, auxbuf, bt656, + maxlinesamples); + else if (bt656->fmode == LINE_SAMPLES) + cxusb_medion_cs_line_smpl(bt656, maxlinesamples, val); + else /* TODO: copy VBI samples */ + bt656->pos++; + + return true; +} + +static bool cxusb_medion_copy_field(struct dvb_usb_device *dvbdev, + struct cxusb_medion_auxbuf *auxbuf, + struct cxusb_bt656_params *bt656, + bool firstfield, + unsigned int maxlines, + unsigned int maxlinesmpls) +{ + while (bt656->line < maxlines) { + unsigned char val; + + if (!cxusb_auxbuf_copy(auxbuf, bt656->pos, &val, 1)) + break; + + if (val == CXUSB_BT656_PREAMBLE[0]) { + unsigned char buf[4]; + + buf[0] = val; + if (!cxusb_auxbuf_copy(auxbuf, bt656->pos + 1, + buf + 1, 3)) + break; + + if (buf[1] == CXUSB_BT656_PREAMBLE[1] && + buf[2] == CXUSB_BT656_PREAMBLE[2]) { + /* + * is this a field change? + * if so, terminate copying the current field + */ + if (cxusb_medion_cf_refc_fld_chg(dvbdev, + bt656, + firstfield, + maxlines, + maxlinesmpls, + buf)) + return true; + + if (cxusb_medion_cf_ref_code(dvbdev, bt656, + firstfield, + maxlines, + maxlinesmpls, + buf)) + bt656->pos += 4; + + continue; + } + } + + if (!cxusb_medion_copy_samples(dvbdev, auxbuf, bt656, + maxlinesmpls, val)) + break; + } + + if (bt656->line < maxlines) { + cxusb_vprintk(dvbdev, BT656, + "end of buffer pos = %u, line = %u\n", + bt656->pos, bt656->line); + return false; + } + + return true; +} + +static bool cxusb_medion_v_process_auxbuf(struct cxusb_medion_dev *cxdev, + bool reset) +{ + struct dvb_usb_device *dvbdev = cxdev->dvbdev; + struct cxusb_bt656_params *bt656 = &cxdev->bt656; + + /* + * if this is a new frame + * fetch a buffer from list + */ + if (bt656->mode == NEW_FRAME) { + if (!list_empty(&cxdev->buflist)) { + cxdev->vbuf = + list_first_entry(&cxdev->buflist, + struct cxusb_medion_vbuffer, + list); + list_del(&cxdev->vbuf->list); + } else { + dev_warn(&dvbdev->udev->dev, "no free buffers\n"); + } + } + + if (bt656->mode == NEW_FRAME || reset) { + cxusb_vprintk(dvbdev, URB, "will copy field 1\n"); + bt656->pos = 0; + bt656->mode = FIRST_FIELD; + bt656->fmode = START_SEARCH; + bt656->line = 0; + + if (cxdev->vbuf) { + cxdev->vbuf->vb2.vb2_buf.timestamp = ktime_get_ns(); + bt656->buf = vb2_plane_vaddr(&cxdev->vbuf->vb2.vb2_buf, + 0); + } + } + + if (bt656->mode == FIRST_FIELD) { + if (!cxusb_medion_copy_field(dvbdev, &cxdev->auxbuf, bt656, + true, cxdev->height / 2, + cxdev->width * 2)) + return false; + + /* + * do not trim buffer there in case + * we need to reset the search later + */ + + cxusb_vprintk(dvbdev, URB, "will copy field 2\n"); + bt656->mode = SECOND_FIELD; + bt656->fmode = START_SEARCH; + bt656->line = 0; + } + + if (bt656->mode == SECOND_FIELD) { + if (!cxusb_medion_copy_field(dvbdev, &cxdev->auxbuf, bt656, + false, cxdev->height / 2, + cxdev->width * 2)) + return false; + + cxusb_auxbuf_head_trim(dvbdev, &cxdev->auxbuf, bt656->pos); + + bt656->mode = NEW_FRAME; + + if (cxdev->vbuf) { + vb2_set_plane_payload(&cxdev->vbuf->vb2.vb2_buf, 0, + cxdev->width * cxdev->height * 2); + + cxdev->vbuf->vb2.field = cxdev->field_order; + cxdev->vbuf->vb2.sequence = cxdev->vbuf_sequence++; + + vb2_buffer_done(&cxdev->vbuf->vb2.vb2_buf, + VB2_BUF_STATE_DONE); + + cxdev->vbuf = NULL; + cxdev->bt656.buf = NULL; + + cxusb_vprintk(dvbdev, URB, "frame done\n"); + } else { + cxusb_vprintk(dvbdev, URB, "frame skipped\n"); + cxdev->vbuf_sequence++; + } + } + + return true; +} + +static bool cxusb_medion_v_complete_handle_urb(struct cxusb_medion_dev *cxdev, + bool *auxbuf_reset) +{ + struct dvb_usb_device *dvbdev = cxdev->dvbdev; + unsigned int urbn; + struct urb *urb; + int ret; + + *auxbuf_reset = false; + + urbn = cxdev->nexturb; + if (!test_bit(urbn, &cxdev->urbcomplete)) + return false; + + clear_bit(urbn, &cxdev->urbcomplete); + + do { + cxdev->nexturb++; + cxdev->nexturb %= CXUSB_VIDEO_URBS; + urb = cxdev->streamurbs[cxdev->nexturb]; + } while (!urb); + + urb = cxdev->streamurbs[urbn]; + cxusb_vprintk(dvbdev, URB, "URB %u status = %d\n", urbn, urb->status); + + if (urb->status == 0 || urb->status == -EXDEV) { + int i; + unsigned long len; + + for (i = 0, len = 0; i < urb->number_of_packets; i++) + len += urb->iso_frame_desc[i].actual_length; + + cxusb_vprintk(dvbdev, URB, "URB %u data len = %lu\n", urbn, + len); + + if (len > 0) { + cxusb_vprintk(dvbdev, URB, "appending URB\n"); + + /* + * append new data to auxbuf while + * overwriting old data if necessary + * + * if any overwrite happens then we can no + * longer rely on consistency of the whole + * data so let's start again the current + * auxbuf frame assembling process from + * the beginning + */ + *auxbuf_reset = + !cxusb_auxbuf_append_urb(dvbdev, + &cxdev->auxbuf, + urb); + } + } + + cxusb_vprintk(dvbdev, URB, "URB %u resubmit\n", urbn); + + ret = usb_submit_urb(urb, GFP_KERNEL); + if (ret != 0) + dev_err(&dvbdev->udev->dev, + "unable to resubmit URB %u (%d), you'll have to restart streaming\n", + urbn, ret); + + /* next URB is complete already? reschedule us then to handle it */ + return test_bit(cxdev->nexturb, &cxdev->urbcomplete); +} + +static void cxusb_medion_v_complete_work(struct work_struct *work) +{ + struct cxusb_medion_dev *cxdev = container_of(work, + struct cxusb_medion_dev, + urbwork); + struct dvb_usb_device *dvbdev = cxdev->dvbdev; + bool auxbuf_reset; + bool reschedule; + + mutex_lock(cxdev->videodev->lock); + + cxusb_vprintk(dvbdev, URB, "worker called, stop_streaming = %d\n", + (int)cxdev->stop_streaming); + + if (cxdev->stop_streaming) + goto unlock; + + reschedule = cxusb_medion_v_complete_handle_urb(cxdev, &auxbuf_reset); + + if (cxusb_medion_v_process_auxbuf(cxdev, auxbuf_reset)) + /* reschedule us until auxbuf no longer can produce any frame */ + reschedule = true; + + if (reschedule) { + cxusb_vprintk(dvbdev, URB, "rescheduling worker\n"); + schedule_work(&cxdev->urbwork); + } + +unlock: + mutex_unlock(cxdev->videodev->lock); +} + +static void cxusb_medion_v_complete(struct urb *u) +{ + struct dvb_usb_device *dvbdev = u->context; + struct cxusb_medion_dev *cxdev = dvbdev->priv; + unsigned int i; + + for (i = 0; i < CXUSB_VIDEO_URBS; i++) + if (cxdev->streamurbs[i] == u) + break; + + if (i >= CXUSB_VIDEO_URBS) { + dev_err(&dvbdev->udev->dev, + "complete on unknown URB\n"); + return; + } + + cxusb_vprintk(dvbdev, URB, "URB %u complete\n", i); + + set_bit(i, &cxdev->urbcomplete); + schedule_work(&cxdev->urbwork); +} + +static void cxusb_medion_urbs_free(struct cxusb_medion_dev *cxdev) +{ + unsigned int i; + + for (i = 0; i < CXUSB_VIDEO_URBS; i++) + if (cxdev->streamurbs[i]) { + kfree(cxdev->streamurbs[i]->transfer_buffer); + usb_free_urb(cxdev->streamurbs[i]); + cxdev->streamurbs[i] = NULL; + } +} + +static void cxusb_medion_return_buffers(struct cxusb_medion_dev *cxdev, + bool requeue) +{ + struct cxusb_medion_vbuffer *vbuf, *vbuf_tmp; + + list_for_each_entry_safe(vbuf, vbuf_tmp, &cxdev->buflist, + list) { + list_del(&vbuf->list); + vb2_buffer_done(&vbuf->vb2.vb2_buf, + requeue ? VB2_BUF_STATE_QUEUED : + VB2_BUF_STATE_ERROR); + } + + if (cxdev->vbuf) { + vb2_buffer_done(&cxdev->vbuf->vb2.vb2_buf, + requeue ? VB2_BUF_STATE_QUEUED : + VB2_BUF_STATE_ERROR); + + cxdev->vbuf = NULL; + cxdev->bt656.buf = NULL; + } +} + +static int cxusb_medion_v_ss_auxbuf_alloc(struct cxusb_medion_dev *cxdev, + int *npackets) +{ + struct dvb_usb_device *dvbdev = cxdev->dvbdev; + u8 *buf; + unsigned int framelen, urblen, auxbuflen; + + framelen = (cxdev->width * 2 + 4 + 4) * + (cxdev->height + 50 /* VBI lines */); + + /* + * try to fit a whole frame into each URB, as long as doing so + * does not require very high order memory allocations + */ + BUILD_BUG_ON(CXUSB_VIDEO_URB_MAX_SIZE / CXUSB_VIDEO_PKT_SIZE > + CXUSB_VIDEO_MAX_FRAME_PKTS); + *npackets = min_t(int, (framelen + CXUSB_VIDEO_PKT_SIZE - 1) / + CXUSB_VIDEO_PKT_SIZE, + CXUSB_VIDEO_URB_MAX_SIZE / CXUSB_VIDEO_PKT_SIZE); + urblen = *npackets * CXUSB_VIDEO_PKT_SIZE; + + cxusb_vprintk(dvbdev, URB, + "each URB will have %d packets for total of %u bytes (%u x %u @ %u)\n", + *npackets, urblen, (unsigned int)cxdev->width, + (unsigned int)cxdev->height, framelen); + + auxbuflen = framelen + urblen; + + buf = vmalloc(auxbuflen); + if (!buf) + return -ENOMEM; + + cxusb_auxbuf_init(dvbdev, &cxdev->auxbuf, buf, auxbuflen); + + return 0; +} + +static u32 cxusb_medion_norm2field_order(v4l2_std_id norm) +{ + bool is625 = norm & V4L2_STD_625_50; + bool is525 = norm & V4L2_STD_525_60; + + if (!is625 && !is525) + return V4L2_FIELD_NONE; + + if (is625 && is525) + return V4L2_FIELD_NONE; + + if (is625) + return V4L2_FIELD_SEQ_TB; + else /* is525 */ + return V4L2_FIELD_SEQ_BT; +} + +static u32 cxusb_medion_field_order(struct cxusb_medion_dev *cxdev) +{ + struct dvb_usb_device *dvbdev = cxdev->dvbdev; + u32 field; + int ret; + v4l2_std_id norm; + + /* TV tuner is PAL-only so it is always TB */ + if (cxdev->input == 0) + return V4L2_FIELD_SEQ_TB; + + field = cxusb_medion_norm2field_order(cxdev->norm); + if (field != V4L2_FIELD_NONE) + return field; + + ret = v4l2_subdev_call(cxdev->cx25840, video, g_std, &norm); + if (ret != 0) { + cxusb_vprintk(dvbdev, OPS, + "cannot get current standard for input %u\n", + (unsigned int)cxdev->input); + } else { + field = cxusb_medion_norm2field_order(norm); + if (field != V4L2_FIELD_NONE) + return field; + } + + dev_warn(&dvbdev->udev->dev, + "cannot determine field order for the current standard setup and received signal, using TB\n"); + return V4L2_FIELD_SEQ_TB; +} + +static int cxusb_medion_v_start_streaming(struct vb2_queue *q, + unsigned int count) +{ + struct dvb_usb_device *dvbdev = vb2_get_drv_priv(q); + struct cxusb_medion_dev *cxdev = dvbdev->priv; + u8 streamon_params[2] = { 0x03, 0x00 }; + int npackets, i; + int ret; + + cxusb_vprintk(dvbdev, OPS, "should start streaming\n"); + + if (cxdev->stop_streaming) { + /* stream is being stopped */ + ret = -EBUSY; + goto ret_retbufs; + } + + cxdev->field_order = cxusb_medion_field_order(cxdev); + + ret = v4l2_subdev_call(cxdev->cx25840, video, s_stream, 1); + if (ret != 0) { + dev_err(&dvbdev->udev->dev, + "unable to start stream (%d)\n", ret); + goto ret_retbufs; + } + + ret = cxusb_ctrl_msg(dvbdev, CMD_STREAMING_ON, streamon_params, 2, + NULL, 0); + if (ret != 0) { + dev_err(&dvbdev->udev->dev, + "unable to start streaming (%d)\n", ret); + goto ret_unstream_cx; + } + + ret = cxusb_medion_v_ss_auxbuf_alloc(cxdev, &npackets); + if (ret != 0) + goto ret_unstream_md; + + for (i = 0; i < CXUSB_VIDEO_URBS; i++) { + int framen; + u8 *streambuf; + struct urb *surb; + + /* + * TODO: change this to an array of single pages to avoid + * doing a large continuous allocation when (if) + * s-g isochronous USB transfers are supported + */ + streambuf = kmalloc(npackets * CXUSB_VIDEO_PKT_SIZE, + GFP_KERNEL); + if (!streambuf) { + if (i < 2) { + ret = -ENOMEM; + goto ret_freeab; + } + break; + } + + surb = usb_alloc_urb(npackets, GFP_KERNEL); + if (!surb) { + kfree(streambuf); + ret = -ENOMEM; + goto ret_freeu; + } + + cxdev->streamurbs[i] = surb; + surb->dev = dvbdev->udev; + surb->context = dvbdev; + surb->pipe = usb_rcvisocpipe(dvbdev->udev, 2); + + surb->interval = 1; + surb->transfer_flags = URB_ISO_ASAP; + + surb->transfer_buffer = streambuf; + + surb->complete = cxusb_medion_v_complete; + surb->number_of_packets = npackets; + surb->transfer_buffer_length = npackets * CXUSB_VIDEO_PKT_SIZE; + + for (framen = 0; framen < npackets; framen++) { + surb->iso_frame_desc[framen].offset = + CXUSB_VIDEO_PKT_SIZE * framen; + + surb->iso_frame_desc[framen].length = + CXUSB_VIDEO_PKT_SIZE; + } + } + + cxdev->urbcomplete = 0; + cxdev->nexturb = 0; + cxdev->vbuf_sequence = 0; + + cxdev->vbuf = NULL; + cxdev->bt656.mode = NEW_FRAME; + cxdev->bt656.buf = NULL; + + for (i = 0; i < CXUSB_VIDEO_URBS; i++) + if (cxdev->streamurbs[i]) { + ret = usb_submit_urb(cxdev->streamurbs[i], + GFP_KERNEL); + if (ret != 0) + dev_err(&dvbdev->udev->dev, + "URB %d submission failed (%d)\n", i, + ret); + } + + return 0; + +ret_freeu: + cxusb_medion_urbs_free(cxdev); + +ret_freeab: + vfree(cxdev->auxbuf.buf); + +ret_unstream_md: + cxusb_ctrl_msg(dvbdev, CMD_STREAMING_OFF, NULL, 0, NULL, 0); + +ret_unstream_cx: + v4l2_subdev_call(cxdev->cx25840, video, s_stream, 0); + +ret_retbufs: + cxusb_medion_return_buffers(cxdev, true); + + return ret; +} + +static void cxusb_medion_v_stop_streaming(struct vb2_queue *q) +{ + struct dvb_usb_device *dvbdev = vb2_get_drv_priv(q); + struct cxusb_medion_dev *cxdev = dvbdev->priv; + int ret; + unsigned int i; + + cxusb_vprintk(dvbdev, OPS, "should stop streaming\n"); + + if (WARN_ON(cxdev->stop_streaming)) + return; + + cxdev->stop_streaming = true; + + cxusb_ctrl_msg(dvbdev, CMD_STREAMING_OFF, NULL, 0, NULL, 0); + + ret = v4l2_subdev_call(cxdev->cx25840, video, s_stream, 0); + if (ret != 0) + dev_err(&dvbdev->udev->dev, "unable to stop stream (%d)\n", + ret); + + /* let URB completion run */ + mutex_unlock(cxdev->videodev->lock); + + for (i = 0; i < CXUSB_VIDEO_URBS; i++) + if (cxdev->streamurbs[i]) + usb_kill_urb(cxdev->streamurbs[i]); + + flush_work(&cxdev->urbwork); + + mutex_lock(cxdev->videodev->lock); + + /* free transfer buffer and URB */ + vfree(cxdev->auxbuf.buf); + + cxusb_medion_urbs_free(cxdev); + + cxusb_medion_return_buffers(cxdev, false); + + cxdev->stop_streaming = false; +} + +static void cxusub_medion_v_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *v4l2buf = to_vb2_v4l2_buffer(vb); + struct cxusb_medion_vbuffer *vbuf = + container_of(v4l2buf, struct cxusb_medion_vbuffer, vb2); + struct dvb_usb_device *dvbdev = vb2_get_drv_priv(vb->vb2_queue); + struct cxusb_medion_dev *cxdev = dvbdev->priv; + + /* cxusb_vprintk(dvbdev, OPS, "mmmm.. a fresh buffer...\n"); */ + + list_add_tail(&vbuf->list, &cxdev->buflist); +} + +static const struct vb2_ops cxdev_video_qops = { + .queue_setup = cxusb_medion_v_queue_setup, + .buf_init = cxusb_medion_v_buf_init, + .start_streaming = cxusb_medion_v_start_streaming, + .stop_streaming = cxusb_medion_v_stop_streaming, + .buf_queue = cxusub_medion_v_buf_queue, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish +}; + +static const __u32 videocaps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | + V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; +static const __u32 radiocaps = V4L2_CAP_TUNER | V4L2_CAP_RADIO; + +static int cxusb_medion_v_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + struct dvb_usb_device *dvbdev = video_drvdata(file); + + strscpy(cap->driver, dvbdev->udev->dev.driver->name, + sizeof(cap->driver)); + strscpy(cap->card, "Medion 95700", sizeof(cap->card)); + usb_make_path(dvbdev->udev, cap->bus_info, sizeof(cap->bus_info)); + + cap->capabilities = videocaps | radiocaps | V4L2_CAP_DEVICE_CAPS; + + return 0; +} + +static int cxusb_medion_v_enum_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_fmtdesc *f) +{ + if (f->index != 0) + return -EINVAL; + + f->pixelformat = V4L2_PIX_FMT_UYVY; + + return 0; +} + +static int cxusb_medion_g_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct dvb_usb_device *dvbdev = video_drvdata(file); + struct cxusb_medion_dev *cxdev = dvbdev->priv; + + f->fmt.pix.width = cxdev->width; + f->fmt.pix.height = cxdev->height; + f->fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY; + f->fmt.pix.field = vb2_start_streaming_called(&cxdev->videoqueue) ? + cxdev->field_order : cxusb_medion_field_order(cxdev); + f->fmt.pix.bytesperline = cxdev->width * 2; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height; + + return 0; +} + +static int cxusb_medion_try_s_fmt_vid_cap(struct file *file, + struct v4l2_format *f, + bool isset) +{ + struct dvb_usb_device *dvbdev = video_drvdata(file); + struct cxusb_medion_dev *cxdev = dvbdev->priv; + struct v4l2_subdev_format subfmt; + u32 field; + int ret; + + if (isset && vb2_is_busy(&cxdev->videoqueue)) + return -EBUSY; + + field = vb2_start_streaming_called(&cxdev->videoqueue) ? + cxdev->field_order : cxusb_medion_field_order(cxdev); + + memset(&subfmt, 0, sizeof(subfmt)); + subfmt.which = isset ? V4L2_SUBDEV_FORMAT_ACTIVE : + V4L2_SUBDEV_FORMAT_TRY; + subfmt.format.width = f->fmt.pix.width & ~1; + subfmt.format.height = f->fmt.pix.height & ~1; + subfmt.format.code = MEDIA_BUS_FMT_FIXED; + subfmt.format.field = field; + subfmt.format.colorspace = V4L2_COLORSPACE_SMPTE170M; + + ret = v4l2_subdev_call(cxdev->cx25840, pad, set_fmt, NULL, &subfmt); + if (ret != 0) + return ret; + + f->fmt.pix.width = subfmt.format.width; + f->fmt.pix.height = subfmt.format.height; + f->fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY; + f->fmt.pix.field = field; + f->fmt.pix.bytesperline = f->fmt.pix.width * 2; + f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + + if (isset) { + cxdev->width = f->fmt.pix.width; + cxdev->height = f->fmt.pix.height; + } + + return 0; +} + +static int cxusb_medion_try_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_format *f) +{ + return cxusb_medion_try_s_fmt_vid_cap(file, f, false); +} + +static int cxusb_medion_s_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_format *f) +{ + return cxusb_medion_try_s_fmt_vid_cap(file, f, true); +} + +static const struct { + struct v4l2_input input; + u32 inputcfg; +} cxusb_medion_inputs[] = { + { .input = { .name = "TV tuner", .type = V4L2_INPUT_TYPE_TUNER, + .tuner = 0, .std = V4L2_STD_PAL }, + .inputcfg = CX25840_COMPOSITE2, }, + + { .input = { .name = "Composite", .type = V4L2_INPUT_TYPE_CAMERA, + .std = V4L2_STD_ALL }, + .inputcfg = CX25840_COMPOSITE1, }, + + { .input = { .name = "S-Video", .type = V4L2_INPUT_TYPE_CAMERA, + .std = V4L2_STD_ALL }, + .inputcfg = CX25840_SVIDEO_LUMA3 | CX25840_SVIDEO_CHROMA4 } +}; + +#define CXUSB_INPUT_CNT ARRAY_SIZE(cxusb_medion_inputs) + +static int cxusb_medion_enum_input(struct file *file, void *fh, + struct v4l2_input *inp) +{ + struct dvb_usb_device *dvbdev = video_drvdata(file); + struct cxusb_medion_dev *cxdev = dvbdev->priv; + u32 index = inp->index; + + if (index >= CXUSB_INPUT_CNT) + return -EINVAL; + + *inp = cxusb_medion_inputs[index].input; + inp->index = index; + inp->capabilities |= V4L2_IN_CAP_STD; + + if (index == cxdev->input) { + int ret; + u32 status = 0; + + ret = v4l2_subdev_call(cxdev->cx25840, video, g_input_status, + &status); + if (ret != 0) + dev_warn(&dvbdev->udev->dev, + "cx25840 input status query failed (%d)\n", + ret); + else + inp->status = status; + } + + return 0; +} + +static int cxusb_medion_g_input(struct file *file, void *fh, + unsigned int *i) +{ + struct dvb_usb_device *dvbdev = video_drvdata(file); + struct cxusb_medion_dev *cxdev = dvbdev->priv; + + *i = cxdev->input; + + return 0; +} + +static int cxusb_medion_set_norm(struct cxusb_medion_dev *cxdev, + v4l2_std_id norm) +{ + struct dvb_usb_device *dvbdev = cxdev->dvbdev; + int ret; + + cxusb_vprintk(dvbdev, OPS, + "trying to set standard for input %u to %lx\n", + (unsigned int)cxdev->input, + (unsigned long)norm); + + /* no autodetection support */ + if (norm == V4L2_STD_UNKNOWN) + return -EINVAL; + + /* on composite or S-Video any std is acceptable */ + if (cxdev->input != 0) { + ret = v4l2_subdev_call(cxdev->cx25840, video, s_std, norm); + if (ret) + return ret; + + goto ret_savenorm; + } + + /* TV tuner is only able to demodulate PAL */ + if ((norm & ~V4L2_STD_PAL) != 0) + return -EINVAL; + + ret = v4l2_subdev_call(cxdev->tda9887, video, s_std, norm); + if (ret != 0) { + dev_err(&dvbdev->udev->dev, + "tda9887 norm setup failed (%d)\n", + ret); + return ret; + } + + ret = v4l2_subdev_call(cxdev->tuner, video, s_std, norm); + if (ret != 0) { + dev_err(&dvbdev->udev->dev, + "tuner norm setup failed (%d)\n", + ret); + return ret; + } + + ret = v4l2_subdev_call(cxdev->cx25840, video, s_std, norm); + if (ret != 0) { + dev_err(&dvbdev->udev->dev, + "cx25840 norm setup failed (%d)\n", + ret); + return ret; + } + +ret_savenorm: + cxdev->norm = norm; + + return 0; +} + +static int cxusb_medion_s_input(struct file *file, void *fh, + unsigned int i) +{ + struct dvb_usb_device *dvbdev = video_drvdata(file); + struct cxusb_medion_dev *cxdev = dvbdev->priv; + int ret; + v4l2_std_id norm; + + if (i >= CXUSB_INPUT_CNT) + return -EINVAL; + + ret = v4l2_subdev_call(cxdev->cx25840, video, s_routing, + cxusb_medion_inputs[i].inputcfg, 0, 0); + if (ret != 0) + return ret; + + cxdev->input = i; + cxdev->videodev->tvnorms = cxusb_medion_inputs[i].input.std; + + norm = cxdev->norm & cxusb_medion_inputs[i].input.std; + if (norm == 0) + norm = cxusb_medion_inputs[i].input.std; + + cxusb_medion_set_norm(cxdev, norm); + + return 0; +} + +static int cxusb_medion_g_tuner(struct file *file, void *fh, + struct v4l2_tuner *tuner) +{ + struct dvb_usb_device *dvbdev = video_drvdata(file); + struct cxusb_medion_dev *cxdev = dvbdev->priv; + struct video_device *vdev = video_devdata(file); + int ret; + + if (tuner->index != 0) + return -EINVAL; + + if (vdev->vfl_type == VFL_TYPE_GRABBER) + tuner->type = V4L2_TUNER_ANALOG_TV; + else + tuner->type = V4L2_TUNER_RADIO; + + tuner->capability = 0; + tuner->afc = 0; + + /* + * fills: + * always: capability (static), rangelow (static), rangehigh (static) + * radio mode: afc (may fail silently), rxsubchans (static), audmode + */ + ret = v4l2_subdev_call(cxdev->tda9887, tuner, g_tuner, tuner); + if (ret != 0) + return ret; + + /* + * fills: + * always: capability (static), rangelow (static), rangehigh (static) + * radio mode: rxsubchans (always stereo), audmode, + * signal (might be wrong) + */ + ret = v4l2_subdev_call(cxdev->tuner, tuner, g_tuner, tuner); + if (ret != 0) + return ret; + + tuner->signal = 0; + + /* + * fills: TV mode: capability, rxsubchans, audmode, signal + */ + ret = v4l2_subdev_call(cxdev->cx25840, tuner, g_tuner, tuner); + if (ret != 0) + return ret; + + if (vdev->vfl_type == VFL_TYPE_GRABBER) + strscpy(tuner->name, "TV tuner", sizeof(tuner->name)); + else + strscpy(tuner->name, "Radio tuner", sizeof(tuner->name)); + + memset(tuner->reserved, 0, sizeof(tuner->reserved)); + + return 0; +} + +static int cxusb_medion_s_tuner(struct file *file, void *fh, + const struct v4l2_tuner *tuner) +{ + struct dvb_usb_device *dvbdev = video_drvdata(file); + struct cxusb_medion_dev *cxdev = dvbdev->priv; + struct video_device *vdev = video_devdata(file); + int ret; + + if (tuner->index != 0) + return -EINVAL; + + ret = v4l2_subdev_call(cxdev->tda9887, tuner, s_tuner, tuner); + if (ret != 0) + return ret; + + ret = v4l2_subdev_call(cxdev->tuner, tuner, s_tuner, tuner); + if (ret != 0) + return ret; + + /* + * make sure that cx25840 is in a correct TV / radio mode, + * since calls above may have changed it for tuner / IF demod + */ + if (vdev->vfl_type == VFL_TYPE_GRABBER) + v4l2_subdev_call(cxdev->cx25840, video, s_std, cxdev->norm); + else + v4l2_subdev_call(cxdev->cx25840, tuner, s_radio); + + return v4l2_subdev_call(cxdev->cx25840, tuner, s_tuner, tuner); +} + +static int cxusb_medion_g_frequency(struct file *file, void *fh, + struct v4l2_frequency *freq) +{ + struct dvb_usb_device *dvbdev = video_drvdata(file); + struct cxusb_medion_dev *cxdev = dvbdev->priv; + + if (freq->tuner != 0) + return -EINVAL; + + return v4l2_subdev_call(cxdev->tuner, tuner, g_frequency, freq); +} + +static int cxusb_medion_s_frequency(struct file *file, void *fh, + const struct v4l2_frequency *freq) +{ + struct dvb_usb_device *dvbdev = video_drvdata(file); + struct cxusb_medion_dev *cxdev = dvbdev->priv; + struct video_device *vdev = video_devdata(file); + int ret; + + if (freq->tuner != 0) + return -EINVAL; + + ret = v4l2_subdev_call(cxdev->tda9887, tuner, s_frequency, freq); + if (ret != 0) + return ret; + + ret = v4l2_subdev_call(cxdev->tuner, tuner, s_frequency, freq); + if (ret != 0) + return ret; + + /* + * make sure that cx25840 is in a correct TV / radio mode, + * since calls above may have changed it for tuner / IF demod + */ + if (vdev->vfl_type == VFL_TYPE_GRABBER) + v4l2_subdev_call(cxdev->cx25840, video, s_std, cxdev->norm); + else + v4l2_subdev_call(cxdev->cx25840, tuner, s_radio); + + return v4l2_subdev_call(cxdev->cx25840, tuner, s_frequency, freq); +} + +static int cxusb_medion_g_std(struct file *file, void *fh, + v4l2_std_id *norm) +{ + struct dvb_usb_device *dvbdev = video_drvdata(file); + struct cxusb_medion_dev *cxdev = dvbdev->priv; + + *norm = cxdev->norm; + + if (*norm == V4L2_STD_UNKNOWN) + return -ENODATA; + + return 0; +} + +static int cxusb_medion_s_std(struct file *file, void *fh, + v4l2_std_id norm) +{ + struct dvb_usb_device *dvbdev = video_drvdata(file); + struct cxusb_medion_dev *cxdev = dvbdev->priv; + + return cxusb_medion_set_norm(cxdev, norm); +} + +static int cxusb_medion_querystd(struct file *file, void *fh, + v4l2_std_id *norm) +{ + struct dvb_usb_device *dvbdev = video_drvdata(file); + struct cxusb_medion_dev *cxdev = dvbdev->priv; + v4l2_std_id norm_mask; + int ret; + + /* + * make sure we don't have improper std bits set for the TV tuner + * (could happen when no signal was present yet after reset) + */ + if (cxdev->input == 0) + norm_mask = V4L2_STD_PAL; + else + norm_mask = V4L2_STD_ALL; + + ret = v4l2_subdev_call(cxdev->cx25840, video, querystd, norm); + if (ret != 0) { + cxusb_vprintk(dvbdev, OPS, + "cannot get detected standard for input %u\n", + (unsigned int)cxdev->input); + return ret; + } + + cxusb_vprintk(dvbdev, OPS, "input %u detected standard is %lx\n", + (unsigned int)cxdev->input, (unsigned long)*norm); + *norm &= norm_mask; + + return 0; +} + +static int cxusb_medion_log_status(struct file *file, void *fh) +{ + struct dvb_usb_device *dvbdev = video_drvdata(file); + struct cxusb_medion_dev *cxdev = dvbdev->priv; + + v4l2_device_call_all(&cxdev->v4l2dev, 0, core, log_status); + + return 0; +} + +static const struct v4l2_ioctl_ops cxusb_video_ioctl = { + .vidioc_querycap = cxusb_medion_v_querycap, + .vidioc_enum_fmt_vid_cap = cxusb_medion_v_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = cxusb_medion_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = cxusb_medion_s_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = cxusb_medion_try_fmt_vid_cap, + .vidioc_enum_input = cxusb_medion_enum_input, + .vidioc_g_input = cxusb_medion_g_input, + .vidioc_s_input = cxusb_medion_s_input, + .vidioc_g_tuner = cxusb_medion_g_tuner, + .vidioc_s_tuner = cxusb_medion_s_tuner, + .vidioc_g_frequency = cxusb_medion_g_frequency, + .vidioc_s_frequency = cxusb_medion_s_frequency, + .vidioc_g_std = cxusb_medion_g_std, + .vidioc_s_std = cxusb_medion_s_std, + .vidioc_querystd = cxusb_medion_querystd, + .vidioc_log_status = cxusb_medion_log_status, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff +}; + +static const struct v4l2_ioctl_ops cxusb_radio_ioctl = { + .vidioc_querycap = cxusb_medion_v_querycap, + .vidioc_g_tuner = cxusb_medion_g_tuner, + .vidioc_s_tuner = cxusb_medion_s_tuner, + .vidioc_g_frequency = cxusb_medion_g_frequency, + .vidioc_s_frequency = cxusb_medion_s_frequency, + .vidioc_log_status = cxusb_medion_log_status +}; + +/* + * in principle, this should be const, but s_io_pin_config is declared + * to take non-const, and gcc complains + */ +static struct v4l2_subdev_io_pin_config cxusub_medion_pin_config[] = { + { .pin = CX25840_PIN_DVALID_PRGM0, .function = CX25840_PAD_DEFAULT, + .strength = CX25840_PIN_DRIVE_MEDIUM }, + { .pin = CX25840_PIN_PLL_CLK_PRGM7, .function = CX25840_PAD_AUX_PLL }, + { .pin = CX25840_PIN_HRESET_PRGM2, .function = CX25840_PAD_ACTIVE, + .strength = CX25840_PIN_DRIVE_MEDIUM } +}; + +int cxusb_medion_analog_init(struct dvb_usb_device *dvbdev) +{ + struct cxusb_medion_dev *cxdev = dvbdev->priv; + u8 tuner_analog_msg_data[] = { 0x9c, 0x60, 0x85, 0x54 }; + struct i2c_msg tuner_analog_msg = { .addr = 0x61, .flags = 0, + .buf = tuner_analog_msg_data, + .len = + sizeof(tuner_analog_msg_data) }; + struct v4l2_subdev_format subfmt; + int ret; + + /* switch tuner to analog mode so IF demod will become accessible */ + ret = i2c_transfer(&dvbdev->i2c_adap, &tuner_analog_msg, 1); + if (ret != 1) + dev_warn(&dvbdev->udev->dev, + "tuner analog switch failed (%d)\n", ret); + + /* + * cx25840 might have lost power during mode switching so we need + * to set it again + */ + ret = v4l2_subdev_call(cxdev->cx25840, core, reset, 0); + if (ret != 0) + dev_warn(&dvbdev->udev->dev, + "cx25840 reset failed (%d)\n", ret); + + ret = v4l2_subdev_call(cxdev->cx25840, video, s_routing, + CX25840_COMPOSITE1, 0, 0); + if (ret != 0) + dev_warn(&dvbdev->udev->dev, + "cx25840 initial input setting failed (%d)\n", ret); + + /* composite */ + cxdev->input = 1; + cxdev->videodev->tvnorms = V4L2_STD_ALL; + cxdev->norm = V4L2_STD_PAL; + + /* TODO: setup audio samples insertion */ + + ret = v4l2_subdev_call(cxdev->cx25840, core, s_io_pin_config, + ARRAY_SIZE(cxusub_medion_pin_config), + cxusub_medion_pin_config); + if (ret != 0) + dev_warn(&dvbdev->udev->dev, + "cx25840 pin config failed (%d)\n", ret); + + /* make sure that we aren't in radio mode */ + v4l2_subdev_call(cxdev->tda9887, video, s_std, cxdev->norm); + v4l2_subdev_call(cxdev->tuner, video, s_std, cxdev->norm); + v4l2_subdev_call(cxdev->cx25840, video, s_std, cxdev->norm); + + memset(&subfmt, 0, sizeof(subfmt)); + subfmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + subfmt.format.width = cxdev->width; + subfmt.format.height = cxdev->height; + subfmt.format.code = MEDIA_BUS_FMT_FIXED; + subfmt.format.field = V4L2_FIELD_SEQ_TB; + subfmt.format.colorspace = V4L2_COLORSPACE_SMPTE170M; + + ret = v4l2_subdev_call(cxdev->cx25840, pad, set_fmt, NULL, &subfmt); + if (ret != 0) + dev_warn(&dvbdev->udev->dev, + "cx25840 format set failed (%d)\n", ret); + + if (ret == 0) { + cxdev->width = subfmt.format.width; + cxdev->height = subfmt.format.height; + } + + return 0; +} + +static int cxusb_videoradio_open(struct file *f) +{ + struct dvb_usb_device *dvbdev = video_drvdata(f); + int ret; + + /* + * no locking needed since this call only modifies analog + * state if there are no other analog handles currenly + * opened so ops done via them cannot create a conflict + */ + ret = cxusb_medion_get(dvbdev, CXUSB_OPEN_ANALOG); + if (ret != 0) + return ret; + + ret = v4l2_fh_open(f); + if (ret != 0) + goto ret_release; + + cxusb_vprintk(dvbdev, OPS, "got open\n"); + + return 0; + +ret_release: + cxusb_medion_put(dvbdev); + + return ret; +} + +static int cxusb_videoradio_release(struct file *f) +{ + struct video_device *vdev = video_devdata(f); + struct dvb_usb_device *dvbdev = video_drvdata(f); + int ret; + + cxusb_vprintk(dvbdev, OPS, "got release\n"); + + if (vdev->vfl_type == VFL_TYPE_GRABBER) + ret = vb2_fop_release(f); + else + ret = v4l2_fh_release(f); + + cxusb_medion_put(dvbdev); + + return ret; +} + +static const struct v4l2_file_operations cxusb_video_fops = { + .owner = THIS_MODULE, + .read = vb2_fop_read, + .poll = vb2_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = vb2_fop_mmap, + .open = cxusb_videoradio_open, + .release = cxusb_videoradio_release +}; + +static const struct v4l2_file_operations cxusb_radio_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = video_ioctl2, + .open = cxusb_videoradio_open, + .release = cxusb_videoradio_release +}; + +static void cxusb_medion_v4l2_release(struct v4l2_device *v4l2_dev) +{ + struct cxusb_medion_dev *cxdev = + container_of(v4l2_dev, struct cxusb_medion_dev, v4l2dev); + struct dvb_usb_device *dvbdev = cxdev->dvbdev; + + cxusb_vprintk(dvbdev, OPS, "v4l2 device release\n"); + + v4l2_device_unregister(&cxdev->v4l2dev); + + mutex_destroy(&cxdev->dev_lock); + + while (completion_done(&cxdev->v4l2_release)) + schedule(); + + complete(&cxdev->v4l2_release); +} + +static void cxusb_medion_videodev_release(struct video_device *vdev) +{ + struct dvb_usb_device *dvbdev = video_get_drvdata(vdev); + + cxusb_vprintk(dvbdev, OPS, "video device release\n"); + + vb2_queue_release(vdev->queue); + + video_device_release(vdev); +} + +static int cxusb_medion_register_analog_video(struct dvb_usb_device *dvbdev) +{ + struct cxusb_medion_dev *cxdev = dvbdev->priv; + int ret; + + cxdev->videoqueue.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + cxdev->videoqueue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ | + VB2_DMABUF; + cxdev->videoqueue.ops = &cxdev_video_qops; + cxdev->videoqueue.mem_ops = &vb2_vmalloc_memops; + cxdev->videoqueue.drv_priv = dvbdev; + cxdev->videoqueue.buf_struct_size = + sizeof(struct cxusb_medion_vbuffer); + cxdev->videoqueue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + cxdev->videoqueue.min_buffers_needed = 6; + cxdev->videoqueue.lock = &cxdev->dev_lock; + + ret = vb2_queue_init(&cxdev->videoqueue); + if (ret) { + dev_err(&dvbdev->udev->dev, + "video queue init failed, ret = %d\n", ret); + return ret; + } + + cxdev->videodev = video_device_alloc(); + if (!cxdev->videodev) { + dev_err(&dvbdev->udev->dev, "video device alloc failed\n"); + ret = -ENOMEM; + goto ret_qrelease; + } + + cxdev->videodev->device_caps = videocaps; + cxdev->videodev->fops = &cxusb_video_fops; + cxdev->videodev->v4l2_dev = &cxdev->v4l2dev; + cxdev->videodev->queue = &cxdev->videoqueue; + strscpy(cxdev->videodev->name, "cxusb", sizeof(cxdev->videodev->name)); + cxdev->videodev->vfl_dir = VFL_DIR_RX; + cxdev->videodev->ioctl_ops = &cxusb_video_ioctl; + cxdev->videodev->tvnorms = V4L2_STD_ALL; + cxdev->videodev->release = cxusb_medion_videodev_release; + cxdev->videodev->lock = &cxdev->dev_lock; + video_set_drvdata(cxdev->videodev, dvbdev); + + ret = video_register_device(cxdev->videodev, VFL_TYPE_GRABBER, -1); + if (ret) { + dev_err(&dvbdev->udev->dev, + "video device register failed, ret = %d\n", ret); + goto ret_vrelease; + } + + return 0; + +ret_vrelease: + video_device_release(cxdev->videodev); + +ret_qrelease: + vb2_queue_release(&cxdev->videoqueue); + + return ret; +} + +static int cxusb_medion_register_analog_radio(struct dvb_usb_device *dvbdev) +{ + struct cxusb_medion_dev *cxdev = dvbdev->priv; + int ret; + + cxdev->radiodev = video_device_alloc(); + if (!cxdev->radiodev) { + dev_err(&dvbdev->udev->dev, "radio device alloc failed\n"); + return -ENOMEM; + } + + cxdev->radiodev->device_caps = radiocaps; + cxdev->radiodev->fops = &cxusb_radio_fops; + cxdev->radiodev->v4l2_dev = &cxdev->v4l2dev; + strscpy(cxdev->radiodev->name, "cxusb", sizeof(cxdev->radiodev->name)); + cxdev->radiodev->vfl_dir = VFL_DIR_RX; + cxdev->radiodev->ioctl_ops = &cxusb_radio_ioctl; + cxdev->radiodev->release = video_device_release; + cxdev->radiodev->lock = &cxdev->dev_lock; + video_set_drvdata(cxdev->radiodev, dvbdev); + + ret = video_register_device(cxdev->radiodev, VFL_TYPE_RADIO, -1); + if (ret) { + dev_err(&dvbdev->udev->dev, + "radio device register failed, ret = %d\n", ret); + video_device_release(cxdev->radiodev); + return ret; + } + + return 0; +} + +static int cxusb_medion_register_analog_subdevs(struct dvb_usb_device *dvbdev) +{ + struct cxusb_medion_dev *cxdev = dvbdev->priv; + int ret; + struct tuner_setup tun_setup; + + /* attach cx25840 capture chip */ + cxdev->cx25840 = v4l2_i2c_new_subdev(&cxdev->v4l2dev, + &dvbdev->i2c_adap, + "cx25840", 0x44, NULL); + if (!cxdev->cx25840) { + dev_err(&dvbdev->udev->dev, "cx25840 not found\n"); + return -ENODEV; + } + + /* + * Initialize cx25840 chip by calling its subdevice init core op. + * + * This switches it into the generic mode that disables some of + * ivtv-related hacks in the cx25840 driver while allowing setting + * of the chip video output configuration (passed in the call below + * as the last argument). + */ + ret = v4l2_subdev_call(cxdev->cx25840, core, init, + CX25840_VCONFIG_FMT_BT656 | + CX25840_VCONFIG_RES_8BIT | + CX25840_VCONFIG_VBIRAW_DISABLED | + CX25840_VCONFIG_ANCDATA_DISABLED | + CX25840_VCONFIG_ACTIVE_COMPOSITE | + CX25840_VCONFIG_VALID_ANDACTIVE | + CX25840_VCONFIG_HRESETW_NORMAL | + CX25840_VCONFIG_CLKGATE_NONE | + CX25840_VCONFIG_DCMODE_DWORDS); + if (ret != 0) { + dev_err(&dvbdev->udev->dev, + "cx25840 init failed (%d)\n", ret); + return ret; + } + + /* attach analog tuner */ + cxdev->tuner = v4l2_i2c_new_subdev(&cxdev->v4l2dev, + &dvbdev->i2c_adap, + "tuner", 0x61, NULL); + if (!cxdev->tuner) { + dev_err(&dvbdev->udev->dev, "tuner not found\n"); + return -ENODEV; + } + + /* configure it */ + memset(&tun_setup, 0, sizeof(tun_setup)); + tun_setup.addr = 0x61; + tun_setup.type = TUNER_PHILIPS_FMD1216ME_MK3; + tun_setup.mode_mask = T_RADIO | T_ANALOG_TV; + v4l2_subdev_call(cxdev->tuner, tuner, s_type_addr, &tun_setup); + + /* attach IF demod */ + cxdev->tda9887 = v4l2_i2c_new_subdev(&cxdev->v4l2dev, + &dvbdev->i2c_adap, + "tuner", 0x43, NULL); + if (!cxdev->tda9887) { + dev_err(&dvbdev->udev->dev, "tda9887 not found\n"); + return -ENODEV; + } + + return 0; +} + +int cxusb_medion_register_analog(struct dvb_usb_device *dvbdev) +{ + struct cxusb_medion_dev *cxdev = dvbdev->priv; + int ret; + + mutex_init(&cxdev->dev_lock); + + init_completion(&cxdev->v4l2_release); + + cxdev->v4l2dev.release = cxusb_medion_v4l2_release; + + ret = v4l2_device_register(&dvbdev->udev->dev, &cxdev->v4l2dev); + if (ret != 0) { + dev_err(&dvbdev->udev->dev, + "V4L2 device registration failed, ret = %d\n", ret); + mutex_destroy(&cxdev->dev_lock); + return ret; + } + + ret = cxusb_medion_register_analog_subdevs(dvbdev); + if (ret) + goto ret_unregister; + + INIT_WORK(&cxdev->urbwork, cxusb_medion_v_complete_work); + INIT_LIST_HEAD(&cxdev->buflist); + + cxdev->width = 320; + cxdev->height = 240; + + ret = cxusb_medion_register_analog_video(dvbdev); + if (ret) + goto ret_unregister; + + ret = cxusb_medion_register_analog_radio(dvbdev); + if (ret) + goto ret_vunreg; + + return 0; + +ret_vunreg: + video_unregister_device(cxdev->videodev); + +ret_unregister: + v4l2_device_put(&cxdev->v4l2dev); + wait_for_completion(&cxdev->v4l2_release); + + return ret; +} + +void cxusb_medion_unregister_analog(struct dvb_usb_device *dvbdev) +{ + struct cxusb_medion_dev *cxdev = dvbdev->priv; + + cxusb_vprintk(dvbdev, OPS, "unregistering analog\n"); + + video_unregister_device(cxdev->radiodev); + video_unregister_device(cxdev->videodev); + + v4l2_device_put(&cxdev->v4l2dev); + wait_for_completion(&cxdev->v4l2_release); + + cxusb_vprintk(dvbdev, OPS, "analog unregistered\n"); +} diff --git a/drivers/media/usb/dvb-usb/cxusb.c b/drivers/media/usb/dvb-usb/cxusb.c index 8039ba4ebf68..bac0778f7def 100644 --- a/drivers/media/usb/dvb-usb/cxusb.c +++ b/drivers/media/usb/dvb-usb/cxusb.c @@ -12,18 +12,21 @@ * design, so it can be reused for the "analogue-only" device (if it will * appear at all). * - * TODO: Use the cx25840-driver for the analogue part * * Copyright (C) 2005 Patrick Boettcher (patrick.boettcher@posteo.de) * Copyright (C) 2006 Michael Krufky (mkrufky@linuxtv.org) * Copyright (C) 2006, 2007 Chris Pascoe (c.pascoe@itee.uq.edu.au) + * Copyright (C) 2011, 2017 Maciej S. Szmigiero (mail@maciej.szmigiero.name) * * see Documentation/media/dvb-drivers/dvb-usb.rst for more information */ #include <media/tuner.h> -#include <linux/vmalloc.h> -#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/device.h> #include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/vmalloc.h> #include "cxusb.h" @@ -44,17 +47,45 @@ #include "si2157.h" /* debug */ -static int dvb_usb_cxusb_debug; +int dvb_usb_cxusb_debug; module_param_named(debug, dvb_usb_cxusb_debug, int, 0644); -MODULE_PARM_DESC(debug, "set debugging level (1=rc (or-able))." DVB_USB_DEBUG_STATUS); +MODULE_PARM_DESC(debug, "set debugging level (see cxusb.h)." + DVB_USB_DEBUG_STATUS); DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); -#define deb_info(args...) dprintk(dvb_usb_cxusb_debug, 0x03, args) -#define deb_i2c(args...) dprintk(dvb_usb_cxusb_debug, 0x02, args) +#define deb_info(args...) dprintk(dvb_usb_cxusb_debug, CXUSB_DBG_MISC, args) +#define deb_i2c(args...) dprintk(dvb_usb_cxusb_debug, CXUSB_DBG_I2C, args) -static int cxusb_ctrl_msg(struct dvb_usb_device *d, - u8 cmd, const u8 *wbuf, int wlen, u8 *rbuf, int rlen) +enum cxusb_table_index { + MEDION_MD95700, + DVICO_BLUEBIRD_LG064F_COLD, + DVICO_BLUEBIRD_LG064F_WARM, + DVICO_BLUEBIRD_DUAL_1_COLD, + DVICO_BLUEBIRD_DUAL_1_WARM, + DVICO_BLUEBIRD_LGZ201_COLD, + DVICO_BLUEBIRD_LGZ201_WARM, + DVICO_BLUEBIRD_TH7579_COLD, + DVICO_BLUEBIRD_TH7579_WARM, + DIGITALNOW_BLUEBIRD_DUAL_1_COLD, + DIGITALNOW_BLUEBIRD_DUAL_1_WARM, + DVICO_BLUEBIRD_DUAL_2_COLD, + DVICO_BLUEBIRD_DUAL_2_WARM, + DVICO_BLUEBIRD_DUAL_4, + DVICO_BLUEBIRD_DVB_T_NANO_2, + DVICO_BLUEBIRD_DVB_T_NANO_2_NFW_WARM, + AVERMEDIA_VOLAR_A868R, + DVICO_BLUEBIRD_DUAL_4_REV_2, + CONEXANT_D680_DMB, + MYGICA_D689, + MYGICA_T230, + NR__cxusb_table_index +}; + +static struct usb_device_id cxusb_table[]; + +int cxusb_ctrl_msg(struct dvb_usb_device *d, + u8 cmd, const u8 *wbuf, int wlen, u8 *rbuf, int rlen) { struct cxusb_state *st = d->priv; int ret; @@ -86,7 +117,8 @@ static void cxusb_gpio_tuner(struct dvb_usb_device *d, int onoff) struct cxusb_state *st = d->priv; u8 o[2], i; - if (st->gpio_write_state[GPIO_TUNER] == onoff) + if (st->gpio_write_state[GPIO_TUNER] == onoff && + !st->gpio_write_refresh[GPIO_TUNER]) return; o[0] = GPIO_TUNER; @@ -97,10 +129,11 @@ static void cxusb_gpio_tuner(struct dvb_usb_device *d, int onoff) deb_info("gpio_write failed.\n"); st->gpio_write_state[GPIO_TUNER] = onoff; + st->gpio_write_refresh[GPIO_TUNER] = false; } static int cxusb_bluebird_gpio_rw(struct dvb_usb_device *d, u8 changemask, - u8 newval) + u8 newval) { u8 o[2], gpio_state; int rc; @@ -128,7 +161,7 @@ static void cxusb_nano2_led(struct dvb_usb_device *d, int onoff) } static int cxusb_d680_dmb_gpio_tuner(struct dvb_usb_device *d, - u8 addr, int onoff) + u8 addr, int onoff) { u8 o[2] = {addr, onoff}; u8 i; @@ -138,12 +171,12 @@ static int cxusb_d680_dmb_gpio_tuner(struct dvb_usb_device *d, if (rc < 0) return rc; + if (i == 0x01) return 0; - else { - deb_info("gpio_write failed.\n"); - return -EIO; - } + + deb_info("gpio_write failed.\n"); + return -EIO; } /* I2C */ @@ -158,7 +191,6 @@ static int cxusb_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], return -EAGAIN; for (i = 0; i < num; i++) { - if (le16_to_cpu(d->udev->descriptor.idVendor) == USB_VID_MEDION) switch (msg[i].addr) { case 0x63: @@ -184,13 +216,13 @@ static int cxusb_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], obuf[2] = msg[i].addr; if (cxusb_ctrl_msg(d, CMD_I2C_READ, obuf, 3, - ibuf, 1+msg[i].len) < 0) { + ibuf, 1 + msg[i].len) < 0) { warn("i2c read failed"); break; } memcpy(msg[i].buf, &ibuf[1], msg[i].len); - } else if (i+1 < num && (msg[i+1].flags & I2C_M_RD) && - msg[i].addr == msg[i+1].addr) { + } else if (i + 1 < num && (msg[i + 1].flags & I2C_M_RD) && + msg[i].addr == msg[i + 1].addr) { /* write to then read from same address */ u8 obuf[MAX_XFER_SIZE], ibuf[MAX_XFER_SIZE]; @@ -207,19 +239,19 @@ static int cxusb_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], goto unlock; } obuf[0] = msg[i].len; - obuf[1] = msg[i+1].len; + obuf[1] = msg[i + 1].len; obuf[2] = msg[i].addr; memcpy(&obuf[3], msg[i].buf, msg[i].len); if (cxusb_ctrl_msg(d, CMD_I2C_READ, - obuf, 3+msg[i].len, - ibuf, 1+msg[i+1].len) < 0) + obuf, 3 + msg[i].len, + ibuf, 1 + msg[i + 1].len) < 0) break; if (ibuf[0] != 0x08) deb_i2c("i2c read may have failed\n"); - memcpy(msg[i+1].buf, &ibuf[1], msg[i+1].len); + memcpy(msg[i + 1].buf, &ibuf[1], msg[i + 1].len); i++; } else { @@ -237,7 +269,7 @@ static int cxusb_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], memcpy(&obuf[2], msg[i].buf, msg[i].len); if (cxusb_ctrl_msg(d, CMD_I2C_WRITE, obuf, - 2+msg[i].len, &ibuf,1) < 0) + 2 + msg[i].len, &ibuf, 1) < 0) break; if (ibuf != 0x08) deb_i2c("i2c write may have failed\n"); @@ -256,7 +288,7 @@ unlock: static u32 cxusb_i2c_func(struct i2c_adapter *adapter) { - return I2C_FUNC_I2C; + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; } static struct i2c_algorithm cxusb_i2c_algo = { @@ -264,29 +296,67 @@ static struct i2c_algorithm cxusb_i2c_algo = { .functionality = cxusb_i2c_func, }; -static int cxusb_power_ctrl(struct dvb_usb_device *d, int onoff) +static int _cxusb_power_ctrl(struct dvb_usb_device *d, int onoff) { u8 b = 0; + + deb_info("setting power %s\n", onoff ? "ON" : "OFF"); + if (onoff) return cxusb_ctrl_msg(d, CMD_POWER_ON, &b, 1, NULL, 0); else return cxusb_ctrl_msg(d, CMD_POWER_OFF, &b, 1, NULL, 0); } +static int cxusb_power_ctrl(struct dvb_usb_device *d, int onoff) +{ + bool is_medion = d->props.devices[0].warm_ids[0] == &cxusb_table[MEDION_MD95700]; + int ret; + + if (is_medion && !onoff) { + struct cxusb_medion_dev *cxdev = d->priv; + + mutex_lock(&cxdev->open_lock); + + if (cxdev->open_type == CXUSB_OPEN_ANALOG) { + deb_info("preventing DVB core from setting power OFF while we are in analog mode\n"); + ret = -EBUSY; + goto ret_unlock; + } + } + + ret = _cxusb_power_ctrl(d, onoff); + +ret_unlock: + if (is_medion && !onoff) { + struct cxusb_medion_dev *cxdev = d->priv; + + mutex_unlock(&cxdev->open_lock); + } + + return ret; +} + static int cxusb_aver_power_ctrl(struct dvb_usb_device *d, int onoff) { int ret; + if (!onoff) return cxusb_ctrl_msg(d, CMD_POWER_OFF, NULL, 0, NULL, 0); if (d->state == DVB_USB_STATE_INIT && usb_set_interface(d->udev, 0, 0) < 0) err("set interface failed"); - do {} while (!(ret = cxusb_ctrl_msg(d, CMD_POWER_ON, NULL, 0, NULL, 0)) && - !(ret = cxusb_ctrl_msg(d, 0x15, NULL, 0, NULL, 0)) && - !(ret = cxusb_ctrl_msg(d, 0x17, NULL, 0, NULL, 0)) && 0); + do { + /* Nothing */ + } while (!(ret = cxusb_ctrl_msg(d, CMD_POWER_ON, NULL, 0, NULL, 0)) && + !(ret = cxusb_ctrl_msg(d, 0x15, NULL, 0, NULL, 0)) && + !(ret = cxusb_ctrl_msg(d, 0x17, NULL, 0, NULL, 0)) && 0); + if (!ret) { - /* FIXME: We don't know why, but we need to configure the - * lgdt3303 with the register settings below on resume */ + /* + * FIXME: We don't know why, but we need to configure the + * lgdt3303 with the register settings below on resume + */ int i; u8 buf; static const u8 bufs[] = { @@ -304,7 +374,7 @@ static int cxusb_aver_power_ctrl(struct dvb_usb_device *d, int onoff) msleep(20); for (i = 0; i < ARRAY_SIZE(bufs); i += 4 / sizeof(u8)) { ret = cxusb_ctrl_msg(d, CMD_I2C_WRITE, - bufs+i, 4, &buf, 1); + bufs + i, 4, &buf, 1); if (ret) break; if (buf != 0x8) @@ -317,6 +387,7 @@ static int cxusb_aver_power_ctrl(struct dvb_usb_device *d, int onoff) static int cxusb_bluebird_power_ctrl(struct dvb_usb_device *d, int onoff) { u8 b = 0; + if (onoff) return cxusb_ctrl_msg(d, CMD_POWER_ON, &b, 1, NULL, 0); else @@ -338,6 +409,7 @@ static int cxusb_d680_dmb_power_ctrl(struct dvb_usb_device *d, int onoff) { int ret; u8 b; + ret = cxusb_power_ctrl(d, onoff); if (!onoff) return ret; @@ -350,11 +422,26 @@ static int cxusb_d680_dmb_power_ctrl(struct dvb_usb_device *d, int onoff) static int cxusb_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) { + struct dvb_usb_device *dvbdev = adap->dev; + bool is_medion = dvbdev->props.devices[0].warm_ids[0] == + &cxusb_table[MEDION_MD95700]; u8 buf[2] = { 0x03, 0x00 }; + + if (is_medion && onoff) { + int ret; + + ret = cxusb_medion_get(dvbdev, CXUSB_OPEN_DIGITAL); + if (ret != 0) + return ret; + } + if (onoff) - cxusb_ctrl_msg(adap->dev, CMD_STREAMING_ON, buf, 2, NULL, 0); + cxusb_ctrl_msg(dvbdev, CMD_STREAMING_ON, buf, 2, NULL, 0); else - cxusb_ctrl_msg(adap->dev, CMD_STREAMING_OFF, NULL, 0, NULL, 0); + cxusb_ctrl_msg(dvbdev, CMD_STREAMING_OFF, NULL, 0, NULL, 0); + + if (is_medion && !onoff) + cxusb_medion_put(dvbdev); return 0; } @@ -370,7 +457,7 @@ static int cxusb_aver_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) } static int cxusb_read_status(struct dvb_frontend *fe, - enum fe_status *status) + enum fe_status *status) { struct dvb_usb_adapter *adap = (struct dvb_usb_adapter *)fe->dvb->priv; struct cxusb_state *state = (struct cxusb_state *)adap->dev->priv; @@ -403,8 +490,8 @@ static void cxusb_d680_dmb_drain_message(struct dvb_usb_device *d) return; while (1) { if (usb_bulk_msg(d->udev, - usb_rcvbulkpipe(d->udev, ep), - junk, junk_len, &rd_count, timeout) < 0) + usb_rcvbulkpipe(d->udev, ep), + junk, junk_len, &rd_count, timeout) < 0) break; if (!rd_count) break; @@ -426,8 +513,8 @@ static void cxusb_d680_dmb_drain_video(struct dvb_usb_device *d) return; while (1) { if (usb_bulk_msg(d->udev, - usb_rcvbulkpipe(d->udev, p->endpoint), - junk, junk_len, &rd_count, timeout) < 0) + usb_rcvbulkpipe(d->udev, p->endpoint), + junk, junk_len, &rd_count, timeout) < 0) break; if (!rd_count) break; @@ -435,17 +522,18 @@ static void cxusb_d680_dmb_drain_video(struct dvb_usb_device *d) kfree(junk); } -static int cxusb_d680_dmb_streaming_ctrl( - struct dvb_usb_adapter *adap, int onoff) +static int cxusb_d680_dmb_streaming_ctrl(struct dvb_usb_adapter *adap, + int onoff) { if (onoff) { u8 buf[2] = { 0x03, 0x00 }; + cxusb_d680_dmb_drain_video(adap->dev); return cxusb_ctrl_msg(adap->dev, CMD_STREAMING_ON, - buf, sizeof(buf), NULL, 0); + buf, sizeof(buf), NULL, 0); } else { int ret = cxusb_ctrl_msg(adap->dev, - CMD_STREAMING_OFF, NULL, 0, NULL, 0); + CMD_STREAMING_OFF, NULL, 0, NULL, 0); return ret; } } @@ -465,8 +553,12 @@ static int cxusb_rc_query(struct dvb_usb_device *d) static int cxusb_bluebird2_rc_query(struct dvb_usb_device *d) { u8 ircode[4]; - struct i2c_msg msg = { .addr = 0x6b, .flags = I2C_M_RD, - .buf = ircode, .len = 4 }; + struct i2c_msg msg = { + .addr = 0x6b, + .flags = I2C_M_RD, + .buf = ircode, + .len = 4 + }; if (cxusb_i2c_xfer(&d->i2c_adap, &msg, 1) != 1) return 0; @@ -490,13 +582,13 @@ static int cxusb_d680_dmb_rc_query(struct dvb_usb_device *d) return 0; } -static int cxusb_dee1601_demod_init(struct dvb_frontend* fe) +static int cxusb_dee1601_demod_init(struct dvb_frontend *fe) { - static u8 clock_config [] = { CLOCK_CTL, 0x38, 0x28 }; - static u8 reset [] = { RESET, 0x80 }; - static u8 adc_ctl_1_cfg [] = { ADC_CTL_1, 0x40 }; - static u8 agc_cfg [] = { AGC_TARGET, 0x28, 0x20 }; - static u8 gpp_ctl_cfg [] = { GPP_CTL, 0x33 }; + static u8 clock_config[] = { CLOCK_CTL, 0x38, 0x28 }; + static u8 reset[] = { RESET, 0x80 }; + static u8 adc_ctl_1_cfg[] = { ADC_CTL_1, 0x40 }; + static u8 agc_cfg[] = { AGC_TARGET, 0x28, 0x20 }; + static u8 gpp_ctl_cfg[] = { GPP_CTL, 0x33 }; static u8 capt_range_cfg[] = { CAPT_RANGE, 0x32 }; mt352_write(fe, clock_config, sizeof(clock_config)); @@ -511,13 +603,14 @@ static int cxusb_dee1601_demod_init(struct dvb_frontend* fe) return 0; } -static int cxusb_mt352_demod_init(struct dvb_frontend* fe) -{ /* used in both lgz201 and th7579 */ - static u8 clock_config [] = { CLOCK_CTL, 0x38, 0x29 }; - static u8 reset [] = { RESET, 0x80 }; - static u8 adc_ctl_1_cfg [] = { ADC_CTL_1, 0x40 }; - static u8 agc_cfg [] = { AGC_TARGET, 0x24, 0x20 }; - static u8 gpp_ctl_cfg [] = { GPP_CTL, 0x33 }; +static int cxusb_mt352_demod_init(struct dvb_frontend *fe) +{ + /* used in both lgz201 and th7579 */ + static u8 clock_config[] = { CLOCK_CTL, 0x38, 0x29 }; + static u8 reset[] = { RESET, 0x80 }; + static u8 adc_ctl_1_cfg[] = { ADC_CTL_1, 0x40 }; + static u8 agc_cfg[] = { AGC_TARGET, 0x24, 0x20 }; + static u8 gpp_ctl_cfg[] = { GPP_CTL, 0x33 }; static u8 capt_range_cfg[] = { CAPT_RANGE, 0x32 }; mt352_write(fe, clock_config, sizeof(clock_config)); @@ -627,9 +720,21 @@ static struct max2165_config mygica_d689_max2165_cfg = { /* Callbacks for DVB USB */ static int cxusb_fmd1216me_tuner_attach(struct dvb_usb_adapter *adap) { + struct dvb_usb_device *dvbdev = adap->dev; + bool is_medion = dvbdev->props.devices[0].warm_ids[0] == + &cxusb_table[MEDION_MD95700]; + dvb_attach(simple_tuner_attach, adap->fe_adap[0].fe, - &adap->dev->i2c_adap, 0x61, + &dvbdev->i2c_adap, 0x61, TUNER_PHILIPS_FMD1216ME_MK3); + + if (is_medion && adap->fe_adap[0].fe) + /* + * make sure that DVB core won't put to sleep (reset, really) + * tuner when we might be open in analog mode + */ + adap->fe_adap[0].fe->ops.tuner_ops.sleep = NULL; + return 0; } @@ -642,7 +747,8 @@ static int cxusb_dee1601_tuner_attach(struct dvb_usb_adapter *adap) static int cxusb_lgz201_tuner_attach(struct dvb_usb_adapter *adap) { - dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x61, NULL, DVB_PLL_LG_Z201); + dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x61, + NULL, DVB_PLL_LG_Z201); return 0; } @@ -702,7 +808,7 @@ static int cxusb_dvico_xc3028_tuner_attach(struct dvb_usb_adapter *adap) adap->fe_adap[0].fe->callback = dvico_bluebird_xc2028_callback; fe = dvb_attach(xc2028_attach, adap->fe_adap[0].fe, &cfg); - if (fe == NULL || fe->ops.tuner_ops.set_config == NULL) + if (!fe || !fe->ops.tuner_ops.set_config) return -EIO; fe->ops.tuner_ops.set_config(fe, &ctl); @@ -720,33 +826,120 @@ static int cxusb_mxl5003s_tuner_attach(struct dvb_usb_adapter *adap) static int cxusb_d680_dmb_tuner_attach(struct dvb_usb_adapter *adap) { struct dvb_frontend *fe; + fe = dvb_attach(mxl5005s_attach, adap->fe_adap[0].fe, &adap->dev->i2c_adap, &d680_dmb_tuner); - return (fe == NULL) ? -EIO : 0; + return (!fe) ? -EIO : 0; } static int cxusb_mygica_d689_tuner_attach(struct dvb_usb_adapter *adap) { struct dvb_frontend *fe; + fe = dvb_attach(max2165_attach, adap->fe_adap[0].fe, &adap->dev->i2c_adap, &mygica_d689_max2165_cfg); - return (fe == NULL) ? -EIO : 0; + return (!fe) ? -EIO : 0; } -static int cxusb_cx22702_frontend_attach(struct dvb_usb_adapter *adap) +static int cxusb_medion_fe_ts_bus_ctrl(struct dvb_frontend *fe, int acquire) { + struct dvb_usb_adapter *adap = fe->dvb->priv; + struct dvb_usb_device *dvbdev = adap->dev; + + if (acquire) + return cxusb_medion_get(dvbdev, CXUSB_OPEN_DIGITAL); + + cxusb_medion_put(dvbdev); + + return 0; +} + +static int cxusb_medion_set_mode(struct dvb_usb_device *dvbdev, bool digital) +{ + struct cxusb_state *st = dvbdev->priv; + int ret; u8 b; - if (usb_set_interface(adap->dev->udev, 0, 6) < 0) - err("set interface failed"); + unsigned int i; - cxusb_ctrl_msg(adap->dev, CMD_DIGITAL, NULL, 0, &b, 1); + /* + * switching mode while doing an I2C transaction often causes + * the device to crash + */ + mutex_lock(&dvbdev->i2c_mutex); + + if (digital) { + ret = usb_set_interface(dvbdev->udev, 0, 6); + if (ret != 0) { + dev_err(&dvbdev->udev->dev, + "digital interface selection failed (%d)\n", + ret); + goto ret_unlock; + } + } else { + ret = usb_set_interface(dvbdev->udev, 0, 1); + if (ret != 0) { + dev_err(&dvbdev->udev->dev, + "analog interface selection failed (%d)\n", + ret); + goto ret_unlock; + } + } + + /* pipes need to be cleared after setting interface */ + ret = usb_clear_halt(dvbdev->udev, usb_rcvbulkpipe(dvbdev->udev, 1)); + if (ret != 0) + dev_warn(&dvbdev->udev->dev, + "clear halt on IN pipe failed (%d)\n", + ret); + + ret = usb_clear_halt(dvbdev->udev, usb_sndbulkpipe(dvbdev->udev, 1)); + if (ret != 0) + dev_warn(&dvbdev->udev->dev, + "clear halt on OUT pipe failed (%d)\n", + ret); + + ret = cxusb_ctrl_msg(dvbdev, digital ? CMD_DIGITAL : CMD_ANALOG, + NULL, 0, &b, 1); + if (ret != 0) { + dev_err(&dvbdev->udev->dev, "mode switch failed (%d)\n", + ret); + goto ret_unlock; + } + + /* mode switch seems to reset GPIO states */ + for (i = 0; i < ARRAY_SIZE(st->gpio_write_refresh); i++) + st->gpio_write_refresh[i] = true; + +ret_unlock: + mutex_unlock(&dvbdev->i2c_mutex); + + return ret; +} + +static int cxusb_cx22702_frontend_attach(struct dvb_usb_adapter *adap) +{ + struct dvb_usb_device *dvbdev = adap->dev; + bool is_medion = dvbdev->props.devices[0].warm_ids[0] == + &cxusb_table[MEDION_MD95700]; + + if (is_medion) { + int ret; + + ret = cxusb_medion_set_mode(dvbdev, true); + if (ret) + return ret; + } adap->fe_adap[0].fe = dvb_attach(cx22702_attach, &cxusb_cx22702_config, - &adap->dev->i2c_adap); - if ((adap->fe_adap[0].fe) != NULL) - return 0; + &dvbdev->i2c_adap); + if (!adap->fe_adap[0].fe) + return -EIO; - return -EIO; + if (is_medion) + adap->fe_adap[0].fe->ops.ts_bus_ctrl = + cxusb_medion_fe_ts_bus_ctrl; + + return 0; } static int cxusb_lgdt3303_frontend_attach(struct dvb_usb_adapter *adap) @@ -760,7 +953,7 @@ static int cxusb_lgdt3303_frontend_attach(struct dvb_usb_adapter *adap) &cxusb_lgdt3303_config, 0x0e, &adap->dev->i2c_adap); - if ((adap->fe_adap[0].fe) != NULL) + if (adap->fe_adap[0].fe) return 0; return -EIO; @@ -772,7 +965,7 @@ static int cxusb_aver_lgdt3303_frontend_attach(struct dvb_usb_adapter *adap) &cxusb_aver_lgdt3303_config, 0x0e, &adap->dev->i2c_adap); - if (adap->fe_adap[0].fe != NULL) + if (adap->fe_adap[0].fe) return 0; return -EIO; @@ -788,7 +981,7 @@ static int cxusb_mt352_frontend_attach(struct dvb_usb_adapter *adap) adap->fe_adap[0].fe = dvb_attach(mt352_attach, &cxusb_mt352_config, &adap->dev->i2c_adap); - if ((adap->fe_adap[0].fe) != NULL) + if (adap->fe_adap[0].fe) return 0; return -EIO; @@ -803,13 +996,13 @@ static int cxusb_dee1601_frontend_attach(struct dvb_usb_adapter *adap) adap->fe_adap[0].fe = dvb_attach(mt352_attach, &cxusb_dee1601_config, &adap->dev->i2c_adap); - if ((adap->fe_adap[0].fe) != NULL) + if (adap->fe_adap[0].fe) return 0; adap->fe_adap[0].fe = dvb_attach(zl10353_attach, &cxusb_zl10353_dee1601_config, &adap->dev->i2c_adap); - if ((adap->fe_adap[0].fe) != NULL) + if (adap->fe_adap[0].fe) return 0; return -EIO; @@ -819,8 +1012,12 @@ static int cxusb_dualdig4_frontend_attach(struct dvb_usb_adapter *adap) { u8 ircode[4]; int i; - struct i2c_msg msg = { .addr = 0x6b, .flags = I2C_M_RD, - .buf = ircode, .len = 4 }; + struct i2c_msg msg = { + .addr = 0x6b, + .flags = I2C_M_RD, + .buf = ircode, + .len = 4 + }; if (usb_set_interface(adap->dev->udev, 0, 1) < 0) err("set interface failed"); @@ -836,7 +1033,7 @@ static int cxusb_dualdig4_frontend_attach(struct dvb_usb_adapter *adap) dvb_attach(zl10353_attach, &cxusb_zl10353_xc3028_config_no_i2c_gate, &adap->dev->i2c_adap); - if ((adap->fe_adap[0].fe) == NULL) + if (!adap->fe_adap[0].fe) return -EIO; /* try to determine if there is no IR decoder on the I2C bus */ @@ -934,7 +1131,7 @@ static struct dib7000p_config cxusb_dualdig4_rev2_config = { }; struct dib0700_adapter_state { - int (*set_param_save)(struct dvb_frontend *); + int (*set_param_save)(struct dvb_frontend *fe); struct dib7000p_ops dib7000p_ops; }; @@ -953,14 +1150,15 @@ static int cxusb_dualdig4_rev2_frontend_attach(struct dvb_usb_adapter *adap) return -ENODEV; if (state->dib7000p_ops.i2c_enumeration(&adap->dev->i2c_adap, 1, 18, - &cxusb_dualdig4_rev2_config) < 0) { - printk(KERN_WARNING "Unable to enumerate dib7000p\n"); + &cxusb_dualdig4_rev2_config) < 0) { + pr_warn("Unable to enumerate dib7000p\n"); return -ENODEV; } - adap->fe_adap[0].fe = state->dib7000p_ops.init(&adap->dev->i2c_adap, 0x80, - &cxusb_dualdig4_rev2_config); - if (adap->fe_adap[0].fe == NULL) + adap->fe_adap[0].fe = state->dib7000p_ops.init(&adap->dev->i2c_adap, + 0x80, + &cxusb_dualdig4_rev2_config); + if (!adap->fe_adap[0].fe) return -EIO; return 0; @@ -993,11 +1191,16 @@ static int dib7070_set_param_override(struct dvb_frontend *fe) struct dib0700_adapter_state *state = adap->priv; u16 offset; - u8 band = BAND_OF_FREQUENCY(p->frequency/1000); + u8 band = BAND_OF_FREQUENCY(p->frequency / 1000); + switch (band) { - case BAND_VHF: offset = 950; break; + case BAND_VHF: + offset = 950; + break; default: - case BAND_UHF: offset = 550; break; + case BAND_UHF: + offset = 550; + break; } state->dib7000p_ops.set_wbd_ref(fe, offset + dib0070_wbd_offset(fe)); @@ -1019,7 +1222,7 @@ static int cxusb_dualdig4_rev2_tuner_attach(struct dvb_usb_adapter *adap) DIBX000_I2C_INTERFACE_TUNER, 1); if (dvb_attach(dib0070_attach, adap->fe_adap[0].fe, tun_i2c, - &dib7070p_dib0070_config) == NULL) + &dib7070p_dib0070_config) == NULL) return -ENODEV; st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params; @@ -1042,13 +1245,13 @@ static int cxusb_nano2_frontend_attach(struct dvb_usb_adapter *adap) adap->fe_adap[0].fe = dvb_attach(zl10353_attach, &cxusb_zl10353_xc3028_config, &adap->dev->i2c_adap); - if ((adap->fe_adap[0].fe) != NULL) + if (adap->fe_adap[0].fe) return 0; adap->fe_adap[0].fe = dvb_attach(mt352_attach, &cxusb_mt352_xc3028_config, &adap->dev->i2c_adap); - if ((adap->fe_adap[0].fe) != NULL) + if (adap->fe_adap[0].fe) return 0; return -EIO; @@ -1079,11 +1282,14 @@ static int cxusb_d680_dmb_frontend_attach(struct dvb_usb_adapter *adap) /* Unblock all USB pipes */ usb_clear_halt(d->udev, - usb_sndbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint)); + usb_sndbulkpipe(d->udev, + d->props.generic_bulk_ctrl_endpoint)); usb_clear_halt(d->udev, - usb_rcvbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint)); + usb_rcvbulkpipe(d->udev, + d->props.generic_bulk_ctrl_endpoint)); usb_clear_halt(d->udev, - usb_rcvbulkpipe(d->udev, d->props.adapter[0].fe[0].stream.endpoint)); + usb_rcvbulkpipe(d->udev, + d->props.adapter[0].fe[0].stream.endpoint)); /* Drain USB pipes to avoid hang after reboot */ for (n = 0; n < 5; n++) { @@ -1105,8 +1311,9 @@ static int cxusb_d680_dmb_frontend_attach(struct dvb_usb_adapter *adap) msleep(100); /* Attach frontend */ - adap->fe_adap[0].fe = dvb_attach(lgs8gxx_attach, &d680_lgs8gl5_cfg, &d->i2c_adap); - if (adap->fe_adap[0].fe == NULL) + adap->fe_adap[0].fe = dvb_attach(lgs8gxx_attach, + &d680_lgs8gl5_cfg, &d->i2c_adap); + if (!adap->fe_adap[0].fe) return -EIO; return 0; @@ -1136,12 +1343,14 @@ static int cxusb_mygica_d689_frontend_attach(struct dvb_usb_adapter *adap) /* Unblock all USB pipes */ usb_clear_halt(d->udev, - usb_sndbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint)); + usb_sndbulkpipe(d->udev, + d->props.generic_bulk_ctrl_endpoint)); usb_clear_halt(d->udev, - usb_rcvbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint)); + usb_rcvbulkpipe(d->udev, + d->props.generic_bulk_ctrl_endpoint)); usb_clear_halt(d->udev, - usb_rcvbulkpipe(d->udev, d->props.adapter[0].fe[0].stream.endpoint)); - + usb_rcvbulkpipe(d->udev, + d->props.adapter[0].fe[0].stream.endpoint)); /* Reset the tuner */ if (cxusb_d680_dmb_gpio_tuner(d, 0x07, 0) < 0) { @@ -1156,9 +1365,10 @@ static int cxusb_mygica_d689_frontend_attach(struct dvb_usb_adapter *adap) msleep(100); /* Attach frontend */ - adap->fe_adap[0].fe = dvb_attach(atbm8830_attach, &mygica_d689_atbm8830_cfg, - &d->i2c_adap); - if (adap->fe_adap[0].fe == NULL) + adap->fe_adap[0].fe = dvb_attach(atbm8830_attach, + &mygica_d689_atbm8830_cfg, + &d->i2c_adap); + if (!adap->fe_adap[0].fe) return -EIO; return 0; @@ -1181,11 +1391,14 @@ static int cxusb_mygica_t230_frontend_attach(struct dvb_usb_adapter *adap) /* Unblock all USB pipes */ usb_clear_halt(d->udev, - usb_sndbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint)); + usb_sndbulkpipe(d->udev, + d->props.generic_bulk_ctrl_endpoint)); usb_clear_halt(d->udev, - usb_rcvbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint)); + usb_rcvbulkpipe(d->udev, + d->props.generic_bulk_ctrl_endpoint)); usb_clear_halt(d->udev, - usb_rcvbulkpipe(d->udev, d->props.adapter[0].fe[0].stream.endpoint)); + usb_rcvbulkpipe(d->udev, + d->props.adapter[0].fe[0].stream.endpoint)); /* attach frontend */ si2168_config.i2c_adapter = &adapter; @@ -1198,7 +1411,7 @@ static int cxusb_mygica_t230_frontend_attach(struct dvb_usb_adapter *adap) info.platform_data = &si2168_config; request_module(info.type); client_demod = i2c_new_device(&d->i2c_adap, &info); - if (client_demod == NULL || client_demod->dev.driver == NULL) + if (!client_demod || !client_demod->dev.driver) return -ENODEV; if (!try_module_get(client_demod->dev.driver->owner)) { @@ -1218,7 +1431,7 @@ static int cxusb_mygica_t230_frontend_attach(struct dvb_usb_adapter *adap) info.platform_data = &si2157_config; request_module(info.type); client_tuner = i2c_new_device(adapter, &info); - if (client_tuner == NULL || client_tuner->dev.driver == NULL) { + if (!client_tuner || !client_tuner->dev.driver) { module_put(client_demod->dev.driver->owner); i2c_unregister_device(client_demod); return -ENODEV; @@ -1309,6 +1522,104 @@ static int bluebird_patch_dvico_firmware_download(struct usb_device *udev, return -EINVAL; } +int cxusb_medion_get(struct dvb_usb_device *dvbdev, + enum cxusb_open_type open_type) +{ + struct cxusb_medion_dev *cxdev = dvbdev->priv; + int ret = 0; + + mutex_lock(&cxdev->open_lock); + + if (WARN_ON((cxdev->open_type == CXUSB_OPEN_INIT || + cxdev->open_type == CXUSB_OPEN_NONE) && + cxdev->open_ctr != 0)) { + ret = -EINVAL; + goto ret_unlock; + } + + if (cxdev->open_type == CXUSB_OPEN_INIT) { + ret = -EAGAIN; + goto ret_unlock; + } + + if (cxdev->open_ctr == 0) { + if (cxdev->open_type != open_type) { + deb_info("will acquire and switch to %s\n", + open_type == CXUSB_OPEN_ANALOG ? + "analog" : "digital"); + + if (open_type == CXUSB_OPEN_ANALOG) { + ret = _cxusb_power_ctrl(dvbdev, 1); + if (ret != 0) + dev_warn(&dvbdev->udev->dev, + "powerup for analog switch failed (%d)\n", + ret); + + ret = cxusb_medion_set_mode(dvbdev, false); + if (ret != 0) + goto ret_unlock; + + ret = cxusb_medion_analog_init(dvbdev); + if (ret != 0) + goto ret_unlock; + } else { /* digital */ + ret = _cxusb_power_ctrl(dvbdev, 1); + if (ret != 0) + dev_warn(&dvbdev->udev->dev, + "powerup for digital switch failed (%d)\n", + ret); + + ret = cxusb_medion_set_mode(dvbdev, true); + if (ret != 0) + goto ret_unlock; + } + + cxdev->open_type = open_type; + } else { + deb_info("reacquired idle %s\n", + open_type == CXUSB_OPEN_ANALOG ? + "analog" : "digital"); + } + + cxdev->open_ctr = 1; + } else if (cxdev->open_type == open_type) { + cxdev->open_ctr++; + deb_info("acquired %s\n", open_type == CXUSB_OPEN_ANALOG ? + "analog" : "digital"); + } else { + ret = -EBUSY; + } + +ret_unlock: + mutex_unlock(&cxdev->open_lock); + + return ret; +} + +void cxusb_medion_put(struct dvb_usb_device *dvbdev) +{ + struct cxusb_medion_dev *cxdev = dvbdev->priv; + + mutex_lock(&cxdev->open_lock); + + if (cxdev->open_type == CXUSB_OPEN_INIT) { + WARN_ON(cxdev->open_ctr != 0); + cxdev->open_type = CXUSB_OPEN_NONE; + goto unlock; + } + + if (!WARN_ON(cxdev->open_ctr < 1)) { + cxdev->open_ctr--; + + deb_info("release %s\n", + cxdev->open_type == CXUSB_OPEN_ANALOG ? + "analog" : "digital"); + } + +unlock: + mutex_unlock(&cxdev->open_lock); +} + /* DVB USB Driver stuff */ static struct dvb_usb_device_properties cxusb_medion_properties; static struct dvb_usb_device_properties cxusb_bluebird_lgh064f_properties; @@ -1324,41 +1635,141 @@ static struct dvb_usb_device_properties cxusb_d680_dmb_properties; static struct dvb_usb_device_properties cxusb_mygica_d689_properties; static struct dvb_usb_device_properties cxusb_mygica_t230_properties; +static int cxusb_medion_priv_init(struct dvb_usb_device *dvbdev) +{ + struct cxusb_medion_dev *cxdev = dvbdev->priv; + + cxdev->dvbdev = dvbdev; + cxdev->open_type = CXUSB_OPEN_INIT; + mutex_init(&cxdev->open_lock); + + return 0; +} + +static void cxusb_medion_priv_destroy(struct dvb_usb_device *dvbdev) +{ + struct cxusb_medion_dev *cxdev = dvbdev->priv; + + mutex_destroy(&cxdev->open_lock); +} + +static bool cxusb_medion_check_altsetting(struct usb_host_interface *as) +{ + unsigned int ctr; + + for (ctr = 0; ctr < as->desc.bNumEndpoints; ctr++) { + if ((as->endpoint[ctr].desc.bEndpointAddress & + USB_ENDPOINT_NUMBER_MASK) != 2) + continue; + + if (as->endpoint[ctr].desc.bEndpointAddress & USB_DIR_IN && + ((as->endpoint[ctr].desc.bmAttributes & + USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_ISOC)) + return true; + + break; + } + + return false; +} + +static bool cxusb_medion_check_intf(struct usb_interface *intf) +{ + unsigned int ctr; + + if (intf->num_altsetting < 2) { + dev_err(intf->usb_dev, "no alternate interface"); + + return false; + } + + for (ctr = 0; ctr < intf->num_altsetting; ctr++) { + if (intf->altsetting[ctr].desc.bAlternateSetting != 1) + continue; + + if (cxusb_medion_check_altsetting(&intf->altsetting[ctr])) + return true; + + break; + } + + dev_err(intf->usb_dev, "no iso interface"); + + return false; +} + static int cxusb_probe(struct usb_interface *intf, const struct usb_device_id *id) { - if (0 == dvb_usb_device_init(intf, &cxusb_medion_properties, - THIS_MODULE, NULL, adapter_nr) || - 0 == dvb_usb_device_init(intf, &cxusb_bluebird_lgh064f_properties, - THIS_MODULE, NULL, adapter_nr) || - 0 == dvb_usb_device_init(intf, &cxusb_bluebird_dee1601_properties, - THIS_MODULE, NULL, adapter_nr) || - 0 == dvb_usb_device_init(intf, &cxusb_bluebird_lgz201_properties, - THIS_MODULE, NULL, adapter_nr) || - 0 == dvb_usb_device_init(intf, &cxusb_bluebird_dtt7579_properties, - THIS_MODULE, NULL, adapter_nr) || - 0 == dvb_usb_device_init(intf, &cxusb_bluebird_dualdig4_properties, - THIS_MODULE, NULL, adapter_nr) || - 0 == dvb_usb_device_init(intf, &cxusb_bluebird_nano2_properties, - THIS_MODULE, NULL, adapter_nr) || - 0 == dvb_usb_device_init(intf, - &cxusb_bluebird_nano2_needsfirmware_properties, - THIS_MODULE, NULL, adapter_nr) || - 0 == dvb_usb_device_init(intf, &cxusb_aver_a868r_properties, - THIS_MODULE, NULL, adapter_nr) || - 0 == dvb_usb_device_init(intf, - &cxusb_bluebird_dualdig4_rev2_properties, - THIS_MODULE, NULL, adapter_nr) || - 0 == dvb_usb_device_init(intf, &cxusb_d680_dmb_properties, - THIS_MODULE, NULL, adapter_nr) || - 0 == dvb_usb_device_init(intf, &cxusb_mygica_d689_properties, - THIS_MODULE, NULL, adapter_nr) || - 0 == dvb_usb_device_init(intf, &cxusb_mygica_t230_properties, - THIS_MODULE, NULL, adapter_nr) || - 0) + struct dvb_usb_device *dvbdev; + int ret; + + /* Medion 95700 */ + if (!dvb_usb_device_init(intf, &cxusb_medion_properties, + THIS_MODULE, &dvbdev, adapter_nr)) { + if (!cxusb_medion_check_intf(intf)) { + ret = -ENODEV; + goto ret_uninit; + } + + _cxusb_power_ctrl(dvbdev, 1); + ret = cxusb_medion_set_mode(dvbdev, false); + if (ret) + goto ret_uninit; + + ret = cxusb_medion_register_analog(dvbdev); + + cxusb_medion_set_mode(dvbdev, true); + _cxusb_power_ctrl(dvbdev, 0); + + if (ret != 0) + goto ret_uninit; + + /* release device from INIT mode to normal operation */ + cxusb_medion_put(dvbdev); + + return 0; + } else if (!dvb_usb_device_init(intf, + &cxusb_bluebird_lgh064f_properties, + THIS_MODULE, NULL, adapter_nr) || + !dvb_usb_device_init(intf, + &cxusb_bluebird_dee1601_properties, + THIS_MODULE, NULL, adapter_nr) || + !dvb_usb_device_init(intf, + &cxusb_bluebird_lgz201_properties, + THIS_MODULE, NULL, adapter_nr) || + !dvb_usb_device_init(intf, + &cxusb_bluebird_dtt7579_properties, + THIS_MODULE, NULL, adapter_nr) || + !dvb_usb_device_init(intf, + &cxusb_bluebird_dualdig4_properties, + THIS_MODULE, NULL, adapter_nr) || + !dvb_usb_device_init(intf, + &cxusb_bluebird_nano2_properties, + THIS_MODULE, NULL, adapter_nr) || + !dvb_usb_device_init(intf, + &cxusb_bluebird_nano2_needsfirmware_properties, + THIS_MODULE, NULL, adapter_nr) || + !dvb_usb_device_init(intf, &cxusb_aver_a868r_properties, + THIS_MODULE, NULL, adapter_nr) || + !dvb_usb_device_init(intf, + &cxusb_bluebird_dualdig4_rev2_properties, + THIS_MODULE, NULL, adapter_nr) || + !dvb_usb_device_init(intf, &cxusb_d680_dmb_properties, + THIS_MODULE, NULL, adapter_nr) || + !dvb_usb_device_init(intf, &cxusb_mygica_d689_properties, + THIS_MODULE, NULL, adapter_nr) || + !dvb_usb_device_init(intf, &cxusb_mygica_t230_properties, + THIS_MODULE, NULL, adapter_nr) || + 0) return 0; return -EINVAL; + +ret_uninit: + dvb_usb_device_exit(intf); + + return ret; } static void cxusb_disconnect(struct usb_interface *intf) @@ -1367,6 +1778,9 @@ static void cxusb_disconnect(struct usb_interface *intf) struct cxusb_state *st = d->priv; struct i2c_client *client; + if (d->props.devices[0].warm_ids[0] == &cxusb_table[MEDION_MD95700]) + cxusb_medion_unregister_analog(d); + /* remove I2C client for tuner */ client = st->i2c_client_tuner; if (client) { @@ -1384,31 +1798,6 @@ static void cxusb_disconnect(struct usb_interface *intf) dvb_usb_device_exit(intf); } -enum cxusb_table_index { - MEDION_MD95700, - DVICO_BLUEBIRD_LG064F_COLD, - DVICO_BLUEBIRD_LG064F_WARM, - DVICO_BLUEBIRD_DUAL_1_COLD, - DVICO_BLUEBIRD_DUAL_1_WARM, - DVICO_BLUEBIRD_LGZ201_COLD, - DVICO_BLUEBIRD_LGZ201_WARM, - DVICO_BLUEBIRD_TH7579_COLD, - DVICO_BLUEBIRD_TH7579_WARM, - DIGITALNOW_BLUEBIRD_DUAL_1_COLD, - DIGITALNOW_BLUEBIRD_DUAL_1_WARM, - DVICO_BLUEBIRD_DUAL_2_COLD, - DVICO_BLUEBIRD_DUAL_2_WARM, - DVICO_BLUEBIRD_DUAL_4, - DVICO_BLUEBIRD_DVB_T_NANO_2, - DVICO_BLUEBIRD_DVB_T_NANO_2_NFW_WARM, - AVERMEDIA_VOLAR_A868R, - DVICO_BLUEBIRD_DUAL_4_REV_2, - CONEXANT_D680_DMB, - MYGICA_D689, - MYGICA_T230, - NR__cxusb_table_index -}; - static struct usb_device_id cxusb_table[NR__cxusb_table_index + 1] = { [MEDION_MD95700] = { USB_DEVICE(USB_VID_MEDION, USB_PID_MEDION_MD95700) @@ -1438,10 +1827,12 @@ static struct usb_device_id cxusb_table[NR__cxusb_table_index + 1] = { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_TH7579_WARM) }, [DIGITALNOW_BLUEBIRD_DUAL_1_COLD] = { - USB_DEVICE(USB_VID_DVICO, USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_COLD) + USB_DEVICE(USB_VID_DVICO, + USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_COLD) }, [DIGITALNOW_BLUEBIRD_DUAL_1_WARM] = { - USB_DEVICE(USB_VID_DVICO, USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_WARM) + USB_DEVICE(USB_VID_DVICO, + USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_WARM) }, [DVICO_BLUEBIRD_DUAL_2_COLD] = { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_2_COLD) @@ -1456,7 +1847,8 @@ static struct usb_device_id cxusb_table[NR__cxusb_table_index + 1] = { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DVB_T_NANO_2) }, [DVICO_BLUEBIRD_DVB_T_NANO_2_NFW_WARM] = { - USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DVB_T_NANO_2_NFW_WARM) + USB_DEVICE(USB_VID_DVICO, + USB_PID_DVICO_BLUEBIRD_DVB_T_NANO_2_NFW_WARM) }, [AVERMEDIA_VOLAR_A868R] = { USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR_A868R) @@ -1475,14 +1867,16 @@ static struct usb_device_id cxusb_table[NR__cxusb_table_index + 1] = { }, {} /* Terminating entry */ }; -MODULE_DEVICE_TABLE (usb, cxusb_table); +MODULE_DEVICE_TABLE(usb, cxusb_table); static struct dvb_usb_device_properties cxusb_medion_properties = { .caps = DVB_USB_IS_AN_I2C_ADAPTER, .usb_ctrl = CYPRESS_FX2, - .size_of_priv = sizeof(struct cxusb_state), + .size_of_priv = sizeof(struct cxusb_medion_dev), + .priv_init = cxusb_medion_priv_init, + .priv_destroy = cxusb_medion_priv_destroy, .num_adapters = 1, .adapter = { @@ -1503,7 +1897,7 @@ static struct dvb_usb_device_properties cxusb_medion_properties = { } } }, - }}, + } }, }, }, .power_ctrl = cxusb_power_ctrl, @@ -1514,7 +1908,8 @@ static struct dvb_usb_device_properties cxusb_medion_properties = { .num_device_descs = 1, .devices = { - { "Medion MD95700 (MDUSBTV-HYBRID)", + { + "Medion MD95700 (MDUSBTV-HYBRID)", { NULL }, { &cxusb_table[MEDION_MD95700], NULL }, }, @@ -1527,8 +1922,10 @@ static struct dvb_usb_device_properties cxusb_bluebird_lgh064f_properties = { .usb_ctrl = DEVICE_SPECIFIC, .firmware = "dvb-usb-bluebird-01.fw", .download_firmware = bluebird_patch_dvico_firmware_download, - /* use usb alt setting 0 for EP4 transfer (dvb-t), - use usb alt setting 7 for EP2 transfer (atsc) */ + /* + * use usb alt setting 0 for EP4 transfer (dvb-t), + * use usb alt setting 7 for EP2 transfer (atsc) + */ .size_of_priv = sizeof(struct cxusb_state), @@ -1552,7 +1949,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_lgh064f_properties = { } } }, - }}, + } }, }, }, @@ -1585,8 +1982,10 @@ static struct dvb_usb_device_properties cxusb_bluebird_dee1601_properties = { .usb_ctrl = DEVICE_SPECIFIC, .firmware = "dvb-usb-bluebird-01.fw", .download_firmware = bluebird_patch_dvico_firmware_download, - /* use usb alt setting 0 for EP4 transfer (dvb-t), - use usb alt setting 7 for EP2 transfer (atsc) */ + /* + * use usb alt setting 0 for EP4 transfer (dvb-t), + * use usb alt setting 7 for EP2 transfer (atsc) + */ .size_of_priv = sizeof(struct cxusb_state), @@ -1609,7 +2008,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_dee1601_properties = { } } }, - }}, + } }, }, }, @@ -1634,7 +2033,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_dee1601_properties = { { &cxusb_table[DVICO_BLUEBIRD_DUAL_1_WARM], NULL }, }, { "DigitalNow DVB-T Dual USB", - { &cxusb_table[DIGITALNOW_BLUEBIRD_DUAL_1_COLD], NULL }, + { &cxusb_table[DIGITALNOW_BLUEBIRD_DUAL_1_COLD], NULL }, { &cxusb_table[DIGITALNOW_BLUEBIRD_DUAL_1_WARM], NULL }, }, { "DViCO FusionHDTV DVB-T Dual Digital 2", @@ -1650,8 +2049,10 @@ static struct dvb_usb_device_properties cxusb_bluebird_lgz201_properties = { .usb_ctrl = DEVICE_SPECIFIC, .firmware = "dvb-usb-bluebird-01.fw", .download_firmware = bluebird_patch_dvico_firmware_download, - /* use usb alt setting 0 for EP4 transfer (dvb-t), - use usb alt setting 7 for EP2 transfer (atsc) */ + /* + * use usb alt setting 0 for EP4 transfer (dvb-t), + * use usb alt setting 7 for EP2 transfer (atsc) + */ .size_of_priv = sizeof(struct cxusb_state), @@ -1675,7 +2076,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_lgz201_properties = { } } }, - }}, + } }, }, }, .power_ctrl = cxusb_bluebird_power_ctrl, @@ -1706,8 +2107,11 @@ static struct dvb_usb_device_properties cxusb_bluebird_dtt7579_properties = { .usb_ctrl = DEVICE_SPECIFIC, .firmware = "dvb-usb-bluebird-01.fw", .download_firmware = bluebird_patch_dvico_firmware_download, - /* use usb alt setting 0 for EP4 transfer (dvb-t), - use usb alt setting 7 for EP2 transfer (atsc) */ + + /* + * use usb alt setting 0 for EP4 transfer (dvb-t), + * use usb alt setting 7 for EP2 transfer (atsc) + */ .size_of_priv = sizeof(struct cxusb_state), @@ -1731,7 +2135,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_dtt7579_properties = { } } }, - }}, + } }, }, }, .power_ctrl = cxusb_bluebird_power_ctrl, @@ -1783,7 +2187,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_dualdig4_properties = { } } }, - }}, + } }, }, }, @@ -1837,7 +2241,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_nano2_properties = { } } }, - }}, + } }, }, }, @@ -1864,7 +2268,8 @@ static struct dvb_usb_device_properties cxusb_bluebird_nano2_properties = { } }; -static struct dvb_usb_device_properties cxusb_bluebird_nano2_needsfirmware_properties = { +static struct dvb_usb_device_properties +cxusb_bluebird_nano2_needsfirmware_properties = { .caps = DVB_USB_IS_AN_I2C_ADAPTER, .usb_ctrl = DEVICE_SPECIFIC, @@ -1893,7 +2298,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_nano2_needsfirmware_prope } } }, - }}, + } }, }, }, @@ -1912,10 +2317,11 @@ static struct dvb_usb_device_properties cxusb_bluebird_nano2_needsfirmware_prope }, .num_device_descs = 1, - .devices = { - { "DViCO FusionHDTV DVB-T NANO2 w/o firmware", + .devices = { { + "DViCO FusionHDTV DVB-T NANO2 w/o firmware", { &cxusb_table[DVICO_BLUEBIRD_DVB_T_NANO_2], NULL }, - { &cxusb_table[DVICO_BLUEBIRD_DVB_T_NANO_2_NFW_WARM], NULL }, + { &cxusb_table[DVICO_BLUEBIRD_DVB_T_NANO_2_NFW_WARM], + NULL }, }, } }; @@ -1946,7 +2352,7 @@ static struct dvb_usb_device_properties cxusb_aver_a868r_properties = { } } }, - }}, + } }, }, }, .power_ctrl = cxusb_aver_power_ctrl, @@ -1992,7 +2398,7 @@ struct dvb_usb_device_properties cxusb_bluebird_dualdig4_rev2_properties = { } } }, - }}, + } }, }, }, @@ -2046,7 +2452,7 @@ static struct dvb_usb_device_properties cxusb_d680_dmb_properties = { } } }, - }}, + } }, }, }, @@ -2101,7 +2507,7 @@ static struct dvb_usb_device_properties cxusb_mygica_d689_properties = { } } }, - }}, + } }, }, }, @@ -2195,6 +2601,6 @@ module_usb_driver(cxusb_driver); MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@posteo.de>"); MODULE_AUTHOR("Michael Krufky <mkrufky@linuxtv.org>"); MODULE_AUTHOR("Chris Pascoe <c.pascoe@itee.uq.edu.au>"); +MODULE_AUTHOR("Maciej S. Szmigiero <mail@maciej.szmigiero.name>"); MODULE_DESCRIPTION("Driver for Conexant USB2.0 hybrid reference design"); -MODULE_VERSION("1.0-alpha"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/usb/dvb-usb/cxusb.h b/drivers/media/usb/dvb-usb/cxusb.h index 88f9b9804b25..9e374e53125b 100644 --- a/drivers/media/usb/dvb-usb/cxusb.h +++ b/drivers/media/usb/dvb-usb/cxusb.h @@ -2,9 +2,29 @@ #ifndef _DVB_USB_CXUSB_H_ #define _DVB_USB_CXUSB_H_ +#include <linux/completion.h> +#include <linux/i2c.h> +#include <linux/list.h> +#include <linux/mutex.h> +#include <linux/usb.h> +#include <linux/workqueue.h> +#include <media/v4l2-common.h> +#include <media/v4l2-dev.h> +#include <media/v4l2-device.h> +#include <media/videobuf2-core.h> +#include <media/videobuf2-v4l2.h> + #define DVB_USB_LOG_PREFIX "cxusb" #include "dvb-usb.h" +#define CXUSB_VIDEO_URBS (5) +#define CXUSB_VIDEO_URB_MAX_SIZE (512 * 1024) + +#define CXUSB_VIDEO_PKT_SIZE 3030 +#define CXUSB_VIDEO_MAX_FRAME_PKTS 346 +#define CXUSB_VIDEO_MAX_FRAME_SIZE (CXUSB_VIDEO_MAX_FRAME_PKTS * \ + CXUSB_VIDEO_PKT_SIZE) + /* usb commands - some of it are guesses, don't have a reference yet */ #define CMD_BLUEBIRD_GPIO_RW 0x05 @@ -29,11 +49,26 @@ #define CMD_ANALOG 0x50 #define CMD_DIGITAL 0x51 +#define CXUSB_BT656_PREAMBLE ((const u8 *)"\xff\x00\x00") + +#define CXUSB_BT656_FIELD_MASK BIT(6) +#define CXUSB_BT656_FIELD_1 0 +#define CXUSB_BT656_FIELD_2 BIT(6) + +#define CXUSB_BT656_VBI_MASK BIT(5) +#define CXUSB_BT656_VBI_ON BIT(5) +#define CXUSB_BT656_VBI_OFF 0 + +#define CXUSB_BT656_SEAV_MASK BIT(4) +#define CXUSB_BT656_SEAV_EAV BIT(4) +#define CXUSB_BT656_SEAV_SAV 0 + /* Max transfer size done by I2C transfer functions */ #define MAX_XFER_SIZE 80 struct cxusb_state { u8 gpio_write_state[3]; + bool gpio_write_refresh[3]; struct i2c_client *i2c_client_demod; struct i2c_client *i2c_client_tuner; @@ -42,7 +77,128 @@ struct cxusb_state { struct mutex stream_mutex; u8 last_lock; int (*fe_read_status)(struct dvb_frontend *fe, - enum fe_status *status); + enum fe_status *status); +}; + +enum cxusb_open_type { + CXUSB_OPEN_INIT, + CXUSB_OPEN_NONE, + CXUSB_OPEN_ANALOG, + CXUSB_OPEN_DIGITAL +}; + +struct cxusb_medion_auxbuf { + u8 *buf; + unsigned int len; + unsigned int paylen; +}; + +enum cxusb_bt656_mode { + NEW_FRAME, FIRST_FIELD, SECOND_FIELD +}; + +enum cxusb_bt656_fmode { + START_SEARCH, LINE_SAMPLES, VBI_SAMPLES }; +struct cxusb_bt656_params { + enum cxusb_bt656_mode mode; + enum cxusb_bt656_fmode fmode; + unsigned int pos; + unsigned int line; + unsigned int linesamples; + u8 *buf; +}; + +struct cxusb_medion_dev { + /* has to be the first one */ + struct cxusb_state state; + + struct dvb_usb_device *dvbdev; + + enum cxusb_open_type open_type; + unsigned int open_ctr; + struct mutex open_lock; + +#ifdef CONFIG_DVB_USB_CXUSB_ANALOG + struct v4l2_device v4l2dev; + struct v4l2_subdev *cx25840; + struct v4l2_subdev *tuner; + struct v4l2_subdev *tda9887; + struct video_device *videodev, *radiodev; + struct mutex dev_lock; + + struct vb2_queue videoqueue; + u32 input; + bool stop_streaming; + u32 width, height; + u32 field_order; + struct cxusb_medion_auxbuf auxbuf; + v4l2_std_id norm; + + struct urb *streamurbs[CXUSB_VIDEO_URBS]; + unsigned long urbcomplete; + struct work_struct urbwork; + unsigned int nexturb; + + struct cxusb_bt656_params bt656; + struct cxusb_medion_vbuffer *vbuf; + __u32 vbuf_sequence; + + struct list_head buflist; + + struct completion v4l2_release; +#endif +}; + +struct cxusb_medion_vbuffer { + struct vb2_v4l2_buffer vb2; + struct list_head list; +}; + +/* defines for "debug" module parameter */ +#define CXUSB_DBG_RC BIT(0) +#define CXUSB_DBG_I2C BIT(1) +#define CXUSB_DBG_MISC BIT(2) +#define CXUSB_DBG_BT656 BIT(3) +#define CXUSB_DBG_URB BIT(4) +#define CXUSB_DBG_OPS BIT(5) +#define CXUSB_DBG_AUXB BIT(6) + +extern int dvb_usb_cxusb_debug; + +#define cxusb_vprintk(dvbdev, lvl, ...) do { \ + struct cxusb_medion_dev *_cxdev = (dvbdev)->priv; \ + if (dvb_usb_cxusb_debug & CXUSB_DBG_##lvl) \ + v4l2_printk(KERN_DEBUG, \ + &_cxdev->v4l2dev, __VA_ARGS__); \ + } while (0) + +int cxusb_ctrl_msg(struct dvb_usb_device *d, + u8 cmd, const u8 *wbuf, int wlen, u8 *rbuf, int rlen); + +#ifdef CONFIG_DVB_USB_CXUSB_ANALOG +int cxusb_medion_analog_init(struct dvb_usb_device *dvbdev); +int cxusb_medion_register_analog(struct dvb_usb_device *dvbdev); +void cxusb_medion_unregister_analog(struct dvb_usb_device *dvbdev); +#else +static inline int cxusb_medion_analog_init(struct dvb_usb_device *dvbdev) +{ + return -EINVAL; +} + +static inline int cxusb_medion_register_analog(struct dvb_usb_device *dvbdev) +{ + return 0; +} + +static inline void cxusb_medion_unregister_analog(struct dvb_usb_device *dvbdev) +{ +} +#endif + +int cxusb_medion_get(struct dvb_usb_device *dvbdev, + enum cxusb_open_type open_type); +void cxusb_medion_put(struct dvb_usb_device *dvbdev); + #endif diff --git a/drivers/media/usb/dvb-usb/dvb-usb-dvb.c b/drivers/media/usb/dvb-usb/dvb-usb-dvb.c index 8056053c9ab0..0a7f8ba90992 100644 --- a/drivers/media/usb/dvb-usb/dvb-usb-dvb.c +++ b/drivers/media/usb/dvb-usb/dvb-usb-dvb.c @@ -56,9 +56,6 @@ static int dvb_usb_ctrl_feed(struct dvb_demux_feed *dvbdmxfeed, int onoff) * for reception. */ if (adap->feedcount == onoff && adap->feedcount > 0) { - deb_ts("submitting all URBs\n"); - usb_urb_submit(&adap->fe_adap[adap->active_fe].stream); - deb_ts("controlling pid parser\n"); if (adap->props.fe[adap->active_fe].caps & DVB_USB_ADAP_HAS_PID_FILTER && adap->props.fe[adap->active_fe].caps & @@ -80,6 +77,8 @@ static int dvb_usb_ctrl_feed(struct dvb_demux_feed *dvbdmxfeed, int onoff) } } + deb_ts("submitting all URBs\n"); + usb_urb_submit(&adap->fe_adap[adap->active_fe].stream); } return 0; } diff --git a/drivers/media/usb/dvb-usb/dvb-usb-init.c b/drivers/media/usb/dvb-usb/dvb-usb-init.c index e97f6edc98de..16a0b4a359ea 100644 --- a/drivers/media/usb/dvb-usb/dvb-usb-init.c +++ b/drivers/media/usb/dvb-usb/dvb-usb-init.c @@ -130,6 +130,10 @@ static int dvb_usb_exit(struct dvb_usb_device *d) dvb_usb_i2c_exit(d); deb_info("state should be zero now: %x\n", d->state); d->state = DVB_USB_STATE_INIT; + + if (d->priv != NULL && d->props.priv_destroy != NULL) + d->props.priv_destroy(d); + kfree(d->priv); kfree(d); return 0; @@ -151,6 +155,15 @@ static int dvb_usb_init(struct dvb_usb_device *d, short *adapter_nums) err("no memory for priv in 'struct dvb_usb_device'"); return -ENOMEM; } + + if (d->props.priv_init != NULL) { + ret = d->props.priv_init(d); + if (ret != 0) { + kfree(d->priv); + d->priv = NULL; + return ret; + } + } } /* check the capabilities and set appropriate variables */ @@ -284,12 +297,15 @@ EXPORT_SYMBOL(dvb_usb_device_init); void dvb_usb_device_exit(struct usb_interface *intf) { struct dvb_usb_device *d = usb_get_intfdata(intf); - const char *name = "generic DVB-USB module"; + const char *default_name = "generic DVB-USB module"; + char name[40]; usb_set_intfdata(intf, NULL); if (d != NULL && d->desc != NULL) { - name = d->desc->name; + strscpy(name, d->desc->name, sizeof(name)); dvb_usb_exit(d); + } else { + strscpy(name, default_name, sizeof(name)); } info("%s successfully deinitialized and disconnected.", name); diff --git a/drivers/media/usb/dvb-usb/dvb-usb.h b/drivers/media/usb/dvb-usb/dvb-usb.h index 32829bdd5f22..2eb0e24e8943 100644 --- a/drivers/media/usb/dvb-usb/dvb-usb.h +++ b/drivers/media/usb/dvb-usb/dvb-usb.h @@ -129,6 +129,9 @@ struct usb_data_stream_properties { * @frontend_ctrl: called to power on/off active frontend. * @streaming_ctrl: called to start and stop the MPEG2-TS streaming of the * device (not URB submitting/killing). + * This callback will be called without data URBs being active - data URBs + * will be submitted only after streaming_ctrl(1) returns successfully and + * they will be killed before streaming_ctrl(0) gets called. * @pid_filter_ctrl: called to en/disable the PID filter, if any. * @pid_filter: called to set/unset a PID for filtering. * @frontend_attach: called to attach the possible frontends (fill fe-field @@ -234,6 +237,11 @@ enum dvb_usb_mode { * * @size_of_priv: how many bytes shall be allocated for the private field * of struct dvb_usb_device. + * @priv_init: optional callback to initialize the variable that private field + * of struct dvb_usb_device has pointer to just after it had been allocated and + * zeroed. + * @priv_destroy: just like priv_init, only called before deallocating + * the memory pointed by private field of struct dvb_usb_device. * * @power_ctrl: called to enable/disable power of the device. * @read_mac_address: called to read the MAC address of the device. @@ -275,6 +283,8 @@ struct dvb_usb_device_properties { int no_reconnect; int size_of_priv; + int (*priv_init)(struct dvb_usb_device *); + void (*priv_destroy)(struct dvb_usb_device *); int num_adapters; struct dvb_usb_adapter_properties adapter[MAX_NO_OF_ADAPTER_PER_DEVICE]; diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index d85ea1af6aa1..5aa15a7a49de 100644 --- a/drivers/media/usb/em28xx/em28xx-input.c +++ b/drivers/media/usb/em28xx/em28xx-input.c @@ -24,6 +24,7 @@ #include <linux/delay.h> #include <linux/interrupt.h> #include <linux/usb.h> +#include <linux/usb/input.h> #include <linux/slab.h> #include <linux/bitrev.h> @@ -58,7 +59,6 @@ struct em28xx_ir_poll_result { struct em28xx_IR { struct em28xx *dev; struct rc_dev *rc; - char name[32]; char phys[32]; /* poll decoder */ @@ -277,21 +277,8 @@ static int em2874_polling_getkey(struct em28xx_IR *ir, break; case RC_PROTO_BIT_NEC: - poll_result->scancode = msg[1] << 8 | msg[2]; - if ((msg[3] ^ msg[4]) != 0xff) { /* 32 bits NEC */ - poll_result->protocol = RC_PROTO_NEC32; - poll_result->scancode = RC_SCANCODE_NEC32((msg[1] << 24) | - (msg[2] << 16) | - (msg[3] << 8) | - (msg[4])); - } else if ((msg[1] ^ msg[2]) != 0xff) { /* 24 bits NEC */ - poll_result->protocol = RC_PROTO_NECX; - poll_result->scancode = RC_SCANCODE_NECX(msg[1] << 8 | - msg[2], msg[3]); - } else { /* Normal NEC */ - poll_result->protocol = RC_PROTO_NEC; - poll_result->scancode = RC_SCANCODE_NEC(msg[1], msg[3]); - } + poll_result->scancode = ir_nec_bytes_to_scancode(msg[1], msg[2], msg[3], msg[4], + &poll_result->protocol); break; case RC_PROTO_BIT_RC6_0: @@ -617,10 +604,7 @@ static int em28xx_register_snapshot_button(struct em28xx *dev) set_bit(EM28XX_SNAPSHOT_KEY, input_dev->keybit); input_dev->keycodesize = 0; input_dev->keycodemax = 0; - input_dev->id.bustype = BUS_USB; - input_dev->id.vendor = le16_to_cpu(udev->descriptor.idVendor); - input_dev->id.product = le16_to_cpu(udev->descriptor.idProduct); - input_dev->id.version = 1; + usb_to_input_id(udev, &input_dev->id); input_dev->dev.parent = &dev->intf->dev; err = input_register_device(input_dev); @@ -832,19 +816,12 @@ static int em28xx_ir_init(struct em28xx *dev) /* This is how often we ask the chip for IR information */ ir->polling = 100; /* ms */ - /* init input device */ - snprintf(ir->name, sizeof(ir->name), "%s IR", - dev_name(&dev->intf->dev)); - usb_make_path(udev, ir->phys, sizeof(ir->phys)); strlcat(ir->phys, "/input0", sizeof(ir->phys)); - rc->device_name = ir->name; + rc->device_name = em28xx_boards[dev->model].name; rc->input_phys = ir->phys; - rc->input_id.bustype = BUS_USB; - rc->input_id.version = 1; - rc->input_id.vendor = le16_to_cpu(udev->descriptor.idVendor); - rc->input_id.product = le16_to_cpu(udev->descriptor.idProduct); + usb_to_input_id(udev, &rc->input_id); rc->dev.parent = &dev->intf->dev; rc->driver_name = MODULE_NAME; diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index f43717ea831d..0512e1959394 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -1984,7 +1984,6 @@ static int vidioc_s_register(struct file *file, void *priv, static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { - struct video_device *vdev = video_devdata(file); struct em28xx *dev = video_drvdata(file); struct em28xx_v4l2 *v4l2 = dev->v4l2; struct usb_device *udev = interface_to_usbdev(dev->intf); @@ -1993,23 +1992,12 @@ static int vidioc_querycap(struct file *file, void *priv, strscpy(cap->card, em28xx_boards[dev->model].name, sizeof(cap->card)); usb_make_path(udev, cap->bus_info, sizeof(cap->bus_info)); - if (vdev->vfl_type == VFL_TYPE_GRABBER) - cap->device_caps = V4L2_CAP_READWRITE | - V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; - else if (vdev->vfl_type == VFL_TYPE_RADIO) - cap->device_caps = V4L2_CAP_RADIO; - else - cap->device_caps = V4L2_CAP_READWRITE | V4L2_CAP_VBI_CAPTURE; - + cap->capabilities = V4L2_CAP_DEVICE_CAPS | V4L2_CAP_READWRITE | + V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; if (dev->int_audio_type != EM28XX_INT_AUDIO_NONE) - cap->device_caps |= V4L2_CAP_AUDIO; - + cap->capabilities |= V4L2_CAP_AUDIO; if (dev->tuner_type != TUNER_ABSENT) - cap->device_caps |= V4L2_CAP_TUNER; - - cap->capabilities = cap->device_caps | - V4L2_CAP_DEVICE_CAPS | V4L2_CAP_READWRITE | - V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + cap->capabilities |= V4L2_CAP_TUNER; if (video_is_registered(&v4l2->vbi_dev)) cap->capabilities |= V4L2_CAP_VBI_CAPTURE; if (video_is_registered(&v4l2->radio_dev)) @@ -2782,6 +2770,13 @@ static int em28xx_v4l2_init(struct em28xx *dev) mutex_init(&v4l2->vb_vbi_queue_lock); v4l2->vdev.queue = &v4l2->vb_vidq; v4l2->vdev.queue->lock = &v4l2->vb_queue_lock; + v4l2->vdev.device_caps = V4L2_CAP_READWRITE | V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_STREAMING; + if (dev->int_audio_type != EM28XX_INT_AUDIO_NONE) + v4l2->vdev.device_caps |= V4L2_CAP_AUDIO; + if (dev->tuner_type != TUNER_ABSENT) + v4l2->vdev.device_caps |= V4L2_CAP_TUNER; + /* disable inapplicable ioctls */ if (dev->is_webcam) { @@ -2818,6 +2813,10 @@ static int em28xx_v4l2_init(struct em28xx *dev) v4l2->vbi_dev.queue = &v4l2->vb_vbiq; v4l2->vbi_dev.queue->lock = &v4l2->vb_vbi_queue_lock; + v4l2->vbi_dev.device_caps = V4L2_CAP_STREAMING | + V4L2_CAP_READWRITE | V4L2_CAP_VBI_CAPTURE; + if (dev->tuner_type != TUNER_ABSENT) + v4l2->vbi_dev.device_caps |= V4L2_CAP_TUNER; /* disable inapplicable ioctls */ v4l2_disable_ioctl(&v4l2->vbi_dev, VIDIOC_S_PARM); @@ -2845,6 +2844,7 @@ static int em28xx_v4l2_init(struct em28xx *dev) if (em28xx_boards[dev->model].radio.type == EM28XX_RADIO) { em28xx_vdev_init(dev, &v4l2->radio_dev, &em28xx_radio_template, "radio"); + v4l2->radio_dev.device_caps = V4L2_CAP_RADIO | V4L2_CAP_TUNER; ret = video_register_device(&v4l2->radio_dev, VFL_TYPE_RADIO, radio_nr[dev->devno]); if (ret < 0) { diff --git a/drivers/media/usb/go7007/go7007-v4l2.c b/drivers/media/usb/go7007/go7007-v4l2.c index b63b7bb7745c..88edfef80b40 100644 --- a/drivers/media/usb/go7007/go7007-v4l2.c +++ b/drivers/media/usb/go7007/go7007-v4l2.c @@ -279,15 +279,6 @@ static int vidioc_querycap(struct file *file, void *priv, strscpy(cap->driver, "go7007", sizeof(cap->driver)); strscpy(cap->card, go->name, sizeof(cap->card)); strscpy(cap->bus_info, go->bus_info, sizeof(cap->bus_info)); - - cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING; - - if (go->board_info->num_aud_inputs) - cap->device_caps |= V4L2_CAP_AUDIO; - if (go->board_info->flags & GO7007_BOARD_HAS_TUNER) - cap->device_caps |= V4L2_CAP_TUNER; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -1114,6 +1105,12 @@ int go7007_v4l2_init(struct go7007 *go) *vdev = go7007_template; vdev->lock = &go->serialize_lock; vdev->queue = &go->vidq; + vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING; + if (go->board_info->num_aud_inputs) + vdev->device_caps |= V4L2_CAP_AUDIO; + if (go->board_info->flags & GO7007_BOARD_HAS_TUNER) + vdev->device_caps |= V4L2_CAP_TUNER; video_set_drvdata(vdev, go); vdev->v4l2_dev = &go->v4l2_dev; if (!v4l2_device_has_op(&go->v4l2_dev, 0, video, querystd)) diff --git a/drivers/media/usb/gspca/gspca.c b/drivers/media/usb/gspca/gspca.c index a7ed5257cdba..be11f7830bca 100644 --- a/drivers/media/usb/gspca/gspca.c +++ b/drivers/media/usb/gspca/gspca.c @@ -1209,10 +1209,6 @@ static int vidioc_querycap(struct file *file, void *priv, } usb_make_path(gspca_dev->dev, (char *) cap->bus_info, sizeof(cap->bus_info)); - cap->device_caps = V4L2_CAP_VIDEO_CAPTURE - | V4L2_CAP_STREAMING - | V4L2_CAP_READWRITE; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -1508,6 +1504,8 @@ int gspca_dev_probe2(struct usb_interface *intf, gspca_dev->empty_packet = -1; /* don't check the empty packets */ gspca_dev->vdev = gspca_template; gspca_dev->vdev.v4l2_dev = &gspca_dev->v4l2_dev; + gspca_dev->vdev.device_caps = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; video_set_drvdata(&gspca_dev->vdev, gspca_dev); gspca_dev->module = module; diff --git a/drivers/media/usb/hackrf/hackrf.c b/drivers/media/usb/hackrf/hackrf.c index 7d4a9452f545..cec841ad7495 100644 --- a/drivers/media/usb/hackrf/hackrf.c +++ b/drivers/media/usb/hackrf/hackrf.c @@ -896,19 +896,13 @@ static int hackrf_querycap(struct file *file, void *fh, { struct hackrf_dev *dev = video_drvdata(file); struct usb_interface *intf = dev->intf; - struct video_device *vdev = video_devdata(file); dev_dbg(&intf->dev, "\n"); - cap->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; - if (vdev->vfl_dir == VFL_DIR_RX) - cap->device_caps |= V4L2_CAP_SDR_CAPTURE | V4L2_CAP_TUNER; - else - cap->device_caps |= V4L2_CAP_SDR_OUTPUT | V4L2_CAP_MODULATOR; - cap->capabilities = V4L2_CAP_SDR_CAPTURE | V4L2_CAP_TUNER | V4L2_CAP_SDR_OUTPUT | V4L2_CAP_MODULATOR | - V4L2_CAP_DEVICE_CAPS | cap->device_caps; + V4L2_CAP_STREAMING | V4L2_CAP_READWRITE | + V4L2_CAP_DEVICE_CAPS; strscpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver)); strscpy(cap->card, dev->rx_vdev.name, sizeof(cap->card)); usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); @@ -1487,6 +1481,8 @@ static int hackrf_probe(struct usb_interface *intf, dev->rx_vdev.ctrl_handler = &dev->rx_ctrl_handler; dev->rx_vdev.lock = &dev->v4l2_lock; dev->rx_vdev.vfl_dir = VFL_DIR_RX; + dev->rx_vdev.device_caps = V4L2_CAP_STREAMING | V4L2_CAP_READWRITE | + V4L2_CAP_SDR_CAPTURE | V4L2_CAP_TUNER; video_set_drvdata(&dev->rx_vdev, dev); ret = video_register_device(&dev->rx_vdev, VFL_TYPE_SDR, -1); if (ret) { @@ -1505,6 +1501,8 @@ static int hackrf_probe(struct usb_interface *intf, dev->tx_vdev.ctrl_handler = &dev->tx_ctrl_handler; dev->tx_vdev.lock = &dev->v4l2_lock; dev->tx_vdev.vfl_dir = VFL_DIR_TX; + dev->tx_vdev.device_caps = V4L2_CAP_STREAMING | V4L2_CAP_READWRITE | + V4L2_CAP_SDR_OUTPUT | V4L2_CAP_MODULATOR; video_set_drvdata(&dev->tx_vdev, dev); ret = video_register_device(&dev->tx_vdev, VFL_TYPE_SDR, -1); if (ret) { diff --git a/drivers/media/usb/hdpvr/hdpvr-video.c b/drivers/media/usb/hdpvr/hdpvr-video.c index 7580fc5f2f12..5b3e67b80627 100644 --- a/drivers/media/usb/hdpvr/hdpvr-video.c +++ b/drivers/media/usb/hdpvr/hdpvr-video.c @@ -435,7 +435,7 @@ static ssize_t hdpvr_read(struct file *file, char __user *buffer, size_t count, /* wait for the first buffer */ if (!(file->f_flags & O_NONBLOCK)) { if (wait_event_interruptible(dev->wait_data, - hdpvr_get_next_buffer(dev))) + !list_empty_careful(&dev->rec_buff_list))) return -ERESTARTSYS; } @@ -461,10 +461,17 @@ static ssize_t hdpvr_read(struct file *file, char __user *buffer, size_t count, goto err; } if (!err) { - v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, - "timeout: restart streaming\n"); + v4l2_info(&dev->v4l2_dev, + "timeout: restart streaming\n"); + mutex_lock(&dev->io_mutex); hdpvr_stop_streaming(dev); - msecs_to_jiffies(4000); + mutex_unlock(&dev->io_mutex); + /* + * The FW needs about 4 seconds after streaming + * stopped before it is ready to restart + * streaming. + */ + msleep(4000); err = hdpvr_start_streaming(dev); if (err) { ret = err; @@ -577,9 +584,6 @@ static int vidioc_querycap(struct file *file, void *priv, strscpy(cap->driver, "hdpvr", sizeof(cap->driver)); strscpy(cap->card, "Hauppauge HD PVR", sizeof(cap->card)); usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); - cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_AUDIO | - V4L2_CAP_READWRITE; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -1127,9 +1131,7 @@ static void hdpvr_device_release(struct video_device *vdev) struct hdpvr_device *dev = video_get_drvdata(vdev); hdpvr_delete(dev); - mutex_lock(&dev->io_mutex); flush_work(&dev->worker); - mutex_unlock(&dev->io_mutex); v4l2_device_unregister(&dev->v4l2_dev); v4l2_ctrl_handler_free(&dev->hdl); @@ -1150,6 +1152,8 @@ static const struct video_device hdpvr_video_template = { .release = hdpvr_device_release, .ioctl_ops = &hdpvr_ioctl_ops, .tvnorms = V4L2_STD_ALL, + .device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_AUDIO | + V4L2_CAP_READWRITE, }; static const struct v4l2_ctrl_ops hdpvr_ctrl_ops = { diff --git a/drivers/media/usb/msi2500/msi2500.c b/drivers/media/usb/msi2500/msi2500.c index b405bc3c2781..4c9b2a12acfb 100644 --- a/drivers/media/usb/msi2500/msi2500.c +++ b/drivers/media/usb/msi2500/msi2500.c @@ -598,9 +598,6 @@ static int msi2500_querycap(struct file *file, void *fh, strscpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver)); strscpy(cap->card, dev->vdev.name, sizeof(cap->card)); usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); - cap->device_caps = V4L2_CAP_SDR_CAPTURE | V4L2_CAP_STREAMING | - V4L2_CAP_READWRITE | V4L2_CAP_TUNER; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -1274,6 +1271,8 @@ static int msi2500_probe(struct usb_interface *intf, dev->v4l2_dev.ctrl_handler = &dev->hdl; dev->vdev.v4l2_dev = &dev->v4l2_dev; dev->vdev.lock = &dev->v4l2_lock; + dev->vdev.device_caps = V4L2_CAP_SDR_CAPTURE | V4L2_CAP_STREAMING | + V4L2_CAP_READWRITE | V4L2_CAP_TUNER; ret = video_register_device(&dev->vdev, VFL_TYPE_SDR, -1); if (ret) { diff --git a/drivers/media/usb/pvrusb2/Kconfig b/drivers/media/usb/pvrusb2/Kconfig index 64f9df067269..e6a4f730591b 100644 --- a/drivers/media/usb/pvrusb2/Kconfig +++ b/drivers/media/usb/pvrusb2/Kconfig @@ -41,6 +41,8 @@ config VIDEO_PVRUSB2_DVB select DVB_S5H1409 if MEDIA_SUBDRV_AUTOSELECT select DVB_S5H1411 if MEDIA_SUBDRV_AUTOSELECT select DVB_TDA10048 if MEDIA_SUBDRV_AUTOSELECT + select DVB_LGDT3306A if MEDIA_SUBDRV_AUTOSELECT + select DVB_SI2168 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_SIMPLE if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_TDA8290 if MEDIA_SUBDRV_AUTOSELECT diff --git a/drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.c b/drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.c index 58ca7498e119..e4b31ae02f59 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.c @@ -101,10 +101,35 @@ static const struct routing_scheme routing_defav400 = { .cnt = ARRAY_SIZE(routing_schemeav400), }; +static const struct routing_scheme_item routing_scheme160xxx[] = { + [PVR2_CVAL_INPUT_TV] = { + .vid = CX25840_COMPOSITE7, + .aud = CX25840_AUDIO8, + }, + [PVR2_CVAL_INPUT_RADIO] = { + .vid = CX25840_COMPOSITE4, + .aud = CX25840_AUDIO6, + }, + [PVR2_CVAL_INPUT_COMPOSITE] = { + .vid = CX25840_COMPOSITE3, + .aud = CX25840_AUDIO_SERIAL, + }, + [PVR2_CVAL_INPUT_SVIDEO] = { + .vid = CX25840_SVIDEO1, + .aud = CX25840_AUDIO_SERIAL, + }, +}; + +static const struct routing_scheme routing_def160xxx = { + .def = routing_scheme160xxx, + .cnt = ARRAY_SIZE(routing_scheme160xxx), +}; + static const struct routing_scheme *routing_schemes[] = { [PVR2_ROUTING_SCHEME_HAUPPAUGE] = &routing_def0, [PVR2_ROUTING_SCHEME_GOTVIEW] = &routing_defgv, [PVR2_ROUTING_SCHEME_AV400] = &routing_defav400, + [PVR2_ROUTING_SCHEME_HAUP160XXX] = &routing_def160xxx, }; void pvr2_cx25840_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) diff --git a/drivers/media/usb/pvrusb2/pvrusb2-devattr.c b/drivers/media/usb/pvrusb2/pvrusb2-devattr.c index d476c492b87e..1fcf63218885 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-devattr.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-devattr.c @@ -27,6 +27,9 @@ pvr2_device_desc structures. #include "tda18271.h" #include "tda8290.h" #include "tuner-simple.h" +#include "si2157.h" +#include "lgdt3306a.h" +#include "si2168.h" #endif @@ -178,10 +181,10 @@ static struct lgdt330x_config pvr2_lgdt3303_config = { static int pvr2_lgdt3303_attach(struct pvr2_dvb_adapter *adap) { - adap->fe = dvb_attach(lgdt330x_attach, &pvr2_lgdt3303_config, - 0x0e, - &adap->channel.hdw->i2c_adap); - if (adap->fe) + adap->fe[0] = dvb_attach(lgdt330x_attach, &pvr2_lgdt3303_config, + 0x0e, + &adap->channel.hdw->i2c_adap); + if (adap->fe[0]) return 0; return -EIO; @@ -189,7 +192,7 @@ static int pvr2_lgdt3303_attach(struct pvr2_dvb_adapter *adap) static int pvr2_lgh06xf_attach(struct pvr2_dvb_adapter *adap) { - dvb_attach(simple_tuner_attach, adap->fe, + dvb_attach(simple_tuner_attach, adap->fe[0], &adap->channel.hdw->i2c_adap, 0x61, TUNER_LG_TDVS_H06XF); @@ -238,10 +241,10 @@ static struct lgdt330x_config pvr2_lgdt3302_config = { static int pvr2_lgdt3302_attach(struct pvr2_dvb_adapter *adap) { - adap->fe = dvb_attach(lgdt330x_attach, &pvr2_lgdt3302_config, - 0x0e, - &adap->channel.hdw->i2c_adap); - if (adap->fe) + adap->fe[0] = dvb_attach(lgdt330x_attach, &pvr2_lgdt3302_config, + 0x0e, + &adap->channel.hdw->i2c_adap); + if (adap->fe[0]) return 0; return -EIO; @@ -249,7 +252,7 @@ static int pvr2_lgdt3302_attach(struct pvr2_dvb_adapter *adap) static int pvr2_fcv1236d_attach(struct pvr2_dvb_adapter *adap) { - dvb_attach(simple_tuner_attach, adap->fe, + dvb_attach(simple_tuner_attach, adap->fe[0], &adap->channel.hdw->i2c_adap, 0x61, TUNER_PHILIPS_FCV1236D); @@ -325,9 +328,9 @@ static struct tda18271_config hauppauge_tda18271_dvb_config = { static int pvr2_tda10048_attach(struct pvr2_dvb_adapter *adap) { - adap->fe = dvb_attach(tda10048_attach, &hauppauge_tda10048_config, - &adap->channel.hdw->i2c_adap); - if (adap->fe) + adap->fe[0] = dvb_attach(tda10048_attach, &hauppauge_tda10048_config, + &adap->channel.hdw->i2c_adap); + if (adap->fe[0]) return 0; return -EIO; @@ -335,10 +338,10 @@ static int pvr2_tda10048_attach(struct pvr2_dvb_adapter *adap) static int pvr2_73xxx_tda18271_8295_attach(struct pvr2_dvb_adapter *adap) { - dvb_attach(tda829x_attach, adap->fe, + dvb_attach(tda829x_attach, adap->fe[0], &adap->channel.hdw->i2c_adap, 0x42, &tda829x_no_probe); - dvb_attach(tda18271_attach, adap->fe, 0x60, + dvb_attach(tda18271_attach, adap->fe[0], 0x60, &adap->channel.hdw->i2c_adap, &hauppauge_tda18271_dvb_config); @@ -423,9 +426,9 @@ static struct tda18271_config hauppauge_tda18271_config = { static int pvr2_s5h1409_attach(struct pvr2_dvb_adapter *adap) { - adap->fe = dvb_attach(s5h1409_attach, &pvr2_s5h1409_config, - &adap->channel.hdw->i2c_adap); - if (adap->fe) + adap->fe[0] = dvb_attach(s5h1409_attach, &pvr2_s5h1409_config, + &adap->channel.hdw->i2c_adap); + if (adap->fe[0]) return 0; return -EIO; @@ -433,9 +436,9 @@ static int pvr2_s5h1409_attach(struct pvr2_dvb_adapter *adap) static int pvr2_s5h1411_attach(struct pvr2_dvb_adapter *adap) { - adap->fe = dvb_attach(s5h1411_attach, &pvr2_s5h1411_config, - &adap->channel.hdw->i2c_adap); - if (adap->fe) + adap->fe[0] = dvb_attach(s5h1411_attach, &pvr2_s5h1411_config, + &adap->channel.hdw->i2c_adap); + if (adap->fe[0]) return 0; return -EIO; @@ -443,10 +446,10 @@ static int pvr2_s5h1411_attach(struct pvr2_dvb_adapter *adap) static int pvr2_tda18271_8295_attach(struct pvr2_dvb_adapter *adap) { - dvb_attach(tda829x_attach, adap->fe, + dvb_attach(tda829x_attach, adap->fe[0], &adap->channel.hdw->i2c_adap, 0x42, &tda829x_no_probe); - dvb_attach(tda18271_attach, adap->fe, 0x60, + dvb_attach(tda18271_attach, adap->fe[0], 0x60, &adap->channel.hdw->i2c_adap, &hauppauge_tda18271_config); @@ -515,7 +518,166 @@ static const struct pvr2_device_desc pvr2_device_751xx = { #endif }; +/*------------------------------------------------------------------------*/ +/* Hauppauge PVR-USB2 Model 160000 / 160111 -- HVR-1955 / HVR-1975 */ + +#ifdef CONFIG_VIDEO_PVRUSB2_DVB +static int pvr2_si2157_attach(struct pvr2_dvb_adapter *adap); +static int pvr2_si2168_attach(struct pvr2_dvb_adapter *adap); +static int pvr2_dual_fe_attach(struct pvr2_dvb_adapter *adap); +static int pvr2_lgdt3306a_attach(struct pvr2_dvb_adapter *adap); + +static const struct pvr2_dvb_props pvr2_160000_dvb_props = { + .frontend_attach = pvr2_dual_fe_attach, + .tuner_attach = pvr2_si2157_attach, +}; + +static const struct pvr2_dvb_props pvr2_160111_dvb_props = { + .frontend_attach = pvr2_lgdt3306a_attach, + .tuner_attach = pvr2_si2157_attach, +}; + +static int pvr2_si2157_attach(struct pvr2_dvb_adapter *adap) +{ + struct si2157_config si2157_config = {}; + + si2157_config.inversion = 1; + si2157_config.fe = adap->fe[0]; + + adap->i2c_client_tuner = dvb_module_probe("si2157", "si2177", + &adap->channel.hdw->i2c_adap, + 0x60, &si2157_config); + + if (!adap->i2c_client_tuner) + return -ENODEV; + + return 0; +} + +static int pvr2_si2168_attach(struct pvr2_dvb_adapter *adap) +{ + struct si2168_config si2168_config = {}; + struct i2c_adapter *adapter; + + pr_debug("%s()\n", __func__); + + si2168_config.fe = &adap->fe[1]; + si2168_config.i2c_adapter = &adapter; + si2168_config.ts_mode = SI2168_TS_PARALLEL; /*2, 1-serial, 2-parallel.*/ + si2168_config.ts_clock_gapped = 1; /*0-disabled, 1-enabled.*/ + si2168_config.ts_clock_inv = 0; /*0-not-invert, 1-invert*/ + si2168_config.spectral_inversion = 1; /*0-not-invert, 1-invert*/ + + adap->i2c_client_demod[1] = dvb_module_probe("si2168", NULL, + &adap->channel.hdw->i2c_adap, + 0x64, &si2168_config); + + if (!adap->i2c_client_demod[1]) + return -ENODEV; + + return 0; +} +static int pvr2_lgdt3306a_attach(struct pvr2_dvb_adapter *adap) +{ + struct lgdt3306a_config lgdt3306a_config; + struct i2c_adapter *adapter; + + pr_debug("%s()\n", __func__); + + lgdt3306a_config.fe = &adap->fe[0]; + lgdt3306a_config.i2c_adapter = &adapter; + lgdt3306a_config.deny_i2c_rptr = 1; + lgdt3306a_config.spectral_inversion = 1; + lgdt3306a_config.qam_if_khz = 4000; + lgdt3306a_config.vsb_if_khz = 3250; + lgdt3306a_config.mpeg_mode = LGDT3306A_MPEG_PARALLEL; + lgdt3306a_config.tpclk_edge = LGDT3306A_TPCLK_FALLING_EDGE; + lgdt3306a_config.tpvalid_polarity = LGDT3306A_TP_VALID_LOW; + lgdt3306a_config.xtalMHz = 25, /* demod clock MHz; 24/25 supported */ + + adap->i2c_client_demod[0] = dvb_module_probe("lgdt3306a", NULL, + &adap->channel.hdw->i2c_adap, + 0x59, &lgdt3306a_config); + + if (!adap->i2c_client_demod[0]) + return -ENODEV; + + return 0; +} + +static int pvr2_dual_fe_attach(struct pvr2_dvb_adapter *adap) +{ + pr_debug("%s()\n", __func__); + + if (pvr2_lgdt3306a_attach(adap) != 0) + return -ENODEV; + + if (pvr2_si2168_attach(adap) != 0) { + dvb_module_release(adap->i2c_client_demod[0]); + return -ENODEV; + } + + return 0; +} +#endif + +#define PVR2_FIRMWARE_160xxx "v4l-pvrusb2-160xxx-01.fw" +static const char *pvr2_fw1_names_160xxx[] = { + PVR2_FIRMWARE_160xxx, +}; + +static const struct pvr2_device_client_desc pvr2_cli_160xxx[] = { + { .module_id = PVR2_CLIENT_ID_CX25840 }, +}; + +static const struct pvr2_device_desc pvr2_device_160000 = { + .description = "WinTV HVR-1975 Model 160000", + .shortname = "160000", + .client_table.lst = pvr2_cli_160xxx, + .client_table.cnt = ARRAY_SIZE(pvr2_cli_160xxx), + .fx2_firmware.lst = pvr2_fw1_names_160xxx, + .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_160xxx), + .default_tuner_type = TUNER_ABSENT, + .flag_has_cx25840 = 1, + .flag_has_hauppauge_rom = 1, + .flag_has_analogtuner = 1, + .flag_has_composite = 1, + .flag_has_svideo = 1, + .flag_fx2_16kb = 1, + .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE, + .digital_control_scheme = PVR2_DIGITAL_SCHEME_HAUPPAUGE, + .default_std_mask = V4L2_STD_NTSC_M, + .led_scheme = PVR2_LED_SCHEME_HAUPPAUGE, + .ir_scheme = PVR2_IR_SCHEME_ZILOG, +#ifdef CONFIG_VIDEO_PVRUSB2_DVB + .dvb_props = &pvr2_160000_dvb_props, +#endif +}; + +static const struct pvr2_device_desc pvr2_device_160111 = { + .description = "WinTV HVR-1955 Model 160111", + .shortname = "160111", + .client_table.lst = pvr2_cli_160xxx, + .client_table.cnt = ARRAY_SIZE(pvr2_cli_160xxx), + .fx2_firmware.lst = pvr2_fw1_names_160xxx, + .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_160xxx), + .default_tuner_type = TUNER_ABSENT, + .flag_has_cx25840 = 1, + .flag_has_hauppauge_rom = 1, + .flag_has_analogtuner = 1, + .flag_has_composite = 1, + .flag_has_svideo = 1, + .flag_fx2_16kb = 1, + .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE, + .digital_control_scheme = PVR2_DIGITAL_SCHEME_HAUPPAUGE, + .default_std_mask = V4L2_STD_NTSC_M, + .led_scheme = PVR2_LED_SCHEME_HAUPPAUGE, + .ir_scheme = PVR2_IR_SCHEME_ZILOG, +#ifdef CONFIG_VIDEO_PVRUSB2_DVB + .dvb_props = &pvr2_160111_dvb_props, +#endif +}; /*------------------------------------------------------------------------*/ @@ -542,6 +704,10 @@ struct usb_device_id pvr2_device_table[] = { .driver_info = (kernel_ulong_t)&pvr2_device_751xx}, { USB_DEVICE(0x0ccd, 0x0039), .driver_info = (kernel_ulong_t)&pvr2_device_av400}, + { USB_DEVICE(0x2040, 0x7502), + .driver_info = (kernel_ulong_t)&pvr2_device_160111}, + { USB_DEVICE(0x2040, 0x7510), + .driver_info = (kernel_ulong_t)&pvr2_device_160000}, { } }; diff --git a/drivers/media/usb/pvrusb2/pvrusb2-devattr.h b/drivers/media/usb/pvrusb2/pvrusb2-devattr.h index ed0c129c1b3f..3c88f05d82d9 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-devattr.h +++ b/drivers/media/usb/pvrusb2/pvrusb2-devattr.h @@ -56,6 +56,7 @@ struct pvr2_string_table { #define PVR2_ROUTING_SCHEME_GOTVIEW 1 #define PVR2_ROUTING_SCHEME_ONAIR 2 #define PVR2_ROUTING_SCHEME_AV400 3 +#define PVR2_ROUTING_SCHEME_HAUP160XXX 4 #define PVR2_DIGITAL_SCHEME_NONE 0 #define PVR2_DIGITAL_SCHEME_HAUPPAUGE 1 diff --git a/drivers/media/usb/pvrusb2/pvrusb2-dvb.c b/drivers/media/usb/pvrusb2/pvrusb2-dvb.c index d8874a952418..6954584526a3 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-dvb.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-dvb.c @@ -334,26 +334,19 @@ static int pvr2_dvb_frontend_init(struct pvr2_dvb_adapter *adap) goto done; } - if ((dvb_props->frontend_attach(adap) == 0) && (adap->fe)) { - - if (dvb_register_frontend(&adap->dvb_adap, adap->fe)) { + if (dvb_props->frontend_attach(adap) == 0 && adap->fe[0]) { + if (dvb_register_frontend(&adap->dvb_adap, adap->fe[0])) { pvr2_trace(PVR2_TRACE_ERROR_LEGS, "frontend registration failed!"); - dvb_frontend_detach(adap->fe); - adap->fe = NULL; ret = -ENODEV; - goto done; + goto fail_frontend0; } + if (adap->fe[0]->ops.analog_ops.standby) + adap->fe[0]->ops.analog_ops.standby(adap->fe[0]); - if (dvb_props->tuner_attach) - dvb_props->tuner_attach(adap); - - if (adap->fe->ops.analog_ops.standby) - adap->fe->ops.analog_ops.standby(adap->fe); - - /* Ensure all frontends negotiate bus access */ - adap->fe->ops.ts_bus_ctrl = pvr2_dvb_bus_ctrl; - + pvr2_trace(PVR2_TRACE_INFO, "transferring fe[%d] ts_bus_ctrl() to pvr2_dvb_bus_ctrl()", + adap->fe[0]->id); + adap->fe[0]->ops.ts_bus_ctrl = pvr2_dvb_bus_ctrl; } else { pvr2_trace(PVR2_TRACE_ERROR_LEGS, "no frontend was attached!"); @@ -361,17 +354,74 @@ static int pvr2_dvb_frontend_init(struct pvr2_dvb_adapter *adap) return ret; } - done: + if (dvb_props->tuner_attach && dvb_props->tuner_attach(adap)) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, "tuner attach failed"); + ret = -ENODEV; + goto fail_tuner; + } + + if (adap->fe[1]) { + adap->fe[1]->id = 1; + adap->fe[1]->tuner_priv = adap->fe[0]->tuner_priv; + memcpy(&adap->fe[1]->ops.tuner_ops, + &adap->fe[0]->ops.tuner_ops, + sizeof(struct dvb_tuner_ops)); + + if (dvb_register_frontend(&adap->dvb_adap, adap->fe[1])) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "frontend registration failed!"); + ret = -ENODEV; + goto fail_frontend1; + } + /* MFE lock */ + adap->dvb_adap.mfe_shared = 1; + + if (adap->fe[1]->ops.analog_ops.standby) + adap->fe[1]->ops.analog_ops.standby(adap->fe[1]); + + pvr2_trace(PVR2_TRACE_INFO, "transferring fe[%d] ts_bus_ctrl() to pvr2_dvb_bus_ctrl()", + adap->fe[1]->id); + adap->fe[1]->ops.ts_bus_ctrl = pvr2_dvb_bus_ctrl; + } +done: pvr2_channel_limit_inputs(&adap->channel, 0); return ret; + +fail_frontend1: + dvb_frontend_detach(adap->fe[1]); + adap->fe[1] = NULL; +fail_tuner: + dvb_unregister_frontend(adap->fe[0]); +fail_frontend0: + dvb_frontend_detach(adap->fe[0]); + adap->fe[0] = NULL; + dvb_module_release(adap->i2c_client_tuner); + dvb_module_release(adap->i2c_client_demod[1]); + dvb_module_release(adap->i2c_client_demod[0]); + + return ret; } static int pvr2_dvb_frontend_exit(struct pvr2_dvb_adapter *adap) { - if (adap->fe != NULL) { - dvb_unregister_frontend(adap->fe); - dvb_frontend_detach(adap->fe); + if (adap->fe[1]) { + dvb_unregister_frontend(adap->fe[1]); + dvb_frontend_detach(adap->fe[1]); + adap->fe[1] = NULL; + } + if (adap->fe[0]) { + dvb_unregister_frontend(adap->fe[0]); + dvb_frontend_detach(adap->fe[0]); + adap->fe[0] = NULL; } + + dvb_module_release(adap->i2c_client_tuner); + adap->i2c_client_tuner = NULL; + dvb_module_release(adap->i2c_client_demod[1]); + adap->i2c_client_demod[1] = NULL; + dvb_module_release(adap->i2c_client_demod[0]); + adap->i2c_client_demod[0] = NULL; + return 0; } diff --git a/drivers/media/usb/pvrusb2/pvrusb2-dvb.h b/drivers/media/usb/pvrusb2/pvrusb2-dvb.h index e7f71fb94a6e..c0b27f5211bf 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-dvb.h +++ b/drivers/media/usb/pvrusb2/pvrusb2-dvb.h @@ -18,7 +18,10 @@ struct pvr2_dvb_adapter { struct dmxdev dmxdev; struct dvb_demux demux; struct dvb_net dvb_net; - struct dvb_frontend *fe; + struct dvb_frontend *fe[2]; + + struct i2c_client *i2c_client_demod[2]; + struct i2c_client *i2c_client_tuner; int feedcount; int max_feed_count; diff --git a/drivers/media/usb/pvrusb2/pvrusb2-fx2-cmd.h b/drivers/media/usb/pvrusb2/pvrusb2-fx2-cmd.h index be76911335d3..e54aa42b4115 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-fx2-cmd.h +++ b/drivers/media/usb/pvrusb2/pvrusb2-fx2-cmd.h @@ -28,6 +28,10 @@ #define FX2CMD_FWPOST1 0x52u +/* These 2 only exist on Model 160xxx */ +#define FX2CMD_HCW_DEMOD_RESET_PIN 0xd4u +#define FX2CMD_HCW_MAKO_SLEEP_PIN 0xd5u + #define FX2CMD_POWER_OFF 0xdcu #define FX2CMD_POWER_ON 0xdeu diff --git a/drivers/media/usb/pvrusb2/pvrusb2-hdw.c b/drivers/media/usb/pvrusb2/pvrusb2-hdw.c index 70b5cb08d65b..6fe8b9af858a 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-hdw.c @@ -306,6 +306,8 @@ static const struct pvr2_fx2cmd_descdef pvr2_fx2cmd_desc[] = { {FX2CMD_ONAIR_DTV_STREAMING_OFF, "onair dtv stream off"}, {FX2CMD_ONAIR_DTV_POWER_ON, "onair dtv power on"}, {FX2CMD_ONAIR_DTV_POWER_OFF, "onair dtv power off"}, + {FX2CMD_HCW_DEMOD_RESET_PIN, "hcw demod reset pin"}, + {FX2CMD_HCW_MAKO_SLEEP_PIN, "hcw mako sleep pin"}, }; @@ -1670,7 +1672,7 @@ static int pvr2_decoder_enable(struct pvr2_hdw *hdw,int enablefl) } if (!hdw->flag_decoder_missed) { pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "WARNING: No decoder present"); + "***WARNING*** No decoder present"); hdw->flag_decoder_missed = !0; trace_stbit("flag_decoder_missed", hdw->flag_decoder_missed); @@ -2129,10 +2131,28 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) ((0) << 16)); } - // This step MUST happen after the earlier powerup step. + /* This step MUST happen after the earlier powerup step */ pvr2_i2c_core_init(hdw); if (!pvr2_hdw_dev_ok(hdw)) return; + /* Reset demod only on Hauppauge 160xxx platform */ + if (le16_to_cpu(hdw->usb_dev->descriptor.idVendor) == 0x2040 && + (le16_to_cpu(hdw->usb_dev->descriptor.idProduct) == 0x7502 || + le16_to_cpu(hdw->usb_dev->descriptor.idProduct) == 0x7510)) { + pr_info("%s(): resetting 160xxx demod\n", __func__); + /* TODO: not sure this is proper place to reset once only */ + pvr2_issue_simple_cmd(hdw, + FX2CMD_HCW_DEMOD_RESET_PIN | + (1 << 8) | + ((0) << 16)); + usleep_range(10000, 10500); + pvr2_issue_simple_cmd(hdw, + FX2CMD_HCW_DEMOD_RESET_PIN | + (1 << 8) | + ((1) << 16)); + usleep_range(10000, 10500); + } + pvr2_hdw_load_modules(hdw); if (!pvr2_hdw_dev_ok(hdw)) return; @@ -2356,7 +2376,7 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, if (hdw_desc->flag_is_experimental) { pvr2_trace(PVR2_TRACE_INFO, "**********"); pvr2_trace(PVR2_TRACE_INFO, - "WARNING: Support for this device (%s) is experimental.", + "***WARNING*** Support for this device (%s) is experimental.", hdw_desc->description); pvr2_trace(PVR2_TRACE_INFO, "Important functionality might not be entirely working."); @@ -4002,6 +4022,20 @@ int pvr2_hdw_cmd_decoder_reset(struct pvr2_hdw *hdw) static int pvr2_hdw_cmd_hcw_demod_reset(struct pvr2_hdw *hdw, int onoff) { hdw->flag_ok = !0; + + /* Use this for Hauppauge 160xxx only */ + if (le16_to_cpu(hdw->usb_dev->descriptor.idVendor) == 0x2040 && + (le16_to_cpu(hdw->usb_dev->descriptor.idProduct) == 0x7502 || + le16_to_cpu(hdw->usb_dev->descriptor.idProduct) == 0x7510)) { + pr_debug("%s(): resetting demod on Hauppauge 160xxx platform skipped\n", + __func__); + /* Can't reset 160xxx or it will trash Demod tristate */ + return pvr2_issue_simple_cmd(hdw, + FX2CMD_HCW_MAKO_SLEEP_PIN | + (1 << 8) | + ((onoff ? 1 : 0) << 16)); + } + return pvr2_issue_simple_cmd(hdw, FX2CMD_HCW_DEMOD_RESETIN | (1 << 8) | diff --git a/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c b/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c index 68e323f8d9cf..275394bafe7d 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c @@ -333,11 +333,11 @@ static int i2c_hack_cx25840(struct pvr2_hdw *hdw, if ((ret != 0) || (*rdata == 0x04) || (*rdata == 0x0a)) { pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "WARNING: Detected a wedged cx25840 chip; the device will not work."); + "***WARNING*** Detected a wedged cx25840 chip; the device will not work."); pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "WARNING: Try power cycling the pvrusb2 device."); + "***WARNING*** Try power cycling the pvrusb2 device."); pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "WARNING: Disabling further access to the device to prevent other foul-ups."); + "***WARNING*** Disabling further access to the device to prevent other foul-ups."); // This blocks all further communication with the part. hdw->i2c_func[0x44] = NULL; pvr2_hdw_render_useless(hdw); diff --git a/drivers/media/usb/pvrusb2/pvrusb2-std.c b/drivers/media/usb/pvrusb2/pvrusb2-std.c index 447279b4a545..e7ab41401577 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-std.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-std.c @@ -343,7 +343,7 @@ struct v4l2_standard *pvr2_std_create_enum(unsigned int *countptr, bcnt = pvr2_std_id_to_str(buf,sizeof(buf),fmsk); pvr2_trace( PVR2_TRACE_ERROR_LEGS, - "WARNING: Failed to classify the following standard(s): %.*s", + "***WARNING*** Failed to classify the following standard(s): %.*s", bcnt,buf); } diff --git a/drivers/media/usb/pvrusb2/pvrusb2-sysfs.c b/drivers/media/usb/pvrusb2/pvrusb2-sysfs.c index c5dbd5d96457..3e42e209be37 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-sysfs.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-sysfs.c @@ -792,7 +792,8 @@ struct pvr2_sysfs_class *pvr2_sysfs_class_create(void) void pvr2_sysfs_class_destroy(struct pvr2_sysfs_class *clp) { pvr2_sysfs_trace("Unregistering pvr2_sysfs_class id=%p", clp); - class_unregister(&clp->class); + if (clp) + class_unregister(&clp->class); } diff --git a/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c b/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c index aa4fbc3e88cc..0aff2f396392 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c @@ -118,17 +118,6 @@ static int pvr2_querycap(struct file *file, void *priv, struct v4l2_capability * cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | V4L2_CAP_AUDIO | V4L2_CAP_RADIO | V4L2_CAP_READWRITE | V4L2_CAP_DEVICE_CAPS; - switch (fh->pdi->devbase.vfl_type) { - case VFL_TYPE_GRABBER: - cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_AUDIO; - break; - case VFL_TYPE_RADIO: - cap->device_caps = V4L2_CAP_RADIO; - break; - default: - return -EINVAL; - } - cap->device_caps |= V4L2_CAP_TUNER | V4L2_CAP_READWRITE; return 0; } @@ -1195,6 +1184,8 @@ static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip, int unit_number; struct pvr2_hdw *hdw; int *nr_ptr = NULL; + u32 caps = V4L2_CAP_TUNER | V4L2_CAP_READWRITE; + dip->v4lp = vp; hdw = vp->channel.mc_head->hdw; @@ -1205,6 +1196,7 @@ static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip, dip->config = pvr2_config_mpeg; dip->minor_type = pvr2_v4l_type_video; nr_ptr = video_nr; + caps |= V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_AUDIO; if (!dip->stream) { pr_err(KBUILD_MODNAME ": Failed to set up pvrusb2 v4l video dev due to missing stream instance\n"); @@ -1215,12 +1207,14 @@ static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip, dip->config = pvr2_config_vbi; dip->minor_type = pvr2_v4l_type_vbi; nr_ptr = vbi_nr; + caps |= V4L2_CAP_VBI_CAPTURE; break; case VFL_TYPE_RADIO: dip->stream = &vp->channel.mc_head->video_stream; dip->config = pvr2_config_mpeg; dip->minor_type = pvr2_v4l_type_radio; nr_ptr = radio_nr; + caps |= V4L2_CAP_RADIO; break; default: /* Bail out (this should be impossible) */ @@ -1231,6 +1225,7 @@ static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip, dip->devbase = vdev_template; dip->devbase.release = pvr2_video_device_release; dip->devbase.ioctl_ops = &pvr2_ioctl_ops; + dip->devbase.device_caps = caps; { int val; pvr2_ctrl_get_value( diff --git a/drivers/media/usb/pwc/pwc-if.c b/drivers/media/usb/pwc/pwc-if.c index a15ad0f3faf1..9b76cf133d52 100644 --- a/drivers/media/usb/pwc/pwc-if.c +++ b/drivers/media/usb/pwc/pwc-if.c @@ -1113,6 +1113,8 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id pdev->v4l2_dev.ctrl_handler = &pdev->ctrl_handler; pdev->vdev.v4l2_dev = &pdev->v4l2_dev; pdev->vdev.lock = &pdev->v4l2_lock; + pdev->vdev.device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | + V4L2_CAP_READWRITE; rc = video_register_device(&pdev->vdev, VFL_TYPE_GRABBER, -1); if (rc < 0) { diff --git a/drivers/media/usb/pwc/pwc-v4l.c b/drivers/media/usb/pwc/pwc-v4l.c index 5212898db77c..76c498cccc49 100644 --- a/drivers/media/usb/pwc/pwc-v4l.c +++ b/drivers/media/usb/pwc/pwc-v4l.c @@ -483,9 +483,6 @@ static int pwc_querycap(struct file *file, void *fh, struct v4l2_capability *cap strscpy(cap->driver, PWC_NAME, sizeof(cap->driver)); strscpy(cap->card, pdev->vdev.name, sizeof(cap->card)); usb_make_path(pdev->udev, cap->bus_info, sizeof(cap->bus_info)); - cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | - V4L2_CAP_READWRITE; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } diff --git a/drivers/media/usb/pwc/pwc.h b/drivers/media/usb/pwc/pwc.h index 8aa7e868e6b1..3362962d0d00 100644 --- a/drivers/media/usb/pwc/pwc.h +++ b/drivers/media/usb/pwc/pwc.h @@ -43,15 +43,15 @@ /* Trace certain actions in the driver */ -#define PWC_DEBUG_LEVEL_MODULE (1<<0) -#define PWC_DEBUG_LEVEL_PROBE (1<<1) -#define PWC_DEBUG_LEVEL_OPEN (1<<2) -#define PWC_DEBUG_LEVEL_READ (1<<3) -#define PWC_DEBUG_LEVEL_MEMORY (1<<4) -#define PWC_DEBUG_LEVEL_FLOW (1<<5) -#define PWC_DEBUG_LEVEL_SIZE (1<<6) -#define PWC_DEBUG_LEVEL_IOCTL (1<<7) -#define PWC_DEBUG_LEVEL_TRACE (1<<8) +#define PWC_DEBUG_LEVEL_MODULE BIT(0) +#define PWC_DEBUG_LEVEL_PROBE BIT(1) +#define PWC_DEBUG_LEVEL_OPEN BIT(2) +#define PWC_DEBUG_LEVEL_READ BIT(3) +#define PWC_DEBUG_LEVEL_MEMORY BIT(4) +#define PWC_DEBUG_LEVEL_FLOW BIT(5) +#define PWC_DEBUG_LEVEL_SIZE BIT(6) +#define PWC_DEBUG_LEVEL_IOCTL BIT(7) +#define PWC_DEBUG_LEVEL_TRACE BIT(8) #define PWC_DEBUG_MODULE(fmt, args...) PWC_DEBUG(MODULE, fmt, ##args) #define PWC_DEBUG_PROBE(fmt, args...) PWC_DEBUG(PROBE, fmt, ##args) diff --git a/drivers/media/usb/s2255/Kconfig b/drivers/media/usb/s2255/Kconfig index e0e3c0ba3f23..e4a0c914d9c3 100644 --- a/drivers/media/usb/s2255/Kconfig +++ b/drivers/media/usb/s2255/Kconfig @@ -3,7 +3,6 @@ config USB_S2255 tristate "USB Sensoray 2255 video capture device" depends on VIDEO_V4L2 select VIDEOBUF2_VMALLOC - default n help Say Y here if you want support for the Sensoray 2255 USB device. This driver can be compiled as a module, called s2255drv. diff --git a/drivers/media/usb/s2255/s2255drv.c b/drivers/media/usb/s2255/s2255drv.c index 3eccbd48bdac..aa90558479f7 100644 --- a/drivers/media/usb/s2255/s2255drv.c +++ b/drivers/media/usb/s2255/s2255drv.c @@ -724,9 +724,6 @@ static int vidioc_querycap(struct file *file, void *priv, strscpy(cap->driver, "s2255", sizeof(cap->driver)); strscpy(cap->card, "s2255", sizeof(cap->card)); usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); - cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | - V4L2_CAP_READWRITE; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -1657,6 +1654,8 @@ static int s2255_probe_v4l(struct s2255_dev *dev) vc->vdev.ctrl_handler = &vc->hdl; vc->vdev.lock = &dev->lock; vc->vdev.v4l2_dev = &dev->v4l2_dev; + vc->vdev.device_caps = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; video_set_drvdata(&vc->vdev, vc); if (video_nr == -1) ret = video_register_device(&vc->vdev, diff --git a/drivers/media/usb/stk1160/stk1160-v4l.c b/drivers/media/usb/stk1160/stk1160-v4l.c index 38016632c6d8..b71a0f4b40b5 100644 --- a/drivers/media/usb/stk1160/stk1160-v4l.c +++ b/drivers/media/usb/stk1160/stk1160-v4l.c @@ -337,11 +337,6 @@ static int vidioc_querycap(struct file *file, strscpy(cap->driver, "stk1160", sizeof(cap->driver)); strscpy(cap->card, "stk1160", sizeof(cap->card)); usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); - cap->device_caps = - V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_STREAMING | - V4L2_CAP_READWRITE; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -821,6 +816,8 @@ int stk1160_video_register(struct stk1160 *dev) /* This will be used to set video_device parent */ dev->vdev.v4l2_dev = &dev->v4l2_dev; + dev->vdev.device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | + V4L2_CAP_READWRITE; /* NTSC is default */ dev->norm = V4L2_STD_NTSC_M; diff --git a/drivers/media/usb/stkwebcam/stk-webcam.c b/drivers/media/usb/stkwebcam/stk-webcam.c index cb7d6454bbe1..be8041e3e6b8 100644 --- a/drivers/media/usb/stkwebcam/stk-webcam.c +++ b/drivers/media/usb/stkwebcam/stk-webcam.c @@ -798,10 +798,6 @@ static int stk_vidioc_querycap(struct file *filp, strscpy(cap->driver, "stk", sizeof(cap->driver)); strscpy(cap->card, "stk", sizeof(cap->card)); usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); - - cap->device_caps = V4L2_CAP_VIDEO_CAPTURE - | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -1261,6 +1257,8 @@ static int stk_register_video_device(struct stk_camera *dev) dev->vdev = stk_v4l_data; dev->vdev.lock = &dev->lock; dev->vdev.v4l2_dev = &dev->v4l2_dev; + dev->vdev.device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING; video_set_drvdata(&dev->vdev, dev); err = video_register_device(&dev->vdev, VFL_TYPE_GRABBER, -1); if (err) diff --git a/drivers/media/usb/tm6000/tm6000-video.c b/drivers/media/usb/tm6000/tm6000-video.c index 072210f5f92f..85fcddfb0202 100644 --- a/drivers/media/usb/tm6000/tm6000-video.c +++ b/drivers/media/usb/tm6000/tm6000-video.c @@ -854,22 +854,17 @@ static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { struct tm6000_core *dev = ((struct tm6000_fh *)priv)->dev; - struct video_device *vdev = video_devdata(file); strscpy(cap->driver, "tm6000", sizeof(cap->driver)); strscpy(cap->card, "Trident TVMaster TM5600/6000/6010", sizeof(cap->card)); usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | + V4L2_CAP_DEVICE_CAPS; if (dev->tuner_type != TUNER_ABSENT) - cap->device_caps |= V4L2_CAP_TUNER; - if (vdev->vfl_type == VFL_TYPE_GRABBER) - cap->device_caps |= V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_STREAMING | - V4L2_CAP_READWRITE; - else - cap->device_caps |= V4L2_CAP_RADIO; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS | - V4L2_CAP_RADIO | V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE; + cap->capabilities |= V4L2_CAP_TUNER; + if (dev->caps.has_radio) + cap->capabilities |= V4L2_CAP_RADIO; return 0; } @@ -1639,6 +1634,10 @@ int tm6000_v4l2_register(struct tm6000_core *dev) vdev_init(dev, &dev->vfd, &tm6000_template, "video"); dev->vfd.ctrl_handler = &dev->ctrl_handler; + dev->vfd.device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | + V4L2_CAP_READWRITE; + if (dev->tuner_type != TUNER_ABSENT) + dev->vfd.device_caps |= V4L2_CAP_TUNER; /* init video dma queues */ INIT_LIST_HEAD(&dev->vidq.active); @@ -1659,6 +1658,7 @@ int tm6000_v4l2_register(struct tm6000_core *dev) vdev_init(dev, &dev->radio_dev, &tm6000_radio_template, "radio"); dev->radio_dev.ctrl_handler = &dev->radio_ctrl_handler; + dev->radio_dev.device_caps = V4L2_CAP_RADIO | V4L2_CAP_TUNER; ret = video_register_device(&dev->radio_dev, VFL_TYPE_RADIO, radio_nr); if (ret < 0) { diff --git a/drivers/media/usb/usbtv/usbtv-video.c b/drivers/media/usb/usbtv/usbtv-video.c index 4a1eab711bdc..51f784479e91 100644 --- a/drivers/media/usb/usbtv/usbtv-video.c +++ b/drivers/media/usb/usbtv/usbtv-video.c @@ -603,9 +603,6 @@ static int usbtv_querycap(struct file *file, void *priv, strscpy(cap->driver, "usbtv", sizeof(cap->driver)); strscpy(cap->card, "usbtv", sizeof(cap->card)); usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); - cap->device_caps = V4L2_CAP_VIDEO_CAPTURE; - cap->device_caps |= V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -942,6 +939,8 @@ int usbtv_video_init(struct usbtv *usbtv) usbtv->vdev.tvnorms = USBTV_TV_STD; usbtv->vdev.queue = &usbtv->vb2q; usbtv->vdev.lock = &usbtv->v4l2_lock; + usbtv->vdev.device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING; video_set_drvdata(&usbtv->vdev, usbtv); ret = video_register_device(&usbtv->vdev, VFL_TYPE_GRABBER, -1); if (ret < 0) { diff --git a/drivers/media/usb/usbvision/usbvision-video.c b/drivers/media/usb/usbvision/usbvision-video.c index 6d42154e3d0a..93750af82d98 100644 --- a/drivers/media/usb/usbvision/usbvision-video.c +++ b/drivers/media/usb/usbvision/usbvision-video.c @@ -452,24 +452,18 @@ static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *vc) { struct usb_usbvision *usbvision = video_drvdata(file); - struct video_device *vdev = video_devdata(file); strscpy(vc->driver, "USBVision", sizeof(vc->driver)); strscpy(vc->card, usbvision_device_data[usbvision->dev_model].model_string, sizeof(vc->card)); usb_make_path(usbvision->dev, vc->bus_info, sizeof(vc->bus_info)); - vc->device_caps = usbvision->have_tuner ? V4L2_CAP_TUNER : 0; - if (vdev->vfl_type == VFL_TYPE_GRABBER) - vc->device_caps |= V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; - else - vc->device_caps |= V4L2_CAP_RADIO; - - vc->capabilities = vc->device_caps | V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_READWRITE | V4L2_CAP_STREAMING | V4L2_CAP_DEVICE_CAPS; + vc->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING | V4L2_CAP_DEVICE_CAPS; if (usbvision_device_data[usbvision->dev_model].radio) vc->capabilities |= V4L2_CAP_RADIO; + if (usbvision->have_tuner) + vc->capabilities |= V4L2_CAP_TUNER; return 0; } @@ -1267,6 +1261,11 @@ static int usbvision_register_video(struct usb_usbvision *usbvision) v4l2_disable_ioctl(&usbvision->vdev, VIDIOC_G_FREQUENCY); v4l2_disable_ioctl(&usbvision->vdev, VIDIOC_S_TUNER); } + usbvision->vdev.device_caps = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; + if (usbvision->have_tuner) + usbvision->vdev.device_caps |= V4L2_CAP_TUNER; + if (video_register_device(&usbvision->vdev, VFL_TYPE_GRABBER, video_nr) < 0) goto err_exit; printk(KERN_INFO "USBVision[%d]: registered USBVision Video device %s [v4l2]\n", @@ -1277,6 +1276,7 @@ static int usbvision_register_video(struct usb_usbvision *usbvision) /* usbvision has radio */ usbvision_vdev_init(usbvision, &usbvision->rdev, &usbvision_radio_template, "USBVision Radio"); + usbvision->rdev.device_caps = V4L2_CAP_RADIO | V4L2_CAP_TUNER; if (video_register_device(&usbvision->rdev, VFL_TYPE_RADIO, radio_nr) < 0) goto err_exit; printk(KERN_INFO "USBVision[%d]: registered USBVision Radio device %s [v4l2]\n", diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c index 26163a5bde7d..e399b9fad757 100644 --- a/drivers/media/usb/uvc/uvc_ctrl.c +++ b/drivers/media/usb/uvc/uvc_ctrl.c @@ -2345,7 +2345,9 @@ void uvc_ctrl_cleanup_device(struct uvc_device *dev) struct uvc_entity *entity; unsigned int i; - cancel_work_sync(&dev->async_ctrl.work); + /* Can be uninitialized if we are aborting on probe error. */ + if (dev->async_ctrl.work.func) + cancel_work_sync(&dev->async_ctrl.work); /* Free controls and control mappings for all entities. */ list_for_each_entry(entity, &dev->entities, list) { diff --git a/drivers/media/usb/uvc/uvc_debugfs.c b/drivers/media/usb/uvc/uvc_debugfs.c index 8ba54139a087..d2b109959d82 100644 --- a/drivers/media/usb/uvc/uvc_debugfs.c +++ b/drivers/media/usb/uvc/uvc_debugfs.c @@ -74,12 +74,13 @@ void uvc_debugfs_init_stream(struct uvc_streaming *stream) { struct usb_device *udev = stream->dev->udev; struct dentry *dent; - char dir_name[32]; + char dir_name[33]; if (uvc_debugfs_root_dir == NULL) return; - sprintf(dir_name, "%u-%u", udev->bus->busnum, udev->devnum); + snprintf(dir_name, sizeof(dir_name), "%u-%u-%u", udev->bus->busnum, + udev->devnum, stream->intfnum); dent = debugfs_create_dir(dir_name, uvc_debugfs_root_dir); if (IS_ERR_OR_NULL(dent)) { diff --git a/drivers/media/usb/zr364xx/zr364xx.c b/drivers/media/usb/zr364xx/zr364xx.c index 37a7992585df..a9bcba4fa9c6 100644 --- a/drivers/media/usb/zr364xx/zr364xx.c +++ b/drivers/media/usb/zr364xx/zr364xx.c @@ -694,14 +694,10 @@ static int zr364xx_vidioc_querycap(struct file *file, void *priv, struct zr364xx_camera *cam = video_drvdata(file); strscpy(cap->driver, DRIVER_DESC, sizeof(cap->driver)); - strscpy(cap->card, cam->udev->product, sizeof(cap->card)); + if (cam->udev->product) + strscpy(cap->card, cam->udev->product, sizeof(cap->card)); strscpy(cap->bus_info, dev_name(&cam->udev->dev), sizeof(cap->bus_info)); - cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; - return 0; } @@ -1328,6 +1324,8 @@ static const struct video_device zr364xx_template = { .fops = &zr364xx_fops, .ioctl_ops = &zr364xx_ioctl_ops, .release = video_device_release_empty, + .device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING, }; |