Re: [PATCH v4] media: Add stk1160 new driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Em 29-06-2012 14:34, Ezequiel Garcia escreveu:
> This driver adds support for stk1160 usb bridge as used in some
> video/audio usb capture devices.
> It is a complete rewrite of staging/media/easycap driver and
> it's expected as a future replacement.
> 
> Signed-off-by: Ezequiel Garcia <elezegarcia@xxxxxxxxx>
> ---
> Hi all,
> 
> As stk1160 allows communication with an ac97 codec chip, this
> driver registers a control-only sound card to allow the user
> to access ac97 controls.
> This is achieved through snd_ac97_codec/ac97_bus drivers.
> I'm not sure about this approach so feedback is welcome.
> 
> Testing has been performed using both vlc and mplayer
> on a gentoo machine, including hot unplug and on-the-fly standard
> change using two devices:
> * 1-cvbs video and 1-audio ac97 input,
> * 4-cvbs inputs
> 
> Both of these devices reports with the same id [05e1:0408],
> so the driver tries to support a superset of the capabilities.
> 
> By using keep_buffers module parameter it's possible to prevent
> the driver from releasing urb buffers when streaming is stopped.
> The usage of this parameter can avoid memory fragmentation that may
> cause the driver to stop working on low memory systems.
> A similar mechanism is implemented in em28xx driver (see commit 86d38d).
> 
> This v4 patch is the first one sent as non-RFC, i.e. intended for inclusion.
> I consider the driver to be pretty usable, but of course: bugs happens.
> As usual, any comments/reviews of *any* kind will be greatly
> appreciated.
> 
> Thanks to Hans and Sylwester for their invaluable help,
> Ezequiel.
> 
> Changes from v3:
>   * Incorporated all review comments by Sylwester (Thanks!)
>   * Added keep_buffers module parameter. This parameter keeps the
>     driver from releasing urb buffers when streaming is stopped.
> 
> Changes from v2:
>   * Added support for multiple video input device
>   * Added ac97 initialization
>   * Register an ac97 sound card (mixer control only)
>     to access ac97 registers.
> 
> Changes from v1:
>   * Use media control framework
>   * Register video device as the last thing
>   * Use v4l_device release to release all resources
>   * Add explicit locking for file operations
>   * Add vb2 buffer sanity check
>   * Minor style cleanups
> ---
>   drivers/media/video/Kconfig                 |    2 +
>   drivers/media/video/Makefile                |    1 +
>   drivers/media/video/stk1160/Kconfig         |   12 +
>   drivers/media/video/stk1160/Makefile        |    6 +
>   drivers/media/video/stk1160/stk1160-ac97.c  |  152 +++++
>   drivers/media/video/stk1160/stk1160-core.c  |  433 +++++++++++++
>   drivers/media/video/stk1160/stk1160-i2c.c   |  294 +++++++++
>   drivers/media/video/stk1160/stk1160-reg.h   |   93 +++
>   drivers/media/video/stk1160/stk1160-v4l.c   |  923 +++++++++++++++++++++++++++
>   drivers/media/video/stk1160/stk1160-video.c |  518 +++++++++++++++
>   drivers/media/video/stk1160/stk1160.h       |  202 ++++++
>   11 files changed, 2636 insertions(+), 0 deletions(-)
>   create mode 100644 drivers/media/video/stk1160/Kconfig
>   create mode 100644 drivers/media/video/stk1160/Makefile
>   create mode 100644 drivers/media/video/stk1160/stk1160-ac97.c
>   create mode 100644 drivers/media/video/stk1160/stk1160-core.c
>   create mode 100644 drivers/media/video/stk1160/stk1160-i2c.c
>   create mode 100644 drivers/media/video/stk1160/stk1160-reg.h
>   create mode 100644 drivers/media/video/stk1160/stk1160-v4l.c
>   create mode 100644 drivers/media/video/stk1160/stk1160-video.c
>   create mode 100644 drivers/media/video/stk1160/stk1160.h
> 
> diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
> index 99937c9..8d94d56 100644
> --- a/drivers/media/video/Kconfig
> +++ b/drivers/media/video/Kconfig
> @@ -661,6 +661,8 @@ source "drivers/media/video/hdpvr/Kconfig"
>   
>   source "drivers/media/video/em28xx/Kconfig"
>   
> +source "drivers/media/video/stk1160/Kconfig"
> +
>   source "drivers/media/video/tlg2300/Kconfig"
>   
>   source "drivers/media/video/cx231xx/Kconfig"
> diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
> index d209de0..7698b25 100644
> --- a/drivers/media/video/Makefile
> +++ b/drivers/media/video/Makefile
> @@ -125,6 +125,7 @@ obj-$(CONFIG_VIDEO_HEXIUM_ORION) += hexium_orion.o
>   obj-$(CONFIG_VIDEO_HEXIUM_GEMINI) += hexium_gemini.o
>   obj-$(CONFIG_STA2X11_VIP) += sta2x11_vip.o
>   obj-$(CONFIG_VIDEO_TIMBERDALE)	+= timblogiw.o
> +obj-$(CONFIG_VIDEO_STK1160) += stk1160/
>   
>   obj-$(CONFIG_VIDEOBUF_GEN) += videobuf-core.o
>   obj-$(CONFIG_VIDEOBUF_DMA_SG) += videobuf-dma-sg.o
> diff --git a/drivers/media/video/stk1160/Kconfig b/drivers/media/video/stk1160/Kconfig
> new file mode 100644
> index 0000000..7ae1685
> --- /dev/null
> +++ b/drivers/media/video/stk1160/Kconfig
> @@ -0,0 +1,12 @@
> +config VIDEO_STK1160
> +	tristate "STK1160 USB video capture support"
> +	depends on VIDEO_DEV && I2C && EASYCAP!=m && EASYCAP!=y

Instead of it, why don't you just remove the EASYCAP driver?

> +	select VIDEOBUF2_VMALLOC
> +	select VIDEO_SAA711X if VIDEO_HELPER_CHIPS_AUTO
> +	select SND_AC97_CODEC
> +
> +	---help---
> +	  This is a video4linux driver for STK1160 based video capture devices
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called stk1160
> diff --git a/drivers/media/video/stk1160/Makefile b/drivers/media/video/stk1160/Makefile
> new file mode 100644
> index 0000000..5d8f1ba
> --- /dev/null
> +++ b/drivers/media/video/stk1160/Makefile
> @@ -0,0 +1,6 @@
> +stk1160-y := stk1160-core.o stk1160-v4l.o stk1160-video.o stk1160-i2c.o stk1160-ac97.o
> +
> +obj-$(CONFIG_VIDEO_STK1160) += stk1160.o
> +
> +ccflags-y += -Wall
> +ccflags-y += -Idrivers/media/video
> diff --git a/drivers/media/video/stk1160/stk1160-ac97.c b/drivers/media/video/stk1160/stk1160-ac97.c
> new file mode 100644
> index 0000000..529b05b
> --- /dev/null
> +++ b/drivers/media/video/stk1160/stk1160-ac97.c
> @@ -0,0 +1,152 @@
> +/*
> + * STK1160 driver
> + *
> + * Copyright (C) 2012 Ezequiel Garcia
> + * <elezegarcia--a.t--gmail.com>
> + *
> + * Based on Easycap driver by R.M. Thomas
> + *	Copyright (C) 2010 R.M. Thomas
> + *	<rmthomas--a.t--sciolus.org>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <linux/module.h>
> +#include <sound/core.h>
> +#include <sound/initval.h>
> +#include <sound/ac97_codec.h>
> +
> +#include "stk1160.h"
> +#include "stk1160-reg.h"
> +
> +static struct snd_ac97 *stk1160_ac97;
> +
> +static void stk1160_write_ac97(struct snd_ac97 *ac97, u16 reg, u16 value)
> +{
> +	struct stk1160 *dev = ac97->private_data;
> +
> +	/* Set codec register address */
> +	stk1160_write_reg(dev, STK1160_AC97_ADDR, reg);
> +
> +	/* Set codec command */
> +	stk1160_write_reg(dev, STK1160_AC97_CMD, value & 0xff);
> +	stk1160_write_reg(dev, STK1160_AC97_CMD + 1, (value & 0xff00) >> 8);
> +
> +	/*
> +	 * Set command write bit to initiate write operation.
> +	 * The bit will be cleared when transfer is done.
> +	 */
> +	stk1160_write_reg(dev, STK1160_AC97CTL_0, 0x8c);
> +}
> +
> +static u16 stk1160_read_ac97(struct snd_ac97 *ac97, u16 reg)
> +{
> +	struct stk1160 *dev = ac97->private_data;
> +	u8 vall = 0;
> +	u8 valh = 0;
> +
> +	/* Set codec register address */
> +	stk1160_write_reg(dev, STK1160_AC97_ADDR, reg);
> +
> +	/*
> +	 * Set command read bit to initiate read operation.
> +	 * The bit will be cleared when transfer is done.
> +	 */
> +	stk1160_write_reg(dev, STK1160_AC97CTL_0, 0x8b);
> +
> +	/* Retrieve register value */
> +	stk1160_read_reg(dev, STK1160_AC97_CMD, &vall);
> +	stk1160_read_reg(dev, STK1160_AC97_CMD + 1, &valh);
> +
> +	return (valh << 8) | vall;
> +}
> +
> +static void stk1160_reset_ac97(struct snd_ac97 *ac97)
> +{
> +	struct stk1160 *dev = ac97->private_data;
> +	/* Two-step reset AC97 interface and hardware codec */
> +	stk1160_write_reg(dev, STK1160_AC97CTL_0, 0x94);
> +	stk1160_write_reg(dev, STK1160_AC97CTL_0, 0x88);
> +
> +	/* Set 16-bit audio data and choose L&R channel*/
> +	stk1160_write_reg(dev, STK1160_AC97CTL_1 + 2, 0x01);
> +}
> +
> +static struct snd_ac97_bus_ops stk1160_ac97_ops = {
> +	.read	= stk1160_read_ac97,
> +	.write	= stk1160_write_ac97,
> +	.reset	= stk1160_reset_ac97,
> +};
> +
> +int stk1160_ac97_register(struct stk1160 *dev)
> +{
> +	struct snd_card *card = NULL;
> +	struct snd_ac97_bus *ac97_bus;
> +	struct snd_ac97_template ac97_template;
> +	int rc;
> +
> +	/*
> +	 * Just want a card to access ac96 controls,
> +	 * the actual capture interface will be handled by snd-usb-audio
> +	 */
> +	rc = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
> +			      THIS_MODULE, 0, &card);
> +	if (rc < 0)
> +		return rc;
> +
> +	snd_card_set_dev(card, dev->dev);
> +
> +	/* TODO: I'm not sure where should I get these names :-( */
> +	snprintf(card->shortname, sizeof(card->shortname),
> +		 "stk1160-mixer");
> +	snprintf(card->longname, sizeof(card->longname),
> +		 "stk1160 ac97 codec mixer control");
> +	strncpy(card->driver, dev->dev->driver->name, sizeof(card->driver));
> +
> +	rc = snd_ac97_bus(card, 0, &stk1160_ac97_ops, NULL, &ac97_bus);
> +	if (rc)
> +		goto err;
> +
> +	/* We must set private_data before calling snd_ac97_mixer */
> +	memset(&ac97_template, 0, sizeof(ac97_template));
> +	ac97_template.private_data = dev;
> +	ac97_template.scaps = AC97_SCAP_SKIP_MODEM;
> +	rc = snd_ac97_mixer(ac97_bus, &ac97_template, &stk1160_ac97);
> +	if (rc)
> +		goto err;
> +
> +	dev->snd_card = card;
> +	rc = snd_card_register(card);
> +	if (rc)
> +		goto err;
> +
> +	return 0;
> +
> +err:
> +	if (card)
> +		snd_card_free(card);
> +	return rc;
> +}
> +
> +int stk1160_ac97_unregister(struct stk1160 *dev)
> +{
> +	struct snd_card *card = dev->snd_card;
> +
> +	/*
> +	 * We need to check usb_device,
> +	 * because ac97 release attempts to communicate with codec
> +	 */
> +	if (card && dev->udev)
> +		snd_card_free(card);
> +
> +	return 0;
> +}

