summaryrefslogtreecommitdiff
path: root/linux/drivers/media
diff options
context:
space:
mode:
authorMauro Carvalho Chehab <mchehab@redhat.com>2009-07-20 21:49:25 -0300
committerMauro Carvalho Chehab <mchehab@redhat.com>2009-07-20 21:49:25 -0300
commita0445b01893489ee01d54d08da80d29a24434e0a (patch)
tree183ffaf24616779e2550c86540bf445e86f3b13d /linux/drivers/media
parent59ac780bc36a1d932e6290e0ea492085f2ec35cd (diff)
parentbf2a43cd610fd4c43b23e6dc87e086848de1b290 (diff)
downloadmediapointer-dvb-s2-a0445b01893489ee01d54d08da80d29a24434e0a.tar.gz
mediapointer-dvb-s2-a0445b01893489ee01d54d08da80d29a24434e0a.tar.bz2
merge: http://udev.netup.ru/hg/v4l-dvb-commits
From: Mauro Carvalho Chehab <mchehab@redhat.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'linux/drivers/media')
-rw-r--r--linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c275
-rw-r--r--linux/drivers/media/dvb/dvb-core/dmxdev.c231
-rw-r--r--linux/drivers/media/dvb/dvb-core/dmxdev.h9
-rw-r--r--linux/drivers/media/dvb/dvb-core/dvb_demux.c8
-rw-r--r--linux/drivers/media/dvb/frontends/dvb-pll.c75
-rw-r--r--linux/drivers/media/dvb/frontends/dvb-pll.h4
-rw-r--r--linux/drivers/media/dvb/frontends/mt312.c1
-rw-r--r--linux/drivers/media/video/bt8xx/bttv-cards.c92
-rw-r--r--linux/drivers/media/video/bt8xx/bttv-driver.c1
-rw-r--r--linux/drivers/media/video/bt8xx/bttv.h1
-rw-r--r--linux/drivers/media/video/cx23885/cx23885-417.c61
-rw-r--r--linux/drivers/media/video/cx23885/cx23885-cards.c65
-rw-r--r--linux/drivers/media/video/cx23885/cx23885-core.c30
-rw-r--r--linux/drivers/media/video/cx23885/cx23885-dvb.c11
-rw-r--r--linux/drivers/media/video/cx23885/cx23885.h11
-rw-r--r--linux/drivers/media/video/em28xx/em28xx-video.c125
-rw-r--r--linux/drivers/media/video/gspca/Kconfig16
-rw-r--r--linux/drivers/media/video/gspca/Makefile2
-rw-r--r--linux/drivers/media/video/gspca/conex.c2
-rw-r--r--linux/drivers/media/video/gspca/gspca.c73
-rw-r--r--linux/drivers/media/video/gspca/gspca.h9
-rw-r--r--linux/drivers/media/video/gspca/mars.c2
-rw-r--r--linux/drivers/media/video/gspca/sn9c20x.c2439
-rw-r--r--linux/drivers/media/video/gspca/sonixj.c21
-rw-r--r--linux/drivers/media/video/gspca/spca500.c2
-rw-r--r--linux/drivers/media/video/gspca/stk014.c2
-rw-r--r--linux/drivers/media/video/gspca/sunplus.c12
-rw-r--r--linux/drivers/media/video/gspca/zc3xx.c2
-rw-r--r--linux/drivers/media/video/zr364xx.c1165
29 files changed, 4137 insertions, 610 deletions
diff --git a/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c b/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c
index bc370183e..301bf1688 100644
--- a/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c
+++ b/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c
@@ -20,8 +20,14 @@
#include "tuner-simple.h"
#include "stv0297.h"
+
+/* Can we use the specified front-end? Remember that if we are compiled
+ * into the kernel we can't call code that's in modules. */
+#define FE_SUPPORTED(fe) (defined(CONFIG_DVB_##fe) || \
+ (defined(CONFIG_DVB_##fe##_MODULE) && defined(MODULE)))
+
/* lnb control */
-#if defined(CONFIG_DVB_MT312_MODULE) || defined(CONFIG_DVB_STV0299_MODULE)
+#if FE_SUPPORTED(MT312) || FE_SUPPORTED(STV0299)
static int flexcop_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
{
struct flexcop_device *fc = fe->dvb->priv;
@@ -49,8 +55,7 @@ static int flexcop_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage
}
#endif
-#if defined(CONFIG_DVB_S5H1420_MODULE) || defined(CONFIG_DVB_STV0299_MODULE) \
- || defined(CONFIG_DVB_MT312_MODULE)
+#if FE_SUPPORTED(S5H1420) || FE_SUPPORTED(STV0299) || FE_SUPPORTED(MT312)
static int flexcop_sleep(struct dvb_frontend* fe)
{
struct flexcop_device *fc = fe->dvb->priv;
@@ -61,7 +66,7 @@ static int flexcop_sleep(struct dvb_frontend* fe)
#endif
/* SkyStar2 DVB-S rev 2.3 */
-#if defined(CONFIG_DVB_MT312_MODULE)
+#if FE_SUPPORTED(MT312) && FE_SUPPORTED(PLL)
static int flexcop_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone)
{
/* u16 wz_half_period_for_45_mhz[] = { 0x01ff, 0x0154, 0x00ff, 0x00cc }; */
@@ -150,53 +155,34 @@ static struct mt312_config skystar23_samsung_tbdu18132_config = {
.demod_address = 0x0e,
};
-static int skystar23_samsung_tbdu18132_tuner_set_params(struct dvb_frontend *fe,
- struct dvb_frontend_parameters *params)
-{
- u8 buf[4];
- u32 div;
- struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf,
- .len = sizeof(buf) };
- struct flexcop_device *fc = fe->dvb->priv;
- div = (params->frequency + (125/2)) / 125;
-
- buf[0] = (div >> 8) & 0x7f;
- buf[1] = (div >> 0) & 0xff;
- buf[2] = 0x84 | ((div >> 10) & 0x60);
- buf[3] = 0x80;
-
- if (params->frequency < 1550000)
- buf[3] |= 0x02;
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1);
- if (i2c_transfer(&fc->fc_i2c_adap[0].i2c_adap, &msg, 1) != 1)
- return -EIO;
- return 0;
-}
-
static int skystar2_rev23_attach(struct flexcop_device *fc,
struct i2c_adapter *i2c)
{
+ struct dvb_frontend_ops *ops;
+
fc->fe = dvb_attach(mt312_attach, &skystar23_samsung_tbdu18132_config, i2c);
- if (fc->fe != NULL) {
- struct dvb_frontend_ops *ops = &fc->fe->ops;
- ops->tuner_ops.set_params =
- skystar23_samsung_tbdu18132_tuner_set_params;
- ops->diseqc_send_master_cmd = flexcop_diseqc_send_master_cmd;
- ops->diseqc_send_burst = flexcop_diseqc_send_burst;
- ops->set_tone = flexcop_set_tone;
- ops->set_voltage = flexcop_set_voltage;
- fc->fe_sleep = ops->sleep;
- ops->sleep = flexcop_sleep;
- return 1;
- }
- return 0;
+ if (!fc->fe)
+ return 0;
+
+ if (!dvb_attach(dvb_pll_attach, fc->fe, 0x61, i2c,
+ DVB_PLL_SAMSUNG_TBDU18132))
+ return 0;
+
+ ops = &fc->fe->ops;
+ ops->diseqc_send_master_cmd = flexcop_diseqc_send_master_cmd;
+ ops->diseqc_send_burst = flexcop_diseqc_send_burst;
+ ops->set_tone = flexcop_set_tone;
+ ops->set_voltage = flexcop_set_voltage;
+ fc->fe_sleep = ops->sleep;
+ ops->sleep = flexcop_sleep;
+ return 1;
}
+#else
+#define skystar2_rev23_attach NULL
#endif
/* SkyStar2 DVB-S rev 2.6 */
-#if defined(CONFIG_DVB_STV0299_MODULE)
+#if FE_SUPPORTED(STV0299) && FE_SUPPORTED(PLL)
static int samsung_tbmu24112_set_symbol_rate(struct dvb_frontend *fe,
u32 srate, u32 ratio)
{
@@ -225,31 +211,6 @@ static int samsung_tbmu24112_set_symbol_rate(struct dvb_frontend *fe,
return 0;
}
-static int samsung_tbmu24112_tuner_set_params(struct dvb_frontend *fe,
- struct dvb_frontend_parameters *params)
-{
- u8 buf[4];
- u32 div;
- struct i2c_msg msg = {
- .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) };
- struct flexcop_device *fc = fe->dvb->priv;
- div = params->frequency / 125;
-
- buf[0] = (div >> 8) & 0x7f;
- buf[1] = div & 0xff;
- buf[2] = 0x84; /* 0xC4 */
- buf[3] = 0x08;
-
- if (params->frequency < 1500000)
- buf[3] |= 0x10;
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1);
- if (i2c_transfer(&fc->fc_i2c_adap[0].i2c_adap, &msg, 1) != 1)
- return -EIO;
- return 0;
-}
-
static u8 samsung_tbmu24112_inittab[] = {
0x01, 0x15,
0x02, 0x30,
@@ -311,20 +272,25 @@ static int skystar2_rev26_attach(struct flexcop_device *fc,
struct i2c_adapter *i2c)
{
fc->fe = dvb_attach(stv0299_attach, &samsung_tbmu24112_config, i2c);
- if (fc->fe != NULL) {
- struct dvb_frontend_ops *ops = &fc->fe->ops;
- ops->tuner_ops.set_params = samsung_tbmu24112_tuner_set_params;
- ops->set_voltage = flexcop_set_voltage;
- fc->fe_sleep = ops->sleep;
- ops->sleep = flexcop_sleep;
- return 1;
- }
- return 0;
+ if (!fc->fe)
+ return 0;
+
+ if (!dvb_attach(dvb_pll_attach, fc->fe, 0x61, i2c,
+ DVB_PLL_SAMSUNG_TBMU24112))
+ return 0;
+
+ fc->fe->ops.set_voltage = flexcop_set_voltage;
+ fc->fe_sleep = fc->fe->ops.sleep;
+ fc->fe->ops.sleep = flexcop_sleep;
+ return 1;
+
}
+#else
+#define skystar2_rev26_attach NULL
#endif
/* SkyStar2 DVB-S rev 2.7 */
-#if defined(CONFIG_DVB_S5H1420_MODULE)
+#if FE_SUPPORTED(S5H1420) && FE_SUPPORTED(ISL6421) && FE_SUPPORTED(TUNER_ITD1000)
static struct s5h1420_config skystar2_rev2_7_s5h1420_config = {
.demod_address = 0x53,
.invert = 1,
@@ -385,10 +351,12 @@ fail:
fc->fc_i2c_adap[0].no_base_addr = 0;
return 0;
}
+#else
+#define skystar2_rev27_attach NULL
#endif
/* SkyStar2 rev 2.8 */
-#if defined(CONFIG_DVB_CX24123_MODULE)
+#if FE_SUPPORTED(CX24123) && FE_SUPPORTED(ISL6421) && FE_SUPPORTED(TUNER_CX24113)
static struct cx24123_config skystar2_rev2_8_cx24123_config = {
.demod_address = 0x55,
.dont_use_pll = 1,
@@ -433,10 +401,12 @@ static int skystar2_rev28_attach(struct flexcop_device *fc,
* IR-receiver (PIC16F818) - but the card has no input for that ??? */
return 1;
}
+#else
+#define skystar2_rev28_attach NULL
#endif
/* AirStar DVB-T */
-#if defined(CONFIG_DVB_MT352_MODULE)
+#if FE_SUPPORTED(MT352) && FE_SUPPORTED(PLL)
static int samsung_tdtc9251dh0_demod_init(struct dvb_frontend *fe)
{
static u8 mt352_clock_config[] = { 0x89, 0x18, 0x2d };
@@ -454,32 +424,6 @@ static int samsung_tdtc9251dh0_demod_init(struct dvb_frontend *fe)
return 0;
}
-static int samsung_tdtc9251dh0_calc_regs(struct dvb_frontend *fe,
- struct dvb_frontend_parameters *params, u8* pllbuf, int buf_len)
-{
- u32 div;
- unsigned char bs = 0;
-
- if (buf_len < 5)
- return -EINVAL;
-
-#define IF_FREQUENCYx6 217 /* 6 * 36.16666666667MHz */
- div = (((params->frequency + 83333) * 3) / 500000) + IF_FREQUENCYx6;
- if (params->frequency >= 48000000 && params->frequency <= 154000000) \
- bs = 0x09;
- if (params->frequency >= 161000000 && params->frequency <= 439000000) \
- bs = 0x0a;
- if (params->frequency >= 447000000 && params->frequency <= 863000000) \
- bs = 0x08;
-
- pllbuf[0] = 0x61;
- pllbuf[1] = div >> 8;
- pllbuf[2] = div & 0xff;
- pllbuf[3] = 0xcc;
- pllbuf[4] = bs;
- return 5;
-}
-
static struct mt352_config samsung_tdtc9251dh0_config = {
.demod_address = 0x0f,
.demod_init = samsung_tdtc9251dh0_demod_init,
@@ -489,16 +433,18 @@ static int airstar_dvbt_attach(struct flexcop_device *fc,
struct i2c_adapter *i2c)
{
fc->fe = dvb_attach(mt352_attach, &samsung_tdtc9251dh0_config, i2c);
- if (fc->fe != NULL) {
- fc->fe->ops.tuner_ops.calc_regs = samsung_tdtc9251dh0_calc_regs;
- return 1;
- }
- return 0;
+ if (!fc->fe)
+ return 0;
+
+ return !!dvb_attach(dvb_pll_attach, fc->fe, 0x61, NULL,
+ DVB_PLL_SAMSUNG_TDTC9251DH0);
}
+#else
+#define airstar_dvbt_attach NULL
#endif
/* AirStar ATSC 1st generation */
-#if defined(CONFIG_DVB_BCM3510_MODULE)
+#if FE_SUPPORTED(BCM3510)
static int flexcop_fe_request_firmware(struct dvb_frontend *fe,
const struct firmware **fw, char* name)
{
@@ -517,10 +463,12 @@ static int airstar_atsc1_attach(struct flexcop_device *fc,
fc->fe = dvb_attach(bcm3510_attach, &air2pc_atsc_first_gen_config, i2c);
return fc->fe != NULL;
}
+#else
+#define airstar_atsc1_attach NULL
#endif
/* AirStar ATSC 2nd generation */
-#if defined(CONFIG_DVB_NXT200X_MODULE)
+#if FE_SUPPORTED(NXT200X) && FE_SUPPORTED(PLL)
static struct nxt200x_config samsung_tbmv_config = {
.demod_address = 0x0a,
};
@@ -535,10 +483,12 @@ static int airstar_atsc2_attach(struct flexcop_device *fc,
return !!dvb_attach(dvb_pll_attach, fc->fe, 0x61, NULL,
DVB_PLL_SAMSUNG_TBMV);
}
+#else
+#define airstar_atsc2_attach NULL
#endif
/* AirStar ATSC 3rd generation */
-#if defined(CONFIG_DVB_LGDT330X_MODULE)
+#if FE_SUPPORTED(LGDT330X)
static struct lgdt330x_config air2pc_atsc_hd5000_config = {
.demod_address = 0x59,
.demod_chip = LGDT3303,
@@ -556,57 +506,12 @@ static int airstar_atsc3_attach(struct flexcop_device *fc,
return !!dvb_attach(simple_tuner_attach, fc->fe, i2c, 0x61,
TUNER_LG_TDVS_H06XF);
}
+#else
+#define airstar_atsc3_attach NULL
#endif
/* CableStar2 DVB-C */
-#if defined(CONFIG_DVB_STV0297_MODULE)
-static int alps_tdee4_stv0297_tuner_set_params(struct dvb_frontend* fe,
- struct dvb_frontend_parameters *fep)
-{
- struct flexcop_device *fc = fe->dvb->priv;
- u8 buf[4];
- u16 div;
- int ret;
-
-/* 62.5 kHz * 10 */
-#define REF_FREQ 625
-#define FREQ_OFFSET 36125
-
- div = ((fep->frequency/1000 + FREQ_OFFSET) * 10) / REF_FREQ;
-/* 4 MHz = 4000 KHz */
-
- buf[0] = (u8)( div >> 8) & 0x7f;
- buf[1] = (u8) div & 0xff;
-
-/* F(osc) = N * Reference Freq. (62.5 kHz)
- * byte 2 : 0 N14 N13 N12 N11 N10 N9 N8
- * byte 3 : N7 N6 N5 N4 N3 N2 N1 N0
- * byte 4 : 1 * * AGD R3 R2 R1 R0
- * byte 5 : C1 * RE RTS BS4 BS3 BS2 BS1
- * AGD = 1, R3 R2 R1 R0 = 0 1 0 1 => byte 4 = 1**10101 = 0x95 */
- buf[2] = 0x95;
-
-/* Range(MHz) C1 * RE RTS BS4 BS3 BS2 BS1 Byte 5
- * 47 - 153 0 * 0 0 0 0 0 1 0x01
- * 153 - 430 0 * 0 0 0 0 1 0 0x02
- * 430 - 822 0 * 0 0 1 0 0 0 0x08
- * 822 - 862 1 * 0 0 1 0 0 0 0x88 */
-
- if (fep->frequency <= 153000000) buf[3] = 0x01;
- else if (fep->frequency <= 430000000) buf[3] = 0x02;
- else if (fep->frequency <= 822000000) buf[3] = 0x08;
- else buf[3] = 0x88;
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 0);
- deb_tuner("tuner buffer for %d Hz: %x %x %x %x\n", fep->frequency,
- buf[0], buf[1], buf[2], buf[3]);
- ret = fc->i2c_request(&fc->fc_i2c_adap[2],
- FC_WRITE, 0x61, buf[0], &buf[1], 3);
- deb_tuner("tuner write returned: %d\n",ret);
- return ret;
-}
-
+#if FE_SUPPORTED(STV0297) && FE_SUPPORTED(PLL)
static u8 alps_tdee4_stv0297_inittab[] = {
0x80, 0x01,
0x80, 0x00,
@@ -690,47 +595,43 @@ static int cablestar2_attach(struct flexcop_device *fc,
{
fc->fc_i2c_adap[0].no_base_addr = 1;
fc->fe = dvb_attach(stv0297_attach, &alps_tdee4_stv0297_config, i2c);
- if (!fc->fe) {
- /* Reset for next frontend to try */
- fc->fc_i2c_adap[0].no_base_addr = 0;
- return 0;
- }
- fc->fe->ops.tuner_ops.set_params = alps_tdee4_stv0297_tuner_set_params;
+ if (!fc->fe)
+ goto fail;
+
+ /* This tuner doesn't use the stv0297's I2C gate, but instead the
+ * tuner is connected to a different flexcop I2C adapter. */
+ if (fc->fe->ops.i2c_gate_ctrl)
+ fc->fe->ops.i2c_gate_ctrl(fc->fe, 0);
+ fc->fe->ops.i2c_gate_ctrl = NULL;
+
+ if (!dvb_attach(dvb_pll_attach, fc->fe, 0x61,
+ fc->fc_i2c_adap[2].i2c_adap, DVB_PLL_TDEE4))
+ goto fail;
+
return 1;
+
+fail:
+ /* Reset for next frontend to try */
+ fc->fc_i2c_adap[0].no_base_addr = 0;
+ return 0;
}
+#else
+#define cablestar2_attach NULL
#endif
static struct {
flexcop_device_type_t type;
int (*attach)(struct flexcop_device *, struct i2c_adapter *);
} flexcop_frontends[] = {
-#if defined(CONFIG_DVB_S5H1420_MODULE)
{ FC_SKY_REV27, skystar2_rev27_attach },
-#endif
-#if defined(CONFIG_DVB_CX24123_MODULE)
{ FC_SKY_REV28, skystar2_rev28_attach },
-#endif
-#if defined(CONFIG_DVB_STV0299_MODULE)
{ FC_SKY_REV26, skystar2_rev26_attach },
-#endif
-#if defined(CONFIG_DVB_MT352_MODULE)
{ FC_AIR_DVBT, airstar_dvbt_attach },
-#endif
-#if defined(CONFIG_DVB_NXT200X_MODULE)
{ FC_AIR_ATSC2, airstar_atsc2_attach },
-#endif
-#if defined(CONFIG_DVB_LGDT330X_MODULE)
{ FC_AIR_ATSC3, airstar_atsc3_attach },
-#endif
-#if defined(CONFIG_DVB_BCM3510_MODULE)
{ FC_AIR_ATSC1, airstar_atsc1_attach },
-#endif
-#if defined(CONFIG_DVB_STV0297_MODULE)
{ FC_CABLE, cablestar2_attach },
-#endif
-#if defined(CONFIG_DVB_MT312_MODULE)
{ FC_SKY_REV23, skystar2_rev23_attach },
-#endif
};
/* try to figure out the frontend */
@@ -738,6 +639,8 @@ int flexcop_frontend_init(struct flexcop_device *fc)
{
int i;
for (i = 0; i < ARRAY_SIZE(flexcop_frontends); i++) {
+ if (!flexcop_frontends[i].attach)
+ continue;
/* type needs to be set before, because of some workarounds
* done based on the probed card type */
fc->dev_type = flexcop_frontends[i].type;
diff --git a/linux/drivers/media/dvb/dvb-core/dmxdev.c b/linux/drivers/media/dvb/dvb-core/dmxdev.c
index 6d6121eb5..3750ff48c 100644
--- a/linux/drivers/media/dvb/dvb-core/dmxdev.c
+++ b/linux/drivers/media/dvb/dvb-core/dmxdev.c
@@ -430,6 +430,8 @@ static int dvb_dmxdev_ts_callback(const u8 *buffer1, size_t buffer1_len,
/* stop feed but only mark the specified filter as stopped (state set) */
static int dvb_dmxdev_feed_stop(struct dmxdev_filter *dmxdevfilter)
{
+ struct dmxdev_feed *feed;
+
dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_SET);
switch (dmxdevfilter->type) {
@@ -438,7 +440,8 @@ static int dvb_dmxdev_feed_stop(struct dmxdev_filter *dmxdevfilter)
dmxdevfilter->feed.sec->stop_filtering(dmxdevfilter->feed.sec);
break;
case DMXDEV_TYPE_PES:
- dmxdevfilter->feed.ts->stop_filtering(dmxdevfilter->feed.ts);
+ list_for_each_entry(feed, &dmxdevfilter->feed.ts, next)
+ feed->ts->stop_filtering(feed->ts);
break;
default:
return -EINVAL;
@@ -449,13 +452,23 @@ static int dvb_dmxdev_feed_stop(struct dmxdev_filter *dmxdevfilter)
/* start feed associated with the specified filter */
static int dvb_dmxdev_feed_start(struct dmxdev_filter *filter)
{
+ struct dmxdev_feed *feed;
+ int ret;
+
dvb_dmxdev_filter_state_set(filter, DMXDEV_STATE_GO);
switch (filter->type) {
case DMXDEV_TYPE_SEC:
return filter->feed.sec->start_filtering(filter->feed.sec);
case DMXDEV_TYPE_PES:
- return filter->feed.ts->start_filtering(filter->feed.ts);
+ list_for_each_entry(feed, &filter->feed.ts, next) {
+ ret = feed->ts->start_filtering(feed->ts);
+ if (ret < 0) {
+ dvb_dmxdev_feed_stop(filter);
+ return ret;
+ }
+ }
+ break;
default:
return -EINVAL;
}
@@ -487,6 +500,9 @@ static int dvb_dmxdev_feed_restart(struct dmxdev_filter *filter)
static int dvb_dmxdev_filter_stop(struct dmxdev_filter *dmxdevfilter)
{
+ struct dmxdev_feed *feed;
+ struct dmx_demux *demux;
+
if (dmxdevfilter->state < DMXDEV_STATE_GO)
return 0;
@@ -503,13 +519,12 @@ static int dvb_dmxdev_filter_stop(struct dmxdev_filter *dmxdevfilter)
dmxdevfilter->feed.sec = NULL;
break;
case DMXDEV_TYPE_PES:
- if (!dmxdevfilter->feed.ts)
- break;
dvb_dmxdev_feed_stop(dmxdevfilter);
- dmxdevfilter->dev->demux->
- release_ts_feed(dmxdevfilter->dev->demux,
- dmxdevfilter->feed.ts);
- dmxdevfilter->feed.ts = NULL;
+ demux = dmxdevfilter->dev->demux;
+ list_for_each_entry(feed, &dmxdevfilter->feed.ts, next) {
+ demux->release_ts_feed(demux, feed->ts);
+ feed->ts = NULL;
+ }
break;
default:
if (dmxdevfilter->state == DMXDEV_STATE_ALLOCATED)
@@ -521,19 +536,88 @@ static int dvb_dmxdev_filter_stop(struct dmxdev_filter *dmxdevfilter)
return 0;
}
+static void dvb_dmxdev_delete_pids(struct dmxdev_filter *dmxdevfilter)
+{
+ struct dmxdev_feed *feed, *tmp;
+
+ /* delete all PIDs */
+ list_for_each_entry_safe(feed, tmp, &dmxdevfilter->feed.ts, next) {
+ list_del(&feed->next);
+ kfree(feed);
+ }
+
+ BUG_ON(!list_empty(&dmxdevfilter->feed.ts));
+}
+
static inline int dvb_dmxdev_filter_reset(struct dmxdev_filter *dmxdevfilter)
{
if (dmxdevfilter->state < DMXDEV_STATE_SET)
return 0;
+ if (dmxdevfilter->type == DMXDEV_TYPE_PES)
+ dvb_dmxdev_delete_pids(dmxdevfilter);
+
dmxdevfilter->type = DMXDEV_TYPE_NONE;
dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_ALLOCATED);
return 0;
}
+static int dvb_dmxdev_start_feed(struct dmxdev *dmxdev,
+ struct dmxdev_filter *filter,
+ struct dmxdev_feed *feed)
+{
+ struct timespec timeout = { 0 };
+ struct dmx_pes_filter_params *para = &filter->params.pes;
+ dmx_output_t otype;
+ int ret;
+ int ts_type;
+ enum dmx_ts_pes ts_pes;
+ struct dmx_ts_feed *tsfeed;
+
+ feed->ts = NULL;
+ otype = para->output;
+
+ ts_pes = (enum dmx_ts_pes)para->pes_type;
+
+ if (ts_pes < DMX_PES_OTHER)
+ ts_type = TS_DECODER;
+ else
+ ts_type = 0;
+
+ if (otype == DMX_OUT_TS_TAP)
+ ts_type |= TS_PACKET;
+ else if (otype == DMX_OUT_TSDEMUX_TAP)
+ ts_type |= TS_PACKET | TS_DEMUX;
+ else if (otype == DMX_OUT_TAP)
+ ts_type |= TS_PACKET | TS_DEMUX | TS_PAYLOAD_ONLY;
+
+ ret = dmxdev->demux->allocate_ts_feed(dmxdev->demux, &feed->ts,
+ dvb_dmxdev_ts_callback);
+ if (ret < 0)
+ return ret;
+
+ tsfeed = feed->ts;
+ tsfeed->priv = filter;
+
+ ret = tsfeed->set(tsfeed, feed->pid, ts_type, ts_pes, 32768, timeout);
+ if (ret < 0) {
+ dmxdev->demux->release_ts_feed(dmxdev->demux, tsfeed);
+ return ret;
+ }
+
+ ret = tsfeed->start_filtering(tsfeed);
+ if (ret < 0) {
+ dmxdev->demux->release_ts_feed(dmxdev->demux, tsfeed);
+ return ret;
+ }
+
+ return 0;
+}
+
static int dvb_dmxdev_filter_start(struct dmxdev_filter *filter)
{
struct dmxdev *dmxdev = filter->dev;
+ struct dmxdev_feed *feed;
void *mem;
int ret, i;
@@ -631,56 +715,14 @@ static int dvb_dmxdev_filter_start(struct dmxdev_filter *filter)
break;
}
case DMXDEV_TYPE_PES:
- {
- struct timespec timeout = { 0 };
- struct dmx_pes_filter_params *para = &filter->params.pes;
- dmx_output_t otype;
- int ts_type;
- enum dmx_ts_pes ts_pes;
- struct dmx_ts_feed **tsfeed = &filter->feed.ts;
-
- filter->feed.ts = NULL;
- otype = para->output;
-
- ts_pes = (enum dmx_ts_pes)para->pes_type;
-
- if (ts_pes < DMX_PES_OTHER)
- ts_type = TS_DECODER;
- else
- ts_type = 0;
-
- if (otype == DMX_OUT_TS_TAP)
- ts_type |= TS_PACKET;
- else if (otype == DMX_OUT_TSDEMUX_TAP)
- ts_type |= TS_PACKET | TS_DEMUX;
- else if (otype == DMX_OUT_TAP)
- ts_type |= TS_PACKET | TS_DEMUX | TS_PAYLOAD_ONLY;
-
- ret = dmxdev->demux->allocate_ts_feed(dmxdev->demux,
- tsfeed,
- dvb_dmxdev_ts_callback);
- if (ret < 0)
- return ret;
-
- (*tsfeed)->priv = filter;
-
- ret = (*tsfeed)->set(*tsfeed, para->pid, ts_type, ts_pes,
- 32768, timeout);
- if (ret < 0) {
- dmxdev->demux->release_ts_feed(dmxdev->demux,
- *tsfeed);
- return ret;
- }
-
- ret = filter->feed.ts->start_filtering(filter->feed.ts);
- if (ret < 0) {
- dmxdev->demux->release_ts_feed(dmxdev->demux,
- *tsfeed);
- return ret;
+ list_for_each_entry(feed, &filter->feed.ts, next) {
+ ret = dvb_dmxdev_start_feed(dmxdev, filter, feed);
+ if (ret < 0) {
+ dvb_dmxdev_filter_stop(filter);
+ return ret;
+ }
}
-
break;
- }
default:
return -EINVAL;
}
@@ -718,7 +760,7 @@ static int dvb_demux_open(struct inode *inode, struct file *file)
dvb_ringbuffer_init(&dmxdevfilter->buffer, NULL, 8192);
dmxdevfilter->type = DMXDEV_TYPE_NONE;
dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_ALLOCATED);
- dmxdevfilter->feed.ts = NULL;
+ INIT_LIST_HEAD(&dmxdevfilter->feed.ts);
init_timer(&dmxdevfilter->timer);
dvbdev->users++;
@@ -760,6 +802,55 @@ static inline void invert_mode(dmx_filter_t *filter)
filter->mode[i] ^= 0xff;
}
+static int dvb_dmxdev_add_pid(struct dmxdev *dmxdev,
+ struct dmxdev_filter *filter, u16 pid)
+{
+ struct dmxdev_feed *feed;
+
+ if ((filter->type != DMXDEV_TYPE_PES) ||
+ (filter->state < DMXDEV_STATE_SET))
+ return -EINVAL;
+
+ /* only TS packet filters may have multiple PIDs */
+ if ((filter->params.pes.output != DMX_OUT_TSDEMUX_TAP) &&
+ (!list_empty(&filter->feed.ts)))
+ return -EINVAL;
+
+ feed = kzalloc(sizeof(struct dmxdev_feed), GFP_KERNEL);
+ if (feed == NULL)
+ return -ENOMEM;
+
+ feed->pid = pid;
+ list_add(&feed->next, &filter->feed.ts);
+
+ if (filter->state >= DMXDEV_STATE_GO)
+ return dvb_dmxdev_start_feed(dmxdev, filter, feed);
+
+ return 0;
+}
+
+static int dvb_dmxdev_remove_pid(struct dmxdev *dmxdev,
+ struct dmxdev_filter *filter, u16 pid)
+{
+ struct dmxdev_feed *feed, *tmp;
+
+ if ((filter->type != DMXDEV_TYPE_PES) ||
+ (filter->state < DMXDEV_STATE_SET))
+ return -EINVAL;
+
+ list_for_each_entry_safe(feed, tmp, &filter->feed.ts, next) {
+ if ((feed->pid == pid) && (feed->ts != NULL)) {
+ feed->ts->stop_filtering(feed->ts);
+ filter->dev->demux->release_ts_feed(filter->dev->demux,
+ feed->ts);
+ list_del(&feed->next);
+ kfree(feed);
+ }
+ }
+
+ return 0;
+}
+
static int dvb_dmxdev_filter_set(struct dmxdev *dmxdev,
struct dmxdev_filter *dmxdevfilter,
struct dmx_sct_filter_params *params)
@@ -784,7 +875,10 @@ static int dvb_dmxdev_pes_filter_set(struct dmxdev *dmxdev,
struct dmxdev_filter *dmxdevfilter,
struct dmx_pes_filter_params *params)
{
+ int ret;
+
dvb_dmxdev_filter_stop(dmxdevfilter);
+ dvb_dmxdev_filter_reset(dmxdevfilter);
if (params->pes_type > DMX_PES_OTHER || params->pes_type < 0)
return -EINVAL;
@@ -795,6 +889,11 @@ static int dvb_dmxdev_pes_filter_set(struct dmxdev *dmxdev,
dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_SET);
+ ret = dvb_dmxdev_add_pid(dmxdev, dmxdevfilter,
+ dmxdevfilter->params.pes.pid);
+ if (ret < 0)
+ return ret;
+
if (params->flags & DMX_IMMEDIATE_START)
return dvb_dmxdev_filter_start(dmxdevfilter);
@@ -958,6 +1057,24 @@ static int dvb_demux_do_ioctl(struct inode *inode, struct file *file,
&((struct dmx_stc *)parg)->base);
break;
+ case DMX_ADD_PID:
+ if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+ ret = -ERESTARTSYS;
+ break;
+ }
+ ret = dvb_dmxdev_add_pid(dmxdev, dmxdevfilter, *(u16 *)parg);
+ mutex_unlock(&dmxdevfilter->mutex);
+ break;
+
+ case DMX_REMOVE_PID:
+ if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+ ret = -ERESTARTSYS;
+ break;
+ }
+ ret = dvb_dmxdev_remove_pid(dmxdev, dmxdevfilter, *(u16 *)parg);
+ mutex_unlock(&dmxdevfilter->mutex);
+ break;
+
default:
ret = -EINVAL;
break;
diff --git a/linux/drivers/media/dvb/dvb-core/dmxdev.h b/linux/drivers/media/dvb/dvb-core/dmxdev.h
index 7e8137d0b..be63783ad 100644
--- a/linux/drivers/media/dvb/dvb-core/dmxdev.h
+++ b/linux/drivers/media/dvb/dvb-core/dmxdev.h
@@ -54,13 +54,20 @@ enum dmxdev_state {
DMXDEV_STATE_TIMEDOUT
};
+struct dmxdev_feed {
+ u16 pid;
+ struct dmx_ts_feed *ts;
+ struct list_head next;
+};
+
struct dmxdev_filter {
union {
struct dmx_section_filter *sec;
} filter;
union {
- struct dmx_ts_feed *ts;
+ /* list of TS and PES feeds (struct dmxdev_feed) */
+ struct list_head ts;
struct dmx_section_feed *sec;
} feed;
diff --git a/linux/drivers/media/dvb/dvb-core/dvb_demux.c b/linux/drivers/media/dvb/dvb-core/dvb_demux.c
index cfe2768d2..eef6d3616 100644
--- a/linux/drivers/media/dvb/dvb-core/dvb_demux.c
+++ b/linux/drivers/media/dvb/dvb-core/dvb_demux.c
@@ -425,13 +425,9 @@ no_dvb_demux_tscheck:
if ((DVR_FEED(feed)) && (dvr_done++))
continue;
- if (feed->pid == pid) {
+ if (feed->pid == pid)
dvb_dmx_swfilter_packet_type(feed, buf);
- if (DVR_FEED(feed))
- continue;
- }
-
- if (feed->pid == 0x2000)
+ else if (feed->pid == 0x2000)
feed->cb.ts(buf, 188, NULL, 0, &feed->feed.ts, DMX_OK);
}
}
diff --git a/linux/drivers/media/dvb/frontends/dvb-pll.c b/linux/drivers/media/dvb/frontends/dvb-pll.c
index 8ef5d1eff..d8a7947e0 100644
--- a/linux/drivers/media/dvb/frontends/dvb-pll.c
+++ b/linux/drivers/media/dvb/frontends/dvb-pll.c
@@ -390,6 +390,77 @@ static struct dvb_pll_desc dvb_pll_samsung_dtos403ih102a = {
}
};
+/* Samsung TDTC9251DH0 DVB-T NIM, as used on AirStar 2 */
+static struct dvb_pll_desc dvb_pll_samsung_tdtc9251dh0 = {
+ .name = "Samsung TDTC9251DH0",
+ .min = 48000000,
+ .max = 863000000,
+ .iffreq = 36166667,
+ .count = 3,
+ .entries = {
+ { 157500000, 166667, 0xcc, 0x09 },
+ { 443000000, 166667, 0xcc, 0x0a },
+ { 863000000, 166667, 0xcc, 0x08 },
+ }
+};
+
+/* Samsung TBDU18132 DVB-S NIM with TSA5059 PLL, used in SkyStar2 DVB-S 2.3 */
+static struct dvb_pll_desc dvb_pll_samsung_tbdu18132 = {
+ .name = "Samsung TBDU18132",
+ .min = 950000,
+ .max = 2150000, /* guesses */
+ .iffreq = 0,
+ .count = 2,
+ .entries = {
+ { 1550000, 125, 0x84, 0x82 },
+ { 4095937, 125, 0x84, 0x80 },
+ }
+ /* TSA5059 PLL has a 17 bit divisor rather than the 15 bits supported
+ * by this driver. The two extra bits are 0x60 in the third byte. 15
+ * bits is enough for over 4 GHz, which is enough to cover the range
+ * of this tuner. We could use the additional divisor bits by adding
+ * more entries, e.g.
+ { 0x0ffff * 125 + 125/2, 125, 0x84 | 0x20, },
+ { 0x17fff * 125 + 125/2, 125, 0x84 | 0x40, },
+ { 0x1ffff * 125 + 125/2, 125, 0x84 | 0x60, }, */
+};
+
+/* Samsung TBMU24112 DVB-S NIM with SL1935 zero-IF tuner */
+static struct dvb_pll_desc dvb_pll_samsung_tbmu24112 = {
+ .name = "Samsung TBMU24112",
+ .min = 950000,
+ .max = 2150000, /* guesses */
+ .iffreq = 0,
+ .count = 2,
+ .entries = {
+ { 1500000, 125, 0x84, 0x18 },
+ { 9999999, 125, 0x84, 0x08 },
+ }
+};
+
+/* Alps TDEE4 DVB-C NIM, used on Cablestar 2 */
+/* byte 4 : 1 * * AGD R3 R2 R1 R0
+ * byte 5 : C1 * RE RTS BS4 BS3 BS2 BS1
+ * AGD = 1, R3 R2 R1 R0 = 0 1 0 1 => byte 4 = 1**10101 = 0x95
+ * Range(MHz) C1 * RE RTS BS4 BS3 BS2 BS1 Byte 5
+ * 47 - 153 0 * 0 0 0 0 0 1 0x01
+ * 153 - 430 0 * 0 0 0 0 1 0 0x02
+ * 430 - 822 0 * 0 0 1 0 0 0 0x08
+ * 822 - 862 1 * 0 0 1 0 0 0 0x88 */
+static struct dvb_pll_desc dvb_pll_alps_tdee4 = {
+ .name = "ALPS TDEE4",
+ .min = 47000000,
+ .max = 862000000,
+ .iffreq = 36125000,
+ .count = 4,
+ .entries = {
+ { 153000000, 62500, 0x95, 0x01 },
+ { 430000000, 62500, 0x95, 0x02 },
+ { 822000000, 62500, 0x95, 0x08 },
+ { 999999999, 62500, 0x95, 0x88 },
+ }
+};
+
/* ----------------------------------------------------------- */
static struct dvb_pll_desc *pll_list[] = {
@@ -403,11 +474,15 @@ static struct dvb_pll_desc *pll_list[] = {
[DVB_PLL_TUA6034] = &dvb_pll_tua6034,
[DVB_PLL_TDA665X] = &dvb_pll_tda665x,
[DVB_PLL_TDED4] = &dvb_pll_tded4,
+ [DVB_PLL_TDEE4] = &dvb_pll_alps_tdee4,
[DVB_PLL_TDHU2] = &dvb_pll_tdhu2,
[DVB_PLL_SAMSUNG_TBMV] = &dvb_pll_samsung_tbmv,
[DVB_PLL_PHILIPS_SD1878_TDA8261] = &dvb_pll_philips_sd1878_tda8261,
[DVB_PLL_OPERA1] = &dvb_pll_opera1,
[DVB_PLL_SAMSUNG_DTOS403IH102A] = &dvb_pll_samsung_dtos403ih102a,
+ [DVB_PLL_SAMSUNG_TDTC9251DH0] = &dvb_pll_samsung_tdtc9251dh0,
+ [DVB_PLL_SAMSUNG_TBDU18132] = &dvb_pll_samsung_tbdu18132,
+ [DVB_PLL_SAMSUNG_TBMU24112] = &dvb_pll_samsung_tbmu24112,
};
/* ----------------------------------------------------------- */
diff --git a/linux/drivers/media/dvb/frontends/dvb-pll.h b/linux/drivers/media/dvb/frontends/dvb-pll.h
index 05239f579..086964344 100644
--- a/linux/drivers/media/dvb/frontends/dvb-pll.h
+++ b/linux/drivers/media/dvb/frontends/dvb-pll.h
@@ -23,6 +23,10 @@
#define DVB_PLL_PHILIPS_SD1878_TDA8261 12
#define DVB_PLL_OPERA1 13
#define DVB_PLL_SAMSUNG_DTOS403IH102A 14
+#define DVB_PLL_SAMSUNG_TDTC9251DH0 15
+#define DVB_PLL_SAMSUNG_TBDU18132 16
+#define DVB_PLL_SAMSUNG_TBMU24112 17
+#define DVB_PLL_TDEE4 18
/**
* Attach a dvb-pll to the supplied frontend structure.
diff --git a/linux/drivers/media/dvb/frontends/mt312.c b/linux/drivers/media/dvb/frontends/mt312.c
index 071328d7b..f348f8bea 100644
--- a/linux/drivers/media/dvb/frontends/mt312.c
+++ b/linux/drivers/media/dvb/frontends/mt312.c
@@ -35,6 +35,7 @@
#include "dvb_frontend.h"
#include "mt312_priv.h"
#include "mt312.h"
+#include "compat.h"
struct mt312_state {
diff --git a/linux/drivers/media/video/bt8xx/bttv-cards.c b/linux/drivers/media/video/bt8xx/bttv-cards.c
index 38b238f0f..fdca5f67c 100644
--- a/linux/drivers/media/video/bt8xx/bttv-cards.c
+++ b/linux/drivers/media/video/bt8xx/bttv-cards.c
@@ -3362,8 +3362,6 @@ void __devinit bttv_init_card1(struct bttv *btv)
/* initialization part two -- after registering i2c bus */
void __devinit bttv_init_card2(struct bttv *btv)
{
- int addr=ADDR_UNSET;
-
btv->tuner_type = UNSET;
if (BTTV_BOARD_UNKNOWN == btv->c.type) {
@@ -3508,9 +3506,6 @@ void __devinit bttv_init_card2(struct bttv *btv)
btv->pll.pll_current = -1;
/* tuner configuration (from card list / autodetect / insmod option) */
- if (ADDR_UNSET != bttv_tvcards[btv->c.type].tuner_addr)
- addr = bttv_tvcards[btv->c.type].tuner_addr;
-
if (UNSET != bttv_tvcards[btv->c.type].tuner_type)
if (UNSET == btv->tuner_type)
btv->tuner_type = bttv_tvcards[btv->c.type].tuner_type;
@@ -3534,40 +3529,6 @@ void __devinit bttv_init_card2(struct bttv *btv)
if (UNSET == btv->tuner_type)
btv->tuner_type = TUNER_ABSENT;
- if (btv->tuner_type != TUNER_ABSENT) {
- struct tuner_setup tun_setup;
-
- /* Load tuner module before issuing tuner config call! */
- if (bttv_tvcards[btv->c.type].has_radio)
- v4l2_i2c_new_probed_subdev(&btv->c.v4l2_dev,
- &btv->c.i2c_adap, "tuner", "tuner",
- v4l2_i2c_tuner_addrs(ADDRS_RADIO));
- v4l2_i2c_new_probed_subdev(&btv->c.v4l2_dev,
- &btv->c.i2c_adap, "tuner", "tuner",
- v4l2_i2c_tuner_addrs(ADDRS_DEMOD));
- v4l2_i2c_new_probed_subdev(&btv->c.v4l2_dev,
- &btv->c.i2c_adap, "tuner", "tuner",
- v4l2_i2c_tuner_addrs(ADDRS_TV_WITH_DEMOD));
-
- tun_setup.mode_mask = T_ANALOG_TV | T_DIGITAL_TV;
- tun_setup.type = btv->tuner_type;
- tun_setup.addr = addr;
-
- if (bttv_tvcards[btv->c.type].has_radio)
- tun_setup.mode_mask |= T_RADIO;
-
- bttv_call_all(btv, tuner, s_type_addr, &tun_setup);
- }
-
- if (btv->tda9887_conf) {
- struct v4l2_priv_tun_config tda9887_cfg;
-
- tda9887_cfg.tuner = TUNER_TDA9887;
- tda9887_cfg.priv = &btv->tda9887_conf;
-
- bttv_call_all(btv, tuner, s_config, &tda9887_cfg);
- }
-
btv->dig = bttv_tvcards[btv->c.type].has_dig_in ?
bttv_tvcards[btv->c.type].video_inputs - 1 : UNSET;
btv->svhs = bttv_tvcards[btv->c.type].svhs == NO_SVHS ?
@@ -3578,15 +3539,15 @@ void __devinit bttv_init_card2(struct bttv *btv)
btv->has_remote = remote[btv->c.nr];
if (bttv_tvcards[btv->c.type].has_radio)
- btv->has_radio=1;
+ btv->has_radio = 1;
if (bttv_tvcards[btv->c.type].has_remote)
- btv->has_remote=1;
+ btv->has_remote = 1;
if (!bttv_tvcards[btv->c.type].no_gpioirq)
- btv->gpioirq=1;
+ btv->gpioirq = 1;
if (bttv_tvcards[btv->c.type].volume_gpio)
- btv->volume_gpio=bttv_tvcards[btv->c.type].volume_gpio;
+ btv->volume_gpio = bttv_tvcards[btv->c.type].volume_gpio;
if (bttv_tvcards[btv->c.type].audio_mode_gpio)
- btv->audio_mode_gpio=bttv_tvcards[btv->c.type].audio_mode_gpio;
+ btv->audio_mode_gpio = bttv_tvcards[btv->c.type].audio_mode_gpio;
if (btv->tuner_type == TUNER_ABSENT)
return; /* no tuner or related drivers to load */
@@ -3704,6 +3665,49 @@ no_audio:
}
+/* initialize the tuner */
+void __devinit bttv_init_tuner(struct bttv *btv)
+{
+ int addr = ADDR_UNSET;
+
+ if (ADDR_UNSET != bttv_tvcards[btv->c.type].tuner_addr)
+ addr = bttv_tvcards[btv->c.type].tuner_addr;
+
+ if (btv->tuner_type != TUNER_ABSENT) {
+ struct tuner_setup tun_setup;
+
+ /* Load tuner module before issuing tuner config call! */
+ if (bttv_tvcards[btv->c.type].has_radio)
+ v4l2_i2c_new_probed_subdev(&btv->c.v4l2_dev,
+ &btv->c.i2c_adap, "tuner", "tuner",
+ v4l2_i2c_tuner_addrs(ADDRS_RADIO));
+ v4l2_i2c_new_probed_subdev(&btv->c.v4l2_dev,
+ &btv->c.i2c_adap, "tuner", "tuner",
+ v4l2_i2c_tuner_addrs(ADDRS_DEMOD));
+ v4l2_i2c_new_probed_subdev(&btv->c.v4l2_dev,
+ &btv->c.i2c_adap, "tuner", "tuner",
+ v4l2_i2c_tuner_addrs(ADDRS_TV_WITH_DEMOD));
+
+ tun_setup.mode_mask = T_ANALOG_TV | T_DIGITAL_TV;
+ tun_setup.type = btv->tuner_type;
+ tun_setup.addr = addr;
+
+ if (bttv_tvcards[btv->c.type].has_radio)
+ tun_setup.mode_mask |= T_RADIO;
+
+ bttv_call_all(btv, tuner, s_type_addr, &tun_setup);
+ }
+
+ if (btv->tda9887_conf) {
+ struct v4l2_priv_tun_config tda9887_cfg;
+
+ tda9887_cfg.tuner = TUNER_TDA9887;
+ tda9887_cfg.priv = &btv->tda9887_conf;
+
+ bttv_call_all(btv, tuner, s_config, &tda9887_cfg);
+ }
+}
+
/* ----------------------------------------------------------------------- */
static void modtec_eeprom(struct bttv *btv)
diff --git a/linux/drivers/media/video/bt8xx/bttv-driver.c b/linux/drivers/media/video/bt8xx/bttv-driver.c
index 9c25efe06..18085ba61 100644
--- a/linux/drivers/media/video/bt8xx/bttv-driver.c
+++ b/linux/drivers/media/video/bt8xx/bttv-driver.c
@@ -4457,6 +4457,7 @@ static int __devinit bttv_probe(struct pci_dev *dev,
/* some card-specific stuff (needs working i2c) */
bttv_init_card2(btv);
+ bttv_init_tuner(btv);
init_irqreg(btv);
/* register video4linux + input */
diff --git a/linux/drivers/media/video/bt8xx/bttv.h b/linux/drivers/media/video/bt8xx/bttv.h
index 6b7b62bd4..a0cb26b4a 100644
--- a/linux/drivers/media/video/bt8xx/bttv.h
+++ b/linux/drivers/media/video/bt8xx/bttv.h
@@ -284,6 +284,7 @@ extern struct tvcard bttv_tvcards[];
extern void bttv_idcard(struct bttv *btv);
extern void bttv_init_card1(struct bttv *btv);
extern void bttv_init_card2(struct bttv *btv);
+extern void bttv_init_tuner(struct bttv *btv);
/* card-specific funtions */
extern void tea5757_set_freq(struct bttv *btv, unsigned short freq);
diff --git a/linux/drivers/media/video/cx23885/cx23885-417.c b/linux/drivers/media/video/cx23885/cx23885-417.c
index 9974d9a78..fe0b3c41d 100644
--- a/linux/drivers/media/video/cx23885/cx23885-417.c
+++ b/linux/drivers/media/video/cx23885/cx23885-417.c
@@ -57,7 +57,8 @@ MODULE_PARM_DESC(v4l_debug, "enable V4L debug messages");
#define dprintk(level, fmt, arg...)\
do { if (v4l_debug >= level) \
- printk(KERN_DEBUG "%s: " fmt, dev->name , ## arg);\
+ printk(KERN_DEBUG "%s: " fmt, \
+ (dev) ? dev->name : "cx23885[?]", ## arg); \
} while (0)
static struct cx23885_tvnorm cx23885_tvnorms[] = {
@@ -628,6 +629,39 @@ int mc417_memory_read(struct cx23885_dev *dev, u32 address, u32 *value)
return retval;
}
+void mc417_gpio_set(struct cx23885_dev *dev, u32 mask)
+{
+ u32 val;
+
+ /* Set the gpio value */
+ mc417_register_read(dev, 0x900C, &val);
+ val |= (mask & 0x000ffff);
+ mc417_register_write(dev, 0x900C, val);
+}
+
+void mc417_gpio_clear(struct cx23885_dev *dev, u32 mask)
+{
+ u32 val;
+
+ /* Clear the gpio value */
+ mc417_register_read(dev, 0x900C, &val);
+ val &= ~(mask & 0x0000ffff);
+ mc417_register_write(dev, 0x900C, val);
+}
+
+void mc417_gpio_enable(struct cx23885_dev *dev, u32 mask, int asoutput)
+{
+ u32 val;
+
+ /* Enable GPIO direction bits */
+ mc417_register_read(dev, 0x9020, &val);
+ if (asoutput)
+ val |= (mask & 0x0000ffff);
+ else
+ val &= ~(mask & 0x0000ffff);
+
+ mc417_register_write(dev, 0x9020, val);
+}
/* ------------------------------------------------------------------ */
/* MPEG encoder API */
@@ -953,25 +987,8 @@ static int cx23885_load_firmware(struct cx23885_dev *dev)
retval |= mc417_register_write(dev, IVTV_REG_HW_BLOCKS,
IVTV_CMD_HW_BLOCKS_RST);
- /* Restore GPIO settings, make sure EIO14 is enabled as an output. */
- dprintk(2, "%s: GPIO output EIO 0-15 was = 0x%x\n",
- __func__, gpio_output);
- /* Power-up seems to have GPIOs AFU. This was causing digital side
- * to fail at power-up. Seems GPIOs should be set to 0x10ff0411 at
- * power-up.
- * gpio_output |= (1<<14);
- */
- /* Note: GPIO14 is specific to the HVR1800 here */
- gpio_output = 0x10ff0411 | (1<<14);
- retval |= mc417_register_write(dev, 0x9020, gpio_output | (1<<14));
- dprintk(2, "%s: GPIO output EIO 0-15 now = 0x%x\n",
- __func__, gpio_output);
-
- dprintk(1, "%s: GPIO value EIO 0-15 was = 0x%x\n",
- __func__, value);
- value |= (1<<14);
- dprintk(1, "%s: GPIO value EIO 0-15 now = 0x%x\n",
- __func__, value);
+ /* F/W power up disturbs the GPIOs, restore state */
+ retval |= mc417_register_write(dev, 0x9020, gpio_output);
retval |= mc417_register_write(dev, 0x900C, value);
retval |= mc417_register_read(dev, IVTV_REG_VPU, &value);
@@ -1683,6 +1700,7 @@ static struct v4l2_file_operations mpeg_fops = {
.read = mpeg_read,
.poll = mpeg_poll,
.mmap = mpeg_mmap,
+ .ioctl = video_ioctl2,
};
static const struct v4l2_ioctl_ops mpeg_ioctl_ops = {
@@ -1790,9 +1808,6 @@ int cx23885_417_register(struct cx23885_dev *dev)
return err;
}
- /* Initialize MC417 registers */
- cx23885_mc417_init(dev);
-
printk(KERN_INFO "%s: registered device video%d [mpeg]\n",
dev->name, dev->v4l_device->num);
diff --git a/linux/drivers/media/video/cx23885/cx23885-cards.c b/linux/drivers/media/video/cx23885/cx23885-cards.c
index bf426f041..2ffc83e60 100644
--- a/linux/drivers/media/video/cx23885/cx23885-cards.c
+++ b/linux/drivers/media/video/cx23885/cx23885-cards.c
@@ -206,6 +206,11 @@ struct cx23885_board cx23885_boards[] = {
.name = "Magic-Pro ProHDTV Extreme 2",
.portb = CX23885_MPEG_DVB,
},
+ [CX23885_BOARD_HAUPPAUGE_HVR1850] = {
+ .name = "Hauppauge WinTV-HVR1850",
+ .portb = CX23885_MPEG_ENCODER,
+ .portc = CX23885_MPEG_DVB,
+ },
};
const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards);
@@ -333,6 +338,10 @@ struct cx23885_subid cx23885_subids[] = {
.subvendor = 0x14f1,
.subdevice = 0x8657,
.card = CX23885_BOARD_MAGICPRO_PROHDTVE2,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x8541,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1850,
},
};
const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids);
@@ -492,8 +501,13 @@ static void hauppauge_eeprom(struct cx23885_dev *dev, u8 *eeprom_data)
/* WinTV-HVR1700 (PCIe, OEM, No IR, full height)
* DVB-T and MPEG2 HW Encoder */
break;
+ case 85021:
+ /* WinTV-HVR1850 (PCIe, OEM, RCA in, IR, FM,
+ Dual channel ATSC and MPEG2 HW Encoder */
+ break;
default:
- printk(KERN_WARNING "%s: warning: unknown hauppauge model #%d\n",
+ printk(KERN_WARNING "%s: warning: "
+ "unknown hauppauge model #%d\n",
dev->name, tv.model);
break;
}
@@ -583,13 +597,23 @@ void cx23885_gpio_setup(struct cx23885_dev *dev)
/* CX23417 GPIO's */
/* EIO15 Zilog Reset */
/* EIO14 S5H1409/CX24227 Reset */
+ mc417_gpio_enable(dev, GPIO_15 | GPIO_14, 1);
+
+ /* Put the demod into reset and protect the eeprom */
+ mc417_gpio_clear(dev, GPIO_15 | GPIO_14);
+ mdelay(100);
+
+ /* Bring the demod and blaster out of reset */
+ mc417_gpio_set(dev, GPIO_15 | GPIO_14);
+ mdelay(100);
/* Force the TDA8295A into reset and back */
- cx_set(GP0_IO, 0x00040004);
+ cx23885_gpio_enable(dev, GPIO_2, 1);
+ cx23885_gpio_set(dev, GPIO_2);
mdelay(20);
- cx_clear(GP0_IO, 0x00000004);
+ cx23885_gpio_clear(dev, GPIO_2);
mdelay(20);
- cx_set(GP0_IO, 0x00040004);
+ cx23885_gpio_set(dev, GPIO_2);
mdelay(20);
break;
case CX23885_BOARD_HAUPPAUGE_HVR1200:
@@ -733,6 +757,36 @@ void cx23885_gpio_setup(struct cx23885_dev *dev)
cx_set(GP0_IO, 0x00060006);
mdelay(100);
break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1850:
+ /* GPIO-0 656_CLK */
+ /* GPIO-1 656_D0 */
+ /* GPIO-2 Wake# */
+ /* GPIO-3-10 cx23417 data0-7 */
+ /* GPIO-11-14 cx23417 addr0-3 */
+ /* GPIO-15-18 cx23417 READY, CS, RD, WR */
+ /* GPIO-19 IR_RX */
+ /* GPIO-20 C_IR_TX */
+ /* GPIO-21 I2S DAT */
+ /* GPIO-22 I2S WCLK */
+ /* GPIO-23 I2S BCLK */
+ /* ALT GPIO: EXP GPIO LATCH */
+
+ /* CX23417 GPIO's */
+ /* GPIO-14 S5H1411/CX24228 Reset */
+ /* GPIO-13 EEPROM write protect */
+ mc417_gpio_enable(dev, GPIO_14 | GPIO_13, 1);
+
+ /* Put the demod into reset and protect the eeprom */
+ mc417_gpio_clear(dev, GPIO_14 | GPIO_13);
+ mdelay(100);
+
+ /* Bring the demod out of reset */
+ mc417_gpio_set(dev, GPIO_14);
+ mdelay(100);
+
+ /* CX24228 GPIO */
+ /* Connected to IF / Mux */
+ break;
}
}
@@ -749,6 +803,7 @@ int cx23885_ir_init(struct cx23885_dev *dev)
case CX23885_BOARD_HAUPPAUGE_HVR1275:
case CX23885_BOARD_HAUPPAUGE_HVR1255:
case CX23885_BOARD_HAUPPAUGE_HVR1210:
+ case CX23885_BOARD_HAUPPAUGE_HVR1850:
/* FIXME: Implement me */
break;
case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP:
@@ -788,6 +843,7 @@ void cx23885_card_setup(struct cx23885_dev *dev)
case CX23885_BOARD_HAUPPAUGE_HVR1275:
case CX23885_BOARD_HAUPPAUGE_HVR1255:
case CX23885_BOARD_HAUPPAUGE_HVR1210:
+ case CX23885_BOARD_HAUPPAUGE_HVR1850:
if (dev->i2c_bus[0].i2c_rc == 0)
hauppauge_eeprom(dev, eeprom+0xc0);
break;
@@ -855,6 +911,7 @@ void cx23885_card_setup(struct cx23885_dev *dev)
case CX23885_BOARD_HAUPPAUGE_HVR1275:
case CX23885_BOARD_HAUPPAUGE_HVR1255:
case CX23885_BOARD_HAUPPAUGE_HVR1210:
+ case CX23885_BOARD_HAUPPAUGE_HVR1850:
default:
ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */
ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */
diff --git a/linux/drivers/media/video/cx23885/cx23885-core.c b/linux/drivers/media/video/cx23885/cx23885-core.c
index 6988361c9..4854d54f1 100644
--- a/linux/drivers/media/video/cx23885/cx23885-core.c
+++ b/linux/drivers/media/video/cx23885/cx23885-core.c
@@ -714,12 +714,26 @@ static void cx23885_dev_checkrevision(struct cx23885_dev *dev)
dev->hwrevision = 0xa1;
break;
case 0x02:
- /* CX23885-13Z */
+ /* CX23885-13Z/14Z */
dev->hwrevision = 0xb0;
break;
case 0x03:
- /* CX23888-22Z */
- dev->hwrevision = 0xc0;
+ if (dev->pci->device == 0x8880) {
+ /* CX23888-21Z/22Z */
+ dev->hwrevision = 0xc0;
+ } else {
+ /* CX23885-14Z */
+ dev->hwrevision = 0xa4;
+ }
+ break;
+ case 0x04:
+ if (dev->pci->device == 0x8880) {
+ /* CX23888-31Z */
+ dev->hwrevision = 0xd0;
+ } else {
+ /* CX23885-15Z, CX23888-31Z */
+ dev->hwrevision = 0xa5;
+ }
break;
case 0x0e:
/* CX23887-15Z */
@@ -757,6 +771,7 @@ static int cx23885_dev_setup(struct cx23885_dev *dev)
/* Configure the internal memory */
if (dev->pci->device == 0x8880) {
+ /* Could be 887 or 888, assume a default */
dev->bridge = CX23885_BRIDGE_887;
/* Apply a sensible clock frequency for the PCIe bridge */
dev->clk_freq = 25000000;
@@ -869,6 +884,14 @@ static int cx23885_dev_setup(struct cx23885_dev *dev)
dprintk(1, "%s() radio_type = 0x%x radio_addr = 0x%x\n",
__func__, dev->radio_type, dev->radio_addr);
+ /* The cx23417 encoder has GPIO's that need to be initialised
+ * before DVB, so that demodulators and tuners are out of
+ * reset before DVB uses them.
+ */
+ if ((cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER) ||
+ (cx23885_boards[dev->board].portc == CX23885_MPEG_ENCODER))
+ cx23885_mc417_init(dev);
+
/* init hardware */
cx23885_reset(dev);
@@ -1251,6 +1274,7 @@ static int cx23885_start_dma(struct cx23885_tsport *port,
switch (dev->bridge) {
case CX23885_BRIDGE_885:
case CX23885_BRIDGE_887:
+ case CX23885_BRIDGE_888:
/* enable irqs */
dprintk(1, "%s() enabling TS int's and DMA\n", __func__);
cx_set(port->reg_ts_int_msk, port->ts_int_msk_val);
diff --git a/linux/drivers/media/video/cx23885/cx23885-dvb.c b/linux/drivers/media/video/cx23885/cx23885-dvb.c
index 7f8692f97..9c690a9bd 100644
--- a/linux/drivers/media/video/cx23885/cx23885-dvb.c
+++ b/linux/drivers/media/video/cx23885/cx23885-dvb.c
@@ -867,6 +867,17 @@ static int dvb_register(struct cx23885_tsport *port)
&magicpro_prohdtve2_xc5000_config);
}
break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1850:
+ i2c_bus = &dev->i2c_bus[0];
+ fe0->dvb.frontend = dvb_attach(s5h1411_attach,
+ &hcw_s5h1411_config,
+ &i2c_bus->i2c_adap);
+ if (fe0->dvb.frontend != NULL)
+ dvb_attach(tda18271_attach, fe0->dvb.frontend,
+ 0x60, &dev->i2c_bus[0].i2c_adap,
+ &hauppauge_tda18271_config);
+ break;
+
default:
printk(KERN_INFO "%s: The frontend of your DVB/ATSC card "
" isn't supported yet\n",
diff --git a/linux/drivers/media/video/cx23885/cx23885.h b/linux/drivers/media/video/cx23885/cx23885.h
index 2a384e6aa..6ddabc263 100644
--- a/linux/drivers/media/video/cx23885/cx23885.h
+++ b/linux/drivers/media/video/cx23885/cx23885.h
@@ -78,6 +78,7 @@
#define CX23885_BOARD_HAUPPAUGE_HVR1210 21
#define CX23885_BOARD_MYGICA_X8506 22
#define CX23885_BOARD_MAGICPRO_PROHDTVE2 23
+#define CX23885_BOARD_HAUPPAUGE_HVR1850 24
#define GPIO_0 0x00000001
#define GPIO_1 0x00000002
@@ -89,6 +90,12 @@
#define GPIO_7 0x00000080
#define GPIO_8 0x00000100
#define GPIO_9 0x00000200
+#define GPIO_10 0x00000400
+#define GPIO_11 0x00000800
+#define GPIO_12 0x00001000
+#define GPIO_13 0x00002000
+#define GPIO_14 0x00004000
+#define GPIO_15 0x00008000
/* Currently unsupported by the driver: PAL/H, NTSC/Kr, SECAM B/G/H/LC */
#define CX23885_NORMS (\
@@ -333,6 +340,7 @@ struct cx23885_dev {
CX23885_BRIDGE_UNDEFINED = 0,
CX23885_BRIDGE_885 = 885,
CX23885_BRIDGE_887 = 887,
+ CX23885_BRIDGE_888 = 888,
} bridge;
/* Analog video */
@@ -506,6 +514,9 @@ extern void cx23885_417_check_encoder(struct cx23885_dev *dev);
extern void cx23885_mc417_init(struct cx23885_dev *dev);
extern int mc417_memory_read(struct cx23885_dev *dev, u32 address, u32 *value);
extern int mc417_memory_write(struct cx23885_dev *dev, u32 address, u32 value);
+extern void mc417_gpio_set(struct cx23885_dev *dev, u32 mask);
+extern void mc417_gpio_clear(struct cx23885_dev *dev, u32 mask);
+extern void mc417_gpio_enable(struct cx23885_dev *dev, u32 mask, int asoutput);
/* ----------------------------------------------------------- */
diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c
index cef676bf8..1237e325d 100644
--- a/linux/drivers/media/video/em28xx/em28xx-video.c
+++ b/linux/drivers/media/video/em28xx/em28xx-video.c
@@ -142,7 +142,7 @@ static struct em28xx_fmt format[] = {
/* supported controls */
/* Common to all boards */
-static struct v4l2_queryctrl em28xx_qctrl[] = {
+static struct v4l2_queryctrl ac97_qctrl[] = {
{
.id = V4L2_CID_AUDIO_VOLUME,
.type = V4L2_CTRL_TYPE_INTEGER,
@@ -151,7 +151,7 @@ static struct v4l2_queryctrl em28xx_qctrl[] = {
.maximum = 0x1f,
.step = 0x1,
.default_value = 0x1f,
- .flags = 0,
+ .flags = V4L2_CTRL_FLAG_SLIDER,
}, {
.id = V4L2_CID_AUDIO_MUTE,
.type = V4L2_CTRL_TYPE_BOOLEAN,
@@ -622,10 +622,29 @@ static void res_free(struct em28xx_fh *fh)
}
/*
- * em28xx_get_ctrl()
- * return the current saturation, brightness or contrast, mute state
+ * ac97_queryctrl()
+ * return the ac97 supported controls
+ */
+static int ac97_queryctrl(struct v4l2_queryctrl *qc)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ac97_qctrl); i++) {
+ if (qc->id && qc->id == ac97_qctrl[i].id) {
+ memcpy(qc, &(ac97_qctrl[i]), sizeof(*qc));
+ return 0;
+ }
+ }
+
+ /* Control is not ac97 related */
+ return 1;
+}
+
+/*
+ * ac97_get_ctrl()
+ * return the current values for ac97 mute and volume
*/
-static int em28xx_get_ctrl(struct em28xx *dev, struct v4l2_control *ctrl)
+static int ac97_get_ctrl(struct em28xx *dev, struct v4l2_control *ctrl)
{
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
@@ -635,29 +654,41 @@ static int em28xx_get_ctrl(struct em28xx *dev, struct v4l2_control *ctrl)
ctrl->value = dev->volume;
return 0;
default:
- return -EINVAL;
+ /* Control is not ac97 related */
+ return 1;
}
}
/*
- * em28xx_set_ctrl()
- * mute or set new saturation, brightness or contrast
+ * ac97_set_ctrl()
+ * set values for ac97 mute and volume
*/
-static int em28xx_set_ctrl(struct em28xx *dev, const struct v4l2_control *ctrl)
+static int ac97_set_ctrl(struct em28xx *dev, const struct v4l2_control *ctrl)
{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ac97_qctrl); i++)
+ if (ctrl->id == ac97_qctrl[i].id)
+ goto handle;
+
+ /* Announce that hasn't handle it */
+ return 1;
+
+handle:
+ if (ctrl->value < ac97_qctrl[i].minimum ||
+ ctrl->value > ac97_qctrl[i].maximum)
+ return -ERANGE;
+
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
- if (ctrl->value != dev->mute) {
- dev->mute = ctrl->value;
- return em28xx_audio_analog_set(dev);
- }
- return 0;
+ dev->mute = ctrl->value;
+ break;
case V4L2_CID_AUDIO_VOLUME:
dev->volume = ctrl->value;
- return em28xx_audio_analog_set(dev);
- default:
- return -EINVAL;
+ break;
}
+
+ return em28xx_audio_analog_set(dev);
}
static int check_dev(struct em28xx *dev)
@@ -1015,7 +1046,6 @@ static int vidioc_queryctrl(struct file *file, void *priv,
struct em28xx_fh *fh = priv;
struct em28xx *dev = fh->dev;
int id = qc->id;
- int i;
int rc;
rc = check_dev(dev);
@@ -1026,15 +1056,14 @@ static int vidioc_queryctrl(struct file *file, void *priv,
qc->id = id;
- if (!dev->board.has_msp34xx) {
- for (i = 0; i < ARRAY_SIZE(em28xx_qctrl); i++) {
- if (qc->id && qc->id == em28xx_qctrl[i].id) {
- memcpy(qc, &(em28xx_qctrl[i]), sizeof(*qc));
- return 0;
- }
- }
+ /* enumberate AC97 controls */
+ if (dev->audio_mode.ac97 != EM28XX_NO_AC97) {
+ rc = ac97_queryctrl(qc);
+ if (!rc)
+ return 0;
}
+ /* enumberate V4L2 device controls */
mutex_lock(&dev->lock);
v4l2_device_call_all(&dev->v4l2_dev, 0, core, queryctrl, qc);
mutex_unlock(&dev->lock);
@@ -1059,14 +1088,16 @@ static int vidioc_g_ctrl(struct file *file, void *priv,
mutex_lock(&dev->lock);
- if (dev->board.has_msp34xx)
+ /* Set an AC97 control */
+ if (dev->audio_mode.ac97 != EM28XX_NO_AC97)
+ rc = ac97_get_ctrl(dev, ctrl);
+ else
+ rc = 1;
+
+ /* It were not an AC97 control. Sends it to the v4l2 dev interface */
+ if (rc == 1) {
v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_ctrl, ctrl);
- else {
- rc = em28xx_get_ctrl(dev, ctrl);
- if (rc < 0) {
- v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_ctrl, ctrl);
- rc = 0;
- }
+ rc = 0;
}
mutex_unlock(&dev->lock);
@@ -1078,7 +1109,6 @@ static int vidioc_s_ctrl(struct file *file, void *priv,
{
struct em28xx_fh *fh = priv;
struct em28xx *dev = fh->dev;
- u8 i;
int rc;
rc = check_dev(dev);
@@ -1087,27 +1117,16 @@ static int vidioc_s_ctrl(struct file *file, void *priv,
mutex_lock(&dev->lock);
- if (dev->board.has_msp34xx)
- v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_ctrl, ctrl);
- else {
+ /* Set an AC97 control */
+ if (dev->audio_mode.ac97 != EM28XX_NO_AC97)
+ rc = ac97_set_ctrl(dev, ctrl);
+ else
rc = 1;
- for (i = 0; i < ARRAY_SIZE(em28xx_qctrl); i++) {
- if (ctrl->id == em28xx_qctrl[i].id) {
- if (ctrl->value < em28xx_qctrl[i].minimum ||
- ctrl->value > em28xx_qctrl[i].maximum) {
- rc = -ERANGE;
- break;
- }
-
- rc = em28xx_set_ctrl(dev, ctrl);
- break;
- }
- }
- }
- /* Control not found - try to send it to the attached devices */
+ /* It were not an AC97 control. Sends it to the v4l2 dev interface */
if (rc == 1) {
v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_ctrl, ctrl);
+ /* FIXME: should be returning a meaninful value */
rc = 0;
}
@@ -1668,9 +1687,9 @@ static int radio_queryctrl(struct file *file, void *priv,
qc->id >= V4L2_CID_LASTP1)
return -EINVAL;
- for (i = 0; i < ARRAY_SIZE(em28xx_qctrl); i++) {
- if (qc->id && qc->id == em28xx_qctrl[i].id) {
- memcpy(qc, &(em28xx_qctrl[i]), sizeof(*qc));
+ for (i = 0; i < ARRAY_SIZE(ac97_qctrl); i++) {
+ if (qc->id && qc->id == ac97_qctrl[i].id) {
+ memcpy(qc, &(ac97_qctrl[i]), sizeof(*qc));
return 0;
}
}
diff --git a/linux/drivers/media/video/gspca/Kconfig b/linux/drivers/media/video/gspca/Kconfig
index 578dc4ffc..34f46f2bc 100644
--- a/linux/drivers/media/video/gspca/Kconfig
+++ b/linux/drivers/media/video/gspca/Kconfig
@@ -102,6 +102,22 @@ config USB_GSPCA_PAC7311
To compile this driver as a module, choose M here: the
module will be called gspca_pac7311.
+config USB_GSPCA_SN9C20X
+ tristate "SN9C20X USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the
+ sn9c20x chips (SN9C201 and SN9C202).
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_sn9c20x.
+
+config USB_GSPCA_SN9C20X_EVDEV
+ bool "Enable evdev support"
+ depends on USB_GSPCA_SN9C20X
+ ---help---
+ Say Y here in order to enable evdev support for sn9c20x webcam button.
+
config USB_GSPCA_SONIXB
tristate "SONIX Bayer USB Camera Driver"
depends on VIDEO_V4L2 && USB_GSPCA
diff --git a/linux/drivers/media/video/gspca/Makefile b/linux/drivers/media/video/gspca/Makefile
index 8a6643e8e..f6d3b86e9 100644
--- a/linux/drivers/media/video/gspca/Makefile
+++ b/linux/drivers/media/video/gspca/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_USB_GSPCA_OV519) += gspca_ov519.o
obj-$(CONFIG_USB_GSPCA_OV534) += gspca_ov534.o
obj-$(CONFIG_USB_GSPCA_PAC207) += gspca_pac207.o
obj-$(CONFIG_USB_GSPCA_PAC7311) += gspca_pac7311.o
+obj-$(CONFIG_USB_GSPCA_SN9C20X) += gspca_sn9c20x.o
obj-$(CONFIG_USB_GSPCA_SONIXB) += gspca_sonixb.o
obj-$(CONFIG_USB_GSPCA_SONIXJ) += gspca_sonixj.o
obj-$(CONFIG_USB_GSPCA_SPCA500) += gspca_spca500.o
@@ -35,6 +36,7 @@ gspca_ov519-objs := ov519.o
gspca_ov534-objs := ov534.o
gspca_pac207-objs := pac207.o
gspca_pac7311-objs := pac7311.o
+gspca_sn9c20x-objs := sn9c20x.o
gspca_sonixb-objs := sonixb.o
gspca_sonixj-objs := sonixj.o
gspca_spca500-objs := spca500.o
diff --git a/linux/drivers/media/video/gspca/conex.c b/linux/drivers/media/video/gspca/conex.c
index 219cfa6fb..8d48ea174 100644
--- a/linux/drivers/media/video/gspca/conex.c
+++ b/linux/drivers/media/video/gspca/conex.c
@@ -846,6 +846,8 @@ static int sd_start(struct gspca_dev *gspca_dev)
/* create the JPEG header */
sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL);
+ if (!sd->jpeg_hdr)
+ return -ENOMEM;
jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width,
0x22); /* JPEG 411 */
jpeg_set_qual(sd->jpeg_hdr, sd->quality);
diff --git a/linux/drivers/media/video/gspca/gspca.c b/linux/drivers/media/video/gspca/gspca.c
index fe759178f..eedf9a60c 100644
--- a/linux/drivers/media/video/gspca/gspca.c
+++ b/linux/drivers/media/video/gspca/gspca.c
@@ -758,6 +758,74 @@ static int gspca_get_mode(struct gspca_dev *gspca_dev,
return -EINVAL;
}
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int vidioc_g_register(struct file *file, void *priv,
+ struct v4l2_dbg_register *reg)
+{
+ int ret;
+ struct gspca_dev *gspca_dev = priv;
+
+ if (!gspca_dev->sd_desc->get_chip_ident)
+ return -EINVAL;
+
+ if (!gspca_dev->sd_desc->get_register)
+ return -EINVAL;
+
+ if (mutex_lock_interruptible(&gspca_dev->usb_lock))
+ return -ERESTARTSYS;
+ if (gspca_dev->present)
+ ret = gspca_dev->sd_desc->get_register(gspca_dev, reg);
+ else
+ ret = -ENODEV;
+ mutex_unlock(&gspca_dev->usb_lock);
+
+ return ret;
+}
+
+static int vidioc_s_register(struct file *file, void *priv,
+ struct v4l2_dbg_register *reg)
+{
+ int ret;
+ struct gspca_dev *gspca_dev = priv;
+
+ if (!gspca_dev->sd_desc->get_chip_ident)
+ return -EINVAL;
+
+ if (!gspca_dev->sd_desc->set_register)
+ return -EINVAL;
+
+ if (mutex_lock_interruptible(&gspca_dev->usb_lock))
+ return -ERESTARTSYS;
+ if (gspca_dev->present)
+ ret = gspca_dev->sd_desc->set_register(gspca_dev, reg);
+ else
+ ret = -ENODEV;
+ mutex_unlock(&gspca_dev->usb_lock);
+
+ return ret;
+}
+#endif
+
+static int vidioc_g_chip_ident(struct file *file, void *priv,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ int ret;
+ struct gspca_dev *gspca_dev = priv;
+
+ if (!gspca_dev->sd_desc->get_chip_ident)
+ return -EINVAL;
+
+ if (mutex_lock_interruptible(&gspca_dev->usb_lock))
+ return -ERESTARTSYS;
+ if (gspca_dev->present)
+ ret = gspca_dev->sd_desc->get_chip_ident(gspca_dev, chip);
+ else
+ ret = -ENODEV;
+ mutex_unlock(&gspca_dev->usb_lock);
+
+ return ret;
+}
+
static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_fmtdesc *fmtdesc)
{
@@ -1914,6 +1982,11 @@ static const struct v4l2_ioctl_ops dev_ioctl_ops = {
.vidioc_s_parm = vidioc_s_parm,
.vidioc_s_std = vidioc_s_std,
.vidioc_enum_framesizes = vidioc_enum_framesizes,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = vidioc_g_register,
+ .vidioc_s_register = vidioc_s_register,
+#endif
+ .vidioc_g_chip_ident = vidioc_g_chip_ident,
#ifdef CONFIG_VIDEO_V4L1_COMPAT
.vidiocgmbuf = vidiocgmbuf,
#endif
diff --git a/linux/drivers/media/video/gspca/gspca.h b/linux/drivers/media/video/gspca/gspca.h
index 0508a23b9..70b1fd830 100644
--- a/linux/drivers/media/video/gspca/gspca.h
+++ b/linux/drivers/media/video/gspca/gspca.h
@@ -69,6 +69,10 @@ typedef void (*cam_v_op) (struct gspca_dev *);
typedef int (*cam_cf_op) (struct gspca_dev *, const struct usb_device_id *);
typedef int (*cam_jpg_op) (struct gspca_dev *,
struct v4l2_jpegcompression *);
+typedef int (*cam_reg_op) (struct gspca_dev *,
+ struct v4l2_dbg_register *);
+typedef int (*cam_ident_op) (struct gspca_dev *,
+ struct v4l2_dbg_chip_ident *);
typedef int (*cam_streamparm_op) (struct gspca_dev *,
struct v4l2_streamparm *);
typedef int (*cam_qmnu_op) (struct gspca_dev *,
@@ -107,6 +111,11 @@ struct sd_desc {
cam_qmnu_op querymenu;
cam_streamparm_op get_streamparm;
cam_streamparm_op set_streamparm;
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ cam_reg_op set_register;
+ cam_reg_op get_register;
+#endif
+ cam_ident_op get_chip_ident;
};
/* packet types when moving from iso buf to frame buf */
diff --git a/linux/drivers/media/video/gspca/mars.c b/linux/drivers/media/video/gspca/mars.c
index 789fd178a..68dd1ea0f 100644
--- a/linux/drivers/media/video/gspca/mars.c
+++ b/linux/drivers/media/video/gspca/mars.c
@@ -201,6 +201,8 @@ static int sd_start(struct gspca_dev *gspca_dev)
/* create the JPEG header */
sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL);
+ if (!sd->jpeg_hdr)
+ return -ENOMEM;
jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width,
0x21); /* JPEG 422 */
jpeg_set_qual(sd->jpeg_hdr, sd->quality);
diff --git a/linux/drivers/media/video/gspca/sn9c20x.c b/linux/drivers/media/video/gspca/sn9c20x.c
new file mode 100644
index 000000000..a516866a2
--- /dev/null
+++ b/linux/drivers/media/video/gspca/sn9c20x.c
@@ -0,0 +1,2439 @@
+/*
+ * Sonix sn9c201 sn9c202 library
+ * Copyright (C) 2008-2009 microdia project <microdia@googlegroups.com>
+ * Copyright (C) 2009 Brian Johnson <brijohn@gmail.com>
+ *
+ * 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
+ */
+
+#ifdef CONFIG_USB_GSPCA_SN9C20X_EVDEV
+#include <linux/kthread.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20)
+#include <linux/freezer.h>
+#endif
+#include <linux/usb/input.h>
+#include <linux/input.h>
+#endif
+
+#include "gspca.h"
+#include "jpeg.h"
+#include "compat.h"
+
+#include <media/v4l2-chip-ident.h>
+
+MODULE_AUTHOR("Brian Johnson <brijohn@gmail.com>, "
+ "microdia project <microdia@googlegroups.com>");
+MODULE_DESCRIPTION("GSPCA/SN9C20X USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+#define MODULE_NAME "sn9c20x"
+
+#define MODE_RAW 0x10
+#define MODE_JPEG 0x20
+#define MODE_SXGA 0x80
+
+#define SENSOR_OV9650 0
+#define SENSOR_OV9655 1
+#define SENSOR_SOI968 2
+#define SENSOR_OV7660 3
+#define SENSOR_OV7670 4
+#define SENSOR_MT9V011 5
+#define SENSOR_MT9V111 6
+#define SENSOR_MT9V112 7
+#define SENSOR_MT9M001 8
+#define SENSOR_MT9M111 9
+#define SENSOR_HV7131R 10
+#define SENSOR_MT9VPRB 20
+
+/* specific webcam descriptor */
+struct sd {
+ struct gspca_dev gspca_dev;
+
+#define MIN_AVG_LUM 80
+#define MAX_AVG_LUM 130
+ atomic_t avg_lum;
+ u8 old_step;
+ u8 older_step;
+ u8 exposure_step;
+
+ u8 brightness;
+ u8 contrast;
+ u8 saturation;
+ s16 hue;
+ u8 gamma;
+ u8 red;
+ u8 blue;
+
+ u8 hflip;
+ u8 vflip;
+ u8 gain;
+ u16 exposure;
+ u8 auto_exposure;
+
+ u8 i2c_addr;
+ u8 sensor;
+ u8 hstart;
+ u8 vstart;
+
+ u8 *jpeg_hdr;
+ u8 quality;
+
+#ifdef CONFIG_USB_GSPCA_SN9C20X_EVDEV
+ struct input_dev *input_dev;
+ u8 input_gpio;
+ struct task_struct *input_task;
+#endif
+};
+
+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_setcontrast(struct gspca_dev *gspca_dev, s32 val);
+static int sd_getcontrast(struct gspca_dev *gspca_dev, s32 *val);
+static int sd_setsaturation(struct gspca_dev *gspca_dev, s32 val);
+static int sd_getsaturation(struct gspca_dev *gspca_dev, s32 *val);
+static int sd_sethue(struct gspca_dev *gspca_dev, s32 val);
+static int sd_gethue(struct gspca_dev *gspca_dev, s32 *val);
+static int sd_setgamma(struct gspca_dev *gspca_dev, s32 val);
+static int sd_getgamma(struct gspca_dev *gspca_dev, s32 *val);
+static int sd_setredbalance(struct gspca_dev *gspca_dev, s32 val);
+static int sd_getredbalance(struct gspca_dev *gspca_dev, s32 *val);
+static int sd_setbluebalance(struct gspca_dev *gspca_dev, s32 val);
+static int sd_getbluebalance(struct gspca_dev *gspca_dev, s32 *val);
+static int sd_setvflip(struct gspca_dev *gspca_dev, s32 val);
+static int sd_getvflip(struct gspca_dev *gspca_dev, s32 *val);
+static int sd_sethflip(struct gspca_dev *gspca_dev, s32 val);
+static int sd_gethflip(struct gspca_dev *gspca_dev, s32 *val);
+static int sd_setgain(struct gspca_dev *gspca_dev, s32 val);
+static int sd_getgain(struct gspca_dev *gspca_dev, s32 *val);
+static int sd_setexposure(struct gspca_dev *gspca_dev, s32 val);
+static int sd_getexposure(struct gspca_dev *gspca_dev, s32 *val);
+static int sd_setautoexposure(struct gspca_dev *gspca_dev, s32 val);
+static int sd_getautoexposure(struct gspca_dev *gspca_dev, s32 *val);
+
+static struct ctrl sd_ctrls[] = {
+ {
+#define BRIGHTNESS_IDX 0
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Brightness",
+ .minimum = 0,
+ .maximum = 0xff,
+ .step = 1,
+#define BRIGHTNESS_DEFAULT 0x7f
+ .default_value = BRIGHTNESS_DEFAULT,
+ },
+ .set = sd_setbrightness,
+ .get = sd_getbrightness,
+ },
+ {
+#define CONTRAST_IDX 1
+ {
+ .id = V4L2_CID_CONTRAST,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Contrast",
+ .minimum = 0,
+ .maximum = 0xff,
+ .step = 1,
+#define CONTRAST_DEFAULT 0x7f
+ .default_value = CONTRAST_DEFAULT,
+ },
+ .set = sd_setcontrast,
+ .get = sd_getcontrast,
+ },
+ {
+#define SATURATION_IDX 2
+ {
+ .id = V4L2_CID_SATURATION,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Saturation",
+ .minimum = 0,
+ .maximum = 0xff,
+ .step = 1,
+#define SATURATION_DEFAULT 0x7f
+ .default_value = SATURATION_DEFAULT,
+ },
+ .set = sd_setsaturation,
+ .get = sd_getsaturation,
+ },
+ {
+#define HUE_IDX 3
+ {
+ .id = V4L2_CID_HUE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Hue",
+ .minimum = -180,
+ .maximum = 180,
+ .step = 1,
+#define HUE_DEFAULT 0
+ .default_value = HUE_DEFAULT,
+ },
+ .set = sd_sethue,
+ .get = sd_gethue,
+ },
+ {
+#define GAMMA_IDX 4
+ {
+ .id = V4L2_CID_GAMMA,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gamma",
+ .minimum = 0,
+ .maximum = 0xff,
+ .step = 1,
+#define GAMMA_DEFAULT 0x10
+ .default_value = GAMMA_DEFAULT,
+ },
+ .set = sd_setgamma,
+ .get = sd_getgamma,
+ },
+ {
+#define BLUE_IDX 5
+ {
+ .id = V4L2_CID_BLUE_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Blue Balance",
+ .minimum = 0,
+ .maximum = 0x7f,
+ .step = 1,
+#define BLUE_DEFAULT 0x28
+ .default_value = BLUE_DEFAULT,
+ },
+ .set = sd_setbluebalance,
+ .get = sd_getbluebalance,
+ },
+ {
+#define RED_IDX 6
+ {
+ .id = V4L2_CID_RED_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Red Balance",
+ .minimum = 0,
+ .maximum = 0x7f,
+ .step = 1,
+#define RED_DEFAULT 0x28
+ .default_value = RED_DEFAULT,
+ },
+ .set = sd_setredbalance,
+ .get = sd_getredbalance,
+ },
+ {
+#define HFLIP_IDX 7
+ {
+ .id = V4L2_CID_HFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Horizontal Flip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+#define HFLIP_DEFAULT 0
+ .default_value = HFLIP_DEFAULT,
+ },
+ .set = sd_sethflip,
+ .get = sd_gethflip,
+ },
+ {
+#define VFLIP_IDX 8
+ {
+ .id = V4L2_CID_VFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Vertical Flip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+#define VFLIP_DEFAULT 0
+ .default_value = VFLIP_DEFAULT,
+ },
+ .set = sd_setvflip,
+ .get = sd_getvflip,
+ },
+ {
+#define EXPOSURE_IDX 9
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Exposure",
+ .minimum = 0,
+ .maximum = 0x1780,
+ .step = 1,
+#define EXPOSURE_DEFAULT 0x33
+ .default_value = EXPOSURE_DEFAULT,
+ },
+ .set = sd_setexposure,
+ .get = sd_getexposure,
+ },
+ {
+#define GAIN_IDX 10
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain",
+ .minimum = 0,
+ .maximum = 28,
+ .step = 1,
+#define GAIN_DEFAULT 0x00
+ .default_value = GAIN_DEFAULT,
+ },
+ .set = sd_setgain,
+ .get = sd_getgain,
+ },
+ {
+#define AUTOGAIN_IDX 11
+ {
+ .id = V4L2_CID_AUTOGAIN,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Auto Exposure",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+#define AUTO_EXPOSURE_DEFAULT 1
+ .default_value = AUTO_EXPOSURE_DEFAULT,
+ },
+ .set = sd_setautoexposure,
+ .get = sd_getautoexposure,
+ },
+};
+
+static const struct v4l2_pix_format vga_mode[] = {
+ {160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 240,
+ .sizeimage = 240 * 120,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 0 | MODE_JPEG},
+ {160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 160,
+ .sizeimage = 160 * 120,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0 | MODE_RAW},
+ {160, 120, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE,
+ .bytesperline = 240,
+ .sizeimage = 240 * 120,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0},
+ {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 480,
+ .sizeimage = 480 * 240 ,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 1 | MODE_JPEG},
+ {320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 ,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1 | MODE_RAW},
+ {320, 240, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE,
+ .bytesperline = 480,
+ .sizeimage = 480 * 240 ,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1},
+ {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 960,
+ .sizeimage = 960 * 480,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 2 | MODE_JPEG},
+ {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 2 | MODE_RAW},
+ {640, 480, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE,
+ .bytesperline = 960,
+ .sizeimage = 960 * 480,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 2},
+};
+
+static const struct v4l2_pix_format sxga_mode[] = {
+ {160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 240,
+ .sizeimage = 240 * 120,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 0 | MODE_JPEG},
+ {160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 160,
+ .sizeimage = 160 * 120,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0 | MODE_RAW},
+ {160, 120, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE,
+ .bytesperline = 240,
+ .sizeimage = 240 * 120,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0},
+ {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 480,
+ .sizeimage = 480 * 240 ,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 1 | MODE_JPEG},
+ {320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 ,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1 | MODE_RAW},
+ {320, 240, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE,
+ .bytesperline = 480,
+ .sizeimage = 480 * 240 ,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1},
+ {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 960,
+ .sizeimage = 960 * 480,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 2 | MODE_JPEG},
+ {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 2 | MODE_RAW},
+ {640, 480, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE,
+ .bytesperline = 960,
+ .sizeimage = 960 * 480,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 2},
+ {1280, 1024, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 1280,
+ .sizeimage = (1280 * 1024) + 64,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 3 | MODE_RAW | MODE_SXGA},
+};
+
+static const int hsv_red_x[] = {
+ 41, 44, 46, 48, 50, 52, 54, 56,
+ 58, 60, 62, 64, 66, 68, 70, 72,
+ 74, 76, 78, 80, 81, 83, 85, 87,
+ 88, 90, 92, 93, 95, 97, 98, 100,
+ 101, 102, 104, 105, 107, 108, 109, 110,
+ 112, 113, 114, 115, 116, 117, 118, 119,
+ 120, 121, 122, 123, 123, 124, 125, 125,
+ 126, 127, 127, 128, 128, 129, 129, 129,
+ 130, 130, 130, 130, 131, 131, 131, 131,
+ 131, 131, 131, 131, 130, 130, 130, 130,
+ 129, 129, 129, 128, 128, 127, 127, 126,
+ 125, 125, 124, 123, 122, 122, 121, 120,
+ 119, 118, 117, 116, 115, 114, 112, 111,
+ 110, 109, 107, 106, 105, 103, 102, 101,
+ 99, 98, 96, 94, 93, 91, 90, 88,
+ 86, 84, 83, 81, 79, 77, 75, 74,
+ 72, 70, 68, 66, 64, 62, 60, 58,
+ 56, 54, 52, 49, 47, 45, 43, 41,
+ 39, 36, 34, 32, 30, 28, 25, 23,
+ 21, 19, 16, 14, 12, 9, 7, 5,
+ 3, 0, -1, -3, -6, -8, -10, -12,
+ -15, -17, -19, -22, -24, -26, -28, -30,
+ -33, -35, -37, -39, -41, -44, -46, -48,
+ -50, -52, -54, -56, -58, -60, -62, -64,
+ -66, -68, -70, -72, -74, -76, -78, -80,
+ -81, -83, -85, -87, -88, -90, -92, -93,
+ -95, -97, -98, -100, -101, -102, -104, -105,
+ -107, -108, -109, -110, -112, -113, -114, -115,
+ -116, -117, -118, -119, -120, -121, -122, -123,
+ -123, -124, -125, -125, -126, -127, -127, -128,
+ -128, -128, -128, -128, -128, -128, -128, -128,
+ -128, -128, -128, -128, -128, -128, -128, -128,
+ -128, -128, -128, -128, -128, -128, -128, -128,
+ -128, -127, -127, -126, -125, -125, -124, -123,
+ -122, -122, -121, -120, -119, -118, -117, -116,
+ -115, -114, -112, -111, -110, -109, -107, -106,
+ -105, -103, -102, -101, -99, -98, -96, -94,
+ -93, -91, -90, -88, -86, -84, -83, -81,
+ -79, -77, -75, -74, -72, -70, -68, -66,
+ -64, -62, -60, -58, -56, -54, -52, -49,
+ -47, -45, -43, -41, -39, -36, -34, -32,
+ -30, -28, -25, -23, -21, -19, -16, -14,
+ -12, -9, -7, -5, -3, 0, 1, 3,
+ 6, 8, 10, 12, 15, 17, 19, 22,
+ 24, 26, 28, 30, 33, 35, 37, 39, 41
+};
+
+static const int hsv_red_y[] = {
+ 82, 80, 78, 76, 74, 73, 71, 69,
+ 67, 65, 63, 61, 58, 56, 54, 52,
+ 50, 48, 46, 44, 41, 39, 37, 35,
+ 32, 30, 28, 26, 23, 21, 19, 16,
+ 14, 12, 10, 7, 5, 3, 0, -1,
+ -3, -6, -8, -10, -13, -15, -17, -19,
+ -22, -24, -26, -29, -31, -33, -35, -38,
+ -40, -42, -44, -46, -48, -51, -53, -55,
+ -57, -59, -61, -63, -65, -67, -69, -71,
+ -73, -75, -77, -79, -81, -82, -84, -86,
+ -88, -89, -91, -93, -94, -96, -98, -99,
+ -101, -102, -104, -105, -106, -108, -109, -110,
+ -112, -113, -114, -115, -116, -117, -119, -120,
+ -120, -121, -122, -123, -124, -125, -126, -126,
+ -127, -128, -128, -128, -128, -128, -128, -128,
+ -128, -128, -128, -128, -128, -128, -128, -128,
+ -128, -128, -128, -128, -128, -128, -128, -128,
+ -128, -128, -128, -128, -128, -128, -128, -128,
+ -127, -127, -126, -125, -125, -124, -123, -122,
+ -121, -120, -119, -118, -117, -116, -115, -114,
+ -113, -111, -110, -109, -107, -106, -105, -103,
+ -102, -100, -99, -97, -96, -94, -92, -91,
+ -89, -87, -85, -84, -82, -80, -78, -76,
+ -74, -73, -71, -69, -67, -65, -63, -61,
+ -58, -56, -54, -52, -50, -48, -46, -44,
+ -41, -39, -37, -35, -32, -30, -28, -26,
+ -23, -21, -19, -16, -14, -12, -10, -7,
+ -5, -3, 0, 1, 3, 6, 8, 10,
+ 13, 15, 17, 19, 22, 24, 26, 29,
+ 31, 33, 35, 38, 40, 42, 44, 46,
+ 48, 51, 53, 55, 57, 59, 61, 63,
+ 65, 67, 69, 71, 73, 75, 77, 79,
+ 81, 82, 84, 86, 88, 89, 91, 93,
+ 94, 96, 98, 99, 101, 102, 104, 105,
+ 106, 108, 109, 110, 112, 113, 114, 115,
+ 116, 117, 119, 120, 120, 121, 122, 123,
+ 124, 125, 126, 126, 127, 128, 128, 129,
+ 129, 130, 130, 131, 131, 131, 131, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 131, 131, 131, 130, 130,
+ 130, 129, 129, 128, 127, 127, 126, 125,
+ 125, 124, 123, 122, 121, 120, 119, 118,
+ 117, 116, 115, 114, 113, 111, 110, 109,
+ 107, 106, 105, 103, 102, 100, 99, 97,
+ 96, 94, 92, 91, 89, 87, 85, 84, 82
+};
+
+static const int hsv_green_x[] = {
+ -124, -124, -125, -125, -125, -125, -125, -125,
+ -125, -126, -126, -125, -125, -125, -125, -125,
+ -125, -124, -124, -124, -123, -123, -122, -122,
+ -121, -121, -120, -120, -119, -118, -117, -117,
+ -116, -115, -114, -113, -112, -111, -110, -109,
+ -108, -107, -105, -104, -103, -102, -100, -99,
+ -98, -96, -95, -93, -92, -91, -89, -87,
+ -86, -84, -83, -81, -79, -77, -76, -74,
+ -72, -70, -69, -67, -65, -63, -61, -59,
+ -57, -55, -53, -51, -49, -47, -45, -43,
+ -41, -39, -37, -35, -33, -30, -28, -26,
+ -24, -22, -20, -18, -15, -13, -11, -9,
+ -7, -4, -2, 0, 1, 3, 6, 8,
+ 10, 12, 14, 17, 19, 21, 23, 25,
+ 27, 29, 32, 34, 36, 38, 40, 42,
+ 44, 46, 48, 50, 52, 54, 56, 58,
+ 60, 62, 64, 66, 68, 70, 71, 73,
+ 75, 77, 78, 80, 82, 83, 85, 87,
+ 88, 90, 91, 93, 94, 96, 97, 98,
+ 100, 101, 102, 104, 105, 106, 107, 108,
+ 109, 111, 112, 113, 113, 114, 115, 116,
+ 117, 118, 118, 119, 120, 120, 121, 122,
+ 122, 123, 123, 124, 124, 124, 125, 125,
+ 125, 125, 125, 125, 125, 126, 126, 125,
+ 125, 125, 125, 125, 125, 124, 124, 124,
+ 123, 123, 122, 122, 121, 121, 120, 120,
+ 119, 118, 117, 117, 116, 115, 114, 113,
+ 112, 111, 110, 109, 108, 107, 105, 104,
+ 103, 102, 100, 99, 98, 96, 95, 93,
+ 92, 91, 89, 87, 86, 84, 83, 81,
+ 79, 77, 76, 74, 72, 70, 69, 67,
+ 65, 63, 61, 59, 57, 55, 53, 51,
+ 49, 47, 45, 43, 41, 39, 37, 35,
+ 33, 30, 28, 26, 24, 22, 20, 18,
+ 15, 13, 11, 9, 7, 4, 2, 0,
+ -1, -3, -6, -8, -10, -12, -14, -17,
+ -19, -21, -23, -25, -27, -29, -32, -34,
+ -36, -38, -40, -42, -44, -46, -48, -50,
+ -52, -54, -56, -58, -60, -62, -64, -66,
+ -68, -70, -71, -73, -75, -77, -78, -80,
+ -82, -83, -85, -87, -88, -90, -91, -93,
+ -94, -96, -97, -98, -100, -101, -102, -104,
+ -105, -106, -107, -108, -109, -111, -112, -113,
+ -113, -114, -115, -116, -117, -118, -118, -119,
+ -120, -120, -121, -122, -122, -123, -123, -124, -124
+};
+
+static const int hsv_green_y[] = {
+ -100, -99, -98, -97, -95, -94, -93, -91,
+ -90, -89, -87, -86, -84, -83, -81, -80,
+ -78, -76, -75, -73, -71, -70, -68, -66,
+ -64, -63, -61, -59, -57, -55, -53, -51,
+ -49, -48, -46, -44, -42, -40, -38, -36,
+ -34, -32, -30, -27, -25, -23, -21, -19,
+ -17, -15, -13, -11, -9, -7, -4, -2,
+ 0, 1, 3, 5, 7, 9, 11, 14,
+ 16, 18, 20, 22, 24, 26, 28, 30,
+ 32, 34, 36, 38, 40, 42, 44, 46,
+ 48, 50, 52, 54, 56, 58, 59, 61,
+ 63, 65, 67, 68, 70, 72, 74, 75,
+ 77, 78, 80, 82, 83, 85, 86, 88,
+ 89, 90, 92, 93, 95, 96, 97, 98,
+ 100, 101, 102, 103, 104, 105, 106, 107,
+ 108, 109, 110, 111, 112, 112, 113, 114,
+ 115, 115, 116, 116, 117, 117, 118, 118,
+ 119, 119, 119, 120, 120, 120, 120, 120,
+ 121, 121, 121, 121, 121, 121, 120, 120,
+ 120, 120, 120, 119, 119, 119, 118, 118,
+ 117, 117, 116, 116, 115, 114, 114, 113,
+ 112, 111, 111, 110, 109, 108, 107, 106,
+ 105, 104, 103, 102, 100, 99, 98, 97,
+ 95, 94, 93, 91, 90, 89, 87, 86,
+ 84, 83, 81, 80, 78, 76, 75, 73,
+ 71, 70, 68, 66, 64, 63, 61, 59,
+ 57, 55, 53, 51, 49, 48, 46, 44,
+ 42, 40, 38, 36, 34, 32, 30, 27,
+ 25, 23, 21, 19, 17, 15, 13, 11,
+ 9, 7, 4, 2, 0, -1, -3, -5,
+ -7, -9, -11, -14, -16, -18, -20, -22,
+ -24, -26, -28, -30, -32, -34, -36, -38,
+ -40, -42, -44, -46, -48, -50, -52, -54,
+ -56, -58, -59, -61, -63, -65, -67, -68,
+ -70, -72, -74, -75, -77, -78, -80, -82,
+ -83, -85, -86, -88, -89, -90, -92, -93,
+ -95, -96, -97, -98, -100, -101, -102, -103,
+ -104, -105, -106, -107, -108, -109, -110, -111,
+ -112, -112, -113, -114, -115, -115, -116, -116,
+ -117, -117, -118, -118, -119, -119, -119, -120,
+ -120, -120, -120, -120, -121, -121, -121, -121,
+ -121, -121, -120, -120, -120, -120, -120, -119,
+ -119, -119, -118, -118, -117, -117, -116, -116,
+ -115, -114, -114, -113, -112, -111, -111, -110,
+ -109, -108, -107, -106, -105, -104, -103, -102, -100
+};
+
+static const int hsv_blue_x[] = {
+ 112, 113, 114, 114, 115, 116, 117, 117,
+ 118, 118, 119, 119, 120, 120, 120, 121,
+ 121, 121, 122, 122, 122, 122, 122, 122,
+ 122, 122, 122, 122, 122, 122, 121, 121,
+ 121, 120, 120, 120, 119, 119, 118, 118,
+ 117, 116, 116, 115, 114, 113, 113, 112,
+ 111, 110, 109, 108, 107, 106, 105, 104,
+ 103, 102, 100, 99, 98, 97, 95, 94,
+ 93, 91, 90, 88, 87, 85, 84, 82,
+ 80, 79, 77, 76, 74, 72, 70, 69,
+ 67, 65, 63, 61, 60, 58, 56, 54,
+ 52, 50, 48, 46, 44, 42, 40, 38,
+ 36, 34, 32, 30, 28, 26, 24, 22,
+ 19, 17, 15, 13, 11, 9, 7, 5,
+ 2, 0, -1, -3, -5, -7, -9, -12,
+ -14, -16, -18, -20, -22, -24, -26, -28,
+ -31, -33, -35, -37, -39, -41, -43, -45,
+ -47, -49, -51, -53, -54, -56, -58, -60,
+ -62, -64, -66, -67, -69, -71, -73, -74,
+ -76, -78, -79, -81, -83, -84, -86, -87,
+ -89, -90, -92, -93, -94, -96, -97, -98,
+ -99, -101, -102, -103, -104, -105, -106, -107,
+ -108, -109, -110, -111, -112, -113, -114, -114,
+ -115, -116, -117, -117, -118, -118, -119, -119,
+ -120, -120, -120, -121, -121, -121, -122, -122,
+ -122, -122, -122, -122, -122, -122, -122, -122,
+ -122, -122, -121, -121, -121, -120, -120, -120,
+ -119, -119, -118, -118, -117, -116, -116, -115,
+ -114, -113, -113, -112, -111, -110, -109, -108,
+ -107, -106, -105, -104, -103, -102, -100, -99,
+ -98, -97, -95, -94, -93, -91, -90, -88,
+ -87, -85, -84, -82, -80, -79, -77, -76,
+ -74, -72, -70, -69, -67, -65, -63, -61,
+ -60, -58, -56, -54, -52, -50, -48, -46,
+ -44, -42, -40, -38, -36, -34, -32, -30,
+ -28, -26, -24, -22, -19, -17, -15, -13,
+ -11, -9, -7, -5, -2, 0, 1, 3,
+ 5, 7, 9, 12, 14, 16, 18, 20,
+ 22, 24, 26, 28, 31, 33, 35, 37,
+ 39, 41, 43, 45, 47, 49, 51, 53,
+ 54, 56, 58, 60, 62, 64, 66, 67,
+ 69, 71, 73, 74, 76, 78, 79, 81,
+ 83, 84, 86, 87, 89, 90, 92, 93,
+ 94, 96, 97, 98, 99, 101, 102, 103,
+ 104, 105, 106, 107, 108, 109, 110, 111, 112
+};
+
+static const int hsv_blue_y[] = {
+ -11, -13, -15, -17, -19, -21, -23, -25,
+ -27, -29, -31, -33, -35, -37, -39, -41,
+ -43, -45, -46, -48, -50, -52, -54, -55,
+ -57, -59, -61, -62, -64, -66, -67, -69,
+ -71, -72, -74, -75, -77, -78, -80, -81,
+ -83, -84, -86, -87, -88, -90, -91, -92,
+ -93, -95, -96, -97, -98, -99, -100, -101,
+ -102, -103, -104, -105, -106, -106, -107, -108,
+ -109, -109, -110, -111, -111, -112, -112, -113,
+ -113, -114, -114, -114, -115, -115, -115, -115,
+ -116, -116, -116, -116, -116, -116, -116, -116,
+ -116, -115, -115, -115, -115, -114, -114, -114,
+ -113, -113, -112, -112, -111, -111, -110, -110,
+ -109, -108, -108, -107, -106, -105, -104, -103,
+ -102, -101, -100, -99, -98, -97, -96, -95,
+ -94, -93, -91, -90, -89, -88, -86, -85,
+ -84, -82, -81, -79, -78, -76, -75, -73,
+ -71, -70, -68, -67, -65, -63, -62, -60,
+ -58, -56, -55, -53, -51, -49, -47, -45,
+ -44, -42, -40, -38, -36, -34, -32, -30,
+ -28, -26, -24, -22, -20, -18, -16, -14,
+ -12, -10, -8, -6, -4, -2, 0, 1,
+ 3, 5, 7, 9, 11, 13, 15, 17,
+ 19, 21, 23, 25, 27, 29, 31, 33,
+ 35, 37, 39, 41, 43, 45, 46, 48,
+ 50, 52, 54, 55, 57, 59, 61, 62,
+ 64, 66, 67, 69, 71, 72, 74, 75,
+ 77, 78, 80, 81, 83, 84, 86, 87,
+ 88, 90, 91, 92, 93, 95, 96, 97,
+ 98, 99, 100, 101, 102, 103, 104, 105,
+ 106, 106, 107, 108, 109, 109, 110, 111,
+ 111, 112, 112, 113, 113, 114, 114, 114,
+ 115, 115, 115, 115, 116, 116, 116, 116,
+ 116, 116, 116, 116, 116, 115, 115, 115,
+ 115, 114, 114, 114, 113, 113, 112, 112,
+ 111, 111, 110, 110, 109, 108, 108, 107,
+ 106, 105, 104, 103, 102, 101, 100, 99,
+ 98, 97, 96, 95, 94, 93, 91, 90,
+ 89, 88, 86, 85, 84, 82, 81, 79,
+ 78, 76, 75, 73, 71, 70, 68, 67,
+ 65, 63, 62, 60, 58, 56, 55, 53,
+ 51, 49, 47, 45, 44, 42, 40, 38,
+ 36, 34, 32, 30, 28, 26, 24, 22,
+ 20, 18, 16, 14, 12, 10, 8, 6,
+ 4, 2, 0, -1, -3, -5, -7, -9, -11
+};
+
+static u16 i2c_ident[] = {
+ V4L2_IDENT_OV9650,
+ V4L2_IDENT_OV9655,
+ V4L2_IDENT_SOI968,
+ V4L2_IDENT_OV7660,
+ V4L2_IDENT_OV7670,
+ V4L2_IDENT_MT9V011,
+ V4L2_IDENT_MT9V111,
+ V4L2_IDENT_MT9V112,
+ V4L2_IDENT_MT9M001C12ST,
+ V4L2_IDENT_MT9M111,
+ V4L2_IDENT_HV7131R,
+};
+
+static u16 bridge_init[][2] = {
+ {0x1000, 0x78}, {0x1001, 0x40}, {0x1002, 0x1c},
+ {0x1020, 0x80}, {0x1061, 0x01}, {0x1067, 0x40},
+ {0x1068, 0x30}, {0x1069, 0x20}, {0x106a, 0x10},
+ {0x106b, 0x08}, {0x1188, 0x87}, {0x11a1, 0x00},
+ {0x11a2, 0x00}, {0x11a3, 0x6a}, {0x11a4, 0x50},
+ {0x11ab, 0x00}, {0x11ac, 0x00}, {0x11ad, 0x50},
+ {0x11ae, 0x3c}, {0x118a, 0x04}, {0x0395, 0x04},
+ {0x11b8, 0x3a}, {0x118b, 0x0e}, {0x10f7, 0x05},
+ {0x10f8, 0x14}, {0x10fa, 0xff}, {0x10f9, 0x00},
+ {0x11ba, 0x0a}, {0x11a5, 0x2d}, {0x11a6, 0x2d},
+ {0x11a7, 0x3a}, {0x11a8, 0x05}, {0x11a9, 0x04},
+ {0x11aa, 0x3f}, {0x11af, 0x28}, {0x11b0, 0xd8},
+ {0x11b1, 0x14}, {0x11b2, 0xec}, {0x11b3, 0x32},
+ {0x11b4, 0xdd}, {0x11b5, 0x32}, {0x11b6, 0xdd},
+ {0x10e0, 0x2c}, {0x11bc, 0x40}, {0x11bd, 0x01},
+ {0x11be, 0xf0}, {0x11bf, 0x00}, {0x118c, 0x1f},
+ {0x118d, 0x1f}, {0x118e, 0x1f}, {0x118f, 0x1f},
+ {0x1180, 0x01}, {0x1181, 0x00}, {0x1182, 0x01},
+ {0x1183, 0x00}, {0x1184, 0x50}, {0x1185, 0x80}
+};
+
+/* Gain = (bit[3:0] / 16 + 1) * (bit[4] + 1) * (bit[5] + 1) * (bit[6] + 1) */
+static u8 ov_gain[] = {
+ 0x00 /* 1x */, 0x04 /* 1.25x */, 0x08 /* 1.5x */, 0x0c /* 1.75x */,
+ 0x10 /* 2x */, 0x12 /* 2.25x */, 0x14 /* 2.5x */, 0x16 /* 2.75x */,
+ 0x18 /* 3x */, 0x1a /* 3.25x */, 0x1c /* 3.5x */, 0x1e /* 3.75x */,
+ 0x30 /* 4x */, 0x31 /* 4.25x */, 0x32 /* 4.5x */, 0x33 /* 4.75x */,
+ 0x34 /* 5x */, 0x35 /* 5.25x */, 0x36 /* 5.5x */, 0x37 /* 5.75x */,
+ 0x38 /* 6x */, 0x39 /* 6.25x */, 0x3a /* 6.5x */, 0x3b /* 6.75x */,
+ 0x3c /* 7x */, 0x3d /* 7.25x */, 0x3e /* 7.5x */, 0x3f /* 7.75x */,
+ 0x70 /* 8x */
+};
+
+/* Gain = (bit[8] + 1) * (bit[7] + 1) * (bit[6:0] * 0.03125) */
+static u16 micron1_gain[] = {
+ /* 1x 1.25x 1.5x 1.75x */
+ 0x0020, 0x0028, 0x0030, 0x0038,
+ /* 2x 2.25x 2.5x 2.75x */
+ 0x00a0, 0x00a4, 0x00a8, 0x00ac,
+ /* 3x 3.25x 3.5x 3.75x */
+ 0x00b0, 0x00b4, 0x00b8, 0x00bc,
+ /* 4x 4.25x 4.5x 4.75x */
+ 0x00c0, 0x00c4, 0x00c8, 0x00cc,
+ /* 5x 5.25x 5.5x 5.75x */
+ 0x00d0, 0x00d4, 0x00d8, 0x00dc,
+ /* 6x 6.25x 6.5x 6.75x */
+ 0x00e0, 0x00e4, 0x00e8, 0x00ec,
+ /* 7x 7.25x 7.5x 7.75x */
+ 0x00f0, 0x00f4, 0x00f8, 0x00fc,
+ /* 8x */
+ 0x01c0
+};
+
+/* mt9m001 sensor uses a different gain formula then other micron sensors */
+/* Gain = (bit[6] + 1) * (bit[5-0] * 0.125) */
+static u16 micron2_gain[] = {
+ /* 1x 1.25x 1.5x 1.75x */
+ 0x0008, 0x000a, 0x000c, 0x000e,
+ /* 2x 2.25x 2.5x 2.75x */
+ 0x0010, 0x0012, 0x0014, 0x0016,
+ /* 3x 3.25x 3.5x 3.75x */
+ 0x0018, 0x001a, 0x001c, 0x001e,
+ /* 4x 4.25x 4.5x 4.75x */
+ 0x0020, 0x0051, 0x0052, 0x0053,
+ /* 5x 5.25x 5.5x 5.75x */
+ 0x0054, 0x0055, 0x0056, 0x0057,
+ /* 6x 6.25x 6.5x 6.75x */
+ 0x0058, 0x0059, 0x005a, 0x005b,
+ /* 7x 7.25x 7.5x 7.75x */
+ 0x005c, 0x005d, 0x005e, 0x005f,
+ /* 8x */
+ 0x0060
+};
+
+/* Gain = .5 + bit[7:0] / 16 */
+static u8 hv7131r_gain[] = {
+ 0x08 /* 1x */, 0x0c /* 1.25x */, 0x10 /* 1.5x */, 0x14 /* 1.75x */,
+ 0x18 /* 2x */, 0x1c /* 2.25x */, 0x20 /* 2.5x */, 0x24 /* 2.75x */,
+ 0x28 /* 3x */, 0x2c /* 3.25x */, 0x30 /* 3.5x */, 0x34 /* 3.75x */,
+ 0x38 /* 4x */, 0x3c /* 4.25x */, 0x40 /* 4.5x */, 0x44 /* 4.75x */,
+ 0x48 /* 5x */, 0x4c /* 5.25x */, 0x50 /* 5.5x */, 0x54 /* 5.75x */,
+ 0x58 /* 6x */, 0x5c /* 6.25x */, 0x60 /* 6.5x */, 0x64 /* 6.75x */,
+ 0x68 /* 7x */, 0x6c /* 7.25x */, 0x70 /* 7.5x */, 0x74 /* 7.75x */,
+ 0x78 /* 8x */
+};
+
+static u8 soi968_init[][2] = {
+ {0x12, 0x80}, {0x0c, 0x00}, {0x0f, 0x1f},
+ {0x11, 0x80}, {0x38, 0x52}, {0x1e, 0x00},
+ {0x33, 0x08}, {0x35, 0x8c}, {0x36, 0x0c},
+ {0x37, 0x04}, {0x45, 0x04}, {0x47, 0xff},
+ {0x3e, 0x00}, {0x3f, 0x00}, {0x3b, 0x20},
+ {0x3a, 0x96}, {0x3d, 0x0a}, {0x14, 0x8e},
+ {0x13, 0x8a}, {0x12, 0x40}, {0x17, 0x13},
+ {0x18, 0x63}, {0x19, 0x01}, {0x1a, 0x79},
+ {0x32, 0x24}, {0x03, 0x00}, {0x11, 0x40},
+ {0x2a, 0x10}, {0x2b, 0xe0}, {0x10, 0x32},
+ {0x00, 0x00}, {0x01, 0x80}, {0x02, 0x80},
+};
+
+static u8 ov7660_init[][2] = {
+ {0x0e, 0x80}, {0x0d, 0x08}, {0x0f, 0xc3},
+ {0x04, 0xc3}, {0x10, 0x40}, {0x11, 0x40},
+ {0x12, 0x05}, {0x13, 0xba}, {0x14, 0x2a},
+ {0x37, 0x0f}, {0x38, 0x02}, {0x39, 0x43},
+ {0x3a, 0x00}, {0x69, 0x90}, {0x2d, 0xf6},
+ {0x2e, 0x0b}, {0x01, 0x78}, {0x02, 0x50},
+};
+
+static u8 ov7670_init[][2] = {
+ {0x12, 0x80}, {0x11, 0x80}, {0x3a, 0x04}, {0x12, 0x01},
+ {0x32, 0xb6}, {0x03, 0x0a}, {0x0c, 0x00}, {0x3e, 0x00},
+ {0x70, 0x3a}, {0x71, 0x35}, {0x72, 0x11}, {0x73, 0xf0},
+ {0xa2, 0x02}, {0x13, 0xe0}, {0x00, 0x00}, {0x10, 0x00},
+ {0x0d, 0x40}, {0x14, 0x28}, {0xa5, 0x05}, {0xab, 0x07},
+ {0x24, 0x95}, {0x25, 0x33}, {0x26, 0xe3}, {0x9f, 0x75},
+ {0xa0, 0x65}, {0xa1, 0x0b}, {0xa6, 0xd8}, {0xa7, 0xd8},
+ {0xa8, 0xf0}, {0xa9, 0x90}, {0xaa, 0x94}, {0x13, 0xe5},
+ {0x0e, 0x61}, {0x0f, 0x4b}, {0x16, 0x02}, {0x1e, 0x27},
+ {0x21, 0x02}, {0x22, 0x91}, {0x29, 0x07}, {0x33, 0x0b},
+ {0x35, 0x0b}, {0x37, 0x1d}, {0x38, 0x71}, {0x39, 0x2a},
+ {0x3c, 0x78}, {0x4d, 0x40}, {0x4e, 0x20}, {0x69, 0x00},
+ {0x74, 0x19}, {0x8d, 0x4f}, {0x8e, 0x00}, {0x8f, 0x00},
+ {0x90, 0x00}, {0x91, 0x00}, {0x96, 0x00}, {0x9a, 0x80},
+ {0xb0, 0x84}, {0xb1, 0x0c}, {0xb2, 0x0e}, {0xb3, 0x82},
+ {0xb8, 0x0a}, {0x43, 0x0a}, {0x44, 0xf0}, {0x45, 0x20},
+ {0x46, 0x7d}, {0x47, 0x29}, {0x48, 0x4a}, {0x59, 0x8c},
+ {0x5a, 0xa5}, {0x5b, 0xde}, {0x5c, 0x96}, {0x5d, 0x66},
+ {0x5e, 0x10}, {0x6c, 0x0a}, {0x6d, 0x55}, {0x6e, 0x11},
+ {0x6f, 0x9e}, {0x6a, 0x40}, {0x01, 0x40}, {0x02, 0x40},
+ {0x13, 0xe7}, {0x4f, 0x6e}, {0x50, 0x70}, {0x51, 0x02},
+ {0x52, 0x1d}, {0x53, 0x56}, {0x54, 0x73}, {0x55, 0x0a},
+ {0x56, 0x55}, {0x57, 0x80}, {0x58, 0x9e}, {0x41, 0x08},
+ {0x3f, 0x02}, {0x75, 0x03}, {0x76, 0x63}, {0x4c, 0x04},
+ {0x77, 0x06}, {0x3d, 0x02}, {0x4b, 0x09}, {0xc9, 0x30},
+ {0x41, 0x08}, {0x56, 0x48}, {0x34, 0x11}, {0xa4, 0x88},
+ {0x96, 0x00}, {0x97, 0x30}, {0x98, 0x20}, {0x99, 0x30},
+ {0x9a, 0x84}, {0x9b, 0x29}, {0x9c, 0x03}, {0x9d, 0x99},
+ {0x9e, 0x7f}, {0x78, 0x04}, {0x79, 0x01}, {0xc8, 0xf0},
+ {0x79, 0x0f}, {0xc8, 0x00}, {0x79, 0x10}, {0xc8, 0x7e},
+ {0x79, 0x0a}, {0xc8, 0x80}, {0x79, 0x0b}, {0xc8, 0x01},
+ {0x79, 0x0c}, {0xc8, 0x0f}, {0x79, 0x0d}, {0xc8, 0x20},
+ {0x79, 0x09}, {0xc8, 0x80}, {0x79, 0x02}, {0xc8, 0xc0},
+ {0x79, 0x03}, {0xc8, 0x40}, {0x79, 0x05}, {0xc8, 0x30},
+ {0x79, 0x26}, {0x62, 0x20}, {0x63, 0x00}, {0x64, 0x06},
+ {0x65, 0x00}, {0x66, 0x05}, {0x94, 0x05}, {0x95, 0x0a},
+ {0x17, 0x13}, {0x18, 0x01}, {0x19, 0x02}, {0x1a, 0x7a},
+ {0x46, 0x59}, {0x47, 0x30}, {0x58, 0x9a}, {0x59, 0x84},
+ {0x5a, 0x91}, {0x5b, 0x57}, {0x5c, 0x75}, {0x5d, 0x6d},
+ {0x5e, 0x13}, {0x64, 0x07}, {0x94, 0x07}, {0x95, 0x0d},
+ {0xa6, 0xdf}, {0xa7, 0xdf}, {0x48, 0x4d}, {0x51, 0x00},
+ {0x6b, 0x0a}, {0x11, 0x80}, {0x2a, 0x00}, {0x2b, 0x00},
+ {0x92, 0x00}, {0x93, 0x00}, {0x55, 0x0a}, {0x56, 0x60},
+ {0x4f, 0x6e}, {0x50, 0x70}, {0x51, 0x00}, {0x52, 0x1d},
+ {0x53, 0x56}, {0x54, 0x73}, {0x58, 0x9a}, {0x4f, 0x6e},
+ {0x50, 0x70}, {0x51, 0x00}, {0x52, 0x1d}, {0x53, 0x56},
+ {0x54, 0x73}, {0x58, 0x9a}, {0x3f, 0x01}, {0x7b, 0x03},
+ {0x7c, 0x09}, {0x7d, 0x16}, {0x7e, 0x38}, {0x7f, 0x47},
+ {0x80, 0x53}, {0x81, 0x5e}, {0x82, 0x6a}, {0x83, 0x74},
+ {0x84, 0x80}, {0x85, 0x8c}, {0x86, 0x9b}, {0x87, 0xb2},
+ {0x88, 0xcc}, {0x89, 0xe5}, {0x7a, 0x24}, {0x3b, 0x00},
+ {0x9f, 0x76}, {0xa0, 0x65}, {0x13, 0xe2}, {0x6b, 0x0a},
+ {0x11, 0x80}, {0x2a, 0x00}, {0x2b, 0x00}, {0x92, 0x00},
+ {0x93, 0x00},
+};
+
+static u8 ov9650_init[][2] = {
+ {0x12, 0x80}, {0x00, 0x00}, {0x01, 0x78},
+ {0x02, 0x78}, {0x03, 0x36}, {0x04, 0x03},
+ {0x05, 0x00}, {0x06, 0x00}, {0x08, 0x00},
+ {0x09, 0x01}, {0x0c, 0x00}, {0x0d, 0x00},
+ {0x0e, 0xa0}, {0x0f, 0x52}, {0x10, 0x7c},
+ {0x11, 0x80}, {0x12, 0x45}, {0x13, 0xc2},
+ {0x14, 0x2e}, {0x15, 0x00}, {0x16, 0x07},
+ {0x17, 0x24}, {0x18, 0xc5}, {0x19, 0x00},
+ {0x1a, 0x3c}, {0x1b, 0x00}, {0x1e, 0x04},
+ {0x1f, 0x00}, {0x24, 0x78}, {0x25, 0x68},
+ {0x26, 0xd4}, {0x27, 0x80}, {0x28, 0x80},
+ {0x29, 0x30}, {0x2a, 0x00}, {0x2b, 0x00},
+ {0x2c, 0x80}, {0x2d, 0x00}, {0x2e, 0x00},
+ {0x2f, 0x00}, {0x30, 0x08}, {0x31, 0x30},
+ {0x32, 0x84}, {0x33, 0xe2}, {0x34, 0xbf},
+ {0x35, 0x81}, {0x36, 0xf9}, {0x37, 0x00},
+ {0x38, 0x93}, {0x39, 0x50}, {0x3a, 0x01},
+ {0x3b, 0x01}, {0x3c, 0x73}, {0x3d, 0x19},
+ {0x3e, 0x0b}, {0x3f, 0x80}, {0x40, 0xc1},
+ {0x41, 0x00}, {0x42, 0x08}, {0x67, 0x80},
+ {0x68, 0x80}, {0x69, 0x40}, {0x6a, 0x00},
+ {0x6b, 0x0a}, {0x8b, 0x06}, {0x8c, 0x20},
+ {0x8d, 0x00}, {0x8e, 0x00}, {0x8f, 0xdf},
+ {0x92, 0x00}, {0x93, 0x00}, {0x94, 0x88},
+ {0x95, 0x88}, {0x96, 0x04}, {0xa1, 0x00},
+ {0xa5, 0x80}, {0xa8, 0x80}, {0xa9, 0xb8},
+ {0xaa, 0x92}, {0xab, 0x0a},
+};
+
+static u8 ov9655_init[][2] = {
+ {0x12, 0x80}, {0x12, 0x01}, {0x0d, 0x00}, {0x0e, 0x61},
+ {0x11, 0x80}, {0x13, 0xba}, {0x14, 0x2e}, {0x16, 0x24},
+ {0x1e, 0x04}, {0x1e, 0x04}, {0x1e, 0x04}, {0x27, 0x08},
+ {0x28, 0x08}, {0x29, 0x15}, {0x2c, 0x08}, {0x32, 0xbf},
+ {0x34, 0x3d}, {0x35, 0x00}, {0x36, 0xf8}, {0x38, 0x12},
+ {0x39, 0x57}, {0x3a, 0x00}, {0x3b, 0xcc}, {0x3c, 0x0c},
+ {0x3d, 0x19}, {0x3e, 0x0c}, {0x3f, 0x01}, {0x41, 0x40},
+ {0x42, 0x80}, {0x45, 0x46}, {0x46, 0x62}, {0x47, 0x2a},
+ {0x48, 0x3c}, {0x4a, 0xf0}, {0x4b, 0xdc}, {0x4c, 0xdc},
+ {0x4d, 0xdc}, {0x4e, 0xdc}, {0x69, 0x02}, {0x6c, 0x04},
+ {0x6f, 0x9e}, {0x70, 0x05}, {0x71, 0x78}, {0x77, 0x02},
+ {0x8a, 0x23}, {0x8c, 0x0d}, {0x90, 0x7e}, {0x91, 0x7c},
+ {0x9f, 0x6e}, {0xa0, 0x6e}, {0xa5, 0x68}, {0xa6, 0x60},
+ {0xa8, 0xc1}, {0xa9, 0xfa}, {0xaa, 0x92}, {0xab, 0x04},
+ {0xac, 0x80}, {0xad, 0x80}, {0xae, 0x80}, {0xaf, 0x80},
+ {0xb2, 0xf2}, {0xb3, 0x20}, {0xb5, 0x00}, {0xb6, 0xaf},
+ {0xbb, 0xae}, {0xbc, 0x44}, {0xbd, 0x44}, {0xbe, 0x3b},
+ {0xbf, 0x3a}, {0xc0, 0xe2}, {0xc1, 0xc8}, {0xc2, 0x01},
+ {0xc4, 0x00}, {0xc6, 0x85}, {0xc7, 0x81}, {0xc9, 0xe0},
+ {0xca, 0xe8}, {0xcc, 0xd8}, {0xcd, 0x93}, {0x12, 0x61},
+ {0x36, 0xfa}, {0x8c, 0x8d}, {0xc0, 0xaa}, {0x69, 0x0a},
+ {0x03, 0x12}, {0x17, 0x14}, {0x18, 0x00}, {0x19, 0x01},
+ {0x1a, 0x3d}, {0x32, 0xbf}, {0x11, 0x80}, {0x2a, 0x10},
+ {0x2b, 0x0a}, {0x92, 0x00}, {0x93, 0x00}, {0x1e, 0x04},
+ {0x1e, 0x04}, {0x10, 0x7c}, {0x04, 0x03}, {0xa1, 0x00},
+ {0x2d, 0x00}, {0x2e, 0x00}, {0x00, 0x00}, {0x01, 0x80},
+ {0x02, 0x80}, {0x12, 0x61}, {0x36, 0xfa}, {0x8c, 0x8d},
+ {0xc0, 0xaa}, {0x69, 0x0a}, {0x03, 0x12}, {0x17, 0x14},
+ {0x18, 0x00}, {0x19, 0x01}, {0x1a, 0x3d}, {0x32, 0xbf},
+ {0x11, 0x80}, {0x2a, 0x10}, {0x2b, 0x0a}, {0x92, 0x00},
+ {0x93, 0x00}, {0x04, 0x01}, {0x10, 0x1f}, {0xa1, 0x00},
+ {0x00, 0x0a}, {0xa1, 0x00}, {0x10, 0x5d}, {0x04, 0x03},
+ {0x00, 0x01}, {0xa1, 0x00}, {0x10, 0x7c}, {0x04, 0x03},
+ {0x00, 0x03}, {0x00, 0x0a}, {0x00, 0x10}, {0x00, 0x13},
+};
+
+static u16 mt9v112_init[][2] = {
+ {0xf0, 0x0000}, {0x0d, 0x0021}, {0x0d, 0x0020},
+ {0x34, 0xc019}, {0x0a, 0x0011}, {0x0b, 0x000b},
+ {0x20, 0x0703}, {0x35, 0x2022}, {0xf0, 0x0001},
+ {0x05, 0x0000}, {0x06, 0x340c}, {0x3b, 0x042a},
+ {0x3c, 0x0400}, {0xf0, 0x0002}, {0x2e, 0x0c58},
+ {0x5b, 0x0001}, {0xc8, 0x9f0b}, {0xf0, 0x0001},
+ {0x9b, 0x5300}, {0xf0, 0x0000}, {0x2b, 0x0020},
+ {0x2c, 0x002a}, {0x2d, 0x0032}, {0x2e, 0x0020},
+ {0x09, 0x01dc}, {0x01, 0x000c}, {0x02, 0x0020},
+ {0x03, 0x01e0}, {0x04, 0x0280}, {0x06, 0x000c},
+ {0x05, 0x0098}, {0x20, 0x0703}, {0x09, 0x01f2},
+ {0x2b, 0x00a0}, {0x2c, 0x00a0}, {0x2d, 0x00a0},
+ {0x2e, 0x00a0}, {0x01, 0x000c}, {0x02, 0x0020},
+ {0x03, 0x01e0}, {0x04, 0x0280}, {0x06, 0x000c},
+ {0x05, 0x0098}, {0x09, 0x01c1}, {0x2b, 0x00ae},
+ {0x2c, 0x00ae}, {0x2d, 0x00ae}, {0x2e, 0x00ae},
+};
+
+static u16 mt9v111_init[][2] = {
+ {0x01, 0x0004}, {0x0d, 0x0001}, {0x0d, 0x0000},
+ {0x01, 0x0001}, {0x02, 0x0016}, {0x03, 0x01e1},
+ {0x04, 0x0281}, {0x05, 0x0004}, {0x07, 0x3002},
+ {0x21, 0x0000}, {0x25, 0x4024}, {0x26, 0xff03},
+ {0x27, 0xff10}, {0x2b, 0x7828}, {0x2c, 0xb43c},
+ {0x2d, 0xf0a0}, {0x2e, 0x0c64}, {0x2f, 0x0064},
+ {0x67, 0x4010}, {0x06, 0x301e}, {0x08, 0x0480},
+ {0x01, 0x0004}, {0x02, 0x0016}, {0x03, 0x01e6},
+ {0x04, 0x0286}, {0x05, 0x0004}, {0x06, 0x0000},
+ {0x07, 0x3002}, {0x08, 0x0008}, {0x0c, 0x0000},
+ {0x0d, 0x0000}, {0x0e, 0x0000}, {0x0f, 0x0000},
+ {0x10, 0x0000}, {0x11, 0x0000}, {0x12, 0x00b0},
+ {0x13, 0x007c}, {0x14, 0x0000}, {0x15, 0x0000},
+ {0x16, 0x0000}, {0x17, 0x0000}, {0x18, 0x0000},
+ {0x19, 0x0000}, {0x1a, 0x0000}, {0x1b, 0x0000},
+ {0x1c, 0x0000}, {0x1d, 0x0000}, {0x30, 0x0000},
+ {0x30, 0x0005}, {0x31, 0x0000}, {0x02, 0x0016},
+ {0x03, 0x01e1}, {0x04, 0x0281}, {0x05, 0x0004},
+ {0x06, 0x0000}, {0x07, 0x3002}, {0x06, 0x002d},
+ {0x05, 0x0004}, {0x09, 0x0064}, {0x2b, 0x00a0},
+ {0x2c, 0x00a0}, {0x2d, 0x00a0}, {0x2e, 0x00a0},
+ {0x02, 0x0016}, {0x03, 0x01e1}, {0x04, 0x0281},
+ {0x05, 0x0004}, {0x06, 0x002d}, {0x07, 0x3002},
+ {0x0e, 0x0008}, {0x06, 0x002d}, {0x05, 0x0004},
+};
+
+static u16 mt9v011_init[][2] = {
+ {0x07, 0x0002}, {0x0d, 0x0001}, {0x0d, 0x0000},
+ {0x01, 0x0008}, {0x02, 0x0016}, {0x03, 0x01e1},
+ {0x04, 0x0281}, {0x05, 0x0083}, {0x06, 0x0006},
+ {0x0d, 0x0002}, {0x0a, 0x0000}, {0x0b, 0x0000},
+ {0x0c, 0x0000}, {0x0d, 0x0000}, {0x0e, 0x0000},
+ {0x0f, 0x0000}, {0x10, 0x0000}, {0x11, 0x0000},
+ {0x12, 0x0000}, {0x13, 0x0000}, {0x14, 0x0000},
+ {0x15, 0x0000}, {0x16, 0x0000}, {0x17, 0x0000},
+ {0x18, 0x0000}, {0x19, 0x0000}, {0x1a, 0x0000},
+ {0x1b, 0x0000}, {0x1c, 0x0000}, {0x1d, 0x0000},
+ {0x32, 0x0000}, {0x20, 0x1101}, {0x21, 0x0000},
+ {0x22, 0x0000}, {0x23, 0x0000}, {0x24, 0x0000},
+ {0x25, 0x0000}, {0x26, 0x0000}, {0x27, 0x0024},
+ {0x2f, 0xf7b0}, {0x30, 0x0005}, {0x31, 0x0000},
+ {0x32, 0x0000}, {0x33, 0x0000}, {0x34, 0x0100},
+ {0x3d, 0x068f}, {0x40, 0x01e0}, {0x41, 0x00d1},
+ {0x44, 0x0082}, {0x5a, 0x0000}, {0x5b, 0x0000},
+ {0x5c, 0x0000}, {0x5d, 0x0000}, {0x5e, 0x0000},
+ {0x5f, 0xa31d}, {0x62, 0x0611}, {0x0a, 0x0000},
+ {0x06, 0x0029}, {0x05, 0x0009}, {0x20, 0x1101},
+ {0x20, 0x1101}, {0x09, 0x0064}, {0x07, 0x0003},
+ {0x2b, 0x0033}, {0x2c, 0x00a0}, {0x2d, 0x00a0},
+ {0x2e, 0x0033}, {0x07, 0x0002}, {0x06, 0x0000},
+ {0x06, 0x0029}, {0x05, 0x0009},
+};
+
+static u16 mt9m001_init[][2] = {
+ {0x0d, 0x0001}, {0x0d, 0x0000}, {0x01, 0x000e},
+ {0x02, 0x0014}, {0x03, 0x03c1}, {0x04, 0x0501},
+ {0x05, 0x0083}, {0x06, 0x0006}, {0x0d, 0x0002},
+ {0x0a, 0x0000}, {0x0c, 0x0000}, {0x11, 0x0000},
+ {0x1e, 0x8000}, {0x5f, 0x8904}, {0x60, 0x0000},
+ {0x61, 0x0000}, {0x62, 0x0498}, {0x63, 0x0000},
+ {0x64, 0x0000}, {0x20, 0x111d}, {0x06, 0x00f2},
+ {0x05, 0x0013}, {0x09, 0x10f2}, {0x07, 0x0003},
+ {0x2b, 0x002a}, {0x2d, 0x002a}, {0x2c, 0x002a},
+ {0x2e, 0x0029}, {0x07, 0x0002},
+};
+
+static u16 mt9m111_init[][2] = {
+ {0xf0, 0x0000}, {0x0d, 0x0008}, {0x0d, 0x0009},
+ {0x0d, 0x0008}, {0xf0, 0x0001}, {0x3a, 0x4300},
+ {0x9b, 0x4300}, {0xa1, 0x0280}, {0xa4, 0x0200},
+ {0x06, 0x308e}, {0xf0, 0x0000},
+};
+
+static u8 hv7131r_init[][2] = {
+ {0x02, 0x08}, {0x02, 0x00}, {0x01, 0x08},
+ {0x02, 0x00}, {0x20, 0x00}, {0x21, 0xd0},
+ {0x22, 0x00}, {0x23, 0x09}, {0x01, 0x08},
+ {0x01, 0x08}, {0x01, 0x08}, {0x25, 0x07},
+ {0x26, 0xc3}, {0x27, 0x50}, {0x30, 0x62},
+ {0x31, 0x10}, {0x32, 0x06}, {0x33, 0x10},
+ {0x20, 0x00}, {0x21, 0xd0}, {0x22, 0x00},
+ {0x23, 0x09}, {0x01, 0x08},
+};
+
+int reg_r(struct gspca_dev *gspca_dev, u16 reg, u16 length)
+{
+ struct usb_device *dev = gspca_dev->dev;
+ int result;
+ result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+ 0x00,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+ reg,
+ 0x00,
+ gspca_dev->usb_buf,
+ length,
+ 500);
+ if (unlikely(result < 0 || result != length)) {
+ err("Read register failed 0x%02X", reg);
+ return -EIO;
+ }
+ return 0;
+}
+
+int reg_w(struct gspca_dev *gspca_dev, u16 reg, const u8 *buffer, int length)
+{
+ struct usb_device *dev = gspca_dev->dev;
+ int result;
+ memcpy(gspca_dev->usb_buf, buffer, length);
+ result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ 0x08,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+ reg,
+ 0x00,
+ gspca_dev->usb_buf,
+ length,
+ 500);
+ if (unlikely(result < 0 || result != length)) {
+ err("Write register failed index 0x%02X", reg);
+ return -EIO;
+ }
+ return 0;
+}
+
+int reg_w1(struct gspca_dev *gspca_dev, u16 reg, const u8 value)
+{
+ u8 data[1] = {value};
+ return reg_w(gspca_dev, reg, data, 1);
+}
+
+int i2c_w(struct gspca_dev *gspca_dev, const u8 *buffer)
+{
+ int i;
+ reg_w(gspca_dev, 0x10c0, buffer, 8);
+ for (i = 0; i < 5; i++) {
+ reg_r(gspca_dev, 0x10c0, 1);
+ if (gspca_dev->usb_buf[0] & 0x04) {
+ if (gspca_dev->usb_buf[0] & 0x08)
+ return -1;
+ return 0;
+ }
+ msleep(1);
+ }
+ return -1;
+}
+
+int i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ u8 row[8];
+
+ /*
+ * from the point of view of the bridge, the length
+ * includes the address
+ */
+ row[0] = 0x81 | (2 << 4);
+ row[1] = sd->i2c_addr;
+ row[2] = reg;
+ row[3] = val;
+ row[4] = 0x00;
+ row[5] = 0x00;
+ row[6] = 0x00;
+ row[7] = 0x10;
+
+ return i2c_w(gspca_dev, row);
+}
+
+int i2c_w2(struct gspca_dev *gspca_dev, u8 reg, u16 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 row[8];
+
+ /*
+ * from the point of view of the bridge, the length
+ * includes the address
+ */
+ row[0] = 0x81 | (3 << 4);
+ row[1] = sd->i2c_addr;
+ row[2] = reg;
+ row[3] = (val >> 8) & 0xff;
+ row[4] = val & 0xff;
+ row[5] = 0x00;
+ row[6] = 0x00;
+ row[7] = 0x10;
+
+ return i2c_w(gspca_dev, row);
+}
+
+int i2c_r1(struct gspca_dev *gspca_dev, u8 reg, u8 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 row[8];
+
+ row[0] = 0x81 | 0x10;
+ row[1] = sd->i2c_addr;
+ row[2] = reg;
+ row[3] = 0;
+ row[4] = 0;
+ row[5] = 0;
+ row[6] = 0;
+ row[7] = 0x10;
+ reg_w(gspca_dev, 0x10c0, row, 8);
+ msleep(1);
+ row[0] = 0x81 | (2 << 4) | 0x02;
+ row[2] = 0;
+ reg_w(gspca_dev, 0x10c0, row, 8);
+ msleep(1);
+ reg_r(gspca_dev, 0x10c2, 5);
+ *val = gspca_dev->usb_buf[3];
+ return 0;
+}
+
+int i2c_r2(struct gspca_dev *gspca_dev, u8 reg, u16 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 row[8];
+
+ row[0] = 0x81 | 0x10;
+ row[1] = sd->i2c_addr;
+ row[2] = reg;
+ row[3] = 0;
+ row[4] = 0;
+ row[5] = 0;
+ row[6] = 0;
+ row[7] = 0x10;
+ reg_w(gspca_dev, 0x10c0, row, 8);
+ msleep(1);
+ row[0] = 0x81 | (3 << 4) | 0x02;
+ row[2] = 0;
+ reg_w(gspca_dev, 0x10c0, row, 8);
+ msleep(1);
+ reg_r(gspca_dev, 0x10c2, 5);
+ *val = (gspca_dev->usb_buf[2] << 8) | gspca_dev->usb_buf[3];
+ return 0;
+}
+
+static int ov9650_init_sensor(struct gspca_dev *gspca_dev)
+{
+ int i;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ for (i = 0; i < ARRAY_SIZE(ov9650_init); i++) {
+ if (i2c_w1(gspca_dev, ov9650_init[i][0],
+ ov9650_init[i][1]) < 0) {
+ err("OV9650 sensor initialization failed");
+ return -ENODEV;
+ }
+ }
+ sd->hstart = 1;
+ sd->vstart = 7;
+ return 0;
+}
+
+static int ov9655_init_sensor(struct gspca_dev *gspca_dev)
+{
+ int i;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ for (i = 0; i < ARRAY_SIZE(ov9655_init); i++) {
+ if (i2c_w1(gspca_dev, ov9655_init[i][0],
+ ov9655_init[i][1]) < 0) {
+ err("OV9655 sensor initialization failed");
+ return -ENODEV;
+ }
+ }
+ /* disable hflip and vflip */
+ gspca_dev->ctrl_dis = (1 << HFLIP_IDX) | (1 << VFLIP_IDX);
+ sd->hstart = 0;
+ sd->vstart = 7;
+ return 0;
+}
+
+static int soi968_init_sensor(struct gspca_dev *gspca_dev)
+{
+ int i;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ for (i = 0; i < ARRAY_SIZE(soi968_init); i++) {
+ if (i2c_w1(gspca_dev, soi968_init[i][0],
+ soi968_init[i][1]) < 0) {
+ err("SOI968 sensor initialization failed");
+ return -ENODEV;
+ }
+ }
+ /* disable hflip and vflip */
+ gspca_dev->ctrl_dis = (1 << HFLIP_IDX) | (1 << VFLIP_IDX);
+ sd->hstart = 60;
+ sd->vstart = 11;
+ return 0;
+}
+
+static int ov7660_init_sensor(struct gspca_dev *gspca_dev)
+{
+ int i;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ for (i = 0; i < ARRAY_SIZE(ov7660_init); i++) {
+ if (i2c_w1(gspca_dev, ov7660_init[i][0],
+ ov7660_init[i][1]) < 0) {
+ err("OV7660 sensor initialization failed");
+ return -ENODEV;
+ }
+ }
+ /* disable hflip and vflip */
+ gspca_dev->ctrl_dis = (1 << HFLIP_IDX) | (1 << VFLIP_IDX);
+ sd->hstart = 1;
+ sd->vstart = 1;
+ return 0;
+}
+
+static int ov7670_init_sensor(struct gspca_dev *gspca_dev)
+{
+ int i;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ for (i = 0; i < ARRAY_SIZE(ov7670_init); i++) {
+ if (i2c_w1(gspca_dev, ov7670_init[i][0],
+ ov7670_init[i][1]) < 0) {
+ err("OV7670 sensor initialization failed");
+ return -ENODEV;
+ }
+ }
+ /* disable hflip and vflip */
+ gspca_dev->ctrl_dis = (1 << HFLIP_IDX) | (1 << VFLIP_IDX);
+ sd->hstart = 0;
+ sd->vstart = 1;
+ return 0;
+}
+
+static int mt9v_init_sensor(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int i;
+ u16 value;
+ int ret;
+
+ sd->i2c_addr = 0x5d;
+ ret = i2c_r2(gspca_dev, 0xff, &value);
+ if ((ret == 0) && (value == 0x8243)) {
+ for (i = 0; i < ARRAY_SIZE(mt9v011_init); i++) {
+ if (i2c_w2(gspca_dev, mt9v011_init[i][0],
+ mt9v011_init[i][1]) < 0) {
+ err("MT9V011 sensor initialization failed");
+ return -ENODEV;
+ }
+ }
+ sd->hstart = 2;
+ sd->vstart = 2;
+ sd->sensor = SENSOR_MT9V011;
+ info("MT9V011 sensor detected");
+ return 0;
+ }
+
+ sd->i2c_addr = 0x5c;
+ i2c_w2(gspca_dev, 0x01, 0x0004);
+ ret = i2c_r2(gspca_dev, 0xff, &value);
+ if ((ret == 0) && (value == 0x823a)) {
+ for (i = 0; i < ARRAY_SIZE(mt9v111_init); i++) {
+ if (i2c_w2(gspca_dev, mt9v111_init[i][0],
+ mt9v111_init[i][1]) < 0) {
+ err("MT9V111 sensor initialization failed");
+ return -ENODEV;
+ }
+ }
+ sd->hstart = 2;
+ sd->vstart = 2;
+ sd->sensor = SENSOR_MT9V111;
+ info("MT9V111 sensor detected");
+ return 0;
+ }
+
+ sd->i2c_addr = 0x5d;
+ ret = i2c_w2(gspca_dev, 0xf0, 0x0000);
+ if (ret < 0) {
+ sd->i2c_addr = 0x48;
+ i2c_w2(gspca_dev, 0xf0, 0x0000);
+ }
+ ret = i2c_r2(gspca_dev, 0x00, &value);
+ if ((ret == 0) && (value == 0x1229)) {
+ for (i = 0; i < ARRAY_SIZE(mt9v112_init); i++) {
+ if (i2c_w2(gspca_dev, mt9v112_init[i][0],
+ mt9v112_init[i][1]) < 0) {
+ err("MT9V112 sensor initialization failed");
+ return -ENODEV;
+ }
+ }
+ sd->hstart = 6;
+ sd->vstart = 2;
+ sd->sensor = SENSOR_MT9V112;
+ info("MT9V112 sensor detected");
+ return 0;
+ }
+
+ return -ENODEV;
+}
+
+static int mt9m111_init_sensor(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int i;
+ for (i = 0; i < ARRAY_SIZE(mt9m111_init); i++) {
+ if (i2c_w2(gspca_dev, mt9m111_init[i][0],
+ mt9m111_init[i][1]) < 0) {
+ err("MT9M111 sensor initialization failed");
+ return -ENODEV;
+ }
+ }
+ sd->hstart = 0;
+ sd->vstart = 2;
+ return 0;
+}
+
+static int mt9m001_init_sensor(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int i;
+ for (i = 0; i < ARRAY_SIZE(mt9m001_init); i++) {
+ if (i2c_w2(gspca_dev, mt9m001_init[i][0],
+ mt9m001_init[i][1]) < 0) {
+ err("MT9M001 sensor initialization failed");
+ return -ENODEV;
+ }
+ }
+ /* disable hflip and vflip */
+ gspca_dev->ctrl_dis = (1 << HFLIP_IDX) | (1 << VFLIP_IDX);
+ sd->hstart = 2;
+ sd->vstart = 2;
+ return 0;
+}
+
+static int hv7131r_init_sensor(struct gspca_dev *gspca_dev)
+{
+ int i;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ for (i = 0; i < ARRAY_SIZE(hv7131r_init); i++) {
+ if (i2c_w1(gspca_dev, hv7131r_init[i][0],
+ hv7131r_init[i][1]) < 0) {
+ err("HV7131R Sensor initialization failed");
+ return -ENODEV;
+ }
+ }
+ sd->hstart = 0;
+ sd->vstart = 1;
+ return 0;
+}
+
+#ifdef CONFIG_USB_GSPCA_SN9C20X_EVDEV
+static int input_kthread(void *data)
+{
+ struct gspca_dev *gspca_dev = (struct gspca_dev *)data;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ DECLARE_WAIT_QUEUE_HEAD(wait);
+ set_freezable();
+ for (;;) {
+ if (kthread_should_stop())
+ break;
+
+ if (reg_r(gspca_dev, 0x1005, 1) < 0)
+ continue;
+
+ input_report_key(sd->input_dev,
+ KEY_CAMERA,
+ gspca_dev->usb_buf[0] & sd->input_gpio);
+ input_sync(sd->input_dev);
+
+ wait_event_freezable_timeout(wait,
+ kthread_should_stop(),
+ msecs_to_jiffies(100));
+ }
+ return 0;
+}
+
+
+static int sn9c20x_input_init(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ if (sd->input_gpio == 0)
+ return 0;
+
+ sd->input_dev = input_allocate_device();
+ if (!sd->input_dev)
+ return -ENOMEM;
+
+ sd->input_dev->name = "SN9C20X Webcam";
+
+ sd->input_dev->phys = kasprintf(GFP_KERNEL, "usb-%s-%s",
+ gspca_dev->dev->bus->bus_name,
+ gspca_dev->dev->devpath);
+
+ if (!sd->input_dev->phys)
+ return -ENOMEM;
+
+ usb_to_input_id(gspca_dev->dev, &sd->input_dev->id);
+ sd->input_dev->dev.parent = &gspca_dev->dev->dev;
+
+ set_bit(EV_KEY, sd->input_dev->evbit);
+ set_bit(KEY_CAMERA, sd->input_dev->keybit);
+
+ if (input_register_device(sd->input_dev))
+ return -EINVAL;
+
+ sd->input_task = kthread_run(input_kthread, gspca_dev, "sn9c20x/%d",
+ gspca_dev->vdev.minor);
+
+ if (IS_ERR(sd->input_task))
+ return -EINVAL;
+
+ return 0;
+}
+
+static void sn9c20x_input_cleanup(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ if (sd->input_task != NULL && !IS_ERR(sd->input_task))
+ kthread_stop(sd->input_task);
+
+ if (sd->input_dev != NULL) {
+ input_unregister_device(sd->input_dev);
+ kfree(sd->input_dev->phys);
+ input_free_device(sd->input_dev);
+ sd->input_dev = NULL;
+ }
+}
+#endif
+
+static int set_cmatrix(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 hue_coord, hue_index = 180 + sd->hue;
+ u8 cmatrix[21];
+ memset(cmatrix, 0, 21);
+
+ cmatrix[2] = (sd->contrast * 0x25 / 0x100) + 0x26;
+ cmatrix[0] = 0x13 + (cmatrix[2] - 0x26) * 0x13 / 0x25;
+ cmatrix[4] = 0x07 + (cmatrix[2] - 0x26) * 0x07 / 0x25;
+ cmatrix[18] = sd->brightness - 0x80;
+
+ hue_coord = (hsv_red_x[hue_index] * sd->saturation) >> 8;
+ cmatrix[6] = (unsigned char)(hue_coord & 0xff);
+ cmatrix[7] = (unsigned char)((hue_coord >> 8) & 0x0f);
+
+ hue_coord = (hsv_red_y[hue_index] * sd->saturation) >> 8;
+ cmatrix[8] = (unsigned char)(hue_coord & 0xff);
+ cmatrix[9] = (unsigned char)((hue_coord >> 8) & 0x0f);
+
+ hue_coord = (hsv_green_x[hue_index] * sd->saturation) >> 8;
+ cmatrix[10] = (unsigned char)(hue_coord & 0xff);
+ cmatrix[11] = (unsigned char)((hue_coord >> 8) & 0x0f);
+
+ hue_coord = (hsv_green_y[hue_index] * sd->saturation) >> 8;
+ cmatrix[12] = (unsigned char)(hue_coord & 0xff);
+ cmatrix[13] = (unsigned char)((hue_coord >> 8) & 0x0f);
+
+ hue_coord = (hsv_blue_x[hue_index] * sd->saturation) >> 8;
+ cmatrix[14] = (unsigned char)(hue_coord & 0xff);
+ cmatrix[15] = (unsigned char)((hue_coord >> 8) & 0x0f);
+
+ hue_coord = (hsv_blue_y[hue_index] * sd->saturation) >> 8;
+ cmatrix[16] = (unsigned char)(hue_coord & 0xff);
+ cmatrix[17] = (unsigned char)((hue_coord >> 8) & 0x0f);
+
+ return reg_w(gspca_dev, 0x10e1, cmatrix, 21);
+}
+
+static int set_gamma(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 gamma[17];
+ u8 gval = sd->gamma * 0xb8 / 0x100;
+
+
+ gamma[0] = 0x0a;
+ gamma[1] = 0x13 + (gval * (0xcb - 0x13) / 0xb8);
+ gamma[2] = 0x25 + (gval * (0xee - 0x25) / 0xb8);
+ gamma[3] = 0x37 + (gval * (0xfa - 0x37) / 0xb8);
+ gamma[4] = 0x45 + (gval * (0xfc - 0x45) / 0xb8);
+ gamma[5] = 0x55 + (gval * (0xfb - 0x55) / 0xb8);
+ gamma[6] = 0x65 + (gval * (0xfc - 0x65) / 0xb8);
+ gamma[7] = 0x74 + (gval * (0xfd - 0x74) / 0xb8);
+ gamma[8] = 0x83 + (gval * (0xfe - 0x83) / 0xb8);
+ gamma[9] = 0x92 + (gval * (0xfc - 0x92) / 0xb8);
+ gamma[10] = 0xa1 + (gval * (0xfc - 0xa1) / 0xb8);
+ gamma[11] = 0xb0 + (gval * (0xfc - 0xb0) / 0xb8);
+ gamma[12] = 0xbf + (gval * (0xfb - 0xbf) / 0xb8);
+ gamma[13] = 0xce + (gval * (0xfb - 0xce) / 0xb8);
+ gamma[14] = 0xdf + (gval * (0xfd - 0xdf) / 0xb8);
+ gamma[15] = 0xea + (gval * (0xf9 - 0xea) / 0xb8);
+ gamma[16] = 0xf5;
+
+ return reg_w(gspca_dev, 0x1190, gamma, 17);
+}
+
+static int set_redblue(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ reg_w1(gspca_dev, 0x118c, sd->red);
+ reg_w1(gspca_dev, 0x118f, sd->blue);
+ return 0;
+}
+
+static int set_hvflip(struct gspca_dev *gspca_dev)
+{
+ u8 value, tslb;
+ u16 value2;
+ struct sd *sd = (struct sd *) gspca_dev;
+ switch (sd->sensor) {
+ case SENSOR_OV9650:
+ i2c_r1(gspca_dev, 0x1e, &value);
+ value &= ~0x30;
+ tslb = 0x01;
+ if (sd->hflip)
+ value |= 0x20;
+ if (sd->vflip) {
+ value |= 0x10;
+ tslb = 0x49;
+ }
+ i2c_w1(gspca_dev, 0x1e, value);
+ i2c_w1(gspca_dev, 0x3a, tslb);
+ break;
+ case SENSOR_MT9V111:
+ case SENSOR_MT9V011:
+ i2c_r2(gspca_dev, 0x20, &value2);
+ value2 &= ~0xc0a0;
+ if (sd->hflip)
+ value2 |= 0x8080;
+ if (sd->vflip)
+ value2 |= 0x4020;
+ i2c_w2(gspca_dev, 0x20, value2);
+ break;
+ case SENSOR_MT9M111:
+ case SENSOR_MT9V112:
+ i2c_r2(gspca_dev, 0x20, &value2);
+ value2 &= ~0x0003;
+ if (sd->hflip)
+ value2 |= 0x0002;
+ if (sd->vflip)
+ value2 |= 0x0001;
+ i2c_w2(gspca_dev, 0x20, value2);
+ break;
+ case SENSOR_HV7131R:
+ i2c_r1(gspca_dev, 0x01, &value);
+ value &= ~0x03;
+ if (sd->vflip)
+ value |= 0x01;
+ if (sd->hflip)
+ value |= 0x02;
+ i2c_w1(gspca_dev, 0x01, value);
+ break;
+ }
+ return 0;
+}
+
+static int set_exposure(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 exp[8] = {0x81, sd->i2c_addr, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e};
+ switch (sd->sensor) {
+ case SENSOR_OV7660:
+ case SENSOR_OV7670:
+ case SENSOR_SOI968:
+ case SENSOR_OV9655:
+ case SENSOR_OV9650:
+ exp[0] |= (3 << 4);
+ exp[2] = 0x2d;
+ exp[3] = sd->exposure & 0xff;
+ exp[4] = sd->exposure >> 8;
+ break;
+ case SENSOR_MT9M001:
+ case SENSOR_MT9M111:
+ case SENSOR_MT9V112:
+ case SENSOR_MT9V111:
+ case SENSOR_MT9V011:
+ exp[0] |= (3 << 4);
+ exp[2] = 0x09;
+ exp[3] = sd->exposure >> 8;
+ exp[4] = sd->exposure & 0xff;
+ break;
+ case SENSOR_HV7131R:
+ exp[0] |= (4 << 4);
+ exp[2] = 0x25;
+ exp[3] = ((sd->exposure * 0xffffff) / 0xffff) >> 16;
+ exp[4] = ((sd->exposure * 0xffffff) / 0xffff) >> 8;
+ exp[5] = ((sd->exposure * 0xffffff) / 0xffff) & 0xff;
+ break;
+ }
+ i2c_w(gspca_dev, exp);
+ return 0;
+}
+
+static int set_gain(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 gain[8] = {0x81, sd->i2c_addr, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d};
+ switch (sd->sensor) {
+ case SENSOR_OV7660:
+ case SENSOR_OV7670:
+ case SENSOR_SOI968:
+ case SENSOR_OV9655:
+ case SENSOR_OV9650:
+ gain[0] |= (2 << 4);
+ gain[3] = ov_gain[sd->gain];
+ break;
+ case SENSOR_MT9V011:
+ case SENSOR_MT9V111:
+ gain[0] |= (3 << 4);
+ gain[2] = 0x35;
+ gain[3] = micron1_gain[sd->gain] >> 8;
+ gain[4] = micron1_gain[sd->gain] & 0xff;
+ break;
+ case SENSOR_MT9V112:
+ case SENSOR_MT9M111:
+ gain[0] |= (3 << 4);
+ gain[2] = 0x2f;
+ gain[3] = micron1_gain[sd->gain] >> 8;
+ gain[4] = micron1_gain[sd->gain] & 0xff;
+ break;
+ case SENSOR_MT9M001:
+ gain[0] |= (3 << 4);
+ gain[2] = 0x2f;
+ gain[3] = micron2_gain[sd->gain] >> 8;
+ gain[4] = micron2_gain[sd->gain] & 0xff;
+ break;
+ case SENSOR_HV7131R:
+ gain[0] |= (2 << 4);
+ gain[2] = 0x30;
+ gain[3] = hv7131r_gain[sd->gain];
+ break;
+ }
+ i2c_w(gspca_dev, gain);
+ return 0;
+}
+
+static int sd_setbrightness(struct gspca_dev *gspca_dev, s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->brightness = val;
+ if (gspca_dev->streaming)
+ return set_cmatrix(gspca_dev);
+ return 0;
+}
+
+static int sd_getbrightness(struct gspca_dev *gspca_dev, s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ *val = sd->brightness;
+ return 0;
+}
+
+
+static int sd_setcontrast(struct gspca_dev *gspca_dev, s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->contrast = val;
+ if (gspca_dev->streaming)
+ return set_cmatrix(gspca_dev);
+ return 0;
+}
+
+static int sd_getcontrast(struct gspca_dev *gspca_dev, s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ *val = sd->contrast;
+ return 0;
+}
+
+static int sd_setsaturation(struct gspca_dev *gspca_dev, s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->saturation = val;
+ if (gspca_dev->streaming)
+ return set_cmatrix(gspca_dev);
+ return 0;
+}
+
+static int sd_getsaturation(struct gspca_dev *gspca_dev, s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ *val = sd->saturation;
+ return 0;
+}
+
+static int sd_sethue(struct gspca_dev *gspca_dev, s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->hue = val;
+ if (gspca_dev->streaming)
+ return set_cmatrix(gspca_dev);
+ return 0;
+}
+
+static int sd_gethue(struct gspca_dev *gspca_dev, s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ *val = sd->hue;
+ return 0;
+}
+
+static int sd_setgamma(struct gspca_dev *gspca_dev, s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->gamma = val;
+ if (gspca_dev->streaming)
+ return set_gamma(gspca_dev);
+ return 0;
+}
+
+static int sd_getgamma(struct gspca_dev *gspca_dev, s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ *val = sd->gamma;
+ return 0;
+}
+
+static int sd_setredbalance(struct gspca_dev *gspca_dev, s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->red = val;
+ if (gspca_dev->streaming)
+ return set_redblue(gspca_dev);
+ return 0;
+}
+
+static int sd_getredbalance(struct gspca_dev *gspca_dev, s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ *val = sd->red;
+ return 0;
+}
+
+static int sd_setbluebalance(struct gspca_dev *gspca_dev, s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->blue = val;
+ if (gspca_dev->streaming)
+ return set_redblue(gspca_dev);
+ return 0;
+}
+
+static int sd_getbluebalance(struct gspca_dev *gspca_dev, s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ *val = sd->blue;
+ return 0;
+}
+
+static int sd_sethflip(struct gspca_dev *gspca_dev, s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->hflip = val;
+ if (gspca_dev->streaming)
+ return set_hvflip(gspca_dev);
+ return 0;
+}
+
+static int sd_gethflip(struct gspca_dev *gspca_dev, s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ *val = sd->hflip;
+ return 0;
+}
+
+static int sd_setvflip(struct gspca_dev *gspca_dev, s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->vflip = val;
+ if (gspca_dev->streaming)
+ return set_hvflip(gspca_dev);
+ return 0;
+}
+
+static int sd_getvflip(struct gspca_dev *gspca_dev, s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ *val = sd->vflip;
+ return 0;
+}
+
+static int sd_setexposure(struct gspca_dev *gspca_dev, s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->exposure = val;
+ if (gspca_dev->streaming)
+ return set_exposure(gspca_dev);
+ return 0;
+}
+
+static int sd_getexposure(struct gspca_dev *gspca_dev, s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ *val = sd->exposure;
+ return 0;
+}
+
+static int sd_setgain(struct gspca_dev *gspca_dev, s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->gain = val;
+ if (gspca_dev->streaming)
+ return set_gain(gspca_dev);
+ return 0;
+}
+
+static int sd_getgain(struct gspca_dev *gspca_dev, s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ *val = sd->gain;
+ return 0;
+}
+
+static int sd_setautoexposure(struct gspca_dev *gspca_dev, s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ sd->auto_exposure = val;
+ return 0;
+}
+
+static int sd_getautoexposure(struct gspca_dev *gspca_dev, s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ *val = sd->auto_exposure;
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int sd_dbg_g_register(struct gspca_dev *gspca_dev,
+ struct v4l2_dbg_register *reg)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ switch (reg->match.type) {
+ case V4L2_CHIP_MATCH_HOST:
+ if (reg->match.addr != 0)
+ return -EINVAL;
+ if (reg->reg < 0x1000 || reg->reg > 0x11ff)
+ return -EINVAL;
+ if (reg_r(gspca_dev, reg->reg, 1) < 0)
+ return -EINVAL;
+ reg->val = gspca_dev->usb_buf[0];
+ return 0;
+ case V4L2_CHIP_MATCH_I2C_ADDR:
+ if (reg->match.addr != sd->i2c_addr)
+ return -EINVAL;
+ if (sd->sensor >= SENSOR_MT9V011 &&
+ sd->sensor <= SENSOR_MT9M111) {
+ if (i2c_r2(gspca_dev, reg->reg, (u16 *)&reg->val) < 0)
+ return -EINVAL;
+ } else {
+ if (i2c_r1(gspca_dev, reg->reg, (u8 *)&reg->val) < 0)
+ return -EINVAL;
+ }
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int sd_dbg_s_register(struct gspca_dev *gspca_dev,
+ struct v4l2_dbg_register *reg)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ switch (reg->match.type) {
+ case V4L2_CHIP_MATCH_HOST:
+ if (reg->match.addr != 0)
+ return -EINVAL;
+ if (reg->reg < 0x1000 || reg->reg > 0x11ff)
+ return -EINVAL;
+ if (reg_w1(gspca_dev, reg->reg, reg->val) < 0)
+ return -EINVAL;
+ return 0;
+ case V4L2_CHIP_MATCH_I2C_ADDR:
+ if (reg->match.addr != sd->i2c_addr)
+ return -EINVAL;
+ if (sd->sensor >= SENSOR_MT9V011 &&
+ sd->sensor <= SENSOR_MT9M111) {
+ if (i2c_w2(gspca_dev, reg->reg, reg->val) < 0)
+ return -EINVAL;
+ } else {
+ if (i2c_w1(gspca_dev, reg->reg, reg->val) < 0)
+ return -EINVAL;
+ }
+ return 0;
+ }
+ return -EINVAL;
+}
+#endif
+
+static int sd_chip_ident(struct gspca_dev *gspca_dev,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ switch (chip->match.type) {
+ case V4L2_CHIP_MATCH_HOST:
+ if (chip->match.addr != 0)
+ return -EINVAL;
+ chip->revision = 0;
+ chip->ident = V4L2_IDENT_SN9C20X;
+ return 0;
+ case V4L2_CHIP_MATCH_I2C_ADDR:
+ if (chip->match.addr != sd->i2c_addr)
+ return -EINVAL;
+ chip->revision = 0;
+ chip->ident = i2c_ident[sd->sensor];
+ return 0;
+ }
+ return -EINVAL;
+}
+
+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->sensor = (id->driver_info >> 8) & 0xff;
+ sd->i2c_addr = id->driver_info & 0xff;
+
+ switch (sd->sensor) {
+ case SENSOR_OV9650:
+ cam->cam_mode = sxga_mode;
+ cam->nmodes = ARRAY_SIZE(sxga_mode);
+ break;
+ default:
+ cam->cam_mode = vga_mode;
+ cam->nmodes = ARRAY_SIZE(vga_mode);
+ }
+
+ sd->old_step = 0;
+ sd->older_step = 0;
+ sd->exposure_step = 16;
+
+ sd->brightness = BRIGHTNESS_DEFAULT;
+ sd->contrast = CONTRAST_DEFAULT;
+ sd->saturation = SATURATION_DEFAULT;
+ sd->hue = HUE_DEFAULT;
+ sd->gamma = GAMMA_DEFAULT;
+ sd->red = RED_DEFAULT;
+ sd->blue = BLUE_DEFAULT;
+
+ sd->hflip = HFLIP_DEFAULT;
+ sd->vflip = VFLIP_DEFAULT;
+ sd->exposure = EXPOSURE_DEFAULT;
+ sd->gain = GAIN_DEFAULT;
+ sd->auto_exposure = AUTO_EXPOSURE_DEFAULT;
+
+ sd->quality = 95;
+
+#ifdef CONFIG_USB_GSPCA_SN9C20X_EVDEV
+ sd->input_gpio = (id->driver_info >> 16) & 0xff;
+ if (sn9c20x_input_init(gspca_dev) < 0)
+ return -ENODEV;
+#endif
+ return 0;
+}
+
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int i;
+ u8 value;
+ u8 i2c_init[9] =
+ {0x80, sd->i2c_addr, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03};
+
+ for (i = 0; i < ARRAY_SIZE(bridge_init); i++) {
+ value = bridge_init[i][1];
+ if (reg_w(gspca_dev, bridge_init[i][0], &value, 1) < 0) {
+ err("Device initialization failed");
+ return -ENODEV;
+ }
+ }
+
+ if (reg_w(gspca_dev, 0x10c0, i2c_init, 9) < 0) {
+ err("Device initialization failed");
+ return -ENODEV;
+ }
+
+ switch (sd->sensor) {
+ case SENSOR_OV9650:
+ if (ov9650_init_sensor(gspca_dev) < 0)
+ return -ENODEV;
+ info("OV9650 sensor detected");
+ break;
+ case SENSOR_OV9655:
+ if (ov9655_init_sensor(gspca_dev) < 0)
+ return -ENODEV;
+ info("OV9655 sensor detected");
+ break;
+ case SENSOR_SOI968:
+ if (soi968_init_sensor(gspca_dev) < 0)
+ return -ENODEV;
+ info("SOI968 sensor detected");
+ break;
+ case SENSOR_OV7660:
+ if (ov7660_init_sensor(gspca_dev) < 0)
+ return -ENODEV;
+ info("OV7660 sensor detected");
+ break;
+ case SENSOR_OV7670:
+ if (ov7670_init_sensor(gspca_dev) < 0)
+ return -ENODEV;
+ info("OV7670 sensor detected");
+ break;
+ case SENSOR_MT9VPRB:
+ if (mt9v_init_sensor(gspca_dev) < 0)
+ return -ENODEV;
+ break;
+ case SENSOR_MT9M111:
+ if (mt9m111_init_sensor(gspca_dev) < 0)
+ return -ENODEV;
+ info("MT9M111 sensor detected");
+ break;
+ case SENSOR_MT9M001:
+ if (mt9m001_init_sensor(gspca_dev) < 0)
+ return -ENODEV;
+ info("MT9M001 sensor detected");
+ break;
+ case SENSOR_HV7131R:
+ if (hv7131r_init_sensor(gspca_dev) < 0)
+ return -ENODEV;
+ info("HV7131R sensor detected");
+ break;
+ default:
+ info("Unsupported Sensor");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static void configure_sensor_output(struct gspca_dev *gspca_dev, int mode)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 value;
+ switch (sd->sensor) {
+ case SENSOR_OV9650:
+ if (mode & MODE_SXGA) {
+ i2c_w1(gspca_dev, 0x17, 0x1b);
+ i2c_w1(gspca_dev, 0x18, 0xbc);
+ i2c_w1(gspca_dev, 0x19, 0x01);
+ i2c_w1(gspca_dev, 0x1a, 0x82);
+ i2c_r1(gspca_dev, 0x12, &value);
+ i2c_w1(gspca_dev, 0x12, value & 0x07);
+ } else {
+ i2c_w1(gspca_dev, 0x17, 0x24);
+ i2c_w1(gspca_dev, 0x18, 0xc5);
+ i2c_w1(gspca_dev, 0x19, 0x00);
+ i2c_w1(gspca_dev, 0x1a, 0x3c);
+ i2c_r1(gspca_dev, 0x12, &value);
+ i2c_w1(gspca_dev, 0x12, (value & 0x7) | 0x40);
+ }
+ break;
+ }
+}
+
+#define HW_WIN(mode, hstart, vstart) \
+((const u8 []){hstart & 0xff, hstart >> 8, \
+vstart & 0xff, vstart >> 8, \
+(mode & MODE_SXGA ? 1280 >> 4 : 640 >> 4), \
+(mode & MODE_SXGA ? 1024 >> 3 : 480 >> 3)})
+
+#define CLR_WIN(width, height) \
+((const u8 [])\
+{0, width >> 2, 0, height >> 1,\
+((width >> 10) & 0x01) | ((height >> 8) & 0x6)})
+
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv;
+ int width = gspca_dev->width;
+ int height = gspca_dev->height;
+ u8 fmt, scale = 0;
+
+ sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL);
+ if (sd->jpeg_hdr == NULL)
+ return -ENOMEM;
+
+ jpeg_define(sd->jpeg_hdr, height, width,
+ 0x21);
+ jpeg_set_qual(sd->jpeg_hdr, sd->quality);
+
+ if (mode & MODE_RAW)
+ fmt = 0x2d;
+ else if (mode & MODE_JPEG)
+ fmt = 0x2c;
+ else
+ fmt = 0x2f;
+
+ switch (mode & 0x0f) {
+ case 3:
+ scale = 0xc0;
+ info("Set 1280x1024");
+ break;
+ case 2:
+ scale = 0x80;
+ info("Set 640x480");
+ break;
+ case 1:
+ scale = 0x90;
+ info("Set 320x240");
+ break;
+ case 0:
+ scale = 0xa0;
+ info("Set 160x120");
+ break;
+ }
+
+ configure_sensor_output(gspca_dev, mode);
+ reg_w(gspca_dev, 0x1100, sd->jpeg_hdr + JPEG_QT0_OFFSET, 64);
+ reg_w(gspca_dev, 0x1140, sd->jpeg_hdr + JPEG_QT1_OFFSET, 64);
+ reg_w(gspca_dev, 0x10fb, CLR_WIN(width, height), 5);
+ reg_w(gspca_dev, 0x1180, HW_WIN(mode, sd->hstart, sd->vstart), 6);
+ reg_w1(gspca_dev, 0x1189, scale);
+ reg_w1(gspca_dev, 0x10e0, fmt);
+
+ set_cmatrix(gspca_dev);
+ set_gamma(gspca_dev);
+ set_redblue(gspca_dev);
+ set_gain(gspca_dev);
+ set_exposure(gspca_dev);
+ set_hvflip(gspca_dev);
+
+ reg_r(gspca_dev, 0x1061, 1);
+ reg_w1(gspca_dev, 0x1061, gspca_dev->usb_buf[0] | 0x02);
+ return 0;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+ reg_r(gspca_dev, 0x1061, 1);
+ reg_w1(gspca_dev, 0x1061, gspca_dev->usb_buf[0] & ~0x02);
+}
+
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ kfree(sd->jpeg_hdr);
+}
+
+static void do_autoexposure(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int avg_lum, new_exp;
+
+ if (!sd->auto_exposure)
+ return;
+
+ avg_lum = atomic_read(&sd->avg_lum);
+
+ /*
+ * some hardcoded values are present
+ * like those for maximal/minimal exposure
+ * and exposure steps
+ */
+ if (avg_lum < MIN_AVG_LUM) {
+ if (sd->exposure > 0x1770)
+ return;
+
+ new_exp = sd->exposure + sd->exposure_step;
+ if (new_exp > 0x1770)
+ new_exp = 0x1770;
+ if (new_exp < 0x10)
+ new_exp = 0x10;
+ sd->exposure = new_exp;
+ set_exposure(gspca_dev);
+
+ sd->older_step = sd->old_step;
+ sd->old_step = 1;
+
+ if (sd->old_step ^ sd->older_step)
+ sd->exposure_step /= 2;
+ else
+ sd->exposure_step += 2;
+ }
+ if (avg_lum > MAX_AVG_LUM) {
+ if (sd->exposure < 0x10)
+ return;
+ new_exp = sd->exposure - sd->exposure_step;
+ if (new_exp > 0x1700)
+ new_exp = 0x1770;
+ if (new_exp < 0x10)
+ new_exp = 0x10;
+ sd->exposure = new_exp;
+ set_exposure(gspca_dev);
+ sd->older_step = sd->old_step;
+ sd->old_step = 0;
+
+ if (sd->old_step ^ sd->older_step)
+ sd->exposure_step /= 2;
+ else
+ sd->exposure_step += 2;
+ }
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ struct gspca_frame *frame, /* target */
+ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int avg_lum;
+ static unsigned char frame_header[] =
+ {0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96};
+ if (len == 64 && memcmp(data, frame_header, 6) == 0) {
+ avg_lum = ((data[35] >> 2) & 3) |
+ (data[20] << 2) |
+ (data[19] << 10);
+ avg_lum += ((data[35] >> 4) & 3) |
+ (data[22] << 2) |
+ (data[21] << 10);
+ avg_lum += ((data[35] >> 6) & 3) |
+ (data[24] << 2) |
+ (data[23] << 10);
+ avg_lum += (data[36] & 3) |
+ (data[26] << 2) |
+ (data[25] << 10);
+ avg_lum += ((data[36] >> 2) & 3) |
+ (data[28] << 2) |
+ (data[27] << 10);
+ avg_lum += ((data[36] >> 4) & 3) |
+ (data[30] << 2) |
+ (data[29] << 10);
+ avg_lum += ((data[36] >> 6) & 3) |
+ (data[32] << 2) |
+ (data[31] << 10);
+ avg_lum += ((data[44] >> 4) & 3) |
+ (data[34] << 2) |
+ (data[33] << 10);
+ avg_lum >>= 9;
+ atomic_set(&sd->avg_lum, avg_lum);
+ gspca_frame_add(gspca_dev, LAST_PACKET,
+ frame, data, len);
+ return;
+ }
+ if (gspca_dev->last_packet_type == LAST_PACKET) {
+ if (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv
+ & MODE_JPEG) {
+ gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
+ sd->jpeg_hdr, JPEG_HDR_SZ);
+ gspca_frame_add(gspca_dev, INTER_PACKET, frame,
+ data, len);
+ } else {
+ gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
+ data, len);
+ }
+ } else {
+ gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
+ }
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .ctrls = sd_ctrls,
+ .nctrls = ARRAY_SIZE(sd_ctrls),
+ .config = sd_config,
+ .init = sd_init,
+ .start = sd_start,
+ .stopN = sd_stopN,
+ .stop0 = sd_stop0,
+ .pkt_scan = sd_pkt_scan,
+ .dq_callback = do_autoexposure,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .set_register = sd_dbg_s_register,
+ .get_register = sd_dbg_g_register,
+#endif
+ .get_chip_ident = sd_chip_ident,
+};
+
+#define SN9C20X(sensor, i2c_addr, button_mask) \
+ .driver_info = (button_mask << 16) \
+ | (SENSOR_ ## sensor << 8) \
+ | (i2c_addr)
+
+static const __devinitdata struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x0c45, 0x6240), SN9C20X(MT9M001, 0x5d, 0)},
+ {USB_DEVICE(0x0c45, 0x6242), SN9C20X(MT9M111, 0x5d, 0)},
+ {USB_DEVICE(0x0c45, 0x6248), SN9C20X(OV9655, 0x30, 0)},
+ {USB_DEVICE(0x0c45, 0x624e), SN9C20X(SOI968, 0x30, 0x10)},
+ {USB_DEVICE(0x0c45, 0x624f), SN9C20X(OV9650, 0x30, 0)},
+ {USB_DEVICE(0x0c45, 0x6251), SN9C20X(OV9650, 0x30, 0)},
+ {USB_DEVICE(0x0c45, 0x6253), SN9C20X(OV9650, 0x30, 0)},
+ {USB_DEVICE(0x0c45, 0x6260), SN9C20X(OV7670, 0x21, 0)},
+ {USB_DEVICE(0x0c45, 0x6270), SN9C20X(MT9VPRB, 0x00, 0)},
+ {USB_DEVICE(0x0c45, 0x627b), SN9C20X(OV7660, 0x21, 0)},
+ {USB_DEVICE(0x0c45, 0x627c), SN9C20X(HV7131R, 0x11, 0)},
+ {USB_DEVICE(0x0c45, 0x627f), SN9C20X(OV9650, 0x30, 0)},
+ {USB_DEVICE(0x0c45, 0x6280), SN9C20X(MT9M001, 0x5d, 0)},
+ {USB_DEVICE(0x0c45, 0x6282), SN9C20X(MT9M111, 0x5d, 0)},
+ {USB_DEVICE(0x0c45, 0x6288), SN9C20X(OV9655, 0x30, 0)},
+ {USB_DEVICE(0x0c45, 0x628e), SN9C20X(SOI968, 0x30, 0)},
+ {USB_DEVICE(0x0c45, 0x628f), SN9C20X(OV9650, 0x30, 0)},
+ {USB_DEVICE(0x0c45, 0x62a0), SN9C20X(OV7670, 0x21, 0)},
+ {USB_DEVICE(0x0c45, 0x62b0), SN9C20X(MT9VPRB, 0x00, 0)},
+ {USB_DEVICE(0x0c45, 0x62b3), SN9C20X(OV9655, 0x30, 0)},
+ {USB_DEVICE(0x0c45, 0x62bb), SN9C20X(OV7660, 0x21, 0)},
+ {USB_DEVICE(0x0c45, 0x62bc), SN9C20X(HV7131R, 0x11, 0)},
+ {USB_DEVICE(0x045e, 0x00f4), SN9C20X(OV9650, 0x30, 0)},
+ {USB_DEVICE(0x145f, 0x013d), SN9C20X(OV7660, 0x21, 0)},
+ {USB_DEVICE(0x0458, 0x7029), SN9C20X(HV7131R, 0x11, 0)},
+ {USB_DEVICE(0xa168, 0x0610), SN9C20X(HV7131R, 0x11, 0)},
+ {USB_DEVICE(0xa168, 0x0611), SN9C20X(HV7131R, 0x11, 0)},
+ {USB_DEVICE(0xa168, 0x0613), SN9C20X(HV7131R, 0x11, 0)},
+ {USB_DEVICE(0xa168, 0x0618), SN9C20X(HV7131R, 0x11, 0)},
+ {USB_DEVICE(0xa168, 0x0614), SN9C20X(MT9M111, 0x5d, 0)},
+ {USB_DEVICE(0xa168, 0x0615), SN9C20X(MT9M111, 0x5d, 0)},
+ {USB_DEVICE(0xa168, 0x0617), SN9C20X(MT9M111, 0x5d, 0)},
+ {}
+};
+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 void sd_disconnect(struct usb_interface *intf)
+{
+#ifdef CONFIG_USB_GSPCA_SN9C20X_EVDEV
+ struct gspca_dev *gspca_dev = usb_get_intfdata(intf);
+
+ sn9c20x_input_cleanup(gspca_dev);
+#endif
+
+ gspca_disconnect(intf);
+}
+
+static struct usb_driver sd_driver = {
+ .name = MODULE_NAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = sd_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23)
+ .reset_resume = gspca_resume,
+#endif
+#endif
+};
+
+/* -- module insert / remove -- */
+static int __init sd_mod_init(void)
+{
+ int ret;
+ ret = usb_register(&sd_driver);
+ if (ret < 0)
+ return ret;
+ info("registered");
+ return 0;
+}
+static void __exit sd_mod_exit(void)
+{
+ usb_deregister(&sd_driver);
+ info("deregistered");
+}
+
+module_init(sd_mod_init);
+module_exit(sd_mod_exit);
diff --git a/linux/drivers/media/video/gspca/sonixj.c b/linux/drivers/media/video/gspca/sonixj.c
index 915b3a13e..5568401e8 100644
--- a/linux/drivers/media/video/gspca/sonixj.c
+++ b/linux/drivers/media/video/gspca/sonixj.c
@@ -735,13 +735,17 @@ static const u8 ov7660_sensor_init[][8] = {
{0xa1, 0x21, 0x12, 0x05, 0x00, 0x00, 0x00, 0x10},
/* Outformat = rawRGB */
{0xa1, 0x21, 0x13, 0xb8, 0x00, 0x00, 0x00, 0x10}, /* init COM8 */
- {0xd1, 0x21, 0x00, 0x01, 0x74, 0x74, 0x00, 0x10},
+ {0xd1, 0x21, 0x00, 0x01, 0x74, 0x92, 0x00, 0x10},
/* GAIN BLUE RED VREF */
{0xd1, 0x21, 0x04, 0x00, 0x7d, 0x62, 0x00, 0x10},
/* COM 1 BAVE GEAVE AECHH */
{0xb1, 0x21, 0x08, 0x83, 0x01, 0x00, 0x00, 0x10}, /* RAVE COM2 */
{0xd1, 0x21, 0x0c, 0x00, 0x08, 0x04, 0x4f, 0x10}, /* COM 3 4 5 6 */
+#if 1
+ {0xd1, 0x21, 0x10, 0x7f, 0x40, 0x05, 0xf8, 0x10},
+#else
{0xd1, 0x21, 0x10, 0x7f, 0x40, 0x05, 0xff, 0x10},
+#endif
/* AECH CLKRC COM7 COM8 */
{0xc1, 0x21, 0x14, 0x2c, 0x00, 0x02, 0x00, 0x10}, /* COM9 COM10 */
{0xd1, 0x21, 0x17, 0x10, 0x60, 0x02, 0x7b, 0x10},
@@ -1669,16 +1673,23 @@ static void setfreq(struct gspca_dev *gspca_dev)
if (gspca_dev->ctrl_dis & (1 << FREQ_IDX))
return;
if (sd->sensor == SENSOR_OV7660) {
+ u8 com8;
+
+#if 1
+ com8 = 0xf8; /* no auto gain/wb/expo */
+#else
+ com8 = 0xff;
+#endif
switch (sd->freq) {
case 0: /* Banding filter disabled */
- i2c_w1(gspca_dev, 0x13, 0xdf);
+ i2c_w1(gspca_dev, 0x13, com8 & 0xdf);
break;
case 1: /* 50 hz */
- i2c_w1(gspca_dev, 0x13, 0xff);
+ i2c_w1(gspca_dev, 0x13, com8);
i2c_w1(gspca_dev, 0x3b, 0x0a);
break;
case 2: /* 60 hz */
- i2c_w1(gspca_dev, 0x13, 0xff);
+ i2c_w1(gspca_dev, 0x13, com8);
i2c_w1(gspca_dev, 0x3b, 0x02);
break;
}
@@ -1769,6 +1780,8 @@ static int sd_start(struct gspca_dev *gspca_dev)
/* create the JPEG header */
sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL);
+ if (!sd->jpeg_hdr)
+ return -ENOMEM;
jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width,
0x21); /* JPEG 422 */
jpeg_set_qual(sd->jpeg_hdr, sd->quality);
diff --git a/linux/drivers/media/video/gspca/spca500.c b/linux/drivers/media/video/gspca/spca500.c
index 9717da6af..1fbad8d8f 100644
--- a/linux/drivers/media/video/gspca/spca500.c
+++ b/linux/drivers/media/video/gspca/spca500.c
@@ -687,6 +687,8 @@ static int sd_start(struct gspca_dev *gspca_dev)
/* create the JPEG header */
sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL);
+ if (!sd->jpeg_hdr)
+ return -ENOMEM;
jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width,
0x22); /* JPEG 411 */
jpeg_set_qual(sd->jpeg_hdr, sd->quality);
diff --git a/linux/drivers/media/video/gspca/stk014.c b/linux/drivers/media/video/gspca/stk014.c
index f25be20cf..476289648 100644
--- a/linux/drivers/media/video/gspca/stk014.c
+++ b/linux/drivers/media/video/gspca/stk014.c
@@ -333,6 +333,8 @@ static int sd_start(struct gspca_dev *gspca_dev)
/* create the JPEG header */
sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL);
+ if (!sd->jpeg_hdr)
+ return -ENOMEM;
jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width,
0x22); /* JPEG 411 */
jpeg_set_qual(sd->jpeg_hdr, sd->quality);
diff --git a/linux/drivers/media/video/gspca/sunplus.c b/linux/drivers/media/video/gspca/sunplus.c
index 16bc65dc7..270b86f24 100644
--- a/linux/drivers/media/video/gspca/sunplus.c
+++ b/linux/drivers/media/video/gspca/sunplus.c
@@ -52,6 +52,7 @@ struct sd {
#define LogitechClickSmart420 2
#define LogitechClickSmart820 3
#define MegapixV4 4
+#define MegaImageVI 5
u8 *jpeg_hdr;
};
@@ -875,7 +876,10 @@ static int sd_config(struct gspca_dev *gspca_dev,
break;
case BRIDGE_SPCA533:
cam->cam_mode = custom_mode;
- cam->nmodes = sizeof custom_mode / sizeof custom_mode[0];
+ if (sd->subtype == MegaImageVI) /* 320x240 only */
+ cam->nmodes = ARRAY_SIZE(custom_mode) - 1;
+ else
+ cam->nmodes = ARRAY_SIZE(custom_mode);
break;
case BRIDGE_SPCA504C:
cam->cam_mode = vga_mode2;
@@ -1004,6 +1008,8 @@ static int sd_start(struct gspca_dev *gspca_dev)
/* create the JPEG header */
sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL);
+ if (!sd->jpeg_hdr)
+ return -ENOMEM;
jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width,
0x22); /* JPEG 411 */
jpeg_set_qual(sd->jpeg_hdr, sd->quality);
@@ -1017,7 +1023,8 @@ static int sd_start(struct gspca_dev *gspca_dev)
/* case BRIDGE_SPCA533: */
/* case BRIDGE_SPCA536: */
if (sd->subtype == MegapixV4 ||
- sd->subtype == LogitechClickSmart820) {
+ sd->subtype == LogitechClickSmart820 ||
+ sd->subtype == MegaImageVI) {
reg_w(gspca_dev, 0xf0, 0, 0, 0);
spca504B_WaitCmdStatus(gspca_dev);
reg_r(gspca_dev, 0xf0, 4, 0);
@@ -1413,6 +1420,7 @@ static const __devinitdata struct usb_device_id device_table[] = {
{USB_DEVICE(0x04fc, 0x5360), BS(SPCA536, 0)},
{USB_DEVICE(0x04fc, 0xffff), BS(SPCA504B, 0)},
{USB_DEVICE(0x052b, 0x1513), BS(SPCA533, MegapixV4)},
+ {USB_DEVICE(0x052b, 0x1803), BS(SPCA533, MegaImageVI)},
{USB_DEVICE(0x0546, 0x3155), BS(SPCA533, 0)},
{USB_DEVICE(0x0546, 0x3191), BS(SPCA504B, 0)},
{USB_DEVICE(0x0546, 0x3273), BS(SPCA504B, 0)},
diff --git a/linux/drivers/media/video/gspca/zc3xx.c b/linux/drivers/media/video/gspca/zc3xx.c
index 2855ebf90..2a9480138 100644
--- a/linux/drivers/media/video/gspca/zc3xx.c
+++ b/linux/drivers/media/video/gspca/zc3xx.c
@@ -7272,6 +7272,8 @@ static int sd_start(struct gspca_dev *gspca_dev)
/* create the JPEG header */
sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL);
+ if (!sd->jpeg_hdr)
+ return -ENOMEM;
jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width,
0x21); /* JPEG 422 */
jpeg_set_qual(sd->jpeg_hdr, sd->quality);
diff --git a/linux/drivers/media/video/zr364xx.c b/linux/drivers/media/video/zr364xx.c
index 8746f067d..7dff47834 100644
--- a/linux/drivers/media/video/zr364xx.c
+++ b/linux/drivers/media/video/zr364xx.c
@@ -1,5 +1,5 @@
/*
- * Zoran 364xx based USB webcam module version 0.72
+ * Zoran 364xx based USB webcam module version 0.73
*
* Allows you to use your USB webcam with V4L2 applications
* This is still in heavy developpement !
@@ -10,6 +10,8 @@
* Heavily inspired by usb-skeleton.c, vicam.c, cpia.c and spca50x.c drivers
* V4L2 version inspired by meye.c driver
*
+ * Some video buffer code by Lamarque based on s2255drv.c and vivi.c drivers.
+ *
* 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
@@ -35,25 +37,34 @@
#include <linux/highmem.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
+#include <media/videobuf-vmalloc.h>
#include "compat.h"
/* Version Information */
-#define DRIVER_VERSION "v0.72"
+#define DRIVER_VERSION "v0.73"
+#define ZR364_VERSION_CODE KERNEL_VERSION(0, 7, 3)
#define DRIVER_AUTHOR "Antoine Jacquet, http://royale.zerezo.com/"
#define DRIVER_DESC "Zoran 364xx"
/* Camera */
-#define FRAMES 2
+#define FRAMES 1
#define MAX_FRAME_SIZE 100000
#define BUFFER_SIZE 0x1000
#define CTRL_TIMEOUT 500
+#define ZR364XX_DEF_BUFS 4
+#define ZR364XX_READ_IDLE 0
+#define ZR364XX_READ_FRAME 1
/* Debug macro */
-#define DBG(x...) if (debug) printk(KERN_INFO KBUILD_MODNAME x)
-
+#define DBG(fmt, args...) \
+ do { \
+ if (debug) { \
+ printk(KERN_INFO KBUILD_MODNAME " " fmt, ##args); \
+ } \
+ } while (0)
/* Init methods, need to find nicer names for these
* the exact names of the chipsets would be the best if someone finds it */
@@ -102,24 +113,93 @@ static struct usb_device_id device_table[] = {
MODULE_DEVICE_TABLE(usb, device_table);
+struct zr364xx_mode {
+ u32 color; /* output video color format */
+ u32 brightness; /* brightness */
+};
+
+/* frame structure */
+struct zr364xx_framei {
+ unsigned long ulState; /* ulState:ZR364XX_READ_IDLE,
+ ZR364XX_READ_FRAME */
+ void *lpvbits; /* image data */
+ unsigned long cur_size; /* current data copied to it */
+};
+
+/* image buffer structure */
+struct zr364xx_bufferi {
+ unsigned long dwFrames; /* number of frames in buffer */
+ struct zr364xx_framei frame[FRAMES]; /* array of FRAME structures */
+};
+
+struct zr364xx_dmaqueue {
+ struct list_head active;
+ struct zr364xx_camera *cam;
+};
+
+struct zr364xx_pipeinfo {
+ u32 transfer_size;
+ u8 *transfer_buffer;
+ u32 state;
+ void *stream_urb;
+ void *cam; /* back pointer to zr364xx_camera struct */
+ u32 err_count;
+ u32 idx;
+};
+
+struct zr364xx_fmt {
+ char *name;
+ u32 fourcc;
+ int depth;
+};
+
+/* image formats. */
+static const struct zr364xx_fmt formats[] = {
+ {
+ .name = "JPG",
+ .fourcc = V4L2_PIX_FMT_JPEG,
+ .depth = 24
+ }
+};
/* Camera stuff */
struct zr364xx_camera {
struct usb_device *udev; /* save off the usb device pointer */
struct usb_interface *interface;/* the interface for this device */
struct video_device *vdev; /* v4l video device */
- u8 *framebuf;
int nb;
- unsigned char *buffer;
+ struct zr364xx_bufferi buffer;
int skip;
- int brightness;
int width;
int height;
int method;
struct mutex lock;
+ struct mutex open_lock;
int users;
+
+ spinlock_t slock;
+ struct zr364xx_dmaqueue vidq;
+ int resources;
+ int last_frame;
+ int cur_frame;
+ unsigned long frame_count;
+ int b_acquire;
+ struct zr364xx_pipeinfo pipe[1];
+
+ u8 read_endpoint;
+
+ const struct zr364xx_fmt *fmt;
+ struct videobuf_queue vb_vidq;
+ enum v4l2_buf_type type;
+ struct zr364xx_mode mode;
};
+/* buffer for one video frame */
+struct zr364xx_buffer {
+ /* common v4l buffer stuff -- must be first */
+ struct videobuf_buffer vb;
+ const struct zr364xx_fmt *fmt;
+};
/* function used to send initialisation commands to the camera */
static int send_control_msg(struct usb_device *udev, u8 request, u16 value,
@@ -273,139 +353,116 @@ static unsigned char header2[] = {
};
static unsigned char header3;
+/* ------------------------------------------------------------------
+ Videobuf operations
+ ------------------------------------------------------------------*/
+static int buffer_setup(struct videobuf_queue *vq, unsigned int *count,
+ unsigned int *size)
+{
+ struct zr364xx_camera *cam = vq->priv_data;
-/********************/
-/* V4L2 integration */
-/********************/
+ *size = cam->width * cam->height * (cam->fmt->depth >> 3);
-/* this function reads a full JPEG picture synchronously
- * TODO: do it asynchronously... */
-static int read_frame(struct zr364xx_camera *cam, int framenum)
-{
- int i, n, temp, head, size, actual_length;
- unsigned char *ptr = NULL, *jpeg;
-
- redo:
- /* hardware brightness */
- n = send_control_msg(cam->udev, 1, 0x2001, 0, NULL, 0);
- temp = (0x60 << 8) + 127 - cam->brightness;
- n = send_control_msg(cam->udev, 1, temp, 0, NULL, 0);
-
- /* during the first loop we are going to insert JPEG header */
- head = 0;
- /* this is the place in memory where we are going to build
- * the JPEG image */
- jpeg = cam->framebuf + framenum * MAX_FRAME_SIZE;
- /* read data... */
- do {
- n = usb_bulk_msg(cam->udev,
- usb_rcvbulkpipe(cam->udev, 0x81),
- cam->buffer, BUFFER_SIZE, &actual_length,
- CTRL_TIMEOUT);
- DBG("buffer : %d %d", cam->buffer[0], cam->buffer[1]);
- DBG("bulk : n=%d size=%d", n, actual_length);
- if (n < 0) {
- dev_err(&cam->udev->dev, "error reading bulk msg\n");
- return 0;
- }
- if (actual_length < 0 || actual_length > BUFFER_SIZE) {
- dev_err(&cam->udev->dev, "wrong number of bytes\n");
- return 0;
- }
+ if (*count == 0)
+ *count = ZR364XX_DEF_BUFS;
- /* swap bytes if camera needs it */
- if (cam->method == METHOD0) {
- u16 *buf = (u16*)cam->buffer;
- for (i = 0; i < BUFFER_SIZE/2; i++)
- swab16s(buf + i);
- }
+ while (*size * (*count) > ZR364XX_DEF_BUFS * 1024 * 1024)
+ (*count)--;
- /* write the JPEG header */
- if (!head) {
- DBG("jpeg header");
- ptr = jpeg;
- memcpy(ptr, header1, sizeof(header1));
- ptr += sizeof(header1);
- header3 = 0;
- memcpy(ptr, &header3, 1);
- ptr++;
- memcpy(ptr, cam->buffer, 64);
- ptr += 64;
- header3 = 1;
- memcpy(ptr, &header3, 1);
- ptr++;
- memcpy(ptr, cam->buffer + 64, 64);
- ptr += 64;
- memcpy(ptr, header2, sizeof(header2));
- ptr += sizeof(header2);
- memcpy(ptr, cam->buffer + 128,
- actual_length - 128);
- ptr += actual_length - 128;
- head = 1;
- DBG("header : %d %d %d %d %d %d %d %d %d",
- cam->buffer[0], cam->buffer[1], cam->buffer[2],
- cam->buffer[3], cam->buffer[4], cam->buffer[5],
- cam->buffer[6], cam->buffer[7], cam->buffer[8]);
- } else {
- memcpy(ptr, cam->buffer, actual_length);
- ptr += actual_length;
- }
- }
- /* ... until there is no more */
- while (actual_length == BUFFER_SIZE);
+ return 0;
+}
- /* we skip the 2 first frames which are usually buggy */
- if (cam->skip) {
- cam->skip--;
- goto redo;
- }
+static void free_buffer(struct videobuf_queue *vq, struct zr364xx_buffer *buf)
+{
+ DBG("%s\n", __func__);
- /* go back to find the JPEG EOI marker */
- size = ptr - jpeg;
- ptr -= 2;
- while (ptr > jpeg) {
- if (*ptr == 0xFF && *(ptr + 1) == 0xD9
- && *(ptr + 2) == 0xFF)
- break;
- ptr--;
- }
- if (ptr == jpeg)
- DBG("No EOI marker");
+ if (in_interrupt())
+ BUG();
- /* Sometimes there is junk data in the middle of the picture,
- * we want to skip this bogus frames */
- while (ptr > jpeg) {
- if (*ptr == 0xFF && *(ptr + 1) == 0xFF
- && *(ptr + 2) == 0xFF)
- break;
- ptr--;
+ videobuf_vmalloc_free(&buf->vb);
+ buf->vb.state = VIDEOBUF_NEEDS_INIT;
+}
+
+static int buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct zr364xx_camera *cam = vq->priv_data;
+ struct zr364xx_buffer *buf = container_of(vb, struct zr364xx_buffer,
+ vb);
+ int rc;
+
+ DBG("%s, field=%d, fmt name = %s\n", __func__, field, cam->fmt != NULL ?
+ cam->fmt->name : "");
+ if (cam->fmt == NULL)
+ return -EINVAL;
+
+ buf->vb.size = cam->width * cam->height * (cam->fmt->depth >> 3);
+
+ if (buf->vb.baddr != 0 && buf->vb.bsize < buf->vb.size) {
+ DBG("invalid buffer prepare\n");
+ return -EINVAL;
}
- if (ptr != jpeg) {
- DBG("Bogus frame ? %d", cam->nb);
- goto redo;
+
+ buf->fmt = cam->fmt;
+ buf->vb.width = cam->width;
+ buf->vb.height = cam->height;
+ buf->vb.field = field;
+
+ if (buf->vb.state == VIDEOBUF_NEEDS_INIT) {
+ rc = videobuf_iolock(vq, &buf->vb, NULL);
+ if (rc < 0)
+ goto fail;
}
- DBG("jpeg : %d %d %d %d %d %d %d %d",
- jpeg[0], jpeg[1], jpeg[2], jpeg[3],
- jpeg[4], jpeg[5], jpeg[6], jpeg[7]);
+ buf->vb.state = VIDEOBUF_PREPARED;
+ return 0;
+fail:
+ free_buffer(vq, buf);
+ return rc;
+}
+
+static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
+{
+ struct zr364xx_buffer *buf = container_of(vb, struct zr364xx_buffer,
+ vb);
+ struct zr364xx_camera *cam = vq->priv_data;
+
+ DBG("%s\n", __func__);
- return size;
+ buf->vb.state = VIDEOBUF_QUEUED;
+ list_add_tail(&buf->vb.queue, &cam->vidq.active);
}
+static void buffer_release(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb)
+{
+ struct zr364xx_buffer *buf = container_of(vb, struct zr364xx_buffer,
+ vb);
+
+ DBG("%s\n", __func__);
+ free_buffer(vq, buf);
+}
+
+static struct videobuf_queue_ops zr364xx_video_qops = {
+ .buf_setup = buffer_setup,
+ .buf_prepare = buffer_prepare,
+ .buf_queue = buffer_queue,
+ .buf_release = buffer_release,
+};
+
+/********************/
+/* V4L2 integration */
+/********************/
+static int zr364xx_vidioc_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type type);
-static ssize_t zr364xx_read(struct file *file, char __user *buf, size_t cnt,
+static ssize_t zr364xx_read(struct file *file, char __user *buf, size_t count,
loff_t * ppos)
{
- unsigned long count = cnt;
- struct video_device *vdev = video_devdata(file);
- struct zr364xx_camera *cam;
+ struct zr364xx_camera *cam = video_drvdata(file);
- DBG("zr364xx_read: read %d bytes.", (int) count);
-
- if (vdev == NULL)
- return -ENODEV;
- cam = video_get_drvdata(vdev);
+ DBG("%s\n", __func__);
if (!buf)
return -EINVAL;
@@ -413,21 +470,275 @@ static ssize_t zr364xx_read(struct file *file, char __user *buf, size_t cnt,
if (!count)
return -EINVAL;
- /* NoMan Sux ! */
- count = read_frame(cam, 0);
+ if (cam->type == V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ zr364xx_vidioc_streamon(file, cam, cam->type) == 0) {
+ DBG("%s: reading %d bytes at pos %d.\n", __func__, (int) count,
+ (int) *ppos);
+
+ /* NoMan Sux ! */
+ return videobuf_read_one(&cam->vb_vidq, buf, count, ppos,
+ file->f_flags & O_NONBLOCK);
+ }
+
+ return 0;
+}
+
+/* video buffer vmalloc implementation based partly on VIVI driver which is
+ * Copyright (c) 2006 by
+ * Mauro Carvalho Chehab <mchehab--a.t--infradead.org>
+ * Ted Walther <ted--a.t--enumera.com>
+ * John Sokol <sokol--a.t--videotechnology.com>
+ * http://v4l.videotechnology.com/
+ *
+ */
+static void zr364xx_fillbuff(struct zr364xx_camera *cam,
+ struct zr364xx_buffer *buf,
+ int jpgsize)
+{
+ int pos = 0;
+ struct timeval ts;
+ const char *tmpbuf;
+ char *vbuf = videobuf_to_vmalloc(&buf->vb);
+ unsigned long last_frame;
+ struct zr364xx_framei *frm;
+
+ if (!vbuf)
+ return;
+
+ last_frame = cam->last_frame;
+ if (last_frame != -1) {
+ frm = &cam->buffer.frame[last_frame];
+ tmpbuf = (const char *)cam->buffer.frame[last_frame].lpvbits;
+ switch (buf->fmt->fourcc) {
+ case V4L2_PIX_FMT_JPEG:
+ buf->vb.size = jpgsize;
+ memcpy(vbuf, tmpbuf, buf->vb.size);
+ break;
+ default:
+ printk(KERN_DEBUG KBUILD_MODNAME ": unknown format?\n");
+ }
+ cam->last_frame = -1;
+ } else {
+ printk(KERN_ERR KBUILD_MODNAME ": =======no frame\n");
+ return;
+ }
+ DBG("%s: Buffer 0x%08lx size= %d\n", __func__,
+ (unsigned long)vbuf, pos);
+ /* tell v4l buffer was filled */
+
+ buf->vb.field_count = cam->frame_count * 2;
+ do_gettimeofday(&ts);
+ buf->vb.ts = ts;
+ buf->vb.state = VIDEOBUF_DONE;
+}
+
+static int zr364xx_got_frame(struct zr364xx_camera *cam, int jpgsize)
+{
+ struct zr364xx_dmaqueue *dma_q = &cam->vidq;
+ struct zr364xx_buffer *buf;
+ unsigned long flags = 0;
+ int rc = 0;
+
+ DBG("wakeup: %p\n", &dma_q);
+ spin_lock_irqsave(&cam->slock, flags);
+
+ if (list_empty(&dma_q->active)) {
+ DBG("No active queue to serve\n");
+ rc = -1;
+ goto unlock;
+ }
+ buf = list_entry(dma_q->active.next,
+ struct zr364xx_buffer, vb.queue);
+
+ if (!waitqueue_active(&buf->vb.done)) {
+ /* no one active */
+ rc = -1;
+ goto unlock;
+ }
+ list_del(&buf->vb.queue);
+ do_gettimeofday(&buf->vb.ts);
+ DBG("[%p/%d] wakeup\n", buf, buf->vb.i);
+ zr364xx_fillbuff(cam, buf, jpgsize);
+ wake_up(&buf->vb.done);
+ DBG("wakeup [buf/i] [%p/%d]\n", buf, buf->vb.i);
+unlock:
+ spin_unlock_irqrestore(&cam->slock, flags);
+ return 0;
+}
+
+/* this function moves the usb stream read pipe data
+ * into the system buffers.
+ * returns 0 on success, EAGAIN if more data to process (call this
+ * function again).
+ */
+static int zr364xx_read_video_callback(struct zr364xx_camera *cam,
+ struct zr364xx_pipeinfo *pipe_info,
+ struct urb *purb)
+{
+ unsigned char *pdest;
+ unsigned char *psrc;
+ s32 idx = -1;
+ struct zr364xx_framei *frm;
+ int i = 0;
+ unsigned char *ptr = NULL;
+
+ /*DBG("buffer to user\n");*/
+ idx = cam->cur_frame;
+ frm = &cam->buffer.frame[idx];
+
+ /* swap bytes if camera needs it */
+ if (cam->method == METHOD0) {
+ u16 *buf = (u16 *)pipe_info->transfer_buffer;
+ for (i = 0; i < purb->actual_length/2; i++)
+ swab16s(buf + i);
+ }
+
+ /* search done. now find out if should be acquiring */
+ if (!cam->b_acquire) {
+ /* we found a frame, but this channel is turned off */
+ frm->ulState = ZR364XX_READ_IDLE;
+ return -EINVAL;
+ }
+
+ if (frm->lpvbits == NULL) {
+ DBG("%s: frame buffer == NULL.%p %p %d\n", __func__,
+ frm, cam, idx);
+ return -ENOMEM;
+ }
- if (copy_to_user(buf, cam->framebuf, count))
- return -EFAULT;
+ psrc = (u8 *)pipe_info->transfer_buffer;
+ ptr = pdest = frm->lpvbits;
+
+ if (frm->ulState == ZR364XX_READ_IDLE) {
+ frm->ulState = ZR364XX_READ_FRAME;
+ frm->cur_size = 0;
+
+ DBG("jpeg header, ");
+ memcpy(ptr, header1, sizeof(header1));
+ ptr += sizeof(header1);
+ header3 = 0;
+ memcpy(ptr, &header3, 1);
+ ptr++;
+ memcpy(ptr, psrc, 64);
+ ptr += 64;
+ header3 = 1;
+ memcpy(ptr, &header3, 1);
+ ptr++;
+ memcpy(ptr, psrc + 64, 64);
+ ptr += 64;
+ memcpy(ptr, header2, sizeof(header2));
+ ptr += sizeof(header2);
+ memcpy(ptr, psrc + 128,
+ purb->actual_length - 128);
+ ptr += purb->actual_length - 128;
+ DBG("header : %d %d %d %d %d %d %d %d %d\n",
+ psrc[0], psrc[1], psrc[2],
+ psrc[3], psrc[4], psrc[5],
+ psrc[6], psrc[7], psrc[8]);
+ frm->cur_size = ptr - pdest;
+ } else {
+ pdest += frm->cur_size;
+ memcpy(pdest, psrc, purb->actual_length);
+ frm->cur_size += purb->actual_length;
+ }
+ /*DBG("cur_size %lu urb size %d\n", frm->cur_size,
+ purb->actual_length);*/
+
+ if (purb->actual_length < pipe_info->transfer_size) {
+ DBG("****************Buffer[%d]full*************\n", idx);
+ cam->last_frame = cam->cur_frame;
+ cam->cur_frame++;
+ /* end of system frame ring buffer, start at zero */
+ if (cam->cur_frame == cam->buffer.dwFrames)
+ cam->cur_frame = 0;
+
+ /* frame ready */
+ /* go back to find the JPEG EOI marker */
+ ptr = pdest = frm->lpvbits;
+ ptr += frm->cur_size - 2;
+ while (ptr > pdest) {
+ if (*ptr == 0xFF && *(ptr + 1) == 0xD9
+ && *(ptr + 2) == 0xFF)
+ break;
+ ptr--;
+ }
+ if (ptr == pdest)
+ DBG("No EOI marker\n");
+
+ /* Sometimes there is junk data in the middle of the picture,
+ * we want to skip this bogus frames */
+ while (ptr > pdest) {
+ if (*ptr == 0xFF && *(ptr + 1) == 0xFF
+ && *(ptr + 2) == 0xFF)
+ break;
+ ptr--;
+ }
+ if (ptr != pdest) {
+ DBG("Bogus frame ? %d\n", ++(cam->nb));
+ } else if (cam->b_acquire) {
+ /* we skip the 2 first frames which are usually buggy */
+ if (cam->skip)
+ cam->skip--;
+ else {
+ DBG("jpeg(%lu): %d %d %d %d %d %d %d %d\n",
+ frm->cur_size,
+ pdest[0], pdest[1], pdest[2], pdest[3],
+ pdest[4], pdest[5], pdest[6], pdest[7]);
+
+ zr364xx_got_frame(cam, frm->cur_size);
+ }
+ }
+ cam->frame_count++;
+ frm->ulState = ZR364XX_READ_IDLE;
+ frm->cur_size = 0;
+ }
+ /* done successfully */
+ return 0;
+}
+
+static int res_get(struct zr364xx_camera *cam)
+{
+ /* is it free? */
+ mutex_lock(&cam->lock);
+ if (cam->resources) {
+ /* no, someone else uses it */
+ mutex_unlock(&cam->lock);
+ return 0;
+ }
+ /* it's free, grab it */
+ cam->resources = 1;
+ DBG("res: get\n");
+ mutex_unlock(&cam->lock);
+ return 1;
+}
- return count;
+static inline int res_check(struct zr364xx_camera *cam)
+{
+ return cam->resources;
}
+static void res_free(struct zr364xx_camera *cam)
+{
+ mutex_lock(&cam->lock);
+ cam->resources = 0;
+ mutex_unlock(&cam->lock);
+ DBG("res: put\n");
+}
static int zr364xx_vidioc_querycap(struct file *file, void *priv,
struct v4l2_capability *cap)
{
- strcpy(cap->driver, DRIVER_DESC);
- cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE;
+ struct zr364xx_camera *cam = video_drvdata(file);
+
+ strlcpy(cap->driver, DRIVER_DESC, sizeof(cap->driver));
+ strlcpy(cap->card, cam->udev->product, sizeof(cap->card));
+ strlcpy(cap->bus_info, dev_name(&cam->udev->dev),
+ sizeof(cap->bus_info));
+ cap->version = ZR364_VERSION_CODE;
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_READWRITE |
+ V4L2_CAP_STREAMING;
+
return 0;
}
@@ -459,12 +770,11 @@ static int zr364xx_vidioc_s_input(struct file *file, void *priv,
static int zr364xx_vidioc_queryctrl(struct file *file, void *priv,
struct v4l2_queryctrl *c)
{
- struct video_device *vdev = video_devdata(file);
struct zr364xx_camera *cam;
- if (vdev == NULL)
+ if (file == NULL)
return -ENODEV;
- cam = video_get_drvdata(vdev);
+ cam = video_drvdata(file);
switch (c->id) {
case V4L2_CID_BRIGHTNESS:
@@ -473,7 +783,7 @@ static int zr364xx_vidioc_queryctrl(struct file *file, void *priv,
c->minimum = 0;
c->maximum = 127;
c->step = 1;
- c->default_value = cam->brightness;
+ c->default_value = cam->mode.brightness;
c->flags = 0;
break;
default:
@@ -485,36 +795,42 @@ static int zr364xx_vidioc_queryctrl(struct file *file, void *priv,
static int zr364xx_vidioc_s_ctrl(struct file *file, void *priv,
struct v4l2_control *c)
{
- struct video_device *vdev = video_devdata(file);
struct zr364xx_camera *cam;
+ int temp;
- if (vdev == NULL)
+ if (file == NULL)
return -ENODEV;
- cam = video_get_drvdata(vdev);
+ cam = video_drvdata(file);
switch (c->id) {
case V4L2_CID_BRIGHTNESS:
- cam->brightness = c->value;
+ cam->mode.brightness = c->value;
+ /* hardware brightness */
+ mutex_lock(&cam->lock);
+ send_control_msg(cam->udev, 1, 0x2001, 0, NULL, 0);
+ temp = (0x60 << 8) + 127 - cam->mode.brightness;
+ send_control_msg(cam->udev, 1, temp, 0, NULL, 0);
+ mutex_unlock(&cam->lock);
break;
default:
return -EINVAL;
}
+
return 0;
}
static int zr364xx_vidioc_g_ctrl(struct file *file, void *priv,
struct v4l2_control *c)
{
- struct video_device *vdev = video_devdata(file);
struct zr364xx_camera *cam;
- if (vdev == NULL)
+ if (file == NULL)
return -ENODEV;
- cam = video_get_drvdata(vdev);
+ cam = video_drvdata(file);
switch (c->id) {
case V4L2_CID_BRIGHTNESS:
- c->value = cam->brightness;
+ c->value = cam->mode.brightness;
break;
default:
return -EINVAL;
@@ -528,26 +844,36 @@ static int zr364xx_vidioc_enum_fmt_vid_cap(struct file *file,
if (f->index > 0)
return -EINVAL;
f->flags = V4L2_FMT_FLAG_COMPRESSED;
- strcpy(f->description, "JPEG");
- f->pixelformat = V4L2_PIX_FMT_JPEG;
+ strcpy(f->description, formats[0].name);
+ f->pixelformat = formats[0].fourcc;
return 0;
}
+static char *decode_fourcc(__u32 pixelformat, char *buf)
+{
+ buf[0] = pixelformat & 0xff;
+ buf[1] = (pixelformat >> 8) & 0xff;
+ buf[2] = (pixelformat >> 16) & 0xff;
+ buf[3] = (pixelformat >> 24) & 0xff;
+ buf[4] = '\0';
+ return buf;
+}
+
static int zr364xx_vidioc_try_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
- struct video_device *vdev = video_devdata(file);
- struct zr364xx_camera *cam;
+ struct zr364xx_camera *cam = video_drvdata(file);
+ char pixelformat_name[5];
- if (vdev == NULL)
+ if (cam == NULL)
return -ENODEV;
- cam = video_get_drvdata(vdev);
- if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_JPEG)
- return -EINVAL;
- if (f->fmt.pix.field != V4L2_FIELD_ANY &&
- f->fmt.pix.field != V4L2_FIELD_NONE)
+ if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_JPEG) {
+ DBG("%s: unsupported pixelformat V4L2_PIX_FMT_%s\n", __func__,
+ decode_fourcc(f->fmt.pix.pixelformat, pixelformat_name));
return -EINVAL;
+ }
+
f->fmt.pix.field = V4L2_FIELD_NONE;
f->fmt.pix.width = cam->width;
f->fmt.pix.height = cam->height;
@@ -555,20 +881,22 @@ static int zr364xx_vidioc_try_fmt_vid_cap(struct file *file, void *priv,
f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
f->fmt.pix.colorspace = 0;
f->fmt.pix.priv = 0;
+ DBG("%s: V4L2_PIX_FMT_%s (%d) ok!\n", __func__,
+ decode_fourcc(f->fmt.pix.pixelformat, pixelformat_name),
+ f->fmt.pix.field);
return 0;
}
static int zr364xx_vidioc_g_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
- struct video_device *vdev = video_devdata(file);
struct zr364xx_camera *cam;
- if (vdev == NULL)
+ if (file == NULL)
return -ENODEV;
- cam = video_get_drvdata(vdev);
+ cam = video_drvdata(file);
- f->fmt.pix.pixelformat = V4L2_PIX_FMT_JPEG;
+ f->fmt.pix.pixelformat = formats[0].fourcc;
f->fmt.pix.field = V4L2_FIELD_NONE;
f->fmt.pix.width = cam->width;
f->fmt.pix.height = cam->height;
@@ -579,21 +907,26 @@ static int zr364xx_vidioc_g_fmt_vid_cap(struct file *file, void *priv,
return 0;
}
+/* Lamarque TODO: implement changing resolution on the fly */
static int zr364xx_vidioc_s_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
- struct video_device *vdev = video_devdata(file);
- struct zr364xx_camera *cam;
+ struct zr364xx_camera *cam = video_drvdata(file);
+ struct videobuf_queue *q = &cam->vb_vidq;
+ char pixelformat_name[5];
+ int ret = zr364xx_vidioc_try_fmt_vid_cap(file, cam, f);
- if (vdev == NULL)
- return -ENODEV;
- cam = video_get_drvdata(vdev);
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&q->vb_lock);
+
+ if (videobuf_queue_is_busy(&cam->vb_vidq)) {
+ DBG("%s queue busy\n", __func__);
+ ret = -EBUSY;
+ goto out;
+ }
- if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_JPEG)
- return -EINVAL;
- if (f->fmt.pix.field != V4L2_FIELD_ANY &&
- f->fmt.pix.field != V4L2_FIELD_NONE)
- return -EINVAL;
f->fmt.pix.field = V4L2_FIELD_NONE;
f->fmt.pix.width = cam->width;
f->fmt.pix.height = cam->height;
@@ -601,19 +934,265 @@ static int zr364xx_vidioc_s_fmt_vid_cap(struct file *file, void *priv,
f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
f->fmt.pix.colorspace = 0;
f->fmt.pix.priv = 0;
- DBG("ok!");
+ cam->vb_vidq.field = f->fmt.pix.field;
+ cam->mode.color = V4L2_PIX_FMT_JPEG;
+ ret = 0;
+
+out:
+ mutex_unlock(&q->vb_lock);
+
+ DBG("%s: V4L2_PIX_FMT_%s (%d) ok!\n", __func__,
+ decode_fourcc(f->fmt.pix.pixelformat, pixelformat_name),
+ f->fmt.pix.field);
+ return ret;
+}
+
+static int zr364xx_vidioc_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *p)
+{
+ int rc;
+ struct zr364xx_camera *cam = video_drvdata(file);
+ rc = videobuf_reqbufs(&cam->vb_vidq, p);
+ return rc;
+}
+
+static int zr364xx_vidioc_querybuf(struct file *file,
+ void *priv,
+ struct v4l2_buffer *p)
+{
+ int rc;
+ struct zr364xx_camera *cam = video_drvdata(file);
+ rc = videobuf_querybuf(&cam->vb_vidq, p);
+ return rc;
+}
+
+static int zr364xx_vidioc_qbuf(struct file *file,
+ void *priv,
+ struct v4l2_buffer *p)
+{
+ int rc;
+ struct zr364xx_camera *cam = video_drvdata(file);
+ DBG("%s\n", __func__);
+ rc = videobuf_qbuf(&cam->vb_vidq, p);
+ return rc;
+}
+
+static int zr364xx_vidioc_dqbuf(struct file *file,
+ void *priv,
+ struct v4l2_buffer *p)
+{
+ int rc;
+ struct zr364xx_camera *cam = video_drvdata(file);
+ DBG("%s\n", __func__);
+ rc = videobuf_dqbuf(&cam->vb_vidq, p, file->f_flags & O_NONBLOCK);
+ return rc;
+}
+
+static void read_pipe_completion(struct urb *purb)
+{
+ struct zr364xx_pipeinfo *pipe_info;
+ struct zr364xx_camera *cam;
+ int pipe;
+
+ pipe_info = purb->context;
+ /*DBG("%s %p, status %d\n", __func__, purb, purb->status);*/
+ if (pipe_info == NULL) {
+ printk(KERN_ERR KBUILD_MODNAME ": no context!\n");
+ return;
+ }
+
+ cam = pipe_info->cam;
+ if (cam == NULL) {
+ printk(KERN_ERR KBUILD_MODNAME ": no context!\n");
+ return;
+ }
+
+ /* if shutting down, do not resubmit, exit immediately */
+ if (purb->status == -ESHUTDOWN) {
+ DBG("%s, err shutdown\n", __func__);
+ pipe_info->err_count++;
+ return;
+ }
+
+ if (pipe_info->state == 0) {
+ DBG("exiting USB pipe\n");
+ return;
+ }
+
+ if (purb->actual_length < 0 ||
+ purb->actual_length > pipe_info->transfer_size) {
+ dev_err(&cam->udev->dev, "wrong number of bytes\n");
+ return;
+ }
+
+ if (purb->status == 0)
+ zr364xx_read_video_callback(cam, pipe_info, purb);
+ else {
+ pipe_info->err_count++;
+ DBG("%s: failed URB %d\n", __func__, purb->status);
+ }
+
+ pipe = usb_rcvbulkpipe(cam->udev, cam->read_endpoint);
+
+ /* reuse urb */
+ usb_fill_bulk_urb(pipe_info->stream_urb, cam->udev,
+ pipe,
+ pipe_info->transfer_buffer,
+ pipe_info->transfer_size,
+ read_pipe_completion, pipe_info);
+
+ if (pipe_info->state != 0) {
+ purb->status = usb_submit_urb(pipe_info->stream_urb,
+ GFP_ATOMIC);
+
+ if (purb->status)
+ dev_err(&cam->udev->dev,
+ "error submitting urb (error=%i)\n",
+ purb->status);
+ } else
+ DBG("read pipe complete state 0\n");
+}
+
+static int zr364xx_start_readpipe(struct zr364xx_camera *cam)
+{
+ int pipe;
+ int retval;
+ struct zr364xx_pipeinfo *pipe_info = cam->pipe;
+ pipe = usb_rcvbulkpipe(cam->udev, cam->read_endpoint);
+ DBG("%s: start pipe IN x%x\n", __func__, cam->read_endpoint);
+
+ pipe_info->state = 1;
+ pipe_info->err_count = 0;
+ pipe_info->stream_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!pipe_info->stream_urb) {
+ dev_err(&cam->udev->dev, "ReadStream: Unable to alloc URB\n");
+ return -ENOMEM;
+ }
+ /* transfer buffer allocated in board_init */
+ usb_fill_bulk_urb(pipe_info->stream_urb, cam->udev,
+ pipe,
+ pipe_info->transfer_buffer,
+ pipe_info->transfer_size,
+ read_pipe_completion, pipe_info);
+
+ DBG("submitting URB %p\n", pipe_info->stream_urb);
+ retval = usb_submit_urb(pipe_info->stream_urb, GFP_KERNEL);
+ if (retval) {
+ printk(KERN_ERR KBUILD_MODNAME ": start read pipe failed\n");
+ return retval;
+ }
+
+ return 0;
+}
+
+static void zr364xx_stop_readpipe(struct zr364xx_camera *cam)
+{
+ struct zr364xx_pipeinfo *pipe_info;
+
+ if (cam == NULL) {
+ printk(KERN_ERR KBUILD_MODNAME ": invalid device\n");
+ return;
+ }
+ DBG("stop read pipe\n");
+ pipe_info = cam->pipe;
+ if (pipe_info) {
+ if (pipe_info->state != 0)
+ pipe_info->state = 0;
+
+ if (pipe_info->stream_urb) {
+ /* cancel urb */
+ usb_kill_urb(pipe_info->stream_urb);
+ usb_free_urb(pipe_info->stream_urb);
+ pipe_info->stream_urb = NULL;
+ }
+ }
+ DBG("stop read pipe\n");
+ return;
+}
+
+/* starts acquisition process */
+static int zr364xx_start_acquire(struct zr364xx_camera *cam)
+{
+ int j;
+
+ DBG("start acquire\n");
+
+ cam->last_frame = -1;
+ cam->cur_frame = 0;
+ for (j = 0; j < FRAMES; j++) {
+ cam->buffer.frame[j].ulState = ZR364XX_READ_IDLE;
+ cam->buffer.frame[j].cur_size = 0;
+ }
+ return 0;
+}
+
+static inline int zr364xx_stop_acquire(struct zr364xx_camera *cam)
+{
+ cam->b_acquire = 0;
return 0;
}
static int zr364xx_vidioc_streamon(struct file *file, void *priv,
enum v4l2_buf_type type)
{
- return 0;
+ struct zr364xx_camera *cam = video_drvdata(file);
+ int j;
+ int res;
+
+ DBG("%s\n", __func__);
+
+ if (cam->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ dev_err(&cam->udev->dev, "invalid fh type0\n");
+ return -EINVAL;
+ }
+ if (cam->type != type) {
+ dev_err(&cam->udev->dev, "invalid fh type1\n");
+ return -EINVAL;
+ }
+
+ if (!res_get(cam)) {
+ dev_err(&cam->udev->dev, "stream busy\n");
+ return -EBUSY;
+ }
+
+ cam->last_frame = -1;
+ cam->cur_frame = 0;
+ cam->frame_count = 0;
+ for (j = 0; j < FRAMES; j++) {
+ cam->buffer.frame[j].ulState = ZR364XX_READ_IDLE;
+ cam->buffer.frame[j].cur_size = 0;
+ }
+ res = videobuf_streamon(&cam->vb_vidq);
+ if (res == 0) {
+ zr364xx_start_acquire(cam);
+ cam->b_acquire = 1;
+ } else {
+ res_free(cam);
+ }
+ DBG("%s: %d\n", __func__, res);
+ return res;
}
static int zr364xx_vidioc_streamoff(struct file *file, void *priv,
enum v4l2_buf_type type)
{
+ int res;
+ struct zr364xx_camera *cam = video_drvdata(file);
+
+ DBG("%s\n", __func__);
+ if (cam->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ dev_err(&cam->udev->dev, "invalid fh type0\n");
+ return -EINVAL;
+ }
+ if (cam->type != type) {
+ dev_err(&cam->udev->dev, "invalid fh type1\n");
+ return -EINVAL;
+ }
+ zr364xx_stop_acquire(cam);
+ res = videobuf_streamoff(&cam->vb_vidq);
+ if (res < 0)
+ return res;
+ res_free(cam);
return 0;
}
@@ -622,28 +1201,19 @@ static int zr364xx_vidioc_streamoff(struct file *file, void *priv,
static int zr364xx_open(struct file *file)
{
struct video_device *vdev = video_devdata(file);
- struct zr364xx_camera *cam = video_get_drvdata(vdev);
+ struct zr364xx_camera *cam = video_drvdata(file);
struct usb_device *udev = cam->udev;
int i, err;
- DBG("zr364xx_open");
+ DBG("%s\n", __func__);
- mutex_lock(&cam->lock);
+ mutex_lock(&cam->open_lock);
if (cam->users) {
err = -EBUSY;
goto out;
}
- if (!cam->framebuf) {
- cam->framebuf = vmalloc_32(MAX_FRAME_SIZE * FRAMES);
- if (!cam->framebuf) {
- dev_err(&cam->udev->dev, "vmalloc_32 failed!\n");
- err = -ENOMEM;
- goto out;
- }
- }
-
for (i = 0; init[cam->method][i].size != -1; i++) {
err =
send_control_msg(udev, 1, init[cam->method][i].value,
@@ -659,6 +1229,14 @@ static int zr364xx_open(struct file *file)
cam->skip = 2;
cam->users++;
file->private_data = vdev;
+ cam->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ cam->fmt = formats;
+
+ videobuf_queue_vmalloc_init(&cam->vb_vidq, &zr364xx_video_qops,
+ NULL, &cam->slock,
+ cam->type,
+ V4L2_FIELD_NONE,
+ sizeof(struct zr364xx_buffer), cam);
/* Added some delay here, since opening/closing the camera quickly,
* like Ekiga does during its startup, can crash the webcam
@@ -667,28 +1245,72 @@ static int zr364xx_open(struct file *file)
err = 0;
out:
- mutex_unlock(&cam->lock);
+ mutex_unlock(&cam->open_lock);
+ DBG("%s: %d\n", __func__, err);
return err;
}
+static void zr364xx_destroy(struct zr364xx_camera *cam)
+{
+ unsigned long i;
+
+ if (!cam) {
+ printk(KERN_ERR KBUILD_MODNAME ", %s: no device\n", __func__);
+ return;
+ }
+ mutex_lock(&cam->open_lock);
+ if (cam->vdev)
+ video_unregister_device(cam->vdev);
+ cam->vdev = NULL;
+
+ /* stops the read pipe if it is running */
+ if (cam->b_acquire)
+ zr364xx_stop_acquire(cam);
+
+ zr364xx_stop_readpipe(cam);
+
+ /* release sys buffers */
+ for (i = 0; i < FRAMES; i++) {
+ if (cam->buffer.frame[i].lpvbits) {
+ DBG("vfree %p\n", cam->buffer.frame[i].lpvbits);
+ vfree(cam->buffer.frame[i].lpvbits);
+ }
+ cam->buffer.frame[i].lpvbits = NULL;
+ }
+
+ /* release transfer buffer */
+ kfree(cam->pipe->transfer_buffer);
+ cam->pipe->transfer_buffer = NULL;
+
+ DBG("%s\n", __func__);
+ mutex_unlock(&cam->open_lock);
+ kfree(cam);
+ cam = NULL;
+}
/* release the camera */
static int zr364xx_release(struct file *file)
{
- struct video_device *vdev = video_devdata(file);
struct zr364xx_camera *cam;
struct usb_device *udev;
int i, err;
- DBG("zr364xx_release");
+ DBG("%s\n", __func__);
+ cam = video_drvdata(file);
- if (vdev == NULL)
+ if (!cam)
return -ENODEV;
- cam = video_get_drvdata(vdev);
+ mutex_lock(&cam->open_lock);
udev = cam->udev;
- mutex_lock(&cam->lock);
+ /* turn off stream */
+ if (res_check(cam)) {
+ if (cam->b_acquire)
+ zr364xx_stop_acquire(cam);
+ videobuf_streamoff(&cam->vb_vidq);
+ res_free(cam);
+ }
cam->users--;
file->private_data = NULL;
@@ -711,40 +1333,43 @@ static int zr364xx_release(struct file *file)
err = 0;
out:
- mutex_unlock(&cam->lock);
+ mutex_unlock(&cam->open_lock);
+
return err;
}
static int zr364xx_mmap(struct file *file, struct vm_area_struct *vma)
{
- void *pos;
- unsigned long start = vma->vm_start;
- unsigned long size = vma->vm_end - vma->vm_start;
- struct video_device *vdev = video_devdata(file);
- struct zr364xx_camera *cam;
-
- DBG("zr364xx_mmap: %ld\n", size);
+ struct zr364xx_camera *cam = video_drvdata(file);
+ int ret;
- if (vdev == NULL)
+ if (cam == NULL) {
+ DBG("%s: cam == NULL\n", __func__);
return -ENODEV;
- cam = video_get_drvdata(vdev);
-
- pos = cam->framebuf;
- while (size > 0) {
- if (vm_insert_page(vma, start, vmalloc_to_page(pos)))
- return -EAGAIN;
- start += PAGE_SIZE;
- pos += PAGE_SIZE;
- if (size > PAGE_SIZE)
- size -= PAGE_SIZE;
- else
- size = 0;
}
+ DBG("mmap called, vma=0x%08lx\n", (unsigned long)vma);
- return 0;
+ ret = videobuf_mmap_mapper(&cam->vb_vidq, vma);
+
+ DBG("vma start=0x%08lx, size=%ld, ret=%d\n",
+ (unsigned long)vma->vm_start,
+ (unsigned long)vma->vm_end - (unsigned long)vma->vm_start, ret);
+ return ret;
}
+static unsigned int zr364xx_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct zr364xx_camera *cam = video_drvdata(file);
+ struct videobuf_queue *q = &cam->vb_vidq;
+ DBG("%s\n", __func__);
+
+ if (cam->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return POLLERR;
+
+ return videobuf_poll_stream(file, q, wait);
+}
static const struct v4l2_file_operations zr364xx_fops = {
.owner = THIS_MODULE,
@@ -753,6 +1378,7 @@ static const struct v4l2_file_operations zr364xx_fops = {
.read = zr364xx_read,
.mmap = zr364xx_mmap,
.ioctl = video_ioctl2,
+ .poll = zr364xx_poll,
};
static const struct v4l2_ioctl_ops zr364xx_ioctl_ops = {
@@ -769,6 +1395,10 @@ static const struct v4l2_ioctl_ops zr364xx_ioctl_ops = {
.vidioc_queryctrl = zr364xx_vidioc_queryctrl,
.vidioc_g_ctrl = zr364xx_vidioc_g_ctrl,
.vidioc_s_ctrl = zr364xx_vidioc_s_ctrl,
+ .vidioc_reqbufs = zr364xx_vidioc_reqbufs,
+ .vidioc_querybuf = zr364xx_vidioc_querybuf,
+ .vidioc_qbuf = zr364xx_vidioc_qbuf,
+ .vidioc_dqbuf = zr364xx_vidioc_dqbuf,
};
static struct video_device zr364xx_template = {
@@ -784,15 +1414,76 @@ static struct video_device zr364xx_template = {
/*******************/
/* USB integration */
/*******************/
+static int zr364xx_board_init(struct zr364xx_camera *cam)
+{
+ struct zr364xx_pipeinfo *pipe = cam->pipe;
+ unsigned long i;
+
+ DBG("board init: %p\n", cam);
+ memset(pipe, 0, sizeof(*pipe));
+ pipe->cam = cam;
+ pipe->transfer_size = BUFFER_SIZE;
+
+ pipe->transfer_buffer = kzalloc(pipe->transfer_size,
+ GFP_KERNEL);
+ if (pipe->transfer_buffer == NULL) {
+ DBG("out of memory!\n");
+ return -ENOMEM;
+ }
+
+ cam->b_acquire = 0;
+ cam->frame_count = 0;
+
+ /*** start create system buffers ***/
+ for (i = 0; i < FRAMES; i++) {
+ /* always allocate maximum size for system buffers */
+ cam->buffer.frame[i].lpvbits = vmalloc(MAX_FRAME_SIZE);
+
+ DBG("valloc %p, idx %lu, pdata %p\n",
+ &cam->buffer.frame[i], i,
+ cam->buffer.frame[i].lpvbits);
+ if (cam->buffer.frame[i].lpvbits == NULL) {
+ printk(KERN_INFO KBUILD_MODNAME ": out of memory. "
+ "Using less frames\n");
+ break;
+ }
+ }
+
+ if (i == 0) {
+ printk(KERN_INFO KBUILD_MODNAME ": out of memory. Aborting\n");
+ kfree(cam->pipe->transfer_buffer);
+ cam->pipe->transfer_buffer = NULL;
+ return -ENOMEM;
+ } else
+ cam->buffer.dwFrames = i;
+
+ /* make sure internal states are set */
+ for (i = 0; i < FRAMES; i++) {
+ cam->buffer.frame[i].ulState = ZR364XX_READ_IDLE;
+ cam->buffer.frame[i].cur_size = 0;
+ }
+
+ cam->cur_frame = 0;
+ cam->last_frame = -1;
+ /*** end create system buffers ***/
+
+ /* start read pipe */
+ zr364xx_start_readpipe(cam);
+ DBG(": board initialized\n");
+ return 0;
+}
static int zr364xx_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct usb_device *udev = interface_to_usbdev(intf);
struct zr364xx_camera *cam = NULL;
+ struct usb_host_interface *iface_desc;
+ struct usb_endpoint_descriptor *endpoint;
int err;
+ int i;
- DBG("probing...");
+ DBG("probing...\n");
dev_info(&intf->dev, DRIVER_DESC " compatible webcam plugged\n");
dev_info(&intf->dev, "model %04x:%04x detected\n",
@@ -811,22 +1502,17 @@ static int zr364xx_probe(struct usb_interface *intf,
if (cam->vdev == NULL) {
dev_err(&udev->dev, "cam->vdev: out of memory !\n");
kfree(cam);
+ cam = NULL;
return -ENOMEM;
}
memcpy(cam->vdev, &zr364xx_template, sizeof(zr364xx_template));
+ cam->vdev->parent = &intf->dev;
video_set_drvdata(cam->vdev, cam);
if (debug)
cam->vdev->debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG;
cam->udev = udev;
- if ((cam->buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL)) == NULL) {
- dev_info(&udev->dev, "cam->buffer: out of memory !\n");
- video_device_release(cam->vdev);
- kfree(cam);
- return -ENODEV;
- }
-
switch (mode) {
case 1:
dev_info(&udev->dev, "160x120 mode selected\n");
@@ -853,21 +1539,53 @@ static int zr364xx_probe(struct usb_interface *intf,
header2[439] = cam->width / 256;
header2[440] = cam->width % 256;
+ cam->users = 0;
cam->nb = 0;
- cam->brightness = 64;
+ cam->mode.brightness = 64;
mutex_init(&cam->lock);
+ mutex_init(&cam->open_lock);
+
+ DBG("dev: %p, udev %p interface %p\n", cam, cam->udev, intf);
+
+ /* set up the endpoint information */
+ iface_desc = intf->cur_altsetting;
+ DBG("num endpoints %d\n", iface_desc->desc.bNumEndpoints);
+ for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+ endpoint = &iface_desc->endpoint[i].desc;
+ if (!cam->read_endpoint && usb_endpoint_is_bulk_in(endpoint)) {
+ /* we found the bulk in endpoint */
+ cam->read_endpoint = endpoint->bEndpointAddress;
+ }
+ }
+ if (!cam->read_endpoint) {
+ dev_err(&intf->dev, "Could not find bulk-in endpoint\n");
+ return -ENOMEM;
+ }
+
+ /* v4l */
+ INIT_LIST_HEAD(&cam->vidq.active);
+ cam->vidq.cam = cam;
err = video_register_device(cam->vdev, VFL_TYPE_GRABBER, -1);
if (err) {
dev_err(&udev->dev, "video_register_device failed\n");
video_device_release(cam->vdev);
- kfree(cam->buffer);
kfree(cam);
+ cam = NULL;
return err;
}
usb_set_intfdata(intf, cam);
+ /* load zr364xx board specific */
+ err = zr364xx_board_init(cam);
+ if (err) {
+ spin_lock_init(&cam->slock);
+ return err;
+ }
+
+ spin_lock_init(&cam->slock);
+
dev_info(&udev->dev, DRIVER_DESC " controlling video device %d\n",
cam->vdev->num);
return 0;
@@ -877,17 +1595,10 @@ static int zr364xx_probe(struct usb_interface *intf,
static void zr364xx_disconnect(struct usb_interface *intf)
{
struct zr364xx_camera *cam = usb_get_intfdata(intf);
+ videobuf_mmap_free(&cam->vb_vidq);
usb_set_intfdata(intf, NULL);
dev_info(&intf->dev, DRIVER_DESC " webcam unplugged\n");
- if (cam->vdev)
- video_unregister_device(cam->vdev);
- cam->vdev = NULL;
- kfree(cam->buffer);
- cam->buffer = NULL;
- vfree(cam->framebuf);
- cam->framebuf = NULL;
- kfree(cam);
- cam = NULL;
+ zr364xx_destroy(cam);
}