| // SPDX-License-Identifier: GPL-2.0-or-later |
| /* |
| * SQ930x subdriver |
| * |
| * Copyright (C) 2010 Jean-François Moine <http://moinejf.free.fr> |
| * Copyright (C) 2006 -2008 Gerard Klaver <gerard at gkall dot hobby dot nl> |
| * Copyright (C) 2007 Sam Revitch <samr7@cs.washington.edu> |
| */ |
| |
| #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
| |
| #define MODULE_NAME "sq930x" |
| |
| #include "gspca.h" |
| |
| MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>\n" |
| "Gerard Klaver <gerard at gkall dot hobby dot nl\n" |
| "Sam Revitch <samr7@cs.washington.edu>"); |
| MODULE_DESCRIPTION("GSPCA/SQ930x USB Camera Driver"); |
| MODULE_LICENSE("GPL"); |
| |
| /* Structure to hold all of our device specific stuff */ |
| struct sd { |
| struct gspca_dev gspca_dev; /* !! must be the first item */ |
| |
| struct { /* exposure/gain control cluster */ |
| struct v4l2_ctrl *exposure; |
| struct v4l2_ctrl *gain; |
| }; |
| |
| u8 do_ctrl; |
| u8 gpio[2]; |
| u8 sensor; |
| u8 type; |
| #define Generic 0 |
| #define Creative_live_motion 1 |
| }; |
| enum sensors { |
| SENSOR_ICX098BQ, |
| SENSOR_LZ24BP, |
| SENSOR_MI0360, |
| SENSOR_MT9V111, /* = MI360SOC */ |
| SENSOR_OV7660, |
| SENSOR_OV9630, |
| }; |
| |
| static struct v4l2_pix_format vga_mode[] = { |
| {320, 240, V4L2_PIX_FMT_SRGGB8, V4L2_FIELD_NONE, |
| .bytesperline = 320, |
| .sizeimage = 320 * 240, |
| .colorspace = V4L2_COLORSPACE_SRGB, |
| .priv = 0}, |
| {640, 480, V4L2_PIX_FMT_SRGGB8, V4L2_FIELD_NONE, |
| .bytesperline = 640, |
| .sizeimage = 640 * 480, |
| .colorspace = V4L2_COLORSPACE_SRGB, |
| .priv = 1}, |
| }; |
| |
| /* sq930x registers */ |
| #define SQ930_CTRL_UCBUS_IO 0x0001 |
| #define SQ930_CTRL_I2C_IO 0x0002 |
| #define SQ930_CTRL_GPIO 0x0005 |
| #define SQ930_CTRL_CAP_START 0x0010 |
| #define SQ930_CTRL_CAP_STOP 0x0011 |
| #define SQ930_CTRL_SET_EXPOSURE 0x001d |
| #define SQ930_CTRL_RESET 0x001e |
| #define SQ930_CTRL_GET_DEV_INFO 0x001f |
| |
| /* gpio 1 (8..15) */ |
| #define SQ930_GPIO_DFL_I2C_SDA 0x0001 |
| #define SQ930_GPIO_DFL_I2C_SCL 0x0002 |
| #define SQ930_GPIO_RSTBAR 0x0004 |
| #define SQ930_GPIO_EXTRA1 0x0040 |
| #define SQ930_GPIO_EXTRA2 0x0080 |
| /* gpio 3 (24..31) */ |
| #define SQ930_GPIO_POWER 0x0200 |
| #define SQ930_GPIO_DFL_LED 0x1000 |
| |
| struct ucbus_write_cmd { |
| u16 bw_addr; |
| u8 bw_data; |
| }; |
| struct i2c_write_cmd { |
| u8 reg; |
| u16 val; |
| }; |
| |
| static const struct ucbus_write_cmd icx098bq_start_0[] = { |
| {0x0354, 0x00}, {0x03fa, 0x00}, {0xf800, 0x02}, {0xf801, 0xce}, |
| {0xf802, 0xc1}, {0xf804, 0x00}, {0xf808, 0x00}, {0xf809, 0x0e}, |
| {0xf80a, 0x01}, {0xf80b, 0xee}, {0xf807, 0x60}, {0xf80c, 0x02}, |
| {0xf80d, 0xf0}, {0xf80e, 0x03}, {0xf80f, 0x0a}, {0xf81c, 0x02}, |
| {0xf81d, 0xf0}, {0xf81e, 0x03}, {0xf81f, 0x0a}, {0xf83a, 0x00}, |
| {0xf83b, 0x10}, {0xf83c, 0x00}, {0xf83d, 0x4e}, {0xf810, 0x04}, |
| {0xf811, 0x00}, {0xf812, 0x02}, {0xf813, 0x10}, {0xf803, 0x00}, |
| {0xf814, 0x01}, {0xf815, 0x18}, {0xf816, 0x00}, {0xf817, 0x48}, |
| {0xf818, 0x00}, {0xf819, 0x25}, {0xf81a, 0x00}, {0xf81b, 0x3c}, |
| {0xf82f, 0x03}, {0xf820, 0xff}, {0xf821, 0x0d}, {0xf822, 0xff}, |
| {0xf823, 0x07}, {0xf824, 0xff}, {0xf825, 0x03}, {0xf826, 0xff}, |
| {0xf827, 0x06}, {0xf828, 0xff}, {0xf829, 0x03}, {0xf82a, 0xff}, |
| {0xf82b, 0x0c}, {0xf82c, 0xfd}, {0xf82d, 0x01}, {0xf82e, 0x00}, |
| {0xf830, 0x00}, {0xf831, 0x47}, {0xf832, 0x00}, {0xf833, 0x00}, |
| {0xf850, 0x00}, {0xf851, 0x00}, {0xf852, 0x00}, {0xf853, 0x24}, |
| {0xf854, 0x00}, {0xf855, 0x18}, {0xf856, 0x00}, {0xf857, 0x3c}, |
| {0xf858, 0x00}, {0xf859, 0x0c}, {0xf85a, 0x00}, {0xf85b, 0x30}, |
| {0xf85c, 0x00}, {0xf85d, 0x0c}, {0xf85e, 0x00}, {0xf85f, 0x30}, |
| {0xf860, 0x00}, {0xf861, 0x48}, {0xf862, 0x01}, {0xf863, 0xdc}, |
| {0xf864, 0xff}, {0xf865, 0x98}, {0xf866, 0xff}, {0xf867, 0xc0}, |
| {0xf868, 0xff}, {0xf869, 0x70}, {0xf86c, 0xff}, {0xf86d, 0x00}, |
| {0xf86a, 0xff}, {0xf86b, 0x48}, {0xf86e, 0xff}, {0xf86f, 0x00}, |
| {0xf870, 0x01}, {0xf871, 0xdb}, {0xf872, 0x01}, {0xf873, 0xfa}, |
| {0xf874, 0x01}, {0xf875, 0xdb}, {0xf876, 0x01}, {0xf877, 0xfa}, |
| {0xf878, 0x0f}, {0xf879, 0x0f}, {0xf87a, 0xff}, {0xf87b, 0xff}, |
| {0xf800, 0x03} |
| }; |
| static const struct ucbus_write_cmd icx098bq_start_1[] = { |
| {0xf5f0, 0x00}, {0xf5f1, 0xcd}, {0xf5f2, 0x80}, {0xf5f3, 0x80}, |
| {0xf5f4, 0xc0}, |
| {0xf5f0, 0x49}, {0xf5f1, 0xcd}, {0xf5f2, 0x80}, {0xf5f3, 0x80}, |
| {0xf5f4, 0xc0}, |
| {0xf5fa, 0x00}, {0xf5f6, 0x00}, {0xf5f7, 0x00}, {0xf5f8, 0x00}, |
| {0xf5f9, 0x00} |
| }; |
| |
| static const struct ucbus_write_cmd icx098bq_start_2[] = { |
| {0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x82}, {0xf806, 0x00}, |
| {0xf807, 0x7f}, {0xf800, 0x03}, |
| {0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x40}, {0xf806, 0x00}, |
| {0xf807, 0x7f}, {0xf800, 0x03}, |
| {0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0xcf}, {0xf806, 0xd0}, |
| {0xf807, 0x7f}, {0xf800, 0x03}, |
| {0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x00}, {0xf806, 0x00}, |
| {0xf807, 0x7f}, {0xf800, 0x03} |
| }; |
| |
| static const struct ucbus_write_cmd lz24bp_start_0[] = { |
| {0x0354, 0x00}, {0x03fa, 0x00}, {0xf800, 0x02}, {0xf801, 0xbe}, |
| {0xf802, 0xc6}, {0xf804, 0x00}, {0xf808, 0x00}, {0xf809, 0x06}, |
| {0xf80a, 0x01}, {0xf80b, 0xfe}, {0xf807, 0x84}, {0xf80c, 0x02}, |
| {0xf80d, 0xf7}, {0xf80e, 0x03}, {0xf80f, 0x0b}, {0xf81c, 0x00}, |
| {0xf81d, 0x49}, {0xf81e, 0x03}, {0xf81f, 0x0b}, {0xf83a, 0x00}, |
| {0xf83b, 0x01}, {0xf83c, 0x00}, {0xf83d, 0x6b}, {0xf810, 0x03}, |
| {0xf811, 0x10}, {0xf812, 0x02}, {0xf813, 0x6f}, {0xf803, 0x00}, |
| {0xf814, 0x00}, {0xf815, 0x44}, {0xf816, 0x00}, {0xf817, 0x48}, |
| {0xf818, 0x00}, {0xf819, 0x25}, {0xf81a, 0x00}, {0xf81b, 0x3c}, |
| {0xf82f, 0x03}, {0xf820, 0xff}, {0xf821, 0x0d}, {0xf822, 0xff}, |
| {0xf823, 0x07}, {0xf824, 0xfd}, {0xf825, 0x07}, {0xf826, 0xf0}, |
| {0xf827, 0x0c}, {0xf828, 0xff}, {0xf829, 0x03}, {0xf82a, 0xff}, |
| {0xf82b, 0x0c}, {0xf82c, 0xfc}, {0xf82d, 0x01}, {0xf82e, 0x00}, |
| {0xf830, 0x00}, {0xf831, 0x47}, {0xf832, 0x00}, {0xf833, 0x00}, |
| {0xf850, 0x00}, {0xf851, 0x00}, {0xf852, 0x00}, {0xf853, 0x24}, |
| {0xf854, 0x00}, {0xf855, 0x0c}, {0xf856, 0x00}, {0xf857, 0x30}, |
| {0xf858, 0x00}, {0xf859, 0x18}, {0xf85a, 0x00}, {0xf85b, 0x3c}, |
| {0xf85c, 0x00}, {0xf85d, 0x18}, {0xf85e, 0x00}, {0xf85f, 0x3c}, |
| {0xf860, 0xff}, {0xf861, 0x37}, {0xf862, 0xff}, {0xf863, 0x1d}, |
| {0xf864, 0xff}, {0xf865, 0x98}, {0xf866, 0xff}, {0xf867, 0xc0}, |
| {0xf868, 0x00}, {0xf869, 0x37}, {0xf86c, 0x02}, {0xf86d, 0x1d}, |
| {0xf86a, 0x00}, {0xf86b, 0x37}, {0xf86e, 0x02}, {0xf86f, 0x1d}, |
| {0xf870, 0x01}, {0xf871, 0xc6}, {0xf872, 0x02}, {0xf873, 0x04}, |
| {0xf874, 0x01}, {0xf875, 0xc6}, {0xf876, 0x02}, {0xf877, 0x04}, |
| {0xf878, 0x0f}, {0xf879, 0x0f}, {0xf87a, 0xff}, {0xf87b, 0xff}, |
| {0xf800, 0x03} |
| }; |
| static const struct ucbus_write_cmd lz24bp_start_1_gen[] = { |
| {0xf5f0, 0x00}, {0xf5f1, 0xff}, {0xf5f2, 0x80}, {0xf5f3, 0x80}, |
| {0xf5f4, 0xb3}, |
| {0xf5f0, 0x40}, {0xf5f1, 0xff}, {0xf5f2, 0x80}, {0xf5f3, 0x80}, |
| {0xf5f4, 0xb3}, |
| {0xf5fa, 0x00}, {0xf5f6, 0x00}, {0xf5f7, 0x00}, {0xf5f8, 0x00}, |
| {0xf5f9, 0x00} |
| }; |
| |
| static const struct ucbus_write_cmd lz24bp_start_1_clm[] = { |
| {0xf5f0, 0x00}, {0xf5f1, 0xff}, {0xf5f2, 0x88}, {0xf5f3, 0x88}, |
| {0xf5f4, 0xc0}, |
| {0xf5f0, 0x40}, {0xf5f1, 0xff}, {0xf5f2, 0x88}, {0xf5f3, 0x88}, |
| {0xf5f4, 0xc0}, |
| {0xf5fa, 0x00}, {0xf5f6, 0x00}, {0xf5f7, 0x00}, {0xf5f8, 0x00}, |
| {0xf5f9, 0x00} |
| }; |
| |
| static const struct ucbus_write_cmd lz24bp_start_2[] = { |
| {0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x80}, {0xf806, 0x00}, |
| {0xf807, 0x7f}, {0xf800, 0x03}, |
| {0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x4e}, {0xf806, 0x00}, |
| {0xf807, 0x7f}, {0xf800, 0x03}, |
| {0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0xc0}, {0xf806, 0x48}, |
| {0xf807, 0x7f}, {0xf800, 0x03}, |
| {0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x00}, {0xf806, 0x00}, |
| {0xf807, 0x7f}, {0xf800, 0x03} |
| }; |
| |
| static const struct ucbus_write_cmd mi0360_start_0[] = { |
| {0x0354, 0x00}, {0x03fa, 0x00}, {0xf332, 0xcc}, {0xf333, 0xcc}, |
| {0xf334, 0xcc}, {0xf335, 0xcc}, {0xf33f, 0x00} |
| }; |
| static const struct i2c_write_cmd mi0360_init_23[] = { |
| {0x30, 0x0040}, /* reserved - def 0x0005 */ |
| {0x31, 0x0000}, /* reserved - def 0x002a */ |
| {0x34, 0x0100}, /* reserved - def 0x0100 */ |
| {0x3d, 0x068f}, /* reserved - def 0x068f */ |
| }; |
| static const struct i2c_write_cmd mi0360_init_24[] = { |
| {0x03, 0x01e5}, /* window height */ |
| {0x04, 0x0285}, /* window width */ |
| }; |
| static const struct i2c_write_cmd mi0360_init_25[] = { |
| {0x35, 0x0020}, /* global gain */ |
| {0x2b, 0x0020}, /* green1 gain */ |
| {0x2c, 0x002a}, /* blue gain */ |
| {0x2d, 0x0028}, /* red gain */ |
| {0x2e, 0x0020}, /* green2 gain */ |
| }; |
| static const struct ucbus_write_cmd mi0360_start_1[] = { |
| {0xf5f0, 0x11}, {0xf5f1, 0x99}, {0xf5f2, 0x80}, {0xf5f3, 0x80}, |
| {0xf5f4, 0xa6}, |
| {0xf5f0, 0x51}, {0xf5f1, 0x99}, {0xf5f2, 0x80}, {0xf5f3, 0x80}, |
| {0xf5f4, 0xa6}, |
| {0xf5fa, 0x00}, {0xf5f6, 0x00}, {0xf5f7, 0x00}, {0xf5f8, 0x00}, |
| {0xf5f9, 0x00} |
| }; |
| static const struct i2c_write_cmd mi0360_start_2[] = { |
| {0x62, 0x041d}, /* reserved - def 0x0418 */ |
| }; |
| static const struct i2c_write_cmd mi0360_start_3[] = { |
| {0x05, 0x007b}, /* horiz blanking */ |
| }; |
| static const struct i2c_write_cmd mi0360_start_4[] = { |
| {0x05, 0x03f5}, /* horiz blanking */ |
| }; |
| |
| static const struct i2c_write_cmd mt9v111_init_0[] = { |
| {0x01, 0x0001}, /* select IFP/SOC registers */ |
| {0x06, 0x300c}, /* operating mode control */ |
| {0x08, 0xcc00}, /* output format control (RGB) */ |
| {0x01, 0x0004}, /* select sensor core registers */ |
| }; |
| static const struct i2c_write_cmd mt9v111_init_1[] = { |
| {0x03, 0x01e5}, /* window height */ |
| {0x04, 0x0285}, /* window width */ |
| }; |
| static const struct i2c_write_cmd mt9v111_init_2[] = { |
| {0x30, 0x7800}, |
| {0x31, 0x0000}, |
| {0x07, 0x3002}, /* output control */ |
| {0x35, 0x0020}, /* global gain */ |
| {0x2b, 0x0020}, /* green1 gain */ |
| {0x2c, 0x0020}, /* blue gain */ |
| {0x2d, 0x0020}, /* red gain */ |
| {0x2e, 0x0020}, /* green2 gain */ |
| }; |
| static const struct ucbus_write_cmd mt9v111_start_1[] = { |
| {0xf5f0, 0x11}, {0xf5f1, 0x96}, {0xf5f2, 0x80}, {0xf5f3, 0x80}, |
| {0xf5f4, 0xaa}, |
| {0xf5f0, 0x51}, {0xf5f1, 0x96}, {0xf5f2, 0x80}, {0xf5f3, 0x80}, |
| {0xf5f4, 0xaa}, |
| {0xf5fa, 0x00}, {0xf5f6, 0x0a}, {0xf5f7, 0x0a}, {0xf5f8, 0x0a}, |
| {0xf5f9, 0x0a} |
| }; |
| static const struct i2c_write_cmd mt9v111_init_3[] = { |
| {0x62, 0x0405}, |
| }; |
| static const struct i2c_write_cmd mt9v111_init_4[] = { |
| /* {0x05, 0x00ce}, */ |
| {0x05, 0x005d}, /* horizontal blanking */ |
| }; |
| |
| static const struct ucbus_write_cmd ov7660_start_0[] = { |
| {0x0354, 0x00}, {0x03fa, 0x00}, {0xf332, 0x00}, {0xf333, 0xc0}, |
| {0xf334, 0x39}, {0xf335, 0xe7}, {0xf33f, 0x03} |
| }; |
| |
| static const struct ucbus_write_cmd ov9630_start_0[] = { |
| {0x0354, 0x00}, {0x03fa, 0x00}, {0xf332, 0x00}, {0xf333, 0x00}, |
| {0xf334, 0x3e}, {0xf335, 0xf8}, {0xf33f, 0x03} |
| }; |
| |
| /* start parameters indexed by [sensor][mode] */ |
| static const struct cap_s { |
| u8 cc_sizeid; |
| u8 cc_bytes[32]; |
| } capconfig[4][2] = { |
| [SENSOR_ICX098BQ] = { |
| {2, /* Bayer 320x240 */ |
| {0x05, 0x1f, 0x20, 0x0e, 0x00, 0x9f, 0x02, 0xee, |
| 0x01, 0x01, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }, |
| {4, /* Bayer 640x480 */ |
| {0x01, 0x1f, 0x20, 0x0e, 0x00, 0x9f, 0x02, 0xee, |
| 0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }, |
| }, |
| [SENSOR_LZ24BP] = { |
| {2, /* Bayer 320x240 */ |
| {0x05, 0x22, 0x20, 0x0e, 0x00, 0xa2, 0x02, 0xee, |
| 0x01, 0x01, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }, |
| {4, /* Bayer 640x480 */ |
| {0x01, 0x22, 0x20, 0x0e, 0x00, 0xa2, 0x02, 0xee, |
| 0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }, |
| }, |
| [SENSOR_MI0360] = { |
| {2, /* Bayer 320x240 */ |
| {0x05, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe1, |
| 0x01, 0x01, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }, |
| {4, /* Bayer 640x480 */ |
| {0x01, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe1, |
| 0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }, |
| }, |
| [SENSOR_MT9V111] = { |
| {2, /* Bayer 320x240 */ |
| {0x05, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe1, |
| 0x01, 0x01, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }, |
| {4, /* Bayer 640x480 */ |
| {0x01, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe1, |
| 0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }, |
| }, |
| }; |
| |
| struct sensor_s { |
| const char *name; |
| u8 i2c_addr; |
| u8 i2c_dum; |
| u8 gpio[5]; |
| u8 cmd_len; |
| const struct ucbus_write_cmd *cmd; |
| }; |
| |
| static const struct sensor_s sensor_tb[] = { |
| [SENSOR_ICX098BQ] = { |
| "icx098bp", |
| 0x00, 0x00, |
| {0, |
| SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL, |
| SQ930_GPIO_DFL_I2C_SDA, |
| 0, |
| SQ930_GPIO_RSTBAR |
| }, |
| 8, icx098bq_start_0 |
| }, |
| [SENSOR_LZ24BP] = { |
| "lz24bp", |
| 0x00, 0x00, |
| {0, |
| SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL, |
| SQ930_GPIO_DFL_I2C_SDA, |
| 0, |
| SQ930_GPIO_RSTBAR |
| }, |
| 8, lz24bp_start_0 |
| }, |
| [SENSOR_MI0360] = { |
| "mi0360", |
| 0x5d, 0x80, |
| {SQ930_GPIO_RSTBAR, |
| SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL, |
| SQ930_GPIO_DFL_I2C_SDA, |
| 0, |
| 0 |
| }, |
| 7, mi0360_start_0 |
| }, |
| [SENSOR_MT9V111] = { |
| "mt9v111", |
| 0x5c, 0x7f, |
| {SQ930_GPIO_RSTBAR, |
| SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL, |
| SQ930_GPIO_DFL_I2C_SDA, |
| 0, |
| 0 |
| }, |
| 7, mi0360_start_0 |
| }, |
| [SENSOR_OV7660] = { |
| "ov7660", |
| 0x21, 0x00, |
| {0, |
| SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL, |
| SQ930_GPIO_DFL_I2C_SDA, |
| 0, |
| SQ930_GPIO_RSTBAR |
| }, |
| 7, ov7660_start_0 |
| }, |
| [SENSOR_OV9630] = { |
| "ov9630", |
| 0x30, 0x00, |
| {0, |
| SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL, |
| SQ930_GPIO_DFL_I2C_SDA, |
| 0, |
| SQ930_GPIO_RSTBAR |
| }, |
| 7, ov9630_start_0 |
| }, |
| }; |
| |
| static void reg_r(struct gspca_dev *gspca_dev, |
| u16 value, int len) |
| { |
| int ret; |
| |
| if (gspca_dev->usb_err < 0) |
| return; |
| ret = usb_control_msg(gspca_dev->dev, |
| usb_rcvctrlpipe(gspca_dev->dev, 0), |
| 0x0c, |
| USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, |
| value, 0, gspca_dev->usb_buf, len, |
| 500); |
| if (ret < 0) { |
| pr_err("reg_r %04x failed %d\n", value, ret); |
| gspca_dev->usb_err = ret; |
| /* |
| * Make sure the buffer is zeroed to avoid uninitialized |
| * values. |
| */ |
| memset(gspca_dev->usb_buf, 0, USB_BUF_SZ); |
| } |
| } |
| |
| static void reg_w(struct gspca_dev *gspca_dev, u16 value, u16 index) |
| { |
| int ret; |
| |
| if (gspca_dev->usb_err < 0) |
| return; |
| gspca_dbg(gspca_dev, D_USBO, "reg_w v: %04x i: %04x\n", value, index); |
| ret = usb_control_msg(gspca_dev->dev, |
| usb_sndctrlpipe(gspca_dev->dev, 0), |
| 0x0c, /* request */ |
| USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, |
| value, index, NULL, 0, |
| 500); |
| msleep(30); |
| if (ret < 0) { |
| pr_err("reg_w %04x %04x failed %d\n", value, index, ret); |
| gspca_dev->usb_err = ret; |
| } |
| } |
| |
| static void reg_wb(struct gspca_dev *gspca_dev, u16 value, u16 index, |
| const u8 *data, int len) |
| { |
| int ret; |
| |
| if (gspca_dev->usb_err < 0) |
| return; |
| gspca_dbg(gspca_dev, D_USBO, "reg_wb v: %04x i: %04x %02x...%02x\n", |
| value, index, *data, data[len - 1]); |
| memcpy(gspca_dev->usb_buf, data, len); |
| ret = usb_control_msg(gspca_dev->dev, |
| usb_sndctrlpipe(gspca_dev->dev, 0), |
| 0x0c, /* request */ |
| USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, |
| value, index, gspca_dev->usb_buf, len, |
| 1000); |
| msleep(30); |
| if (ret < 0) { |
| pr_err("reg_wb %04x %04x failed %d\n", value, index, ret); |
| gspca_dev->usb_err = ret; |
| } |
| } |
| |
| static void i2c_write(struct sd *sd, |
| const struct i2c_write_cmd *cmd, |
| int ncmds) |
| { |
| struct gspca_dev *gspca_dev = &sd->gspca_dev; |
| const struct sensor_s *sensor; |
| u16 val, idx; |
| u8 *buf; |
| int ret; |
| |
| if (gspca_dev->usb_err < 0) |
| return; |
| |
| sensor = &sensor_tb[sd->sensor]; |
| |
| val = (sensor->i2c_addr << 8) | SQ930_CTRL_I2C_IO; |
| idx = (cmd->val & 0xff00) | cmd->reg; |
| |
| buf = gspca_dev->usb_buf; |
| *buf++ = sensor->i2c_dum; |
| *buf++ = cmd->val; |
| |
| while (--ncmds > 0) { |
| cmd++; |
| *buf++ = cmd->reg; |
| *buf++ = cmd->val >> 8; |
| *buf++ = sensor->i2c_dum; |
| *buf++ = cmd->val; |
| } |
| |
| gspca_dbg(gspca_dev, D_USBO, "i2c_w v: %04x i: %04x %02x...%02x\n", |
| val, idx, gspca_dev->usb_buf[0], buf[-1]); |
| ret = usb_control_msg(gspca_dev->dev, |
| usb_sndctrlpipe(gspca_dev->dev, 0), |
| 0x0c, /* request */ |
| USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, |
| val, idx, |
| gspca_dev->usb_buf, buf - gspca_dev->usb_buf, |
| 500); |
| if (ret < 0) { |
| pr_err("i2c_write failed %d\n", ret); |
| gspca_dev->usb_err = ret; |
| } |
| } |
| |
| static void ucbus_write(struct gspca_dev *gspca_dev, |
| const struct ucbus_write_cmd *cmd, |
| int ncmds, |
| int batchsize) |
| { |
| u8 *buf; |
| u16 val, idx; |
| int len, ret; |
| |
| if (gspca_dev->usb_err < 0) |
| return; |
| |
| if ((batchsize - 1) * 3 > USB_BUF_SZ) { |
| gspca_err(gspca_dev, "Bug: usb_buf overflow\n"); |
| gspca_dev->usb_err = -ENOMEM; |
| return; |
| } |
| |
| for (;;) { |
| len = ncmds; |
| if (len > batchsize) |
| len = batchsize; |
| ncmds -= len; |
| |
| val = (cmd->bw_addr << 8) | SQ930_CTRL_UCBUS_IO; |
| idx = (cmd->bw_data << 8) | (cmd->bw_addr >> 8); |
| |
| buf = gspca_dev->usb_buf; |
| while (--len > 0) { |
| cmd++; |
| *buf++ = cmd->bw_addr; |
| *buf++ = cmd->bw_addr >> 8; |
| *buf++ = cmd->bw_data; |
| } |
| if (buf != gspca_dev->usb_buf) |
| gspca_dbg(gspca_dev, D_USBO, "ucbus v: %04x i: %04x %02x...%02x\n", |
| val, idx, |
| gspca_dev->usb_buf[0], buf[-1]); |
| else |
| gspca_dbg(gspca_dev, D_USBO, "ucbus v: %04x i: %04x\n", |
| val, idx); |
| ret = usb_control_msg(gspca_dev->dev, |
| usb_sndctrlpipe(gspca_dev->dev, 0), |
| 0x0c, /* request */ |
| USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, |
| val, idx, |
| gspca_dev->usb_buf, buf - gspca_dev->usb_buf, |
| 500); |
| if (ret < 0) { |
| pr_err("ucbus_write failed %d\n", ret); |
| gspca_dev->usb_err = ret; |
| return; |
| } |
| msleep(30); |
| if (ncmds <= 0) |
| break; |
| cmd++; |
| } |
| } |
| |
| static void gpio_set(struct sd *sd, u16 val, u16 mask) |
| { |
| struct gspca_dev *gspca_dev = &sd->gspca_dev; |
| |
| if (mask & 0x00ff) { |
| sd->gpio[0] &= ~mask; |
| sd->gpio[0] |= val; |
| reg_w(gspca_dev, 0x0100 | SQ930_CTRL_GPIO, |
| ~sd->gpio[0] << 8); |
| } |
| mask >>= 8; |
| val >>= 8; |
| if (mask) { |
| sd->gpio[1] &= ~mask; |
| sd->gpio[1] |= val; |
| reg_w(gspca_dev, 0x0300 | SQ930_CTRL_GPIO, |
| ~sd->gpio[1] << 8); |
| } |
| } |
| |
| static void gpio_init(struct sd *sd, |
| const u8 *gpio) |
| { |
| gpio_set(sd, *gpio++, 0x000f); |
| gpio_set(sd, *gpio++, 0x000f); |
| gpio_set(sd, *gpio++, 0x000f); |
| gpio_set(sd, *gpio++, 0x000f); |
| gpio_set(sd, *gpio, 0x000f); |
| } |
| |
| static void bridge_init(struct sd *sd) |
| { |
| static const struct ucbus_write_cmd clkfreq_cmd = { |
| 0xf031, 0 /* SQ930_CLKFREQ_60MHZ */ |
| }; |
| |
| ucbus_write(&sd->gspca_dev, &clkfreq_cmd, 1, 1); |
| |
| gpio_set(sd, SQ930_GPIO_POWER, 0xff00); |
| } |
| |
| static void cmos_probe(struct gspca_dev *gspca_dev) |
| { |
| struct sd *sd = (struct sd *) gspca_dev; |
| int i; |
| const struct sensor_s *sensor; |
| static const u8 probe_order[] = { |
| /* SENSOR_LZ24BP, (tested as ccd) */ |
| SENSOR_OV9630, |
| SENSOR_MI0360, |
| SENSOR_OV7660, |
| SENSOR_MT9V111, |
| }; |
| |
| for (i = 0; i < ARRAY_SIZE(probe_order); i++) { |
| sensor = &sensor_tb[probe_order[i]]; |
| ucbus_write(&sd->gspca_dev, sensor->cmd, sensor->cmd_len, 8); |
| gpio_init(sd, sensor->gpio); |
| msleep(100); |
| reg_r(gspca_dev, (sensor->i2c_addr << 8) | 0x001c, 1); |
| msleep(100); |
| if (gspca_dev->usb_buf[0] != 0) |
| break; |
| } |
| if (i >= ARRAY_SIZE(probe_order)) { |
| pr_err("Unknown sensor\n"); |
| gspca_dev->usb_err = -EINVAL; |
| return; |
| } |
| sd->sensor = probe_order[i]; |
| switch (sd->sensor) { |
| case SENSOR_OV7660: |
| case SENSOR_OV9630: |
| pr_err("Sensor %s not yet treated\n", |
| sensor_tb[sd->sensor].name); |
| gspca_dev->usb_err = -EINVAL; |
| break; |
| } |
| } |
| |
| static void mt9v111_init(struct gspca_dev *gspca_dev) |
| { |
| int i, nwait; |
| static const u8 cmd_001b[] = { |
| 0x00, 0x3b, 0xf6, 0x01, 0x03, 0x02, 0x00, 0x00, |
| 0x00, 0x00, 0x00 |
| }; |
| static const u8 cmd_011b[][7] = { |
| {0x10, 0x01, 0x66, 0x08, 0x00, 0x00, 0x00}, |
| {0x01, 0x00, 0x1a, 0x04, 0x00, 0x00, 0x00}, |
| {0x20, 0x00, 0x10, 0x04, 0x00, 0x00, 0x00}, |
| {0x02, 0x01, 0xae, 0x01, 0x00, 0x00, 0x00}, |
| }; |
| |
| reg_wb(gspca_dev, 0x001b, 0x0000, cmd_001b, sizeof cmd_001b); |
| for (i = 0; i < ARRAY_SIZE(cmd_011b); i++) { |
| reg_wb(gspca_dev, 0x001b, 0x0000, cmd_011b[i], |
| ARRAY_SIZE(cmd_011b[0])); |
| msleep(400); |
| nwait = 20; |
| for (;;) { |
| reg_r(gspca_dev, 0x031b, 1); |
| if (gspca_dev->usb_buf[0] == 0 |
| || gspca_dev->usb_err != 0) |
| break; |
| if (--nwait < 0) { |
| gspca_dbg(gspca_dev, D_PROBE, "mt9v111_init timeout\n"); |
| gspca_dev->usb_err = -ETIME; |
| return; |
| } |
| msleep(50); |
| } |
| } |
| } |
| |
| static void global_init(struct sd *sd, int first_time) |
| { |
| switch (sd->sensor) { |
| case SENSOR_ICX098BQ: |
| if (first_time) |
| ucbus_write(&sd->gspca_dev, |
| icx098bq_start_0, |
| 8, 8); |
| gpio_init(sd, sensor_tb[sd->sensor].gpio); |
| break; |
| case SENSOR_LZ24BP: |
| if (sd->type != Creative_live_motion) |
| gpio_set(sd, SQ930_GPIO_EXTRA1, 0x00ff); |
| else |
| gpio_set(sd, 0, 0x00ff); |
| msleep(50); |
| if (first_time) |
| ucbus_write(&sd->gspca_dev, |
| lz24bp_start_0, |
| 8, 8); |
| gpio_init(sd, sensor_tb[sd->sensor].gpio); |
| break; |
| case SENSOR_MI0360: |
| if (first_time) |
| ucbus_write(&sd->gspca_dev, |
| mi0360_start_0, |
| ARRAY_SIZE(mi0360_start_0), |
| 8); |
| gpio_init(sd, sensor_tb[sd->sensor].gpio); |
| gpio_set(sd, SQ930_GPIO_EXTRA2, SQ930_GPIO_EXTRA2); |
| break; |
| default: |
| /* case SENSOR_MT9V111: */ |
| if (first_time) |
| mt9v111_init(&sd->gspca_dev); |
| else |
| gpio_init(sd, sensor_tb[sd->sensor].gpio); |
| break; |
| } |
| } |
| |
| static void lz24bp_ppl(struct sd *sd, u16 ppl) |
| { |
| struct ucbus_write_cmd cmds[2] = { |
| {0xf810, ppl >> 8}, |
| {0xf811, ppl} |
| }; |
| |
| ucbus_write(&sd->gspca_dev, cmds, ARRAY_SIZE(cmds), 2); |
| } |
| |
| static void setexposure(struct gspca_dev *gspca_dev, s32 expo, s32 gain) |
| { |
| struct sd *sd = (struct sd *) gspca_dev; |
| int i, integclks, intstartclk, frameclks, min_frclk; |
| const struct sensor_s *sensor; |
| u16 cmd; |
| u8 buf[15]; |
| |
| integclks = expo; |
| i = 0; |
| cmd = SQ930_CTRL_SET_EXPOSURE; |
| |
| switch (sd->sensor) { |
| case SENSOR_ICX098BQ: /* ccd */ |
| case SENSOR_LZ24BP: |
| min_frclk = sd->sensor == SENSOR_ICX098BQ ? 0x210 : 0x26f; |
| if (integclks >= min_frclk) { |
| intstartclk = 0; |
| frameclks = integclks; |
| } else { |
| intstartclk = min_frclk - integclks; |
| frameclks = min_frclk; |
| } |
| buf[i++] = intstartclk >> 8; |
| buf[i++] = intstartclk; |
| buf[i++] = frameclks >> 8; |
| buf[i++] = frameclks; |
| buf[i++] = gain; |
| break; |
| default: /* cmos */ |
| /* case SENSOR_MI0360: */ |
| /* case SENSOR_MT9V111: */ |
| cmd |= 0x0100; |
| sensor = &sensor_tb[sd->sensor]; |
| buf[i++] = sensor->i2c_addr; /* i2c_slave_addr */ |
| buf[i++] = 0x08; /* 2 * ni2c */ |
| buf[i++] = 0x09; /* reg = shutter width */ |
| buf[i++] = integclks >> 8; /* val H */ |
| buf[i++] = sensor->i2c_dum; |
| buf[i++] = integclks; /* val L */ |
| buf[i++] = 0x35; /* reg = global gain */ |
| buf[i++] = 0x00; /* val H */ |
| buf[i++] = sensor->i2c_dum; |
| buf[i++] = 0x80 + gain / 2; /* val L */ |
| buf[i++] = 0x00; |
| buf[i++] = 0x00; |
| buf[i++] = 0x00; |
| buf[i++] = 0x00; |
| buf[i++] = 0x83; |
| break; |
| } |
| reg_wb(gspca_dev, cmd, 0, buf, i); |
| } |
| |
| /* This function is called at probe time just before sd_init */ |
| static int sd_config(struct gspca_dev *gspca_dev, |
| const struct usb_device_id *id) |
| { |
| struct sd *sd = (struct sd *) gspca_dev; |
| struct cam *cam = &gspca_dev->cam; |
| |
| sd->sensor = id->driver_info >> 8; |
| sd->type = id->driver_info; |
| |
| cam->cam_mode = vga_mode; |
| cam->nmodes = ARRAY_SIZE(vga_mode); |
| |
| cam->bulk = 1; |
| |
| return 0; |
| } |
| |
| /* this function is called at probe and resume time */ |
| static int sd_init(struct gspca_dev *gspca_dev) |
| { |
| struct sd *sd = (struct sd *) gspca_dev; |
| |
| sd->gpio[0] = sd->gpio[1] = 0xff; /* force gpio rewrite */ |
| |
| /*fixme: is this needed for icx098bp and mi0360? |
| if (sd->sensor != SENSOR_LZ24BP) |
| reg_w(gspca_dev, SQ930_CTRL_RESET, 0x0000); |
| */ |
| |
| reg_r(gspca_dev, SQ930_CTRL_GET_DEV_INFO, 8); |
| if (gspca_dev->usb_err < 0) |
| return gspca_dev->usb_err; |
| |
| /* it returns: |
| * 03 00 12 93 0b f6 c9 00 live! ultra |
| * 03 00 07 93 0b f6 ca 00 live! ultra for notebook |
| * 03 00 12 93 0b fe c8 00 Trust WB-3500T |
| * 02 00 06 93 0b fe c8 00 Joy-IT 318S |
| * 03 00 12 93 0b f6 cf 00 icam tracer - sensor icx098bq |
| * 02 00 12 93 0b fe cf 00 ProQ Motion Webcam |
| * |
| * byte |
| * 0: 02 = usb 1.0 (12Mbit) / 03 = usb2.0 (480Mbit) |
| * 1: 00 |
| * 2: 06 / 07 / 12 = mode webcam? firmware?? |
| * 3: 93 chip = 930b (930b or 930c) |
| * 4: 0b |
| * 5: f6 = cdd (icx098bq, lz24bp) / fe or de = cmos (i2c) (other sensors) |
| * 6: c8 / c9 / ca / cf = mode webcam?, sensor? webcam? |
| * 7: 00 |
| */ |
| gspca_dbg(gspca_dev, D_PROBE, "info: %*ph\n", 8, gspca_dev->usb_buf); |
| |
| bridge_init(sd); |
| |
| if (sd->sensor == SENSOR_MI0360) { |
| |
| /* no sensor probe for icam tracer */ |
| if (gspca_dev->usb_buf[5] == 0xf6) /* if ccd */ |
| sd->sensor = SENSOR_ICX098BQ; |
| else |
| cmos_probe(gspca_dev); |
| } |
| if (gspca_dev->usb_err >= 0) { |
| gspca_dbg(gspca_dev, D_PROBE, "Sensor %s\n", |
| sensor_tb[sd->sensor].name); |
| global_init(sd, 1); |
| } |
| return gspca_dev->usb_err; |
| } |
| |
| /* send the start/stop commands to the webcam */ |
| static void send_start(struct gspca_dev *gspca_dev) |
| { |
| struct sd *sd = (struct sd *) gspca_dev; |
| const struct cap_s *cap; |
| int mode; |
| |
| mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; |
| cap = &capconfig[sd->sensor][mode]; |
| reg_wb(gspca_dev, 0x0900 | SQ930_CTRL_CAP_START, |
| 0x0a00 | cap->cc_sizeid, |
| cap->cc_bytes, 32); |
| } |
| |
| static void send_stop(struct gspca_dev *gspca_dev) |
| { |
| reg_w(gspca_dev, SQ930_CTRL_CAP_STOP, 0); |
| } |
| |
| /* function called at start time before URB creation */ |
| static int sd_isoc_init(struct gspca_dev *gspca_dev) |
| { |
| struct sd *sd = (struct sd *) gspca_dev; |
| |
| gspca_dev->cam.bulk_nurbs = 1; /* there must be one URB only */ |
| sd->do_ctrl = 0; |
| gspca_dev->cam.bulk_size = gspca_dev->pixfmt.width * |
| gspca_dev->pixfmt.height + 8; |
| return 0; |
| } |
| |
| /* start the capture */ |
| static int sd_start(struct gspca_dev *gspca_dev) |
| { |
| struct sd *sd = (struct sd *) gspca_dev; |
| int mode; |
| |
| bridge_init(sd); |
| global_init(sd, 0); |
| msleep(100); |
| |
| switch (sd->sensor) { |
| case SENSOR_ICX098BQ: |
| ucbus_write(gspca_dev, icx098bq_start_0, |
| ARRAY_SIZE(icx098bq_start_0), |
| 8); |
| ucbus_write(gspca_dev, icx098bq_start_1, |
| ARRAY_SIZE(icx098bq_start_1), |
| 5); |
| ucbus_write(gspca_dev, icx098bq_start_2, |
| ARRAY_SIZE(icx098bq_start_2), |
| 6); |
| msleep(50); |
| |
| /* 1st start */ |
| send_start(gspca_dev); |
| gpio_set(sd, SQ930_GPIO_EXTRA2 | SQ930_GPIO_RSTBAR, 0x00ff); |
| msleep(70); |
| reg_w(gspca_dev, SQ930_CTRL_CAP_STOP, 0x0000); |
| gpio_set(sd, 0x7f, 0x00ff); |
| |
| /* 2nd start */ |
| send_start(gspca_dev); |
| gpio_set(sd, SQ930_GPIO_EXTRA2 | SQ930_GPIO_RSTBAR, 0x00ff); |
| goto out; |
| case SENSOR_LZ24BP: |
| ucbus_write(gspca_dev, lz24bp_start_0, |
| ARRAY_SIZE(lz24bp_start_0), |
| 8); |
| if (sd->type != Creative_live_motion) |
| ucbus_write(gspca_dev, lz24bp_start_1_gen, |
| ARRAY_SIZE(lz24bp_start_1_gen), |
| 5); |
| else |
| ucbus_write(gspca_dev, lz24bp_start_1_clm, |
| ARRAY_SIZE(lz24bp_start_1_clm), |
| 5); |
| ucbus_write(gspca_dev, lz24bp_start_2, |
| ARRAY_SIZE(lz24bp_start_2), |
| 6); |
| mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; |
| lz24bp_ppl(sd, mode == 1 ? 0x0564 : 0x0310); |
| msleep(10); |
| break; |
| case SENSOR_MI0360: |
| ucbus_write(gspca_dev, mi0360_start_0, |
| ARRAY_SIZE(mi0360_start_0), |
| 8); |
| i2c_write(sd, mi0360_init_23, |
| ARRAY_SIZE(mi0360_init_23)); |
| i2c_write(sd, mi0360_init_24, |
| ARRAY_SIZE(mi0360_init_24)); |
| i2c_write(sd, mi0360_init_25, |
| ARRAY_SIZE(mi0360_init_25)); |
| ucbus_write(gspca_dev, mi0360_start_1, |
| ARRAY_SIZE(mi0360_start_1), |
| 5); |
| i2c_write(sd, mi0360_start_2, |
| ARRAY_SIZE(mi0360_start_2)); |
| i2c_write(sd, mi0360_start_3, |
| ARRAY_SIZE(mi0360_start_3)); |
| |
| /* 1st start */ |
| send_start(gspca_dev); |
| msleep(60); |
| send_stop(gspca_dev); |
| |
| i2c_write(sd, |
| mi0360_start_4, ARRAY_SIZE(mi0360_start_4)); |
| break; |
| default: |
| /* case SENSOR_MT9V111: */ |
| ucbus_write(gspca_dev, mi0360_start_0, |
| ARRAY_SIZE(mi0360_start_0), |
| 8); |
| i2c_write(sd, mt9v111_init_0, |
| ARRAY_SIZE(mt9v111_init_0)); |
| i2c_write(sd, mt9v111_init_1, |
| ARRAY_SIZE(mt9v111_init_1)); |
| i2c_write(sd, mt9v111_init_2, |
| ARRAY_SIZE(mt9v111_init_2)); |
| ucbus_write(gspca_dev, mt9v111_start_1, |
| ARRAY_SIZE(mt9v111_start_1), |
| 5); |
| i2c_write(sd, mt9v111_init_3, |
| ARRAY_SIZE(mt9v111_init_3)); |
| i2c_write(sd, mt9v111_init_4, |
| ARRAY_SIZE(mt9v111_init_4)); |
| break; |
| } |
| |
| send_start(gspca_dev); |
| out: |
| msleep(1000); |
| |
| if (sd->sensor == SENSOR_MT9V111) |
| gpio_set(sd, SQ930_GPIO_DFL_LED, SQ930_GPIO_DFL_LED); |
| |
| sd->do_ctrl = 1; /* set the exposure */ |
| |
| return gspca_dev->usb_err; |
| } |
| |
| static void sd_stopN(struct gspca_dev *gspca_dev) |
| { |
| struct sd *sd = (struct sd *) gspca_dev; |
| |
| if (sd->sensor == SENSOR_MT9V111) |
| gpio_set(sd, 0, SQ930_GPIO_DFL_LED); |
| send_stop(gspca_dev); |
| } |
| |
| /* function called when the application gets a new frame */ |
| /* It sets the exposure if required and restart the bulk transfer. */ |
| static void sd_dq_callback(struct gspca_dev *gspca_dev) |
| { |
| struct sd *sd = (struct sd *) gspca_dev; |
| int ret; |
| |
| if (!sd->do_ctrl || gspca_dev->cam.bulk_nurbs != 0) |
| return; |
| sd->do_ctrl = 0; |
| |
| setexposure(gspca_dev, v4l2_ctrl_g_ctrl(sd->exposure), |
| v4l2_ctrl_g_ctrl(sd->gain)); |
| |
| gspca_dev->cam.bulk_nurbs = 1; |
| ret = usb_submit_urb(gspca_dev->urb[0], GFP_KERNEL); |
| if (ret < 0) |
| pr_err("sd_dq_callback() err %d\n", ret); |
| |
| /* wait a little time, otherwise the webcam crashes */ |
| msleep(100); |
| } |
| |
| static void sd_pkt_scan(struct gspca_dev *gspca_dev, |
| u8 *data, /* isoc packet */ |
| int len) /* iso packet length */ |
| { |
| struct sd *sd = (struct sd *) gspca_dev; |
| |
| if (sd->do_ctrl) |
| gspca_dev->cam.bulk_nurbs = 0; |
| gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0); |
| gspca_frame_add(gspca_dev, INTER_PACKET, data, len - 8); |
| gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); |
| } |
| |
| static int sd_s_ctrl(struct v4l2_ctrl *ctrl) |
| { |
| struct gspca_dev *gspca_dev = |
| container_of(ctrl->handler, struct gspca_dev, ctrl_handler); |
| struct sd *sd = (struct sd *) gspca_dev; |
| |
| gspca_dev->usb_err = 0; |
| |
| if (!gspca_dev->streaming) |
| return 0; |
| |
| switch (ctrl->id) { |
| case V4L2_CID_EXPOSURE: |
| setexposure(gspca_dev, ctrl->val, sd->gain->val); |
| break; |
| } |
| return gspca_dev->usb_err; |
| } |
| |
| static const struct v4l2_ctrl_ops sd_ctrl_ops = { |
| .s_ctrl = sd_s_ctrl, |
| }; |
| |
| static int sd_init_controls(struct gspca_dev *gspca_dev) |
| { |
| struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; |
| struct sd *sd = (struct sd *) gspca_dev; |
| |
| gspca_dev->vdev.ctrl_handler = hdl; |
| v4l2_ctrl_handler_init(hdl, 2); |
| sd->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, |
| V4L2_CID_EXPOSURE, 1, 0xfff, 1, 0x356); |
| sd->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, |
| V4L2_CID_GAIN, 1, 255, 1, 0x8d); |
| |
| if (hdl->error) { |
| pr_err("Could not initialize controls\n"); |
| return hdl->error; |
| } |
| v4l2_ctrl_cluster(2, &sd->exposure); |
| return 0; |
| } |
| |
| /* sub-driver description */ |
| static const struct sd_desc sd_desc = { |
| .name = MODULE_NAME, |
| .config = sd_config, |
| .init = sd_init, |
| .init_controls = sd_init_controls, |
| .isoc_init = sd_isoc_init, |
| .start = sd_start, |
| .stopN = sd_stopN, |
| .pkt_scan = sd_pkt_scan, |
| .dq_callback = sd_dq_callback, |
| }; |
| |
| /* Table of supported USB devices */ |
| #define ST(sensor, type) \ |
| .driver_info = (SENSOR_ ## sensor << 8) \ |
| | (type) |
| static const struct usb_device_id device_table[] = { |
| {USB_DEVICE(0x041e, 0x4038), ST(MI0360, 0)}, |
| {USB_DEVICE(0x041e, 0x403c), ST(LZ24BP, 0)}, |
| {USB_DEVICE(0x041e, 0x403d), ST(LZ24BP, 0)}, |
| {USB_DEVICE(0x041e, 0x4041), ST(LZ24BP, Creative_live_motion)}, |
| {USB_DEVICE(0x2770, 0x930b), ST(MI0360, 0)}, |
| {USB_DEVICE(0x2770, 0x930c), ST(MI0360, 0)}, |
| {} |
| }; |
| MODULE_DEVICE_TABLE(usb, device_table); |
| |
| |
| /* -- device connect -- */ |
| static int sd_probe(struct usb_interface *intf, |
| const struct usb_device_id *id) |
| { |
| return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), |
| THIS_MODULE); |
| } |
| |
| static struct usb_driver sd_driver = { |
| .name = MODULE_NAME, |
| .id_table = device_table, |
| .probe = sd_probe, |
| .disconnect = gspca_disconnect, |
| #ifdef CONFIG_PM |
| .suspend = gspca_suspend, |
| .resume = gspca_resume, |
| .reset_resume = gspca_resume, |
| #endif |
| }; |
| |
| module_usb_driver(sd_driver); |