xref: /openbmc/linux/drivers/media/usb/pwc/pwc-if.c (revision 8be98d2f2a0a262f8bf8a0bc1fdf522b3c7aab17)
11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
20c0d06caSMauro Carvalho Chehab /* Linux driver for Philips webcam
30c0d06caSMauro Carvalho Chehab    USB and Video4Linux interface part.
40c0d06caSMauro Carvalho Chehab    (C) 1999-2004 Nemosoft Unv.
50c0d06caSMauro Carvalho Chehab    (C) 2004-2006 Luc Saillard (luc@saillard.org)
60c0d06caSMauro Carvalho Chehab    (C) 2011 Hans de Goede <hdegoede@redhat.com>
70c0d06caSMauro Carvalho Chehab 
80c0d06caSMauro Carvalho Chehab    NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx
90c0d06caSMauro Carvalho Chehab    driver and thus may have bugs that are not present in the original version.
100c0d06caSMauro Carvalho Chehab    Please send bug reports and support requests to <luc@saillard.org>.
110c0d06caSMauro Carvalho Chehab    The decompression routines have been implemented by reverse-engineering the
120c0d06caSMauro Carvalho Chehab    Nemosoft binary pwcx module. Caveat emptor.
130c0d06caSMauro Carvalho Chehab 
140c0d06caSMauro Carvalho Chehab 
150c0d06caSMauro Carvalho Chehab */
160c0d06caSMauro Carvalho Chehab 
170c0d06caSMauro Carvalho Chehab /*
180c0d06caSMauro Carvalho Chehab    This code forms the interface between the USB layers and the Philips
190c0d06caSMauro Carvalho Chehab    specific stuff. Some adanved stuff of the driver falls under an
200c0d06caSMauro Carvalho Chehab    NDA, signed between me and Philips B.V., Eindhoven, the Netherlands, and
210c0d06caSMauro Carvalho Chehab    is thus not distributed in source form. The binary pwcx.o module
220c0d06caSMauro Carvalho Chehab    contains the code that falls under the NDA.
230c0d06caSMauro Carvalho Chehab 
240c0d06caSMauro Carvalho Chehab    In case you're wondering: 'pwc' stands for "Philips WebCam", but
250c0d06caSMauro Carvalho Chehab    I really didn't want to type 'philips_web_cam' every time (I'm lazy as
260c0d06caSMauro Carvalho Chehab    any Linux kernel hacker, but I don't like uncomprehensible abbreviations
270c0d06caSMauro Carvalho Chehab    without explanation).
280c0d06caSMauro Carvalho Chehab 
290c0d06caSMauro Carvalho Chehab    Oh yes, convention: to disctinguish between all the various pointers to
300c0d06caSMauro Carvalho Chehab    device-structures, I use these names for the pointer variables:
310c0d06caSMauro Carvalho Chehab    udev: struct usb_device *
320c0d06caSMauro Carvalho Chehab    vdev: struct video_device (member of pwc_dev)
330c0d06caSMauro Carvalho Chehab    pdev: struct pwc_devive *
340c0d06caSMauro Carvalho Chehab */
350c0d06caSMauro Carvalho Chehab 
360c0d06caSMauro Carvalho Chehab /* Contributors:
370c0d06caSMauro Carvalho Chehab    - Alvarado: adding whitebalance code
380c0d06caSMauro Carvalho Chehab    - Alistar Moire: QuickCam 3000 Pro device/product ID
390c0d06caSMauro Carvalho Chehab    - Tony Hoyle: Creative Labs Webcam 5 device/product ID
400c0d06caSMauro Carvalho Chehab    - Mark Burazin: solving hang in VIDIOCSYNC when camera gets unplugged
410c0d06caSMauro Carvalho Chehab    - Jk Fang: Sotec Afina Eye ID
420c0d06caSMauro Carvalho Chehab    - Xavier Roche: QuickCam Pro 4000 ID
430c0d06caSMauro Carvalho Chehab    - Jens Knudsen: QuickCam Zoom ID
440c0d06caSMauro Carvalho Chehab    - J. Debert: QuickCam for Notebooks ID
450c0d06caSMauro Carvalho Chehab    - Pham Thanh Nam: webcam snapshot button as an event input device
460c0d06caSMauro Carvalho Chehab */
470c0d06caSMauro Carvalho Chehab 
480c0d06caSMauro Carvalho Chehab #include <linux/errno.h>
490c0d06caSMauro Carvalho Chehab #include <linux/init.h>
500c0d06caSMauro Carvalho Chehab #include <linux/mm.h>
510c0d06caSMauro Carvalho Chehab #include <linux/module.h>
520c0d06caSMauro Carvalho Chehab #include <linux/poll.h>
530c0d06caSMauro Carvalho Chehab #include <linux/slab.h>
540c0d06caSMauro Carvalho Chehab #ifdef CONFIG_USB_PWC_INPUT_EVDEV
550c0d06caSMauro Carvalho Chehab #include <linux/usb/input.h>
560c0d06caSMauro Carvalho Chehab #endif
570c0d06caSMauro Carvalho Chehab #include <linux/vmalloc.h>
580c0d06caSMauro Carvalho Chehab #include <asm/io.h>
590c0d06caSMauro Carvalho Chehab #include <linux/kernel.h>		/* simple_strtol() */
600c0d06caSMauro Carvalho Chehab 
610c0d06caSMauro Carvalho Chehab #include "pwc.h"
620c0d06caSMauro Carvalho Chehab #include "pwc-kiara.h"
630c0d06caSMauro Carvalho Chehab #include "pwc-timon.h"
640c0d06caSMauro Carvalho Chehab #include "pwc-dec23.h"
650c0d06caSMauro Carvalho Chehab #include "pwc-dec1.h"
660c0d06caSMauro Carvalho Chehab 
67c1d5fb01SMatwey V. Kornilov #define CREATE_TRACE_POINTS
68c1d5fb01SMatwey V. Kornilov #include <trace/events/pwc.h>
69c1d5fb01SMatwey V. Kornilov 
700c0d06caSMauro Carvalho Chehab /* Function prototypes and driver templates */
710c0d06caSMauro Carvalho Chehab 
720c0d06caSMauro Carvalho Chehab /* hotplug device table support */
730c0d06caSMauro Carvalho Chehab static const struct usb_device_id pwc_device_table [] = {
7478710391SMauro Carvalho Chehab 	{ USB_DEVICE(0x041E, 0x400C) }, /* Creative Webcam 5 */
7578710391SMauro Carvalho Chehab 	{ USB_DEVICE(0x041E, 0x4011) }, /* Creative Webcam Pro Ex */
7678710391SMauro Carvalho Chehab 
7778710391SMauro Carvalho Chehab 	{ USB_DEVICE(0x046D, 0x08B0) }, /* Logitech QuickCam 3000 Pro */
7878710391SMauro Carvalho Chehab 	{ USB_DEVICE(0x046D, 0x08B1) }, /* Logitech QuickCam Notebook Pro */
7978710391SMauro Carvalho Chehab 	{ USB_DEVICE(0x046D, 0x08B2) }, /* Logitech QuickCam 4000 Pro */
8078710391SMauro Carvalho Chehab 	{ USB_DEVICE(0x046D, 0x08B3) }, /* Logitech QuickCam Zoom (old model) */
8178710391SMauro Carvalho Chehab 	{ USB_DEVICE(0x046D, 0x08B4) }, /* Logitech QuickCam Zoom (new model) */
8278710391SMauro Carvalho Chehab 	{ USB_DEVICE(0x046D, 0x08B5) }, /* Logitech QuickCam Orbit/Sphere */
8378710391SMauro Carvalho Chehab 	{ USB_DEVICE(0x046D, 0x08B6) }, /* Logitech/Cisco VT Camera */
8478710391SMauro Carvalho Chehab 	{ USB_DEVICE(0x046D, 0x08B7) }, /* Logitech ViewPort AV 100 */
8578710391SMauro Carvalho Chehab 	{ USB_DEVICE(0x046D, 0x08B8) }, /* Logitech QuickCam */
8678710391SMauro Carvalho Chehab 
8752b88c87SMauro Carvalho Chehab 	{ USB_DEVICE(0x0471, 0x0302) }, /* Philips PCA645VC */
8852b88c87SMauro Carvalho Chehab 	{ USB_DEVICE(0x0471, 0x0303) }, /* Philips PCA646VC */
8952b88c87SMauro Carvalho Chehab 	{ USB_DEVICE(0x0471, 0x0304) }, /* Askey VC010 type 2 */
9052b88c87SMauro Carvalho Chehab 	{ USB_DEVICE(0x0471, 0x0307) }, /* Philips PCVC675K (Vesta) */
9152b88c87SMauro Carvalho Chehab 	{ USB_DEVICE(0x0471, 0x0308) }, /* Philips PCVC680K (Vesta Pro) */
9252b88c87SMauro Carvalho Chehab 	{ USB_DEVICE(0x0471, 0x030C) }, /* Philips PCVC690K (Vesta Pro Scan) */
9352b88c87SMauro Carvalho Chehab 	{ USB_DEVICE(0x0471, 0x0310) }, /* Philips PCVC730K (ToUCam Fun)/PCVC830 (ToUCam II) */
9452b88c87SMauro Carvalho Chehab 	{ USB_DEVICE(0x0471, 0x0311) }, /* Philips PCVC740K (ToUCam Pro)/PCVC840 (ToUCam II) */
9552b88c87SMauro Carvalho Chehab 	{ USB_DEVICE(0x0471, 0x0312) }, /* Philips PCVC750K (ToUCam Pro Scan) */
9652b88c87SMauro Carvalho Chehab 	{ USB_DEVICE(0x0471, 0x0313) }, /* Philips PCVC720K/40 (ToUCam XS) */
9752b88c87SMauro Carvalho Chehab 	{ USB_DEVICE(0x0471, 0x0329) }, /* Philips SPC 900NC webcam */
9852b88c87SMauro Carvalho Chehab 	{ USB_DEVICE(0x0471, 0x032C) }, /* Philips SPC 880NC webcam */
9978710391SMauro Carvalho Chehab 
10078710391SMauro Carvalho Chehab 	{ USB_DEVICE(0x04CC, 0x8116) }, /* Sotec Afina Eye */
10178710391SMauro Carvalho Chehab 
1020c0d06caSMauro Carvalho Chehab 	{ USB_DEVICE(0x055D, 0x9000) }, /* Samsung MPC-C10 */
1030c0d06caSMauro Carvalho Chehab 	{ USB_DEVICE(0x055D, 0x9001) }, /* Samsung MPC-C30 */
1040c0d06caSMauro Carvalho Chehab 	{ USB_DEVICE(0x055D, 0x9002) },	/* Samsung SNC-35E (Ver3.0) */
10578710391SMauro Carvalho Chehab 
10678710391SMauro Carvalho Chehab 	{ USB_DEVICE(0x069A, 0x0001) }, /* Askey VC010 type 1 */
10778710391SMauro Carvalho Chehab 
10852b88c87SMauro Carvalho Chehab 	{ USB_DEVICE(0x06BE, 0x8116) }, /* AME Co. Afina Eye */
10978710391SMauro Carvalho Chehab 
11052b88c87SMauro Carvalho Chehab 	{ USB_DEVICE(0x0d81, 0x1900) }, /* Visionite VCS-UC300 */
11152b88c87SMauro Carvalho Chehab 	{ USB_DEVICE(0x0d81, 0x1910) }, /* Visionite VCS-UM100 */
11278710391SMauro Carvalho Chehab 
1130c0d06caSMauro Carvalho Chehab 	{ }
1140c0d06caSMauro Carvalho Chehab };
1150c0d06caSMauro Carvalho Chehab MODULE_DEVICE_TABLE(usb, pwc_device_table);
1160c0d06caSMauro Carvalho Chehab 
1170c0d06caSMauro Carvalho Chehab static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id *id);
1180c0d06caSMauro Carvalho Chehab static void usb_pwc_disconnect(struct usb_interface *intf);
1190c0d06caSMauro Carvalho Chehab static void pwc_isoc_cleanup(struct pwc_device *pdev);
1200c0d06caSMauro Carvalho Chehab 
1210c0d06caSMauro Carvalho Chehab static struct usb_driver pwc_driver = {
1220c0d06caSMauro Carvalho Chehab 	.name =			"Philips webcam",	/* name */
1230c0d06caSMauro Carvalho Chehab 	.id_table =		pwc_device_table,
1240c0d06caSMauro Carvalho Chehab 	.probe =		usb_pwc_probe,		/* probe() */
1250c0d06caSMauro Carvalho Chehab 	.disconnect =		usb_pwc_disconnect,	/* disconnect() */
1260c0d06caSMauro Carvalho Chehab };
1270c0d06caSMauro Carvalho Chehab 
1280c0d06caSMauro Carvalho Chehab #define MAX_DEV_HINTS	20
1290c0d06caSMauro Carvalho Chehab #define MAX_ISOC_ERRORS	20
1300c0d06caSMauro Carvalho Chehab 
1310c0d06caSMauro Carvalho Chehab #ifdef CONFIG_USB_PWC_DEBUG
1320c0d06caSMauro Carvalho Chehab 	int pwc_trace = PWC_DEBUG_LEVEL;
1330c0d06caSMauro Carvalho Chehab #endif
1340c0d06caSMauro Carvalho Chehab static int power_save = -1;
1350c0d06caSMauro Carvalho Chehab static int leds[2] = { 100, 0 };
1360c0d06caSMauro Carvalho Chehab 
1370c0d06caSMauro Carvalho Chehab /***/
1380c0d06caSMauro Carvalho Chehab 
1390c0d06caSMauro Carvalho Chehab static const struct v4l2_file_operations pwc_fops = {
1400c0d06caSMauro Carvalho Chehab 	.owner =	THIS_MODULE,
1410c0d06caSMauro Carvalho Chehab 	.open =		v4l2_fh_open,
1420c0d06caSMauro Carvalho Chehab 	.release =	vb2_fop_release,
1430c0d06caSMauro Carvalho Chehab 	.read =		vb2_fop_read,
1440c0d06caSMauro Carvalho Chehab 	.poll =		vb2_fop_poll,
1450c0d06caSMauro Carvalho Chehab 	.mmap =		vb2_fop_mmap,
1460c0d06caSMauro Carvalho Chehab 	.unlocked_ioctl = video_ioctl2,
1470c0d06caSMauro Carvalho Chehab };
14886844942SBhumika Goyal static const struct video_device pwc_template = {
1490c0d06caSMauro Carvalho Chehab 	.name =		"Philips Webcam",	/* Filled in later */
1500c0d06caSMauro Carvalho Chehab 	.release =	video_device_release_empty,
1510c0d06caSMauro Carvalho Chehab 	.fops =         &pwc_fops,
1520c0d06caSMauro Carvalho Chehab 	.ioctl_ops =	&pwc_ioctl_ops,
1530c0d06caSMauro Carvalho Chehab };
1540c0d06caSMauro Carvalho Chehab 
1550c0d06caSMauro Carvalho Chehab /***************************************************************************/
1560c0d06caSMauro Carvalho Chehab /* Private functions */
1570c0d06caSMauro Carvalho Chehab 
pwc_alloc_urb_buffer(struct usb_device * dev,size_t size,dma_addr_t * dma_handle)158*69c9e825SMatwey V. Kornilov static void *pwc_alloc_urb_buffer(struct usb_device *dev,
1591161db67SMatwey V. Kornilov 				  size_t size, dma_addr_t *dma_handle)
1601161db67SMatwey V. Kornilov {
161*69c9e825SMatwey V. Kornilov 	struct device *dmadev = dev->bus->sysdev;
1621161db67SMatwey V. Kornilov 	void *buffer = kmalloc(size, GFP_KERNEL);
1631161db67SMatwey V. Kornilov 
1641161db67SMatwey V. Kornilov 	if (!buffer)
1651161db67SMatwey V. Kornilov 		return NULL;
1661161db67SMatwey V. Kornilov 
167*69c9e825SMatwey V. Kornilov 	*dma_handle = dma_map_single(dmadev, buffer, size, DMA_FROM_DEVICE);
168*69c9e825SMatwey V. Kornilov 	if (dma_mapping_error(dmadev, *dma_handle)) {
1691161db67SMatwey V. Kornilov 		kfree(buffer);
1701161db67SMatwey V. Kornilov 		return NULL;
1711161db67SMatwey V. Kornilov 	}
1721161db67SMatwey V. Kornilov 
1731161db67SMatwey V. Kornilov 	return buffer;
1741161db67SMatwey V. Kornilov }
1751161db67SMatwey V. Kornilov 
pwc_free_urb_buffer(struct usb_device * dev,size_t size,void * buffer,dma_addr_t dma_handle)176*69c9e825SMatwey V. Kornilov static void pwc_free_urb_buffer(struct usb_device *dev,
1771161db67SMatwey V. Kornilov 				size_t size,
1781161db67SMatwey V. Kornilov 				void *buffer,
1791161db67SMatwey V. Kornilov 				dma_addr_t dma_handle)
1801161db67SMatwey V. Kornilov {
181*69c9e825SMatwey V. Kornilov 	struct device *dmadev = dev->bus->sysdev;
182*69c9e825SMatwey V. Kornilov 
183*69c9e825SMatwey V. Kornilov 	dma_unmap_single(dmadev, dma_handle, size, DMA_FROM_DEVICE);
1841161db67SMatwey V. Kornilov 	kfree(buffer);
1851161db67SMatwey V. Kornilov }
1861161db67SMatwey V. Kornilov 
pwc_get_next_fill_buf(struct pwc_device * pdev)1870dc6eb9fSMauro Carvalho Chehab static struct pwc_frame_buf *pwc_get_next_fill_buf(struct pwc_device *pdev)
1880c0d06caSMauro Carvalho Chehab {
1890c0d06caSMauro Carvalho Chehab 	unsigned long flags = 0;
1900c0d06caSMauro Carvalho Chehab 	struct pwc_frame_buf *buf = NULL;
1910c0d06caSMauro Carvalho Chehab 
1920c0d06caSMauro Carvalho Chehab 	spin_lock_irqsave(&pdev->queued_bufs_lock, flags);
1930c0d06caSMauro Carvalho Chehab 	if (list_empty(&pdev->queued_bufs))
1940c0d06caSMauro Carvalho Chehab 		goto leave;
1950c0d06caSMauro Carvalho Chehab 
1960c0d06caSMauro Carvalho Chehab 	buf = list_entry(pdev->queued_bufs.next, struct pwc_frame_buf, list);
1970c0d06caSMauro Carvalho Chehab 	list_del(&buf->list);
1980c0d06caSMauro Carvalho Chehab leave:
1990c0d06caSMauro Carvalho Chehab 	spin_unlock_irqrestore(&pdev->queued_bufs_lock, flags);
2000c0d06caSMauro Carvalho Chehab 	return buf;
2010c0d06caSMauro Carvalho Chehab }
2020c0d06caSMauro Carvalho Chehab 
pwc_snapshot_button(struct pwc_device * pdev,int down)2030c0d06caSMauro Carvalho Chehab static void pwc_snapshot_button(struct pwc_device *pdev, int down)
2040c0d06caSMauro Carvalho Chehab {
2050c0d06caSMauro Carvalho Chehab 	if (down) {
2060c0d06caSMauro Carvalho Chehab 		PWC_TRACE("Snapshot button pressed.\n");
2070c0d06caSMauro Carvalho Chehab 	} else {
2080c0d06caSMauro Carvalho Chehab 		PWC_TRACE("Snapshot button released.\n");
2090c0d06caSMauro Carvalho Chehab 	}
2100c0d06caSMauro Carvalho Chehab 
2110c0d06caSMauro Carvalho Chehab #ifdef CONFIG_USB_PWC_INPUT_EVDEV
2120c0d06caSMauro Carvalho Chehab 	if (pdev->button_dev) {
2130c0d06caSMauro Carvalho Chehab 		input_report_key(pdev->button_dev, KEY_CAMERA, down);
2140c0d06caSMauro Carvalho Chehab 		input_sync(pdev->button_dev);
2150c0d06caSMauro Carvalho Chehab 	}
2160c0d06caSMauro Carvalho Chehab #endif
2170c0d06caSMauro Carvalho Chehab }
2180c0d06caSMauro Carvalho Chehab 
pwc_frame_complete(struct pwc_device * pdev)2190c0d06caSMauro Carvalho Chehab static void pwc_frame_complete(struct pwc_device *pdev)
2200c0d06caSMauro Carvalho Chehab {
2210c0d06caSMauro Carvalho Chehab 	struct pwc_frame_buf *fbuf = pdev->fill_buf;
2220c0d06caSMauro Carvalho Chehab 
2230c0d06caSMauro Carvalho Chehab 	/* The ToUCam Fun CMOS sensor causes the firmware to send 2 or 3 bogus
2240c0d06caSMauro Carvalho Chehab 	   frames on the USB wire after an exposure change. This conditition is
2250c0d06caSMauro Carvalho Chehab 	   however detected  in the cam and a bit is set in the header.
2260c0d06caSMauro Carvalho Chehab 	   */
2270c0d06caSMauro Carvalho Chehab 	if (pdev->type == 730) {
2280c0d06caSMauro Carvalho Chehab 		unsigned char *ptr = (unsigned char *)fbuf->data;
2290c0d06caSMauro Carvalho Chehab 
2300c0d06caSMauro Carvalho Chehab 		if (ptr[1] == 1 && ptr[0] & 0x10) {
2310c0d06caSMauro Carvalho Chehab 			PWC_TRACE("Hyundai CMOS sensor bug. Dropping frame.\n");
2320c0d06caSMauro Carvalho Chehab 			pdev->drop_frames += 2;
2330c0d06caSMauro Carvalho Chehab 		}
2340c0d06caSMauro Carvalho Chehab 		if ((ptr[0] ^ pdev->vmirror) & 0x01) {
2350c0d06caSMauro Carvalho Chehab 			pwc_snapshot_button(pdev, ptr[0] & 0x01);
2360c0d06caSMauro Carvalho Chehab 		}
2370c0d06caSMauro Carvalho Chehab 		if ((ptr[0] ^ pdev->vmirror) & 0x02) {
2380c0d06caSMauro Carvalho Chehab 			if (ptr[0] & 0x02)
2390c0d06caSMauro Carvalho Chehab 				PWC_TRACE("Image is mirrored.\n");
2400c0d06caSMauro Carvalho Chehab 			else
2410c0d06caSMauro Carvalho Chehab 				PWC_TRACE("Image is normal.\n");
2420c0d06caSMauro Carvalho Chehab 		}
2430c0d06caSMauro Carvalho Chehab 		pdev->vmirror = ptr[0] & 0x03;
2440c0d06caSMauro Carvalho Chehab 		/* Sometimes the trailer of the 730 is still sent as a 4 byte packet
2450c0d06caSMauro Carvalho Chehab 		   after a short frame; this condition is filtered out specifically. A 4 byte
2460c0d06caSMauro Carvalho Chehab 		   frame doesn't make sense anyway.
2470c0d06caSMauro Carvalho Chehab 		   So we get either this sequence:
2480c0d06caSMauro Carvalho Chehab 		   drop_bit set -> 4 byte frame -> short frame -> good frame
2490c0d06caSMauro Carvalho Chehab 		   Or this one:
2500c0d06caSMauro Carvalho Chehab 		   drop_bit set -> short frame -> good frame
2510c0d06caSMauro Carvalho Chehab 		   So we drop either 3 or 2 frames in all!
2520c0d06caSMauro Carvalho Chehab 		   */
2530c0d06caSMauro Carvalho Chehab 		if (fbuf->filled == 4)
2540c0d06caSMauro Carvalho Chehab 			pdev->drop_frames++;
2550c0d06caSMauro Carvalho Chehab 	} else if (pdev->type == 740 || pdev->type == 720) {
2560c0d06caSMauro Carvalho Chehab 		unsigned char *ptr = (unsigned char *)fbuf->data;
2570c0d06caSMauro Carvalho Chehab 		if ((ptr[0] ^ pdev->vmirror) & 0x01) {
2580c0d06caSMauro Carvalho Chehab 			pwc_snapshot_button(pdev, ptr[0] & 0x01);
2590c0d06caSMauro Carvalho Chehab 		}
2600c0d06caSMauro Carvalho Chehab 		pdev->vmirror = ptr[0] & 0x03;
2610c0d06caSMauro Carvalho Chehab 	}
2620c0d06caSMauro Carvalho Chehab 
2630c0d06caSMauro Carvalho Chehab 	/* In case we were instructed to drop the frame, do so silently. */
2640c0d06caSMauro Carvalho Chehab 	if (pdev->drop_frames > 0) {
2650c0d06caSMauro Carvalho Chehab 		pdev->drop_frames--;
2660c0d06caSMauro Carvalho Chehab 	} else {
2670c0d06caSMauro Carvalho Chehab 		/* Check for underflow first */
2680c0d06caSMauro Carvalho Chehab 		if (fbuf->filled < pdev->frame_total_size) {
269c91e42f5SMauro Carvalho Chehab 			PWC_DEBUG_FLOW("Frame buffer underflow (%d bytes); discarded.\n",
270c91e42f5SMauro Carvalho Chehab 				       fbuf->filled);
2710c0d06caSMauro Carvalho Chehab 		} else {
2722d700715SJunghak Sung 			fbuf->vb.field = V4L2_FIELD_NONE;
2732d700715SJunghak Sung 			fbuf->vb.sequence = pdev->vframe_count;
2742d700715SJunghak Sung 			vb2_buffer_done(&fbuf->vb.vb2_buf, VB2_BUF_STATE_DONE);
2750c0d06caSMauro Carvalho Chehab 			pdev->fill_buf = NULL;
2760c0d06caSMauro Carvalho Chehab 			pdev->vsync = 0;
2770c0d06caSMauro Carvalho Chehab 		}
2780c0d06caSMauro Carvalho Chehab 	} /* !drop_frames */
2790c0d06caSMauro Carvalho Chehab 	pdev->vframe_count++;
2800c0d06caSMauro Carvalho Chehab }
2810c0d06caSMauro Carvalho Chehab 
2820c0d06caSMauro Carvalho Chehab /* This gets called for the Isochronous pipe (video). This is done in
2830c0d06caSMauro Carvalho Chehab  * interrupt time, so it has to be fast, not crash, and not stall. Neat.
2840c0d06caSMauro Carvalho Chehab  */
pwc_isoc_handler(struct urb * urb)2850c0d06caSMauro Carvalho Chehab static void pwc_isoc_handler(struct urb *urb)
2860c0d06caSMauro Carvalho Chehab {
2870c0d06caSMauro Carvalho Chehab 	struct pwc_device *pdev = (struct pwc_device *)urb->context;
288*69c9e825SMatwey V. Kornilov 	struct device *dmadev = urb->dev->bus->sysdev;
2890c0d06caSMauro Carvalho Chehab 	int i, fst, flen;
2900c0d06caSMauro Carvalho Chehab 	unsigned char *iso_buf = NULL;
2910c0d06caSMauro Carvalho Chehab 
292c1d5fb01SMatwey V. Kornilov 	trace_pwc_handler_enter(urb, pdev);
293c1d5fb01SMatwey V. Kornilov 
2940c0d06caSMauro Carvalho Chehab 	if (urb->status == -ENOENT || urb->status == -ECONNRESET ||
2950c0d06caSMauro Carvalho Chehab 	    urb->status == -ESHUTDOWN) {
296b436e26eSColin Ian King 		PWC_DEBUG_OPEN("URB (%p) unlinked %ssynchronously.\n",
297b436e26eSColin Ian King 			       urb, urb->status == -ENOENT ? "" : "a");
2980c0d06caSMauro Carvalho Chehab 		return;
2990c0d06caSMauro Carvalho Chehab 	}
3000c0d06caSMauro Carvalho Chehab 
3010c0d06caSMauro Carvalho Chehab 	if (pdev->fill_buf == NULL)
3020c0d06caSMauro Carvalho Chehab 		pdev->fill_buf = pwc_get_next_fill_buf(pdev);
3030c0d06caSMauro Carvalho Chehab 
3040c0d06caSMauro Carvalho Chehab 	if (urb->status != 0) {
3050c0d06caSMauro Carvalho Chehab 		const char *errmsg;
3060c0d06caSMauro Carvalho Chehab 
3070c0d06caSMauro Carvalho Chehab 		errmsg = "Unknown";
3080c0d06caSMauro Carvalho Chehab 		switch(urb->status) {
3090c0d06caSMauro Carvalho Chehab 			case -ENOSR:		errmsg = "Buffer error (overrun)"; break;
3100c0d06caSMauro Carvalho Chehab 			case -EPIPE:		errmsg = "Stalled (device not responding)"; break;
3110c0d06caSMauro Carvalho Chehab 			case -EOVERFLOW:	errmsg = "Babble (bad cable?)"; break;
3120c0d06caSMauro Carvalho Chehab 			case -EPROTO:		errmsg = "Bit-stuff error (bad cable?)"; break;
3130c0d06caSMauro Carvalho Chehab 			case -EILSEQ:		errmsg = "CRC/Timeout (could be anything)"; break;
3140c0d06caSMauro Carvalho Chehab 			case -ETIME:		errmsg = "Device does not respond"; break;
3150c0d06caSMauro Carvalho Chehab 		}
3160c0d06caSMauro Carvalho Chehab 		PWC_ERROR("pwc_isoc_handler() called with status %d [%s].\n",
3170c0d06caSMauro Carvalho Chehab 			  urb->status, errmsg);
3180c0d06caSMauro Carvalho Chehab 		/* Give up after a number of contiguous errors */
3190c0d06caSMauro Carvalho Chehab 		if (++pdev->visoc_errors > MAX_ISOC_ERRORS)
3200c0d06caSMauro Carvalho Chehab 		{
3210c0d06caSMauro Carvalho Chehab 			PWC_ERROR("Too many ISOC errors, bailing out.\n");
3220c0d06caSMauro Carvalho Chehab 			if (pdev->fill_buf) {
3232d700715SJunghak Sung 				vb2_buffer_done(&pdev->fill_buf->vb.vb2_buf,
3240c0d06caSMauro Carvalho Chehab 						VB2_BUF_STATE_ERROR);
3250c0d06caSMauro Carvalho Chehab 				pdev->fill_buf = NULL;
3260c0d06caSMauro Carvalho Chehab 			}
3270c0d06caSMauro Carvalho Chehab 		}
3280c0d06caSMauro Carvalho Chehab 		pdev->vsync = 0; /* Drop the current frame */
3290c0d06caSMauro Carvalho Chehab 		goto handler_end;
3300c0d06caSMauro Carvalho Chehab 	}
3310c0d06caSMauro Carvalho Chehab 
3320c0d06caSMauro Carvalho Chehab 	/* Reset ISOC error counter. We did get here, after all. */
3330c0d06caSMauro Carvalho Chehab 	pdev->visoc_errors = 0;
3340c0d06caSMauro Carvalho Chehab 
335*69c9e825SMatwey V. Kornilov 	dma_sync_single_for_cpu(dmadev,
3361161db67SMatwey V. Kornilov 				urb->transfer_dma,
3371161db67SMatwey V. Kornilov 				urb->transfer_buffer_length,
3381161db67SMatwey V. Kornilov 				DMA_FROM_DEVICE);
3391161db67SMatwey V. Kornilov 
3400c0d06caSMauro Carvalho Chehab 	/* vsync: 0 = don't copy data
3410c0d06caSMauro Carvalho Chehab 		  1 = sync-hunt
3420c0d06caSMauro Carvalho Chehab 		  2 = synched
3430c0d06caSMauro Carvalho Chehab 	 */
3440c0d06caSMauro Carvalho Chehab 	/* Compact data */
3450c0d06caSMauro Carvalho Chehab 	for (i = 0; i < urb->number_of_packets; i++) {
3460c0d06caSMauro Carvalho Chehab 		fst  = urb->iso_frame_desc[i].status;
3470c0d06caSMauro Carvalho Chehab 		flen = urb->iso_frame_desc[i].actual_length;
3480c0d06caSMauro Carvalho Chehab 		iso_buf = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
3490c0d06caSMauro Carvalho Chehab 		if (fst != 0) {
3500c0d06caSMauro Carvalho Chehab 			PWC_ERROR("Iso frame %d has error %d\n", i, fst);
3510c0d06caSMauro Carvalho Chehab 			continue;
3520c0d06caSMauro Carvalho Chehab 		}
3530c0d06caSMauro Carvalho Chehab 		if (flen > 0 && pdev->vsync) {
3540c0d06caSMauro Carvalho Chehab 			struct pwc_frame_buf *fbuf = pdev->fill_buf;
3550c0d06caSMauro Carvalho Chehab 
3560c0d06caSMauro Carvalho Chehab 			if (pdev->vsync == 1) {
357d6dd645eSJunghak Sung 				fbuf->vb.vb2_buf.timestamp = ktime_get_ns();
3580c0d06caSMauro Carvalho Chehab 				pdev->vsync = 2;
3590c0d06caSMauro Carvalho Chehab 			}
3600c0d06caSMauro Carvalho Chehab 
3610c0d06caSMauro Carvalho Chehab 			if (flen + fbuf->filled > pdev->frame_total_size) {
3620c0d06caSMauro Carvalho Chehab 				PWC_ERROR("Frame overflow (%d > %d)\n",
3630c0d06caSMauro Carvalho Chehab 					  flen + fbuf->filled,
3640c0d06caSMauro Carvalho Chehab 					  pdev->frame_total_size);
3650c0d06caSMauro Carvalho Chehab 				pdev->vsync = 0; /* Let's wait for an EOF */
3660c0d06caSMauro Carvalho Chehab 			} else {
3670c0d06caSMauro Carvalho Chehab 				memcpy(fbuf->data + fbuf->filled, iso_buf,
3680c0d06caSMauro Carvalho Chehab 				       flen);
3690c0d06caSMauro Carvalho Chehab 				fbuf->filled += flen;
3700c0d06caSMauro Carvalho Chehab 			}
3710c0d06caSMauro Carvalho Chehab 		}
3720c0d06caSMauro Carvalho Chehab 		if (flen < pdev->vlast_packet_size) {
3730c0d06caSMauro Carvalho Chehab 			/* Shorter packet... end of frame */
3740c0d06caSMauro Carvalho Chehab 			if (pdev->vsync == 2)
3750c0d06caSMauro Carvalho Chehab 				pwc_frame_complete(pdev);
3760c0d06caSMauro Carvalho Chehab 			if (pdev->fill_buf == NULL)
3770c0d06caSMauro Carvalho Chehab 				pdev->fill_buf = pwc_get_next_fill_buf(pdev);
3780c0d06caSMauro Carvalho Chehab 			if (pdev->fill_buf) {
3790c0d06caSMauro Carvalho Chehab 				pdev->fill_buf->filled = 0;
3800c0d06caSMauro Carvalho Chehab 				pdev->vsync = 1;
3810c0d06caSMauro Carvalho Chehab 			}
3820c0d06caSMauro Carvalho Chehab 		}
3830c0d06caSMauro Carvalho Chehab 		pdev->vlast_packet_size = flen;
3840c0d06caSMauro Carvalho Chehab 	}
3850c0d06caSMauro Carvalho Chehab 
386*69c9e825SMatwey V. Kornilov 	dma_sync_single_for_device(dmadev,
3871161db67SMatwey V. Kornilov 				   urb->transfer_dma,
3881161db67SMatwey V. Kornilov 				   urb->transfer_buffer_length,
3891161db67SMatwey V. Kornilov 				   DMA_FROM_DEVICE);
3901161db67SMatwey V. Kornilov 
3910c0d06caSMauro Carvalho Chehab handler_end:
392c1d5fb01SMatwey V. Kornilov 	trace_pwc_handler_exit(urb, pdev);
393c1d5fb01SMatwey V. Kornilov 
3940c0d06caSMauro Carvalho Chehab 	i = usb_submit_urb(urb, GFP_ATOMIC);
3950c0d06caSMauro Carvalho Chehab 	if (i != 0)
3960c0d06caSMauro Carvalho Chehab 		PWC_ERROR("Error (%d) re-submitting urb in pwc_isoc_handler.\n", i);
3970c0d06caSMauro Carvalho Chehab }
3980c0d06caSMauro Carvalho Chehab 
3990c0d06caSMauro Carvalho Chehab /* Both v4l2_lock and vb_queue_lock should be locked when calling this */
pwc_isoc_init(struct pwc_device * pdev)4000c0d06caSMauro Carvalho Chehab static int pwc_isoc_init(struct pwc_device *pdev)
4010c0d06caSMauro Carvalho Chehab {
4020c0d06caSMauro Carvalho Chehab 	struct usb_device *udev;
4030c0d06caSMauro Carvalho Chehab 	struct urb *urb;
4040c0d06caSMauro Carvalho Chehab 	int i, j, ret;
4050c0d06caSMauro Carvalho Chehab 	struct usb_interface *intf;
4060c0d06caSMauro Carvalho Chehab 	struct usb_host_interface *idesc = NULL;
4070c0d06caSMauro Carvalho Chehab 	int compression = 0; /* 0..3 = uncompressed..high */
4080c0d06caSMauro Carvalho Chehab 
4090c0d06caSMauro Carvalho Chehab 	pdev->vsync = 0;
4100c0d06caSMauro Carvalho Chehab 	pdev->vlast_packet_size = 0;
4110c0d06caSMauro Carvalho Chehab 	pdev->fill_buf = NULL;
4120c0d06caSMauro Carvalho Chehab 	pdev->vframe_count = 0;
4130c0d06caSMauro Carvalho Chehab 	pdev->visoc_errors = 0;
4140c0d06caSMauro Carvalho Chehab 	udev = pdev->udev;
4150c0d06caSMauro Carvalho Chehab 
4160c0d06caSMauro Carvalho Chehab retry:
4170c0d06caSMauro Carvalho Chehab 	/* We first try with low compression and then retry with a higher
4180c0d06caSMauro Carvalho Chehab 	   compression setting if there is not enough bandwidth. */
4190c0d06caSMauro Carvalho Chehab 	ret = pwc_set_video_mode(pdev, pdev->width, pdev->height, pdev->pixfmt,
4200c0d06caSMauro Carvalho Chehab 				 pdev->vframes, &compression, 1);
4210c0d06caSMauro Carvalho Chehab 
4220c0d06caSMauro Carvalho Chehab 	/* Get the current alternate interface, adjust packet size */
4230c0d06caSMauro Carvalho Chehab 	intf = usb_ifnum_to_if(udev, 0);
4240c0d06caSMauro Carvalho Chehab 	if (intf)
4250c0d06caSMauro Carvalho Chehab 		idesc = usb_altnum_to_altsetting(intf, pdev->valternate);
4260c0d06caSMauro Carvalho Chehab 	if (!idesc)
4270c0d06caSMauro Carvalho Chehab 		return -EIO;
4280c0d06caSMauro Carvalho Chehab 
4290c0d06caSMauro Carvalho Chehab 	/* Search video endpoint */
4300c0d06caSMauro Carvalho Chehab 	pdev->vmax_packet_size = -1;
4310c0d06caSMauro Carvalho Chehab 	for (i = 0; i < idesc->desc.bNumEndpoints; i++) {
4320c0d06caSMauro Carvalho Chehab 		if ((idesc->endpoint[i].desc.bEndpointAddress & 0xF) == pdev->vendpoint) {
4330c0d06caSMauro Carvalho Chehab 			pdev->vmax_packet_size = le16_to_cpu(idesc->endpoint[i].desc.wMaxPacketSize);
4340c0d06caSMauro Carvalho Chehab 			break;
4350c0d06caSMauro Carvalho Chehab 		}
4360c0d06caSMauro Carvalho Chehab 	}
4370c0d06caSMauro Carvalho Chehab 
4380c0d06caSMauro Carvalho Chehab 	if (pdev->vmax_packet_size < 0 || pdev->vmax_packet_size > ISO_MAX_FRAME_SIZE) {
4390c0d06caSMauro Carvalho Chehab 		PWC_ERROR("Failed to find packet size for video endpoint in current alternate setting.\n");
4400c0d06caSMauro Carvalho Chehab 		return -ENFILE; /* Odd error, that should be noticeable */
4410c0d06caSMauro Carvalho Chehab 	}
4420c0d06caSMauro Carvalho Chehab 
4430c0d06caSMauro Carvalho Chehab 	/* Set alternate interface */
4440c0d06caSMauro Carvalho Chehab 	PWC_DEBUG_OPEN("Setting alternate interface %d\n", pdev->valternate);
4450c0d06caSMauro Carvalho Chehab 	ret = usb_set_interface(pdev->udev, 0, pdev->valternate);
4460c0d06caSMauro Carvalho Chehab 	if (ret == -ENOSPC && compression < 3) {
4470c0d06caSMauro Carvalho Chehab 		compression++;
4480c0d06caSMauro Carvalho Chehab 		goto retry;
4490c0d06caSMauro Carvalho Chehab 	}
4500c0d06caSMauro Carvalho Chehab 	if (ret < 0)
4510c0d06caSMauro Carvalho Chehab 		return ret;
4520c0d06caSMauro Carvalho Chehab 
4530c0d06caSMauro Carvalho Chehab 	/* Allocate and init Isochronuous urbs */
4540c0d06caSMauro Carvalho Chehab 	for (i = 0; i < MAX_ISO_BUFS; i++) {
4550c0d06caSMauro Carvalho Chehab 		urb = usb_alloc_urb(ISO_FRAMES_PER_DESC, GFP_KERNEL);
4560c0d06caSMauro Carvalho Chehab 		if (urb == NULL) {
4570c0d06caSMauro Carvalho Chehab 			pwc_isoc_cleanup(pdev);
4580c0d06caSMauro Carvalho Chehab 			return -ENOMEM;
4590c0d06caSMauro Carvalho Chehab 		}
4600c0d06caSMauro Carvalho Chehab 		pdev->urbs[i] = urb;
4610c0d06caSMauro Carvalho Chehab 		PWC_DEBUG_MEMORY("Allocated URB at 0x%p\n", urb);
4620c0d06caSMauro Carvalho Chehab 
4630c0d06caSMauro Carvalho Chehab 		urb->interval = 1; // devik
4640c0d06caSMauro Carvalho Chehab 		urb->dev = udev;
4650c0d06caSMauro Carvalho Chehab 		urb->pipe = usb_rcvisocpipe(udev, pdev->vendpoint);
4660c0d06caSMauro Carvalho Chehab 		urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
4671161db67SMatwey V. Kornilov 		urb->transfer_buffer_length = ISO_BUFFER_SIZE;
468*69c9e825SMatwey V. Kornilov 		urb->transfer_buffer = pwc_alloc_urb_buffer(udev,
4691161db67SMatwey V. Kornilov 							    urb->transfer_buffer_length,
4700c0d06caSMauro Carvalho Chehab 							    &urb->transfer_dma);
4710c0d06caSMauro Carvalho Chehab 		if (urb->transfer_buffer == NULL) {
4720c0d06caSMauro Carvalho Chehab 			PWC_ERROR("Failed to allocate urb buffer %d\n", i);
4730c0d06caSMauro Carvalho Chehab 			pwc_isoc_cleanup(pdev);
4740c0d06caSMauro Carvalho Chehab 			return -ENOMEM;
4750c0d06caSMauro Carvalho Chehab 		}
4760c0d06caSMauro Carvalho Chehab 		urb->complete = pwc_isoc_handler;
4770c0d06caSMauro Carvalho Chehab 		urb->context = pdev;
4780c0d06caSMauro Carvalho Chehab 		urb->start_frame = 0;
4790c0d06caSMauro Carvalho Chehab 		urb->number_of_packets = ISO_FRAMES_PER_DESC;
4800c0d06caSMauro Carvalho Chehab 		for (j = 0; j < ISO_FRAMES_PER_DESC; j++) {
4810c0d06caSMauro Carvalho Chehab 			urb->iso_frame_desc[j].offset = j * ISO_MAX_FRAME_SIZE;
4820c0d06caSMauro Carvalho Chehab 			urb->iso_frame_desc[j].length = pdev->vmax_packet_size;
4830c0d06caSMauro Carvalho Chehab 		}
4840c0d06caSMauro Carvalho Chehab 	}
4850c0d06caSMauro Carvalho Chehab 
4860c0d06caSMauro Carvalho Chehab 	/* link */
4870c0d06caSMauro Carvalho Chehab 	for (i = 0; i < MAX_ISO_BUFS; i++) {
4880c0d06caSMauro Carvalho Chehab 		ret = usb_submit_urb(pdev->urbs[i], GFP_KERNEL);
4890c0d06caSMauro Carvalho Chehab 		if (ret == -ENOSPC && compression < 3) {
4900c0d06caSMauro Carvalho Chehab 			compression++;
4910c0d06caSMauro Carvalho Chehab 			pwc_isoc_cleanup(pdev);
4920c0d06caSMauro Carvalho Chehab 			goto retry;
4930c0d06caSMauro Carvalho Chehab 		}
4940c0d06caSMauro Carvalho Chehab 		if (ret) {
4950c0d06caSMauro Carvalho Chehab 			PWC_ERROR("isoc_init() submit_urb %d failed with error %d\n", i, ret);
4960c0d06caSMauro Carvalho Chehab 			pwc_isoc_cleanup(pdev);
4970c0d06caSMauro Carvalho Chehab 			return ret;
4980c0d06caSMauro Carvalho Chehab 		}
4990c0d06caSMauro Carvalho Chehab 		PWC_DEBUG_MEMORY("URB 0x%p submitted.\n", pdev->urbs[i]);
5000c0d06caSMauro Carvalho Chehab 	}
5010c0d06caSMauro Carvalho Chehab 
5020c0d06caSMauro Carvalho Chehab 	/* All is done... */
5030c0d06caSMauro Carvalho Chehab 	PWC_DEBUG_OPEN("<< pwc_isoc_init()\n");
5040c0d06caSMauro Carvalho Chehab 	return 0;
5050c0d06caSMauro Carvalho Chehab }
5060c0d06caSMauro Carvalho Chehab 
pwc_iso_stop(struct pwc_device * pdev)5070c0d06caSMauro Carvalho Chehab static void pwc_iso_stop(struct pwc_device *pdev)
5080c0d06caSMauro Carvalho Chehab {
5090c0d06caSMauro Carvalho Chehab 	int i;
5100c0d06caSMauro Carvalho Chehab 
5110c0d06caSMauro Carvalho Chehab 	/* Unlinking ISOC buffers one by one */
5120c0d06caSMauro Carvalho Chehab 	for (i = 0; i < MAX_ISO_BUFS; i++) {
5130c0d06caSMauro Carvalho Chehab 		if (pdev->urbs[i]) {
5140c0d06caSMauro Carvalho Chehab 			PWC_DEBUG_MEMORY("Unlinking URB %p\n", pdev->urbs[i]);
5150c0d06caSMauro Carvalho Chehab 			usb_kill_urb(pdev->urbs[i]);
5160c0d06caSMauro Carvalho Chehab 		}
5170c0d06caSMauro Carvalho Chehab 	}
5180c0d06caSMauro Carvalho Chehab }
5190c0d06caSMauro Carvalho Chehab 
pwc_iso_free(struct pwc_device * pdev)5200c0d06caSMauro Carvalho Chehab static void pwc_iso_free(struct pwc_device *pdev)
5210c0d06caSMauro Carvalho Chehab {
5220c0d06caSMauro Carvalho Chehab 	int i;
5230c0d06caSMauro Carvalho Chehab 
5240c0d06caSMauro Carvalho Chehab 	/* Freeing ISOC buffers one by one */
5250c0d06caSMauro Carvalho Chehab 	for (i = 0; i < MAX_ISO_BUFS; i++) {
5261161db67SMatwey V. Kornilov 		struct urb *urb = pdev->urbs[i];
5271161db67SMatwey V. Kornilov 
5281161db67SMatwey V. Kornilov 		if (urb) {
5290c0d06caSMauro Carvalho Chehab 			PWC_DEBUG_MEMORY("Freeing URB\n");
5301161db67SMatwey V. Kornilov 			if (urb->transfer_buffer)
531*69c9e825SMatwey V. Kornilov 				pwc_free_urb_buffer(urb->dev,
5321161db67SMatwey V. Kornilov 						    urb->transfer_buffer_length,
5331161db67SMatwey V. Kornilov 						    urb->transfer_buffer,
5341161db67SMatwey V. Kornilov 						    urb->transfer_dma);
5351161db67SMatwey V. Kornilov 			usb_free_urb(urb);
5360c0d06caSMauro Carvalho Chehab 			pdev->urbs[i] = NULL;
5370c0d06caSMauro Carvalho Chehab 		}
5380c0d06caSMauro Carvalho Chehab 	}
5390c0d06caSMauro Carvalho Chehab }
5400c0d06caSMauro Carvalho Chehab 
5410c0d06caSMauro Carvalho Chehab /* Both v4l2_lock and vb_queue_lock should be locked when calling this */
pwc_isoc_cleanup(struct pwc_device * pdev)5420c0d06caSMauro Carvalho Chehab static void pwc_isoc_cleanup(struct pwc_device *pdev)
5430c0d06caSMauro Carvalho Chehab {
5440c0d06caSMauro Carvalho Chehab 	PWC_DEBUG_OPEN(">> pwc_isoc_cleanup()\n");
5450c0d06caSMauro Carvalho Chehab 
5460c0d06caSMauro Carvalho Chehab 	pwc_iso_stop(pdev);
5470c0d06caSMauro Carvalho Chehab 	pwc_iso_free(pdev);
5480c0d06caSMauro Carvalho Chehab 	usb_set_interface(pdev->udev, 0, 0);
5490c0d06caSMauro Carvalho Chehab 
5500c0d06caSMauro Carvalho Chehab 	PWC_DEBUG_OPEN("<< pwc_isoc_cleanup()\n");
5510c0d06caSMauro Carvalho Chehab }
5520c0d06caSMauro Carvalho Chehab 
5530c0d06caSMauro Carvalho Chehab /* Must be called with vb_queue_lock hold */
pwc_cleanup_queued_bufs(struct pwc_device * pdev,enum vb2_buffer_state state)55480b0963eSHans Verkuil static void pwc_cleanup_queued_bufs(struct pwc_device *pdev,
55580b0963eSHans Verkuil 				    enum vb2_buffer_state state)
5560c0d06caSMauro Carvalho Chehab {
5570c0d06caSMauro Carvalho Chehab 	unsigned long flags = 0;
5580c0d06caSMauro Carvalho Chehab 
5590c0d06caSMauro Carvalho Chehab 	spin_lock_irqsave(&pdev->queued_bufs_lock, flags);
5600c0d06caSMauro Carvalho Chehab 	while (!list_empty(&pdev->queued_bufs)) {
5610c0d06caSMauro Carvalho Chehab 		struct pwc_frame_buf *buf;
5620c0d06caSMauro Carvalho Chehab 
5630c0d06caSMauro Carvalho Chehab 		buf = list_entry(pdev->queued_bufs.next, struct pwc_frame_buf,
5640c0d06caSMauro Carvalho Chehab 				 list);
5650c0d06caSMauro Carvalho Chehab 		list_del(&buf->list);
5662d700715SJunghak Sung 		vb2_buffer_done(&buf->vb.vb2_buf, state);
5670c0d06caSMauro Carvalho Chehab 	}
5680c0d06caSMauro Carvalho Chehab 	spin_unlock_irqrestore(&pdev->queued_bufs_lock, flags);
5690c0d06caSMauro Carvalho Chehab }
5700c0d06caSMauro Carvalho Chehab 
5710c0d06caSMauro Carvalho Chehab #ifdef CONFIG_USB_PWC_DEBUG
pwc_sensor_type_to_string(unsigned int sensor_type)5720c0d06caSMauro Carvalho Chehab static const char *pwc_sensor_type_to_string(unsigned int sensor_type)
5730c0d06caSMauro Carvalho Chehab {
5740c0d06caSMauro Carvalho Chehab 	switch(sensor_type) {
5750c0d06caSMauro Carvalho Chehab 		case 0x00:
5760c0d06caSMauro Carvalho Chehab 			return "Hyundai CMOS sensor";
5770c0d06caSMauro Carvalho Chehab 		case 0x20:
5780c0d06caSMauro Carvalho Chehab 			return "Sony CCD sensor + TDA8787";
5790c0d06caSMauro Carvalho Chehab 		case 0x2E:
5800c0d06caSMauro Carvalho Chehab 			return "Sony CCD sensor + Exas 98L59";
5810c0d06caSMauro Carvalho Chehab 		case 0x2F:
5820c0d06caSMauro Carvalho Chehab 			return "Sony CCD sensor + ADI 9804";
5830c0d06caSMauro Carvalho Chehab 		case 0x30:
5840c0d06caSMauro Carvalho Chehab 			return "Sharp CCD sensor + TDA8787";
5850c0d06caSMauro Carvalho Chehab 		case 0x3E:
5860c0d06caSMauro Carvalho Chehab 			return "Sharp CCD sensor + Exas 98L59";
5870c0d06caSMauro Carvalho Chehab 		case 0x3F:
5880c0d06caSMauro Carvalho Chehab 			return "Sharp CCD sensor + ADI 9804";
5890c0d06caSMauro Carvalho Chehab 		case 0x40:
5900c0d06caSMauro Carvalho Chehab 			return "UPA 1021 sensor";
5910c0d06caSMauro Carvalho Chehab 		case 0x100:
5920c0d06caSMauro Carvalho Chehab 			return "VGA sensor";
5930c0d06caSMauro Carvalho Chehab 		case 0x101:
5940c0d06caSMauro Carvalho Chehab 			return "PAL MR sensor";
5950c0d06caSMauro Carvalho Chehab 		default:
5960c0d06caSMauro Carvalho Chehab 			return "unknown type of sensor";
5970c0d06caSMauro Carvalho Chehab 	}
5980c0d06caSMauro Carvalho Chehab }
5990c0d06caSMauro Carvalho Chehab #endif
6000c0d06caSMauro Carvalho Chehab 
6010c0d06caSMauro Carvalho Chehab /***************************************************************************/
6020c0d06caSMauro Carvalho Chehab /* Video4Linux functions */
6030c0d06caSMauro Carvalho Chehab 
pwc_video_release(struct v4l2_device * v)6040c0d06caSMauro Carvalho Chehab static void pwc_video_release(struct v4l2_device *v)
6050c0d06caSMauro Carvalho Chehab {
6060c0d06caSMauro Carvalho Chehab 	struct pwc_device *pdev = container_of(v, struct pwc_device, v4l2_dev);
6070c0d06caSMauro Carvalho Chehab 
6080c0d06caSMauro Carvalho Chehab 	v4l2_ctrl_handler_free(&pdev->ctrl_handler);
6090c0d06caSMauro Carvalho Chehab 	v4l2_device_unregister(&pdev->v4l2_dev);
6100c0d06caSMauro Carvalho Chehab 	kfree(pdev->ctrl_buf);
6110c0d06caSMauro Carvalho Chehab 	kfree(pdev);
6120c0d06caSMauro Carvalho Chehab }
6130c0d06caSMauro Carvalho Chehab 
6140c0d06caSMauro Carvalho Chehab /***************************************************************************/
6150c0d06caSMauro Carvalho Chehab /* Videobuf2 operations */
6160c0d06caSMauro Carvalho Chehab 
queue_setup(struct vb2_queue * vq,unsigned int * nbuffers,unsigned int * nplanes,unsigned int sizes[],struct device * alloc_devs[])617df9ecb0cSHans Verkuil static int queue_setup(struct vb2_queue *vq,
6180c0d06caSMauro Carvalho Chehab 				unsigned int *nbuffers, unsigned int *nplanes,
61936c0f8b3SHans Verkuil 				unsigned int sizes[], struct device *alloc_devs[])
6200c0d06caSMauro Carvalho Chehab {
6210c0d06caSMauro Carvalho Chehab 	struct pwc_device *pdev = vb2_get_drv_priv(vq);
6220c0d06caSMauro Carvalho Chehab 	int size;
6230c0d06caSMauro Carvalho Chehab 
6240c0d06caSMauro Carvalho Chehab 	if (*nbuffers < MIN_FRAMES)
6250c0d06caSMauro Carvalho Chehab 		*nbuffers = MIN_FRAMES;
6260c0d06caSMauro Carvalho Chehab 	else if (*nbuffers > MAX_FRAMES)
6270c0d06caSMauro Carvalho Chehab 		*nbuffers = MAX_FRAMES;
6280c0d06caSMauro Carvalho Chehab 
6290c0d06caSMauro Carvalho Chehab 	*nplanes = 1;
6300c0d06caSMauro Carvalho Chehab 
6310c0d06caSMauro Carvalho Chehab 	size = pwc_get_size(pdev, MAX_WIDTH, MAX_HEIGHT);
6320c0d06caSMauro Carvalho Chehab 	sizes[0] = PAGE_ALIGN(pwc_image_sizes[size][0] *
6330c0d06caSMauro Carvalho Chehab 			      pwc_image_sizes[size][1] * 3 / 2);
6340c0d06caSMauro Carvalho Chehab 
6350c0d06caSMauro Carvalho Chehab 	return 0;
6360c0d06caSMauro Carvalho Chehab }
6370c0d06caSMauro Carvalho Chehab 
buffer_init(struct vb2_buffer * vb)6380c0d06caSMauro Carvalho Chehab static int buffer_init(struct vb2_buffer *vb)
6390c0d06caSMauro Carvalho Chehab {
6402d700715SJunghak Sung 	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
6412d700715SJunghak Sung 	struct pwc_frame_buf *buf =
6422d700715SJunghak Sung 		container_of(vbuf, struct pwc_frame_buf, vb);
6430c0d06caSMauro Carvalho Chehab 
6440c0d06caSMauro Carvalho Chehab 	/* need vmalloc since frame buffer > 128K */
6450c0d06caSMauro Carvalho Chehab 	buf->data = vzalloc(PWC_FRAME_SIZE);
6460c0d06caSMauro Carvalho Chehab 	if (buf->data == NULL)
6470c0d06caSMauro Carvalho Chehab 		return -ENOMEM;
6480c0d06caSMauro Carvalho Chehab 
6490c0d06caSMauro Carvalho Chehab 	return 0;
6500c0d06caSMauro Carvalho Chehab }
6510c0d06caSMauro Carvalho Chehab 
buffer_prepare(struct vb2_buffer * vb)6520c0d06caSMauro Carvalho Chehab static int buffer_prepare(struct vb2_buffer *vb)
6530c0d06caSMauro Carvalho Chehab {
6540c0d06caSMauro Carvalho Chehab 	struct pwc_device *pdev = vb2_get_drv_priv(vb->vb2_queue);
6550c0d06caSMauro Carvalho Chehab 
6563e4d8f48SMauro Carvalho Chehab 	/* Don't allow queueing new buffers after device disconnection */
6570c0d06caSMauro Carvalho Chehab 	if (!pdev->udev)
6580c0d06caSMauro Carvalho Chehab 		return -ENODEV;
6590c0d06caSMauro Carvalho Chehab 
6600c0d06caSMauro Carvalho Chehab 	return 0;
6610c0d06caSMauro Carvalho Chehab }
6620c0d06caSMauro Carvalho Chehab 
buffer_finish(struct vb2_buffer * vb)66306470642SHans Verkuil static void buffer_finish(struct vb2_buffer *vb)
6640c0d06caSMauro Carvalho Chehab {
6650c0d06caSMauro Carvalho Chehab 	struct pwc_device *pdev = vb2_get_drv_priv(vb->vb2_queue);
6662d700715SJunghak Sung 	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
6672d700715SJunghak Sung 	struct pwc_frame_buf *buf =
6682d700715SJunghak Sung 		container_of(vbuf, struct pwc_frame_buf, vb);
6690c0d06caSMauro Carvalho Chehab 
6701a179481SHans Verkuil 	if (vb->state == VB2_BUF_STATE_DONE) {
6710c0d06caSMauro Carvalho Chehab 		/*
6721a179481SHans Verkuil 		 * Application has called dqbuf and is getting back a buffer
6731a179481SHans Verkuil 		 * we've filled, take the pwc data we've stored in buf->data
6741a179481SHans Verkuil 		 * and decompress it into a usable format, storing the result
6751a179481SHans Verkuil 		 * in the vb2_buffer.
6760c0d06caSMauro Carvalho Chehab 		 */
67706470642SHans Verkuil 		pwc_decompress(pdev, buf);
6780c0d06caSMauro Carvalho Chehab 	}
6791a179481SHans Verkuil }
6800c0d06caSMauro Carvalho Chehab 
buffer_cleanup(struct vb2_buffer * vb)6810c0d06caSMauro Carvalho Chehab static void buffer_cleanup(struct vb2_buffer *vb)
6820c0d06caSMauro Carvalho Chehab {
6832d700715SJunghak Sung 	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
6842d700715SJunghak Sung 	struct pwc_frame_buf *buf =
6852d700715SJunghak Sung 		container_of(vbuf, struct pwc_frame_buf, vb);
6860c0d06caSMauro Carvalho Chehab 
6870c0d06caSMauro Carvalho Chehab 	vfree(buf->data);
6880c0d06caSMauro Carvalho Chehab }
6890c0d06caSMauro Carvalho Chehab 
buffer_queue(struct vb2_buffer * vb)6900c0d06caSMauro Carvalho Chehab static void buffer_queue(struct vb2_buffer *vb)
6910c0d06caSMauro Carvalho Chehab {
6920c0d06caSMauro Carvalho Chehab 	struct pwc_device *pdev = vb2_get_drv_priv(vb->vb2_queue);
6932d700715SJunghak Sung 	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
6942d700715SJunghak Sung 	struct pwc_frame_buf *buf =
6952d700715SJunghak Sung 		container_of(vbuf, struct pwc_frame_buf, vb);
6960c0d06caSMauro Carvalho Chehab 	unsigned long flags = 0;
6970c0d06caSMauro Carvalho Chehab 
6980c0d06caSMauro Carvalho Chehab 	/* Check the device has not disconnected between prep and queuing */
6990c0d06caSMauro Carvalho Chehab 	if (!pdev->udev) {
7002d700715SJunghak Sung 		vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
7010c0d06caSMauro Carvalho Chehab 		return;
7020c0d06caSMauro Carvalho Chehab 	}
7030c0d06caSMauro Carvalho Chehab 
7040c0d06caSMauro Carvalho Chehab 	spin_lock_irqsave(&pdev->queued_bufs_lock, flags);
7050c0d06caSMauro Carvalho Chehab 	list_add_tail(&buf->list, &pdev->queued_bufs);
7060c0d06caSMauro Carvalho Chehab 	spin_unlock_irqrestore(&pdev->queued_bufs_lock, flags);
7070c0d06caSMauro Carvalho Chehab }
7080c0d06caSMauro Carvalho Chehab 
start_streaming(struct vb2_queue * vq,unsigned int count)7090c0d06caSMauro Carvalho Chehab static int start_streaming(struct vb2_queue *vq, unsigned int count)
7100c0d06caSMauro Carvalho Chehab {
7110c0d06caSMauro Carvalho Chehab 	struct pwc_device *pdev = vb2_get_drv_priv(vq);
7120c0d06caSMauro Carvalho Chehab 	int r;
7130c0d06caSMauro Carvalho Chehab 
7140c0d06caSMauro Carvalho Chehab 	if (!pdev->udev)
7150c0d06caSMauro Carvalho Chehab 		return -ENODEV;
7160c0d06caSMauro Carvalho Chehab 
7170c0d06caSMauro Carvalho Chehab 	if (mutex_lock_interruptible(&pdev->v4l2_lock))
7180c0d06caSMauro Carvalho Chehab 		return -ERESTARTSYS;
7190c0d06caSMauro Carvalho Chehab 	/* Turn on camera and set LEDS on */
7200c0d06caSMauro Carvalho Chehab 	pwc_camera_power(pdev, 1);
7210c0d06caSMauro Carvalho Chehab 	pwc_set_leds(pdev, leds[0], leds[1]);
7220c0d06caSMauro Carvalho Chehab 
7230c0d06caSMauro Carvalho Chehab 	r = pwc_isoc_init(pdev);
7240c0d06caSMauro Carvalho Chehab 	if (r) {
7250c0d06caSMauro Carvalho Chehab 		/* If we failed turn camera and LEDS back off */
7260c0d06caSMauro Carvalho Chehab 		pwc_set_leds(pdev, 0, 0);
7270c0d06caSMauro Carvalho Chehab 		pwc_camera_power(pdev, 0);
7280c0d06caSMauro Carvalho Chehab 		/* And cleanup any queued bufs!! */
72980b0963eSHans Verkuil 		pwc_cleanup_queued_bufs(pdev, VB2_BUF_STATE_QUEUED);
7300c0d06caSMauro Carvalho Chehab 	}
7310c0d06caSMauro Carvalho Chehab 	mutex_unlock(&pdev->v4l2_lock);
7320c0d06caSMauro Carvalho Chehab 
7330c0d06caSMauro Carvalho Chehab 	return r;
7340c0d06caSMauro Carvalho Chehab }
7350c0d06caSMauro Carvalho Chehab 
stop_streaming(struct vb2_queue * vq)736e37559b2SHans Verkuil static void stop_streaming(struct vb2_queue *vq)
7370c0d06caSMauro Carvalho Chehab {
7380c0d06caSMauro Carvalho Chehab 	struct pwc_device *pdev = vb2_get_drv_priv(vq);
7390c0d06caSMauro Carvalho Chehab 
740e37559b2SHans Verkuil 	mutex_lock(&pdev->v4l2_lock);
7410c0d06caSMauro Carvalho Chehab 	if (pdev->udev) {
7420c0d06caSMauro Carvalho Chehab 		pwc_set_leds(pdev, 0, 0);
7430c0d06caSMauro Carvalho Chehab 		pwc_camera_power(pdev, 0);
7440c0d06caSMauro Carvalho Chehab 		pwc_isoc_cleanup(pdev);
7450c0d06caSMauro Carvalho Chehab 	}
7460c0d06caSMauro Carvalho Chehab 
74780b0963eSHans Verkuil 	pwc_cleanup_queued_bufs(pdev, VB2_BUF_STATE_ERROR);
74880b0963eSHans Verkuil 	if (pdev->fill_buf)
7492d700715SJunghak Sung 		vb2_buffer_done(&pdev->fill_buf->vb.vb2_buf,
7502d700715SJunghak Sung 				VB2_BUF_STATE_ERROR);
7510c0d06caSMauro Carvalho Chehab 	mutex_unlock(&pdev->v4l2_lock);
7520c0d06caSMauro Carvalho Chehab }
7530c0d06caSMauro Carvalho Chehab 
7541bc17717SJulia Lawall static const struct vb2_ops pwc_vb_queue_ops = {
7550c0d06caSMauro Carvalho Chehab 	.queue_setup		= queue_setup,
7560c0d06caSMauro Carvalho Chehab 	.buf_init		= buffer_init,
7570c0d06caSMauro Carvalho Chehab 	.buf_prepare		= buffer_prepare,
7580c0d06caSMauro Carvalho Chehab 	.buf_finish		= buffer_finish,
7590c0d06caSMauro Carvalho Chehab 	.buf_cleanup		= buffer_cleanup,
7600c0d06caSMauro Carvalho Chehab 	.buf_queue		= buffer_queue,
7610c0d06caSMauro Carvalho Chehab 	.start_streaming	= start_streaming,
7620c0d06caSMauro Carvalho Chehab 	.stop_streaming		= stop_streaming,
7630c0d06caSMauro Carvalho Chehab 	.wait_prepare		= vb2_ops_wait_prepare,
7640c0d06caSMauro Carvalho Chehab 	.wait_finish		= vb2_ops_wait_finish,
7650c0d06caSMauro Carvalho Chehab };
7660c0d06caSMauro Carvalho Chehab 
7670c0d06caSMauro Carvalho Chehab /***************************************************************************/
7680c0d06caSMauro Carvalho Chehab /* USB functions */
7690c0d06caSMauro Carvalho Chehab 
7700c0d06caSMauro Carvalho Chehab /* This function gets called when a new device is plugged in or the usb core
7710c0d06caSMauro Carvalho Chehab  * is loaded.
7720c0d06caSMauro Carvalho Chehab  */
7730c0d06caSMauro Carvalho Chehab 
usb_pwc_probe(struct usb_interface * intf,const struct usb_device_id * id)7740c0d06caSMauro Carvalho Chehab static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id *id)
7750c0d06caSMauro Carvalho Chehab {
7760c0d06caSMauro Carvalho Chehab 	struct usb_device *udev = interface_to_usbdev(intf);
7770c0d06caSMauro Carvalho Chehab 	struct pwc_device *pdev = NULL;
7780c0d06caSMauro Carvalho Chehab 	int vendor_id, product_id, type_id;
7790c0d06caSMauro Carvalho Chehab 	int rc;
7800c0d06caSMauro Carvalho Chehab 	int features = 0;
7810c0d06caSMauro Carvalho Chehab 	int compression = 0;
7820c0d06caSMauro Carvalho Chehab 	int my_power_save = power_save;
7830c0d06caSMauro Carvalho Chehab 	char serial_number[30], *name;
7840c0d06caSMauro Carvalho Chehab 
7850c0d06caSMauro Carvalho Chehab 	vendor_id = le16_to_cpu(udev->descriptor.idVendor);
7860c0d06caSMauro Carvalho Chehab 	product_id = le16_to_cpu(udev->descriptor.idProduct);
7870c0d06caSMauro Carvalho Chehab 
7880c0d06caSMauro Carvalho Chehab 	/* Check if we can handle this device */
7890c0d06caSMauro Carvalho Chehab 	PWC_DEBUG_PROBE("probe() called [%04X %04X], if %d\n",
7900c0d06caSMauro Carvalho Chehab 		vendor_id, product_id,
7910c0d06caSMauro Carvalho Chehab 		intf->altsetting->desc.bInterfaceNumber);
7920c0d06caSMauro Carvalho Chehab 
7930c0d06caSMauro Carvalho Chehab 	/* the interfaces are probed one by one. We are only interested in the
7940c0d06caSMauro Carvalho Chehab 	   video interface (0) now.
7950c0d06caSMauro Carvalho Chehab 	   Interface 1 is the Audio Control, and interface 2 Audio itself.
7960c0d06caSMauro Carvalho Chehab 	 */
7970c0d06caSMauro Carvalho Chehab 	if (intf->altsetting->desc.bInterfaceNumber > 0)
7980c0d06caSMauro Carvalho Chehab 		return -ENODEV;
7990c0d06caSMauro Carvalho Chehab 
8000c0d06caSMauro Carvalho Chehab 	if (vendor_id == 0x0471) {
8010c0d06caSMauro Carvalho Chehab 		switch (product_id) {
8020c0d06caSMauro Carvalho Chehab 		case 0x0302:
8030c0d06caSMauro Carvalho Chehab 			PWC_INFO("Philips PCA645VC USB webcam detected.\n");
8040c0d06caSMauro Carvalho Chehab 			name = "Philips 645 webcam";
8050c0d06caSMauro Carvalho Chehab 			type_id = 645;
8060c0d06caSMauro Carvalho Chehab 			break;
8070c0d06caSMauro Carvalho Chehab 		case 0x0303:
8080c0d06caSMauro Carvalho Chehab 			PWC_INFO("Philips PCA646VC USB webcam detected.\n");
8090c0d06caSMauro Carvalho Chehab 			name = "Philips 646 webcam";
8100c0d06caSMauro Carvalho Chehab 			type_id = 646;
8110c0d06caSMauro Carvalho Chehab 			break;
8120c0d06caSMauro Carvalho Chehab 		case 0x0304:
8130c0d06caSMauro Carvalho Chehab 			PWC_INFO("Askey VC010 type 2 USB webcam detected.\n");
8140c0d06caSMauro Carvalho Chehab 			name = "Askey VC010 webcam";
8150c0d06caSMauro Carvalho Chehab 			type_id = 646;
8160c0d06caSMauro Carvalho Chehab 			break;
8170c0d06caSMauro Carvalho Chehab 		case 0x0307:
8180c0d06caSMauro Carvalho Chehab 			PWC_INFO("Philips PCVC675K (Vesta) USB webcam detected.\n");
8190c0d06caSMauro Carvalho Chehab 			name = "Philips 675 webcam";
8200c0d06caSMauro Carvalho Chehab 			type_id = 675;
8210c0d06caSMauro Carvalho Chehab 			break;
8220c0d06caSMauro Carvalho Chehab 		case 0x0308:
8230c0d06caSMauro Carvalho Chehab 			PWC_INFO("Philips PCVC680K (Vesta Pro) USB webcam detected.\n");
8240c0d06caSMauro Carvalho Chehab 			name = "Philips 680 webcam";
8250c0d06caSMauro Carvalho Chehab 			type_id = 680;
8260c0d06caSMauro Carvalho Chehab 			break;
8270c0d06caSMauro Carvalho Chehab 		case 0x030C:
8280c0d06caSMauro Carvalho Chehab 			PWC_INFO("Philips PCVC690K (Vesta Pro Scan) USB webcam detected.\n");
8290c0d06caSMauro Carvalho Chehab 			name = "Philips 690 webcam";
8300c0d06caSMauro Carvalho Chehab 			type_id = 690;
8310c0d06caSMauro Carvalho Chehab 			break;
8320c0d06caSMauro Carvalho Chehab 		case 0x0310:
8330c0d06caSMauro Carvalho Chehab 			PWC_INFO("Philips PCVC730K (ToUCam Fun)/PCVC830 (ToUCam II) USB webcam detected.\n");
8340c0d06caSMauro Carvalho Chehab 			name = "Philips 730 webcam";
8350c0d06caSMauro Carvalho Chehab 			type_id = 730;
8360c0d06caSMauro Carvalho Chehab 			break;
8370c0d06caSMauro Carvalho Chehab 		case 0x0311:
8380c0d06caSMauro Carvalho Chehab 			PWC_INFO("Philips PCVC740K (ToUCam Pro)/PCVC840 (ToUCam II) USB webcam detected.\n");
8390c0d06caSMauro Carvalho Chehab 			name = "Philips 740 webcam";
8400c0d06caSMauro Carvalho Chehab 			type_id = 740;
8410c0d06caSMauro Carvalho Chehab 			break;
8420c0d06caSMauro Carvalho Chehab 		case 0x0312:
8430c0d06caSMauro Carvalho Chehab 			PWC_INFO("Philips PCVC750K (ToUCam Pro Scan) USB webcam detected.\n");
8440c0d06caSMauro Carvalho Chehab 			name = "Philips 750 webcam";
8450c0d06caSMauro Carvalho Chehab 			type_id = 750;
8460c0d06caSMauro Carvalho Chehab 			break;
8470c0d06caSMauro Carvalho Chehab 		case 0x0313:
8480c0d06caSMauro Carvalho Chehab 			PWC_INFO("Philips PCVC720K/40 (ToUCam XS) USB webcam detected.\n");
8490c0d06caSMauro Carvalho Chehab 			name = "Philips 720K/40 webcam";
8500c0d06caSMauro Carvalho Chehab 			type_id = 720;
8510c0d06caSMauro Carvalho Chehab 			break;
8520c0d06caSMauro Carvalho Chehab 		case 0x0329:
8530c0d06caSMauro Carvalho Chehab 			PWC_INFO("Philips SPC 900NC USB webcam detected.\n");
8540c0d06caSMauro Carvalho Chehab 			name = "Philips SPC 900NC webcam";
8550c0d06caSMauro Carvalho Chehab 			type_id = 740;
8560c0d06caSMauro Carvalho Chehab 			break;
8577445e45dSHans de Goede 		case 0x032C:
8587445e45dSHans de Goede 			PWC_INFO("Philips SPC 880NC USB webcam detected.\n");
8597445e45dSHans de Goede 			name = "Philips SPC 880NC webcam";
8607445e45dSHans de Goede 			type_id = 740;
8617445e45dSHans de Goede 			break;
8620c0d06caSMauro Carvalho Chehab 		default:
8630c0d06caSMauro Carvalho Chehab 			return -ENODEV;
8640c0d06caSMauro Carvalho Chehab 		}
8650c0d06caSMauro Carvalho Chehab 	}
8660c0d06caSMauro Carvalho Chehab 	else if (vendor_id == 0x069A) {
8670c0d06caSMauro Carvalho Chehab 		switch(product_id) {
8680c0d06caSMauro Carvalho Chehab 		case 0x0001:
8690c0d06caSMauro Carvalho Chehab 			PWC_INFO("Askey VC010 type 1 USB webcam detected.\n");
8700c0d06caSMauro Carvalho Chehab 			name = "Askey VC010 webcam";
8710c0d06caSMauro Carvalho Chehab 			type_id = 645;
8720c0d06caSMauro Carvalho Chehab 			break;
8730c0d06caSMauro Carvalho Chehab 		default:
8740c0d06caSMauro Carvalho Chehab 			return -ENODEV;
8750c0d06caSMauro Carvalho Chehab 		}
8760c0d06caSMauro Carvalho Chehab 	}
8770c0d06caSMauro Carvalho Chehab 	else if (vendor_id == 0x046d) {
8780c0d06caSMauro Carvalho Chehab 		switch(product_id) {
8790c0d06caSMauro Carvalho Chehab 		case 0x08b0:
8800c0d06caSMauro Carvalho Chehab 			PWC_INFO("Logitech QuickCam Pro 3000 USB webcam detected.\n");
8810c0d06caSMauro Carvalho Chehab 			name = "Logitech QuickCam Pro 3000";
8820c0d06caSMauro Carvalho Chehab 			type_id = 740; /* CCD sensor */
8830c0d06caSMauro Carvalho Chehab 			break;
8840c0d06caSMauro Carvalho Chehab 		case 0x08b1:
8850c0d06caSMauro Carvalho Chehab 			PWC_INFO("Logitech QuickCam Notebook Pro USB webcam detected.\n");
8860c0d06caSMauro Carvalho Chehab 			name = "Logitech QuickCam Notebook Pro";
8870c0d06caSMauro Carvalho Chehab 			type_id = 740; /* CCD sensor */
8880c0d06caSMauro Carvalho Chehab 			break;
8890c0d06caSMauro Carvalho Chehab 		case 0x08b2:
8900c0d06caSMauro Carvalho Chehab 			PWC_INFO("Logitech QuickCam 4000 Pro USB webcam detected.\n");
8910c0d06caSMauro Carvalho Chehab 			name = "Logitech QuickCam Pro 4000";
8920c0d06caSMauro Carvalho Chehab 			type_id = 740; /* CCD sensor */
8930c0d06caSMauro Carvalho Chehab 			if (my_power_save == -1)
8940c0d06caSMauro Carvalho Chehab 				my_power_save = 1;
8950c0d06caSMauro Carvalho Chehab 			break;
8960c0d06caSMauro Carvalho Chehab 		case 0x08b3:
8970c0d06caSMauro Carvalho Chehab 			PWC_INFO("Logitech QuickCam Zoom USB webcam detected.\n");
8980c0d06caSMauro Carvalho Chehab 			name = "Logitech QuickCam Zoom";
8990c0d06caSMauro Carvalho Chehab 			type_id = 740; /* CCD sensor */
9000c0d06caSMauro Carvalho Chehab 			break;
9010c0d06caSMauro Carvalho Chehab 		case 0x08B4:
9020c0d06caSMauro Carvalho Chehab 			PWC_INFO("Logitech QuickCam Zoom (new model) USB webcam detected.\n");
9030c0d06caSMauro Carvalho Chehab 			name = "Logitech QuickCam Zoom";
9040c0d06caSMauro Carvalho Chehab 			type_id = 740; /* CCD sensor */
9050c0d06caSMauro Carvalho Chehab 			if (my_power_save == -1)
9060c0d06caSMauro Carvalho Chehab 				my_power_save = 1;
9070c0d06caSMauro Carvalho Chehab 			break;
9080c0d06caSMauro Carvalho Chehab 		case 0x08b5:
9090c0d06caSMauro Carvalho Chehab 			PWC_INFO("Logitech QuickCam Orbit/Sphere USB webcam detected.\n");
9100c0d06caSMauro Carvalho Chehab 			name = "Logitech QuickCam Orbit";
9110c0d06caSMauro Carvalho Chehab 			type_id = 740; /* CCD sensor */
9120c0d06caSMauro Carvalho Chehab 			if (my_power_save == -1)
9130c0d06caSMauro Carvalho Chehab 				my_power_save = 1;
9140c0d06caSMauro Carvalho Chehab 			features |= FEATURE_MOTOR_PANTILT;
9150c0d06caSMauro Carvalho Chehab 			break;
9160c0d06caSMauro Carvalho Chehab 		case 0x08b6:
9170c0d06caSMauro Carvalho Chehab 			PWC_INFO("Logitech/Cisco VT Camera webcam detected.\n");
9180c0d06caSMauro Carvalho Chehab 			name = "Cisco VT Camera";
9190c0d06caSMauro Carvalho Chehab 			type_id = 740; /* CCD sensor */
9200c0d06caSMauro Carvalho Chehab 			break;
9210c0d06caSMauro Carvalho Chehab 		case 0x08b7:
9220c0d06caSMauro Carvalho Chehab 			PWC_INFO("Logitech ViewPort AV 100 webcam detected.\n");
9230c0d06caSMauro Carvalho Chehab 			name = "Logitech ViewPort AV 100";
9240c0d06caSMauro Carvalho Chehab 			type_id = 740; /* CCD sensor */
9250c0d06caSMauro Carvalho Chehab 			break;
9260c0d06caSMauro Carvalho Chehab 		case 0x08b8: /* Where this released? */
9270c0d06caSMauro Carvalho Chehab 			PWC_INFO("Logitech QuickCam detected (reserved ID).\n");
9280c0d06caSMauro Carvalho Chehab 			name = "Logitech QuickCam (res.)";
9290c0d06caSMauro Carvalho Chehab 			type_id = 730; /* Assuming CMOS */
9300c0d06caSMauro Carvalho Chehab 			break;
9310c0d06caSMauro Carvalho Chehab 		default:
9320c0d06caSMauro Carvalho Chehab 			return -ENODEV;
9330c0d06caSMauro Carvalho Chehab 		}
9340c0d06caSMauro Carvalho Chehab 	}
9350c0d06caSMauro Carvalho Chehab 	else if (vendor_id == 0x055d) {
9360c0d06caSMauro Carvalho Chehab 		/* I don't know the difference between the C10 and the C30;
9370c0d06caSMauro Carvalho Chehab 		   I suppose the difference is the sensor, but both cameras
9380c0d06caSMauro Carvalho Chehab 		   work equally well with a type_id of 675
9390c0d06caSMauro Carvalho Chehab 		 */
9400c0d06caSMauro Carvalho Chehab 		switch(product_id) {
9410c0d06caSMauro Carvalho Chehab 		case 0x9000:
9420c0d06caSMauro Carvalho Chehab 			PWC_INFO("Samsung MPC-C10 USB webcam detected.\n");
9430c0d06caSMauro Carvalho Chehab 			name = "Samsung MPC-C10";
9440c0d06caSMauro Carvalho Chehab 			type_id = 675;
9450c0d06caSMauro Carvalho Chehab 			break;
9460c0d06caSMauro Carvalho Chehab 		case 0x9001:
9470c0d06caSMauro Carvalho Chehab 			PWC_INFO("Samsung MPC-C30 USB webcam detected.\n");
9480c0d06caSMauro Carvalho Chehab 			name = "Samsung MPC-C30";
9490c0d06caSMauro Carvalho Chehab 			type_id = 675;
9500c0d06caSMauro Carvalho Chehab 			break;
9510c0d06caSMauro Carvalho Chehab 		case 0x9002:
9520c0d06caSMauro Carvalho Chehab 			PWC_INFO("Samsung SNC-35E (v3.0) USB webcam detected.\n");
9530c0d06caSMauro Carvalho Chehab 			name = "Samsung MPC-C30";
9540c0d06caSMauro Carvalho Chehab 			type_id = 740;
9550c0d06caSMauro Carvalho Chehab 			break;
9560c0d06caSMauro Carvalho Chehab 		default:
9570c0d06caSMauro Carvalho Chehab 			return -ENODEV;
9580c0d06caSMauro Carvalho Chehab 		}
9590c0d06caSMauro Carvalho Chehab 	}
9600c0d06caSMauro Carvalho Chehab 	else if (vendor_id == 0x041e) {
9610c0d06caSMauro Carvalho Chehab 		switch(product_id) {
9620c0d06caSMauro Carvalho Chehab 		case 0x400c:
9630c0d06caSMauro Carvalho Chehab 			PWC_INFO("Creative Labs Webcam 5 detected.\n");
9640c0d06caSMauro Carvalho Chehab 			name = "Creative Labs Webcam 5";
9650c0d06caSMauro Carvalho Chehab 			type_id = 730;
9660c0d06caSMauro Carvalho Chehab 			if (my_power_save == -1)
9670c0d06caSMauro Carvalho Chehab 				my_power_save = 1;
9680c0d06caSMauro Carvalho Chehab 			break;
9690c0d06caSMauro Carvalho Chehab 		case 0x4011:
9700c0d06caSMauro Carvalho Chehab 			PWC_INFO("Creative Labs Webcam Pro Ex detected.\n");
9710c0d06caSMauro Carvalho Chehab 			name = "Creative Labs Webcam Pro Ex";
9720c0d06caSMauro Carvalho Chehab 			type_id = 740;
9730c0d06caSMauro Carvalho Chehab 			break;
9740c0d06caSMauro Carvalho Chehab 		default:
9750c0d06caSMauro Carvalho Chehab 			return -ENODEV;
9760c0d06caSMauro Carvalho Chehab 		}
9770c0d06caSMauro Carvalho Chehab 	}
9780c0d06caSMauro Carvalho Chehab 	else if (vendor_id == 0x04cc) {
9790c0d06caSMauro Carvalho Chehab 		switch(product_id) {
9800c0d06caSMauro Carvalho Chehab 		case 0x8116:
9810c0d06caSMauro Carvalho Chehab 			PWC_INFO("Sotec Afina Eye USB webcam detected.\n");
9820c0d06caSMauro Carvalho Chehab 			name = "Sotec Afina Eye";
9830c0d06caSMauro Carvalho Chehab 			type_id = 730;
9840c0d06caSMauro Carvalho Chehab 			break;
9850c0d06caSMauro Carvalho Chehab 		default:
9860c0d06caSMauro Carvalho Chehab 			return -ENODEV;
9870c0d06caSMauro Carvalho Chehab 		}
9880c0d06caSMauro Carvalho Chehab 	}
9890c0d06caSMauro Carvalho Chehab 	else if (vendor_id == 0x06be) {
9900c0d06caSMauro Carvalho Chehab 		switch(product_id) {
9910c0d06caSMauro Carvalho Chehab 		case 0x8116:
9920c0d06caSMauro Carvalho Chehab 			/* This is essentially the same cam as the Sotec Afina Eye */
9930c0d06caSMauro Carvalho Chehab 			PWC_INFO("AME Co. Afina Eye USB webcam detected.\n");
9940c0d06caSMauro Carvalho Chehab 			name = "AME Co. Afina Eye";
9950c0d06caSMauro Carvalho Chehab 			type_id = 750;
9960c0d06caSMauro Carvalho Chehab 			break;
9970c0d06caSMauro Carvalho Chehab 		default:
9980c0d06caSMauro Carvalho Chehab 			return -ENODEV;
9990c0d06caSMauro Carvalho Chehab 		}
10000c0d06caSMauro Carvalho Chehab 
10010c0d06caSMauro Carvalho Chehab 	}
10020c0d06caSMauro Carvalho Chehab 	else if (vendor_id == 0x0d81) {
10030c0d06caSMauro Carvalho Chehab 		switch(product_id) {
10040c0d06caSMauro Carvalho Chehab 		case 0x1900:
10050c0d06caSMauro Carvalho Chehab 			PWC_INFO("Visionite VCS-UC300 USB webcam detected.\n");
10060c0d06caSMauro Carvalho Chehab 			name = "Visionite VCS-UC300";
10070c0d06caSMauro Carvalho Chehab 			type_id = 740; /* CCD sensor */
10080c0d06caSMauro Carvalho Chehab 			break;
10090c0d06caSMauro Carvalho Chehab 		case 0x1910:
10100c0d06caSMauro Carvalho Chehab 			PWC_INFO("Visionite VCS-UM100 USB webcam detected.\n");
10110c0d06caSMauro Carvalho Chehab 			name = "Visionite VCS-UM100";
10120c0d06caSMauro Carvalho Chehab 			type_id = 730; /* CMOS sensor */
10130c0d06caSMauro Carvalho Chehab 			break;
10140c0d06caSMauro Carvalho Chehab 		default:
10150c0d06caSMauro Carvalho Chehab 			return -ENODEV;
10160c0d06caSMauro Carvalho Chehab 		}
10170c0d06caSMauro Carvalho Chehab 	}
10180c0d06caSMauro Carvalho Chehab 	else
10190c0d06caSMauro Carvalho Chehab 		return -ENODEV; /* Not any of the know types; but the list keeps growing. */
10200c0d06caSMauro Carvalho Chehab 
10210c0d06caSMauro Carvalho Chehab 	if (my_power_save == -1)
10220c0d06caSMauro Carvalho Chehab 		my_power_save = 0;
10230c0d06caSMauro Carvalho Chehab 
10240c0d06caSMauro Carvalho Chehab 	memset(serial_number, 0, 30);
10250c0d06caSMauro Carvalho Chehab 	usb_string(udev, udev->descriptor.iSerialNumber, serial_number, 29);
10260c0d06caSMauro Carvalho Chehab 	PWC_DEBUG_PROBE("Device serial number is %s\n", serial_number);
10270c0d06caSMauro Carvalho Chehab 
10280c0d06caSMauro Carvalho Chehab 	if (udev->descriptor.bNumConfigurations > 1)
10290c0d06caSMauro Carvalho Chehab 		PWC_WARNING("Warning: more than 1 configuration available.\n");
10300c0d06caSMauro Carvalho Chehab 
10310c0d06caSMauro Carvalho Chehab 	/* Allocate structure, initialize pointers, mutexes, etc. and link it to the usb_device */
10320c0d06caSMauro Carvalho Chehab 	pdev = kzalloc(sizeof(struct pwc_device), GFP_KERNEL);
10330c0d06caSMauro Carvalho Chehab 	if (pdev == NULL) {
10340c0d06caSMauro Carvalho Chehab 		PWC_ERROR("Oops, could not allocate memory for pwc_device.\n");
10350c0d06caSMauro Carvalho Chehab 		return -ENOMEM;
10360c0d06caSMauro Carvalho Chehab 	}
10370c0d06caSMauro Carvalho Chehab 	pdev->type = type_id;
10380c0d06caSMauro Carvalho Chehab 	pdev->features = features;
10390c0d06caSMauro Carvalho Chehab 	pwc_construct(pdev); /* set min/max sizes correct */
10400c0d06caSMauro Carvalho Chehab 
10410c0d06caSMauro Carvalho Chehab 	mutex_init(&pdev->v4l2_lock);
10420c0d06caSMauro Carvalho Chehab 	mutex_init(&pdev->vb_queue_lock);
10430c0d06caSMauro Carvalho Chehab 	spin_lock_init(&pdev->queued_bufs_lock);
10440c0d06caSMauro Carvalho Chehab 	INIT_LIST_HEAD(&pdev->queued_bufs);
10450c0d06caSMauro Carvalho Chehab 
10460c0d06caSMauro Carvalho Chehab 	pdev->udev = udev;
10470c0d06caSMauro Carvalho Chehab 	pdev->power_save = my_power_save;
10480c0d06caSMauro Carvalho Chehab 
10490c0d06caSMauro Carvalho Chehab 	/* Init videobuf2 queue structure */
10500c0d06caSMauro Carvalho Chehab 	pdev->vb_queue.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
10510c0d06caSMauro Carvalho Chehab 	pdev->vb_queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ;
10520c0d06caSMauro Carvalho Chehab 	pdev->vb_queue.drv_priv = pdev;
10530c0d06caSMauro Carvalho Chehab 	pdev->vb_queue.buf_struct_size = sizeof(struct pwc_frame_buf);
10540c0d06caSMauro Carvalho Chehab 	pdev->vb_queue.ops = &pwc_vb_queue_ops;
10550c0d06caSMauro Carvalho Chehab 	pdev->vb_queue.mem_ops = &vb2_vmalloc_memops;
1056ade48681SSakari Ailus 	pdev->vb_queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
1057eda94710SMauro Carvalho Chehab 	rc = vb2_queue_init(&pdev->vb_queue);
1058eda94710SMauro Carvalho Chehab 	if (rc < 0) {
1059eda94710SMauro Carvalho Chehab 		PWC_ERROR("Oops, could not initialize vb2 queue.\n");
1060eda94710SMauro Carvalho Chehab 		goto err_free_mem;
1061eda94710SMauro Carvalho Chehab 	}
10620c0d06caSMauro Carvalho Chehab 
10630c0d06caSMauro Carvalho Chehab 	/* Init video_device structure */
10645c2edefeSEzequiel Garcia 	pdev->vdev = pwc_template;
1065cc1e6315SMauro Carvalho Chehab 	strscpy(pdev->vdev.name, name, sizeof(pdev->vdev.name));
10660c0d06caSMauro Carvalho Chehab 	pdev->vdev.queue = &pdev->vb_queue;
10670c0d06caSMauro Carvalho Chehab 	pdev->vdev.queue->lock = &pdev->vb_queue_lock;
10680c0d06caSMauro Carvalho Chehab 	video_set_drvdata(&pdev->vdev, pdev);
10690c0d06caSMauro Carvalho Chehab 
10700c0d06caSMauro Carvalho Chehab 	pdev->release = le16_to_cpu(udev->descriptor.bcdDevice);
10710c0d06caSMauro Carvalho Chehab 	PWC_DEBUG_PROBE("Release: %04x\n", pdev->release);
10720c0d06caSMauro Carvalho Chehab 
10730c0d06caSMauro Carvalho Chehab 	/* Allocate USB command buffers */
10740c0d06caSMauro Carvalho Chehab 	pdev->ctrl_buf = kmalloc(sizeof(pdev->cmd_buf), GFP_KERNEL);
10750c0d06caSMauro Carvalho Chehab 	if (!pdev->ctrl_buf) {
10760c0d06caSMauro Carvalho Chehab 		PWC_ERROR("Oops, could not allocate memory for pwc_device.\n");
10770c0d06caSMauro Carvalho Chehab 		rc = -ENOMEM;
10780c0d06caSMauro Carvalho Chehab 		goto err_free_mem;
10790c0d06caSMauro Carvalho Chehab 	}
10800c0d06caSMauro Carvalho Chehab 
10810c0d06caSMauro Carvalho Chehab #ifdef CONFIG_USB_PWC_DEBUG
10820c0d06caSMauro Carvalho Chehab 	/* Query sensor type */
10830c0d06caSMauro Carvalho Chehab 	if (pwc_get_cmos_sensor(pdev, &rc) >= 0) {
10840c0d06caSMauro Carvalho Chehab 		PWC_DEBUG_OPEN("This %s camera is equipped with a %s (%d).\n",
10850c0d06caSMauro Carvalho Chehab 				pdev->vdev.name,
10860c0d06caSMauro Carvalho Chehab 				pwc_sensor_type_to_string(rc), rc);
10870c0d06caSMauro Carvalho Chehab 	}
10880c0d06caSMauro Carvalho Chehab #endif
10890c0d06caSMauro Carvalho Chehab 
10900c0d06caSMauro Carvalho Chehab 	/* Set the leds off */
10910c0d06caSMauro Carvalho Chehab 	pwc_set_leds(pdev, 0, 0);
10920c0d06caSMauro Carvalho Chehab 
109339c1cb2bSJonathan McCrohan 	/* Setup initial videomode */
10940c0d06caSMauro Carvalho Chehab 	rc = pwc_set_video_mode(pdev, MAX_WIDTH, MAX_HEIGHT,
10950c0d06caSMauro Carvalho Chehab 				V4L2_PIX_FMT_YUV420, 30, &compression, 1);
10960c0d06caSMauro Carvalho Chehab 	if (rc)
10970c0d06caSMauro Carvalho Chehab 		goto err_free_mem;
10980c0d06caSMauro Carvalho Chehab 
10990c0d06caSMauro Carvalho Chehab 	/* Register controls (and read default values from camera */
11000c0d06caSMauro Carvalho Chehab 	rc = pwc_init_controls(pdev);
11010c0d06caSMauro Carvalho Chehab 	if (rc) {
11020c0d06caSMauro Carvalho Chehab 		PWC_ERROR("Failed to register v4l2 controls (%d).\n", rc);
11030c0d06caSMauro Carvalho Chehab 		goto err_free_mem;
11040c0d06caSMauro Carvalho Chehab 	}
11050c0d06caSMauro Carvalho Chehab 
11060c0d06caSMauro Carvalho Chehab 	/* And powerdown the camera until streaming starts */
11070c0d06caSMauro Carvalho Chehab 	pwc_camera_power(pdev, 0);
11080c0d06caSMauro Carvalho Chehab 
11090c0d06caSMauro Carvalho Chehab 	/* Register the v4l2_device structure */
11100c0d06caSMauro Carvalho Chehab 	pdev->v4l2_dev.release = pwc_video_release;
11110c0d06caSMauro Carvalho Chehab 	rc = v4l2_device_register(&intf->dev, &pdev->v4l2_dev);
11120c0d06caSMauro Carvalho Chehab 	if (rc) {
11130c0d06caSMauro Carvalho Chehab 		PWC_ERROR("Failed to register v4l2-device (%d).\n", rc);
11140c0d06caSMauro Carvalho Chehab 		goto err_free_controls;
11150c0d06caSMauro Carvalho Chehab 	}
11160c0d06caSMauro Carvalho Chehab 
11170c0d06caSMauro Carvalho Chehab 	pdev->v4l2_dev.ctrl_handler = &pdev->ctrl_handler;
11180c0d06caSMauro Carvalho Chehab 	pdev->vdev.v4l2_dev = &pdev->v4l2_dev;
11190c0d06caSMauro Carvalho Chehab 	pdev->vdev.lock = &pdev->v4l2_lock;
11208c3854d0SHans Verkuil 	pdev->vdev.device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
11218c3854d0SHans Verkuil 				 V4L2_CAP_READWRITE;
11220c0d06caSMauro Carvalho Chehab 
11237fbbbc78SHans Verkuil 	rc = video_register_device(&pdev->vdev, VFL_TYPE_VIDEO, -1);
11240c0d06caSMauro Carvalho Chehab 	if (rc < 0) {
11250c0d06caSMauro Carvalho Chehab 		PWC_ERROR("Failed to register as video device (%d).\n", rc);
11260c0d06caSMauro Carvalho Chehab 		goto err_unregister_v4l2_dev;
11270c0d06caSMauro Carvalho Chehab 	}
11280c0d06caSMauro Carvalho Chehab 	PWC_INFO("Registered as %s.\n", video_device_node_name(&pdev->vdev));
11290c0d06caSMauro Carvalho Chehab 
11300c0d06caSMauro Carvalho Chehab #ifdef CONFIG_USB_PWC_INPUT_EVDEV
11310c0d06caSMauro Carvalho Chehab 	/* register webcam snapshot button input device */
11320c0d06caSMauro Carvalho Chehab 	pdev->button_dev = input_allocate_device();
11330c0d06caSMauro Carvalho Chehab 	if (!pdev->button_dev) {
11340c0d06caSMauro Carvalho Chehab 		rc = -ENOMEM;
11350c0d06caSMauro Carvalho Chehab 		goto err_video_unreg;
11360c0d06caSMauro Carvalho Chehab 	}
11370c0d06caSMauro Carvalho Chehab 
11380c0d06caSMauro Carvalho Chehab 	usb_make_path(udev, pdev->button_phys, sizeof(pdev->button_phys));
11390c0d06caSMauro Carvalho Chehab 	strlcat(pdev->button_phys, "/input0", sizeof(pdev->button_phys));
11400c0d06caSMauro Carvalho Chehab 
11410c0d06caSMauro Carvalho Chehab 	pdev->button_dev->name = "PWC snapshot button";
11420c0d06caSMauro Carvalho Chehab 	pdev->button_dev->phys = pdev->button_phys;
11430c0d06caSMauro Carvalho Chehab 	usb_to_input_id(pdev->udev, &pdev->button_dev->id);
11440c0d06caSMauro Carvalho Chehab 	pdev->button_dev->dev.parent = &pdev->udev->dev;
11450c0d06caSMauro Carvalho Chehab 	pdev->button_dev->evbit[0] = BIT_MASK(EV_KEY);
11460c0d06caSMauro Carvalho Chehab 	pdev->button_dev->keybit[BIT_WORD(KEY_CAMERA)] = BIT_MASK(KEY_CAMERA);
11470c0d06caSMauro Carvalho Chehab 
11480c0d06caSMauro Carvalho Chehab 	rc = input_register_device(pdev->button_dev);
11490c0d06caSMauro Carvalho Chehab 	if (rc) {
11500c0d06caSMauro Carvalho Chehab 		input_free_device(pdev->button_dev);
11510c0d06caSMauro Carvalho Chehab 		pdev->button_dev = NULL;
11520c0d06caSMauro Carvalho Chehab 		goto err_video_unreg;
11530c0d06caSMauro Carvalho Chehab 	}
11540c0d06caSMauro Carvalho Chehab #endif
11550c0d06caSMauro Carvalho Chehab 
11560c0d06caSMauro Carvalho Chehab 	return 0;
11570c0d06caSMauro Carvalho Chehab 
11581f6bcd01SArnd Bergmann #ifdef CONFIG_USB_PWC_INPUT_EVDEV
11590c0d06caSMauro Carvalho Chehab err_video_unreg:
11600c0d06caSMauro Carvalho Chehab 	video_unregister_device(&pdev->vdev);
11611f6bcd01SArnd Bergmann #endif
11620c0d06caSMauro Carvalho Chehab err_unregister_v4l2_dev:
11630c0d06caSMauro Carvalho Chehab 	v4l2_device_unregister(&pdev->v4l2_dev);
11640c0d06caSMauro Carvalho Chehab err_free_controls:
11650c0d06caSMauro Carvalho Chehab 	v4l2_ctrl_handler_free(&pdev->ctrl_handler);
11660c0d06caSMauro Carvalho Chehab err_free_mem:
11670c0d06caSMauro Carvalho Chehab 	kfree(pdev->ctrl_buf);
11680c0d06caSMauro Carvalho Chehab 	kfree(pdev);
11690c0d06caSMauro Carvalho Chehab 	return rc;
11700c0d06caSMauro Carvalho Chehab }
11710c0d06caSMauro Carvalho Chehab 
11720c0d06caSMauro Carvalho Chehab /* The user yanked out the cable... */
usb_pwc_disconnect(struct usb_interface * intf)11730c0d06caSMauro Carvalho Chehab static void usb_pwc_disconnect(struct usb_interface *intf)
11740c0d06caSMauro Carvalho Chehab {
11750c0d06caSMauro Carvalho Chehab 	struct v4l2_device *v = usb_get_intfdata(intf);
11760c0d06caSMauro Carvalho Chehab 	struct pwc_device *pdev = container_of(v, struct pwc_device, v4l2_dev);
11770c0d06caSMauro Carvalho Chehab 
11780c0d06caSMauro Carvalho Chehab 	mutex_lock(&pdev->vb_queue_lock);
11790c0d06caSMauro Carvalho Chehab 	mutex_lock(&pdev->v4l2_lock);
11800c0d06caSMauro Carvalho Chehab 	/* No need to keep the urbs around after disconnection */
11810c0d06caSMauro Carvalho Chehab 	if (pdev->vb_queue.streaming)
11820c0d06caSMauro Carvalho Chehab 		pwc_isoc_cleanup(pdev);
11830c0d06caSMauro Carvalho Chehab 	pdev->udev = NULL;
11840c0d06caSMauro Carvalho Chehab 
11850c0d06caSMauro Carvalho Chehab 	v4l2_device_disconnect(&pdev->v4l2_dev);
11860c0d06caSMauro Carvalho Chehab 	video_unregister_device(&pdev->vdev);
11870c0d06caSMauro Carvalho Chehab 	mutex_unlock(&pdev->v4l2_lock);
11880ab61196SEzequiel Garcia 	mutex_unlock(&pdev->vb_queue_lock);
11890c0d06caSMauro Carvalho Chehab 
11900c0d06caSMauro Carvalho Chehab #ifdef CONFIG_USB_PWC_INPUT_EVDEV
11910c0d06caSMauro Carvalho Chehab 	if (pdev->button_dev)
11920c0d06caSMauro Carvalho Chehab 		input_unregister_device(pdev->button_dev);
11930c0d06caSMauro Carvalho Chehab #endif
11940c0d06caSMauro Carvalho Chehab 
11950c0d06caSMauro Carvalho Chehab 	v4l2_device_put(&pdev->v4l2_dev);
11960c0d06caSMauro Carvalho Chehab }
11970c0d06caSMauro Carvalho Chehab 
11980c0d06caSMauro Carvalho Chehab 
11990c0d06caSMauro Carvalho Chehab /*
12000c0d06caSMauro Carvalho Chehab  * Initialization code & module stuff
12010c0d06caSMauro Carvalho Chehab  */
12020c0d06caSMauro Carvalho Chehab 
12030c0d06caSMauro Carvalho Chehab static unsigned int leds_nargs;
12040c0d06caSMauro Carvalho Chehab 
12050c0d06caSMauro Carvalho Chehab #ifdef CONFIG_USB_PWC_DEBUG
12060c0d06caSMauro Carvalho Chehab module_param_named(trace, pwc_trace, int, 0644);
12070c0d06caSMauro Carvalho Chehab #endif
12080c0d06caSMauro Carvalho Chehab module_param(power_save, int, 0644);
12090c0d06caSMauro Carvalho Chehab module_param_array(leds, int, &leds_nargs, 0444);
12100c0d06caSMauro Carvalho Chehab 
12110c0d06caSMauro Carvalho Chehab #ifdef CONFIG_USB_PWC_DEBUG
12120c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(trace, "For debugging purposes");
12130c0d06caSMauro Carvalho Chehab #endif
12140c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(power_save, "Turn power saving for new cameras on or off");
12150c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(leds, "LED on,off time in milliseconds");
12160c0d06caSMauro Carvalho Chehab 
12170c0d06caSMauro Carvalho Chehab MODULE_DESCRIPTION("Philips & OEM USB webcam driver");
12180c0d06caSMauro Carvalho Chehab MODULE_AUTHOR("Luc Saillard <luc@saillard.org>");
12190c0d06caSMauro Carvalho Chehab MODULE_LICENSE("GPL");
12200c0d06caSMauro Carvalho Chehab MODULE_ALIAS("pwcx");
12210c0d06caSMauro Carvalho Chehab MODULE_VERSION( PWC_VERSION );
12220c0d06caSMauro Carvalho Chehab 
12230c0d06caSMauro Carvalho Chehab module_usb_driver(pwc_driver);
1224