Path: blob/master/drivers/media/video/gspca/sq930x.c
17653 views
/*1* SQ930x subdriver2*3* Copyright (C) 2010 Jean-François Moine <http://moinejf.free.fr>4* Copyright (C) 2006 -2008 Gerard Klaver <gerard at gkall dot hobby dot nl>5* Copyright (C) 2007 Sam Revitch <[email protected]>6*7* This program is free software; you can redistribute it and/or modify8* it under the terms of the GNU General Public License as published by9* the Free Software Foundation; either version 2 of the License, or10* any later version.11*12* This program is distributed in the hope that it will be useful,13* but WITHOUT ANY WARRANTY; without even the implied warranty of14* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the15* GNU General Public License for more details.16*17* You should have received a copy of the GNU General Public License18* along with this program; if not, write to the Free Software19* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA20*/2122#define MODULE_NAME "sq930x"2324#include "gspca.h"2526MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>\n"27"Gerard Klaver <gerard at gkall dot hobby dot nl\n"28"Sam Revitch <[email protected]>");29MODULE_DESCRIPTION("GSPCA/SQ930x USB Camera Driver");30MODULE_LICENSE("GPL");3132/* Structure to hold all of our device specific stuff */33struct sd {34struct gspca_dev gspca_dev; /* !! must be the first item */3536u16 expo;37u8 gain;3839u8 do_ctrl;40u8 gpio[2];41u8 sensor;42u8 type;43#define Generic 044#define Creative_live_motion 145};46enum sensors {47SENSOR_ICX098BQ,48SENSOR_LZ24BP,49SENSOR_MI0360,50SENSOR_MT9V111, /* = MI360SOC */51SENSOR_OV7660,52SENSOR_OV9630,53};5455static int sd_setexpo(struct gspca_dev *gspca_dev, __s32 val);56static int sd_getexpo(struct gspca_dev *gspca_dev, __s32 *val);57static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val);58static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val);5960static const struct ctrl sd_ctrls[] = {61{62{63.id = V4L2_CID_EXPOSURE,64.type = V4L2_CTRL_TYPE_INTEGER,65.name = "Exposure",66.minimum = 0x0001,67.maximum = 0x0fff,68.step = 1,69#define EXPO_DEF 0x035670.default_value = EXPO_DEF,71},72.set = sd_setexpo,73.get = sd_getexpo,74},75{76{77.id = V4L2_CID_GAIN,78.type = V4L2_CTRL_TYPE_INTEGER,79.name = "Gain",80.minimum = 0x01,81.maximum = 0xff,82.step = 1,83#define GAIN_DEF 0x8d84.default_value = GAIN_DEF,85},86.set = sd_setgain,87.get = sd_getgain,88},89};9091static struct v4l2_pix_format vga_mode[] = {92{320, 240, V4L2_PIX_FMT_SRGGB8, V4L2_FIELD_NONE,93.bytesperline = 320,94.sizeimage = 320 * 240,95.colorspace = V4L2_COLORSPACE_SRGB,96.priv = 0},97{640, 480, V4L2_PIX_FMT_SRGGB8, V4L2_FIELD_NONE,98.bytesperline = 640,99.sizeimage = 640 * 480,100.colorspace = V4L2_COLORSPACE_SRGB,101.priv = 1},102};103104/* sq930x registers */105#define SQ930_CTRL_UCBUS_IO 0x0001106#define SQ930_CTRL_I2C_IO 0x0002107#define SQ930_CTRL_GPIO 0x0005108#define SQ930_CTRL_CAP_START 0x0010109#define SQ930_CTRL_CAP_STOP 0x0011110#define SQ930_CTRL_SET_EXPOSURE 0x001d111#define SQ930_CTRL_RESET 0x001e112#define SQ930_CTRL_GET_DEV_INFO 0x001f113114/* gpio 1 (8..15) */115#define SQ930_GPIO_DFL_I2C_SDA 0x0001116#define SQ930_GPIO_DFL_I2C_SCL 0x0002117#define SQ930_GPIO_RSTBAR 0x0004118#define SQ930_GPIO_EXTRA1 0x0040119#define SQ930_GPIO_EXTRA2 0x0080120/* gpio 3 (24..31) */121#define SQ930_GPIO_POWER 0x0200122#define SQ930_GPIO_DFL_LED 0x1000123124struct ucbus_write_cmd {125u16 bw_addr;126u8 bw_data;127};128struct i2c_write_cmd {129u8 reg;130u16 val;131};132133static const struct ucbus_write_cmd icx098bq_start_0[] = {134{0x0354, 0x00}, {0x03fa, 0x00}, {0xf800, 0x02}, {0xf801, 0xce},135{0xf802, 0xc1}, {0xf804, 0x00}, {0xf808, 0x00}, {0xf809, 0x0e},136{0xf80a, 0x01}, {0xf80b, 0xee}, {0xf807, 0x60}, {0xf80c, 0x02},137{0xf80d, 0xf0}, {0xf80e, 0x03}, {0xf80f, 0x0a}, {0xf81c, 0x02},138{0xf81d, 0xf0}, {0xf81e, 0x03}, {0xf81f, 0x0a}, {0xf83a, 0x00},139{0xf83b, 0x10}, {0xf83c, 0x00}, {0xf83d, 0x4e}, {0xf810, 0x04},140{0xf811, 0x00}, {0xf812, 0x02}, {0xf813, 0x10}, {0xf803, 0x00},141{0xf814, 0x01}, {0xf815, 0x18}, {0xf816, 0x00}, {0xf817, 0x48},142{0xf818, 0x00}, {0xf819, 0x25}, {0xf81a, 0x00}, {0xf81b, 0x3c},143{0xf82f, 0x03}, {0xf820, 0xff}, {0xf821, 0x0d}, {0xf822, 0xff},144{0xf823, 0x07}, {0xf824, 0xff}, {0xf825, 0x03}, {0xf826, 0xff},145{0xf827, 0x06}, {0xf828, 0xff}, {0xf829, 0x03}, {0xf82a, 0xff},146{0xf82b, 0x0c}, {0xf82c, 0xfd}, {0xf82d, 0x01}, {0xf82e, 0x00},147{0xf830, 0x00}, {0xf831, 0x47}, {0xf832, 0x00}, {0xf833, 0x00},148{0xf850, 0x00}, {0xf851, 0x00}, {0xf852, 0x00}, {0xf853, 0x24},149{0xf854, 0x00}, {0xf855, 0x18}, {0xf856, 0x00}, {0xf857, 0x3c},150{0xf858, 0x00}, {0xf859, 0x0c}, {0xf85a, 0x00}, {0xf85b, 0x30},151{0xf85c, 0x00}, {0xf85d, 0x0c}, {0xf85e, 0x00}, {0xf85f, 0x30},152{0xf860, 0x00}, {0xf861, 0x48}, {0xf862, 0x01}, {0xf863, 0xdc},153{0xf864, 0xff}, {0xf865, 0x98}, {0xf866, 0xff}, {0xf867, 0xc0},154{0xf868, 0xff}, {0xf869, 0x70}, {0xf86c, 0xff}, {0xf86d, 0x00},155{0xf86a, 0xff}, {0xf86b, 0x48}, {0xf86e, 0xff}, {0xf86f, 0x00},156{0xf870, 0x01}, {0xf871, 0xdb}, {0xf872, 0x01}, {0xf873, 0xfa},157{0xf874, 0x01}, {0xf875, 0xdb}, {0xf876, 0x01}, {0xf877, 0xfa},158{0xf878, 0x0f}, {0xf879, 0x0f}, {0xf87a, 0xff}, {0xf87b, 0xff},159{0xf800, 0x03}160};161static const struct ucbus_write_cmd icx098bq_start_1[] = {162{0xf5f0, 0x00}, {0xf5f1, 0xcd}, {0xf5f2, 0x80}, {0xf5f3, 0x80},163{0xf5f4, 0xc0},164{0xf5f0, 0x49}, {0xf5f1, 0xcd}, {0xf5f2, 0x80}, {0xf5f3, 0x80},165{0xf5f4, 0xc0},166{0xf5fa, 0x00}, {0xf5f6, 0x00}, {0xf5f7, 0x00}, {0xf5f8, 0x00},167{0xf5f9, 0x00}168};169170static const struct ucbus_write_cmd icx098bq_start_2[] = {171{0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x82}, {0xf806, 0x00},172{0xf807, 0x7f}, {0xf800, 0x03},173{0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x40}, {0xf806, 0x00},174{0xf807, 0x7f}, {0xf800, 0x03},175{0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0xcf}, {0xf806, 0xd0},176{0xf807, 0x7f}, {0xf800, 0x03},177{0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x00}, {0xf806, 0x00},178{0xf807, 0x7f}, {0xf800, 0x03}179};180181static const struct ucbus_write_cmd lz24bp_start_0[] = {182{0x0354, 0x00}, {0x03fa, 0x00}, {0xf800, 0x02}, {0xf801, 0xbe},183{0xf802, 0xc6}, {0xf804, 0x00}, {0xf808, 0x00}, {0xf809, 0x06},184{0xf80a, 0x01}, {0xf80b, 0xfe}, {0xf807, 0x84}, {0xf80c, 0x02},185{0xf80d, 0xf7}, {0xf80e, 0x03}, {0xf80f, 0x0b}, {0xf81c, 0x00},186{0xf81d, 0x49}, {0xf81e, 0x03}, {0xf81f, 0x0b}, {0xf83a, 0x00},187{0xf83b, 0x01}, {0xf83c, 0x00}, {0xf83d, 0x6b}, {0xf810, 0x03},188{0xf811, 0x10}, {0xf812, 0x02}, {0xf813, 0x6f}, {0xf803, 0x00},189{0xf814, 0x00}, {0xf815, 0x44}, {0xf816, 0x00}, {0xf817, 0x48},190{0xf818, 0x00}, {0xf819, 0x25}, {0xf81a, 0x00}, {0xf81b, 0x3c},191{0xf82f, 0x03}, {0xf820, 0xff}, {0xf821, 0x0d}, {0xf822, 0xff},192{0xf823, 0x07}, {0xf824, 0xfd}, {0xf825, 0x07}, {0xf826, 0xf0},193{0xf827, 0x0c}, {0xf828, 0xff}, {0xf829, 0x03}, {0xf82a, 0xff},194{0xf82b, 0x0c}, {0xf82c, 0xfc}, {0xf82d, 0x01}, {0xf82e, 0x00},195{0xf830, 0x00}, {0xf831, 0x47}, {0xf832, 0x00}, {0xf833, 0x00},196{0xf850, 0x00}, {0xf851, 0x00}, {0xf852, 0x00}, {0xf853, 0x24},197{0xf854, 0x00}, {0xf855, 0x0c}, {0xf856, 0x00}, {0xf857, 0x30},198{0xf858, 0x00}, {0xf859, 0x18}, {0xf85a, 0x00}, {0xf85b, 0x3c},199{0xf85c, 0x00}, {0xf85d, 0x18}, {0xf85e, 0x00}, {0xf85f, 0x3c},200{0xf860, 0xff}, {0xf861, 0x37}, {0xf862, 0xff}, {0xf863, 0x1d},201{0xf864, 0xff}, {0xf865, 0x98}, {0xf866, 0xff}, {0xf867, 0xc0},202{0xf868, 0x00}, {0xf869, 0x37}, {0xf86c, 0x02}, {0xf86d, 0x1d},203{0xf86a, 0x00}, {0xf86b, 0x37}, {0xf86e, 0x02}, {0xf86f, 0x1d},204{0xf870, 0x01}, {0xf871, 0xc6}, {0xf872, 0x02}, {0xf873, 0x04},205{0xf874, 0x01}, {0xf875, 0xc6}, {0xf876, 0x02}, {0xf877, 0x04},206{0xf878, 0x0f}, {0xf879, 0x0f}, {0xf87a, 0xff}, {0xf87b, 0xff},207{0xf800, 0x03}208};209static const struct ucbus_write_cmd lz24bp_start_1_gen[] = {210{0xf5f0, 0x00}, {0xf5f1, 0xff}, {0xf5f2, 0x80}, {0xf5f3, 0x80},211{0xf5f4, 0xb3},212{0xf5f0, 0x40}, {0xf5f1, 0xff}, {0xf5f2, 0x80}, {0xf5f3, 0x80},213{0xf5f4, 0xb3},214{0xf5fa, 0x00}, {0xf5f6, 0x00}, {0xf5f7, 0x00}, {0xf5f8, 0x00},215{0xf5f9, 0x00}216};217218static const struct ucbus_write_cmd lz24bp_start_1_clm[] = {219{0xf5f0, 0x00}, {0xf5f1, 0xff}, {0xf5f2, 0x88}, {0xf5f3, 0x88},220{0xf5f4, 0xc0},221{0xf5f0, 0x40}, {0xf5f1, 0xff}, {0xf5f2, 0x88}, {0xf5f3, 0x88},222{0xf5f4, 0xc0},223{0xf5fa, 0x00}, {0xf5f6, 0x00}, {0xf5f7, 0x00}, {0xf5f8, 0x00},224{0xf5f9, 0x00}225};226227static const struct ucbus_write_cmd lz24bp_start_2[] = {228{0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x80}, {0xf806, 0x00},229{0xf807, 0x7f}, {0xf800, 0x03},230{0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x4e}, {0xf806, 0x00},231{0xf807, 0x7f}, {0xf800, 0x03},232{0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0xc0}, {0xf806, 0x48},233{0xf807, 0x7f}, {0xf800, 0x03},234{0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x00}, {0xf806, 0x00},235{0xf807, 0x7f}, {0xf800, 0x03}236};237238static const struct ucbus_write_cmd mi0360_start_0[] = {239{0x0354, 0x00}, {0x03fa, 0x00}, {0xf332, 0xcc}, {0xf333, 0xcc},240{0xf334, 0xcc}, {0xf335, 0xcc}, {0xf33f, 0x00}241};242static const struct i2c_write_cmd mi0360_init_23[] = {243{0x30, 0x0040}, /* reserved - def 0x0005 */244{0x31, 0x0000}, /* reserved - def 0x002a */245{0x34, 0x0100}, /* reserved - def 0x0100 */246{0x3d, 0x068f}, /* reserved - def 0x068f */247};248static const struct i2c_write_cmd mi0360_init_24[] = {249{0x03, 0x01e5}, /* window height */250{0x04, 0x0285}, /* window width */251};252static const struct i2c_write_cmd mi0360_init_25[] = {253{0x35, 0x0020}, /* global gain */254{0x2b, 0x0020}, /* green1 gain */255{0x2c, 0x002a}, /* blue gain */256{0x2d, 0x0028}, /* red gain */257{0x2e, 0x0020}, /* green2 gain */258};259static const struct ucbus_write_cmd mi0360_start_1[] = {260{0xf5f0, 0x11}, {0xf5f1, 0x99}, {0xf5f2, 0x80}, {0xf5f3, 0x80},261{0xf5f4, 0xa6},262{0xf5f0, 0x51}, {0xf5f1, 0x99}, {0xf5f2, 0x80}, {0xf5f3, 0x80},263{0xf5f4, 0xa6},264{0xf5fa, 0x00}, {0xf5f6, 0x00}, {0xf5f7, 0x00}, {0xf5f8, 0x00},265{0xf5f9, 0x00}266};267static const struct i2c_write_cmd mi0360_start_2[] = {268{0x62, 0x041d}, /* reserved - def 0x0418 */269};270static const struct i2c_write_cmd mi0360_start_3[] = {271{0x05, 0x007b}, /* horiz blanking */272};273static const struct i2c_write_cmd mi0360_start_4[] = {274{0x05, 0x03f5}, /* horiz blanking */275};276277static const struct i2c_write_cmd mt9v111_init_0[] = {278{0x01, 0x0001}, /* select IFP/SOC registers */279{0x06, 0x300c}, /* operating mode control */280{0x08, 0xcc00}, /* output format control (RGB) */281{0x01, 0x0004}, /* select sensor core registers */282};283static const struct i2c_write_cmd mt9v111_init_1[] = {284{0x03, 0x01e5}, /* window height */285{0x04, 0x0285}, /* window width */286};287static const struct i2c_write_cmd mt9v111_init_2[] = {288{0x30, 0x7800},289{0x31, 0x0000},290{0x07, 0x3002}, /* output control */291{0x35, 0x0020}, /* global gain */292{0x2b, 0x0020}, /* green1 gain */293{0x2c, 0x0020}, /* blue gain */294{0x2d, 0x0020}, /* red gain */295{0x2e, 0x0020}, /* green2 gain */296};297static const struct ucbus_write_cmd mt9v111_start_1[] = {298{0xf5f0, 0x11}, {0xf5f1, 0x96}, {0xf5f2, 0x80}, {0xf5f3, 0x80},299{0xf5f4, 0xaa},300{0xf5f0, 0x51}, {0xf5f1, 0x96}, {0xf5f2, 0x80}, {0xf5f3, 0x80},301{0xf5f4, 0xaa},302{0xf5fa, 0x00}, {0xf5f6, 0x0a}, {0xf5f7, 0x0a}, {0xf5f8, 0x0a},303{0xf5f9, 0x0a}304};305static const struct i2c_write_cmd mt9v111_init_3[] = {306{0x62, 0x0405},307};308static const struct i2c_write_cmd mt9v111_init_4[] = {309/* {0x05, 0x00ce}, */310{0x05, 0x005d}, /* horizontal blanking */311};312313static const struct ucbus_write_cmd ov7660_start_0[] = {314{0x0354, 0x00}, {0x03fa, 0x00}, {0xf332, 0x00}, {0xf333, 0xc0},315{0xf334, 0x39}, {0xf335, 0xe7}, {0xf33f, 0x03}316};317318static const struct ucbus_write_cmd ov9630_start_0[] = {319{0x0354, 0x00}, {0x03fa, 0x00}, {0xf332, 0x00}, {0xf333, 0x00},320{0xf334, 0x3e}, {0xf335, 0xf8}, {0xf33f, 0x03}321};322323/* start parameters indexed by [sensor][mode] */324static const struct cap_s {325u8 cc_sizeid;326u8 cc_bytes[32];327} capconfig[4][2] = {328[SENSOR_ICX098BQ] = {329{2, /* Bayer 320x240 */330{0x05, 0x1f, 0x20, 0x0e, 0x00, 0x9f, 0x02, 0xee,3310x01, 0x01, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,3320x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0,3330x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },334{4, /* Bayer 640x480 */335{0x01, 0x1f, 0x20, 0x0e, 0x00, 0x9f, 0x02, 0xee,3360x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,3370x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,3380x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },339},340[SENSOR_LZ24BP] = {341{2, /* Bayer 320x240 */342{0x05, 0x22, 0x20, 0x0e, 0x00, 0xa2, 0x02, 0xee,3430x01, 0x01, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,3440x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,3450x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },346{4, /* Bayer 640x480 */347{0x01, 0x22, 0x20, 0x0e, 0x00, 0xa2, 0x02, 0xee,3480x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,3490x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,3500x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },351},352[SENSOR_MI0360] = {353{2, /* Bayer 320x240 */354{0x05, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe1,3550x01, 0x01, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,3560x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,3570x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },358{4, /* Bayer 640x480 */359{0x01, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe1,3600x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,3610x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,3620x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },363},364[SENSOR_MT9V111] = {365{2, /* Bayer 320x240 */366{0x05, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe1,3670x01, 0x01, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,3680x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,3690x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },370{4, /* Bayer 640x480 */371{0x01, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe1,3720x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,3730x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,3740x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },375},376};377378struct sensor_s {379const char *name;380u8 i2c_addr;381u8 i2c_dum;382u8 gpio[5];383u8 cmd_len;384const struct ucbus_write_cmd *cmd;385};386387static const struct sensor_s sensor_tb[] = {388[SENSOR_ICX098BQ] = {389"icx098bp",3900x00, 0x00,391{0,392SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL,393SQ930_GPIO_DFL_I2C_SDA,3940,395SQ930_GPIO_RSTBAR396},3978, icx098bq_start_0398},399[SENSOR_LZ24BP] = {400"lz24bp",4010x00, 0x00,402{0,403SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL,404SQ930_GPIO_DFL_I2C_SDA,4050,406SQ930_GPIO_RSTBAR407},4088, lz24bp_start_0409},410[SENSOR_MI0360] = {411"mi0360",4120x5d, 0x80,413{SQ930_GPIO_RSTBAR,414SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL,415SQ930_GPIO_DFL_I2C_SDA,4160,4170418},4197, mi0360_start_0420},421[SENSOR_MT9V111] = {422"mt9v111",4230x5c, 0x7f,424{SQ930_GPIO_RSTBAR,425SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL,426SQ930_GPIO_DFL_I2C_SDA,4270,4280429},4307, mi0360_start_0431},432[SENSOR_OV7660] = {433"ov7660",4340x21, 0x00,435{0,436SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL,437SQ930_GPIO_DFL_I2C_SDA,4380,439SQ930_GPIO_RSTBAR440},4417, ov7660_start_0442},443[SENSOR_OV9630] = {444"ov9630",4450x30, 0x00,446{0,447SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL,448SQ930_GPIO_DFL_I2C_SDA,4490,450SQ930_GPIO_RSTBAR451},4527, ov9630_start_0453},454};455456static void reg_r(struct gspca_dev *gspca_dev,457u16 value, int len)458{459int ret;460461if (gspca_dev->usb_err < 0)462return;463ret = usb_control_msg(gspca_dev->dev,464usb_rcvctrlpipe(gspca_dev->dev, 0),4650x0c,466USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,467value, 0, gspca_dev->usb_buf, len,468500);469if (ret < 0) {470err("reg_r %04x failed %d", value, ret);471gspca_dev->usb_err = ret;472}473}474475static void reg_w(struct gspca_dev *gspca_dev, u16 value, u16 index)476{477int ret;478479if (gspca_dev->usb_err < 0)480return;481PDEBUG(D_USBO, "reg_w v: %04x i: %04x", value, index);482ret = usb_control_msg(gspca_dev->dev,483usb_sndctrlpipe(gspca_dev->dev, 0),4840x0c, /* request */485USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,486value, index, NULL, 0,487500);488msleep(30);489if (ret < 0) {490err("reg_w %04x %04x failed %d", value, index, ret);491gspca_dev->usb_err = ret;492}493}494495static void reg_wb(struct gspca_dev *gspca_dev, u16 value, u16 index,496const u8 *data, int len)497{498int ret;499500if (gspca_dev->usb_err < 0)501return;502PDEBUG(D_USBO, "reg_wb v: %04x i: %04x %02x...%02x",503value, index, *data, data[len - 1]);504memcpy(gspca_dev->usb_buf, data, len);505ret = usb_control_msg(gspca_dev->dev,506usb_sndctrlpipe(gspca_dev->dev, 0),5070x0c, /* request */508USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,509value, index, gspca_dev->usb_buf, len,5101000);511msleep(30);512if (ret < 0) {513err("reg_wb %04x %04x failed %d", value, index, ret);514gspca_dev->usb_err = ret;515}516}517518static void i2c_write(struct sd *sd,519const struct i2c_write_cmd *cmd,520int ncmds)521{522struct gspca_dev *gspca_dev = &sd->gspca_dev;523const struct sensor_s *sensor;524u16 val, idx;525u8 *buf;526int ret;527528if (gspca_dev->usb_err < 0)529return;530531sensor = &sensor_tb[sd->sensor];532533val = (sensor->i2c_addr << 8) | SQ930_CTRL_I2C_IO;534idx = (cmd->val & 0xff00) | cmd->reg;535536buf = gspca_dev->usb_buf;537*buf++ = sensor->i2c_dum;538*buf++ = cmd->val;539540while (--ncmds > 0) {541cmd++;542*buf++ = cmd->reg;543*buf++ = cmd->val >> 8;544*buf++ = sensor->i2c_dum;545*buf++ = cmd->val;546}547548PDEBUG(D_USBO, "i2c_w v: %04x i: %04x %02x...%02x",549val, idx, gspca_dev->usb_buf[0], buf[-1]);550ret = usb_control_msg(gspca_dev->dev,551usb_sndctrlpipe(gspca_dev->dev, 0),5520x0c, /* request */553USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,554val, idx,555gspca_dev->usb_buf, buf - gspca_dev->usb_buf,556500);557if (ret < 0) {558err("i2c_write failed %d", ret);559gspca_dev->usb_err = ret;560}561}562563static void ucbus_write(struct gspca_dev *gspca_dev,564const struct ucbus_write_cmd *cmd,565int ncmds,566int batchsize)567{568u8 *buf;569u16 val, idx;570int len, ret;571572if (gspca_dev->usb_err < 0)573return;574575#ifdef GSPCA_DEBUG576if ((batchsize - 1) * 3 > USB_BUF_SZ) {577err("Bug: usb_buf overflow");578gspca_dev->usb_err = -ENOMEM;579return;580}581#endif582583for (;;) {584len = ncmds;585if (len > batchsize)586len = batchsize;587ncmds -= len;588589val = (cmd->bw_addr << 8) | SQ930_CTRL_UCBUS_IO;590idx = (cmd->bw_data << 8) | (cmd->bw_addr >> 8);591592buf = gspca_dev->usb_buf;593while (--len > 0) {594cmd++;595*buf++ = cmd->bw_addr;596*buf++ = cmd->bw_addr >> 8;597*buf++ = cmd->bw_data;598}599if (buf != gspca_dev->usb_buf)600PDEBUG(D_USBO, "ucbus v: %04x i: %04x %02x...%02x",601val, idx,602gspca_dev->usb_buf[0], buf[-1]);603else604PDEBUG(D_USBO, "ucbus v: %04x i: %04x",605val, idx);606ret = usb_control_msg(gspca_dev->dev,607usb_sndctrlpipe(gspca_dev->dev, 0),6080x0c, /* request */609USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,610val, idx,611gspca_dev->usb_buf, buf - gspca_dev->usb_buf,612500);613if (ret < 0) {614err("ucbus_write failed %d", ret);615gspca_dev->usb_err = ret;616return;617}618msleep(30);619if (ncmds <= 0)620break;621cmd++;622}623}624625static void gpio_set(struct sd *sd, u16 val, u16 mask)626{627struct gspca_dev *gspca_dev = &sd->gspca_dev;628629if (mask & 0x00ff) {630sd->gpio[0] &= ~mask;631sd->gpio[0] |= val;632reg_w(gspca_dev, 0x0100 | SQ930_CTRL_GPIO,633~sd->gpio[0] << 8);634}635mask >>= 8;636val >>= 8;637if (mask) {638sd->gpio[1] &= ~mask;639sd->gpio[1] |= val;640reg_w(gspca_dev, 0x0300 | SQ930_CTRL_GPIO,641~sd->gpio[1] << 8);642}643}644645static void gpio_init(struct sd *sd,646const u8 *gpio)647{648gpio_set(sd, *gpio++, 0x000f);649gpio_set(sd, *gpio++, 0x000f);650gpio_set(sd, *gpio++, 0x000f);651gpio_set(sd, *gpio++, 0x000f);652gpio_set(sd, *gpio, 0x000f);653}654655static void bridge_init(struct sd *sd)656{657static const struct ucbus_write_cmd clkfreq_cmd = {6580xf031, 0 /* SQ930_CLKFREQ_60MHZ */659};660661ucbus_write(&sd->gspca_dev, &clkfreq_cmd, 1, 1);662663gpio_set(sd, SQ930_GPIO_POWER, 0xff00);664}665666static void cmos_probe(struct gspca_dev *gspca_dev)667{668struct sd *sd = (struct sd *) gspca_dev;669int i;670const struct sensor_s *sensor;671static const u8 probe_order[] = {672/* SENSOR_LZ24BP, (tested as ccd) */673SENSOR_OV9630,674SENSOR_MI0360,675SENSOR_OV7660,676SENSOR_MT9V111,677};678679for (i = 0; i < ARRAY_SIZE(probe_order); i++) {680sensor = &sensor_tb[probe_order[i]];681ucbus_write(&sd->gspca_dev, sensor->cmd, sensor->cmd_len, 8);682gpio_init(sd, sensor->gpio);683msleep(100);684reg_r(gspca_dev, (sensor->i2c_addr << 8) | 0x001c, 1);685msleep(100);686if (gspca_dev->usb_buf[0] != 0)687break;688}689if (i >= ARRAY_SIZE(probe_order)) {690err("Unknown sensor");691gspca_dev->usb_err = -EINVAL;692return;693}694sd->sensor = probe_order[i];695switch (sd->sensor) {696case SENSOR_OV7660:697case SENSOR_OV9630:698err("Sensor %s not yet treated", sensor_tb[sd->sensor].name);699gspca_dev->usb_err = -EINVAL;700break;701}702}703704static void mt9v111_init(struct gspca_dev *gspca_dev)705{706int i, nwait;707static const u8 cmd_001b[] = {7080x00, 0x3b, 0xf6, 0x01, 0x03, 0x02, 0x00, 0x00,7090x00, 0x00, 0x00710};711static const u8 cmd_011b[][7] = {712{0x10, 0x01, 0x66, 0x08, 0x00, 0x00, 0x00},713{0x01, 0x00, 0x1a, 0x04, 0x00, 0x00, 0x00},714{0x20, 0x00, 0x10, 0x04, 0x00, 0x00, 0x00},715{0x02, 0x01, 0xae, 0x01, 0x00, 0x00, 0x00},716};717718reg_wb(gspca_dev, 0x001b, 0x0000, cmd_001b, sizeof cmd_001b);719for (i = 0; i < ARRAY_SIZE(cmd_011b); i++) {720reg_wb(gspca_dev, 0x001b, 0x0000, cmd_011b[i],721ARRAY_SIZE(cmd_011b[0]));722msleep(400);723nwait = 20;724for (;;) {725reg_r(gspca_dev, 0x031b, 1);726if (gspca_dev->usb_buf[0] == 0727|| gspca_dev->usb_err != 0)728break;729if (--nwait < 0) {730PDEBUG(D_PROBE, "mt9v111_init timeout");731gspca_dev->usb_err = -ETIME;732return;733}734msleep(50);735}736}737}738739static void global_init(struct sd *sd, int first_time)740{741switch (sd->sensor) {742case SENSOR_ICX098BQ:743if (first_time)744ucbus_write(&sd->gspca_dev,745icx098bq_start_0,7468, 8);747gpio_init(sd, sensor_tb[sd->sensor].gpio);748break;749case SENSOR_LZ24BP:750if (sd->type != Creative_live_motion)751gpio_set(sd, SQ930_GPIO_EXTRA1, 0x00ff);752else753gpio_set(sd, 0, 0x00ff);754msleep(50);755if (first_time)756ucbus_write(&sd->gspca_dev,757lz24bp_start_0,7588, 8);759gpio_init(sd, sensor_tb[sd->sensor].gpio);760break;761case SENSOR_MI0360:762if (first_time)763ucbus_write(&sd->gspca_dev,764mi0360_start_0,765ARRAY_SIZE(mi0360_start_0),7668);767gpio_init(sd, sensor_tb[sd->sensor].gpio);768gpio_set(sd, SQ930_GPIO_EXTRA2, SQ930_GPIO_EXTRA2);769break;770default:771/* case SENSOR_MT9V111: */772if (first_time)773mt9v111_init(&sd->gspca_dev);774else775gpio_init(sd, sensor_tb[sd->sensor].gpio);776break;777}778}779780static void lz24bp_ppl(struct sd *sd, u16 ppl)781{782struct ucbus_write_cmd cmds[2] = {783{0xf810, ppl >> 8},784{0xf811, ppl}785};786787ucbus_write(&sd->gspca_dev, cmds, ARRAY_SIZE(cmds), 2);788}789790static void setexposure(struct gspca_dev *gspca_dev)791{792struct sd *sd = (struct sd *) gspca_dev;793int i, integclks, intstartclk, frameclks, min_frclk;794const struct sensor_s *sensor;795u16 cmd;796u8 buf[15];797798integclks = sd->expo;799i = 0;800cmd = SQ930_CTRL_SET_EXPOSURE;801802switch (sd->sensor) {803case SENSOR_ICX098BQ: /* ccd */804case SENSOR_LZ24BP:805min_frclk = sd->sensor == SENSOR_ICX098BQ ? 0x210 : 0x26f;806if (integclks >= min_frclk) {807intstartclk = 0;808frameclks = integclks;809} else {810intstartclk = min_frclk - integclks;811frameclks = min_frclk;812}813buf[i++] = intstartclk >> 8;814buf[i++] = intstartclk;815buf[i++] = frameclks >> 8;816buf[i++] = frameclks;817buf[i++] = sd->gain;818break;819default: /* cmos */820/* case SENSOR_MI0360: */821/* case SENSOR_MT9V111: */822cmd |= 0x0100;823sensor = &sensor_tb[sd->sensor];824buf[i++] = sensor->i2c_addr; /* i2c_slave_addr */825buf[i++] = 0x08; /* 2 * ni2c */826buf[i++] = 0x09; /* reg = shutter width */827buf[i++] = integclks >> 8; /* val H */828buf[i++] = sensor->i2c_dum;829buf[i++] = integclks; /* val L */830buf[i++] = 0x35; /* reg = global gain */831buf[i++] = 0x00; /* val H */832buf[i++] = sensor->i2c_dum;833buf[i++] = 0x80 + sd->gain / 2; /* val L */834buf[i++] = 0x00;835buf[i++] = 0x00;836buf[i++] = 0x00;837buf[i++] = 0x00;838buf[i++] = 0x83;839break;840}841reg_wb(gspca_dev, cmd, 0, buf, i);842}843844/* This function is called at probe time just before sd_init */845static int sd_config(struct gspca_dev *gspca_dev,846const struct usb_device_id *id)847{848struct sd *sd = (struct sd *) gspca_dev;849struct cam *cam = &gspca_dev->cam;850851sd->sensor = id->driver_info >> 8;852sd->type = id->driver_info;853854cam->cam_mode = vga_mode;855cam->nmodes = ARRAY_SIZE(vga_mode);856857cam->bulk = 1;858859sd->gain = GAIN_DEF;860sd->expo = EXPO_DEF;861862return 0;863}864865/* this function is called at probe and resume time */866static int sd_init(struct gspca_dev *gspca_dev)867{868struct sd *sd = (struct sd *) gspca_dev;869870sd->gpio[0] = sd->gpio[1] = 0xff; /* force gpio rewrite */871872/*fixme: is this needed for icx098bp and mi0360?873if (sd->sensor != SENSOR_LZ24BP)874reg_w(gspca_dev, SQ930_CTRL_RESET, 0x0000);875*/876877reg_r(gspca_dev, SQ930_CTRL_GET_DEV_INFO, 8);878if (gspca_dev->usb_err < 0)879return gspca_dev->usb_err;880881/* it returns:882* 03 00 12 93 0b f6 c9 00 live! ultra883* 03 00 07 93 0b f6 ca 00 live! ultra for notebook884* 03 00 12 93 0b fe c8 00 Trust WB-3500T885* 02 00 06 93 0b fe c8 00 Joy-IT 318S886* 03 00 12 93 0b f6 cf 00 icam tracer - sensor icx098bq887* 02 00 12 93 0b fe cf 00 ProQ Motion Webcam888*889* byte890* 0: 02 = usb 1.0 (12Mbit) / 03 = usb2.0 (480Mbit)891* 1: 00892* 2: 06 / 07 / 12 = mode webcam? firmware??893* 3: 93 chip = 930b (930b or 930c)894* 4: 0b895* 5: f6 = cdd (icx098bq, lz24bp) / fe or de = cmos (i2c) (other sensors)896* 6: c8 / c9 / ca / cf = mode webcam?, sensor? webcam?897* 7: 00898*/899PDEBUG(D_PROBE, "info: %02x %02x %02x %02x %02x %02x %02x %02x",900gspca_dev->usb_buf[0],901gspca_dev->usb_buf[1],902gspca_dev->usb_buf[2],903gspca_dev->usb_buf[3],904gspca_dev->usb_buf[4],905gspca_dev->usb_buf[5],906gspca_dev->usb_buf[6],907gspca_dev->usb_buf[7]);908909bridge_init(sd);910911if (sd->sensor == SENSOR_MI0360) {912913/* no sensor probe for icam tracer */914if (gspca_dev->usb_buf[5] == 0xf6) /* if ccd */915sd->sensor = SENSOR_ICX098BQ;916else917cmos_probe(gspca_dev);918}919if (gspca_dev->usb_err >= 0) {920PDEBUG(D_PROBE, "Sensor %s", sensor_tb[sd->sensor].name);921global_init(sd, 1);922}923return gspca_dev->usb_err;924}925926/* send the start/stop commands to the webcam */927static void send_start(struct gspca_dev *gspca_dev)928{929struct sd *sd = (struct sd *) gspca_dev;930const struct cap_s *cap;931int mode;932933mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;934cap = &capconfig[sd->sensor][mode];935reg_wb(gspca_dev, 0x0900 | SQ930_CTRL_CAP_START,9360x0a00 | cap->cc_sizeid,937cap->cc_bytes, 32);938}939940static void send_stop(struct gspca_dev *gspca_dev)941{942reg_w(gspca_dev, SQ930_CTRL_CAP_STOP, 0);943}944945/* function called at start time before URB creation */946static int sd_isoc_init(struct gspca_dev *gspca_dev)947{948struct sd *sd = (struct sd *) gspca_dev;949950gspca_dev->cam.bulk_nurbs = 1; /* there must be one URB only */951sd->do_ctrl = 0;952gspca_dev->cam.bulk_size = gspca_dev->width * gspca_dev->height + 8;953return 0;954}955956/* start the capture */957static int sd_start(struct gspca_dev *gspca_dev)958{959struct sd *sd = (struct sd *) gspca_dev;960int mode;961962bridge_init(sd);963global_init(sd, 0);964msleep(100);965966switch (sd->sensor) {967case SENSOR_ICX098BQ:968ucbus_write(gspca_dev, icx098bq_start_0,969ARRAY_SIZE(icx098bq_start_0),9708);971ucbus_write(gspca_dev, icx098bq_start_1,972ARRAY_SIZE(icx098bq_start_1),9735);974ucbus_write(gspca_dev, icx098bq_start_2,975ARRAY_SIZE(icx098bq_start_2),9766);977msleep(50);978979/* 1st start */980send_start(gspca_dev);981gpio_set(sd, SQ930_GPIO_EXTRA2 | SQ930_GPIO_RSTBAR, 0x00ff);982msleep(70);983reg_w(gspca_dev, SQ930_CTRL_CAP_STOP, 0x0000);984gpio_set(sd, 0x7f, 0x00ff);985986/* 2nd start */987send_start(gspca_dev);988gpio_set(sd, SQ930_GPIO_EXTRA2 | SQ930_GPIO_RSTBAR, 0x00ff);989goto out;990case SENSOR_LZ24BP:991ucbus_write(gspca_dev, lz24bp_start_0,992ARRAY_SIZE(lz24bp_start_0),9938);994if (sd->type != Creative_live_motion)995ucbus_write(gspca_dev, lz24bp_start_1_gen,996ARRAY_SIZE(lz24bp_start_1_gen),9975);998else999ucbus_write(gspca_dev, lz24bp_start_1_clm,1000ARRAY_SIZE(lz24bp_start_1_clm),10015);1002ucbus_write(gspca_dev, lz24bp_start_2,1003ARRAY_SIZE(lz24bp_start_2),10046);1005mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;1006lz24bp_ppl(sd, mode == 1 ? 0x0564 : 0x0310);1007msleep(10);1008break;1009case SENSOR_MI0360:1010ucbus_write(gspca_dev, mi0360_start_0,1011ARRAY_SIZE(mi0360_start_0),10128);1013i2c_write(sd, mi0360_init_23,1014ARRAY_SIZE(mi0360_init_23));1015i2c_write(sd, mi0360_init_24,1016ARRAY_SIZE(mi0360_init_24));1017i2c_write(sd, mi0360_init_25,1018ARRAY_SIZE(mi0360_init_25));1019ucbus_write(gspca_dev, mi0360_start_1,1020ARRAY_SIZE(mi0360_start_1),10215);1022i2c_write(sd, mi0360_start_2,1023ARRAY_SIZE(mi0360_start_2));1024i2c_write(sd, mi0360_start_3,1025ARRAY_SIZE(mi0360_start_3));10261027/* 1st start */1028send_start(gspca_dev);1029msleep(60);1030send_stop(gspca_dev);10311032i2c_write(sd,1033mi0360_start_4, ARRAY_SIZE(mi0360_start_4));1034break;1035default:1036/* case SENSOR_MT9V111: */1037ucbus_write(gspca_dev, mi0360_start_0,1038ARRAY_SIZE(mi0360_start_0),10398);1040i2c_write(sd, mt9v111_init_0,1041ARRAY_SIZE(mt9v111_init_0));1042i2c_write(sd, mt9v111_init_1,1043ARRAY_SIZE(mt9v111_init_1));1044i2c_write(sd, mt9v111_init_2,1045ARRAY_SIZE(mt9v111_init_2));1046ucbus_write(gspca_dev, mt9v111_start_1,1047ARRAY_SIZE(mt9v111_start_1),10485);1049i2c_write(sd, mt9v111_init_3,1050ARRAY_SIZE(mt9v111_init_3));1051i2c_write(sd, mt9v111_init_4,1052ARRAY_SIZE(mt9v111_init_4));1053break;1054}10551056send_start(gspca_dev);1057out:1058msleep(1000);10591060if (sd->sensor == SENSOR_MT9V111)1061gpio_set(sd, SQ930_GPIO_DFL_LED, SQ930_GPIO_DFL_LED);10621063sd->do_ctrl = 1; /* set the exposure */10641065return gspca_dev->usb_err;1066}10671068static void sd_stopN(struct gspca_dev *gspca_dev)1069{1070struct sd *sd = (struct sd *) gspca_dev;10711072if (sd->sensor == SENSOR_MT9V111)1073gpio_set(sd, 0, SQ930_GPIO_DFL_LED);1074send_stop(gspca_dev);1075}10761077/* function called when the application gets a new frame */1078/* It sets the exposure if required and restart the bulk transfer. */1079static void sd_dq_callback(struct gspca_dev *gspca_dev)1080{1081struct sd *sd = (struct sd *) gspca_dev;1082int ret;10831084if (!sd->do_ctrl || gspca_dev->cam.bulk_nurbs != 0)1085return;1086sd->do_ctrl = 0;10871088setexposure(gspca_dev);10891090gspca_dev->cam.bulk_nurbs = 1;1091ret = usb_submit_urb(gspca_dev->urb[0], GFP_ATOMIC);1092if (ret < 0)1093err("sd_dq_callback() err %d", ret);10941095/* wait a little time, otherwise the webcam crashes */1096msleep(100);1097}10981099static void sd_pkt_scan(struct gspca_dev *gspca_dev,1100u8 *data, /* isoc packet */1101int len) /* iso packet length */1102{1103struct sd *sd = (struct sd *) gspca_dev;11041105if (sd->do_ctrl)1106gspca_dev->cam.bulk_nurbs = 0;1107gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0);1108gspca_frame_add(gspca_dev, INTER_PACKET, data, len - 8);1109gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);1110}11111112static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)1113{1114struct sd *sd = (struct sd *) gspca_dev;11151116sd->gain = val;1117if (gspca_dev->streaming)1118sd->do_ctrl = 1;1119return 0;1120}11211122static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val)1123{1124struct sd *sd = (struct sd *) gspca_dev;11251126*val = sd->gain;1127return 0;1128}1129static int sd_setexpo(struct gspca_dev *gspca_dev, __s32 val)1130{1131struct sd *sd = (struct sd *) gspca_dev;11321133sd->expo = val;1134if (gspca_dev->streaming)1135sd->do_ctrl = 1;1136return 0;1137}11381139static int sd_getexpo(struct gspca_dev *gspca_dev, __s32 *val)1140{1141struct sd *sd = (struct sd *) gspca_dev;11421143*val = sd->expo;1144return 0;1145}11461147/* sub-driver description */1148static const struct sd_desc sd_desc = {1149.name = MODULE_NAME,1150.ctrls = sd_ctrls,1151.nctrls = ARRAY_SIZE(sd_ctrls),1152.config = sd_config,1153.init = sd_init,1154.isoc_init = sd_isoc_init,1155.start = sd_start,1156.stopN = sd_stopN,1157.pkt_scan = sd_pkt_scan,1158.dq_callback = sd_dq_callback,1159};11601161/* Table of supported USB devices */1162#define ST(sensor, type) \1163.driver_info = (SENSOR_ ## sensor << 8) \1164| (type)1165static const struct usb_device_id device_table[] = {1166{USB_DEVICE(0x041e, 0x4038), ST(MI0360, 0)},1167{USB_DEVICE(0x041e, 0x403c), ST(LZ24BP, 0)},1168{USB_DEVICE(0x041e, 0x403d), ST(LZ24BP, 0)},1169{USB_DEVICE(0x041e, 0x4041), ST(LZ24BP, Creative_live_motion)},1170{USB_DEVICE(0x2770, 0x930b), ST(MI0360, 0)},1171{USB_DEVICE(0x2770, 0x930c), ST(MI0360, 0)},1172{}1173};1174MODULE_DEVICE_TABLE(usb, device_table);117511761177/* -- device connect -- */1178static int sd_probe(struct usb_interface *intf,1179const struct usb_device_id *id)1180{1181return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),1182THIS_MODULE);1183}11841185static struct usb_driver sd_driver = {1186.name = MODULE_NAME,1187.id_table = device_table,1188.probe = sd_probe,1189.disconnect = gspca_disconnect,1190#ifdef CONFIG_PM1191.suspend = gspca_suspend,1192.resume = gspca_resume,1193#endif1194};11951196/* -- module insert / remove -- */1197static int __init sd_mod_init(void)1198{1199return usb_register(&sd_driver);1200}1201static void __exit sd_mod_exit(void)1202{1203usb_deregister(&sd_driver);1204}12051206module_init(sd_mod_init);1207module_exit(sd_mod_exit);120812091210