// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ov534 - ov9xxx gspca driver
*
* Copyright ( C ) 2009 - 2011 Jean - Francois Moine http : //moinejf.free.fr
* Copyright ( C ) 2008 Antonio Ospite < ospite @ studenti . unina . it >
* Copyright ( C ) 2008 Jim Paris < jim @ jtan . com >
*
* Based on a prototype written by Mark Ferrell < majortrips @ gmail . com >
* USB protocol reverse engineered by Jim Paris < jim @ jtan . com >
* https : //jim.sh/svn/jim/devl/playstation/ps3/eye/test/
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#define MODULE_NAME "ov534_9"
#include "gspca.h"
#define OV534_REG_ADDRESS 0 xf1 /* sensor address */
#define OV534_REG_SUBADDR 0 xf2
#define OV534_REG_WRITE 0 xf3
#define OV534_REG_READ 0 xf4
#define OV534_REG_OPERATION 0 xf5
#define OV534_REG_STATUS 0 xf6
#define OV534_OP_WRITE_3 0 x37
#define OV534_OP_WRITE_2 0 x33
#define OV534_OP_READ_2 0 xf9
#define CTRL_TIMEOUT 500
MODULE_AUTHOR("Jean-Francois Moine <moinejf@free.fr>" );
MODULE_DESCRIPTION("GSPCA/OV534_9 USB Camera Driver" );
MODULE_LICENSE("GPL" );
/* specific webcam descriptor */
struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
__u32 last_pts;
u8 last_fid;
u8 sensor;
};
enum sensors {
SENSOR_OV965x, /* ov9657 */
SENSOR_OV971x, /* ov9712 */
SENSOR_OV562x, /* ov5621 */
SENSOR_OV361x, /* ov3610 */
NSENSORS
};
static const struct v4l2_pix_format ov965x_mode[] = {
#define QVGA_MODE 0
{320 , 240 , V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 320 ,
.sizeimage = 320 * 240 * 3 / 8 + 590 ,
.colorspace = V4L2_COLORSPACE_JPEG},
#define VGA_MODE 1
{640 , 480 , V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 640 ,
.sizeimage = 640 * 480 * 3 / 8 + 590 ,
.colorspace = V4L2_COLORSPACE_JPEG},
#define SVGA_MODE 2
{800 , 600 , V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 800 ,
.sizeimage = 800 * 600 * 3 / 8 + 590 ,
.colorspace = V4L2_COLORSPACE_JPEG},
#define XGA_MODE 3
{1024 , 768 , V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 1024 ,
.sizeimage = 1024 * 768 * 3 / 8 + 590 ,
.colorspace = V4L2_COLORSPACE_JPEG},
#define SXGA_MODE 4
{1280 , 1024 , V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 1280 ,
.sizeimage = 1280 * 1024 * 3 / 8 + 590 ,
.colorspace = V4L2_COLORSPACE_JPEG},
};
static const struct v4l2_pix_format ov971x_mode[] = {
{640 , 480 , V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
.bytesperline = 640 ,
.sizeimage = 640 * 480 ,
.colorspace = V4L2_COLORSPACE_SRGB
}
};
static const struct v4l2_pix_format ov562x_mode[] = {
{2592 , 1680 , V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
.bytesperline = 2592 ,
.sizeimage = 2592 * 1680 ,
.colorspace = V4L2_COLORSPACE_SRGB
}
};
enum ov361x {
ov361x_2048 = 0 ,
ov361x_1600,
ov361x_1024,
ov361x_640,
ov361x_320,
ov361x_160,
ov361x_last
};
static const struct v4l2_pix_format ov361x_mode[] = {
{0 x800, 0 x600, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
.bytesperline = 0 x800,
.sizeimage = 0 x800 * 0 x600,
.colorspace = V4L2_COLORSPACE_SRGB},
{1600 , 1200 , V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
.bytesperline = 1600 ,
.sizeimage = 1600 * 1200 ,
.colorspace = V4L2_COLORSPACE_SRGB},
{1024 , 768 , V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
.bytesperline = 768 ,
.sizeimage = 1024 * 768 ,
.colorspace = V4L2_COLORSPACE_SRGB},
{640 , 480 , V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
.bytesperline = 640 ,
.sizeimage = 640 * 480 ,
.colorspace = V4L2_COLORSPACE_SRGB},
{320 , 240 , V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
.bytesperline = 320 ,
.sizeimage = 320 * 240 ,
.colorspace = V4L2_COLORSPACE_SRGB},
{160 , 120 , V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
.bytesperline = 160 ,
.sizeimage = 160 * 120 ,
.colorspace = V4L2_COLORSPACE_SRGB}
};
static const u8 ov361x_start_2048[][2 ] = {
{0 x12, 0 x80},
{0 x13, 0 xcf},
{0 x14, 0 x40},
{0 x15, 0 x00},
{0 x01, 0 x80},
{0 x02, 0 x80},
{0 x04, 0 x70},
{0 x0d, 0 x40},
{0 x0f, 0 x47},
{0 x11, 0 x81},
{0 x32, 0 x36},
{0 x33, 0 x0c},
{0 x34, 0 x00},
{0 x35, 0 x90},
{0 x12, 0 x00},
{0 x17, 0 x10},
{0 x18, 0 x90},
{0 x19, 0 x00},
{0 x1a, 0 xc0},
};
static const u8 ov361x_bridge_start_2048[][2 ] = {
{0 xf1, 0 x60},
{0 x88, 0 x00},
{0 x89, 0 x08},
{0 x8a, 0 x00},
{0 x8b, 0 x06},
{0 x8c, 0 x01},
{0 x8d, 0 x10},
{0 x1c, 0 x00},
{0 x1d, 0 x48},
{0 x1d, 0 x00},
{0 x1d, 0 xff},
{0 x1c, 0 x0a},
{0 x1d, 0 x2e},
{0 x1d, 0 x1e},
};
static const u8 ov361x_start_1600[][2 ] = {
{0 x12, 0 x80},
{0 x13, 0 xcf},
{0 x14, 0 x40},
{0 x15, 0 x00},
{0 x01, 0 x80},
{0 x02, 0 x80},
{0 x04, 0 x70},
{0 x0d, 0 x40},
{0 x0f, 0 x47},
{0 x11, 0 x81},
{0 x32, 0 x36},
{0 x33, 0 x0C},
{0 x34, 0 x00},
{0 x35, 0 x90},
{0 x12, 0 x00},
{0 x17, 0 x10},
{0 x18, 0 x90},
{0 x19, 0 x00},
{0 x1a, 0 xc0},
};
static const u8 ov361x_bridge_start_1600[][2 ] = {
{0 xf1, 0 x60}, /* Hsize[7:0] */
{0 x88, 0 x00}, /* Hsize[15:8] Write Only, can't read */
{0 x89, 0 x08}, /* Vsize[7:0] */
{0 x8a, 0 x00}, /* Vsize[15:8] Write Only, can't read */
{0 x8b, 0 x06}, /* for Iso */
{0 x8c, 0 x01}, /* RAW input */
{0 x8d, 0 x10},
{0 x1c, 0 x00}, /* RAW output, Iso transfer */
{0 x1d, 0 x48},
{0 x1d, 0 x00},
{0 x1d, 0 xff},
{0 x1c, 0 x0a}, /* turn off JPEG, Iso mode */
{0 x1d, 0 x2e}, /* for Iso */
{0 x1d, 0 x1e},
};
static const u8 ov361x_start_1024[][2 ] = {
{0 x12, 0 x80},
{0 x13, 0 xcf},
{0 x14, 0 x40},
{0 x15, 0 x00},
{0 x01, 0 x80},
{0 x02, 0 x80},
{0 x04, 0 x70},
{0 x0d, 0 x40},
{0 x0f, 0 x47},
{0 x11, 0 x81},
{0 x32, 0 x36},
{0 x33, 0 x0C},
{0 x34, 0 x00},
{0 x35, 0 x90},
{0 x12, 0 x40},
{0 x17, 0 x1f},
{0 x18, 0 x5f},
{0 x19, 0 x00},
{0 x1a, 0 x68},
};
static const u8 ov361x_bridge_start_1024[][2 ] = {
{0 xf1, 0 x60}, /* Hsize[7:0] */
{0 x88, 0 x00}, /* Hsize[15:8] Write Only, can't read */
{0 x89, 0 x04}, /* Vsize[7:0] */
{0 x8a, 0 x00}, /* Vsize[15:8] Write Only, can't read */
{0 x8b, 0 x03}, /* for Iso */
{0 x8c, 0 x01}, /* RAW input */
{0 x8d, 0 x10},
{0 x1c, 0 x00}, /* RAW output, Iso transfer */
{0 x1d, 0 x48},
{0 x1d, 0 x00},
{0 x1d, 0 xff},
{0 x1c, 0 x0a}, /* turn off JPEG, Iso mode */
{0 x1d, 0 x2e}, /* for Iso */
{0 x1d, 0 x1e},
};
static const u8 ov361x_start_640[][2 ] = {
{0 x12, 0 x80},
{0 x13, 0 xcf},
{0 x14, 0 x40},
{0 x15, 0 x00},
{0 x01, 0 x80},
{0 x02, 0 x80},
{0 x04, 0 x70},
{0 x0d, 0 x40},
{0 x0f, 0 x47},
{0 x11, 0 x81},
{0 x32, 0 x36},
{0 x33, 0 x0C},
{0 x34, 0 x00},
{0 x35, 0 x90},
{0 x12, 0 x40},
{0 x17, 0 x1f},
{0 x18, 0 x5f},
{0 x19, 0 x00},
{0 x1a, 0 x68},
};
static const u8 ov361x_bridge_start_640[][2 ] = {
{0 xf1, 0 x60}, /* Hsize[7:0]*/
{0 x88, 0 x00}, /* Hsize[15:8] Write Only, can't read */
{0 x89, 0 x04}, /* Vsize[7:0] */
{0 x8a, 0 x00}, /* Vsize[15:8] Write Only, can't read */
{0 x8b, 0 x03}, /* for Iso */
{0 x8c, 0 x01}, /* RAW input */
{0 x8d, 0 x10},
{0 x1c, 0 x00}, /* RAW output, Iso transfer */
{0 x1d, 0 x48},
{0 x1d, 0 x00},
{0 x1d, 0 xff},
{0 x1c, 0 x0a}, /* turn off JPEG, Iso mode */
{0 x1d, 0 x2e}, /* for Iso */
{0 x1d, 0 x1e},
};
static const u8 ov361x_start_320[][2 ] = {
{0 x12, 0 x80},
{0 x13, 0 xcf},
{0 x14, 0 x40},
{0 x15, 0 x00},
{0 x01, 0 x80},
{0 x02, 0 x80},
{0 x04, 0 x70},
{0 x0d, 0 x40},
{0 x0f, 0 x47},
{0 x11, 0 x81},
{0 x32, 0 x36},
{0 x33, 0 x0C},
{0 x34, 0 x00},
{0 x35, 0 x90},
{0 x12, 0 x40},
{0 x17, 0 x1f},
{0 x18, 0 x5f},
{0 x19, 0 x00},
{0 x1a, 0 x68},
};
static const u8 ov361x_bridge_start_320[][2 ] = {
{0 xf1, 0 x60}, /* Hsize[7:0] */
{0 x88, 0 x00}, /* Hsize[15:8] Write Only, can't read */
{0 x89, 0 x04}, /* Vsize[7:0] */
{0 x8a, 0 x00}, /* Vsize[15:8] Write Only, can't read */
{0 x8b, 0 x03}, /* for Iso */
{0 x8c, 0 x01}, /* RAW input */
{0 x8d, 0 x10},
{0 x1c, 0 x00}, /* RAW output, Iso transfer; */
{0 x1d, 0 x48},
{0 x1d, 0 x00},
{0 x1d, 0 xff},
{0 x1c, 0 x0a}, /* turn off JPEG, Iso mode */
{0 x1d, 0 x2e}, /* for Iso */
{0 x1d, 0 x1e},
};
static const u8 ov361x_start_160[][2 ] = {
{0 x12, 0 x80},
{0 x13, 0 xcf},
{0 x14, 0 x40},
{0 x15, 0 x00},
{0 x01, 0 x80},
{0 x02, 0 x80},
{0 x04, 0 x70},
{0 x0d, 0 x40},
{0 x0f, 0 x47},
{0 x11, 0 x81},
{0 x32, 0 x36},
{0 x33, 0 x0C},
{0 x34, 0 x00},
{0 x35, 0 x90},
{0 x12, 0 x40},
{0 x17, 0 x1f},
{0 x18, 0 x5f},
{0 x19, 0 x00},
{0 x1a, 0 x68},
};
static const u8 ov361x_bridge_start_160[][2 ] = {
{0 xf1, 0 x60}, /* Hsize[7:0] */
{0 x88, 0 x00}, /* Hsize[15:8] Write Only, can't read */
{0 x89, 0 x04}, /* Vsize[7:0] */
{0 x8a, 0 x00}, /* Vsize[15:8] Write Only, can't read */
{0 x8b, 0 x03}, /* for Iso */
{0 x8c, 0 x01}, /* RAW input */
{0 x8d, 0 x10},
{0 x1c, 0 x00}, /* RAW output, Iso transfer */
{0 x1d, 0 x48},
{0 x1d, 0 x00},
{0 x1d, 0 xff},
{0 x1c, 0 x0a}, /* turn off JPEG, Iso mode */
{0 x1d, 0 x2e}, /* for Iso */
{0 x1d, 0 x1e},
};
static const u8 bridge_init[][2 ] = {
{0 x88, 0 xf8},
{0 x89, 0 xff},
{0 x76, 0 x03},
{0 x92, 0 x03},
{0 x95, 0 x10},
{0 xe2, 0 x00},
{0 xe7, 0 x3e},
{0 x8d, 0 x1c},
{0 x8e, 0 x00},
{0 x8f, 0 x00},
{0 x1f, 0 x00},
{0 xc3, 0 xf9},
{0 x89, 0 xff},
{0 x88, 0 xf8},
{0 x76, 0 x03},
{0 x92, 0 x01},
{0 x93, 0 x18},
{0 x1c, 0 x0a},
{0 x1d, 0 x48},
{0 xc0, 0 x50},
{0 xc1, 0 x3c},
{0 x34, 0 x05},
{0 xc2, 0 x0c},
{0 xc3, 0 xf9},
{0 x34, 0 x05},
{0 xe7, 0 x2e},
{0 x31, 0 xf9},
{0 x35, 0 x02},
{0 xd9, 0 x10},
{0 x25, 0 x42},
{0 x94, 0 x11},
};
static const u8 ov965x_init[][2 ] = {
{0 x12, 0 x80}, /* com7 - SSCB reset */
{0 x00, 0 x00}, /* gain */
{0 x01, 0 x80}, /* blue */
{0 x02, 0 x80}, /* red */
{0 x03, 0 x1b}, /* vref */
{0 x04, 0 x03}, /* com1 - exposure low bits */
{0 x0b, 0 x57}, /* ver */
{0 x0e, 0 x61}, /* com5 */
{0 x0f, 0 x42}, /* com6 */
{0 x11, 0 x00}, /* clkrc */
{0 x12, 0 x02}, /* com7 - 15fps VGA YUYV */
{0 x13, 0 xe7}, /* com8 - everything (AGC, AWB and AEC) */
{0 x14, 0 x28}, /* com9 */
{0 x16, 0 x24}, /* reg16 */
{0 x17, 0 x1d}, /* hstart*/
{0 x18, 0 xbd}, /* hstop */
{0 x19, 0 x01}, /* vstrt */
{0 x1a, 0 x81}, /* vstop*/
{0 x1e, 0 x04}, /* mvfp */
{0 x24, 0 x3c}, /* aew */
{0 x25, 0 x36}, /* aeb */
{0 x26, 0 x71}, /* vpt */
{0 x27, 0 x08}, /* bbias */
{0 x28, 0 x08}, /* gbbias */
{0 x29, 0 x15}, /* gr com */
{0 x2a, 0 x00}, /* exhch */
{0 x2b, 0 x00}, /* exhcl */
{0 x2c, 0 x08}, /* rbias */
{0 x32, 0 xff}, /* href */
{0 x33, 0 x00}, /* chlf */
{0 x34, 0 x3f}, /* aref1 */
{0 x35, 0 x00}, /* aref2 */
{0 x36, 0 xf8}, /* aref3 */
{0 x38, 0 x72}, /* adc2 */
{0 x39, 0 x57}, /* aref4 */
{0 x3a, 0 x80}, /* tslb - yuyv */
{0 x3b, 0 xc4}, /* com11 - night mode 1/4 frame rate */
{0 x3d, 0 x99}, /* com13 */
{0 x3f, 0 xc1}, /* edge */
{0 x40, 0 xc0}, /* com15 */
{0 x41, 0 x40}, /* com16 */
{0 x42, 0 xc0}, /* com17 */
{0 x43, 0 x0a}, /* rsvd */
{0 x44, 0 xf0},
{0 x45, 0 x46},
{0 x46, 0 x62},
{0 x47, 0 x2a},
{0 x48, 0 x3c},
{0 x4a, 0 xfc},
{0 x4b, 0 xfc},
{0 x4c, 0 x7f},
{0 x4d, 0 x7f},
{0 x4e, 0 x7f},
{0 x4f, 0 x98}, /* matrix */
{0 x50, 0 x98},
{0 x51, 0 x00},
{0 x52, 0 x28},
{0 x53, 0 x70},
{0 x54, 0 x98},
{0 x58, 0 x1a}, /* matrix coef sign */
{0 x59, 0 x85}, /* AWB control */
{0 x5a, 0 xa9},
{0 x5b, 0 x64},
{0 x5c, 0 x84},
{0 x5d, 0 x53},
{0 x5e, 0 x0e},
{0 x5f, 0 xf0}, /* AWB blue limit */
{0 x60, 0 xf0}, /* AWB red limit */
{0 x61, 0 xf0}, /* AWB green limit */
{0 x62, 0 x00}, /* lcc1 */
{0 x63, 0 x00}, /* lcc2 */
{0 x64, 0 x02}, /* lcc3 */
{0 x65, 0 x16}, /* lcc4 */
{0 x66, 0 x01}, /* lcc5 */
{0 x69, 0 x02}, /* hv */
{0 x6b, 0 x5a}, /* dbvl */
{0 x6c, 0 x04},
{0 x6d, 0 x55},
{0 x6e, 0 x00},
{0 x6f, 0 x9d},
{0 x70, 0 x21}, /* dnsth */
{0 x71, 0 x78},
{0 x72, 0 x00}, /* poidx */
{0 x73, 0 x01}, /* pckdv */
{0 x74, 0 x3a}, /* xindx */
{0 x75, 0 x35}, /* yindx */
{0 x76, 0 x01},
{0 x77, 0 x02},
{0 x7a, 0 x12}, /* gamma curve */
{0 x7b, 0 x08},
{0 x7c, 0 x16},
{0 x7d, 0 x30},
{0 x7e, 0 x5e},
{0 x7f, 0 x72},
{0 x80, 0 x82},
{0 x81, 0 x8e},
{0 x82, 0 x9a},
{0 x83, 0 xa4},
{0 x84, 0 xac},
{0 x85, 0 xb8},
{0 x86, 0 xc3},
{0 x87, 0 xd6},
{0 x88, 0 xe6},
{0 x89, 0 xf2},
{0 x8a, 0 x03},
{0 x8c, 0 x89}, /* com19 */
{0 x14, 0 x28}, /* com9 */
{0 x90, 0 x7d},
{0 x91, 0 x7b},
{0 x9d, 0 x03}, /* lcc6 */
{0 x9e, 0 x04}, /* lcc7 */
{0 x9f, 0 x7a},
{0 xa0, 0 x79},
{0 xa1, 0 x40}, /* aechm */
{0 xa4, 0 x50}, /* com21 */
{0 xa5, 0 x68}, /* com26 */
{0 xa6, 0 x4a}, /* AWB green */
{0 xa8, 0 xc1}, /* refa8 */
{0 xa9, 0 xef}, /* refa9 */
{0 xaa, 0 x92},
{0 xab, 0 x04},
{0 xac, 0 x80}, /* black level control */
{0 xad, 0 x80},
{0 xae, 0 x80},
{0 xaf, 0 x80},
{0 xb2, 0 xf2},
{0 xb3, 0 x20},
{0 xb4, 0 x20}, /* ctrlb4 */
{0 xb5, 0 x00},
{0 xb6, 0 xaf},
{0 xbb, 0 xae},
{0 xbc, 0 x7f}, /* ADC channel offsets */
{0 xdb, 0 x7f},
{0 xbe, 0 x7f},
{0 xbf, 0 x7f},
{0 xc0, 0 xe2},
{0 xc1, 0 xc0},
{0 xc2, 0 x01},
{0 xc3, 0 x4e},
{0 xc6, 0 x85},
{0 xc7, 0 x80}, /* com24 */
{0 xc9, 0 xe0},
{0 xca, 0 xe8},
{0 xcb, 0 xf0},
{0 xcc, 0 xd8},
{0 xcd, 0 xf1},
{0 x4f, 0 x98}, /* matrix */
{0 x50, 0 x98},
{0 x51, 0 x00},
{0 x52, 0 x28},
{0 x53, 0 x70},
{0 x54, 0 x98},
{0 x58, 0 x1a},
{0 xff, 0 x41}, /* read 41, write ff 00 */
{0 x41, 0 x40}, /* com16 */
{0 xc5, 0 x03}, /* 60 Hz banding filter */
{0 x6a, 0 x02}, /* 50 Hz banding filter */
{0 x12, 0 x62}, /* com7 - 30fps VGA YUV */
{0 x36, 0 xfa}, /* aref3 */
{0 x69, 0 x0a}, /* hv */
{0 x8c, 0 x89}, /* com22 */
{0 x14, 0 x28}, /* com9 */
{0 x3e, 0 x0c},
{0 x41, 0 x40}, /* com16 */
{0 x72, 0 x00},
{0 x73, 0 x00},
{0 x74, 0 x3a},
{0 x75, 0 x35},
{0 x76, 0 x01},
{0 xc7, 0 x80},
{0 x03, 0 x12}, /* vref */
{0 x17, 0 x16}, /* hstart */
{0 x18, 0 x02}, /* hstop */
{0 x19, 0 x01}, /* vstrt */
{0 x1a, 0 x3d}, /* vstop */
{0 x32, 0 xff}, /* href */
{0 xc0, 0 xaa},
};
static const u8 bridge_init_2[][2 ] = {
{0 x94, 0 xaa},
{0 xf1, 0 x60},
{0 xe5, 0 x04},
{0 xc0, 0 x50},
{0 xc1, 0 x3c},
{0 x8c, 0 x00},
{0 x8d, 0 x1c},
{0 x34, 0 x05},
{0 xc2, 0 x0c},
{0 xc3, 0 xf9},
{0 xda, 0 x01},
{0 x50, 0 x00},
{0 x51, 0 xa0},
{0 x52, 0 x3c},
{0 x53, 0 x00},
{0 x54, 0 x00},
{0 x55, 0 x00},
{0 x57, 0 x00},
{0 x5c, 0 x00},
{0 x5a, 0 xa0},
{0 x5b, 0 x78},
{0 x35, 0 x02},
{0 xd9, 0 x10},
{0 x94, 0 x11},
};
static const u8 ov965x_init_2[][2 ] = {
{0 x3b, 0 xc4},
{0 x1e, 0 x04}, /* mvfp */
{0 x13, 0 xe0}, /* com8 */
{0 x00, 0 x00}, /* gain */
{0 x13, 0 xe7}, /* com8 - everything (AGC, AWB and AEC) */
{0 x11, 0 x03}, /* clkrc */
{0 x6b, 0 x5a}, /* dblv */
{0 x6a, 0 x05},
{0 xc5, 0 x07},
{0 xa2, 0 x4b},
{0 xa3, 0 x3e},
{0 x2d, 0 x00},
{0 xff, 0 x42}, /* read 42, write ff 00 */
{0 x42, 0 xc0}, /* com17 */
{0 x2d, 0 x00},
{0 xff, 0 x42}, /* read 42, write ff 00 */
{0 x42, 0 xc1}, /* com17 */
/* sharpness */
{0 x3f, 0 x01},
{0 xff, 0 x42}, /* read 42, write ff 00 */
{0 x42, 0 xc1}, /* com17 */
/* saturation */
{0 x4f, 0 x98}, /* matrix */
{0 x50, 0 x98},
{0 x51, 0 x00},
{0 x52, 0 x28},
{0 x53, 0 x70},
{0 x54, 0 x98},
{0 x58, 0 x1a},
{0 xff, 0 x41}, /* read 41, write ff 00 */
{0 x41, 0 x40}, /* com16 */
/* contrast */
{0 x56, 0 x40},
/* brightness */
{0 x55, 0 x8f},
/* expo */
{0 x10, 0 x25}, /* aech - exposure high bits */
{0 xff, 0 x13}, /* read 13, write ff 00 */
{0 x13, 0 xe7}, /* com8 - everything (AGC, AWB and AEC) */
};
static const u8 ov971x_init[][2 ] = {
{0 x12, 0 x80},
{0 x09, 0 x10},
{0 x1e, 0 x07},
{0 x5f, 0 x18},
{0 x69, 0 x04},
{0 x65, 0 x2a},
{0 x68, 0 x0a},
{0 x39, 0 x28},
{0 x4d, 0 x90},
{0 xc1, 0 x80},
{0 x0c, 0 x30},
{0 x6d, 0 x02},
{0 x96, 0 xf1},
{0 xbc, 0 x68},
{0 x12, 0 x00},
{0 x3b, 0 x00},
{0 x97, 0 x80},
{0 x17, 0 x25},
{0 x18, 0 xa2},
{0 x19, 0 x01},
{0 x1a, 0 xca},
{0 x03, 0 x0a},
{0 x32, 0 x07},
{0 x98, 0 x40}, /*{0x98, 0x00},*/
{0 x99, 0 xA0}, /*{0x99, 0x00},*/
{0 x9a, 0 x01}, /*{0x9a, 0x00},*/
{0 x57, 0 x00},
{0 x58, 0 x78}, /*{0x58, 0xc8},*/
{0 x59, 0 x50}, /*{0x59, 0xa0},*/
{0 x4c, 0 x13},
{0 x4b, 0 x36},
{0 x3d, 0 x3c},
{0 x3e, 0 x03},
{0 xbd, 0 x50}, /*{0xbd, 0xa0},*/
{0 xbe, 0 x78}, /*{0xbe, 0xc8},*/
{0 x4e, 0 x55},
{0 x4f, 0 x55},
{0 x50, 0 x55},
{0 x51, 0 x55},
{0 x24, 0 x55},
{0 x25, 0 x40},
{0 x26, 0 xa1},
{0 x5c, 0 x59},
{0 x5d, 0 x00},
{0 x11, 0 x00},
{0 x2a, 0 x98},
{0 x2b, 0 x06},
{0 x2d, 0 x00},
{0 x2e, 0 x00},
{0 x13, 0 xa5},
{0 x14, 0 x40},
{0 x4a, 0 x00},
{0 x49, 0 xce},
{0 x22, 0 x03},
{0 x09, 0 x00}
};
static const u8 ov965x_start_1_vga[][2 ] = { /* same for qvga */
{0 x12, 0 x62}, /* com7 - 30fps VGA YUV */
{0 x36, 0 xfa}, /* aref3 */
{0 x69, 0 x0a}, /* hv */
{0 x8c, 0 x89}, /* com22 */
{0 x14, 0 x28}, /* com9 */
{0 x3e, 0 x0c}, /* com14 */
{0 x41, 0 x40}, /* com16 */
{0 x72, 0 x00},
{0 x73, 0 x00},
{0 x74, 0 x3a},
{0 x75, 0 x35},
{0 x76, 0 x01},
{0 xc7, 0 x80}, /* com24 */
{0 x03, 0 x12}, /* vref */
{0 x17, 0 x16}, /* hstart */
{0 x18, 0 x02}, /* hstop */
{0 x19, 0 x01}, /* vstrt */
{0 x1a, 0 x3d}, /* vstop */
{0 x32, 0 xff}, /* href */
{0 xc0, 0 xaa},
};
static const u8 ov965x_start_1_svga[][2 ] = {
{0 x12, 0 x02}, /* com7 - YUYV - VGA 15 full resolution */
{0 x36, 0 xf8}, /* aref3 */
{0 x69, 0 x02}, /* hv */
{0 x8c, 0 x0d}, /* com22 */
{0 x3e, 0 x0c}, /* com14 */
{0 x41, 0 x40}, /* com16 */
{0 x72, 0 x00},
{0 x73, 0 x01},
{0 x74, 0 x3a},
{0 x75, 0 x35},
{0 x76, 0 x01},
{0 xc7, 0 x80}, /* com24 */
{0 x03, 0 x1b}, /* vref */
{0 x17, 0 x1d}, /* hstart */
{0 x18, 0 xbd}, /* hstop */
{0 x19, 0 x01}, /* vstrt */
{0 x1a, 0 x81}, /* vstop */
{0 x32, 0 xff}, /* href */
{0 xc0, 0 xe2},
};
static const u8 ov965x_start_1_xga[][2 ] = {
{0 x12, 0 x02}, /* com7 */
{0 x36, 0 xf8}, /* aref3 */
{0 x69, 0 x02}, /* hv */
{0 x8c, 0 x89}, /* com22 */
{0 x14, 0 x28}, /* com9 */
{0 x3e, 0 x0c}, /* com14 */
{0 x41, 0 x40}, /* com16 */
{0 x72, 0 x00},
{0 x73, 0 x01},
{0 x74, 0 x3a},
{0 x75, 0 x35},
{0 x76, 0 x01},
{0 xc7, 0 x80}, /* com24 */
{0 x03, 0 x1b}, /* vref */
{0 x17, 0 x1d}, /* hstart */
{0 x18, 0 xbd}, /* hstop */
{0 x19, 0 x01}, /* vstrt */
{0 x1a, 0 x81}, /* vstop */
{0 x32, 0 xff}, /* href */
{0 xc0, 0 xe2},
};
static const u8 ov965x_start_1_sxga[][2 ] = {
{0 x12, 0 x02}, /* com7 */
{0 x36, 0 xf8}, /* aref3 */
{0 x69, 0 x02}, /* hv */
{0 x8c, 0 x89}, /* com22 */
{0 x14, 0 x28}, /* com9 */
{0 x3e, 0 x0c}, /* com14 */
{0 x41, 0 x40}, /* com16 */
{0 x72, 0 x00},
{0 x73, 0 x01},
{0 x74, 0 x3a},
{0 x75, 0 x35},
{0 x76, 0 x01},
{0 xc7, 0 x80}, /* com24 */
{0 x03, 0 x1b}, /* vref */
{0 x17, 0 x1d}, /* hstart */
{0 x18, 0 x02}, /* hstop */
{0 x19, 0 x01}, /* vstrt */
{0 x1a, 0 x81}, /* vstop */
{0 x32, 0 xff}, /* href */
{0 xc0, 0 xe2},
};
static const u8 bridge_start_qvga[][2 ] = {
{0 x94, 0 xaa},
{0 xf1, 0 x60},
{0 xe5, 0 x04},
{0 xc0, 0 x50},
{0 xc1, 0 x3c},
{0 x8c, 0 x00},
{0 x8d, 0 x1c},
{0 x34, 0 x05},
{0 xc2, 0 x4c},
{0 xc3, 0 xf9},
{0 xda, 0 x00},
{0 x50, 0 x00},
{0 x51, 0 xa0},
{0 x52, 0 x78},
{0 x53, 0 x00},
{0 x54, 0 x00},
{0 x55, 0 x00},
{0 x57, 0 x00},
{0 x5c, 0 x00},
{0 x5a, 0 x50},
{0 x5b, 0 x3c},
{0 x35, 0 x02},
{0 xd9, 0 x10},
{0 x94, 0 x11},
};
static const u8 bridge_start_vga[][2 ] = {
{0 x94, 0 xaa},
{0 xf1, 0 x60},
{0 xe5, 0 x04},
{0 xc0, 0 x50},
{0 xc1, 0 x3c},
{0 x8c, 0 x00},
{0 x8d, 0 x1c},
{0 x34, 0 x05},
{0 xc2, 0 x0c},
{0 xc3, 0 xf9},
{0 xda, 0 x01},
{0 x50, 0 x00},
{0 x51, 0 xa0},
{0 x52, 0 x3c},
{0 x53, 0 x00},
{0 x54, 0 x00},
{0 x55, 0 x00},
{0 x57, 0 x00},
{0 x5c, 0 x00},
{0 x5a, 0 xa0},
{0 x5b, 0 x78},
{0 x35, 0 x02},
{0 xd9, 0 x10},
{0 x94, 0 x11},
};
static const u8 bridge_start_svga[][2 ] = {
{0 x94, 0 xaa},
{0 xf1, 0 x60},
{0 xe5, 0 x04},
{0 xc0, 0 xa0},
{0 xc1, 0 x80},
{0 x8c, 0 x00},
{0 x8d, 0 x1c},
{0 x34, 0 x05},
{0 xc2, 0 x4c},
{0 xc3, 0 xf9},
{0 x50, 0 x00},
{0 x51, 0 x40},
{0 x52, 0 x00},
{0 x53, 0 x00},
{0 x54, 0 x00},
{0 x55, 0 x88},
{0 x57, 0 x00},
{0 x5c, 0 x00},
{0 x5a, 0 xc8},
{0 x5b, 0 x96},
{0 x35, 0 x02},
{0 xd9, 0 x10},
{0 xda, 0 x00},
{0 x94, 0 x11},
};
static const u8 bridge_start_xga[][2 ] = {
{0 x94, 0 xaa},
{0 xf1, 0 x60},
{0 xe5, 0 x04},
{0 xc0, 0 xa0},
{0 xc1, 0 x80},
{0 x8c, 0 x00},
{0 x8d, 0 x1c},
{0 x34, 0 x05},
{0 xc2, 0 x4c},
{0 xc3, 0 xf9},
{0 x50, 0 x00},
{0 x51, 0 x40},
{0 x52, 0 x00},
{0 x53, 0 x00},
{0 x54, 0 x00},
{0 x55, 0 x88},
{0 x57, 0 x00},
{0 x5c, 0 x01},
{0 x5a, 0 x00},
{0 x5b, 0 xc0},
{0 x35, 0 x02},
{0 xd9, 0 x10},
{0 xda, 0 x01},
{0 x94, 0 x11},
};
static const u8 bridge_start_sxga[][2 ] = {
{0 x94, 0 xaa},
{0 xf1, 0 x60},
{0 xe5, 0 x04},
{0 xc0, 0 xa0},
{0 xc1, 0 x80},
{0 x8c, 0 x00},
{0 x8d, 0 x1c},
{0 x34, 0 x05},
{0 xc2, 0 x0c},
{0 xc3, 0 xf9},
{0 xda, 0 x00},
{0 x35, 0 x02},
{0 xd9, 0 x10},
{0 x94, 0 x11},
};
static const u8 ov965x_start_2_qvga[][2 ] = {
{0 x3b, 0 xe4}, /* com11 - night mode 1/4 frame rate */
{0 x1e, 0 x04}, /* mvfp */
{0 x13, 0 xe0}, /* com8 */
{0 x00, 0 x00},
{0 x13, 0 xe7}, /* com8 - everything (AGC, AWB and AEC) */
{0 x11, 0 x01}, /* clkrc */
{0 x6b, 0 x5a}, /* dblv */
{0 x6a, 0 x02}, /* 50 Hz banding filter */
{0 xc5, 0 x03}, /* 60 Hz banding filter */
{0 xa2, 0 x96}, /* bd50 */
{0 xa3, 0 x7d}, /* bd60 */
{0 xff, 0 x13}, /* read 13, write ff 00 */
{0 x13, 0 xe7},
{0 x3a, 0 x80}, /* tslb - yuyv */
};
static const u8 ov965x_start_2_vga[][2 ] = {
{0 x3b, 0 xc4}, /* com11 - night mode 1/4 frame rate */
{0 x1e, 0 x04}, /* mvfp */
{0 x13, 0 xe0}, /* com8 */
{0 x00, 0 x00},
{0 x13, 0 xe7}, /* com8 - everything (AGC, AWB and AEC) */
{0 x11, 0 x03}, /* clkrc */
{0 x6b, 0 x5a}, /* dblv */
{0 x6a, 0 x05}, /* 50 Hz banding filter */
{0 xc5, 0 x07}, /* 60 Hz banding filter */
{0 xa2, 0 x4b}, /* bd50 */
{0 xa3, 0 x3e}, /* bd60 */
{0 x2d, 0 x00}, /* advfl */
};
static const u8 ov965x_start_2_svga[][2 ] = { /* same for xga */
{0 x3b, 0 xc4}, /* com11 - night mode 1/4 frame rate */
{0 x1e, 0 x04}, /* mvfp */
{0 x13, 0 xe0}, /* com8 */
{0 x00, 0 x00},
{0 x13, 0 xe7}, /* com8 - everything (AGC, AWB and AEC) */
{0 x11, 0 x01}, /* clkrc */
{0 x6b, 0 x5a}, /* dblv */
{0 x6a, 0 x0c}, /* 50 Hz banding filter */
{0 xc5, 0 x0f}, /* 60 Hz banding filter */
{0 xa2, 0 x4e}, /* bd50 */
{0 xa3, 0 x41}, /* bd60 */
};
static const u8 ov965x_start_2_sxga[][2 ] = {
{0 x13, 0 xe0}, /* com8 */
{0 x00, 0 x00},
{0 x13, 0 xe7}, /* com8 - everything (AGC, AWB and AEC) */
{0 x3b, 0 xc4}, /* com11 - night mode 1/4 frame rate */
{0 x1e, 0 x04}, /* mvfp */
{0 x11, 0 x01}, /* clkrc */
{0 x6b, 0 x5a}, /* dblv */
{0 x6a, 0 x0c}, /* 50 Hz banding filter */
{0 xc5, 0 x0f}, /* 60 Hz banding filter */
{0 xa2, 0 x4e}, /* bd50 */
{0 xa3, 0 x41}, /* bd60 */
};
static const u8 ov562x_init[][2 ] = {
{0 x88, 0 x20},
{0 x89, 0 x0a},
{0 x8a, 0 x90},
{0 x8b, 0 x06},
{0 x8c, 0 x01},
{0 x8d, 0 x10},
{0 x1c, 0 x00},
{0 x1d, 0 x48},
{0 x1d, 0 x00},
{0 x1d, 0 xff},
{0 x1c, 0 x0a},
{0 x1d, 0 x2e},
{0 x1d, 0 x1e},
};
static const u8 ov562x_init_2[][2 ] = {
{0 x12, 0 x80},
{0 x11, 0 x41},
{0 x13, 0 x00},
{0 x10, 0 x1e},
{0 x3b, 0 x07},
{0 x5b, 0 x40},
{0 x39, 0 x07},
{0 x53, 0 x02},
{0 x54, 0 x60},
{0 x04, 0 x20},
{0 x27, 0 x04},
{0 x3d, 0 x40},
{0 x36, 0 x00},
{0 xc5, 0 x04},
{0 x4e, 0 x00},
{0 x4f, 0 x93},
{0 x50, 0 x7b},
{0 xca, 0 x0c},
{0 xcb, 0 x0f},
{0 x39, 0 x07},
{0 x4a, 0 x10},
{0 x3e, 0 x0a},
{0 x3d, 0 x00},
{0 x0c, 0 x38},
{0 x38, 0 x90},
{0 x46, 0 x30},
{0 x4f, 0 x93},
{0 x50, 0 x7b},
{0 xab, 0 x00},
{0 xca, 0 x0c},
{0 xcb, 0 x0f},
{0 x37, 0 x02},
{0 x44, 0 x48},
{0 x8d, 0 x44},
{0 x2a, 0 x00},
{0 x2b, 0 x00},
{0 x32, 0 x00},
{0 x38, 0 x90},
{0 x53, 0 x02},
{0 x54, 0 x60},
{0 x12, 0 x00},
{0 x17, 0 x12},
{0 x18, 0 xb4},
{0 x19, 0 x0c},
{0 x1a, 0 xf4},
{0 x03, 0 x4a},
{0 x89, 0 x20},
{0 x83, 0 x80},
{0 xb7, 0 x9d},
{0 xb6, 0 x11},
{0 xb5, 0 x55},
{0 xb4, 0 x00},
{0 xa9, 0 xf0},
{0 xa8, 0 x0a},
{0 xb8, 0 xf0},
{0 xb9, 0 xf0},
{0 xba, 0 xf0},
{0 x81, 0 x07},
{0 x63, 0 x44},
{0 x13, 0 xc7},
{0 x14, 0 x60},
{0 x33, 0 x75},
{0 x2c, 0 x00},
{0 x09, 0 x00},
{0 x35, 0 x30},
{0 x27, 0 x04},
{0 x3c, 0 x07},
{0 x3a, 0 x0a},
{0 x3b, 0 x07},
{0 x01, 0 x40},
{0 x02, 0 x40},
{0 x16, 0 x40},
{0 x52, 0 xb0},
{0 x51, 0 x83},
{0 x21, 0 xbb},
{0 x22, 0 x10},
{0 x23, 0 x03},
{0 x35, 0 x38},
{0 x20, 0 x90},
{0 x28, 0 x30},
{0 x73, 0 xe1},
{0 x6c, 0 x00},
{0 x6d, 0 x80},
{0 x6e, 0 x00},
{0 x70, 0 x04},
{0 x71, 0 x00},
{0 x8d, 0 x04},
{0 x64, 0 x00},
{0 x65, 0 x00},
{0 x66, 0 x00},
{0 x67, 0 x00},
{0 x68, 0 x00},
{0 x69, 0 x00},
{0 x6a, 0 x00},
{0 x6b, 0 x00},
{0 x71, 0 x94},
{0 x74, 0 x20},
{0 x80, 0 x09},
{0 x85, 0 xc0},
};
static void reg_w_i(struct gspca_dev *gspca_dev, u16 reg, u8 val)
{
struct usb_device *udev = gspca_dev->dev;
int ret;
if (gspca_dev->usb_err < 0 )
return ;
gspca_dev->usb_buf[0 ] = val;
ret = usb_control_msg(udev,
usb_sndctrlpipe(udev, 0 ),
0 x01,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
0 x00, reg, gspca_dev->usb_buf, 1 , CTRL_TIMEOUT);
if (ret < 0 ) {
pr_err("reg_w failed %d\n" , ret);
gspca_dev->usb_err = ret;
}
}
static void reg_w(struct gspca_dev *gspca_dev, u16 reg, u8 val)
{
gspca_dbg(gspca_dev, D_USBO, "reg_w [%04x] = %02x\n" , reg, val);
reg_w_i(gspca_dev, reg, val);
}
static u8 reg_r(struct gspca_dev *gspca_dev, u16 reg)
{
struct usb_device *udev = gspca_dev->dev;
int ret;
if (gspca_dev->usb_err < 0 )
return 0 ;
ret = usb_control_msg(udev,
usb_rcvctrlpipe(udev, 0 ),
0 x01,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
0 x00, reg, gspca_dev->usb_buf, 1 , CTRL_TIMEOUT);
gspca_dbg(gspca_dev, D_USBI, "reg_r [%04x] -> %02x\n" ,
reg, gspca_dev->usb_buf[0 ]);
if (ret < 0 ) {
pr_err("reg_r err %d\n" , ret);
gspca_dev->usb_err = ret;
return 0 ;
}
return gspca_dev->usb_buf[0 ];
}
static int sccb_check_status(struct gspca_dev *gspca_dev)
{
u8 data;
int i;
for (i = 0 ; i < 5 ; i++) {
msleep(20 );
data = reg_r(gspca_dev, OV534_REG_STATUS);
switch (data) {
case 0 x00:
return 1 ;
case 0 x04:
return 0 ;
case 0 x03:
break ;
default :
gspca_dbg(gspca_dev, D_USBI|D_USBO,
"sccb status 0x%02x, attempt %d/5\n" ,
data, i + 1 );
}
}
return 0 ;
}
static void sccb_write(struct gspca_dev *gspca_dev, u8 reg, u8 val)
{
gspca_dbg(gspca_dev, D_USBO, "sccb_write [%02x] = %02x\n" , reg, val);
reg_w_i(gspca_dev, OV534_REG_SUBADDR, reg);
reg_w_i(gspca_dev, OV534_REG_WRITE, val);
reg_w_i(gspca_dev, OV534_REG_OPERATION, OV534_OP_WRITE_3);
if (!sccb_check_status(gspca_dev))
pr_err("sccb_write failed\n" );
}
static u8 sccb_read(struct gspca_dev *gspca_dev, u16 reg)
{
reg_w(gspca_dev, OV534_REG_SUBADDR, reg);
reg_w(gspca_dev, OV534_REG_OPERATION, OV534_OP_WRITE_2);
if (!sccb_check_status(gspca_dev))
pr_err("sccb_read failed 1\n" );
reg_w(gspca_dev, OV534_REG_OPERATION, OV534_OP_READ_2);
if (!sccb_check_status(gspca_dev))
pr_err("sccb_read failed 2\n" );
return reg_r(gspca_dev, OV534_REG_READ);
}
/* output a bridge sequence (reg - val) */
static void reg_w_array(struct gspca_dev *gspca_dev,
const u8 (*data)[2 ], int len)
{
while (--len >= 0 ) {
reg_w(gspca_dev, (*data)[0 ], (*data)[1 ]);
data++;
}
}
/* output a sensor sequence (reg - val) */
static void sccb_w_array(struct gspca_dev *gspca_dev,
const u8 (*data)[2 ], int len)
{
while (--len >= 0 ) {
if ((*data)[0 ] != 0 xff) {
sccb_write(gspca_dev, (*data)[0 ], (*data)[1 ]);
} else {
sccb_read(gspca_dev, (*data)[1 ]);
sccb_write(gspca_dev, 0 xff, 0 x00);
}
data++;
}
}
/* Two bits control LED: 0x21 bit 7 and 0x23 bit 7.
* (direction and output)? */
static void set_led(struct gspca_dev *gspca_dev, int status)
{
u8 data;
gspca_dbg(gspca_dev, D_CONF, "led status: %d\n" , status);
data = reg_r(gspca_dev, 0 x21);
data |= 0 x80;
reg_w(gspca_dev, 0 x21, data);
data = reg_r(gspca_dev, 0 x23);
if (status)
data |= 0 x80;
else
data &= ~0 x80;
reg_w(gspca_dev, 0 x23, data);
if (!status) {
data = reg_r(gspca_dev, 0 x21);
data &= ~0 x80;
reg_w(gspca_dev, 0 x21, data);
}
}
static void setbrightness(struct gspca_dev *gspca_dev, s32 brightness)
{
struct sd *sd = (struct sd *) gspca_dev;
u8 val;
s8 sval;
if (sd->sensor == SENSOR_OV562x) {
sval = brightness;
val = 0 x76;
val += sval;
sccb_write(gspca_dev, 0 x24, val);
val = 0 x6a;
val += sval;
sccb_write(gspca_dev, 0 x25, val);
if (sval < -40 )
val = 0 x71;
else if (sval < 20 )
val = 0 x94;
else
val = 0 xe6;
sccb_write(gspca_dev, 0 x26, val);
} else {
val = brightness;
if (val < 8 )
val = 15 - val; /* f .. 8 */
else
val = val - 8 ; /* 0 .. 7 */
sccb_write(gspca_dev, 0 x55, /* brtn - brightness adjustment */
0 x0f | (val << 4 ));
}
}
static void setcontrast(struct gspca_dev *gspca_dev, s32 val)
{
sccb_write(gspca_dev, 0 x56, /* cnst1 - contrast 1 ctrl coeff */
val << 4 );
}
static void setautogain(struct gspca_dev *gspca_dev, s32 autogain)
{
u8 val;
/*fixme: should adjust agc/awb/aec by different controls */
val = sccb_read(gspca_dev, 0 x13); /* com8 */
sccb_write(gspca_dev, 0 xff, 0 x00);
if (autogain)
val |= 0 x05; /* agc & aec */
else
val &= 0 xfa;
sccb_write(gspca_dev, 0 x13, val);
}
static void setexposure(struct gspca_dev *gspca_dev, s32 exposure)
{
static const u8 expo[4 ] = {0 x00, 0 x25, 0 x38, 0 x5e};
u8 val;
sccb_write(gspca_dev, 0 x10, expo[exposure]); /* aec[9:2] */
val = sccb_read(gspca_dev, 0 x13); /* com8 */
sccb_write(gspca_dev, 0 xff, 0 x00);
sccb_write(gspca_dev, 0 x13, val);
val = sccb_read(gspca_dev, 0 xa1); /* aech */
sccb_write(gspca_dev, 0 xff, 0 x00);
sccb_write(gspca_dev, 0 xa1, val & 0 xe0); /* aec[15:10] = 0 */
}
static void setsharpness(struct gspca_dev *gspca_dev, s32 val)
{
if (val < 0 ) { /* auto */
val = sccb_read(gspca_dev, 0 x42); /* com17 */
sccb_write(gspca_dev, 0 xff, 0 x00);
sccb_write(gspca_dev, 0 x42, val | 0 x40);
/* Edge enhancement strength auto adjust */
return ;
}
if (val != 0 )
val = 1 << (val - 1 );
sccb_write(gspca_dev, 0 x3f, /* edge - edge enhance. factor */
val);
val = sccb_read(gspca_dev, 0 x42); /* com17 */
sccb_write(gspca_dev, 0 xff, 0 x00);
sccb_write(gspca_dev, 0 x42, val & 0 xbf);
}
static void setsatur(struct gspca_dev *gspca_dev, s32 val)
{
u8 val1, val2, val3;
static const u8 matrix[5 ][2 ] = {
{0 x14, 0 x38},
{0 x1e, 0 x54},
{0 x28, 0 x70},
{0 x32, 0 x8c},
{0 x48, 0 x90}
};
val1 = matrix[val][0 ];
val2 = matrix[val][1 ];
val3 = val1 + val2;
sccb_write(gspca_dev, 0 x4f, val3); /* matrix coeff */
sccb_write(gspca_dev, 0 x50, val3);
sccb_write(gspca_dev, 0 x51, 0 x00);
sccb_write(gspca_dev, 0 x52, val1);
sccb_write(gspca_dev, 0 x53, val2);
sccb_write(gspca_dev, 0 x54, val3);
sccb_write(gspca_dev, 0 x58, 0 x1a); /* mtxs - coeff signs */
val1 = sccb_read(gspca_dev, 0 x41); /* com16 */
sccb_write(gspca_dev, 0 xff, 0 x00);
sccb_write(gspca_dev, 0 x41, val1);
}
static void setlightfreq(struct gspca_dev *gspca_dev, s32 freq)
{
u8 val;
val = sccb_read(gspca_dev, 0 x13); /* com8 */
sccb_write(gspca_dev, 0 xff, 0 x00);
if (freq == 0 ) {
sccb_write(gspca_dev, 0 x13, val & 0 xdf);
return ;
}
sccb_write(gspca_dev, 0 x13, val | 0 x20);
val = sccb_read(gspca_dev, 0 x42); /* com17 */
sccb_write(gspca_dev, 0 xff, 0 x00);
if (freq == 1 )
val |= 0 x01;
else
val &= 0 xfe;
sccb_write(gspca_dev, 0 x42, val);
}
/* this function is called at probe time */
static int sd_config(struct gspca_dev *gspca_dev,
const struct usb_device_id *id)
{
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;
u16 sensor_id;
/* reset bridge */
reg_w(gspca_dev, 0 xe7, 0 x3a);
reg_w(gspca_dev, 0 xe0, 0 x08);
msleep(100 );
/* initialize the sensor address */
reg_w(gspca_dev, OV534_REG_ADDRESS, 0 x60);
/* reset sensor */
sccb_write(gspca_dev, 0 x12, 0 x80);
msleep(10 );
/* probe the sensor */
sccb_read(gspca_dev, 0 x0a);
sensor_id = sccb_read(gspca_dev, 0 x0a) << 8 ;
sccb_read(gspca_dev, 0 x0b);
sensor_id |= sccb_read(gspca_dev, 0 x0b);
gspca_dbg(gspca_dev, D_PROBE, "Sensor ID: %04x\n" , sensor_id);
/* initialize */
if ((sensor_id & 0 xfff0) == 0 x9650) {
sd->sensor = SENSOR_OV965x;
gspca_dev->cam.cam_mode = ov965x_mode;
gspca_dev->cam.nmodes = ARRAY_SIZE(ov965x_mode);
reg_w_array(gspca_dev, bridge_init,
ARRAY_SIZE(bridge_init));
sccb_w_array(gspca_dev, ov965x_init,
ARRAY_SIZE(ov965x_init));
reg_w_array(gspca_dev, bridge_init_2,
ARRAY_SIZE(bridge_init_2));
sccb_w_array(gspca_dev, ov965x_init_2,
ARRAY_SIZE(ov965x_init_2));
reg_w(gspca_dev, 0 xe0, 0 x00);
reg_w(gspca_dev, 0 xe0, 0 x01);
set_led(gspca_dev, 0 );
reg_w(gspca_dev, 0 xe0, 0 x00);
} else if ((sensor_id & 0 xfff0) == 0 x9710) {
const char *p;
int l;
sd->sensor = SENSOR_OV971x;
gspca_dev->cam.cam_mode = ov971x_mode;
gspca_dev->cam.nmodes = ARRAY_SIZE(ov971x_mode);
gspca_dev->cam.bulk = 1 ;
gspca_dev->cam.bulk_size = 16384 ;
gspca_dev->cam.bulk_nurbs = 2 ;
sccb_w_array(gspca_dev, ov971x_init,
ARRAY_SIZE(ov971x_init));
/* set video format on bridge processor */
/* access bridge processor's video format registers at: 0x00 */
reg_w(gspca_dev, 0 x1c, 0 x00);
/*set register: 0x00 is 'RAW8', 0x40 is 'YUV422' (YUYV?)*/
reg_w(gspca_dev, 0 x1d, 0 x00);
/* Will W. specific stuff
* set VSYNC to
* output ( 0 x1f ) if first webcam
* input (0x17) if 2nd or 3rd webcam */
p = video_device_node_name(&gspca_dev->vdev);
l = strlen(p) - 1 ;
if (p[l] == '0' )
reg_w(gspca_dev, 0 x56, 0 x1f);
else
reg_w(gspca_dev, 0 x56, 0 x17);
} else if ((sensor_id & 0 xfff0) == 0 x5620) {
sd->sensor = SENSOR_OV562x;
gspca_dev->cam.cam_mode = ov562x_mode;
gspca_dev->cam.nmodes = ARRAY_SIZE(ov562x_mode);
reg_w_array(gspca_dev, ov562x_init,
ARRAY_SIZE(ov562x_init));
sccb_w_array(gspca_dev, ov562x_init_2,
ARRAY_SIZE(ov562x_init_2));
reg_w(gspca_dev, 0 xe0, 0 x00);
} else if ((sensor_id & 0 xfff0) == 0 x3610) {
sd->sensor = SENSOR_OV361x;
gspca_dev->cam.cam_mode = ov361x_mode;
gspca_dev->cam.nmodes = ARRAY_SIZE(ov361x_mode);
reg_w(gspca_dev, 0 xe7, 0 x3a);
reg_w(gspca_dev, 0 xf1, 0 x60);
sccb_write(gspca_dev, 0 x12, 0 x80);
} else {
pr_err("Unknown sensor %04x" , sensor_id);
return -EINVAL;
}
return gspca_dev->usb_err;
}
static int sd_start_ov361x(struct gspca_dev *gspca_dev)
{
sccb_write(gspca_dev, 0 x12, 0 x80);
msleep(20 );
switch (gspca_dev->curr_mode % (ov361x_last)) {
case ov361x_2048:
reg_w_array(gspca_dev, ov361x_bridge_start_2048,
ARRAY_SIZE(ov361x_bridge_start_2048));
sccb_w_array(gspca_dev, ov361x_start_2048,
ARRAY_SIZE(ov361x_start_2048));
break ;
case ov361x_1600:
reg_w_array(gspca_dev, ov361x_bridge_start_1600,
ARRAY_SIZE(ov361x_bridge_start_1600));
sccb_w_array(gspca_dev, ov361x_start_1600,
ARRAY_SIZE(ov361x_start_1600));
break ;
case ov361x_1024:
reg_w_array(gspca_dev, ov361x_bridge_start_1024,
ARRAY_SIZE(ov361x_bridge_start_1024));
sccb_w_array(gspca_dev, ov361x_start_1024,
ARRAY_SIZE(ov361x_start_1024));
break ;
case ov361x_640:
reg_w_array(gspca_dev, ov361x_bridge_start_640,
ARRAY_SIZE(ov361x_bridge_start_640));
sccb_w_array(gspca_dev, ov361x_start_640,
ARRAY_SIZE(ov361x_start_640));
break ;
case ov361x_320:
reg_w_array(gspca_dev, ov361x_bridge_start_320,
ARRAY_SIZE(ov361x_bridge_start_320));
sccb_w_array(gspca_dev, ov361x_start_320,
ARRAY_SIZE(ov361x_start_320));
break ;
case ov361x_160:
reg_w_array(gspca_dev, ov361x_bridge_start_160,
ARRAY_SIZE(ov361x_bridge_start_160));
sccb_w_array(gspca_dev, ov361x_start_160,
ARRAY_SIZE(ov361x_start_160));
break ;
}
reg_w(gspca_dev, 0 xe0, 0 x00); /* start transfer */
return gspca_dev->usb_err;
}
static int sd_start(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
if (sd->sensor == SENSOR_OV971x)
return gspca_dev->usb_err;
if (sd->sensor == SENSOR_OV562x)
return gspca_dev->usb_err;
if (sd->sensor == SENSOR_OV361x)
return sd_start_ov361x(gspca_dev);
switch (gspca_dev->curr_mode) {
case QVGA_MODE: /* 320x240 */
sccb_w_array(gspca_dev, ov965x_start_1_vga,
ARRAY_SIZE(ov965x_start_1_vga));
reg_w_array(gspca_dev, bridge_start_qvga,
ARRAY_SIZE(bridge_start_qvga));
sccb_w_array(gspca_dev, ov965x_start_2_qvga,
ARRAY_SIZE(ov965x_start_2_qvga));
break ;
case VGA_MODE: /* 640x480 */
sccb_w_array(gspca_dev, ov965x_start_1_vga,
ARRAY_SIZE(ov965x_start_1_vga));
reg_w_array(gspca_dev, bridge_start_vga,
ARRAY_SIZE(bridge_start_vga));
sccb_w_array(gspca_dev, ov965x_start_2_vga,
ARRAY_SIZE(ov965x_start_2_vga));
break ;
case SVGA_MODE: /* 800x600 */
sccb_w_array(gspca_dev, ov965x_start_1_svga,
ARRAY_SIZE(ov965x_start_1_svga));
reg_w_array(gspca_dev, bridge_start_svga,
ARRAY_SIZE(bridge_start_svga));
sccb_w_array(gspca_dev, ov965x_start_2_svga,
ARRAY_SIZE(ov965x_start_2_svga));
break ;
case XGA_MODE: /* 1024x768 */
sccb_w_array(gspca_dev, ov965x_start_1_xga,
ARRAY_SIZE(ov965x_start_1_xga));
reg_w_array(gspca_dev, bridge_start_xga,
ARRAY_SIZE(bridge_start_xga));
sccb_w_array(gspca_dev, ov965x_start_2_svga,
ARRAY_SIZE(ov965x_start_2_svga));
break ;
default :
/* case SXGA_MODE: * 1280x1024 */
sccb_w_array(gspca_dev, ov965x_start_1_sxga,
ARRAY_SIZE(ov965x_start_1_sxga));
reg_w_array(gspca_dev, bridge_start_sxga,
ARRAY_SIZE(bridge_start_sxga));
sccb_w_array(gspca_dev, ov965x_start_2_sxga,
ARRAY_SIZE(ov965x_start_2_sxga));
break ;
}
reg_w(gspca_dev, 0 xe0, 0 x00);
reg_w(gspca_dev, 0 xe0, 0 x00);
set_led(gspca_dev, 1 );
return gspca_dev->usb_err;
}
static void sd_stopN(struct gspca_dev *gspca_dev)
{
if (((struct sd *)gspca_dev)->sensor == SENSOR_OV361x) {
reg_w(gspca_dev, 0 xe0, 0 x01); /* stop transfer */
/* reg_w(gspca_dev, 0x31, 0x09); */
return ;
}
reg_w(gspca_dev, 0 xe0, 0 x01);
set_led(gspca_dev, 0 );
reg_w(gspca_dev, 0 xe0, 0 x00);
}
/* Values for bmHeaderInfo (Video and Still Image Payload Headers, 2.4.3.3) */
#define UVC_STREAM_EOH (1 << 7 )
#define UVC_STREAM_ERR (1 << 6 )
#define UVC_STREAM_STI (1 << 5 )
#define UVC_STREAM_RES (1 << 4 )
#define UVC_STREAM_SCR (1 << 3 )
#define UVC_STREAM_PTS (1 << 2 )
#define UVC_STREAM_EOF (1 << 1 )
#define UVC_STREAM_FID (1 << 0 )
static void sd_pkt_scan(struct gspca_dev *gspca_dev,
u8 *data, int len)
{
struct sd *sd = (struct sd *) gspca_dev;
__u32 this_pts;
u8 this_fid;
int remaining_len = len;
int payload_len;
payload_len = gspca_dev->cam.bulk ? 2048 : 2040 ;
do {
len = min(remaining_len, payload_len);
/* Payloads are prefixed with a UVC-style header. We
consider a frame to start when the FID toggles , or the PTS
changes . A frame ends when EOF is set , and we ' ve received
the correct number of bytes. */
/* Verify UVC header. Header length is always 12 */
if (data[0 ] != 12 || len < 12 ) {
gspca_dbg(gspca_dev, D_PACK, "bad header\n" );
goto discard;
}
/* Check errors */
if (data[1 ] & UVC_STREAM_ERR) {
gspca_dbg(gspca_dev, D_PACK, "payload error\n" );
goto discard;
}
/* Extract PTS and FID */
if (!(data[1 ] & UVC_STREAM_PTS)) {
gspca_dbg(gspca_dev, D_PACK, "PTS not present\n" );
goto discard;
}
this_pts = (data[5 ] << 24 ) | (data[4 ] << 16 )
| (data[3 ] << 8 ) | data[2 ];
this_fid = data[1 ] & UVC_STREAM_FID;
/* If PTS or FID has changed, start a new frame. */
if (this_pts != sd->last_pts || this_fid != sd->last_fid) {
if (gspca_dev->last_packet_type == INTER_PACKET)
gspca_frame_add(gspca_dev, LAST_PACKET,
NULL, 0 );
sd->last_pts = this_pts;
sd->last_fid = this_fid;
gspca_frame_add(gspca_dev, FIRST_PACKET,
data + 12 , len - 12 );
/* If this packet is marked as EOF, end the frame */
} else if (data[1 ] & UVC_STREAM_EOF) {
sd->last_pts = 0 ;
gspca_frame_add(gspca_dev, LAST_PACKET,
data + 12 , len - 12 );
} else {
/* Add the data from this payload */
gspca_frame_add(gspca_dev, INTER_PACKET,
data + 12 , len - 12 );
}
/* Done this payload */
goto scan_next;
discard:
/* Discard data until a new frame starts. */
gspca_dev->last_packet_type = DISCARD_PACKET;
scan_next:
remaining_len -= len;
data += len;
} while (remaining_len > 0 );
}
static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct gspca_dev *gspca_dev =
container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
gspca_dev->usb_err = 0 ;
if (!gspca_dev->streaming)
return 0 ;
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
setbrightness(gspca_dev, ctrl->val);
break ;
case V4L2_CID_CONTRAST:
setcontrast(gspca_dev, ctrl->val);
break ;
case V4L2_CID_SATURATION:
setsatur(gspca_dev, ctrl->val);
break ;
case V4L2_CID_POWER_LINE_FREQUENCY:
setlightfreq(gspca_dev, ctrl->val);
break ;
case V4L2_CID_SHARPNESS:
setsharpness(gspca_dev, ctrl->val);
break ;
case V4L2_CID_AUTOGAIN:
if (ctrl->is_new)
setautogain(gspca_dev, ctrl->val);
if (!ctrl->val && gspca_dev->exposure->is_new)
setexposure(gspca_dev, gspca_dev->exposure->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 sd *sd = (struct sd *)gspca_dev;
struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
if (sd->sensor == SENSOR_OV971x)
return 0 ;
if (sd->sensor == SENSOR_OV361x)
return 0 ;
gspca_dev->vdev.ctrl_handler = hdl;
v4l2_ctrl_handler_init(hdl, 7 );
if (sd->sensor == SENSOR_OV562x) {
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
V4L2_CID_BRIGHTNESS, -90 , 90 , 1 , 0 );
} else {
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
V4L2_CID_BRIGHTNESS, 0 , 15 , 1 , 7 );
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
V4L2_CID_CONTRAST, 0 , 15 , 1 , 3 );
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
V4L2_CID_SATURATION, 0 , 4 , 1 , 2 );
/* -1 = auto */
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
V4L2_CID_SHARPNESS, -1 , 4 , 1 , -1 );
gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
V4L2_CID_AUTOGAIN, 0 , 1 , 1 , 1 );
gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
V4L2_CID_EXPOSURE, 0 , 3 , 1 , 0 );
v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
V4L2_CID_POWER_LINE_FREQUENCY,
V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0 , 0 );
v4l2_ctrl_auto_cluster(3 , &gspca_dev->autogain, 0 , false );
}
if (hdl->error) {
pr_err("Could not initialize controls\n" );
return hdl->error;
}
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,
.start = sd_start,
.stopN = sd_stopN,
.pkt_scan = sd_pkt_scan,
};
/* -- module initialisation -- */
static const struct usb_device_id device_table[] = {
{USB_DEVICE(0 x05a9, 0 x8065)},
{USB_DEVICE(0 x06f8, 0 x3003)},
{USB_DEVICE(0 x05a9, 0 x1550)},
{}
};
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=82 H=95 G=88
¤ Dauer der Verarbeitung: 0.64 Sekunden
(vorverarbeitet am 2026-09-28)
¤
*© Formatika GbR, Deutschland