It probably makes sense to put the ac97 part of the code inside the -alsa
tree. Anyway, this should be submitted c/c alsa ML, as we want them to take
a look on it and ack.


> diff --git a/drivers/media/video/stk1160/stk1160-core.c b/drivers/media/video/stk1160/stk1160-core.c
> new file mode 100644
> index 0000000..6f62fe6
> --- /dev/null
> +++ b/drivers/media/video/stk1160/stk1160-core.c
> @@ -0,0 +1,433 @@
> +/*
> + * STK1160 driver
> + *
> + * Copyright (C) 2012 Ezequiel Garcia
> + * <elezegarcia--a.t--gmail.com>
> + *
> + * Based on Easycap driver by R.M. Thomas
> + *	Copyright (C) 2010 R.M. Thomas
> + *	<rmthomas--a.t--sciolus.org>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * TODO:
> + * Should I dealloc buffers when stop or only on disconnect?
> + * See em28xx patch about memory fragmentation.
> + * (Perhaps allow a parameter to control this)
> + *
> + * How about framesize and frameinterval v4l2 ioctl?
> + *
> + */
> +
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/kernel.h>
> +#include <linux/errno.h>
> +#include <linux/slab.h>
> +
> +#include <linux/usb.h>
> +#include <linux/mm.h>
> +#include <linux/vmalloc.h>
> +#include <media/saa7115.h>
> +
> +#include "stk1160.h"
> +#include "stk1160-reg.h"
> +
> +static unsigned int input;
> +module_param(input, int, 0644);
> +MODULE_PARM_DESC(input, "Set default input");
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Ezequiel Garcia");
> +MODULE_DESCRIPTION("STK1160 driver");
> +
> +/* Devices supported by this driver */
> +static struct usb_device_id stk1160_id_table[] = {
> +	{ USB_DEVICE(0x05e1, 0x0408) },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(usb, stk1160_id_table);
> +
> +/* saa7113 I2C address */
> +static unsigned short saa7113_addrs[] = {
> +	0x4a >> 1,
> +	I2C_CLIENT_END
> +};
> +
> +/*
> + * Read/Write stk registers
> + */
> +int stk1160_read_reg(struct stk1160 *dev, u16 reg, u8 *value)
> +{
> +	int ret;
> +	int pipe = usb_rcvctrlpipe(dev->udev, 0);
> +
> +	*value = 0;
> +	ret = usb_control_msg(dev->udev, pipe, 0x00,
> +			USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
> +			0x00, reg, value, sizeof(u8), HZ);
> +	if (ret < 0) {
> +		stk1160_err("read failed on reg 0x%x (%d)\n",
> +			reg, ret);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +int stk1160_write_reg(struct stk1160 *dev, u16 reg, u16 value)
> +{
> +	int ret;
> +	int pipe = usb_sndctrlpipe(dev->udev, 0);
> +
> +	ret =  usb_control_msg(dev->udev, pipe, 0x01,
> +			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
> +			value, reg, NULL, 0, HZ);
> +	if (ret < 0) {
> +		stk1160_err("write failed on reg 0x%x (%d)\n",
> +			reg, ret);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +void stk1160_select_input(struct stk1160 *dev)
> +{
> +	static const u8 gctrl[] = {
> +		0x98, 0x90, 0x88, 0x80
> +	};
> +
> +	if (dev->ctl_input < ARRAY_SIZE(gctrl))
> +		stk1160_write_reg(dev, STK1160_GCTRL, gctrl[dev->ctl_input]);
> +}
> +
> +/* TODO: We should break this into pieces */
> +static void stk1160_reg_reset(struct stk1160 *dev)
> +{
> +	int i;
> +
> +	static const struct regval ctl[] = {
> +		{STK1160_GCTRL+2, 0x0078},
> +
> +		{STK1160_RMCTL+1, 0x0000},
> +		{STK1160_RMCTL+3, 0x0002},
> +
> +		{STK1160_PLLSO,   0x0010},
> +		{STK1160_PLLSO+1, 0x0000},
> +		{STK1160_PLLSO+2, 0x0014},
> +		{STK1160_PLLSO+3, 0x000E},
> +
> +		{STK1160_PLLFD,   0x0046},
> +
> +		/* Timing generator setup */
> +		{STK1160_TIGEN,   0x0012},
> +		{STK1160_TICTL,   0x002D},
> +		{STK1160_TICTL+1, 0x0001},
> +		{STK1160_TICTL+2, 0x0000},
> +		{STK1160_TICTL+3, 0x0000},
> +		{STK1160_TIGEN,   0x0080},
> +
> +		{0xffff, 0xffff}
> +	};
> +
> +	for (i = 0; ctl[i].reg != 0xffff; i++)
> +		stk1160_write_reg(dev, ctl[i].reg, ctl[i].val);
> +}
> +
> +static void stk1160_release(struct v4l2_device *v4l2_dev)
> +{
> +	struct stk1160 *dev = container_of(v4l2_dev, struct stk1160, v4l2_dev);
> +
> +	stk1160_info("releasing all resources\n");
> +
> +	stk1160_i2c_unregister(dev);
> +
> +	v4l2_ctrl_handler_free(&dev->ctrl_handler);
> +	v4l2_device_unregister(&dev->v4l2_dev);
> +	kfree(dev->alt_max_pkt_size);
> +	kfree(dev);
> +}
> +
> +/* high bandwidth multiplier, as encoded in highspeed endpoint descriptors */
> +#define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03))
> +
> +/*
> + * Scan usb interface and populate max_pkt_size array
> + * with information on each alternate setting.
> + * The array should be allocated by the caller.
> + */
> +static int stk1160_scan_usb(struct usb_interface *intf, struct usb_device *udev,
> +		unsigned int *max_pkt_size)
> +{
> +	int i, e, sizedescr, size, ifnum;
> +	const struct usb_endpoint_descriptor *desc;
> +
> +	bool has_video = false, has_audio = false;
> +	char *speed;
> +
> +	ifnum = intf->altsetting[0].desc.bInterfaceNumber;
> +
> +	/* Get endpoints */
> +	for (i = 0; i < intf->num_altsetting; i++) {
> +
> +		for (e = 0; e < intf->altsetting[i].desc.bNumEndpoints; e++) {
> +
> +			/* This isn't clear enough, at least to me */
> +			desc = &intf->altsetting[i].endpoint[e].desc;
> +			sizedescr = le16_to_cpu(desc->wMaxPacketSize);
> +			size = sizedescr & 0x7ff;
> +
> +			if (udev->speed == USB_SPEED_HIGH)
> +				size = size * hb_mult(sizedescr);
> +
> +			if (usb_endpoint_xfer_isoc(desc) &&
> +			    usb_endpoint_dir_in(desc)) {
> +				switch (desc->bEndpointAddress) {
> +				case STK1160_EP_AUDIO:
> +					has_audio = true;
> +					break;
> +				case STK1160_EP_VIDEO:
> +					has_video = true;
> +					max_pkt_size[i] = size;
> +					break;
> +				}
> +			}
> +		}
> +	}
> +
> +	/* Is this even possible? */
> +	if (!(has_audio || has_video)) {
> +		dev_err(&udev->dev, "no audio or video endpoints found\n");
> +		return -ENODEV;
> +	}
> +
> +	switch (udev->speed) {
> +	case USB_SPEED_LOW:
> +		speed = "1.5";
> +		break;
> +	case USB_SPEED_UNKNOWN:
> +	case USB_SPEED_FULL:
> +		speed = "12";
> +		break;
> +	case USB_SPEED_HIGH:
> +		speed = "480";
> +		break;
> +	default:
> +		speed = "unknown";
> +	}
> +
> +	dev_info(&udev->dev, "New device %s %s @ %s Mbps (%04x:%04x, interface %d, class %d)\n",
> +		udev->manufacturer ? udev->manufacturer : "",
> +		udev->product ? udev->product : "",
> +		speed,
> +		le16_to_cpu(udev->descriptor.idVendor),
> +		le16_to_cpu(udev->descriptor.idProduct),
> +		ifnum,
> +		intf->altsetting->desc.bInterfaceNumber);
> +
> +	/* This should never happen, since we rejected audio interfaces */
> +	if (has_audio)
> +		dev_warn(&udev->dev, "audio interface %d found.\n\
> +				This is not implemented by this driver,\
> +				you should use snd-usb-audio instead\n", ifnum);
> +
> +	if (has_video)
> +		dev_info(&udev->dev, "video interface %d found\n",
> +				ifnum);
> +
> +	/*
> +	 * Make sure we have 480 Mbps of bandwidth, otherwise things like
> +	 * video stream wouldn't likely work, since 12 Mbps is generally
> +	 * not enough even for most streams.
> +	 */
> +	if (udev->speed != USB_SPEED_HIGH) {
> +		dev_err(&udev->dev, "must be connected to a high-speed USB 2.0 port\n");
> +		return -ENODEV;
> +	}
> +
> +	return 0;
> +}
> +
> +static int stk1160_probe(struct usb_interface *interface,
> +		const struct usb_device_id *id)
> +{
> +	int ifnum;
> +	int rc = 0;
> +
> +	unsigned int *alt_max_pkt_size;	/* array of wMaxPacketSize */
> +	struct usb_device *udev;
> +	struct stk1160 *dev;
> +
> +	ifnum = interface->altsetting[0].desc.bInterfaceNumber;
> +	udev = interface_to_usbdev(interface);
> +
> +	/*
> +	 * Since usb audio class is supported by snd-usb-audio,
> +	 * we reject audio interface.
> +	 */
> +	if (interface->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO)
> +		return -ENODEV;
> +
> +	/* Alloc an array for all possible max_pkt_size */
> +	alt_max_pkt_size = kmalloc(sizeof(alt_max_pkt_size[0]) *
> +			interface->num_altsetting, GFP_KERNEL);
> +	if (alt_max_pkt_size == NULL)
> +		return -ENOMEM;
> +
> +	/*
> +	 * Scan usb posibilities and populate alt_max_pkt_size array.
> +	 * Also, check if device speed is fast enough.
> +	 */
> +	rc = stk1160_scan_usb(interface, udev, alt_max_pkt_size);
> +	if (rc < 0) {
> +		kfree(alt_max_pkt_size);
> +		return rc;
> +	}
> +
> +	dev = kzalloc(sizeof(struct stk1160), GFP_KERNEL);
> +	if (dev == NULL) {
> +		kfree(alt_max_pkt_size);
> +		return -ENOMEM;
> +	}
> +
> +	dev->alt_max_pkt_size = alt_max_pkt_size;
> +	dev->udev = udev;
> +	dev->num_alt = interface->num_altsetting;
> +	dev->ctl_input = input;
> +
> +	/* We save struct device for debug purposes only */
> +	dev->dev = &interface->dev;
> +
> +	usb_set_intfdata(interface, dev);
> +
> +	/* initialize videobuf2 stuff */
> +	rc = stk1160_vb2_setup(dev);
> +	if (rc < 0)
> +		goto free_err;
> +
> +	/*
> +	 * There is no need to take any locks here in probe
> +	 * because we register the device node as the *last* thing.
> +	 */
> +	spin_lock_init(&dev->buf_lock);
> +	mutex_init(&dev->v4l_lock);
> +
> +	rc = v4l2_ctrl_handler_init(&dev->ctrl_handler, 0);
> +	if (rc) {
> +		stk1160_err("v4l2_ctrl_handler_init failed (%d)\n", rc);
> +		goto free_err;
> +	}
> +
> +	/*
> +	 * We obtain a v4l2_dev but defer
> +	 * registration of video device node as the last thing.
> +	 * There is no need to set the name if we give a device struct
> +	 */
> +	dev->v4l2_dev.release = stk1160_release;
> +	dev->v4l2_dev.ctrl_handler = &dev->ctrl_handler;
> +	rc = v4l2_device_register(dev->dev, &dev->v4l2_dev);
> +	if (rc) {
> +		stk1160_err("v4l2_device_register failed (%d)\n", rc);
> +		goto free_ctrl;
> +	}
> +
> +	rc = stk1160_i2c_register(dev);
> +	if (rc < 0)
> +		goto unreg_v4l2;
> +
> +	/*
> +	 * To the best of my knowledge stk1160 boards only have
> +	 * saa7113, but it doesn't hurt to support them all.
> +	 */
> +	dev->sd_saa7115 = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
> +		"saa7115_auto", 0, saa7113_addrs);
> +
> +	stk1160_info("driver ver %s successfully loaded\n",
> +		STK1160_VERSION);
> +
> +	/* i2c reset saa711x */
> +	v4l2_device_call_all(&dev->v4l2_dev, 0, core, reset, 0);
> +	v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_routing,
> +				0, 0, 0);
> +	v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 0);
> +
> +	/* reset stk1160 to default values */
> +	stk1160_reg_reset(dev);
> +
> +	/* select default input */
> +	stk1160_select_input(dev);
> +
> +	rc = stk1160_video_register(dev);
> +	if (rc < 0)
> +		goto unreg_i2c;
> +
> +	return 0;
> +
> +unreg_i2c:
> +	stk1160_i2c_unregister(dev);
> +unreg_v4l2:
> +	v4l2_device_unregister(&dev->v4l2_dev);
> +free_ctrl:
> +	v4l2_ctrl_handler_free(&dev->ctrl_handler);
> +free_err:
> +	kfree(alt_max_pkt_size);
> +	kfree(dev);
> +
> +	return rc;
> +}
> +
> +/*
> + * TODO: What happens if device gets diconnected while probing?
> + */
> +static void stk1160_disconnect(struct usb_interface *interface)
> +{
> +	struct stk1160 *dev;
> +
> +	dev = usb_get_intfdata(interface);
> +	usb_set_intfdata(interface, NULL);
> +
> +	/*
> +	 * Wait until all current v4l2 operation are finished
> +	 * then deallocate resources
> +	 */
> +	mutex_lock(&dev->v4l_lock);
> +
> +	/* Here is the only place where isoc get released */
> +	stk1160_uninit_isoc(dev);
> +
> +	/* ac97 unregister needs to be done before usb_device is cleared */
> +	stk1160_ac97_unregister(dev);
> +
> +	stk1160_stop_streaming(dev, false);
> +	video_unregister_device(&dev->vdev);
> +	v4l2_device_disconnect(&dev->v4l2_dev);
> +
> +	/* This way current users can detect device is gone */
> +	dev->udev = NULL;
> +
> +	mutex_unlock(&dev->v4l_lock);
> +
> +	/*
> +	 * This calls stk1160_release if it's the last reference.
> +	 * therwise, release is posponed until there are no users left.
> +	 */
> +	v4l2_device_put(&dev->v4l2_dev);
> +}
> +
> +static struct usb_driver stk1160_usb_driver = {
> +	.name = "stk1160",
> +	.id_table = stk1160_id_table,
> +	.probe = stk1160_probe,
> +	.disconnect = stk1160_disconnect,
> +};
> +
> +module_usb_driver(stk1160_usb_driver);
> diff --git a/drivers/media/video/stk1160/stk1160-i2c.c b/drivers/media/video/stk1160/stk1160-i2c.c
> new file mode 100644
> index 0000000..176ac93
> --- /dev/null
> +++ b/drivers/media/video/stk1160/stk1160-i2c.c
> @@ -0,0 +1,294 @@
> +/*
> + * STK1160 driver
> + *
> + * Copyright (C) 2012 Ezequiel Garcia
> + * <elezegarcia--a.t--gmail.com>
> + *
> + * Based on Easycap driver by R.M. Thomas
> + *	Copyright (C) 2010 R.M. Thomas
> + *	<rmthomas--a.t--sciolus.org>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <linux/module.h>
> +#include <linux/usb.h>
> +#include <linux/i2c.h>
> +
> +#include "stk1160.h"
> +#include "stk1160-reg.h"
> +
> +static unsigned int i2c_debug;
> +module_param(i2c_debug, int, 0644);
> +MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]");
> +
> +#define dprintk_i2c(fmt, args...)				\
> +do {								\
> +	if (i2c_debug)						\
> +		printk(KERN_DEBUG fmt, ##args);			\
> +} while (0)
> +
> +static int stk1160_i2c_busy_wait(struct stk1160 *dev, u8 wait_bit_mask)
> +{
> +	unsigned long end;
> +	u8 flag;
> +
> +	/* Wait until read/write finish bit is set */
> +	end = jiffies + msecs_to_jiffies(STK1160_I2C_TIMEOUT);
> +	while (time_is_after_jiffies(end)) {
> +
> +		stk1160_read_reg(dev, STK1160_SICTL+1, &flag);
> +		/* read/write done? */
> +		if (flag & wait_bit_mask)
> +			goto done;
> +
> +		usleep_range(10 * USEC_PER_MSEC, 20 * USEC_PER_MSEC);
> +	}
> +
> +	return -ETIMEDOUT;
> +
> +done:
> +	return 0;
> +}
> +
> +static int stk1160_i2c_write_reg(struct stk1160 *dev, u8 addr,
> +		u8 reg, u8 value)
> +{
> +	int rc;
> +
> +	/* Set serial device address */
> +	rc = stk1160_write_reg(dev, STK1160_SICTL_SDA, addr);
> +	if (rc < 0)
> +		return rc;
> +
> +	/* Set i2c device register sub-address */
> +	rc = stk1160_write_reg(dev, STK1160_SBUSW_WA, reg);
> +	if (rc < 0)
> +		return rc;
> +
> +	/* Set i2c device register value */
> +	rc = stk1160_write_reg(dev, STK1160_SBUSW_WD, value);
> +	if (rc < 0)
> +		return rc;
> +
> +	/* Start write now */
> +	rc = stk1160_write_reg(dev, STK1160_SICTL, 0x01);
> +	if (rc < 0)
> +		return rc;
> +
> +	rc = stk1160_i2c_busy_wait(dev, 0x04);
> +	if (rc < 0)
> +		return rc;
> +
> +	return 0;
> +}
> +
> +static int stk1160_i2c_read_reg(struct stk1160 *dev, u8 addr,
> +		u8 reg, u8 *value)
> +{
> +	int rc;
> +
> +	/* Set serial device address */
> +	rc = stk1160_write_reg(dev, STK1160_SICTL_SDA, addr);
> +	if (rc < 0)
> +		return rc;
> +
> +	/* Set i2c device register sub-address */
> +	rc = stk1160_write_reg(dev, STK1160_SBUSR_RA, reg);
> +	if (rc < 0)
> +		return rc;
> +
> +	/* Start read now */
> +	rc = stk1160_write_reg(dev, STK1160_SICTL, 0x20);
> +	if (rc < 0)
> +		return rc;
> +
> +	rc = stk1160_i2c_busy_wait(dev, 0x01);
> +	if (rc < 0)
> +		return rc;
> +
> +	stk1160_read_reg(dev, STK1160_SBUSR_RD, value);
> +	if (rc < 0)
> +		return rc;
> +
> +	return 0;
> +}
> +
> +/*
> + * stk1160_i2c_check_for_device()
> + * check if there is a i2c_device at the supplied address
> + */
> +static int stk1160_i2c_check_for_device(struct stk1160 *dev,
> +		unsigned char addr)
> +{
> +	int rc;
> +
> +	/* Set serial device address */
> +	rc = stk1160_write_reg(dev, STK1160_SICTL_SDA, addr);
> +	if (rc < 0)
> +		return rc;
> +
> +	/* Set device sub-address, we'll chip version reg */
> +	rc = stk1160_write_reg(dev, STK1160_SBUSR_RA, 0x00);
> +	if (rc < 0)
> +		return rc;
> +
> +	/* Start read now */
> +	rc = stk1160_write_reg(dev, STK1160_SICTL, 0x20);
> +	if (rc < 0)
> +		return rc;
> +
> +	rc = stk1160_i2c_busy_wait(dev, 0x01);
> +	if (rc < 0)
> +		return -ENODEV;
> +
> +	return 0;
> +}
> +
> +/*
> + * stk1160_i2c_xfer()
> + * the main i2c transfer function
> + */
> +static int stk1160_i2c_xfer(struct i2c_adapter *i2c_adap,
> +			   struct i2c_msg msgs[], int num)
> +{
> +	struct stk1160 *dev = i2c_adap->algo_data;
> +	int addr, rc, i;
> +
> +	for (i = 0; i < num; i++) {
> +		addr = msgs[i].addr << 1;
> +		dprintk_i2c("%s: addr=%x", __func__, addr);
> +
> +		if (!msgs[i].len) {
> +			/* no len: check only for device presence */
> +			rc = stk1160_i2c_check_for_device(dev, addr);
> +			if (rc < 0) {
> +				dprintk_i2c(" no device\n");
> +				return rc;
> +			}
> +
> +		} else if (msgs[i].flags & I2C_M_RD) {
> +			/* read request without preceding register selection */
> +			dprintk_i2c(" subaddr not selected");
> +			rc = -EOPNOTSUPP;
> +			goto err;
> +
> +		} else if (i + 1 < num && msgs[i].len <= 2 &&
> +			   (msgs[i + 1].flags & I2C_M_RD) &&
> +			   msgs[i].addr == msgs[i + 1].addr) {
> +
> +			if (msgs[i].len != 1 || msgs[i + 1].len != 1) {
> +				dprintk_i2c(" len not supported");
> +				rc = -EOPNOTSUPP;
> +				goto err;
> +			}
> +
> +			dprintk_i2c(" subaddr=%x", msgs[i].buf[0]);
> +
> +			rc = stk1160_i2c_read_reg(dev, addr, msgs[i].buf[0],
> +				msgs[i + 1].buf);
> +
> +			dprintk_i2c(" read=%x", *msgs[i + 1].buf);
> +
> +			/* consumed two msgs, so we skip one of them */
> +			i++;
> +
> +		} else {
> +			if (msgs[i].len != 2) {
> +				dprintk_i2c(" len not supported");
> +				rc = -EOPNOTSUPP;
> +				goto err;
> +			}
> +
> +			dprintk_i2c(" subaddr=%x write=%x",
> +				msgs[i].buf[0],  msgs[i].buf[1]);
> +
> +			rc = stk1160_i2c_write_reg(dev, addr, msgs[i].buf[0],
> +				msgs[i].buf[1]);
> +		}
> +
> +		if (rc < 0)
> +			goto err;
> +		dprintk_i2c(" OK\n");
> +	}
> +
> +	return num;
> +err:
> +	dprintk_i2c(" ERROR: %d\n", rc);
> +	return num;
> +}
> +
> +/*
> + * functionality(), what da heck is this?
> + */
> +static u32 functionality(struct i2c_adapter *adap)
> +{
> +	return I2C_FUNC_SMBUS_EMUL;
> +}
> +
> +static struct i2c_algorithm algo = {
> +	.master_xfer   = stk1160_i2c_xfer,
> +	.functionality = functionality,
> +};
> +
> +static struct i2c_adapter adap_template = {
> +	.owner = THIS_MODULE,
> +	.name = "stk1160",
> +	.algo = &algo,
> +};
> +
> +static struct i2c_client client_template = {
> +	.name = "stk1160 internal",
> +};
> +
> +/*
> + * stk1160_i2c_register()
> + * register i2c bus
> + */
> +int stk1160_i2c_register(struct stk1160 *dev)
> +{
> +	int rc;
> +
> +	dev->i2c_adap = adap_template;
> +	dev->i2c_adap.dev.parent = dev->dev;
> +	strcpy(dev->i2c_adap.name, "stk1160");
> +	dev->i2c_adap.algo_data = dev;
> +
> +	i2c_set_adapdata(&dev->i2c_adap, &dev->v4l2_dev);
> +
> +	rc = i2c_add_adapter(&dev->i2c_adap);
> +	if (rc < 0) {
> +		stk1160_err("cannot add i2c adapter (%d)\n", rc);
> +		return rc;
> +	}
> +
> +	dev->i2c_client = client_template;
> +	dev->i2c_client.adapter = &dev->i2c_adap;
> +
> +	/* Set i2c clock divider device address */
> +	stk1160_write_reg(dev, STK1160_SICTL_CD,  0x0f);
> +
> +	/* ??? */
> +	stk1160_write_reg(dev, STK1160_ASIC + 3,  0x00);
> +
> +	return 0;
> +}
> +
> +/*
> + * stk1160_i2c_unregister()
> + * unregister i2c_bus
> + */
> +int stk1160_i2c_unregister(struct stk1160 *dev)
> +{
> +	i2c_del_adapter(&dev->i2c_adap);
> +	return 0;
> +}
> diff --git a/drivers/media/video/stk1160/stk1160-reg.h b/drivers/media/video/stk1160/stk1160-reg.h
> new file mode 100644
> index 0000000..3e49da6
> --- /dev/null
> +++ b/drivers/media/video/stk1160/stk1160-reg.h
> @@ -0,0 +1,93 @@
> +/*
> + * STK1160 driver
> + *
> + * Copyright (C) 2012 Ezequiel Garcia
> + * <elezegarcia--a.t--gmail.com>
> + *
> + * Based on Easycap driver by R.M. Thomas
> + *	Copyright (C) 2010 R.M. Thomas
> + *	<rmthomas--a.t--sciolus.org>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +/* GPIO Control */
> +#define STK1160_GCTRL			0x000
> +
> +/* Remote Wakup Control */
> +#define STK1160_RMCTL			0x00c
> +
> +/*
> + * Decoder Control Register:
> + * This byte controls capture start/stop
> + * with bit #7 (0x?? OR 0x80 to activate).
> + */
> +#define STK1160_DCTRL			0x100
> +
> +/* Capture Frame Start Position */
> +#define STK116_CFSPO			0x110
> +#define STK116_CFSPO_STX_L		0x110
> +#define STK116_CFSPO_STX_H		0x111
> +#define STK116_CFSPO_STY_L		0x112
> +#define STK116_CFSPO_STY_H		0x113
> +
> +/* Capture Frame End Position */
> +#define STK116_CFEPO			0x114
> +#define STK116_CFEPO_ENX_L		0x114
> +#define STK116_CFEPO_ENX_H		0x115
> +#define STK116_CFEPO_ENY_L		0x116
> +#define STK116_CFEPO_ENY_H		0x117
> +
> +/* Serial Interface Control  */
> +#define STK1160_SICTL			0x200
> +#define STK1160_SICTL_CD		0x202
> +#define STK1160_SICTL_SDA		0x203
> +
> +/* Serial Bus Write */
> +#define STK1160_SBUSW			0x204
> +#define STK1160_SBUSW_WA		0x204
> +#define STK1160_SBUSW_WD		0x205
> +
> +/* Serial Bus Read */
> +#define STK1160_SBUSR			0x208
> +#define STK1160_SBUSR_RA		0x208
> +#define STK1160_SBUSR_RD		0x209
> +
> +/* Alternate Serial Inteface Control */
> +#define STK1160_ASIC			0x2fc
> +
> +/* PLL Select Options */
> +#define STK1160_PLLSO			0x018
> +
> +/* PLL Frequency Divider */
> +#define STK1160_PLLFD			0x01c
> +
> +/* Timing Generator */
> +#define STK1160_TIGEN			0x300
> +
> +/* Timing Control Parameter */
> +#define STK1160_TICTL			0x350
> +
> +/* AC97 Audio Control */
> +#define STK1160_AC97CTL_0		0x500
> +#define STK1160_AC97CTL_1		0x504
> +
> +/* Use [0:6] bits of register 0x504 to set codec command address */
> +#define STK1160_AC97_ADDR		0x504
> +/* Use [16:31] bits of register 0x500 to set codec command data */
> +#define STK1160_AC97_CMD		0x502
> +
> +/* Audio I2S Interface */
> +#define STK1160_I2SCTL			0x50c
> +
> +/* EEPROM Interface */
> +#define STK1160_EEPROM_SZ		0x5f0
> diff --git a/drivers/media/video/stk1160/stk1160-v4l.c b/drivers/media/video/stk1160/stk1160-v4l.c
> new file mode 100644
> index 0000000..024ddf7
> --- /dev/null
> +++ b/drivers/media/video/stk1160/stk1160-v4l.c
> @@ -0,0 +1,923 @@
> +/*
> + * STK1160 driver
> + *
> + * Copyright (C) 2012 Ezequiel Garcia
> + * <elezegarcia--a.t--gmail.com>
> + *
> + * Based on Easycap driver by R.M. Thomas
> + *	Copyright (C) 2010 R.M. Thomas
> + *	<rmthomas--a.t--sciolus.org>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <linux/module.h>
> +#include <linux/usb.h>
> +#include <linux/mm.h>
> +#include <linux/slab.h>
> +
> +#include <linux/videodev2.h>
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-common.h>
> +#include <media/v4l2-ioctl.h>
> +#include <media/v4l2-fh.h>
> +#include <media/v4l2-event.h>
> +#include <media/v4l2-chip-ident.h>
> +#include <media/videobuf2-vmalloc.h>
> +
> +#include <media/saa7115.h>
> +
> +#include "stk1160.h"
> +#include "stk1160-reg.h"
> +
> +static unsigned int vidioc_debug;
> +module_param(vidioc_debug, int, 0644);
> +MODULE_PARM_DESC(vidioc_debug, "enable debug messages [vidioc]");
> +
> +static bool keep_buffers;
> +module_param(keep_buffers, bool, 0644);
> +MODULE_PARM_DESC(keep_buffers, "don't release buffers upon stop streaming");
> +
> +/* supported video standards */
> +static struct stk1160_fmt format[] = {
> +	{
> +		.name     = "16 bpp YUY2, 4:2:2, packed",
> +		.fourcc   = V4L2_PIX_FMT_UYVY,
> +		.depth    = 16,
> +	}
> +};
> +
> +static void stk1160_set_std(struct stk1160 *dev)
> +{
> +	int i;
> +
> +	static struct regval std525[] = {
> +
> +		/* 720x480 */
> +
> +		/* Frame start */
> +		{STK116_CFSPO_STX_L, 0x0000},
> +		{STK116_CFSPO_STX_H, 0x0000},
> +		{STK116_CFSPO_STY_L, 0x0003},
> +		{STK116_CFSPO_STY_H, 0x0000},
> +
> +		/* Frame end */
> +		{STK116_CFEPO_ENX_L, 0x05a0},
> +		{STK116_CFEPO_ENX_H, 0x0005},
> +		{STK116_CFEPO_ENY_L, 0x00f3},
> +		{STK116_CFEPO_ENY_H, 0x0000},
> +
> +		{0xffff, 0xffff}
> +	};
> +
> +	static struct regval std625[] = {
> +
> +		/* 720x576 */
> +
> +		/* TODO: Each line of frame has some junk at the end */
> +		/* Frame start */
> +		{STK116_CFSPO,   0x0000},
> +		{STK116_CFSPO+1, 0x0000},
> +		{STK116_CFSPO+2, 0x0001},
> +		{STK116_CFSPO+3, 0x0000},
> +
> +		/* Frame end */
> +		{STK116_CFEPO,   0x05a0},
> +		{STK116_CFEPO+1, 0x0005},
> +		{STK116_CFEPO+2, 0x0121},
> +		{STK116_CFEPO+3, 0x0001},
> +
> +		{0xffff, 0xffff}
> +	};
> +
> +	if (dev->norm & V4L2_STD_525_60) {
> +		stk1160_dbg("registers to NTSC like standard\n");
> +		for (i = 0; std525[i].reg != 0xffff; i++)
> +			stk1160_write_reg(dev, std525[i].reg, std525[i].val);
> +	} else if (dev->norm & V4L2_STD_625_50) {
> +		stk1160_dbg("registers to PAL like standard\n");
> +		for (i = 0; std625[i].reg != 0xffff; i++)
> +			stk1160_write_reg(dev, std625[i].reg, std625[i].val);
> +	} else {
> +		BUG();


It doesn't make sense to add a bug here: a wrong parameter generated
from userspace shouldn't cause a kernel bug.

Just do:
if (dev->norm & V4L2_STD_525_60) {
	...
} else {
	...
}

> +	}
> +
> +}
> +
> +/*
> + * Set a new alternate setting.
> + * Returns true is dev->max_pkt_size has changed, false otherwise.
> + */
> +static bool stk1160_set_alternate(struct stk1160 *dev)
> +{
> +	int i, prev_alt = dev->alt;
> +	unsigned int min_pkt_size;
> +	bool new_pkt_size;
> +
> +	/*
> +	 * If we don't set right alternate,
> +	 * then we will get a green screen with junk.
> +	 */
> +	min_pkt_size = STK1160_MIN_PKT_SIZE;
> +
> +	for (i = 0; i < dev->num_alt; i++) {
> +		/* stop when the selected alt setting offers enough bandwidth */
> +		if (dev->alt_max_pkt_size[i] >= min_pkt_size) {
> +			dev->alt = i;
> +			break;
> +		/*
> +		 * otherwise make sure that we end up with the maximum bandwidth
> +		 * because the min_pkt_size equation might be wrong...
> +		 */
> +		} else if (dev->alt_max_pkt_size[i] >
> +			   dev->alt_max_pkt_size[dev->alt])
> +			dev->alt = i;
> +	}
> +
> +	stk1160_info("setting alternate %d\n", dev->alt);
> +
> +	if (dev->alt != prev_alt) {
> +		stk1160_dbg("minimum isoc packet size: %u (alt=%d)\n",
> +				min_pkt_size, dev->alt);
> +		stk1160_dbg("setting alt %d with wMaxPacketSize=%u\n",
> +			       dev->alt, dev->alt_max_pkt_size[dev->alt]);
> +		usb_set_interface(dev->udev, 0, dev->alt);
> +	}
> +
> +	new_pkt_size = dev->max_pkt_size != dev->alt_max_pkt_size[dev->alt];
> +	dev->max_pkt_size = dev->alt_max_pkt_size[dev->alt];
> +
> +	return new_pkt_size;
> +}
> +
> +static bool stk1160_acquire_owner(struct stk1160 *dev, struct file *file)
> +{
> +	/* If there is an owner and it's not this filehandle */
> +	if (dev->fh_owner != NULL && dev->fh_owner != file)
> +		return false;
> +
> +	/* We are the owner of this queue and queue operations */
> +	dev->fh_owner = file;
> +
> +	return true;
> +}
> +
> +static void stk1160_drop_owner(struct stk1160 *dev)
> +{
> +	dev->fh_owner = NULL;
> +}
> +
> +static bool stk1160_is_owner(struct stk1160 *dev, struct file *file)
> +{
> +	return dev->fh_owner == file;
> +}
> +
> +static int stk1160_start_streaming(struct stk1160 *dev)
> +{
> +	int i, rc;
> +	bool new_pkt_size;
> +
> +	/* Check device presence */
> +	if (!dev->udev)
> +		return -ENODEV;
> +
> +	/*
> +	 * For some reason it is mandatory to set alternate *first*
> +	 * and only *then* initialize isoc urbs.
> +	 * Someone please explain me why ;)
> +	 */
> +	new_pkt_size = stk1160_set_alternate(dev);
> +
> +	/*
> +	 * We (re)allocate isoc urbs if:
> +	 * there is no allocated isoc urbs, OR
> +	 * a new dev->max_pkt_size is detected
> +	 */
> +	if (!dev->isoc_ctl.num_bufs || new_pkt_size) {
> +		rc = stk1160_alloc_isoc(dev);
> +		if (rc < 0)
> +			return rc;
> +	}
> +
> +	/* submit urbs and enables IRQ */
> +	for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
> +		rc = usb_submit_urb(dev->isoc_ctl.urb[i], GFP_KERNEL);
> +		if (rc) {
> +			stk1160_err("cannot submit urb[%d] (%d)\n", i, rc);
> +			stk1160_uninit_isoc(dev);
> +			return rc;
> +		}
> +	}
> +
> +	/* Start saa711x */
> +	v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 1);
> +
> +	/* Start stk1160 */
> +	stk1160_write_reg(dev, STK1160_DCTRL, 0xb3);
> +	stk1160_write_reg(dev, STK1160_DCTRL+3, 0x00);
> +
> +	stk1160_dbg("streaming started\n");
> +
> +	return 0;
> +}
> +
> +int stk1160_stop_streaming(struct stk1160 *dev, bool connected)
> +{
> +	struct stk1160_buffer *buf;
> +	unsigned long flags = 0;
> +
> +	stk1160_cancel_isoc(dev);
> +
> +	/*
> +	 * It is possible to keep buffers around using a module parameter.
> +	 * This is intended to avoid memory fragmentation.
> +	 */
> +	if (!keep_buffers)
> +		stk1160_free_isoc(dev);
> +
> +	/* If the device is physically plugged */
> +	if (connected && dev->udev) {
> +
> +		/* set alternate 0 */
> +		dev->alt = 0;
> +		stk1160_info("setting alternate %d\n", dev->alt);
> +		usb_set_interface(dev->udev, 0, 0);
> +
> +		/* Stop stk1160 */
> +		stk1160_write_reg(dev, STK1160_DCTRL, 0x00);
> +		stk1160_write_reg(dev, STK1160_DCTRL+3, 0x00);
> +
> +		/* Stop saa711x */
> +		v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 0);
> +	}
> +
> +	/* Release all active buffers */
> +	spin_lock_irqsave(&dev->buf_lock, flags);
> +	while (!list_empty(&dev->avail_bufs)) {
> +		buf = list_first_entry(&dev->avail_bufs,
> +			struct stk1160_buffer, list);
> +		list_del(&buf->list);
> +		vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
> +		stk1160_info("buffer [%p/%d] aborted\n",
> +				buf, buf->vb.v4l2_buf.index);
> +	}
> +	/* It's important to clear current buffer */
> +	dev->isoc_ctl.buf = NULL;
> +	spin_unlock_irqrestore(&dev->buf_lock, flags);
> +
> +	stk1160_dbg("streaming stopped\n");
> +	return 0;
> +}
> +
> +/* fops */
> +static ssize_t stk1160_read(struct file *file,
> +	char __user *data, size_t count, loff_t *ppos)
> +{
> +	struct stk1160 *dev = video_drvdata(file);
> +	int rc;
> +
> +	mutex_lock(&dev->v4l_lock);
> +	/*
> +	 * Read operation is emulated by videobuf2.
> +	 * When vb2 calls reqbufs it acquires ownership of queue.
> +	 * When the transfer is done, vb2 calls reqbufs with zero count,
> +	 * dropping ownership.
> +	 */
> +	rc = vb2_read(&dev->vb_vidq, data, count, ppos,
> +			file->f_flags & O_NONBLOCK);
> +
> +	mutex_unlock(&dev->v4l_lock);
> +	return rc;
> +}
> +
> +static unsigned int
> +stk1160_poll(struct file *file, struct poll_table_struct *wait)
> +{
> +	struct stk1160 *dev = video_drvdata(file);
> +	int rc;
> +
> +	mutex_lock(&dev->v4l_lock);
> +	rc = vb2_poll(&dev->vb_vidq, file, wait);
> +	mutex_unlock(&dev->v4l_lock);
> +
> +	return rc;
> +}
> +
> +static int stk1160_close(struct file *file)
> +{
> +	struct stk1160 *dev = video_drvdata(file);
> +
> +	mutex_lock(&dev->v4l_lock);
> +	/*
> +	 * If this is the owner handle we stop
> +	 * streaming to free/dequeue all buffers.
> +	 * Also, we drop ownership.
> +	 */
> +	if (stk1160_is_owner(dev, file)) {
> +		vb2_queue_release(&dev->vb_vidq);
> +		stk1160_drop_owner(dev);
> +	}
> +	mutex_unlock(&dev->v4l_lock);
> +
> +	return v4l2_fh_release(file);
> +}
> +
> +static int stk1160_mmap(struct file *file, struct vm_area_struct *vma)
> +{
> +	struct stk1160 *dev = video_drvdata(file);
> +	int rc;
> +
> +	stk1160_dbg("vma=0x%08lx\n", (unsigned long)vma);
> +
> +	/* TODO: Lock or trylock? */
> +	mutex_lock(&dev->v4l_lock);
> +	rc = vb2_mmap(&dev->vb_vidq, vma);
> +	mutex_unlock(&dev->v4l_lock);
> +
> +	stk1160_dbg("vma start=0x%08lx, size=%ld (%d)\n",
> +		(unsigned long)vma->vm_start,
> +		(unsigned long)vma->vm_end - (unsigned long)vma->vm_start,
> +		rc);
> +	return rc;
> +}
> +
> +static struct v4l2_file_operations stk1160_fops = {
> +	.owner = THIS_MODULE,
> +	.open = v4l2_fh_open,
> +	.release = stk1160_close,
> +	.read = stk1160_read,
> +	.poll = stk1160_poll,
> +	.unlocked_ioctl = video_ioctl2,
> +	.mmap = stk1160_mmap,
> +};
> +
> +/*
> + * vb2 ioctls
> + */
> +static int vidioc_reqbufs(struct file *file, void *priv,
> +			  struct v4l2_requestbuffers *p)
> +{
> +	struct stk1160 *dev = video_drvdata(file);
> +	int rc;
> +
> +	if (!stk1160_acquire_owner(dev, file))
> +		return -EBUSY;
> +
> +	rc = vb2_reqbufs(&dev->vb_vidq, p);
> +
> +	/*
> +	 * If reqbufs has been called with count == 0
> +	 * it means the owner is releasing the queue,
> +	 * thus dropping ownership.
> +	 */
> +	if (p->count == 0)
> +		stk1160_drop_owner(dev);
> +
> +	return rc;
> +}
> +
> +static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p)
> +{
> +	struct stk1160 *dev = video_drvdata(file);
> +
> +	if (!stk1160_is_owner(dev, file))
> +		return -EBUSY;
> +
> +	return vb2_querybuf(&dev->vb_vidq, p);
> +}
> +
> +static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
> +{
> +	struct stk1160 *dev = video_drvdata(file);
> +
> +	if (!stk1160_is_owner(dev, file))
> +		return -EBUSY;
> +
> +	return vb2_qbuf(&dev->vb_vidq, p);
> +}
> +
> +static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
> +{
> +	struct stk1160 *dev = video_drvdata(file);
> +
> +	if (!stk1160_is_owner(dev, file))
> +		return -EBUSY;
> +
> +	return vb2_dqbuf(&dev->vb_vidq, p, file->f_flags & O_NONBLOCK);

Why to use O_NONBLOCK here? it should be doing whatever userspace wants.

> +}
> +
> +static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
> +{
> +	struct stk1160 *dev = video_drvdata(file);
> +
> +	if (!stk1160_is_owner(dev, file))
> +		return -EBUSY;
> +
> +	return vb2_streamon(&dev->vb_vidq, i);
> +}
> +
> +static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
> +{
> +	struct stk1160 *dev = video_drvdata(file);
> +
> +	if (!stk1160_is_owner(dev, file))
> +		return -EBUSY;
> +
> +	return vb2_streamoff(&dev->vb_vidq, i);
> +}
> +
> +/*
> + * vidioc ioctls
> + */
> +static int vidioc_querycap(struct file *file,
> +		void *priv, struct v4l2_capability *cap)
> +{
> +	struct stk1160 *dev = video_drvdata(file);
> +
> +	strcpy(cap->driver, "stk1160");
> +	strcpy(cap->card, "stk1160");
> +	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;
> +}
> +
> +static int vidioc_enum_fmt_vid_cap(struct file *file, void  *priv,
> +		struct v4l2_fmtdesc *f)
> +{
> +	if (f->index != 0)
> +		return -EINVAL;
> +
> +	strlcpy(f->description, format[f->index].name, sizeof(f->description));
> +	f->pixelformat = format[f->index].fourcc;
> +	return 0;
> +}
> +
> +static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
> +					struct v4l2_format *f)
> +{
> +	struct stk1160 *dev = video_drvdata(file);
> +
> +	f->fmt.pix.width = dev->width;
> +	f->fmt.pix.height = dev->height;
> +	f->fmt.pix.field = V4L2_FIELD_INTERLACED;
> +	f->fmt.pix.pixelformat = dev->fmt->fourcc;
> +	f->fmt.pix.bytesperline = dev->width * 2;
> +	f->fmt.pix.sizeimage = dev->height * f->fmt.pix.bytesperline;
> +	f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
> +
> +	return 0;
> +}
> +
> +static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
> +			struct v4l2_format *f)
> +{
> +	struct stk1160 *dev = video_drvdata(file);
> +
> +	if (f->fmt.pix.pixelformat != format[0].fourcc) {
> +		stk1160_err("fourcc format 0x%08x invalid\n",
> +			f->fmt.pix.pixelformat);
> +		return -EINVAL;
> +	}
> +
> +	/*
> +	 * User can't choose size at his own will,
> +	 * so we just return him the current size chosen
> +	 * at standard selection.
> +	 * TODO: Implement frame scaling?
> +	 */
> +
> +	f->fmt.pix.width = dev->width;
> +	f->fmt.pix.height = dev->height;
> +	f->fmt.pix.field = V4L2_FIELD_INTERLACED;
> +	f->fmt.pix.bytesperline = dev->width * 2;
> +	f->fmt.pix.sizeimage = dev->height * f->fmt.pix.bytesperline;
> +	f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
> +
> +	return 0;
> +}
> +
> +static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
> +					struct v4l2_format *f)
> +{
> +	struct stk1160 *dev = video_drvdata(file);
> +	struct vb2_queue *q = &dev->vb_vidq;
> +	int rc;
> +
> +	if (!stk1160_acquire_owner(dev, file))
> +		return -EBUSY;
> +
> +	rc = vidioc_try_fmt_vid_cap(file, priv, f);
> +	if (rc < 0)
> +		return rc;
> +
> +	if (vb2_is_streaming(q)) {
> +		stk1160_err("device busy\n");
> +		return -EBUSY;
> +	}
> +
> +	return 0;
> +}
> +
> +static int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *norm)
> +{
> +	struct stk1160 *dev = video_drvdata(file);
> +	v4l2_device_call_all(&dev->v4l2_dev, 0, video, querystd, norm);
> +	return 0;
> +}
> +
> +static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *norm)
> +{
> +	struct stk1160 *dev = video_drvdata(file);
> +
> +	*norm = dev->norm;
> +	return 0;
> +}

