forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
979 lines
26 KiB
979 lines
26 KiB
// SPDX-License-Identifier: GPL-2.0-or-later |
|
/* |
|
* SPCA500 chip based cameras initialization data |
|
* |
|
* V4L2 by Jean-Francois Moine <http://moinejf.free.fr> |
|
*/ |
|
|
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
|
|
|
#define MODULE_NAME "spca500" |
|
|
|
#include "gspca.h" |
|
#include "jpeg.h" |
|
|
|
MODULE_AUTHOR("Michel Xhaard <[email protected]>"); |
|
MODULE_DESCRIPTION("GSPCA/SPCA500 USB Camera Driver"); |
|
MODULE_LICENSE("GPL"); |
|
|
|
#define QUALITY 85 |
|
|
|
/* specific webcam descriptor */ |
|
struct sd { |
|
struct gspca_dev gspca_dev; /* !! must be the first item */ |
|
|
|
char subtype; |
|
#define AgfaCl20 0 |
|
#define AiptekPocketDV 1 |
|
#define BenqDC1016 2 |
|
#define CreativePCCam300 3 |
|
#define DLinkDSC350 4 |
|
#define Gsmartmini 5 |
|
#define IntelPocketPCCamera 6 |
|
#define KodakEZ200 7 |
|
#define LogitechClickSmart310 8 |
|
#define LogitechClickSmart510 9 |
|
#define LogitechTraveler 10 |
|
#define MustekGsmart300 11 |
|
#define Optimedia 12 |
|
#define PalmPixDC85 13 |
|
#define ToptroIndus 14 |
|
|
|
u8 jpeg_hdr[JPEG_HDR_SZ]; |
|
}; |
|
|
|
static const struct v4l2_pix_format vga_mode[] = { |
|
{320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, |
|
.bytesperline = 320, |
|
.sizeimage = 320 * 240 * 3 / 8 + 590, |
|
.colorspace = V4L2_COLORSPACE_JPEG, |
|
.priv = 1}, |
|
{640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, |
|
.bytesperline = 640, |
|
.sizeimage = 640 * 480 * 3 / 8 + 590, |
|
.colorspace = V4L2_COLORSPACE_JPEG, |
|
.priv = 0}, |
|
}; |
|
|
|
static const struct v4l2_pix_format sif_mode[] = { |
|
{176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, |
|
.bytesperline = 176, |
|
.sizeimage = 176 * 144 * 3 / 8 + 590, |
|
.colorspace = V4L2_COLORSPACE_JPEG, |
|
.priv = 1}, |
|
{352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, |
|
.bytesperline = 352, |
|
.sizeimage = 352 * 288 * 3 / 8 + 590, |
|
.colorspace = V4L2_COLORSPACE_JPEG, |
|
.priv = 0}, |
|
}; |
|
|
|
/* Frame packet header offsets for the spca500 */ |
|
#define SPCA500_OFFSET_PADDINGLB 2 |
|
#define SPCA500_OFFSET_PADDINGHB 3 |
|
#define SPCA500_OFFSET_MODE 4 |
|
#define SPCA500_OFFSET_IMGWIDTH 5 |
|
#define SPCA500_OFFSET_IMGHEIGHT 6 |
|
#define SPCA500_OFFSET_IMGMODE 7 |
|
#define SPCA500_OFFSET_QTBLINDEX 8 |
|
#define SPCA500_OFFSET_FRAMSEQ 9 |
|
#define SPCA500_OFFSET_CDSPINFO 10 |
|
#define SPCA500_OFFSET_GPIO 11 |
|
#define SPCA500_OFFSET_AUGPIO 12 |
|
#define SPCA500_OFFSET_DATA 16 |
|
|
|
|
|
static const __u16 spca500_visual_defaults[][3] = { |
|
{0x00, 0x0003, 0x816b}, /* SSI not active sync with vsync, |
|
* hue (H byte) = 0, |
|
* saturation/hue enable, |
|
* brightness/contrast enable. |
|
*/ |
|
{0x00, 0x0000, 0x8167}, /* brightness = 0 */ |
|
{0x00, 0x0020, 0x8168}, /* contrast = 0 */ |
|
{0x00, 0x0003, 0x816b}, /* SSI not active sync with vsync, |
|
* hue (H byte) = 0, saturation/hue enable, |
|
* brightness/contrast enable. |
|
* was 0x0003, now 0x0000. |
|
*/ |
|
{0x00, 0x0000, 0x816a}, /* hue (L byte) = 0 */ |
|
{0x00, 0x0020, 0x8169}, /* saturation = 0x20 */ |
|
{0x00, 0x0050, 0x8157}, /* edge gain high threshold */ |
|
{0x00, 0x0030, 0x8158}, /* edge gain low threshold */ |
|
{0x00, 0x0028, 0x8159}, /* edge bandwidth high threshold */ |
|
{0x00, 0x000a, 0x815a}, /* edge bandwidth low threshold */ |
|
{0x00, 0x0001, 0x8202}, /* clock rate compensation = 1/25 sec/frame */ |
|
{0x0c, 0x0004, 0x0000}, |
|
/* set interface */ |
|
{} |
|
}; |
|
static const __u16 Clicksmart510_defaults[][3] = { |
|
{0x00, 0x00, 0x8211}, |
|
{0x00, 0x01, 0x82c0}, |
|
{0x00, 0x10, 0x82cb}, |
|
{0x00, 0x0f, 0x800d}, |
|
{0x00, 0x82, 0x8225}, |
|
{0x00, 0x21, 0x8228}, |
|
{0x00, 0x00, 0x8203}, |
|
{0x00, 0x00, 0x8204}, |
|
{0x00, 0x08, 0x8205}, |
|
{0x00, 0xf8, 0x8206}, |
|
{0x00, 0x28, 0x8207}, |
|
{0x00, 0xa0, 0x8208}, |
|
{0x00, 0x08, 0x824a}, |
|
{0x00, 0x08, 0x8214}, |
|
{0x00, 0x80, 0x82c1}, |
|
{0x00, 0x00, 0x82c2}, |
|
{0x00, 0x00, 0x82ca}, |
|
{0x00, 0x80, 0x82c1}, |
|
{0x00, 0x04, 0x82c2}, |
|
{0x00, 0x00, 0x82ca}, |
|
{0x00, 0xfc, 0x8100}, |
|
{0x00, 0xfc, 0x8105}, |
|
{0x00, 0x30, 0x8101}, |
|
{0x00, 0x00, 0x8102}, |
|
{0x00, 0x00, 0x8103}, |
|
{0x00, 0x66, 0x8107}, |
|
{0x00, 0x00, 0x816b}, |
|
{0x00, 0x00, 0x8155}, |
|
{0x00, 0x01, 0x8156}, |
|
{0x00, 0x60, 0x8157}, |
|
{0x00, 0x40, 0x8158}, |
|
{0x00, 0x0a, 0x8159}, |
|
{0x00, 0x06, 0x815a}, |
|
{0x00, 0x00, 0x813f}, |
|
{0x00, 0x00, 0x8200}, |
|
{0x00, 0x19, 0x8201}, |
|
{0x00, 0x00, 0x82c1}, |
|
{0x00, 0xa0, 0x82c2}, |
|
{0x00, 0x00, 0x82ca}, |
|
{0x00, 0x00, 0x8117}, |
|
{0x00, 0x00, 0x8118}, |
|
{0x00, 0x65, 0x8119}, |
|
{0x00, 0x00, 0x811a}, |
|
{0x00, 0x00, 0x811b}, |
|
{0x00, 0x55, 0x811c}, |
|
{0x00, 0x65, 0x811d}, |
|
{0x00, 0x55, 0x811e}, |
|
{0x00, 0x16, 0x811f}, |
|
{0x00, 0x19, 0x8120}, |
|
{0x00, 0x80, 0x8103}, |
|
{0x00, 0x83, 0x816b}, |
|
{0x00, 0x25, 0x8168}, |
|
{0x00, 0x01, 0x820f}, |
|
{0x00, 0xff, 0x8115}, |
|
{0x00, 0x48, 0x8116}, |
|
{0x00, 0x50, 0x8151}, |
|
{0x00, 0x40, 0x8152}, |
|
{0x00, 0x78, 0x8153}, |
|
{0x00, 0x40, 0x8154}, |
|
{0x00, 0x00, 0x8167}, |
|
{0x00, 0x20, 0x8168}, |
|
{0x00, 0x00, 0x816a}, |
|
{0x00, 0x03, 0x816b}, |
|
{0x00, 0x20, 0x8169}, |
|
{0x00, 0x60, 0x8157}, |
|
{0x00, 0x00, 0x8190}, |
|
{0x00, 0x00, 0x81a1}, |
|
{0x00, 0x00, 0x81b2}, |
|
{0x00, 0x27, 0x8191}, |
|
{0x00, 0x27, 0x81a2}, |
|
{0x00, 0x27, 0x81b3}, |
|
{0x00, 0x4b, 0x8192}, |
|
{0x00, 0x4b, 0x81a3}, |
|
{0x00, 0x4b, 0x81b4}, |
|
{0x00, 0x66, 0x8193}, |
|
{0x00, 0x66, 0x81a4}, |
|
{0x00, 0x66, 0x81b5}, |
|
{0x00, 0x79, 0x8194}, |
|
{0x00, 0x79, 0x81a5}, |
|
{0x00, 0x79, 0x81b6}, |
|
{0x00, 0x8a, 0x8195}, |
|
{0x00, 0x8a, 0x81a6}, |
|
{0x00, 0x8a, 0x81b7}, |
|
{0x00, 0x9b, 0x8196}, |
|
{0x00, 0x9b, 0x81a7}, |
|
{0x00, 0x9b, 0x81b8}, |
|
{0x00, 0xa6, 0x8197}, |
|
{0x00, 0xa6, 0x81a8}, |
|
{0x00, 0xa6, 0x81b9}, |
|
{0x00, 0xb2, 0x8198}, |
|
{0x00, 0xb2, 0x81a9}, |
|
{0x00, 0xb2, 0x81ba}, |
|
{0x00, 0xbe, 0x8199}, |
|
{0x00, 0xbe, 0x81aa}, |
|
{0x00, 0xbe, 0x81bb}, |
|
{0x00, 0xc8, 0x819a}, |
|
{0x00, 0xc8, 0x81ab}, |
|
{0x00, 0xc8, 0x81bc}, |
|
{0x00, 0xd2, 0x819b}, |
|
{0x00, 0xd2, 0x81ac}, |
|
{0x00, 0xd2, 0x81bd}, |
|
{0x00, 0xdb, 0x819c}, |
|
{0x00, 0xdb, 0x81ad}, |
|
{0x00, 0xdb, 0x81be}, |
|
{0x00, 0xe4, 0x819d}, |
|
{0x00, 0xe4, 0x81ae}, |
|
{0x00, 0xe4, 0x81bf}, |
|
{0x00, 0xed, 0x819e}, |
|
{0x00, 0xed, 0x81af}, |
|
{0x00, 0xed, 0x81c0}, |
|
{0x00, 0xf7, 0x819f}, |
|
{0x00, 0xf7, 0x81b0}, |
|
{0x00, 0xf7, 0x81c1}, |
|
{0x00, 0xff, 0x81a0}, |
|
{0x00, 0xff, 0x81b1}, |
|
{0x00, 0xff, 0x81c2}, |
|
{0x00, 0x03, 0x8156}, |
|
{0x00, 0x00, 0x8211}, |
|
{0x00, 0x20, 0x8168}, |
|
{0x00, 0x01, 0x8202}, |
|
{0x00, 0x30, 0x8101}, |
|
{0x00, 0x00, 0x8111}, |
|
{0x00, 0x00, 0x8112}, |
|
{0x00, 0x00, 0x8113}, |
|
{0x00, 0x00, 0x8114}, |
|
{} |
|
}; |
|
|
|
static const __u8 qtable_creative_pccam[2][64] = { |
|
{ /* Q-table Y-components */ |
|
0x05, 0x03, 0x03, 0x05, 0x07, 0x0c, 0x0f, 0x12, |
|
0x04, 0x04, 0x04, 0x06, 0x08, 0x11, 0x12, 0x11, |
|
0x04, 0x04, 0x05, 0x07, 0x0c, 0x11, 0x15, 0x11, |
|
0x04, 0x05, 0x07, 0x09, 0x0f, 0x1a, 0x18, 0x13, |
|
0x05, 0x07, 0x0b, 0x11, 0x14, 0x21, 0x1f, 0x17, |
|
0x07, 0x0b, 0x11, 0x13, 0x18, 0x1f, 0x22, 0x1c, |
|
0x0f, 0x13, 0x17, 0x1a, 0x1f, 0x24, 0x24, 0x1e, |
|
0x16, 0x1c, 0x1d, 0x1d, 0x22, 0x1e, 0x1f, 0x1e}, |
|
{ /* Q-table C-components */ |
|
0x05, 0x05, 0x07, 0x0e, 0x1e, 0x1e, 0x1e, 0x1e, |
|
0x05, 0x06, 0x08, 0x14, 0x1e, 0x1e, 0x1e, 0x1e, |
|
0x07, 0x08, 0x11, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, |
|
0x0e, 0x14, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, |
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, |
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, |
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, |
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e} |
|
}; |
|
|
|
static const __u8 qtable_kodak_ez200[2][64] = { |
|
{ /* Q-table Y-components */ |
|
0x02, 0x01, 0x01, 0x02, 0x02, 0x04, 0x05, 0x06, |
|
0x01, 0x01, 0x01, 0x02, 0x03, 0x06, 0x06, 0x06, |
|
0x01, 0x01, 0x02, 0x02, 0x04, 0x06, 0x07, 0x06, |
|
0x01, 0x02, 0x02, 0x03, 0x05, 0x09, 0x08, 0x06, |
|
0x02, 0x02, 0x04, 0x06, 0x07, 0x0b, 0x0a, 0x08, |
|
0x02, 0x04, 0x06, 0x06, 0x08, 0x0a, 0x0b, 0x09, |
|
0x05, 0x06, 0x08, 0x09, 0x0a, 0x0c, 0x0c, 0x0a, |
|
0x07, 0x09, 0x0a, 0x0a, 0x0b, 0x0a, 0x0a, 0x0a}, |
|
{ /* Q-table C-components */ |
|
0x02, 0x02, 0x02, 0x05, 0x0a, 0x0a, 0x0a, 0x0a, |
|
0x02, 0x02, 0x03, 0x07, 0x0a, 0x0a, 0x0a, 0x0a, |
|
0x02, 0x03, 0x06, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, |
|
0x05, 0x07, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, |
|
0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, |
|
0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, |
|
0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, |
|
0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a} |
|
}; |
|
|
|
static const __u8 qtable_pocketdv[2][64] = { |
|
{ /* Q-table Y-components start registers 0x8800 */ |
|
0x06, 0x04, 0x04, 0x06, 0x0a, 0x10, 0x14, 0x18, |
|
0x05, 0x05, 0x06, 0x08, 0x0a, 0x17, 0x18, 0x16, |
|
0x06, 0x05, 0x06, 0x0a, 0x10, 0x17, 0x1c, 0x16, |
|
0x06, 0x07, 0x09, 0x0c, 0x14, 0x23, 0x20, 0x19, |
|
0x07, 0x09, 0x0f, 0x16, 0x1b, 0x2c, 0x29, 0x1f, |
|
0x0a, 0x0e, 0x16, 0x1a, 0x20, 0x2a, 0x2d, 0x25, |
|
0x14, 0x1a, 0x1f, 0x23, 0x29, 0x30, 0x30, 0x28, |
|
0x1d, 0x25, 0x26, 0x27, 0x2d, 0x28, 0x29, 0x28, |
|
}, |
|
{ /* Q-table C-components start registers 0x8840 */ |
|
0x07, 0x07, 0x0a, 0x13, 0x28, 0x28, 0x28, 0x28, |
|
0x07, 0x08, 0x0a, 0x1a, 0x28, 0x28, 0x28, 0x28, |
|
0x0a, 0x0a, 0x16, 0x28, 0x28, 0x28, 0x28, 0x28, |
|
0x13, 0x1a, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, |
|
0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, |
|
0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, |
|
0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, |
|
0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28} |
|
}; |
|
|
|
/* read 'len' bytes to gspca_dev->usb_buf */ |
|
static void reg_r(struct gspca_dev *gspca_dev, |
|
__u16 index, |
|
__u16 length) |
|
{ |
|
usb_control_msg(gspca_dev->dev, |
|
usb_rcvctrlpipe(gspca_dev->dev, 0), |
|
0, |
|
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, |
|
0, /* value */ |
|
index, gspca_dev->usb_buf, length, 500); |
|
} |
|
|
|
static int reg_w(struct gspca_dev *gspca_dev, |
|
__u16 req, __u16 index, __u16 value) |
|
{ |
|
int ret; |
|
|
|
gspca_dbg(gspca_dev, D_USBO, "reg write: [0x%02x] = 0x%02x\n", |
|
index, value); |
|
ret = usb_control_msg(gspca_dev->dev, |
|
usb_sndctrlpipe(gspca_dev->dev, 0), |
|
req, |
|
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, |
|
value, index, NULL, 0, 500); |
|
if (ret < 0) |
|
pr_err("reg write: error %d\n", ret); |
|
return ret; |
|
} |
|
|
|
/* returns: negative is error, pos or zero is data */ |
|
static int reg_r_12(struct gspca_dev *gspca_dev, |
|
__u16 req, /* bRequest */ |
|
__u16 index, /* wIndex */ |
|
__u16 length) /* wLength (1 or 2 only) */ |
|
{ |
|
int ret; |
|
|
|
gspca_dev->usb_buf[1] = 0; |
|
ret = usb_control_msg(gspca_dev->dev, |
|
usb_rcvctrlpipe(gspca_dev->dev, 0), |
|
req, |
|
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, |
|
0, /* value */ |
|
index, |
|
gspca_dev->usb_buf, length, |
|
500); /* timeout */ |
|
if (ret < 0) { |
|
pr_err("reg_r_12 err %d\n", ret); |
|
return ret; |
|
} |
|
return (gspca_dev->usb_buf[1] << 8) + gspca_dev->usb_buf[0]; |
|
} |
|
|
|
/* |
|
* Simple function to wait for a given 8-bit value to be returned from |
|
* a reg_read call. |
|
* Returns: negative is error or timeout, zero is success. |
|
*/ |
|
static int reg_r_wait(struct gspca_dev *gspca_dev, |
|
__u16 reg, __u16 index, __u16 value) |
|
{ |
|
int ret, cnt = 20; |
|
|
|
while (--cnt > 0) { |
|
ret = reg_r_12(gspca_dev, reg, index, 1); |
|
if (ret == value) |
|
return 0; |
|
msleep(50); |
|
} |
|
return -EIO; |
|
} |
|
|
|
static int write_vector(struct gspca_dev *gspca_dev, |
|
const __u16 data[][3]) |
|
{ |
|
int ret, i = 0; |
|
|
|
while (data[i][0] != 0 || data[i][1] != 0 || data[i][2] != 0) { |
|
ret = reg_w(gspca_dev, data[i][0], data[i][2], data[i][1]); |
|
if (ret < 0) |
|
return ret; |
|
i++; |
|
} |
|
return 0; |
|
} |
|
|
|
static int spca50x_setup_qtable(struct gspca_dev *gspca_dev, |
|
unsigned int request, |
|
unsigned int ybase, |
|
unsigned int cbase, |
|
const __u8 qtable[2][64]) |
|
{ |
|
int i, err; |
|
|
|
/* loop over y components */ |
|
for (i = 0; i < 64; i++) { |
|
err = reg_w(gspca_dev, request, ybase + i, qtable[0][i]); |
|
if (err < 0) |
|
return err; |
|
} |
|
|
|
/* loop over c components */ |
|
for (i = 0; i < 64; i++) { |
|
err = reg_w(gspca_dev, request, cbase + i, qtable[1][i]); |
|
if (err < 0) |
|
return err; |
|
} |
|
return 0; |
|
} |
|
|
|
static void spca500_ping310(struct gspca_dev *gspca_dev) |
|
{ |
|
reg_r(gspca_dev, 0x0d04, 2); |
|
gspca_dbg(gspca_dev, D_STREAM, "ClickSmart310 ping 0x0d04 0x%02x 0x%02x\n", |
|
gspca_dev->usb_buf[0], gspca_dev->usb_buf[1]); |
|
} |
|
|
|
static void spca500_clksmart310_init(struct gspca_dev *gspca_dev) |
|
{ |
|
reg_r(gspca_dev, 0x0d05, 2); |
|
gspca_dbg(gspca_dev, D_STREAM, "ClickSmart310 init 0x0d05 0x%02x 0x%02x\n", |
|
gspca_dev->usb_buf[0], gspca_dev->usb_buf[1]); |
|
reg_w(gspca_dev, 0x00, 0x8167, 0x5a); |
|
spca500_ping310(gspca_dev); |
|
|
|
reg_w(gspca_dev, 0x00, 0x8168, 0x22); |
|
reg_w(gspca_dev, 0x00, 0x816a, 0xc0); |
|
reg_w(gspca_dev, 0x00, 0x816b, 0x0b); |
|
reg_w(gspca_dev, 0x00, 0x8169, 0x25); |
|
reg_w(gspca_dev, 0x00, 0x8157, 0x5b); |
|
reg_w(gspca_dev, 0x00, 0x8158, 0x5b); |
|
reg_w(gspca_dev, 0x00, 0x813f, 0x03); |
|
reg_w(gspca_dev, 0x00, 0x8151, 0x4a); |
|
reg_w(gspca_dev, 0x00, 0x8153, 0x78); |
|
reg_w(gspca_dev, 0x00, 0x0d01, 0x04); |
|
/* 00 for adjust shutter */ |
|
reg_w(gspca_dev, 0x00, 0x0d02, 0x01); |
|
reg_w(gspca_dev, 0x00, 0x8169, 0x25); |
|
reg_w(gspca_dev, 0x00, 0x0d01, 0x02); |
|
} |
|
|
|
static void spca500_setmode(struct gspca_dev *gspca_dev, |
|
__u8 xmult, __u8 ymult) |
|
{ |
|
int mode; |
|
|
|
/* set x multiplier */ |
|
reg_w(gspca_dev, 0, 0x8001, xmult); |
|
|
|
/* set y multiplier */ |
|
reg_w(gspca_dev, 0, 0x8002, ymult); |
|
|
|
/* use compressed mode, VGA, with mode specific subsample */ |
|
mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; |
|
reg_w(gspca_dev, 0, 0x8003, mode << 4); |
|
} |
|
|
|
static int spca500_full_reset(struct gspca_dev *gspca_dev) |
|
{ |
|
int err; |
|
|
|
/* send the reset command */ |
|
err = reg_w(gspca_dev, 0xe0, 0x0001, 0x0000); |
|
if (err < 0) |
|
return err; |
|
|
|
/* wait for the reset to complete */ |
|
err = reg_r_wait(gspca_dev, 0x06, 0x0000, 0x0000); |
|
if (err < 0) |
|
return err; |
|
err = reg_w(gspca_dev, 0xe0, 0x0000, 0x0000); |
|
if (err < 0) |
|
return err; |
|
err = reg_r_wait(gspca_dev, 0x06, 0, 0); |
|
if (err < 0) { |
|
gspca_err(gspca_dev, "reg_r_wait() failed\n"); |
|
return err; |
|
} |
|
/* all ok */ |
|
return 0; |
|
} |
|
|
|
/* Synchro the Bridge with sensor */ |
|
/* Maybe that will work on all spca500 chip */ |
|
/* because i only own a clicksmart310 try for that chip */ |
|
/* using spca50x_set_packet_size() cause an Ooops here */ |
|
/* usb_set_interface from kernel 2.6.x clear all the urb stuff */ |
|
/* up-port the same feature as in 2.4.x kernel */ |
|
static int spca500_synch310(struct gspca_dev *gspca_dev) |
|
{ |
|
if (usb_set_interface(gspca_dev->dev, gspca_dev->iface, 0) < 0) { |
|
gspca_err(gspca_dev, "Set packet size: set interface error\n"); |
|
goto error; |
|
} |
|
spca500_ping310(gspca_dev); |
|
|
|
reg_r(gspca_dev, 0x0d00, 1); |
|
|
|
/* need alt setting here */ |
|
gspca_dbg(gspca_dev, D_PACK, "ClickSmart310 sync alt: %d\n", |
|
gspca_dev->alt); |
|
|
|
/* Windoze use pipe with altsetting 6 why 7 here */ |
|
if (usb_set_interface(gspca_dev->dev, |
|
gspca_dev->iface, |
|
gspca_dev->alt) < 0) { |
|
gspca_err(gspca_dev, "Set packet size: set interface error\n"); |
|
goto error; |
|
} |
|
return 0; |
|
error: |
|
return -EBUSY; |
|
} |
|
|
|
static void spca500_reinit(struct gspca_dev *gspca_dev) |
|
{ |
|
int err; |
|
__u8 Data; |
|
|
|
/* some unknown command from Aiptek pocket dv and family300 */ |
|
|
|
reg_w(gspca_dev, 0x00, 0x0d01, 0x01); |
|
reg_w(gspca_dev, 0x00, 0x0d03, 0x00); |
|
reg_w(gspca_dev, 0x00, 0x0d02, 0x01); |
|
|
|
/* enable drop packet */ |
|
reg_w(gspca_dev, 0x00, 0x850a, 0x0001); |
|
|
|
err = spca50x_setup_qtable(gspca_dev, 0x00, 0x8800, 0x8840, |
|
qtable_pocketdv); |
|
if (err < 0) |
|
gspca_err(gspca_dev, "spca50x_setup_qtable failed on init\n"); |
|
|
|
/* set qtable index */ |
|
reg_w(gspca_dev, 0x00, 0x8880, 2); |
|
/* family cam Quicksmart stuff */ |
|
reg_w(gspca_dev, 0x00, 0x800a, 0x00); |
|
/* Set agc transfer: synced between frames */ |
|
reg_w(gspca_dev, 0x00, 0x820f, 0x01); |
|
/* Init SDRAM - needed for SDRAM access */ |
|
reg_w(gspca_dev, 0x00, 0x870a, 0x04); |
|
/*Start init sequence or stream */ |
|
reg_w(gspca_dev, 0, 0x8003, 0x00); |
|
/* switch to video camera mode */ |
|
reg_w(gspca_dev, 0x00, 0x8000, 0x0004); |
|
msleep(2000); |
|
if (reg_r_wait(gspca_dev, 0, 0x8000, 0x44) != 0) { |
|
reg_r(gspca_dev, 0x816b, 1); |
|
Data = gspca_dev->usb_buf[0]; |
|
reg_w(gspca_dev, 0x00, 0x816b, Data); |
|
} |
|
} |
|
|
|
/* this function is called at probe time */ |
|
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; |
|
|
|
cam = &gspca_dev->cam; |
|
sd->subtype = id->driver_info; |
|
if (sd->subtype != LogitechClickSmart310) { |
|
cam->cam_mode = vga_mode; |
|
cam->nmodes = ARRAY_SIZE(vga_mode); |
|
} else { |
|
cam->cam_mode = sif_mode; |
|
cam->nmodes = ARRAY_SIZE(sif_mode); |
|
} |
|
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; |
|
|
|
/* initialisation of spca500 based cameras is deferred */ |
|
gspca_dbg(gspca_dev, D_STREAM, "SPCA500 init\n"); |
|
if (sd->subtype == LogitechClickSmart310) |
|
spca500_clksmart310_init(gspca_dev); |
|
/* else |
|
spca500_initialise(gspca_dev); */ |
|
gspca_dbg(gspca_dev, D_STREAM, "SPCA500 init done\n"); |
|
return 0; |
|
} |
|
|
|
static int sd_start(struct gspca_dev *gspca_dev) |
|
{ |
|
struct sd *sd = (struct sd *) gspca_dev; |
|
int err; |
|
__u8 Data; |
|
__u8 xmult, ymult; |
|
|
|
/* create the JPEG header */ |
|
jpeg_define(sd->jpeg_hdr, gspca_dev->pixfmt.height, |
|
gspca_dev->pixfmt.width, |
|
0x22); /* JPEG 411 */ |
|
jpeg_set_qual(sd->jpeg_hdr, QUALITY); |
|
|
|
if (sd->subtype == LogitechClickSmart310) { |
|
xmult = 0x16; |
|
ymult = 0x12; |
|
} else { |
|
xmult = 0x28; |
|
ymult = 0x1e; |
|
} |
|
|
|
/* is there a sensor here ? */ |
|
reg_r(gspca_dev, 0x8a04, 1); |
|
gspca_dbg(gspca_dev, D_STREAM, "Spca500 Sensor Address 0x%02x\n", |
|
gspca_dev->usb_buf[0]); |
|
gspca_dbg(gspca_dev, D_STREAM, "Spca500 curr_mode: %d Xmult: 0x%02x, Ymult: 0x%02x", |
|
gspca_dev->curr_mode, xmult, ymult); |
|
|
|
/* setup qtable */ |
|
switch (sd->subtype) { |
|
case LogitechClickSmart310: |
|
spca500_setmode(gspca_dev, xmult, ymult); |
|
|
|
/* enable drop packet */ |
|
reg_w(gspca_dev, 0x00, 0x850a, 0x0001); |
|
reg_w(gspca_dev, 0x00, 0x8880, 3); |
|
err = spca50x_setup_qtable(gspca_dev, |
|
0x00, 0x8800, 0x8840, |
|
qtable_creative_pccam); |
|
if (err < 0) |
|
gspca_err(gspca_dev, "spca50x_setup_qtable failed\n"); |
|
/* Init SDRAM - needed for SDRAM access */ |
|
reg_w(gspca_dev, 0x00, 0x870a, 0x04); |
|
|
|
/* switch to video camera mode */ |
|
reg_w(gspca_dev, 0x00, 0x8000, 0x0004); |
|
msleep(500); |
|
if (reg_r_wait(gspca_dev, 0, 0x8000, 0x44) != 0) |
|
gspca_err(gspca_dev, "reg_r_wait() failed\n"); |
|
|
|
reg_r(gspca_dev, 0x816b, 1); |
|
Data = gspca_dev->usb_buf[0]; |
|
reg_w(gspca_dev, 0x00, 0x816b, Data); |
|
|
|
spca500_synch310(gspca_dev); |
|
|
|
write_vector(gspca_dev, spca500_visual_defaults); |
|
spca500_setmode(gspca_dev, xmult, ymult); |
|
/* enable drop packet */ |
|
err = reg_w(gspca_dev, 0x00, 0x850a, 0x0001); |
|
if (err < 0) |
|
gspca_err(gspca_dev, "failed to enable drop packet\n"); |
|
reg_w(gspca_dev, 0x00, 0x8880, 3); |
|
err = spca50x_setup_qtable(gspca_dev, |
|
0x00, 0x8800, 0x8840, |
|
qtable_creative_pccam); |
|
if (err < 0) |
|
gspca_err(gspca_dev, "spca50x_setup_qtable failed\n"); |
|
|
|
/* Init SDRAM - needed for SDRAM access */ |
|
reg_w(gspca_dev, 0x00, 0x870a, 0x04); |
|
|
|
/* switch to video camera mode */ |
|
reg_w(gspca_dev, 0x00, 0x8000, 0x0004); |
|
|
|
if (reg_r_wait(gspca_dev, 0, 0x8000, 0x44) != 0) |
|
gspca_err(gspca_dev, "reg_r_wait() failed\n"); |
|
|
|
reg_r(gspca_dev, 0x816b, 1); |
|
Data = gspca_dev->usb_buf[0]; |
|
reg_w(gspca_dev, 0x00, 0x816b, Data); |
|
break; |
|
case CreativePCCam300: /* Creative PC-CAM 300 640x480 CCD */ |
|
case IntelPocketPCCamera: /* FIXME: Temporary fix for |
|
* Intel Pocket PC Camera |
|
* - NWG (Sat 29th March 2003) */ |
|
|
|
/* do a full reset */ |
|
err = spca500_full_reset(gspca_dev); |
|
if (err < 0) |
|
gspca_err(gspca_dev, "spca500_full_reset failed\n"); |
|
|
|
/* enable drop packet */ |
|
err = reg_w(gspca_dev, 0x00, 0x850a, 0x0001); |
|
if (err < 0) |
|
gspca_err(gspca_dev, "failed to enable drop packet\n"); |
|
reg_w(gspca_dev, 0x00, 0x8880, 3); |
|
err = spca50x_setup_qtable(gspca_dev, |
|
0x00, 0x8800, 0x8840, |
|
qtable_creative_pccam); |
|
if (err < 0) |
|
gspca_err(gspca_dev, "spca50x_setup_qtable failed\n"); |
|
|
|
spca500_setmode(gspca_dev, xmult, ymult); |
|
reg_w(gspca_dev, 0x20, 0x0001, 0x0004); |
|
|
|
/* switch to video camera mode */ |
|
reg_w(gspca_dev, 0x00, 0x8000, 0x0004); |
|
|
|
if (reg_r_wait(gspca_dev, 0, 0x8000, 0x44) != 0) |
|
gspca_err(gspca_dev, "reg_r_wait() failed\n"); |
|
|
|
reg_r(gspca_dev, 0x816b, 1); |
|
Data = gspca_dev->usb_buf[0]; |
|
reg_w(gspca_dev, 0x00, 0x816b, Data); |
|
|
|
/* write_vector(gspca_dev, spca500_visual_defaults); */ |
|
break; |
|
case KodakEZ200: /* Kodak EZ200 */ |
|
|
|
/* do a full reset */ |
|
err = spca500_full_reset(gspca_dev); |
|
if (err < 0) |
|
gspca_err(gspca_dev, "spca500_full_reset failed\n"); |
|
/* enable drop packet */ |
|
reg_w(gspca_dev, 0x00, 0x850a, 0x0001); |
|
reg_w(gspca_dev, 0x00, 0x8880, 0); |
|
err = spca50x_setup_qtable(gspca_dev, |
|
0x00, 0x8800, 0x8840, |
|
qtable_kodak_ez200); |
|
if (err < 0) |
|
gspca_err(gspca_dev, "spca50x_setup_qtable failed\n"); |
|
spca500_setmode(gspca_dev, xmult, ymult); |
|
|
|
reg_w(gspca_dev, 0x20, 0x0001, 0x0004); |
|
|
|
/* switch to video camera mode */ |
|
reg_w(gspca_dev, 0x00, 0x8000, 0x0004); |
|
|
|
if (reg_r_wait(gspca_dev, 0, 0x8000, 0x44) != 0) |
|
gspca_err(gspca_dev, "reg_r_wait() failed\n"); |
|
|
|
reg_r(gspca_dev, 0x816b, 1); |
|
Data = gspca_dev->usb_buf[0]; |
|
reg_w(gspca_dev, 0x00, 0x816b, Data); |
|
|
|
/* write_vector(gspca_dev, spca500_visual_defaults); */ |
|
break; |
|
|
|
case BenqDC1016: |
|
case DLinkDSC350: /* FamilyCam 300 */ |
|
case AiptekPocketDV: /* Aiptek PocketDV */ |
|
case Gsmartmini: /*Mustek Gsmart Mini */ |
|
case MustekGsmart300: /* Mustek Gsmart 300 */ |
|
case PalmPixDC85: |
|
case Optimedia: |
|
case ToptroIndus: |
|
case AgfaCl20: |
|
spca500_reinit(gspca_dev); |
|
reg_w(gspca_dev, 0x00, 0x0d01, 0x01); |
|
/* enable drop packet */ |
|
reg_w(gspca_dev, 0x00, 0x850a, 0x0001); |
|
|
|
err = spca50x_setup_qtable(gspca_dev, |
|
0x00, 0x8800, 0x8840, qtable_pocketdv); |
|
if (err < 0) |
|
gspca_err(gspca_dev, "spca50x_setup_qtable failed\n"); |
|
reg_w(gspca_dev, 0x00, 0x8880, 2); |
|
|
|
/* familycam Quicksmart pocketDV stuff */ |
|
reg_w(gspca_dev, 0x00, 0x800a, 0x00); |
|
/* Set agc transfer: synced between frames */ |
|
reg_w(gspca_dev, 0x00, 0x820f, 0x01); |
|
/* Init SDRAM - needed for SDRAM access */ |
|
reg_w(gspca_dev, 0x00, 0x870a, 0x04); |
|
|
|
spca500_setmode(gspca_dev, xmult, ymult); |
|
/* switch to video camera mode */ |
|
reg_w(gspca_dev, 0x00, 0x8000, 0x0004); |
|
|
|
reg_r_wait(gspca_dev, 0, 0x8000, 0x44); |
|
|
|
reg_r(gspca_dev, 0x816b, 1); |
|
Data = gspca_dev->usb_buf[0]; |
|
reg_w(gspca_dev, 0x00, 0x816b, Data); |
|
break; |
|
case LogitechTraveler: |
|
case LogitechClickSmart510: |
|
reg_w(gspca_dev, 0x02, 0x00, 0x00); |
|
/* enable drop packet */ |
|
reg_w(gspca_dev, 0x00, 0x850a, 0x0001); |
|
|
|
err = spca50x_setup_qtable(gspca_dev, |
|
0x00, 0x8800, |
|
0x8840, qtable_creative_pccam); |
|
if (err < 0) |
|
gspca_err(gspca_dev, "spca50x_setup_qtable failed\n"); |
|
reg_w(gspca_dev, 0x00, 0x8880, 3); |
|
reg_w(gspca_dev, 0x00, 0x800a, 0x00); |
|
/* Init SDRAM - needed for SDRAM access */ |
|
reg_w(gspca_dev, 0x00, 0x870a, 0x04); |
|
|
|
spca500_setmode(gspca_dev, xmult, ymult); |
|
|
|
/* switch to video camera mode */ |
|
reg_w(gspca_dev, 0x00, 0x8000, 0x0004); |
|
reg_r_wait(gspca_dev, 0, 0x8000, 0x44); |
|
|
|
reg_r(gspca_dev, 0x816b, 1); |
|
Data = gspca_dev->usb_buf[0]; |
|
reg_w(gspca_dev, 0x00, 0x816b, Data); |
|
write_vector(gspca_dev, Clicksmart510_defaults); |
|
break; |
|
} |
|
return 0; |
|
} |
|
|
|
static void sd_stopN(struct gspca_dev *gspca_dev) |
|
{ |
|
reg_w(gspca_dev, 0, 0x8003, 0x00); |
|
|
|
/* switch to video camera mode */ |
|
reg_w(gspca_dev, 0x00, 0x8000, 0x0004); |
|
reg_r(gspca_dev, 0x8000, 1); |
|
gspca_dbg(gspca_dev, D_STREAM, "stop SPCA500 done reg8000: 0x%2x\n", |
|
gspca_dev->usb_buf[0]); |
|
} |
|
|
|
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; |
|
int i; |
|
static __u8 ffd9[] = {0xff, 0xd9}; |
|
|
|
/* frames are jpeg 4.1.1 without 0xff escape */ |
|
if (data[0] == 0xff) { |
|
if (data[1] != 0x01) { /* drop packet */ |
|
/* gspca_dev->last_packet_type = DISCARD_PACKET; */ |
|
return; |
|
} |
|
gspca_frame_add(gspca_dev, LAST_PACKET, |
|
ffd9, 2); |
|
|
|
/* put the JPEG header in the new frame */ |
|
gspca_frame_add(gspca_dev, FIRST_PACKET, |
|
sd->jpeg_hdr, JPEG_HDR_SZ); |
|
|
|
data += SPCA500_OFFSET_DATA; |
|
len -= SPCA500_OFFSET_DATA; |
|
} else { |
|
data += 1; |
|
len -= 1; |
|
} |
|
|
|
/* add 0x00 after 0xff */ |
|
i = 0; |
|
do { |
|
if (data[i] == 0xff) { |
|
gspca_frame_add(gspca_dev, INTER_PACKET, |
|
data, i + 1); |
|
len -= i; |
|
data += i; |
|
*data = 0x00; |
|
i = 0; |
|
} |
|
i++; |
|
} while (i < len); |
|
gspca_frame_add(gspca_dev, INTER_PACKET, data, len); |
|
} |
|
|
|
static void setbrightness(struct gspca_dev *gspca_dev, s32 val) |
|
{ |
|
reg_w(gspca_dev, 0x00, 0x8167, |
|
(__u8) (val - 128)); |
|
} |
|
|
|
static void setcontrast(struct gspca_dev *gspca_dev, s32 val) |
|
{ |
|
reg_w(gspca_dev, 0x00, 0x8168, val); |
|
} |
|
|
|
static void setcolors(struct gspca_dev *gspca_dev, s32 val) |
|
{ |
|
reg_w(gspca_dev, 0x00, 0x8169, val); |
|
} |
|
|
|
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: |
|
setcolors(gspca_dev, ctrl->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; |
|
|
|
gspca_dev->vdev.ctrl_handler = hdl; |
|
v4l2_ctrl_handler_init(hdl, 3); |
|
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, |
|
V4L2_CID_BRIGHTNESS, 0, 255, 1, 127); |
|
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, |
|
V4L2_CID_CONTRAST, 0, 63, 1, 31); |
|
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, |
|
V4L2_CID_SATURATION, 0, 63, 1, 31); |
|
|
|
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(0x040a, 0x0300), .driver_info = KodakEZ200}, |
|
{USB_DEVICE(0x041e, 0x400a), .driver_info = CreativePCCam300}, |
|
{USB_DEVICE(0x046d, 0x0890), .driver_info = LogitechTraveler}, |
|
{USB_DEVICE(0x046d, 0x0900), .driver_info = LogitechClickSmart310}, |
|
{USB_DEVICE(0x046d, 0x0901), .driver_info = LogitechClickSmart510}, |
|
{USB_DEVICE(0x04a5, 0x300c), .driver_info = BenqDC1016}, |
|
{USB_DEVICE(0x04fc, 0x7333), .driver_info = PalmPixDC85}, |
|
{USB_DEVICE(0x055f, 0xc200), .driver_info = MustekGsmart300}, |
|
{USB_DEVICE(0x055f, 0xc220), .driver_info = Gsmartmini}, |
|
{USB_DEVICE(0x06bd, 0x0404), .driver_info = AgfaCl20}, |
|
{USB_DEVICE(0x06be, 0x0800), .driver_info = Optimedia}, |
|
{USB_DEVICE(0x084d, 0x0003), .driver_info = DLinkDSC350}, |
|
{USB_DEVICE(0x08ca, 0x0103), .driver_info = AiptekPocketDV}, |
|
{USB_DEVICE(0x2899, 0x012c), .driver_info = ToptroIndus}, |
|
{USB_DEVICE(0x8086, 0x0630), .driver_info = IntelPocketPCCamera}, |
|
{} |
|
}; |
|
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);
|
|
|