#define MODULE_NAME "sonixj"
#include "gspca.h"
-#define QUANT_VAL 4 /* quantization table */
#include "jpeg.h"
#define V4L2_CID_INFRARED (V4L2_CID_PRIVATE_BASE + 0)
u8 blue;
u8 red;
u8 gamma;
- u8 vflip; /* ov7630 only */
+ u8 vflip; /* ov7630/ov7648 only */
u8 infrared; /* mt9v111 only */
+ u8 quality; /* image quality */
+#define QUALITY_MIN 60
+#define QUALITY_MAX 95
+#define QUALITY_DEF 80
+ u8 jpegqual; /* webcam quality */
+
+ u8 reg18;
s8 ag_cnt;
#define AG_CNT_START 13
#define BRIDGE_SN9C105 1
#define BRIDGE_SN9C110 2
#define BRIDGE_SN9C120 3
-#define BRIDGE_SN9C325 4
u8 sensor; /* Type of image sensor chip */
#define SENSOR_HV7131R 0
#define SENSOR_MI0360 1
#define SENSOR_OV7660 7
#define SENSOR_SP80708 8
u8 i2c_base;
+
+ u8 *jpeg_hdr;
};
/* V4L2 controls supported by the driver */
.set = sd_setautogain,
.get = sd_getautogain,
},
-/* ov7630 only */
+/* ov7630/ov7648 only */
#define VFLIP_IDX 6
{
{
.minimum = 0,
.maximum = 1,
.step = 1,
-#define VFLIP_DEF 1
+#define VFLIP_DEF 0 /* vflip def = 1 for ov7630 */
.default_value = VFLIP_DEF,
},
.set = sd_setvflip,
/* SENSOR_MI0360 1 */
(1 << INFRARED_IDX) | (1 << VFLIP_IDX),
/* SENSOR_MO4000 2 */
- 0,
+ (1 << VFLIP_IDX),
/* SENSOR_MT9V111 3 */
(1 << INFRARED_IDX) | (1 << VFLIP_IDX),
/* SENSOR_OM6802 4 */
(1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX),
/* SENSOR_OV7630 5 */
- (1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX) | (1 << VFLIP_IDX),
+ (1 << INFRARED_IDX),
/* SENSOR_OV7648 6 */
(1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX) | (1 << VFLIP_IDX),
/* SENSOR_OV7660 7 */
static const u8 sn_ov7660[0x1c] = {
/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
- 0x00, 0x61, 0x40, 0x00, 0x1a, 0x20, 0x20, 0x20,
+ 0x00, 0x61, 0x40, 0x00, 0x1a, 0x00, 0x00, 0x00,
/* reg8 reg9 rega regb regc regd rege regf */
- 0x81, 0x21, 0x07, 0x00, 0x00, 0x00, 0x00, 0x10,
+ 0x81, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
0x03, 0x00, 0x01, 0x01, 0x08, 0x28, 0x1e, 0x20,
/* reg18 reg19 reg1a reg1b */
{0xb1, 0x21, 0x2d, 0x85, 0x00, 0x00, 0x00, 0x10},
/*...*/
/* {0xa1, 0x21, 0x12, 0x08, 0x00, 0x00, 0x00, 0x10}, jfm done */
-/* {0xa1, 0x21, 0x75, 0x06, 0x00, 0x00, 0x00, 0x10}, jfm done */
+/* {0xa1, 0x21, 0x75, 0x06, 0x00, 0x00, 0x00, 0x10}, * COMN
+ * set by setvflip */
{0xa1, 0x21, 0x19, 0x02, 0x00, 0x00, 0x00, 0x10},
{0xa1, 0x21, 0x10, 0x32, 0x00, 0x00, 0x00, 0x10},
/* {0xa1, 0x21, 0x16, 0x00, 0x00, 0x00, 0x00, 0x10}, jfm done */
{0xc1, 0x21, 0x88, 0xaf, 0xc7, 0xdf, 0x00, 0x10}, /* gamma curve */
{0xc1, 0x21, 0x8b, 0x99, 0x99, 0xcf, 0x00, 0x10}, /* reserved */
{0xb1, 0x21, 0x92, 0x00, 0x00, 0x00, 0x00, 0x10}, /* DM_LNL/H */
+ {0xb1, 0x21, 0xa1, 0x00, 0x00, 0x00, 0x00, 0x10},
/****** (some exchanges in the win trace) ******/
{0xa1, 0x21, 0x1e, 0x01, 0x00, 0x00, 0x00, 0x10}, /* MVFP */
/* bits[3..0]reserved */
{}
};
-static const u8 qtable4[] = {
- 0x06, 0x04, 0x04, 0x06, 0x04, 0x04, 0x06, 0x06,
- 0x06, 0x06, 0x08, 0x06, 0x06, 0x08, 0x0a, 0x11,
- 0x0a, 0x0a, 0x08, 0x08, 0x0a, 0x15, 0x0f, 0x0f,
- 0x0c, 0x11, 0x19, 0x15, 0x19, 0x19, 0x17, 0x15,
- 0x17, 0x17, 0x1b, 0x1d, 0x25, 0x21, 0x1b, 0x1d,
- 0x23, 0x1d, 0x17, 0x17, 0x21, 0x2e, 0x21, 0x23,
- 0x27, 0x29, 0x2c, 0x2c, 0x2c, 0x19, 0x1f, 0x30,
- 0x32, 0x2e, 0x29, 0x32, 0x25, 0x29, 0x2c, 0x29,
- 0x06, 0x08, 0x08, 0x0a, 0x08, 0x0a, 0x13, 0x0a,
- 0x0a, 0x13, 0x29, 0x1b, 0x17, 0x1b, 0x29, 0x29,
- 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
- 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
- 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
- 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
- 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
- 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29
-};
-
/* read <len> bytes to gspca_dev->usb_buf */
static void reg_r(struct gspca_dev *gspca_dev,
u16 value, int len)
struct sd *sd = (struct sd *) gspca_dev;
const u8 *reg9a;
static const u8 reg9a_def[] =
- {0x08, 0x40, 0x20, 0x10, 0x00, 0x04};
- static const u8 reg9a_sn9c325[] =
- {0x0a, 0x40, 0x38, 0x30, 0x00, 0x20};
+ {0x00, 0x40, 0x20, 0x00, 0x00, 0x00};
+ static const u8 reg9a_spec[] =
+ {0x00, 0x40, 0x38, 0x30, 0x00, 0x20};
static const u8 regd4[] = {0x60, 0x00, 0x00};
reg_w1(gspca_dev, 0xf1, 0x00);
reg_w(gspca_dev, 0x01, &sn9c1xx[1], 2);
reg_w(gspca_dev, 0x08, &sn9c1xx[8], 2);
reg_w(gspca_dev, 0x17, &sn9c1xx[0x17], 5); /* jfm len was 3 */
- switch (sd->bridge) {
- case BRIDGE_SN9C325:
- reg9a = reg9a_sn9c325;
+ switch (sd->sensor) {
+ case SENSOR_OV7660:
+ case SENSOR_SP80708:
+ reg9a = reg9a_spec;
break;
default:
reg9a = reg9a_def;
reg_w1(gspca_dev, 0x17, 0x64);
reg_w1(gspca_dev, 0x01, 0x42);
break;
-/*jfm: from win trace */
case SENSOR_OV7630:
reg_w1(gspca_dev, 0x01, 0x61);
reg_w1(gspca_dev, 0x17, 0xe2);
case SENSOR_OV7648:
reg_w1(gspca_dev, 0x01, 0x63);
reg_w1(gspca_dev, 0x17, 0x20);
+ reg_w1(gspca_dev, 0x01, 0x62);
reg_w1(gspca_dev, 0x01, 0x42);
break;
-/*jfm: from win trace */
case SENSOR_OV7660:
- if (sd->bridge == BRIDGE_SN9C120) {
- reg_w1(gspca_dev, 0x01, 0x61);
- reg_w1(gspca_dev, 0x17, 0x20);
- reg_w1(gspca_dev, 0x01, 0x60);
- reg_w1(gspca_dev, 0x01, 0x40);
- break;
- }
- /* fall thru */
+ reg_w1(gspca_dev, 0x01, 0x61);
+ reg_w1(gspca_dev, 0x17, 0x20);
+ reg_w1(gspca_dev, 0x01, 0x60);
+ reg_w1(gspca_dev, 0x01, 0x40);
+ break;
case SENSOR_SP80708:
reg_w1(gspca_dev, 0x01, 0x63);
reg_w1(gspca_dev, 0x17, 0x20);
mdelay(100);
reg_w1(gspca_dev, 0x02, 0x62);
break;
+/* case SENSOR_HV7131R: */
+/* case SENSOR_MI0360: */
+/* case SENSOR_MO4000: */
default:
reg_w1(gspca_dev, 0x01, 0x43);
reg_w1(gspca_dev, 0x17, 0x61);
cam = &gspca_dev->cam;
cam->cam_mode = vga_mode;
cam->nmodes = ARRAY_SIZE(vga_mode);
+ cam->npkt = 24; /* 24 packets per ISOC message */
sd->bridge = id->driver_info >> 16;
sd->sensor = id->driver_info >> 8;
sd->gamma = GAMMA_DEF;
sd->autogain = AUTOGAIN_DEF;
sd->ag_cnt = -1;
- sd->vflip = VFLIP_DEF;
+ if (sd->sensor != SENSOR_OV7630)
+ sd->vflip = 0;
+ else
+ sd->vflip = 1;
sd->infrared = INFRARED_DEF;
+ sd->quality = QUALITY_DEF;
+ sd->jpegqual = 80;
gspca_dev->ctrl_dis = ctrl_dis[sd->sensor];
return 0;
if (gspca_dev->ctrl_dis & (1 << AUTOGAIN_IDX))
return;
+ switch (sd->sensor) {
+ case SENSOR_OV7630:
+ case SENSOR_OV7648: {
+ u8 comb;
+
+ if (sd->sensor == SENSOR_OV7630)
+ comb = 0xc0;
+ else
+ comb = 0xa0;
+ if (sd->autogain)
+ comb |= 0x02;
+ i2c_w1(&sd->gspca_dev, 0x13, comb);
+ return;
+ }
+ }
if (sd->autogain)
sd->ag_cnt = AG_CNT_START;
else
sd->ag_cnt = -1;
}
+/* ov7630/ov7648 only */
static void setvflip(struct sd *sd)
{
- i2c_w1(&sd->gspca_dev, 0x75, /* COMN */
- sd->vflip ? 0x82 : 0x02);
+ u8 comn;
+
+ if (sd->sensor == SENSOR_OV7630)
+ comn = 0x02;
+ else
+ comn = 0x06;
+ if (sd->vflip)
+ comn |= 0x80;
+ i2c_w1(&sd->gspca_dev, 0x75, comn);
}
static void setinfrared(struct sd *sd)
sd->infrared ? 0x66 : 0x64);
}
+static void setjpegqual(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int i, sc;
+
+ if (sd->jpegqual < 50)
+ sc = 5000 / sd->jpegqual;
+ else
+ sc = 200 - sd->jpegqual * 2;
+#if USB_BUF_SZ < 64
+#error "No room enough in usb_buf for quantization table"
+#endif
+ for (i = 0; i < 64; i++)
+ gspca_dev->usb_buf[i] =
+ (jpeg_head[JPEG_QT0_OFFSET + i] * sc + 50) / 100;
+ usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ 0x08,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+ 0x0100, 0,
+ gspca_dev->usb_buf, 64,
+ 500);
+ for (i = 0; i < 64; i++)
+ gspca_dev->usb_buf[i] =
+ (jpeg_head[JPEG_QT1_OFFSET + i] * sc + 50) / 100;
+ usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ 0x08,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+ 0x0140, 0,
+ gspca_dev->usb_buf, 64,
+ 500);
+
+ sd->reg18 ^= 0x40;
+ reg_w1(gspca_dev, 0x18, sd->reg18);
+}
+
/* -- start the camera -- */
static int sd_start(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
int i;
- u8 reg1, reg17, reg18;
+ u8 reg1, reg17;
const u8 *sn9c1xx;
int mode;
static const u8 C0[] = { 0x2d, 0x2d, 0x3a, 0x05, 0x04, 0x3f };
static const u8 CE_ov76xx[] =
{ 0x32, 0xdd, 0x32, 0xdd };
+ /* create the JPEG header */
+ sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL);
+ jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width,
+ 0x21); /* JPEG 422 */
+ jpeg_set_qual(sd->jpeg_hdr, sd->quality);
+
sn9c1xx = sn_tb[(int) sd->sensor];
configure_gpio(gspca_dev, sn9c1xx);
case SENSOR_OV7648:
reg17 = 0x20;
break;
-/*jfm: from win trace */
case SENSOR_OV7660:
- if (sd->bridge == BRIDGE_SN9C120) {
- reg17 = 0xa0;
- break;
- }
- /* fall thru */
+ reg17 = 0xa0;
+ break;
default:
reg17 = 0x60;
break;
reg_w1(gspca_dev, 0x9a, 0x0a);
reg_w1(gspca_dev, 0x99, 0x60);
break;
+ case SENSOR_OV7660:
+ reg_w1(gspca_dev, 0x9a, 0x05);
+ if (sd->bridge == BRIDGE_SN9C105)
+ reg_w1(gspca_dev, 0x99, 0xff);
+ else
+ reg_w1(gspca_dev, 0x99, 0x5b);
+ break;
case SENSOR_SP80708:
reg_w1(gspca_dev, 0x9a, 0x05);
reg_w1(gspca_dev, 0x99, 0x59);
break;
- case SENSOR_OV7660:
- if (sd->bridge == BRIDGE_SN9C120) {
- reg_w1(gspca_dev, 0x9a, 0x05);
- break;
- }
- /* fall thru */
default:
reg_w1(gspca_dev, 0x9a, 0x08);
reg_w1(gspca_dev, 0x99, 0x59);
}
/* here change size mode 0 -> VGA; 1 -> CIF */
- reg18 = sn9c1xx[0x18] | (mode << 4);
- reg_w1(gspca_dev, 0x18, reg18 | 0x40);
-
- reg_w(gspca_dev, 0x0100, qtable4, 0x40);
- reg_w(gspca_dev, 0x0140, qtable4 + 0x40, 0x40);
-
- reg_w1(gspca_dev, 0x18, reg18);
+ sd->reg18 = sn9c1xx[0x18] | (mode << 4) | 0x40;
+ reg_w1(gspca_dev, 0x18, sd->reg18);
+ setjpegqual(gspca_dev);
reg_w1(gspca_dev, 0x17, reg17);
reg_w1(gspca_dev, 0x01, reg1);
reg_w1(gspca_dev, 0xf1, 0x00);
}
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ kfree(sd->jpeg_hdr);
+}
+
static void do_autogain(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
if (gspca_dev->last_packet_type == LAST_PACKET) {
/* put the JPEG 422 header */
- jpeg_put_header(gspca_dev, frame, 0x21);
+ gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
+ sd->jpeg_hdr, JPEG_HDR_SZ);
}
gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
}
return 0;
}
+static int sd_set_jcomp(struct gspca_dev *gspca_dev,
+ struct v4l2_jpegcompression *jcomp)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (jcomp->quality < QUALITY_MIN)
+ sd->quality = QUALITY_MIN;
+ else if (jcomp->quality > QUALITY_MAX)
+ sd->quality = QUALITY_MAX;
+ else
+ sd->quality = jcomp->quality;
+ if (gspca_dev->streaming)
+ jpeg_set_qual(sd->jpeg_hdr, sd->quality);
+ return 0;
+}
+
+static int sd_get_jcomp(struct gspca_dev *gspca_dev,
+ struct v4l2_jpegcompression *jcomp)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ memset(jcomp, 0, sizeof *jcomp);
+ jcomp->quality = sd->quality;
+ jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT
+ | V4L2_JPEG_MARKER_DQT;
+ return 0;
+}
+
/* sub-driver description */
static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
.init = sd_init,
.start = sd_start,
.stopN = sd_stopN,
+ .stop0 = sd_stop0,
.pkt_scan = sd_pkt_scan,
.dq_callback = do_autogain,
+ .get_jcomp = sd_get_jcomp,
+ .set_jcomp = sd_set_jcomp,
};
/* -- module initialisation -- */
#endif
{USB_DEVICE(0x045e, 0x00f5), BSI(SN9C105, OV7660, 0x21)},
{USB_DEVICE(0x045e, 0x00f7), BSI(SN9C105, OV7660, 0x21)},
-#if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE
{USB_DEVICE(0x0471, 0x0327), BSI(SN9C105, MI0360, 0x5d)},
-#endif
{USB_DEVICE(0x0471, 0x0328), BSI(SN9C105, MI0360, 0x5d)},
{USB_DEVICE(0x0471, 0x0330), BSI(SN9C105, MI0360, 0x5d)},
{USB_DEVICE(0x06f8, 0x3004), BSI(SN9C105, OV7660, 0x21)},
#if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE
{USB_DEVICE(0x0c45, 0x60fe), BSI(SN9C105, OV7630, 0x21)},
#endif
+ {USB_DEVICE(0x0c45, 0x6100), BSI(SN9C120, MI0360, 0x5d)}, /*sn9c128*/
/* {USB_DEVICE(0x0c45, 0x6108), BSI(SN9C120, OM6801, 0x??)}, */
+ {USB_DEVICE(0x0c45, 0x610a), BSI(SN9C120, OV7648, 0x21)}, /*sn9c128*/
+ {USB_DEVICE(0x0c45, 0x610b), BSI(SN9C120, OV7660, 0x21)}, /*sn9c128*/
+ {USB_DEVICE(0x0c45, 0x610c), BSI(SN9C120, HV7131R, 0x11)}, /*sn9c128*/
+ {USB_DEVICE(0x0c45, 0x610e), BSI(SN9C120, OV7630, 0x21)}, /*sn9c128*/
/* {USB_DEVICE(0x0c45, 0x6122), BSI(SN9C110, ICM105C, 0x??)}, */
/* {USB_DEVICE(0x0c45, 0x6123), BSI(SN9C110, SanyoCCD, 0x??)}, */
{USB_DEVICE(0x0c45, 0x6128), BSI(SN9C110, OM6802, 0x21)}, /*sn9c325?*/
{USB_DEVICE(0x0c45, 0x613a), BSI(SN9C120, OV7648, 0x21)},
#if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE
{USB_DEVICE(0x0c45, 0x613b), BSI(SN9C120, OV7660, 0x21)},
+#endif
{USB_DEVICE(0x0c45, 0x613c), BSI(SN9C120, HV7131R, 0x11)},
/* {USB_DEVICE(0x0c45, 0x613e), BSI(SN9C120, OV7630, 0x??)}, */
-#endif
{USB_DEVICE(0x0c45, 0x6143), BSI(SN9C120, SP80708, 0x18)},
{}
};