lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <524E86DE.2040503@xs4all.nl>
Date:	Fri, 04 Oct 2013 11:14:06 +0200
From:	Hans Verkuil <hverkuil@...all.nl>
To:	Jon Arne Jørgensen <jonarne@...arne.no>
CC:	linux-media@...r.kernel.org, m.chehab@...sung.com,
	rdunlap@...radead.org, hans.verkuil@...co.com, mkrufky@...uxtv.org,
	lkundrak@...sk, linux-kernel@...r.kernel.org
Subject: Re: [RFC v3] Add a driver for the Somagic smi2021 chip

On 09/01/2013 09:42 PM, Jon Arne Jørgensen wrote:
> This chip is found in a series of usb video capture devices
> branded as Easycap.
> 
> On first insertion, the device will identify as 0x1c88:0x0007.
> This is just a bootloader stage. After uploading the firmware, the
> device will reconnect with usb product id 0x003c, 0x003d, 0x003e or 0x003f
> depending on the firmware.
> 
> The device uses the gm7113c chip for video ADC, this is a clone of the
> saa7113 chip. This chip is controlled over i2c-bus from the bridge
> chip by a proprietary usb control transfer.

This patch slipped through, but, better late than never, here is my review.

> 
> The device also has a CirrusLogic CS5340 chip for audio ADC.
> 
> Signed-off-by: Jon Arne Jørgensen <jonarne@...arne.no>
> ---
>  drivers/media/usb/Kconfig                      |   1 +
>  drivers/media/usb/Makefile                     |   1 +
>  drivers/media/usb/smi2021/Kconfig              |  11 +
>  drivers/media/usb/smi2021/Makefile             |   9 +
>  drivers/media/usb/smi2021/smi2021.h            | 193 +++++
>  drivers/media/usb/smi2021/smi2021_audio.c      | 401 +++++++++++
>  drivers/media/usb/smi2021/smi2021_bootloader.c | 256 +++++++
>  drivers/media/usb/smi2021/smi2021_main.c       | 952 +++++++++++++++++++++++++
>  drivers/media/usb/smi2021/smi2021_v4l2.c       | 277 +++++++
>  9 files changed, 2101 insertions(+)
>  create mode 100644 drivers/media/usb/smi2021/Kconfig
>  create mode 100644 drivers/media/usb/smi2021/Makefile
>  create mode 100644 drivers/media/usb/smi2021/smi2021.h
>  create mode 100644 drivers/media/usb/smi2021/smi2021_audio.c
>  create mode 100644 drivers/media/usb/smi2021/smi2021_bootloader.c
>  create mode 100644 drivers/media/usb/smi2021/smi2021_main.c
>  create mode 100644 drivers/media/usb/smi2021/smi2021_v4l2.c
> 
> diff --git a/drivers/media/usb/Kconfig b/drivers/media/usb/Kconfig
> index cfe8056..da6376c 100644
> --- a/drivers/media/usb/Kconfig
> +++ b/drivers/media/usb/Kconfig
> @@ -28,6 +28,7 @@ source "drivers/media/usb/hdpvr/Kconfig"
>  source "drivers/media/usb/tlg2300/Kconfig"
>  source "drivers/media/usb/usbvision/Kconfig"
>  source "drivers/media/usb/stk1160/Kconfig"
> +source "drivers/media/usb/smi2021/Kconfig"
>  endif
>  
>  if (MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT)
> diff --git a/drivers/media/usb/Makefile b/drivers/media/usb/Makefile
> index 0935f47..0e4fda5 100644
> --- a/drivers/media/usb/Makefile
> +++ b/drivers/media/usb/Makefile
> @@ -21,3 +21,4 @@ obj-$(CONFIG_VIDEO_CX231XX) += cx231xx/
>  obj-$(CONFIG_VIDEO_TM6000) += tm6000/
>  obj-$(CONFIG_VIDEO_EM28XX) += em28xx/
>  obj-$(CONFIG_VIDEO_USBTV) += usbtv/
> +obj-$(CONFIG_VIDEO_SMI2021) += smi2021/
> diff --git a/drivers/media/usb/smi2021/Kconfig b/drivers/media/usb/smi2021/Kconfig
> new file mode 100644
> index 0000000..6a6fb8a
> --- /dev/null
> +++ b/drivers/media/usb/smi2021/Kconfig
> @@ -0,0 +1,11 @@
> +config VIDEO_SMI2021
> +	tristate "Somagic SMI2021 USB video/audio capture support"
> +	depends on VIDEO_DEV && I2C && SND && USB
> +	select VIDEOBUF2_VMALLOC
> +	select VIDEO_SAA711X
> +	select SND_PCM
> +	help
> +	  This is a video4linux driver for SMI2021 based video capture devices.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called smi2021
> diff --git a/drivers/media/usb/smi2021/Makefile b/drivers/media/usb/smi2021/Makefile
> new file mode 100644
> index 0000000..b04ff4e
> --- /dev/null
> +++ b/drivers/media/usb/smi2021/Makefile
> @@ -0,0 +1,9 @@
> +smi2021-y := smi2021_main.o		\
> +	     smi2021_bootloader.o	\
> +	     smi2021_v4l2.o		\
> +	     smi2021_audio.o		\
> +
> +
> +obj-$(CONFIG_VIDEO_SMI2021) += smi2021.o
> +
> +ccflags-y += -Idrivers/media/i2c
> diff --git a/drivers/media/usb/smi2021/smi2021.h b/drivers/media/usb/smi2021/smi2021.h
> new file mode 100644
> index 0000000..70d3254
> --- /dev/null
> +++ b/drivers/media/usb/smi2021/smi2021.h
> @@ -0,0 +1,193 @@
> +/************************************************************************
> + * smi2021.h								*
> + *									*
> + * USB Driver for SMI2021 - EasyCap					*
> + * **********************************************************************
> + *
> + * Copyright 2011-2013 Jon Arne Jørgensen
> + * <jonjon.arnearne--a.t--gmail.com>
> + *
> + * Copyright 2011, 2012 Tony Brown, Michal Demin, Jeffry Johnston
> + *
> + * 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.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, see <http://www.gnu.org/licenses/>.
> + *
> + * This driver is heavily influensed by the STK1160 driver.
> + * Copyright (C) 2012 Ezequiel Garcia
> + * <elezegarcia--a.t--gmail.com>
> + *
> + */
> +
> +#ifndef SMI2021_H
> +#define SMI2021_H
> +
> +#include <linux/module.h>
> +#include <linux/usb.h>
> +#include <linux/i2c.h>
> +
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-ioctl.h>
> +#include <media/v4l2-event.h>
> +#include <media/v4l2-ctrls.h>
> +#include <media/videobuf2-core.h>
> +#include <media/videobuf2-vmalloc.h>
> +#include <media/saa7115.h>
> +
> +#include <sound/core.h>
> +#include <sound/pcm.h>
> +#include <sound/pcm_params.h>
> +#include <sound/initval.h>
> +
> +#define SMI2021_DRIVER_VERSION "0.1"
> +
> +#define SMI2021_ISOC_TRANSFERS	16
> +#define SMI2021_ISOC_PACKETS	10
> +#define SMI2021_ISOC_EP		0x82
> +
> +/* General USB control setup */
> +#define SMI2021_USB_REQUEST	0x01
> +#define SMI2021_USB_INDEX	0x00
> +#define SMI2021_USB_SNDPIPE	0x00
> +#define SMI2021_USB_RCVPIPE	0x80
> +
> +/* General video constants */
> +#define SMI2021_BYTES_PER_LINE	1440
> +#define SMI2021_PAL_LINES	576
> +#define SMI2021_NTSC_LINES	484
> +
> +/* Timing Referance Codes, see saa7113 datasheet */
> +#define SMI2021_TRC_EAV		0x10
> +#define SMI2021_TRC_VBI		0x20
> +#define SMI2021_TRC_FIELD_2	0x40
> +#define SMI2021_TRC		0x80
> +
> +#ifdef DEBUG
> +#define smi2021_dbg(fmt, args...)		\
> +	pr_debug("smi2021::%s: " fmt, __func__, \
> +			##args)
> +#else
> +#define smi2021_dbg(fmt, args...)
> +#endif
> +
> +#define smi2021_info(fmt, args...)		\
> +	pr_info("smi2021::%s: " fmt,		\
> +		__func__, ##args)
> +
> +#define smi2021_warn(fmt, args...)		\
> +	pr_warn("smi2021::%s: " fmt,		\
> +		__func__, ##args)
> +
> +#define smi2021_err(fmt, args...)		\
> +	pr_err("smi2021::%s: " fmt,		\
> +		__func__, ##args)
> +
> +/* Structs passed on USB for device setup */
> +struct smi2021_set_hw_state {
> +	u8 head;
> +	u8 state;
> +} __packed;
> +
> +/* A single videobuf2 frame buffer */
> +struct smi2021_buf {
> +	/* Common vb2 stuff, must be first */
> +	struct vb2_buffer		vb;
> +	struct list_head		list;
> +
> +	void				*mem;
> +	unsigned int			length;
> +
> +	bool				active;
> +	bool				second_field;
> +	bool				in_blank;
> +	unsigned int			pos;
> +
> +	/* ActiveVideo - Line counter */
> +	u16				trc_av;
> +};
> +
> +struct smi2021_vid_input {
> +	char				*name;
> +	int				type;
> +};
> +
> +enum smi2021_sync {
> +	HSYNC,
> +	SYNCZ1,
> +	SYNCZ2,
> +	TRC
> +};
> +
> +struct smi2021 {
> +	struct device			*dev;
> +	struct usb_device		*udev;
> +	struct i2c_adapter		i2c_adap;
> +	struct i2c_client		i2c_client;
> +	struct v4l2_ctrl_handler	ctrl_handler;
> +	struct v4l2_subdev		*gm7113c_subdev;
> +	struct v4l2_device		v4l2_dev;
> +	struct video_device		vdev;
> +	struct vb2_queue		vb2q;
> +	struct mutex			v4l2_lock;
> +	struct mutex			vb2q_lock;
> +
> +	/* List of videobuf2 buffers protected by a lock. */
> +	spinlock_t			buf_lock;
> +	struct list_head		bufs;
> +	struct smi2021_buf		*cur_buf;
> +
> +	int				sequence;
> +
> +	/* Frame settings */
> +	int				cur_height;
> +	v4l2_std_id			cur_norm;
> +	enum smi2021_sync		sync_state;
> +
> +	struct snd_card			*snd_card;
> +	struct snd_pcm_substream	*pcm_substream;
> +
> +	unsigned int			pcm_write_ptr;
> +	unsigned int			pcm_complete_samples;
> +
> +	u8				pcm_read_offset;
> +	struct work_struct		adev_capture_trigger;
> +	atomic_t			adev_capturing;
> +
> +	/* Device settings */
> +	unsigned int		vid_input_count;
> +	const struct smi2021_vid_input	*vid_inputs;
> +	int				cur_input;
> +
> +	int				iso_size;
> +	struct urb			*isoc_urbs[SMI2021_ISOC_TRANSFERS];
> +};
> +
> +/* Provided by smi2021_bootloader.c */
> +int smi2021_bootloader_probe(struct usb_interface *intf,
> +					const struct usb_device_id *devid);
> +void smi2021_bootloader_disconnect(struct usb_interface *intf);
> +
> +/* Provided by smi2021_main.c */
> +void smi2021_toggle_audio(struct smi2021 *smi2021, bool enable);
> +int smi2021_start(struct smi2021 *smi2021);
> +void smi2021_stop(struct smi2021 *smi2021);
> +
> +/* Provided by smi2021_v4l2.c */
> +int smi2021_vb2_setup(struct smi2021 *smi2021);
> +int smi2021_video_register(struct smi2021 *smi2021);
> +
> +/* Provided by smi2021_audio.c */
> +int smi2021_snd_register(struct smi2021 *smi2021);
> +void smi2021_snd_unregister(struct smi2021 *smi2021);
> +void smi2021_stop_audio(struct smi2021 *smi2021);
> +void smi2021_audio(struct smi2021 *smi2021, u8 *data, int len);
> +#endif /* SMI2021_H */
> diff --git a/drivers/media/usb/smi2021/smi2021_audio.c b/drivers/media/usb/smi2021/smi2021_audio.c
> new file mode 100644
> index 0000000..b4d6e76
> --- /dev/null
> +++ b/drivers/media/usb/smi2021/smi2021_audio.c
> @@ -0,0 +1,401 @@
> +/************************************************************************
> + * smi2021_audio.c							*
> + *									*
> + * USB Driver for SMI2021 - EasyCap					*
> + * **********************************************************************
> + *
> + * Copyright 2011-2013 Jon Arne Jørgensen
> + * <jonjon.arnearne--a.t--gmail.com>
> + *
> + * Copyright 2011, 2012 Tony Brown, Michal Demin, Jeffry Johnston
> + *
> + * 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.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, see <http://www.gnu.org/licenses/>.
> + *
> + * This driver is heavily influensed by the STK1160 driver.
> + * Copyright (C) 2012 Ezequiel Garcia
> + * <elezegarcia--a.t--gmail.com>
> + *
> + */
> +
> +#include "smi2021.h"
> +
> +static void pcm_buffer_free(struct snd_pcm_substream *substream)
> +{
> +	vfree(substream->runtime->dma_area);
> +	substream->runtime->dma_area = NULL;
> +	substream->runtime->dma_bytes = 0;
> +}
> +
> +static int pcm_buffer_alloc(struct snd_pcm_substream *substream, int size)
> +{
> +	if (substream->runtime->dma_area) {
> +		if (substream->runtime->dma_bytes > size)
> +			return 0;
> +		pcm_buffer_free(substream);
> +	}
> +
> +	substream->runtime->dma_area = vmalloc(size);
> +	if (substream->runtime->dma_area == NULL)
> +		return -ENOMEM;
> +
> +	substream->runtime->dma_bytes = size;
> +
> +	return 0;
> +}
> +
> +static const struct snd_pcm_hardware smi2021_pcm_hw = {
> +	.info = SNDRV_PCM_INFO_BLOCK_TRANSFER |
> +		SNDRV_PCM_INFO_INTERLEAVED    |
> +		SNDRV_PCM_INFO_MMAP           |
> +		SNDRV_PCM_INFO_MMAP_VALID     |
> +		SNDRV_PCM_INFO_BATCH,
> +
> +	.formats = SNDRV_PCM_FMTBIT_S32_LE,
> +
> +	.rates = SNDRV_PCM_RATE_48000,
> +	.rate_min = 48000,
> +	.rate_max = 48000,
> +	.channels_min = 2,
> +	.channels_max = 2,
> +	.period_bytes_min = 992,	/* 32640 */ /* 15296 */
> +	.period_bytes_max = 15872,	/* 65280 */
> +	.periods_min = 1,		/* 1 */
> +	.periods_max = 16,		/* 2 */
> +	.buffer_bytes_max = 65280,	/* 65280 */
> +};
> +
> +static int smi2021_pcm_open(struct snd_pcm_substream *substream)
> +{
> +	struct smi2021 *smi2021 = snd_pcm_substream_chip(substream);
> +	struct snd_pcm_runtime *runtime = substream->runtime;
> +	int rc;
> +
> +	rc = snd_pcm_hw_constraint_pow2(runtime, 0,
> +					SNDRV_PCM_HW_PARAM_PERIODS);
> +	if (rc < 0)
> +		return rc;
> +
> +	smi2021->pcm_substream = substream;
> +
> +	runtime->hw = smi2021_pcm_hw;
> +	snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
> +
> +	return 0;
> +}
> +
> +static int smi2021_pcm_close(struct snd_pcm_substream *substream)
> +{
> +	struct smi2021 *smi2021 = snd_pcm_substream_chip(substream);
> +
> +	if (atomic_read(&smi2021->adev_capturing)) {
> +		atomic_set(&smi2021->adev_capturing, 0);
> +		schedule_work(&smi2021->adev_capture_trigger);
> +	}
> +	return 0;
> +
> +}
> +
> +
> +static int smi2021_pcm_hw_params(struct snd_pcm_substream *substream,
> +				struct snd_pcm_hw_params *hw_params)
> +{
> +	int size, rc;
> +	size = params_period_bytes(hw_params) * params_periods(hw_params);
> +
> +	rc = pcm_buffer_alloc(substream, size);
> +	if (rc < 0)
> +		return rc;
> +
> +
> +	return 0;
> +}
> +
> +static int smi2021_pcm_hw_free(struct snd_pcm_substream *substream)
> +{
> +	struct smi2021 *smi2021 = snd_pcm_substream_chip(substream);
> +
> +	if (atomic_read(&smi2021->adev_capturing)) {
> +		atomic_set(&smi2021->adev_capturing, 0);
> +		schedule_work(&smi2021->adev_capture_trigger);
> +	}
> +
> +	pcm_buffer_free(substream);
> +	return 0;
> +}
> +
> +static int smi2021_pcm_prepare(struct snd_pcm_substream *substream)
> +{
> +	struct smi2021 *smi2021 = snd_pcm_substream_chip(substream);
> +
> +	smi2021->pcm_complete_samples = 0;
> +	smi2021->pcm_read_offset = 0;
> +	smi2021->pcm_write_ptr = 0;
> +
> +	return 0;
> +}
> +
> +static void capture_trigger(struct work_struct *work)
> +{
> +	struct smi2021 *smi2021 = container_of(work, struct smi2021,
> +						adev_capture_trigger);
> +
> +	if (atomic_read(&smi2021->adev_capturing))
> +		smi2021_toggle_audio(smi2021, true);
> +	else
> +		smi2021_toggle_audio(smi2021, false);
> +}
> +
> +/* This callback is ATOMIC, must not sleep */
> +static int smi2021_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
> +{
> +	struct smi2021 *smi2021 = snd_pcm_substream_chip(substream);
> +
> +	switch (cmd) {
> +	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
> +	case SNDRV_PCM_TRIGGER_RESUME:
> +	case SNDRV_PCM_TRIGGER_START:
> +		atomic_set(&smi2021->adev_capturing, 1);
> +		break;
> +	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
> +	case SNDRV_PCM_TRIGGER_SUSPEND:
> +	case SNDRV_PCM_TRIGGER_STOP:
> +		atomic_set(&smi2021->adev_capturing, 0);
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	schedule_work(&smi2021->adev_capture_trigger);
> +
> +	return 0;
> +}
> +
> +static snd_pcm_uframes_t smi2021_pcm_pointer(
> +				struct snd_pcm_substream *substream)
> +{
> +	struct smi2021 *smi2021 = snd_pcm_substream_chip(substream);
> +	return smi2021->pcm_write_ptr / 8;
> +}
> +
> +static struct page *smi2021_pcm_get_vmalloc_page(
> +					struct snd_pcm_substream *subs,
> +					unsigned long offset)
> +{
> +	void *pageptr = subs->runtime->dma_area + offset;
> +
> +	return vmalloc_to_page(pageptr);
> +}
> +
> +static struct snd_pcm_ops smi2021_pcm_ops = {
> +	.open = smi2021_pcm_open,
> +	.close = smi2021_pcm_close,
> +	.ioctl = snd_pcm_lib_ioctl,
> +	.hw_params = smi2021_pcm_hw_params,
> +	.hw_free = smi2021_pcm_hw_free,
> +	.prepare = smi2021_pcm_prepare,
> +	.trigger = smi2021_pcm_trigger,
> +	.pointer = smi2021_pcm_pointer,
> +	.page = smi2021_pcm_get_vmalloc_page,
> +};
> +
> +int smi2021_snd_register(struct smi2021 *smi2021)
> +{
> +	struct snd_card	*card;
> +	struct snd_pcm *pcm;
> +	int rc = 0;
> +
> +	rc = snd_card_create(SNDRV_DEFAULT_IDX1, "smi2021 Audio", THIS_MODULE,
> +				0, &card);
> +	if (rc < 0)
> +		return rc;
> +
> +	rc = snd_pcm_new(card, "smi2021 Audio", 0, 0, 1, &pcm);
> +	if (rc < 0)
> +		goto err_free_card;
> +
> +	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &smi2021_pcm_ops);
> +	pcm->info_flags = 0;
> +	pcm->private_data = smi2021;
> +	strcpy(pcm->name, "Somagic smi2021 Capture");
> +
> +	strcpy(card->driver, "smi2021-Audio");
> +	strcpy(card->shortname, "smi2021 Audio");
> +	strcpy(card->longname, "Somagic smi2021 Audio");
> +
> +	INIT_WORK(&smi2021->adev_capture_trigger, capture_trigger);
> +
> +	rc = snd_card_register(card);
> +	if (rc < 0)
> +		goto err_free_card;
> +
> +	smi2021->snd_card = card;
> +
> +	return 0;
> +
> +err_free_card:
> +	snd_card_free(card);
> +	return rc;
> +}
> +
> +void smi2021_snd_unregister(struct smi2021 *smi2021)
> +{
> +	if (smi2021 == NULL)
> +		return;
> +
> +	if (smi2021->snd_card == NULL)
> +		return;
> +
> +	snd_card_free(smi2021->snd_card);
> +	smi2021->snd_card = NULL;
> +}
> +
> +void smi2021_stop_audio(struct smi2021 *smi2021)
> +{
> +	/*
> +	 * HACK: Stop the audio subsystem,
> +	 * without this, the pcm middle-layer will hang waiting for more data.
> +	 *
> +	 * Is there a better way to do this?
> +	 */
> +	if (smi2021->pcm_substream && smi2021->pcm_substream->runtime) {
> +		struct snd_pcm_runtime *runtime;
> +
> +		runtime = smi2021->pcm_substream->runtime;
> +		if (runtime->status) {
> +			runtime->status->state = SNDRV_PCM_STATE_DRAINING;
> +			wake_up(&runtime->sleep);
> +		}
> +	}
> +}
> +
> +void smi2021_audio(struct smi2021 *smi2021, u8 *data, int len)
> +{
> +	struct snd_pcm_runtime *runtime;
> +	u8 offset;
> +	int new_offset = 0;
> +
> +	int skip;
> +	unsigned int stride, oldptr, headptr;
> +
> +	int diff = 0;
> +	int samples = 0;
> +	bool period_elapsed = false;
> +
> +
> +	if (smi2021->udev == NULL)
> +		return;
> +
> +	if (atomic_read(&smi2021->adev_capturing) == 0)
> +		return;
> +
> +	if (smi2021->pcm_substream == NULL)
> +		return;
> +
> +	runtime = smi2021->pcm_substream->runtime;
> +	if (!runtime || !runtime->dma_area)
> +		return;
> +
> +	offset = smi2021->pcm_read_offset;
> +	stride = runtime->frame_bits >> 3;
> +
> +	if (stride == 0)
> +		return;
> +
> +	diff = smi2021->pcm_write_ptr;
> +
> +	/*
> +	 * Check that the end of the last buffer was correct.
> +	 * If not correct, we mark any partial frames in buffer as complete
> +	 */
> +	headptr = smi2021->pcm_write_ptr - offset - 4;
> +	if (smi2021->pcm_write_ptr > 10
> +	    && runtime->dma_area[headptr] != 0x00) {
> +		skip = stride - (smi2021->pcm_write_ptr % stride);
> +		snd_pcm_stream_lock(smi2021->pcm_substream);
> +		smi2021->pcm_write_ptr += skip;
> +
> +		if (smi2021->pcm_write_ptr >= runtime->dma_bytes)
> +			smi2021->pcm_write_ptr -= runtime->dma_bytes;
> +
> +		snd_pcm_stream_unlock(smi2021->pcm_substream);
> +		offset = smi2021->pcm_read_offset = 0;
> +	}
> +	/*
> +	 * The device is actually sending 24Bit pcm data
> +	 * with 0x00 as the header byte before each sample.
> +	 * We look for this byte to make sure we did not
> +	 * loose any bytes during transfer.
> +	 */
> +	while (len > stride && (data[offset] != 0x00 ||
> +			data[offset + (stride / 2)] != 0x00)) {
> +		new_offset++;
> +		data++;
> +		len--;
> +	}
> +
> +	if (len <= stride) {
> +		/* We exhausted the buffer looking for 0x00 */
> +		smi2021->pcm_read_offset = 0;
> +		return;
> +	}
> +	if (new_offset != 0) {
> +		/*
> +		 * This buffer can not be appended to the current buffer,
> +		 * so we mark any partial frames in the buffer as complete.
> +		 */
> +		skip = stride - (smi2021->pcm_write_ptr % stride);
> +		snd_pcm_stream_lock(smi2021->pcm_substream);
> +		smi2021->pcm_write_ptr += skip;
> +
> +		if (smi2021->pcm_write_ptr >= runtime->dma_bytes)
> +			smi2021->pcm_write_ptr -= runtime->dma_bytes;
> +
> +		snd_pcm_stream_unlock(smi2021->pcm_substream);
> +
> +		offset = smi2021->pcm_read_offset = new_offset % (stride / 2);
> +
> +	}
> +
> +	oldptr = smi2021->pcm_write_ptr;
> +	if (oldptr + len >= runtime->dma_bytes) {
> +		unsigned int cnt = runtime->dma_bytes - oldptr;
> +		memcpy(runtime->dma_area + oldptr, data, cnt);
> +		memcpy(runtime->dma_area, data + cnt, len - cnt);
> +	} else {
> +		memcpy(runtime->dma_area + oldptr, data, len);
> +	}
> +
> +	snd_pcm_stream_lock(smi2021->pcm_substream);
> +	smi2021->pcm_write_ptr += len;
> +
> +	if (smi2021->pcm_write_ptr >= runtime->dma_bytes)
> +		smi2021->pcm_write_ptr -= runtime->dma_bytes;
> +
> +	samples = smi2021->pcm_write_ptr - diff;
> +	if (samples < 0)
> +		samples += runtime->dma_bytes;
> +
> +	samples /= (stride / 2);
> +
> +	smi2021->pcm_complete_samples += samples;
> +	if (smi2021->pcm_complete_samples / 2 >= runtime->period_size) {
> +		smi2021->pcm_complete_samples -= runtime->period_size * 2;
> +		period_elapsed = true;
> +	}
> +	snd_pcm_stream_unlock(smi2021->pcm_substream);
> +
> +	if (period_elapsed)
> +		snd_pcm_period_elapsed(smi2021->pcm_substream);
> +
> +}
> diff --git a/drivers/media/usb/smi2021/smi2021_bootloader.c b/drivers/media/usb/smi2021/smi2021_bootloader.c
> new file mode 100644
> index 0000000..52b4edd
> --- /dev/null
> +++ b/drivers/media/usb/smi2021/smi2021_bootloader.c
> @@ -0,0 +1,256 @@
> +/************************************************************************
> + * smi2021_bootloader.c							*
> + *									*
> + * USB Driver for SMI2021 - EasyCAP					*
> + * **********************************************************************
> + *
> + * Copyright 2011-2013 Jon Arne Jørgensen
> + * <jonjon.arnearne--a.t--gmail.com>
> + *
> + * Copyright 2011, 2012 Tony Brown, Michal Demin, Jeffry Johnston
> + *
> + * 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.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, see <http://www.gnu.org/licenses/>.
> + *
> + * This driver is heavily influensed by the STK1160 driver.
> + * Copyright (C) 2012 Ezequiel Garcia
> + * <elezegarcia--a.t--gmail.com>
> + *
> + */
> +
> +#include "smi2021.h"
> +
> +#include <linux/module.h>
> +#include <linux/usb.h>
> +#include <linux/firmware.h>
> +#include <linux/slab.h>
> +
> +#define FIRMWARE_CHUNK_SIZE	62
> +#define FIRMWARE_HEADER_SIZE	2
> +
> +#define FIRMWARE_CHUNK_HEAD_0	0x05
> +#define FIRMWARE_CHUNK_HEAD_1	0xff
> +#define FIRMWARE_HW_STATE_HEAD	0x01
> +#define FIRMWARE_HW_READY_STATE	0x07
> +
> +#define SMI2021_3C_FIRMWARE	"smi2021_3c.bin"
> +#define SMI2021_3E_FIRMWARE	"smi2021_3e.bin"
> +#define SMI2021_3F_FIRMWARE	"smi2021_3f.bin"
> +
> +static unsigned int firmware_version;
> +module_param(firmware_version, int, 0644);
> +MODULE_PARM_DESC(firmware_version,
> +			"Firmware version to be uploaded to device\n"
> +			"if there are more than one firmware present");
> +
> +struct smi2021_firmware {
> +	int		id;
> +	const char	*name;
> +	int		found;
> +};
> +
> +struct smi2021_firmware available_fw[] = {
> +	{
> +		.id = 0x3c,
> +		.name = SMI2021_3C_FIRMWARE,
> +	},
> +	{
> +		.id = 0x3e,
> +		.name = SMI2021_3E_FIRMWARE,
> +	},
> +	{
> +		.id = 0x3f,
> +		.name = SMI2021_3F_FIRMWARE,
> +	}
> +};
> +
> +static const struct firmware *firmware[ARRAY_SIZE(available_fw)];
> +static int firmwares = -1;
> +
> +static int smi2021_load_firmware(struct usb_device *udev,
> +					const struct firmware *firmware)
> +{
> +	int i, size, rc;
> +	struct smi2021_set_hw_state *hw_state;
> +	u8 *chunk;
> +
> +	size = FIRMWARE_CHUNK_SIZE + FIRMWARE_HEADER_SIZE;
> +	chunk = kzalloc(size, GFP_KERNEL);
> +
> +	if (chunk == NULL) {
> +		dev_err(&udev->dev,
> +			"could not allocate space for firmware chunk\n");
> +		rc = -ENOMEM;
> +		goto end_out;
> +	}
> +
> +	hw_state = kzalloc(sizeof(*hw_state), GFP_KERNEL);
> +	if (hw_state == NULL) {
> +		dev_err(&udev->dev, "could not allocate space for usb data\n");
> +		rc = -ENOMEM;
> +		goto free_out;
> +	}
> +
> +	if (firmware == NULL) {
> +		dev_err(&udev->dev, "firmware is NULL\n");
> +		rc = -ENODEV;
> +		goto free_out;
> +	}
> +
> +	if (firmware->size % FIRMWARE_CHUNK_SIZE) {
> +		dev_err(&udev->dev, "firmware has wrong size\n");
> +		rc = -ENODEV;
> +		goto free_out;
> +	}
> +
> +	rc = usb_control_msg(udev, usb_rcvctrlpipe(udev, SMI2021_USB_RCVPIPE),
> +			SMI2021_USB_REQUEST,
> +			USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
> +			FIRMWARE_HW_STATE_HEAD, SMI2021_USB_INDEX,
> +			hw_state, sizeof(*hw_state), 1000);
> +
> +	if (rc < 0 || hw_state->state != FIRMWARE_HW_READY_STATE) {
> +		dev_err(&udev->dev,
> +			"device is not ready for firmware upload: %d\n", rc);
> +		goto free_out;
> +	}
> +
> +	chunk[0] = FIRMWARE_CHUNK_HEAD_0;
> +	chunk[1] = FIRMWARE_CHUNK_HEAD_1;
> +
> +	for (i = 0; i < firmware->size / FIRMWARE_CHUNK_SIZE; i++) {
> +		memcpy(chunk + FIRMWARE_HEADER_SIZE,
> +			firmware->data + (i * FIRMWARE_CHUNK_SIZE),
> +			FIRMWARE_CHUNK_SIZE);
> +
> +		rc = usb_control_msg(udev,
> +			usb_sndctrlpipe(udev, SMI2021_USB_SNDPIPE),
> +			SMI2021_USB_REQUEST,
> +			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
> +			FIRMWARE_CHUNK_HEAD_0, SMI2021_USB_INDEX,
> +			chunk, size, 1000);
> +		if (rc < 0) {
> +			dev_err(&udev->dev, "firmware upload failed: %d\n",
> +				rc);
> +			goto free_out;
> +		}
> +	}
> +
> +	hw_state->head = FIRMWARE_HW_READY_STATE;
> +	hw_state->state = 0x00;
> +	rc = usb_control_msg(udev, usb_sndctrlpipe(udev, SMI2021_USB_SNDPIPE),
> +			SMI2021_USB_REQUEST,
> +			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
> +			FIRMWARE_HW_READY_STATE, SMI2021_USB_INDEX,
> +			hw_state, sizeof(*hw_state), 1000);
> +
> +	if (rc < 0) {
> +		dev_err(&udev->dev, "device failed to ack firmware: %d\n", rc);
> +		goto free_out;
> +	}
> +
> +	rc = 0;
> +
> +free_out:
> +	kfree(chunk);
> +	kfree(hw_state);
> +end_out:
> +	return rc;
> +}
> +
> +static int smi2021_choose_firmware(struct usb_device *udev)
> +{
> +	int i, found, id;
> +	for (i = 0; i < ARRAY_SIZE(available_fw); i++) {
> +		found = available_fw[i].found;
> +		id = available_fw[i].id;
> +		if (firmware_version == id && found >= 0) {
> +			dev_info(&udev->dev, "uploading firmware for 0x%x\n",
> +					id);
> +			return smi2021_load_firmware(udev, firmware[found]);
> +		}
> +	}
> +
> +	dev_info(&udev->dev,
> +	"could not decide what firmware to upload, user action required\n");
> +	return 0;
> +}
> +
> +int smi2021_bootloader_probe(struct usb_interface *intf,
> +					const struct usb_device_id *devid)
> +{
> +	struct usb_device *udev = interface_to_usbdev(intf);
> +	int rc, i;
> +
> +	/* Check what firmwares are available in the system */
> +	for (i = 0; i < ARRAY_SIZE(available_fw); i++) {
> +		dev_info(&udev->dev, "Looking for: %s\n",
> +			 available_fw[i].name);
> +		rc = request_firmware(&firmware[firmwares + 1],
> +			available_fw[i].name, &udev->dev);
> +
> +		if (rc == 0) {
> +			firmwares++;
> +			available_fw[i].found = firmwares;
> +			dev_info(&udev->dev, "Found firmware for 0x00%x\n",
> +				available_fw[i].id);
> +		} else if (rc == -ENOENT) {
> +			available_fw[i].found = -1;
> +		} else {
> +			dev_err(&udev->dev,
> +				"request_firmware failed with: %d\n", rc);
> +			goto err_out;
> +		}
> +	}
> +
> +	if (firmwares < 0) {
> +		dev_err(&udev->dev,
> +			"could not find any firmware for this device\n");
> +		goto no_dev;
> +	} else if (firmwares == 0) {
> +		rc = smi2021_load_firmware(udev, firmware[0]);
> +		if (rc < 0)
> +			goto err_out;
> +	} else {
> +		smi2021_choose_firmware(udev);
> +	}
> +
> +	return 0;
> +
> +no_dev:
> +	rc = -ENODEV;
> +err_out:
> +	return rc;
> +}
> +
> +void smi2021_bootloader_disconnect(struct usb_interface *intf)
> +{
> +	struct usb_device *udev = interface_to_usbdev(intf);
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(available_fw); i++) {
> +		if (available_fw[i].found >= 0) {
> +			dev_info(&udev->dev, "Releasing firmware for 0x00%x\n",
> +							available_fw[i].id);
> +			release_firmware(firmware[available_fw[i].found]);
> +			firmware[available_fw[i].found] = NULL;
> +			available_fw[i].found = -1;
> +		}
> +	}
> +	firmwares = -1;
> +
> +}
> +
> +MODULE_FIRMWARE(SMI2021_3C_FIRMWARE);
> +MODULE_FIRMWARE(SMI2021_3E_FIRMWARE);
> +MODULE_FIRMWARE(SMI2021_3F_FIRMWARE);
> diff --git a/drivers/media/usb/smi2021/smi2021_main.c b/drivers/media/usb/smi2021/smi2021_main.c
> new file mode 100644
> index 0000000..5844379
> --- /dev/null
> +++ b/drivers/media/usb/smi2021/smi2021_main.c
> @@ -0,0 +1,952 @@
> +/************************************************************************
> + * smi2021_main.c							*
> + *									*
> + * USB Driver for SMI2021 - EasyCAP					*
> + * **********************************************************************
> + *
> + * Copyright 2011-2013 Jon Arne Jørgensen
> + * <jonjon.arnearne--a.t--gmail.com>
> + *
> + * Copyright 2011, 2012 Tony Brown, Michal Demin, Jeffry Johnston
> + *
> + * 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.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, see <http://www.gnu.org/licenses/>.
> + *
> + * This driver is heavily influensed by the STK1160 driver.
> + * Copyright (C) 2012 Ezequiel Garcia
> + * <elezegarcia--a.t--gmail.com>
> + *
> + */
> +
> +#include "smi2021.h"
> +
> +#define VENDOR_ID 0x1c88
> +#define BOOTLOADER_ID 0x0007
> +
> +#define SMI2021_MODE_CTRL_HEAD		0x01
> +#define SMI2021_MODE_CAPTURE		0x05
> +#define SMI2021_MODE_STANDBY		0x03
> +#define SMI2021_REG_CTRL_HEAD		0x0b
> +
> +static int smi2021_set_mode(struct smi2021 *smi2021, u8 mode)
> +{
> +	int pipe, rc;
> +	struct mode_ctrl_transfer {
> +		u8 head;
> +		u8 mode;
> +	} *transfer_buf = kzalloc(sizeof(*transfer_buf), GFP_KERNEL);
> +	if (transfer_buf == NULL)
> +		return -ENOMEM;
> +
> +	transfer_buf->head = SMI2021_MODE_CTRL_HEAD;
> +	transfer_buf->mode = mode;
> +
> +	pipe = usb_sndctrlpipe(smi2021->udev, SMI2021_USB_SNDPIPE);
> +	rc = usb_control_msg(smi2021->udev, pipe, SMI2021_USB_REQUEST,
> +			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
> +			transfer_buf->head, SMI2021_USB_INDEX,
> +			transfer_buf, sizeof(*transfer_buf), 1000);
> +
> +	kfree(transfer_buf);
> +
> +	return rc;
> +}
> +
> +/*
> + * The smi2021 chip will handle two different types of register settings.
> + * Settings for the gm7113c chip via i2c or settings for the smi2021 chip.
> + * All settings are passed with the following struct.
> + * Some bits in data_offset and data_cntl parameters tells the device what
> + * kind of setting it's receiving and if it's a read or write request.
> + */
> +struct smi2021_reg_ctrl_transfer {
> +	u8 head;
> +	u8 i2c_addr;
> +	u8 data_cntl;
> +	u8 data_offset;
> +	u8 data_size;
> +	union data {
> +		u8 val;
> +		struct i2c_data {
> +			u8 reg;
> +			u8 val;
> +		} __packed i2c_data;
> +		struct smi_data {
> +			u8 reg_hi;
> +			u8 reg_lo;
> +			u8 val;
> +		} __packed smi_data;
> +		u8 reserved[8];
> +	} __packed data;
> +} __packed;
> +
> +static int smi2021_set_reg(struct smi2021 *smi2021, u8 i2c_addr,
> +			   u16 reg, u8 val)
> +{
> +	int rc, pipe;
> +	struct smi2021_reg_ctrl_transfer *transfer_buf;
> +
> +	static const struct smi2021_reg_ctrl_transfer smi_data = {
> +		.head = SMI2021_REG_CTRL_HEAD,
> +		.i2c_addr = 0x00,
> +		.data_cntl = 0x00,
> +		.data_offset = 0x82,
> +		.data_size = sizeof(u8),
> +	};
> +
> +	static const struct smi2021_reg_ctrl_transfer i2c_data = {
> +		.head = SMI2021_REG_CTRL_HEAD,
> +		.i2c_addr = 0x00,
> +		.data_cntl = 0xc0,
> +		.data_offset = 0x01,
> +		.data_size = sizeof(u8)
> +	};
> +
> +	if (smi2021->udev == NULL) {
> +		rc = -ENODEV;
> +		goto out;
> +	}
> +
> +	transfer_buf = kzalloc(sizeof(*transfer_buf), GFP_KERNEL);
> +	if (transfer_buf == NULL) {
> +		rc = -ENOMEM;
> +		goto out;
> +	}
> +
> +	if (i2c_addr) {
> +		memcpy(transfer_buf, &i2c_data, sizeof(*transfer_buf));
> +		transfer_buf->i2c_addr = i2c_addr;
> +		transfer_buf->data.i2c_data.reg = reg;
> +		transfer_buf->data.i2c_data.val = val;
> +	} else {
> +		memcpy(transfer_buf, &smi_data, sizeof(*transfer_buf));
> +		transfer_buf->data.smi_data.reg_lo = __cpu_to_le16(reg) & 0xff;
> +		transfer_buf->data.smi_data.reg_hi = __cpu_to_le16(reg) >> 8;
> +		transfer_buf->data.smi_data.val = val;
> +	}
> +
> +	pipe = usb_sndctrlpipe(smi2021->udev, SMI2021_USB_SNDPIPE);
> +	rc = usb_control_msg(smi2021->udev, pipe, SMI2021_USB_REQUEST,
> +			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
> +			transfer_buf->head, SMI2021_USB_INDEX,
> +			transfer_buf, sizeof(*transfer_buf), 1000);
> +
> +	kfree(transfer_buf);
> +out:
> +	return rc;
> +}
> +
> +static int smi2021_get_reg(struct smi2021 *smi2021, u8 i2c_addr,
> +			   u16 reg, u8 *val)
> +{
> +	int rc, pipe;
> +	struct smi2021_reg_ctrl_transfer *transfer_buf;
> +
> +	static const struct smi2021_reg_ctrl_transfer i2c_prepare_read = {
> +		.head = SMI2021_REG_CTRL_HEAD,
> +		.i2c_addr = 0x00,
> +		.data_cntl = 0x84,
> +		.data_offset = 0x00,
> +		.data_size = sizeof(u8)
> +	};
> +
> +	static const struct smi2021_reg_ctrl_transfer smi_read = {
> +		.head = SMI2021_REG_CTRL_HEAD,
> +		.i2c_addr = 0x00,
> +		.data_cntl = 0x20,
> +		.data_offset = 0x82,
> +		.data_size = sizeof(u8)
> +	};
> +
> +	*val = 0;
> +
> +	if (smi2021->udev == NULL) {
> +		rc = -ENODEV;
> +		goto out;
> +	}
> +
> +	transfer_buf = kzalloc(sizeof(*transfer_buf), GFP_KERNEL);
> +	if (transfer_buf == NULL) {
> +		rc = -ENOMEM;
> +		goto out;
> +	}
> +
> +	pipe = usb_sndctrlpipe(smi2021->udev, SMI2021_USB_SNDPIPE);
> +
> +	if (i2c_addr) {
> +		memcpy(transfer_buf, &i2c_prepare_read, sizeof(*transfer_buf));
> +		transfer_buf->i2c_addr = i2c_addr;
> +		transfer_buf->data.i2c_data.reg = reg;
> +
> +		rc = usb_control_msg(smi2021->udev, pipe, SMI2021_USB_REQUEST,
> +			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
> +			transfer_buf->head, SMI2021_USB_INDEX,
> +			transfer_buf, sizeof(*transfer_buf), 1000);
> +		if (rc < 0)
> +			goto free_out;
> +
> +		transfer_buf->data_cntl = 0xa0;
> +	} else {
> +		memcpy(transfer_buf, &smi_read, sizeof(*transfer_buf));
> +		transfer_buf->data.smi_data.reg_lo = __cpu_to_le16(reg) & 0xff;
> +		transfer_buf->data.smi_data.reg_hi = __cpu_to_le16(reg) >> 8;
> +	}
> +
> +	rc = usb_control_msg(smi2021->udev, pipe, SMI2021_USB_REQUEST,
> +			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
> +			transfer_buf->head, SMI2021_USB_INDEX,
> +			transfer_buf, sizeof(*transfer_buf), 1000);
> +	if (rc < 0)
> +		goto free_out;
> +
> +	pipe = usb_rcvctrlpipe(smi2021->udev, SMI2021_USB_RCVPIPE);
> +	rc = usb_control_msg(smi2021->udev, pipe, SMI2021_USB_REQUEST,
> +			USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
> +			transfer_buf->head, SMI2021_USB_INDEX,
> +			transfer_buf, sizeof(*transfer_buf), 1000);
> +	if (rc < 0)
> +		goto free_out;
> +
> +	*val = transfer_buf->data.val;
> +
> +free_out:
> +	kfree(transfer_buf);
> +out:
> +	return rc;
> +}
> +
> +static int smi2021_i2c_xfer(struct i2c_adapter *i2c_adap,
> +				struct i2c_msg msgs[], int num)
> +{
> +	struct smi2021 *smi2021 = i2c_adap->algo_data;
> +
> +	switch (num) {
> +	case 2:  /* Read reg */
> +		if (msgs[0].len != 1 || msgs[1].len != 1)
> +			goto err_out;
> +
> +		if ((msgs[1].flags & I2C_M_RD) != I2C_M_RD)
> +			goto err_out;
> +		smi2021_get_reg(smi2021, msgs[0].addr, msgs[0].buf[0],
> +							msgs[1].buf);
> +		break;
> +	case 1: /* Write reg */
> +		if (msgs[0].len == 0)
> +			break;
> +		else if (msgs[0].len != 2)
> +			goto err_out;
> +		if (msgs[0].buf[0] == 0)
> +			break;
> +		smi2021_set_reg(smi2021, msgs[0].addr, msgs[0].buf[0],
> +							msgs[0].buf[1]);
> +		break;
> +	default:
> +		goto err_out;
> +	}
> +	return num;
> +
> +err_out:
> +	return -EOPNOTSUPP;
> +}
> +
> +static u32 smi2021_i2c_functionality(struct i2c_adapter *adap)
> +{
> +	return I2C_FUNC_SMBUS_EMUL;
> +}
> +
> +static struct i2c_algorithm smi2021_algo = {
> +	.master_xfer = smi2021_i2c_xfer,
> +	.functionality = smi2021_i2c_functionality,
> +};
> +
> +/* gm7113c_init table overrides */
> +static enum saa7113_r10_ofts r10_ofts = SAA7113_OFTS_VFLAG_BY_VREF;
> +static bool r10_vrln = true;
> +static bool r13_adlsb = true;
> +
> +static struct saa7115_platform_data gm7113c_data = {
> +	.saa7113_r10_ofts = &r10_ofts,
> +	.saa7113_r10_vrln = &r10_vrln,
> +	.saa7113_r13_adlsb = &r13_adlsb,
> +};
> +
> +static struct i2c_board_info gm7113c_info = {
> +	.type = "gm7113c",
> +	.addr = 0x4a,
> +	.platform_data = &gm7113c_data,
> +};
> +
> +static int smi2021_initialize(struct smi2021 *smi2021)
> +{
> +	int i, rc;
> +
> +	/*
> +	 * These registers initializes the smi2021 chip,
> +	 * but I have not been able to figure out exactly what they do.
> +	 * My guess is that they toggle the reset pins of the
> +	 * cs5350 and gm7113c chips.
> +	 */
> +	static const u16 init[][2] = {
> +		{ 0x3a, 0x80 },
> +		{ 0x3b, 0x00 },
> +		{ 0x34, 0x01 },
> +		{ 0x35, 0x00 },
> +		{ 0x34, 0x11 },
> +		{ 0x35, 0x11 },
> +		{ 0x3b, 0x80 },
> +		{ 0x3b, 0x00 },
> +	};
> +
> +	for (i = 0; i < ARRAY_SIZE(init); i++) {
> +		rc = smi2021_set_reg(smi2021, 0x00, init[i][0], init[i][1]);
> +		if (rc < 0)
> +			return rc;
> +	}
> +
> +	return 0;
> +}
> +
> +static struct smi2021_buf *smi2021_get_buf(struct smi2021 *smi2021)
> +{
> +	unsigned long flags;
> +	struct smi2021_buf *buf = NULL;
> +
> +	spin_lock_irqsave(&smi2021->buf_lock, flags);
> +	if (list_empty(&smi2021->bufs)) {
> +		/* No free buffers, userspace likely to slow! */
> +		spin_unlock_irqrestore(&smi2021->buf_lock, flags);
> +		return NULL;
> +	}
> +	buf = list_first_entry(&smi2021->bufs, struct smi2021_buf, list);
> +	list_del(&buf->list);
> +	spin_unlock_irqrestore(&smi2021->buf_lock, flags);
> +
> +	return buf;
> +}
> +
> +static void smi2021_buf_done(struct smi2021 *smi2021)
> +{
> +	struct smi2021_buf *buf = smi2021->cur_buf;
> +
> +	v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp);
> +	buf->vb.v4l2_buf.sequence = smi2021->sequence++;
> +	buf->vb.v4l2_buf.field = V4L2_FIELD_INTERLACED;
> +
> +	if (buf->pos < (SMI2021_BYTES_PER_LINE * smi2021->cur_height)) {
> +		vb2_set_plane_payload(&buf->vb, 0, 0);
> +		vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
> +	} else {
> +		vb2_set_plane_payload(&buf->vb, 0, buf->pos);
> +		vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE);
> +	}
> +
> +	smi2021->cur_buf = NULL;
> +}
> +
> +#define is_sav(trc)						\
> +	((trc & SMI2021_TRC_EAV) == 0x00)
> +#define is_field2(trc)						\
> +	((trc & SMI2021_TRC_FIELD_2) == SMI2021_TRC_FIELD_2)
> +#define is_active_video(trc)					\
> +	((trc & SMI2021_TRC_VBI) == 0x00)
> +/*
> + * Parse the TRC.
> + * Grab a new buffer from the queue if don't have one
> + * and we are recieving the start of a video frame.
> + *
> + * Mark video buffers as done if we have one full frame.
> + */
> +static void parse_trc(struct smi2021 *smi2021, u8 trc)
> +{
> +	struct smi2021_buf *buf = smi2021->cur_buf;
> +	int lines_per_field = smi2021->cur_height / 2;
> +	int line = 0;
> +
> +	if (buf == NULL) {
> +		if (!is_sav(trc))
> +			return;
> +
> +		if (!is_active_video(trc))
> +			return;
> +
> +		if (is_field2(trc))
> +			return;
> +
> +		buf = smi2021_get_buf(smi2021);
> +		if (buf == NULL)
> +			return;
> +
> +		smi2021->cur_buf = buf;
> +	}
> +
> +	if (is_sav(trc)) {
> +		/* Start of VBI or ACTIVE VIDEO */
> +		if (is_active_video(trc)) {
> +			buf->in_blank = false;
> +			buf->trc_av++;
> +		} else {
> +			/* VBI */
> +			buf->in_blank = true;
> +		}
> +
> +		if (!buf->second_field && is_field2(trc)) {
> +			line = buf->pos / SMI2021_BYTES_PER_LINE;
> +			if (line < lines_per_field)
> +				goto buf_done;
> +
> +			buf->second_field = true;
> +			buf->trc_av = 0;
> +		}
> +
> +		if (buf->second_field && !is_field2(trc))
> +			goto buf_done;
> +	} else {
> +		/* End of VBI or ACTIVE VIDEO */
> +		buf->in_blank = true;
> +	}
> +
> +	return;
> +
> +buf_done:
> +	smi2021_buf_done(smi2021);
> +}
> +
> +static void copy_video(struct smi2021 *smi2021, u8 p)
> +{
> +	struct smi2021_buf *buf = smi2021->cur_buf;
> +
> +	int lines_per_field = smi2021->cur_height / 2;
> +	int line = 0;
> +	int pos_in_line = 0;
> +	unsigned int offset = 0;
> +	u8 *dst;
> +
> +	if (buf == NULL)
> +		return;
> +
> +	if (buf->in_blank)
> +		return;
> +
> +	if (buf->pos >= buf->length) {
> +		smi2021_buf_done(smi2021);
> +		return;
> +	}
> +
> +	pos_in_line = buf->pos % SMI2021_BYTES_PER_LINE;
> +	line = buf->pos / SMI2021_BYTES_PER_LINE;
> +	if (line >= lines_per_field)
> +			line -= lines_per_field;
> +
> +	if (line != buf->trc_av - 1) {
> +		/* Keep video synchronized.
> +		 * The device will sometimes give us too many bytes
> +		 * for a line, before we get a new TRC.
> +		 * We just drop these bytes */
> +		return;
> +	}
> +
> +	if (buf->second_field)
> +		offset += SMI2021_BYTES_PER_LINE;
> +
> +	offset += (SMI2021_BYTES_PER_LINE * line * 2) + pos_in_line;
> +
> +	/* Will this ever happen? */
> +	if (offset >= buf->length)
> +		return;
> +
> +	dst = buf->mem + offset;
> +	*dst = p;
> +	buf->pos++;
> +}
> +
> +/*
> + * Scan the saa7113 Active video data.
> + * This data is:
> + *	4 bytes header (0xff 0x00 0x00 [TRC/SAV])
> + *	1440 bytes of UYUV Video data
> + *	4 bytes footer (0xff 0x00 0x00 [TRC/EAV])
> + *
> + * TRC = Time Reference Code.
> + * SAV = Start Active Video.
> + * EAV = End Active Video.
> + * This is described in the saa7113 datasheet.
> + */
> +static void parse_video(struct smi2021 *smi2021, u8 *p, int size)
> +{
> +	int i;
> +
> +	for (i = 0; i < size; i++) {
> +		switch (smi2021->sync_state) {
> +		case HSYNC:
> +			if (p[i] == 0xff)
> +				smi2021->sync_state = SYNCZ1;
> +			else
> +				copy_video(smi2021, p[i]);
> +			break;
> +		case SYNCZ1:
> +			if (p[i] == 0x00) {
> +				smi2021->sync_state = SYNCZ2;
> +			} else {
> +				smi2021->sync_state = HSYNC;
> +				copy_video(smi2021, 0xff);
> +				copy_video(smi2021, p[i]);
> +			}
> +			break;
> +		case SYNCZ2:
> +			if (p[i] == 0x00) {
> +				smi2021->sync_state = TRC;
> +			} else {
> +				smi2021->sync_state = HSYNC;
> +				copy_video(smi2021, 0xff);
> +				copy_video(smi2021, 0x00);
> +				copy_video(smi2021, p[i]);
> +			}
> +			break;
> +		case TRC:
> +			smi2021->sync_state = HSYNC;
> +			parse_trc(smi2021, p[i]);
> +			break;
> +		}
> +	}
> +}
> +
> +/*
> + * The device delivers data in chunks of 0x400 bytes.
> + * The four first bytes is a magic header to identify the chunks.
> + *	0xaa 0xaa 0x00 0x00 = saa7113 Active Video Data
> + *	0xaa 0xaa 0x00 0x01 = PCM - 24Bit 2 Channel audio data
> + */
> +static void process_packet(struct smi2021 *smi2021, u8 *p, int size)
> +{
> +	int i;
> +	u32 *header;
> +
> +	if (size % 0x400 != 0) {
> +		printk_ratelimited(KERN_INFO "smi2021::%s: size: %d\n",
> +				__func__, size);
> +		return;
> +	}
> +
> +	for (i = 0; i < size; i += 0x400) {
> +		header = (u32 *)(p + i);
> +		switch (*header) {
> +		case cpu_to_be32(0xaaaa0000):
> +			parse_video(smi2021, p+i+4, 0x400-4);
> +			break;
> +		case cpu_to_be32(0xaaaa0001):
> +			smi2021_audio(smi2021, p+i+4, 0x400-4);
> +			break;
> +		}
> +	}
> +}
> +
> +static void smi2021_iso_cb(struct urb *ip)
> +{
> +	int i, rc;
> +	struct smi2021 *smi2021 = ip->context;
> +
> +	switch (ip->status) {
> +	case 0:
> +	/* All fine */
> +		break;
> +	/* Device disconnected or capture stopped? */
> +	case -ENODEV:
> +	case -ENOENT:
> +	case -ECONNRESET:
> +	case -ESHUTDOWN:
> +		return;
> +	/* Unknown error, retry */
> +	default:
> +		dev_warn(smi2021->dev, "urb error! status %d\n", ip->status);
> +		goto resubmit;
> +	}
> +
> +	for (i = 0; i < ip->number_of_packets; i++) {
> +		int size = ip->iso_frame_desc[i].actual_length;
> +		unsigned char *data = ip->transfer_buffer +
> +				ip->iso_frame_desc[i].offset;
> +		process_packet(smi2021, data, size);
> +	}
> +
> +resubmit:
> +	rc = usb_submit_urb(ip, GFP_ATOMIC);
> +	if (rc)
> +		dev_warn(smi2021->dev, "urb re-submit failed (%d)\n", rc);
> +
> +}
> +
> +static struct urb *smi2021_setup_iso_transfer(struct smi2021 *smi2021)
> +{
> +	struct urb *ip;
> +	int i, size = smi2021->iso_size;
> +
> +	ip = usb_alloc_urb(SMI2021_ISOC_PACKETS, GFP_KERNEL);
> +	if (ip == NULL)
> +		return NULL;
> +
> +	ip->dev = smi2021->udev;
> +	ip->context = smi2021;
> +	ip->pipe = usb_rcvisocpipe(smi2021->udev, SMI2021_ISOC_EP);
> +	ip->interval = 1;
> +	ip->transfer_flags = URB_ISO_ASAP;
> +	ip->transfer_buffer = kzalloc(SMI2021_ISOC_PACKETS * size, GFP_KERNEL);
> +	ip->complete = smi2021_iso_cb;
> +	ip->number_of_packets = SMI2021_ISOC_PACKETS;
> +	ip->transfer_buffer_length = SMI2021_ISOC_PACKETS * size;
> +	for (i = 0; i < SMI2021_ISOC_PACKETS; i++) {
> +		ip->iso_frame_desc[i].offset = size * i;
> +		ip->iso_frame_desc[i].length = size;
> +	}
> +
> +	return ip;
> +}
> +
> +void smi2021_toggle_audio(struct smi2021 *smi2021, bool enable)
> +{
> +	/*
> +	 * I know that setting this register enables and disables
> +	 * the transfer of audio data over usb.
> +	 * I have no idea about what the number 0x1d really represents.
> +	 * */
> +	if (enable)
> +		smi2021_set_reg(smi2021, 0, 0x1740, 0x1d);
> +	else
> +		smi2021_set_reg(smi2021, 0, 0x1740, 0x00);
> +}
> +
> +int smi2021_start(struct smi2021 *smi2021)
> +{
> +	int i, rc;
> +	u8 reg;
> +	smi2021->sync_state = HSYNC;
> +
> +	v4l2_device_call_all(&smi2021->v4l2_dev, 0, video, s_stream, 1);
> +
> +	/*
> +	 * Enble automatic field detection on gm7113c (Bit 7)
> +	 * It seems the device needs this to not fail when receiving bad video
> +	 * i.e. from an old VHS tape.
> +	 */
> +	smi2021_get_reg(smi2021, 0x4a, 0x08, &reg);
> +	smi2021_set_reg(smi2021, 0x4a, 0x08, reg | 0x80);
> +
> +	/*
> +	 * Reset RTSO0 6 Times (Bit 7)
> +	 * The Windows driver does this, not sure if it's really needed.
> +	 */
> +	smi2021_get_reg(smi2021, 0x4a, 0x0e, &reg);
> +	reg |= 0x80;
> +	for (i = 0; i < 6; i++)
> +		smi2021_set_reg(smi2021, 0x4a, 0x0e, reg);
> +
> +	rc = smi2021_set_mode(smi2021, SMI2021_MODE_CAPTURE);
> +	if (rc < 0)
> +		goto start_fail;
> +
> +	rc = usb_set_interface(smi2021->udev, 0, 2);
> +	if (rc < 0)
> +		goto start_fail;
> +
> +	smi2021_toggle_audio(smi2021, false);
> +
> +	for (i = 0; i < SMI2021_ISOC_TRANSFERS; i++) {
> +		struct urb *ip;
> +
> +		ip = smi2021_setup_iso_transfer(smi2021);
> +		if (ip == NULL) {
> +			rc = -ENOMEM;
> +			goto start_fail;
> +		}
> +		smi2021->isoc_urbs[i] = ip;
> +		rc = usb_submit_urb(ip, GFP_KERNEL);
> +		if (rc < 0)
> +			goto start_fail;
> +	}
> +
> +	/* I have no idea about what this register does with this value. */
> +	smi2021_set_reg(smi2021, 0, 0x1800, 0x0d);
> +
> +	return 0;
> +
> +start_fail:
> +	smi2021_stop(smi2021);
> +
> +	return rc;
> +
> +}
> +
> +void smi2021_stop(struct smi2021 *smi2021)
> +{
> +	int i;
> +	unsigned long flags;
> +
> +	/* Cancel running transfers */
> +	for (i = 0; i < SMI2021_ISOC_TRANSFERS; i++) {
> +		struct urb *ip = smi2021->isoc_urbs[i];
> +		if (ip == NULL)
> +			continue;
> +		usb_kill_urb(ip);
> +		kfree(ip->transfer_buffer);
> +		usb_free_urb(ip);
> +		smi2021->isoc_urbs[i] = NULL;
> +	}
> +
> +	usb_set_interface(smi2021->udev, 0, 0);
> +	smi2021_set_mode(smi2021, SMI2021_MODE_STANDBY);
> +
> +	smi2021_stop_audio(smi2021);
> +
> +	/* Return buffers to userspace */
> +	spin_lock_irqsave(&smi2021->buf_lock, flags);
> +	while (!list_empty(&smi2021->bufs)) {
> +		struct smi2021_buf *buf = list_first_entry(&smi2021->bufs,
> +						struct smi2021_buf, list);
> +		vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
> +		list_del(&buf->list);
> +	}
> +	spin_unlock_irqrestore(&smi2021->buf_lock, flags);
> +
> +	return;
> +}
> +
> +static void smi2021_release(struct v4l2_device *v4l2_dev)
> +{
> +	struct smi2021 *smi2021 = container_of(v4l2_dev, struct smi2021,
> +								v4l2_dev);
> +	i2c_del_adapter(&smi2021->i2c_adap);
> +
> +	v4l2_ctrl_handler_free(&smi2021->ctrl_handler);
> +	v4l2_device_unregister(&smi2021->v4l2_dev);
> +	vb2_queue_release(&smi2021->vb2q);
> +	kfree(smi2021);
> +}
> +
> +/************************************************************************
> + *									*
> + *          DEVICE  -  PROBE   &   DISCONNECT				*
> + *									*
> + ***********************************************************************/
> +
> +static const struct usb_device_id smi2021_usb_device_id_table[] = {
> +	{ USB_DEVICE(VENDOR_ID, BOOTLOADER_ID)	},
> +	{ USB_DEVICE(VENDOR_ID, 0x003c)		},
> +	{ USB_DEVICE(VENDOR_ID, 0x003d)		},
> +	{ USB_DEVICE(VENDOR_ID, 0x003e)		},
> +	{ USB_DEVICE(VENDOR_ID, 0x003f)		},
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(usb, smi2021_usb_device_id_table);
> +
> +static const struct smi2021_vid_input dual_input[] = {
> +	{
> +		.name = "Composite",
> +		.type = SAA7115_COMPOSITE0,
> +	},
> +	{
> +		.name = "S-Video",
> +		.type = SAA7115_SVIDEO1,
> +	}
> +};
> +
> +static const struct smi2021_vid_input quad_input[] = {
> +	{
> +		.name = "Composite 0",
> +		.type = SAA7115_COMPOSITE0,
> +	},
> +	{
> +		.name = "Composite 1",
> +		.type = SAA7115_COMPOSITE1,
> +	},
> +	{
> +		.name = "Composite 2",
> +		.type = SAA7115_COMPOSITE2,
> +	},
> +	{
> +		.name = "Composite 3",
> +		.type = SAA7115_COMPOSITE3,
> +	},
> +};
> +
> +
> +static int smi2021_usb_probe(struct usb_interface *intf,
> +					const struct usb_device_id *devid)
> +{
> +	int rc, size, input_count;
> +	const struct smi2021_vid_input *vid_inputs;
> +	struct device *dev = &intf->dev;
> +	struct usb_device *udev = interface_to_usbdev(intf);
> +	struct smi2021 *smi2021;
> +
> +	if (udev->descriptor.idProduct == BOOTLOADER_ID)
> +		return smi2021_bootloader_probe(intf, devid);
> +
> +	if (intf->num_altsetting != 3)
> +		return -ENODEV;
> +	if (intf->altsetting[2].desc.bNumEndpoints != 1)
> +		return -ENODEV;
> +
> +	size = usb_endpoint_maxp(&intf->altsetting[2].endpoint[0].desc);
> +	size = (size & 0x07ff) * (((size & 0x1800) >> 11) + 1);
> +
> +	switch (udev->descriptor.idProduct) {
> +	case 0x3e:
> +	case 0x3f:
> +		input_count = ARRAY_SIZE(quad_input);
> +		vid_inputs = quad_input;
> +		break;
> +	case 0x3c:
> +	case 0x3d:
> +	default:
> +		input_count = ARRAY_SIZE(dual_input);
> +		vid_inputs = dual_input;
> +	}
> +
> +	smi2021 = kzalloc(sizeof(struct smi2021), GFP_KERNEL);
> +	if (dev == NULL)
> +		return -ENOMEM;
> +
> +	smi2021->dev = dev;
> +	smi2021->udev = usb_get_dev(udev);
> +
> +	smi2021->vid_input_count = input_count;
> +	smi2021->vid_inputs = vid_inputs;
> +	smi2021->iso_size = size;
> +
> +	/* videobuf2 struct and locks */
> +	smi2021->cur_norm = V4L2_STD_NTSC;
> +	smi2021->cur_height = SMI2021_NTSC_LINES;
> +
> +	spin_lock_init(&smi2021->buf_lock);
> +	mutex_init(&smi2021->v4l2_lock);
> +	mutex_init(&smi2021->vb2q_lock);
> +	INIT_LIST_HEAD(&smi2021->bufs);
> +
> +	rc = smi2021_vb2_setup(smi2021);
> +	if (rc < 0) {
> +		dev_warn(dev, "Could not initialize videobuf2 queue\n");
> +		goto smi2021_fail;
> +	}
> +
> +	rc = v4l2_ctrl_handler_init(&smi2021->ctrl_handler, 0);
> +	if (rc < 0) {
> +		dev_warn(dev, "Could not initialize v4l2 ctrl handler\n");
> +		goto ctrl_fail;
> +	}
> +
> +	/* v4l2 struct */
> +	smi2021->v4l2_dev.release = smi2021_release;
> +	smi2021->v4l2_dev.ctrl_handler = &smi2021->ctrl_handler;
> +	rc = v4l2_device_register(dev, &smi2021->v4l2_dev);
> +	if (rc < 0) {
> +		dev_warn(dev, "Could not register v4l2 device\n");
> +		goto v4l2_fail;
> +	}
> +
> +	smi2021_initialize(smi2021);
> +
> +	/* i2c adapter */
> +	strlcpy(smi2021->i2c_adap.name, "smi2021",
> +				sizeof(smi2021->i2c_adap.name));
> +	smi2021->i2c_adap.dev.parent = smi2021->dev;
> +	smi2021->i2c_adap.owner = THIS_MODULE;
> +	smi2021->i2c_adap.algo = &smi2021_algo;
> +	smi2021->i2c_adap.algo_data = smi2021;
> +	i2c_set_adapdata(&smi2021->i2c_adap, &smi2021->v4l2_dev);
> +	rc = i2c_add_adapter(&smi2021->i2c_adap);
> +	if (rc < 0) {
> +		dev_warn(dev, "Could not add i2c adapter\n");
> +		goto i2c_fail;
> +	}
> +
> +	/* i2c client */
> +	strlcpy(smi2021->i2c_client.name, "smi2021 internal",
> +				sizeof(smi2021->i2c_client.name));
> +	smi2021->i2c_client.adapter = &smi2021->i2c_adap;
> +	smi2021->gm7113c_subdev = v4l2_i2c_new_subdev_board(&smi2021->v4l2_dev,
> +							&smi2021->i2c_adap,
> +							&gm7113c_info, NULL);
> +
> +
> +	v4l2_device_call_all(&smi2021->v4l2_dev, 0, video, s_routing,
> +			smi2021->vid_inputs[smi2021->cur_input].type, 0, 0);

Don't use v4l2_device_call_all for the s_routing op. The meaning of the s_routing
arguments is subdev specific, so this is one of the very few ops that you can't
'broadcast' using v4l2_device_call_all. Since you have only one subdev anyway, I
would suggest replacing v4l2_device_call_all by v4l2_subdev_call.

> +	v4l2_device_call_all(&smi2021->v4l2_dev, 0, core, s_std,
> +			smi2021->cur_norm);
> +
> +	usb_set_intfdata(intf, smi2021);
> +	smi2021_snd_register(smi2021);
> +
> +
> +	/* video structure */
> +	rc = smi2021_video_register(smi2021);
> +	if (rc < 0) {
> +		dev_warn(dev, "Could not register video device\n");
> +		goto vdev_fail;
> +	}
> +
> +	dev_info(dev, "Somagic Easy-Cap Video Grabber\n");
> +	return 0;
> +
> +vdev_fail:
> +	i2c_del_adapter(&smi2021->i2c_adap);
> +i2c_fail:
> +	v4l2_device_unregister(&smi2021->v4l2_dev);
> +v4l2_fail:
> +	v4l2_ctrl_handler_free(&smi2021->ctrl_handler);
> +ctrl_fail:
> +	vb2_queue_release(&smi2021->vb2q);
> +smi2021_fail:
> +	kfree(smi2021);
> +
> +	return rc;
> +
> +}
> +
> +static void smi2021_usb_disconnect(struct usb_interface *intf)
> +{
> +	struct smi2021 *smi2021;
> +	struct usb_device *udev = interface_to_usbdev(intf);
> +
> +	if (udev->descriptor.idProduct == BOOTLOADER_ID)
> +		return smi2021_bootloader_disconnect(intf);
> +
> +	smi2021 = usb_get_intfdata(intf);
> +	smi2021_snd_unregister(smi2021);
> +
> +	mutex_lock(&smi2021->vb2q_lock);
> +	mutex_lock(&smi2021->v4l2_lock);
> +
> +	usb_set_intfdata(intf, NULL);
> +	video_unregister_device(&smi2021->vdev);
> +	v4l2_device_disconnect(&smi2021->v4l2_dev);
> +	usb_put_dev(smi2021->udev);
> +	smi2021->udev = NULL;
> +
> +	mutex_unlock(&smi2021->v4l2_lock);
> +	mutex_unlock(&smi2021->vb2q_lock);
> +
> +	v4l2_device_put(&smi2021->v4l2_dev);
> +}
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Jon Arne Jørgensen <jonjon.arnearne--a.t--gmail.com>");
> +MODULE_DESCRIPTION("SMI2021 - EasyCap");
> +MODULE_VERSION(SMI2021_DRIVER_VERSION);
> +
> +struct usb_driver smi2021_usb_driver = {
> +	.name = "smi2021",
> +	.id_table = smi2021_usb_device_id_table,
> +	.probe = smi2021_usb_probe,
> +	.disconnect = smi2021_usb_disconnect
> +};
> +
> +module_usb_driver(smi2021_usb_driver);
> diff --git a/drivers/media/usb/smi2021/smi2021_v4l2.c b/drivers/media/usb/smi2021/smi2021_v4l2.c
> new file mode 100644
> index 0000000..4a4eb3c
> --- /dev/null
> +++ b/drivers/media/usb/smi2021/smi2021_v4l2.c
> @@ -0,0 +1,277 @@
> +/************************************************************************
> + * smi2021_v4l2.c							*
> + *									*
> + * USB Driver for smi2021 - EasyCap					*
> + * **********************************************************************
> + *
> + * Copyright 2011-2013 Jon Arne Jørgensen
> + * <jonjon.arnearne--a.t--gmail.com>
> + *
> + * Copyright 2011, 2012 Tony Brown, Michal Demin, Jeffry Johnston
> + *
> + * 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.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, see <http://www.gnu.org/licenses/>.
> + *
> + * This driver is heavily influensed by the STK1160 driver.
> + * Copyright (C) 2012 Ezequiel Garcia
> + * <elezegarcia--a.t--gmail.com>
> + *
> + */
> +
> +#include "smi2021.h"
> +
> +static int vidioc_querycap(struct file *file, void *priv,
> +			struct v4l2_capability *cap)
> +{
> +	struct smi2021 *smi2021 = video_drvdata(file);
> +
> +	strlcpy(cap->driver, "smi2021", sizeof(cap->driver));
> +	strlcpy(cap->card, "smi2021", sizeof(cap->card));
> +	usb_make_path(smi2021->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_input(struct file *file, void *priv,
> +				struct v4l2_input *i)
> +{
> +	struct smi2021 *smi2021 = video_drvdata(file);
> +
> +	if (i->index >= smi2021->vid_input_count)
> +		return -EINVAL;
> +
> +	strlcpy(i->name, smi2021->vid_inputs[i->index].name, sizeof(i->name));
> +	i->type = V4L2_INPUT_TYPE_CAMERA;
> +	i->std = smi2021->vdev.tvnorms;
> +	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, "16bpp YU2, 4:2:2, packed",
> +					sizeof(f->description));
> +	f->pixelformat = V4L2_PIX_FMT_UYVY;
> +	return 0;
> +}
> +
> +static int vidioc_fmt_vid_cap(struct file *file, void *priv,
> +			struct v4l2_format *f)
> +{
> +	struct smi2021 *smi2021 = video_drvdata(file);
> +
> +	f->fmt.pix.width = SMI2021_BYTES_PER_LINE / 2;
> +	f->fmt.pix.height = smi2021->cur_height;
> +	f->fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY;
> +	f->fmt.pix.field = V4L2_FIELD_INTERLACED;
> +	f->fmt.pix.bytesperline = SMI2021_BYTES_PER_LINE;
> +	f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
> +	f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
> +	f->fmt.pix.priv = 0;
> +	return 0;
> +}
> +
> +static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *norm)
> +{
> +	struct smi2021 *smi2021 = video_drvdata(file);

Please add an empty line here.

> +	*norm = smi2021->cur_norm;
> +	return 0;
> +}
> +
> +static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
> +{
> +	struct smi2021 *smi2021 = video_drvdata(file);

Ditto.

> +	*i = smi2021->cur_input;
> +	return 0;
> +}
> +
> +static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm)
> +{
> +	struct smi2021 *smi2021 = video_drvdata(file);
> +

Check if the new norm == cur_norm and return 0 if that's the case. Some apps are known
to set the std again after REQBUFS.

> +	if (vb2_is_busy(&smi2021->vb2q))
> +		return -EBUSY;
> +
> +	smi2021->cur_norm = norm;
> +	if (norm & V4L2_STD_525_60)
> +		smi2021->cur_height = SMI2021_NTSC_LINES;
> +	else if (norm & V4L2_STD_625_50)
> +		smi2021->cur_height = SMI2021_PAL_LINES;
> +	else
> +		return -EINVAL;
> +
> +	v4l2_device_call_all(&smi2021->v4l2_dev, 0, core, s_std,
> +			     smi2021->cur_norm);
> +	return 0;
> +}
> +
> +static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
> +{
> +	struct smi2021 *smi2021 = video_drvdata(file);
> +	if (i >= smi2021->vid_input_count)
> +		return -EINVAL;
> +
> +	v4l2_device_call_all(&smi2021->v4l2_dev, 0, video, s_routing,
> +		smi2021->vid_inputs[i].type, 0, 0);

As mentioned before, you can't use v4l2_device_call_all for s_routing. This must
be handled per sub-device.

> +	smi2021->cur_input = i;
> +
> +	return 0;
> +}
> +
> +static const struct v4l2_ioctl_ops smi2021_ioctl_ops = {
> +	.vidioc_querycap		= vidioc_querycap,
> +	.vidioc_enum_input		= vidioc_enum_input,
> +	.vidioc_enum_fmt_vid_cap	= vidioc_enum_fmt_vid_cap,
> +	.vidioc_g_fmt_vid_cap		= vidioc_fmt_vid_cap,
> +	.vidioc_try_fmt_vid_cap		= vidioc_fmt_vid_cap,
> +	.vidioc_s_fmt_vid_cap		= vidioc_fmt_vid_cap,
> +	.vidioc_g_std			= vidioc_g_std,
> +	.vidioc_s_std			= vidioc_s_std,
> +	.vidioc_g_input			= vidioc_g_input,
> +	.vidioc_s_input			= vidioc_s_input,
> +
> +	/* vb2 handle these */
> +	.vidioc_reqbufs			= vb2_ioctl_reqbufs,
> +	.vidioc_prepare_buf		= vb2_ioctl_prepare_buf,
> +	.vidioc_create_bufs		= vb2_ioctl_create_bufs,

You do not handle the v4l2_format argument in queue_setup, so you do not really
support create_bufs. I would just remove this ioctl, it's not really relevant to
this driver. Of course, alternatively you can fix queue_setup.

> +	.vidioc_querybuf		= vb2_ioctl_querybuf,
> +	.vidioc_qbuf			= vb2_ioctl_qbuf,
> +	.vidioc_dqbuf			= vb2_ioctl_dqbuf,
> +	.vidioc_streamon		= vb2_ioctl_streamon,
> +	.vidioc_streamoff		= vb2_ioctl_streamoff,
> +
> +	/* v4l2-event and v4l2-cntrl handle these */
> +	.vidioc_log_status		= v4l2_ctrl_log_status,
> +	.vidioc_subscribe_event		= v4l2_ctrl_subscribe_event,
> +	.vidioc_unsubscribe_event	= v4l2_event_unsubscribe,
> +};
> +
> +static struct v4l2_file_operations smi2021_fops = {
> +	.owner = THIS_MODULE,
> +	.open = v4l2_fh_open,
> +	.release = vb2_fop_release,
> +	.read = vb2_fop_read,
> +	.poll = vb2_fop_poll,
> +	.mmap = vb2_fop_mmap,
> +	.unlocked_ioctl = video_ioctl2,
> +};
> +
> +/************************************************************************
> + *									*
> + *          Videobuf2 operations					*
> + *									*
> + ***********************************************************************/
> +static int queue_setup(struct vb2_queue *vq,
> +				const struct v4l2_format *v4l2_fmt,
> +				unsigned int *nbuffers, unsigned int *nplanes,
> +				unsigned int sizes[], void *alloc_ctxs[])
> +{
> +	struct smi2021 *smi2021 = vb2_get_drv_priv(vq);
> +	*nbuffers = clamp_t(unsigned int, *nbuffers, 4, 16);
> +
> +	*nplanes = 1;
> +	sizes[0] = SMI2021_BYTES_PER_LINE * smi2021->cur_height;
> +
> +	return 0;
> +}
> +
> +static void buffer_queue(struct vb2_buffer *vb)
> +{
> +	unsigned long flags;
> +	struct smi2021 *smi2021 = vb2_get_drv_priv(vb->vb2_queue);
> +	struct smi2021_buf *buf = container_of(vb, struct smi2021_buf, vb);
> +
> +	if (smi2021->udev == NULL) {
> +		vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
> +		return;
> +	}
> +
> +	buf->mem = vb2_plane_vaddr(vb, 0);
> +	buf->length = vb2_plane_size(vb, 0);
> +
> +	buf->pos = 0;
> +	buf->trc_av = 0;
> +	buf->in_blank = true;
> +	buf->second_field = false;
> +
> +	spin_lock_irqsave(&smi2021->buf_lock, flags);
> +	if (buf->length < smi2021->cur_height * SMI2021_BYTES_PER_LINE)
> +		vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
> +	else
> +		list_add_tail(&buf->list, &smi2021->bufs);
> +	spin_unlock_irqrestore(&smi2021->buf_lock, flags);
> +}
> +
> +static int start_streaming(struct vb2_queue *vq, unsigned int count)
> +{
> +	struct smi2021 *smi2021 = vb2_get_drv_priv(vq);
> +
> +	if (smi2021->udev == NULL)
> +		return -ENODEV;
> +
> +	return smi2021_start(smi2021);
> +}
> +
> +static int stop_streaming(struct vb2_queue *vq)
> +{
> +	struct smi2021 *smi2021 = vb2_get_drv_priv(vq);
> +
> +	if (smi2021->udev == NULL)
> +		return -ENODEV;
> +
> +	smi2021_stop(smi2021);
> +	return 0;
> +}
> +
> +static struct vb2_ops smi2021_vb2_ops = {
> +	.queue_setup		= queue_setup,
> +	.buf_queue		= buffer_queue,
> +	.start_streaming	= start_streaming,
> +	.stop_streaming		= stop_streaming,
> +};
> +
> +int smi2021_vb2_setup(struct smi2021 *smi2021)
> +{
> +	smi2021->vb2q.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> +	smi2021->vb2q.io_modes = VB2_READ | VB2_MMAP | VB2_USERPTR;
> +	smi2021->vb2q.drv_priv = smi2021;
> +	smi2021->vb2q.buf_struct_size = sizeof(struct smi2021_buf);
> +	smi2021->vb2q.ops = &smi2021_vb2_ops;
> +	smi2021->vb2q.mem_ops = &vb2_vmalloc_memops;
> +	smi2021->vb2q.timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
> +	smi2021->vb2q.lock = &smi2021->vb2q_lock;
> +
> +	return vb2_queue_init(&smi2021->vb2q);
> +}
> +
> +int smi2021_video_register(struct smi2021 *smi2021)
> +{
> +	strlcpy(smi2021->vdev.name, "smi2021", sizeof(smi2021->vdev.name));
> +	smi2021->vdev.v4l2_dev = &smi2021->v4l2_dev;
> +	smi2021->vdev.release = video_device_release_empty;
> +	smi2021->vdev.fops = &smi2021_fops;
> +	smi2021->vdev.ioctl_ops = &smi2021_ioctl_ops;
> +	smi2021->vdev.tvnorms = V4L2_STD_ALL;
> +	smi2021->vdev.queue = &smi2021->vb2q;
> +	smi2021->vdev.lock = &smi2021->v4l2_lock;
> +	set_bit(V4L2_FL_USE_FH_PRIO, &smi2021->vdev.flags);
> +	video_set_drvdata(&smi2021->vdev, smi2021);
> +
> +	return video_register_device(&smi2021->vdev, VFL_TYPE_GRABBER, -1);
> +}
> 

Regards,

	Hans
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