You don't need the above. the logic at v4l2-ioctl already does that:

		/* Calls the specific handler */
		if (ops->vidioc_g_std)
			ret = ops->vidioc_g_std(file, fh, id);
		else if (vfd->current_norm) {
			ret = 0;
			*id = vfd->current_norm;
		}


> +
> +static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm)
> +{
> +	struct stk1160 *dev = video_drvdata(file);
> +	struct vb2_queue *q = &dev->vb_vidq;
> +
> +	if (!stk1160_acquire_owner(dev, file))
> +		return -EBUSY;
> +
> +	if (vb2_is_streaming(q)) {
> +		stk1160_err("device busy\n");
> +		return -EBUSY;
> +	}
> +
> +	/* Check device presence */
> +	if (!dev->udev)
> +		return -ENODEV;
> +
> +	/* This is taken from saa7115 video decoder */
> +	if (dev->norm & V4L2_STD_525_60) {
> +		dev->width = 720;
> +		dev->height = 480;
> +	} else if (dev->norm & V4L2_STD_625_50) {
> +		dev->width = 720;
> +		dev->height = 576;
> +	} else {
> +		stk1160_err("invalid standard\n");
> +		return -EINVAL;
> +	}
> +
> +	/* We need to set this now, before we call stk1160_set_std */
> +	dev->norm = *norm;
> +
> +	stk1160_set_std(dev);
> +
> +	v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std,
> +			dev->norm);
> +
> +	return 0;
> +}
> +
> +/* FIXME: Extend support for other inputs */
> +static int vidioc_enum_input(struct file *file, void *priv,
> +				struct v4l2_input *i)
> +{
> +	struct stk1160 *dev = video_drvdata(file);
> +
> +	if (i->index > STK1160_MAX_INPUT)
> +		return -EINVAL;
> +
> +	sprintf(i->name, "Composite%d", i->index);
> +	i->type = V4L2_INPUT_TYPE_CAMERA;
> +	i->std = dev->vdev.tvnorms;
> +	return 0;
> +}
> +
> +static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
> +{
> +	struct stk1160 *dev = video_drvdata(file);
> +	*i = dev->ctl_input;
> +	return 0;
> +}
> +
> +static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
> +{
> +	struct stk1160 *dev = video_drvdata(file);
> +
> +	if (!stk1160_acquire_owner(dev, file))
> +		return -EBUSY;
> +
> +	if (i > STK1160_MAX_INPUT)
> +		return -EINVAL;
> +
> +	dev->ctl_input = i;
> +
> +	stk1160_select_input(dev);
> +
> +	return 0;
> +}
> +


