18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
48c2ecf20Sopenharmony_ci *		      Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
58c2ecf20Sopenharmony_ci * Copyright (c) 2002, 2003 Tuukka Toivonen
68c2ecf20Sopenharmony_ci * Copyright (c) 2008 Erik Andrén
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * P/N 861037:      Sensor HDCS1000        ASIC STV0600
98c2ecf20Sopenharmony_ci * P/N 861050-0010: Sensor HDCS1000        ASIC STV0600
108c2ecf20Sopenharmony_ci * P/N 861050-0020: Sensor Photobit PB100  ASIC STV0600-1 - QuickCam Express
118c2ecf20Sopenharmony_ci * P/N 861055:      Sensor ST VV6410       ASIC STV0610   - LEGO cam
128c2ecf20Sopenharmony_ci * P/N 861075-0040: Sensor HDCS1000        ASIC
138c2ecf20Sopenharmony_ci * P/N 961179-0700: Sensor ST VV6410       ASIC STV0602   - Dexxa WebCam USB
148c2ecf20Sopenharmony_ci * P/N 861040-0000: Sensor ST VV6410       ASIC STV0610   - QuickCam Web
158c2ecf20Sopenharmony_ci */
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include <linux/input.h>
208c2ecf20Sopenharmony_ci#include "stv06xx_sensor.h"
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ciMODULE_AUTHOR("Erik Andrén");
238c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("STV06XX USB Camera Driver");
248c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistatic bool dump_bridge;
278c2ecf20Sopenharmony_cistatic bool dump_sensor;
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ciint stv06xx_write_bridge(struct sd *sd, u16 address, u16 i2c_data)
308c2ecf20Sopenharmony_ci{
318c2ecf20Sopenharmony_ci	int err;
328c2ecf20Sopenharmony_ci	struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
338c2ecf20Sopenharmony_ci	struct usb_device *udev = sd->gspca_dev.dev;
348c2ecf20Sopenharmony_ci	__u8 *buf = sd->gspca_dev.usb_buf;
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	u8 len = (i2c_data > 0xff) ? 2 : 1;
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	buf[0] = i2c_data & 0xff;
398c2ecf20Sopenharmony_ci	buf[1] = (i2c_data >> 8) & 0xff;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
428c2ecf20Sopenharmony_ci			      0x04, 0x40, address, 0, buf, len,
438c2ecf20Sopenharmony_ci			      STV06XX_URB_MSG_TIMEOUT);
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	gspca_dbg(gspca_dev, D_CONF, "Written 0x%x to address 0x%x, status: %d\n",
468c2ecf20Sopenharmony_ci		  i2c_data, address, err);
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	return (err < 0) ? err : 0;
498c2ecf20Sopenharmony_ci}
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ciint stv06xx_read_bridge(struct sd *sd, u16 address, u8 *i2c_data)
528c2ecf20Sopenharmony_ci{
538c2ecf20Sopenharmony_ci	int err;
548c2ecf20Sopenharmony_ci	struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
558c2ecf20Sopenharmony_ci	struct usb_device *udev = sd->gspca_dev.dev;
568c2ecf20Sopenharmony_ci	__u8 *buf = sd->gspca_dev.usb_buf;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	err = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
598c2ecf20Sopenharmony_ci			      0x04, 0xc0, address, 0, buf, 1,
608c2ecf20Sopenharmony_ci			      STV06XX_URB_MSG_TIMEOUT);
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	*i2c_data = buf[0];
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	gspca_dbg(gspca_dev, D_CONF, "Reading 0x%x from address 0x%x, status %d\n",
658c2ecf20Sopenharmony_ci		  *i2c_data, address, err);
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	return (err < 0) ? err : 0;
688c2ecf20Sopenharmony_ci}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci/* Wraps the normal write sensor bytes / words functions for writing a
718c2ecf20Sopenharmony_ci   single value */
728c2ecf20Sopenharmony_ciint stv06xx_write_sensor(struct sd *sd, u8 address, u16 value)
738c2ecf20Sopenharmony_ci{
748c2ecf20Sopenharmony_ci	if (sd->sensor->i2c_len == 2) {
758c2ecf20Sopenharmony_ci		u16 data[2] = { address, value };
768c2ecf20Sopenharmony_ci		return stv06xx_write_sensor_words(sd, data, 1);
778c2ecf20Sopenharmony_ci	} else {
788c2ecf20Sopenharmony_ci		u8 data[2] = { address, value };
798c2ecf20Sopenharmony_ci		return stv06xx_write_sensor_bytes(sd, data, 1);
808c2ecf20Sopenharmony_ci	}
818c2ecf20Sopenharmony_ci}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cistatic int stv06xx_write_sensor_finish(struct sd *sd)
848c2ecf20Sopenharmony_ci{
858c2ecf20Sopenharmony_ci	int err = 0;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	if (sd->bridge == BRIDGE_STV610) {
888c2ecf20Sopenharmony_ci		struct usb_device *udev = sd->gspca_dev.dev;
898c2ecf20Sopenharmony_ci		__u8 *buf = sd->gspca_dev.usb_buf;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci		buf[0] = 0;
928c2ecf20Sopenharmony_ci		err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
938c2ecf20Sopenharmony_ci				      0x04, 0x40, 0x1704, 0, buf, 1,
948c2ecf20Sopenharmony_ci				      STV06XX_URB_MSG_TIMEOUT);
958c2ecf20Sopenharmony_ci	}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	return (err < 0) ? err : 0;
988c2ecf20Sopenharmony_ci}
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ciint stv06xx_write_sensor_bytes(struct sd *sd, const u8 *data, u8 len)
1018c2ecf20Sopenharmony_ci{
1028c2ecf20Sopenharmony_ci	int err, i, j;
1038c2ecf20Sopenharmony_ci	struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
1048c2ecf20Sopenharmony_ci	struct usb_device *udev = sd->gspca_dev.dev;
1058c2ecf20Sopenharmony_ci	__u8 *buf = sd->gspca_dev.usb_buf;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	gspca_dbg(gspca_dev, D_CONF, "I2C: Command buffer contains %d entries\n",
1088c2ecf20Sopenharmony_ci		  len);
1098c2ecf20Sopenharmony_ci	for (i = 0; i < len;) {
1108c2ecf20Sopenharmony_ci		/* Build the command buffer */
1118c2ecf20Sopenharmony_ci		memset(buf, 0, I2C_BUFFER_LENGTH);
1128c2ecf20Sopenharmony_ci		for (j = 0; j < I2C_MAX_BYTES && i < len; j++, i++) {
1138c2ecf20Sopenharmony_ci			buf[j] = data[2*i];
1148c2ecf20Sopenharmony_ci			buf[0x10 + j] = data[2*i+1];
1158c2ecf20Sopenharmony_ci			gspca_dbg(gspca_dev, D_CONF, "I2C: Writing 0x%02x to reg 0x%02x\n",
1168c2ecf20Sopenharmony_ci				  data[2*i+1], data[2*i]);
1178c2ecf20Sopenharmony_ci		}
1188c2ecf20Sopenharmony_ci		buf[0x20] = sd->sensor->i2c_addr;
1198c2ecf20Sopenharmony_ci		buf[0x21] = j - 1; /* Number of commands to send - 1 */
1208c2ecf20Sopenharmony_ci		buf[0x22] = I2C_WRITE_CMD;
1218c2ecf20Sopenharmony_ci		err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
1228c2ecf20Sopenharmony_ci				      0x04, 0x40, 0x0400, 0, buf,
1238c2ecf20Sopenharmony_ci				      I2C_BUFFER_LENGTH,
1248c2ecf20Sopenharmony_ci				      STV06XX_URB_MSG_TIMEOUT);
1258c2ecf20Sopenharmony_ci		if (err < 0)
1268c2ecf20Sopenharmony_ci			return err;
1278c2ecf20Sopenharmony_ci	}
1288c2ecf20Sopenharmony_ci	return stv06xx_write_sensor_finish(sd);
1298c2ecf20Sopenharmony_ci}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ciint stv06xx_write_sensor_words(struct sd *sd, const u16 *data, u8 len)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	int err, i, j;
1348c2ecf20Sopenharmony_ci	struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
1358c2ecf20Sopenharmony_ci	struct usb_device *udev = sd->gspca_dev.dev;
1368c2ecf20Sopenharmony_ci	__u8 *buf = sd->gspca_dev.usb_buf;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	gspca_dbg(gspca_dev, D_CONF, "I2C: Command buffer contains %d entries\n",
1398c2ecf20Sopenharmony_ci		  len);
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	for (i = 0; i < len;) {
1428c2ecf20Sopenharmony_ci		/* Build the command buffer */
1438c2ecf20Sopenharmony_ci		memset(buf, 0, I2C_BUFFER_LENGTH);
1448c2ecf20Sopenharmony_ci		for (j = 0; j < I2C_MAX_WORDS && i < len; j++, i++) {
1458c2ecf20Sopenharmony_ci			buf[j] = data[2*i];
1468c2ecf20Sopenharmony_ci			buf[0x10 + j * 2] = data[2*i+1];
1478c2ecf20Sopenharmony_ci			buf[0x10 + j * 2 + 1] = data[2*i+1] >> 8;
1488c2ecf20Sopenharmony_ci			gspca_dbg(gspca_dev, D_CONF, "I2C: Writing 0x%04x to reg 0x%02x\n",
1498c2ecf20Sopenharmony_ci				  data[2*i+1], data[2*i]);
1508c2ecf20Sopenharmony_ci		}
1518c2ecf20Sopenharmony_ci		buf[0x20] = sd->sensor->i2c_addr;
1528c2ecf20Sopenharmony_ci		buf[0x21] = j - 1; /* Number of commands to send - 1 */
1538c2ecf20Sopenharmony_ci		buf[0x22] = I2C_WRITE_CMD;
1548c2ecf20Sopenharmony_ci		err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
1558c2ecf20Sopenharmony_ci				0x04, 0x40, 0x0400, 0, buf,
1568c2ecf20Sopenharmony_ci				I2C_BUFFER_LENGTH,
1578c2ecf20Sopenharmony_ci				STV06XX_URB_MSG_TIMEOUT);
1588c2ecf20Sopenharmony_ci		if (err < 0)
1598c2ecf20Sopenharmony_ci			return err;
1608c2ecf20Sopenharmony_ci	}
1618c2ecf20Sopenharmony_ci	return stv06xx_write_sensor_finish(sd);
1628c2ecf20Sopenharmony_ci}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ciint stv06xx_read_sensor(struct sd *sd, const u8 address, u16 *value)
1658c2ecf20Sopenharmony_ci{
1668c2ecf20Sopenharmony_ci	int err;
1678c2ecf20Sopenharmony_ci	struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
1688c2ecf20Sopenharmony_ci	struct usb_device *udev = sd->gspca_dev.dev;
1698c2ecf20Sopenharmony_ci	__u8 *buf = sd->gspca_dev.usb_buf;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	err = stv06xx_write_bridge(sd, STV_I2C_FLUSH, sd->sensor->i2c_flush);
1728c2ecf20Sopenharmony_ci	if (err < 0)
1738c2ecf20Sopenharmony_ci		return err;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	/* Clear mem */
1768c2ecf20Sopenharmony_ci	memset(buf, 0, I2C_BUFFER_LENGTH);
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	buf[0] = address;
1798c2ecf20Sopenharmony_ci	buf[0x20] = sd->sensor->i2c_addr;
1808c2ecf20Sopenharmony_ci	buf[0x21] = 0;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	/* Read I2C register */
1838c2ecf20Sopenharmony_ci	buf[0x22] = I2C_READ_CMD;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
1868c2ecf20Sopenharmony_ci			      0x04, 0x40, 0x1400, 0, buf, I2C_BUFFER_LENGTH,
1878c2ecf20Sopenharmony_ci			      STV06XX_URB_MSG_TIMEOUT);
1888c2ecf20Sopenharmony_ci	if (err < 0) {
1898c2ecf20Sopenharmony_ci		pr_err("I2C: Read error writing address: %d\n", err);
1908c2ecf20Sopenharmony_ci		return err;
1918c2ecf20Sopenharmony_ci	}
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	err = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
1948c2ecf20Sopenharmony_ci			      0x04, 0xc0, 0x1410, 0, buf, sd->sensor->i2c_len,
1958c2ecf20Sopenharmony_ci			      STV06XX_URB_MSG_TIMEOUT);
1968c2ecf20Sopenharmony_ci	if (sd->sensor->i2c_len == 2)
1978c2ecf20Sopenharmony_ci		*value = buf[0] | (buf[1] << 8);
1988c2ecf20Sopenharmony_ci	else
1998c2ecf20Sopenharmony_ci		*value = buf[0];
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	gspca_dbg(gspca_dev, D_CONF, "I2C: Read 0x%x from address 0x%x, status: %d\n",
2028c2ecf20Sopenharmony_ci		  *value, address, err);
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	return (err < 0) ? err : 0;
2058c2ecf20Sopenharmony_ci}
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci/* Dumps all bridge registers */
2088c2ecf20Sopenharmony_cistatic void stv06xx_dump_bridge(struct sd *sd)
2098c2ecf20Sopenharmony_ci{
2108c2ecf20Sopenharmony_ci	int i;
2118c2ecf20Sopenharmony_ci	u8 data, buf;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	pr_info("Dumping all stv06xx bridge registers\n");
2148c2ecf20Sopenharmony_ci	for (i = 0x1400; i < 0x160f; i++) {
2158c2ecf20Sopenharmony_ci		stv06xx_read_bridge(sd, i, &data);
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci		pr_info("Read 0x%x from address 0x%x\n", data, i);
2188c2ecf20Sopenharmony_ci	}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	pr_info("Testing stv06xx bridge registers for writability\n");
2218c2ecf20Sopenharmony_ci	for (i = 0x1400; i < 0x160f; i++) {
2228c2ecf20Sopenharmony_ci		stv06xx_read_bridge(sd, i, &data);
2238c2ecf20Sopenharmony_ci		buf = data;
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci		stv06xx_write_bridge(sd, i, 0xff);
2268c2ecf20Sopenharmony_ci		stv06xx_read_bridge(sd, i, &data);
2278c2ecf20Sopenharmony_ci		if (data == 0xff)
2288c2ecf20Sopenharmony_ci			pr_info("Register 0x%x is read/write\n", i);
2298c2ecf20Sopenharmony_ci		else if (data != buf)
2308c2ecf20Sopenharmony_ci			pr_info("Register 0x%x is read/write, but only partially\n",
2318c2ecf20Sopenharmony_ci				i);
2328c2ecf20Sopenharmony_ci		else
2338c2ecf20Sopenharmony_ci			pr_info("Register 0x%x is read-only\n", i);
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci		stv06xx_write_bridge(sd, i, buf);
2368c2ecf20Sopenharmony_ci	}
2378c2ecf20Sopenharmony_ci}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci/* this function is called at probe and resume time */
2408c2ecf20Sopenharmony_cistatic int stv06xx_init(struct gspca_dev *gspca_dev)
2418c2ecf20Sopenharmony_ci{
2428c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
2438c2ecf20Sopenharmony_ci	int err;
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	gspca_dbg(gspca_dev, D_PROBE, "Initializing camera\n");
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	/* Let the usb init settle for a bit
2488c2ecf20Sopenharmony_ci	   before performing the initialization */
2498c2ecf20Sopenharmony_ci	msleep(250);
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	err = sd->sensor->init(sd);
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	if (dump_sensor && sd->sensor->dump)
2548c2ecf20Sopenharmony_ci		sd->sensor->dump(sd);
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	return (err < 0) ? err : 0;
2578c2ecf20Sopenharmony_ci}
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci/* this function is called at probe time */
2608c2ecf20Sopenharmony_cistatic int stv06xx_init_controls(struct gspca_dev *gspca_dev)
2618c2ecf20Sopenharmony_ci{
2628c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	gspca_dbg(gspca_dev, D_PROBE, "Initializing controls\n");
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	gspca_dev->vdev.ctrl_handler = &gspca_dev->ctrl_handler;
2678c2ecf20Sopenharmony_ci	return sd->sensor->init_controls(sd);
2688c2ecf20Sopenharmony_ci}
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci/* Start the camera */
2718c2ecf20Sopenharmony_cistatic int stv06xx_start(struct gspca_dev *gspca_dev)
2728c2ecf20Sopenharmony_ci{
2738c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
2748c2ecf20Sopenharmony_ci	struct usb_host_interface *alt;
2758c2ecf20Sopenharmony_ci	struct usb_interface *intf;
2768c2ecf20Sopenharmony_ci	int err, packet_size;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	intf = usb_ifnum_to_if(sd->gspca_dev.dev, sd->gspca_dev.iface);
2798c2ecf20Sopenharmony_ci	alt = usb_altnum_to_altsetting(intf, sd->gspca_dev.alt);
2808c2ecf20Sopenharmony_ci	if (!alt) {
2818c2ecf20Sopenharmony_ci		gspca_err(gspca_dev, "Couldn't get altsetting\n");
2828c2ecf20Sopenharmony_ci		return -EIO;
2838c2ecf20Sopenharmony_ci	}
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	if (alt->desc.bNumEndpoints < 1)
2868c2ecf20Sopenharmony_ci		return -ENODEV;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize);
2898c2ecf20Sopenharmony_ci	err = stv06xx_write_bridge(sd, STV_ISO_SIZE_L, packet_size);
2908c2ecf20Sopenharmony_ci	if (err < 0)
2918c2ecf20Sopenharmony_ci		return err;
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	/* Prepare the sensor for start */
2948c2ecf20Sopenharmony_ci	err = sd->sensor->start(sd);
2958c2ecf20Sopenharmony_ci	if (err < 0)
2968c2ecf20Sopenharmony_ci		goto out;
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	/* Start isochronous streaming */
2998c2ecf20Sopenharmony_ci	err = stv06xx_write_bridge(sd, STV_ISO_ENABLE, 1);
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ciout:
3028c2ecf20Sopenharmony_ci	if (err < 0)
3038c2ecf20Sopenharmony_ci		gspca_dbg(gspca_dev, D_STREAM, "Starting stream failed\n");
3048c2ecf20Sopenharmony_ci	else
3058c2ecf20Sopenharmony_ci		gspca_dbg(gspca_dev, D_STREAM, "Started streaming\n");
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	return (err < 0) ? err : 0;
3088c2ecf20Sopenharmony_ci}
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_cistatic int stv06xx_isoc_init(struct gspca_dev *gspca_dev)
3118c2ecf20Sopenharmony_ci{
3128c2ecf20Sopenharmony_ci	struct usb_interface_cache *intfc;
3138c2ecf20Sopenharmony_ci	struct usb_host_interface *alt;
3148c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	intfc = gspca_dev->dev->actconfig->intf_cache[0];
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	if (intfc->num_altsetting < 2)
3198c2ecf20Sopenharmony_ci		return -ENODEV;
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	alt = &intfc->altsetting[1];
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	if (alt->desc.bNumEndpoints < 1)
3248c2ecf20Sopenharmony_ci		return -ENODEV;
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	/* Start isoc bandwidth "negotiation" at max isoc bandwidth */
3278c2ecf20Sopenharmony_ci	alt->endpoint[0].desc.wMaxPacketSize =
3288c2ecf20Sopenharmony_ci		cpu_to_le16(sd->sensor->max_packet_size[gspca_dev->curr_mode]);
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	return 0;
3318c2ecf20Sopenharmony_ci}
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_cistatic int stv06xx_isoc_nego(struct gspca_dev *gspca_dev)
3348c2ecf20Sopenharmony_ci{
3358c2ecf20Sopenharmony_ci	int ret, packet_size, min_packet_size;
3368c2ecf20Sopenharmony_ci	struct usb_host_interface *alt;
3378c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	/*
3408c2ecf20Sopenharmony_ci	 * Existence of altsetting and endpoint was verified in
3418c2ecf20Sopenharmony_ci	 * stv06xx_isoc_init()
3428c2ecf20Sopenharmony_ci	 */
3438c2ecf20Sopenharmony_ci	alt = &gspca_dev->dev->actconfig->intf_cache[0]->altsetting[1];
3448c2ecf20Sopenharmony_ci	packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize);
3458c2ecf20Sopenharmony_ci	min_packet_size = sd->sensor->min_packet_size[gspca_dev->curr_mode];
3468c2ecf20Sopenharmony_ci	if (packet_size <= min_packet_size)
3478c2ecf20Sopenharmony_ci		return -EIO;
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	packet_size -= 100;
3508c2ecf20Sopenharmony_ci	if (packet_size < min_packet_size)
3518c2ecf20Sopenharmony_ci		packet_size = min_packet_size;
3528c2ecf20Sopenharmony_ci	alt->endpoint[0].desc.wMaxPacketSize = cpu_to_le16(packet_size);
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, 1);
3558c2ecf20Sopenharmony_ci	if (ret < 0)
3568c2ecf20Sopenharmony_ci		gspca_err(gspca_dev, "set alt 1 err %d\n", ret);
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	return ret;
3598c2ecf20Sopenharmony_ci}
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_cistatic void stv06xx_stopN(struct gspca_dev *gspca_dev)
3628c2ecf20Sopenharmony_ci{
3638c2ecf20Sopenharmony_ci	int err;
3648c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	/* stop ISO-streaming */
3678c2ecf20Sopenharmony_ci	err = stv06xx_write_bridge(sd, STV_ISO_ENABLE, 0);
3688c2ecf20Sopenharmony_ci	if (err < 0)
3698c2ecf20Sopenharmony_ci		goto out;
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	err = sd->sensor->stop(sd);
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ciout:
3748c2ecf20Sopenharmony_ci	if (err < 0)
3758c2ecf20Sopenharmony_ci		gspca_dbg(gspca_dev, D_STREAM, "Failed to stop stream\n");
3768c2ecf20Sopenharmony_ci	else
3778c2ecf20Sopenharmony_ci		gspca_dbg(gspca_dev, D_STREAM, "Stopped streaming\n");
3788c2ecf20Sopenharmony_ci}
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci/*
3818c2ecf20Sopenharmony_ci * Analyse an USB packet of the data stream and store it appropriately.
3828c2ecf20Sopenharmony_ci * Each packet contains an integral number of chunks. Each chunk has
3838c2ecf20Sopenharmony_ci * 2-bytes identification, followed by 2-bytes that describe the chunk
3848c2ecf20Sopenharmony_ci * length. Known/guessed chunk identifications are:
3858c2ecf20Sopenharmony_ci * 8001/8005/C001/C005 - Begin new frame
3868c2ecf20Sopenharmony_ci * 8002/8006/C002/C006 - End frame
3878c2ecf20Sopenharmony_ci * 0200/4200           - Contains actual image data, bayer or compressed
3888c2ecf20Sopenharmony_ci * 0005                - 11 bytes of unknown data
3898c2ecf20Sopenharmony_ci * 0100                - 2 bytes of unknown data
3908c2ecf20Sopenharmony_ci * The 0005 and 0100 chunks seem to appear only in compressed stream.
3918c2ecf20Sopenharmony_ci */
3928c2ecf20Sopenharmony_cistatic void stv06xx_pkt_scan(struct gspca_dev *gspca_dev,
3938c2ecf20Sopenharmony_ci			u8 *data,			/* isoc packet */
3948c2ecf20Sopenharmony_ci			int len)			/* iso packet length */
3958c2ecf20Sopenharmony_ci{
3968c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	gspca_dbg(gspca_dev, D_PACK, "Packet of length %d arrived\n", len);
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	/* A packet may contain several frames
4018c2ecf20Sopenharmony_ci	   loop until the whole packet is reached */
4028c2ecf20Sopenharmony_ci	while (len) {
4038c2ecf20Sopenharmony_ci		int id, chunk_len;
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci		if (len < 4) {
4068c2ecf20Sopenharmony_ci			gspca_dbg(gspca_dev, D_PACK, "Packet is smaller than 4 bytes\n");
4078c2ecf20Sopenharmony_ci			return;
4088c2ecf20Sopenharmony_ci		}
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci		/* Capture the id */
4118c2ecf20Sopenharmony_ci		id = (data[0] << 8) | data[1];
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci		/* Capture the chunk length */
4148c2ecf20Sopenharmony_ci		chunk_len = (data[2] << 8) | data[3];
4158c2ecf20Sopenharmony_ci		gspca_dbg(gspca_dev, D_PACK, "Chunk id: %x, length: %d\n",
4168c2ecf20Sopenharmony_ci			  id, chunk_len);
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci		data += 4;
4198c2ecf20Sopenharmony_ci		len -= 4;
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci		if (len < chunk_len) {
4228c2ecf20Sopenharmony_ci			gspca_err(gspca_dev, "URB packet length is smaller than the specified chunk length\n");
4238c2ecf20Sopenharmony_ci			gspca_dev->last_packet_type = DISCARD_PACKET;
4248c2ecf20Sopenharmony_ci			return;
4258c2ecf20Sopenharmony_ci		}
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci		/* First byte seem to be 02=data 2nd byte is unknown??? */
4288c2ecf20Sopenharmony_ci		if (sd->bridge == BRIDGE_ST6422 && (id & 0xff00) == 0x0200)
4298c2ecf20Sopenharmony_ci			goto frame_data;
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci		switch (id) {
4328c2ecf20Sopenharmony_ci		case 0x0200:
4338c2ecf20Sopenharmony_ci		case 0x4200:
4348c2ecf20Sopenharmony_ciframe_data:
4358c2ecf20Sopenharmony_ci			gspca_dbg(gspca_dev, D_PACK, "Frame data packet detected\n");
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci			if (sd->to_skip) {
4388c2ecf20Sopenharmony_ci				int skip = (sd->to_skip < chunk_len) ?
4398c2ecf20Sopenharmony_ci					    sd->to_skip : chunk_len;
4408c2ecf20Sopenharmony_ci				data += skip;
4418c2ecf20Sopenharmony_ci				len -= skip;
4428c2ecf20Sopenharmony_ci				chunk_len -= skip;
4438c2ecf20Sopenharmony_ci				sd->to_skip -= skip;
4448c2ecf20Sopenharmony_ci			}
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci			gspca_frame_add(gspca_dev, INTER_PACKET,
4478c2ecf20Sopenharmony_ci					data, chunk_len);
4488c2ecf20Sopenharmony_ci			break;
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci		case 0x8001:
4518c2ecf20Sopenharmony_ci		case 0x8005:
4528c2ecf20Sopenharmony_ci		case 0xc001:
4538c2ecf20Sopenharmony_ci		case 0xc005:
4548c2ecf20Sopenharmony_ci			gspca_dbg(gspca_dev, D_PACK, "Starting new frame\n");
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci			/* Create a new frame, chunk length should be zero */
4578c2ecf20Sopenharmony_ci			gspca_frame_add(gspca_dev, FIRST_PACKET,
4588c2ecf20Sopenharmony_ci					NULL, 0);
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci			if (sd->bridge == BRIDGE_ST6422)
4618c2ecf20Sopenharmony_ci				sd->to_skip = gspca_dev->pixfmt.width * 4;
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci			if (chunk_len)
4648c2ecf20Sopenharmony_ci				gspca_err(gspca_dev, "Chunk length is non-zero on a SOF\n");
4658c2ecf20Sopenharmony_ci			break;
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci		case 0x8002:
4688c2ecf20Sopenharmony_ci		case 0x8006:
4698c2ecf20Sopenharmony_ci		case 0xc002:
4708c2ecf20Sopenharmony_ci			gspca_dbg(gspca_dev, D_PACK, "End of frame detected\n");
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci			/* Complete the last frame (if any) */
4738c2ecf20Sopenharmony_ci			gspca_frame_add(gspca_dev, LAST_PACKET,
4748c2ecf20Sopenharmony_ci					NULL, 0);
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci			if (chunk_len)
4778c2ecf20Sopenharmony_ci				gspca_err(gspca_dev, "Chunk length is non-zero on a EOF\n");
4788c2ecf20Sopenharmony_ci			break;
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci		case 0x0005:
4818c2ecf20Sopenharmony_ci			gspca_dbg(gspca_dev, D_PACK, "Chunk 0x005 detected\n");
4828c2ecf20Sopenharmony_ci			/* Unknown chunk with 11 bytes of data,
4838c2ecf20Sopenharmony_ci			   occurs just before end of each frame
4848c2ecf20Sopenharmony_ci			   in compressed mode */
4858c2ecf20Sopenharmony_ci			break;
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci		case 0x0100:
4888c2ecf20Sopenharmony_ci			gspca_dbg(gspca_dev, D_PACK, "Chunk 0x0100 detected\n");
4898c2ecf20Sopenharmony_ci			/* Unknown chunk with 2 bytes of data,
4908c2ecf20Sopenharmony_ci			   occurs 2-3 times per USB interrupt */
4918c2ecf20Sopenharmony_ci			break;
4928c2ecf20Sopenharmony_ci		case 0x42ff:
4938c2ecf20Sopenharmony_ci			gspca_dbg(gspca_dev, D_PACK, "Chunk 0x42ff detected\n");
4948c2ecf20Sopenharmony_ci			/* Special chunk seen sometimes on the ST6422 */
4958c2ecf20Sopenharmony_ci			break;
4968c2ecf20Sopenharmony_ci		default:
4978c2ecf20Sopenharmony_ci			gspca_dbg(gspca_dev, D_PACK, "Unknown chunk 0x%04x detected\n",
4988c2ecf20Sopenharmony_ci				  id);
4998c2ecf20Sopenharmony_ci			/* Unknown chunk */
5008c2ecf20Sopenharmony_ci		}
5018c2ecf20Sopenharmony_ci		data    += chunk_len;
5028c2ecf20Sopenharmony_ci		len     -= chunk_len;
5038c2ecf20Sopenharmony_ci	}
5048c2ecf20Sopenharmony_ci}
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_INPUT)
5078c2ecf20Sopenharmony_cistatic int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
5088c2ecf20Sopenharmony_ci			u8 *data,		/* interrupt packet data */
5098c2ecf20Sopenharmony_ci			int len)		/* interrupt packet length */
5108c2ecf20Sopenharmony_ci{
5118c2ecf20Sopenharmony_ci	int ret = -EINVAL;
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	if (len == 1 && (data[0] == 0x80 || data[0] == 0x10)) {
5148c2ecf20Sopenharmony_ci		input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1);
5158c2ecf20Sopenharmony_ci		input_sync(gspca_dev->input_dev);
5168c2ecf20Sopenharmony_ci		ret = 0;
5178c2ecf20Sopenharmony_ci	}
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci	if (len == 1 && (data[0] == 0x88 || data[0] == 0x11)) {
5208c2ecf20Sopenharmony_ci		input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
5218c2ecf20Sopenharmony_ci		input_sync(gspca_dev->input_dev);
5228c2ecf20Sopenharmony_ci		ret = 0;
5238c2ecf20Sopenharmony_ci	}
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	return ret;
5268c2ecf20Sopenharmony_ci}
5278c2ecf20Sopenharmony_ci#endif
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_cistatic int stv06xx_config(struct gspca_dev *gspca_dev,
5308c2ecf20Sopenharmony_ci			  const struct usb_device_id *id);
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_cistatic void stv06xx_probe_error(struct gspca_dev *gspca_dev)
5338c2ecf20Sopenharmony_ci{
5348c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *)gspca_dev;
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	kfree(sd->sensor_priv);
5378c2ecf20Sopenharmony_ci	sd->sensor_priv = NULL;
5388c2ecf20Sopenharmony_ci}
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci/* sub-driver description */
5418c2ecf20Sopenharmony_cistatic const struct sd_desc sd_desc = {
5428c2ecf20Sopenharmony_ci	.name = MODULE_NAME,
5438c2ecf20Sopenharmony_ci	.config = stv06xx_config,
5448c2ecf20Sopenharmony_ci	.init = stv06xx_init,
5458c2ecf20Sopenharmony_ci	.init_controls = stv06xx_init_controls,
5468c2ecf20Sopenharmony_ci	.probe_error = stv06xx_probe_error,
5478c2ecf20Sopenharmony_ci	.start = stv06xx_start,
5488c2ecf20Sopenharmony_ci	.stopN = stv06xx_stopN,
5498c2ecf20Sopenharmony_ci	.pkt_scan = stv06xx_pkt_scan,
5508c2ecf20Sopenharmony_ci	.isoc_init = stv06xx_isoc_init,
5518c2ecf20Sopenharmony_ci	.isoc_nego = stv06xx_isoc_nego,
5528c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_INPUT)
5538c2ecf20Sopenharmony_ci	.int_pkt_scan = sd_int_pkt_scan,
5548c2ecf20Sopenharmony_ci#endif
5558c2ecf20Sopenharmony_ci};
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci/* This function is called at probe time */
5588c2ecf20Sopenharmony_cistatic int stv06xx_config(struct gspca_dev *gspca_dev,
5598c2ecf20Sopenharmony_ci			  const struct usb_device_id *id)
5608c2ecf20Sopenharmony_ci{
5618c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	gspca_dbg(gspca_dev, D_PROBE, "Configuring camera\n");
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	sd->bridge = id->driver_info;
5668c2ecf20Sopenharmony_ci	gspca_dev->sd_desc = &sd_desc;
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	if (dump_bridge)
5698c2ecf20Sopenharmony_ci		stv06xx_dump_bridge(sd);
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci	sd->sensor = &stv06xx_sensor_st6422;
5728c2ecf20Sopenharmony_ci	if (!sd->sensor->probe(sd))
5738c2ecf20Sopenharmony_ci		return 0;
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	sd->sensor = &stv06xx_sensor_vv6410;
5768c2ecf20Sopenharmony_ci	if (!sd->sensor->probe(sd))
5778c2ecf20Sopenharmony_ci		return 0;
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci	sd->sensor = &stv06xx_sensor_hdcs1x00;
5808c2ecf20Sopenharmony_ci	if (!sd->sensor->probe(sd))
5818c2ecf20Sopenharmony_ci		return 0;
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	sd->sensor = &stv06xx_sensor_hdcs1020;
5848c2ecf20Sopenharmony_ci	if (!sd->sensor->probe(sd))
5858c2ecf20Sopenharmony_ci		return 0;
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	sd->sensor = &stv06xx_sensor_pb0100;
5888c2ecf20Sopenharmony_ci	if (!sd->sensor->probe(sd))
5898c2ecf20Sopenharmony_ci		return 0;
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	sd->sensor = NULL;
5928c2ecf20Sopenharmony_ci	return -ENODEV;
5938c2ecf20Sopenharmony_ci}
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci/* -- module initialisation -- */
5988c2ecf20Sopenharmony_cistatic const struct usb_device_id device_table[] = {
5998c2ecf20Sopenharmony_ci	{USB_DEVICE(0x046d, 0x0840), .driver_info = BRIDGE_STV600 },	/* QuickCam Express */
6008c2ecf20Sopenharmony_ci	{USB_DEVICE(0x046d, 0x0850), .driver_info = BRIDGE_STV610 },	/* LEGO cam / QuickCam Web */
6018c2ecf20Sopenharmony_ci	{USB_DEVICE(0x046d, 0x0870), .driver_info = BRIDGE_STV602 },	/* Dexxa WebCam USB */
6028c2ecf20Sopenharmony_ci	{USB_DEVICE(0x046D, 0x08F0), .driver_info = BRIDGE_ST6422 },	/* QuickCam Messenger */
6038c2ecf20Sopenharmony_ci	{USB_DEVICE(0x046D, 0x08F5), .driver_info = BRIDGE_ST6422 },	/* QuickCam Communicate */
6048c2ecf20Sopenharmony_ci	{USB_DEVICE(0x046D, 0x08F6), .driver_info = BRIDGE_ST6422 },	/* QuickCam Messenger (new) */
6058c2ecf20Sopenharmony_ci	{}
6068c2ecf20Sopenharmony_ci};
6078c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, device_table);
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci/* -- device connect -- */
6108c2ecf20Sopenharmony_cistatic int sd_probe(struct usb_interface *intf,
6118c2ecf20Sopenharmony_ci			const struct usb_device_id *id)
6128c2ecf20Sopenharmony_ci{
6138c2ecf20Sopenharmony_ci	return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
6148c2ecf20Sopenharmony_ci			       THIS_MODULE);
6158c2ecf20Sopenharmony_ci}
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_cistatic void sd_disconnect(struct usb_interface *intf)
6188c2ecf20Sopenharmony_ci{
6198c2ecf20Sopenharmony_ci	struct gspca_dev *gspca_dev = usb_get_intfdata(intf);
6208c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
6218c2ecf20Sopenharmony_ci	void *priv = sd->sensor_priv;
6228c2ecf20Sopenharmony_ci	gspca_dbg(gspca_dev, D_PROBE, "Disconnecting the stv06xx device\n");
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	sd->sensor = NULL;
6258c2ecf20Sopenharmony_ci	gspca_disconnect(intf);
6268c2ecf20Sopenharmony_ci	kfree(priv);
6278c2ecf20Sopenharmony_ci}
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_cistatic struct usb_driver sd_driver = {
6308c2ecf20Sopenharmony_ci	.name = MODULE_NAME,
6318c2ecf20Sopenharmony_ci	.id_table = device_table,
6328c2ecf20Sopenharmony_ci	.probe = sd_probe,
6338c2ecf20Sopenharmony_ci	.disconnect = sd_disconnect,
6348c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
6358c2ecf20Sopenharmony_ci	.suspend = gspca_suspend,
6368c2ecf20Sopenharmony_ci	.resume = gspca_resume,
6378c2ecf20Sopenharmony_ci	.reset_resume = gspca_resume,
6388c2ecf20Sopenharmony_ci#endif
6398c2ecf20Sopenharmony_ci};
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_cimodule_usb_driver(sd_driver);
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_cimodule_param(dump_bridge, bool, S_IRUGO | S_IWUSR);
6448c2ecf20Sopenharmony_ciMODULE_PARM_DESC(dump_bridge, "Dumps all usb bridge registers at startup");
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_cimodule_param(dump_sensor, bool, S_IRUGO | S_IWUSR);
6478c2ecf20Sopenharmony_ciMODULE_PARM_DESC(dump_sensor, "Dumps all sensor registers at startup");
648