提交 a511ba94 编写于 作者: H Hans de Goede 提交者: Mauro Carvalho Chehab

V4L/DVB (13178): gspca: Add support for Winbond W9967CF and W9968CF camera's

This patch adds support to gspca for the Winbond W9967CF and W9968CF
camera's. This is mostly a port of the existing v4l1 driver to gspca
(making it v4l2). But this also features fixes to the bitbanging i2c code
(send a nack not an ack after reading the last byte of a transfer), which
gets rid of the weird errors which were being seen there, and of
the smbus_refresh() hack to get around these errors.

Also the vstart settings have been tweaked to work with different
frequency filter settings.
Signed-off-by: NHans de Goede <hdegoede@redhat.com>
Signed-off-by: NMauro Carvalho Chehab <mchehab@redhat.com>
上级 eea85b0a
...@@ -951,9 +951,13 @@ source "drivers/media/video/usbvideo/Kconfig" ...@@ -951,9 +951,13 @@ source "drivers/media/video/usbvideo/Kconfig"
source "drivers/media/video/et61x251/Kconfig" source "drivers/media/video/et61x251/Kconfig"
config VIDEO_OVCAMCHIP config VIDEO_OVCAMCHIP
tristate "OmniVision Camera Chip support" tristate "OmniVision Camera Chip support (DEPRECATED)"
depends on I2C && VIDEO_V4L1 depends on I2C && VIDEO_V4L1
---help--- ---help---
This driver is DEPRECATED please use the gspca ov519 module
instead. Note that for the ov511 / ov518 support of the gspca module
you need atleast version 0.6.0 of libv4l.
Support for the OmniVision OV6xxx and OV7xxx series of camera chips. Support for the OmniVision OV6xxx and OV7xxx series of camera chips.
This driver is intended to be used with the ov511 and w9968cf USB This driver is intended to be used with the ov511 and w9968cf USB
camera drivers. camera drivers.
...@@ -962,9 +966,12 @@ config VIDEO_OVCAMCHIP ...@@ -962,9 +966,12 @@ config VIDEO_OVCAMCHIP
module will be called ovcamchip. module will be called ovcamchip.
config USB_W9968CF config USB_W9968CF
tristate "USB W996[87]CF JPEG Dual Mode Camera support" tristate "USB W996[87]CF JPEG Dual Mode Camera support (DEPRECATED)"
depends on VIDEO_V4L1 && I2C && VIDEO_OVCAMCHIP depends on VIDEO_V4L1 && I2C && VIDEO_OVCAMCHIP
---help--- ---help---
This driver is DEPRECATED please use the gspca ov519 module
instead.
Say Y here if you want support for cameras based on OV681 or Say Y here if you want support for cameras based on OV681 or
Winbond W9967CF/W9968CF JPEG USB Dual Mode Camera Chips. Winbond W9967CF/W9968CF JPEG USB Dual Mode Camera Chips.
......
...@@ -76,10 +76,11 @@ config USB_GSPCA_MR97310A ...@@ -76,10 +76,11 @@ config USB_GSPCA_MR97310A
module will be called gspca_mr97310a. module will be called gspca_mr97310a.
config USB_GSPCA_OV519 config USB_GSPCA_OV519
tristate "OV519 USB Camera Driver" tristate "OV51x / OVFX2 / W996xCF USB Camera Driver"
depends on VIDEO_V4L2 && USB_GSPCA depends on VIDEO_V4L2 && USB_GSPCA
help help
Say Y here if you want support for cameras based on the OV519 chip. Say Y here if you want support for cameras based on one of these:
OV511(+), OV518(+), OV519, OVFX2, W9967CF, W9968CF
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called gspca_ov519. module will be called gspca_ov519.
......
...@@ -475,10 +475,18 @@ static struct usb_host_endpoint *get_ep(struct gspca_dev *gspca_dev) ...@@ -475,10 +475,18 @@ static struct usb_host_endpoint *get_ep(struct gspca_dev *gspca_dev)
xfer = gspca_dev->cam.bulk ? USB_ENDPOINT_XFER_BULK xfer = gspca_dev->cam.bulk ? USB_ENDPOINT_XFER_BULK
: USB_ENDPOINT_XFER_ISOC; : USB_ENDPOINT_XFER_ISOC;
i = gspca_dev->alt; /* previous alt setting */ i = gspca_dev->alt; /* previous alt setting */
while (--i >= 0) { if (gspca_dev->cam.reverse_alts) {
ep = alt_xfer(&intf->altsetting[i], xfer); while (++i < gspca_dev->nbalt) {
if (ep) ep = alt_xfer(&intf->altsetting[i], xfer);
break; if (ep)
break;
}
} else {
while (--i >= 0) {
ep = alt_xfer(&intf->altsetting[i], xfer);
if (ep)
break;
}
} }
if (ep == NULL) { if (ep == NULL) {
err("no transfer endpoint found"); err("no transfer endpoint found");
...@@ -599,7 +607,11 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) ...@@ -599,7 +607,11 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev)
/* set the higher alternate setting and /* set the higher alternate setting and
* loop until urb submit succeeds */ * loop until urb submit succeeds */
gspca_dev->alt = gspca_dev->nbalt; if (gspca_dev->cam.reverse_alts)
gspca_dev->alt = 0;
else
gspca_dev->alt = gspca_dev->nbalt;
if (gspca_dev->sd_desc->isoc_init) { if (gspca_dev->sd_desc->isoc_init) {
ret = gspca_dev->sd_desc->isoc_init(gspca_dev); ret = gspca_dev->sd_desc->isoc_init(gspca_dev);
if (ret < 0) if (ret < 0)
......
...@@ -58,6 +58,7 @@ struct cam { ...@@ -58,6 +58,7 @@ struct cam {
u8 npkt; /* number of packets in an ISOC message u8 npkt; /* number of packets in an ISOC message
* 0 is the default value: 32 packets */ * 0 is the default value: 32 packets */
u32 input_flags; /* value for ENUM_INPUT status flags */ u32 input_flags; /* value for ENUM_INPUT status flags */
char reverse_alts; /* Alt settings are in high to low order */
}; };
struct gspca_dev; struct gspca_dev;
......
...@@ -64,6 +64,7 @@ struct sd { ...@@ -64,6 +64,7 @@ struct sd {
#define BRIDGE_OV518PLUS 3 #define BRIDGE_OV518PLUS 3
#define BRIDGE_OV519 4 #define BRIDGE_OV519 4
#define BRIDGE_OVFX2 5 #define BRIDGE_OVFX2 5
#define BRIDGE_W9968CF 6
#define BRIDGE_MASK 7 #define BRIDGE_MASK 7
char invert_led; char invert_led;
...@@ -98,8 +99,17 @@ struct sd { ...@@ -98,8 +99,17 @@ struct sd {
#define SEN_OV7670 9 #define SEN_OV7670 9
#define SEN_OV76BE 10 #define SEN_OV76BE 10
#define SEN_OV8610 11 #define SEN_OV8610 11
u8 sensor_addr;
int sensor_width;
int sensor_height;
}; };
/* Note this is a bit of a hack, but the w9968cf driver needs the code for all
the ov sensors which is already present here. When we have the time we
really should move the sensor drivers to v4l2 sub drivers. */
#include "w996Xcf.c"
/* V4L2 controls supported by the driver */ /* V4L2 controls supported by the driver */
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
...@@ -1471,6 +1481,7 @@ static const struct ov_i2c_regvals norm_7610[] = { ...@@ -1471,6 +1481,7 @@ static const struct ov_i2c_regvals norm_7610[] = {
}; };
static const struct ov_i2c_regvals norm_7620[] = { static const struct ov_i2c_regvals norm_7620[] = {
{ 0x12, 0x80 }, /* reset */
{ 0x00, 0x00 }, /* gain */ { 0x00, 0x00 }, /* gain */
{ 0x01, 0x80 }, /* blue gain */ { 0x01, 0x80 }, /* blue gain */
{ 0x02, 0x80 }, /* red gain */ { 0x02, 0x80 }, /* red gain */
...@@ -1835,10 +1846,9 @@ static unsigned char ov7670_abs_to_sm(unsigned char v) ...@@ -1835,10 +1846,9 @@ static unsigned char ov7670_abs_to_sm(unsigned char v)
} }
/* Write a OV519 register */ /* Write a OV519 register */
static int reg_w(struct sd *sd, __u16 index, __u8 value) static int reg_w(struct sd *sd, __u16 index, __u16 value)
{ {
int ret; int ret, req = 0;
int req;
switch (sd->bridge) { switch (sd->bridge) {
case BRIDGE_OV511: case BRIDGE_OV511:
...@@ -1846,11 +1856,14 @@ static int reg_w(struct sd *sd, __u16 index, __u8 value) ...@@ -1846,11 +1856,14 @@ static int reg_w(struct sd *sd, __u16 index, __u8 value)
req = 2; req = 2;
break; break;
case BRIDGE_OVFX2: case BRIDGE_OVFX2:
req = 0x0a;
/* fall through */
case BRIDGE_W9968CF:
ret = usb_control_msg(sd->gspca_dev.dev, ret = usb_control_msg(sd->gspca_dev.dev,
usb_sndctrlpipe(sd->gspca_dev.dev, 0), usb_sndctrlpipe(sd->gspca_dev.dev, 0),
0x0a, req,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
(__u16)value, index, NULL, 0, 500); value, index, NULL, 0, 500);
goto leave; goto leave;
default: default:
req = 1; req = 1;
...@@ -1864,12 +1877,17 @@ static int reg_w(struct sd *sd, __u16 index, __u8 value) ...@@ -1864,12 +1877,17 @@ static int reg_w(struct sd *sd, __u16 index, __u8 value)
0, index, 0, index,
sd->gspca_dev.usb_buf, 1, 500); sd->gspca_dev.usb_buf, 1, 500);
leave: leave:
if (ret < 0) if (ret < 0) {
PDEBUG(D_ERR, "Write reg [%02x] %02x failed", index, value); PDEBUG(D_ERR, "Write reg 0x%04x -> [0x%02x] failed",
return ret; value, index);
return ret;
}
PDEBUG(D_USBO, "Write reg 0x%04x -> [0x%02x]", value, index);
return 0;
} }
/* Read from a OV519 register */ /* Read from a OV519 register, note not valid for the w9968cf!! */
/* returns: negative is error, pos or zero is data */ /* returns: negative is error, pos or zero is data */
static int reg_r(struct sd *sd, __u16 index) static int reg_r(struct sd *sd, __u16 index)
{ {
...@@ -1894,10 +1912,12 @@ static int reg_r(struct sd *sd, __u16 index) ...@@ -1894,10 +1912,12 @@ static int reg_r(struct sd *sd, __u16 index)
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
0, index, sd->gspca_dev.usb_buf, 1, 500); 0, index, sd->gspca_dev.usb_buf, 1, 500);
if (ret >= 0) if (ret >= 0) {
ret = sd->gspca_dev.usb_buf[0]; ret = sd->gspca_dev.usb_buf[0];
else PDEBUG(D_USBI, "Read reg [0x%02X] -> 0x%04X", index, ret);
} else
PDEBUG(D_ERR, "Read reg [0x%02x] failed", index); PDEBUG(D_ERR, "Read reg [0x%02x] failed", index);
return ret; return ret;
} }
...@@ -1917,6 +1937,7 @@ static int reg_r8(struct sd *sd, ...@@ -1917,6 +1937,7 @@ static int reg_r8(struct sd *sd,
ret = sd->gspca_dev.usb_buf[0]; ret = sd->gspca_dev.usb_buf[0];
else else
PDEBUG(D_ERR, "Read reg 8 [0x%02x] failed", index); PDEBUG(D_ERR, "Read reg 8 [0x%02x] failed", index);
return ret; return ret;
} }
...@@ -1962,9 +1983,12 @@ static int ov518_reg_w32(struct sd *sd, __u16 index, u32 value, int n) ...@@ -1962,9 +1983,12 @@ static int ov518_reg_w32(struct sd *sd, __u16 index, u32 value, int n)
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
0, index, 0, index,
sd->gspca_dev.usb_buf, n, 500); sd->gspca_dev.usb_buf, n, 500);
if (ret < 0) if (ret < 0) {
PDEBUG(D_ERR, "Write reg32 [%02x] %08x failed", index, value); PDEBUG(D_ERR, "Write reg32 [%02x] %08x failed", index, value);
return ret; return ret;
}
return 0;
} }
static int ov511_i2c_w(struct sd *sd, __u8 reg, __u8 value) static int ov511_i2c_w(struct sd *sd, __u8 reg, __u8 value)
...@@ -2156,12 +2180,13 @@ static int ovfx2_i2c_w(struct sd *sd, __u8 reg, __u8 value) ...@@ -2156,12 +2180,13 @@ static int ovfx2_i2c_w(struct sd *sd, __u8 reg, __u8 value)
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
(__u16)value, (__u16)reg, NULL, 0, 500); (__u16)value, (__u16)reg, NULL, 0, 500);
if (ret >= 0) if (ret < 0) {
PDEBUG(D_USBO, "i2c 0x%02x -> [0x%02x]", value, reg);
else
PDEBUG(D_ERR, "i2c 0x%02x -> [0x%02x] failed", value, reg); PDEBUG(D_ERR, "i2c 0x%02x -> [0x%02x] failed", value, reg);
return ret;
}
return ret; PDEBUG(D_USBO, "i2c 0x%02x -> [0x%02x]", value, reg);
return 0;
} }
static int ovfx2_i2c_r(struct sd *sd, __u8 reg) static int ovfx2_i2c_r(struct sd *sd, __u8 reg)
...@@ -2195,6 +2220,8 @@ static int i2c_w(struct sd *sd, __u8 reg, __u8 value) ...@@ -2195,6 +2220,8 @@ static int i2c_w(struct sd *sd, __u8 reg, __u8 value)
return ov518_i2c_w(sd, reg, value); return ov518_i2c_w(sd, reg, value);
case BRIDGE_OVFX2: case BRIDGE_OVFX2:
return ovfx2_i2c_w(sd, reg, value); return ovfx2_i2c_w(sd, reg, value);
case BRIDGE_W9968CF:
return w9968cf_i2c_w(sd, reg, value);
} }
return -1; /* Should never happen */ return -1; /* Should never happen */
} }
...@@ -2211,6 +2238,8 @@ static int i2c_r(struct sd *sd, __u8 reg) ...@@ -2211,6 +2238,8 @@ static int i2c_r(struct sd *sd, __u8 reg)
return ov518_i2c_r(sd, reg); return ov518_i2c_r(sd, reg);
case BRIDGE_OVFX2: case BRIDGE_OVFX2:
return ovfx2_i2c_r(sd, reg); return ovfx2_i2c_r(sd, reg);
case BRIDGE_W9968CF:
return w9968cf_i2c_r(sd, reg);
} }
return -1; /* Should never happen */ return -1; /* Should never happen */
} }
...@@ -2241,6 +2270,8 @@ static int i2c_w_mask(struct sd *sd, ...@@ -2241,6 +2270,8 @@ static int i2c_w_mask(struct sd *sd,
* registers while the camera is streaming */ * registers while the camera is streaming */
static inline int ov51x_stop(struct sd *sd) static inline int ov51x_stop(struct sd *sd)
{ {
int ret;
PDEBUG(D_STREAM, "stopping"); PDEBUG(D_STREAM, "stopping");
sd->stopped = 1; sd->stopped = 1;
switch (sd->bridge) { switch (sd->bridge) {
...@@ -2254,6 +2285,11 @@ static inline int ov51x_stop(struct sd *sd) ...@@ -2254,6 +2285,11 @@ static inline int ov51x_stop(struct sd *sd)
return reg_w(sd, OV519_SYS_RESET1, 0x0f); return reg_w(sd, OV519_SYS_RESET1, 0x0f);
case BRIDGE_OVFX2: case BRIDGE_OVFX2:
return reg_w_mask(sd, 0x0f, 0x00, 0x02); return reg_w_mask(sd, 0x0f, 0x00, 0x02);
case BRIDGE_W9968CF:
ret = reg_w(sd, 0x3c, 0x0a05); /* stop USB transfer */
ret += reg_w(sd, 0x39, 0x0000); /* disable JPEG encoder */
ret += reg_w(sd, 0x16, 0x0000); /* stop video capture */
return ret;
} }
return 0; return 0;
...@@ -2285,6 +2321,8 @@ static inline int ov51x_restart(struct sd *sd) ...@@ -2285,6 +2321,8 @@ static inline int ov51x_restart(struct sd *sd)
return reg_w(sd, OV519_SYS_RESET1, 0x00); return reg_w(sd, OV519_SYS_RESET1, 0x00);
case BRIDGE_OVFX2: case BRIDGE_OVFX2:
return reg_w_mask(sd, 0x0f, 0x02, 0x02); return reg_w_mask(sd, 0x0f, 0x02, 0x02);
case BRIDGE_W9968CF:
return reg_w(sd, 0x3c, 0x8a05); /* USB FIFO enable */
} }
return 0; return 0;
...@@ -2338,8 +2376,13 @@ static int ov51x_set_slave_ids(struct sd *sd, ...@@ -2338,8 +2376,13 @@ static int ov51x_set_slave_ids(struct sd *sd,
{ {
int rc; int rc;
if (sd->bridge == BRIDGE_OVFX2) switch (sd->bridge) {
case BRIDGE_OVFX2:
return reg_w(sd, OVFX2_I2C_ADDR, slave); return reg_w(sd, OVFX2_I2C_ADDR, slave);
case BRIDGE_W9968CF:
sd->sensor_addr = slave;
return 0;
}
rc = reg_w(sd, R51x_I2C_W_SID, slave); rc = reg_w(sd, R51x_I2C_W_SID, slave);
if (rc < 0) if (rc < 0)
...@@ -2920,6 +2963,10 @@ static int sd_config(struct gspca_dev *gspca_dev, ...@@ -2920,6 +2963,10 @@ static int sd_config(struct gspca_dev *gspca_dev,
cam->bulk_nurbs = MAX_NURBS; cam->bulk_nurbs = MAX_NURBS;
cam->bulk = 1; cam->bulk = 1;
break; break;
case BRIDGE_W9968CF:
ret = w9968cf_configure(sd);
cam->reverse_alts = 1;
break;
} }
if (ret) if (ret)
...@@ -3005,6 +3052,16 @@ static int sd_config(struct gspca_dev *gspca_dev, ...@@ -3005,6 +3052,16 @@ static int sd_config(struct gspca_dev *gspca_dev,
cam->nmodes = ARRAY_SIZE(ov519_sif_mode); cam->nmodes = ARRAY_SIZE(ov519_sif_mode);
} }
break; break;
case BRIDGE_W9968CF:
cam->cam_mode = w9968cf_vga_mode;
cam->nmodes = ARRAY_SIZE(w9968cf_vga_mode);
/* if (sd->sif)
cam->nmodes--; */
/* w9968cf needs initialisation once the sensor is known */
if (w9968cf_init(sd) < 0)
goto error;
break;
} }
sd->brightness = BRIGHTNESS_DEF; sd->brightness = BRIGHTNESS_DEF;
if (sd->sensor == SEN_OV6630 || sd->sensor == SEN_OV66308AF) if (sd->sensor == SEN_OV6630 || sd->sensor == SEN_OV66308AF)
...@@ -3753,9 +3810,9 @@ static int set_ov_sensor_window(struct sd *sd) ...@@ -3753,9 +3810,9 @@ static int set_ov_sensor_window(struct sd *sd)
return ret; return ret;
i2c_w(sd, 0x17, hwsbase); i2c_w(sd, 0x17, hwsbase);
i2c_w(sd, 0x18, hwebase + (sd->gspca_dev.width >> hwscale)); i2c_w(sd, 0x18, hwebase + (sd->sensor_width >> hwscale));
i2c_w(sd, 0x19, vwsbase); i2c_w(sd, 0x19, vwsbase);
i2c_w(sd, 0x1a, vwebase + (sd->gspca_dev.height >> vwscale)); i2c_w(sd, 0x1a, vwebase + (sd->sensor_height >> vwscale));
return 0; return 0;
} }
...@@ -3766,6 +3823,10 @@ static int sd_start(struct gspca_dev *gspca_dev) ...@@ -3766,6 +3823,10 @@ static int sd_start(struct gspca_dev *gspca_dev)
struct sd *sd = (struct sd *) gspca_dev; struct sd *sd = (struct sd *) gspca_dev;
int ret = 0; int ret = 0;
/* Default for most bridges, allow bridge_mode_init_regs to override */
sd->sensor_width = sd->gspca_dev.width;
sd->sensor_height = sd->gspca_dev.height;
switch (sd->bridge) { switch (sd->bridge) {
case BRIDGE_OV511: case BRIDGE_OV511:
case BRIDGE_OV511PLUS: case BRIDGE_OV511PLUS:
...@@ -3779,6 +3840,9 @@ static int sd_start(struct gspca_dev *gspca_dev) ...@@ -3779,6 +3840,9 @@ static int sd_start(struct gspca_dev *gspca_dev)
ret = ov519_mode_init_regs(sd); ret = ov519_mode_init_regs(sd);
break; break;
/* case BRIDGE_OVFX2: nothing to do */ /* case BRIDGE_OVFX2: nothing to do */
case BRIDGE_W9968CF:
ret = w9968cf_mode_init_regs(sd);
break;
} }
if (ret < 0) if (ret < 0)
goto out; goto out;
...@@ -3980,6 +4044,9 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, ...@@ -3980,6 +4044,9 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
case BRIDGE_OVFX2: case BRIDGE_OVFX2:
ovfx2_pkt_scan(gspca_dev, frame, data, len); ovfx2_pkt_scan(gspca_dev, frame, data, len);
break; break;
case BRIDGE_W9968CF:
w9968cf_pkt_scan(gspca_dev, frame, data, len);
break;
} }
} }
...@@ -4275,8 +4342,12 @@ static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val) ...@@ -4275,8 +4342,12 @@ static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val)
struct sd *sd = (struct sd *) gspca_dev; struct sd *sd = (struct sd *) gspca_dev;
sd->freq = val; sd->freq = val;
if (gspca_dev->streaming) if (gspca_dev->streaming) {
setfreq(sd); setfreq(sd);
/* Ugly but necessary */
if (sd->bridge == BRIDGE_W9968CF)
w9968cf_set_crop_window(sd);
}
return 0; return 0;
} }
...@@ -4332,6 +4403,7 @@ static const struct sd_desc sd_desc = { ...@@ -4332,6 +4403,7 @@ static const struct sd_desc sd_desc = {
/* -- module initialisation -- */ /* -- module initialisation -- */
static const __devinitdata struct usb_device_id device_table[] = { static const __devinitdata struct usb_device_id device_table[] = {
{USB_DEVICE(0x041e, 0x4003), .driver_info = BRIDGE_W9968CF },
{USB_DEVICE(0x041e, 0x4052), .driver_info = BRIDGE_OV519 }, {USB_DEVICE(0x041e, 0x4052), .driver_info = BRIDGE_OV519 },
{USB_DEVICE(0x041e, 0x405f), .driver_info = BRIDGE_OV519 }, {USB_DEVICE(0x041e, 0x405f), .driver_info = BRIDGE_OV519 },
{USB_DEVICE(0x041e, 0x4060), .driver_info = BRIDGE_OV519 }, {USB_DEVICE(0x041e, 0x4060), .driver_info = BRIDGE_OV519 },
...@@ -4356,6 +4428,7 @@ static const __devinitdata struct usb_device_id device_table[] = { ...@@ -4356,6 +4428,7 @@ static const __devinitdata struct usb_device_id device_table[] = {
{USB_DEVICE(0x0813, 0x0002), .driver_info = BRIDGE_OV511PLUS }, {USB_DEVICE(0x0813, 0x0002), .driver_info = BRIDGE_OV511PLUS },
{USB_DEVICE(0x0b62, 0x0059), .driver_info = BRIDGE_OVFX2 }, {USB_DEVICE(0x0b62, 0x0059), .driver_info = BRIDGE_OVFX2 },
{USB_DEVICE(0x0e96, 0xc001), .driver_info = BRIDGE_OVFX2 }, {USB_DEVICE(0x0e96, 0xc001), .driver_info = BRIDGE_OVFX2 },
{USB_DEVICE(0x1046, 0x9967), .driver_info = BRIDGE_W9968CF },
{USB_DEVICE(0x8020, 0xEF04), .driver_info = BRIDGE_OVFX2 }, {USB_DEVICE(0x8020, 0xEF04), .driver_info = BRIDGE_OVFX2 },
{} {}
}; };
......
/**
*
* GSPCA sub driver for W996[78]CF JPEG USB Dual Mode Camera Chip.
*
* Copyright (C) 2009 Hans de Goede <hdegoede@redhat.com>
*
* This module is adapted from the in kernel v4l1 w9968cf driver:
*
* Copyright (C) 2002-2004 by Luca Risolia <luca.risolia@studio.unibo.it>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
/* Note this is not a stand alone driver, it gets included in ov519.c, this
is a bit of a hack, but it needs the driver code for a lot of different
ov sensors which is already present in ov519.c (the old v4l1 driver used
the ovchipcam framework). When we have the time we really should move
the sensor drivers to v4l2 sub drivers, and properly split of this
driver from ov519.c */
#define W9968CF_I2C_BUS_DELAY 4 /* delay in us for I2C bit r/w operations */
/* FIXME make this runtime configurable */
/* Comment/uncomment this for high/low quality of compressed video */
#define W9968CF_DEC_FAST_LOWQUALITY_VIDEO
#ifdef W9968CF_DEC_FAST_LOWQUALITY_VIDEO
static const unsigned char Y_QUANTABLE[64] = {
16, 11, 10, 16, 24, 40, 51, 61,
12, 12, 14, 19, 26, 58, 60, 55,
14, 13, 16, 24, 40, 57, 69, 56,
14, 17, 22, 29, 51, 87, 80, 62,
18, 22, 37, 56, 68, 109, 103, 77,
24, 35, 55, 64, 81, 104, 113, 92,
49, 64, 78, 87, 103, 121, 120, 101,
72, 92, 95, 98, 112, 100, 103, 99
};
static const unsigned char UV_QUANTABLE[64] = {
17, 18, 24, 47, 99, 99, 99, 99,
18, 21, 26, 66, 99, 99, 99, 99,
24, 26, 56, 99, 99, 99, 99, 99,
47, 66, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99
};
#else
static const unsigned char Y_QUANTABLE[64] = {
8, 5, 5, 8, 12, 20, 25, 30,
6, 6, 7, 9, 13, 29, 30, 27,
7, 6, 8, 12, 20, 28, 34, 28,
7, 8, 11, 14, 25, 43, 40, 31,
9, 11, 18, 28, 34, 54, 51, 38,
12, 17, 27, 32, 40, 52, 56, 46,
24, 32, 39, 43, 51, 60, 60, 50,
36, 46, 47, 49, 56, 50, 51, 49
};
static const unsigned char UV_QUANTABLE[64] = {
8, 9, 12, 23, 49, 49, 49, 49,
9, 10, 13, 33, 49, 49, 49, 49,
12, 13, 28, 49, 49, 49, 49, 49,
23, 33, 49, 49, 49, 49, 49, 49,
49, 49, 49, 49, 49, 49, 49, 49,
49, 49, 49, 49, 49, 49, 49, 49,
49, 49, 49, 49, 49, 49, 49, 49,
49, 49, 49, 49, 49, 49, 49, 49
};
#endif
static const struct v4l2_pix_format w9968cf_vga_mode[] = {
{160, 120, V4L2_PIX_FMT_UYVY, V4L2_FIELD_NONE,
.bytesperline = 160 * 2,
.sizeimage = 160 * 120 * 2,
.colorspace = V4L2_COLORSPACE_JPEG},
{176, 144, V4L2_PIX_FMT_UYVY, V4L2_FIELD_NONE,
.bytesperline = 176 * 2,
.sizeimage = 176 * 144 * 2,
.colorspace = V4L2_COLORSPACE_JPEG},
{320, 240, V4L2_PIX_FMT_UYVY, V4L2_FIELD_NONE,
.bytesperline = 320 * 2,
.sizeimage = 320 * 240 * 2,
.colorspace = V4L2_COLORSPACE_JPEG},
{352, 288, V4L2_PIX_FMT_UYVY, V4L2_FIELD_NONE,
.bytesperline = 352 * 2,
.sizeimage = 352 * 288 * 2,
.colorspace = V4L2_COLORSPACE_JPEG},
/* {640, 480, V4L2_PIX_FMT_UYVY, V4L2_FIELD_NONE,
.bytesperline = 640 * 2,
.sizeimage = 640 * 480 * 2,
.colorspace = V4L2_COLORSPACE_JPEG}, */
};
static int reg_w(struct sd *sd, __u16 index, __u16 value);
/*--------------------------------------------------------------------------
Write 64-bit data to the fast serial bus registers.
Return 0 on success, -1 otherwise.
--------------------------------------------------------------------------*/
static int w9968cf_write_fsb(struct sd *sd, u16* data)
{
struct usb_device* udev = sd->gspca_dev.dev;
u16 value;
int ret;
value = *data++;
memcpy(sd->gspca_dev.usb_buf, data, 6);
ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0,
USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE,
value, 0x06, sd->gspca_dev.usb_buf, 6, 500);
if (ret < 0) {
PDEBUG(D_ERR, "Write FSB registers failed (%d)", ret);
return ret;
}
return 0;
}
/*--------------------------------------------------------------------------
Write data to the serial bus control register.
Return 0 on success, a negative number otherwise.
--------------------------------------------------------------------------*/
static int w9968cf_write_sb(struct sd *sd, u16 value)
{
int ret;
/* We don't use reg_w here, as that would cause all writes when
bitbanging i2c to be logged, making the logs impossible to read */
ret = usb_control_msg(sd->gspca_dev.dev,
usb_sndctrlpipe(sd->gspca_dev.dev, 0),
0,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
value, 0x01, NULL, 0, 500);
udelay(W9968CF_I2C_BUS_DELAY);
if (ret < 0) {
PDEBUG(D_ERR, "Write SB reg [01] %04x failed", value);
return ret;
}
return 0;
}
/*--------------------------------------------------------------------------
Read data from the serial bus control register.
Return 0 on success, a negative number otherwise.
--------------------------------------------------------------------------*/
static int w9968cf_read_sb(struct sd *sd)
{
int ret;
/* We don't use reg_r here, as the w9968cf is special and has 16
bit registers instead of 8 bit */
ret = usb_control_msg(sd->gspca_dev.dev,
usb_rcvctrlpipe(sd->gspca_dev.dev, 0),
1,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
0, 0x01, sd->gspca_dev.usb_buf, 2, 500);
if (ret >= 0)
ret = sd->gspca_dev.usb_buf[0] |
(sd->gspca_dev.usb_buf[1] << 8);
else
PDEBUG(D_ERR, "Read SB reg [01] failed");
udelay(W9968CF_I2C_BUS_DELAY);
return ret;
}
/*--------------------------------------------------------------------------
Upload quantization tables for the JPEG compression.
This function is called by w9968cf_start_transfer().
Return 0 on success, a negative number otherwise.
--------------------------------------------------------------------------*/
static int w9968cf_upload_quantizationtables(struct sd *sd)
{
u16 a, b;
int ret = 0, i, j;
ret += reg_w(sd, 0x39, 0x0010); /* JPEG clock enable */
for (i = 0, j = 0; i < 32; i++, j += 2) {
a = Y_QUANTABLE[j] | ((unsigned)(Y_QUANTABLE[j+1]) << 8);
b = UV_QUANTABLE[j] | ((unsigned)(UV_QUANTABLE[j+1]) << 8);
ret += reg_w(sd, 0x40+i, a);
ret += reg_w(sd, 0x60+i, b);
}
ret += reg_w(sd, 0x39, 0x0012); /* JPEG encoder enable */
return ret;
}
/****************************************************************************
* Low-level I2C I/O functions. *
* The adapter supports the following I2C transfer functions: *
* i2c_adap_fastwrite_byte_data() (at 400 kHz bit frequency only) *
* i2c_adap_read_byte_data() *
* i2c_adap_read_byte() *
****************************************************************************/
static int w9968cf_smbus_start(struct sd *sd)
{
int ret = 0;
ret += w9968cf_write_sb(sd, 0x0011); /* SDE=1, SDA=0, SCL=1 */
ret += w9968cf_write_sb(sd, 0x0010); /* SDE=1, SDA=0, SCL=0 */
return ret;
}
static int w9968cf_smbus_stop(struct sd *sd)
{
int ret = 0;
ret += w9968cf_write_sb(sd, 0x0010); /* SDE=1, SDA=0, SCL=0 */
ret += w9968cf_write_sb(sd, 0x0011); /* SDE=1, SDA=0, SCL=1 */
ret += w9968cf_write_sb(sd, 0x0013); /* SDE=1, SDA=1, SCL=1 */
return ret;
}
static int w9968cf_smbus_write_byte(struct sd *sd, u8 v)
{
u8 bit;
int ret = 0, sda;
for (bit = 0 ; bit < 8 ; bit++) {
sda = (v & 0x80) ? 2 : 0;
v <<= 1;
/* SDE=1, SDA=sda, SCL=0 */
ret += w9968cf_write_sb(sd, 0x10 | sda);
/* SDE=1, SDA=sda, SCL=1 */
ret += w9968cf_write_sb(sd, 0x11 | sda);
/* SDE=1, SDA=sda, SCL=0 */
ret += w9968cf_write_sb(sd, 0x10 | sda);
}
return ret;
}
static int w9968cf_smbus_read_byte(struct sd *sd, u8* v)
{
u8 bit;
int ret = 0;
/* No need to ensure SDA is high as we are always called after
read_ack which ends with SDA high */
*v = 0;
for (bit = 0 ; bit < 8 ; bit++) {
*v <<= 1;
/* SDE=1, SDA=1, SCL=1 */
ret += w9968cf_write_sb(sd, 0x0013);
*v |= (w9968cf_read_sb(sd) & 0x0008) ? 1 : 0;
/* SDE=1, SDA=1, SCL=0 */
ret += w9968cf_write_sb(sd, 0x0012);
}
return ret;
}
static int w9968cf_smbus_write_nack(struct sd *sd)
{
int ret = 0;
/* No need to ensure SDA is high as we are always called after
read_byte which ends with SDA high */
ret += w9968cf_write_sb(sd, 0x0013); /* SDE=1, SDA=1, SCL=1 */
ret += w9968cf_write_sb(sd, 0x0012); /* SDE=1, SDA=1, SCL=0 */
return ret;
}
static int w9968cf_smbus_read_ack(struct sd *sd)
{
int ret = 0, sda;
/* Ensure SDA is high before raising clock to avoid a spurious stop */
ret += w9968cf_write_sb(sd, 0x0012); /* SDE=1, SDA=1, SCL=0 */
ret += w9968cf_write_sb(sd, 0x0013); /* SDE=1, SDA=1, SCL=1 */
sda = w9968cf_read_sb(sd);
ret += w9968cf_write_sb(sd, 0x0012); /* SDE=1, SDA=1, SCL=0 */
if (sda < 0)
ret += sda;
else if (sda & 0x08) {
PDEBUG(D_USBI, "Did not receive i2c ACK");
ret += -1;
}
return ret;
}
/* SMBus protocol: S Addr Wr [A] Subaddr [A] Value [A] P */
static int w9968cf_i2c_w(struct sd *sd, u8 reg, u8 value)
{
u16* data = (u16 *)sd->gspca_dev.usb_buf;
int ret = 0;
data[0] = 0x082f | ((sd->sensor_addr & 0x80) ? 0x1500 : 0x0);
data[0] |= (sd->sensor_addr & 0x40) ? 0x4000 : 0x0;
data[1] = 0x2082 | ((sd->sensor_addr & 0x40) ? 0x0005 : 0x0);
data[1] |= (sd->sensor_addr & 0x20) ? 0x0150 : 0x0;
data[1] |= (sd->sensor_addr & 0x10) ? 0x5400 : 0x0;
data[2] = 0x8208 | ((sd->sensor_addr & 0x08) ? 0x0015 : 0x0);
data[2] |= (sd->sensor_addr & 0x04) ? 0x0540 : 0x0;
data[2] |= (sd->sensor_addr & 0x02) ? 0x5000 : 0x0;
data[3] = 0x1d20 | ((sd->sensor_addr & 0x02) ? 0x0001 : 0x0);
data[3] |= (sd->sensor_addr & 0x01) ? 0x0054 : 0x0;
ret += w9968cf_write_fsb(sd, data);
data[0] = 0x8208 | ((reg & 0x80) ? 0x0015 : 0x0);
data[0] |= (reg & 0x40) ? 0x0540 : 0x0;
data[0] |= (reg & 0x20) ? 0x5000 : 0x0;
data[1] = 0x0820 | ((reg & 0x20) ? 0x0001 : 0x0);
data[1] |= (reg & 0x10) ? 0x0054 : 0x0;
data[1] |= (reg & 0x08) ? 0x1500 : 0x0;
data[1] |= (reg & 0x04) ? 0x4000 : 0x0;
data[2] = 0x2082 | ((reg & 0x04) ? 0x0005 : 0x0);
data[2] |= (reg & 0x02) ? 0x0150 : 0x0;
data[2] |= (reg & 0x01) ? 0x5400 : 0x0;
data[3] = 0x001d;
ret += w9968cf_write_fsb(sd, data);
data[0] = 0x8208 | ((value & 0x80) ? 0x0015 : 0x0);
data[0] |= (value & 0x40) ? 0x0540 : 0x0;
data[0] |= (value & 0x20) ? 0x5000 : 0x0;
data[1] = 0x0820 | ((value & 0x20) ? 0x0001 : 0x0);
data[1] |= (value & 0x10) ? 0x0054 : 0x0;
data[1] |= (value & 0x08) ? 0x1500 : 0x0;
data[1] |= (value & 0x04) ? 0x4000 : 0x0;
data[2] = 0x2082 | ((value & 0x04) ? 0x0005 : 0x0);
data[2] |= (value & 0x02) ? 0x0150 : 0x0;
data[2] |= (value & 0x01) ? 0x5400 : 0x0;
data[3] = 0xfe1d;
ret += w9968cf_write_fsb(sd, data);
if (!ret)
PDEBUG(D_USBO, "i2c 0x%02x -> [0x%02x]", value, reg);
else
PDEBUG(D_ERR, "i2c 0x%02x -> [0x%02x] failed", value, reg);
return ret;
}
/* SMBus protocol: S Addr Wr [A] Subaddr [A] P S Addr+1 Rd [A] [Value] NA P */
static int w9968cf_i2c_r(struct sd *sd, u8 reg)
{
int ret = 0;
u8 value;
/* Fast serial bus data control disable */
ret += w9968cf_write_sb(sd, 0x0013); /* don't change ! */
ret += w9968cf_smbus_start(sd);
ret += w9968cf_smbus_write_byte(sd, sd->sensor_addr);
ret += w9968cf_smbus_read_ack(sd);
ret += w9968cf_smbus_write_byte(sd, reg);
ret += w9968cf_smbus_read_ack(sd);
ret += w9968cf_smbus_stop(sd);
ret += w9968cf_smbus_start(sd);
ret += w9968cf_smbus_write_byte(sd, sd->sensor_addr + 1);
ret += w9968cf_smbus_read_ack(sd);
ret += w9968cf_smbus_read_byte(sd, &value);
/* signal we don't want to read anymore, the v4l1 driver used to
send an ack here which is very wrong! (and then fixed
the issues this gave by retrying reads) */
ret += w9968cf_smbus_write_nack(sd);
ret += w9968cf_smbus_stop(sd);
/* Fast serial bus data control re-enable */
ret += w9968cf_write_sb(sd, 0x0030);
if (!ret) {
ret = value;
PDEBUG(D_USBI, "i2c [0x%02X] -> 0x%02X", reg, value);
} else
PDEBUG(D_ERR, "i2c read [0x%02x] failed", reg);
return ret;
}
/*--------------------------------------------------------------------------
Turn on the LED on some webcams. A beep should be heard too.
Return 0 on success, a negative number otherwise.
--------------------------------------------------------------------------*/
static int w9968cf_configure(struct sd *sd)
{
int ret = 0;
ret += reg_w(sd, 0x00, 0xff00); /* power-down */
ret += reg_w(sd, 0x00, 0xbf17); /* reset everything */
ret += reg_w(sd, 0x00, 0xbf10); /* normal operation */
ret += reg_w(sd, 0x01, 0x0010); /* serial bus, SDS high */
ret += reg_w(sd, 0x01, 0x0000); /* serial bus, SDS low */
ret += reg_w(sd, 0x01, 0x0010); /* ..high 'beep-beep' */
ret += reg_w(sd, 0x01, 0x0030); /* Set sda scl to FSB mode */
if (ret)
PDEBUG(D_ERR, "Couldn't turn on the LED");
sd->stopped = 1;
return ret;
}
static int w9968cf_init(struct sd *sd)
{
int ret = 0;
unsigned long hw_bufsize = sd->sif ? (352 * 288 * 2) : (640 * 480 * 2),
y0 = 0x0000,
u0 = y0 + hw_bufsize/2,
v0 = u0 + hw_bufsize/4,
y1 = v0 + hw_bufsize/4,
u1 = y1 + hw_bufsize/2,
v1 = u1 + hw_bufsize/4;
ret += reg_w(sd, 0x00, 0xff00); /* power off */
ret += reg_w(sd, 0x00, 0xbf10); /* power on */
ret += reg_w(sd, 0x03, 0x405d); /* DRAM timings */
ret += reg_w(sd, 0x04, 0x0030); /* SDRAM timings */
ret += reg_w(sd, 0x20, y0 & 0xffff); /* Y buf.0, low */
ret += reg_w(sd, 0x21, y0 >> 16); /* Y buf.0, high */
ret += reg_w(sd, 0x24, u0 & 0xffff); /* U buf.0, low */
ret += reg_w(sd, 0x25, u0 >> 16); /* U buf.0, high */
ret += reg_w(sd, 0x28, v0 & 0xffff); /* V buf.0, low */
ret += reg_w(sd, 0x29, v0 >> 16); /* V buf.0, high */
ret += reg_w(sd, 0x22, y1 & 0xffff); /* Y buf.1, low */
ret += reg_w(sd, 0x23, y1 >> 16); /* Y buf.1, high */
ret += reg_w(sd, 0x26, u1 & 0xffff); /* U buf.1, low */
ret += reg_w(sd, 0x27, u1 >> 16); /* U buf.1, high */
ret += reg_w(sd, 0x2a, v1 & 0xffff); /* V buf.1, low */
ret += reg_w(sd, 0x2b, v1 >> 16); /* V buf.1, high */
ret += reg_w(sd, 0x32, y1 & 0xffff); /* JPEG buf 0 low */
ret += reg_w(sd, 0x33, y1 >> 16); /* JPEG buf 0 high */
ret += reg_w(sd, 0x34, y1 & 0xffff); /* JPEG buf 1 low */
ret += reg_w(sd, 0x35, y1 >> 16); /* JPEG bug 1 high */
ret += reg_w(sd, 0x36, 0x0000);/* JPEG restart interval */
ret += reg_w(sd, 0x37, 0x0804);/*JPEG VLE FIFO threshold*/
ret += reg_w(sd, 0x38, 0x0000);/* disable hw up-scaling */
ret += reg_w(sd, 0x3f, 0x0000); /* JPEG/MCTL test data */
return ret;
}
static int w9968cf_set_crop_window(struct sd *sd)
{
int ret = 0, start_cropx, start_cropy, x, y, fw, fh, cw, ch,
max_width, max_height;
if (sd->sif) {
max_width = 352;
max_height = 288;
} else {
max_width = 640;
max_height = 480;
}
if (sd->sensor == SEN_OV7620) {
/* Sigh, this is dependend on the clock / framerate changes
made by the frequency control, sick. */
if (sd->freq == 1) {
start_cropx = 279;
start_cropy = 35;
} else {
start_cropx = 103;
start_cropy = 35;
}
} else {
start_cropx = 320;
start_cropy = 35;
}
/* Work around to avoid FP arithmetics */
#define SC(x) ((x) << 10)
/* Scaling factors */
fw = SC(sd->gspca_dev.width) / max_width;
fh = SC(sd->gspca_dev.height) / max_height;
cw = (fw >= fh) ? max_width : SC(sd->gspca_dev.width)/fh;
ch = (fw >= fh) ? SC(sd->gspca_dev.height)/fw : max_height;
sd->sensor_width = max_width;
sd->sensor_height = max_height;
x = (max_width - cw) / 2;
y = (max_height - ch) / 2;
ret += reg_w(sd, 0x10, start_cropx + x);
ret += reg_w(sd, 0x11, start_cropy + y);
ret += reg_w(sd, 0x12, start_cropx + x + cw);
ret += reg_w(sd, 0x13, start_cropy + y + ch);
return ret;
}
static int w9968cf_mode_init_regs(struct sd *sd)
{
int ret = 0, val, vs_polarity, hs_polarity;
ret += w9968cf_set_crop_window(sd);
ret += reg_w(sd, 0x14, sd->gspca_dev.width);
ret += reg_w(sd, 0x15, sd->gspca_dev.height);
/* JPEG width & height */
ret += reg_w(sd, 0x30, sd->gspca_dev.width);
ret += reg_w(sd, 0x31, sd->gspca_dev.height);
/* Y & UV frame buffer strides (in WORD) */
ret += reg_w(sd, 0x2c, sd->gspca_dev.width);
ret += reg_w(sd, 0x00, 0xbf17); /* reset everything */
ret += reg_w(sd, 0x00, 0xbf10); /* normal operation */
/* Transfer size */
/* FIXME JPEG * 4 ?? */
val = sd->gspca_dev.width * sd->gspca_dev.height;
ret += reg_w(sd, 0x3d, val & 0xffff); /* low bits */
ret += reg_w(sd, 0x3e, val >> 16); /* high bits */
/* Video Capture Control Register */
if (sd->sensor == SEN_OV7620) {
/* Seems to work around a bug in the image sensor */
vs_polarity = 1;
hs_polarity = 1;
} else {
vs_polarity = 1;
hs_polarity = 0;
}
val = (vs_polarity << 12) | (hs_polarity << 11);
val |= 0x0080; /* Enable HW double buffering */
/* val |= 0x0020; enable clamping */
/* val |= 0x0008; enable (1-2-1) filter */
/* val |= 0x000c; enable (2-3-6-3-2) filter */
val |= 0x8000; /* capt. enable */
ret += reg_w(sd, 0x16, val);
sd->gspca_dev.empty_packet = 0;
return ret;
}
static void w9968cf_pkt_scan(struct gspca_dev *gspca_dev,
struct gspca_frame *frame, /* target */
__u8 *data, /* isoc packet */
int len) /* iso packet length */
{
/* An empty packet signals EOF */
if (gspca_dev->empty_packet) {
frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
data, len);
gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
NULL, 0);
gspca_dev->empty_packet = 0;
}
gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册