> +static int vidioc_enum_framesizes(struct file *file, void *fh,
> +				 struct v4l2_frmsizeenum *fsize)
> +{
> +	/* TODO: Is this needed? */
> +	return -EINVAL;
> +}
> +
> +static int vidioc_enum_frameintervals(struct file *file, void *fh,
> +				  struct v4l2_frmivalenum *fival)
> +{
> +	/* TODO: Is this needed? */
> +	return -EINVAL;
> +}

The return codes for the above functions are wrong. Just don't implement
those two functions and the core will do the right thing.

> +
> +static int vidioc_g_chip_ident(struct file *file, void *priv,
> +	       struct v4l2_dbg_chip_ident *chip)
> +{
> +	switch (chip->match.type) {
> +	case V4L2_CHIP_MATCH_HOST:
> +		chip->ident = V4L2_IDENT_NONE;
> +		chip->revision = 0;
> +		return 0;
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +#ifdef CONFIG_VIDEO_ADV_DEBUG
> +static int vidioc_g_register(struct file *file, void *priv,
> +			     struct v4l2_dbg_register *reg)
> +{
> +	struct stk1160 *dev = video_drvdata(file);
> +	int rc;
> +	u8 val;
> +
> +	switch (reg->match.type) {
> +	case V4L2_CHIP_MATCH_AC97:
> +		/* TODO: Support me please :-( */
> +		return -EINVAL;
> +	case V4L2_CHIP_MATCH_I2C_DRIVER:
> +		v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_register, reg);
> +		return 0;
> +	case V4L2_CHIP_MATCH_I2C_ADDR:
> +		/* TODO: is this correct? */
> +		v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_register, reg);
> +		return 0;
> +	default:
> +		if (!v4l2_chip_match_host(&reg->match))
> +			return -EINVAL;
> +	}
> +
> +	/* Match host */
> +	rc = stk1160_read_reg(dev, reg->reg, &val);
> +	reg->val = val;
> +	reg->size = 1;
> +
> +	return rc;
> +}
> +
> +static int vidioc_s_register(struct file *file, void *priv,
> +			     struct v4l2_dbg_register *reg)
> +{
> +	struct stk1160 *dev = video_drvdata(file);
> +
> +	switch (reg->match.type) {
> +	case V4L2_CHIP_MATCH_AC97:
> +		return -EINVAL;
> +	case V4L2_CHIP_MATCH_I2C_DRIVER:
> +		v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_register, reg);
> +		return 0;
> +	case V4L2_CHIP_MATCH_I2C_ADDR:
> +		/* TODO: is this correct? */
> +		v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_register, reg);
> +		return 0;
> +	default:
> +		if (!v4l2_chip_match_host(&reg->match))
> +			return -EINVAL;
> +	}
> +
> +	/* Match host */
> +	return stk1160_write_reg(dev, reg->reg, cpu_to_le16(reg->val));
> +}
> +#endif
> +
> +static const struct v4l2_ioctl_ops stk1160_ioctl_ops = {
> +	.vidioc_querycap      = vidioc_querycap,
> +	.vidioc_enum_fmt_vid_cap  = vidioc_enum_fmt_vid_cap,
> +	.vidioc_g_fmt_vid_cap     = vidioc_g_fmt_vid_cap,
> +	.vidioc_try_fmt_vid_cap   = vidioc_try_fmt_vid_cap,
> +	.vidioc_s_fmt_vid_cap     = vidioc_s_fmt_vid_cap,
> +	.vidioc_querystd      = vidioc_querystd,
> +	.vidioc_g_std         = vidioc_g_std,
> +	.vidioc_s_std         = vidioc_s_std,
> +	.vidioc_enum_input    = vidioc_enum_input,
> +	.vidioc_g_input       = vidioc_g_input,
> +	.vidioc_s_input       = vidioc_s_input,
> +	.vidioc_enum_framesizes = vidioc_enum_framesizes,
> +	.vidioc_enum_frameintervals = vidioc_enum_frameintervals,
> +
> +	/* vb2 takes care of these */
> +	.vidioc_reqbufs       = vidioc_reqbufs,
> +	.vidioc_querybuf      = vidioc_querybuf,
> +	.vidioc_qbuf          = vidioc_qbuf,
> +	.vidioc_dqbuf         = vidioc_dqbuf,
> +	.vidioc_streamon      = vidioc_streamon,
> +	.vidioc_streamoff     = vidioc_streamoff,
> +
> +	.vidioc_log_status  = v4l2_ctrl_log_status,
> +	.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
> +	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
> +	.vidioc_g_chip_ident = vidioc_g_chip_ident,
> +
> +#ifdef CONFIG_VIDEO_ADV_DEBUG
> +	.vidioc_g_register = vidioc_g_register,
> +	.vidioc_s_register = vidioc_s_register,
> +#endif
> +};
> +
> +/********************************************************************/
> +
> +/*
> + * Videobuf2 operations
> + */
> +static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *v4l_fmt,
> +				unsigned int *nbuffers, unsigned int *nplanes,
> +				unsigned int sizes[], void *alloc_ctxs[])
> +{
> +	struct stk1160 *dev = vb2_get_drv_priv(vq);
> +	unsigned long size;
> +
> +	size = dev->width * dev->height * 2;
> +
> +	/*
> +	 * Here we can change the number of buffers being requested.
> +	 * So, we set a minimum and a maximum like this:
> +	 */
> +	*nbuffers = clamp_t(unsigned int, *nbuffers,
> +			STK1160_MIN_VIDEO_BUFFERS, STK1160_MAX_VIDEO_BUFFERS);
> +
> +	/* This means a packed colorformat */
> +	*nplanes = 1;
> +
> +	sizes[0] = size;
> +
> +	stk1160_info("%s: buffer count %d, each %ld bytes\n",
> +			__func__, *nbuffers, size);
> +
> +	return 0;
> +}
> +
> +static void buffer_queue(struct vb2_buffer *vb)
> +{
> +	unsigned long flags;
> +	struct stk1160 *dev = vb2_get_drv_priv(vb->vb2_queue);
> +	struct stk1160_buffer *buf =
> +		container_of(vb, struct stk1160_buffer, vb);
> +
> +	spin_lock_irqsave(&dev->buf_lock, flags);
> +	if (!dev->udev) {
> +		/*
> +		 * If the device is disconnected return the buffer to userspace
> +		 * directly. The next QBUF call will fail with -ENODEV.
> +		 */
> +		vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
> +	} else {
> +
> +		buf->mem = vb2_plane_vaddr(vb, 0);
> +		buf->length = vb2_plane_size(vb, 0);
> +		buf->bytesused = 0;
> +		buf->pos = 0;
> +
> +		/*
> +		 * If buffer length is less from expected then we return
> +		 * the buffer to userspace directly.
> +		 */
> +		if (buf->length < dev->width * dev->height * 2)
> +			vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
> +		else
> +			list_add_tail(&buf->list, &dev->avail_bufs);
> +
> +	}
> +	spin_unlock_irqrestore(&dev->buf_lock, flags);
> +}
> +
> +static int start_streaming(struct vb2_queue *vq, unsigned int count)
> +{
> +	struct stk1160 *dev = vb2_get_drv_priv(vq);
> +	return stk1160_start_streaming(dev);
> +}
> +
> +/* abort streaming and wait for last buffer */
> +static int stop_streaming(struct vb2_queue *vq)
> +{
> +	struct stk1160 *dev = vb2_get_drv_priv(vq);
> +	return stk1160_stop_streaming(dev, true);
> +}
> +
> +static void stk1160_lock(struct vb2_queue *vq)
> +{
> +	struct stk1160 *dev = vb2_get_drv_priv(vq);
> +	mutex_lock(&dev->v4l_lock);
> +}
> +
> +static void stk1160_unlock(struct vb2_queue *vq)
> +{
> +	struct stk1160 *dev = vb2_get_drv_priv(vq);
> +	mutex_unlock(&dev->v4l_lock);
> +}
> +
> +static struct vb2_ops stk1160_video_qops = {
> +	.queue_setup		= queue_setup,
> +	.buf_queue		= buffer_queue,
> +	.start_streaming	= start_streaming,
> +	.stop_streaming		= stop_streaming,
> +	.wait_prepare		= stk1160_unlock,
> +	.wait_finish		= stk1160_lock,
> +};
> +
> +static struct video_device v4l_template = {
> +	.name = "stk1160",
> +	.tvnorms = V4L2_STD_525_60 | V4L2_STD_625_50,
> +	.fops = &stk1160_fops,
> +	.ioctl_ops = &stk1160_ioctl_ops,
> +	.release = video_device_release_empty,
> +};
> +
> +/********************************************************************/
> +
> +int stk1160_vb2_setup(struct stk1160 *dev)
> +{
> +	int rc;
> +	struct vb2_queue *q;
> +
> +	q = &dev->vb_vidq;
> +	memset(q, 0, sizeof(dev->vb_vidq));
> +	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> +	q->io_modes = VB2_READ | VB2_MMAP | VB2_USERPTR;
> +	q->drv_priv = dev;
> +	q->buf_struct_size = sizeof(struct stk1160_buffer);
> +	q->ops = &stk1160_video_qops;
> +	q->mem_ops = &vb2_vmalloc_memops;
> +
> +	rc = vb2_queue_init(q);
> +	if (rc < 0)
> +		return rc;
> +
> +	/* initialize video dma queue */
> +	INIT_LIST_HEAD(&dev->avail_bufs);
> +
> +	return 0;
> +}
> +
> +int stk1160_video_register(struct stk1160 *dev)
> +{
> +	int rc;
> +
> +	/* Initialize video_device with a template structure */
> +	dev->vdev = v4l_template;
> +	dev->vdev.debug = vidioc_debug;
> +
> +	/*
> +	 * Provide a mutex to v4l2 core.
> +	 * It will be used to protect *only* v4l2 ioctls.
> +	 */
> +	dev->vdev.lock = &dev->v4l_lock;
> +
> +	/* This will be used to set video_device parent */
> +	dev->vdev.v4l2_dev = &dev->v4l2_dev;
> +	set_bit(V4L2_FL_USE_FH_PRIO, &dev->vdev.flags);
> +
> +	/* NTSC is default */
> +	dev->norm = V4L2_STD_NTSC_M;
> +	dev->width = 720;
> +	dev->height = 480;
> +
> +	/* set default format */
> +	dev->fmt = &format[0];
> +	stk1160_set_std(dev);
> +
> +	stk1160_ac97_register(dev);
> +
> +	v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std,
> +			dev->norm);
> +
> +	video_set_drvdata(&dev->vdev, dev);
> +	rc = video_register_device(&dev->vdev, VFL_TYPE_GRABBER, -1);
> +	if (rc < 0) {
> +		stk1160_err("video_register_device failed (%d)\n", rc);
> +		return rc;
> +	}
> +
> +	v4l2_info(&dev->v4l2_dev, "V4L2 device registered as %s\n",
> +		  video_device_node_name(&dev->vdev));
> +
> +	return 0;
> +}
> diff --git a/drivers/media/video/stk1160/stk1160-video.c b/drivers/media/video/stk1160/stk1160-video.c
> new file mode 100644
> index 0000000..3785269
> --- /dev/null
> +++ b/drivers/media/video/stk1160/stk1160-video.c
> @@ -0,0 +1,518 @@
> +/*
> + * STK1160 driver
> + *
> + * Copyright (C) 2012 Ezequiel Garcia
> + * <elezegarcia--a.t--gmail.com>
> + *
> + * Based on Easycap driver by R.M. Thomas
> + *	Copyright (C) 2010 R.M. Thomas
> + *	<rmthomas--a.t--sciolus.org>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <linux/module.h>
> +#include <linux/usb.h>
> +#include <linux/slab.h>
> +#include <linux/ratelimit.h>
> +
> +#include "stk1160.h"
> +
> +static unsigned int debug;
> +module_param(debug, int, 0644);
> +MODULE_PARM_DESC(debug, "enable debug messages");
> +
> +static inline void print_err_status(struct stk1160 *dev,
> +				     int packet, int status)
> +{
> +	char *errmsg = "Unknown";
> +
> +	switch (status) {
> +	case -ENOENT:
> +		errmsg = "unlinked synchronuously";
> +		break;
> +	case -ECONNRESET:
> +		errmsg = "unlinked asynchronuously";
> +		break;
> +	case -ENOSR:
> +		errmsg = "Buffer error (overrun)";
> +		break;
> +	case -EPIPE:
> +		errmsg = "Stalled (device not responding)";
> +		break;
> +	case -EOVERFLOW:
> +		errmsg = "Babble (bad cable?)";
> +		break;
> +	case -EPROTO:
> +		errmsg = "Bit-stuff error (bad cable?)";
> +		break;
> +	case -EILSEQ:
> +		errmsg = "CRC/Timeout (could be anything)";
> +		break;
> +	case -ETIME:
> +		errmsg = "Device does not respond";
> +		break;
> +	}
> +
> +	if (packet < 0)
> +		printk_ratelimited(KERN_WARNING "URB status %d [%s].\n",
> +				status, errmsg);
> +	else
> +		printk_ratelimited(KERN_INFO "URB packet %d, status %d [%s].\n",
> +			       packet, status, errmsg);
> +}
> +
> +static inline
> +struct stk1160_buffer *stk1160_next_buffer(struct stk1160 *dev)
> +{
> +	struct stk1160_buffer *buf = NULL;
> +	unsigned long flags = 0;
> +
> +	/* Current buffer must be NULL when this functions gets called */
> +	BUG_ON(dev->isoc_ctl.buf);
> +
> +	spin_lock_irqsave(&dev->buf_lock, flags);
> +	if (!list_empty(&dev->avail_bufs)) {
> +		buf = list_first_entry(&dev->avail_bufs,
> +				struct stk1160_buffer, list);
> +		list_del(&buf->list);
> +	}
> +	spin_unlock_irqrestore(&dev->buf_lock, flags);
> +
> +	return buf;
> +}
> +
> +static inline
> +void stk1160_buffer_done(struct stk1160 *dev)
> +{
> +	struct stk1160_buffer *buf = dev->isoc_ctl.buf;
> +
> +	dev->field_count++;
> +
> +	buf->vb.v4l2_buf.sequence = dev->field_count >> 1;
> +	buf->vb.v4l2_buf.field = V4L2_FIELD_INTERLACED;
> +	buf->vb.v4l2_buf.bytesused = buf->bytesused;
> +	do_gettimeofday(&buf->vb.v4l2_buf.timestamp);
> +
> +	vb2_set_plane_payload(&buf->vb, 0, buf->bytesused);
> +	vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE);
> +
> +	dev->isoc_ctl.buf = NULL;
> +}
> +
> +static inline
> +void stk1160_copy_video(struct stk1160 *dev, u8 *src, int len)
> +{
> +	int linesdone, lineoff, lencopy;
> +	int bytesperline = dev->width * 2;
> +	struct stk1160_buffer *buf = dev->isoc_ctl.buf;
> +	u8 *dst = buf->mem;
> +	int remain;
> +
> +	/*
> +	 * TODO: These stk1160_dbg are very spammy!
> +	 * We should 1) check why we are getting them
> +	 * and 2) add ratelimit.
> +	 *
> +	 * UPDATE: One of the reasons (the only one?) for getting these
> +	 * is incorrect standard (mismatch between expected and configured).
> +	 * So perhaps, we could add a counter for errors. When the counter
> +	 * reaches some value, we simply stop streaming.
> +	 */
> +
> +	len -= 4;
> +	src += 4;
> +
> +	remain = len;
> +
> +	linesdone = buf->pos / bytesperline;
> +	lineoff = buf->pos % bytesperline; /* offset in current line */
> +
> +	if (!buf->odd)
> +		dst += bytesperline;
> +
> +	/* Multiply linesdone by two, to take account of the other field */
> +	dst += linesdone * bytesperline * 2 + lineoff;
> +
> +	/* Copy the remaining of current line */
> +	if (remain < (bytesperline - lineoff))
> +		lencopy = remain;
> +	else
> +		lencopy = bytesperline - lineoff;
> +
> +	/*
> +	 * Check if we have enough space left in the buffer.
> +	 * In that case, we force loop exit after copy.
> +	 */
> +	if (lencopy > buf->bytesused - buf->length) {
> +		lencopy = buf->bytesused - buf->length;
> +		remain = lencopy;
> +	}
> +
> +	/* Check if the copy is done */
> +	if (lencopy == 0 || remain == 0)
> +		return;
> +
> +	/* Let the bug hunt begin! sanity checks! */
> +	if (lencopy < 0) {
> +		stk1160_dbg("copy skipped: negative lencopy\n");
> +		return;
> +	}
> +
> +	if ((unsigned long)dst + lencopy >
> +		(unsigned long)buf->mem + buf->length) {
> +		printk_ratelimited(KERN_WARNING "stk1160: buffer overflow detected\n");
> +		return;
> +	}
> +
> +	memcpy(dst, src, lencopy);
> +
> +	buf->bytesused += lencopy;
> +	buf->pos += lencopy;
> +	remain -= lencopy;
> +
> +	/* Copy current field line by line, interlacing with the other field */
> +	while (remain > 0) {
> +
> +		dst += lencopy + bytesperline;
> +		src += lencopy;
> +
> +		/* Copy one line at a time */
> +		if (remain < bytesperline)
> +			lencopy = remain;
> +		else
> +			lencopy = bytesperline;
> +
> +		/*
> +		 * Check if we have enough space left in the buffer.
> +		 * In that case, we force loop exit after copy.
> +		 */
> +		if (lencopy > buf->bytesused - buf->length) {
> +			lencopy = buf->bytesused - buf->length;
> +			remain = lencopy;
> +		}
> +
> +		/* Check if the copy is done */
> +		if (lencopy == 0 || remain == 0)
> +			return;
> +
> +		if (lencopy < 0) {
> +			printk_ratelimited(KERN_WARNING "stk1160: negative lencopy detected\n");
> +			return;
> +		}
> +
> +		if ((unsigned long)dst + lencopy >
> +			(unsigned long)buf->mem + buf->length) {
> +			printk_ratelimited(KERN_WARNING "stk1160: buffer overflow detected\n");
> +			return;
> +		}
> +
> +		memcpy(dst, src, lencopy);
> +		remain -= lencopy;
> +
> +		buf->bytesused += lencopy;
> +		buf->pos += lencopy;
> +	}
> +}
> +
> +/*
> + * Controls the isoc copy of each urb packet
> + */
> +static void stk1160_process_isoc(struct stk1160 *dev, struct urb *urb)
> +{
> +	int i, len, status;
> +	u8 *p;
> +
> +	if (!dev) {
> +		stk1160_warn("%s called with null device\n", __func__);
> +		return;
> +	}
> +
> +	if (urb->status < 0) {
> +		/* Print status and drop current packet (or field?) */
> +		print_err_status(dev, -1, urb->status);
> +		return;
> +	}
> +
> +	for (i = 0; i < urb->number_of_packets; i++) {
> +		status = urb->iso_frame_desc[i].status;
> +		if (status < 0) {
> +			print_err_status(dev, i, status);
> +			continue;
> +		}
> +
> +		/* Get packet actual length and pointer to data */
> +		p = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
> +		len = urb->iso_frame_desc[i].actual_length;
> +
> +		/* Empty packet */
> +		if (len <= 4)
> +			continue;
> +
> +		/*
> +		 * An 8-byte packet sequence means end of field.
> +		 * So if we don't have any packet, we start receiving one now
> +		 * and if we do have a packet, then we are done with it.
> +		 *
> +		 * These end of field packets are always 0xc0 or 0x80,
> +		 * but not always 8-byte long so we don't check packet length.
> +		 */
> +		if (p[0] == 0xc0) {
> +
> +			/*
> +			 * If first byte is 0xc0 then we received
> +			 * second field, and frame has ended.
> +			 */
> +			if (dev->isoc_ctl.buf != NULL)
> +				stk1160_buffer_done(dev);
> +
> +			dev->isoc_ctl.buf = stk1160_next_buffer(dev);
> +			if (dev->isoc_ctl.buf == NULL)
> +				return;
> +		}
> +
> +		/*
> +		 * If we don't have a buffer here, then it means we
> +		 * haven't found the start mark sequence.
> +		 */
> +		if (dev->isoc_ctl.buf == NULL)
> +			continue;
> +
> +		if (p[0] == 0xc0 || p[0] == 0x80) {
> +
> +			/* We set next packet parity and
> +			 * continue to get next one
> +			 */
> +			dev->isoc_ctl.buf->odd = *p & 0x40;
> +			dev->isoc_ctl.buf->pos = 0;
> +			continue;
> +		}
> +
> +		stk1160_copy_video(dev, p, len);
> +	}
> +}
> +
> +
> +/*
> + * IRQ callback, called by URB callback
> + */
> +static void stk1160_isoc_irq(struct urb *urb)
> +{
> +	int i, rc;
> +	struct stk1160 *dev = urb->context;
> +
> +	switch (urb->status) {
> +	case 0:
> +		break;
> +	case -ECONNRESET:   /* kill */
> +	case -ENOENT:
> +	case -ESHUTDOWN:
> +		/* TODO: check uvc driver: he frees the queue here */
> +		return;
> +	default:
> +		stk1160_err("urb error! status %d\n", urb->status);
> +		return;
> +	}
> +
> +	stk1160_process_isoc(dev, urb);
> +
> +	/* Reset urb buffers */
> +	for (i = 0; i < urb->number_of_packets; i++) {
> +		urb->iso_frame_desc[i].status = 0;
> +		urb->iso_frame_desc[i].actual_length = 0;
> +	}
> +
> +	rc = usb_submit_urb(urb, GFP_ATOMIC);
> +	if (rc)
> +		stk1160_err("urb re-submit failed (%d)\n", rc);
> +}
> +
> +/*
> + * Cancel urbs
> + * This function can't be called in atomic context
> + */
> +void stk1160_cancel_isoc(struct stk1160 *dev)
> +{
> +	int i;
> +
> +	/*
> +	 * This check is not necessary, but we add it
> +	 * to avoid a spurious debug message
> +	 */
> +	if (!dev->isoc_ctl.num_bufs)
> +		return;
> +
> +	stk1160_dbg("killing urbs...\n");
> +
> +	for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
> +
> +		/*
> +		 * To kill urbs we can't be in atomic context.
> +		 * We don't care for NULL pointer since
> +		 * usb_kill_urb allows it.
> +		 */
> +		usb_kill_urb(dev->isoc_ctl.urb[i]);
> +	}
> +
> +	stk1160_dbg("all urbs killed\n");
> +}
> +
> +/*
> + * Releases urb and transfer buffers
> + * Obviusly, associated urb must be killed before releasing it.
> + */
> +void stk1160_free_isoc(struct stk1160 *dev)
> +{
> +	struct urb *urb;
> +	int i;
> +
> +	stk1160_dbg("freeing urb buffers...\n");
> +
> +	for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
> +
> +		urb = dev->isoc_ctl.urb[i];
> +		if (urb) {
> +
> +			if (dev->isoc_ctl.transfer_buffer[i]) {
> +#ifndef CONFIG_DMA_NONCOHERENT
> +				usb_free_coherent(dev->udev,
> +					urb->transfer_buffer_length,
> +					dev->isoc_ctl.transfer_buffer[i],
> +					urb->transfer_dma);
> +#else
> +				kfree(dev->isoc_ctl.transfer_buffer[i]);
> +#endif
> +			}
> +			usb_free_urb(urb);
> +			dev->isoc_ctl.urb[i] = NULL;
> +		}
> +		dev->isoc_ctl.transfer_buffer[i] = NULL;
> +	}
> +
> +	kfree(dev->isoc_ctl.urb);
> +	kfree(dev->isoc_ctl.transfer_buffer);
> +
> +	dev->isoc_ctl.urb = NULL;
> +	dev->isoc_ctl.transfer_buffer = NULL;
> +	dev->isoc_ctl.num_bufs = 0;
> +
> +	stk1160_dbg("all urb buffers freed\n");
> +}
> +
> +/*
> + * Helper for cancelling and freeing urbs
> + * This function can't be called in atomic context
> + */
> +void stk1160_uninit_isoc(struct stk1160 *dev)
> +{
> +	stk1160_cancel_isoc(dev);
> +	stk1160_free_isoc(dev);
> +}
> +
> +/*
> + * Allocate URBs
> + */
> +int stk1160_alloc_isoc(struct stk1160 *dev)
> +{
> +	struct urb *urb;
> +	int i, j, k, sb_size, max_packets, num_bufs;
> +
> +	/*
> +	 * It may be necessary to release isoc here,
> +	 * since isoc are only released on disconnection.
> +	 * (see new_pkt_size flag)
> +	 */
> +	if (dev->isoc_ctl.num_bufs)
> +		stk1160_uninit_isoc(dev);
> +
> +	stk1160_dbg("allocating urbs...\n");
> +
> +	num_bufs = STK1160_NUM_BUFS;
> +	max_packets = STK1160_NUM_PACKETS;
> +	sb_size = max_packets * dev->max_pkt_size;
> +
> +	dev->isoc_ctl.buf = NULL;
> +	dev->isoc_ctl.max_pkt_size = dev->max_pkt_size;
> +	dev->isoc_ctl.urb = kzalloc(sizeof(void *)*num_bufs, GFP_KERNEL);
> +	if (!dev->isoc_ctl.urb) {
> +		stk1160_err("out of memory for urb array\n");
> +		return -ENOMEM;
> +	}
> +
> +	dev->isoc_ctl.transfer_buffer = kzalloc(sizeof(void *)*num_bufs,
> +					      GFP_KERNEL);
> +	if (!dev->isoc_ctl.transfer_buffer) {
> +		stk1160_err("out of memory for usb transfers\n");
> +		kfree(dev->isoc_ctl.urb);
> +		return -ENOMEM;
> +	}
> +
> +	/* allocate urbs and transfer buffers */
> +	for (i = 0; i < num_bufs; i++) {
> +
> +		urb = usb_alloc_urb(max_packets, GFP_KERNEL);
> +		if (!urb) {
> +			stk1160_err("cannot alloc urb[%d]\n", i);
> +			stk1160_uninit_isoc(dev);
> +			return -ENOMEM;
> +		}
> +		dev->isoc_ctl.urb[i] = urb;
> +
> +#ifndef CONFIG_DMA_NONCOHERENT
> +		dev->isoc_ctl.transfer_buffer[i] = usb_alloc_coherent(dev->udev,
> +			sb_size, GFP_KERNEL, &urb->transfer_dma);
> +#else
> +		dev->isoc_ctl.transfer_buffer[i] = kmalloc(sb_size, GFP_KERNEL);
> +#endif
> +		if (!dev->isoc_ctl.transfer_buffer[i]) {
> +			stk1160_err("cannot alloc %d bytes for tx buffer\n",
> +				sb_size);
> +			stk1160_uninit_isoc(dev);
> +			return -ENOMEM;
> +		}
> +		memset(dev->isoc_ctl.transfer_buffer[i], 0, sb_size);
> +
> +		/*
> +		 * FIXME: Where can I get the endpoint?
> +		 */
> +		urb->dev = dev->udev;
> +		urb->pipe = usb_rcvisocpipe(dev->udev, STK1160_EP_VIDEO);
> +		urb->transfer_buffer = dev->isoc_ctl.transfer_buffer[i];
> +		urb->transfer_buffer_length = sb_size;
> +		urb->complete = stk1160_isoc_irq;
> +		urb->context = dev;
> +		urb->interval = 1;
> +		urb->start_frame = 0;
> +		urb->number_of_packets = max_packets;
> +#ifndef CONFIG_DMA_NONCOHERENT
> +		urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
> +#else
> +		urb->transfer_flags = URB_ISO_ASAP;
> +#endif
> +
> +		k = 0;
> +		for (j = 0; j < max_packets; j++) {
> +			urb->iso_frame_desc[j].offset = k;
> +			urb->iso_frame_desc[j].length =
> +					dev->isoc_ctl.max_pkt_size;
> +			k += dev->isoc_ctl.max_pkt_size;
> +		}
> +	}
> +
> +	stk1160_dbg("urbs allocated\n");
> +
> +	/* At last we can say we have some buffers */
> +	dev->isoc_ctl.num_bufs = num_bufs;
> +
> +	return 0;
> +}
> +
> diff --git a/drivers/media/video/stk1160/stk1160.h b/drivers/media/video/stk1160/stk1160.h
> new file mode 100644
> index 0000000..8cf3001
> --- /dev/null
> +++ b/drivers/media/video/stk1160/stk1160.h
> @@ -0,0 +1,202 @@
> +/*
> + * STK1160 driver
> + *
> + * Copyright (C) 2012 Ezequiel Garcia
> + * <elezegarcia--a.t--gmail.com>
> + *
> + * Based on Easycap driver by R.M. Thomas
> + *	Copyright (C) 2010 R.M. Thomas
> + *	<rmthomas--a.t--sciolus.org>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <linux/i2c.h>
> +#include <sound/core.h>
> +#include <sound/ac97_codec.h>
> +#include <media/videobuf2-core.h>
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-ctrls.h>
> +
> +#define STK1160_VERSION		"0.9.3"
> +#define STK1160_VERSION_NUM	0x000903
> +
> +/* TODO: Decide on number of packets for each buffer */
> +#define STK1160_NUM_PACKETS 64
> +
> +/* Number of buffers for isoc transfers */
> +#define STK1160_NUM_BUFS 16 /* TODO */
> +
> +/* TODO: This endpoint address should be retrieved */
> +#define STK1160_EP_VIDEO 0x82
> +#define STK1160_EP_AUDIO 0x81
> +
> +/* Max and min video buffers */
> +#define STK1160_MIN_VIDEO_BUFFERS 8
> +#define STK1160_MAX_VIDEO_BUFFERS 32
> +
> +#define STK1160_MIN_PKT_SIZE 3072
> +
> +#define STK1160_MAX_INPUT 3
> +
> +#define STK1160_I2C_TIMEOUT 100
> +
> +/* TODO: Print helpers
> + * I could use dev_xxx, pr_xxx, v4l2_xxx or printk.
> + * However, there isn't a solid consensus on which
> + * new drivers should use.
> + *
> + */
> +#define DEBUG
> +#ifdef DEBUG
> +#define stk1160_dbg(fmt, args...) \
> +	printk(KERN_DEBUG "stk1160: " fmt,  ## args)
> +#else
> +#define stk1160_dbg(fmt, args...)
> +#endif
> +
> +#define stk1160_info(fmt, args...) \
> +	pr_info("stk1160: " fmt, ## args)
> +
> +#define stk1160_warn(fmt, args...) \
> +	pr_warn("stk1160: " fmt, ## args)
> +
> +#define stk1160_err(fmt, args...) \
> +	pr_err("stk1160: " fmt, ## args)
> +
> +/* Buffer for one video frame */
> +struct stk1160_buffer {
> +	/* common v4l buffer stuff -- must be first */
> +	struct vb2_buffer vb;
> +	struct list_head list;
> +
> +	void *mem;
> +	unsigned int length;		/* buffer length */
> +	unsigned int bytesused;		/* bytes written */
> +	int odd;			/* current oddity */
> +
> +	/*
> +	 * Since we interlace two fields per frame,
> +	 * this is different from bytesused.
> +	 */
> +	unsigned int pos;		/* current pos inside buffer */
> +};
> +
> +struct stk1160_isoc_ctl {
> +	/* max packet size of isoc transaction */
> +	int max_pkt_size;
> +
> +	/* number of allocated urbs */
> +	int num_bufs;
> +
> +	/* urb for isoc transfers */
> +	struct urb **urb;
> +
> +	/* transfer buffers for isoc transfer */
> +	char **transfer_buffer;
> +
> +	/* current buffer */
> +	struct stk1160_buffer *buf;
> +};
> +
> +struct stk1160_fmt {
> +	char  *name;
> +	u32   fourcc;          /* v4l2 format id */
> +	int   depth;
> +};
> +
> +struct stk1160 {
> +	struct v4l2_device v4l2_dev;
> +	struct video_device vdev;
> +	struct v4l2_ctrl_handler ctrl_handler;
> +
> +	struct device *dev;
> +	struct usb_device *udev;
> +
> +	/* saa7115 subdev */
> +	struct v4l2_subdev *sd_saa7115;
> +
> +	/* isoc control struct */
> +	struct list_head avail_bufs;
> +
> +	/* video capture */
> +	struct vb2_queue vb_vidq;
> +
> +	/* max packet size of isoc transaction */
> +	int max_pkt_size;
> +	/* array of wMaxPacketSize */
> +	unsigned int *alt_max_pkt_size;
> +	/* alternate */
> +	int alt;
> +	/* Number of alternative settings */
> +	int num_alt;
> +
> +	struct stk1160_isoc_ctl isoc_ctl;
> +	char urb_buf[255];	 /* urb control msg buffer */
> +
> +	/* frame properties */
> +	int width;		  /* current frame width */
> +	int height;		  /* current frame height */
> +	unsigned int ctl_input;	  /* selected input */
> +	v4l2_std_id norm;	  /* current norm */
> +	struct stk1160_fmt *fmt;  /* selected format */
> +
> +	unsigned int field_count; /* not sure ??? */
> +	enum v4l2_field field;    /* also not sure :/ */
> +
> +	/* i2c i/o */
> +	struct i2c_adapter i2c_adap;
> +	struct i2c_client i2c_client;
> +
> +	struct mutex v4l_lock;
> +	spinlock_t buf_lock;
> +
> +	struct file *fh_owner;	/* filehandle ownership */
> +
> +	/* EXPERIMENTAL */
> +	struct snd_card *snd_card;
> +};
> +
> +struct regval {
> +	u16 reg;
> +	u16 val;
> +};
> +
> +/* Provided by stk1160-v4l.c */
> +int stk1160_vb2_setup(struct stk1160 *dev);
> +int stk1160_video_register(struct stk1160 *dev);
> +void stk1160_video_unregister(struct stk1160 *dev);
> +int stk1160_stop_streaming(struct stk1160 *dev, bool connected);
> +
> +/* Provided by stk1160-video.c */
> +int stk1160_alloc_isoc(struct stk1160 *dev);
> +void stk1160_free_isoc(struct stk1160 *dev);
> +void stk1160_cancel_isoc(struct stk1160 *dev);
> +void stk1160_uninit_isoc(struct stk1160 *dev);
> +
> +/* Provided by stk1160-i2c.c */
> +int stk1160_i2c_register(struct stk1160 *dev);
> +int stk1160_i2c_unregister(struct stk1160 *dev);
> +
> +/* Provided by stk1160-core.c */
> +int stk1160_read_reg(struct stk1160 *dev, u16 reg, u8 *value);
> +int stk1160_write_reg(struct stk1160 *dev, u16 reg, u16 value);
> +int stk1160_write_regs_req(struct stk1160 *dev, u8 req, u16 reg,
> +		char *buf, int len);
> +int stk1160_read_reg_req_len(struct stk1160 *dev, u8 req, u16 reg,
> +		char *buf, int len);
> +void stk1160_select_input(struct stk1160 *dev);
> +
> +/* Provided by stk1160-ac97.c */
> +int stk1160_ac97_register(struct stk1160 *dev);
> +int stk1160_ac97_unregister(struct stk1160 *dev);
> +
> 


--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux