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

V4L/DVB (13181): gspca w9968cf: Add support for JPEG compression

gspca w9968cf: Add support for JPEG compression, this enables much higher
framerates at 320x240 / 352x288 and also allows for 640x480 mode for
cams which can do this. The w9968cf uses planar JPEG, which libv4l until
now did not support, so this requires atleast version 0.6.3 of libv4l.

And something important I forgot to mention in my earlier w9968cf commits:
Many thanks to Hans Verkuil for giving me a w9968cf based cam, which has
allowed me to develop the gspca w9968cf support.
Signed-off-by: NHans de Goede <hdegoede@redhat.com>
Signed-off-by: NMauro Carvalho Chehab <mchehab@redhat.com>
上级 8394bcf3
...@@ -956,7 +956,8 @@ config VIDEO_OVCAMCHIP ...@@ -956,7 +956,8 @@ config VIDEO_OVCAMCHIP
---help--- ---help---
This driver is DEPRECATED please use the gspca ov519 module This driver is DEPRECATED please use the gspca ov519 module
instead. Note that for the ov511 / ov518 support of the gspca module instead. Note that for the ov511 / ov518 support of the gspca module
you need atleast version 0.6.0 of libv4l. you need atleast version 0.6.0 of libv4l and for the w9968cf
atleast version 0.6.3 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
...@@ -970,7 +971,8 @@ config USB_W9968CF ...@@ -970,7 +971,8 @@ config USB_W9968CF
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 This driver is DEPRECATED please use the gspca ov519 module
instead. instead. Note that for the w9968cf support of the gspca module
you need atleast version 0.6.3 of libv4l.
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.
......
...@@ -80,6 +80,10 @@ struct sd { ...@@ -80,6 +80,10 @@ struct sd {
__u8 vflip; __u8 vflip;
__u8 autobrightness; __u8 autobrightness;
__u8 freq; __u8 freq;
__u8 quality;
#define QUALITY_MIN 50
#define QUALITY_MAX 70
#define QUALITY_DEF 50
__u8 stopped; /* Streaming is temporarily paused */ __u8 stopped; /* Streaming is temporarily paused */
...@@ -104,6 +108,8 @@ struct sd { ...@@ -104,6 +108,8 @@ struct sd {
int sensor_width; int sensor_width;
int sensor_height; int sensor_height;
int sensor_reg_cache[256]; int sensor_reg_cache[256];
u8 *jpeg_hdr;
}; };
/* Note this is a bit of a hack, but the w9968cf driver needs the code for all /* Note this is a bit of a hack, but the w9968cf driver needs the code for all
...@@ -2303,8 +2309,6 @@ static int i2c_w_mask(struct sd *sd, ...@@ -2303,8 +2309,6 @@ 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) {
...@@ -2319,10 +2323,7 @@ static inline int ov51x_stop(struct sd *sd) ...@@ -2319,10 +2323,7 @@ static inline int ov51x_stop(struct sd *sd)
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: case BRIDGE_W9968CF:
ret = reg_w(sd, 0x3c, 0x0a05); /* stop USB transfer */ return 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;
...@@ -3088,8 +3089,8 @@ static int sd_config(struct gspca_dev *gspca_dev, ...@@ -3088,8 +3089,8 @@ static int sd_config(struct gspca_dev *gspca_dev,
case BRIDGE_W9968CF: case BRIDGE_W9968CF:
cam->cam_mode = w9968cf_vga_mode; cam->cam_mode = w9968cf_vga_mode;
cam->nmodes = ARRAY_SIZE(w9968cf_vga_mode); cam->nmodes = ARRAY_SIZE(w9968cf_vga_mode);
/* if (sd->sif) if (sd->sif)
cam->nmodes--; */ cam->nmodes--;
/* w9968cf needs initialisation once the sensor is known */ /* w9968cf needs initialisation once the sensor is known */
if (w9968cf_init(sd) < 0) if (w9968cf_init(sd) < 0)
...@@ -3113,6 +3114,7 @@ static int sd_config(struct gspca_dev *gspca_dev, ...@@ -3113,6 +3114,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
gspca_dev->ctrl_dis = (1 << HFLIP_IDX) | (1 << VFLIP_IDX) | gspca_dev->ctrl_dis = (1 << HFLIP_IDX) | (1 << VFLIP_IDX) |
(1 << OV7670_FREQ_IDX); (1 << OV7670_FREQ_IDX);
} }
sd->quality = QUALITY_DEF;
if (sd->sensor == SEN_OV7640 || sd->sensor == SEN_OV7670) if (sd->sensor == SEN_OV7640 || sd->sensor == SEN_OV7670)
gspca_dev->ctrl_dis |= 1 << AUTOBRIGHT_IDX; gspca_dev->ctrl_dis |= 1 << AUTOBRIGHT_IDX;
/* OV8610 Frequency filter control should work but needs testing */ /* OV8610 Frequency filter control should work but needs testing */
...@@ -3909,6 +3911,14 @@ static void sd_stopN(struct gspca_dev *gspca_dev) ...@@ -3909,6 +3911,14 @@ static void sd_stopN(struct gspca_dev *gspca_dev)
ov51x_led_control(sd, 0); ov51x_led_control(sd, 0);
} }
static void sd_stop0(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
if (sd->bridge == BRIDGE_W9968CF)
w9968cf_stop0(sd);
}
static void ov511_pkt_scan(struct gspca_dev *gspca_dev, static void ov511_pkt_scan(struct gspca_dev *gspca_dev,
struct gspca_frame *frame, /* target */ struct gspca_frame *frame, /* target */
__u8 *in, /* isoc packet */ __u8 *in, /* isoc packet */
...@@ -4421,6 +4431,45 @@ static int sd_querymenu(struct gspca_dev *gspca_dev, ...@@ -4421,6 +4431,45 @@ static int sd_querymenu(struct gspca_dev *gspca_dev,
return -EINVAL; return -EINVAL;
} }
static int sd_get_jcomp(struct gspca_dev *gspca_dev,
struct v4l2_jpegcompression *jcomp)
{
struct sd *sd = (struct sd *) gspca_dev;
if (sd->bridge != BRIDGE_W9968CF)
return -EINVAL;
memset(jcomp, 0, sizeof *jcomp);
jcomp->quality = sd->quality;
jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT | V4L2_JPEG_MARKER_DQT |
V4L2_JPEG_MARKER_DRI;
return 0;
}
static int sd_set_jcomp(struct gspca_dev *gspca_dev,
struct v4l2_jpegcompression *jcomp)
{
struct sd *sd = (struct sd *) gspca_dev;
if (sd->bridge != BRIDGE_W9968CF)
return -EINVAL;
if (gspca_dev->streaming)
return -EBUSY;
if (jcomp->quality < QUALITY_MIN)
sd->quality = QUALITY_MIN;
else if (jcomp->quality > QUALITY_MAX)
sd->quality = QUALITY_MAX;
else
sd->quality = jcomp->quality;
/* Return resulting jcomp params to app */
sd_get_jcomp(gspca_dev, jcomp);
return 0;
}
/* sub-driver description */ /* sub-driver description */
static const struct sd_desc sd_desc = { static const struct sd_desc sd_desc = {
.name = MODULE_NAME, .name = MODULE_NAME,
...@@ -4430,8 +4479,11 @@ static const struct sd_desc sd_desc = { ...@@ -4430,8 +4479,11 @@ static const struct sd_desc sd_desc = {
.init = sd_init, .init = sd_init,
.start = sd_start, .start = sd_start,
.stopN = sd_stopN, .stopN = sd_stopN,
.stop0 = sd_stop0,
.pkt_scan = sd_pkt_scan, .pkt_scan = sd_pkt_scan,
.querymenu = sd_querymenu, .querymenu = sd_querymenu,
.get_jcomp = sd_get_jcomp,
.set_jcomp = sd_set_jcomp,
}; };
/* -- module initialisation -- */ /* -- module initialisation -- */
......
...@@ -31,57 +31,14 @@ ...@@ -31,57 +31,14 @@
the sensor drivers to v4l2 sub drivers, and properly split of this the sensor drivers to v4l2 sub drivers, and properly split of this
driver from ov519.c */ driver from ov519.c */
#define W9968CF_I2C_BUS_DELAY 4 /* delay in us for I2C bit r/w operations */ /* The CONEX_CAM define for jpeg.h needs renaming, now its used here too */
#define CONEX_CAM
#include "jpeg.h"
/* FIXME make this runtime configurable */ #define W9968CF_I2C_BUS_DELAY 4 /* delay in us for I2C bit r/w operations */
/* 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] = { #define Y_QUANTABLE (sd->jpeg_hdr + JPEG_QT0_OFFSET)
17, 18, 24, 47, 99, 99, 99, 99, #define UV_QUANTABLE (sd->jpeg_hdr + JPEG_QT1_OFFSET)
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[] = { static const struct v4l2_pix_format w9968cf_vga_mode[] = {
{160, 120, V4L2_PIX_FMT_UYVY, V4L2_FIELD_NONE, {160, 120, V4L2_PIX_FMT_UYVY, V4L2_FIELD_NONE,
...@@ -92,18 +49,18 @@ static const struct v4l2_pix_format w9968cf_vga_mode[] = { ...@@ -92,18 +49,18 @@ static const struct v4l2_pix_format w9968cf_vga_mode[] = {
.bytesperline = 176 * 2, .bytesperline = 176 * 2,
.sizeimage = 176 * 144 * 2, .sizeimage = 176 * 144 * 2,
.colorspace = V4L2_COLORSPACE_JPEG}, .colorspace = V4L2_COLORSPACE_JPEG},
{320, 240, V4L2_PIX_FMT_UYVY, V4L2_FIELD_NONE, {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 320 * 2, .bytesperline = 320 * 2,
.sizeimage = 320 * 240 * 2, .sizeimage = 320 * 240 * 2,
.colorspace = V4L2_COLORSPACE_JPEG}, .colorspace = V4L2_COLORSPACE_JPEG},
{352, 288, V4L2_PIX_FMT_UYVY, V4L2_FIELD_NONE, {352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 352 * 2, .bytesperline = 352 * 2,
.sizeimage = 352 * 288 * 2, .sizeimage = 352 * 288 * 2,
.colorspace = V4L2_COLORSPACE_JPEG}, .colorspace = V4L2_COLORSPACE_JPEG},
/* {640, 480, V4L2_PIX_FMT_UYVY, V4L2_FIELD_NONE, {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 640 * 2, .bytesperline = 640 * 2,
.sizeimage = 640 * 480 * 2, .sizeimage = 640 * 480 * 2,
.colorspace = V4L2_COLORSPACE_JPEG}, */ .colorspace = V4L2_COLORSPACE_JPEG},
}; };
static int reg_w(struct sd *sd, __u16 index, __u16 value); static int reg_w(struct sd *sd, __u16 index, __u16 value);
...@@ -534,17 +491,35 @@ static int w9968cf_mode_init_regs(struct sd *sd) ...@@ -534,17 +491,35 @@ static int w9968cf_mode_init_regs(struct sd *sd)
ret += reg_w(sd, 0x31, sd->gspca_dev.height); ret += reg_w(sd, 0x31, sd->gspca_dev.height);
/* Y & UV frame buffer strides (in WORD) */ /* Y & UV frame buffer strides (in WORD) */
if (w9968cf_vga_mode[sd->gspca_dev.curr_mode].pixelformat ==
V4L2_PIX_FMT_JPEG) {
ret += reg_w(sd, 0x2c, sd->gspca_dev.width/2);
ret += reg_w(sd, 0x2d, sd->gspca_dev.width/4);
} else
ret += reg_w(sd, 0x2c, sd->gspca_dev.width); ret += reg_w(sd, 0x2c, sd->gspca_dev.width);
ret += reg_w(sd, 0x00, 0xbf17); /* reset everything */ ret += reg_w(sd, 0x00, 0xbf17); /* reset everything */
ret += reg_w(sd, 0x00, 0xbf10); /* normal operation */ ret += reg_w(sd, 0x00, 0xbf10); /* normal operation */
/* Transfer size */ /* Transfer size in WORDS (for UYVY format only) */
/* FIXME JPEG * 4 ?? */
val = sd->gspca_dev.width * sd->gspca_dev.height; val = sd->gspca_dev.width * sd->gspca_dev.height;
ret += reg_w(sd, 0x3d, val & 0xffff); /* low bits */ ret += reg_w(sd, 0x3d, val & 0xffff); /* low bits */
ret += reg_w(sd, 0x3e, val >> 16); /* high bits */ ret += reg_w(sd, 0x3e, val >> 16); /* high bits */
if (w9968cf_vga_mode[sd->gspca_dev.curr_mode].pixelformat ==
V4L2_PIX_FMT_JPEG) {
/* We may get called multiple times (usb isoc bw negotiat.) */
if (!sd->jpeg_hdr)
sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL);
if (!sd->jpeg_hdr)
return -ENOMEM;
jpeg_define(sd->jpeg_hdr, sd->gspca_dev.height,
sd->gspca_dev.width, 0x22); /* JPEG 420 */
jpeg_set_qual(sd->jpeg_hdr, sd->quality);
ret += w9968cf_upload_quantizationtables(sd);
}
/* Video Capture Control Register */ /* Video Capture Control Register */
if (sd->sensor == SEN_OV7620) { if (sd->sensor == SEN_OV7620) {
/* Seems to work around a bug in the image sensor */ /* Seems to work around a bug in the image sensor */
...@@ -557,6 +532,15 @@ static int w9968cf_mode_init_regs(struct sd *sd) ...@@ -557,6 +532,15 @@ static int w9968cf_mode_init_regs(struct sd *sd)
val = (vs_polarity << 12) | (hs_polarity << 11); val = (vs_polarity << 12) | (hs_polarity << 11);
/* NOTE: We may not have enough memory to do double buffering while
doing compression (amount of memory differs per model cam).
So we use the second image buffer also as jpeg stream buffer
(see w9968cf_init), and disable double buffering. */
if (w9968cf_vga_mode[sd->gspca_dev.curr_mode].pixelformat ==
V4L2_PIX_FMT_JPEG) {
/* val |= 0x0002; YUV422P */
val |= 0x0003; /* YUV420P */
} else
val |= 0x0080; /* Enable HW double buffering */ val |= 0x0080; /* Enable HW double buffering */
/* val |= 0x0020; enable clamping */ /* val |= 0x0020; enable clamping */
...@@ -572,18 +556,55 @@ static int w9968cf_mode_init_regs(struct sd *sd) ...@@ -572,18 +556,55 @@ static int w9968cf_mode_init_regs(struct sd *sd)
return ret; return ret;
} }
static void w9968cf_stop0(struct sd *sd)
{
if (sd->gspca_dev.present) {
reg_w(sd, 0x39, 0x0000); /* disable JPEG encoder */
reg_w(sd, 0x16, 0x0000); /* stop video capture */
}
kfree(sd->jpeg_hdr);
sd->jpeg_hdr = NULL;
}
/* The w9968cf docs say that a 0 sized packet means EOF (and also SOF
for the next frame). This seems to simply not be true when operating
in JPEG mode, in this case there may be empty packets within the
frame. So in JPEG mode use the JPEG SOI marker to detect SOF.
Note to make things even more interesting the w9968cf sends *PLANAR* jpeg,
to be precise it sends: SOI, SOF, DRI, SOS, Y-data, SOS, U-data, SOS,
V-data, EOI. */
static void w9968cf_pkt_scan(struct gspca_dev *gspca_dev, static void w9968cf_pkt_scan(struct gspca_dev *gspca_dev,
struct gspca_frame *frame, /* target */ struct gspca_frame *frame, /* target */
__u8 *data, /* isoc packet */ __u8 *data, /* isoc packet */
int len) /* iso packet length */ int len) /* iso packet length */
{ {
/* An empty packet signals EOF */ struct sd *sd = (struct sd *) gspca_dev;
if (gspca_dev->empty_packet) {
frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, if (w9968cf_vga_mode[gspca_dev->curr_mode].pixelformat ==
V4L2_PIX_FMT_JPEG) {
if (len >= 2 &&
data[0] == 0xff &&
data[1] == 0xd8) {
frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
NULL, 0); NULL, 0);
gspca_frame_add(gspca_dev, FIRST_PACKET, frame, gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
NULL, 0); sd->jpeg_hdr, JPEG_HDR_SZ);
gspca_dev->empty_packet = 0; /* Strip the ff d8, our own header (which adds
huffman and quantization tables) already has this */
len -= 2;
data += 2;
}
} else {
/* In UYVY mode an empty packet signals EOF */
if (gspca_dev->empty_packet) {
frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
NULL, 0);
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); 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.
先完成此消息的编辑!
想要评论请 注册