// 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
0 x0001
#define SQ930_CTRL_I2C_IO
0 x0002
#define SQ930_CTRL_GPIO
0 x0005
#define SQ930_CTRL_CAP_START
0 x0010
#define SQ930_CTRL_CAP_STOP
0 x0011
#define SQ930_CTRL_SET_EXPOSURE
0 x001d
#define SQ930_CTRL_RESET
0 x001e
#define SQ930_CTRL_GET_DEV_INFO
0 x001f
/* gpio 1 (8..15) */
#define SQ930_GPIO_DFL_I2C_SDA
0 x0001
#define SQ930_GPIO_DFL_I2C_SCL
0 x0002
#define SQ930_GPIO_RSTBAR
0 x0004
#define SQ930_GPIO_EXTRA1
0 x0040
#define SQ930_GPIO_EXTRA2
0 x0080
/* gpio 3 (24..31) */
#define SQ930_GPIO_POWER
0 x0200
#define SQ930_GPIO_DFL_LED
0 x1000
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[] = {
{
0 x0354,
0 x00}, {
0 x03fa,
0 x00}, {
0 xf800,
0 x02}, {
0 xf801,
0 xce},
{
0 xf802,
0 xc1}, {
0 xf804,
0 x00}, {
0 xf808,
0 x00}, {
0 xf809,
0 x0e},
{
0 xf80a,
0 x01}, {
0 xf80b,
0 xee}, {
0 xf807,
0 x60}, {
0 xf80c,
0 x02},
{
0 xf80d,
0 xf0}, {
0 xf80e,
0 x03}, {
0 xf80f,
0 x0a}, {
0 xf81c,
0 x02},
{
0 xf81d,
0 xf0}, {
0 xf81e,
0 x03}, {
0 xf81f,
0 x0a}, {
0 xf83a,
0 x00},
{
0 xf83b,
0 x10}, {
0 xf83c,
0 x00}, {
0 xf83d,
0 x4e}, {
0 xf810,
0 x04},
{
0 xf811,
0 x00}, {
0 xf812,
0 x02}, {
0 xf813,
0 x10}, {
0 xf803,
0 x00},
{
0 xf814,
0 x01}, {
0 xf815,
0 x18}, {
0 xf816,
0 x00}, {
0 xf817,
0 x48},
{
0 xf818,
0 x00}, {
0 xf819,
0 x25}, {
0 xf81a,
0 x00}, {
0 xf81b,
0 x3c},
{
0 xf82f,
0 x03}, {
0 xf820,
0 xff}, {
0 xf821,
0 x0d}, {
0 xf822,
0 xff},
{
0 xf823,
0 x07}, {
0 xf824,
0 xff}, {
0 xf825,
0 x03}, {
0 xf826,
0 xff},
{
0 xf827,
0 x06}, {
0 xf828,
0 xff}, {
0 xf829,
0 x03}, {
0 xf82a,
0 xff},
{
0 xf82b,
0 x0c}, {
0 xf82c,
0 xfd}, {
0 xf82d,
0 x01}, {
0 xf82e,
0 x00},
{
0 xf830,
0 x00}, {
0 xf831,
0 x47}, {
0 xf832,
0 x00}, {
0 xf833,
0 x00},
{
0 xf850,
0 x00}, {
0 xf851,
0 x00}, {
0 xf852,
0 x00}, {
0 xf853,
0 x24},
{
0 xf854,
0 x00}, {
0 xf855,
0 x18}, {
0 xf856,
0 x00}, {
0 xf857,
0 x3c},
{
0 xf858,
0 x00}, {
0 xf859,
0 x0c}, {
0 xf85a,
0 x00}, {
0 xf85b,
0 x30},
{
0 xf85c,
0 x00}, {
0 xf85d,
0 x0c}, {
0 xf85e,
0 x00}, {
0 xf85f,
0 x30},
{
0 xf860,
0 x00}, {
0 xf861,
0 x48}, {
0 xf862,
0 x01}, {
0 xf863,
0 xdc},
{
0 xf864,
0 xff}, {
0 xf865,
0 x98}, {
0 xf866,
0 xff}, {
0 xf867,
0 xc0},
{
0 xf868,
0 xff}, {
0 xf869,
0 x70}, {
0 xf86c,
0 xff}, {
0 xf86d,
0 x00},
{
0 xf86a,
0 xff}, {
0 xf86b,
0 x48}, {
0 xf86e,
0 xff}, {
0 xf86f,
0 x00},
{
0 xf870,
0 x01}, {
0 xf871,
0 xdb}, {
0 xf872,
0 x01}, {
0 xf873,
0 xfa},
{
0 xf874,
0 x01}, {
0 xf875,
0 xdb}, {
0 xf876,
0 x01}, {
0 xf877,
0 xfa},
{
0 xf878,
0 x0f}, {
0 xf879,
0 x0f}, {
0 xf87a,
0 xff}, {
0 xf87b,
0 xff},
{
0 xf800,
0 x03}
};
static const struct ucbus_write_cmd icx098bq_start_1[] = {
{
0 xf5f0,
0 x00}, {
0 xf5f1,
0 xcd}, {
0 xf5f2,
0 x80}, {
0 xf5f3,
0 x80},
{
0 xf5f4,
0 xc0},
{
0 xf5f0,
0 x49}, {
0 xf5f1,
0 xcd}, {
0 xf5f2,
0 x80}, {
0 xf5f3,
0 x80},
{
0 xf5f4,
0 xc0},
{
0 xf5fa,
0 x00}, {
0 xf5f6,
0 x00}, {
0 xf5f7,
0 x00}, {
0 xf5f8,
0 x00},
{
0 xf5f9,
0 x00}
};
static const struct ucbus_write_cmd icx098bq_start_2[] = {
{
0 xf800,
0 x02}, {
0 xf807,
0 xff}, {
0 xf805,
0 x82}, {
0 xf806,
0 x00},
{
0 xf807,
0 x7f}, {
0 xf800,
0 x03},
{
0 xf800,
0 x02}, {
0 xf807,
0 xff}, {
0 xf805,
0 x40}, {
0 xf806,
0 x00},
{
0 xf807,
0 x7f}, {
0 xf800,
0 x03},
{
0 xf800,
0 x02}, {
0 xf807,
0 xff}, {
0 xf805,
0 xcf}, {
0 xf806,
0 xd0},
{
0 xf807,
0 x7f}, {
0 xf800,
0 x03},
{
0 xf800,
0 x02}, {
0 xf807,
0 xff}, {
0 xf805,
0 x00}, {
0 xf806,
0 x00},
{
0 xf807,
0 x7f}, {
0 xf800,
0 x03}
};
static const struct ucbus_write_cmd lz24bp_start_0[] = {
{
0 x0354,
0 x00}, {
0 x03fa,
0 x00}, {
0 xf800,
0 x02}, {
0 xf801,
0 xbe},
{
0 xf802,
0 xc6}, {
0 xf804,
0 x00}, {
0 xf808,
0 x00}, {
0 xf809,
0 x06},
{
0 xf80a,
0 x01}, {
0 xf80b,
0 xfe}, {
0 xf807,
0 x84}, {
0 xf80c,
0 x02},
{
0 xf80d,
0 xf7}, {
0 xf80e,
0 x03}, {
0 xf80f,
0 x0b}, {
0 xf81c,
0 x00},
{
0 xf81d,
0 x49}, {
0 xf81e,
0 x03}, {
0 xf81f,
0 x0b}, {
0 xf83a,
0 x00},
{
0 xf83b,
0 x01}, {
0 xf83c,
0 x00}, {
0 xf83d,
0 x6b}, {
0 xf810,
0 x03},
{
0 xf811,
0 x10}, {
0 xf812,
0 x02}, {
0 xf813,
0 x6f}, {
0 xf803,
0 x00},
{
0 xf814,
0 x00}, {
0 xf815,
0 x44}, {
0 xf816,
0 x00}, {
0 xf817,
0 x48},
{
0 xf818,
0 x00}, {
0 xf819,
0 x25}, {
0 xf81a,
0 x00}, {
0 xf81b,
0 x3c},
{
0 xf82f,
0 x03}, {
0 xf820,
0 xff}, {
0 xf821,
0 x0d}, {
0 xf822,
0 xff},
{
0 xf823,
0 x07}, {
0 xf824,
0 xfd}, {
0 xf825,
0 x07}, {
0 xf826,
0 xf0},
{
0 xf827,
0 x0c}, {
0 xf828,
0 xff}, {
0 xf829,
0 x03}, {
0 xf82a,
0 xff},
{
0 xf82b,
0 x0c}, {
0 xf82c,
0 xfc}, {
0 xf82d,
0 x01}, {
0 xf82e,
0 x00},
{
0 xf830,
0 x00}, {
0 xf831,
0 x47}, {
0 xf832,
0 x00}, {
0 xf833,
0 x00},
{
0 xf850,
0 x00}, {
0 xf851,
0 x00}, {
0 xf852,
0 x00}, {
0 xf853,
0 x24},
{
0 xf854,
0 x00}, {
0 xf855,
0 x0c}, {
0 xf856,
0 x00}, {
0 xf857,
0 x30},
{
0 xf858,
0 x00}, {
0 xf859,
0 x18}, {
0 xf85a,
0 x00}, {
0 xf85b,
0 x3c},
{
0 xf85c,
0 x00}, {
0 xf85d,
0 x18}, {
0 xf85e,
0 x00}, {
0 xf85f,
0 x3c},
{
0 xf860,
0 xff}, {
0 xf861,
0 x37}, {
0 xf862,
0 xff}, {
0 xf863,
0 x1d},
{
0 xf864,
0 xff}, {
0 xf865,
0 x98}, {
0 xf866,
0 xff}, {
0 xf867,
0 xc0},
{
0 xf868,
0 x00}, {
0 xf869,
0 x37}, {
0 xf86c,
0 x02}, {
0 xf86d,
0 x1d},
{
0 xf86a,
0 x00}, {
0 xf86b,
0 x37}, {
0 xf86e,
0 x02}, {
0 xf86f,
0 x1d},
{
0 xf870,
0 x01}, {
0 xf871,
0 xc6}, {
0 xf872,
0 x02}, {
0 xf873,
0 x04},
{
0 xf874,
0 x01}, {
0 xf875,
0 xc6}, {
0 xf876,
0 x02}, {
0 xf877,
0 x04},
{
0 xf878,
0 x0f}, {
0 xf879,
0 x0f}, {
0 xf87a,
0 xff}, {
0 xf87b,
0 xff},
{
0 xf800,
0 x03}
};
static const struct ucbus_write_cmd lz24bp_start_1_gen[] = {
{
0 xf5f0,
0 x00}, {
0 xf5f1,
0 xff}, {
0 xf5f2,
0 x80}, {
0 xf5f3,
0 x80},
{
0 xf5f4,
0 xb3},
{
0 xf5f0,
0 x40}, {
0 xf5f1,
0 xff}, {
0 xf5f2,
0 x80}, {
0 xf5f3,
0 x80},
{
0 xf5f4,
0 xb3},
{
0 xf5fa,
0 x00}, {
0 xf5f6,
0 x00}, {
0 xf5f7,
0 x00}, {
0 xf5f8,
0 x00},
{
0 xf5f9,
0 x00}
};
static const struct ucbus_write_cmd lz24bp_start_1_clm[] = {
{
0 xf5f0,
0 x00}, {
0 xf5f1,
0 xff}, {
0 xf5f2,
0 x88}, {
0 xf5f3,
0 x88},
{
0 xf5f4,
0 xc0},
{
0 xf5f0,
0 x40}, {
0 xf5f1,
0 xff}, {
0 xf5f2,
0 x88}, {
0 xf5f3,
0 x88},
{
0 xf5f4,
0 xc0},
{
0 xf5fa,
0 x00}, {
0 xf5f6,
0 x00}, {
0 xf5f7,
0 x00}, {
0 xf5f8,
0 x00},
{
0 xf5f9,
0 x00}
};
static const struct ucbus_write_cmd lz24bp_start_2[] = {
{
0 xf800,
0 x02}, {
0 xf807,
0 xff}, {
0 xf805,
0 x80}, {
0 xf806,
0 x00},
{
0 xf807,
0 x7f}, {
0 xf800,
0 x03},
{
0 xf800,
0 x02}, {
0 xf807,
0 xff}, {
0 xf805,
0 x4e}, {
0 xf806,
0 x00},
{
0 xf807,
0 x7f}, {
0 xf800,
0 x03},
{
0 xf800,
0 x02}, {
0 xf807,
0 xff}, {
0 xf805,
0 xc0}, {
0 xf806,
0 x48},
{
0 xf807,
0 x7f}, {
0 xf800,
0 x03},
{
0 xf800,
0 x02}, {
0 xf807,
0 xff}, {
0 xf805,
0 x00}, {
0 xf806,
0 x00},
{
0 xf807,
0 x7f}, {
0 xf800,
0 x03}
};
static const struct ucbus_write_cmd mi0360_start_0[] = {
{
0 x0354,
0 x00}, {
0 x03fa,
0 x00}, {
0 xf332,
0 xcc}, {
0 xf333,
0 xcc},
{
0 xf334,
0 xcc}, {
0 xf335,
0 xcc}, {
0 xf33f,
0 x00}
};
static const struct i2c_write_cmd mi0360_init_23[] = {
{
0 x30,
0 x0040},
/* reserved - def 0x0005 */
{
0 x31,
0 x0000},
/* reserved - def 0x002a */
{
0 x34,
0 x0100},
/* reserved - def 0x0100 */
{
0 x3d,
0 x068f},
/* reserved - def 0x068f */
};
static const struct i2c_write_cmd mi0360_init_24[] = {
{
0 x03,
0 x01e5},
/* window height */
{
0 x04,
0 x0285},
/* window width */
};
static const struct i2c_write_cmd mi0360_init_25[] = {
{
0 x35,
0 x0020},
/* global gain */
{
0 x2b,
0 x0020},
/* green1 gain */
{
0 x2c,
0 x002a},
/* blue gain */
{
0 x2d,
0 x0028},
/* red gain */
{
0 x2e,
0 x0020},
/* green2 gain */
};
static const struct ucbus_write_cmd mi0360_start_1[] = {
{
0 xf5f0,
0 x11}, {
0 xf5f1,
0 x99}, {
0 xf5f2,
0 x80}, {
0 xf5f3,
0 x80},
{
0 xf5f4,
0 xa6},
{
0 xf5f0,
0 x51}, {
0 xf5f1,
0 x99}, {
0 xf5f2,
0 x80}, {
0 xf5f3,
0 x80},
{
0 xf5f4,
0 xa6},
{
0 xf5fa,
0 x00}, {
0 xf5f6,
0 x00}, {
0 xf5f7,
0 x00}, {
0 xf5f8,
0 x00},
{
0 xf5f9,
0 x00}
};
static const struct i2c_write_cmd mi0360_start_2[] = {
{
0 x62,
0 x041d},
/* reserved - def 0x0418 */
};
static const struct i2c_write_cmd mi0360_start_3[] = {
{
0 x05,
0 x007b},
/* horiz blanking */
};
static const struct i2c_write_cmd mi0360_start_4[] = {
{
0 x05,
0 x03f5},
/* horiz blanking */
};
static const struct i2c_write_cmd mt9v111_init_0[] = {
{
0 x01,
0 x0001},
/* select IFP/SOC registers */
{
0 x06,
0 x300c},
/* operating mode control */
{
0 x08,
0 xcc00},
/* output format control (RGB) */
{
0 x01,
0 x0004},
/* select sensor core registers */
};
static const struct i2c_write_cmd mt9v111_init_1[] = {
{
0 x03,
0 x01e5},
/* window height */
{
0 x04,
0 x0285},
/* window width */
};
static const struct i2c_write_cmd mt9v111_init_2[] = {
{
0 x30,
0 x7800},
{
0 x31,
0 x0000},
{
0 x07,
0 x3002},
/* output control */
{
0 x35,
0 x0020},
/* global gain */
{
0 x2b,
0 x0020},
/* green1 gain */
{
0 x2c,
0 x0020},
/* blue gain */
{
0 x2d,
0 x0020},
/* red gain */
{
0 x2e,
0 x0020},
/* green2 gain */
};
static const struct ucbus_write_cmd mt9v111_start_1[] = {
{
0 xf5f0,
0 x11}, {
0 xf5f1,
0 x96}, {
0 xf5f2,
0 x80}, {
0 xf5f3,
0 x80},
{
0 xf5f4,
0 xaa},
{
0 xf5f0,
0 x51}, {
0 xf5f1,
0 x96}, {
0 xf5f2,
0 x80}, {
0 xf5f3,
0 x80},
{
0 xf5f4,
0 xaa},
{
0 xf5fa,
0 x00}, {
0 xf5f6,
0 x0a}, {
0 xf5f7,
0 x0a}, {
0 xf5f8,
0 x0a},
{
0 xf5f9,
0 x0a}
};
static const struct i2c_write_cmd mt9v111_init_3[] = {
{
0 x62,
0 x0405},
};
static const struct i2c_write_cmd mt9v111_init_4[] = {
/* {0x05, 0x00ce}, */
{
0 x05,
0 x005d},
/* horizontal blanking */
};
static const struct ucbus_write_cmd ov7660_start_0[] = {
{
0 x0354,
0 x00}, {
0 x03fa,
0 x00}, {
0 xf332,
0 x00}, {
0 xf333,
0 xc0},
{
0 xf334,
0 x39}, {
0 xf335,
0 xe7}, {
0 xf33f,
0 x03}
};
static const struct ucbus_write_cmd ov9630_start_0[] = {
{
0 x0354,
0 x00}, {
0 x03fa,
0 x00}, {
0 xf332,
0 x00}, {
0 xf333,
0 x00},
{
0 xf334,
0 x3e}, {
0 xf335,
0 xf8}, {
0 xf33f,
0 x03}
};
/* 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 */
{
0 x05,
0 x1f,
0 x20,
0 x0e,
0 x00,
0 x9f,
0 x02,
0 xee,
0 x01,
0 x01,
0 x00,
0 x08,
0 x18,
0 x12,
0 x78,
0 xc8,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x0,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00} },
{
4 ,
/* Bayer 640x480 */
{
0 x01,
0 x1f,
0 x20,
0 x0e,
0 x00,
0 x9f,
0 x02,
0 xee,
0 x01,
0 x02,
0 x00,
0 x08,
0 x18,
0 x12,
0 x78,
0 xc8,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00} },
},
[SENSOR_LZ24BP] = {
{
2 ,
/* Bayer 320x240 */
{
0 x05,
0 x22,
0 x20,
0 x0e,
0 x00,
0 xa2,
0 x02,
0 xee,
0 x01,
0 x01,
0 x00,
0 x08,
0 x18,
0 x12,
0 x78,
0 xc8,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00} },
{
4 ,
/* Bayer 640x480 */
{
0 x01,
0 x22,
0 x20,
0 x0e,
0 x00,
0 xa2,
0 x02,
0 xee,
0 x01,
0 x02,
0 x00,
0 x08,
0 x18,
0 x12,
0 x78,
0 xc8,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00} },
},
[SENSOR_MI0360] = {
{
2 ,
/* Bayer 320x240 */
{
0 x05,
0 x02,
0 x20,
0 x01,
0 x20,
0 x82,
0 x02,
0 xe1,
0 x01,
0 x01,
0 x00,
0 x08,
0 x18,
0 x12,
0 x78,
0 xc8,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00} },
{
4 ,
/* Bayer 640x480 */
{
0 x01,
0 x02,
0 x20,
0 x01,
0 x20,
0 x82,
0 x02,
0 xe1,
0 x01,
0 x02,
0 x00,
0 x08,
0 x18,
0 x12,
0 x78,
0 xc8,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00} },
},
[SENSOR_MT9V111] = {
{
2 ,
/* Bayer 320x240 */
{
0 x05,
0 x02,
0 x20,
0 x01,
0 x20,
0 x82,
0 x02,
0 xe1,
0 x01,
0 x01,
0 x00,
0 x08,
0 x18,
0 x12,
0 x78,
0 xc8,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00} },
{
4 ,
/* Bayer 640x480 */
{
0 x01,
0 x02,
0 x20,
0 x01,
0 x20,
0 x82,
0 x02,
0 xe1,
0 x01,
0 x02,
0 x00,
0 x08,
0 x18,
0 x12,
0 x78,
0 xc8,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00} },
},
};
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" ,
0 x00,
0 x00,
{
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" ,
0 x00,
0 x00,
{
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" ,
0 x5d,
0 x80,
{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" ,
0 x5c,
0 x7f,
{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" ,
0 x21,
0 x00,
{
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" ,
0 x30,
0 x00,
{
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 ),
0 x0c,
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 ),
0 x0c,
/* 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 ),
0 x0c,
/* 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 &
0 xff00) | 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 ),
0 x0c,
/* 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 ),
0 x0c,
/* 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 &
0 x00ff) {
sd->gpio[
0 ] &= ~mask;
sd->gpio[
0 ] |= val;
reg_w(gspca_dev,
0 x0100 | 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,
0 x0300 | SQ930_CTRL_GPIO,
~sd->gpio[
1 ] <<
8 );
}
}
static void gpio_init(
struct sd *sd,
const u8 *gpio)
{
gpio_set(sd, *gpio++,
0 x000f);
gpio_set(sd, *gpio++,
0 x000f);
gpio_set(sd, *gpio++,
0 x000f);
gpio_set(sd, *gpio++,
0 x000f);
gpio_set(sd, *gpio,
0 x000f);
}
static void bridge_init(
struct sd *sd)
{
static const struct ucbus_write_cmd clkfreq_cmd = {
0 xf031,
0 /* SQ930_CLKFREQ_60MHZ */
};
ucbus_write(&sd->gspca_dev, &clkfreq_cmd,
1 ,
1 );
gpio_set(sd, SQ930_GPIO_POWER,
0 xff00);
}
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 ) |
0 x001c,
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[] = {
0 x00,
0 x3b,
0 xf6,
0 x01,
0 x03,
0 x02,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00
};
static const u8 cmd_011b[][
7 ] = {
{
0 x10,
0 x01,
0 x66,
0 x08,
0 x00,
0 x00,
0 x00},
{
0 x01,
0 x00,
0 x1a,
0 x04,
0 x00,
0 x00,
0 x00},
{
0 x20,
0 x00,
0 x10,
0 x04,
0 x00,
0 x00,
0 x00},
{
0 x02,
0 x01,
0 xae,
0 x01,
0 x00,
0 x00,
0 x00},
};
reg_wb(gspca_dev,
0 x001b,
0 x0000, cmd_001b,
sizeof cmd_001b);
for (i =
0 ; i < ARRAY_SIZE(cmd_011b); i++) {
reg_wb(gspca_dev,
0 x001b,
0 x0000, cmd_011b[i],
ARRAY_SIZE(cmd_011b[
0 ]));
msleep(
400 );
nwait =
20 ;
for (;;) {
reg_r(gspca_dev,
0 x031b,
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,
0 x00ff);
else
gpio_set(sd,
0 ,
0 x00ff);
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 ] = {
{
0 xf810, ppl >>
8 },
{
0 xf811, 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 ?
0 x210 :
0 x26f;
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 |=
0 x0100;
sensor = &sensor_tb[sd->sensor];
buf[i++] = sensor->i2c_addr;
/* i2c_slave_addr */
buf[i++] =
0 x08;
/* 2 * ni2c */
buf[i++] =
0 x09;
/* reg = shutter width */
buf[i++] = integclks >>
8 ;
/* val H */
buf[i++] = sensor->i2c_dum;
buf[i++] = integclks;
/* val L */
buf[i++] =
0 x35;
/* reg = global gain */
buf[i++] =
0 x00;
/* val H */
buf[i++] = sensor->i2c_dum;
buf[i++] =
0 x80 + gain /
2 ;
/* val L */
buf[i++] =
0 x00;
buf[i++] =
0 x00;
buf[i++] =
0 x00;
buf[i++] =
0 x00;
buf[i++] =
0 x83;
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 ] =
0 xff;
/* 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 ] ==
0 xf6)
/* 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,
0 x0900 | SQ930_CTRL_CAP_START,
0 x0a00 | 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,
0 x00ff);
msleep(
70 );
reg_w(gspca_dev, SQ930_CTRL_CAP_STOP,
0 x0000);
gpio_set(sd,
0 x7f,
0 x00ff);
/* 2nd start */
send_start(gspca_dev);
gpio_set(sd, SQ930_GPIO_EXTRA2 | SQ930_GPIO_RSTBAR,
0 x00ff);
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 ?
0 x0564 :
0 x0310);
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 ,
0 xfff,
1 ,
0 x356);
sd->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
V4L2_CID_GAIN,
1 ,
255 ,
1 ,
0 x8d);
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(
0 x041e,
0 x4038), ST(MI0360,
0 )},
{USB_DEVICE(
0 x041e,
0 x403c), ST(LZ24BP,
0 )},
{USB_DEVICE(
0 x041e,
0 x403d), ST(LZ24BP,
0 )},
{USB_DEVICE(
0 x041e,
0 x4041), ST(LZ24BP, Creative_live_motion)},
{USB_DEVICE(
0 x2770,
0 x930b), ST(MI0360,
0 )},
{USB_DEVICE(
0 x2770,
0 x930c), 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);
Messung V0.5 in Prozent C=97 H=95 G=95
¤ Dauer der Verarbeitung: 0.24 Sekunden
(vorverarbeitet am 2026-06-08)
¤
*© Formatika GbR, Deutschland