18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/* Linux driver for Philips webcam
38c2ecf20Sopenharmony_ci   USB and Video4Linux interface part.
48c2ecf20Sopenharmony_ci   (C) 1999-2004 Nemosoft Unv.
58c2ecf20Sopenharmony_ci   (C) 2004-2006 Luc Saillard (luc@saillard.org)
68c2ecf20Sopenharmony_ci   (C) 2011 Hans de Goede <hdegoede@redhat.com>
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci   NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx
98c2ecf20Sopenharmony_ci   driver and thus may have bugs that are not present in the original version.
108c2ecf20Sopenharmony_ci   Please send bug reports and support requests to <luc@saillard.org>.
118c2ecf20Sopenharmony_ci   The decompression routines have been implemented by reverse-engineering the
128c2ecf20Sopenharmony_ci   Nemosoft binary pwcx module. Caveat emptor.
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci*/
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci/*
188c2ecf20Sopenharmony_ci   This code forms the interface between the USB layers and the Philips
198c2ecf20Sopenharmony_ci   specific stuff. Some adanved stuff of the driver falls under an
208c2ecf20Sopenharmony_ci   NDA, signed between me and Philips B.V., Eindhoven, the Netherlands, and
218c2ecf20Sopenharmony_ci   is thus not distributed in source form. The binary pwcx.o module
228c2ecf20Sopenharmony_ci   contains the code that falls under the NDA.
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci   In case you're wondering: 'pwc' stands for "Philips WebCam", but
258c2ecf20Sopenharmony_ci   I really didn't want to type 'philips_web_cam' every time (I'm lazy as
268c2ecf20Sopenharmony_ci   any Linux kernel hacker, but I don't like uncomprehensible abbreviations
278c2ecf20Sopenharmony_ci   without explanation).
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci   Oh yes, convention: to disctinguish between all the various pointers to
308c2ecf20Sopenharmony_ci   device-structures, I use these names for the pointer variables:
318c2ecf20Sopenharmony_ci   udev: struct usb_device *
328c2ecf20Sopenharmony_ci   vdev: struct video_device (member of pwc_dev)
338c2ecf20Sopenharmony_ci   pdev: struct pwc_devive *
348c2ecf20Sopenharmony_ci*/
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci/* Contributors:
378c2ecf20Sopenharmony_ci   - Alvarado: adding whitebalance code
388c2ecf20Sopenharmony_ci   - Alistar Moire: QuickCam 3000 Pro device/product ID
398c2ecf20Sopenharmony_ci   - Tony Hoyle: Creative Labs Webcam 5 device/product ID
408c2ecf20Sopenharmony_ci   - Mark Burazin: solving hang in VIDIOCSYNC when camera gets unplugged
418c2ecf20Sopenharmony_ci   - Jk Fang: Sotec Afina Eye ID
428c2ecf20Sopenharmony_ci   - Xavier Roche: QuickCam Pro 4000 ID
438c2ecf20Sopenharmony_ci   - Jens Knudsen: QuickCam Zoom ID
448c2ecf20Sopenharmony_ci   - J. Debert: QuickCam for Notebooks ID
458c2ecf20Sopenharmony_ci   - Pham Thanh Nam: webcam snapshot button as an event input device
468c2ecf20Sopenharmony_ci*/
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci#include <linux/errno.h>
498c2ecf20Sopenharmony_ci#include <linux/init.h>
508c2ecf20Sopenharmony_ci#include <linux/mm.h>
518c2ecf20Sopenharmony_ci#include <linux/module.h>
528c2ecf20Sopenharmony_ci#include <linux/poll.h>
538c2ecf20Sopenharmony_ci#include <linux/slab.h>
548c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_PWC_INPUT_EVDEV
558c2ecf20Sopenharmony_ci#include <linux/usb/input.h>
568c2ecf20Sopenharmony_ci#endif
578c2ecf20Sopenharmony_ci#include <linux/vmalloc.h>
588c2ecf20Sopenharmony_ci#include <asm/io.h>
598c2ecf20Sopenharmony_ci#include <linux/kernel.h>		/* simple_strtol() */
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci#include "pwc.h"
628c2ecf20Sopenharmony_ci#include "pwc-kiara.h"
638c2ecf20Sopenharmony_ci#include "pwc-timon.h"
648c2ecf20Sopenharmony_ci#include "pwc-dec23.h"
658c2ecf20Sopenharmony_ci#include "pwc-dec1.h"
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci#define CREATE_TRACE_POINTS
688c2ecf20Sopenharmony_ci#include <trace/events/pwc.h>
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci/* Function prototypes and driver templates */
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci/* hotplug device table support */
738c2ecf20Sopenharmony_cistatic const struct usb_device_id pwc_device_table [] = {
748c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x041E, 0x400C) }, /* Creative Webcam 5 */
758c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x041E, 0x4011) }, /* Creative Webcam Pro Ex */
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x046D, 0x08B0) }, /* Logitech QuickCam 3000 Pro */
788c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x046D, 0x08B1) }, /* Logitech QuickCam Notebook Pro */
798c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x046D, 0x08B2) }, /* Logitech QuickCam 4000 Pro */
808c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x046D, 0x08B3) }, /* Logitech QuickCam Zoom (old model) */
818c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x046D, 0x08B4) }, /* Logitech QuickCam Zoom (new model) */
828c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x046D, 0x08B5) }, /* Logitech QuickCam Orbit/Sphere */
838c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x046D, 0x08B6) }, /* Logitech/Cisco VT Camera */
848c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x046D, 0x08B7) }, /* Logitech ViewPort AV 100 */
858c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x046D, 0x08B8) }, /* Logitech QuickCam */
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x0471, 0x0302) }, /* Philips PCA645VC */
888c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x0471, 0x0303) }, /* Philips PCA646VC */
898c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x0471, 0x0304) }, /* Askey VC010 type 2 */
908c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x0471, 0x0307) }, /* Philips PCVC675K (Vesta) */
918c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x0471, 0x0308) }, /* Philips PCVC680K (Vesta Pro) */
928c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x0471, 0x030C) }, /* Philips PCVC690K (Vesta Pro Scan) */
938c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x0471, 0x0310) }, /* Philips PCVC730K (ToUCam Fun)/PCVC830 (ToUCam II) */
948c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x0471, 0x0311) }, /* Philips PCVC740K (ToUCam Pro)/PCVC840 (ToUCam II) */
958c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x0471, 0x0312) }, /* Philips PCVC750K (ToUCam Pro Scan) */
968c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x0471, 0x0313) }, /* Philips PCVC720K/40 (ToUCam XS) */
978c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x0471, 0x0329) }, /* Philips SPC 900NC webcam */
988c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x0471, 0x032C) }, /* Philips SPC 880NC webcam */
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x04CC, 0x8116) }, /* Sotec Afina Eye */
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x055D, 0x9000) }, /* Samsung MPC-C10 */
1038c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x055D, 0x9001) }, /* Samsung MPC-C30 */
1048c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x055D, 0x9002) },	/* Samsung SNC-35E (Ver3.0) */
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x069A, 0x0001) }, /* Askey VC010 type 1 */
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x06BE, 0x8116) }, /* AME Co. Afina Eye */
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x0d81, 0x1900) }, /* Visionite VCS-UC300 */
1118c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x0d81, 0x1910) }, /* Visionite VCS-UM100 */
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	{ }
1148c2ecf20Sopenharmony_ci};
1158c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, pwc_device_table);
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_cistatic int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id *id);
1188c2ecf20Sopenharmony_cistatic void usb_pwc_disconnect(struct usb_interface *intf);
1198c2ecf20Sopenharmony_cistatic void pwc_isoc_cleanup(struct pwc_device *pdev);
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_cistatic struct usb_driver pwc_driver = {
1228c2ecf20Sopenharmony_ci	.name =			"Philips webcam",	/* name */
1238c2ecf20Sopenharmony_ci	.id_table =		pwc_device_table,
1248c2ecf20Sopenharmony_ci	.probe =		usb_pwc_probe,		/* probe() */
1258c2ecf20Sopenharmony_ci	.disconnect =		usb_pwc_disconnect,	/* disconnect() */
1268c2ecf20Sopenharmony_ci};
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci#define MAX_DEV_HINTS	20
1298c2ecf20Sopenharmony_ci#define MAX_ISOC_ERRORS	20
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_PWC_DEBUG
1328c2ecf20Sopenharmony_ci	int pwc_trace = PWC_DEBUG_LEVEL;
1338c2ecf20Sopenharmony_ci#endif
1348c2ecf20Sopenharmony_cistatic int power_save = -1;
1358c2ecf20Sopenharmony_cistatic int leds[2] = { 100, 0 };
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci/***/
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_cistatic const struct v4l2_file_operations pwc_fops = {
1408c2ecf20Sopenharmony_ci	.owner =	THIS_MODULE,
1418c2ecf20Sopenharmony_ci	.open =		v4l2_fh_open,
1428c2ecf20Sopenharmony_ci	.release =	vb2_fop_release,
1438c2ecf20Sopenharmony_ci	.read =		vb2_fop_read,
1448c2ecf20Sopenharmony_ci	.poll =		vb2_fop_poll,
1458c2ecf20Sopenharmony_ci	.mmap =		vb2_fop_mmap,
1468c2ecf20Sopenharmony_ci	.unlocked_ioctl = video_ioctl2,
1478c2ecf20Sopenharmony_ci};
1488c2ecf20Sopenharmony_cistatic const struct video_device pwc_template = {
1498c2ecf20Sopenharmony_ci	.name =		"Philips Webcam",	/* Filled in later */
1508c2ecf20Sopenharmony_ci	.release =	video_device_release_empty,
1518c2ecf20Sopenharmony_ci	.fops =         &pwc_fops,
1528c2ecf20Sopenharmony_ci	.ioctl_ops =	&pwc_ioctl_ops,
1538c2ecf20Sopenharmony_ci};
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci/***************************************************************************/
1568c2ecf20Sopenharmony_ci/* Private functions */
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_cistatic void *pwc_alloc_urb_buffer(struct usb_device *dev,
1598c2ecf20Sopenharmony_ci				  size_t size, dma_addr_t *dma_handle)
1608c2ecf20Sopenharmony_ci{
1618c2ecf20Sopenharmony_ci	struct device *dmadev = dev->bus->sysdev;
1628c2ecf20Sopenharmony_ci	void *buffer = kmalloc(size, GFP_KERNEL);
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	if (!buffer)
1658c2ecf20Sopenharmony_ci		return NULL;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	*dma_handle = dma_map_single(dmadev, buffer, size, DMA_FROM_DEVICE);
1688c2ecf20Sopenharmony_ci	if (dma_mapping_error(dmadev, *dma_handle)) {
1698c2ecf20Sopenharmony_ci		kfree(buffer);
1708c2ecf20Sopenharmony_ci		return NULL;
1718c2ecf20Sopenharmony_ci	}
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	return buffer;
1748c2ecf20Sopenharmony_ci}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_cistatic void pwc_free_urb_buffer(struct usb_device *dev,
1778c2ecf20Sopenharmony_ci				size_t size,
1788c2ecf20Sopenharmony_ci				void *buffer,
1798c2ecf20Sopenharmony_ci				dma_addr_t dma_handle)
1808c2ecf20Sopenharmony_ci{
1818c2ecf20Sopenharmony_ci	struct device *dmadev = dev->bus->sysdev;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	dma_unmap_single(dmadev, dma_handle, size, DMA_FROM_DEVICE);
1848c2ecf20Sopenharmony_ci	kfree(buffer);
1858c2ecf20Sopenharmony_ci}
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_cistatic struct pwc_frame_buf *pwc_get_next_fill_buf(struct pwc_device *pdev)
1888c2ecf20Sopenharmony_ci{
1898c2ecf20Sopenharmony_ci	unsigned long flags = 0;
1908c2ecf20Sopenharmony_ci	struct pwc_frame_buf *buf = NULL;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	spin_lock_irqsave(&pdev->queued_bufs_lock, flags);
1938c2ecf20Sopenharmony_ci	if (list_empty(&pdev->queued_bufs))
1948c2ecf20Sopenharmony_ci		goto leave;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	buf = list_entry(pdev->queued_bufs.next, struct pwc_frame_buf, list);
1978c2ecf20Sopenharmony_ci	list_del(&buf->list);
1988c2ecf20Sopenharmony_cileave:
1998c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&pdev->queued_bufs_lock, flags);
2008c2ecf20Sopenharmony_ci	return buf;
2018c2ecf20Sopenharmony_ci}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_cistatic void pwc_snapshot_button(struct pwc_device *pdev, int down)
2048c2ecf20Sopenharmony_ci{
2058c2ecf20Sopenharmony_ci	if (down) {
2068c2ecf20Sopenharmony_ci		PWC_TRACE("Snapshot button pressed.\n");
2078c2ecf20Sopenharmony_ci	} else {
2088c2ecf20Sopenharmony_ci		PWC_TRACE("Snapshot button released.\n");
2098c2ecf20Sopenharmony_ci	}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_PWC_INPUT_EVDEV
2128c2ecf20Sopenharmony_ci	if (pdev->button_dev) {
2138c2ecf20Sopenharmony_ci		input_report_key(pdev->button_dev, KEY_CAMERA, down);
2148c2ecf20Sopenharmony_ci		input_sync(pdev->button_dev);
2158c2ecf20Sopenharmony_ci	}
2168c2ecf20Sopenharmony_ci#endif
2178c2ecf20Sopenharmony_ci}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_cistatic void pwc_frame_complete(struct pwc_device *pdev)
2208c2ecf20Sopenharmony_ci{
2218c2ecf20Sopenharmony_ci	struct pwc_frame_buf *fbuf = pdev->fill_buf;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	/* The ToUCam Fun CMOS sensor causes the firmware to send 2 or 3 bogus
2248c2ecf20Sopenharmony_ci	   frames on the USB wire after an exposure change. This conditition is
2258c2ecf20Sopenharmony_ci	   however detected  in the cam and a bit is set in the header.
2268c2ecf20Sopenharmony_ci	   */
2278c2ecf20Sopenharmony_ci	if (pdev->type == 730) {
2288c2ecf20Sopenharmony_ci		unsigned char *ptr = (unsigned char *)fbuf->data;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci		if (ptr[1] == 1 && ptr[0] & 0x10) {
2318c2ecf20Sopenharmony_ci			PWC_TRACE("Hyundai CMOS sensor bug. Dropping frame.\n");
2328c2ecf20Sopenharmony_ci			pdev->drop_frames += 2;
2338c2ecf20Sopenharmony_ci		}
2348c2ecf20Sopenharmony_ci		if ((ptr[0] ^ pdev->vmirror) & 0x01) {
2358c2ecf20Sopenharmony_ci			pwc_snapshot_button(pdev, ptr[0] & 0x01);
2368c2ecf20Sopenharmony_ci		}
2378c2ecf20Sopenharmony_ci		if ((ptr[0] ^ pdev->vmirror) & 0x02) {
2388c2ecf20Sopenharmony_ci			if (ptr[0] & 0x02)
2398c2ecf20Sopenharmony_ci				PWC_TRACE("Image is mirrored.\n");
2408c2ecf20Sopenharmony_ci			else
2418c2ecf20Sopenharmony_ci				PWC_TRACE("Image is normal.\n");
2428c2ecf20Sopenharmony_ci		}
2438c2ecf20Sopenharmony_ci		pdev->vmirror = ptr[0] & 0x03;
2448c2ecf20Sopenharmony_ci		/* Sometimes the trailer of the 730 is still sent as a 4 byte packet
2458c2ecf20Sopenharmony_ci		   after a short frame; this condition is filtered out specifically. A 4 byte
2468c2ecf20Sopenharmony_ci		   frame doesn't make sense anyway.
2478c2ecf20Sopenharmony_ci		   So we get either this sequence:
2488c2ecf20Sopenharmony_ci		   drop_bit set -> 4 byte frame -> short frame -> good frame
2498c2ecf20Sopenharmony_ci		   Or this one:
2508c2ecf20Sopenharmony_ci		   drop_bit set -> short frame -> good frame
2518c2ecf20Sopenharmony_ci		   So we drop either 3 or 2 frames in all!
2528c2ecf20Sopenharmony_ci		   */
2538c2ecf20Sopenharmony_ci		if (fbuf->filled == 4)
2548c2ecf20Sopenharmony_ci			pdev->drop_frames++;
2558c2ecf20Sopenharmony_ci	} else if (pdev->type == 740 || pdev->type == 720) {
2568c2ecf20Sopenharmony_ci		unsigned char *ptr = (unsigned char *)fbuf->data;
2578c2ecf20Sopenharmony_ci		if ((ptr[0] ^ pdev->vmirror) & 0x01) {
2588c2ecf20Sopenharmony_ci			pwc_snapshot_button(pdev, ptr[0] & 0x01);
2598c2ecf20Sopenharmony_ci		}
2608c2ecf20Sopenharmony_ci		pdev->vmirror = ptr[0] & 0x03;
2618c2ecf20Sopenharmony_ci	}
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	/* In case we were instructed to drop the frame, do so silently. */
2648c2ecf20Sopenharmony_ci	if (pdev->drop_frames > 0) {
2658c2ecf20Sopenharmony_ci		pdev->drop_frames--;
2668c2ecf20Sopenharmony_ci	} else {
2678c2ecf20Sopenharmony_ci		/* Check for underflow first */
2688c2ecf20Sopenharmony_ci		if (fbuf->filled < pdev->frame_total_size) {
2698c2ecf20Sopenharmony_ci			PWC_DEBUG_FLOW("Frame buffer underflow (%d bytes); discarded.\n",
2708c2ecf20Sopenharmony_ci				       fbuf->filled);
2718c2ecf20Sopenharmony_ci		} else {
2728c2ecf20Sopenharmony_ci			fbuf->vb.field = V4L2_FIELD_NONE;
2738c2ecf20Sopenharmony_ci			fbuf->vb.sequence = pdev->vframe_count;
2748c2ecf20Sopenharmony_ci			vb2_buffer_done(&fbuf->vb.vb2_buf, VB2_BUF_STATE_DONE);
2758c2ecf20Sopenharmony_ci			pdev->fill_buf = NULL;
2768c2ecf20Sopenharmony_ci			pdev->vsync = 0;
2778c2ecf20Sopenharmony_ci		}
2788c2ecf20Sopenharmony_ci	} /* !drop_frames */
2798c2ecf20Sopenharmony_ci	pdev->vframe_count++;
2808c2ecf20Sopenharmony_ci}
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci/* This gets called for the Isochronous pipe (video). This is done in
2838c2ecf20Sopenharmony_ci * interrupt time, so it has to be fast, not crash, and not stall. Neat.
2848c2ecf20Sopenharmony_ci */
2858c2ecf20Sopenharmony_cistatic void pwc_isoc_handler(struct urb *urb)
2868c2ecf20Sopenharmony_ci{
2878c2ecf20Sopenharmony_ci	struct pwc_device *pdev = (struct pwc_device *)urb->context;
2888c2ecf20Sopenharmony_ci	struct device *dmadev = urb->dev->bus->sysdev;
2898c2ecf20Sopenharmony_ci	int i, fst, flen;
2908c2ecf20Sopenharmony_ci	unsigned char *iso_buf = NULL;
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	trace_pwc_handler_enter(urb, pdev);
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	if (urb->status == -ENOENT || urb->status == -ECONNRESET ||
2958c2ecf20Sopenharmony_ci	    urb->status == -ESHUTDOWN) {
2968c2ecf20Sopenharmony_ci		PWC_DEBUG_OPEN("URB (%p) unlinked %ssynchronously.\n",
2978c2ecf20Sopenharmony_ci			       urb, urb->status == -ENOENT ? "" : "a");
2988c2ecf20Sopenharmony_ci		return;
2998c2ecf20Sopenharmony_ci	}
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	if (pdev->fill_buf == NULL)
3028c2ecf20Sopenharmony_ci		pdev->fill_buf = pwc_get_next_fill_buf(pdev);
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	if (urb->status != 0) {
3058c2ecf20Sopenharmony_ci		const char *errmsg;
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci		errmsg = "Unknown";
3088c2ecf20Sopenharmony_ci		switch(urb->status) {
3098c2ecf20Sopenharmony_ci			case -ENOSR:		errmsg = "Buffer error (overrun)"; break;
3108c2ecf20Sopenharmony_ci			case -EPIPE:		errmsg = "Stalled (device not responding)"; break;
3118c2ecf20Sopenharmony_ci			case -EOVERFLOW:	errmsg = "Babble (bad cable?)"; break;
3128c2ecf20Sopenharmony_ci			case -EPROTO:		errmsg = "Bit-stuff error (bad cable?)"; break;
3138c2ecf20Sopenharmony_ci			case -EILSEQ:		errmsg = "CRC/Timeout (could be anything)"; break;
3148c2ecf20Sopenharmony_ci			case -ETIME:		errmsg = "Device does not respond"; break;
3158c2ecf20Sopenharmony_ci		}
3168c2ecf20Sopenharmony_ci		PWC_ERROR("pwc_isoc_handler() called with status %d [%s].\n",
3178c2ecf20Sopenharmony_ci			  urb->status, errmsg);
3188c2ecf20Sopenharmony_ci		/* Give up after a number of contiguous errors */
3198c2ecf20Sopenharmony_ci		if (++pdev->visoc_errors > MAX_ISOC_ERRORS)
3208c2ecf20Sopenharmony_ci		{
3218c2ecf20Sopenharmony_ci			PWC_ERROR("Too many ISOC errors, bailing out.\n");
3228c2ecf20Sopenharmony_ci			if (pdev->fill_buf) {
3238c2ecf20Sopenharmony_ci				vb2_buffer_done(&pdev->fill_buf->vb.vb2_buf,
3248c2ecf20Sopenharmony_ci						VB2_BUF_STATE_ERROR);
3258c2ecf20Sopenharmony_ci				pdev->fill_buf = NULL;
3268c2ecf20Sopenharmony_ci			}
3278c2ecf20Sopenharmony_ci		}
3288c2ecf20Sopenharmony_ci		pdev->vsync = 0; /* Drop the current frame */
3298c2ecf20Sopenharmony_ci		goto handler_end;
3308c2ecf20Sopenharmony_ci	}
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	/* Reset ISOC error counter. We did get here, after all. */
3338c2ecf20Sopenharmony_ci	pdev->visoc_errors = 0;
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	dma_sync_single_for_cpu(dmadev,
3368c2ecf20Sopenharmony_ci				urb->transfer_dma,
3378c2ecf20Sopenharmony_ci				urb->transfer_buffer_length,
3388c2ecf20Sopenharmony_ci				DMA_FROM_DEVICE);
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	/* vsync: 0 = don't copy data
3418c2ecf20Sopenharmony_ci		  1 = sync-hunt
3428c2ecf20Sopenharmony_ci		  2 = synched
3438c2ecf20Sopenharmony_ci	 */
3448c2ecf20Sopenharmony_ci	/* Compact data */
3458c2ecf20Sopenharmony_ci	for (i = 0; i < urb->number_of_packets; i++) {
3468c2ecf20Sopenharmony_ci		fst  = urb->iso_frame_desc[i].status;
3478c2ecf20Sopenharmony_ci		flen = urb->iso_frame_desc[i].actual_length;
3488c2ecf20Sopenharmony_ci		iso_buf = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
3498c2ecf20Sopenharmony_ci		if (fst != 0) {
3508c2ecf20Sopenharmony_ci			PWC_ERROR("Iso frame %d has error %d\n", i, fst);
3518c2ecf20Sopenharmony_ci			continue;
3528c2ecf20Sopenharmony_ci		}
3538c2ecf20Sopenharmony_ci		if (flen > 0 && pdev->vsync) {
3548c2ecf20Sopenharmony_ci			struct pwc_frame_buf *fbuf = pdev->fill_buf;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci			if (pdev->vsync == 1) {
3578c2ecf20Sopenharmony_ci				fbuf->vb.vb2_buf.timestamp = ktime_get_ns();
3588c2ecf20Sopenharmony_ci				pdev->vsync = 2;
3598c2ecf20Sopenharmony_ci			}
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci			if (flen + fbuf->filled > pdev->frame_total_size) {
3628c2ecf20Sopenharmony_ci				PWC_ERROR("Frame overflow (%d > %d)\n",
3638c2ecf20Sopenharmony_ci					  flen + fbuf->filled,
3648c2ecf20Sopenharmony_ci					  pdev->frame_total_size);
3658c2ecf20Sopenharmony_ci				pdev->vsync = 0; /* Let's wait for an EOF */
3668c2ecf20Sopenharmony_ci			} else {
3678c2ecf20Sopenharmony_ci				memcpy(fbuf->data + fbuf->filled, iso_buf,
3688c2ecf20Sopenharmony_ci				       flen);
3698c2ecf20Sopenharmony_ci				fbuf->filled += flen;
3708c2ecf20Sopenharmony_ci			}
3718c2ecf20Sopenharmony_ci		}
3728c2ecf20Sopenharmony_ci		if (flen < pdev->vlast_packet_size) {
3738c2ecf20Sopenharmony_ci			/* Shorter packet... end of frame */
3748c2ecf20Sopenharmony_ci			if (pdev->vsync == 2)
3758c2ecf20Sopenharmony_ci				pwc_frame_complete(pdev);
3768c2ecf20Sopenharmony_ci			if (pdev->fill_buf == NULL)
3778c2ecf20Sopenharmony_ci				pdev->fill_buf = pwc_get_next_fill_buf(pdev);
3788c2ecf20Sopenharmony_ci			if (pdev->fill_buf) {
3798c2ecf20Sopenharmony_ci				pdev->fill_buf->filled = 0;
3808c2ecf20Sopenharmony_ci				pdev->vsync = 1;
3818c2ecf20Sopenharmony_ci			}
3828c2ecf20Sopenharmony_ci		}
3838c2ecf20Sopenharmony_ci		pdev->vlast_packet_size = flen;
3848c2ecf20Sopenharmony_ci	}
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	dma_sync_single_for_device(dmadev,
3878c2ecf20Sopenharmony_ci				   urb->transfer_dma,
3888c2ecf20Sopenharmony_ci				   urb->transfer_buffer_length,
3898c2ecf20Sopenharmony_ci				   DMA_FROM_DEVICE);
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_cihandler_end:
3928c2ecf20Sopenharmony_ci	trace_pwc_handler_exit(urb, pdev);
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	i = usb_submit_urb(urb, GFP_ATOMIC);
3958c2ecf20Sopenharmony_ci	if (i != 0)
3968c2ecf20Sopenharmony_ci		PWC_ERROR("Error (%d) re-submitting urb in pwc_isoc_handler.\n", i);
3978c2ecf20Sopenharmony_ci}
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci/* Both v4l2_lock and vb_queue_lock should be locked when calling this */
4008c2ecf20Sopenharmony_cistatic int pwc_isoc_init(struct pwc_device *pdev)
4018c2ecf20Sopenharmony_ci{
4028c2ecf20Sopenharmony_ci	struct usb_device *udev;
4038c2ecf20Sopenharmony_ci	struct urb *urb;
4048c2ecf20Sopenharmony_ci	int i, j, ret;
4058c2ecf20Sopenharmony_ci	struct usb_interface *intf;
4068c2ecf20Sopenharmony_ci	struct usb_host_interface *idesc = NULL;
4078c2ecf20Sopenharmony_ci	int compression = 0; /* 0..3 = uncompressed..high */
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	pdev->vsync = 0;
4108c2ecf20Sopenharmony_ci	pdev->vlast_packet_size = 0;
4118c2ecf20Sopenharmony_ci	pdev->fill_buf = NULL;
4128c2ecf20Sopenharmony_ci	pdev->vframe_count = 0;
4138c2ecf20Sopenharmony_ci	pdev->visoc_errors = 0;
4148c2ecf20Sopenharmony_ci	udev = pdev->udev;
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ciretry:
4178c2ecf20Sopenharmony_ci	/* We first try with low compression and then retry with a higher
4188c2ecf20Sopenharmony_ci	   compression setting if there is not enough bandwidth. */
4198c2ecf20Sopenharmony_ci	ret = pwc_set_video_mode(pdev, pdev->width, pdev->height, pdev->pixfmt,
4208c2ecf20Sopenharmony_ci				 pdev->vframes, &compression, 1);
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	/* Get the current alternate interface, adjust packet size */
4238c2ecf20Sopenharmony_ci	intf = usb_ifnum_to_if(udev, 0);
4248c2ecf20Sopenharmony_ci	if (intf)
4258c2ecf20Sopenharmony_ci		idesc = usb_altnum_to_altsetting(intf, pdev->valternate);
4268c2ecf20Sopenharmony_ci	if (!idesc)
4278c2ecf20Sopenharmony_ci		return -EIO;
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	/* Search video endpoint */
4308c2ecf20Sopenharmony_ci	pdev->vmax_packet_size = -1;
4318c2ecf20Sopenharmony_ci	for (i = 0; i < idesc->desc.bNumEndpoints; i++) {
4328c2ecf20Sopenharmony_ci		if ((idesc->endpoint[i].desc.bEndpointAddress & 0xF) == pdev->vendpoint) {
4338c2ecf20Sopenharmony_ci			pdev->vmax_packet_size = le16_to_cpu(idesc->endpoint[i].desc.wMaxPacketSize);
4348c2ecf20Sopenharmony_ci			break;
4358c2ecf20Sopenharmony_ci		}
4368c2ecf20Sopenharmony_ci	}
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	if (pdev->vmax_packet_size < 0 || pdev->vmax_packet_size > ISO_MAX_FRAME_SIZE) {
4398c2ecf20Sopenharmony_ci		PWC_ERROR("Failed to find packet size for video endpoint in current alternate setting.\n");
4408c2ecf20Sopenharmony_ci		return -ENFILE; /* Odd error, that should be noticeable */
4418c2ecf20Sopenharmony_ci	}
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	/* Set alternate interface */
4448c2ecf20Sopenharmony_ci	PWC_DEBUG_OPEN("Setting alternate interface %d\n", pdev->valternate);
4458c2ecf20Sopenharmony_ci	ret = usb_set_interface(pdev->udev, 0, pdev->valternate);
4468c2ecf20Sopenharmony_ci	if (ret == -ENOSPC && compression < 3) {
4478c2ecf20Sopenharmony_ci		compression++;
4488c2ecf20Sopenharmony_ci		goto retry;
4498c2ecf20Sopenharmony_ci	}
4508c2ecf20Sopenharmony_ci	if (ret < 0)
4518c2ecf20Sopenharmony_ci		return ret;
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	/* Allocate and init Isochronuous urbs */
4548c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_ISO_BUFS; i++) {
4558c2ecf20Sopenharmony_ci		urb = usb_alloc_urb(ISO_FRAMES_PER_DESC, GFP_KERNEL);
4568c2ecf20Sopenharmony_ci		if (urb == NULL) {
4578c2ecf20Sopenharmony_ci			pwc_isoc_cleanup(pdev);
4588c2ecf20Sopenharmony_ci			return -ENOMEM;
4598c2ecf20Sopenharmony_ci		}
4608c2ecf20Sopenharmony_ci		pdev->urbs[i] = urb;
4618c2ecf20Sopenharmony_ci		PWC_DEBUG_MEMORY("Allocated URB at 0x%p\n", urb);
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci		urb->interval = 1; // devik
4648c2ecf20Sopenharmony_ci		urb->dev = udev;
4658c2ecf20Sopenharmony_ci		urb->pipe = usb_rcvisocpipe(udev, pdev->vendpoint);
4668c2ecf20Sopenharmony_ci		urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
4678c2ecf20Sopenharmony_ci		urb->transfer_buffer_length = ISO_BUFFER_SIZE;
4688c2ecf20Sopenharmony_ci		urb->transfer_buffer = pwc_alloc_urb_buffer(udev,
4698c2ecf20Sopenharmony_ci							    urb->transfer_buffer_length,
4708c2ecf20Sopenharmony_ci							    &urb->transfer_dma);
4718c2ecf20Sopenharmony_ci		if (urb->transfer_buffer == NULL) {
4728c2ecf20Sopenharmony_ci			PWC_ERROR("Failed to allocate urb buffer %d\n", i);
4738c2ecf20Sopenharmony_ci			pwc_isoc_cleanup(pdev);
4748c2ecf20Sopenharmony_ci			return -ENOMEM;
4758c2ecf20Sopenharmony_ci		}
4768c2ecf20Sopenharmony_ci		urb->complete = pwc_isoc_handler;
4778c2ecf20Sopenharmony_ci		urb->context = pdev;
4788c2ecf20Sopenharmony_ci		urb->start_frame = 0;
4798c2ecf20Sopenharmony_ci		urb->number_of_packets = ISO_FRAMES_PER_DESC;
4808c2ecf20Sopenharmony_ci		for (j = 0; j < ISO_FRAMES_PER_DESC; j++) {
4818c2ecf20Sopenharmony_ci			urb->iso_frame_desc[j].offset = j * ISO_MAX_FRAME_SIZE;
4828c2ecf20Sopenharmony_ci			urb->iso_frame_desc[j].length = pdev->vmax_packet_size;
4838c2ecf20Sopenharmony_ci		}
4848c2ecf20Sopenharmony_ci	}
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	/* link */
4878c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_ISO_BUFS; i++) {
4888c2ecf20Sopenharmony_ci		ret = usb_submit_urb(pdev->urbs[i], GFP_KERNEL);
4898c2ecf20Sopenharmony_ci		if (ret == -ENOSPC && compression < 3) {
4908c2ecf20Sopenharmony_ci			compression++;
4918c2ecf20Sopenharmony_ci			pwc_isoc_cleanup(pdev);
4928c2ecf20Sopenharmony_ci			goto retry;
4938c2ecf20Sopenharmony_ci		}
4948c2ecf20Sopenharmony_ci		if (ret) {
4958c2ecf20Sopenharmony_ci			PWC_ERROR("isoc_init() submit_urb %d failed with error %d\n", i, ret);
4968c2ecf20Sopenharmony_ci			pwc_isoc_cleanup(pdev);
4978c2ecf20Sopenharmony_ci			return ret;
4988c2ecf20Sopenharmony_ci		}
4998c2ecf20Sopenharmony_ci		PWC_DEBUG_MEMORY("URB 0x%p submitted.\n", pdev->urbs[i]);
5008c2ecf20Sopenharmony_ci	}
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	/* All is done... */
5038c2ecf20Sopenharmony_ci	PWC_DEBUG_OPEN("<< pwc_isoc_init()\n");
5048c2ecf20Sopenharmony_ci	return 0;
5058c2ecf20Sopenharmony_ci}
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_cistatic void pwc_iso_stop(struct pwc_device *pdev)
5088c2ecf20Sopenharmony_ci{
5098c2ecf20Sopenharmony_ci	int i;
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	/* Unlinking ISOC buffers one by one */
5128c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_ISO_BUFS; i++) {
5138c2ecf20Sopenharmony_ci		if (pdev->urbs[i]) {
5148c2ecf20Sopenharmony_ci			PWC_DEBUG_MEMORY("Unlinking URB %p\n", pdev->urbs[i]);
5158c2ecf20Sopenharmony_ci			usb_kill_urb(pdev->urbs[i]);
5168c2ecf20Sopenharmony_ci		}
5178c2ecf20Sopenharmony_ci	}
5188c2ecf20Sopenharmony_ci}
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_cistatic void pwc_iso_free(struct pwc_device *pdev)
5218c2ecf20Sopenharmony_ci{
5228c2ecf20Sopenharmony_ci	int i;
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	/* Freeing ISOC buffers one by one */
5258c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_ISO_BUFS; i++) {
5268c2ecf20Sopenharmony_ci		struct urb *urb = pdev->urbs[i];
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci		if (urb) {
5298c2ecf20Sopenharmony_ci			PWC_DEBUG_MEMORY("Freeing URB\n");
5308c2ecf20Sopenharmony_ci			if (urb->transfer_buffer)
5318c2ecf20Sopenharmony_ci				pwc_free_urb_buffer(urb->dev,
5328c2ecf20Sopenharmony_ci						    urb->transfer_buffer_length,
5338c2ecf20Sopenharmony_ci						    urb->transfer_buffer,
5348c2ecf20Sopenharmony_ci						    urb->transfer_dma);
5358c2ecf20Sopenharmony_ci			usb_free_urb(urb);
5368c2ecf20Sopenharmony_ci			pdev->urbs[i] = NULL;
5378c2ecf20Sopenharmony_ci		}
5388c2ecf20Sopenharmony_ci	}
5398c2ecf20Sopenharmony_ci}
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci/* Both v4l2_lock and vb_queue_lock should be locked when calling this */
5428c2ecf20Sopenharmony_cistatic void pwc_isoc_cleanup(struct pwc_device *pdev)
5438c2ecf20Sopenharmony_ci{
5448c2ecf20Sopenharmony_ci	PWC_DEBUG_OPEN(">> pwc_isoc_cleanup()\n");
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci	pwc_iso_stop(pdev);
5478c2ecf20Sopenharmony_ci	pwc_iso_free(pdev);
5488c2ecf20Sopenharmony_ci	usb_set_interface(pdev->udev, 0, 0);
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	PWC_DEBUG_OPEN("<< pwc_isoc_cleanup()\n");
5518c2ecf20Sopenharmony_ci}
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci/* Must be called with vb_queue_lock hold */
5548c2ecf20Sopenharmony_cistatic void pwc_cleanup_queued_bufs(struct pwc_device *pdev,
5558c2ecf20Sopenharmony_ci				    enum vb2_buffer_state state)
5568c2ecf20Sopenharmony_ci{
5578c2ecf20Sopenharmony_ci	unsigned long flags = 0;
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	spin_lock_irqsave(&pdev->queued_bufs_lock, flags);
5608c2ecf20Sopenharmony_ci	while (!list_empty(&pdev->queued_bufs)) {
5618c2ecf20Sopenharmony_ci		struct pwc_frame_buf *buf;
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci		buf = list_entry(pdev->queued_bufs.next, struct pwc_frame_buf,
5648c2ecf20Sopenharmony_ci				 list);
5658c2ecf20Sopenharmony_ci		list_del(&buf->list);
5668c2ecf20Sopenharmony_ci		vb2_buffer_done(&buf->vb.vb2_buf, state);
5678c2ecf20Sopenharmony_ci	}
5688c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&pdev->queued_bufs_lock, flags);
5698c2ecf20Sopenharmony_ci}
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_PWC_DEBUG
5728c2ecf20Sopenharmony_cistatic const char *pwc_sensor_type_to_string(unsigned int sensor_type)
5738c2ecf20Sopenharmony_ci{
5748c2ecf20Sopenharmony_ci	switch(sensor_type) {
5758c2ecf20Sopenharmony_ci		case 0x00:
5768c2ecf20Sopenharmony_ci			return "Hyundai CMOS sensor";
5778c2ecf20Sopenharmony_ci		case 0x20:
5788c2ecf20Sopenharmony_ci			return "Sony CCD sensor + TDA8787";
5798c2ecf20Sopenharmony_ci		case 0x2E:
5808c2ecf20Sopenharmony_ci			return "Sony CCD sensor + Exas 98L59";
5818c2ecf20Sopenharmony_ci		case 0x2F:
5828c2ecf20Sopenharmony_ci			return "Sony CCD sensor + ADI 9804";
5838c2ecf20Sopenharmony_ci		case 0x30:
5848c2ecf20Sopenharmony_ci			return "Sharp CCD sensor + TDA8787";
5858c2ecf20Sopenharmony_ci		case 0x3E:
5868c2ecf20Sopenharmony_ci			return "Sharp CCD sensor + Exas 98L59";
5878c2ecf20Sopenharmony_ci		case 0x3F:
5888c2ecf20Sopenharmony_ci			return "Sharp CCD sensor + ADI 9804";
5898c2ecf20Sopenharmony_ci		case 0x40:
5908c2ecf20Sopenharmony_ci			return "UPA 1021 sensor";
5918c2ecf20Sopenharmony_ci		case 0x100:
5928c2ecf20Sopenharmony_ci			return "VGA sensor";
5938c2ecf20Sopenharmony_ci		case 0x101:
5948c2ecf20Sopenharmony_ci			return "PAL MR sensor";
5958c2ecf20Sopenharmony_ci		default:
5968c2ecf20Sopenharmony_ci			return "unknown type of sensor";
5978c2ecf20Sopenharmony_ci	}
5988c2ecf20Sopenharmony_ci}
5998c2ecf20Sopenharmony_ci#endif
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci/***************************************************************************/
6028c2ecf20Sopenharmony_ci/* Video4Linux functions */
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_cistatic void pwc_video_release(struct v4l2_device *v)
6058c2ecf20Sopenharmony_ci{
6068c2ecf20Sopenharmony_ci	struct pwc_device *pdev = container_of(v, struct pwc_device, v4l2_dev);
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci	v4l2_ctrl_handler_free(&pdev->ctrl_handler);
6098c2ecf20Sopenharmony_ci	v4l2_device_unregister(&pdev->v4l2_dev);
6108c2ecf20Sopenharmony_ci	kfree(pdev->ctrl_buf);
6118c2ecf20Sopenharmony_ci	kfree(pdev);
6128c2ecf20Sopenharmony_ci}
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci/***************************************************************************/
6158c2ecf20Sopenharmony_ci/* Videobuf2 operations */
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_cistatic int queue_setup(struct vb2_queue *vq,
6188c2ecf20Sopenharmony_ci				unsigned int *nbuffers, unsigned int *nplanes,
6198c2ecf20Sopenharmony_ci				unsigned int sizes[], struct device *alloc_devs[])
6208c2ecf20Sopenharmony_ci{
6218c2ecf20Sopenharmony_ci	struct pwc_device *pdev = vb2_get_drv_priv(vq);
6228c2ecf20Sopenharmony_ci	int size;
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	if (*nbuffers < MIN_FRAMES)
6258c2ecf20Sopenharmony_ci		*nbuffers = MIN_FRAMES;
6268c2ecf20Sopenharmony_ci	else if (*nbuffers > MAX_FRAMES)
6278c2ecf20Sopenharmony_ci		*nbuffers = MAX_FRAMES;
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci	*nplanes = 1;
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci	size = pwc_get_size(pdev, MAX_WIDTH, MAX_HEIGHT);
6328c2ecf20Sopenharmony_ci	sizes[0] = PAGE_ALIGN(pwc_image_sizes[size][0] *
6338c2ecf20Sopenharmony_ci			      pwc_image_sizes[size][1] * 3 / 2);
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci	return 0;
6368c2ecf20Sopenharmony_ci}
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_cistatic int buffer_init(struct vb2_buffer *vb)
6398c2ecf20Sopenharmony_ci{
6408c2ecf20Sopenharmony_ci	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
6418c2ecf20Sopenharmony_ci	struct pwc_frame_buf *buf =
6428c2ecf20Sopenharmony_ci		container_of(vbuf, struct pwc_frame_buf, vb);
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci	/* need vmalloc since frame buffer > 128K */
6458c2ecf20Sopenharmony_ci	buf->data = vzalloc(PWC_FRAME_SIZE);
6468c2ecf20Sopenharmony_ci	if (buf->data == NULL)
6478c2ecf20Sopenharmony_ci		return -ENOMEM;
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ci	return 0;
6508c2ecf20Sopenharmony_ci}
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_cistatic int buffer_prepare(struct vb2_buffer *vb)
6538c2ecf20Sopenharmony_ci{
6548c2ecf20Sopenharmony_ci	struct pwc_device *pdev = vb2_get_drv_priv(vb->vb2_queue);
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci	/* Don't allow queueing new buffers after device disconnection */
6578c2ecf20Sopenharmony_ci	if (!pdev->udev)
6588c2ecf20Sopenharmony_ci		return -ENODEV;
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci	return 0;
6618c2ecf20Sopenharmony_ci}
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_cistatic void buffer_finish(struct vb2_buffer *vb)
6648c2ecf20Sopenharmony_ci{
6658c2ecf20Sopenharmony_ci	struct pwc_device *pdev = vb2_get_drv_priv(vb->vb2_queue);
6668c2ecf20Sopenharmony_ci	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
6678c2ecf20Sopenharmony_ci	struct pwc_frame_buf *buf =
6688c2ecf20Sopenharmony_ci		container_of(vbuf, struct pwc_frame_buf, vb);
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_ci	if (vb->state == VB2_BUF_STATE_DONE) {
6718c2ecf20Sopenharmony_ci		/*
6728c2ecf20Sopenharmony_ci		 * Application has called dqbuf and is getting back a buffer
6738c2ecf20Sopenharmony_ci		 * we've filled, take the pwc data we've stored in buf->data
6748c2ecf20Sopenharmony_ci		 * and decompress it into a usable format, storing the result
6758c2ecf20Sopenharmony_ci		 * in the vb2_buffer.
6768c2ecf20Sopenharmony_ci		 */
6778c2ecf20Sopenharmony_ci		pwc_decompress(pdev, buf);
6788c2ecf20Sopenharmony_ci	}
6798c2ecf20Sopenharmony_ci}
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_cistatic void buffer_cleanup(struct vb2_buffer *vb)
6828c2ecf20Sopenharmony_ci{
6838c2ecf20Sopenharmony_ci	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
6848c2ecf20Sopenharmony_ci	struct pwc_frame_buf *buf =
6858c2ecf20Sopenharmony_ci		container_of(vbuf, struct pwc_frame_buf, vb);
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_ci	vfree(buf->data);
6888c2ecf20Sopenharmony_ci}
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_cistatic void buffer_queue(struct vb2_buffer *vb)
6918c2ecf20Sopenharmony_ci{
6928c2ecf20Sopenharmony_ci	struct pwc_device *pdev = vb2_get_drv_priv(vb->vb2_queue);
6938c2ecf20Sopenharmony_ci	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
6948c2ecf20Sopenharmony_ci	struct pwc_frame_buf *buf =
6958c2ecf20Sopenharmony_ci		container_of(vbuf, struct pwc_frame_buf, vb);
6968c2ecf20Sopenharmony_ci	unsigned long flags = 0;
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci	/* Check the device has not disconnected between prep and queuing */
6998c2ecf20Sopenharmony_ci	if (!pdev->udev) {
7008c2ecf20Sopenharmony_ci		vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
7018c2ecf20Sopenharmony_ci		return;
7028c2ecf20Sopenharmony_ci	}
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci	spin_lock_irqsave(&pdev->queued_bufs_lock, flags);
7058c2ecf20Sopenharmony_ci	list_add_tail(&buf->list, &pdev->queued_bufs);
7068c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&pdev->queued_bufs_lock, flags);
7078c2ecf20Sopenharmony_ci}
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_cistatic int start_streaming(struct vb2_queue *vq, unsigned int count)
7108c2ecf20Sopenharmony_ci{
7118c2ecf20Sopenharmony_ci	struct pwc_device *pdev = vb2_get_drv_priv(vq);
7128c2ecf20Sopenharmony_ci	int r;
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci	if (!pdev->udev)
7158c2ecf20Sopenharmony_ci		return -ENODEV;
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci	if (mutex_lock_interruptible(&pdev->v4l2_lock))
7188c2ecf20Sopenharmony_ci		return -ERESTARTSYS;
7198c2ecf20Sopenharmony_ci	/* Turn on camera and set LEDS on */
7208c2ecf20Sopenharmony_ci	pwc_camera_power(pdev, 1);
7218c2ecf20Sopenharmony_ci	pwc_set_leds(pdev, leds[0], leds[1]);
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci	r = pwc_isoc_init(pdev);
7248c2ecf20Sopenharmony_ci	if (r) {
7258c2ecf20Sopenharmony_ci		/* If we failed turn camera and LEDS back off */
7268c2ecf20Sopenharmony_ci		pwc_set_leds(pdev, 0, 0);
7278c2ecf20Sopenharmony_ci		pwc_camera_power(pdev, 0);
7288c2ecf20Sopenharmony_ci		/* And cleanup any queued bufs!! */
7298c2ecf20Sopenharmony_ci		pwc_cleanup_queued_bufs(pdev, VB2_BUF_STATE_QUEUED);
7308c2ecf20Sopenharmony_ci	}
7318c2ecf20Sopenharmony_ci	mutex_unlock(&pdev->v4l2_lock);
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci	return r;
7348c2ecf20Sopenharmony_ci}
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_cistatic void stop_streaming(struct vb2_queue *vq)
7378c2ecf20Sopenharmony_ci{
7388c2ecf20Sopenharmony_ci	struct pwc_device *pdev = vb2_get_drv_priv(vq);
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_ci	mutex_lock(&pdev->v4l2_lock);
7418c2ecf20Sopenharmony_ci	if (pdev->udev) {
7428c2ecf20Sopenharmony_ci		pwc_set_leds(pdev, 0, 0);
7438c2ecf20Sopenharmony_ci		pwc_camera_power(pdev, 0);
7448c2ecf20Sopenharmony_ci		pwc_isoc_cleanup(pdev);
7458c2ecf20Sopenharmony_ci	}
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_ci	pwc_cleanup_queued_bufs(pdev, VB2_BUF_STATE_ERROR);
7488c2ecf20Sopenharmony_ci	if (pdev->fill_buf)
7498c2ecf20Sopenharmony_ci		vb2_buffer_done(&pdev->fill_buf->vb.vb2_buf,
7508c2ecf20Sopenharmony_ci				VB2_BUF_STATE_ERROR);
7518c2ecf20Sopenharmony_ci	mutex_unlock(&pdev->v4l2_lock);
7528c2ecf20Sopenharmony_ci}
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_cistatic const struct vb2_ops pwc_vb_queue_ops = {
7558c2ecf20Sopenharmony_ci	.queue_setup		= queue_setup,
7568c2ecf20Sopenharmony_ci	.buf_init		= buffer_init,
7578c2ecf20Sopenharmony_ci	.buf_prepare		= buffer_prepare,
7588c2ecf20Sopenharmony_ci	.buf_finish		= buffer_finish,
7598c2ecf20Sopenharmony_ci	.buf_cleanup		= buffer_cleanup,
7608c2ecf20Sopenharmony_ci	.buf_queue		= buffer_queue,
7618c2ecf20Sopenharmony_ci	.start_streaming	= start_streaming,
7628c2ecf20Sopenharmony_ci	.stop_streaming		= stop_streaming,
7638c2ecf20Sopenharmony_ci	.wait_prepare		= vb2_ops_wait_prepare,
7648c2ecf20Sopenharmony_ci	.wait_finish		= vb2_ops_wait_finish,
7658c2ecf20Sopenharmony_ci};
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci/***************************************************************************/
7688c2ecf20Sopenharmony_ci/* USB functions */
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_ci/* This function gets called when a new device is plugged in or the usb core
7718c2ecf20Sopenharmony_ci * is loaded.
7728c2ecf20Sopenharmony_ci */
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_cistatic int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id *id)
7758c2ecf20Sopenharmony_ci{
7768c2ecf20Sopenharmony_ci	struct usb_device *udev = interface_to_usbdev(intf);
7778c2ecf20Sopenharmony_ci	struct pwc_device *pdev = NULL;
7788c2ecf20Sopenharmony_ci	int vendor_id, product_id, type_id;
7798c2ecf20Sopenharmony_ci	int rc;
7808c2ecf20Sopenharmony_ci	int features = 0;
7818c2ecf20Sopenharmony_ci	int compression = 0;
7828c2ecf20Sopenharmony_ci	int my_power_save = power_save;
7838c2ecf20Sopenharmony_ci	char serial_number[30], *name;
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci	vendor_id = le16_to_cpu(udev->descriptor.idVendor);
7868c2ecf20Sopenharmony_ci	product_id = le16_to_cpu(udev->descriptor.idProduct);
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_ci	/* Check if we can handle this device */
7898c2ecf20Sopenharmony_ci	PWC_DEBUG_PROBE("probe() called [%04X %04X], if %d\n",
7908c2ecf20Sopenharmony_ci		vendor_id, product_id,
7918c2ecf20Sopenharmony_ci		intf->altsetting->desc.bInterfaceNumber);
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_ci	/* the interfaces are probed one by one. We are only interested in the
7948c2ecf20Sopenharmony_ci	   video interface (0) now.
7958c2ecf20Sopenharmony_ci	   Interface 1 is the Audio Control, and interface 2 Audio itself.
7968c2ecf20Sopenharmony_ci	 */
7978c2ecf20Sopenharmony_ci	if (intf->altsetting->desc.bInterfaceNumber > 0)
7988c2ecf20Sopenharmony_ci		return -ENODEV;
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_ci	if (vendor_id == 0x0471) {
8018c2ecf20Sopenharmony_ci		switch (product_id) {
8028c2ecf20Sopenharmony_ci		case 0x0302:
8038c2ecf20Sopenharmony_ci			PWC_INFO("Philips PCA645VC USB webcam detected.\n");
8048c2ecf20Sopenharmony_ci			name = "Philips 645 webcam";
8058c2ecf20Sopenharmony_ci			type_id = 645;
8068c2ecf20Sopenharmony_ci			break;
8078c2ecf20Sopenharmony_ci		case 0x0303:
8088c2ecf20Sopenharmony_ci			PWC_INFO("Philips PCA646VC USB webcam detected.\n");
8098c2ecf20Sopenharmony_ci			name = "Philips 646 webcam";
8108c2ecf20Sopenharmony_ci			type_id = 646;
8118c2ecf20Sopenharmony_ci			break;
8128c2ecf20Sopenharmony_ci		case 0x0304:
8138c2ecf20Sopenharmony_ci			PWC_INFO("Askey VC010 type 2 USB webcam detected.\n");
8148c2ecf20Sopenharmony_ci			name = "Askey VC010 webcam";
8158c2ecf20Sopenharmony_ci			type_id = 646;
8168c2ecf20Sopenharmony_ci			break;
8178c2ecf20Sopenharmony_ci		case 0x0307:
8188c2ecf20Sopenharmony_ci			PWC_INFO("Philips PCVC675K (Vesta) USB webcam detected.\n");
8198c2ecf20Sopenharmony_ci			name = "Philips 675 webcam";
8208c2ecf20Sopenharmony_ci			type_id = 675;
8218c2ecf20Sopenharmony_ci			break;
8228c2ecf20Sopenharmony_ci		case 0x0308:
8238c2ecf20Sopenharmony_ci			PWC_INFO("Philips PCVC680K (Vesta Pro) USB webcam detected.\n");
8248c2ecf20Sopenharmony_ci			name = "Philips 680 webcam";
8258c2ecf20Sopenharmony_ci			type_id = 680;
8268c2ecf20Sopenharmony_ci			break;
8278c2ecf20Sopenharmony_ci		case 0x030C:
8288c2ecf20Sopenharmony_ci			PWC_INFO("Philips PCVC690K (Vesta Pro Scan) USB webcam detected.\n");
8298c2ecf20Sopenharmony_ci			name = "Philips 690 webcam";
8308c2ecf20Sopenharmony_ci			type_id = 690;
8318c2ecf20Sopenharmony_ci			break;
8328c2ecf20Sopenharmony_ci		case 0x0310:
8338c2ecf20Sopenharmony_ci			PWC_INFO("Philips PCVC730K (ToUCam Fun)/PCVC830 (ToUCam II) USB webcam detected.\n");
8348c2ecf20Sopenharmony_ci			name = "Philips 730 webcam";
8358c2ecf20Sopenharmony_ci			type_id = 730;
8368c2ecf20Sopenharmony_ci			break;
8378c2ecf20Sopenharmony_ci		case 0x0311:
8388c2ecf20Sopenharmony_ci			PWC_INFO("Philips PCVC740K (ToUCam Pro)/PCVC840 (ToUCam II) USB webcam detected.\n");
8398c2ecf20Sopenharmony_ci			name = "Philips 740 webcam";
8408c2ecf20Sopenharmony_ci			type_id = 740;
8418c2ecf20Sopenharmony_ci			break;
8428c2ecf20Sopenharmony_ci		case 0x0312:
8438c2ecf20Sopenharmony_ci			PWC_INFO("Philips PCVC750K (ToUCam Pro Scan) USB webcam detected.\n");
8448c2ecf20Sopenharmony_ci			name = "Philips 750 webcam";
8458c2ecf20Sopenharmony_ci			type_id = 750;
8468c2ecf20Sopenharmony_ci			break;
8478c2ecf20Sopenharmony_ci		case 0x0313:
8488c2ecf20Sopenharmony_ci			PWC_INFO("Philips PCVC720K/40 (ToUCam XS) USB webcam detected.\n");
8498c2ecf20Sopenharmony_ci			name = "Philips 720K/40 webcam";
8508c2ecf20Sopenharmony_ci			type_id = 720;
8518c2ecf20Sopenharmony_ci			break;
8528c2ecf20Sopenharmony_ci		case 0x0329:
8538c2ecf20Sopenharmony_ci			PWC_INFO("Philips SPC 900NC USB webcam detected.\n");
8548c2ecf20Sopenharmony_ci			name = "Philips SPC 900NC webcam";
8558c2ecf20Sopenharmony_ci			type_id = 740;
8568c2ecf20Sopenharmony_ci			break;
8578c2ecf20Sopenharmony_ci		case 0x032C:
8588c2ecf20Sopenharmony_ci			PWC_INFO("Philips SPC 880NC USB webcam detected.\n");
8598c2ecf20Sopenharmony_ci			name = "Philips SPC 880NC webcam";
8608c2ecf20Sopenharmony_ci			type_id = 740;
8618c2ecf20Sopenharmony_ci			break;
8628c2ecf20Sopenharmony_ci		default:
8638c2ecf20Sopenharmony_ci			return -ENODEV;
8648c2ecf20Sopenharmony_ci			break;
8658c2ecf20Sopenharmony_ci		}
8668c2ecf20Sopenharmony_ci	}
8678c2ecf20Sopenharmony_ci	else if (vendor_id == 0x069A) {
8688c2ecf20Sopenharmony_ci		switch(product_id) {
8698c2ecf20Sopenharmony_ci		case 0x0001:
8708c2ecf20Sopenharmony_ci			PWC_INFO("Askey VC010 type 1 USB webcam detected.\n");
8718c2ecf20Sopenharmony_ci			name = "Askey VC010 webcam";
8728c2ecf20Sopenharmony_ci			type_id = 645;
8738c2ecf20Sopenharmony_ci			break;
8748c2ecf20Sopenharmony_ci		default:
8758c2ecf20Sopenharmony_ci			return -ENODEV;
8768c2ecf20Sopenharmony_ci			break;
8778c2ecf20Sopenharmony_ci		}
8788c2ecf20Sopenharmony_ci	}
8798c2ecf20Sopenharmony_ci	else if (vendor_id == 0x046d) {
8808c2ecf20Sopenharmony_ci		switch(product_id) {
8818c2ecf20Sopenharmony_ci		case 0x08b0:
8828c2ecf20Sopenharmony_ci			PWC_INFO("Logitech QuickCam Pro 3000 USB webcam detected.\n");
8838c2ecf20Sopenharmony_ci			name = "Logitech QuickCam Pro 3000";
8848c2ecf20Sopenharmony_ci			type_id = 740; /* CCD sensor */
8858c2ecf20Sopenharmony_ci			break;
8868c2ecf20Sopenharmony_ci		case 0x08b1:
8878c2ecf20Sopenharmony_ci			PWC_INFO("Logitech QuickCam Notebook Pro USB webcam detected.\n");
8888c2ecf20Sopenharmony_ci			name = "Logitech QuickCam Notebook Pro";
8898c2ecf20Sopenharmony_ci			type_id = 740; /* CCD sensor */
8908c2ecf20Sopenharmony_ci			break;
8918c2ecf20Sopenharmony_ci		case 0x08b2:
8928c2ecf20Sopenharmony_ci			PWC_INFO("Logitech QuickCam 4000 Pro USB webcam detected.\n");
8938c2ecf20Sopenharmony_ci			name = "Logitech QuickCam Pro 4000";
8948c2ecf20Sopenharmony_ci			type_id = 740; /* CCD sensor */
8958c2ecf20Sopenharmony_ci			if (my_power_save == -1)
8968c2ecf20Sopenharmony_ci				my_power_save = 1;
8978c2ecf20Sopenharmony_ci			break;
8988c2ecf20Sopenharmony_ci		case 0x08b3:
8998c2ecf20Sopenharmony_ci			PWC_INFO("Logitech QuickCam Zoom USB webcam detected.\n");
9008c2ecf20Sopenharmony_ci			name = "Logitech QuickCam Zoom";
9018c2ecf20Sopenharmony_ci			type_id = 740; /* CCD sensor */
9028c2ecf20Sopenharmony_ci			break;
9038c2ecf20Sopenharmony_ci		case 0x08B4:
9048c2ecf20Sopenharmony_ci			PWC_INFO("Logitech QuickCam Zoom (new model) USB webcam detected.\n");
9058c2ecf20Sopenharmony_ci			name = "Logitech QuickCam Zoom";
9068c2ecf20Sopenharmony_ci			type_id = 740; /* CCD sensor */
9078c2ecf20Sopenharmony_ci			if (my_power_save == -1)
9088c2ecf20Sopenharmony_ci				my_power_save = 1;
9098c2ecf20Sopenharmony_ci			break;
9108c2ecf20Sopenharmony_ci		case 0x08b5:
9118c2ecf20Sopenharmony_ci			PWC_INFO("Logitech QuickCam Orbit/Sphere USB webcam detected.\n");
9128c2ecf20Sopenharmony_ci			name = "Logitech QuickCam Orbit";
9138c2ecf20Sopenharmony_ci			type_id = 740; /* CCD sensor */
9148c2ecf20Sopenharmony_ci			if (my_power_save == -1)
9158c2ecf20Sopenharmony_ci				my_power_save = 1;
9168c2ecf20Sopenharmony_ci			features |= FEATURE_MOTOR_PANTILT;
9178c2ecf20Sopenharmony_ci			break;
9188c2ecf20Sopenharmony_ci		case 0x08b6:
9198c2ecf20Sopenharmony_ci			PWC_INFO("Logitech/Cisco VT Camera webcam detected.\n");
9208c2ecf20Sopenharmony_ci			name = "Cisco VT Camera";
9218c2ecf20Sopenharmony_ci			type_id = 740; /* CCD sensor */
9228c2ecf20Sopenharmony_ci			break;
9238c2ecf20Sopenharmony_ci		case 0x08b7:
9248c2ecf20Sopenharmony_ci			PWC_INFO("Logitech ViewPort AV 100 webcam detected.\n");
9258c2ecf20Sopenharmony_ci			name = "Logitech ViewPort AV 100";
9268c2ecf20Sopenharmony_ci			type_id = 740; /* CCD sensor */
9278c2ecf20Sopenharmony_ci			break;
9288c2ecf20Sopenharmony_ci		case 0x08b8: /* Where this released? */
9298c2ecf20Sopenharmony_ci			PWC_INFO("Logitech QuickCam detected (reserved ID).\n");
9308c2ecf20Sopenharmony_ci			name = "Logitech QuickCam (res.)";
9318c2ecf20Sopenharmony_ci			type_id = 730; /* Assuming CMOS */
9328c2ecf20Sopenharmony_ci			break;
9338c2ecf20Sopenharmony_ci		default:
9348c2ecf20Sopenharmony_ci			return -ENODEV;
9358c2ecf20Sopenharmony_ci			break;
9368c2ecf20Sopenharmony_ci		}
9378c2ecf20Sopenharmony_ci	}
9388c2ecf20Sopenharmony_ci	else if (vendor_id == 0x055d) {
9398c2ecf20Sopenharmony_ci		/* I don't know the difference between the C10 and the C30;
9408c2ecf20Sopenharmony_ci		   I suppose the difference is the sensor, but both cameras
9418c2ecf20Sopenharmony_ci		   work equally well with a type_id of 675
9428c2ecf20Sopenharmony_ci		 */
9438c2ecf20Sopenharmony_ci		switch(product_id) {
9448c2ecf20Sopenharmony_ci		case 0x9000:
9458c2ecf20Sopenharmony_ci			PWC_INFO("Samsung MPC-C10 USB webcam detected.\n");
9468c2ecf20Sopenharmony_ci			name = "Samsung MPC-C10";
9478c2ecf20Sopenharmony_ci			type_id = 675;
9488c2ecf20Sopenharmony_ci			break;
9498c2ecf20Sopenharmony_ci		case 0x9001:
9508c2ecf20Sopenharmony_ci			PWC_INFO("Samsung MPC-C30 USB webcam detected.\n");
9518c2ecf20Sopenharmony_ci			name = "Samsung MPC-C30";
9528c2ecf20Sopenharmony_ci			type_id = 675;
9538c2ecf20Sopenharmony_ci			break;
9548c2ecf20Sopenharmony_ci		case 0x9002:
9558c2ecf20Sopenharmony_ci			PWC_INFO("Samsung SNC-35E (v3.0) USB webcam detected.\n");
9568c2ecf20Sopenharmony_ci			name = "Samsung MPC-C30";
9578c2ecf20Sopenharmony_ci			type_id = 740;
9588c2ecf20Sopenharmony_ci			break;
9598c2ecf20Sopenharmony_ci		default:
9608c2ecf20Sopenharmony_ci			return -ENODEV;
9618c2ecf20Sopenharmony_ci			break;
9628c2ecf20Sopenharmony_ci		}
9638c2ecf20Sopenharmony_ci	}
9648c2ecf20Sopenharmony_ci	else if (vendor_id == 0x041e) {
9658c2ecf20Sopenharmony_ci		switch(product_id) {
9668c2ecf20Sopenharmony_ci		case 0x400c:
9678c2ecf20Sopenharmony_ci			PWC_INFO("Creative Labs Webcam 5 detected.\n");
9688c2ecf20Sopenharmony_ci			name = "Creative Labs Webcam 5";
9698c2ecf20Sopenharmony_ci			type_id = 730;
9708c2ecf20Sopenharmony_ci			if (my_power_save == -1)
9718c2ecf20Sopenharmony_ci				my_power_save = 1;
9728c2ecf20Sopenharmony_ci			break;
9738c2ecf20Sopenharmony_ci		case 0x4011:
9748c2ecf20Sopenharmony_ci			PWC_INFO("Creative Labs Webcam Pro Ex detected.\n");
9758c2ecf20Sopenharmony_ci			name = "Creative Labs Webcam Pro Ex";
9768c2ecf20Sopenharmony_ci			type_id = 740;
9778c2ecf20Sopenharmony_ci			break;
9788c2ecf20Sopenharmony_ci		default:
9798c2ecf20Sopenharmony_ci			return -ENODEV;
9808c2ecf20Sopenharmony_ci			break;
9818c2ecf20Sopenharmony_ci		}
9828c2ecf20Sopenharmony_ci	}
9838c2ecf20Sopenharmony_ci	else if (vendor_id == 0x04cc) {
9848c2ecf20Sopenharmony_ci		switch(product_id) {
9858c2ecf20Sopenharmony_ci		case 0x8116:
9868c2ecf20Sopenharmony_ci			PWC_INFO("Sotec Afina Eye USB webcam detected.\n");
9878c2ecf20Sopenharmony_ci			name = "Sotec Afina Eye";
9888c2ecf20Sopenharmony_ci			type_id = 730;
9898c2ecf20Sopenharmony_ci			break;
9908c2ecf20Sopenharmony_ci		default:
9918c2ecf20Sopenharmony_ci			return -ENODEV;
9928c2ecf20Sopenharmony_ci			break;
9938c2ecf20Sopenharmony_ci		}
9948c2ecf20Sopenharmony_ci	}
9958c2ecf20Sopenharmony_ci	else if (vendor_id == 0x06be) {
9968c2ecf20Sopenharmony_ci		switch(product_id) {
9978c2ecf20Sopenharmony_ci		case 0x8116:
9988c2ecf20Sopenharmony_ci			/* This is essentially the same cam as the Sotec Afina Eye */
9998c2ecf20Sopenharmony_ci			PWC_INFO("AME Co. Afina Eye USB webcam detected.\n");
10008c2ecf20Sopenharmony_ci			name = "AME Co. Afina Eye";
10018c2ecf20Sopenharmony_ci			type_id = 750;
10028c2ecf20Sopenharmony_ci			break;
10038c2ecf20Sopenharmony_ci		default:
10048c2ecf20Sopenharmony_ci			return -ENODEV;
10058c2ecf20Sopenharmony_ci			break;
10068c2ecf20Sopenharmony_ci		}
10078c2ecf20Sopenharmony_ci
10088c2ecf20Sopenharmony_ci	}
10098c2ecf20Sopenharmony_ci	else if (vendor_id == 0x0d81) {
10108c2ecf20Sopenharmony_ci		switch(product_id) {
10118c2ecf20Sopenharmony_ci		case 0x1900:
10128c2ecf20Sopenharmony_ci			PWC_INFO("Visionite VCS-UC300 USB webcam detected.\n");
10138c2ecf20Sopenharmony_ci			name = "Visionite VCS-UC300";
10148c2ecf20Sopenharmony_ci			type_id = 740; /* CCD sensor */
10158c2ecf20Sopenharmony_ci			break;
10168c2ecf20Sopenharmony_ci		case 0x1910:
10178c2ecf20Sopenharmony_ci			PWC_INFO("Visionite VCS-UM100 USB webcam detected.\n");
10188c2ecf20Sopenharmony_ci			name = "Visionite VCS-UM100";
10198c2ecf20Sopenharmony_ci			type_id = 730; /* CMOS sensor */
10208c2ecf20Sopenharmony_ci			break;
10218c2ecf20Sopenharmony_ci		default:
10228c2ecf20Sopenharmony_ci			return -ENODEV;
10238c2ecf20Sopenharmony_ci			break;
10248c2ecf20Sopenharmony_ci		}
10258c2ecf20Sopenharmony_ci	}
10268c2ecf20Sopenharmony_ci	else
10278c2ecf20Sopenharmony_ci		return -ENODEV; /* Not any of the know types; but the list keeps growing. */
10288c2ecf20Sopenharmony_ci
10298c2ecf20Sopenharmony_ci	if (my_power_save == -1)
10308c2ecf20Sopenharmony_ci		my_power_save = 0;
10318c2ecf20Sopenharmony_ci
10328c2ecf20Sopenharmony_ci	memset(serial_number, 0, 30);
10338c2ecf20Sopenharmony_ci	usb_string(udev, udev->descriptor.iSerialNumber, serial_number, 29);
10348c2ecf20Sopenharmony_ci	PWC_DEBUG_PROBE("Device serial number is %s\n", serial_number);
10358c2ecf20Sopenharmony_ci
10368c2ecf20Sopenharmony_ci	if (udev->descriptor.bNumConfigurations > 1)
10378c2ecf20Sopenharmony_ci		PWC_WARNING("Warning: more than 1 configuration available.\n");
10388c2ecf20Sopenharmony_ci
10398c2ecf20Sopenharmony_ci	/* Allocate structure, initialize pointers, mutexes, etc. and link it to the usb_device */
10408c2ecf20Sopenharmony_ci	pdev = kzalloc(sizeof(struct pwc_device), GFP_KERNEL);
10418c2ecf20Sopenharmony_ci	if (pdev == NULL) {
10428c2ecf20Sopenharmony_ci		PWC_ERROR("Oops, could not allocate memory for pwc_device.\n");
10438c2ecf20Sopenharmony_ci		return -ENOMEM;
10448c2ecf20Sopenharmony_ci	}
10458c2ecf20Sopenharmony_ci	pdev->type = type_id;
10468c2ecf20Sopenharmony_ci	pdev->features = features;
10478c2ecf20Sopenharmony_ci	pwc_construct(pdev); /* set min/max sizes correct */
10488c2ecf20Sopenharmony_ci
10498c2ecf20Sopenharmony_ci	mutex_init(&pdev->v4l2_lock);
10508c2ecf20Sopenharmony_ci	mutex_init(&pdev->vb_queue_lock);
10518c2ecf20Sopenharmony_ci	spin_lock_init(&pdev->queued_bufs_lock);
10528c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&pdev->queued_bufs);
10538c2ecf20Sopenharmony_ci
10548c2ecf20Sopenharmony_ci	pdev->udev = udev;
10558c2ecf20Sopenharmony_ci	pdev->power_save = my_power_save;
10568c2ecf20Sopenharmony_ci
10578c2ecf20Sopenharmony_ci	/* Init videobuf2 queue structure */
10588c2ecf20Sopenharmony_ci	pdev->vb_queue.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
10598c2ecf20Sopenharmony_ci	pdev->vb_queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ;
10608c2ecf20Sopenharmony_ci	pdev->vb_queue.drv_priv = pdev;
10618c2ecf20Sopenharmony_ci	pdev->vb_queue.buf_struct_size = sizeof(struct pwc_frame_buf);
10628c2ecf20Sopenharmony_ci	pdev->vb_queue.ops = &pwc_vb_queue_ops;
10638c2ecf20Sopenharmony_ci	pdev->vb_queue.mem_ops = &vb2_vmalloc_memops;
10648c2ecf20Sopenharmony_ci	pdev->vb_queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
10658c2ecf20Sopenharmony_ci	rc = vb2_queue_init(&pdev->vb_queue);
10668c2ecf20Sopenharmony_ci	if (rc < 0) {
10678c2ecf20Sopenharmony_ci		PWC_ERROR("Oops, could not initialize vb2 queue.\n");
10688c2ecf20Sopenharmony_ci		goto err_free_mem;
10698c2ecf20Sopenharmony_ci	}
10708c2ecf20Sopenharmony_ci
10718c2ecf20Sopenharmony_ci	/* Init video_device structure */
10728c2ecf20Sopenharmony_ci	pdev->vdev = pwc_template;
10738c2ecf20Sopenharmony_ci	strscpy(pdev->vdev.name, name, sizeof(pdev->vdev.name));
10748c2ecf20Sopenharmony_ci	pdev->vdev.queue = &pdev->vb_queue;
10758c2ecf20Sopenharmony_ci	pdev->vdev.queue->lock = &pdev->vb_queue_lock;
10768c2ecf20Sopenharmony_ci	video_set_drvdata(&pdev->vdev, pdev);
10778c2ecf20Sopenharmony_ci
10788c2ecf20Sopenharmony_ci	pdev->release = le16_to_cpu(udev->descriptor.bcdDevice);
10798c2ecf20Sopenharmony_ci	PWC_DEBUG_PROBE("Release: %04x\n", pdev->release);
10808c2ecf20Sopenharmony_ci
10818c2ecf20Sopenharmony_ci	/* Allocate USB command buffers */
10828c2ecf20Sopenharmony_ci	pdev->ctrl_buf = kmalloc(sizeof(pdev->cmd_buf), GFP_KERNEL);
10838c2ecf20Sopenharmony_ci	if (!pdev->ctrl_buf) {
10848c2ecf20Sopenharmony_ci		PWC_ERROR("Oops, could not allocate memory for pwc_device.\n");
10858c2ecf20Sopenharmony_ci		rc = -ENOMEM;
10868c2ecf20Sopenharmony_ci		goto err_free_mem;
10878c2ecf20Sopenharmony_ci	}
10888c2ecf20Sopenharmony_ci
10898c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_PWC_DEBUG
10908c2ecf20Sopenharmony_ci	/* Query sensor type */
10918c2ecf20Sopenharmony_ci	if (pwc_get_cmos_sensor(pdev, &rc) >= 0) {
10928c2ecf20Sopenharmony_ci		PWC_DEBUG_OPEN("This %s camera is equipped with a %s (%d).\n",
10938c2ecf20Sopenharmony_ci				pdev->vdev.name,
10948c2ecf20Sopenharmony_ci				pwc_sensor_type_to_string(rc), rc);
10958c2ecf20Sopenharmony_ci	}
10968c2ecf20Sopenharmony_ci#endif
10978c2ecf20Sopenharmony_ci
10988c2ecf20Sopenharmony_ci	/* Set the leds off */
10998c2ecf20Sopenharmony_ci	pwc_set_leds(pdev, 0, 0);
11008c2ecf20Sopenharmony_ci
11018c2ecf20Sopenharmony_ci	/* Setup initial videomode */
11028c2ecf20Sopenharmony_ci	rc = pwc_set_video_mode(pdev, MAX_WIDTH, MAX_HEIGHT,
11038c2ecf20Sopenharmony_ci				V4L2_PIX_FMT_YUV420, 30, &compression, 1);
11048c2ecf20Sopenharmony_ci	if (rc)
11058c2ecf20Sopenharmony_ci		goto err_free_mem;
11068c2ecf20Sopenharmony_ci
11078c2ecf20Sopenharmony_ci	/* Register controls (and read default values from camera */
11088c2ecf20Sopenharmony_ci	rc = pwc_init_controls(pdev);
11098c2ecf20Sopenharmony_ci	if (rc) {
11108c2ecf20Sopenharmony_ci		PWC_ERROR("Failed to register v4l2 controls (%d).\n", rc);
11118c2ecf20Sopenharmony_ci		goto err_free_mem;
11128c2ecf20Sopenharmony_ci	}
11138c2ecf20Sopenharmony_ci
11148c2ecf20Sopenharmony_ci	/* And powerdown the camera until streaming starts */
11158c2ecf20Sopenharmony_ci	pwc_camera_power(pdev, 0);
11168c2ecf20Sopenharmony_ci
11178c2ecf20Sopenharmony_ci	/* Register the v4l2_device structure */
11188c2ecf20Sopenharmony_ci	pdev->v4l2_dev.release = pwc_video_release;
11198c2ecf20Sopenharmony_ci	rc = v4l2_device_register(&intf->dev, &pdev->v4l2_dev);
11208c2ecf20Sopenharmony_ci	if (rc) {
11218c2ecf20Sopenharmony_ci		PWC_ERROR("Failed to register v4l2-device (%d).\n", rc);
11228c2ecf20Sopenharmony_ci		goto err_free_controls;
11238c2ecf20Sopenharmony_ci	}
11248c2ecf20Sopenharmony_ci
11258c2ecf20Sopenharmony_ci	pdev->v4l2_dev.ctrl_handler = &pdev->ctrl_handler;
11268c2ecf20Sopenharmony_ci	pdev->vdev.v4l2_dev = &pdev->v4l2_dev;
11278c2ecf20Sopenharmony_ci	pdev->vdev.lock = &pdev->v4l2_lock;
11288c2ecf20Sopenharmony_ci	pdev->vdev.device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
11298c2ecf20Sopenharmony_ci				 V4L2_CAP_READWRITE;
11308c2ecf20Sopenharmony_ci
11318c2ecf20Sopenharmony_ci	rc = video_register_device(&pdev->vdev, VFL_TYPE_VIDEO, -1);
11328c2ecf20Sopenharmony_ci	if (rc < 0) {
11338c2ecf20Sopenharmony_ci		PWC_ERROR("Failed to register as video device (%d).\n", rc);
11348c2ecf20Sopenharmony_ci		goto err_unregister_v4l2_dev;
11358c2ecf20Sopenharmony_ci	}
11368c2ecf20Sopenharmony_ci	PWC_INFO("Registered as %s.\n", video_device_node_name(&pdev->vdev));
11378c2ecf20Sopenharmony_ci
11388c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_PWC_INPUT_EVDEV
11398c2ecf20Sopenharmony_ci	/* register webcam snapshot button input device */
11408c2ecf20Sopenharmony_ci	pdev->button_dev = input_allocate_device();
11418c2ecf20Sopenharmony_ci	if (!pdev->button_dev) {
11428c2ecf20Sopenharmony_ci		rc = -ENOMEM;
11438c2ecf20Sopenharmony_ci		goto err_video_unreg;
11448c2ecf20Sopenharmony_ci	}
11458c2ecf20Sopenharmony_ci
11468c2ecf20Sopenharmony_ci	usb_make_path(udev, pdev->button_phys, sizeof(pdev->button_phys));
11478c2ecf20Sopenharmony_ci	strlcat(pdev->button_phys, "/input0", sizeof(pdev->button_phys));
11488c2ecf20Sopenharmony_ci
11498c2ecf20Sopenharmony_ci	pdev->button_dev->name = "PWC snapshot button";
11508c2ecf20Sopenharmony_ci	pdev->button_dev->phys = pdev->button_phys;
11518c2ecf20Sopenharmony_ci	usb_to_input_id(pdev->udev, &pdev->button_dev->id);
11528c2ecf20Sopenharmony_ci	pdev->button_dev->dev.parent = &pdev->udev->dev;
11538c2ecf20Sopenharmony_ci	pdev->button_dev->evbit[0] = BIT_MASK(EV_KEY);
11548c2ecf20Sopenharmony_ci	pdev->button_dev->keybit[BIT_WORD(KEY_CAMERA)] = BIT_MASK(KEY_CAMERA);
11558c2ecf20Sopenharmony_ci
11568c2ecf20Sopenharmony_ci	rc = input_register_device(pdev->button_dev);
11578c2ecf20Sopenharmony_ci	if (rc) {
11588c2ecf20Sopenharmony_ci		input_free_device(pdev->button_dev);
11598c2ecf20Sopenharmony_ci		pdev->button_dev = NULL;
11608c2ecf20Sopenharmony_ci		goto err_video_unreg;
11618c2ecf20Sopenharmony_ci	}
11628c2ecf20Sopenharmony_ci#endif
11638c2ecf20Sopenharmony_ci
11648c2ecf20Sopenharmony_ci	return 0;
11658c2ecf20Sopenharmony_ci
11668c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_PWC_INPUT_EVDEV
11678c2ecf20Sopenharmony_cierr_video_unreg:
11688c2ecf20Sopenharmony_ci	video_unregister_device(&pdev->vdev);
11698c2ecf20Sopenharmony_ci#endif
11708c2ecf20Sopenharmony_cierr_unregister_v4l2_dev:
11718c2ecf20Sopenharmony_ci	v4l2_device_unregister(&pdev->v4l2_dev);
11728c2ecf20Sopenharmony_cierr_free_controls:
11738c2ecf20Sopenharmony_ci	v4l2_ctrl_handler_free(&pdev->ctrl_handler);
11748c2ecf20Sopenharmony_cierr_free_mem:
11758c2ecf20Sopenharmony_ci	kfree(pdev->ctrl_buf);
11768c2ecf20Sopenharmony_ci	kfree(pdev);
11778c2ecf20Sopenharmony_ci	return rc;
11788c2ecf20Sopenharmony_ci}
11798c2ecf20Sopenharmony_ci
11808c2ecf20Sopenharmony_ci/* The user yanked out the cable... */
11818c2ecf20Sopenharmony_cistatic void usb_pwc_disconnect(struct usb_interface *intf)
11828c2ecf20Sopenharmony_ci{
11838c2ecf20Sopenharmony_ci	struct v4l2_device *v = usb_get_intfdata(intf);
11848c2ecf20Sopenharmony_ci	struct pwc_device *pdev = container_of(v, struct pwc_device, v4l2_dev);
11858c2ecf20Sopenharmony_ci
11868c2ecf20Sopenharmony_ci	mutex_lock(&pdev->vb_queue_lock);
11878c2ecf20Sopenharmony_ci	mutex_lock(&pdev->v4l2_lock);
11888c2ecf20Sopenharmony_ci	/* No need to keep the urbs around after disconnection */
11898c2ecf20Sopenharmony_ci	if (pdev->vb_queue.streaming)
11908c2ecf20Sopenharmony_ci		pwc_isoc_cleanup(pdev);
11918c2ecf20Sopenharmony_ci	pdev->udev = NULL;
11928c2ecf20Sopenharmony_ci
11938c2ecf20Sopenharmony_ci	v4l2_device_disconnect(&pdev->v4l2_dev);
11948c2ecf20Sopenharmony_ci	video_unregister_device(&pdev->vdev);
11958c2ecf20Sopenharmony_ci	mutex_unlock(&pdev->v4l2_lock);
11968c2ecf20Sopenharmony_ci	mutex_unlock(&pdev->vb_queue_lock);
11978c2ecf20Sopenharmony_ci
11988c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_PWC_INPUT_EVDEV
11998c2ecf20Sopenharmony_ci	if (pdev->button_dev)
12008c2ecf20Sopenharmony_ci		input_unregister_device(pdev->button_dev);
12018c2ecf20Sopenharmony_ci#endif
12028c2ecf20Sopenharmony_ci
12038c2ecf20Sopenharmony_ci	v4l2_device_put(&pdev->v4l2_dev);
12048c2ecf20Sopenharmony_ci}
12058c2ecf20Sopenharmony_ci
12068c2ecf20Sopenharmony_ci
12078c2ecf20Sopenharmony_ci/*
12088c2ecf20Sopenharmony_ci * Initialization code & module stuff
12098c2ecf20Sopenharmony_ci */
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_cistatic unsigned int leds_nargs;
12128c2ecf20Sopenharmony_ci
12138c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_PWC_DEBUG
12148c2ecf20Sopenharmony_cimodule_param_named(trace, pwc_trace, int, 0644);
12158c2ecf20Sopenharmony_ci#endif
12168c2ecf20Sopenharmony_cimodule_param(power_save, int, 0644);
12178c2ecf20Sopenharmony_cimodule_param_array(leds, int, &leds_nargs, 0444);
12188c2ecf20Sopenharmony_ci
12198c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_PWC_DEBUG
12208c2ecf20Sopenharmony_ciMODULE_PARM_DESC(trace, "For debugging purposes");
12218c2ecf20Sopenharmony_ci#endif
12228c2ecf20Sopenharmony_ciMODULE_PARM_DESC(power_save, "Turn power saving for new cameras on or off");
12238c2ecf20Sopenharmony_ciMODULE_PARM_DESC(leds, "LED on,off time in milliseconds");
12248c2ecf20Sopenharmony_ci
12258c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Philips & OEM USB webcam driver");
12268c2ecf20Sopenharmony_ciMODULE_AUTHOR("Luc Saillard <luc@saillard.org>");
12278c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
12288c2ecf20Sopenharmony_ciMODULE_ALIAS("pwcx");
12298c2ecf20Sopenharmony_ciMODULE_VERSION( PWC_VERSION );
12308c2ecf20Sopenharmony_ci
12318c2ecf20Sopenharmony_cimodule_usb_driver(pwc_driver);
1232