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