summaryrefslogtreecommitdiff
path: root/linux/drivers
diff options
context:
space:
mode:
authorMauro Carvalho Chehab <mchehab@redhat.com>2008-10-04 14:51:50 -0300
committerMauro Carvalho Chehab <mchehab@redhat.com>2008-10-04 14:51:50 -0300
commit88778479f1fa5916aa079b9c17929716ebc182a6 (patch)
tree8960ed0705c81262a8b534cf182c6b56d07bb6b4 /linux/drivers
parent5fa51c4127709c7bcbcbbc6e14b4780908e36990 (diff)
parentedd1d19fe9c951f51b87b6a614f3e5abc2a6c12e (diff)
downloadmediapointer-dvb-s2-88778479f1fa5916aa079b9c17929716ebc182a6.tar.gz
mediapointer-dvb-s2-88778479f1fa5916aa079b9c17929716ebc182a6.tar.bz2
merge: http://linuxtv.org/hg/~tmerle/cinergyT2
From: Mauro Carvalho Chehab <mchehab@redhat.com> Priority: normal Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'linux/drivers')
-rw-r--r--linux/drivers/media/common/ir-keymaps.c133
-rw-r--r--linux/drivers/media/common/tuners/mt2060.c38
-rw-r--r--linux/drivers/media/common/tuners/tda827x.c12
-rw-r--r--linux/drivers/media/common/tuners/tda827x.h1
-rw-r--r--linux/drivers/media/common/tuners/tda8290.c4
-rw-r--r--linux/drivers/media/common/tuners/tda8290.h1
-rw-r--r--linux/drivers/media/common/tuners/tuner-xc2028.c74
-rw-r--r--linux/drivers/media/common/tuners/tuner-xc2028.h11
-rw-r--r--linux/drivers/media/common/tuners/xc5000.c8
-rw-r--r--linux/drivers/media/common/tuners/xc5000.h2
-rw-r--r--linux/drivers/media/dvb/Kconfig4
-rw-r--r--linux/drivers/media/dvb/Makefile2
-rw-r--r--linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c1
-rw-r--r--linux/drivers/media/dvb/dm1105/Kconfig18
-rw-r--r--linux/drivers/media/dvb/dm1105/Makefile3
-rw-r--r--linux/drivers/media/dvb/dm1105/dm1105.c919
-rw-r--r--linux/drivers/media/dvb/dvb-core/dmxdev.c16
-rw-r--r--linux/drivers/media/dvb/dvb-core/dvb_ca_en50221.c24
-rw-r--r--linux/drivers/media/dvb/dvb-core/dvb_ca_en50221.h6
-rw-r--r--linux/drivers/media/dvb/dvb-core/dvb_demux.c16
-rw-r--r--linux/drivers/media/dvb/dvb-core/dvb_frontend.c682
-rw-r--r--linux/drivers/media/dvb/dvb-core/dvb_frontend.h46
-rw-r--r--linux/drivers/media/dvb/dvb-usb/Kconfig23
-rw-r--r--linux/drivers/media/dvb/dvb-usb/Makefile3
-rw-r--r--linux/drivers/media/dvb/dvb-usb/af9015.c1474
-rw-r--r--linux/drivers/media/dvb/dvb-usb/af9015.h524
-rw-r--r--linux/drivers/media/dvb/dvb-usb/cxusb.c9
-rw-r--r--linux/drivers/media/dvb/dvb-usb/dib0700.h4
-rw-r--r--linux/drivers/media/dvb/dvb-usb/dib0700_core.c115
-rw-r--r--linux/drivers/media/dvb/dvb-usb/dib0700_devices.c190
-rw-r--r--linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h25
-rw-r--r--linux/drivers/media/dvb/dvb-usb/dw2102.c451
-rw-r--r--linux/drivers/media/dvb/dvb-usb/dw2102.h1
-rw-r--r--linux/drivers/media/dvb/frontends/Kconfig34
-rw-r--r--linux/drivers/media/dvb/frontends/Makefile5
-rw-r--r--linux/drivers/media/dvb/frontends/af9013.c1687
-rw-r--r--linux/drivers/media/dvb/frontends/af9013.h107
-rw-r--r--linux/drivers/media/dvb/frontends/af9013_priv.h869
-rw-r--r--linux/drivers/media/dvb/frontends/cx24116.c1359
-rw-r--r--linux/drivers/media/dvb/frontends/cx24116.h53
-rw-r--r--linux/drivers/media/dvb/frontends/s5h1420.c11
-rw-r--r--linux/drivers/media/dvb/frontends/s5h1420.h8
-rw-r--r--linux/drivers/media/dvb/frontends/si21xx.c1049
-rw-r--r--linux/drivers/media/dvb/frontends/si21xx.h37
-rw-r--r--linux/drivers/media/dvb/frontends/stb6000.c255
-rw-r--r--linux/drivers/media/dvb/frontends/stb6000.h51
-rw-r--r--linux/drivers/media/dvb/frontends/stv0288.c606
-rw-r--r--linux/drivers/media/dvb/frontends/stv0288.h65
-rw-r--r--linux/drivers/media/dvb/frontends/stv0299.c2
-rw-r--r--linux/drivers/media/dvb/frontends/stv0299.h13
-rw-r--r--linux/drivers/media/dvb/siano/sms-cards.c4
-rw-r--r--linux/drivers/media/dvb/ttpci/budget-av.c6
-rw-r--r--linux/drivers/media/dvb/ttpci/budget-ci.c5
-rw-r--r--linux/drivers/media/dvb/ttpci/budget-core.c6
-rw-r--r--linux/drivers/media/dvb/ttpci/budget-patch.c7
-rw-r--r--linux/drivers/media/dvb/ttpci/budget.c5
-rw-r--r--linux/drivers/media/dvb/ttpci/budget.h2
-rw-r--r--linux/drivers/media/dvb/ttusb-dec/ttusbdecfe.c16
-rw-r--r--linux/drivers/media/radio/Kconfig12
-rw-r--r--linux/drivers/media/radio/Makefile1
-rw-r--r--linux/drivers/media/radio/radio-mr800.c628
-rw-r--r--linux/drivers/media/video/au0828/au0828-cards.c2
-rw-r--r--linux/drivers/media/video/au0828/au0828-dvb.c3
-rw-r--r--linux/drivers/media/video/au0828/au0828.h3
-rw-r--r--linux/drivers/media/video/cx18/cx18-gpio.c2
-rw-r--r--linux/drivers/media/video/cx18/cx18-gpio.h2
-rw-r--r--linux/drivers/media/video/cx23885/cx23885-cards.c2
-rw-r--r--linux/drivers/media/video/cx23885/cx23885-dvb.c23
-rw-r--r--linux/drivers/media/video/cx23885/cx23885.h2
-rw-r--r--linux/drivers/media/video/cx88/Kconfig4
-rw-r--r--linux/drivers/media/video/cx88/cx88-cards.c295
-rw-r--r--linux/drivers/media/video/cx88/cx88-dvb.c193
-rw-r--r--linux/drivers/media/video/cx88/cx88-i2c.c16
-rw-r--r--linux/drivers/media/video/cx88/cx88-input.c33
-rw-r--r--linux/drivers/media/video/cx88/cx88-video.c5
-rw-r--r--linux/drivers/media/video/cx88/cx88.h10
-rw-r--r--linux/drivers/media/video/em28xx/em28xx-audio.c4
-rw-r--r--linux/drivers/media/video/em28xx/em28xx-cards.c42
-rw-r--r--linux/drivers/media/video/em28xx/em28xx-dvb.c12
-rw-r--r--linux/drivers/media/video/em28xx/em28xx.h2
-rw-r--r--linux/drivers/media/video/gspca/Kconfig206
-rw-r--r--linux/drivers/media/video/gspca/Makefile73
-rw-r--r--linux/drivers/media/video/gspca/conex.c3
-rw-r--r--linux/drivers/media/video/gspca/etoms.c3
-rw-r--r--linux/drivers/media/video/gspca/finepix.c466
-rw-r--r--linux/drivers/media/video/gspca/gspca.c153
-rw-r--r--linux/drivers/media/video/gspca/gspca.h18
-rw-r--r--linux/drivers/media/video/gspca/m5602/Kconfig11
-rw-r--r--linux/drivers/media/video/gspca/m5602/Makefile8
-rw-r--r--linux/drivers/media/video/gspca/m5602/m5602_bridge.h182
-rw-r--r--linux/drivers/media/video/gspca/m5602/m5602_core.c308
-rw-r--r--linux/drivers/media/video/gspca/m5602/m5602_mt9m111.c345
-rw-r--r--linux/drivers/media/video/gspca/m5602/m5602_mt9m111.h1020
-rw-r--r--linux/drivers/media/video/gspca/m5602/m5602_ov9650.c546
-rw-r--r--linux/drivers/media/video/gspca/m5602/m5602_ov9650.h501
-rw-r--r--linux/drivers/media/video/gspca/m5602/m5602_po1030.c336
-rw-r--r--linux/drivers/media/video/gspca/m5602/m5602_po1030.h478
-rw-r--r--linux/drivers/media/video/gspca/m5602/m5602_s5k4aa.c463
-rw-r--r--linux/drivers/media/video/gspca/m5602/m5602_s5k4aa.h368
-rw-r--r--linux/drivers/media/video/gspca/m5602/m5602_s5k83a.c333
-rw-r--r--linux/drivers/media/video/gspca/m5602/m5602_s5k83a.h444
-rw-r--r--linux/drivers/media/video/gspca/m5602/m5602_sensor.h76
-rw-r--r--linux/drivers/media/video/gspca/mars.c20
-rw-r--r--linux/drivers/media/video/gspca/ov519.c5
-rw-r--r--linux/drivers/media/video/gspca/pac207.c3
-rw-r--r--linux/drivers/media/video/gspca/pac7311.c3
-rw-r--r--linux/drivers/media/video/gspca/sonixb.c3
-rw-r--r--linux/drivers/media/video/gspca/sonixj.c79
-rw-r--r--linux/drivers/media/video/gspca/spca500.c3
-rw-r--r--linux/drivers/media/video/gspca/spca501.c3
-rw-r--r--linux/drivers/media/video/gspca/spca505.c3
-rw-r--r--linux/drivers/media/video/gspca/spca506.c3
-rw-r--r--linux/drivers/media/video/gspca/spca508.c3
-rw-r--r--linux/drivers/media/video/gspca/spca561.c12
-rw-r--r--linux/drivers/media/video/gspca/stk014.c5
-rw-r--r--linux/drivers/media/video/gspca/sunplus.c3
-rw-r--r--linux/drivers/media/video/gspca/t613.c5
-rw-r--r--linux/drivers/media/video/gspca/tv8532.c3
-rw-r--r--linux/drivers/media/video/gspca/vc032x.c5
-rw-r--r--linux/drivers/media/video/gspca/zc3xx.c3
-rw-r--r--linux/drivers/media/video/ir-kbd-i2c.c22
-rw-r--r--linux/drivers/media/video/ivtv/ivtv-gpio.c2
-rw-r--r--linux/drivers/media/video/ivtv/ivtv-gpio.h2
-rw-r--r--linux/drivers/media/video/pxa_camera.c24
-rw-r--r--linux/drivers/media/video/saa7134/saa7134-cards.c221
-rw-r--r--linux/drivers/media/video/saa7134/saa7134-core.c4
-rw-r--r--linux/drivers/media/video/saa7134/saa7134-dvb.c47
-rw-r--r--linux/drivers/media/video/saa7134/saa7134-input.c18
-rw-r--r--linux/drivers/media/video/saa7134/saa7134.h7
-rw-r--r--linux/drivers/media/video/stk-webcam.c84
-rw-r--r--linux/drivers/media/video/stk-webcam.h2
-rw-r--r--linux/drivers/media/video/tda9840.c4
-rw-r--r--linux/drivers/media/video/tuner-core.c10
-rw-r--r--linux/drivers/media/video/uvc/uvc_ctrl.c187
-rw-r--r--linux/drivers/media/video/uvc/uvc_driver.c9
-rw-r--r--linux/drivers/media/video/uvc/uvc_status.c11
-rw-r--r--linux/drivers/media/video/uvc/uvc_v4l2.c4
-rw-r--r--linux/drivers/media/video/uvc/uvc_video.c5
-rw-r--r--linux/drivers/media/video/uvc/uvcvideo.h4
139 files changed, 18941 insertions, 629 deletions
diff --git a/linux/drivers/media/common/ir-keymaps.c b/linux/drivers/media/common/ir-keymaps.c
index f1d172a3f..9c2494168 100644
--- a/linux/drivers/media/common/ir-keymaps.c
+++ b/linux/drivers/media/common/ir-keymaps.c
@@ -104,6 +104,56 @@ IR_KEYTAB_TYPE ir_codes_avermedia_dvbt[IR_KEYTAB_SIZE] = {
EXPORT_SYMBOL_GPL(ir_codes_avermedia_dvbt);
+/* Mauro Carvalho Chehab <mchehab@infradead.org> */
+IR_KEYTAB_TYPE ir_codes_avermedia_m135a[IR_KEYTAB_SIZE] = {
+ [0x00] = KEY_POWER2,
+ [0x2e] = KEY_DOT, /* '.' */
+ [0x01] = KEY_MODE, /* TV/FM */
+
+ [0x05] = KEY_1,
+ [0x06] = KEY_2,
+ [0x07] = KEY_3,
+ [0x09] = KEY_4,
+ [0x0a] = KEY_5,
+ [0x0b] = KEY_6,
+ [0x0d] = KEY_7,
+ [0x0e] = KEY_8,
+ [0x0f] = KEY_9,
+ [0x11] = KEY_0,
+
+ [0x13] = KEY_RIGHT, /* -> */
+ [0x12] = KEY_LEFT, /* <- */
+
+ [0x17] = KEY_SLEEP, /* Capturar Imagem */
+ [0x10] = KEY_SHUFFLE, /* Amostra */
+
+ /* FIXME: The keys bellow aren't ok */
+
+ [0x43] = KEY_CHANNELUP,
+ [0x42] = KEY_CHANNELDOWN,
+ [0x1f] = KEY_VOLUMEUP,
+ [0x1e] = KEY_VOLUMEDOWN,
+ [0x0c] = KEY_ENTER,
+
+ [0x14] = KEY_MUTE,
+ [0x08] = KEY_AUDIO,
+
+ [0x03] = KEY_TEXT,
+ [0x04] = KEY_EPG,
+ [0x2b] = KEY_TV2, /* TV2 */
+
+ [0x1d] = KEY_RED,
+ [0x1c] = KEY_YELLOW,
+ [0x41] = KEY_GREEN,
+ [0x40] = KEY_BLUE,
+
+ [0x1a] = KEY_PLAYPAUSE,
+ [0x19] = KEY_RECORD,
+ [0x18] = KEY_PLAY,
+ [0x1b] = KEY_STOP,
+};
+EXPORT_SYMBOL_GPL(ir_codes_avermedia_m135a);
+
/* Attila Kondoros <attila.kondoros@chello.hu> */
IR_KEYTAB_TYPE ir_codes_apac_viewcomp[IR_KEYTAB_SIZE] = {
@@ -2338,3 +2388,86 @@ IR_KEYTAB_TYPE ir_codes_avermedia_a16d[IR_KEYTAB_SIZE] = {
[0x2a] = KEY_MENU,
};
EXPORT_SYMBOL_GPL(ir_codes_avermedia_a16d);
+
+/* Encore ENLTV-FM v5.3
+ Mauro Carvalho Chehab <mchehab@infradead.org>
+ */
+IR_KEYTAB_TYPE ir_codes_encore_enltv_fm53[IR_KEYTAB_SIZE] = {
+ [0x10] = KEY_POWER2,
+ [0x06] = KEY_MUTE,
+
+ [0x09] = KEY_1,
+ [0x1d] = KEY_2,
+ [0x1f] = KEY_3,
+ [0x19] = KEY_4,
+ [0x1b] = KEY_5,
+ [0x11] = KEY_6,
+ [0x17] = KEY_7,
+ [0x12] = KEY_8,
+ [0x16] = KEY_9,
+ [0x48] = KEY_0,
+
+ [0x04] = KEY_LIST, /* -/-- */
+ [0x40] = KEY_LAST, /* recall */
+
+ [0x02] = KEY_MODE, /* TV/AV */
+ [0x05] = KEY_SHUFFLE, /* SNAPSHOT */
+
+ [0x4c] = KEY_CHANNELUP, /* UP */
+ [0x00] = KEY_CHANNELDOWN, /* DOWN */
+ [0x0d] = KEY_VOLUMEUP, /* RIGHT */
+ [0x15] = KEY_VOLUMEDOWN, /* LEFT */
+ [0x49] = KEY_ENTER, /* OK */
+
+ [0x54] = KEY_RECORD,
+ [0x4d] = KEY_PLAY, /* pause */
+
+ [0x1e] = KEY_UP, /* video setting */
+ [0x0e] = KEY_RIGHT, /* <- */
+ [0x1a] = KEY_LEFT, /* -> */
+
+ [0x0a] = KEY_DOWN, /* video default */
+ [0x0c] = KEY_ZOOM, /* hide pannel */
+ [0x47] = KEY_SLEEP, /* shutdown */
+};
+EXPORT_SYMBOL_GPL(ir_codes_encore_enltv_fm53);
+
+/* Zogis Real Audio 220 - 32 keys IR */
+IR_KEYTAB_TYPE ir_codes_real_audio_220_32_keys[IR_KEYTAB_SIZE] = {
+ [0x1c] = KEY_RADIO,
+ [0x12] = KEY_POWER2,
+
+ [0x01] = KEY_1,
+ [0x02] = KEY_2,
+ [0x03] = KEY_3,
+ [0x04] = KEY_4,
+ [0x05] = KEY_5,
+ [0x06] = KEY_6,
+ [0x07] = KEY_7,
+ [0x08] = KEY_8,
+ [0x09] = KEY_9,
+ [0x00] = KEY_0,
+
+ [0x0c] = KEY_VOLUMEUP,
+ [0x18] = KEY_VOLUMEDOWN,
+ [0x0b] = KEY_CHANNELUP,
+ [0x15] = KEY_CHANNELDOWN,
+ [0x16] = KEY_ENTER,
+
+ [0x11] = KEY_LIST, /* Source */
+ [0x0d] = KEY_AUDIO, /* stereo */
+
+ [0x0f] = KEY_PREVIOUS, /* Prev */
+ [0x1b] = KEY_PAUSE, /* Timeshift */
+ [0x1a] = KEY_NEXT, /* Next */
+
+ [0x0e] = KEY_STOP,
+ [0x1f] = KEY_PLAY,
+ [0x1e] = KEY_PLAYPAUSE, /* Pause */
+
+ [0x1d] = KEY_RECORD,
+ [0x13] = KEY_MUTE,
+ [0x19] = KEY_SHUFFLE, /* Snapshot */
+
+};
+EXPORT_SYMBOL_GPL(ir_codes_real_audio_220_32_keys);
diff --git a/linux/drivers/media/common/tuners/mt2060.c b/linux/drivers/media/common/tuners/mt2060.c
index 355817754..d1962bb24 100644
--- a/linux/drivers/media/common/tuners/mt2060.c
+++ b/linux/drivers/media/common/tuners/mt2060.c
@@ -177,6 +177,9 @@ static int mt2060_set_params(struct dvb_frontend *fe, struct dvb_frontend_parame
b[0] = REG_LO1B1;
b[1] = 0xFF;
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */
+
mt2060_writeregs(priv,b,2);
freq = params->frequency / 1000; // Hz -> kHz
@@ -240,6 +243,9 @@ static int mt2060_set_params(struct dvb_frontend *fe, struct dvb_frontend_parame
i++;
} while (i<10);
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */
+
return ret;
}
@@ -303,13 +309,35 @@ static int mt2060_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
static int mt2060_init(struct dvb_frontend *fe)
{
struct mt2060_priv *priv = fe->tuner_priv;
- return mt2060_writereg(priv, REG_VGAG, (priv->cfg->clock_out << 6) | 0x33);
+ int ret;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */
+
+ ret = mt2060_writereg(priv, REG_VGAG,
+ (priv->cfg->clock_out << 6) | 0x33);
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */
+
+ return ret;
}
static int mt2060_sleep(struct dvb_frontend *fe)
{
struct mt2060_priv *priv = fe->tuner_priv;
- return mt2060_writereg(priv, REG_VGAG, (priv->cfg->clock_out << 6) | 0x30);
+ int ret;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */
+
+ ret = mt2060_writereg(priv, REG_VGAG,
+ (priv->cfg->clock_out << 6) | 0x30);
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */
+
+ return ret;
}
static int mt2060_release(struct dvb_frontend *fe)
@@ -351,6 +379,9 @@ struct dvb_frontend * mt2060_attach(struct dvb_frontend *fe, struct i2c_adapter
priv->i2c = i2c;
priv->if1_freq = if1;
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */
+
if (mt2060_readreg(priv,REG_PART_REV,&id) != 0) {
kfree(priv);
return NULL;
@@ -367,6 +398,9 @@ struct dvb_frontend * mt2060_attach(struct dvb_frontend *fe, struct i2c_adapter
mt2060_calibrate(priv);
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */
+
return fe;
}
EXPORT_SYMBOL(mt2060_attach);
diff --git a/linux/drivers/media/common/tuners/tda827x.c b/linux/drivers/media/common/tuners/tda827x.c
index 763b0dd95..83a017c53 100644
--- a/linux/drivers/media/common/tuners/tda827x.c
+++ b/linux/drivers/media/common/tuners/tda827x.c
@@ -448,17 +448,19 @@ static void tda827xa_lna_gain(struct dvb_frontend *fe, int high,
else
arg = 0;
}
- if (priv->cfg->tuner_callback)
- priv->cfg->tuner_callback(priv->i2c_adap->algo_data,
- gp_func, arg);
+ if (fe->callback)
+ fe->callback(priv->i2c_adap->algo_data,
+ DVB_FRONTEND_COMPONENT_TUNER,
+ gp_func, arg);
buf[1] = high ? 0 : 1;
if (priv->cfg->config == 2)
buf[1] = high ? 1 : 0;
i2c_transfer(priv->i2c_adap, &msg, 1);
break;
case 3: /* switch with GPIO of saa713x */
- if (priv->cfg->tuner_callback)
- priv->cfg->tuner_callback(priv->i2c_adap->algo_data, 0, high);
+ if (fe->callback)
+ fe->callback(priv->i2c_adap->algo_data,
+ DVB_FRONTEND_COMPONENT_TUNER, 0, high);
break;
}
}
diff --git a/linux/drivers/media/common/tuners/tda827x.h b/linux/drivers/media/common/tuners/tda827x.h
index 7850a9a1d..7d72ce0a0 100644
--- a/linux/drivers/media/common/tuners/tda827x.h
+++ b/linux/drivers/media/common/tuners/tda827x.h
@@ -36,7 +36,6 @@ struct tda827x_config
/* interface to tda829x driver */
unsigned int config;
int switch_addr;
- int (*tuner_callback) (void *dev, int command, int arg);
void (*agcf)(struct dvb_frontend *fe);
};
diff --git a/linux/drivers/media/common/tuners/tda8290.c b/linux/drivers/media/common/tuners/tda8290.c
index 751845554..637849ec8 100644
--- a/linux/drivers/media/common/tuners/tda8290.c
+++ b/linux/drivers/media/common/tuners/tda8290.c
@@ -695,10 +695,8 @@ struct dvb_frontend *tda829x_attach(struct dvb_frontend *fe,
priv->i2c_props.addr = i2c_addr;
priv->i2c_props.adap = i2c_adap;
priv->i2c_props.name = "tda829x";
- if (cfg) {
+ if (cfg)
priv->cfg.config = cfg->lna_cfg;
- priv->cfg.tuner_callback = cfg->tuner_callback;
- }
if (tda8290_probe(&priv->i2c_props) == 0) {
priv->ver = TDA8290;
diff --git a/linux/drivers/media/common/tuners/tda8290.h b/linux/drivers/media/common/tuners/tda8290.h
index aa074f3f0..7e288b26f 100644
--- a/linux/drivers/media/common/tuners/tda8290.h
+++ b/linux/drivers/media/common/tuners/tda8290.h
@@ -22,7 +22,6 @@
struct tda829x_config {
unsigned int lna_cfg;
- int (*tuner_callback) (void *dev, int command, int arg);
unsigned int probe_tuner:1;
#define TDA829X_PROBE_TUNER 0
diff --git a/linux/drivers/media/common/tuners/tuner-xc2028.c b/linux/drivers/media/common/tuners/tuner-xc2028.c
index a3272e81e..7f43272ca 100644
--- a/linux/drivers/media/common/tuners/tuner-xc2028.c
+++ b/linux/drivers/media/common/tuners/tuner-xc2028.c
@@ -75,9 +75,6 @@ struct firmware_properties {
struct xc2028_data {
struct list_head hybrid_tuner_instance_list;
struct tuner_i2c_props i2c_props;
- int (*tuner_callback) (void *dev,
- int command, int arg);
- void *video_dev;
__u32 frequency;
struct firmware_description *firm;
@@ -496,6 +493,23 @@ ret:
return i;
}
+static inline int do_tuner_callback(struct dvb_frontend *fe, int cmd, int arg)
+{
+ struct xc2028_data *priv = fe->tuner_priv;
+
+ /* analog side (tuner-core) uses i2c_adap->algo_data.
+ * digital side is not guaranteed to have algo_data defined.
+ *
+ * digital side will always have fe->dvb defined.
+ * analog side (tuner-core) doesn't (yet) define fe->dvb.
+ */
+
+ return (!fe->callback) ? -EINVAL :
+ fe->callback(((fe->dvb) && (fe->dvb->priv)) ?
+ fe->dvb->priv : priv->i2c_props.adap->algo_data,
+ DVB_FRONTEND_COMPONENT_TUNER, cmd, arg);
+}
+
static int load_firmware(struct dvb_frontend *fe, unsigned int type,
v4l2_std_id *id)
{
@@ -534,8 +548,7 @@ static int load_firmware(struct dvb_frontend *fe, unsigned int type,
if (!size) {
/* Special callback command received */
- rc = priv->tuner_callback(priv->video_dev,
- XC2028_TUNER_RESET, 0);
+ rc = do_tuner_callback(fe, XC2028_TUNER_RESET, 0);
if (rc < 0) {
tuner_err("Error at RESET code %d\n",
(*p) & 0x7f);
@@ -546,8 +559,7 @@ static int load_firmware(struct dvb_frontend *fe, unsigned int type,
if (size >= 0xff00) {
switch (size) {
case 0xff00:
- rc = priv->tuner_callback(priv->video_dev,
- XC2028_RESET_CLK, 0);
+ rc = do_tuner_callback(fe, XC2028_RESET_CLK, 0);
if (rc < 0) {
tuner_err("Error at RESET code %d\n",
(*p) & 0x7f);
@@ -719,8 +731,7 @@ retry:
memset(&priv->cur_fw, 0, sizeof(priv->cur_fw));
/* Reset is needed before loading firmware */
- rc = priv->tuner_callback(priv->video_dev,
- XC2028_TUNER_RESET, 0);
+ rc = do_tuner_callback(fe, XC2028_TUNER_RESET, 0);
if (rc < 0)
goto fail;
@@ -937,7 +948,7 @@ static int generic_set_freq(struct dvb_frontend *fe, u32 freq /* in HZ */,
The reset CLK is needed only with tm6000.
Driver should work fine even if this fails.
*/
- priv->tuner_callback(priv->video_dev, XC2028_RESET_CLK, 1);
+ do_tuner_callback(fe, XC2028_RESET_CLK, 1);
msleep(10);
@@ -1006,11 +1017,6 @@ static int xc2028_set_params(struct dvb_frontend *fe,
tuner_dbg("%s called\n", __func__);
- if (priv->ctrl.d2633)
- type |= D2633;
- else
- type |= D2620;
-
switch(fe->ops.info.type) {
case FE_OFDM:
bw = p->u.ofdm.bandwidth;
@@ -1025,10 +1031,8 @@ static int xc2028_set_params(struct dvb_frontend *fe,
break;
case FE_ATSC:
bw = BANDWIDTH_6_MHZ;
- /* The only ATSC firmware (at least on v2.7) is D2633,
- so overrides ctrl->d2633 */
- type |= ATSC| D2633;
- type &= ~D2620;
+ /* The only ATSC firmware (at least on v2.7) is D2633 */
+ type |= ATSC | D2633;
break;
/* DVB-S is not supported */
default:
@@ -1061,6 +1065,28 @@ static int xc2028_set_params(struct dvb_frontend *fe,
tuner_err("error: bandwidth not supported.\n");
};
+ /*
+ Selects between D2633 or D2620 firmware.
+ It doesn't make sense for ATSC, since it should be D2633 on all cases
+ */
+ if (fe->ops.info.type != FE_ATSC) {
+ switch (priv->ctrl.type) {
+ case XC2028_D2633:
+ type |= D2633;
+ break;
+ case XC2028_D2620:
+ type |= D2620;
+ break;
+ case XC2028_AUTO:
+ default:
+ /* Zarlink seems to need D2633 */
+ if (priv->ctrl.demod == XC3028_FE_ZARLINK456)
+ type |= D2633;
+ else
+ type |= D2620;
+ }
+ }
+
/* All S-code tables need a 200kHz shift */
if (priv->ctrl.demod)
demod = priv->ctrl.demod + 200;
@@ -1213,20 +1239,10 @@ struct dvb_frontend *xc2028_attach(struct dvb_frontend *fe,
break;
case 1:
/* new tuner instance */
- priv->tuner_callback = cfg->callback;
priv->ctrl.max_len = 13;
mutex_init(&priv->lock);
- /* analog side (tuner-core) uses i2c_adap->algo_data.
- * digital side is not guaranteed to have algo_data defined.
- *
- * digital side will always have fe->dvb defined.
- * analog side (tuner-core) doesn't (yet) define fe->dvb.
- */
- priv->video_dev = ((fe->dvb) && (fe->dvb->priv)) ?
- fe->dvb->priv : cfg->i2c_adap->algo_data;
-
fe->tuner_priv = priv;
break;
case 2:
diff --git a/linux/drivers/media/common/tuners/tuner-xc2028.h b/linux/drivers/media/common/tuners/tuner-xc2028.h
index 216025cf5..19de7928a 100644
--- a/linux/drivers/media/common/tuners/tuner-xc2028.h
+++ b/linux/drivers/media/common/tuners/tuner-xc2028.h
@@ -10,6 +10,7 @@
#include "dvb_frontend.h"
#define XC2028_DEFAULT_FIRMWARE "xc3028-v27.fw"
+#define XC3028L_DEFAULT_FIRMWARE "xc3028L-v36.fw"
/* Dmoduler IF (kHz) */
#define XC3028_FE_DEFAULT 0 /* Don't load SCODE */
@@ -23,24 +24,28 @@
#define XC3028_FE_ZARLINK456 4560
#define XC3028_FE_CHINA 5200
+enum firmware_type {
+ XC2028_AUTO = 0, /* By default, auto-detects */
+ XC2028_D2633,
+ XC2028_D2620,
+};
+
struct xc2028_ctrl {
char *fname;
int max_len;
unsigned int scode_table;
unsigned int mts :1;
- unsigned int d2633 :1;
unsigned int input1:1;
unsigned int vhfbw7:1;
unsigned int uhfbw8:1;
unsigned int demod;
+ enum firmware_type type:2;
};
struct xc2028_config {
struct i2c_adapter *i2c_adap;
u8 i2c_addr;
- void *video_dev;
struct xc2028_ctrl *ctrl;
- int (*callback) (void *dev, int command, int arg);
};
/* xc2028 commands for callback */
diff --git a/linux/drivers/media/common/tuners/xc5000.c b/linux/drivers/media/common/tuners/xc5000.c
index 7d4d82d23..f3a3c47fe 100644
--- a/linux/drivers/media/common/tuners/xc5000.c
+++ b/linux/drivers/media/common/tuners/xc5000.c
@@ -58,8 +58,6 @@ struct xc5000_priv {
u32 bandwidth;
u8 video_standard;
u8 rf_mode;
-
- int (*tuner_callback) (void *priv, int command, int arg);
};
/* Misc Defines */
@@ -232,10 +230,11 @@ static void xc5000_TunerReset(struct dvb_frontend *fe)
dprintk(1, "%s()\n", __func__);
- if (priv->tuner_callback) {
- ret = priv->tuner_callback(((fe->dvb) && (fe->dvb->priv)) ?
+ if (fe->callback) {
+ ret = fe->callback(((fe->dvb) && (fe->dvb->priv)) ?
fe->dvb->priv :
priv->i2c_props.adap->algo_data,
+ DVB_FRONTEND_COMPONENT_TUNER,
XC5000_TUNER_RESET, 0);
if (ret)
printk(KERN_ERR "xc5000: reset failed\n");
@@ -994,7 +993,6 @@ struct dvb_frontend *xc5000_attach(struct dvb_frontend *fe,
/* new tuner instance */
priv->bandwidth = BANDWIDTH_6_MHZ;
priv->if_khz = cfg->if_khz;
- priv->tuner_callback = cfg->tuner_callback;
fe->tuner_priv = priv;
break;
diff --git a/linux/drivers/media/common/tuners/xc5000.h b/linux/drivers/media/common/tuners/xc5000.h
index fa0321cfd..cf1a558e0 100644
--- a/linux/drivers/media/common/tuners/xc5000.h
+++ b/linux/drivers/media/common/tuners/xc5000.h
@@ -30,8 +30,6 @@ struct i2c_adapter;
struct xc5000_config {
u8 i2c_address;
u32 if_khz;
-
- int (*tuner_callback) (void *priv, int command, int arg);
};
/* xc5000 callback command */
diff --git a/linux/drivers/media/dvb/Kconfig b/linux/drivers/media/dvb/Kconfig
index 52df38d28..0bcd85257 100644
--- a/linux/drivers/media/dvb/Kconfig
+++ b/linux/drivers/media/dvb/Kconfig
@@ -34,6 +34,10 @@ comment "Supported Pluto2 Adapters"
depends on DVB_CORE && PCI && I2C
source "drivers/media/dvb/pluto2/Kconfig"
+comment "Supported SDMC DM1105 Adapters"
+ depends on DVB_CORE && PCI && I2C
+source "drivers/media/dvb/dm1105/Kconfig"
+
comment "Supported DVB Frontends"
depends on DVB_CORE
source "drivers/media/dvb/frontends/Kconfig"
diff --git a/linux/drivers/media/dvb/Makefile b/linux/drivers/media/dvb/Makefile
index d6ba4d195..ea953a03e 100644
--- a/linux/drivers/media/dvb/Makefile
+++ b/linux/drivers/media/dvb/Makefile
@@ -2,4 +2,4 @@
# Makefile for the kernel multimedia device drivers.
#
-obj-y := dvb-core/ frontends/ ttpci/ ttusb-dec/ ttusb-budget/ b2c2/ bt8xx/ cinergyT2/ dvb-usb/ pluto2/ siano/
+obj-y := dvb-core/ frontends/ ttpci/ ttusb-dec/ ttusb-budget/ b2c2/ bt8xx/ cinergyT2/ dvb-usb/ pluto2/ siano/ dm1105/
diff --git a/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c b/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c
index 4eed783f4..a127a4175 100644
--- a/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c
+++ b/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c
@@ -491,6 +491,7 @@ static struct s5h1420_config skystar2_rev2_7_s5h1420_config = {
.demod_address = 0x53,
.invert = 1,
.repeated_start_workaround = 1,
+ .serial_mpeg = 1,
};
static struct itd1000_config skystar2_rev2_7_itd1000_config = {
diff --git a/linux/drivers/media/dvb/dm1105/Kconfig b/linux/drivers/media/dvb/dm1105/Kconfig
new file mode 100644
index 000000000..1332301ef
--- /dev/null
+++ b/linux/drivers/media/dvb/dm1105/Kconfig
@@ -0,0 +1,18 @@
+config DVB_DM1105
+ tristate "SDMC DM1105 based PCI cards"
+ depends on DVB_CORE && PCI && I2C
+ select DVB_PLL if !DVB_FE_CUSTOMISE
+ select DVB_STV0299 if !DVB_FE_CUSTOMISE
+ select DVB_STV0288 if !DVB_FE_CUSTOMISE
+ select DVB_STB6000 if !DVB_FE_CUSTOMISE
+ select DVB_CX24116 if !DVB_FE_CUSTOMISE
+ select DVB_SI21XX if !DVB_FE_CUSTOMISE
+ help
+ Support for cards based on the SDMC DM1105 PCI chip like
+ DvbWorld 2002
+
+ Since these cards have no MPEG decoder onboard, they transmit
+ only compressed MPEG data over the PCI bus, so you need
+ an external software decoder to watch TV on your computer.
+
+ Say Y or M if you own such a device and want to use it.
diff --git a/linux/drivers/media/dvb/dm1105/Makefile b/linux/drivers/media/dvb/dm1105/Makefile
new file mode 100644
index 000000000..8ac28b054
--- /dev/null
+++ b/linux/drivers/media/dvb/dm1105/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_DVB_DM1105) += dm1105.o
+
+EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends
diff --git a/linux/drivers/media/dvb/dm1105/dm1105.c b/linux/drivers/media/dvb/dm1105/dm1105.c
new file mode 100644
index 000000000..07ebe639f
--- /dev/null
+++ b/linux/drivers/media/dvb/dm1105/dm1105.c
@@ -0,0 +1,919 @@
+/*
+ * dm1105.c - driver for DVB cards based on SDMC DM1105 PCI chip
+ *
+ * Copyright (C) 2008 Igor M. Liplianin <liplianin@me.by>
+ *
+ * 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
+ * (at your option) 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/version.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
+#include <linux/input.h>
+#include <media/ir-common.h>
+
+#include "demux.h"
+#include "dmxdev.h"
+#include "dvb_demux.h"
+#include "dvb_frontend.h"
+#include "dvb_net.h"
+#include "dvbdev.h"
+#include "dvb-pll.h"
+
+#include "stv0299.h"
+#include "stv0288.h"
+#include "stb6000.h"
+#include "si21xx.h"
+#include "cx24116.h"
+#include "z0194a.h"
+
+/* ----------------------------------------------- */
+/*
+ * PCI ID's
+ */
+#ifndef PCI_VENDOR_ID_TRIGEM
+#define PCI_VENDOR_ID_TRIGEM 0x109f
+#endif
+#ifndef PCI_DEVICE_ID_DM1105
+#define PCI_DEVICE_ID_DM1105 0x036f
+#endif
+#ifndef PCI_DEVICE_ID_DW2002
+#define PCI_DEVICE_ID_DW2002 0x2002
+#endif
+#ifndef PCI_DEVICE_ID_DW2004
+#define PCI_DEVICE_ID_DW2004 0x2004
+#endif
+/* ----------------------------------------------- */
+/* sdmc dm1105 registers */
+
+/* TS Control */
+#define DM1105_TSCTR 0x00
+#define DM1105_DTALENTH 0x04
+
+/* GPIO Interface */
+#define DM1105_GPIOVAL 0x08
+#define DM1105_GPIOCTR 0x0c
+
+/* PID serial number */
+#define DM1105_PIDN 0x10
+
+/* Odd-even secret key select */
+#define DM1105_CWSEL 0x14
+
+/* Host Command Interface */
+#define DM1105_HOST_CTR 0x18
+#define DM1105_HOST_AD 0x1c
+
+/* PCI Interface */
+#define DM1105_CR 0x30
+#define DM1105_RST 0x34
+#define DM1105_STADR 0x38
+#define DM1105_RLEN 0x3c
+#define DM1105_WRP 0x40
+#define DM1105_INTCNT 0x44
+#define DM1105_INTMAK 0x48
+#define DM1105_INTSTS 0x4c
+
+/* CW Value */
+#define DM1105_ODD 0x50
+#define DM1105_EVEN 0x58
+
+/* PID Value */
+#define DM1105_PID 0x60
+
+/* IR Control */
+#define DM1105_IRCTR 0x64
+#define DM1105_IRMODE 0x68
+#define DM1105_SYSTEMCODE 0x6c
+#define DM1105_IRCODE 0x70
+
+/* Unknown Values */
+#define DM1105_ENCRYPT 0x74
+#define DM1105_VER 0x7c
+
+/* I2C Interface */
+#define DM1105_I2CCTR 0x80
+#define DM1105_I2CSTS 0x81
+#define DM1105_I2CDAT 0x82
+#define DM1105_I2C_RA 0x83
+/* ----------------------------------------------- */
+/* Interrupt Mask Bits */
+
+#define INTMAK_TSIRQM 0x01
+#define INTMAK_HIRQM 0x04
+#define INTMAK_IRM 0x08
+#define INTMAK_ALLMASK (INTMAK_TSIRQM | \
+ INTMAK_HIRQM | \
+ INTMAK_IRM)
+#define INTMAK_NONEMASK 0x00
+
+/* Interrupt Status Bits */
+#define INTSTS_TSIRQ 0x01
+#define INTSTS_HIRQ 0x04
+#define INTSTS_IR 0x08
+
+/* IR Control Bits */
+#define DM1105_IR_EN 0x01
+#define DM1105_SYS_CHK 0x02
+#define DM1105_REP_FLG 0x08
+
+/* EEPROM addr */
+#define IIC_24C01_addr 0xa0
+/* Max board count */
+#define DM1105_MAX 0x04
+
+#define DRIVER_NAME "dm1105"
+
+#define DM1105_DMA_PACKETS 47
+#define DM1105_DMA_PACKET_LENGTH (128*4)
+#define DM1105_DMA_BYTES (128 * 4 * DM1105_DMA_PACKETS)
+
+/* GPIO's for LNB power control */
+#define DM1105_LNB_MASK 0x00000000
+#define DM1105_LNB_13V 0x00010100
+#define DM1105_LNB_18V 0x00000100
+
+static int ir_debug;
+module_param(ir_debug, int, 0644);
+MODULE_PARM_DESC(ir_debug, "enable debugging information for IR decoding");
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+static u16 ir_codes_dm1105_nec[128] = {
+ [0x0a] = KEY_Q, /*power*/
+ [0x0c] = KEY_M, /*mute*/
+ [0x11] = KEY_1,
+ [0x12] = KEY_2,
+ [0x13] = KEY_3,
+ [0x14] = KEY_4,
+ [0x15] = KEY_5,
+ [0x16] = KEY_6,
+ [0x17] = KEY_7,
+ [0x18] = KEY_8,
+ [0x19] = KEY_9,
+ [0x10] = KEY_0,
+ [0x1c] = KEY_PAGEUP, /*ch+*/
+ [0x0f] = KEY_PAGEDOWN, /*ch-*/
+ [0x1a] = KEY_O, /*vol+*/
+ [0x0e] = KEY_Z, /*vol-*/
+ [0x04] = KEY_R, /*rec*/
+ [0x09] = KEY_D, /*fav*/
+ [0x08] = KEY_BACKSPACE, /*rewind*/
+ [0x07] = KEY_A, /*fast*/
+ [0x0b] = KEY_P, /*pause*/
+ [0x02] = KEY_ESC, /*cancel*/
+ [0x03] = KEY_G, /*tab*/
+ [0x00] = KEY_UP, /*up*/
+ [0x1f] = KEY_ENTER, /*ok*/
+ [0x01] = KEY_DOWN, /*down*/
+ [0x05] = KEY_C, /*cap*/
+ [0x06] = KEY_S, /*stop*/
+ [0x40] = KEY_F, /*full*/
+ [0x1e] = KEY_W, /*tvmode*/
+ [0x1b] = KEY_B, /*recall*/
+};
+
+/* infrared remote control */
+struct infrared {
+ u16 key_map[128];
+ struct input_dev *input_dev;
+ char input_phys[32];
+ struct tasklet_struct ir_tasklet;
+ u32 ir_command;
+};
+
+struct dm1105dvb {
+ /* pci */
+ struct pci_dev *pdev;
+ u8 __iomem *io_mem;
+
+ /* ir */
+ struct infrared ir;
+
+ /* dvb */
+ struct dmx_frontend hw_frontend;
+ struct dmx_frontend mem_frontend;
+ struct dmxdev dmxdev;
+ struct dvb_adapter dvb_adapter;
+ struct dvb_demux demux;
+ struct dvb_frontend *fe;
+ struct dvb_net dvbnet;
+ unsigned int full_ts_users;
+
+ /* i2c */
+ struct i2c_adapter i2c_adap;
+
+ /* dma */
+ dma_addr_t dma_addr;
+ unsigned char *ts_buf;
+ u32 wrp;
+ u32 buffer_size;
+ unsigned int PacketErrorCount;
+ unsigned int dmarst;
+ spinlock_t lock;
+
+};
+
+#define dm_io_mem(reg) ((unsigned long)(&dm1105dvb->io_mem[reg]))
+
+static struct dm1105dvb *dm1105dvb_local;
+
+static int dm1105_i2c_xfer(struct i2c_adapter *i2c_adap,
+ struct i2c_msg *msgs, int num)
+{
+ struct dm1105dvb *dm1105dvb ;
+
+ int addr, rc, i, j, k, len, byte, data;
+ u8 status;
+
+ dm1105dvb = i2c_adap->algo_data;
+ for (i = 0; i < num; i++) {
+ outb(0x00, dm_io_mem(DM1105_I2CCTR));
+ if (msgs[i].flags & I2C_M_RD) {
+ /* read bytes */
+ addr = msgs[i].addr << 1;
+ addr |= 1;
+ outb(addr, dm_io_mem(DM1105_I2CDAT));
+ for (byte = 0; byte < msgs[i].len; byte++)
+ outb(0, dm_io_mem(DM1105_I2CDAT + byte + 1));
+
+ outb(0x81 + msgs[i].len, dm_io_mem(DM1105_I2CCTR));
+ for (j = 0; j < 55; j++) {
+ mdelay(10);
+ status = inb(dm_io_mem(DM1105_I2CSTS));
+ if ((status & 0xc0) == 0x40)
+ break;
+ }
+ if (j >= 55)
+ return -1;
+
+ for (byte = 0; byte < msgs[i].len; byte++) {
+ rc = inb(dm_io_mem(DM1105_I2CDAT + byte + 1));
+ if (rc < 0)
+ goto err;
+ msgs[i].buf[byte] = rc;
+ }
+ } else {
+ if ((msgs[i].buf[0] == 0xf7) && (msgs[i].addr == 0x55)) {
+ /* prepaired for cx24116 firmware */
+ /* Write in small blocks */
+ len = msgs[i].len - 1;
+ k = 1;
+ do {
+ outb(msgs[i].addr << 1, dm_io_mem(DM1105_I2CDAT));
+ outb(0xf7, dm_io_mem(DM1105_I2CDAT + 1));
+ for (byte = 0; byte < (len > 48 ? 48 : len); byte++) {
+ data = msgs[i].buf[k+byte];
+ outb(data, dm_io_mem(DM1105_I2CDAT + byte + 2));
+ }
+ outb(0x82 + (len > 48 ? 48 : len), dm_io_mem(DM1105_I2CCTR));
+ for (j = 0; j < 25; j++) {
+ mdelay(10);
+ status = inb(dm_io_mem(DM1105_I2CSTS));
+ if ((status & 0xc0) == 0x40)
+ break;
+ }
+
+ if (j >= 25)
+ return -1;
+
+ k += 48;
+ len -= 48;
+ } while (len > 0);
+ } else {
+ /* write bytes */
+ outb(msgs[i].addr<<1, dm_io_mem(DM1105_I2CDAT));
+ for (byte = 0; byte < msgs[i].len; byte++) {
+ data = msgs[i].buf[byte];
+ outb(data, dm_io_mem(DM1105_I2CDAT + byte + 1));
+ }
+ outb(0x81 + msgs[i].len, dm_io_mem(DM1105_I2CCTR));
+ for (j = 0; j < 25; j++) {
+ mdelay(10);
+ status = inb(dm_io_mem(DM1105_I2CSTS));
+ if ((status & 0xc0) == 0x40)
+ break;
+ }
+
+ if (j >= 25)
+ return -1;
+ }
+ }
+ }
+ return num;
+ err:
+ return rc;
+}
+
+static u32 functionality(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm dm1105_algo = {
+ .master_xfer = dm1105_i2c_xfer,
+ .functionality = functionality,
+};
+
+static inline struct dm1105dvb *feed_to_dm1105dvb(struct dvb_demux_feed *feed)
+{
+ return container_of(feed->demux, struct dm1105dvb, demux);
+}
+
+static inline struct dm1105dvb *frontend_to_dm1105dvb(struct dvb_frontend *fe)
+{
+ return container_of(fe->dvb, struct dm1105dvb, dvb_adapter);
+}
+
+static int dm1105dvb_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
+{
+ struct dm1105dvb *dm1105dvb = frontend_to_dm1105dvb(fe);
+
+ if (voltage == SEC_VOLTAGE_18) {
+ outl(DM1105_LNB_MASK, dm_io_mem(DM1105_GPIOCTR));
+ outl(DM1105_LNB_18V, dm_io_mem(DM1105_GPIOVAL));
+ } else {
+ /*LNB ON-13V by default!*/
+ outl(DM1105_LNB_MASK, dm_io_mem(DM1105_GPIOCTR));
+ outl(DM1105_LNB_13V, dm_io_mem(DM1105_GPIOVAL));
+ }
+
+ return 0;
+}
+
+static void dm1105dvb_set_dma_addr(struct dm1105dvb *dm1105dvb)
+{
+ outl(cpu_to_le32(dm1105dvb->dma_addr), dm_io_mem(DM1105_STADR));
+}
+
+static int __devinit dm1105dvb_dma_map(struct dm1105dvb *dm1105dvb)
+{
+ dm1105dvb->ts_buf = pci_alloc_consistent(dm1105dvb->pdev, 6*DM1105_DMA_BYTES, &dm1105dvb->dma_addr);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 27)
+ return pci_dma_mapping_error(dm1105dvb->dma_addr);
+#else
+ return pci_dma_mapping_error(dm1105dvb->pdev, dm1105dvb->dma_addr);
+#endif
+}
+
+static void dm1105dvb_dma_unmap(struct dm1105dvb *dm1105dvb)
+{
+ pci_free_consistent(dm1105dvb->pdev, 6*DM1105_DMA_BYTES, dm1105dvb->ts_buf, dm1105dvb->dma_addr);
+}
+
+static void __devinit dm1105dvb_enable_irqs(struct dm1105dvb *dm1105dvb)
+{
+ outb(INTMAK_ALLMASK, dm_io_mem(DM1105_INTMAK));
+ outb(1, dm_io_mem(DM1105_CR));
+}
+
+static void dm1105dvb_disable_irqs(struct dm1105dvb *dm1105dvb)
+{
+ outb(INTMAK_IRM, dm_io_mem(DM1105_INTMAK));
+ outb(0, dm_io_mem(DM1105_CR));
+}
+
+static int dm1105dvb_start_feed(struct dvb_demux_feed *f)
+{
+ struct dm1105dvb *dm1105dvb = feed_to_dm1105dvb(f);
+
+ if (dm1105dvb->full_ts_users++ == 0)
+ dm1105dvb_enable_irqs(dm1105dvb);
+
+ return 0;
+}
+
+static int dm1105dvb_stop_feed(struct dvb_demux_feed *f)
+{
+ struct dm1105dvb *dm1105dvb = feed_to_dm1105dvb(f);
+
+ if (--dm1105dvb->full_ts_users == 0)
+ dm1105dvb_disable_irqs(dm1105dvb);
+
+ return 0;
+}
+
+/* ir tasklet */
+static void dm1105_emit_key(unsigned long parm)
+{
+ struct infrared *ir = (struct infrared *) parm;
+ u32 ircom = ir->ir_command;
+ u8 data;
+ u16 keycode;
+
+ data = (ircom >> 8) & 0x7f;
+
+ input_event(ir->input_dev, EV_MSC, MSC_RAW, (0x0000f8 << 16) | data);
+ input_event(ir->input_dev, EV_MSC, MSC_SCAN, data);
+ keycode = ir->key_map[data];
+
+ if (!keycode)
+ return;
+
+ input_event(ir->input_dev, EV_KEY, keycode, 1);
+ input_sync(ir->input_dev);
+ input_event(ir->input_dev, EV_KEY, keycode, 0);
+ input_sync(ir->input_dev);
+
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
+static irqreturn_t dm1105dvb_irq(int irq, void *dev_id, struct pt_regs *regs)
+#else
+static irqreturn_t dm1105dvb_irq(int irq, void *dev_id)
+#endif
+{
+ struct dm1105dvb *dm1105dvb = dev_id;
+ unsigned int piece;
+ unsigned int nbpackets;
+ u32 command;
+ u32 nextwrp;
+ u32 oldwrp;
+
+ /* Read-Write INSTS Ack's Interrupt for DM1105 chip 16.03.2008 */
+ unsigned int intsts = inb(dm_io_mem(DM1105_INTSTS));
+ outb(intsts, dm_io_mem(DM1105_INTSTS));
+
+ switch (intsts) {
+ case INTSTS_TSIRQ:
+ case (INTSTS_TSIRQ | INTSTS_IR):
+ nextwrp = inl(dm_io_mem(DM1105_WRP)) -
+ inl(dm_io_mem(DM1105_STADR)) ;
+ oldwrp = dm1105dvb->wrp;
+ spin_lock(&dm1105dvb->lock);
+ if (!((dm1105dvb->ts_buf[oldwrp] == 0x47) &&
+ (dm1105dvb->ts_buf[oldwrp + 188] == 0x47) &&
+ (dm1105dvb->ts_buf[oldwrp + 188 * 2] == 0x47))) {
+ dm1105dvb->PacketErrorCount++;
+ /* bad packet found */
+ if ((dm1105dvb->PacketErrorCount >= 2) &&
+ (dm1105dvb->dmarst == 0)) {
+ outb(1, dm_io_mem(DM1105_RST));
+ dm1105dvb->wrp = 0;
+ dm1105dvb->PacketErrorCount = 0;
+ dm1105dvb->dmarst = 0;
+ spin_unlock(&dm1105dvb->lock);
+ return IRQ_HANDLED;
+ }
+ }
+ if (nextwrp < oldwrp) {
+ piece = dm1105dvb->buffer_size - oldwrp;
+ memcpy(dm1105dvb->ts_buf + dm1105dvb->buffer_size, dm1105dvb->ts_buf, nextwrp);
+ nbpackets = (piece + nextwrp)/188;
+ } else {
+ nbpackets = (nextwrp - oldwrp)/188;
+ }
+ dvb_dmx_swfilter_packets(&dm1105dvb->demux, &dm1105dvb->ts_buf[oldwrp], nbpackets);
+ dm1105dvb->wrp = nextwrp;
+ spin_unlock(&dm1105dvb->lock);
+ break;
+ case INTSTS_IR:
+ command = inl(dm_io_mem(DM1105_IRCODE));
+ if (ir_debug)
+ printk("dm1105: received byte 0x%04x\n", command);
+
+ dm1105dvb->ir.ir_command = command;
+ tasklet_schedule(&dm1105dvb->ir.ir_tasklet);
+ break;
+ }
+ return IRQ_HANDLED;
+
+
+}
+
+/* register with input layer */
+static void input_register_keys(struct infrared *ir)
+{
+ int i;
+
+ memset(ir->input_dev->keybit, 0, sizeof(ir->input_dev->keybit));
+
+ for (i = 0; i < ARRAY_SIZE(ir->key_map); i++)
+ set_bit(ir->key_map[i], ir->input_dev->keybit);
+
+ ir->input_dev->keycode = ir->key_map;
+ ir->input_dev->keycodesize = sizeof(ir->key_map[0]);
+ ir->input_dev->keycodemax = ARRAY_SIZE(ir->key_map);
+}
+
+int __devinit dm1105_ir_init(struct dm1105dvb *dm1105)
+{
+ struct input_dev *input_dev;
+ int err;
+
+ dm1105dvb_local = dm1105;
+
+ input_dev = input_allocate_device();
+ if (!input_dev)
+ return -ENOMEM;
+
+ dm1105->ir.input_dev = input_dev;
+ snprintf(dm1105->ir.input_phys, sizeof(dm1105->ir.input_phys),
+ "pci-%s/ir0", pci_name(dm1105->pdev));
+
+ input_dev->evbit[0] = BIT(EV_KEY);
+ input_dev->name = "DVB on-card IR receiver";
+
+ input_dev->phys = dm1105->ir.input_phys;
+ input_dev->id.bustype = BUS_PCI;
+ input_dev->id.version = 2;
+ if (dm1105->pdev->subsystem_vendor) {
+ input_dev->id.vendor = dm1105->pdev->subsystem_vendor;
+ input_dev->id.product = dm1105->pdev->subsystem_device;
+ } else {
+ input_dev->id.vendor = dm1105->pdev->vendor;
+ input_dev->id.product = dm1105->pdev->device;
+ }
+ input_dev->dev.parent = &dm1105->pdev->dev;
+ /* initial keymap */
+ memcpy(dm1105->ir.key_map, ir_codes_dm1105_nec, sizeof dm1105->ir.key_map);
+ input_register_keys(&dm1105->ir);
+ err = input_register_device(input_dev);
+ if (err) {
+ input_free_device(input_dev);
+ return err;
+ }
+
+ tasklet_init(&dm1105->ir.ir_tasklet, dm1105_emit_key, (unsigned long) &dm1105->ir);
+
+ return 0;
+}
+
+
+void __devexit dm1105_ir_exit(struct dm1105dvb *dm1105)
+{
+ tasklet_kill(&dm1105->ir.ir_tasklet);
+ input_unregister_device(dm1105->ir.input_dev);
+
+}
+
+static int __devinit dm1105dvb_hw_init(struct dm1105dvb *dm1105dvb)
+{
+ dm1105dvb_disable_irqs(dm1105dvb);
+
+ outb(0, dm_io_mem(DM1105_HOST_CTR));
+
+ /*DATALEN 188,*/
+ outb(188, dm_io_mem(DM1105_DTALENTH));
+ /*TS_STRT TS_VALP MSBFIRST TS_MODE ALPAS TSPES*/
+ outw(0xc10a, dm_io_mem(DM1105_TSCTR));
+
+ /* map DMA and set address */
+ dm1105dvb_dma_map(dm1105dvb);
+ dm1105dvb_set_dma_addr(dm1105dvb);
+ /* big buffer */
+ outl(5*DM1105_DMA_BYTES, dm_io_mem(DM1105_RLEN));
+ outb(47, dm_io_mem(DM1105_INTCNT));
+
+ /* IR NEC mode enable */
+ outb((DM1105_IR_EN | DM1105_SYS_CHK), dm_io_mem(DM1105_IRCTR));
+ outb(0, dm_io_mem(DM1105_IRMODE));
+ outw(0, dm_io_mem(DM1105_SYSTEMCODE));
+
+ return 0;
+}
+
+static void dm1105dvb_hw_exit(struct dm1105dvb *dm1105dvb)
+{
+ dm1105dvb_disable_irqs(dm1105dvb);
+
+ /* IR disable */
+ outb(0, dm_io_mem(DM1105_IRCTR));
+ outb(INTMAK_NONEMASK, dm_io_mem(DM1105_INTMAK));
+
+ dm1105dvb_dma_unmap(dm1105dvb);
+}
+
+static struct stv0288_config earda_config = {
+ .demod_address = 0x68,
+ .min_delay_ms = 100,
+};
+
+static struct si21xx_config serit_config = {
+ .demod_address = 0x68,
+ .min_delay_ms = 100,
+
+};
+
+static struct cx24116_config serit_sp2633_config = {
+ .demod_address = 0x55,
+};
+
+static int __devinit frontend_init(struct dm1105dvb *dm1105dvb)
+{
+ int ret;
+
+ switch (dm1105dvb->pdev->subsystem_device) {
+ case PCI_DEVICE_ID_DW2002:
+ dm1105dvb->fe = dvb_attach(
+ stv0299_attach, &sharp_z0194a_config,
+ &dm1105dvb->i2c_adap);
+
+ if (dm1105dvb->fe) {
+ dm1105dvb->fe->ops.set_voltage =
+ dm1105dvb_set_voltage;
+ dvb_attach(dvb_pll_attach, dm1105dvb->fe, 0x60,
+ &dm1105dvb->i2c_adap, DVB_PLL_OPERA1);
+ }
+
+ if (!dm1105dvb->fe) {
+ dm1105dvb->fe = dvb_attach(
+ stv0288_attach, &earda_config,
+ &dm1105dvb->i2c_adap);
+ if (dm1105dvb->fe) {
+ dm1105dvb->fe->ops.set_voltage =
+ dm1105dvb_set_voltage;
+ dvb_attach(stb6000_attach, dm1105dvb->fe, 0x61,
+ &dm1105dvb->i2c_adap);
+ }
+ }
+
+ if (!dm1105dvb->fe) {
+ dm1105dvb->fe = dvb_attach(
+ si21xx_attach, &serit_config,
+ &dm1105dvb->i2c_adap);
+ if (dm1105dvb->fe)
+ dm1105dvb->fe->ops.set_voltage =
+ dm1105dvb_set_voltage;
+ }
+ break;
+ case PCI_DEVICE_ID_DW2004:
+ dm1105dvb->fe = dvb_attach(
+ cx24116_attach, &serit_sp2633_config,
+ &dm1105dvb->i2c_adap);
+ if (dm1105dvb->fe)
+ dm1105dvb->fe->ops.set_voltage = dm1105dvb_set_voltage;
+ break;
+ }
+
+ if (!dm1105dvb->fe) {
+ dev_err(&dm1105dvb->pdev->dev, "could not attach frontend\n");
+ return -ENODEV;
+ }
+
+ ret = dvb_register_frontend(&dm1105dvb->dvb_adapter, dm1105dvb->fe);
+ if (ret < 0) {
+ if (dm1105dvb->fe->ops.release)
+ dm1105dvb->fe->ops.release(dm1105dvb->fe);
+ dm1105dvb->fe = NULL;
+ return ret;
+ }
+
+ return 0;
+}
+
+static void __devinit dm1105dvb_read_mac(struct dm1105dvb *dm1105dvb, u8 *mac)
+{
+ static u8 command[1] = { 0x28 };
+
+ struct i2c_msg msg[] = {
+ { .addr = IIC_24C01_addr >> 1, .flags = 0,
+ .buf = command, .len = 1 },
+ { .addr = IIC_24C01_addr >> 1, .flags = I2C_M_RD,
+ .buf = mac, .len = 6 },
+ };
+
+ dm1105_i2c_xfer(&dm1105dvb->i2c_adap, msg , 2);
+ dev_info(&dm1105dvb->pdev->dev, "MAC %02x:%02x:%02x:%02x:%02x:%02x\n",
+ mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+}
+
+static int __devinit dm1105_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct dm1105dvb *dm1105dvb;
+ struct dvb_adapter *dvb_adapter;
+ struct dvb_demux *dvbdemux;
+ struct dmx_demux *dmx;
+ int ret = -ENOMEM;
+
+ dm1105dvb = kzalloc(sizeof(struct dm1105dvb), GFP_KERNEL);
+ if (!dm1105dvb)
+ goto out;
+
+ dm1105dvb->pdev = pdev;
+ dm1105dvb->buffer_size = 5 * DM1105_DMA_BYTES;
+ dm1105dvb->PacketErrorCount = 0;
+ dm1105dvb->dmarst = 0;
+
+ ret = pci_enable_device(pdev);
+ if (ret < 0)
+ goto err_kfree;
+
+ ret = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
+ if (ret < 0)
+ goto err_pci_disable_device;
+
+ pci_set_master(pdev);
+
+ ret = pci_request_regions(pdev, DRIVER_NAME);
+ if (ret < 0)
+ goto err_pci_disable_device;
+
+ dm1105dvb->io_mem = pci_iomap(pdev, 0, pci_resource_len(pdev, 0));
+ if (!dm1105dvb->io_mem) {
+ ret = -EIO;
+ goto err_pci_release_regions;
+ }
+
+ spin_lock_init(&dm1105dvb->lock);
+ pci_set_drvdata(pdev, dm1105dvb);
+
+ ret = request_irq(pdev->irq, dm1105dvb_irq, IRQF_SHARED, DRIVER_NAME, dm1105dvb);
+ if (ret < 0)
+ goto err_pci_iounmap;
+
+ ret = dm1105dvb_hw_init(dm1105dvb);
+ if (ret < 0)
+ goto err_free_irq;
+
+ /* i2c */
+ i2c_set_adapdata(&dm1105dvb->i2c_adap, dm1105dvb);
+ strcpy(dm1105dvb->i2c_adap.name, DRIVER_NAME);
+ dm1105dvb->i2c_adap.owner = THIS_MODULE;
+ dm1105dvb->i2c_adap.class = I2C_CLASS_TV_DIGITAL;
+ dm1105dvb->i2c_adap.dev.parent = &pdev->dev;
+ dm1105dvb->i2c_adap.algo = &dm1105_algo;
+ dm1105dvb->i2c_adap.algo_data = dm1105dvb;
+ ret = i2c_add_adapter(&dm1105dvb->i2c_adap);
+
+ if (ret < 0)
+ goto err_dm1105dvb_hw_exit;
+
+ /* dvb */
+ ret = dvb_register_adapter(&dm1105dvb->dvb_adapter, DRIVER_NAME,
+ THIS_MODULE, &pdev->dev, adapter_nr);
+ if (ret < 0)
+ goto err_i2c_del_adapter;
+
+ dvb_adapter = &dm1105dvb->dvb_adapter;
+
+ dm1105dvb_read_mac(dm1105dvb, dvb_adapter->proposed_mac);
+
+ dvbdemux = &dm1105dvb->demux;
+ dvbdemux->filternum = 256;
+ dvbdemux->feednum = 256;
+ dvbdemux->start_feed = dm1105dvb_start_feed;
+ dvbdemux->stop_feed = dm1105dvb_stop_feed;
+ dvbdemux->dmx.capabilities = (DMX_TS_FILTERING |
+ DMX_SECTION_FILTERING | DMX_MEMORY_BASED_FILTERING);
+ ret = dvb_dmx_init(dvbdemux);
+ if (ret < 0)
+ goto err_dvb_unregister_adapter;
+
+ dmx = &dvbdemux->dmx;
+ dm1105dvb->dmxdev.filternum = 256;
+ dm1105dvb->dmxdev.demux = dmx;
+ dm1105dvb->dmxdev.capabilities = 0;
+
+ ret = dvb_dmxdev_init(&dm1105dvb->dmxdev, dvb_adapter);
+ if (ret < 0)
+ goto err_dvb_dmx_release;
+
+ dm1105dvb->hw_frontend.source = DMX_FRONTEND_0;
+
+ ret = dmx->add_frontend(dmx, &dm1105dvb->hw_frontend);
+ if (ret < 0)
+ goto err_dvb_dmxdev_release;
+
+ dm1105dvb->mem_frontend.source = DMX_MEMORY_FE;
+
+ ret = dmx->add_frontend(dmx, &dm1105dvb->mem_frontend);
+ if (ret < 0)
+ goto err_remove_hw_frontend;
+
+ ret = dmx->connect_frontend(dmx, &dm1105dvb->hw_frontend);
+ if (ret < 0)
+ goto err_remove_mem_frontend;
+
+ ret = frontend_init(dm1105dvb);
+ if (ret < 0)
+ goto err_disconnect_frontend;
+
+ dvb_net_init(dvb_adapter, &dm1105dvb->dvbnet, dmx);
+ dm1105_ir_init(dm1105dvb);
+out:
+ return ret;
+
+err_disconnect_frontend:
+ dmx->disconnect_frontend(dmx);
+err_remove_mem_frontend:
+ dmx->remove_frontend(dmx, &dm1105dvb->mem_frontend);
+err_remove_hw_frontend:
+ dmx->remove_frontend(dmx, &dm1105dvb->hw_frontend);
+err_dvb_dmxdev_release:
+ dvb_dmxdev_release(&dm1105dvb->dmxdev);
+err_dvb_dmx_release:
+ dvb_dmx_release(dvbdemux);
+err_dvb_unregister_adapter:
+ dvb_unregister_adapter(dvb_adapter);
+err_i2c_del_adapter:
+ i2c_del_adapter(&dm1105dvb->i2c_adap);
+err_dm1105dvb_hw_exit:
+ dm1105dvb_hw_exit(dm1105dvb);
+err_free_irq:
+ free_irq(pdev->irq, dm1105dvb);
+err_pci_iounmap:
+ pci_iounmap(pdev, dm1105dvb->io_mem);
+err_pci_release_regions:
+ pci_release_regions(pdev);
+err_pci_disable_device:
+ pci_disable_device(pdev);
+err_kfree:
+ pci_set_drvdata(pdev, NULL);
+ kfree(dm1105dvb);
+ goto out;
+}
+
+static void __devexit dm1105_remove(struct pci_dev *pdev)
+{
+ struct dm1105dvb *dm1105dvb = pci_get_drvdata(pdev);
+ struct dvb_adapter *dvb_adapter = &dm1105dvb->dvb_adapter;
+ struct dvb_demux *dvbdemux = &dm1105dvb->demux;
+ struct dmx_demux *dmx = &dvbdemux->dmx;
+
+ dm1105_ir_exit(dm1105dvb);
+ dmx->close(dmx);
+ dvb_net_release(&dm1105dvb->dvbnet);
+ if (dm1105dvb->fe)
+ dvb_unregister_frontend(dm1105dvb->fe);
+
+ dmx->disconnect_frontend(dmx);
+ dmx->remove_frontend(dmx, &dm1105dvb->mem_frontend);
+ dmx->remove_frontend(dmx, &dm1105dvb->hw_frontend);
+ dvb_dmxdev_release(&dm1105dvb->dmxdev);
+ dvb_dmx_release(dvbdemux);
+ dvb_unregister_adapter(dvb_adapter);
+ if (&dm1105dvb->i2c_adap)
+ i2c_del_adapter(&dm1105dvb->i2c_adap);
+
+ dm1105dvb_hw_exit(dm1105dvb);
+ synchronize_irq(pdev->irq);
+ free_irq(pdev->irq, dm1105dvb);
+ pci_iounmap(pdev, dm1105dvb->io_mem);
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+ pci_set_drvdata(pdev, NULL);
+ kfree(dm1105dvb);
+}
+
+static struct pci_device_id dm1105_id_table[] __devinitdata = {
+ {
+ .vendor = PCI_VENDOR_ID_TRIGEM,
+ .device = PCI_DEVICE_ID_DM1105,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_DEVICE_ID_DW2002,
+ }, {
+ .vendor = PCI_VENDOR_ID_TRIGEM,
+ .device = PCI_DEVICE_ID_DM1105,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_DEVICE_ID_DW2004,
+ }, {
+ /* empty */
+ },
+};
+
+MODULE_DEVICE_TABLE(pci, dm1105_id_table);
+
+static struct pci_driver dm1105_driver = {
+ .name = DRIVER_NAME,
+ .id_table = dm1105_id_table,
+ .probe = dm1105_probe,
+ .remove = __devexit_p(dm1105_remove),
+};
+
+static int __init dm1105_init(void)
+{
+ return pci_register_driver(&dm1105_driver);
+}
+
+static void __exit dm1105_exit(void)
+{
+ pci_unregister_driver(&dm1105_driver);
+}
+
+module_init(dm1105_init);
+module_exit(dm1105_exit);
+
+MODULE_AUTHOR("Igor M. Liplianin <liplianin@me.by>");
+MODULE_DESCRIPTION("SDMC DM1105 DVB driver");
+MODULE_LICENSE("GPL");
diff --git a/linux/drivers/media/dvb/dvb-core/dmxdev.c b/linux/drivers/media/dvb/dvb-core/dmxdev.c
index 069d847ba..0c733c66a 100644
--- a/linux/drivers/media/dvb/dvb-core/dmxdev.c
+++ b/linux/drivers/media/dvb/dvb-core/dmxdev.c
@@ -364,15 +364,16 @@ static int dvb_dmxdev_section_callback(const u8 *buffer1, size_t buffer1_len,
enum dmx_success success)
{
struct dmxdev_filter *dmxdevfilter = filter->priv;
+ unsigned long flags;
int ret;
if (dmxdevfilter->buffer.error) {
wake_up(&dmxdevfilter->buffer.queue);
return 0;
}
- spin_lock(&dmxdevfilter->dev->lock);
+ spin_lock_irqsave(&dmxdevfilter->dev->lock, flags);
if (dmxdevfilter->state != DMXDEV_STATE_GO) {
- spin_unlock(&dmxdevfilter->dev->lock);
+ spin_unlock_irqrestore(&dmxdevfilter->dev->lock, flags);
return 0;
}
del_timer(&dmxdevfilter->timer);
@@ -391,7 +392,7 @@ static int dvb_dmxdev_section_callback(const u8 *buffer1, size_t buffer1_len,
}
if (dmxdevfilter->params.sec.flags & DMX_ONESHOT)
dmxdevfilter->state = DMXDEV_STATE_DONE;
- spin_unlock(&dmxdevfilter->dev->lock);
+ spin_unlock_irqrestore(&dmxdevfilter->dev->lock, flags);
wake_up(&dmxdevfilter->buffer.queue);
return 0;
}
@@ -403,11 +404,12 @@ static int dvb_dmxdev_ts_callback(const u8 *buffer1, size_t buffer1_len,
{
struct dmxdev_filter *dmxdevfilter = feed->priv;
struct dvb_ringbuffer *buffer;
+ unsigned long flags;
int ret;
- spin_lock(&dmxdevfilter->dev->lock);
+ spin_lock_irqsave(&dmxdevfilter->dev->lock, flags);
if (dmxdevfilter->params.pes.output == DMX_OUT_DECODER) {
- spin_unlock(&dmxdevfilter->dev->lock);
+ spin_unlock_irqrestore(&dmxdevfilter->dev->lock, flags);
return 0;
}
@@ -417,7 +419,7 @@ static int dvb_dmxdev_ts_callback(const u8 *buffer1, size_t buffer1_len,
else
buffer = &dmxdevfilter->dev->dvr_buffer;
if (buffer->error) {
- spin_unlock(&dmxdevfilter->dev->lock);
+ spin_unlock_irqrestore(&dmxdevfilter->dev->lock, flags);
wake_up(&buffer->queue);
return 0;
}
@@ -428,7 +430,7 @@ static int dvb_dmxdev_ts_callback(const u8 *buffer1, size_t buffer1_len,
dvb_ringbuffer_flush(buffer);
buffer->error = ret;
}
- spin_unlock(&dmxdevfilter->dev->lock);
+ spin_unlock_irqrestore(&dmxdevfilter->dev->lock, flags);
wake_up(&buffer->queue);
return 0;
}
diff --git a/linux/drivers/media/dvb/dvb-core/dvb_ca_en50221.c b/linux/drivers/media/dvb/dvb-core/dvb_ca_en50221.c
index f6016feab..19965a055 100644
--- a/linux/drivers/media/dvb/dvb-core/dvb_ca_en50221.c
+++ b/linux/drivers/media/dvb/dvb-core/dvb_ca_en50221.c
@@ -94,6 +94,9 @@ struct dvb_ca_slot {
/* current state of the CAM */
int slot_state;
+ /* mutex used for serializing access to one CI slot */
+ struct mutex slot_lock;
+
/* Number of CAMCHANGES that have occurred since last processing */
atomic_t camchange_count;
@@ -712,14 +715,20 @@ static int dvb_ca_en50221_write_data(struct dvb_ca_private *ca, int slot, u8 * b
dprintk("%s\n", __func__);
- // sanity check
+ /* sanity check */
if (bytes_write > ca->slot_info[slot].link_buf_size)
return -EINVAL;
- /* check if interface is actually waiting for us to read from it, or if a read is in progress */
+ /* it is possible we are dealing with a single buffer implementation,
+ thus if there is data available for read or if there is even a read
+ already in progress, we do nothing but awake the kernel thread to
+ process the data if necessary. */
if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0)
goto exitnowrite;
if (status & (STATUSREG_DA | STATUSREG_RE)) {
+ if (status & STATUSREG_DA)
+ dvb_ca_en50221_thread_wakeup(ca);
+
status = -EAGAIN;
goto exitnowrite;
}
@@ -988,6 +997,8 @@ static int dvb_ca_en50221_thread(void *data)
/* go through all the slots processing them */
for (slot = 0; slot < ca->slot_count; slot++) {
+ mutex_lock(&ca->slot_info[slot].slot_lock);
+
// check the cam status + deal with CAMCHANGEs
while (dvb_ca_en50221_check_camstatus(ca, slot)) {
/* clear down an old CI slot if necessary */
@@ -1123,7 +1134,7 @@ static int dvb_ca_en50221_thread(void *data)
case DVB_CA_SLOTSTATE_RUNNING:
if (!ca->open)
- continue;
+ break;
// poll slots for data
pktcount = 0;
@@ -1147,6 +1158,8 @@ static int dvb_ca_en50221_thread(void *data)
}
break;
}
+
+ mutex_unlock(&ca->slot_info[slot].slot_lock);
}
}
@@ -1182,6 +1195,7 @@ static int dvb_ca_en50221_io_do_ioctl(struct inode *inode, struct file *file,
switch (cmd) {
case CA_RESET:
for (slot = 0; slot < ca->slot_count; slot++) {
+ mutex_lock(&ca->slot_info[slot].slot_lock);
if (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_NONE) {
dvb_ca_en50221_slot_shutdown(ca, slot);
if (ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE)
@@ -1189,6 +1203,7 @@ static int dvb_ca_en50221_io_do_ioctl(struct inode *inode, struct file *file,
slot,
DVB_CA_EN50221_CAMCHANGE_INSERTED);
}
+ mutex_unlock(&ca->slot_info[slot].slot_lock);
}
ca->next_read_slot = 0;
dvb_ca_en50221_thread_wakeup(ca);
@@ -1309,7 +1324,9 @@ static ssize_t dvb_ca_en50221_io_write(struct file *file,
goto exit;
}
+ mutex_lock(&ca->slot_info[slot].slot_lock);
status = dvb_ca_en50221_write_data(ca, slot, fragbuf, fraglen + 2);
+ mutex_unlock(&ca->slot_info[slot].slot_lock);
if (status == (fraglen + 2)) {
written = 1;
break;
@@ -1665,6 +1682,7 @@ int dvb_ca_en50221_init(struct dvb_adapter *dvb_adapter,
ca->slot_info[i].slot_state = DVB_CA_SLOTSTATE_NONE;
atomic_set(&ca->slot_info[i].camchange_count, 0);
ca->slot_info[i].camchange_type = DVB_CA_EN50221_CAMCHANGE_REMOVED;
+ mutex_init(&ca->slot_info[i].slot_lock);
}
if (signal_pending(current)) {
diff --git a/linux/drivers/media/dvb/dvb-core/dvb_ca_en50221.h b/linux/drivers/media/dvb/dvb-core/dvb_ca_en50221.h
index 8467e63dd..7df2e1411 100644
--- a/linux/drivers/media/dvb/dvb-core/dvb_ca_en50221.h
+++ b/linux/drivers/media/dvb/dvb-core/dvb_ca_en50221.h
@@ -45,8 +45,10 @@ struct dvb_ca_en50221 {
/* the module owning this structure */
struct module* owner;
- /* NOTE: the read_*, write_* and poll_slot_status functions must use locks as
- * they may be called from several threads at once */
+ /* NOTE: the read_*, write_* and poll_slot_status functions will be
+ * called for different slots concurrently and need to use locks where
+ * and if appropriate. There will be no concurrent access to one slot.
+ */
/* functions for accessing attribute memory on the CAM */
int (*read_attribute_mem)(struct dvb_ca_en50221* ca, int slot, int address);
diff --git a/linux/drivers/media/dvb/dvb-core/dvb_demux.c b/linux/drivers/media/dvb/dvb-core/dvb_demux.c
index e2eca0b1f..a2c1fd5d2 100644
--- a/linux/drivers/media/dvb/dvb-core/dvb_demux.c
+++ b/linux/drivers/media/dvb/dvb-core/dvb_demux.c
@@ -399,7 +399,9 @@ static void dvb_dmx_swfilter_packet(struct dvb_demux *demux, const u8 *buf)
void dvb_dmx_swfilter_packets(struct dvb_demux *demux, const u8 *buf,
size_t count)
{
- spin_lock(&demux->lock);
+ unsigned long flags;
+
+ spin_lock_irqsave(&demux->lock, flags);
while (count--) {
if (buf[0] == 0x47)
@@ -407,16 +409,17 @@ void dvb_dmx_swfilter_packets(struct dvb_demux *demux, const u8 *buf,
buf += 188;
}
- spin_unlock(&demux->lock);
+ spin_unlock_irqrestore(&demux->lock, flags);
}
EXPORT_SYMBOL(dvb_dmx_swfilter_packets);
void dvb_dmx_swfilter(struct dvb_demux *demux, const u8 *buf, size_t count)
{
+ unsigned long flags;
int p = 0, i, j;
- spin_lock(&demux->lock);
+ spin_lock_irqsave(&demux->lock, flags);
if (demux->tsbufp) {
i = demux->tsbufp;
@@ -449,17 +452,18 @@ void dvb_dmx_swfilter(struct dvb_demux *demux, const u8 *buf, size_t count)
}
bailout:
- spin_unlock(&demux->lock);
+ spin_unlock_irqrestore(&demux->lock, flags);
}
EXPORT_SYMBOL(dvb_dmx_swfilter);
void dvb_dmx_swfilter_204(struct dvb_demux *demux, const u8 *buf, size_t count)
{
+ unsigned long flags;
int p = 0, i, j;
u8 tmppack[188];
- spin_lock(&demux->lock);
+ spin_lock_irqsave(&demux->lock, flags);
if (demux->tsbufp) {
i = demux->tsbufp;
@@ -500,7 +504,7 @@ void dvb_dmx_swfilter_204(struct dvb_demux *demux, const u8 *buf, size_t count)
}
bailout:
- spin_unlock(&demux->lock);
+ spin_unlock_irqrestore(&demux->lock, flags);
}
EXPORT_SYMBOL(dvb_dmx_swfilter_204);
diff --git a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c
index 60a94dc85..6f0dc252f 100644
--- a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c
+++ b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c
@@ -45,6 +45,7 @@
#include "dvb_frontend.h"
#include "dvbdev.h"
#include "compat.h"
+#include <linux/dvb/version.h>
static int dvb_frontend_debug;
static int dvb_shutdown_timeout;
@@ -766,6 +767,555 @@ static int dvb_frontend_check_parameters(struct dvb_frontend *fe,
return 0;
}
+struct dtv_cmds_h dtv_cmds[] = {
+ [DTV_TUNE] = {
+ .name = "DTV_TUNE",
+ .cmd = DTV_TUNE,
+ .set = 1,
+ },
+ [DTV_CLEAR] = {
+ .name = "DTV_CLEAR",
+ .cmd = DTV_CLEAR,
+ .set = 1,
+ },
+
+ /* Set */
+ [DTV_FREQUENCY] = {
+ .name = "DTV_FREQUENCY",
+ .cmd = DTV_FREQUENCY,
+ .set = 1,
+ },
+ [DTV_BANDWIDTH_HZ] = {
+ .name = "DTV_BANDWIDTH_HZ",
+ .cmd = DTV_BANDWIDTH_HZ,
+ .set = 1,
+ },
+ [DTV_MODULATION] = {
+ .name = "DTV_MODULATION",
+ .cmd = DTV_MODULATION,
+ .set = 1,
+ },
+ [DTV_INVERSION] = {
+ .name = "DTV_INVERSION",
+ .cmd = DTV_INVERSION,
+ .set = 1,
+ },
+ [DTV_DISEQC_MASTER] = {
+ .name = "DTV_DISEQC_MASTER",
+ .cmd = DTV_DISEQC_MASTER,
+ .set = 1,
+ .buffer = 1,
+ },
+ [DTV_SYMBOL_RATE] = {
+ .name = "DTV_SYMBOL_RATE",
+ .cmd = DTV_SYMBOL_RATE,
+ .set = 1,
+ },
+ [DTV_INNER_FEC] = {
+ .name = "DTV_INNER_FEC",
+ .cmd = DTV_INNER_FEC,
+ .set = 1,
+ },
+ [DTV_VOLTAGE] = {
+ .name = "DTV_VOLTAGE",
+ .cmd = DTV_VOLTAGE,
+ .set = 1,
+ },
+ [DTV_TONE] = {
+ .name = "DTV_TONE",
+ .cmd = DTV_TONE,
+ .set = 1,
+ },
+ [DTV_PILOT] = {
+ .name = "DTV_PILOT",
+ .cmd = DTV_PILOT,
+ .set = 1,
+ },
+ [DTV_ROLLOFF] = {
+ .name = "DTV_ROLLOFF",
+ .cmd = DTV_ROLLOFF,
+ .set = 1,
+ },
+ [DTV_DELIVERY_SYSTEM] = {
+ .name = "DTV_DELIVERY_SYSTEM",
+ .cmd = DTV_DELIVERY_SYSTEM,
+ .set = 1,
+ },
+#if 0
+ [DTV_ISDB_SEGMENT_IDX] = {
+ .name = "DTV_ISDB_SEGMENT_IDX",
+ .cmd = DTV_ISDB_SEGMENT_IDX,
+ .set = 1,
+ },
+ [DTV_ISDB_SEGMENT_WIDTH] = {
+ .name = "DTV_ISDB_SEGMENT_WIDTH",
+ .cmd = DTV_ISDB_SEGMENT_WIDTH,
+ .set = 1,
+ },
+#endif
+ /* Get */
+ [DTV_DISEQC_SLAVE_REPLY] = {
+ .name = "DTV_DISEQC_SLAVE_REPLY",
+ .cmd = DTV_DISEQC_SLAVE_REPLY,
+ .set = 0,
+ .buffer = 1,
+ },
+#if 0
+ [DTV_ISDB_LAYERA_FEC] = {
+ .name = "DTV_ISDB_LAYERA_FEC",
+ .cmd = DTV_ISDB_LAYERA_FEC,
+ .set = 0,
+ },
+ [DTV_ISDB_LAYERA_MODULATION] = {
+ .name = "DTV_ISDB_LAYERA_MODULATION",
+ .cmd = DTV_ISDB_LAYERA_MODULATION,
+ .set = 0,
+ },
+ [DTV_ISDB_LAYERA_SEGMENT_WIDTH] = {
+ .name = "DTV_ISDB_LAYERA_SEGMENT_WIDTH",
+ .cmd = DTV_ISDB_LAYERA_SEGMENT_WIDTH,
+ .set = 0,
+ },
+ [DTV_ISDB_LAYERB_FEC] = {
+ .name = "DTV_ISDB_LAYERB_FEC",
+ .cmd = DTV_ISDB_LAYERB_FEC,
+ .set = 0,
+ },
+ [DTV_ISDB_LAYERB_MODULATION] = {
+ .name = "DTV_ISDB_LAYERB_MODULATION",
+ .cmd = DTV_ISDB_LAYERB_MODULATION,
+ .set = 0,
+ },
+ [DTV_ISDB_LAYERB_SEGMENT_WIDTH] = {
+ .name = "DTV_ISDB_LAYERB_SEGMENT_WIDTH",
+ .cmd = DTV_ISDB_LAYERB_SEGMENT_WIDTH,
+ .set = 0,
+ },
+ [DTV_ISDB_LAYERC_FEC] = {
+ .name = "DTV_ISDB_LAYERC_FEC",
+ .cmd = DTV_ISDB_LAYERC_FEC,
+ .set = 0,
+ },
+ [DTV_ISDB_LAYERC_MODULATION] = {
+ .name = "DTV_ISDB_LAYERC_MODULATION",
+ .cmd = DTV_ISDB_LAYERC_MODULATION,
+ .set = 0,
+ },
+ [DTV_ISDB_LAYERC_SEGMENT_WIDTH] = {
+ .name = "DTV_ISDB_LAYERC_SEGMENT_WIDTH",
+ .cmd = DTV_ISDB_LAYERC_SEGMENT_WIDTH,
+ .set = 0,
+ },
+#endif
+ [DTV_API_VERSION] = {
+ .name = "DTV_API_VERSION",
+ .cmd = DTV_API_VERSION,
+ .set = 0,
+ },
+};
+
+void dtv_property_dump(struct dtv_property *tvp)
+{
+ int i;
+
+ printk("%s() tvp.cmd = 0x%08x (%s)\n"
+ ,__FUNCTION__
+ ,tvp->cmd
+ ,dtv_cmds[ tvp->cmd ].name);
+
+ if(dtv_cmds[ tvp->cmd ].buffer) {
+
+ printk("%s() tvp.u.buffer.len = 0x%02x\n"
+ ,__FUNCTION__
+ ,tvp->u.buffer.len);
+
+ for(i = 0; i < tvp->u.buffer.len; i++)
+ printk("%s() tvp.u.buffer.data[0x%02x] = 0x%02x\n"
+ ,__FUNCTION__
+ ,i
+ ,tvp->u.buffer.data[i]);
+
+ } else
+ printk("%s() tvp.u.data = 0x%08x\n", __FUNCTION__, tvp->u.data);
+}
+
+int is_legacy_delivery_system(fe_delivery_system_t s)
+{
+ if((s == SYS_UNDEFINED) || (s == SYS_DVBC_ANNEX_AC) ||
+ (s == SYS_DVBC_ANNEX_B) || (s == SYS_DVBT) || (s == SYS_DVBS))
+ return 1;
+
+ return 0;
+}
+
+/* Synchronise the legacy tuning parameters into the cache, so that demodulator
+ * drivers can use a single set_frontend tuning function, regardless of whether
+ * it's being used for the legacy or new API, reducing code and complexity.
+ */
+void dtv_property_cache_sync(struct dvb_frontend *fe, struct dvb_frontend_parameters *p)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+
+ printk("%s()\n", __FUNCTION__);
+
+ c->frequency = p->frequency;
+ c->inversion = p->inversion;
+
+ switch (fe->ops.info.type) {
+ case FE_QPSK:
+ c->symbol_rate = p->u.qpsk.symbol_rate;
+ c->fec_inner = p->u.qpsk.fec_inner;
+ c->delivery_system = SYS_DVBS;
+ break;
+ case FE_QAM:
+ c->symbol_rate = p->u.qam.symbol_rate;
+ c->fec_inner = p->u.qam.fec_inner;
+ c->modulation = p->u.qam.modulation;
+ c->delivery_system = SYS_DVBC_ANNEX_AC;
+ break;
+ case FE_OFDM:
+ if (p->u.ofdm.bandwidth == BANDWIDTH_6_MHZ)
+ c->bandwidth_hz = 6000000;
+ else if (p->u.ofdm.bandwidth == BANDWIDTH_7_MHZ)
+ c->bandwidth_hz = 7000000;
+ else if (p->u.ofdm.bandwidth == BANDWIDTH_8_MHZ)
+ c->bandwidth_hz = 8000000;
+ else
+ /* Including BANDWIDTH_AUTO */
+ c->bandwidth_hz = 0;
+ c->code_rate_HP = p->u.ofdm.code_rate_HP;
+ c->code_rate_LP = p->u.ofdm.code_rate_LP;
+ c->modulation = p->u.ofdm.constellation;
+ c->transmission_mode = p->u.ofdm.transmission_mode;
+ c->guard_interval = p->u.ofdm.guard_interval;
+ c->hierarchy = p->u.ofdm.hierarchy_information;
+ c->delivery_system = SYS_DVBT;
+ break;
+ case FE_ATSC:
+ c->modulation = p->u.vsb.modulation;
+ if ((c->modulation == VSB_8) || (c->modulation == VSB_16))
+ c->delivery_system = SYS_ATSC;
+ else
+ c->delivery_system = SYS_DVBC_ANNEX_B;
+ break;
+ }
+}
+
+/* Ensure the cached values are set correctly in the frontend
+ * legacy tuning structures, for the advanced tuning API.
+ */
+void dtv_property_legacy_params_sync(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+ struct dvb_frontend_parameters *p = &fepriv->parameters;
+
+ printk("%s()\n", __FUNCTION__);
+
+ p->frequency = c->frequency;
+ p->inversion = c->inversion;
+
+ switch (fe->ops.info.type) {
+ case FE_QPSK:
+ printk("%s() Preparing QPSK req\n", __FUNCTION__);
+ p->u.qpsk.symbol_rate = c->symbol_rate;
+ p->u.qpsk.fec_inner = c->fec_inner;
+ c->delivery_system = SYS_DVBS;
+ break;
+ case FE_QAM:
+ printk("%s() Preparing QAM req\n", __FUNCTION__);
+ p->u.qam.symbol_rate = c->symbol_rate;
+ p->u.qam.fec_inner = c->fec_inner;
+ p->u.qam.modulation = c->modulation;
+ c->delivery_system = SYS_DVBC_ANNEX_AC;
+ break;
+ case FE_OFDM:
+ printk("%s() Preparing OFDM req\n", __FUNCTION__);
+ if (c->bandwidth_hz == 6000000)
+ p->u.ofdm.bandwidth = BANDWIDTH_6_MHZ;
+ else if (c->bandwidth_hz == 7000000)
+ p->u.ofdm.bandwidth = BANDWIDTH_7_MHZ;
+ else if (c->bandwidth_hz == 8000000)
+ p->u.ofdm.bandwidth = BANDWIDTH_8_MHZ;
+ else
+ p->u.ofdm.bandwidth = BANDWIDTH_AUTO;
+ p->u.ofdm.code_rate_HP = c->code_rate_HP;
+ p->u.ofdm.code_rate_LP = c->code_rate_LP;
+ p->u.ofdm.constellation = c->modulation;
+ p->u.ofdm.transmission_mode = c->transmission_mode;
+ p->u.ofdm.guard_interval = c->guard_interval;
+ p->u.ofdm.hierarchy_information = c->hierarchy;
+ c->delivery_system = SYS_DVBT;
+ break;
+ case FE_ATSC:
+ printk("%s() Preparing VSB req\n", __FUNCTION__);
+ p->u.vsb.modulation = c->modulation;
+ if ((c->modulation == VSB_8) || (c->modulation == VSB_16))
+ c->delivery_system = SYS_ATSC;
+ else
+ c->delivery_system = SYS_DVBC_ANNEX_B;
+ break;
+ }
+}
+
+/* Ensure the cached values are set correctly in the frontend
+ * legacy tuning structures, for the legacy tuning API.
+ */
+void dtv_property_adv_params_sync(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+ struct dvb_frontend_parameters *p = &fepriv->parameters;
+
+ printk("%s()\n", __FUNCTION__);
+
+ p->frequency = c->frequency;
+ p->inversion = c->inversion;
+
+ switch(c->modulation) {
+ case _8PSK:
+ case _16APSK:
+ case NBC_QPSK:
+ p->u.qpsk.symbol_rate = c->symbol_rate;
+ p->u.qpsk.fec_inner = c->fec_inner;
+ break;
+ default:
+ break;
+ }
+
+ if(c->delivery_system == SYS_ISDBT) {
+ /* Fake out a generic DVB-T request so we pass validation in the ioctl */
+ p->frequency = c->frequency;
+ p->inversion = INVERSION_AUTO;
+ p->u.ofdm.constellation = QAM_AUTO;
+ p->u.ofdm.code_rate_HP = FEC_AUTO;
+ p->u.ofdm.code_rate_LP = FEC_AUTO;
+ p->u.ofdm.bandwidth = BANDWIDTH_AUTO;
+ p->u.ofdm.transmission_mode = TRANSMISSION_MODE_AUTO;
+ p->u.ofdm.guard_interval = GUARD_INTERVAL_AUTO;
+ p->u.ofdm.hierarchy_information = HIERARCHY_AUTO;
+ }
+}
+
+void dtv_property_cache_submit(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+
+ printk("%s()\n", __FUNCTION__);
+
+ /* For legacy delivery systems we don't need the delivery_system to
+ * be specified, but we populate the older structures from the cache
+ * so we can call set_frontend on older drivers.
+ */
+ if(is_legacy_delivery_system(c->delivery_system)) {
+
+ printk("%s() legacy, modulation = %d\n", __FUNCTION__, c->modulation);
+ dtv_property_legacy_params_sync(fe);
+
+ } else {
+ printk("%s() adv, modulation = %d\n", __FUNCTION__, c->modulation);
+
+ /* For advanced delivery systems / modulation types ...
+ * we seed the lecacy dvb_frontend_parameters structure
+ * so that the sanity checking code later in the IOCTL processing
+ * can validate our basic frequency ranges, symbolrates, modulation
+ * etc.
+ */
+ dtv_property_adv_params_sync(fe);
+ }
+}
+
+static int dvb_frontend_ioctl_legacy(struct inode *inode, struct file *file,
+ unsigned int cmd, void *parg);
+static int dvb_frontend_ioctl_properties(struct inode *inode, struct file *file,
+ unsigned int cmd, void *parg);
+
+int dtv_property_process_get(struct dvb_frontend *fe, struct dtv_property *tvp,
+ struct inode *inode, struct file *file)
+{
+ int r = 0;
+
+ printk("%s()\n", __FUNCTION__);
+
+ dtv_property_dump(tvp);
+
+ /* Allow the frontend to validate incoming properties */
+ if (fe->ops.get_property)
+ r = fe->ops.get_property(fe, tvp);
+
+ if (r < 0)
+ return r;
+
+ switch(tvp->cmd) {
+ case DTV_FREQUENCY:
+ tvp->u.data = fe->dtv_property_cache.frequency;
+ break;
+ case DTV_MODULATION:
+ tvp->u.data = fe->dtv_property_cache.modulation;
+ break;
+ case DTV_BANDWIDTH_HZ:
+ tvp->u.data = fe->dtv_property_cache.bandwidth_hz;
+ break;
+ case DTV_INVERSION:
+ tvp->u.data = fe->dtv_property_cache.inversion;
+ break;
+ case DTV_SYMBOL_RATE:
+ tvp->u.data = fe->dtv_property_cache.symbol_rate;
+ break;
+ case DTV_INNER_FEC:
+ tvp->u.data = fe->dtv_property_cache.fec_inner;
+ break;
+ case DTV_PILOT:
+ tvp->u.data = fe->dtv_property_cache.pilot;
+ break;
+ case DTV_ROLLOFF:
+ tvp->u.data = fe->dtv_property_cache.rolloff;
+ break;
+ case DTV_DELIVERY_SYSTEM:
+ tvp->u.data = fe->dtv_property_cache.delivery_system;
+ break;
+#if 0
+ /* ISDB-T Support here */
+ case DTV_ISDB_SEGMENT_IDX:
+ tvp->u.data = fe->dtv_property_cache.isdb_segment_idx;
+ break;
+ case DTV_ISDB_SEGMENT_WIDTH:
+ tvp->u.data = fe->dtv_property_cache.isdb_segment_width;
+ break;
+ case DTV_ISDB_LAYERA_FEC:
+ tvp->u.data = fe->dtv_property_cache.isdb_layera_fec;
+ break;
+ case DTV_ISDB_LAYERA_MODULATION:
+ tvp->u.data = fe->dtv_property_cache.isdb_layera_modulation;
+ break;
+ case DTV_ISDB_LAYERA_SEGMENT_WIDTH:
+ tvp->u.data = fe->dtv_property_cache.isdb_layera_segment_width;
+ break;
+ case DTV_ISDB_LAYERB_FEC:
+ tvp->u.data = fe->dtv_property_cache.isdb_layerb_fec;
+ break;
+ case DTV_ISDB_LAYERB_MODULATION:
+ tvp->u.data = fe->dtv_property_cache.isdb_layerb_modulation;
+ break;
+ case DTV_ISDB_LAYERB_SEGMENT_WIDTH:
+ tvp->u.data = fe->dtv_property_cache.isdb_layerb_segment_width;
+ break;
+ case DTV_ISDB_LAYERC_FEC:
+ tvp->u.data = fe->dtv_property_cache.isdb_layerc_fec;
+ break;
+ case DTV_ISDB_LAYERC_MODULATION:
+ tvp->u.data = fe->dtv_property_cache.isdb_layerc_modulation;
+ break;
+ case DTV_ISDB_LAYERC_SEGMENT_WIDTH:
+ tvp->u.data = fe->dtv_property_cache.isdb_layerc_segment_width;
+ break;
+#endif
+ case DTV_VOLTAGE:
+ tvp->u.data = fe->dtv_property_cache.voltage;
+ break;
+ case DTV_TONE:
+ tvp->u.data = fe->dtv_property_cache.sectone;
+ break;
+ case DTV_API_VERSION:
+ tvp->u.data = (DVB_API_VERSION << 8) | DVB_API_VERSION_MINOR;
+ break;
+ default:
+ r = -1;
+ }
+
+ return r;
+}
+
+int dtv_property_process_set(struct dvb_frontend *fe, struct dtv_property *tvp,
+ struct inode *inode, struct file *file)
+{
+ int r = 0;
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+ printk("%s()\n", __FUNCTION__);
+ dtv_property_dump(tvp);
+
+ /* Allow the frontend to validate incoming properties */
+ if (fe->ops.set_property)
+ r = fe->ops.set_property(fe, tvp);
+
+ if (r < 0)
+ return r;
+
+ switch(tvp->cmd) {
+ case DTV_CLEAR:
+ /* Reset a cache of data specific to the frontend here. This does
+ * not effect hardware.
+ */
+ printk("%s() Flushing property cache\n", __FUNCTION__);
+ memset(&fe->dtv_property_cache, 0, sizeof(struct dtv_frontend_properties));
+ fe->dtv_property_cache.state = tvp->cmd;
+ fe->dtv_property_cache.delivery_system = SYS_UNDEFINED;
+ break;
+ case DTV_TUNE:
+ /* interpret the cache of data, build either a traditional frontend
+ * tunerequest so we can pass validation in the FE_SET_FRONTEND
+ * ioctl.
+ */
+ fe->dtv_property_cache.state = tvp->cmd;
+ printk("%s() Finalised property cache\n", __FUNCTION__);
+ dtv_property_cache_submit(fe);
+
+ r |= dvb_frontend_ioctl_legacy(inode, file, FE_SET_FRONTEND,
+ &fepriv->parameters);
+ break;
+ case DTV_FREQUENCY:
+ fe->dtv_property_cache.frequency = tvp->u.data;
+ break;
+ case DTV_MODULATION:
+ fe->dtv_property_cache.modulation = tvp->u.data;
+ break;
+ case DTV_BANDWIDTH_HZ:
+ fe->dtv_property_cache.bandwidth_hz = tvp->u.data;
+ break;
+ case DTV_INVERSION:
+ fe->dtv_property_cache.inversion = tvp->u.data;
+ break;
+ case DTV_SYMBOL_RATE:
+ fe->dtv_property_cache.symbol_rate = tvp->u.data;
+ break;
+ case DTV_INNER_FEC:
+ fe->dtv_property_cache.fec_inner = tvp->u.data;
+ break;
+ case DTV_PILOT:
+ fe->dtv_property_cache.pilot = tvp->u.data;
+ break;
+ case DTV_ROLLOFF:
+ fe->dtv_property_cache.rolloff = tvp->u.data;
+ break;
+ case DTV_DELIVERY_SYSTEM:
+ fe->dtv_property_cache.delivery_system = tvp->u.data;
+ break;
+#if 0
+ /* ISDB-T Support here */
+ case DTV_ISDB_SEGMENT_IDX:
+ fe->dtv_property_cache.isdb_segment_idx = tvp->u.data;
+ break;
+ case DTV_ISDB_SEGMENT_WIDTH:
+ fe->dtv_property_cache.isdb_segment_width = tvp->u.data;
+ break;
+#endif
+ case DTV_VOLTAGE:
+ fe->dtv_property_cache.voltage = tvp->u.data;
+ r = dvb_frontend_ioctl_legacy(inode, file, FE_SET_VOLTAGE,
+ (void *)fe->dtv_property_cache.voltage);
+ break;
+ case DTV_TONE:
+ fe->dtv_property_cache.sectone = tvp->u.data;
+ r = dvb_frontend_ioctl_legacy(inode, file, FE_SET_TONE,
+ (void *)fe->dtv_property_cache.sectone);
+ break;
+ default:
+ r = -1;
+ }
+
+ return r;
+}
+
static int dvb_frontend_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, void *parg)
{
@@ -787,6 +1337,116 @@ static int dvb_frontend_ioctl(struct inode *inode, struct file *file,
if (down_interruptible (&fepriv->sem))
return -ERESTARTSYS;
+ if ((cmd == FE_SET_PROPERTY) || (cmd == FE_GET_PROPERTY))
+ err = dvb_frontend_ioctl_properties(inode, file, cmd, parg);
+ else {
+ fe->dtv_property_cache.state = DTV_UNDEFINED;
+ err = dvb_frontend_ioctl_legacy(inode, file, cmd, parg);
+ }
+
+ up(&fepriv->sem);
+ return err;
+}
+
+static int dvb_frontend_ioctl_properties(struct inode *inode, struct file *file,
+ unsigned int cmd, void *parg)
+{
+ struct dvb_device *dvbdev = file->private_data;
+ struct dvb_frontend *fe = dvbdev->priv;
+ int err = 0;
+
+ struct dtv_properties *tvps = NULL;
+ struct dtv_property *tvp = NULL;
+ int i;
+
+ dprintk("%s\n", __func__);
+
+ if(cmd == FE_SET_PROPERTY) {
+ printk("%s() FE_SET_PROPERTY\n", __FUNCTION__);
+
+ tvps = (struct dtv_properties __user *)parg;
+
+ printk("%s() properties.num = %d\n", __FUNCTION__, tvps->num);
+ printk("%s() properties.props = %p\n", __FUNCTION__, tvps->props);
+
+ /* Put an arbitrary limit on the number of messages that can
+ * be sent at once */
+ if (tvps->num > DTV_IOCTL_MAX_MSGS)
+ return -EINVAL;
+
+ tvp = (struct dtv_property *) kmalloc(tvps->num *
+ sizeof(struct dtv_property), GFP_KERNEL);
+ if (!tvp) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ if (copy_from_user(tvp, tvps->props, tvps->num * sizeof(struct dtv_property))) {
+ err = -EFAULT;
+ goto out;
+ }
+
+ for (i = 0; i < tvps->num; i++) {
+ (tvp + i)->result = dtv_property_process_set(fe, tvp + i, inode, file);
+ err |= (tvp + i)->result;
+ }
+
+ if(fe->dtv_property_cache.state == DTV_TUNE) {
+ printk("%s() Property cache is full, tuning\n", __FUNCTION__);
+ }
+
+ } else
+ if(cmd == FE_GET_PROPERTY) {
+ printk("%s() FE_GET_PROPERTY\n", __FUNCTION__);
+
+ tvps = (struct dtv_properties __user *)parg;
+
+ printk("%s() properties.num = %d\n", __FUNCTION__, tvps->num);
+ printk("%s() properties.props = %p\n", __FUNCTION__, tvps->props);
+
+ /* Put an arbitrary limit on the number of messages that can
+ * be sent at once */
+ if (tvps->num > DTV_IOCTL_MAX_MSGS)
+ return -EINVAL;
+
+ tvp = (struct dtv_property *) kmalloc(tvps->num *
+ sizeof(struct dtv_property), GFP_KERNEL);
+ if (!tvp) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ if (copy_from_user(tvp, tvps->props, tvps->num * sizeof(struct dtv_property))) {
+ err = -EFAULT;
+ goto out;
+ }
+
+ for (i = 0; i < tvps->num; i++) {
+ (tvp + i)->result = dtv_property_process_get(fe, tvp + i, inode, file);
+ err |= (tvp + i)->result;
+ }
+
+ if (copy_to_user(tvps->props, tvp, tvps->num * sizeof(struct dtv_property))) {
+ err = -EFAULT;
+ goto out;
+ }
+
+ } else
+ err = -EOPNOTSUPP;
+
+out:
+ kfree(tvp);
+ return err;
+}
+
+static int dvb_frontend_ioctl_legacy(struct inode *inode, struct file *file,
+ unsigned int cmd, void *parg)
+{
+ struct dvb_device *dvbdev = file->private_data;
+ struct dvb_frontend *fe = dvbdev->priv;
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+ int err = -EOPNOTSUPP;
+
switch (cmd) {
case FE_GET_INFO: {
struct dvb_frontend_info* info = parg;
@@ -953,13 +1613,21 @@ static int dvb_frontend_ioctl(struct inode *inode, struct file *file,
case FE_SET_FRONTEND: {
struct dvb_frontend_tune_settings fetunesettings;
- if (dvb_frontend_check_parameters(fe, parg) < 0) {
- err = -EINVAL;
- break;
- }
+ if(fe->dtv_property_cache.state == DTV_TUNE) {
+ if (dvb_frontend_check_parameters(fe, &fepriv->parameters) < 0) {
+ err = -EINVAL;
+ break;
+ }
+ } else {
+ if (dvb_frontend_check_parameters(fe, parg) < 0) {
+ err = -EINVAL;
+ break;
+ }
- memcpy (&fepriv->parameters, parg,
- sizeof (struct dvb_frontend_parameters));
+ memcpy (&fepriv->parameters, parg,
+ sizeof (struct dvb_frontend_parameters));
+ dtv_property_cache_sync(fe, &fepriv->parameters);
+ }
memset(&fetunesettings, 0, sizeof(struct dvb_frontend_tune_settings));
memcpy(&fetunesettings.parameters, parg,
@@ -1038,10 +1706,10 @@ static int dvb_frontend_ioctl(struct inode *inode, struct file *file,
break;
};
- up (&fepriv->sem);
return err;
}
+
static unsigned int dvb_frontend_poll(struct file *file, struct poll_table_struct *wait)
{
struct dvb_device *dvbdev = file->private_data;
diff --git a/linux/drivers/media/dvb/dvb-core/dvb_frontend.h b/linux/drivers/media/dvb/dvb-core/dvb_frontend.h
index aa4133f0b..a0c591b3b 100644
--- a/linux/drivers/media/dvb/dvb-core/dvb_frontend.h
+++ b/linux/drivers/media/dvb/dvb-core/dvb_frontend.h
@@ -169,6 +169,9 @@ struct dvb_frontend_ops {
struct dvb_tuner_ops tuner_ops;
struct analog_demod_ops analog_ops;
+
+ int (*set_property)(struct dvb_frontend* fe, struct dtv_property* tvp);
+ int (*get_property)(struct dvb_frontend* fe, struct dtv_property* tvp);
};
#define MAX_EVENT 8
@@ -182,6 +185,46 @@ struct dvb_fe_events {
struct mutex mtx;
};
+struct dtv_frontend_properties {
+
+ /* Cache State */
+ u32 state;
+
+ u32 frequency;
+ fe_modulation_t modulation;
+
+ fe_sec_voltage_t voltage;
+ fe_sec_tone_mode_t sectone;
+ fe_spectral_inversion_t inversion;
+ fe_code_rate_t fec_inner;
+ fe_transmit_mode_t transmission_mode;
+ u32 bandwidth_hz; /* 0 = AUTO */
+ fe_guard_interval_t guard_interval;
+ fe_hierarchy_t hierarchy;
+ u32 symbol_rate;
+ fe_code_rate_t code_rate_HP;
+ fe_code_rate_t code_rate_LP;
+
+ fe_pilot_t pilot;
+ fe_rolloff_t rolloff;
+
+ fe_delivery_system_t delivery_system;
+#if 0
+ /* ISDB-T specifics */
+ u32 isdb_segment_idx;
+ u32 isdb_segment_width;
+ fe_code_rate_t isdb_layera_fec;
+ fe_modulation_t isdb_layera_modulation;
+ u32 isdb_layera_segment_width;
+ fe_code_rate_t isdb_layerb_fec;
+ fe_modulation_t isdb_layerb_modulation;
+ u32 isdb_layerb_segment_width;
+ fe_code_rate_t isdb_layerc_fec;
+ fe_modulation_t isdb_layerc_modulation;
+ u32 isdb_layerc_segment_width;
+#endif
+};
+
struct dvb_frontend {
struct dvb_frontend_ops ops;
struct dvb_adapter *dvb;
@@ -190,6 +233,9 @@ struct dvb_frontend {
void *frontend_priv;
void *sec_priv;
void *analog_demod_priv;
+ struct dtv_frontend_properties dtv_property_cache;
+#define DVB_FRONTEND_COMPONENT_TUNER 0
+ int (*callback)(void *adapter_priv, int component, int cmd, int arg);
};
extern int dvb_register_frontend(struct dvb_adapter *dvb,
diff --git a/linux/drivers/media/dvb/dvb-usb/Kconfig b/linux/drivers/media/dvb/dvb-usb/Kconfig
index 2e8000fc4..57bb470bd 100644
--- a/linux/drivers/media/dvb/dvb-usb/Kconfig
+++ b/linux/drivers/media/dvb/dvb-usb/Kconfig
@@ -72,9 +72,11 @@ config DVB_USB_DIB0700
select DVB_DIB7000P
select DVB_DIB7000M
select DVB_DIB3000MC
+ select DVB_S5H1411 if !DVB_FE_CUSTOMISE
select MEDIA_TUNER_MT2060 if !DVB_FE_CUSTOMISE
select MEDIA_TUNER_MT2266 if !DVB_FE_CUSTOMISE
select MEDIA_TUNER_XC2028 if !DVB_FE_CUSTOMISE
+ select MEDIA_TUNER_XC5000 if !DVB_FE_CUSTOMIZE
select DVB_TUNER_DIB0070
help
Support for USB2.0/1.1 DVB receivers based on the DiB0700 USB bridge. The
@@ -247,12 +249,15 @@ config DVB_USB_AF9005_REMOTE
Afatech AF9005 based receiver.
config DVB_USB_DW2102
- tristate "DvbWorld 2102 DVB-S USB2.0 receiver"
+ tristate "DvbWorld DVB-S/S2 USB2.0 support"
depends on DVB_USB
- select DVB_STV0299 if !DVB_FE_CUSTOMISE
select DVB_PLL if !DVB_FE_CUSTOMISE
+ select DVB_STV0299 if !DVB_FE_CUSTOMISE
+ select DVB_CX24116 if !DVB_FE_CUSTOMISE
+ select DVB_SI21XX if !DVB_FE_CUSTOMISE
help
- Say Y here to support the DvbWorld 2102 DVB-S USB2.0 receiver.
+ Say Y here to support the DvbWorld DVB-S/S2 USB2.0 receivers
+ and the TeVii S650.
config DVB_USB_CINERGY_T2
tristate "Terratec CinergyT2/qanu USB 2.0 DVB-T receiver"
@@ -279,3 +284,15 @@ config DVB_USB_DTV5100
select MEDIA_TUNER_QT1010 if !DVB_FE_CUSTOMISE
help
Say Y here to support the AME DTV-5100 USB2.0 DVB-T receiver.
+
+config DVB_USB_AF9015
+ tristate "Afatech AF9015 DVB-T USB2.0 support"
+ depends on DVB_USB && EXPERIMENTAL
+ select DVB_AF9013
+ select DVB_PLL if !DVB_FE_CUSTOMISE
+ select MEDIA_TUNER_MT2060 if !DVB_FE_CUSTOMISE
+ select MEDIA_TUNER_QT1010 if !DVB_FE_CUSTOMISE
+ select MEDIA_TUNER_TDA18271 if !DVB_FE_CUSTOMISE
+ select MEDIA_TUNER_MXL5005S if !DVB_FE_CUSTOMISE
+ help
+ Say Y here to support the Afatech AF9015 based DVB-T USB2.0 receiver
diff --git a/linux/drivers/media/dvb/dvb-usb/Makefile b/linux/drivers/media/dvb/dvb-usb/Makefile
index 39789ed15..3122b7cc2 100644
--- a/linux/drivers/media/dvb/dvb-usb/Makefile
+++ b/linux/drivers/media/dvb/dvb-usb/Makefile
@@ -70,6 +70,9 @@ obj-$(CONFIG_DVB_USB_DW2102) += dvb-usb-dw2102.o
dvb-usb-dtv5100-objs = dtv5100.o
obj-$(CONFIG_DVB_USB_DTV5100) += dvb-usb-dtv5100.o
+dvb-usb-af9015-objs = af9015.o
+obj-$(CONFIG_DVB_USB_AF9015) += dvb-usb-af9015.o
+
dvb-usb-cinergyT2-objs = cinergyT2-core.o cinergyT2-fe.o
obj-$(CONFIG_DVB_USB_CINERGY_T2) += dvb-usb-cinergyT2.o
diff --git a/linux/drivers/media/dvb/dvb-usb/af9015.c b/linux/drivers/media/dvb/dvb-usb/af9015.c
new file mode 100644
index 000000000..776d01d4a
--- /dev/null
+++ b/linux/drivers/media/dvb/dvb-usb/af9015.c
@@ -0,0 +1,1474 @@
+/*
+ * DVB USB Linux driver for Afatech AF9015 DVB-T USB2.0 receiver
+ *
+ * Copyright (C) 2007 Antti Palosaari <crope@iki.fi>
+ *
+ * Thanks to Afatech who kindly provided information.
+ *
+ * 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
+ * (at your option) 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "af9015.h"
+#include "af9013.h"
+#include "mt2060.h"
+#include "qt1010.h"
+#include "tda18271.h"
+#include "mxl5005s.h"
+#if 0 /* keep */
+#include "mc44s80x.h"
+#endif
+
+int dvb_usb_af9015_debug;
+module_param_named(debug, dvb_usb_af9015_debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debugging level" DVB_USB_DEBUG_STATUS);
+int dvb_usb_af9015_remote;
+module_param_named(remote, dvb_usb_af9015_remote, int, 0644);
+MODULE_PARM_DESC(remote, "select remote");
+int dvb_usb_af9015_dual_mode;
+module_param_named(dual_mode, dvb_usb_af9015_dual_mode, int, 0644);
+MODULE_PARM_DESC(dual_mode, "enable dual mode");
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+static DEFINE_MUTEX(af9015_usb_mutex);
+
+static struct af9015_config af9015_config;
+static struct dvb_usb_device_properties af9015_properties[2];
+int af9015_properties_count = ARRAY_SIZE(af9015_properties);
+
+static struct af9013_config af9015_af9013_config[] = {
+ {
+ .demod_address = AF9015_I2C_DEMOD,
+ .output_mode = AF9013_OUTPUT_MODE_USB,
+ .api_version = { 0, 1, 9, 0 },
+ .gpio[0] = AF9013_GPIO_HI,
+ .gpio[1] = AF9013_GPIO_LO,
+ .gpio[3] = AF9013_GPIO_TUNER_ON,
+
+ }, {
+ .output_mode = AF9013_OUTPUT_MODE_SERIAL,
+ .api_version = { 0, 1, 9, 0 },
+ .gpio[0] = AF9013_GPIO_TUNER_ON,
+ .gpio[1] = AF9013_GPIO_LO,
+ }
+};
+
+static int af9015_rw_udev(struct usb_device *udev, struct req_t *req)
+{
+ int act_len, ret;
+ u8 buf[64];
+ u8 write = 1;
+ u8 msg_len = 8;
+ static u8 seq; /* packet sequence number */
+
+ if (mutex_lock_interruptible(&af9015_usb_mutex) < 0)
+ return -EAGAIN;
+
+ buf[0] = req->cmd;
+ buf[1] = seq++;
+ buf[2] = req->i2c_addr;
+ buf[3] = req->addr >> 8;
+ buf[4] = req->addr & 0xff;
+ buf[5] = req->mbox;
+ buf[6] = req->addr_len;
+ buf[7] = req->data_len;
+
+ switch (req->cmd) {
+ case GET_CONFIG:
+ case BOOT:
+ case READ_MEMORY:
+ case RECONNECT_USB:
+ case GET_IR_CODE:
+ write = 0;
+ break;
+ case READ_I2C:
+ write = 0;
+ buf[2] |= 0x01; /* set I2C direction */
+ case WRITE_I2C:
+ buf[0] = READ_WRITE_I2C;
+ break;
+ case WRITE_MEMORY:
+ if (((req->addr & 0xff00) == 0xff00) ||
+ ((req->addr & 0xae00) == 0xae00))
+ buf[0] = WRITE_VIRTUAL_MEMORY;
+ case WRITE_VIRTUAL_MEMORY:
+ case COPY_FIRMWARE:
+ case DOWNLOAD_FIRMWARE:
+ break;
+ default:
+ err("unknown command:%d", req->cmd);
+ ret = -1;
+ goto error_unlock;
+ }
+
+ /* write requested */
+ if (write) {
+ memcpy(&buf[8], req->data, req->data_len);
+ msg_len += req->data_len;
+ }
+ deb_xfer(">>> ");
+ debug_dump(buf, msg_len, deb_xfer);
+
+ /* send req */
+ ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, 0x02), buf, msg_len,
+ &act_len, AF9015_USB_TIMEOUT);
+ if (ret)
+ err("bulk message failed:%d (%d/%d)", ret, msg_len, act_len);
+ else
+ if (act_len != msg_len)
+ ret = -1; /* all data is not send */
+ if (ret)
+ goto error_unlock;
+
+ /* no ack for those packets */
+ if (req->cmd == DOWNLOAD_FIRMWARE || req->cmd == RECONNECT_USB)
+ goto exit_unlock;
+
+ /* receive ack and data if read req */
+ msg_len = 1 + 1 + req->data_len; /* seq + status + data len */
+ ret = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, 0x81), buf, msg_len,
+ &act_len, AF9015_USB_TIMEOUT);
+ if (ret) {
+ err("recv bulk message failed:%d", ret);
+ ret = -1;
+ goto error_unlock;
+ }
+
+ deb_xfer("<<< ");
+ debug_dump(buf, act_len, deb_xfer);
+
+ /* remote controller query status is 1 if remote code is not received */
+ if (req->cmd == GET_IR_CODE && buf[1] == 1) {
+ buf[1] = 0; /* clear command "error" status */
+ memset(&buf[2], 0, req->data_len);
+ buf[3] = 1; /* no remote code received mark */
+ }
+
+ /* check status */
+ if (buf[1]) {
+ err("command failed:%d", buf[1]);
+ ret = -1;
+ goto error_unlock;
+ }
+
+ /* read request, copy returned data to return buf */
+ if (!write)
+ memcpy(req->data, &buf[2], req->data_len);
+
+error_unlock:
+exit_unlock:
+ mutex_unlock(&af9015_usb_mutex);
+
+ return ret;
+}
+
+static int af9015_ctrl_msg(struct dvb_usb_device *d, struct req_t *req)
+{
+ return af9015_rw_udev(d->udev, req);
+}
+
+static int af9015_write_regs(struct dvb_usb_device *d, u16 addr, u8 *val,
+ u8 len)
+{
+ struct req_t req = {WRITE_MEMORY, AF9015_I2C_DEMOD, addr, 0, 0, len,
+ val};
+ return af9015_ctrl_msg(d, &req);
+}
+
+static int af9015_write_reg(struct dvb_usb_device *d, u16 addr, u8 val)
+{
+ return af9015_write_regs(d, addr, &val, 1);
+}
+
+static int af9015_read_reg(struct dvb_usb_device *d, u16 addr, u8 *val)
+{
+ struct req_t req = {READ_MEMORY, AF9015_I2C_DEMOD, addr, 0, 0, 1, val};
+ return af9015_ctrl_msg(d, &req);
+}
+
+static int af9015_write_reg_i2c(struct dvb_usb_device *d, u8 addr, u16 reg,
+ u8 val)
+{
+ struct req_t req = {WRITE_I2C, addr, reg, 1, 1, 1, &val};
+
+ if (addr == af9015_af9013_config[0].demod_address ||
+ addr == af9015_af9013_config[1].demod_address)
+ req.addr_len = 3;
+
+ return af9015_ctrl_msg(d, &req);
+}
+
+static int af9015_read_reg_i2c(struct dvb_usb_device *d, u8 addr, u16 reg,
+ u8 *val)
+{
+ struct req_t req = {READ_I2C, addr, reg, 0, 1, 1, val};
+
+ if (addr == af9015_af9013_config[0].demod_address ||
+ addr == af9015_af9013_config[1].demod_address)
+ req.addr_len = 3;
+
+ return af9015_ctrl_msg(d, &req);
+}
+
+static int af9015_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
+ int num)
+{
+ struct dvb_usb_device *d = i2c_get_adapdata(adap);
+ int ret = 0, i = 0;
+ u16 addr;
+ u8 mbox, addr_len;
+ struct req_t req;
+
+/* TODO: implement bus lock
+
+The bus lock is needed because there is two tuners both using same I2C-address.
+Due to that the only way to select correct tuner is use demodulator I2C-gate.
+
+................................................
+. AF9015 includes integrated AF9013 demodulator.
+. ____________ ____________ . ____________
+.| uC | | demod | . | tuner |
+.|------------| |------------| . |------------|
+.| AF9015 | | AF9013/5 | . | MXL5003 |
+.| |--+----I2C-------|-----/ -----|-.-----I2C-------| |
+.| | | | addr 0x38 | . | addr 0xc6 |
+.|____________| | |____________| . |____________|
+.................|..............................
+ | ____________ ____________
+ | | demod | | tuner |
+ | |------------| |------------|
+ | | AF9013 | | MXL5003 |
+ +----I2C-------|-----/ -----|-------I2C-------| |
+ | addr 0x3a | | addr 0xc6 |
+ |____________| |____________|
+*/
+ if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+ return -EAGAIN;
+
+ while (i < num) {
+ if (msg[i].addr == af9015_af9013_config[0].demod_address ||
+ msg[i].addr == af9015_af9013_config[1].demod_address) {
+ addr = msg[i].buf[0] << 8;
+ addr += msg[i].buf[1];
+ mbox = msg[i].buf[2];
+ addr_len = 3;
+ } else {
+ addr = msg[i].buf[0];
+ addr_len = 1;
+ mbox = 0;
+ }
+
+ if (num > i + 1 && (msg[i+1].flags & I2C_M_RD)) {
+ if (msg[i].addr ==
+ af9015_af9013_config[0].demod_address)
+ req.cmd = READ_MEMORY;
+ else
+ req.cmd = READ_I2C;
+ req.i2c_addr = msg[i].addr;
+ req.addr = addr;
+ req.mbox = mbox;
+ req.addr_len = addr_len;
+ req.data_len = msg[i+1].len;
+ req.data = &msg[i+1].buf[0];
+ ret = af9015_ctrl_msg(d, &req);
+ i += 2;
+ } else {
+ if (msg[i].addr ==
+ af9015_af9013_config[0].demod_address)
+ req.cmd = WRITE_MEMORY;
+ else
+ req.cmd = WRITE_I2C;
+ req.i2c_addr = msg[i].addr;
+ req.addr = addr;
+ req.mbox = mbox;
+ req.addr_len = addr_len;
+ req.data_len = msg[i].len-addr_len;
+ req.data = &msg[i].buf[addr_len];
+ ret = af9015_ctrl_msg(d, &req);
+ i += 1;
+ }
+ if (ret)
+ goto error;
+
+ }
+ ret = i;
+
+error:
+ mutex_unlock(&d->i2c_mutex);
+
+ return ret;
+}
+
+static u32 af9015_i2c_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm af9015_i2c_algo = {
+ .master_xfer = af9015_i2c_xfer,
+ .functionality = af9015_i2c_func,
+};
+
+static int af9015_do_reg_bit(struct dvb_usb_device *d, u16 addr, u8 bit, u8 op)
+{
+ int ret;
+ u8 val, mask = 0x01;
+
+ ret = af9015_read_reg(d, addr, &val);
+ if (ret)
+ return ret;
+
+ mask <<= bit;
+ if (op) {
+ /* set bit */
+ val |= mask;
+ } else {
+ /* clear bit */
+ mask ^= 0xff;
+ val &= mask;
+ }
+
+ return af9015_write_reg(d, addr, val);
+}
+
+static int af9015_set_reg_bit(struct dvb_usb_device *d, u16 addr, u8 bit)
+{
+ return af9015_do_reg_bit(d, addr, bit, 1);
+}
+
+static int af9015_clear_reg_bit(struct dvb_usb_device *d, u16 addr, u8 bit)
+{
+ return af9015_do_reg_bit(d, addr, bit, 0);
+}
+
+static int af9015_init_endpoint(struct dvb_usb_device *d)
+{
+ int ret;
+ u16 frame_size;
+ u8 packet_size;
+ deb_info("%s: USB speed:%d\n", __func__, d->udev->speed);
+
+#define TS_PACKET_SIZE 188
+
+#define TS_USB20_PACKET_COUNT 348
+#define TS_USB20_FRAME_SIZE (TS_PACKET_SIZE*TS_USB20_PACKET_COUNT)
+
+#define TS_USB11_PACKET_COUNT 21
+#define TS_USB11_FRAME_SIZE (TS_PACKET_SIZE*TS_USB11_PACKET_COUNT)
+
+#define TS_USB20_MAX_PACKET_SIZE 512
+#define TS_USB11_MAX_PACKET_SIZE 64
+
+ if (d->udev->speed == USB_SPEED_FULL) {
+ frame_size = TS_USB11_FRAME_SIZE/4;
+ packet_size = TS_USB11_MAX_PACKET_SIZE/4;
+ } else {
+ frame_size = TS_USB20_FRAME_SIZE/4;
+ packet_size = TS_USB20_MAX_PACKET_SIZE/4;
+ }
+
+ ret = af9015_set_reg_bit(d, 0xd507, 2); /* assert EP4 reset */
+ if (ret)
+ goto error;
+ ret = af9015_set_reg_bit(d, 0xd50b, 1); /* assert EP5 reset */
+ if (ret)
+ goto error;
+ ret = af9015_clear_reg_bit(d, 0xdd11, 5); /* disable EP4 */
+ if (ret)
+ goto error;
+ ret = af9015_clear_reg_bit(d, 0xdd11, 6); /* disable EP5 */
+ if (ret)
+ goto error;
+ ret = af9015_set_reg_bit(d, 0xdd11, 5); /* enable EP4 */
+ if (ret)
+ goto error;
+ if (af9015_config.dual_mode) {
+ ret = af9015_set_reg_bit(d, 0xdd11, 6); /* enable EP5 */
+ if (ret)
+ goto error;
+ }
+ ret = af9015_clear_reg_bit(d, 0xdd13, 5); /* disable EP4 NAK */
+ if (ret)
+ goto error;
+ if (af9015_config.dual_mode) {
+ ret = af9015_clear_reg_bit(d, 0xdd13, 6); /* disable EP5 NAK */
+ if (ret)
+ goto error;
+ }
+ /* EP4 xfer length */
+ ret = af9015_write_reg(d, 0xdd88, frame_size & 0xff);
+ if (ret)
+ goto error;
+ ret = af9015_write_reg(d, 0xdd89, frame_size >> 8);
+ if (ret)
+ goto error;
+ /* EP5 xfer length */
+ ret = af9015_write_reg(d, 0xdd8a, frame_size & 0xff);
+ if (ret)
+ goto error;
+ ret = af9015_write_reg(d, 0xdd8b, frame_size >> 8);
+ if (ret)
+ goto error;
+ ret = af9015_write_reg(d, 0xdd0c, packet_size); /* EP4 packet size */
+ if (ret)
+ goto error;
+ ret = af9015_write_reg(d, 0xdd0d, packet_size); /* EP5 packet size */
+ if (ret)
+ goto error;
+ ret = af9015_clear_reg_bit(d, 0xd507, 2); /* negate EP4 reset */
+ if (ret)
+ goto error;
+ if (af9015_config.dual_mode) {
+ ret = af9015_clear_reg_bit(d, 0xd50b, 1); /* negate EP5 reset */
+ if (ret)
+ goto error;
+ }
+
+ /* enable / disable mp2if2 */
+ if (af9015_config.dual_mode)
+ ret = af9015_set_reg_bit(d, 0xd50b, 0);
+ else
+ ret = af9015_clear_reg_bit(d, 0xd50b, 0);
+error:
+ if (ret)
+ err("endpoint init failed:%d", ret);
+ return ret;
+}
+
+static int af9015_copy_firmware(struct dvb_usb_device *d)
+{
+ int ret;
+ u8 fw_params[4];
+ u8 val, i;
+ struct req_t req = {COPY_FIRMWARE, 0, 0x5100, 0, 0, sizeof(fw_params),
+ fw_params };
+ deb_info("%s:\n", __func__);
+
+ fw_params[0] = af9015_config.firmware_size >> 8;
+ fw_params[1] = af9015_config.firmware_size & 0xff;
+ fw_params[2] = af9015_config.firmware_checksum >> 8;
+ fw_params[3] = af9015_config.firmware_checksum & 0xff;
+
+ /* wait 2nd demodulator ready */
+ msleep(100);
+
+ ret = af9015_read_reg_i2c(d, 0x3a, 0x98be, &val);
+ if (ret)
+ goto error;
+ else
+ deb_info("%s: firmware status:%02x\n", __func__, val);
+
+ if (val == 0x0c) /* fw is running, no need for download */
+ goto exit;
+
+ /* set I2C master clock to fast (to speed up firmware copy) */
+ ret = af9015_write_reg(d, 0xd416, 0x04); /* 0x04 * 400ns */
+ if (ret)
+ goto error;
+
+ msleep(50);
+
+ /* copy firmware */
+ ret = af9015_ctrl_msg(d, &req);
+ if (ret)
+ err("firmware copy cmd failed:%d", ret);
+ deb_info("%s: firmware copy done\n", __func__);
+
+ /* set I2C master clock back to normal */
+ ret = af9015_write_reg(d, 0xd416, 0x14); /* 0x14 * 400ns */
+ if (ret)
+ goto error;
+
+ /* request boot firmware */
+ ret = af9015_write_reg_i2c(d, af9015_af9013_config[1].demod_address,
+ 0xe205, 1);
+ deb_info("%s: firmware boot cmd status:%d\n", __func__, ret);
+ if (ret)
+ goto error;
+
+ for (i = 0; i < 15; i++) {
+ msleep(100);
+
+ /* check firmware status */
+ ret = af9015_read_reg_i2c(d,
+ af9015_af9013_config[1].demod_address, 0x98be, &val);
+ deb_info("%s: firmware status cmd status:%d fw status:%02x\n",
+ __func__, ret, val);
+ if (ret)
+ goto error;
+
+ if (val == 0x0c || val == 0x04) /* success or fail */
+ break;
+ }
+
+ if (val == 0x04) {
+ err("firmware did not run");
+ ret = -1;
+ } else if (val != 0x0c) {
+ err("firmware boot timeout");
+ ret = -1;
+ }
+
+error:
+exit:
+ return ret;
+}
+
+/* dump eeprom */
+static int af9015_eeprom_dump(struct dvb_usb_device *d)
+{
+ char buf[52], buf2[4];
+ u8 reg, val;
+
+ for (reg = 0; ; reg++) {
+ if (reg % 16 == 0) {
+ if (reg)
+ deb_info("%s\n", buf);
+ sprintf(buf, "%02x: ", reg);
+ }
+ if (af9015_read_reg_i2c(d, AF9015_I2C_EEPROM, reg, &val) == 0)
+ sprintf(buf2, "%02x ", val);
+ else
+ strcpy(buf2, "-- ");
+ strcat(buf, buf2);
+ if (reg == 0xff)
+ break;
+ }
+ deb_info("%s\n", buf);
+ return 0;
+}
+
+int af9015_download_ir_table(struct dvb_usb_device *d)
+{
+ int i, packets = 0, ret;
+ u16 addr = 0x9a56; /* ir-table start address */
+ struct req_t req = {WRITE_MEMORY, 0, 0, 0, 0, 1, NULL};
+ u8 *data = NULL;
+ deb_info("%s:\n", __func__);
+
+ data = af9015_config.ir_table;
+ packets = af9015_config.ir_table_size;
+
+ /* no remote */
+ if (!packets)
+ goto exit;
+
+ /* load remote ir-table */
+ for (i = 0; i < packets; i++) {
+ req.addr = addr + i;
+ req.data = &data[i];
+ ret = af9015_ctrl_msg(d, &req);
+ if (ret) {
+ err("ir-table download failed at packet %d with " \
+ "code %d", i, ret);
+ return ret;
+ }
+ }
+
+exit:
+ return 0;
+}
+
+static int af9015_init(struct dvb_usb_device *d)
+{
+ int ret;
+ deb_info("%s:\n", __func__);
+
+ ret = af9015_init_endpoint(d);
+ if (ret)
+ goto error;
+
+ ret = af9015_download_ir_table(d);
+ if (ret)
+ goto error;
+
+error:
+ return ret;
+}
+
+static int af9015_pid_filter_ctrl(struct dvb_usb_adapter *adap, int onoff)
+{
+ int ret;
+ deb_info("%s: onoff:%d\n", __func__, onoff);
+
+ if (onoff)
+ ret = af9015_set_reg_bit(adap->dev, 0xd503, 0);
+ else
+ ret = af9015_clear_reg_bit(adap->dev, 0xd503, 0);
+
+ return ret;
+}
+
+static int af9015_pid_filter(struct dvb_usb_adapter *adap, int index, u16 pid,
+ int onoff)
+{
+ int ret;
+ u8 idx;
+
+ deb_info("%s: set pid filter, index %d, pid %x, onoff %d\n",
+ __func__, index, pid, onoff);
+
+ ret = af9015_write_reg(adap->dev, 0xd505, (pid & 0xff));
+ if (ret)
+ goto error;
+
+ ret = af9015_write_reg(adap->dev, 0xd506, (pid >> 8));
+ if (ret)
+ goto error;
+
+ idx = ((index & 0x1f) | (1 << 5));
+ ret = af9015_write_reg(adap->dev, 0xd504, idx);
+
+error:
+ return ret;
+}
+
+static int af9015_download_firmware(struct usb_device *udev,
+ const struct firmware *fw)
+{
+ int i, len, packets, remainder, ret;
+ struct req_t req = {DOWNLOAD_FIRMWARE, 0, 0, 0, 0, 0, NULL};
+ u16 addr = 0x5100; /* firmware start address */
+ u16 checksum = 0;
+
+ deb_info("%s:\n", __func__);
+
+ /* calc checksum */
+ for (i = 0; i < fw->size; i++)
+ checksum += fw->data[i];
+
+ af9015_config.firmware_size = fw->size;
+ af9015_config.firmware_checksum = checksum;
+
+ #define FW_PACKET_MAX_DATA 55
+
+ packets = fw->size / FW_PACKET_MAX_DATA;
+ remainder = fw->size % FW_PACKET_MAX_DATA;
+ len = FW_PACKET_MAX_DATA;
+ for (i = 0; i <= packets; i++) {
+ if (i == packets) /* set size of the last packet */
+ len = remainder;
+
+ req.data_len = len;
+ req.data = (fw->data + i * FW_PACKET_MAX_DATA);
+ req.addr = addr;
+ addr += FW_PACKET_MAX_DATA;
+
+ ret = af9015_rw_udev(udev, &req);
+ if (ret) {
+ err("firmware download failed at packet %d with " \
+ "code %d", i, ret);
+ goto error;
+ }
+ }
+ #undef FW_PACKET_MAX_DATA
+
+ /* firmware loaded, request boot */
+ req.cmd = BOOT;
+ ret = af9015_rw_udev(udev, &req);
+ if (ret) {
+ err("firmware boot failed:%d", ret);
+ goto error;
+ }
+
+ /* firmware is running, reconnect device in the usb bus */
+ req.cmd = RECONNECT_USB;
+ ret = af9015_rw_udev(udev, &req);
+ if (ret)
+ err("reconnect failed: %d", ret);
+
+error:
+ return ret;
+}
+
+static int af9015_read_config(struct usb_device *udev)
+{
+ int ret;
+ u8 val, i, offset = 0;
+ struct req_t req = {READ_I2C, AF9015_I2C_EEPROM, 0, 0, 1, 1, &val};
+ char manufacturer[10];
+
+ /* IR remote controller */
+ req.addr = AF9015_EEPROM_IR_MODE;
+ ret = af9015_rw_udev(udev, &req);
+ if (ret)
+ goto error;
+ deb_info("%s: IR mode:%d\n", __func__, val);
+ for (i = 0; i < af9015_properties_count; i++) {
+ if (val == AF9015_IR_MODE_DISABLED || val == 0x04) {
+ af9015_properties[i].rc_key_map = NULL;
+ af9015_properties[i].rc_key_map_size = 0;
+ } else if (dvb_usb_af9015_remote) {
+ /* load remote defined as module param */
+ switch (dvb_usb_af9015_remote) {
+ case AF9015_REMOTE_A_LINK_DTU_M:
+ af9015_properties[i].rc_key_map =
+ af9015_rc_keys_a_link;
+ af9015_properties[i].rc_key_map_size =
+ ARRAY_SIZE(af9015_rc_keys_a_link);
+ af9015_config.ir_table = af9015_ir_table_a_link;
+ af9015_config.ir_table_size =
+ ARRAY_SIZE(af9015_ir_table_a_link);
+ break;
+ case AF9015_REMOTE_MSI_DIGIVOX_MINI_II_V3:
+ af9015_properties[i].rc_key_map =
+ af9015_rc_keys_msi;
+ af9015_properties[i].rc_key_map_size =
+ ARRAY_SIZE(af9015_rc_keys_msi);
+ af9015_config.ir_table = af9015_ir_table_msi;
+ af9015_config.ir_table_size =
+ ARRAY_SIZE(af9015_ir_table_msi);
+ break;
+ case AF9015_REMOTE_MYGICTV_U718:
+ af9015_properties[i].rc_key_map =
+ af9015_rc_keys_mygictv;
+ af9015_properties[i].rc_key_map_size =
+ ARRAY_SIZE(af9015_rc_keys_mygictv);
+ af9015_config.ir_table =
+ af9015_ir_table_mygictv;
+ af9015_config.ir_table_size =
+ ARRAY_SIZE(af9015_ir_table_mygictv);
+ break;
+ }
+ } else {
+ switch (udev->descriptor.idVendor) {
+ case cpu_to_le16(USB_VID_LEADTEK):
+ af9015_properties[i].rc_key_map =
+ af9015_rc_keys_leadtek;
+ af9015_properties[i].rc_key_map_size =
+ ARRAY_SIZE(af9015_rc_keys_leadtek);
+ af9015_config.ir_table =
+ af9015_ir_table_leadtek;
+ af9015_config.ir_table_size =
+ ARRAY_SIZE(af9015_ir_table_leadtek);
+ break;
+ case cpu_to_le16(USB_VID_VISIONPLUS):
+ if (udev->descriptor.idProduct ==
+ cpu_to_le16(USB_PID_AZUREWAVE_AD_TU700)) {
+ af9015_properties[i].rc_key_map =
+ af9015_rc_keys_twinhan;
+ af9015_properties[i].rc_key_map_size =
+ ARRAY_SIZE(af9015_rc_keys_twinhan);
+ af9015_config.ir_table =
+ af9015_ir_table_twinhan;
+ af9015_config.ir_table_size =
+ ARRAY_SIZE(af9015_ir_table_twinhan);
+ }
+ break;
+ case cpu_to_le16(USB_VID_KWORLD_2):
+ /* TODO: use correct rc keys */
+ af9015_properties[i].rc_key_map =
+ af9015_rc_keys_twinhan;
+ af9015_properties[i].rc_key_map_size =
+ ARRAY_SIZE(af9015_rc_keys_twinhan);
+ af9015_config.ir_table = af9015_ir_table_kworld;
+ af9015_config.ir_table_size =
+ ARRAY_SIZE(af9015_ir_table_kworld);
+ break;
+ /* Check USB manufacturer and product strings and try
+ to determine correct remote in case of chip vendor
+ reference IDs are used. */
+ case cpu_to_le16(USB_VID_AFATECH):
+ memset(manufacturer, 0, sizeof(manufacturer));
+ usb_string(udev, udev->descriptor.iManufacturer,
+ manufacturer, sizeof(manufacturer));
+ if (!strcmp("Geniatech", manufacturer)) {
+ /* iManufacturer 1 Geniatech
+ iProduct 2 AF9015 */
+ af9015_properties[i].rc_key_map =
+ af9015_rc_keys_mygictv;
+ af9015_properties[i].rc_key_map_size =
+ ARRAY_SIZE(af9015_rc_keys_mygictv);
+ af9015_config.ir_table =
+ af9015_ir_table_mygictv;
+ af9015_config.ir_table_size =
+ ARRAY_SIZE(af9015_ir_table_mygictv);
+ } else if (!strcmp("MSI", manufacturer)) {
+ /* iManufacturer 1 MSI
+ iProduct 2 MSI K-VOX */
+ af9015_properties[i].rc_key_map =
+ af9015_rc_keys_msi;
+ af9015_properties[i].rc_key_map_size =
+ ARRAY_SIZE(af9015_rc_keys_msi);
+ af9015_config.ir_table =
+ af9015_ir_table_msi;
+ af9015_config.ir_table_size =
+ ARRAY_SIZE(af9015_ir_table_msi);
+ }
+ break;
+ }
+ }
+ }
+
+ /* TS mode - one or two receivers */
+ req.addr = AF9015_EEPROM_TS_MODE;
+ ret = af9015_rw_udev(udev, &req);
+ if (ret)
+ goto error;
+ af9015_config.dual_mode = val;
+ deb_info("%s: TS mode:%d\n", __func__, af9015_config.dual_mode);
+ /* disable dual mode by default because it is buggy */
+ if (!dvb_usb_af9015_dual_mode)
+ af9015_config.dual_mode = 0;
+
+ /* set buffer size according to USB port speed */
+ for (i = 0; i < af9015_properties_count; i++) {
+ /* USB1.1 set smaller buffersize and disable 2nd adapter */
+ if (udev->speed == USB_SPEED_FULL) {
+ af9015_properties[i].adapter->stream.u.bulk.buffersize =
+ TS_USB11_MAX_PACKET_SIZE;
+ /* disable 2nd adapter because we don't have
+ PID-filters */
+ af9015_config.dual_mode = 0;
+ } else {
+ af9015_properties[i].adapter->stream.u.bulk.buffersize =
+ TS_USB20_MAX_PACKET_SIZE;
+ }
+ }
+
+ if (af9015_config.dual_mode) {
+ /* read 2nd demodulator I2C address */
+ req.addr = AF9015_EEPROM_DEMOD2_I2C;
+ ret = af9015_rw_udev(udev, &req);
+ if (ret)
+ goto error;
+ af9015_af9013_config[1].demod_address = val;
+
+ /* enable 2nd adapter */
+ for (i = 0; i < af9015_properties_count; i++)
+ af9015_properties[i].num_adapters = 2;
+
+ } else {
+ /* disable 2nd adapter */
+ for (i = 0; i < af9015_properties_count; i++)
+ af9015_properties[i].num_adapters = 1;
+ }
+
+ for (i = 0; i < af9015_properties[0].num_adapters; i++) {
+ if (i == 1)
+ offset = AF9015_EEPROM_OFFSET;
+ /* xtal */
+ req.addr = AF9015_EEPROM_XTAL_TYPE1 + offset;
+ ret = af9015_rw_udev(udev, &req);
+ if (ret)
+ goto error;
+ switch (val) {
+ case 0:
+ af9015_af9013_config[i].adc_clock = 28800;
+ break;
+ case 1:
+ af9015_af9013_config[i].adc_clock = 20480;
+ break;
+ case 2:
+ af9015_af9013_config[i].adc_clock = 28000;
+ break;
+ case 3:
+ af9015_af9013_config[i].adc_clock = 25000;
+ break;
+ };
+ deb_info("%s: [%d] xtal:%d set adc_clock:%d\n", __func__, i,
+ val, af9015_af9013_config[i].adc_clock);
+
+ /* tuner IF */
+ req.addr = AF9015_EEPROM_IF1H + offset;
+ ret = af9015_rw_udev(udev, &req);
+ if (ret)
+ goto error;
+ af9015_af9013_config[i].tuner_if = val << 8;
+ req.addr = AF9015_EEPROM_IF1L + offset;
+ ret = af9015_rw_udev(udev, &req);
+ if (ret)
+ goto error;
+ af9015_af9013_config[i].tuner_if += val;
+ deb_info("%s: [%d] IF1:%d\n", __func__, i,
+ af9015_af9013_config[0].tuner_if);
+
+ /* MT2060 IF1 */
+ req.addr = AF9015_EEPROM_MT2060_IF1H + offset;
+ ret = af9015_rw_udev(udev, &req);
+ if (ret)
+ goto error;
+ af9015_config.mt2060_if1[i] = val << 8;
+ req.addr = AF9015_EEPROM_MT2060_IF1L + offset;
+ ret = af9015_rw_udev(udev, &req);
+ if (ret)
+ goto error;
+ af9015_config.mt2060_if1[i] += val;
+ deb_info("%s: [%d] MT2060 IF1:%d\n", __func__, i,
+ af9015_config.mt2060_if1[i]);
+
+ /* tuner */
+ req.addr = AF9015_EEPROM_TUNER_ID1 + offset;
+ ret = af9015_rw_udev(udev, &req);
+ if (ret)
+ goto error;
+ switch (val) {
+ case AF9013_TUNER_ENV77H11D5:
+ case AF9013_TUNER_MT2060:
+ case AF9013_TUNER_MC44S803:
+ case AF9013_TUNER_QT1010:
+ case AF9013_TUNER_UNKNOWN:
+ case AF9013_TUNER_MT2060_2:
+ case AF9013_TUNER_TDA18271:
+ case AF9013_TUNER_QT1010A:
+ af9015_af9013_config[i].rf_spec_inv = 1;
+ break;
+ case AF9013_TUNER_MXL5003D:
+ case AF9013_TUNER_MXL5005D:
+ case AF9013_TUNER_MXL5005R:
+ af9015_af9013_config[i].rf_spec_inv = 0;
+ break;
+ default:
+ warn("tuner id:%d not supported, please report!", val);
+ return -ENODEV;
+ };
+
+ af9015_af9013_config[i].tuner = val;
+ deb_info("%s: [%d] tuner id:%d\n", __func__, i, val);
+ }
+
+error:
+ if (ret)
+ err("eeprom read failed:%d", ret);
+
+ return ret;
+}
+
+static int af9015_identify_state(struct usb_device *udev,
+ struct dvb_usb_device_properties *props,
+ struct dvb_usb_device_description **desc,
+ int *cold)
+{
+ int ret;
+ u8 reply;
+ struct req_t req = {GET_CONFIG, 0, 0, 0, 0, 1, &reply};
+
+ ret = af9015_rw_udev(udev, &req);
+ if (ret)
+ return ret;
+
+ deb_info("%s: reply:%02x\n", __func__, reply);
+ if (reply == 0x02)
+ *cold = 0;
+ else
+ *cold = 1;
+
+ return ret;
+}
+
+static int af9015_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
+{
+ u8 buf[8];
+ struct req_t req = {GET_IR_CODE, 0, 0, 0, 0, sizeof(buf), buf};
+ struct dvb_usb_rc_key *keymap = d->props.rc_key_map;
+ int i, ret;
+
+ memset(buf, 0, sizeof(buf));
+
+ ret = af9015_ctrl_msg(d, &req);
+ if (ret)
+ return ret;
+
+ *event = 0;
+ *state = REMOTE_NO_KEY_PRESSED;
+
+ for (i = 0; i < d->props.rc_key_map_size; i++) {
+ if (!buf[1] && keymap[i].custom == buf[0] &&
+ keymap[i].data == buf[2]) {
+ *event = keymap[i].event;
+ *state = REMOTE_KEY_PRESSED;
+ break;
+ }
+ }
+ if (!buf[1])
+ deb_rc("%s: %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ __func__, buf[0], buf[1], buf[2], buf[3], buf[4],
+ buf[5], buf[6], buf[7]);
+
+ return 0;
+}
+
+/* init 2nd I2C adapter */
+int af9015_i2c_init(struct dvb_usb_device *d)
+{
+ int ret;
+ struct af9015_state *state = d->priv;
+ deb_info("%s:\n", __func__);
+
+ strncpy(state->i2c_adap.name, d->desc->name,
+ sizeof(state->i2c_adap.name));
+#ifdef I2C_ADAP_CLASS_TV_DIGITAL
+ state->i2c_adap.class = I2C_ADAP_CLASS_TV_DIGITAL,
+#else
+ state->i2c_adap.class = I2C_CLASS_TV_DIGITAL,
+#endif
+ state->i2c_adap.algo = d->props.i2c_algo;
+ state->i2c_adap.algo_data = NULL;
+ state->i2c_adap.dev.parent = &d->udev->dev;
+
+ i2c_set_adapdata(&state->i2c_adap, d);
+
+ ret = i2c_add_adapter(&state->i2c_adap);
+ if (ret < 0)
+ err("could not add i2c adapter");
+
+ return ret;
+}
+
+static int af9015_af9013_frontend_attach(struct dvb_usb_adapter *adap)
+{
+ int ret;
+ struct af9015_state *state = adap->dev->priv;
+ struct i2c_adapter *i2c_adap;
+
+ if (adap->id == 0) {
+ /* select I2C adapter */
+ i2c_adap = &adap->dev->i2c_adap;
+
+ deb_info("%s: init I2C\n", __func__);
+ ret = af9015_i2c_init(adap->dev);
+
+ /* dump eeprom (debug) */
+ ret = af9015_eeprom_dump(adap->dev);
+ if (ret)
+ return ret;
+ } else {
+ /* select I2C adapter */
+ i2c_adap = &state->i2c_adap;
+
+ /* copy firmware to 2nd demodulator */
+ if (af9015_config.dual_mode) {
+ ret = af9015_copy_firmware(adap->dev);
+ if (ret) {
+ err("firmware copy to 2nd frontend " \
+ "failed, will disable it");
+ af9015_config.dual_mode = 0;
+ return -ENODEV;
+ }
+ } else {
+ return -ENODEV;
+ }
+ }
+
+ /* attach demodulator */
+ adap->fe = dvb_attach(af9013_attach, &af9015_af9013_config[adap->id],
+ i2c_adap);
+
+ return adap->fe == NULL ? -ENODEV : 0;
+}
+
+static struct mt2060_config af9015_mt2060_config = {
+ .i2c_address = 0xc0,
+ .clock_out = 0,
+};
+
+static struct qt1010_config af9015_qt1010_config = {
+ .i2c_address = 0xc4,
+};
+
+static struct tda18271_config af9015_tda18271_config = {
+ .gate = TDA18271_GATE_DIGITAL,
+ .small_i2c = 1,
+};
+
+static struct mxl5005s_config af9015_mxl5003_config = {
+ .i2c_address = 0xc6,
+ .if_freq = IF_FREQ_4570000HZ,
+ .xtal_freq = CRYSTAL_FREQ_16000000HZ,
+ .agc_mode = MXL_SINGLE_AGC,
+ .tracking_filter = MXL_TF_DEFAULT,
+ .rssi_enable = MXL_RSSI_DISABLE,
+ .cap_select = MXL_CAP_SEL_ENABLE,
+ .div_out = MXL_DIV_OUT_4,
+ .clock_out = MXL_CLOCK_OUT_DISABLE,
+ .output_load = MXL5005S_IF_OUTPUT_LOAD_200_OHM,
+ .top = MXL5005S_TOP_25P2,
+ .mod_mode = MXL_DIGITAL_MODE,
+ .if_mode = MXL_ZERO_IF,
+ .AgcMasterByte = 0x00,
+};
+
+static struct mxl5005s_config af9015_mxl5005_config = {
+ .i2c_address = 0xc6,
+ .if_freq = IF_FREQ_4570000HZ,
+ .xtal_freq = CRYSTAL_FREQ_16000000HZ,
+ .agc_mode = MXL_SINGLE_AGC,
+ .tracking_filter = MXL_TF_OFF,
+ .rssi_enable = MXL_RSSI_DISABLE,
+ .cap_select = MXL_CAP_SEL_ENABLE,
+ .div_out = MXL_DIV_OUT_4,
+ .clock_out = MXL_CLOCK_OUT_DISABLE,
+ .output_load = MXL5005S_IF_OUTPUT_LOAD_200_OHM,
+ .top = MXL5005S_TOP_25P2,
+ .mod_mode = MXL_DIGITAL_MODE,
+ .if_mode = MXL_ZERO_IF,
+ .AgcMasterByte = 0x00,
+};
+
+static int af9015_tuner_attach(struct dvb_usb_adapter *adap)
+{
+ struct af9015_state *state = adap->dev->priv;
+ struct i2c_adapter *i2c_adap;
+ int ret;
+ deb_info("%s: \n", __func__);
+
+ /* select I2C adapter */
+ if (adap->id == 0)
+ i2c_adap = &adap->dev->i2c_adap;
+ else
+ i2c_adap = &state->i2c_adap;
+
+ switch (af9015_af9013_config[adap->id].tuner) {
+ case AF9013_TUNER_MT2060:
+ case AF9013_TUNER_MT2060_2:
+ ret = dvb_attach(mt2060_attach, adap->fe, i2c_adap,
+ &af9015_mt2060_config,
+ af9015_config.mt2060_if1[adap->id])
+ == NULL ? -ENODEV : 0;
+ break;
+ case AF9013_TUNER_QT1010:
+ case AF9013_TUNER_QT1010A:
+ ret = dvb_attach(qt1010_attach, adap->fe, i2c_adap,
+ &af9015_qt1010_config) == NULL ? -ENODEV : 0;
+ break;
+ case AF9013_TUNER_TDA18271:
+ ret = dvb_attach(tda18271_attach, adap->fe, 0xc0, i2c_adap,
+ &af9015_tda18271_config) == NULL ? -ENODEV : 0;
+ break;
+ case AF9013_TUNER_MXL5003D:
+ ret = dvb_attach(mxl5005s_attach, adap->fe, i2c_adap,
+ &af9015_mxl5003_config) == NULL ? -ENODEV : 0;
+ break;
+ case AF9013_TUNER_MXL5005D:
+ case AF9013_TUNER_MXL5005R:
+ ret = dvb_attach(mxl5005s_attach, adap->fe, i2c_adap,
+ &af9015_mxl5005_config) == NULL ? -ENODEV : 0;
+ break;
+ case AF9013_TUNER_ENV77H11D5:
+ ret = dvb_attach(dvb_pll_attach, adap->fe, 0xc0, i2c_adap,
+ DVB_PLL_TDA665X) == NULL ? -ENODEV : 0;
+ break;
+ case AF9013_TUNER_MC44S803:
+#if 0 /* keep */
+ ret = dvb_attach(mc44s80x_attach, adap->fe, i2c_adap)
+ == NULL ? -ENODEV : 0;
+#else
+ ret = -ENODEV;
+ info("Freescale MC44S803 tuner found but no driver for that" \
+ "tuner. Look at the Linuxtv.org for tuner driver" \
+ "status.");
+#endif
+ break;
+ case AF9013_TUNER_UNKNOWN:
+ default:
+ ret = -ENODEV;
+ err("Unknown tuner id:%d",
+ af9015_af9013_config[adap->id].tuner);
+ }
+ return ret;
+}
+
+static struct usb_device_id af9015_usb_table[] = {
+/* 0 */{USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9015_9015)},
+ {USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9015_9016)},
+ {USB_DEVICE(USB_VID_LEADTEK, USB_PID_WINFAST_DTV_DONGLE_GOLD)},
+ {USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV71E)},
+ {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_399U)},
+/* 5 */{USB_DEVICE(USB_VID_VISIONPLUS,
+ USB_PID_TINYTWIN)},
+ {USB_DEVICE(USB_VID_VISIONPLUS,
+ USB_PID_AZUREWAVE_AD_TU700)},
+ {USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_T_USB_XE_REV2)},
+ {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_PC160_2T)},
+ {USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR_X)},
+/* 10 */{USB_DEVICE(USB_VID_XTENSIONS, USB_PID_XTENSIONS_XD_380)},
+ {USB_DEVICE(USB_VID_MSI_2, USB_PID_MSI_DIGIVOX_DUO)},
+ {USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR_X_2)},
+ {USB_DEVICE(USB_VID_TELESTAR, USB_PID_TELESTAR_STARSTICK_2)},
+ {USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A309)},
+ {0},
+};
+MODULE_DEVICE_TABLE(usb, af9015_usb_table);
+
+static struct dvb_usb_device_properties af9015_properties[] = {
+ {
+ .caps = DVB_USB_IS_AN_I2C_ADAPTER,
+
+ .usb_ctrl = DEVICE_SPECIFIC,
+ .download_firmware = af9015_download_firmware,
+ .firmware = "dvb-usb-af9015.fw",
+
+ .size_of_priv = sizeof(struct af9015_state), \
+
+ .num_adapters = 2,
+ .adapter = {
+ {
+ .caps = DVB_USB_ADAP_HAS_PID_FILTER |
+ DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+
+ .pid_filter_count = 32,
+ .pid_filter = af9015_pid_filter,
+ .pid_filter_ctrl = af9015_pid_filter_ctrl,
+
+ .frontend_attach =
+ af9015_af9013_frontend_attach,
+ .tuner_attach = af9015_tuner_attach,
+ .stream = {
+ .type = USB_BULK,
+ .count = 6,
+ .endpoint = 0x84,
+ },
+ },
+ {
+ .frontend_attach =
+ af9015_af9013_frontend_attach,
+ .tuner_attach = af9015_tuner_attach,
+ .stream = {
+ .type = USB_BULK,
+ .count = 6,
+ .endpoint = 0x85,
+ },
+ }
+ },
+
+ .identify_state = af9015_identify_state,
+
+ .rc_query = af9015_rc_query,
+ .rc_interval = 150,
+
+ .i2c_algo = &af9015_i2c_algo,
+
+ .num_device_descs = 9,
+ .devices = {
+ {
+ .name = "Afatech AF9015 DVB-T USB2.0 stick",
+ .cold_ids = {&af9015_usb_table[0],
+ &af9015_usb_table[1], NULL},
+ .warm_ids = {NULL},
+ },
+ {
+ .name = "Leadtek WinFast DTV Dongle Gold",
+ .cold_ids = {&af9015_usb_table[2], NULL},
+ .warm_ids = {NULL},
+ },
+ {
+ .name = "Pinnacle PCTV 71e",
+ .cold_ids = {&af9015_usb_table[3], NULL},
+ .warm_ids = {NULL},
+ },
+ {
+ .name = "KWorld PlusTV Dual DVB-T Stick " \
+ "(DVB-T 399U)",
+ .cold_ids = {&af9015_usb_table[4], NULL},
+ .warm_ids = {NULL},
+ },
+ {
+ .name = "DigitalNow TinyTwin DVB-T Receiver",
+ .cold_ids = {&af9015_usb_table[5], NULL},
+ .warm_ids = {NULL},
+ },
+ {
+ .name = "TwinHan AzureWave AD-TU700(704J)",
+ .cold_ids = {&af9015_usb_table[6], NULL},
+ .warm_ids = {NULL},
+ },
+ {
+ .name = "TerraTec Cinergy T USB XE",
+ .cold_ids = {&af9015_usb_table[7], NULL},
+ .warm_ids = {NULL},
+ },
+ {
+ .name = "KWorld PlusTV Dual DVB-T PCI " \
+ "(DVB-T PC160-2T)",
+ .cold_ids = {&af9015_usb_table[8], NULL},
+ .warm_ids = {NULL},
+ },
+ {
+ .name = "AVerMedia AVerTV DVB-T Volar X",
+ .cold_ids = {&af9015_usb_table[9], NULL},
+ .warm_ids = {NULL},
+ },
+ }
+ }, {
+ .caps = DVB_USB_IS_AN_I2C_ADAPTER,
+
+ .usb_ctrl = DEVICE_SPECIFIC,
+ .download_firmware = af9015_download_firmware,
+ .firmware = "dvb-usb-af9015.fw",
+
+ .size_of_priv = sizeof(struct af9015_state), \
+
+ .num_adapters = 2,
+ .adapter = {
+ {
+ .caps = DVB_USB_ADAP_HAS_PID_FILTER |
+ DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+
+ .pid_filter_count = 32,
+ .pid_filter = af9015_pid_filter,
+ .pid_filter_ctrl = af9015_pid_filter_ctrl,
+
+ .frontend_attach =
+ af9015_af9013_frontend_attach,
+ .tuner_attach = af9015_tuner_attach,
+ .stream = {
+ .type = USB_BULK,
+ .count = 6,
+ .endpoint = 0x84,
+ },
+ },
+ {
+ .frontend_attach =
+ af9015_af9013_frontend_attach,
+ .tuner_attach = af9015_tuner_attach,
+ .stream = {
+ .type = USB_BULK,
+ .count = 6,
+ .endpoint = 0x85,
+ },
+ }
+ },
+
+ .identify_state = af9015_identify_state,
+
+ .rc_query = af9015_rc_query,
+ .rc_interval = 150,
+
+ .i2c_algo = &af9015_i2c_algo,
+
+ .num_device_descs = 5,
+ .devices = {
+ {
+ .name = "Xtensions XD-380",
+ .cold_ids = {&af9015_usb_table[10], NULL},
+ .warm_ids = {NULL},
+ },
+ {
+ .name = "MSI DIGIVOX Duo",
+ .cold_ids = {&af9015_usb_table[11], NULL},
+ .warm_ids = {NULL},
+ },
+ {
+ .name = "Fujitsu-Siemens Slim Mobile USB DVB-T",
+ .cold_ids = {&af9015_usb_table[12], NULL},
+ .warm_ids = {NULL},
+ },
+ {
+ .name = "Telestar Starstick 2",
+ .cold_ids = {&af9015_usb_table[13], NULL},
+ .warm_ids = {NULL},
+ },
+ {
+ .name = "AVerMedia A309",
+ .cold_ids = {&af9015_usb_table[14], NULL},
+ .warm_ids = {NULL},
+ },
+ }
+ }
+};
+#undef AF9015_DEFAULT_PROPERTIES
+
+static int af9015_usb_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ int ret = 0;
+ struct dvb_usb_device *d = NULL;
+ struct usb_device *udev = interface_to_usbdev(intf);
+ u8 i;
+
+ deb_info("%s: interface:%d\n", __func__,
+ intf->cur_altsetting->desc.bInterfaceNumber);
+
+ /* interface 0 is used by DVB-T receiver and
+ interface 1 is for remote controller (HID) */
+ if (intf->cur_altsetting->desc.bInterfaceNumber == 0) {
+ ret = af9015_read_config(udev);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < af9015_properties_count; i++) {
+ ret = dvb_usb_device_init(intf, &af9015_properties[i],
+ THIS_MODULE, &d, adapter_nr);
+ if (!ret)
+ break;
+ if (ret != -ENODEV)
+ return ret;
+ }
+ if (ret)
+ return ret;
+
+ if (d)
+ ret = af9015_init(d);
+ }
+
+ return ret;
+}
+
+void af9015_i2c_exit(struct dvb_usb_device *d)
+{
+ struct af9015_state *state = d->priv;
+ deb_info("%s: \n", __func__);
+
+ /* remove 2nd I2C adapter */
+ if (d->state & DVB_USB_STATE_I2C)
+ i2c_del_adapter(&state->i2c_adap);
+}
+
+static void af9015_usb_device_exit(struct usb_interface *intf)
+{
+ struct dvb_usb_device *d = usb_get_intfdata(intf);
+ deb_info("%s: \n", __func__);
+
+ /* remove 2nd I2C adapter */
+ if (d != NULL && d->desc != NULL)
+ af9015_i2c_exit(d);
+
+ dvb_usb_device_exit(intf);
+}
+
+/* usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver af9015_usb_driver = {
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 15)
+ .owner = THIS_MODULE,
+#endif
+ .name = "dvb_usb_af9015",
+ .probe = af9015_usb_probe,
+ .disconnect = af9015_usb_device_exit,
+ .id_table = af9015_usb_table,
+};
+
+/* module stuff */
+static int __init af9015_usb_module_init(void)
+{
+ int ret;
+ ret = usb_register(&af9015_usb_driver);
+ if (ret)
+ err("module init failed:%d", ret);
+
+ return ret;
+}
+
+static void __exit af9015_usb_module_exit(void)
+{
+ /* deregister this driver from the USB subsystem */
+ usb_deregister(&af9015_usb_driver);
+}
+
+module_init(af9015_usb_module_init);
+module_exit(af9015_usb_module_exit);
+
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_DESCRIPTION("Driver for Afatech AF9015 DVB-T");
+MODULE_LICENSE("GPL");
diff --git a/linux/drivers/media/dvb/dvb-usb/af9015.h b/linux/drivers/media/dvb/dvb-usb/af9015.h
new file mode 100644
index 000000000..882e8a4b3
--- /dev/null
+++ b/linux/drivers/media/dvb/dvb-usb/af9015.h
@@ -0,0 +1,524 @@
+/*
+ * DVB USB Linux driver for Afatech AF9015 DVB-T USB2.0 receiver
+ *
+ * Copyright (C) 2007 Antti Palosaari <crope@iki.fi>
+ *
+ * Thanks to Afatech who kindly provided information.
+ *
+ * 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
+ * (at your option) 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef _DVB_USB_AF9015_H_
+#define _DVB_USB_AF9015_H_
+
+#define DVB_USB_LOG_PREFIX "af9015"
+#include "dvb-usb.h"
+
+extern int dvb_usb_af9015_debug;
+#define deb_info(args...) dprintk(dvb_usb_af9015_debug, 0x01, args)
+#define deb_rc(args...) dprintk(dvb_usb_af9015_debug, 0x02, args)
+#define deb_xfer(args...) dprintk(dvb_usb_af9015_debug, 0x04, args)
+#define deb_reg(args...) dprintk(dvb_usb_af9015_debug, 0x08, args)
+#define deb_i2c(args...) dprintk(dvb_usb_af9015_debug, 0x10, args)
+#define deb_fw(args...) dprintk(dvb_usb_af9015_debug, 0x20, args)
+
+#define AF9015_I2C_EEPROM 0xa0
+#define AF9015_I2C_DEMOD 0x38
+#define AF9015_USB_TIMEOUT 2000
+
+/* EEPROM locations */
+#define AF9015_EEPROM_IR_MODE 0x18
+#define AF9015_EEPROM_IR_REMOTE_TYPE 0x34
+#define AF9015_EEPROM_TS_MODE 0x31
+#define AF9015_EEPROM_DEMOD2_I2C 0x32
+
+#define AF9015_EEPROM_SAW_BW1 0x35
+#define AF9015_EEPROM_XTAL_TYPE1 0x36
+#define AF9015_EEPROM_SPEC_INV1 0x37
+#define AF9015_EEPROM_IF1L 0x38
+#define AF9015_EEPROM_IF1H 0x39
+#define AF9015_EEPROM_MT2060_IF1L 0x3a
+#define AF9015_EEPROM_MT2060_IF1H 0x3b
+#define AF9015_EEPROM_TUNER_ID1 0x3c
+
+#define AF9015_EEPROM_SAW_BW2 0x45
+#define AF9015_EEPROM_XTAL_TYPE2 0x46
+#define AF9015_EEPROM_SPEC_INV2 0x47
+#define AF9015_EEPROM_IF2L 0x48
+#define AF9015_EEPROM_IF2H 0x49
+#define AF9015_EEPROM_MT2060_IF2L 0x4a
+#define AF9015_EEPROM_MT2060_IF2H 0x4b
+#define AF9015_EEPROM_TUNER_ID2 0x4c
+
+#define AF9015_EEPROM_OFFSET (AF9015_EEPROM_SAW_BW2 - AF9015_EEPROM_SAW_BW1)
+
+#define AF9015_GPIO_ON (1 << 0)
+#define AF9015_GPIO_EN (1 << 1)
+#define AF9015_GPIO_O (1 << 2)
+#define AF9015_GPIO_I (1 << 3)
+
+#define AF9015_GPIO_TUNER_ON (AF9015_GPIO_ON|AF9015_GPIO_EN)
+#define AF9015_GPIO_TUNER_OFF (AF9015_GPIO_ON|AF9015_GPIO_EN|AF9015_GPIO_O)
+
+struct req_t {
+ u8 cmd; /* [0] */
+ /* seq */ /* [1] */
+ u8 i2c_addr; /* [2] */
+ u16 addr; /* [3|4] */
+ u8 mbox; /* [5] */
+ u8 addr_len; /* [6] */
+ u8 data_len; /* [7] */
+ u8 *data;
+};
+
+enum af9015_cmd {
+ GET_CONFIG = 0x10,
+ DOWNLOAD_FIRMWARE = 0x11,
+ BOOT = 0x13,
+ READ_MEMORY = 0x20,
+ WRITE_MEMORY = 0x21,
+ READ_WRITE_I2C = 0x22,
+ COPY_FIRMWARE = 0x23,
+ RECONNECT_USB = 0x5a,
+ WRITE_VIRTUAL_MEMORY = 0x26,
+ GET_IR_CODE = 0x27,
+ READ_I2C,
+ WRITE_I2C,
+};
+
+enum af9015_ir_mode {
+ AF9015_IR_MODE_DISABLED = 0,
+ AF9015_IR_MODE_HID,
+ AF9015_IR_MODE_RLC,
+ AF9015_IR_MODE_RC6,
+};
+
+struct af9015_state {
+ struct i2c_adapter i2c_adap; /* I2C adapter for 2nd FE */
+};
+
+struct af9015_config {
+ u8 dual_mode:1;
+ u16 mt2060_if1[2];
+ u16 firmware_size;
+ u16 firmware_checksum;
+ u8 *ir_table;
+ u16 ir_table_size;
+};
+
+enum af9015_remote {
+ AF9015_REMOTE_NONE = 0,
+ AF9015_REMOTE_A_LINK_DTU_M,
+ AF9015_REMOTE_MSI_DIGIVOX_MINI_II_V3,
+ AF9015_REMOTE_MYGICTV_U718,
+};
+
+/* Leadtek WinFast DTV Dongle Gold */
+static struct dvb_usb_rc_key af9015_rc_keys_leadtek[] = {
+ { 0x00, 0x1e, KEY_1 },
+ { 0x00, 0x1f, KEY_2 },
+ { 0x00, 0x20, KEY_3 },
+ { 0x00, 0x21, KEY_4 },
+ { 0x00, 0x22, KEY_5 },
+ { 0x00, 0x23, KEY_6 },
+ { 0x00, 0x24, KEY_7 },
+ { 0x00, 0x25, KEY_8 },
+ { 0x00, 0x26, KEY_9 },
+ { 0x00, 0x27, KEY_0 },
+ { 0x00, 0x28, KEY_ENTER },
+ { 0x00, 0x4f, KEY_VOLUMEUP },
+ { 0x00, 0x50, KEY_VOLUMEDOWN },
+ { 0x00, 0x51, KEY_CHANNELDOWN },
+ { 0x00, 0x52, KEY_CHANNELUP },
+};
+
+static u8 af9015_ir_table_leadtek[] = {
+ 0x03, 0xfc, 0x00, 0xff, 0x1a, 0x01, 0x00,
+ 0x03, 0xfc, 0x56, 0xa9, 0x00, 0x00, 0x00,
+ 0x03, 0xfc, 0x4b, 0xb4, 0x00, 0x00, 0x00,
+ 0x03, 0xfc, 0x4c, 0xb3, 0xb2, 0x04, 0x00,
+ 0x03, 0xfc, 0x4d, 0xb2, 0x00, 0x00, 0x00,
+ 0x03, 0xfc, 0x4e, 0xb1, 0x00, 0x00, 0x00,
+ 0x03, 0xfc, 0x1f, 0xe0, 0x3d, 0x00, 0x00,
+ 0x03, 0xfc, 0x40, 0xbf, 0x13, 0x01, 0x00,
+ 0x03, 0xfc, 0x14, 0xeb, 0x10, 0x00, 0x00,
+ 0x03, 0xfc, 0x49, 0xb6, 0x05, 0x01, 0x00,
+ 0x03, 0xfc, 0x50, 0xaf, 0x29, 0x00, 0x00,
+ 0x03, 0xfc, 0x0c, 0xf3, 0x52, 0x00, 0x00,
+ 0x03, 0xfc, 0x03, 0xfc, 0x09, 0x00, 0x00,
+ 0x03, 0xfc, 0x08, 0xf7, 0x50, 0x00, 0x00,
+ 0x03, 0xfc, 0x13, 0xec, 0x28, 0x00, 0x00,
+ 0x03, 0xfc, 0x04, 0xfb, 0x4f, 0x00, 0x00,
+ 0x03, 0xfc, 0x4f, 0xb0, 0x0f, 0x01, 0x00,
+ 0x03, 0xfc, 0x10, 0xef, 0x51, 0x00, 0x00,
+ 0x03, 0xfc, 0x51, 0xae, 0x3f, 0x00, 0x00,
+ 0x03, 0xfc, 0x42, 0xbd, 0x13, 0x00, 0x00,
+ 0x03, 0xfc, 0x43, 0xbc, 0x00, 0x00, 0x00,
+ 0x03, 0xfc, 0x44, 0xbb, 0x11, 0x00, 0x00,
+ 0x03, 0xfc, 0x52, 0xad, 0x19, 0x00, 0x00,
+ 0x03, 0xfc, 0x54, 0xab, 0x05, 0x00, 0x00,
+ 0x03, 0xfc, 0x46, 0xb9, 0x29, 0x00, 0x00,
+ 0x03, 0xfc, 0x55, 0xaa, 0x2b, 0x00, 0x00,
+ 0x03, 0xfc, 0x53, 0xac, 0x41, 0x00, 0x00,
+ 0x03, 0xfc, 0x05, 0xfa, 0x1e, 0x00, 0x00,
+ 0x03, 0xfc, 0x06, 0xf9, 0x1f, 0x00, 0x00,
+ 0x03, 0xfc, 0x07, 0xf8, 0x20, 0x00, 0x00,
+ 0x03, 0xfc, 0x1e, 0xe1, 0x19, 0x00, 0x00,
+ 0x03, 0xfc, 0x09, 0xf6, 0x21, 0x00, 0x00,
+ 0x03, 0xfc, 0x0a, 0xf5, 0x22, 0x00, 0x00,
+ 0x03, 0xfc, 0x0b, 0xf4, 0x23, 0x00, 0x00,
+ 0x03, 0xfc, 0x1b, 0xe4, 0x16, 0x00, 0x00,
+ 0x03, 0xfc, 0x0d, 0xf2, 0x24, 0x00, 0x00,
+ 0x03, 0xfc, 0x0e, 0xf1, 0x25, 0x00, 0x00,
+ 0x03, 0xfc, 0x0f, 0xf0, 0x26, 0x00, 0x00,
+ 0x03, 0xfc, 0x16, 0xe9, 0x28, 0x00, 0x00,
+ 0x03, 0xfc, 0x41, 0xbe, 0x37, 0x00, 0x00,
+ 0x03, 0xfc, 0x12, 0xed, 0x27, 0x00, 0x00,
+ 0x03, 0xfc, 0x11, 0xee, 0x2a, 0x00, 0x00,
+ 0x03, 0xfc, 0x48, 0xb7, 0x2c, 0x00, 0x00,
+ 0x03, 0xfc, 0x4a, 0xb5, 0x3c, 0x00, 0x00,
+ 0x03, 0xfc, 0x47, 0xb8, 0x15, 0x01, 0x00,
+ 0x03, 0xfc, 0x45, 0xba, 0x0b, 0x01, 0x00,
+ 0x03, 0xfc, 0x5e, 0xa1, 0x43, 0x00, 0x00,
+ 0x03, 0xfc, 0x5a, 0xa5, 0x42, 0x00, 0x00,
+ 0x03, 0xfc, 0x5b, 0xa4, 0x4b, 0x00, 0x00,
+ 0x03, 0xfc, 0x5f, 0xa0, 0x4e, 0x00, 0x00,
+};
+
+/* TwinHan AzureWave AD-TU700(704J) */
+static struct dvb_usb_rc_key af9015_rc_keys_twinhan[] = {
+ { 0x05, 0x3f, KEY_POWER },
+ { 0x00, 0x19, KEY_FAVORITES }, /* Favorite List */
+ { 0x00, 0x04, KEY_TEXT }, /* Teletext */
+ { 0x00, 0x0e, KEY_POWER },
+ { 0x00, 0x0e, KEY_INFO }, /* Preview */
+ { 0x00, 0x08, KEY_EPG }, /* Info/EPG */
+ { 0x00, 0x0f, KEY_LIST }, /* Record List */
+ { 0x00, 0x1e, KEY_1 },
+ { 0x00, 0x1f, KEY_2 },
+ { 0x00, 0x20, KEY_3 },
+ { 0x00, 0x21, KEY_4 },
+ { 0x00, 0x22, KEY_5 },
+ { 0x00, 0x23, KEY_6 },
+ { 0x00, 0x24, KEY_7 },
+ { 0x00, 0x25, KEY_8 },
+ { 0x00, 0x26, KEY_9 },
+ { 0x00, 0x27, KEY_0 },
+ { 0x00, 0x29, KEY_CANCEL }, /* Cancel */
+ { 0x00, 0x4c, KEY_CLEAR }, /* Clear */
+ { 0x00, 0x2a, KEY_BACK }, /* Back */
+ { 0x00, 0x2b, KEY_TAB }, /* Tab */
+ { 0x00, 0x52, KEY_UP }, /* up arrow */
+ { 0x00, 0x51, KEY_DOWN }, /* down arrow */
+ { 0x00, 0x4f, KEY_RIGHT }, /* right arrow */
+ { 0x00, 0x50, KEY_LEFT }, /* left arrow */
+ { 0x00, 0x28, KEY_ENTER }, /* Enter / ok */
+ { 0x02, 0x52, KEY_VOLUMEUP },
+ { 0x02, 0x51, KEY_VOLUMEDOWN },
+ { 0x00, 0x4e, KEY_CHANNELDOWN },
+ { 0x00, 0x4b, KEY_CHANNELUP },
+ { 0x00, 0x4a, KEY_RECORD },
+ { 0x01, 0x11, KEY_PLAY },
+ { 0x00, 0x17, KEY_PAUSE },
+ { 0x00, 0x0c, KEY_REWIND }, /* FR << */
+ { 0x00, 0x11, KEY_FASTFORWARD }, /* FF >> */
+ { 0x01, 0x15, KEY_PREVIOUS }, /* Replay */
+ { 0x01, 0x0e, KEY_NEXT }, /* Skip */
+ { 0x00, 0x13, KEY_CAMERA }, /* Capture */
+ { 0x01, 0x0f, KEY_LANGUAGE }, /* SAP */
+ { 0x01, 0x13, KEY_TV2 }, /* PIP */
+ { 0x00, 0x1d, KEY_ZOOM }, /* Full Screen */
+ { 0x01, 0x17, KEY_SUBTITLE }, /* Subtitle / CC */
+ { 0x00, 0x10, KEY_MUTE },
+ { 0x01, 0x19, KEY_AUDIO }, /* L/R */ /* TODO better event */
+ { 0x01, 0x16, KEY_SLEEP }, /* Hibernate */
+ { 0x01, 0x16, KEY_SWITCHVIDEOMODE },
+ /* A/V */ /* TODO does not work */
+ { 0x00, 0x06, KEY_AGAIN }, /* Recall */
+ { 0x01, 0x16, KEY_KPPLUS }, /* Zoom+ */ /* TODO does not work */
+ { 0x01, 0x16, KEY_KPMINUS }, /* Zoom- */ /* TODO does not work */
+ { 0x02, 0x15, KEY_RED },
+ { 0x02, 0x0a, KEY_GREEN },
+ { 0x02, 0x1c, KEY_YELLOW },
+ { 0x02, 0x05, KEY_BLUE },
+};
+
+static u8 af9015_ir_table_twinhan[] = {
+ 0x00, 0xff, 0x16, 0xe9, 0x3f, 0x05, 0x00,
+ 0x00, 0xff, 0x07, 0xf8, 0x16, 0x01, 0x00,
+ 0x00, 0xff, 0x14, 0xeb, 0x11, 0x01, 0x00,
+ 0x00, 0xff, 0x1a, 0xe5, 0x4d, 0x00, 0x00,
+ 0x00, 0xff, 0x4c, 0xb3, 0x17, 0x00, 0x00,
+ 0x00, 0xff, 0x12, 0xed, 0x11, 0x00, 0x00,
+ 0x00, 0xff, 0x40, 0xbf, 0x0c, 0x00, 0x00,
+ 0x00, 0xff, 0x11, 0xee, 0x4a, 0x00, 0x00,
+ 0x00, 0xff, 0x54, 0xab, 0x13, 0x00, 0x00,
+ 0x00, 0xff, 0x41, 0xbe, 0x15, 0x01, 0x00,
+ 0x00, 0xff, 0x42, 0xbd, 0x0e, 0x01, 0x00,
+ 0x00, 0xff, 0x43, 0xbc, 0x17, 0x01, 0x00,
+ 0x00, 0xff, 0x50, 0xaf, 0x0f, 0x01, 0x00,
+ 0x00, 0xff, 0x4d, 0xb2, 0x1d, 0x00, 0x00,
+ 0x00, 0xff, 0x47, 0xb8, 0x13, 0x01, 0x00,
+ 0x00, 0xff, 0x05, 0xfa, 0x4b, 0x00, 0x00,
+ 0x00, 0xff, 0x02, 0xfd, 0x4e, 0x00, 0x00,
+ 0x00, 0xff, 0x0e, 0xf1, 0x06, 0x00, 0x00,
+ 0x00, 0xff, 0x1e, 0xe1, 0x52, 0x02, 0x00,
+ 0x00, 0xff, 0x0a, 0xf5, 0x51, 0x02, 0x00,
+ 0x00, 0xff, 0x10, 0xef, 0x10, 0x00, 0x00,
+ 0x00, 0xff, 0x49, 0xb6, 0x19, 0x01, 0x00,
+ 0x00, 0xff, 0x15, 0xea, 0x27, 0x00, 0x00,
+ 0x00, 0xff, 0x03, 0xfc, 0x1e, 0x00, 0x00,
+ 0x00, 0xff, 0x01, 0xfe, 0x1f, 0x00, 0x00,
+ 0x00, 0xff, 0x06, 0xf9, 0x20, 0x00, 0x00,
+ 0x00, 0xff, 0x09, 0xf6, 0x21, 0x00, 0x00,
+ 0x00, 0xff, 0x1d, 0xe2, 0x22, 0x00, 0x00,
+ 0x00, 0xff, 0x1f, 0xe0, 0x23, 0x00, 0x00,
+ 0x00, 0xff, 0x0d, 0xf2, 0x24, 0x00, 0x00,
+ 0x00, 0xff, 0x19, 0xe6, 0x25, 0x00, 0x00,
+ 0x00, 0xff, 0x1b, 0xe4, 0x26, 0x00, 0x00,
+ 0x00, 0xff, 0x00, 0xff, 0x2b, 0x00, 0x00,
+ 0x00, 0xff, 0x4a, 0xb5, 0x4c, 0x00, 0x00,
+ 0x00, 0xff, 0x4b, 0xb4, 0x52, 0x00, 0x00,
+ 0x00, 0xff, 0x51, 0xae, 0x51, 0x00, 0x00,
+ 0x00, 0xff, 0x52, 0xad, 0x4f, 0x00, 0x00,
+ 0x00, 0xff, 0x4e, 0xb1, 0x50, 0x00, 0x00,
+ 0x00, 0xff, 0x0c, 0xf3, 0x29, 0x00, 0x00,
+ 0x00, 0xff, 0x4f, 0xb0, 0x28, 0x00, 0x00,
+ 0x00, 0xff, 0x13, 0xec, 0x2a, 0x00, 0x00,
+ 0x00, 0xff, 0x17, 0xe8, 0x19, 0x00, 0x00,
+ 0x00, 0xff, 0x04, 0xfb, 0x0f, 0x00, 0x00,
+ 0x00, 0xff, 0x48, 0xb7, 0x0e, 0x00, 0x00,
+ 0x00, 0xff, 0x0f, 0xf0, 0x04, 0x00, 0x00,
+ 0x00, 0xff, 0x1c, 0xe3, 0x08, 0x00, 0x00,
+ 0x00, 0xff, 0x18, 0xe7, 0x15, 0x02, 0x00,
+ 0x00, 0xff, 0x53, 0xac, 0x0a, 0x02, 0x00,
+ 0x00, 0xff, 0x5e, 0xa1, 0x1c, 0x02, 0x00,
+ 0x00, 0xff, 0x5f, 0xa0, 0x05, 0x02, 0x00,
+};
+
+/* A-Link DTU(m) */
+static struct dvb_usb_rc_key af9015_rc_keys_a_link[] = {
+ { 0x00, 0x1e, KEY_1 },
+ { 0x00, 0x1f, KEY_2 },
+ { 0x00, 0x20, KEY_3 },
+ { 0x00, 0x21, KEY_4 },
+ { 0x00, 0x22, KEY_5 },
+ { 0x00, 0x23, KEY_6 },
+ { 0x00, 0x24, KEY_7 },
+ { 0x00, 0x25, KEY_8 },
+ { 0x00, 0x26, KEY_9 },
+ { 0x00, 0x27, KEY_0 },
+ { 0x00, 0x2e, KEY_CHANNELUP },
+ { 0x00, 0x2d, KEY_CHANNELDOWN },
+ { 0x04, 0x28, KEY_ZOOM },
+ { 0x00, 0x41, KEY_MUTE },
+ { 0x00, 0x42, KEY_VOLUMEDOWN },
+ { 0x00, 0x43, KEY_VOLUMEUP },
+ { 0x00, 0x44, KEY_GOTO }, /* jump */
+ { 0x05, 0x45, KEY_POWER },
+};
+
+static u8 af9015_ir_table_a_link[] = {
+ 0x08, 0xf7, 0x12, 0xed, 0x45, 0x05, 0x00, /* power */
+ 0x08, 0xf7, 0x1a, 0xe5, 0x41, 0x00, 0x00, /* mute */
+ 0x08, 0xf7, 0x01, 0xfe, 0x1e, 0x00, 0x00, /* 1 */
+ 0x08, 0xf7, 0x1c, 0xe3, 0x21, 0x00, 0x00, /* 4 */
+ 0x08, 0xf7, 0x03, 0xfc, 0x24, 0x00, 0x00, /* 7 */
+ 0x08, 0xf7, 0x05, 0xfa, 0x28, 0x04, 0x00, /* zoom */
+ 0x08, 0xf7, 0x00, 0xff, 0x43, 0x00, 0x00, /* volume up */
+ 0x08, 0xf7, 0x16, 0xe9, 0x42, 0x00, 0x00, /* volume down */
+ 0x08, 0xf7, 0x0f, 0xf0, 0x1f, 0x00, 0x00, /* 2 */
+ 0x08, 0xf7, 0x0d, 0xf2, 0x22, 0x00, 0x00, /* 5 */
+ 0x08, 0xf7, 0x1b, 0xe4, 0x25, 0x00, 0x00, /* 8 */
+ 0x08, 0xf7, 0x06, 0xf9, 0x27, 0x00, 0x00, /* 0 */
+ 0x08, 0xf7, 0x14, 0xeb, 0x2e, 0x00, 0x00, /* channel up */
+ 0x08, 0xf7, 0x1d, 0xe2, 0x2d, 0x00, 0x00, /* channel down */
+ 0x08, 0xf7, 0x02, 0xfd, 0x20, 0x00, 0x00, /* 3 */
+ 0x08, 0xf7, 0x18, 0xe7, 0x23, 0x00, 0x00, /* 6 */
+ 0x08, 0xf7, 0x04, 0xfb, 0x26, 0x00, 0x00, /* 9 */
+ 0x08, 0xf7, 0x07, 0xf8, 0x44, 0x00, 0x00, /* jump */
+};
+
+/* MSI DIGIVOX mini II V3.0 */
+static struct dvb_usb_rc_key af9015_rc_keys_msi[] = {
+ { 0x00, 0x1e, KEY_1 },
+ { 0x00, 0x1f, KEY_2 },
+ { 0x00, 0x20, KEY_3 },
+ { 0x00, 0x21, KEY_4 },
+ { 0x00, 0x22, KEY_5 },
+ { 0x00, 0x23, KEY_6 },
+ { 0x00, 0x24, KEY_7 },
+ { 0x00, 0x25, KEY_8 },
+ { 0x00, 0x26, KEY_9 },
+ { 0x00, 0x27, KEY_0 },
+ { 0x03, 0x0f, KEY_CHANNELUP },
+ { 0x03, 0x0e, KEY_CHANNELDOWN },
+ { 0x00, 0x42, KEY_VOLUMEDOWN },
+ { 0x00, 0x43, KEY_VOLUMEUP },
+ { 0x05, 0x45, KEY_POWER },
+ { 0x00, 0x52, KEY_UP }, /* up */
+ { 0x00, 0x51, KEY_DOWN }, /* down */
+ { 0x00, 0x28, KEY_ENTER },
+};
+
+static u8 af9015_ir_table_msi[] = {
+ 0x03, 0xfc, 0x17, 0xe8, 0x45, 0x05, 0x00, /* power */
+ 0x03, 0xfc, 0x0d, 0xf2, 0x51, 0x00, 0x00, /* down */
+ 0x03, 0xfc, 0x03, 0xfc, 0x52, 0x00, 0x00, /* up */
+ 0x03, 0xfc, 0x1a, 0xe5, 0x1e, 0x00, 0x00, /* 1 */
+ 0x03, 0xfc, 0x02, 0xfd, 0x1f, 0x00, 0x00, /* 2 */
+ 0x03, 0xfc, 0x04, 0xfb, 0x20, 0x00, 0x00, /* 3 */
+ 0x03, 0xfc, 0x1c, 0xe3, 0x21, 0x00, 0x00, /* 4 */
+ 0x03, 0xfc, 0x08, 0xf7, 0x22, 0x00, 0x00, /* 5 */
+ 0x03, 0xfc, 0x1d, 0xe2, 0x23, 0x00, 0x00, /* 6 */
+ 0x03, 0xfc, 0x11, 0xee, 0x24, 0x00, 0x00, /* 7 */
+ 0x03, 0xfc, 0x0b, 0xf4, 0x25, 0x00, 0x00, /* 8 */
+ 0x03, 0xfc, 0x10, 0xef, 0x26, 0x00, 0x00, /* 9 */
+ 0x03, 0xfc, 0x09, 0xf6, 0x27, 0x00, 0x00, /* 0 */
+ 0x03, 0xfc, 0x14, 0xeb, 0x43, 0x00, 0x00, /* volume up */
+ 0x03, 0xfc, 0x1f, 0xe0, 0x42, 0x00, 0x00, /* volume down */
+ 0x03, 0xfc, 0x15, 0xea, 0x0f, 0x03, 0x00, /* channel up */
+ 0x03, 0xfc, 0x05, 0xfa, 0x0e, 0x03, 0x00, /* channel down */
+ 0x03, 0xfc, 0x16, 0xe9, 0x28, 0x00, 0x00, /* enter */
+};
+
+/* MYGICTV U718 */
+static struct dvb_usb_rc_key af9015_rc_keys_mygictv[] = {
+ { 0x00, 0x3d, KEY_SWITCHVIDEOMODE },
+ /* TV / AV */
+ { 0x05, 0x45, KEY_POWER },
+ { 0x00, 0x1e, KEY_1 },
+ { 0x00, 0x1f, KEY_2 },
+ { 0x00, 0x20, KEY_3 },
+ { 0x00, 0x21, KEY_4 },
+ { 0x00, 0x22, KEY_5 },
+ { 0x00, 0x23, KEY_6 },
+ { 0x00, 0x24, KEY_7 },
+ { 0x00, 0x25, KEY_8 },
+ { 0x00, 0x26, KEY_9 },
+ { 0x00, 0x27, KEY_0 },
+ { 0x00, 0x41, KEY_MUTE },
+ { 0x00, 0x2a, KEY_ESC }, /* Esc */
+ { 0x00, 0x2e, KEY_CHANNELUP },
+ { 0x00, 0x2d, KEY_CHANNELDOWN },
+ { 0x00, 0x42, KEY_VOLUMEDOWN },
+ { 0x00, 0x43, KEY_VOLUMEUP },
+ { 0x00, 0x52, KEY_UP }, /* up arrow */
+ { 0x00, 0x51, KEY_DOWN }, /* down arrow */
+ { 0x00, 0x4f, KEY_RIGHT }, /* right arrow */
+ { 0x00, 0x50, KEY_LEFT }, /* left arrow */
+ { 0x00, 0x28, KEY_ENTER }, /* ok */
+ { 0x01, 0x15, KEY_RECORD },
+ { 0x03, 0x13, KEY_PLAY },
+ { 0x01, 0x13, KEY_PAUSE },
+ { 0x01, 0x16, KEY_STOP },
+ { 0x03, 0x07, KEY_REWIND }, /* FR << */
+ { 0x03, 0x09, KEY_FASTFORWARD }, /* FF >> */
+ { 0x00, 0x3b, KEY_TIME }, /* TimeShift */
+ { 0x00, 0x3e, KEY_CAMERA }, /* Snapshot */
+ { 0x03, 0x16, KEY_CYCLEWINDOWS }, /* yellow, min / max */
+ { 0x00, 0x00, KEY_ZOOM }, /* 'select' (?) */
+ { 0x03, 0x16, KEY_SHUFFLE }, /* Shuffle */
+ { 0x03, 0x45, KEY_POWER },
+};
+
+static u8 af9015_ir_table_mygictv[] = {
+ 0x02, 0xbd, 0x0c, 0xf3, 0x3d, 0x00, 0x00, /* TV / AV */
+ 0x02, 0xbd, 0x14, 0xeb, 0x45, 0x05, 0x00, /* power */
+ 0x02, 0xbd, 0x00, 0xff, 0x1e, 0x00, 0x00, /* 1 */
+ 0x02, 0xbd, 0x01, 0xfe, 0x1f, 0x00, 0x00, /* 2 */
+ 0x02, 0xbd, 0x02, 0xfd, 0x20, 0x00, 0x00, /* 3 */
+ 0x02, 0xbd, 0x03, 0xfc, 0x21, 0x00, 0x00, /* 4 */
+ 0x02, 0xbd, 0x04, 0xfb, 0x22, 0x00, 0x00, /* 5 */
+ 0x02, 0xbd, 0x05, 0xfa, 0x23, 0x00, 0x00, /* 6 */
+ 0x02, 0xbd, 0x06, 0xf9, 0x24, 0x00, 0x00, /* 7 */
+ 0x02, 0xbd, 0x07, 0xf8, 0x25, 0x00, 0x00, /* 8 */
+ 0x02, 0xbd, 0x08, 0xf7, 0x26, 0x00, 0x00, /* 9 */
+ 0x02, 0xbd, 0x09, 0xf6, 0x27, 0x00, 0x00, /* 0 */
+ 0x02, 0xbd, 0x0a, 0xf5, 0x41, 0x00, 0x00, /* mute */
+ 0x02, 0xbd, 0x1c, 0xe3, 0x2a, 0x00, 0x00, /* esc */
+ 0x02, 0xbd, 0x1f, 0xe0, 0x43, 0x00, 0x00, /* volume up */
+ 0x02, 0xbd, 0x12, 0xed, 0x52, 0x00, 0x00, /* up arrow */
+ 0x02, 0xbd, 0x11, 0xee, 0x50, 0x00, 0x00, /* left arrow */
+ 0x02, 0xbd, 0x15, 0xea, 0x28, 0x00, 0x00, /* ok */
+ 0x02, 0xbd, 0x10, 0xef, 0x4f, 0x00, 0x00, /* right arrow */
+ 0x02, 0xbd, 0x13, 0xec, 0x51, 0x00, 0x00, /* down arrow */
+ 0x02, 0xbd, 0x0e, 0xf1, 0x42, 0x00, 0x00, /* volume down */
+ 0x02, 0xbd, 0x19, 0xe6, 0x15, 0x01, 0x00, /* record */
+ 0x02, 0xbd, 0x1e, 0xe1, 0x13, 0x03, 0x00, /* play */
+ 0x02, 0xbd, 0x16, 0xe9, 0x16, 0x01, 0x00, /* stop */
+ 0x02, 0xbd, 0x0b, 0xf4, 0x28, 0x04, 0x00, /* yellow, min / max */
+ 0x02, 0xbd, 0x0f, 0xf0, 0x3b, 0x00, 0x00, /* time shift */
+ 0x02, 0xbd, 0x18, 0xe7, 0x2e, 0x00, 0x00, /* channel up */
+ 0x02, 0xbd, 0x1a, 0xe5, 0x2d, 0x00, 0x00, /* channel down */
+ 0x02, 0xbd, 0x17, 0xe8, 0x3e, 0x00, 0x00, /* snapshot */
+ 0x02, 0xbd, 0x40, 0xbf, 0x13, 0x01, 0x00, /* pause */
+ 0x02, 0xbd, 0x41, 0xbe, 0x09, 0x03, 0x00, /* FF >> */
+ 0x02, 0xbd, 0x42, 0xbd, 0x07, 0x03, 0x00, /* FR << */
+ 0x02, 0xbd, 0x43, 0xbc, 0x00, 0x00, 0x00, /* 'select' (?) */
+ 0x02, 0xbd, 0x44, 0xbb, 0x16, 0x03, 0x00, /* shuffle */
+ 0x02, 0xbd, 0x45, 0xba, 0x45, 0x03, 0x00, /* power */
+};
+
+/* KWorld PlusTV Dual DVB-T Stick (DVB-T 399U) */
+static u8 af9015_ir_table_kworld[] = {
+ 0x86, 0x6b, 0x0c, 0xf3, 0x2e, 0x07, 0x00,
+ 0x86, 0x6b, 0x16, 0xe9, 0x2d, 0x07, 0x00,
+ 0x86, 0x6b, 0x1d, 0xe2, 0x37, 0x07, 0x00,
+ 0x86, 0x6b, 0x00, 0xff, 0x1e, 0x07, 0x00,
+ 0x86, 0x6b, 0x01, 0xfe, 0x1f, 0x07, 0x00,
+ 0x86, 0x6b, 0x02, 0xfd, 0x20, 0x07, 0x00,
+ 0x86, 0x6b, 0x03, 0xfc, 0x21, 0x07, 0x00,
+ 0x86, 0x6b, 0x04, 0xfb, 0x22, 0x07, 0x00,
+ 0x86, 0x6b, 0x05, 0xfa, 0x23, 0x07, 0x00,
+ 0x86, 0x6b, 0x06, 0xf9, 0x24, 0x07, 0x00,
+ 0x86, 0x6b, 0x07, 0xf8, 0x25, 0x07, 0x00,
+ 0x86, 0x6b, 0x08, 0xf7, 0x26, 0x07, 0x00,
+ 0x86, 0x6b, 0x09, 0xf6, 0x4d, 0x07, 0x00,
+ 0x86, 0x6b, 0x0a, 0xf5, 0x4e, 0x07, 0x00,
+ 0x86, 0x6b, 0x14, 0xeb, 0x4f, 0x07, 0x00,
+ 0x86, 0x6b, 0x1e, 0xe1, 0x50, 0x07, 0x00,
+ 0x86, 0x6b, 0x17, 0xe8, 0x52, 0x07, 0x00,
+ 0x86, 0x6b, 0x1f, 0xe0, 0x51, 0x07, 0x00,
+ 0x86, 0x6b, 0x0e, 0xf1, 0x0b, 0x07, 0x00,
+ 0x86, 0x6b, 0x20, 0xdf, 0x0c, 0x07, 0x00,
+ 0x86, 0x6b, 0x42, 0xbd, 0x0d, 0x07, 0x00,
+ 0x86, 0x6b, 0x0b, 0xf4, 0x0e, 0x07, 0x00,
+ 0x86, 0x6b, 0x43, 0xbc, 0x0f, 0x07, 0x00,
+ 0x86, 0x6b, 0x10, 0xef, 0x10, 0x07, 0x00,
+ 0x86, 0x6b, 0x21, 0xde, 0x11, 0x07, 0x00,
+ 0x86, 0x6b, 0x13, 0xec, 0x12, 0x07, 0x00,
+ 0x86, 0x6b, 0x11, 0xee, 0x13, 0x07, 0x00,
+ 0x86, 0x6b, 0x12, 0xed, 0x14, 0x07, 0x00,
+ 0x86, 0x6b, 0x19, 0xe6, 0x15, 0x07, 0x00,
+ 0x86, 0x6b, 0x1a, 0xe5, 0x16, 0x07, 0x00,
+ 0x86, 0x6b, 0x1b, 0xe4, 0x17, 0x07, 0x00,
+ 0x86, 0x6b, 0x4b, 0xb4, 0x18, 0x07, 0x00,
+ 0x86, 0x6b, 0x40, 0xbf, 0x19, 0x07, 0x00,
+ 0x86, 0x6b, 0x44, 0xbb, 0x1a, 0x07, 0x00,
+ 0x86, 0x6b, 0x41, 0xbe, 0x1b, 0x07, 0x00,
+ 0x86, 0x6b, 0x22, 0xdd, 0x1c, 0x07, 0x00,
+ 0x86, 0x6b, 0x15, 0xea, 0x1d, 0x07, 0x00,
+ 0x86, 0x6b, 0x0f, 0xf0, 0x3f, 0x07, 0x00,
+ 0x86, 0x6b, 0x1c, 0xe3, 0x40, 0x07, 0x00,
+ 0x86, 0x6b, 0x4a, 0xb5, 0x41, 0x07, 0x00,
+ 0x86, 0x6b, 0x48, 0xb7, 0x42, 0x07, 0x00,
+ 0x86, 0x6b, 0x49, 0xb6, 0x43, 0x07, 0x00,
+ 0x86, 0x6b, 0x18, 0xe7, 0x44, 0x07, 0x00,
+ 0x86, 0x6b, 0x23, 0xdc, 0x45, 0x07, 0x00,
+};
+
+#endif
diff --git a/linux/drivers/media/dvb/dvb-usb/cxusb.c b/linux/drivers/media/dvb/dvb-usb/cxusb.c
index bffb44380..406d7fba3 100644
--- a/linux/drivers/media/dvb/dvb-usb/cxusb.c
+++ b/linux/drivers/media/dvb/dvb-usb/cxusb.c
@@ -742,7 +742,8 @@ static int cxusb_lgh064f_tuner_attach(struct dvb_usb_adapter *adap)
return 0;
}
-static int dvico_bluebird_xc2028_callback(void *ptr, int command, int arg)
+static int dvico_bluebird_xc2028_callback(void *ptr, int component,
+ int command, int arg)
{
struct dvb_usb_adapter *adap = ptr;
struct dvb_usb_device *d = adap->dev;
@@ -770,14 +771,16 @@ static int cxusb_dvico_xc3028_tuner_attach(struct dvb_usb_adapter *adap)
struct xc2028_config cfg = {
.i2c_adap = &adap->dev->i2c_adap,
.i2c_addr = 0x61,
- .callback = dvico_bluebird_xc2028_callback,
};
static struct xc2028_ctrl ctl = {
- .fname = "xc3028-v27.fw",
+ .fname = XC2028_DEFAULT_FIRMWARE,
.max_len = 64,
.demod = XC3028_FE_ZARLINK456,
};
+ /* FIXME: generalize & move to common area */
+ adap->fe->callback = dvico_bluebird_xc2028_callback;
+
fe = dvb_attach(xc2028_attach, adap->fe, &cfg);
if (fe == NULL || fe->ops.tuner_ops.set_config == NULL)
return -EIO;
diff --git a/linux/drivers/media/dvb/dvb-usb/dib0700.h b/linux/drivers/media/dvb/dvb-usb/dib0700.h
index 66d4dc6ba..739193943 100644
--- a/linux/drivers/media/dvb/dvb-usb/dib0700.h
+++ b/linux/drivers/media/dvb/dvb-usb/dib0700.h
@@ -31,6 +31,8 @@ extern int dvb_usb_dib0700_debug;
// 2 Byte: MPEG2 mode: 4MSB(1 = Master Mode, 0 = Slave Mode) 4LSB(Channel 1 = bit0, Channel 2 = bit1)
// 2 Byte: Analog mode: 4MSB(0 = 625 lines, 1 = 525 lines) 4LSB( " " )
#define REQUEST_SET_RC 0x11
+#define REQUEST_NEW_I2C_READ 0x12
+#define REQUEST_NEW_I2C_WRITE 0x13
#define REQUEST_GET_VERSION 0x15
struct dib0700_state {
@@ -39,6 +41,8 @@ struct dib0700_state {
u8 rc_toggle;
u8 rc_counter;
u8 is_dib7000pc;
+ u8 fw_use_new_i2c_api;
+ u8 disable_streaming_master_mode;
};
extern int dib0700_set_gpio(struct dvb_usb_device *, enum dib07x0_gpios gpio, u8 gpio_dir, u8 gpio_val);
diff --git a/linux/drivers/media/dvb/dvb-usb/dib0700_core.c b/linux/drivers/media/dvb/dvb-usb/dib0700_core.c
index a2162fda5..3c7bdef28 100644
--- a/linux/drivers/media/dvb/dvb-usb/dib0700_core.c
+++ b/linux/drivers/media/dvb/dvb-usb/dib0700_core.c
@@ -82,9 +82,98 @@ int dib0700_set_gpio(struct dvb_usb_device *d, enum dib07x0_gpios gpio, u8 gpio_
}
/*
- * I2C master xfer function
+ * I2C master xfer function (supported in 1.20 firmware)
*/
-static int dib0700_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg *msg,int num)
+static int dib0700_i2c_xfer_new(struct i2c_adapter *adap, struct i2c_msg *msg,
+ int num)
+{
+ /* The new i2c firmware messages are more reliable and in particular
+ properly support i2c read calls not preceded by a write */
+
+ struct dvb_usb_device *d = i2c_get_adapdata(adap);
+ uint8_t bus_mode = 1; /* 0=eeprom bus, 1=frontend bus */
+ uint8_t gen_mode = 0; /* 0=master i2c, 1=gpio i2c */
+ uint8_t en_start = 0;
+ uint8_t en_stop = 0;
+ uint8_t buf[255]; /* TBV: malloc ? */
+ int result, i;
+
+ /* Ensure nobody else hits the i2c bus while we're sending our
+ sequence of messages, (such as the remote control thread) */
+ if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+ return -EAGAIN;
+
+ for (i = 0; i < num; i++) {
+ if (i == 0) {
+ /* First message in the transaction */
+ en_start = 1;
+ } else if (!(msg[i].flags & I2C_M_NOSTART)) {
+ /* Device supports repeated-start */
+ en_start = 1;
+ } else {
+ /* Not the first packet and device doesn't support
+ repeated start */
+ en_start = 0;
+ }
+ if (i == (num - 1)) {
+ /* Last message in the transaction */
+ en_stop = 1;
+ }
+
+ if (msg[i].flags & I2C_M_RD) {
+ /* Read request */
+ u16 index, value;
+ uint8_t i2c_dest;
+
+ i2c_dest = (msg[i].addr << 1);
+ value = ((en_start << 7) | (en_stop << 6) |
+ (msg[i].len & 0x3F)) << 8 | i2c_dest;
+ /* I2C ctrl + FE bus; */
+ index = ((gen_mode<<6)&0xC0) | ((bus_mode<<4)&0x30);
+
+ result = usb_control_msg(d->udev,
+ usb_rcvctrlpipe(d->udev, 0),
+ REQUEST_NEW_I2C_READ,
+ USB_TYPE_VENDOR | USB_DIR_IN,
+ value, index, msg[i].buf,
+ msg[i].len,
+ USB_CTRL_GET_TIMEOUT);
+ if (result < 0) {
+ err("i2c read error (status = %d)\n", result);
+ break;
+ }
+ } else {
+ /* Write request */
+ buf[0] = REQUEST_NEW_I2C_WRITE;
+ buf[1] = (msg[i].addr << 1);
+ buf[2] = (en_start << 7) | (en_stop << 6) |
+ (msg[i].len & 0x3F);
+ /* I2C ctrl + FE bus; */
+ buf[3] = ((gen_mode<<6)&0xC0) | ((bus_mode<<4)&0x30);
+ /* The Actual i2c payload */
+ memcpy(&buf[4], msg[i].buf, msg[i].len);
+
+ result = usb_control_msg(d->udev,
+ usb_sndctrlpipe(d->udev, 0),
+ REQUEST_NEW_I2C_WRITE,
+ USB_TYPE_VENDOR | USB_DIR_OUT,
+ 0, 0, buf, msg[i].len + 4,
+ USB_CTRL_GET_TIMEOUT);
+ if (result < 0) {
+ err("i2c write error (status = %d)\n", result);
+ break;
+ }
+ }
+ }
+ mutex_unlock(&d->i2c_mutex);
+ return i;
+}
+
+/*
+ * I2C master xfer function (pre-1.20 firmware)
+ */
+static int dib0700_i2c_xfer_legacy(struct i2c_adapter *adap,
+ struct i2c_msg *msg, int num)
{
struct dvb_usb_device *d = i2c_get_adapdata(adap);
int i,len;
@@ -124,6 +213,21 @@ static int dib0700_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg *msg,int num
return i;
}
+static int dib0700_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msg,
+ int num)
+{
+ struct dvb_usb_device *d = i2c_get_adapdata(adap);
+ struct dib0700_state *st = d->priv;
+
+ if (st->fw_use_new_i2c_api == 1) {
+ /* User running at least fw 1.20 */
+ return dib0700_i2c_xfer_new(adap, msg, num);
+ } else {
+ /* Use legacy calls */
+ return dib0700_i2c_xfer_legacy(adap, msg, num);
+ }
+}
+
static u32 dib0700_i2c_func(struct i2c_adapter *adapter)
{
return I2C_FUNC_I2C;
@@ -246,7 +350,12 @@ int dib0700_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
b[0] = REQUEST_ENABLE_VIDEO;
b[1] = (onoff << 4) | 0x00; /* this bit gives a kind of command, rather than enabling something or not */
- b[2] = (0x01 << 4); /* Master mode */
+
+ if (st->disable_streaming_master_mode == 1)
+ b[2] = 0x00;
+ else
+ b[2] = (0x01 << 4); /* Master mode */
+
b[3] = 0x00;
deb_info("modifying (%d) streaming state for %d\n", onoff, adap->id);
diff --git a/linux/drivers/media/dvb/dvb-usb/dib0700_devices.c b/linux/drivers/media/dvb/dvb-usb/dib0700_devices.c
index 6c0e5c5f4..9891ca092 100644
--- a/linux/drivers/media/dvb/dvb-usb/dib0700_devices.c
+++ b/linux/drivers/media/dvb/dvb-usb/dib0700_devices.c
@@ -14,6 +14,8 @@
#include "mt2060.h"
#include "mt2266.h"
#include "tuner-xc2028.h"
+#include "xc5000.h"
+#include "s5h1411.h"
#include "dib0070.h"
static int force_lna_activation;
@@ -366,7 +368,8 @@ static struct dib7000p_config stk7700ph_dib7700_xc3028_config = {
.gpio_pwm_pos = DIB7000P_GPIO_DEFAULT_PWM_POS,
};
-static int stk7700ph_xc3028_callback(void *ptr, int command, int arg)
+static int stk7700ph_xc3028_callback(void *ptr, int component,
+ int command, int arg)
{
struct dvb_usb_adapter *adap = ptr;
@@ -394,7 +397,6 @@ static struct xc2028_ctrl stk7700ph_xc3028_ctrl = {
static struct xc2028_config stk7700ph_xc3028_config = {
.i2c_addr = 0x61,
- .callback = stk7700ph_xc3028_callback,
.ctrl = &stk7700ph_xc3028_ctrl,
};
@@ -435,7 +437,9 @@ static int stk7700ph_tuner_attach(struct dvb_usb_adapter *adap)
DIBX000_I2C_INTERFACE_TUNER, 1);
stk7700ph_xc3028_config.i2c_adap = tun_i2c;
- stk7700ph_xc3028_config.video_dev = adap;
+
+ /* FIXME: generalize & move to common area */
+ adap->fe->callback = stk7700ph_xc3028_callback;
return dvb_attach(xc2028_attach, adap->fe, &stk7700ph_xc3028_config)
== NULL ? -ENODEV : 0;
@@ -677,6 +681,43 @@ static struct dvb_usb_rc_key dib0700_rc_keys[] = {
{ 0x01, 0x7d, KEY_VOLUMEDOWN },
{ 0x02, 0x42, KEY_CHANNELUP },
{ 0x00, 0x7d, KEY_CHANNELDOWN },
+
+ /* Key codes for Nova-TD "credit card" remote control. */
+ { 0x1d, 0x00, KEY_0 },
+ { 0x1d, 0x01, KEY_1 },
+ { 0x1d, 0x02, KEY_2 },
+ { 0x1d, 0x03, KEY_3 },
+ { 0x1d, 0x04, KEY_4 },
+ { 0x1d, 0x05, KEY_5 },
+ { 0x1d, 0x06, KEY_6 },
+ { 0x1d, 0x07, KEY_7 },
+ { 0x1d, 0x08, KEY_8 },
+ { 0x1d, 0x09, KEY_9 },
+ { 0x1d, 0x0a, KEY_TEXT },
+ { 0x1d, 0x0d, KEY_MENU },
+ { 0x1d, 0x0f, KEY_MUTE },
+ { 0x1d, 0x10, KEY_VOLUMEUP },
+ { 0x1d, 0x11, KEY_VOLUMEDOWN },
+ { 0x1d, 0x12, KEY_CHANNEL },
+ { 0x1d, 0x14, KEY_UP },
+ { 0x1d, 0x15, KEY_DOWN },
+ { 0x1d, 0x16, KEY_LEFT },
+ { 0x1d, 0x17, KEY_RIGHT },
+ { 0x1d, 0x1c, KEY_TV },
+ { 0x1d, 0x1e, KEY_NEXT },
+ { 0x1d, 0x1f, KEY_BACK },
+ { 0x1d, 0x20, KEY_CHANNELUP },
+ { 0x1d, 0x21, KEY_CHANNELDOWN },
+ { 0x1d, 0x24, KEY_LAST },
+ { 0x1d, 0x25, KEY_OK },
+ { 0x1d, 0x30, KEY_PAUSE },
+ { 0x1d, 0x32, KEY_REWIND },
+ { 0x1d, 0x34, KEY_FASTFORWARD },
+ { 0x1d, 0x35, KEY_PLAY },
+ { 0x1d, 0x36, KEY_STOP },
+ { 0x1d, 0x37, KEY_RECORD },
+ { 0x1d, 0x3b, KEY_GOTO },
+ { 0x1d, 0x3d, KEY_POWER },
};
/* STK7700P: Hauppauge Nova-T Stick, AVerMedia Volar */
@@ -1078,6 +1119,92 @@ static int stk7070pd_frontend_attach1(struct dvb_usb_adapter *adap)
return adap->fe == NULL ? -ENODEV : 0;
}
+/* S5H1411 */
+static struct s5h1411_config pinnacle_801e_config = {
+ .output_mode = S5H1411_PARALLEL_OUTPUT,
+ .gpio = S5H1411_GPIO_OFF,
+ .mpeg_timing = S5H1411_MPEGTIMING_NONCONTINOUS_NONINVERTING_CLOCK,
+ .qam_if = S5H1411_IF_44000,
+ .vsb_if = S5H1411_IF_44000,
+ .inversion = S5H1411_INVERSION_OFF,
+ .status_mode = S5H1411_DEMODLOCKING
+};
+
+/* Pinnacle PCTV HD Pro 801e GPIOs map:
+ GPIO0 - currently unknown
+ GPIO1 - xc5000 tuner reset
+ GPIO2 - CX25843 sleep
+ GPIO3 - currently unknown
+ GPIO4 - currently unknown
+ GPIO6 - currently unknown
+ GPIO7 - currently unknown
+ GPIO9 - currently unknown
+ GPIO10 - CX25843 reset
+ */
+static int s5h1411_frontend_attach(struct dvb_usb_adapter *adap)
+{
+ struct dib0700_state *st = adap->dev->priv;
+
+ /* Make use of the new i2c functions from FW 1.20 */
+ st->fw_use_new_i2c_api = 1;
+
+ /* The s5h1411 requires the dib0700 to not be in master mode */
+ st->disable_streaming_master_mode = 1;
+
+ /* All msleep values taken from Windows USB trace */
+ dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 0);
+ dib0700_set_gpio(adap->dev, GPIO3, GPIO_OUT, 0);
+ dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1);
+ msleep(400);
+ dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0);
+ msleep(60);
+ dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1);
+ msleep(30);
+ dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1);
+ dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1);
+ dib0700_set_gpio(adap->dev, GPIO4, GPIO_OUT, 1);
+ dib0700_set_gpio(adap->dev, GPIO7, GPIO_OUT, 1);
+ dib0700_set_gpio(adap->dev, GPIO2, GPIO_OUT, 0);
+ msleep(30);
+
+ /* Put the CX25843 to sleep for now since we're in digital mode */
+ dib0700_set_gpio(adap->dev, GPIO2, GPIO_OUT, 1);
+
+ /* GPIOs are initialized, do the attach */
+ adap->fe = dvb_attach(s5h1411_attach, &pinnacle_801e_config,
+ &adap->dev->i2c_adap);
+ return adap->fe == NULL ? -ENODEV : 0;
+}
+
+static int dib0700_xc5000_tuner_callback(void *priv, int component,
+ int command, int arg)
+{
+ struct dvb_usb_adapter *adap = priv;
+
+ /* Reset the tuner */
+ dib0700_set_gpio(adap->dev, GPIO1, GPIO_OUT, 0);
+ msleep(330); /* from Windows USB trace */
+ dib0700_set_gpio(adap->dev, GPIO1, GPIO_OUT, 1);
+ msleep(330); /* from Windows USB trace */
+
+ return 0;
+}
+
+static struct xc5000_config s5h1411_xc5000_tunerconfig = {
+ .i2c_address = 0x64,
+ .if_khz = 5380,
+};
+
+static int xc5000_tuner_attach(struct dvb_usb_adapter *adap)
+{
+ return dvb_attach(xc5000_attach, adap->fe, &adap->dev->i2c_adap,
+ &s5h1411_xc5000_tunerconfig)
+ == NULL ? -ENODEV : 0;
+
+ /* FIXME: generalize & move to common area */
+ adap->fe->callback = dib0700_xc5000_tuner_callback;
+}
+
/* DVB-USB and USB stuff follows */
struct usb_device_id dib0700_usb_id_table[] = {
/* 0 */ { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_STK7700P) },
@@ -1119,6 +1246,11 @@ struct usb_device_id dib0700_usb_id_table[] = {
{ USB_DEVICE(USB_VID_LEADTEK, USB_PID_WINFAST_DTV_DONGLE_STK7700P_2) },
/* 35 */{ USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_NOVA_TD_STICK_52009) },
{ USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_NOVA_T_500_3) },
+ { USB_DEVICE(USB_VID_GIGABYTE, USB_PID_GIGABYTE_U8000) },
+ { USB_DEVICE(USB_VID_YUAN, USB_PID_YUAN_STK7700PH) },
+ { USB_DEVICE(USB_VID_ASUS, USB_PID_ASUS_U3000H) },
+/* 40 */{ USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV801E) },
+ { USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV801E_SE) },
{ 0 } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, dib0700_usb_id_table);
@@ -1126,7 +1258,7 @@ MODULE_DEVICE_TABLE(usb, dib0700_usb_id_table);
#define DIB0700_DEFAULT_DEVICE_PROPERTIES \
.caps = DVB_USB_IS_AN_I2C_ADAPTER, \
.usb_ctrl = DEVICE_SPECIFIC, \
- .firmware = "dvb-usb-dib0700-1.10.fw", \
+ .firmware = "dvb-usb-dib0700-1.20.fw", \
.download_firmware = dib0700_download_firmware, \
.no_reconnect = 1, \
.size_of_priv = sizeof(struct dib0700_state), \
@@ -1293,7 +1425,12 @@ struct dvb_usb_device_properties dib0700_devices[] = {
{ &dib0700_usb_id_table[31], NULL },
{ NULL },
}
- }
+ },
+
+ .rc_interval = DEFAULT_RC_INTERVAL,
+ .rc_key_map = dib0700_rc_keys,
+ .rc_key_map_size = ARRAY_SIZE(dib0700_rc_keys),
+ .rc_query = dib0700_rc_query
}, { DIB0700_DEFAULT_DEVICE_PROPERTIES,
.num_adapters = 1,
@@ -1408,7 +1545,7 @@ struct dvb_usb_device_properties dib0700_devices[] = {
},
},
- .num_device_descs = 3,
+ .num_device_descs = 5,
.devices = {
{ "Terratec Cinergy HT USB XE",
{ &dib0700_usb_id_table[27], NULL },
@@ -1422,6 +1559,47 @@ struct dvb_usb_device_properties dib0700_devices[] = {
{ &dib0700_usb_id_table[32], NULL },
{ NULL },
},
+ { "Gigabyte U8000-RH",
+ { &dib0700_usb_id_table[37], NULL },
+ { NULL },
+ },
+ { "YUAN High-Tech STK7700PH",
+ { &dib0700_usb_id_table[38], NULL },
+ { NULL },
+ },
+ { "Asus My Cinema-U3000Hybrid",
+ { &dib0700_usb_id_table[39], NULL },
+ { NULL },
+ },
+ },
+ .rc_interval = DEFAULT_RC_INTERVAL,
+ .rc_key_map = dib0700_rc_keys,
+ .rc_key_map_size = ARRAY_SIZE(dib0700_rc_keys),
+ .rc_query = dib0700_rc_query
+ }, { DIB0700_DEFAULT_DEVICE_PROPERTIES,
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .frontend_attach = s5h1411_frontend_attach,
+ .tuner_attach = xc5000_tuner_attach,
+
+ DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
+
+ .size_of_priv = sizeof(struct
+ dib0700_adapter_state),
+ },
+ },
+
+ .num_device_descs = 2,
+ .devices = {
+ { "Pinnacle PCTV HD Pro USB Stick",
+ { &dib0700_usb_id_table[40], NULL },
+ { NULL },
+ },
+ { "Pinnacle PCTV HD USB Stick",
+ { &dib0700_usb_id_table[41], NULL },
+ { NULL },
+ },
},
.rc_interval = DEFAULT_RC_INTERVAL,
.rc_key_map = dib0700_rc_keys,
diff --git a/linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
index 81369b79e..dfaf1d257 100644
--- a/linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
+++ b/linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
@@ -34,16 +34,19 @@
#define USB_VID_HAUPPAUGE 0x2040
#define USB_VID_HYPER_PALTEK 0x1025
#define USB_VID_KWORLD 0xeb2a
+#define USB_VID_KWORLD_2 0x1b80
#define USB_VID_KYE 0x0458
#define USB_VID_LEADTEK 0x0413
#define USB_VID_LITEON 0x04ca
#define USB_VID_MEDION 0x1660
#define USB_VID_MIGLIA 0x18f3
#define USB_VID_MSI 0x0db0
+#define USB_VID_MSI_2 0x1462
#define USB_VID_OPERA1 0x695c
#define USB_VID_PINNACLE 0x2304
#define USB_VID_TECHNOTREND 0x0b48
#define USB_VID_TERRATEC 0x0ccd
+#define USB_VID_TELESTAR 0x10b9
#define USB_VID_VISIONPLUS 0x13d3
#define USB_VID_TWINHAN 0x1822
#define USB_VID_ULTIMA_ELECTRONIC 0x05d8
@@ -51,15 +54,18 @@
#define USB_VID_WIDEVIEW 0x14aa
#define USB_VID_GIGABYTE 0x1044
#define USB_VID_YUAN 0x1164
-
+#define USB_VID_XTENSIONS 0x1ae7
/* Product IDs */
#define USB_PID_ADSTECH_USB2_COLD 0xa333
#define USB_PID_ADSTECH_USB2_WARM 0xa334
#define USB_PID_AFATECH_AF9005 0x9020
+#define USB_PID_AFATECH_AF9015_9015 0x9015
+#define USB_PID_AFATECH_AF9015_9016 0x9016
#define USB_VID_ALINK_DTU 0xf170
#define USB_PID_ANSONIC_DVBT_USB 0x6000
#define USB_PID_ANYSEE 0x861f
+#define USB_PID_AZUREWAVE_AD_TU700 0x3237
#define USB_PID_AVERMEDIA_DVBT_USB_COLD 0x0001
#define USB_PID_AVERMEDIA_DVBT_USB_WARM 0x0002
#define USB_PID_AVERMEDIA_DVBT_USB2_COLD 0xa800
@@ -89,9 +95,12 @@
#define USB_PID_UNIWILL_STK7700P 0x6003
#define USB_PID_GRANDTEC_DVBT_USB_COLD 0x0fa0
#define USB_PID_GRANDTEC_DVBT_USB_WARM 0x0fa1
+#define USB_PID_KWORLD_399U 0xe399
+#define USB_PID_KWORLD_PC160_2T 0xc160
#define USB_PID_KWORLD_VSTREAM_COLD 0x17de
#define USB_PID_KWORLD_VSTREAM_WARM 0x17df
#define USB_PID_TERRATEC_CINERGY_T_USB_XE 0x0055
+#define USB_PID_TERRATEC_CINERGY_T_USB_XE_REV2 0x0069
#define USB_PID_TWINHAN_VP7041_COLD 0x3201
#define USB_PID_TWINHAN_VP7041_WARM 0x3202
#define USB_PID_TWINHAN_VP7020_COLD 0x3203
@@ -100,6 +109,7 @@
#define USB_PID_TWINHAN_VP7045_WARM 0x3206
#define USB_PID_TWINHAN_VP7021_COLD 0x3207
#define USB_PID_TWINHAN_VP7021_WARM 0x3208
+#define USB_PID_TINYTWIN 0x3226
#define USB_PID_DNTV_TINYUSB2_COLD 0x3223
#define USB_PID_DNTV_TINYUSB2_WARM 0x3224
#define USB_PID_ULTIMA_TVBOX_COLD 0x8105
@@ -146,6 +156,9 @@
#define USB_PID_AVERMEDIA_HYBRID_ULTRA_USB_M039R 0x0039
#define USB_PID_AVERMEDIA_HYBRID_ULTRA_USB_M039R_ATSC 0x1039
#define USB_PID_AVERMEDIA_HYBRID_ULTRA_USB_M039R_DVBT 0x2039
+#define USB_PID_AVERMEDIA_VOLAR_X 0xa815
+#define USB_PID_AVERMEDIA_VOLAR_X_2 0x8150
+#define USB_PID_AVERMEDIA_A309 0xa309
#define USB_PID_TECHNOTREND_CONNECT_S2400 0x3006
#define USB_PID_TERRATEC_CINERGY_DT_XS_DIVERSITY 0x005a
#define USB_PID_TERRATEC_CINERGY_HT_USB_XE 0x0058
@@ -155,8 +168,11 @@
#define USB_PID_PINNACLE_PCTV2000E 0x022c
#define USB_PID_PINNACLE_PCTV_DVB_T_FLASH 0x0228
#define USB_PID_PINNACLE_PCTV_DUAL_DIVERSITY_DVB_T 0x0229
+#define USB_PID_PINNACLE_PCTV71E 0x022b
#define USB_PID_PINNACLE_PCTV72E 0x0236
#define USB_PID_PINNACLE_PCTV73E 0x0237
+#define USB_PID_PINNACLE_PCTV801E 0x023a
+#define USB_PID_PINNACLE_PCTV801E_SE 0x023b
#define USB_PID_PCTV_200E 0x020e
#define USB_PID_PCTV_400E 0x020f
#define USB_PID_PCTV_450E 0x0222
@@ -193,6 +209,7 @@
#define USB_PID_WINFAST_DTV_DONGLE_WARM 0x6026
#define USB_PID_WINFAST_DTV_DONGLE_STK7700P 0x6f00
#define USB_PID_WINFAST_DTV_DONGLE_STK7700P_2 0x6f01
+#define USB_PID_WINFAST_DTV_DONGLE_GOLD 0x6029
#define USB_PID_GENPIX_8PSK_REV_1_COLD 0x0200
#define USB_PID_GENPIX_8PSK_REV_1_WARM 0x0201
#define USB_PID_GENPIX_8PSK_REV_2 0x0202
@@ -200,14 +217,20 @@
#define USB_PID_GENPIX_SKYWALKER_CW3K 0x0204
#define USB_PID_SIGMATEK_DVB_110 0x6610
#define USB_PID_MSI_DIGI_VOX_MINI_II 0x1513
+#define USB_PID_MSI_DIGIVOX_DUO 0x8801
#define USB_PID_OPERA1_COLD 0x2830
#define USB_PID_OPERA1_WARM 0x3829
#define USB_PID_LIFEVIEW_TV_WALKER_TWIN_COLD 0x0514
#define USB_PID_LIFEVIEW_TV_WALKER_TWIN_WARM 0x0513
#define USB_PID_GIGABYTE_U7000 0x7001
+#define USB_PID_GIGABYTE_U8000 0x7002
#define USB_PID_ASUS_U3000 0x171f
+#define USB_PID_ASUS_U3000H 0x1736
#define USB_PID_ASUS_U3100 0x173f
#define USB_PID_YUAN_EC372S 0x1edc
+#define USB_PID_YUAN_STK7700PH 0x1f08
#define USB_PID_DW2102 0x2102
+#define USB_PID_XTENSIONS_XD_380 0x0381
+#define USB_PID_TELESTAR_STARSTICK_2 0x8000
#endif
diff --git a/linux/drivers/media/dvb/dvb-usb/dw2102.c b/linux/drivers/media/dvb/dvb-usb/dw2102.c
index a4d898b44..20ba3f129 100644
--- a/linux/drivers/media/dvb/dvb-usb/dw2102.c
+++ b/linux/drivers/media/dvb/dvb-usb/dw2102.c
@@ -1,4 +1,5 @@
-/* DVB USB framework compliant Linux driver for the DVBWorld DVB-S 2102 Card
+/* DVB USB framework compliant Linux driver for the
+* DVBWorld DVB-S 2101, 2102, DVB-S2 2104 Card
*
* Copyright (C) 2008 Igor M. Liplianin (liplianin@me.by)
*
@@ -10,62 +11,71 @@
*/
#include <linux/version.h>
#include "dw2102.h"
+#include "si21xx.h"
#include "stv0299.h"
#include "z0194a.h"
+#include "cx24116.h"
#ifndef USB_PID_DW2102
#define USB_PID_DW2102 0x2102
#endif
-#define DW2102_READ_MSG 0
-#define DW2102_WRITE_MSG 1
+#ifndef USB_PID_DW2104
+#define USB_PID_DW2104 0x2104
+#endif
+
+#define DW210X_READ_MSG 0
+#define DW210X_WRITE_MSG 1
#define REG_1F_SYMBOLRATE_BYTE0 0x1f
#define REG_20_SYMBOLRATE_BYTE1 0x20
#define REG_21_SYMBOLRATE_BYTE2 0x21
-
+/* on my own*/
#define DW2102_VOLTAGE_CTRL (0x1800)
#define DW2102_RC_QUERY (0x1a00)
-struct dw2102_state {
+struct dw210x_state {
u32 last_key_pressed;
};
-struct dw2102_rc_keys {
+struct dw210x_rc_keys {
u32 keycode;
u32 event;
};
+/* debug */
+static int dvb_usb_dw2102_debug;
+module_param_named(debug, dvb_usb_dw2102_debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debugging level (1=info 2=xfer (or-able))." DVB_USB_DEBUG_STATUS);
+
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
-static int dw2102_op_rw(struct usb_device *dev, u8 request, u16 value,
- u8 *data, u16 len, int flags)
+static int dw210x_op_rw(struct usb_device *dev, u8 request, u16 value,
+ u16 index, u8 * data, u16 len, int flags)
{
int ret;
u8 u8buf[len];
- unsigned int pipe = (flags == DW2102_READ_MSG) ?
- usb_rcvctrlpipe(dev, 0) : usb_sndctrlpipe(dev, 0);
- u8 request_type = (flags == DW2102_READ_MSG) ? USB_DIR_IN : USB_DIR_OUT;
+ unsigned int pipe = (flags == DW210X_READ_MSG) ?
+ usb_rcvctrlpipe(dev, 0) : usb_sndctrlpipe(dev, 0);
+ u8 request_type = (flags == DW210X_READ_MSG) ? USB_DIR_IN : USB_DIR_OUT;
- if (flags == DW2102_WRITE_MSG)
+ if (flags == DW210X_WRITE_MSG)
memcpy(u8buf, data, len);
- ret = usb_control_msg(dev, pipe, request,
- request_type | USB_TYPE_VENDOR, value, 0 , u8buf, len, 2000);
+ ret = usb_control_msg(dev, pipe, request, request_type | USB_TYPE_VENDOR,
+ value, index , u8buf, len, 2000);
- if (flags == DW2102_READ_MSG)
+ if (flags == DW210X_READ_MSG)
memcpy(data, u8buf, len);
return ret;
}
/* I2C */
-
static int dw2102_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],
int num)
{
struct dvb_usb_device *d = i2c_get_adapdata(adap);
int i = 0, ret = 0;
u8 buf6[] = {0x2c, 0x05, 0xc0, 0, 0, 0, 0};
- u8 request;
u16 value;
if (!d)
@@ -76,14 +86,12 @@ struct dvb_usb_device *d = i2c_get_adapdata(adap);
switch (num) {
case 2:
/* read stv0299 register */
- request = 0xb5;
value = msg[0].buf[0];/* register */
for (i = 0; i < msg[1].len; i++) {
value = value + i;
- ret = dw2102_op_rw(d->udev, 0xb5,
- value, buf6, 2, DW2102_READ_MSG);
+ ret = dw210x_op_rw(d->udev, 0xb5, value, 0,
+ buf6, 2, DW210X_READ_MSG);
msg[1].buf[i] = buf6[0];
-
}
break;
case 1:
@@ -93,8 +101,8 @@ struct dvb_usb_device *d = i2c_get_adapdata(adap);
buf6[0] = 0x2a;
buf6[1] = msg[0].buf[0];
buf6[2] = msg[0].buf[1];
- ret = dw2102_op_rw(d->udev, 0xb2,
- 0, buf6, 3, DW2102_WRITE_MSG);
+ ret = dw210x_op_rw(d->udev, 0xb2, 0, 0,
+ buf6, 3, DW210X_WRITE_MSG);
break;
case 0x60:
if (msg[0].flags == 0) {
@@ -106,26 +114,26 @@ struct dvb_usb_device *d = i2c_get_adapdata(adap);
buf6[4] = msg[0].buf[1];
buf6[5] = msg[0].buf[2];
buf6[6] = msg[0].buf[3];
- ret = dw2102_op_rw(d->udev, 0xb2,
- 0, buf6, 7, DW2102_WRITE_MSG);
+ ret = dw210x_op_rw(d->udev, 0xb2, 0, 0,
+ buf6, 7, DW210X_WRITE_MSG);
} else {
- /* write to tuner pll */
- ret = dw2102_op_rw(d->udev, 0xb5,
- 0, buf6, 1, DW2102_READ_MSG);
+ /* read from tuner */
+ ret = dw210x_op_rw(d->udev, 0xb5, 0, 0,
+ buf6, 1, DW210X_READ_MSG);
msg[0].buf[0] = buf6[0];
}
break;
case (DW2102_RC_QUERY):
- ret = dw2102_op_rw(d->udev, 0xb8,
- 0, buf6, 2, DW2102_READ_MSG);
+ ret = dw210x_op_rw(d->udev, 0xb8, 0, 0,
+ buf6, 2, DW210X_READ_MSG);
msg[0].buf[0] = buf6[0];
msg[0].buf[1] = buf6[1];
break;
case (DW2102_VOLTAGE_CTRL):
buf6[0] = 0x30;
buf6[1] = msg[0].buf[0];
- ret = dw2102_op_rw(d->udev, 0xb2,
- 0, buf6, 2, DW2102_WRITE_MSG);
+ ret = dw210x_op_rw(d->udev, 0xb2, 0, 0,
+ buf6, 2, DW210X_WRITE_MSG);
break;
}
@@ -136,17 +144,188 @@ struct dvb_usb_device *d = i2c_get_adapdata(adap);
return num;
}
-static u32 dw2102_i2c_func(struct i2c_adapter *adapter)
+static int dw2102_serit_i2c_transfer(struct i2c_adapter *adap,
+ struct i2c_msg msg[], int num)
+{
+ struct dvb_usb_device *d = i2c_get_adapdata(adap);
+ int ret = 0;
+ u8 buf6[] = {0, 0, 0, 0, 0, 0, 0};
+
+ if (!d)
+ return -ENODEV;
+ if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+ return -EAGAIN;
+
+ switch (num) {
+ case 2:
+ /* read si2109 register by number */
+ buf6[0] = 0xd0;
+ buf6[1] = msg[0].len;
+ buf6[2] = msg[0].buf[0];
+ ret = dw210x_op_rw(d->udev, 0xc2, 0, 0,
+ buf6, msg[0].len + 2, DW210X_WRITE_MSG);
+ /* read si2109 register */
+ ret = dw210x_op_rw(d->udev, 0xc3, 0xd0, 0,
+ buf6, msg[1].len + 2, DW210X_READ_MSG);
+ memcpy(msg[1].buf, buf6 + 2, msg[1].len);
+
+ break;
+ case 1:
+ switch (msg[0].addr) {
+ case 0x68:
+ /* write to si2109 register */
+ buf6[0] = 0xd0;
+ buf6[1] = msg[0].len;
+ memcpy(buf6 + 2, msg[0].buf, msg[0].len);
+ ret = dw210x_op_rw(d->udev, 0xc2, 0, 0, buf6,
+ msg[0].len + 2, DW210X_WRITE_MSG);
+ break;
+ case(DW2102_RC_QUERY):
+ ret = dw210x_op_rw(d->udev, 0xb8, 0, 0,
+ buf6, 2, DW210X_READ_MSG);
+ msg[0].buf[0] = buf6[0];
+ msg[0].buf[1] = buf6[1];
+ break;
+ case(DW2102_VOLTAGE_CTRL):
+ buf6[0] = 0x30;
+ buf6[1] = msg[0].buf[0];
+ ret = dw210x_op_rw(d->udev, 0xb2, 0, 0,
+ buf6, 2, DW210X_WRITE_MSG);
+ break;
+ }
+ break;
+ }
+
+ mutex_unlock(&d->i2c_mutex);
+ return num;
+}
+
+static int dw2104_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], int num)
+{
+ struct dvb_usb_device *d = i2c_get_adapdata(adap);
+ int ret = 0;
+ int len, i;
+
+ if (!d)
+ return -ENODEV;
+ if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+ return -EAGAIN;
+
+ switch (num) {
+ case 2: {
+ /* read */
+ /* first write first register number */
+ u8 ibuf [msg[1].len + 2], obuf[3];
+ obuf[0] = 0xaa;
+ obuf[1] = msg[0].len;
+ obuf[2] = msg[0].buf[0];
+ ret = dw210x_op_rw(d->udev, 0xc2, 0, 0,
+ obuf, msg[0].len + 2, DW210X_WRITE_MSG);
+ /* second read registers */
+ ret = dw210x_op_rw(d->udev, 0xc3, 0xab , 0,
+ ibuf, msg[1].len + 2, DW210X_READ_MSG);
+ memcpy(msg[1].buf, ibuf + 2, msg[1].len);
+
+ break;
+ }
+ case 1:
+ switch (msg[0].addr) {
+ case 0x55: {
+ if (msg[0].buf[0] == 0xf7) {
+ /* firmware */
+ /* Write in small blocks */
+ u8 obuf[19];
+ obuf[0] = 0xaa;
+ obuf[1] = 0x11;
+ obuf[2] = 0xf7;
+ len = msg[0].len - 1;
+ i = 1;
+ do {
+ memcpy(obuf + 3, msg[0].buf + i, (len > 16 ? 16 : len));
+ ret = dw210x_op_rw(d->udev, 0xc2, 0, 0,
+ obuf, (len > 16 ? 16 : len) + 3, DW210X_WRITE_MSG);
+ i += 16;
+ len -= 16;
+ } while (len > 0);
+ } else {
+ /* write to register */
+ u8 obuf[msg[0].len + 2];
+ obuf[0] = 0xaa;
+ obuf[1] = msg[0].len;
+ memcpy(obuf + 2, msg[0].buf, msg[0].len);
+ ret = dw210x_op_rw(d->udev, 0xc2, 0, 0,
+ obuf, msg[0].len + 2, DW210X_WRITE_MSG);
+ }
+ break;
+ }
+ case(DW2102_RC_QUERY): {
+ u8 ibuf[2];
+ ret = dw210x_op_rw(d->udev, 0xb8, 0, 0,
+ ibuf, 2, DW210X_READ_MSG);
+ memcpy(msg[0].buf, ibuf , 2);
+ break;
+ }
+ case(DW2102_VOLTAGE_CTRL): {
+ u8 obuf[2];
+ obuf[0] = 0x30;
+ obuf[1] = msg[0].buf[0];
+ ret = dw210x_op_rw(d->udev, 0xb2, 0, 0,
+ obuf, 2, DW210X_WRITE_MSG);
+ break;
+ }
+ }
+
+ break;
+ }
+
+ mutex_unlock(&d->i2c_mutex);
+ return num;
+}
+
+static u32 dw210x_i2c_func(struct i2c_adapter *adapter)
{
return I2C_FUNC_I2C;
}
static struct i2c_algorithm dw2102_i2c_algo = {
.master_xfer = dw2102_i2c_transfer,
- .functionality = dw2102_i2c_func,
+ .functionality = dw210x_i2c_func,
};
-static int dw2102_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
+static struct i2c_algorithm dw2102_serit_i2c_algo = {
+ .master_xfer = dw2102_serit_i2c_transfer,
+ .functionality = dw210x_i2c_func,
+};
+
+static struct i2c_algorithm dw2104_i2c_algo = {
+ .master_xfer = dw2104_i2c_transfer,
+ .functionality = dw210x_i2c_func,
+};
+
+static int dw210x_read_mac_address(struct dvb_usb_device *d, u8 mac[6])
+{
+ int i;
+ u8 ibuf[] = {0, 0};
+ u8 eeprom[256], eepromline[16];
+
+ for (i = 0; i < 256; i++) {
+ if (dw210x_op_rw(d->udev, 0xb6, 0xa0 , i, ibuf, 2, DW210X_READ_MSG) < 0) {
+ err("read eeprom failed.");
+ return -1;
+ } else {
+ eepromline[i%16] = ibuf[0];
+ eeprom[i] = ibuf[0];
+ }
+ if ((i % 16) == 15) {
+ deb_xfer("%02x: ", i - 15);
+ debug_dump(eepromline, 16, deb_xfer);
+ }
+ }
+ memcpy(mac, eeprom + 8, 6);
+ return 0;
+};
+
+static int dw210x_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
{
static u8 command_13v[1] = {0x00};
static u8 command_18v[1] = {0x01};
@@ -163,18 +342,55 @@ static int dw2102_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
return 0;
}
-static int dw2102_frontend_attach(struct dvb_usb_adapter *d)
+static struct cx24116_config dw2104_config = {
+ .demod_address = 0x55,
+ .mpg_clk_pos_pol = 0x01,
+};
+
+static struct si21xx_config serit_sp1511lhb_config = {
+ .demod_address = 0x68,
+ .min_delay_ms = 100,
+
+};
+
+static int dw2104_frontend_attach(struct dvb_usb_adapter *d)
{
- d->fe = dvb_attach(stv0299_attach, &sharp_z0194a_config,
- &d->dev->i2c_adap);
- if (d->fe != NULL) {
- d->fe->ops.set_voltage = dw2102_set_voltage;
- info("Attached stv0299!\n");
+ if ((d->fe = dvb_attach(cx24116_attach, &dw2104_config,
+ &d->dev->i2c_adap)) != NULL) {
+ d->fe->ops.set_voltage = dw210x_set_voltage;
+ info("Attached cx24116!\n");
return 0;
}
return -EIO;
}
+static struct dvb_usb_device_properties dw2102_properties;
+
+static int dw2102_frontend_attach(struct dvb_usb_adapter *d)
+{
+ if (dw2102_properties.i2c_algo == &dw2102_serit_i2c_algo) {
+ /*dw2102_properties.adapter->tuner_attach = NULL;*/
+ d->fe = dvb_attach(si21xx_attach, &serit_sp1511lhb_config,
+ &d->dev->i2c_adap);
+ if (d->fe != NULL) {
+ d->fe->ops.set_voltage = dw210x_set_voltage;
+ info("Attached si21xx!\n");
+ return 0;
+ }
+ }
+ if (dw2102_properties.i2c_algo == &dw2102_i2c_algo) {
+ /*dw2102_properties.adapter->tuner_attach = dw2102_tuner_attach;*/
+ d->fe = dvb_attach(stv0299_attach, &sharp_z0194a_config,
+ &d->dev->i2c_adap);
+ if (d->fe != NULL) {
+ d->fe->ops.set_voltage = dw210x_set_voltage;
+ info("Attached stv0299!\n");
+ return 0;
+ }
+ }
+ return -EIO;
+}
+
static int dw2102_tuner_attach(struct dvb_usb_adapter *adap)
{
dvb_attach(dvb_pll_attach, adap->fe, 0x60,
@@ -182,7 +398,7 @@ static int dw2102_tuner_attach(struct dvb_usb_adapter *adap)
return 0;
}
-static struct dvb_usb_rc_key dw2102_rc_keys[] = {
+static struct dvb_usb_rc_key dw210x_rc_keys[] = {
{ 0xf8, 0x0a, KEY_Q }, /*power*/
{ 0xf8, 0x0c, KEY_M }, /*mute*/
{ 0xf8, 0x11, KEY_1 },
@@ -221,7 +437,7 @@ static struct dvb_usb_rc_key dw2102_rc_keys[] = {
static int dw2102_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
{
- struct dw2102_state *st = d->priv;
+ struct dw210x_state *st = d->priv;
u8 key[2];
struct i2c_msg msg[] = {
{.addr = DW2102_RC_QUERY, .flags = I2C_M_RD, .buf = key,
@@ -231,12 +447,12 @@ static int dw2102_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
*state = REMOTE_NO_KEY_PRESSED;
if (dw2102_i2c_transfer(&d->i2c_adap, msg, 1) == 1) {
- for (i = 0; i < ARRAY_SIZE(dw2102_rc_keys); i++) {
- if (dw2102_rc_keys[i].data == msg[0].buf[0]) {
+ for (i = 0; i < ARRAY_SIZE(dw210x_rc_keys); i++) {
+ if (dw210x_rc_keys[i].data == msg[0].buf[0]) {
*state = REMOTE_KEY_PRESSED;
- *event = dw2102_rc_keys[i].event;
+ *event = dw210x_rc_keys[i].event;
st->last_key_pressed =
- dw2102_rc_keys[i].event;
+ dw210x_rc_keys[i].event;
break;
}
st->last_key_pressed = 0;
@@ -249,6 +465,8 @@ static int dw2102_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
static struct usb_device_id dw2102_table[] = {
{USB_DEVICE(USB_VID_CYPRESS, USB_PID_DW2102)},
{USB_DEVICE(USB_VID_CYPRESS, 0x2101)},
+ {USB_DEVICE(USB_VID_CYPRESS, 0x2104)},
+ {USB_DEVICE(0x9022, 0xd650)},
{ }
};
@@ -273,25 +491,23 @@ static int dw2102_load_firmware(struct usb_device *dev,
return ret;
}
break;
- case USB_PID_DW2102:
+ default:
fw = frmwr;
break;
}
- info("start downloading DW2102 firmware");
+ info("start downloading DW210X firmware");
p = kmalloc(fw->size, GFP_KERNEL);
reset = 1;
/*stop the CPU*/
- dw2102_op_rw(dev, 0xa0, 0x7f92, &reset, 1, DW2102_WRITE_MSG);
- dw2102_op_rw(dev, 0xa0, 0xe600, &reset, 1, DW2102_WRITE_MSG);
+ dw210x_op_rw(dev, 0xa0, 0x7f92, 0, &reset, 1, DW210X_WRITE_MSG);
+ dw210x_op_rw(dev, 0xa0, 0xe600, 0, &reset, 1, DW210X_WRITE_MSG);
if (p != NULL) {
memcpy(p, fw->data, fw->size);
for (i = 0; i < fw->size; i += 0x40) {
b = (u8 *) p + i;
- if (dw2102_op_rw
- (dev, 0xa0, i, b , 0x40,
- DW2102_WRITE_MSG) != 0x40
- ) {
+ if (dw210x_op_rw(dev, 0xa0, i, 0, b , 0x40,
+ DW210X_WRITE_MSG) != 0x40) {
err("error while transferring firmware");
ret = -EINVAL;
break;
@@ -299,43 +515,50 @@ static int dw2102_load_firmware(struct usb_device *dev,
}
/* restart the CPU */
reset = 0;
- if (ret || dw2102_op_rw
- (dev, 0xa0, 0x7f92, &reset, 1,
- DW2102_WRITE_MSG) != 1) {
+ if (ret || dw210x_op_rw(dev, 0xa0, 0x7f92, 0, &reset, 1,
+ DW210X_WRITE_MSG) != 1) {
err("could not restart the USB controller CPU.");
ret = -EINVAL;
}
- if (ret || dw2102_op_rw
- (dev, 0xa0, 0xe600, &reset, 1,
- DW2102_WRITE_MSG) != 1) {
+ if (ret || dw210x_op_rw(dev, 0xa0, 0xe600, 0, &reset, 1,
+ DW210X_WRITE_MSG) != 1) {
err("could not restart the USB controller CPU.");
ret = -EINVAL;
}
/* init registers */
switch (dev->descriptor.idProduct) {
+ case USB_PID_DW2104:
+ case 0xd650:
+ reset = 1;
+ dw210x_op_rw(dev, 0xc4, 0x0000, 0, &reset, 1,
+ DW210X_WRITE_MSG);
+ reset = 0;
+ dw210x_op_rw(dev, 0xbf, 0x0040, 0, &reset, 0,
+ DW210X_WRITE_MSG);
+ break;
case USB_PID_DW2102:
- dw2102_op_rw
- (dev, 0xbf, 0x0040, &reset, 0,
- DW2102_WRITE_MSG);
- dw2102_op_rw
- (dev, 0xb9, 0x0000, &reset16[0], 2,
- DW2102_READ_MSG);
+ dw210x_op_rw(dev, 0xbf, 0x0040, 0, &reset, 0,
+ DW210X_WRITE_MSG);
+ dw210x_op_rw(dev, 0xb9, 0x0000, 0, &reset16[0], 2,
+ DW210X_READ_MSG);
+ /* check STV0299 frontend */
+ dw210x_op_rw(dev, 0xb5, 0, 0, &reset16[0], 2,
+ DW210X_READ_MSG);
+ if (reset16[0] == 0xa1)
+ dw2102_properties.i2c_algo = &dw2102_i2c_algo;
break;
case 0x2101:
- dw2102_op_rw
- (dev, 0xbc, 0x0030, &reset16[0], 2,
- DW2102_READ_MSG);
- dw2102_op_rw
- (dev, 0xba, 0x0000, &reset16[0], 7,
- DW2102_READ_MSG);
- dw2102_op_rw
- (dev, 0xba, 0x0000, &reset16[0], 7,
- DW2102_READ_MSG);
- dw2102_op_rw
- (dev, 0xb9, 0x0000, &reset16[0], 2,
- DW2102_READ_MSG);
+ dw210x_op_rw(dev, 0xbc, 0x0030, 0, &reset16[0], 2,
+ DW210X_READ_MSG);
+ dw210x_op_rw(dev, 0xba, 0x0000, 0, &reset16[0], 7,
+ DW210X_READ_MSG);
+ dw210x_op_rw(dev, 0xba, 0x0000, 0, &reset16[0], 7,
+ DW210X_READ_MSG);
+ dw210x_op_rw(dev, 0xb9, 0x0000, 0, &reset16[0], 2,
+ DW210X_READ_MSG);
break;
}
+ msleep(100);
kfree(p);
}
return ret;
@@ -345,12 +568,12 @@ static struct dvb_usb_device_properties dw2102_properties = {
.caps = DVB_USB_IS_AN_I2C_ADAPTER,
.usb_ctrl = DEVICE_SPECIFIC,
.firmware = "dvb-usb-dw2102.fw",
- .size_of_priv = sizeof(struct dw2102_state),
+ .size_of_priv = sizeof(struct dw210x_state),
.no_reconnect = 1,
- .i2c_algo = &dw2102_i2c_algo,
- .rc_key_map = dw2102_rc_keys,
- .rc_key_map_size = ARRAY_SIZE(dw2102_rc_keys),
+ .i2c_algo = &dw2102_serit_i2c_algo,
+ .rc_key_map = dw210x_rc_keys,
+ .rc_key_map_size = ARRAY_SIZE(dw210x_rc_keys),
.rc_interval = 150,
.rc_query = dw2102_rc_query,
@@ -358,7 +581,8 @@ static struct dvb_usb_device_properties dw2102_properties = {
/* parameter for the MPEG2-data transfer */
.num_adapters = 1,
.download_firmware = dw2102_load_firmware,
- .adapter = {
+ .read_mac_address = dw210x_read_mac_address,
+ .adapter = {
{
.frontend_attach = dw2102_frontend_attach,
.streaming_ctrl = NULL,
@@ -388,11 +612,64 @@ static struct dvb_usb_device_properties dw2102_properties = {
}
};
+static struct dvb_usb_device_properties dw2104_properties = {
+ .caps = DVB_USB_IS_AN_I2C_ADAPTER,
+ .usb_ctrl = DEVICE_SPECIFIC,
+ .firmware = "dvb-usb-dw2104.fw",
+ .size_of_priv = sizeof(struct dw210x_state),
+ .no_reconnect = 1,
+
+ .i2c_algo = &dw2104_i2c_algo,
+ .rc_key_map = dw210x_rc_keys,
+ .rc_key_map_size = ARRAY_SIZE(dw210x_rc_keys),
+ .rc_interval = 150,
+ .rc_query = dw2102_rc_query,
+
+ .generic_bulk_ctrl_endpoint = 0x81,
+ /* parameter for the MPEG2-data transfer */
+ .num_adapters = 1,
+ .download_firmware = dw2102_load_firmware,
+ .read_mac_address = dw210x_read_mac_address,
+ .adapter = {
+ {
+ .frontend_attach = dw2104_frontend_attach,
+ .streaming_ctrl = NULL,
+ /*.tuner_attach = dw2104_tuner_attach,*/
+ .stream = {
+ .type = USB_BULK,
+ .count = 8,
+ .endpoint = 0x82,
+ .u = {
+ .bulk = {
+ .buffersize = 4096,
+ }
+ }
+ },
+ }
+ },
+ .num_device_descs = 2,
+ .devices = {
+ { "DVBWorld DW2104 USB2.0",
+ {&dw2102_table[2], NULL},
+ {NULL},
+ },
+ { "TeVii S650 USB2.0",
+ {&dw2102_table[3], NULL},
+ {NULL},
+ },
+ }
+};
+
static int dw2102_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
- return dvb_usb_device_init(intf, &dw2102_properties,
- THIS_MODULE, NULL, adapter_nr);
+ if (0 == dvb_usb_device_init(intf, &dw2102_properties,
+ THIS_MODULE, NULL, adapter_nr) ||
+ 0 == dvb_usb_device_init(intf, &dw2104_properties,
+ THIS_MODULE, NULL, adapter_nr)) {
+ return 0;
+ }
+ return -ENODEV;
}
static struct usb_driver dw2102_driver = {
@@ -420,6 +697,6 @@ module_init(dw2102_module_init);
module_exit(dw2102_module_exit);
MODULE_AUTHOR("Igor M. Liplianin (c) liplianin@me.by");
-MODULE_DESCRIPTION("Driver for DVBWorld DVB-S 2101 2102 USB2.0 device");
+MODULE_DESCRIPTION("Driver for DVBWorld DVB-S 2101, 2102, DVB-S2 2104 USB2.0 device");
MODULE_VERSION("0.1");
MODULE_LICENSE("GPL");
diff --git a/linux/drivers/media/dvb/dvb-usb/dw2102.h b/linux/drivers/media/dvb/dvb-usb/dw2102.h
index 7a310f906..e3370734e 100644
--- a/linux/drivers/media/dvb/dvb-usb/dw2102.h
+++ b/linux/drivers/media/dvb/dvb-usb/dw2102.h
@@ -4,6 +4,5 @@
#define DVB_USB_LOG_PREFIX "dw2102"
#include "dvb-usb.h"
-extern int dvb_usb_dw2102_debug;
#define deb_xfer(args...) dprintk(dvb_usb_dw2102_debug, 0x02, args)
#endif
diff --git a/linux/drivers/media/dvb/frontends/Kconfig b/linux/drivers/media/dvb/frontends/Kconfig
index e2444f270..96b93e21a 100644
--- a/linux/drivers/media/dvb/frontends/Kconfig
+++ b/linux/drivers/media/dvb/frontends/Kconfig
@@ -43,6 +43,20 @@ config DVB_S5H1420
help
A DVB-S tuner module. Say Y when you want to support this frontend.
+config DVB_STV0288
+ tristate "ST STV0288 based"
+ depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ A DVB-S tuner module. Say Y when you want to support this frontend.
+
+config DVB_STB6000
+ tristate "ST STB6000 silicon tuner"
+ depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ A DVB-S silicon tuner module. Say Y when you want to support this tuner.
+
config DVB_STV0299
tristate "ST STV0299 based"
depends on DVB_CORE && I2C
@@ -92,6 +106,20 @@ config DVB_TUA6100
help
A DVB-S PLL chip.
+config DVB_CX24116
+ tristate "Conexant CX24116 based"
+ depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ A DVB-S/S2 tuner module. Say Y when you want to support this frontend.
+
+config DVB_SI21XX
+ tristate "Silicon Labs SI21XX based"
+ depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ A DVB-S tuner module. Say Y when you want to support this frontend.
+
comment "DVB-T (terrestrial) frontends"
depends on DVB_CORE
@@ -398,4 +426,10 @@ config DVB_DUMMY_FE
tristate "Dummy frontend driver"
default n
+config DVB_AF9013
+ tristate "Afatech AF9013 demodulator"
+ depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ Say Y when you want to support this frontend.
endmenu
diff --git a/linux/drivers/media/dvb/frontends/Makefile b/linux/drivers/media/dvb/frontends/Makefile
index ca24618d5..aba79f4a6 100644
--- a/linux/drivers/media/dvb/frontends/Makefile
+++ b/linux/drivers/media/dvb/frontends/Makefile
@@ -50,3 +50,8 @@ obj-$(CONFIG_DVB_TDA10048) += tda10048.o
obj-$(CONFIG_DVB_S5H1411) += s5h1411.o
obj-$(CONFIG_DVB_LGS8GL5) += lgs8gl5.o
obj-$(CONFIG_DVB_DUMMY_FE) += dvb_dummy_fe.o
+obj-$(CONFIG_DVB_AF9013) += af9013.o
+obj-$(CONFIG_DVB_CX24116) += cx24116.o
+obj-$(CONFIG_DVB_SI21XX) += si21xx.o
+obj-$(CONFIG_DVB_STV0288) += stv0288.o
+obj-$(CONFIG_DVB_STB6000) += stb6000.o
diff --git a/linux/drivers/media/dvb/frontends/af9013.c b/linux/drivers/media/dvb/frontends/af9013.c
new file mode 100644
index 000000000..739d885b3
--- /dev/null
+++ b/linux/drivers/media/dvb/frontends/af9013.c
@@ -0,0 +1,1687 @@
+/*
+ * DVB USB Linux driver for Afatech AF9015 DVB-T USB2.0 receiver
+ *
+ * Copyright (C) 2007 Antti Palosaari <crope@iki.fi>
+ *
+ * Thanks to Afatech who kindly provided information.
+ *
+ * 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
+ * (at your option) 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/firmware.h>
+
+#include "dvb_frontend.h"
+#include "af9013_priv.h"
+#include "af9013.h"
+
+int debug;
+
+struct af9013_state {
+ struct i2c_adapter *i2c;
+ struct dvb_frontend frontend;
+
+ struct af9013_config config;
+
+ u16 signal_strength;
+ u32 ber;
+ u32 ucblocks;
+ u16 snr;
+ u32 frequency;
+ unsigned long next_statistics_check;
+};
+
+static u8 regmask[8] = { 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff };
+
+static int af9013_write_regs(struct af9013_state *state, u8 mbox, u16 reg,
+ u8 *val, u8 len)
+{
+ u8 buf[3+len];
+ struct i2c_msg msg = {
+ .addr = state->config.demod_address,
+ .flags = 0,
+ .len = sizeof(buf),
+ .buf = buf };
+
+ buf[0] = reg >> 8;
+ buf[1] = reg & 0xff;
+ buf[2] = mbox;
+ memcpy(&buf[3], val, len);
+
+ if (i2c_transfer(state->i2c, &msg, 1) != 1) {
+ warn("I2C write failed reg:%04x len:%d", reg, len);
+ return -EREMOTEIO;
+ }
+ return 0;
+}
+
+static int af9013_write_ofdm_regs(struct af9013_state *state, u16 reg, u8 *val,
+ u8 len)
+{
+ u8 mbox = (1 << 0)|(1 << 1)|((len - 1) << 2)|(0 << 6)|(0 << 7);
+ return af9013_write_regs(state, mbox, reg, val, len);
+}
+
+static int af9013_write_ofsm_regs(struct af9013_state *state, u16 reg, u8 *val,
+ u8 len)
+{
+ u8 mbox = (1 << 0)|(1 << 1)|((len - 1) << 2)|(1 << 6)|(1 << 7);
+ return af9013_write_regs(state, mbox, reg, val, len);
+}
+
+/* write single register */
+static int af9013_write_reg(struct af9013_state *state, u16 reg, u8 val)
+{
+ return af9013_write_ofdm_regs(state, reg, &val, 1);
+}
+
+/* read single register */
+static int af9013_read_reg(struct af9013_state *state, u16 reg, u8 *val)
+{
+ u8 obuf[3] = { reg >> 8, reg & 0xff, 0 };
+ u8 ibuf[1];
+ struct i2c_msg msg[2] = {
+ {
+ .addr = state->config.demod_address,
+ .flags = 0,
+ .len = sizeof(obuf),
+ .buf = obuf
+ }, {
+ .addr = state->config.demod_address,
+ .flags = I2C_M_RD,
+ .len = sizeof(ibuf),
+ .buf = ibuf
+ }
+ };
+
+ if (i2c_transfer(state->i2c, msg, 2) != 2) {
+ warn("I2C read failed reg:%04x", reg);
+ return -EREMOTEIO;
+ }
+ *val = ibuf[0];
+ return 0;
+}
+
+static int af9013_write_reg_bits(struct af9013_state *state, u16 reg, u8 pos,
+ u8 len, u8 val)
+{
+ int ret;
+ u8 tmp, mask;
+
+ ret = af9013_read_reg(state, reg, &tmp);
+ if (ret)
+ return ret;
+
+ mask = regmask[len - 1] << pos;
+ tmp = (tmp & ~mask) | ((val << pos) & mask);
+
+ return af9013_write_reg(state, reg, tmp);
+}
+
+static int af9013_read_reg_bits(struct af9013_state *state, u16 reg, u8 pos,
+ u8 len, u8 *val)
+{
+ int ret;
+ u8 tmp;
+
+ ret = af9013_read_reg(state, reg, &tmp);
+ if (ret)
+ return ret;
+ *val = (tmp >> pos) & regmask[len - 1];
+ return 0;
+}
+
+static int af9013_set_gpio(struct af9013_state *state, u8 gpio, u8 gpioval)
+{
+ int ret;
+ u8 pos;
+ u16 addr;
+ deb_info("%s: gpio:%d gpioval:%02x\n", __func__, gpio, gpioval);
+
+/* GPIO0 & GPIO1 0xd735
+ GPIO2 & GPIO3 0xd736 */
+
+ switch (gpio) {
+ case 0:
+ case 1:
+ addr = 0xd735;
+ break;
+ case 2:
+ case 3:
+ addr = 0xd736;
+ break;
+
+ default:
+ err("invalid gpio:%d\n", gpio);
+ ret = -EINVAL;
+ goto error;
+ };
+
+ switch (gpio) {
+ case 0:
+ case 2:
+ pos = 0;
+ break;
+ case 1:
+ case 3:
+ default:
+ pos = 4;
+ break;
+ };
+
+ ret = af9013_write_reg_bits(state, addr, pos, 4, gpioval);
+
+error:
+ return ret;
+}
+
+static u32 af913_div(u32 a, u32 b, u32 x)
+{
+ u32 r = 0, c = 0, i;
+ deb_info("%s: a:%d b:%d x:%d\n", __func__, a, b, x);
+
+ if (a > b) {
+ c = a / b;
+ a = a - c * b;
+ }
+
+ for (i = 0; i < x; i++) {
+ if (a >= b) {
+ r += 1;
+ a -= b;
+ }
+ a <<= 1;
+ r <<= 1;
+ }
+ r = (c << (u32)x) + r;
+
+ deb_info("%s: a:%d b:%d x:%d r:%d r:%x\n", __func__, a, b, x, r, r);
+ return r;
+}
+
+static int af9013_set_coeff(struct af9013_state *state, fe_bandwidth_t bw)
+{
+ int ret = 0;
+ u8 i = 0;
+ u8 buf[24];
+ u32 ns_coeff1_2048nu;
+ u32 ns_coeff1_8191nu;
+ u32 ns_coeff1_8192nu;
+ u32 ns_coeff1_8193nu;
+ u32 ns_coeff2_2k;
+ u32 ns_coeff2_8k;
+
+ deb_info("%s: adc_clock:%d bw:%d\n", __func__,
+ state->config.adc_clock, bw);
+
+ switch (state->config.adc_clock) {
+ case 28800: /* 28.800 MHz */
+ switch (bw) {
+ case BANDWIDTH_6_MHZ:
+ ns_coeff1_2048nu = 0x01e79e7a;
+ ns_coeff1_8191nu = 0x0079eb6e;
+ ns_coeff1_8192nu = 0x0079e79e;
+ ns_coeff1_8193nu = 0x0079e3cf;
+ ns_coeff2_2k = 0x00f3cf3d;
+ ns_coeff2_8k = 0x003cf3cf;
+ break;
+ case BANDWIDTH_7_MHZ:
+ ns_coeff1_2048nu = 0x0238e38e;
+ ns_coeff1_8191nu = 0x008e3d55;
+ ns_coeff1_8192nu = 0x008e38e4;
+ ns_coeff1_8193nu = 0x008e3472;
+ ns_coeff2_2k = 0x011c71c7;
+ ns_coeff2_8k = 0x00471c72;
+ break;
+ case BANDWIDTH_8_MHZ:
+ ns_coeff1_2048nu = 0x028a28a3;
+ ns_coeff1_8191nu = 0x00a28f3d;
+ ns_coeff1_8192nu = 0x00a28a29;
+ ns_coeff1_8193nu = 0x00a28514;
+ ns_coeff2_2k = 0x01451451;
+ ns_coeff2_8k = 0x00514514;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ break;
+ case 20480: /* 20.480 MHz */
+ switch (bw) {
+ case BANDWIDTH_6_MHZ:
+ ns_coeff1_2048nu = 0x02adb6dc;
+ ns_coeff1_8191nu = 0x00ab7313;
+ ns_coeff1_8192nu = 0x00ab6db7;
+ ns_coeff1_8193nu = 0x00ab685c;
+ ns_coeff2_2k = 0x0156db6e;
+ ns_coeff2_8k = 0x0055b6dc;
+ break;
+ case BANDWIDTH_7_MHZ:
+ ns_coeff1_2048nu = 0x03200001;
+ ns_coeff1_8191nu = 0x00c80640;
+ ns_coeff1_8192nu = 0x00c80000;
+ ns_coeff1_8193nu = 0x00c7f9c0;
+ ns_coeff2_2k = 0x01900000;
+ ns_coeff2_8k = 0x00640000;
+ break;
+ case BANDWIDTH_8_MHZ:
+ ns_coeff1_2048nu = 0x03924926;
+ ns_coeff1_8191nu = 0x00e4996e;
+ ns_coeff1_8192nu = 0x00e49249;
+ ns_coeff1_8193nu = 0x00e48b25;
+ ns_coeff2_2k = 0x01c92493;
+ ns_coeff2_8k = 0x00724925;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ break;
+ case 28000: /* 28.000 MHz */
+ switch (bw) {
+ case BANDWIDTH_6_MHZ:
+ ns_coeff1_2048nu = 0x01f58d10;
+ ns_coeff1_8191nu = 0x007d672f;
+ ns_coeff1_8192nu = 0x007d6344;
+ ns_coeff1_8193nu = 0x007d5f59;
+ ns_coeff2_2k = 0x00fac688;
+ ns_coeff2_8k = 0x003eb1a2;
+ break;
+ case BANDWIDTH_7_MHZ:
+ ns_coeff1_2048nu = 0x02492492;
+ ns_coeff1_8191nu = 0x00924db7;
+ ns_coeff1_8192nu = 0x00924925;
+ ns_coeff1_8193nu = 0x00924492;
+ ns_coeff2_2k = 0x01249249;
+ ns_coeff2_8k = 0x00492492;
+ break;
+ case BANDWIDTH_8_MHZ:
+ ns_coeff1_2048nu = 0x029cbc15;
+ ns_coeff1_8191nu = 0x00a7343f;
+ ns_coeff1_8192nu = 0x00a72f05;
+ ns_coeff1_8193nu = 0x00a729cc;
+ ns_coeff2_2k = 0x014e5e0a;
+ ns_coeff2_8k = 0x00539783;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ break;
+ case 25000: /* 25.000 MHz */
+ switch (bw) {
+ case BANDWIDTH_6_MHZ:
+ ns_coeff1_2048nu = 0x0231bcb5;
+ ns_coeff1_8191nu = 0x008c7391;
+ ns_coeff1_8192nu = 0x008c6f2d;
+ ns_coeff1_8193nu = 0x008c6aca;
+ ns_coeff2_2k = 0x0118de5b;
+ ns_coeff2_8k = 0x00463797;
+ break;
+ case BANDWIDTH_7_MHZ:
+ ns_coeff1_2048nu = 0x028f5c29;
+ ns_coeff1_8191nu = 0x00a3dc29;
+ ns_coeff1_8192nu = 0x00a3d70a;
+ ns_coeff1_8193nu = 0x00a3d1ec;
+ ns_coeff2_2k = 0x0147ae14;
+ ns_coeff2_8k = 0x0051eb85;
+ break;
+ case BANDWIDTH_8_MHZ:
+ ns_coeff1_2048nu = 0x02ecfb9d;
+ ns_coeff1_8191nu = 0x00bb44c1;
+ ns_coeff1_8192nu = 0x00bb3ee7;
+ ns_coeff1_8193nu = 0x00bb390d;
+ ns_coeff2_2k = 0x01767dce;
+ ns_coeff2_8k = 0x005d9f74;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ break;
+ default:
+ err("invalid xtal");
+ return -EINVAL;
+ }
+ if (ret) {
+ err("invalid bandwidth");
+ return ret;
+ }
+
+ buf[i++] = (u8) ((ns_coeff1_2048nu & 0x03000000) >> 24);
+ buf[i++] = (u8) ((ns_coeff1_2048nu & 0x00ff0000) >> 16);
+ buf[i++] = (u8) ((ns_coeff1_2048nu & 0x0000ff00) >> 8);
+ buf[i++] = (u8) ((ns_coeff1_2048nu & 0x000000ff));
+ buf[i++] = (u8) ((ns_coeff2_2k & 0x01c00000) >> 22);
+ buf[i++] = (u8) ((ns_coeff2_2k & 0x003fc000) >> 14);
+ buf[i++] = (u8) ((ns_coeff2_2k & 0x00003fc0) >> 6);
+ buf[i++] = (u8) ((ns_coeff2_2k & 0x0000003f));
+ buf[i++] = (u8) ((ns_coeff1_8191nu & 0x03000000) >> 24);
+ buf[i++] = (u8) ((ns_coeff1_8191nu & 0x00ffc000) >> 16);
+ buf[i++] = (u8) ((ns_coeff1_8191nu & 0x0000ff00) >> 8);
+ buf[i++] = (u8) ((ns_coeff1_8191nu & 0x000000ff));
+ buf[i++] = (u8) ((ns_coeff1_8192nu & 0x03000000) >> 24);
+ buf[i++] = (u8) ((ns_coeff1_8192nu & 0x00ffc000) >> 16);
+ buf[i++] = (u8) ((ns_coeff1_8192nu & 0x0000ff00) >> 8);
+ buf[i++] = (u8) ((ns_coeff1_8192nu & 0x000000ff));
+ buf[i++] = (u8) ((ns_coeff1_8193nu & 0x03000000) >> 24);
+ buf[i++] = (u8) ((ns_coeff1_8193nu & 0x00ffc000) >> 16);
+ buf[i++] = (u8) ((ns_coeff1_8193nu & 0x0000ff00) >> 8);
+ buf[i++] = (u8) ((ns_coeff1_8193nu & 0x000000ff));
+ buf[i++] = (u8) ((ns_coeff2_8k & 0x01c00000) >> 22);
+ buf[i++] = (u8) ((ns_coeff2_8k & 0x003fc000) >> 14);
+ buf[i++] = (u8) ((ns_coeff2_8k & 0x00003fc0) >> 6);
+ buf[i++] = (u8) ((ns_coeff2_8k & 0x0000003f));
+
+ deb_info("%s: coeff:", __func__);
+ debug_dump(buf, sizeof(buf), deb_info);
+
+ /* program */
+ for (i = 0; i < sizeof(buf); i++) {
+ ret = af9013_write_reg(state, 0xae00 + i, buf[i]);
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
+
+static int af9013_set_adc_ctrl(struct af9013_state *state)
+{
+ int ret;
+ u8 buf[3], tmp, i;
+ u32 adc_cw;
+
+ deb_info("%s: adc_clock:%d\n", __func__, state->config.adc_clock);
+
+ /* adc frequency type */
+ switch (state->config.adc_clock) {
+ case 28800: /* 28.800 MHz */
+ tmp = 0;
+ break;
+ case 20480: /* 20.480 MHz */
+ tmp = 1;
+ break;
+ case 28000: /* 28.000 MHz */
+ tmp = 2;
+ break;
+ case 25000: /* 25.000 MHz */
+ tmp = 3;
+ break;
+ default:
+ err("invalid xtal");
+ return -EINVAL;
+ }
+
+ adc_cw = af913_div(state->config.adc_clock*1000, 1000000ul, 19ul);
+
+ buf[0] = (u8) ((adc_cw & 0x000000ff));
+ buf[1] = (u8) ((adc_cw & 0x0000ff00) >> 8);
+ buf[2] = (u8) ((adc_cw & 0x00ff0000) >> 16);
+
+ deb_info("%s: adc_cw:", __func__);
+ debug_dump(buf, sizeof(buf), deb_info);
+
+ /* program */
+ for (i = 0; i < sizeof(buf); i++) {
+ ret = af9013_write_reg(state, 0xd180 + i, buf[i]);
+ if (ret)
+ goto error;
+ }
+ ret = af9013_write_reg_bits(state, 0x9bd2, 0, 4, tmp);
+error:
+ return ret;
+}
+
+static int af9013_set_freq_ctrl(struct af9013_state *state, fe_bandwidth_t bw)
+{
+ int ret;
+ u16 addr;
+ u8 buf[3], i, j;
+ u32 adc_freq, freq_cw;
+ s8 bfs_spec_inv;
+ int if_sample_freq;
+
+ for (j = 0; j < 3; j++) {
+ if (j == 0) {
+ addr = 0xd140; /* fcw normal */
+ bfs_spec_inv = state->config.rf_spec_inv ? -1 : 1;
+ } else if (j == 1) {
+ addr = 0x9be7; /* fcw dummy ram */
+ bfs_spec_inv = state->config.rf_spec_inv ? -1 : 1;
+ } else {
+ addr = 0x9bea; /* fcw inverted */
+ bfs_spec_inv = state->config.rf_spec_inv ? 1 : -1;
+ }
+
+ adc_freq = state->config.adc_clock * 1000;
+ if_sample_freq = state->config.tuner_if * 1000;
+
+ /* TDA18271 uses different sampling freq for every bw */
+ if (state->config.tuner == AF9013_TUNER_TDA18271) {
+ switch (bw) {
+ case BANDWIDTH_6_MHZ:
+ if_sample_freq = 3300000; /* 3.3 MHz */
+ break;
+ case BANDWIDTH_7_MHZ:
+ if_sample_freq = 3800000; /* 3.8 MHz */
+ break;
+ case BANDWIDTH_8_MHZ:
+ default:
+ if_sample_freq = 4300000; /* 4.3 MHz */
+ break;
+ }
+ }
+
+ while (if_sample_freq > (adc_freq / 2))
+ if_sample_freq = if_sample_freq - adc_freq;
+
+ if (if_sample_freq >= 0)
+ bfs_spec_inv = bfs_spec_inv * (-1);
+ else
+ if_sample_freq = if_sample_freq * (-1);
+
+ freq_cw = af913_div(if_sample_freq, adc_freq, 23ul);
+
+ if (bfs_spec_inv == -1)
+ freq_cw = 0x00800000 - freq_cw;
+
+ buf[0] = (u8) ((freq_cw & 0x000000ff));
+ buf[1] = (u8) ((freq_cw & 0x0000ff00) >> 8);
+ buf[2] = (u8) ((freq_cw & 0x007f0000) >> 16);
+
+
+ deb_info("%s: freq_cw:", __func__);
+ debug_dump(buf, sizeof(buf), deb_info);
+
+ /* program */
+ for (i = 0; i < sizeof(buf); i++) {
+ ret = af9013_write_reg(state, addr++, buf[i]);
+ if (ret)
+ goto error;
+ }
+ }
+error:
+ return ret;
+}
+
+static int af9013_set_ofdm_params(struct af9013_state *state,
+ struct dvb_ofdm_parameters *params, u8 *auto_mode)
+{
+ int ret;
+ u8 i, buf[3] = {0, 0, 0};
+ *auto_mode = 0; /* set if parameters are requested to auto set */
+
+ switch (params->transmission_mode) {
+ case TRANSMISSION_MODE_AUTO:
+ *auto_mode = 1;
+ case TRANSMISSION_MODE_2K:
+ break;
+ case TRANSMISSION_MODE_8K:
+ buf[0] |= (1 << 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (params->guard_interval) {
+ case GUARD_INTERVAL_AUTO:
+ *auto_mode = 1;
+ case GUARD_INTERVAL_1_32:
+ break;
+ case GUARD_INTERVAL_1_16:
+ buf[0] |= (1 << 2);
+ break;
+ case GUARD_INTERVAL_1_8:
+ buf[0] |= (2 << 2);
+ break;
+ case GUARD_INTERVAL_1_4:
+ buf[0] |= (3 << 2);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (params->hierarchy_information) {
+ case HIERARCHY_AUTO:
+ *auto_mode = 1;
+ case HIERARCHY_NONE:
+ break;
+ case HIERARCHY_1:
+ buf[0] |= (1 << 4);
+ break;
+ case HIERARCHY_2:
+ buf[0] |= (2 << 4);
+ break;
+ case HIERARCHY_4:
+ buf[0] |= (3 << 4);
+ break;
+ default:
+ return -EINVAL;
+ };
+
+ switch (params->constellation) {
+ case QAM_AUTO:
+ *auto_mode = 1;
+ case QPSK:
+ break;
+ case QAM_16:
+ buf[1] |= (1 << 6);
+ break;
+ case QAM_64:
+ buf[1] |= (2 << 6);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Use HP. How and which case we can switch to LP? */
+ buf[1] |= (1 << 4);
+
+ switch (params->code_rate_HP) {
+ case FEC_AUTO:
+ *auto_mode = 1;
+ case FEC_1_2:
+ break;
+ case FEC_2_3:
+ buf[2] |= (1 << 0);
+ break;
+ case FEC_3_4:
+ buf[2] |= (2 << 0);
+ break;
+ case FEC_5_6:
+ buf[2] |= (3 << 0);
+ break;
+ case FEC_7_8:
+ buf[2] |= (4 << 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (params->code_rate_LP) {
+ case FEC_AUTO:
+ /* if HIERARCHY_NONE and FEC_NONE then LP FEC is set to FEC_AUTO
+ by dvb_frontend.c for compatibility */
+ if (params->hierarchy_information != HIERARCHY_NONE)
+ *auto_mode = 1;
+ case FEC_1_2:
+ break;
+ case FEC_2_3:
+ buf[2] |= (1 << 3);
+ break;
+ case FEC_3_4:
+ buf[2] |= (2 << 3);
+ break;
+ case FEC_5_6:
+ buf[2] |= (3 << 3);
+ break;
+ case FEC_7_8:
+ buf[2] |= (4 << 3);
+ break;
+ case FEC_NONE:
+ if (params->hierarchy_information == HIERARCHY_AUTO)
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (params->bandwidth) {
+ case BANDWIDTH_6_MHZ:
+ break;
+ case BANDWIDTH_7_MHZ:
+ buf[1] |= (1 << 2);
+ break;
+ case BANDWIDTH_8_MHZ:
+ buf[1] |= (2 << 2);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* program */
+ for (i = 0; i < sizeof(buf); i++) {
+ ret = af9013_write_reg(state, 0xd3c0 + i, buf[i]);
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
+
+static int af9013_reset(struct af9013_state *state, u8 sleep)
+{
+ int ret;
+ u8 tmp, i;
+ deb_info("%s\n", __func__);
+
+ /* enable OFDM reset */
+ ret = af9013_write_reg_bits(state, 0xd417, 4, 1, 1);
+ if (ret)
+ goto error;
+
+ /* start reset mechanism */
+ ret = af9013_write_reg(state, 0xaeff, 1);
+ if (ret)
+ goto error;
+
+ /* reset is done when bit 1 is set */
+ for (i = 0; i < 150; i++) {
+ ret = af9013_read_reg_bits(state, 0xd417, 1, 1, &tmp);
+ if (ret)
+ goto error;
+ if (tmp)
+ break; /* reset done */
+ msleep(10);
+ }
+ if (!tmp)
+ return -ETIMEDOUT;
+
+ /* don't clear reset when going to sleep */
+ if (!sleep) {
+ /* clear OFDM reset */
+ ret = af9013_write_reg_bits(state, 0xd417, 1, 1, 0);
+ if (ret)
+ goto error;
+
+ /* disable OFDM reset */
+ ret = af9013_write_reg_bits(state, 0xd417, 4, 1, 0);
+ }
+error:
+ return ret;
+}
+
+static int af9013_power_ctrl(struct af9013_state *state, u8 onoff)
+{
+ int ret;
+ deb_info("%s: onoff:%d\n", __func__, onoff);
+
+ if (onoff) {
+ /* power on */
+ ret = af9013_write_reg_bits(state, 0xd73a, 3, 1, 0);
+ if (ret)
+ goto error;
+ ret = af9013_write_reg_bits(state, 0xd417, 1, 1, 0);
+ if (ret)
+ goto error;
+ ret = af9013_write_reg_bits(state, 0xd417, 4, 1, 0);
+ } else {
+ /* power off */
+ ret = af9013_reset(state, 1);
+ if (ret)
+ goto error;
+ ret = af9013_write_reg_bits(state, 0xd73a, 3, 1, 1);
+ }
+error:
+ return ret;
+}
+
+static int af9013_lock_led(struct af9013_state *state, u8 onoff)
+{
+ deb_info("%s: onoff:%d\n", __func__, onoff);
+
+ return af9013_write_reg_bits(state, 0xd730, 0, 1, onoff);
+}
+
+static int af9013_set_frontend(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *params)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ int ret;
+ u8 auto_mode; /* auto set TPS */
+
+ deb_info("%s: freq:%d bw:%d\n", __func__, params->frequency,
+ params->u.ofdm.bandwidth);
+
+ state->frequency = params->frequency;
+
+ /* program CFOE coefficients */
+ ret = af9013_set_coeff(state, params->u.ofdm.bandwidth);
+ if (ret)
+ goto error;
+
+ /* program frequency control */
+ ret = af9013_set_freq_ctrl(state, params->u.ofdm.bandwidth);
+ if (ret)
+ goto error;
+
+ /* clear TPS lock flag (inverted flag) */
+ ret = af9013_write_reg_bits(state, 0xd330, 3, 1, 1);
+ if (ret)
+ goto error;
+
+ /* clear MPEG2 lock flag */
+ ret = af9013_write_reg_bits(state, 0xd507, 6, 1, 0);
+ if (ret)
+ goto error;
+
+ /* empty channel function */
+ ret = af9013_write_reg_bits(state, 0x9bfe, 0, 1, 0);
+ if (ret)
+ goto error;
+
+ /* empty DVB-T channel function */
+ ret = af9013_write_reg_bits(state, 0x9bc2, 0, 1, 0);
+ if (ret)
+ goto error;
+
+ /* program tuner */
+ if (fe->ops.tuner_ops.set_params)
+ fe->ops.tuner_ops.set_params(fe, params);
+
+ /* program TPS and bandwidth, check if auto mode needed */
+ ret = af9013_set_ofdm_params(state, &params->u.ofdm, &auto_mode);
+ if (ret)
+ goto error;
+
+ if (auto_mode) {
+ /* clear easy mode flag */
+ ret = af9013_write_reg(state, 0xaefd, 0);
+ deb_info("%s: auto TPS\n", __func__);
+ } else {
+ /* set easy mode flag */
+ ret = af9013_write_reg(state, 0xaefd, 1);
+ if (ret)
+ goto error;
+ ret = af9013_write_reg(state, 0xaefe, 0);
+ deb_info("%s: manual TPS\n", __func__);
+ }
+ if (ret)
+ goto error;
+
+ /* everything is set, lets try to receive channel - OFSM GO! */
+ ret = af9013_write_reg(state, 0xffff, 0);
+ if (ret)
+ goto error;
+
+error:
+ return ret;
+}
+
+static int af9013_get_frontend(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *p)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ int ret;
+ u8 i, buf[3];
+ deb_info("%s\n", __func__);
+
+ /* read TPS registers */
+ for (i = 0; i < 3; i++) {
+ ret = af9013_read_reg(state, 0xd3c0 + i, &buf[i]);
+ if (ret)
+ goto error;
+ }
+
+ switch ((buf[1] >> 6) & 3) {
+ case 0:
+ p->u.ofdm.constellation = QPSK;
+ break;
+ case 1:
+ p->u.ofdm.constellation = QAM_16;
+ break;
+ case 2:
+ p->u.ofdm.constellation = QAM_64;
+ break;
+ }
+
+ switch ((buf[0] >> 0) & 3) {
+ case 0:
+ p->u.ofdm.transmission_mode = TRANSMISSION_MODE_2K;
+ break;
+ case 1:
+ p->u.ofdm.transmission_mode = TRANSMISSION_MODE_8K;
+ }
+
+ switch ((buf[0] >> 2) & 3) {
+ case 0:
+ p->u.ofdm.guard_interval = GUARD_INTERVAL_1_32;
+ break;
+ case 1:
+ p->u.ofdm.guard_interval = GUARD_INTERVAL_1_16;
+ break;
+ case 2:
+ p->u.ofdm.guard_interval = GUARD_INTERVAL_1_8;
+ break;
+ case 3:
+ p->u.ofdm.guard_interval = GUARD_INTERVAL_1_4;
+ break;
+ }
+
+ switch ((buf[0] >> 4) & 7) {
+ case 0:
+ p->u.ofdm.hierarchy_information = HIERARCHY_NONE;
+ break;
+ case 1:
+ p->u.ofdm.hierarchy_information = HIERARCHY_1;
+ break;
+ case 2:
+ p->u.ofdm.hierarchy_information = HIERARCHY_2;
+ break;
+ case 3:
+ p->u.ofdm.hierarchy_information = HIERARCHY_4;
+ break;
+ }
+
+ switch ((buf[2] >> 0) & 7) {
+ case 0:
+ p->u.ofdm.code_rate_HP = FEC_1_2;
+ break;
+ case 1:
+ p->u.ofdm.code_rate_HP = FEC_2_3;
+ break;
+ case 2:
+ p->u.ofdm.code_rate_HP = FEC_3_4;
+ break;
+ case 3:
+ p->u.ofdm.code_rate_HP = FEC_5_6;
+ break;
+ case 4:
+ p->u.ofdm.code_rate_HP = FEC_7_8;
+ break;
+ }
+
+ switch ((buf[2] >> 3) & 7) {
+ case 0:
+ p->u.ofdm.code_rate_LP = FEC_1_2;
+ break;
+ case 1:
+ p->u.ofdm.code_rate_LP = FEC_2_3;
+ break;
+ case 2:
+ p->u.ofdm.code_rate_LP = FEC_3_4;
+ break;
+ case 3:
+ p->u.ofdm.code_rate_LP = FEC_5_6;
+ break;
+ case 4:
+ p->u.ofdm.code_rate_LP = FEC_7_8;
+ break;
+ }
+
+ switch ((buf[1] >> 2) & 3) {
+ case 0:
+ p->u.ofdm.bandwidth = BANDWIDTH_6_MHZ;
+ break;
+ case 1:
+ p->u.ofdm.bandwidth = BANDWIDTH_7_MHZ;
+ break;
+ case 2:
+ p->u.ofdm.bandwidth = BANDWIDTH_8_MHZ;
+ break;
+ }
+
+ p->inversion = INVERSION_AUTO;
+ p->frequency = state->frequency;
+
+error:
+ return ret;
+}
+
+static int af9013_update_ber_unc(struct dvb_frontend *fe)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ int ret;
+ u8 buf[3], i;
+ u32 error_bit_count = 0;
+ u32 total_bit_count = 0;
+ u32 abort_packet_count = 0;
+
+ state->ber = 0;
+
+ /* check if error bit count is ready */
+ ret = af9013_read_reg_bits(state, 0xd391, 4, 1, &buf[0]);
+ if (ret)
+ goto error;
+ if (!buf[0])
+ goto exit;
+
+ /* get RSD packet abort count */
+ for (i = 0; i < 2; i++) {
+ ret = af9013_read_reg(state, 0xd38a + i, &buf[i]);
+ if (ret)
+ goto error;
+ }
+ abort_packet_count = (buf[1] << 8) + buf[0];
+
+ /* get error bit count */
+ for (i = 0; i < 3; i++) {
+ ret = af9013_read_reg(state, 0xd387 + i, &buf[i]);
+ if (ret)
+ goto error;
+ }
+ error_bit_count = (buf[2] << 16) + (buf[1] << 8) + buf[0];
+ error_bit_count = error_bit_count - abort_packet_count * 8 * 8;
+
+ /* get used RSD counting period (10000 RSD packets used) */
+ for (i = 0; i < 2; i++) {
+ ret = af9013_read_reg(state, 0xd385 + i, &buf[i]);
+ if (ret)
+ goto error;
+ }
+ total_bit_count = (buf[1] << 8) + buf[0];
+ total_bit_count = total_bit_count - abort_packet_count;
+ total_bit_count = total_bit_count * 204 * 8;
+
+ if (total_bit_count)
+ state->ber = error_bit_count * 1000000000 / total_bit_count;
+
+ state->ucblocks += abort_packet_count;
+
+ deb_info("%s: err bits:%d total bits:%d abort count:%d\n", __func__,
+ error_bit_count, total_bit_count, abort_packet_count);
+
+ /* set BER counting range */
+ ret = af9013_write_reg(state, 0xd385, 10000 & 0xff);
+ if (ret)
+ goto error;
+ ret = af9013_write_reg(state, 0xd386, 10000 >> 8);
+ if (ret)
+ goto error;
+ /* reset and start BER counter */
+ ret = af9013_write_reg_bits(state, 0xd391, 4, 1, 1);
+ if (ret)
+ goto error;
+
+exit:
+error:
+ return ret;
+}
+
+static int af9013_update_snr(struct dvb_frontend *fe)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ int ret;
+ u8 buf[3], i, len;
+ u32 quant = 0;
+ struct snr_table *snr_table;
+
+ /* check if quantizer ready (for snr) */
+ ret = af9013_read_reg_bits(state, 0xd2e1, 3, 1, &buf[0]);
+ if (ret)
+ goto error;
+ if (buf[0]) {
+ /* quantizer ready - read it */
+ for (i = 0; i < 3; i++) {
+ ret = af9013_read_reg(state, 0xd2e3 + i, &buf[i]);
+ if (ret)
+ goto error;
+ }
+ quant = (buf[2] << 16) + (buf[1] << 8) + buf[0];
+
+ /* read current constellation */
+ ret = af9013_read_reg(state, 0xd3c1, &buf[0]);
+ if (ret)
+ goto error;
+
+ switch ((buf[0] >> 6) & 3) {
+ case 0:
+ len = ARRAY_SIZE(qpsk_snr_table);
+ snr_table = qpsk_snr_table;
+ break;
+ case 1:
+ len = ARRAY_SIZE(qam16_snr_table);
+ snr_table = qam16_snr_table;
+ break;
+ case 2:
+ len = ARRAY_SIZE(qam64_snr_table);
+ snr_table = qam64_snr_table;
+ break;
+ default:
+ len = 0;
+ break;
+ }
+
+ if (len) {
+ for (i = 0; i < len; i++) {
+ if (quant < snr_table[i].val) {
+ state->snr = snr_table[i].snr * 10;
+ break;
+ }
+ }
+ }
+
+ /* set quantizer super frame count */
+ ret = af9013_write_reg(state, 0xd2e2, 1);
+ if (ret)
+ goto error;
+
+ /* check quantizer availability */
+ for (i = 0; i < 10; i++) {
+ msleep(10);
+ ret = af9013_read_reg_bits(state, 0xd2e6, 0, 1,
+ &buf[0]);
+ if (ret)
+ goto error;
+ if (!buf[0])
+ break;
+ }
+
+ /* reset quantizer */
+ ret = af9013_write_reg_bits(state, 0xd2e1, 3, 1, 1);
+ if (ret)
+ goto error;
+ }
+
+error:
+ return ret;
+}
+
+static int af9013_update_signal_strength(struct dvb_frontend *fe)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ int ret;
+ u8 tmp0;
+ u8 rf_gain, rf_50, rf_80, if_gain, if_50, if_80;
+ int signal_strength;
+
+ deb_info("%s\n", __func__);
+
+ state->signal_strength = 0;
+
+ ret = af9013_read_reg_bits(state, 0x9bee, 0, 1, &tmp0);
+ if (ret)
+ goto error;
+ if (tmp0) {
+ ret = af9013_read_reg(state, 0x9bbd, &rf_50);
+ if (ret)
+ goto error;
+ ret = af9013_read_reg(state, 0x9bd0, &rf_80);
+ if (ret)
+ goto error;
+ ret = af9013_read_reg(state, 0x9be2, &if_50);
+ if (ret)
+ goto error;
+ ret = af9013_read_reg(state, 0x9be4, &if_80);
+ if (ret)
+ goto error;
+ ret = af9013_read_reg(state, 0xd07c, &rf_gain);
+ if (ret)
+ goto error;
+ ret = af9013_read_reg(state, 0xd07d, &if_gain);
+ if (ret)
+ goto error;
+ signal_strength = (0xffff / (9 * (rf_50 + if_50) - \
+ 11 * (rf_80 + if_80))) * (10 * (rf_gain + if_gain) - \
+ 11 * (rf_80 + if_80));
+ if (signal_strength < 0)
+ signal_strength = 0;
+ else if (signal_strength > 0xffff)
+ signal_strength = 0xffff;
+
+ state->signal_strength = signal_strength;
+ }
+
+error:
+ return ret;
+}
+
+static int af9013_update_statistics(struct dvb_frontend *fe)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ int ret;
+
+ if (time_before(jiffies, state->next_statistics_check))
+ return 0;
+
+ /* set minimum statistic update interval */
+ state->next_statistics_check = jiffies + msecs_to_jiffies(1200);
+
+ ret = af9013_update_signal_strength(fe);
+ if (ret)
+ goto error;
+ ret = af9013_update_snr(fe);
+ if (ret)
+ goto error;
+ ret = af9013_update_ber_unc(fe);
+ if (ret)
+ goto error;
+
+error:
+ return ret;
+}
+
+static int af9013_get_tune_settings(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *fesettings)
+{
+ fesettings->min_delay_ms = 800;
+ fesettings->step_size = 0;
+ fesettings->max_drift = 0;
+
+ return 0;
+}
+
+static int af9013_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ int ret = 0;
+ u8 tmp;
+ *status = 0;
+
+ /* TPS lock */
+ ret = af9013_read_reg_bits(state, 0xd330, 3, 1, &tmp);
+ if (ret)
+ goto error;
+ if (tmp)
+ *status |= FE_HAS_VITERBI | FE_HAS_CARRIER | FE_HAS_SIGNAL;
+
+ /* MPEG2 lock */
+ ret = af9013_read_reg_bits(state, 0xd507, 6, 1, &tmp);
+ if (ret)
+ goto error;
+ if (tmp)
+ *status |= FE_HAS_SYNC | FE_HAS_LOCK;
+
+ if (!*status & FE_HAS_SIGNAL) {
+ /* AGC lock */
+ ret = af9013_read_reg_bits(state, 0xd1a0, 6, 1, &tmp);
+ if (ret)
+ goto error;
+ if (tmp)
+ *status |= FE_HAS_SIGNAL;
+ }
+
+ if (!*status & FE_HAS_CARRIER) {
+ /* CFO lock */
+ ret = af9013_read_reg_bits(state, 0xd333, 7, 1, &tmp);
+ if (ret)
+ goto error;
+ if (tmp)
+ *status |= FE_HAS_CARRIER;
+ }
+
+ if (!*status & FE_HAS_CARRIER) {
+ /* SFOE lock */
+ ret = af9013_read_reg_bits(state, 0xd334, 6, 1, &tmp);
+ if (ret)
+ goto error;
+ if (tmp)
+ *status |= FE_HAS_CARRIER;
+ }
+
+ ret = af9013_update_statistics(fe);
+
+error:
+ return ret;
+}
+
+
+static int af9013_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ int ret;
+ ret = af9013_update_statistics(fe);
+ *ber = state->ber;
+ return ret;
+}
+
+static int af9013_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ int ret;
+ ret = af9013_update_statistics(fe);
+ *strength = state->signal_strength;
+ return ret;
+}
+
+static int af9013_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ int ret;
+ ret = af9013_update_statistics(fe);
+ *snr = state->snr;
+ return ret;
+}
+
+static int af9013_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ int ret;
+ ret = af9013_update_statistics(fe);
+ *ucblocks = state->ucblocks;
+ return ret;
+}
+
+static int af9013_sleep(struct dvb_frontend *fe)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ int ret;
+ deb_info("%s\n", __func__);
+
+ ret = af9013_lock_led(state, 0);
+ if (ret)
+ goto error;
+
+ ret = af9013_power_ctrl(state, 0);
+error:
+ return ret;
+}
+
+static int af9013_init(struct dvb_frontend *fe)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ int ret, i, len;
+ u8 tmp0, tmp1;
+ struct regdesc *init;
+ deb_info("%s\n", __func__);
+
+ /* reset OFDM */
+ ret = af9013_reset(state, 0);
+ if (ret)
+ goto error;
+
+ /* power on */
+ ret = af9013_power_ctrl(state, 1);
+ if (ret)
+ goto error;
+
+ /* enable ADC */
+ ret = af9013_write_reg(state, 0xd73a, 0xa4);
+ if (ret)
+ goto error;
+
+ /* write API version to firmware */
+ for (i = 0; i < sizeof(state->config.api_version); i++) {
+ ret = af9013_write_reg(state, 0x9bf2 + i,
+ state->config.api_version[i]);
+ if (ret)
+ goto error;
+ }
+
+ /* program ADC control */
+ ret = af9013_set_adc_ctrl(state);
+ if (ret)
+ goto error;
+
+ /* set I2C master clock */
+ ret = af9013_write_reg(state, 0xd416, 0x14);
+ if (ret)
+ goto error;
+
+ /* set 16 embx */
+ ret = af9013_write_reg_bits(state, 0xd700, 1, 1, 1);
+ if (ret)
+ goto error;
+
+ /* set no trigger */
+ ret = af9013_write_reg_bits(state, 0xd700, 2, 1, 0);
+ if (ret)
+ goto error;
+
+ /* set read-update bit for constellation */
+ ret = af9013_write_reg_bits(state, 0xd371, 1, 1, 1);
+ if (ret)
+ goto error;
+
+ /* enable FEC monitor */
+ ret = af9013_write_reg_bits(state, 0xd392, 1, 1, 1);
+ if (ret)
+ goto error;
+
+ /* load OFSM settings */
+ deb_info("%s: load ofsm settings\n", __func__);
+ len = ARRAY_SIZE(ofsm_init);
+ init = ofsm_init;
+ for (i = 0; i < len; i++) {
+ ret = af9013_write_reg_bits(state, init[i].addr, init[i].pos,
+ init[i].len, init[i].val);
+ if (ret)
+ goto error;
+ }
+
+ /* load tuner specific settings */
+ deb_info("%s: load tuner specific settings\n", __func__);
+ switch (state->config.tuner) {
+ case AF9013_TUNER_MXL5003D:
+ len = ARRAY_SIZE(tuner_init_mxl5003d);
+ init = tuner_init_mxl5003d;
+ break;
+ case AF9013_TUNER_MXL5005D:
+ case AF9013_TUNER_MXL5005R:
+ len = ARRAY_SIZE(tuner_init_mxl5005);
+ init = tuner_init_mxl5005;
+ break;
+ case AF9013_TUNER_ENV77H11D5:
+ len = ARRAY_SIZE(tuner_init_env77h11d5);
+ init = tuner_init_env77h11d5;
+ break;
+ case AF9013_TUNER_MT2060:
+ len = ARRAY_SIZE(tuner_init_mt2060);
+ init = tuner_init_mt2060;
+ break;
+ case AF9013_TUNER_MC44S803:
+ len = ARRAY_SIZE(tuner_init_mc44s803);
+ init = tuner_init_mc44s803;
+ break;
+ case AF9013_TUNER_QT1010:
+ case AF9013_TUNER_QT1010A:
+ len = ARRAY_SIZE(tuner_init_qt1010);
+ init = tuner_init_qt1010;
+ break;
+ case AF9013_TUNER_MT2060_2:
+ len = ARRAY_SIZE(tuner_init_mt2060_2);
+ init = tuner_init_mt2060_2;
+ break;
+ case AF9013_TUNER_TDA18271:
+ len = ARRAY_SIZE(tuner_init_tda18271);
+ init = tuner_init_tda18271;
+ break;
+ case AF9013_TUNER_UNKNOWN:
+ default:
+ len = ARRAY_SIZE(tuner_init_unknown);
+ init = tuner_init_unknown;
+ break;
+ }
+
+ for (i = 0; i < len; i++) {
+ ret = af9013_write_reg_bits(state, init[i].addr, init[i].pos,
+ init[i].len, init[i].val);
+ if (ret)
+ goto error;
+ }
+
+ /* set TS mode */
+ deb_info("%s: setting ts mode\n", __func__);
+ tmp0 = 0; /* parallel mode */
+ tmp1 = 0; /* serial mode */
+ switch (state->config.output_mode) {
+ case AF9013_OUTPUT_MODE_PARALLEL:
+ tmp0 = 1;
+ break;
+ case AF9013_OUTPUT_MODE_SERIAL:
+ tmp1 = 1;
+ break;
+ case AF9013_OUTPUT_MODE_USB:
+ /* usb mode for AF9015 */
+ default:
+ break;
+ }
+ ret = af9013_write_reg_bits(state, 0xd500, 1, 1, tmp0); /* parallel */
+ if (ret)
+ goto error;
+ ret = af9013_write_reg_bits(state, 0xd500, 2, 1, tmp1); /* serial */
+ if (ret)
+ goto error;
+
+ /* enable lock led */
+ ret = af9013_lock_led(state, 1);
+ if (ret)
+ goto error;
+
+error:
+ return ret;
+}
+
+static struct dvb_frontend_ops af9013_ops;
+
+static int af9013_download_firmware(struct af9013_state *state)
+{
+ int i, len, packets, remainder, ret;
+ const struct firmware *fw;
+ u16 addr = 0x5100; /* firmware start address */
+ u16 checksum = 0;
+ u8 val;
+ u8 fw_params[4];
+ u8 *data;
+ u8 *fw_file = AF9013_DEFAULT_FIRMWARE;
+
+ msleep(100);
+ /* check whether firmware is already running */
+ ret = af9013_read_reg(state, 0x98be, &val);
+ if (ret)
+ goto error;
+ else
+ deb_info("%s: firmware status:%02x\n", __func__, val);
+
+ if (val == 0x0c) /* fw is running, no need for download */
+ goto exit;
+
+ info("found a '%s' in cold state, will try to load a firmware",
+ af9013_ops.info.name);
+
+ /* request the firmware, this will block and timeout */
+ ret = request_firmware(&fw, fw_file, &state->i2c->dev);
+ if (ret) {
+ err("did not find the firmware file. (%s) "
+ "Please see linux/Documentation/dvb/ for more details" \
+ " on firmware-problems. (%d)",
+ fw_file, ret);
+ goto error;
+ }
+
+ info("downloading firmware from file '%s'", fw_file);
+
+ /* calc checksum */
+ for (i = 0; i < fw->size; i++)
+ checksum += fw->data[i];
+
+ fw_params[0] = checksum >> 8;
+ fw_params[1] = checksum & 0xff;
+ fw_params[2] = fw->size >> 8;
+ fw_params[3] = fw->size & 0xff;
+
+ /* write fw checksum & size */
+ ret = af9013_write_ofsm_regs(state, 0x50fc,
+ fw_params, sizeof(fw_params));
+ if (ret)
+ goto error_release;
+
+ #define FW_PACKET_MAX_DATA 16
+
+ packets = fw->size / FW_PACKET_MAX_DATA;
+ remainder = fw->size % FW_PACKET_MAX_DATA;
+ len = FW_PACKET_MAX_DATA;
+ for (i = 0; i <= packets; i++) {
+ if (i == packets) /* set size of the last packet */
+ len = remainder;
+
+ data = (fw->data + i * FW_PACKET_MAX_DATA);
+ ret = af9013_write_ofsm_regs(state, addr, data, len);
+ addr += FW_PACKET_MAX_DATA;
+
+ if (ret) {
+ err("firmware download failed at %d with %d", i, ret);
+ goto error_release;
+ }
+ }
+
+ #undef FW_PACKET_MAX_DATA
+
+ /* request boot firmware */
+ ret = af9013_write_reg(state, 0xe205, 1);
+ if (ret)
+ goto error_release;
+
+ for (i = 0; i < 15; i++) {
+ msleep(100);
+
+ /* check firmware status */
+ ret = af9013_read_reg(state, 0x98be, &val);
+ if (ret)
+ goto error_release;
+
+ deb_info("%s: firmware status:%02x\n", __func__, val);
+
+ if (val == 0x0c || val == 0x04) /* success or fail */
+ break;
+ }
+
+ if (val == 0x04) {
+ err("firmware did not run");
+ ret = -1;
+ } else if (val != 0x0c) {
+ err("firmware boot timeout");
+ ret = -1;
+ }
+
+error_release:
+ release_firmware(fw);
+error:
+exit:
+ if (!ret)
+ info("found a '%s' in warm state.", af9013_ops.info.name);
+ return ret;
+}
+
+static int af9013_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+ int ret;
+ struct af9013_state *state = fe->demodulator_priv;
+ deb_info("%s: enable:%d\n", __func__, enable);
+
+ if (state->config.output_mode == AF9013_OUTPUT_MODE_USB)
+ ret = af9013_write_reg_bits(state, 0xd417, 3, 1, enable);
+ else
+ ret = af9013_write_reg_bits(state, 0xd607, 2, 1, enable);
+
+ return ret;
+}
+
+static void af9013_release(struct dvb_frontend *fe)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ kfree(state);
+}
+
+static struct dvb_frontend_ops af9013_ops;
+
+struct dvb_frontend *af9013_attach(const struct af9013_config *config,
+ struct i2c_adapter *i2c)
+{
+ int ret;
+ struct af9013_state *state = NULL;
+ u8 buf[3], i;
+
+ /* allocate memory for the internal state */
+ state = kzalloc(sizeof(struct af9013_state), GFP_KERNEL);
+ if (state == NULL)
+ goto error;
+
+ /* setup the state */
+ state->i2c = i2c;
+ memcpy(&state->config, config, sizeof(struct af9013_config));
+
+ /* chip version */
+ ret = af9013_read_reg_bits(state, 0xd733, 4, 4, &buf[2]);
+ if (ret)
+ goto error;
+
+ /* ROM version */
+ for (i = 0; i < 2; i++) {
+ ret = af9013_read_reg(state, 0x116b + i, &buf[i]);
+ if (ret)
+ goto error;
+ }
+ deb_info("%s: chip version:%d ROM version:%d.%d\n", __func__,
+ buf[2], buf[0], buf[1]);
+
+ /* download firmware */
+ if (state->config.output_mode != AF9013_OUTPUT_MODE_USB) {
+ ret = af9013_download_firmware(state);
+ if (ret)
+ goto error;
+ }
+
+ /* firmware version */
+ for (i = 0; i < 3; i++) {
+ ret = af9013_read_reg(state, 0x5103 + i, &buf[i]);
+ if (ret)
+ goto error;
+ }
+ info("firmware version:%d.%d.%d", buf[0], buf[1], buf[2]);
+
+ /* settings for mp2if */
+ if (state->config.output_mode == AF9013_OUTPUT_MODE_USB) {
+ /* AF9015 split PSB to 1.5k + 0.5k */
+ ret = af9013_write_reg_bits(state, 0xd50b, 2, 1, 1);
+ } else {
+ /* AF9013 change the output bit to data7 */
+ ret = af9013_write_reg_bits(state, 0xd500, 3, 1, 1);
+ if (ret)
+ goto error;
+ /* AF9013 set mpeg to full speed */
+ ret = af9013_write_reg_bits(state, 0xd502, 4, 1, 1);
+ }
+ if (ret)
+ goto error;
+ ret = af9013_write_reg_bits(state, 0xd520, 4, 1, 1);
+ if (ret)
+ goto error;
+
+ /* set GPIOs */
+ for (i = 0; i < sizeof(state->config.gpio); i++) {
+ ret = af9013_set_gpio(state, i, state->config.gpio[i]);
+ if (ret)
+ goto error;
+ }
+
+ /* create dvb_frontend */
+ memcpy(&state->frontend.ops, &af9013_ops,
+ sizeof(struct dvb_frontend_ops));
+ state->frontend.demodulator_priv = state;
+
+ return &state->frontend;
+error:
+ kfree(state);
+ return NULL;
+}
+EXPORT_SYMBOL(af9013_attach);
+
+static struct dvb_frontend_ops af9013_ops = {
+ .info = {
+ .name = "Afatech AF9013 DVB-T",
+ .type = FE_OFDM,
+ .frequency_min = 174000000,
+ .frequency_max = 862000000,
+ .frequency_stepsize = 250000,
+ .frequency_tolerance = 0,
+ .caps =
+ FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+ FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
+ FE_CAN_QPSK | FE_CAN_QAM_16 |
+ FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
+ FE_CAN_TRANSMISSION_MODE_AUTO |
+ FE_CAN_GUARD_INTERVAL_AUTO |
+ FE_CAN_HIERARCHY_AUTO |
+ FE_CAN_RECOVER |
+ FE_CAN_MUTE_TS
+ },
+
+ .release = af9013_release,
+ .init = af9013_init,
+ .sleep = af9013_sleep,
+ .i2c_gate_ctrl = af9013_i2c_gate_ctrl,
+
+ .set_frontend = af9013_set_frontend,
+ .get_frontend = af9013_get_frontend,
+
+ .get_tune_settings = af9013_get_tune_settings,
+
+ .read_status = af9013_read_status,
+ .read_ber = af9013_read_ber,
+ .read_signal_strength = af9013_read_signal_strength,
+ .read_snr = af9013_read_snr,
+ .read_ucblocks = af9013_read_ucblocks,
+};
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_DESCRIPTION("Afatech AF9013 DVB-T demodulator driver");
+MODULE_LICENSE("GPL");
diff --git a/linux/drivers/media/dvb/frontends/af9013.h b/linux/drivers/media/dvb/frontends/af9013.h
new file mode 100644
index 000000000..28b90c91c
--- /dev/null
+++ b/linux/drivers/media/dvb/frontends/af9013.h
@@ -0,0 +1,107 @@
+/*
+ * DVB USB Linux driver for Afatech AF9015 DVB-T USB2.0 receiver
+ *
+ * Copyright (C) 2007 Antti Palosaari <crope@iki.fi>
+ *
+ * Thanks to Afatech who kindly provided information.
+ *
+ * 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
+ * (at your option) 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef _AF9013_H_
+#define _AF9013_H_
+
+#include <linux/dvb/frontend.h>
+
+enum af9013_ts_mode {
+ AF9013_OUTPUT_MODE_PARALLEL,
+ AF9013_OUTPUT_MODE_SERIAL,
+ AF9013_OUTPUT_MODE_USB, /* only for AF9015 */
+};
+
+enum af9013_tuner {
+ AF9013_TUNER_MXL5003D = 3, /* MaxLinear */
+ AF9013_TUNER_MXL5005D = 13, /* MaxLinear */
+ AF9013_TUNER_MXL5005R = 30, /* MaxLinear */
+ AF9013_TUNER_ENV77H11D5 = 129, /* Panasonic */
+ AF9013_TUNER_MT2060 = 130, /* Microtune */
+ AF9013_TUNER_MC44S803 = 133, /* Freescale */
+ AF9013_TUNER_QT1010 = 134, /* Quantek */
+ AF9013_TUNER_UNKNOWN = 140, /* for can tuners ? */
+ AF9013_TUNER_MT2060_2 = 147, /* Microtune */
+ AF9013_TUNER_TDA18271 = 156, /* NXP */
+ AF9013_TUNER_QT1010A = 162, /* Quantek */
+};
+
+/* AF9013/5 GPIOs (mostly guessed)
+ demod#1-gpio#0 - set demod#2 i2c-addr for dual devices
+ demod#1-gpio#1 - xtal setting (?)
+ demod#1-gpio#3 - tuner#1
+ demod#2-gpio#0 - tuner#2
+ demod#2-gpio#1 - xtal setting (?)
+*/
+#define AF9013_GPIO_ON (1 << 0)
+#define AF9013_GPIO_EN (1 << 1)
+#define AF9013_GPIO_O (1 << 2)
+#define AF9013_GPIO_I (1 << 3)
+
+#define AF9013_GPIO_LO (AF9013_GPIO_ON|AF9013_GPIO_EN)
+#define AF9013_GPIO_HI (AF9013_GPIO_ON|AF9013_GPIO_EN|AF9013_GPIO_O)
+
+#define AF9013_GPIO_TUNER_ON (AF9013_GPIO_ON|AF9013_GPIO_EN)
+#define AF9013_GPIO_TUNER_OFF (AF9013_GPIO_ON|AF9013_GPIO_EN|AF9013_GPIO_O)
+
+struct af9013_config {
+ /* demodulator's I2C address */
+ u8 demod_address;
+
+ /* frequencies in kHz */
+ u32 adc_clock;
+
+ /* tuner ID */
+ u8 tuner;
+
+ /* tuner IF */
+ u16 tuner_if;
+
+ /* TS data output mode */
+ u8 output_mode:2;
+
+ /* RF spectrum inversion */
+ u8 rf_spec_inv:1;
+
+ /* API version */
+ u8 api_version[4];
+
+ /* GPIOs */
+ u8 gpio[4];
+};
+
+
+#if defined(CONFIG_DVB_AF9013) || \
+ (defined(CONFIG_DVB_AF9013_MODULE) && defined(MODULE))
+extern struct dvb_frontend *af9013_attach(const struct af9013_config *config,
+ struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *af9013_attach(
+const struct af9013_config *config, struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif /* CONFIG_DVB_AF9013 */
+
+#endif /* _AF9013_H_ */
diff --git a/linux/drivers/media/dvb/frontends/af9013_priv.h b/linux/drivers/media/dvb/frontends/af9013_priv.h
new file mode 100644
index 000000000..b62e4cc9b
--- /dev/null
+++ b/linux/drivers/media/dvb/frontends/af9013_priv.h
@@ -0,0 +1,869 @@
+/*
+ * DVB USB Linux driver for Afatech AF9015 DVB-T USB2.0 receiver
+ *
+ * Copyright (C) 2007 Antti Palosaari <crope@iki.fi>
+ *
+ * Thanks to Afatech who kindly provided information.
+ *
+ * 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
+ * (at your option) 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef _AF9013_PRIV_
+#define _AF9013_PRIV_
+
+#define LOG_PREFIX "af9013"
+extern int debug;
+
+#define dprintk(var, level, args...) \
+ do { if ((var & level)) printk(args); } while (0)
+
+#define debug_dump(b, l, func) {\
+ int loop_; \
+ for (loop_ = 0; loop_ < l; loop_++) \
+ func("%02x ", b[loop_]); \
+ func("\n");\
+}
+
+#define deb_info(args...) dprintk(debug, 0x01, args)
+
+#undef err
+#define err(f, arg...) printk(KERN_ERR LOG_PREFIX": " f "\n" , ## arg)
+#undef info
+#define info(f, arg...) printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg)
+#undef warn
+#define warn(f, arg...) printk(KERN_WARNING LOG_PREFIX": " f "\n" , ## arg)
+
+#define AF9013_DEFAULT_FIRMWARE "dvb-fe-af9013.fw"
+
+struct regdesc {
+ u16 addr;
+ u8 pos:4;
+ u8 len:4;
+ u8 val;
+};
+
+struct snr_table {
+ u32 val;
+ u8 snr;
+};
+
+/* QPSK SNR lookup table */
+static struct snr_table qpsk_snr_table[] = {
+ { 0x0b4771, 0 },
+ { 0x0c1aed, 1 },
+ { 0x0d0d27, 2 },
+ { 0x0e4d19, 3 },
+ { 0x0e5da8, 4 },
+ { 0x107097, 5 },
+ { 0x116975, 6 },
+ { 0x1252d9, 7 },
+ { 0x131fa4, 8 },
+ { 0x13d5e1, 9 },
+ { 0x148e53, 10 },
+ { 0x15358b, 11 },
+ { 0x15dd29, 12 },
+ { 0x168112, 13 },
+ { 0x170b61, 14 },
+ { 0xffffff, 15 },
+};
+
+/* QAM16 SNR lookup table */
+static struct snr_table qam16_snr_table[] = {
+ { 0x05eb62, 5 },
+ { 0x05fecf, 6 },
+ { 0x060b80, 7 },
+ { 0x062501, 8 },
+ { 0x064865, 9 },
+ { 0x069604, 10 },
+ { 0x06f356, 11 },
+ { 0x07706a, 12 },
+ { 0x0804d3, 13 },
+ { 0x089d1a, 14 },
+ { 0x093e3d, 15 },
+ { 0x09e35d, 16 },
+ { 0x0a7c3c, 17 },
+ { 0x0afaf8, 18 },
+ { 0x0b719d, 19 },
+ { 0xffffff, 20 },
+};
+
+/* QAM64 SNR lookup table */
+static struct snr_table qam64_snr_table[] = {
+ { 0x03109b, 12 },
+ { 0x0310d4, 13 },
+ { 0x031920, 14 },
+ { 0x0322d0, 15 },
+ { 0x0339fc, 16 },
+ { 0x0364a1, 17 },
+ { 0x038bcc, 18 },
+ { 0x03c7d3, 19 },
+ { 0x0408cc, 20 },
+ { 0x043bed, 21 },
+ { 0x048061, 22 },
+ { 0x04be95, 23 },
+ { 0x04fa7d, 24 },
+ { 0x052405, 25 },
+ { 0x05570d, 26 },
+ { 0xffffff, 27 },
+};
+
+static struct regdesc ofsm_init[] = {
+ { 0xd73a, 0, 8, 0xa1 },
+ { 0xd73b, 0, 8, 0x1f },
+ { 0xd73c, 4, 4, 0x0a },
+ { 0xd732, 3, 1, 0x00 },
+ { 0xd731, 4, 2, 0x03 },
+ { 0xd73d, 7, 1, 0x01 },
+ { 0xd740, 0, 1, 0x00 },
+ { 0xd740, 1, 1, 0x00 },
+ { 0xd740, 2, 1, 0x00 },
+ { 0xd740, 3, 1, 0x01 },
+ { 0xd3c1, 4, 1, 0x01 },
+ { 0xd3a2, 0, 8, 0x00 },
+ { 0xd3a3, 0, 8, 0x04 },
+ { 0xd305, 0, 8, 0x32 },
+ { 0xd306, 0, 8, 0x10 },
+ { 0xd304, 0, 8, 0x04 },
+ { 0x9112, 0, 1, 0x01 },
+ { 0x911d, 0, 1, 0x01 },
+ { 0x911a, 0, 1, 0x01 },
+ { 0x911b, 0, 1, 0x01 },
+ { 0x9bce, 0, 4, 0x02 },
+ { 0x9116, 0, 1, 0x01 },
+ { 0x9bd1, 0, 1, 0x01 },
+ { 0xd2e0, 0, 8, 0xd0 },
+ { 0xd2e9, 0, 4, 0x0d },
+ { 0xd38c, 0, 8, 0xfc },
+ { 0xd38d, 0, 8, 0x00 },
+ { 0xd38e, 0, 8, 0x7e },
+ { 0xd38f, 0, 8, 0x00 },
+ { 0xd390, 0, 8, 0x2f },
+ { 0xd145, 4, 1, 0x01 },
+ { 0xd1a9, 4, 1, 0x01 },
+ { 0xd158, 5, 3, 0x01 },
+ { 0xd159, 0, 6, 0x06 },
+ { 0xd167, 0, 8, 0x00 },
+ { 0xd168, 0, 4, 0x07 },
+ { 0xd1c3, 5, 3, 0x00 },
+ { 0xd1c4, 0, 6, 0x00 },
+ { 0xd1c5, 0, 7, 0x10 },
+ { 0xd1c6, 0, 3, 0x02 },
+ { 0xd080, 2, 5, 0x03 },
+ { 0xd081, 4, 4, 0x09 },
+ { 0xd098, 4, 4, 0x0f },
+ { 0xd098, 0, 4, 0x03 },
+ { 0xdbc0, 3, 1, 0x01 },
+ { 0xdbc0, 4, 1, 0x01 },
+ { 0xdbc7, 0, 8, 0x08 },
+ { 0xdbc8, 4, 4, 0x00 },
+ { 0xdbc9, 0, 5, 0x01 },
+ { 0xd280, 0, 8, 0xe0 },
+ { 0xd281, 0, 8, 0xff },
+ { 0xd282, 0, 8, 0xff },
+ { 0xd283, 0, 8, 0xc3 },
+ { 0xd284, 0, 8, 0xff },
+ { 0xd285, 0, 4, 0x01 },
+ { 0xd0f0, 0, 7, 0x1a },
+ { 0xd0f1, 4, 1, 0x01 },
+ { 0xd0f2, 0, 8, 0x0c },
+ { 0xd103, 0, 4, 0x08 },
+ { 0xd0f8, 0, 7, 0x20 },
+ { 0xd111, 5, 1, 0x00 },
+ { 0xd111, 6, 1, 0x00 },
+ { 0x910b, 0, 8, 0x0a },
+ { 0x9115, 0, 8, 0x02 },
+ { 0x910c, 0, 8, 0x02 },
+ { 0x910d, 0, 8, 0x08 },
+ { 0x910e, 0, 8, 0x0a },
+ { 0x9bf6, 0, 8, 0x06 },
+ { 0x9bf8, 0, 8, 0x02 },
+ { 0x9bf7, 0, 8, 0x05 },
+ { 0x9bf9, 0, 8, 0x0f },
+ { 0x9bfc, 0, 8, 0x13 },
+ { 0x9bd3, 0, 8, 0xff },
+ { 0x9bbe, 0, 1, 0x01 },
+ { 0x9bcc, 0, 1, 0x01 },
+};
+
+/* Panasonic ENV77H11D5 tuner init
+ AF9013_TUNER_ENV77H11D5 = 129 */
+static struct regdesc tuner_init_env77h11d5[] = {
+ { 0x9bd5, 0, 8, 0x01 },
+ { 0x9bd6, 0, 8, 0x03 },
+ { 0x9bbe, 0, 8, 0x01 },
+ { 0xd1a0, 1, 1, 0x01 },
+ { 0xd000, 0, 1, 0x01 },
+ { 0xd000, 1, 1, 0x00 },
+ { 0xd001, 1, 1, 0x01 },
+ { 0xd001, 0, 1, 0x00 },
+ { 0xd001, 5, 1, 0x00 },
+ { 0xd002, 0, 5, 0x19 },
+ { 0xd003, 0, 5, 0x1a },
+ { 0xd004, 0, 5, 0x19 },
+ { 0xd005, 0, 5, 0x1a },
+ { 0xd00e, 0, 5, 0x10 },
+ { 0xd00f, 0, 3, 0x04 },
+ { 0xd00f, 3, 3, 0x05 },
+ { 0xd010, 0, 3, 0x04 },
+ { 0xd010, 3, 3, 0x05 },
+ { 0xd016, 4, 4, 0x03 },
+ { 0xd01f, 0, 6, 0x0a },
+ { 0xd020, 0, 6, 0x0a },
+ { 0x9bda, 0, 8, 0x00 },
+ { 0x9be3, 0, 8, 0x00 },
+ { 0xd015, 0, 8, 0x50 },
+ { 0xd016, 0, 1, 0x00 },
+ { 0xd044, 0, 8, 0x46 },
+ { 0xd045, 0, 1, 0x00 },
+ { 0xd008, 0, 8, 0xdf },
+ { 0xd009, 0, 2, 0x02 },
+ { 0xd006, 0, 8, 0x44 },
+ { 0xd007, 0, 2, 0x01 },
+ { 0xd00c, 0, 8, 0xeb },
+ { 0xd00d, 0, 2, 0x02 },
+ { 0xd00a, 0, 8, 0xf4 },
+ { 0xd00b, 0, 2, 0x01 },
+ { 0x9bba, 0, 8, 0xf9 },
+ { 0x9bc3, 0, 8, 0xdf },
+ { 0x9bc4, 0, 8, 0x02 },
+ { 0x9bc5, 0, 8, 0xeb },
+ { 0x9bc6, 0, 8, 0x02 },
+ { 0x9bc9, 0, 8, 0x52 },
+ { 0xd011, 0, 8, 0x3c },
+ { 0xd012, 0, 2, 0x01 },
+ { 0xd013, 0, 8, 0xf7 },
+ { 0xd014, 0, 2, 0x02 },
+ { 0xd040, 0, 8, 0x0b },
+ { 0xd041, 0, 2, 0x02 },
+ { 0xd042, 0, 8, 0x4d },
+ { 0xd043, 0, 2, 0x00 },
+ { 0xd045, 1, 1, 0x00 },
+ { 0x9bcf, 0, 1, 0x01 },
+ { 0xd045, 2, 1, 0x01 },
+ { 0xd04f, 0, 8, 0x9a },
+ { 0xd050, 0, 1, 0x01 },
+ { 0xd051, 0, 8, 0x5a },
+ { 0xd052, 0, 1, 0x01 },
+ { 0xd053, 0, 8, 0x50 },
+ { 0xd054, 0, 8, 0x46 },
+ { 0x9bd7, 0, 8, 0x0a },
+ { 0x9bd8, 0, 8, 0x14 },
+ { 0x9bd9, 0, 8, 0x08 },
+};
+
+/* Microtune MT2060 tuner init
+ AF9013_TUNER_MT2060 = 130 */
+static struct regdesc tuner_init_mt2060[] = {
+ { 0x9bd5, 0, 8, 0x01 },
+ { 0x9bd6, 0, 8, 0x07 },
+ { 0xd1a0, 1, 1, 0x01 },
+ { 0xd000, 0, 1, 0x01 },
+ { 0xd000, 1, 1, 0x00 },
+ { 0xd001, 1, 1, 0x01 },
+ { 0xd001, 0, 1, 0x00 },
+ { 0xd001, 5, 1, 0x00 },
+ { 0xd002, 0, 5, 0x19 },
+ { 0xd003, 0, 5, 0x1a },
+ { 0xd004, 0, 5, 0x19 },
+ { 0xd005, 0, 5, 0x1a },
+ { 0xd00e, 0, 5, 0x10 },
+ { 0xd00f, 0, 3, 0x04 },
+ { 0xd00f, 3, 3, 0x05 },
+ { 0xd010, 0, 3, 0x04 },
+ { 0xd010, 3, 3, 0x05 },
+ { 0xd016, 4, 4, 0x03 },
+ { 0xd01f, 0, 6, 0x0a },
+ { 0xd020, 0, 6, 0x0a },
+ { 0x9bda, 0, 8, 0x00 },
+ { 0x9be3, 0, 8, 0x00 },
+ { 0x9bbe, 0, 1, 0x00 },
+ { 0x9bcc, 0, 1, 0x00 },
+ { 0x9bb9, 0, 8, 0x75 },
+ { 0x9bcd, 0, 8, 0x24 },
+ { 0x9bff, 0, 8, 0x30 },
+ { 0xd015, 0, 8, 0x46 },
+ { 0xd016, 0, 1, 0x00 },
+ { 0xd044, 0, 8, 0x46 },
+ { 0xd045, 0, 1, 0x00 },
+ { 0xd008, 0, 8, 0x0f },
+ { 0xd009, 0, 2, 0x02 },
+ { 0xd006, 0, 8, 0x32 },
+ { 0xd007, 0, 2, 0x01 },
+ { 0xd00c, 0, 8, 0x36 },
+ { 0xd00d, 0, 2, 0x03 },
+ { 0xd00a, 0, 8, 0x35 },
+ { 0xd00b, 0, 2, 0x01 },
+ { 0x9bc7, 0, 8, 0x07 },
+ { 0x9bc8, 0, 8, 0x90 },
+ { 0x9bc3, 0, 8, 0x0f },
+ { 0x9bc4, 0, 8, 0x02 },
+ { 0x9bc5, 0, 8, 0x36 },
+ { 0x9bc6, 0, 8, 0x03 },
+ { 0x9bba, 0, 8, 0xc9 },
+ { 0x9bc9, 0, 8, 0x79 },
+ { 0xd011, 0, 8, 0x10 },
+ { 0xd012, 0, 2, 0x01 },
+ { 0xd013, 0, 8, 0x45 },
+ { 0xd014, 0, 2, 0x03 },
+ { 0xd040, 0, 8, 0x98 },
+ { 0xd041, 0, 2, 0x00 },
+ { 0xd042, 0, 8, 0xcf },
+ { 0xd043, 0, 2, 0x03 },
+ { 0xd045, 1, 1, 0x00 },
+ { 0x9bcf, 0, 1, 0x01 },
+ { 0xd045, 2, 1, 0x01 },
+ { 0xd04f, 0, 8, 0x9a },
+ { 0xd050, 0, 1, 0x01 },
+ { 0xd051, 0, 8, 0x5a },
+ { 0xd052, 0, 1, 0x01 },
+ { 0xd053, 0, 8, 0x50 },
+ { 0xd054, 0, 8, 0x46 },
+ { 0x9bd7, 0, 8, 0x0a },
+ { 0x9bd8, 0, 8, 0x14 },
+ { 0x9bd9, 0, 8, 0x08 },
+ { 0x9bd0, 0, 8, 0xcc },
+ { 0x9be4, 0, 8, 0xa0 },
+ { 0x9bbd, 0, 8, 0x8e },
+ { 0x9be2, 0, 8, 0x4d },
+ { 0x9bee, 0, 1, 0x01 },
+};
+
+/* Microtune MT2060 tuner init
+ AF9013_TUNER_MT2060_2 = 147 */
+static struct regdesc tuner_init_mt2060_2[] = {
+ { 0x9bd5, 0, 8, 0x01 },
+ { 0x9bd6, 0, 8, 0x06 },
+ { 0x9bbe, 0, 8, 0x01 },
+ { 0xd1a0, 1, 1, 0x01 },
+ { 0xd000, 0, 1, 0x01 },
+ { 0xd000, 1, 1, 0x00 },
+ { 0xd001, 1, 1, 0x01 },
+ { 0xd001, 0, 1, 0x00 },
+ { 0xd001, 5, 1, 0x00 },
+ { 0xd002, 0, 5, 0x19 },
+ { 0xd003, 0, 5, 0x1a },
+ { 0xd004, 0, 5, 0x19 },
+ { 0xd005, 0, 5, 0x1a },
+ { 0xd00e, 0, 5, 0x10 },
+ { 0xd00f, 0, 3, 0x04 },
+ { 0xd00f, 3, 3, 0x05 },
+ { 0xd010, 0, 3, 0x04 },
+ { 0xd010, 3, 3, 0x05 },
+ { 0xd016, 4, 4, 0x03 },
+ { 0xd01f, 0, 6, 0x0a },
+ { 0xd020, 0, 6, 0x0a },
+ { 0xd015, 0, 8, 0x46 },
+ { 0xd016, 0, 1, 0x00 },
+ { 0xd044, 0, 8, 0x46 },
+ { 0xd045, 0, 1, 0x00 },
+ { 0xd008, 0, 8, 0x0f },
+ { 0xd009, 0, 2, 0x02 },
+ { 0xd006, 0, 8, 0x32 },
+ { 0xd007, 0, 2, 0x01 },
+ { 0xd00c, 0, 8, 0x36 },
+ { 0xd00d, 0, 2, 0x03 },
+ { 0xd00a, 0, 8, 0x35 },
+ { 0xd00b, 0, 2, 0x01 },
+ { 0x9bc7, 0, 8, 0x07 },
+ { 0x9bc8, 0, 8, 0x90 },
+ { 0x9bc3, 0, 8, 0x0f },
+ { 0x9bc4, 0, 8, 0x02 },
+ { 0x9bc5, 0, 8, 0x36 },
+ { 0x9bc6, 0, 8, 0x03 },
+ { 0x9bba, 0, 8, 0xc9 },
+ { 0x9bc9, 0, 8, 0x79 },
+ { 0xd011, 0, 8, 0x10 },
+ { 0xd012, 0, 2, 0x01 },
+ { 0xd013, 0, 8, 0x45 },
+ { 0xd014, 0, 2, 0x03 },
+ { 0xd040, 0, 8, 0x98 },
+ { 0xd041, 0, 2, 0x00 },
+ { 0xd042, 0, 8, 0xcf },
+ { 0xd043, 0, 2, 0x03 },
+ { 0xd045, 1, 1, 0x00 },
+ { 0x9bcf, 0, 8, 0x01 },
+ { 0xd045, 2, 1, 0x01 },
+ { 0xd04f, 0, 8, 0x9a },
+ { 0xd050, 0, 1, 0x01 },
+ { 0xd051, 0, 8, 0x5a },
+ { 0xd052, 0, 1, 0x01 },
+ { 0xd053, 0, 8, 0x96 },
+ { 0xd054, 0, 8, 0x46 },
+ { 0xd045, 7, 1, 0x00 },
+ { 0x9bd7, 0, 8, 0x0a },
+ { 0x9bd8, 0, 8, 0x14 },
+ { 0x9bd9, 0, 8, 0x08 },
+};
+
+/* MaxLinear MXL5003 tuner init
+ AF9013_TUNER_MXL5003D = 3 */
+static struct regdesc tuner_init_mxl5003d[] = {
+ { 0x9bd5, 0, 8, 0x01 },
+ { 0x9bd6, 0, 8, 0x09 },
+ { 0xd1a0, 1, 1, 0x01 },
+ { 0xd000, 0, 1, 0x01 },
+ { 0xd000, 1, 1, 0x00 },
+ { 0xd001, 1, 1, 0x01 },
+ { 0xd001, 0, 1, 0x00 },
+ { 0xd001, 5, 1, 0x00 },
+ { 0xd002, 0, 5, 0x19 },
+ { 0xd003, 0, 5, 0x1a },
+ { 0xd004, 0, 5, 0x19 },
+ { 0xd005, 0, 5, 0x1a },
+ { 0xd00e, 0, 5, 0x10 },
+ { 0xd00f, 0, 3, 0x04 },
+ { 0xd00f, 3, 3, 0x05 },
+ { 0xd010, 0, 3, 0x04 },
+ { 0xd010, 3, 3, 0x05 },
+ { 0xd016, 4, 4, 0x03 },
+ { 0xd01f, 0, 6, 0x0a },
+ { 0xd020, 0, 6, 0x0a },
+ { 0x9bda, 0, 8, 0x00 },
+ { 0x9be3, 0, 8, 0x00 },
+ { 0x9bfc, 0, 8, 0x0f },
+ { 0x9bf6, 0, 8, 0x01 },
+ { 0x9bbe, 0, 1, 0x01 },
+ { 0xd015, 0, 8, 0x33 },
+ { 0xd016, 0, 1, 0x00 },
+ { 0xd044, 0, 8, 0x40 },
+ { 0xd045, 0, 1, 0x00 },
+ { 0xd008, 0, 8, 0x0f },
+ { 0xd009, 0, 2, 0x02 },
+ { 0xd006, 0, 8, 0x6c },
+ { 0xd007, 0, 2, 0x00 },
+ { 0xd00c, 0, 8, 0x3d },
+ { 0xd00d, 0, 2, 0x00 },
+ { 0xd00a, 0, 8, 0x45 },
+ { 0xd00b, 0, 2, 0x01 },
+ { 0x9bc7, 0, 8, 0x07 },
+ { 0x9bc8, 0, 8, 0x52 },
+ { 0x9bc3, 0, 8, 0x0f },
+ { 0x9bc4, 0, 8, 0x02 },
+ { 0x9bc5, 0, 8, 0x3d },
+ { 0x9bc6, 0, 8, 0x00 },
+ { 0x9bba, 0, 8, 0xa2 },
+ { 0x9bc9, 0, 8, 0xa0 },
+ { 0xd011, 0, 8, 0x56 },
+ { 0xd012, 0, 2, 0x00 },
+ { 0xd013, 0, 8, 0x50 },
+ { 0xd014, 0, 2, 0x00 },
+ { 0xd040, 0, 8, 0x56 },
+ { 0xd041, 0, 2, 0x00 },
+ { 0xd042, 0, 8, 0x50 },
+ { 0xd043, 0, 2, 0x00 },
+ { 0xd045, 1, 1, 0x00 },
+ { 0x9bcf, 0, 8, 0x01 },
+ { 0xd045, 2, 1, 0x01 },
+ { 0xd04f, 0, 8, 0x9a },
+ { 0xd050, 0, 1, 0x01 },
+ { 0xd051, 0, 8, 0x5a },
+ { 0xd052, 0, 1, 0x01 },
+ { 0xd053, 0, 8, 0x50 },
+ { 0xd054, 0, 8, 0x46 },
+ { 0x9bd7, 0, 8, 0x0a },
+ { 0x9bd8, 0, 8, 0x14 },
+ { 0x9bd9, 0, 8, 0x08 },
+};
+
+/* MaxLinear MXL5005 tuner init
+ AF9013_TUNER_MXL5005D = 13
+ AF9013_TUNER_MXL5005R = 30 */
+static struct regdesc tuner_init_mxl5005[] = {
+ { 0x9bd5, 0, 8, 0x01 },
+ { 0x9bd6, 0, 8, 0x07 },
+ { 0xd1a0, 1, 1, 0x01 },
+ { 0xd000, 0, 1, 0x01 },
+ { 0xd000, 1, 1, 0x00 },
+ { 0xd001, 1, 1, 0x01 },
+ { 0xd001, 0, 1, 0x00 },
+ { 0xd001, 5, 1, 0x00 },
+ { 0xd002, 0, 5, 0x19 },
+ { 0xd003, 0, 5, 0x1a },
+ { 0xd004, 0, 5, 0x19 },
+ { 0xd005, 0, 5, 0x1a },
+ { 0xd00e, 0, 5, 0x10 },
+ { 0xd00f, 0, 3, 0x04 },
+ { 0xd00f, 3, 3, 0x05 },
+ { 0xd010, 0, 3, 0x04 },
+ { 0xd010, 3, 3, 0x05 },
+ { 0xd016, 4, 4, 0x03 },
+ { 0xd01f, 0, 6, 0x0a },
+ { 0xd020, 0, 6, 0x0a },
+ { 0x9bda, 0, 8, 0x01 },
+ { 0x9be3, 0, 8, 0x01 },
+ { 0x9bbe, 0, 1, 0x01 },
+ { 0x9bcc, 0, 1, 0x01 },
+ { 0x9bb9, 0, 8, 0x00 },
+ { 0x9bcd, 0, 8, 0x28 },
+ { 0x9bff, 0, 8, 0x24 },
+ { 0xd015, 0, 8, 0x40 },
+ { 0xd016, 0, 1, 0x00 },
+ { 0xd044, 0, 8, 0x40 },
+ { 0xd045, 0, 1, 0x00 },
+ { 0xd008, 0, 8, 0x0f },
+ { 0xd009, 0, 2, 0x02 },
+ { 0xd006, 0, 8, 0x73 },
+ { 0xd007, 0, 2, 0x01 },
+ { 0xd00c, 0, 8, 0xfa },
+ { 0xd00d, 0, 2, 0x01 },
+ { 0xd00a, 0, 8, 0xff },
+ { 0xd00b, 0, 2, 0x01 },
+ { 0x9bc7, 0, 8, 0x23 },
+ { 0x9bc8, 0, 8, 0x55 },
+ { 0x9bc3, 0, 8, 0x01 },
+ { 0x9bc4, 0, 8, 0x02 },
+ { 0x9bc5, 0, 8, 0xfa },
+ { 0x9bc6, 0, 8, 0x01 },
+ { 0x9bba, 0, 8, 0xff },
+ { 0x9bc9, 0, 8, 0xff },
+ { 0x9bd3, 0, 8, 0x95 },
+ { 0xd011, 0, 8, 0x70 },
+ { 0xd012, 0, 2, 0x01 },
+ { 0xd013, 0, 8, 0xfb },
+ { 0xd014, 0, 2, 0x01 },
+ { 0xd040, 0, 8, 0x70 },
+ { 0xd041, 0, 2, 0x01 },
+ { 0xd042, 0, 8, 0xfb },
+ { 0xd043, 0, 2, 0x01 },
+ { 0xd045, 1, 1, 0x00 },
+ { 0x9bcf, 0, 1, 0x01 },
+ { 0xd045, 2, 1, 0x01 },
+ { 0xd04f, 0, 8, 0x9a },
+ { 0xd050, 0, 1, 0x01 },
+ { 0xd051, 0, 8, 0x5a },
+ { 0xd052, 0, 1, 0x01 },
+ { 0xd053, 0, 8, 0x50 },
+ { 0xd054, 0, 8, 0x46 },
+ { 0x9bd7, 0, 8, 0x0a },
+ { 0x9bd8, 0, 8, 0x14 },
+ { 0x9bd9, 0, 8, 0x08 },
+ { 0x9bd0, 0, 8, 0x93 },
+ { 0x9be4, 0, 8, 0xfe },
+ { 0x9bbd, 0, 8, 0x63 },
+ { 0x9be2, 0, 8, 0xfe },
+ { 0x9bee, 0, 1, 0x01 },
+};
+
+/* Quantek QT1010 tuner init
+ AF9013_TUNER_QT1010 = 134
+ AF9013_TUNER_QT1010A = 162 */
+static struct regdesc tuner_init_qt1010[] = {
+ { 0x9bd5, 0, 8, 0x01 },
+ { 0x9bd6, 0, 8, 0x09 },
+ { 0xd1a0, 1, 1, 0x01 },
+ { 0xd000, 0, 1, 0x01 },
+ { 0xd000, 1, 1, 0x00 },
+ { 0xd001, 1, 1, 0x01 },
+ { 0xd001, 0, 1, 0x00 },
+ { 0xd001, 5, 1, 0x00 },
+ { 0xd002, 0, 5, 0x19 },
+ { 0xd003, 0, 5, 0x1a },
+ { 0xd004, 0, 5, 0x19 },
+ { 0xd005, 0, 5, 0x1a },
+ { 0xd00e, 0, 5, 0x10 },
+ { 0xd00f, 0, 3, 0x04 },
+ { 0xd00f, 3, 3, 0x05 },
+ { 0xd010, 0, 3, 0x04 },
+ { 0xd010, 3, 3, 0x05 },
+ { 0xd016, 4, 4, 0x03 },
+ { 0xd01f, 0, 6, 0x0a },
+ { 0xd020, 0, 6, 0x0a },
+ { 0x9bda, 0, 8, 0x01 },
+ { 0x9be3, 0, 8, 0x01 },
+ { 0xd015, 0, 8, 0x46 },
+ { 0xd016, 0, 1, 0x00 },
+ { 0xd044, 0, 8, 0x46 },
+ { 0xd045, 0, 1, 0x00 },
+ { 0x9bbe, 0, 1, 0x01 },
+ { 0x9bcc, 0, 1, 0x01 },
+ { 0x9bb9, 0, 8, 0x00 },
+ { 0x9bcd, 0, 8, 0x28 },
+ { 0x9bff, 0, 8, 0x20 },
+ { 0xd008, 0, 8, 0x0f },
+ { 0xd009, 0, 2, 0x02 },
+ { 0xd006, 0, 8, 0x99 },
+ { 0xd007, 0, 2, 0x01 },
+ { 0xd00c, 0, 8, 0x0f },
+ { 0xd00d, 0, 2, 0x02 },
+ { 0xd00a, 0, 8, 0x50 },
+ { 0xd00b, 0, 2, 0x01 },
+ { 0x9bc7, 0, 8, 0x00 },
+ { 0x9bc8, 0, 8, 0x00 },
+ { 0x9bc3, 0, 8, 0x0f },
+ { 0x9bc4, 0, 8, 0x02 },
+ { 0x9bc5, 0, 8, 0x0f },
+ { 0x9bc6, 0, 8, 0x02 },
+ { 0x9bba, 0, 8, 0xc5 },
+ { 0x9bc9, 0, 8, 0xff },
+ { 0xd011, 0, 8, 0x58 },
+ { 0xd012, 0, 2, 0x02 },
+ { 0xd013, 0, 8, 0x89 },
+ { 0xd014, 0, 2, 0x01 },
+ { 0xd040, 0, 8, 0x58 },
+ { 0xd041, 0, 2, 0x02 },
+ { 0xd042, 0, 8, 0x89 },
+ { 0xd043, 0, 2, 0x01 },
+ { 0xd045, 1, 1, 0x00 },
+ { 0x9bcf, 0, 1, 0x01 },
+ { 0xd045, 2, 1, 0x01 },
+ { 0xd04f, 0, 8, 0x9a },
+ { 0xd050, 0, 1, 0x01 },
+ { 0xd051, 0, 8, 0x5a },
+ { 0xd052, 0, 1, 0x01 },
+ { 0xd053, 0, 8, 0x50 },
+ { 0xd054, 0, 8, 0x46 },
+ { 0x9bd7, 0, 8, 0x0a },
+ { 0x9bd8, 0, 8, 0x14 },
+ { 0x9bd9, 0, 8, 0x08 },
+ { 0x9bd0, 0, 8, 0xcd },
+ { 0x9be4, 0, 8, 0xbb },
+ { 0x9bbd, 0, 8, 0x93 },
+ { 0x9be2, 0, 8, 0x80 },
+ { 0x9bee, 0, 1, 0x01 },
+};
+
+/* Freescale MC44S803 tuner init
+ AF9013_TUNER_MC44S803 = 133 */
+static struct regdesc tuner_init_mc44s803[] = {
+ { 0x9bd5, 0, 8, 0x01 },
+ { 0x9bd6, 0, 8, 0x06 },
+ { 0xd1a0, 1, 1, 0x01 },
+ { 0xd000, 0, 1, 0x01 },
+ { 0xd000, 1, 1, 0x00 },
+ { 0xd001, 1, 1, 0x01 },
+ { 0xd001, 0, 1, 0x00 },
+ { 0xd001, 5, 1, 0x00 },
+ { 0xd002, 0, 5, 0x19 },
+ { 0xd003, 0, 5, 0x1a },
+ { 0xd004, 0, 5, 0x19 },
+ { 0xd005, 0, 5, 0x1a },
+ { 0xd00e, 0, 5, 0x10 },
+ { 0xd00f, 0, 3, 0x04 },
+ { 0xd00f, 3, 3, 0x05 },
+ { 0xd010, 0, 3, 0x04 },
+ { 0xd010, 3, 3, 0x05 },
+ { 0xd016, 4, 4, 0x03 },
+ { 0xd01f, 0, 6, 0x0a },
+ { 0xd020, 0, 6, 0x0a },
+ { 0x9bda, 0, 8, 0x00 },
+ { 0x9be3, 0, 8, 0x00 },
+ { 0x9bf6, 0, 8, 0x01 },
+ { 0x9bf8, 0, 8, 0x02 },
+ { 0x9bf9, 0, 8, 0x02 },
+ { 0x9bfc, 0, 8, 0x1f },
+ { 0x9bbe, 0, 1, 0x01 },
+ { 0x9bcc, 0, 1, 0x01 },
+ { 0x9bb9, 0, 8, 0x00 },
+ { 0x9bcd, 0, 8, 0x24 },
+ { 0x9bff, 0, 8, 0x24 },
+ { 0xd015, 0, 8, 0x46 },
+ { 0xd016, 0, 1, 0x00 },
+ { 0xd044, 0, 8, 0x46 },
+ { 0xd045, 0, 1, 0x00 },
+ { 0xd008, 0, 8, 0x01 },
+ { 0xd009, 0, 2, 0x02 },
+ { 0xd006, 0, 8, 0x7b },
+ { 0xd007, 0, 2, 0x00 },
+ { 0xd00c, 0, 8, 0x7c },
+ { 0xd00d, 0, 2, 0x02 },
+ { 0xd00a, 0, 8, 0xfe },
+ { 0xd00b, 0, 2, 0x01 },
+ { 0x9bc7, 0, 8, 0x08 },
+ { 0x9bc8, 0, 8, 0x9a },
+ { 0x9bc3, 0, 8, 0x01 },
+ { 0x9bc4, 0, 8, 0x02 },
+ { 0x9bc5, 0, 8, 0x7c },
+ { 0x9bc6, 0, 8, 0x02 },
+ { 0x9bba, 0, 8, 0xfc },
+ { 0x9bc9, 0, 8, 0xaa },
+ { 0xd011, 0, 8, 0x6b },
+ { 0xd012, 0, 2, 0x00 },
+ { 0xd013, 0, 8, 0x88 },
+ { 0xd014, 0, 2, 0x02 },
+ { 0xd040, 0, 8, 0x6b },
+ { 0xd041, 0, 2, 0x00 },
+ { 0xd042, 0, 8, 0x7c },
+ { 0xd043, 0, 2, 0x02 },
+ { 0xd045, 1, 1, 0x00 },
+ { 0x9bcf, 0, 1, 0x01 },
+ { 0xd045, 2, 1, 0x01 },
+ { 0xd04f, 0, 8, 0x9a },
+ { 0xd050, 0, 1, 0x01 },
+ { 0xd051, 0, 8, 0x5a },
+ { 0xd052, 0, 1, 0x01 },
+ { 0xd053, 0, 8, 0x50 },
+ { 0xd054, 0, 8, 0x46 },
+ { 0x9bd7, 0, 8, 0x0a },
+ { 0x9bd8, 0, 8, 0x14 },
+ { 0x9bd9, 0, 8, 0x08 },
+ { 0x9bd0, 0, 8, 0x9e },
+ { 0x9be4, 0, 8, 0xff },
+ { 0x9bbd, 0, 8, 0x9e },
+ { 0x9be2, 0, 8, 0x25 },
+ { 0x9bee, 0, 1, 0x01 },
+ { 0xd73b, 3, 1, 0x00 },
+};
+
+/* unknown, probably for tin can tuner, tuner init
+ AF9013_TUNER_UNKNOWN = 140 */
+static struct regdesc tuner_init_unknown[] = {
+ { 0x9bd5, 0, 8, 0x01 },
+ { 0x9bd6, 0, 8, 0x02 },
+ { 0xd1a0, 1, 1, 0x01 },
+ { 0xd000, 0, 1, 0x01 },
+ { 0xd000, 1, 1, 0x00 },
+ { 0xd001, 1, 1, 0x01 },
+ { 0xd001, 0, 1, 0x00 },
+ { 0xd001, 5, 1, 0x00 },
+ { 0xd002, 0, 5, 0x19 },
+ { 0xd003, 0, 5, 0x1a },
+ { 0xd004, 0, 5, 0x19 },
+ { 0xd005, 0, 5, 0x1a },
+ { 0xd00e, 0, 5, 0x10 },
+ { 0xd00f, 0, 3, 0x04 },
+ { 0xd00f, 3, 3, 0x05 },
+ { 0xd010, 0, 3, 0x04 },
+ { 0xd010, 3, 3, 0x05 },
+ { 0xd016, 4, 4, 0x03 },
+ { 0xd01f, 0, 6, 0x0a },
+ { 0xd020, 0, 6, 0x0a },
+ { 0x9bda, 0, 8, 0x01 },
+ { 0x9be3, 0, 8, 0x01 },
+ { 0xd1a0, 1, 1, 0x00 },
+ { 0x9bbe, 0, 1, 0x01 },
+ { 0x9bcc, 0, 1, 0x01 },
+ { 0x9bb9, 0, 8, 0x00 },
+ { 0x9bcd, 0, 8, 0x18 },
+ { 0x9bff, 0, 8, 0x2c },
+ { 0xd015, 0, 8, 0x46 },
+ { 0xd016, 0, 1, 0x00 },
+ { 0xd044, 0, 8, 0x46 },
+ { 0xd045, 0, 1, 0x00 },
+ { 0xd008, 0, 8, 0xdf },
+ { 0xd009, 0, 2, 0x02 },
+ { 0xd006, 0, 8, 0x44 },
+ { 0xd007, 0, 2, 0x01 },
+ { 0xd00c, 0, 8, 0x00 },
+ { 0xd00d, 0, 2, 0x02 },
+ { 0xd00a, 0, 8, 0xf6 },
+ { 0xd00b, 0, 2, 0x01 },
+ { 0x9bba, 0, 8, 0xf9 },
+ { 0x9bc8, 0, 8, 0xaa },
+ { 0x9bc3, 0, 8, 0xdf },
+ { 0x9bc4, 0, 8, 0x02 },
+ { 0x9bc5, 0, 8, 0x00 },
+ { 0x9bc6, 0, 8, 0x02 },
+ { 0x9bc9, 0, 8, 0xf0 },
+ { 0xd011, 0, 8, 0x3c },
+ { 0xd012, 0, 2, 0x01 },
+ { 0xd013, 0, 8, 0xf7 },
+ { 0xd014, 0, 2, 0x02 },
+ { 0xd040, 0, 8, 0x0b },
+ { 0xd041, 0, 2, 0x02 },
+ { 0xd042, 0, 8, 0x4d },
+ { 0xd043, 0, 2, 0x00 },
+ { 0xd045, 1, 1, 0x00 },
+ { 0x9bcf, 0, 1, 0x01 },
+ { 0xd045, 2, 1, 0x01 },
+ { 0xd04f, 0, 8, 0x9a },
+ { 0xd050, 0, 1, 0x01 },
+ { 0xd051, 0, 8, 0x5a },
+ { 0xd052, 0, 1, 0x01 },
+ { 0xd053, 0, 8, 0x50 },
+ { 0xd054, 0, 8, 0x46 },
+ { 0x9bd7, 0, 8, 0x0a },
+ { 0x9bd8, 0, 8, 0x14 },
+ { 0x9bd9, 0, 8, 0x08 },
+};
+
+/* NXP TDA18271 tuner init
+ AF9013_TUNER_TDA18271 = 156 */
+static struct regdesc tuner_init_tda18271[] = {
+ { 0x9bd5, 0, 8, 0x01 },
+ { 0x9bd6, 0, 8, 0x04 },
+ { 0xd1a0, 1, 1, 0x01 },
+ { 0xd000, 0, 1, 0x01 },
+ { 0xd000, 1, 1, 0x00 },
+ { 0xd001, 1, 1, 0x01 },
+ { 0xd001, 0, 1, 0x00 },
+ { 0xd001, 5, 1, 0x00 },
+ { 0xd002, 0, 5, 0x19 },
+ { 0xd003, 0, 5, 0x1a },
+ { 0xd004, 0, 5, 0x19 },
+ { 0xd005, 0, 5, 0x1a },
+ { 0xd00e, 0, 5, 0x10 },
+ { 0xd00f, 0, 3, 0x04 },
+ { 0xd00f, 3, 3, 0x05 },
+ { 0xd010, 0, 3, 0x04 },
+ { 0xd010, 3, 3, 0x05 },
+ { 0xd016, 4, 4, 0x03 },
+ { 0xd01f, 0, 6, 0x0a },
+ { 0xd020, 0, 6, 0x0a },
+ { 0x9bda, 0, 8, 0x01 },
+ { 0x9be3, 0, 8, 0x01 },
+ { 0xd1a0, 1, 1, 0x00 },
+ { 0x9bbe, 0, 1, 0x01 },
+ { 0x9bcc, 0, 1, 0x01 },
+ { 0x9bb9, 0, 8, 0x00 },
+ { 0x9bcd, 0, 8, 0x18 },
+ { 0x9bff, 0, 8, 0x2c },
+ { 0xd015, 0, 8, 0x46 },
+ { 0xd016, 0, 1, 0x00 },
+ { 0xd044, 0, 8, 0x46 },
+ { 0xd045, 0, 1, 0x00 },
+ { 0xd008, 0, 8, 0xdf },
+ { 0xd009, 0, 2, 0x02 },
+ { 0xd006, 0, 8, 0x44 },
+ { 0xd007, 0, 2, 0x01 },
+ { 0xd00c, 0, 8, 0x00 },
+ { 0xd00d, 0, 2, 0x02 },
+ { 0xd00a, 0, 8, 0xf6 },
+ { 0xd00b, 0, 2, 0x01 },
+ { 0x9bba, 0, 8, 0xf9 },
+ { 0x9bc8, 0, 8, 0xaa },
+ { 0x9bc3, 0, 8, 0xdf },
+ { 0x9bc4, 0, 8, 0x02 },
+ { 0x9bc5, 0, 8, 0x00 },
+ { 0x9bc6, 0, 8, 0x02 },
+ { 0x9bc9, 0, 8, 0xf0 },
+ { 0xd011, 0, 8, 0x3c },
+ { 0xd012, 0, 2, 0x01 },
+ { 0xd013, 0, 8, 0xf7 },
+ { 0xd014, 0, 2, 0x02 },
+ { 0xd040, 0, 8, 0x0b },
+ { 0xd041, 0, 2, 0x02 },
+ { 0xd042, 0, 8, 0x4d },
+ { 0xd043, 0, 2, 0x00 },
+ { 0xd045, 1, 1, 0x00 },
+ { 0x9bcf, 0, 1, 0x01 },
+ { 0xd045, 2, 1, 0x01 },
+ { 0xd04f, 0, 8, 0x9a },
+ { 0xd050, 0, 1, 0x01 },
+ { 0xd051, 0, 8, 0x5a },
+ { 0xd052, 0, 1, 0x01 },
+ { 0xd053, 0, 8, 0x50 },
+ { 0xd054, 0, 8, 0x46 },
+ { 0x9bd7, 0, 8, 0x0a },
+ { 0x9bd8, 0, 8, 0x14 },
+ { 0x9bd9, 0, 8, 0x08 },
+ { 0x9bd0, 0, 8, 0xa8 },
+ { 0x9be4, 0, 8, 0x7f },
+ { 0x9bbd, 0, 8, 0xa8 },
+ { 0x9be2, 0, 8, 0x20 },
+ { 0x9bee, 0, 1, 0x01 },
+};
+
+#endif /* _AF9013_PRIV_ */
diff --git a/linux/drivers/media/dvb/frontends/cx24116.c b/linux/drivers/media/dvb/frontends/cx24116.c
new file mode 100644
index 000000000..163190d54
--- /dev/null
+++ b/linux/drivers/media/dvb/frontends/cx24116.c
@@ -0,0 +1,1359 @@
+/*
+ Conexant cx24116/cx24118 - DVBS/S2 Satellite demod/tuner driver
+
+ Copyright (C) 2006-2008 Steven Toth <stoth@hauppauge.com>
+ Copyright (C) 2006-2007 Georg Acher
+ Copyright (C) 2007-2008 Darron Broad
+ March 2007
+ Fixed some bugs.
+ Added diseqc support.
+ Added corrected signal strength support.
+ August 2007
+ Sync with legacy version.
+ Some clean ups.
+ Copyright (C) 2008 Igor Liplianin
+ September, 9th 2008
+ Fixed locking on high symbol rates (>30000).
+ Implement MPEG initialization parameter.
+
+ 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
+ (at your option) 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/firmware.h>
+
+#include "dvb_frontend.h"
+#include "cx24116.h"
+
+static int debug = 0;
+#define dprintk(args...) \
+ do { \
+ if (debug) printk ("cx24116: " args); \
+ } while (0)
+
+#define CX24116_DEFAULT_FIRMWARE "dvb-fe-cx24116.fw"
+#define CX24116_SEARCH_RANGE_KHZ 5000
+
+/* known registers */
+#define CX24116_REG_COMMAND (0x00) /* command args 0x00..0x1e */
+#define CX24116_REG_EXECUTE (0x1f) /* execute command */
+#define CX24116_REG_MAILBOX (0x96) /* FW or multipurpose mailbox? */
+#define CX24116_REG_RESET (0x20) /* reset status > 0 */
+#define CX24116_REG_SIGNAL (0x9e) /* signal low */
+#define CX24116_REG_SSTATUS (0x9d) /* signal high / status */
+#define CX24116_REG_QSTATUS (0xbc)
+#define CX24116_REG_QUALITY (0xd5)
+#define CX24116_REG_BER0 (0xc9)
+#define CX24116_REG_BER8 (0xc8)
+#define CX24116_REG_BER16 (0xc7)
+#define CX24116_REG_BER24 (0xc6)
+#define CX24116_REG_UCB0 (0xcb)
+#define CX24116_REG_UCB8 (0xca)
+#define CX24116_REG_CLKDIV (0xf3)
+#define CX24116_REG_RATEDIV (0xf9)
+#define CX24116_REG_FECSTATUS (0x9c) /* configured fec (not tuned) or actual FEC (tuned) 1=1/2 2=2/3 etc */
+
+/* FECSTATUS bits */
+#define CX24116_FEC_FECMASK (0x1f) /* mask to determine configured fec (not tuned) or actual fec (tuned) */
+#define CX24116_FEC_DVBS (0x20) /* Select DVB-S demodulator, else DVB-S2 */
+#define CX24116_FEC_UNKNOWN (0x40) /* Unknown/unused */
+#define CX24116_FEC_PILOT (0x80) /* Pilot mode requested when tuning else always reset when tuned */
+
+/* arg buffer size */
+#define CX24116_ARGLEN (0x1e)
+
+/* rolloff */
+#define CX24116_ROLLOFF_020 (0x00)
+#define CX24116_ROLLOFF_025 (0x01)
+#define CX24116_ROLLOFF_035 (0x02)
+
+/* pilot bit */
+#define CX24116_PILOT (0x40)
+
+/* signal status */
+#define CX24116_HAS_SIGNAL (0x01)
+#define CX24116_HAS_CARRIER (0x02)
+#define CX24116_HAS_VITERBI (0x04)
+#define CX24116_HAS_SYNCLOCK (0x08)
+#define CX24116_HAS_UNKNOWN1 (0x10)
+#define CX24116_HAS_UNKNOWN2 (0x20)
+#define CX24116_STATUS_MASK (0x3f)
+#define CX24116_SIGNAL_MASK (0xc0)
+
+#define CX24116_DISEQC_TONEOFF (0) /* toneburst never sent */
+#define CX24116_DISEQC_TONECACHE (1) /* toneburst cached */
+#define CX24116_DISEQC_MESGCACHE (2) /* message cached */
+
+/* arg offset for DiSEqC */
+#define CX24116_DISEQC_BURST (1)
+#define CX24116_DISEQC_ARG2_2 (2) /* unknown value=2 */
+#define CX24116_DISEQC_ARG3_0 (3) /* unknown value=0 */
+#define CX24116_DISEQC_ARG4_0 (4) /* unknown value=0 */
+#define CX24116_DISEQC_MSGLEN (5)
+#define CX24116_DISEQC_MSGOFS (6)
+
+/* DiSEqC burst */
+#define CX24116_DISEQC_MINI_A (0)
+#define CX24116_DISEQC_MINI_B (1)
+
+/* DiSEqC tone burst */
+static int toneburst = 1;
+
+enum cmds
+{
+ CMD_SET_VCO = 0x10,
+ CMD_TUNEREQUEST = 0x11,
+ CMD_MPEGCONFIG = 0x13,
+ CMD_TUNERINIT = 0x14,
+ CMD_BANDWIDTH = 0x15,
+ CMD_GETAGC = 0x19,
+ CMD_LNBCONFIG = 0x20,
+ CMD_LNBSEND = 0x21, /* Formerly CMD_SEND_DISEQC */
+ CMD_SET_TONEPRE = 0x22,
+ CMD_SET_TONE = 0x23,
+ CMD_UPDFWVERS = 0x35,
+ CMD_TUNERSLEEP = 0x36,
+ CMD_AGCCONTROL = 0x3b, /* Unknown */
+};
+
+/* The Demod/Tuner can't easily provide these, we cache them */
+struct cx24116_tuning
+{
+ u32 frequency;
+ u32 symbol_rate;
+ fe_spectral_inversion_t inversion;
+ fe_code_rate_t fec;
+
+ fe_modulation_t modulation;
+ fe_pilot_t pilot;
+ fe_rolloff_t rolloff;
+
+ /* Demod values */
+ u8 fec_val;
+ u8 fec_mask;
+ u8 inversion_val;
+ u8 rolloff_val;
+};
+
+/* Basic commands that are sent to the firmware */
+struct cx24116_cmd
+{
+ u8 len;
+ u8 args[CX24116_ARGLEN];
+};
+
+struct cx24116_state
+{
+ struct i2c_adapter* i2c;
+ const struct cx24116_config* config;
+
+ struct dvb_frontend frontend;
+
+ struct cx24116_tuning dcur;
+ struct cx24116_tuning dnxt;
+
+ u8 skip_fw_load;
+ u8 burst;
+ struct cx24116_cmd dsec_cmd;
+};
+
+static int cx24116_writereg(struct cx24116_state* state, int reg, int data)
+{
+ u8 buf[] = { reg, data };
+ struct i2c_msg msg = { .addr = state->config->demod_address,
+ .flags = 0, .buf = buf, .len = 2 };
+ int err;
+
+ if (debug>1)
+ printk("cx24116: %s: write reg 0x%02x, value 0x%02x\n",
+ __func__,reg, data);
+
+ if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) {
+ printk("%s: writereg error(err == %i, reg == 0x%02x,"
+ " value == 0x%02x)\n", __func__, err, reg, data);
+ return -EREMOTEIO;
+ }
+
+ return 0;
+}
+
+/* Bulk byte writes to a single I2C address, for 32k firmware load */
+static int cx24116_writeregN(struct cx24116_state* state, int reg, u8 *data, u16 len)
+{
+ int ret = -EREMOTEIO;
+ struct i2c_msg msg;
+ u8 *buf;
+
+ buf = kmalloc(len + 1, GFP_KERNEL);
+ if (buf == NULL) {
+ printk("Unable to kmalloc\n");
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ *(buf) = reg;
+ memcpy(buf + 1, data, len);
+
+ msg.addr = state->config->demod_address;
+ msg.flags = 0;
+ msg.buf = buf;
+ msg.len = len + 1;
+
+ if (debug>1)
+ printk("cx24116: %s: write regN 0x%02x, len = %d\n",
+ __func__,reg, len);
+
+ if ((ret = i2c_transfer(state->i2c, &msg, 1)) != 1) {
+ printk("%s: writereg error(err == %i, reg == 0x%02x\n",
+ __func__, ret, reg);
+ ret = -EREMOTEIO;
+ }
+
+error:
+ kfree(buf);
+
+ return ret;
+}
+
+static int cx24116_readreg(struct cx24116_state* state, u8 reg)
+{
+ int ret;
+ u8 b0[] = { reg };
+ u8 b1[] = { 0 };
+ struct i2c_msg msg[] = {
+ { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 },
+ { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 }
+ };
+
+ ret = i2c_transfer(state->i2c, msg, 2);
+
+ if (ret != 2) {
+ printk("%s: reg=0x%x (error=%d)\n", __func__, reg, ret);
+ return ret;
+ }
+
+ if (debug>1)
+ printk("cx24116: read reg 0x%02x, value 0x%02x\n",reg, b1[0]);
+
+ return b1[0];
+}
+
+static int cx24116_set_inversion(struct cx24116_state* state, fe_spectral_inversion_t inversion)
+{
+ dprintk("%s(%d)\n", __func__, inversion);
+
+ switch (inversion) {
+ case INVERSION_OFF:
+ state->dnxt.inversion_val = 0x00;
+ break;
+ case INVERSION_ON:
+ state->dnxt.inversion_val = 0x04;
+ break;
+ case INVERSION_AUTO:
+ state->dnxt.inversion_val = 0x0C;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ state->dnxt.inversion = inversion;
+
+ return 0;
+}
+
+/*
+ * modfec (modulation and FEC)
+ * ===========================
+ *
+ * MOD FEC mask/val standard
+ * ---- -------- ----------- --------
+ * QPSK FEC_1_2 0x02 0x02+X DVB-S
+ * QPSK FEC_2_3 0x04 0x02+X DVB-S
+ * QPSK FEC_3_4 0x08 0x02+X DVB-S
+ * QPSK FEC_4_5 0x10 0x02+X DVB-S (?)
+ * QPSK FEC_5_6 0x20 0x02+X DVB-S
+ * QPSK FEC_6_7 0x40 0x02+X DVB-S
+ * QPSK FEC_7_8 0x80 0x02+X DVB-S
+ * QPSK FEC_8_9 0x01 0x02+X DVB-S (?) (NOT SUPPORTED?)
+ * QPSK AUTO 0xff 0x02+X DVB-S
+ *
+ * For DVB-S high byte probably represents FEC
+ * and low byte selects the modulator. The high
+ * byte is search range mask. Bit 5 may turn
+ * on DVB-S and remaining bits represent some
+ * kind of calibration (how/what i do not know).
+ *
+ * Eg.(2/3) szap "Zone Horror"
+ *
+ * mask/val = 0x04, 0x20
+ * status 1f | signal c3c0 | snr a333 | ber 00000098 | unc 00000000 | FE_HAS_LOCK
+ *
+ * mask/val = 0x04, 0x30
+ * status 1f | signal c3c0 | snr a333 | ber 00000000 | unc 00000000 | FE_HAS_LOCK
+ *
+ * After tuning FECSTATUS contains actual FEC
+ * in use numbered 1 through to 8 for 1/2 .. 2/3 etc
+ *
+ * NBC=NOT/NON BACKWARD COMPATIBLE WITH DVB-S (DVB-S2 only)
+ *
+ * NBC-QPSK FEC_1_2 0x00, 0x04 DVB-S2
+ * NBC-QPSK FEC_3_5 0x00, 0x05 DVB-S2
+ * NBC-QPSK FEC_2_3 0x00, 0x06 DVB-S2
+ * NBC-QPSK FEC_3_4 0x00, 0x07 DVB-S2
+ * NBC-QPSK FEC_4_5 0x00, 0x08 DVB-S2
+ * NBC-QPSK FEC_5_6 0x00, 0x09 DVB-S2
+ * NBC-QPSK FEC_8_9 0x00, 0x0a DVB-S2
+ * NBC-QPSK FEC_9_10 0x00, 0x0b DVB-S2
+ *
+ * NBC-8PSK FEC_3_5 0x00, 0x0c DVB-S2
+ * NBC-8PSK FEC_2_3 0x00, 0x0d DVB-S2
+ * NBC-8PSK FEC_3_4 0x00, 0x0e DVB-S2
+ * NBC-8PSK FEC_5_6 0x00, 0x0f DVB-S2
+ * NBC-8PSK FEC_8_9 0x00, 0x10 DVB-S2
+ * NBC-8PSK FEC_9_10 0x00, 0x11 DVB-S2
+ *
+ * For DVB-S2 low bytes selects both modulator
+ * and FEC. High byte is meaningless here. To
+ * set pilot, bit 6 (0x40) is set. When inspecting
+ * FECSTATUS bit 7 (0x80) represents the pilot
+ * selection whilst not tuned. When tuned, actual FEC
+ * in use is found in FECSTATUS as per above. Pilot
+ * value is reset.
+ */
+
+/* A table of modulation, fec and configuration bytes for the demod.
+ * Not all S2 mmodulation schemes are support and not all rates with
+ * a scheme are support. Especially, no auto detect when in S2 mode.
+ */
+struct cx24116_modfec {
+ fe_modulation_t modulation;
+ fe_code_rate_t fec;
+ u8 mask; /* In DVBS mode this is used to autodetect */
+ u8 val; /* Passed to the firmware to indicate mode selection */
+} CX24116_MODFEC_MODES[] = {
+ /* QPSK. For unknown rates we set hardware to auto detect 0xfe 0x30 */
+
+ /*mod fec mask val */
+ { QPSK, FEC_NONE, 0xfe, 0x30 },
+ { QPSK, FEC_1_2, 0x02, 0x2e }, /* 00000010 00101110 */
+ { QPSK, FEC_2_3, 0x04, 0x2f }, /* 00000100 00101111 */
+ { QPSK, FEC_3_4, 0x08, 0x30 }, /* 00001000 00110000 */
+ { QPSK, FEC_4_5, 0xfe, 0x30 }, /* 000?0000 ? */
+ { QPSK, FEC_5_6, 0x20, 0x31 }, /* 00100000 00110001 */
+ { QPSK, FEC_6_7, 0xfe, 0x30 }, /* 0?000000 ? */
+ { QPSK, FEC_7_8, 0x80, 0x32 }, /* 10000000 00110010 */
+ { QPSK, FEC_8_9, 0xfe, 0x30 }, /* 0000000? ? */
+ { QPSK, FEC_AUTO, 0xfe, 0x30 },
+ /* NBC-QPSK */
+ { NBC_QPSK, FEC_1_2, 0x00, 0x04 },
+ { NBC_QPSK, FEC_3_5, 0x00, 0x05 },
+ { NBC_QPSK, FEC_2_3, 0x00, 0x06 },
+ { NBC_QPSK, FEC_3_4, 0x00, 0x07 },
+ { NBC_QPSK, FEC_4_5, 0x00, 0x08 },
+ { NBC_QPSK, FEC_5_6, 0x00, 0x09 },
+ { NBC_QPSK, FEC_8_9, 0x00, 0x0a },
+ { NBC_QPSK, FEC_9_10, 0x00, 0x0b },
+ /* 8PSK */
+ { _8PSK, FEC_3_5, 0x00, 0x0c },
+ { _8PSK, FEC_2_3, 0x00, 0x0d },
+ { _8PSK, FEC_3_4, 0x00, 0x0e },
+ { _8PSK, FEC_5_6, 0x00, 0x0f },
+ { _8PSK, FEC_8_9, 0x00, 0x10 },
+ { _8PSK, FEC_9_10, 0x00, 0x11 },
+ /*
+ * `val' can be found in the FECSTATUS register when tuning.
+ * FECSTATUS will give the actual FEC in use if tuning was successful.
+ */
+};
+
+static int cx24116_lookup_fecmod(struct cx24116_state* state,
+ fe_modulation_t m, fe_code_rate_t f)
+{
+ int i, ret = -EOPNOTSUPP;
+
+ dprintk("%s(0x%02x,0x%02x)\n", __func__, m, f);
+
+ for(i=0 ; i < sizeof(CX24116_MODFEC_MODES) / sizeof(struct cx24116_modfec) ; i++)
+ {
+ if( (m == CX24116_MODFEC_MODES[i].modulation) &&
+ (f == CX24116_MODFEC_MODES[i].fec) )
+ {
+ ret = i;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static int cx24116_set_fec(struct cx24116_state* state, fe_modulation_t mod, fe_code_rate_t fec)
+{
+ int ret = 0;
+
+ dprintk("%s(0x%02x,0x%02x)\n", __func__, mod, fec);
+
+ ret = cx24116_lookup_fecmod(state, mod, fec);
+
+ if(ret < 0)
+ return ret;
+
+ state->dnxt.fec = fec;
+ state->dnxt.fec_val = CX24116_MODFEC_MODES[ret].val;
+ state->dnxt.fec_mask = CX24116_MODFEC_MODES[ret].mask;
+ dprintk("%s() mask/val = 0x%02x/0x%02x\n", __func__,
+ state->dnxt.fec_mask, state->dnxt.fec_val);
+
+ return 0;
+}
+
+static int cx24116_set_symbolrate(struct cx24116_state* state, u32 rate)
+{
+ dprintk("%s(%d)\n", __func__, rate);
+
+ /* check if symbol rate is within limits */
+ if ((rate > state->frontend.ops.info.symbol_rate_max) ||
+ (rate < state->frontend.ops.info.symbol_rate_min)) {
+ dprintk("%s() unsupported symbol_rate = %d\n", __func__, rate);
+ return -EOPNOTSUPP;
+ }
+
+ state->dnxt.symbol_rate = rate;
+ dprintk("%s() symbol_rate = %d\n", __func__, rate);
+
+ return 0;
+}
+
+static int cx24116_load_firmware (struct dvb_frontend* fe, const struct firmware *fw);
+
+static int cx24116_firmware_ondemand(struct dvb_frontend* fe)
+{
+ struct cx24116_state *state = fe->demodulator_priv;
+ const struct firmware *fw;
+ int ret = 0;
+
+ dprintk("%s()\n",__func__);
+
+ if (cx24116_readreg(state, 0x20) > 0)
+ {
+
+ if (state->skip_fw_load)
+ return 0;
+
+ /* Load firmware */
+ /* request the firmware, this will block until someone uploads it */
+ printk("%s: Waiting for firmware upload (%s)...\n", __func__, CX24116_DEFAULT_FIRMWARE);
+ ret = request_firmware(&fw, CX24116_DEFAULT_FIRMWARE, &state->i2c->dev);
+ printk("%s: Waiting for firmware upload(2)...\n", __func__);
+ if (ret) {
+ printk("%s: No firmware uploaded (timeout or file not found?)\n", __func__);
+ return ret;
+ }
+
+ /* Make sure we don't recurse back through here during loading */
+ state->skip_fw_load = 1;
+
+ ret = cx24116_load_firmware(fe, fw);
+ if (ret)
+ printk("%s: Writing firmware to device failed\n", __func__);
+
+ release_firmware(fw);
+
+ printk("%s: Firmware upload %s\n", __func__, ret == 0 ? "complete" : "failed");
+
+ /* Ensure firmware is always loaded if required */
+ state->skip_fw_load = 0;
+ }
+
+ return ret;
+}
+
+/* Take a basic firmware command structure, format it and forward it for processing */
+static int cx24116_cmd_execute(struct dvb_frontend* fe, struct cx24116_cmd *cmd)
+{
+ struct cx24116_state *state = fe->demodulator_priv;
+ int i, ret;
+
+ dprintk("%s()\n", __func__);
+
+ /* Load the firmware if required */
+ if ( (ret = cx24116_firmware_ondemand(fe)) != 0)
+ {
+ printk("%s(): Unable initialise the firmware\n", __func__);
+ return ret;
+ }
+
+ /* Write the command */
+ for(i = 0; i < cmd->len ; i++)
+ {
+ dprintk("%s: 0x%02x == 0x%02x\n", __func__, i, cmd->args[i]);
+ cx24116_writereg(state, i, cmd->args[i]);
+ }
+
+ /* Start execution and wait for cmd to terminate */
+ cx24116_writereg(state, CX24116_REG_EXECUTE, 0x01);
+ while( cx24116_readreg(state, CX24116_REG_EXECUTE) )
+ {
+ msleep(10);
+ if(i++ > 64)
+ {
+ /* Avoid looping forever if the firmware does no respond */
+ printk("%s() Firmware not responding\n", __func__);
+ return -EREMOTEIO;
+ }
+ }
+ return 0;
+}
+
+static int cx24116_load_firmware (struct dvb_frontend* fe, const struct firmware *fw)
+{
+ struct cx24116_state* state = fe->demodulator_priv;
+ struct cx24116_cmd cmd;
+ int i, ret;
+ unsigned char vers[4];
+
+ dprintk("%s\n", __func__);
+ dprintk("Firmware is %zu bytes (%02x %02x .. %02x %02x)\n"
+ ,fw->size
+ ,fw->data[0]
+ ,fw->data[1]
+ ,fw->data[ fw->size-2 ]
+ ,fw->data[ fw->size-1 ]
+ );
+
+ /* Toggle 88x SRST pin to reset demod */
+ if (state->config->reset_device)
+ state->config->reset_device(fe);
+
+ /* Begin the firmware load process */
+ /* Prepare the demod, load the firmware, cleanup after load */
+
+ /* Init PLL */
+ cx24116_writereg(state, 0xE5, 0x00);
+ cx24116_writereg(state, 0xF1, 0x08);
+ cx24116_writereg(state, 0xF2, 0x13);
+
+ /* Start PLL */
+ cx24116_writereg(state, 0xe0, 0x03);
+ cx24116_writereg(state, 0xe0, 0x00);
+
+ /* Unknown */
+ cx24116_writereg(state, CX24116_REG_CLKDIV, 0x46);
+ cx24116_writereg(state, CX24116_REG_RATEDIV, 0x00);
+
+ /* Unknown */
+ cx24116_writereg(state, 0xF0, 0x03);
+ cx24116_writereg(state, 0xF4, 0x81);
+ cx24116_writereg(state, 0xF5, 0x00);
+ cx24116_writereg(state, 0xF6, 0x00);
+
+ /* write the entire firmware as one transaction */
+ cx24116_writeregN(state, 0xF7, fw->data, fw->size);
+
+ cx24116_writereg(state, 0xF4, 0x10);
+ cx24116_writereg(state, 0xF0, 0x00);
+ cx24116_writereg(state, 0xF8, 0x06);
+
+ /* Firmware CMD 10: VCO config */
+ cmd.args[0x00] = CMD_SET_VCO;
+ cmd.args[0x01] = 0x05;
+ cmd.args[0x02] = 0xdc;
+ cmd.args[0x03] = 0xda;
+ cmd.args[0x04] = 0xae;
+ cmd.args[0x05] = 0xaa;
+ cmd.args[0x06] = 0x04;
+ cmd.args[0x07] = 0x9d;
+ cmd.args[0x08] = 0xfc;
+ cmd.args[0x09] = 0x06;
+ cmd.len= 0x0a;
+ ret = cx24116_cmd_execute(fe, &cmd);
+ if (ret != 0)
+ return ret;
+
+ cx24116_writereg(state, CX24116_REG_SSTATUS, 0x00);
+
+ /* Firmware CMD 14: Tuner config */
+ cmd.args[0x00] = CMD_TUNERINIT;
+ cmd.args[0x01] = 0x00;
+ cmd.args[0x02] = 0x00;
+ cmd.len= 0x03;
+ ret = cx24116_cmd_execute(fe, &cmd);
+ if (ret != 0)
+ return ret;
+
+ cx24116_writereg(state, 0xe5, 0x00);
+
+ /* Firmware CMD 13: MPEG config */
+ cmd.args[0x00] = CMD_MPEGCONFIG;
+ cmd.args[0x01] = 0x01;
+ cmd.args[0x02] = 0x75;
+ cmd.args[0x03] = 0x00;
+ if (state->config->mpg_clk_pos_pol)
+ cmd.args[0x04] = state->config->mpg_clk_pos_pol;
+ else
+ cmd.args[0x04] = 0x02;
+ cmd.args[0x05] = 0x00;
+ cmd.len= 0x06;
+ ret = cx24116_cmd_execute(fe, &cmd);
+ if (ret != 0)
+ return ret;
+
+ /* Firmware CMD 35: Get firmware version */
+ cmd.args[0x00] = CMD_UPDFWVERS;
+ cmd.len= 0x02;
+ for(i=0; i<4; i++) {
+ cmd.args[0x01] = i;
+ ret = cx24116_cmd_execute(fe, &cmd);
+ if (ret != 0)
+ return ret;
+ vers[i]= cx24116_readreg(state, CX24116_REG_MAILBOX);
+ }
+ printk("%s: FW version %i.%i.%i.%i\n", __func__,
+ vers[0], vers[1], vers[2], vers[3]);
+
+ return 0;
+}
+
+static int cx24116_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage)
+{
+ /* The isl6421 module will override this function in the fops. */
+ dprintk("%s() This should never appear if the isl6421 module is loaded correctly\n",__func__);
+
+ return -EOPNOTSUPP;
+}
+
+static int cx24116_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+ struct cx24116_state *state = fe->demodulator_priv;
+
+ int lock = cx24116_readreg(state, CX24116_REG_SSTATUS);
+
+ dprintk("%s: status = 0x%02x\n", __func__, lock);
+
+ *status = 0;
+
+ if (lock & CX24116_HAS_SIGNAL)
+ *status |= FE_HAS_SIGNAL;
+ if (lock & CX24116_HAS_CARRIER)
+ *status |= FE_HAS_CARRIER;
+ if (lock & CX24116_HAS_VITERBI)
+ *status |= FE_HAS_VITERBI;
+ if (lock & CX24116_HAS_SYNCLOCK)
+ *status |= FE_HAS_SYNC | FE_HAS_LOCK;
+
+ return 0;
+}
+
+static int cx24116_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+ struct cx24116_state *state = fe->demodulator_priv;
+
+ dprintk("%s()\n", __func__);
+
+ *ber = ( cx24116_readreg(state, CX24116_REG_BER24) << 24 ) |
+ ( cx24116_readreg(state, CX24116_REG_BER16) << 16 ) |
+ ( cx24116_readreg(state, CX24116_REG_BER8 ) << 8 ) |
+ cx24116_readreg(state, CX24116_REG_BER0 );
+
+ return 0;
+}
+
+/* TODO Determine function and scale appropriately */
+static int cx24116_read_signal_strength(struct dvb_frontend* fe, u16* signal_strength)
+{
+ struct cx24116_state *state = fe->demodulator_priv;
+ struct cx24116_cmd cmd;
+ int ret;
+ u16 sig_reading;
+
+ dprintk("%s()\n", __func__);
+
+ /* Firmware CMD 19: Get AGC */
+ cmd.args[0x00] = CMD_GETAGC;
+ cmd.len= 0x01;
+ ret = cx24116_cmd_execute(fe, &cmd);
+ if (ret != 0)
+ return ret;
+
+ sig_reading = ( cx24116_readreg(state, CX24116_REG_SSTATUS) & CX24116_SIGNAL_MASK ) |
+ ( cx24116_readreg(state, CX24116_REG_SIGNAL) << 6 );
+ *signal_strength= 0 - sig_reading;
+
+ dprintk("%s: raw / cooked = 0x%04x / 0x%04x\n", __func__, sig_reading, *signal_strength);
+
+ return 0;
+}
+
+/* SNR (0..100)% = (sig & 0xf0) * 10 + (sig & 0x0f) * 10 / 16 */
+static int cx24116_read_snr(struct dvb_frontend* fe, u16* snr)
+{
+ struct cx24116_state *state = fe->demodulator_priv;
+ u8 snr_reading;
+ static const u32 snr_tab[] = { /* 10 x Table (rounded up) */
+ 0x00000,0x0199A,0x03333,0x04ccD,0x06667,
+ 0x08000,0x0999A,0x0b333,0x0cccD,0x0e667,
+ 0x10000,0x1199A,0x13333,0x14ccD,0x16667,0x18000 };
+
+ dprintk("%s()\n", __func__);
+
+ snr_reading = cx24116_readreg(state, CX24116_REG_QUALITY);
+
+ if(snr_reading >= 0xa0 /* 100% */)
+ *snr = 0xffff;
+ else
+ *snr = snr_tab [ ( snr_reading & 0xf0 ) >> 4 ] +
+ ( snr_tab [ ( snr_reading & 0x0f ) ] >> 4 );
+
+ dprintk("%s: raw / cooked = 0x%02x / 0x%04x\n", __func__,
+ snr_reading, *snr);
+
+ return 0;
+}
+
+static int cx24116_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+ struct cx24116_state *state = fe->demodulator_priv;
+
+ dprintk("%s()\n", __func__);
+
+ *ucblocks = ( cx24116_readreg(state, CX24116_REG_UCB8) << 8 ) |
+ cx24116_readreg(state, CX24116_REG_UCB0);
+
+ return 0;
+}
+
+/* Overwrite the current tuning params, we are about to tune */
+static void cx24116_clone_params(struct dvb_frontend* fe)
+{
+ struct cx24116_state *state = fe->demodulator_priv;
+ memcpy(&state->dcur, &state->dnxt, sizeof(state->dcur));
+}
+
+/* Wait for LNB */
+static int cx24116_wait_for_lnb(struct dvb_frontend* fe)
+{
+ struct cx24116_state *state = fe->demodulator_priv;
+ int i;
+
+ dprintk("%s() qstatus = 0x%02x\n", __func__,
+ cx24116_readreg(state, CX24116_REG_QSTATUS));
+
+ /* Wait for up to 300 ms */
+ for(i = 0; i < 30 ; i++) {
+ if (cx24116_readreg(state, CX24116_REG_QSTATUS) & 0x20)
+ return 0;
+ msleep(10);
+ }
+
+ dprintk("%s(): LNB not ready\n", __func__);
+
+ return -ETIMEDOUT; /* -EBUSY ? */
+}
+
+static int cx24116_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
+{
+ struct cx24116_cmd cmd;
+ int ret;
+
+ dprintk("%s(%d)\n", __func__, tone);
+ if ( (tone != SEC_TONE_ON) && (tone != SEC_TONE_OFF) ) {
+ printk("%s: Invalid, tone=%d\n", __func__, tone);
+ return -EINVAL;
+ }
+
+ /* Wait for LNB ready */
+ ret = cx24116_wait_for_lnb(fe);
+ if(ret != 0)
+ return ret;
+
+ /* Min delay time after DiSEqC send */
+ msleep(15); /* XXX determine is FW does this, see send_diseqc/burst */
+
+ /* This is always done before the tone is set */
+ cmd.args[0x00] = CMD_SET_TONEPRE;
+ cmd.args[0x01] = 0x00;
+ cmd.len= 0x02;
+ ret = cx24116_cmd_execute(fe, &cmd);
+ if (ret != 0)
+ return ret;
+
+ /* Now we set the tone */
+ cmd.args[0x00] = CMD_SET_TONE;
+ cmd.args[0x01] = 0x00;
+ cmd.args[0x02] = 0x00;
+
+ switch (tone) {
+ case SEC_TONE_ON:
+ dprintk("%s: setting tone on\n", __func__);
+ cmd.args[0x03] = 0x01;
+ break;
+ case SEC_TONE_OFF:
+ dprintk("%s: setting tone off\n",__func__);
+ cmd.args[0x03] = 0x00;
+ break;
+ }
+ cmd.len= 0x04;
+
+ /* Min delay time before DiSEqC send */
+ msleep(15); /* XXX determine is FW does this, see send_diseqc/burst */
+
+ return cx24116_cmd_execute(fe, &cmd);
+}
+
+/* Initialise DiSEqC */
+static int cx24116_diseqc_init(struct dvb_frontend* fe)
+{
+ struct cx24116_state *state = fe->demodulator_priv;
+ struct cx24116_cmd cmd;
+ int ret;
+
+ /* Firmware CMD 20: LNB/DiSEqC config */
+ cmd.args[0x00] = CMD_LNBCONFIG;
+ cmd.args[0x01] = 0x00;
+ cmd.args[0x02] = 0x10;
+ cmd.args[0x03] = 0x00;
+ cmd.args[0x04] = 0x8f;
+ cmd.args[0x05] = 0x28;
+ cmd.args[0x06] = (toneburst == CX24116_DISEQC_TONEOFF) ? 0x00 : 0x01;
+ cmd.args[0x07] = 0x01;
+ cmd.len= 0x08;
+ ret = cx24116_cmd_execute(fe, &cmd);
+ if (ret != 0)
+ return ret;
+
+ /* Prepare a DiSEqC command */
+ state->dsec_cmd.args[0x00] = CMD_LNBSEND;
+
+ /* DiSEqC burst */
+ state->dsec_cmd.args[CX24116_DISEQC_BURST] = CX24116_DISEQC_MINI_A;
+
+ /* Unknown */
+ state->dsec_cmd.args[CX24116_DISEQC_ARG2_2] = 0x02;
+ state->dsec_cmd.args[CX24116_DISEQC_ARG3_0] = 0x00;
+ state->dsec_cmd.args[CX24116_DISEQC_ARG4_0] = 0x00; /* Continuation flag? */
+
+ /* DiSEqC message length */
+ state->dsec_cmd.args[CX24116_DISEQC_MSGLEN] = 0x00;
+
+ /* Command length */
+ state->dsec_cmd.len= CX24116_DISEQC_MSGOFS;
+
+ return 0;
+}
+
+/* Send DiSEqC message with derived burst (hack) || previous burst */
+static int cx24116_send_diseqc_msg(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd *d)
+{
+ struct cx24116_state *state = fe->demodulator_priv;
+ int i, ret;
+
+ /* Dump DiSEqC message */
+ if (debug) {
+ printk("cx24116: %s(", __func__);
+ for(i = 0 ; i < d->msg_len ;) {
+ printk("0x%02x", d->msg[i]);
+ if(++i < d->msg_len)
+ printk(", ");
+ }
+ printk(") toneburst=%d\n", toneburst);
+ }
+
+ /* Validate length */
+ if(d->msg_len > (CX24116_ARGLEN - CX24116_DISEQC_MSGOFS))
+ return -EINVAL;
+
+ /* DiSEqC message */
+ for (i = 0; i < d->msg_len; i++)
+ state->dsec_cmd.args[CX24116_DISEQC_MSGOFS + i] = d->msg[i];
+
+ /* DiSEqC message length */
+ state->dsec_cmd.args[CX24116_DISEQC_MSGLEN] = d->msg_len;
+
+ /* Command length */
+ state->dsec_cmd.len= CX24116_DISEQC_MSGOFS + state->dsec_cmd.args[CX24116_DISEQC_MSGLEN];
+
+ /* DiSEqC toneburst */
+ if(toneburst == CX24116_DISEQC_MESGCACHE)
+ /* Message is cached */
+ return 0;
+
+ else if(toneburst == CX24116_DISEQC_TONEOFF)
+ /* Message is sent without burst */
+ state->dsec_cmd.args[CX24116_DISEQC_BURST] = 0;
+
+ else if(toneburst == CX24116_DISEQC_TONECACHE) {
+ /*
+ * Message is sent with derived else cached burst
+ *
+ * WRITE PORT GROUP COMMAND 38
+ *
+ * 0/A/A: E0 10 38 F0..F3
+ * 1/B/B: E0 10 38 F4..F7
+ * 2/C/A: E0 10 38 F8..FB
+ * 3/D/B: E0 10 38 FC..FF
+ *
+ * databyte[3]= 8421:8421
+ * ABCD:WXYZ
+ * CLR :SET
+ *
+ * WX= PORT SELECT 0..3 (X=TONEBURST)
+ * Y = VOLTAGE (0=13V, 1=18V)
+ * Z = BAND (0=LOW, 1=HIGH(22K))
+ */
+ if(d->msg_len >= 4 && d->msg[2] == 0x38)
+ state->dsec_cmd.args[CX24116_DISEQC_BURST] = ((d->msg[3] & 4) >> 2);
+ if(debug)
+ dprintk("%s burst=%d\n", __func__, state->dsec_cmd.args[CX24116_DISEQC_BURST]);
+ }
+
+ /* Wait for LNB ready */
+ ret = cx24116_wait_for_lnb(fe);
+ if(ret != 0)
+ return ret;
+
+ /* Wait for voltage/min repeat delay */
+ msleep(100);
+
+ /* Command */
+ ret = cx24116_cmd_execute(fe, &state->dsec_cmd);
+ if(ret != 0)
+ return ret;
+ /*
+ * Wait for send
+ *
+ * Eutelsat spec:
+ * >15ms delay + (XXX determine if FW does this, see set_tone)
+ * 13.5ms per byte +
+ * >15ms delay +
+ * 12.5ms burst +
+ * >15ms delay (XXX determine if FW does this, see set_tone)
+ */
+ msleep( (state->dsec_cmd.args[CX24116_DISEQC_MSGLEN] << 4) + ((toneburst == CX24116_DISEQC_TONEOFF) ? 30 : 60) );
+
+ return 0;
+}
+
+/* Send DiSEqC burst */
+static int cx24116_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t burst)
+{
+ struct cx24116_state *state = fe->demodulator_priv;
+ int ret;
+
+ dprintk("%s(%d) toneburst=%d\n",__func__, burst, toneburst);
+
+ /* DiSEqC burst */
+ if (burst == SEC_MINI_A)
+ state->dsec_cmd.args[CX24116_DISEQC_BURST] = CX24116_DISEQC_MINI_A;
+ else if(burst == SEC_MINI_B)
+ state->dsec_cmd.args[CX24116_DISEQC_BURST] = CX24116_DISEQC_MINI_B;
+ else
+ return -EINVAL;
+
+ /* DiSEqC toneburst */
+ if(toneburst != CX24116_DISEQC_MESGCACHE)
+ /* Burst is cached */
+ return 0;
+
+ /* Burst is to be sent with cached message */
+
+ /* Wait for LNB ready */
+ ret = cx24116_wait_for_lnb(fe);
+ if(ret != 0)
+ return ret;
+
+ /* Wait for voltage/min repeat delay */
+ msleep(100);
+
+ /* Command */
+ ret = cx24116_cmd_execute(fe, &state->dsec_cmd);
+ if(ret != 0)
+ return ret;
+
+ /*
+ * Wait for send
+ *
+ * Eutelsat spec:
+ * >15ms delay + (XXX determine if FW does this, see set_tone)
+ * 13.5ms per byte +
+ * >15ms delay +
+ * 12.5ms burst +
+ * >15ms delay (XXX determine if FW does this, see set_tone)
+ */
+ msleep( (state->dsec_cmd.args[CX24116_DISEQC_MSGLEN] << 4) + 60 );
+
+ return 0;
+}
+
+static void cx24116_release(struct dvb_frontend* fe)
+{
+ struct cx24116_state* state = fe->demodulator_priv;
+ dprintk("%s\n",__func__);
+ kfree(state);
+}
+
+static struct dvb_frontend_ops cx24116_ops;
+
+struct dvb_frontend* cx24116_attach(const struct cx24116_config* config,
+ struct i2c_adapter* i2c)
+{
+ struct cx24116_state* state = NULL;
+ int ret;
+
+ dprintk("%s\n",__func__);
+
+ /* allocate memory for the internal state */
+ state = kmalloc(sizeof(struct cx24116_state), GFP_KERNEL);
+ if (state == NULL) {
+ printk("Unable to kmalloc\n");
+ goto error1;
+ }
+
+ /* setup the state */
+ memset(state, 0, sizeof(struct cx24116_state));
+
+ state->config = config;
+ state->i2c = i2c;
+
+ /* check if the demod is present */
+ ret = (cx24116_readreg(state, 0xFF) << 8) | cx24116_readreg(state, 0xFE);
+ if (ret != 0x0501) {
+ printk("Invalid probe, probably not a CX24116 device\n");
+ goto error2;
+ }
+
+ /* create dvb_frontend */
+ memcpy(&state->frontend.ops, &cx24116_ops, sizeof(struct dvb_frontend_ops));
+ state->frontend.demodulator_priv = state;
+ return &state->frontend;
+
+error2: kfree(state);
+error1: return NULL;
+}
+#if 0
+static int cx24116_get_params(struct dvb_frontend* fe)
+{
+ struct cx24116_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *cache = &fe->dtv_property_cache;
+
+ dprintk("%s()\n",__func__);
+
+ cache->frequency = state->dcur.frequency;
+ cache->inversion = state->dcur.inversion;
+ cache->modulation = state->dcur.modulation;
+ cache->fec_inner = state->dcur.fec;
+ cache->symbol_rate = state->dcur.symbol_rate;
+
+ return 0;
+}
+#endif
+/*
+ * Initialise or wake up device
+ *
+ * Power config will reset and load initial firmware if required
+ */
+static int cx24116_initfe(struct dvb_frontend* fe)
+{
+ struct cx24116_state* state = fe->demodulator_priv;
+ struct cx24116_cmd cmd;
+ int ret;
+
+ dprintk("%s()\n",__func__);
+
+ /* Power on */
+ cx24116_writereg(state, 0xe0, 0);
+ cx24116_writereg(state, 0xe1, 0);
+ cx24116_writereg(state, 0xea, 0);
+
+ /* Firmware CMD 36: Power config */
+ cmd.args[0x00] = CMD_TUNERSLEEP;
+ cmd.args[0x01] = 0;
+ cmd.len= 0x02;
+ ret = cx24116_cmd_execute(fe, &cmd);
+ if(ret != 0)
+ return ret;
+
+ return cx24116_diseqc_init(fe);
+}
+
+/*
+ * Put device to sleep
+ */
+static int cx24116_sleep(struct dvb_frontend* fe)
+{
+ struct cx24116_state* state = fe->demodulator_priv;
+ struct cx24116_cmd cmd;
+ int ret;
+
+ dprintk("%s()\n",__func__);
+
+ /* Firmware CMD 36: Power config */
+ cmd.args[0x00] = CMD_TUNERSLEEP;
+ cmd.args[0x01] = 1;
+ cmd.len= 0x02;
+ ret = cx24116_cmd_execute(fe, &cmd);
+ if(ret != 0)
+ return ret;
+
+ /* Power off (Shutdown clocks) */
+ cx24116_writereg(state, 0xea, 0xff);
+ cx24116_writereg(state, 0xe1, 1);
+ cx24116_writereg(state, 0xe0, 1);
+
+ return 0;
+}
+
+static int cx24116_set_property(struct dvb_frontend *fe, struct dtv_property* tvp)
+{
+ dprintk("%s(..)\n", __func__);
+ return 0;
+}
+
+static int cx24116_get_property(struct dvb_frontend *fe, struct dtv_property* tvp)
+{
+ dprintk("%s(..)\n", __func__);
+ return 0;
+}
+
+/* dvb-core told us to tune, the tv property cache will be complete,
+ * it's safe for is to pull values and use them for tuning purposes.
+ */
+static int cx24116_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+ struct cx24116_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct cx24116_cmd cmd;
+ fe_status_t tunerstat;
+ int i, status, ret, retune = 1;
+
+ dprintk("%s()\n",__func__);
+
+ state->dnxt.modulation = c->modulation;
+ state->dnxt.frequency = c->frequency;
+
+ switch(c->delivery_system) {
+ case SYS_DVBS:
+ dprintk("%s: DVB-S delivery system selected\n",__func__);
+ state->dnxt.pilot = PILOT_OFF;
+ state->dnxt.rolloff_val = CX24116_ROLLOFF_035;
+ state->dnxt.rolloff = c->rolloff;
+ break;
+ case SYS_DVBS2:
+ dprintk("%s: DVB-S2 delivery system selected\n",__func__);
+ if(c->pilot == PILOT_AUTO)
+ retune++;
+ state->dnxt.pilot = c->pilot;
+ switch(c->rolloff) {
+ case ROLLOFF_20:
+ state->dnxt.rolloff_val= CX24116_ROLLOFF_020;
+ break;
+ case ROLLOFF_25:
+ state->dnxt.rolloff_val= CX24116_ROLLOFF_025;
+ break;
+ case ROLLOFF_35:
+ state->dnxt.rolloff_val= CX24116_ROLLOFF_035;
+ break;
+ case ROLLOFF_AUTO:
+ return -EOPNOTSUPP;
+ }
+ state->dnxt.rolloff = c->rolloff;
+ break;
+ default:
+ dprintk("%s: unsupported delivery system selected (%d)\n",
+ __func__, c->delivery_system);
+ return -EOPNOTSUPP;
+ }
+
+ if ((ret = cx24116_set_inversion(state, c->inversion)) != 0)
+ return ret;
+
+ if ((ret = cx24116_set_fec(state, c->modulation, c->fec_inner)) != 0)
+ return ret;
+
+ if ((ret = cx24116_set_symbolrate(state, c->symbol_rate)) != 0)
+ return ret;
+
+ /* discard the 'current' tuning parameters and prepare to tune */
+ cx24116_clone_params(fe);
+
+ dprintk("%s: retune = %d\n", __func__, retune);
+ dprintk("%s: rolloff = %d\n", __func__, state->dcur.rolloff);
+ dprintk("%s: pilot = %d\n", __func__, state->dcur.pilot);
+ dprintk("%s: frequency = %d\n", __func__, state->dcur.frequency);
+ dprintk("%s: symbol_rate = %d\n", __func__, state->dcur.symbol_rate);
+ dprintk("%s: FEC = %d (mask/val = 0x%02x/0x%02x)\n", __func__,
+ state->dcur.fec, state->dcur.fec_mask, state->dcur.fec_val);
+ dprintk("%s: Inversion = %d (val = 0x%02x)\n", __func__,
+ state->dcur.inversion, state->dcur.inversion_val);
+
+ /* This is also done in advise/acquire on HVR4000 but not on LITE */
+ if (state->config->set_ts_params)
+ state->config->set_ts_params(fe, 0);
+
+ /* Set/Reset B/W */
+ cmd.args[0x00] = CMD_BANDWIDTH;
+ cmd.args[0x01] = 0x01;
+ cmd.len= 0x02;
+ ret = cx24116_cmd_execute(fe, &cmd);
+ if (ret != 0)
+ return ret;
+
+ /* Prepare a tune request */
+ cmd.args[0x00] = CMD_TUNEREQUEST;
+
+ /* Frequency */
+ cmd.args[0x01] = (state->dcur.frequency & 0xff0000) >> 16;
+ cmd.args[0x02] = (state->dcur.frequency & 0x00ff00) >> 8;
+ cmd.args[0x03] = (state->dcur.frequency & 0x0000ff);
+
+ /* Symbol Rate */
+ cmd.args[0x04] = ((state->dcur.symbol_rate / 1000) & 0xff00) >> 8;
+ cmd.args[0x05] = ((state->dcur.symbol_rate / 1000) & 0x00ff);
+
+ /* Automatic Inversion */
+ cmd.args[0x06] = state->dcur.inversion_val;
+
+ /* Modulation / FEC & Pilot Off */
+ cmd.args[0x07] = state->dcur.fec_val;
+
+ if (state->dcur.pilot == PILOT_ON)
+ cmd.args[0x07] |= CX24116_PILOT;
+
+ cmd.args[0x08] = CX24116_SEARCH_RANGE_KHZ >> 8;
+ cmd.args[0x09] = CX24116_SEARCH_RANGE_KHZ & 0xff;
+ cmd.args[0x0a] = 0x00;
+ cmd.args[0x0b] = 0x00;
+ cmd.args[0x0c] = state->dcur.rolloff_val;
+ cmd.args[0x0d] = state->dcur.fec_mask;
+
+ if (state->dcur.symbol_rate > 30000000) {
+ cmd.args[0x0e] = 0x04;
+ cmd.args[0x0f] = 0x00;
+ cmd.args[0x10] = 0x01;
+ cmd.args[0x11] = 0x77;
+ cmd.args[0x12] = 0x36;
+ cx24116_writereg(state, CX24116_REG_CLKDIV, 0x44);
+ cx24116_writereg(state, CX24116_REG_RATEDIV, 0x01);
+ } else {
+ cmd.args[0x0e] = 0x06;
+ cmd.args[0x0f] = 0x00;
+ cmd.args[0x10] = 0x00;
+ cmd.args[0x11] = 0xFA;
+ cmd.args[0x12] = 0x24;
+ cx24116_writereg(state, CX24116_REG_CLKDIV, 0x46);
+ cx24116_writereg(state, CX24116_REG_RATEDIV, 0x00);
+ }
+
+ cmd.len= 0x13;
+
+ /* We need to support pilot and non-pilot tuning in the
+ * driver automatically. This is a workaround for because
+ * the demod does not support autodetect.
+ */
+ do {
+ /* Reset status register */
+ status = cx24116_readreg(state, CX24116_REG_SSTATUS) & CX24116_SIGNAL_MASK;
+ cx24116_writereg(state, CX24116_REG_SSTATUS, status);
+
+ /* Tune */
+ ret = cx24116_cmd_execute(fe, &cmd);
+ if( ret != 0 )
+ break;
+
+ /*
+ * Wait for up to 500 ms before retrying
+ *
+ * If we are able to tune then generally it occurs within 100ms.
+ * If it takes longer, try a different toneburst setting.
+ */
+ for(i = 0; i < 50 ; i++) {
+ cx24116_read_status(fe, &tunerstat);
+ status = tunerstat & (FE_HAS_SIGNAL | FE_HAS_SYNC);
+ if(status == (FE_HAS_SIGNAL | FE_HAS_SYNC)) {
+ dprintk("%s: Tuned\n",__func__);
+ goto tuned;
+ }
+ msleep(10);
+ }
+
+ dprintk("%s: Not tuned\n",__func__);
+
+ /* Toggle pilot bit when in auto-pilot */
+ if(state->dcur.pilot == PILOT_AUTO)
+ cmd.args[0x07] ^= CX24116_PILOT;
+ }
+ while(--retune);
+
+tuned: /* Set/Reset B/W */
+ cmd.args[0x00] = CMD_BANDWIDTH;
+ cmd.args[0x01] = 0x00;
+ cmd.len= 0x02;
+ ret = cx24116_cmd_execute(fe, &cmd);
+ if (ret != 0)
+ return ret;
+
+ return ret;
+}
+
+static struct dvb_frontend_ops cx24116_ops = {
+
+ .info = {
+ .name = "Conexant CX24116/CX24118",
+ .type = FE_QPSK,
+ .frequency_min = 950000,
+ .frequency_max = 2150000,
+ .frequency_stepsize = 1011, /* kHz for QPSK frontends */
+ .frequency_tolerance = 5000,
+ .symbol_rate_min = 1000000,
+ .symbol_rate_max = 45000000,
+ .caps = FE_CAN_INVERSION_AUTO |
+ FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+ FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 |
+ FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
+ FE_CAN_QPSK | FE_CAN_RECOVER
+ },
+
+ .release = cx24116_release,
+
+ .init = cx24116_initfe,
+ .sleep = cx24116_sleep,
+ .read_status = cx24116_read_status,
+ .read_ber = cx24116_read_ber,
+ .read_signal_strength = cx24116_read_signal_strength,
+ .read_snr = cx24116_read_snr,
+ .read_ucblocks = cx24116_read_ucblocks,
+ .set_tone = cx24116_set_tone,
+ .set_voltage = cx24116_set_voltage,
+ .diseqc_send_master_cmd = cx24116_send_diseqc_msg,
+ .diseqc_send_burst = cx24116_diseqc_send_burst,
+
+ .set_property = cx24116_set_property,
+ .get_property = cx24116_get_property,
+ .set_frontend = cx24116_set_frontend,
+};
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)");
+
+module_param(toneburst, int, 0644);
+MODULE_PARM_DESC(toneburst, "DiSEqC toneburst 0=OFF, 1=TONE CACHE, 2=MESSAGE CACHE (default:1)");
+
+MODULE_DESCRIPTION("DVB Frontend module for Conexant cx24116/cx24118 hardware");
+MODULE_AUTHOR("Steven Toth");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(cx24116_attach);
diff --git a/linux/drivers/media/dvb/frontends/cx24116.h b/linux/drivers/media/dvb/frontends/cx24116.h
new file mode 100644
index 000000000..8dbcec268
--- /dev/null
+++ b/linux/drivers/media/dvb/frontends/cx24116.h
@@ -0,0 +1,53 @@
+/*
+ Conexant cx24116/cx24118 - DVBS/S2 Satellite demod/tuner driver
+
+ Copyright (C) 2006 Steven Toth <stoth@linuxtv.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
+ (at your option) 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef CX24116_H
+#define CX24116_H
+
+#include <linux/dvb/frontend.h>
+
+struct cx24116_config
+{
+ /* the demodulator's i2c address */
+ u8 demod_address;
+
+ /* Need to set device param for start_dma */
+ int (*set_ts_params)(struct dvb_frontend* fe, int is_punctured);
+
+ /* Need to reset device during firmware loading */
+ int (*reset_device)(struct dvb_frontend* fe);
+
+ /* Need to set MPEG parameters */
+ u8 mpg_clk_pos_pol:0x02;
+};
+
+#if defined(CONFIG_DVB_CX24116) || defined(CONFIG_DVB_CX24116_MODULE)
+extern struct dvb_frontend* cx24116_attach(const struct cx24116_config* config,
+ struct i2c_adapter* i2c);
+#else
+static inline struct dvb_frontend* cx24116_attach(const struct cx24116_config* config,
+ struct i2c_adapter* i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_CX24116
+
+#endif /* CX24116_H */
diff --git a/linux/drivers/media/dvb/frontends/s5h1420.c b/linux/drivers/media/dvb/frontends/s5h1420.c
index b768da972..46d55e393 100644
--- a/linux/drivers/media/dvb/frontends/s5h1420.c
+++ b/linux/drivers/media/dvb/frontends/s5h1420.c
@@ -59,7 +59,7 @@ struct s5h1420_state {
* it does not support repeated-start, workaround: write addr-1
* and then read
*/
- u8 shadow[255];
+ u8 shadow[256];
};
static u32 s5h1420_getsymbolrate(struct s5h1420_state* state);
@@ -94,8 +94,11 @@ static u8 s5h1420_readreg(struct s5h1420_state *state, u8 reg)
if (ret != 3)
return ret;
} else {
- ret = i2c_transfer(state->i2c, &msg[1], 2);
- if (ret != 2)
+ ret = i2c_transfer(state->i2c, &msg[1], 1);
+ if (ret != 1)
+ return ret;
+ ret = i2c_transfer(state->i2c, &msg[2], 1);
+ if (ret != 1)
return ret;
}
@@ -854,7 +857,7 @@ static int s5h1420_init (struct dvb_frontend* fe)
struct s5h1420_state* state = fe->demodulator_priv;
/* disable power down and do reset */
- state->CON_1_val = 0x10;
+ state->CON_1_val = state->config->serial_mpeg << 4;
s5h1420_writereg(state, 0x02, state->CON_1_val);
msleep(10);
s5h1420_reset(state);
diff --git a/linux/drivers/media/dvb/frontends/s5h1420.h b/linux/drivers/media/dvb/frontends/s5h1420.h
index 4c913f142..ff308136d 100644
--- a/linux/drivers/media/dvb/frontends/s5h1420.h
+++ b/linux/drivers/media/dvb/frontends/s5h1420.h
@@ -32,10 +32,12 @@ struct s5h1420_config
u8 demod_address;
/* does the inversion require inversion? */
- u8 invert : 1;
+ u8 invert:1;
- u8 repeated_start_workaround : 1;
- u8 cdclk_polarity : 1; /* 1 == falling edge, 0 == raising edge */
+ u8 repeated_start_workaround:1;
+ u8 cdclk_polarity:1; /* 1 == falling edge, 0 == raising edge */
+
+ u8 serial_mpeg:1;
};
#if defined(CONFIG_DVB_S5H1420) || (defined(CONFIG_DVB_S5H1420_MODULE) && defined(MODULE))
diff --git a/linux/drivers/media/dvb/frontends/si21xx.c b/linux/drivers/media/dvb/frontends/si21xx.c
new file mode 100644
index 000000000..2f69a173d
--- /dev/null
+++ b/linux/drivers/media/dvb/frontends/si21xx.c
@@ -0,0 +1,1049 @@
+/* DVB compliant Linux driver for the DVB-S si2109/2110 demodulator
+*
+* Copyright (C) 2008 Igor M. Liplianin (liplianin@me.by)
+*
+* 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
+* (at your option) any later version.
+*
+*/
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <asm/div64.h>
+
+#include "dvb_frontend.h"
+#include "si21xx.h"
+
+#define REVISION_REG 0x00
+#define SYSTEM_MODE_REG 0x01
+#define TS_CTRL_REG_1 0x02
+#define TS_CTRL_REG_2 0x03
+#define PIN_CTRL_REG_1 0x04
+#define PIN_CTRL_REG_2 0x05
+#define LOCK_STATUS_REG_1 0x0f
+#define LOCK_STATUS_REG_2 0x10
+#define ACQ_STATUS_REG 0x11
+#define ACQ_CTRL_REG_1 0x13
+#define ACQ_CTRL_REG_2 0x14
+#define PLL_DIVISOR_REG 0x15
+#define COARSE_TUNE_REG 0x16
+#define FINE_TUNE_REG_L 0x17
+#define FINE_TUNE_REG_H 0x18
+
+#define ANALOG_AGC_POWER_LEVEL_REG 0x28
+#define CFO_ESTIMATOR_CTRL_REG_1 0x29
+#define CFO_ESTIMATOR_CTRL_REG_2 0x2a
+#define CFO_ESTIMATOR_CTRL_REG_3 0x2b
+
+#define SYM_RATE_ESTIMATE_REG_L 0x31
+#define SYM_RATE_ESTIMATE_REG_M 0x32
+#define SYM_RATE_ESTIMATE_REG_H 0x33
+
+#define CFO_ESTIMATOR_OFFSET_REG_L 0x36
+#define CFO_ESTIMATOR_OFFSET_REG_H 0x37
+#define CFO_ERROR_REG_L 0x38
+#define CFO_ERROR_REG_H 0x39
+#define SYM_RATE_ESTIMATOR_CTRL_REG 0x3a
+
+#define SYM_RATE_REG_L 0x3f
+#define SYM_RATE_REG_M 0x40
+#define SYM_RATE_REG_H 0x41
+#define SYM_RATE_ESTIMATOR_MAXIMUM_REG 0x42
+#define SYM_RATE_ESTIMATOR_MINIMUM_REG 0x43
+
+#define C_N_ESTIMATOR_CTRL_REG 0x7c
+#define C_N_ESTIMATOR_THRSHLD_REG 0x7d
+#define C_N_ESTIMATOR_LEVEL_REG_L 0x7e
+#define C_N_ESTIMATOR_LEVEL_REG_H 0x7f
+
+#define BLIND_SCAN_CTRL_REG 0x80
+
+#define LSA_CTRL_REG_1 0x8D
+#define SPCTRM_TILT_CORR_THRSHLD_REG 0x8f
+#define ONE_DB_BNDWDTH_THRSHLD_REG 0x90
+#define TWO_DB_BNDWDTH_THRSHLD_REG 0x91
+#define THREE_DB_BNDWDTH_THRSHLD_REG 0x92
+#define INBAND_POWER_THRSHLD_REG 0x93
+#define REF_NOISE_LVL_MRGN_THRSHLD_REG 0x94
+
+#define VIT_SRCH_CTRL_REG_1 0xa0
+#define VIT_SRCH_CTRL_REG_2 0xa1
+#define VIT_SRCH_CTRL_REG_3 0xa2
+#define VIT_SRCH_STATUS_REG 0xa3
+#define VITERBI_BER_COUNT_REG_L 0xab
+#define REED_SOLOMON_CTRL_REG 0xb0
+#define REED_SOLOMON_ERROR_COUNT_REG_L 0xb1
+#define PRBS_CTRL_REG 0xb5
+
+#define LNB_CTRL_REG_1 0xc0
+#define LNB_CTRL_REG_2 0xc1
+#define LNB_CTRL_REG_3 0xc2
+#define LNB_CTRL_REG_4 0xc3
+#define LNB_CTRL_STATUS_REG 0xc4
+#define LNB_FIFO_REGS_0 0xc5
+#define LNB_FIFO_REGS_1 0xc6
+#define LNB_FIFO_REGS_2 0xc7
+#define LNB_FIFO_REGS_3 0xc8
+#define LNB_FIFO_REGS_4 0xc9
+#define LNB_FIFO_REGS_5 0xca
+#define LNB_SUPPLY_CTRL_REG_1 0xcb
+#define LNB_SUPPLY_CTRL_REG_2 0xcc
+#define LNB_SUPPLY_CTRL_REG_3 0xcd
+#define LNB_SUPPLY_CTRL_REG_4 0xce
+#define LNB_SUPPLY_STATUS_REG 0xcf
+
+#define FALSE 0
+#define TRUE 1
+#define FAIL -1
+#define PASS 0
+
+#define ALLOWABLE_FS_COUNT 10
+#define STATUS_BER 0
+#define STATUS_UCBLOCKS 1
+
+static int debug;
+#define dprintk(args...) \
+ do { \
+ if (debug) \
+ printk(KERN_DEBUG "si21xx: " args); \
+ } while (0)
+
+enum {
+ ACTIVE_HIGH,
+ ACTIVE_LOW
+};
+enum {
+ BYTE_WIDE,
+ BIT_WIDE
+};
+enum {
+ CLK_GAPPED_MODE,
+ CLK_CONTINUOUS_MODE
+};
+enum {
+ RISING_EDGE,
+ FALLING_EDGE
+};
+enum {
+ MSB_FIRST,
+ LSB_FIRST
+};
+enum {
+ SERIAL,
+ PARALLEL
+};
+
+struct si21xx_state {
+ struct i2c_adapter *i2c;
+ const struct si21xx_config *config;
+ struct dvb_frontend frontend;
+ u8 initialised:1;
+ int errmode;
+ int fs; /*Sampling rate of the ADC in MHz*/
+};
+
+/* register default initialization */
+static u8 serit_sp1511lhb_inittab[] = {
+ 0x01, 0x28, /* set i2c_inc_disable */
+ 0x20, 0x03,
+ 0x27, 0x20,
+ 0xe0, 0x45,
+ 0xe1, 0x08,
+ 0xfe, 0x01,
+ 0x01, 0x28,
+ 0x89, 0x09,
+ 0x04, 0x80,
+ 0x05, 0x01,
+ 0x06, 0x00,
+ 0x20, 0x03,
+ 0x24, 0x88,
+ 0x29, 0x09,
+ 0x2a, 0x0f,
+ 0x2c, 0x10,
+ 0x2d, 0x19,
+ 0x2e, 0x08,
+ 0x2f, 0x10,
+ 0x30, 0x19,
+ 0x34, 0x20,
+ 0x35, 0x03,
+ 0x45, 0x02,
+ 0x46, 0x45,
+ 0x47, 0xd0,
+ 0x48, 0x00,
+ 0x49, 0x40,
+ 0x4a, 0x03,
+ 0x4c, 0xfd,
+ 0x4f, 0x2e,
+ 0x50, 0x2e,
+ 0x51, 0x10,
+ 0x52, 0x10,
+ 0x56, 0x92,
+ 0x59, 0x00,
+ 0x5a, 0x2d,
+ 0x5b, 0x33,
+ 0x5c, 0x1f,
+ 0x5f, 0x76,
+ 0x62, 0xc0,
+ 0x63, 0xc0,
+ 0x64, 0xf3,
+ 0x65, 0xf3,
+ 0x79, 0x40,
+ 0x6a, 0x40,
+ 0x6b, 0x0a,
+ 0x6c, 0x80,
+ 0x6d, 0x27,
+ 0x71, 0x06,
+ 0x75, 0x60,
+ 0x78, 0x00,
+ 0x79, 0xb5,
+ 0x7c, 0x05,
+ 0x7d, 0x1a,
+ 0x87, 0x55,
+ 0x88, 0x72,
+ 0x8f, 0x08,
+ 0x90, 0xe0,
+ 0x94, 0x40,
+ 0xa0, 0x3f,
+ 0xa1, 0xc0,
+ 0xa4, 0xcc,
+ 0xa5, 0x66,
+ 0xa6, 0x66,
+ 0xa7, 0x7b,
+ 0xa8, 0x7b,
+ 0xa9, 0x7b,
+ 0xaa, 0x9a,
+ 0xed, 0x04,
+ 0xad, 0x00,
+ 0xae, 0x03,
+ 0xcc, 0xab,
+ 0x01, 0x08,
+ 0xff, 0xff
+};
+
+/* low level read/writes */
+static int si21_writeregs(struct si21xx_state *state, u8 reg1,
+ u8 *data, int len)
+{
+ int ret;
+ u8 buf[60];/* = { reg1, data };*/
+ struct i2c_msg msg = {
+ .addr = state->config->demod_address,
+ .flags = 0,
+ .buf = buf,
+ .len = len + 1
+ };
+
+ msg.buf[0] = reg1;
+ memcpy(msg.buf + 1, data, len);
+
+ ret = i2c_transfer(state->i2c, &msg, 1);
+
+ if (ret != 1)
+ dprintk("%s: writereg error (reg1 == 0x%02x, data == 0x%02x, "
+ "ret == %i)\n", __func__, reg1, data[0], ret);
+
+ return (ret != 1) ? -EREMOTEIO : 0;
+}
+
+static int si21_writereg(struct si21xx_state *state, u8 reg, u8 data)
+{
+ int ret;
+ u8 buf[] = { reg, data };
+ struct i2c_msg msg = {
+ .addr = state->config->demod_address,
+ .flags = 0,
+ .buf = buf,
+ .len = 2
+ };
+
+ ret = i2c_transfer(state->i2c, &msg, 1);
+
+ if (ret != 1)
+ dprintk("%s: writereg error (reg == 0x%02x, data == 0x%02x, "
+ "ret == %i)\n", __func__, reg, data, ret);
+
+ return (ret != 1) ? -EREMOTEIO : 0;
+}
+
+static int si21_write(struct dvb_frontend *fe, u8 *buf, int len)
+{
+ struct si21xx_state *state = fe->demodulator_priv;
+
+ if (len != 2)
+ return -EINVAL;
+
+ return si21_writereg(state, buf[0], buf[1]);
+}
+
+static u8 si21_readreg(struct si21xx_state *state, u8 reg)
+{
+ int ret;
+ u8 b0[] = { reg };
+ u8 b1[] = { 0 };
+ struct i2c_msg msg[] = {
+ {
+ .addr = state->config->demod_address,
+ .flags = 0,
+ .buf = b0,
+ .len = 1
+ }, {
+ .addr = state->config->demod_address,
+ .flags = I2C_M_RD,
+ .buf = b1,
+ .len = 1
+ }
+ };
+
+ ret = i2c_transfer(state->i2c, msg, 2);
+
+ if (ret != 2)
+ dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n",
+ __func__, reg, ret);
+
+ return b1[0];
+}
+
+static int si21_readregs(struct si21xx_state *state, u8 reg1, u8 *b, u8 len)
+{
+ int ret;
+ struct i2c_msg msg[] = {
+ {
+ .addr = state->config->demod_address,
+ .flags = 0,
+ .buf = &reg1,
+ .len = 1
+ }, {
+ .addr = state->config->demod_address,
+ .flags = I2C_M_RD,
+ .buf = b,
+ .len = len
+ }
+ };
+
+ ret = i2c_transfer(state->i2c, msg, 2);
+
+ if (ret != 2)
+ dprintk("%s: readreg error (ret == %i)\n", __func__, ret);
+
+ return ret == 2 ? 0 : -1;
+}
+#if 0
+static int si21xx_wait_diseqc_fifo(struct si21xx_state *state, int timeout)
+{
+ unsigned long start = jiffies;
+
+ dprintk("%s\n", __func__);
+
+ while (((si21_readreg(state, 0xc4) >> 7) & 1) == 0) {
+ if (jiffies - start > timeout) {
+ dprintk("%s: timeout!!\n", __func__);
+ return -ETIMEDOUT;
+ }
+ msleep(10);
+ };
+
+ return 0;
+}
+#endif
+
+static int si21xx_wait_diseqc_idle(struct si21xx_state *state, int timeout)
+{
+ unsigned long start = jiffies;
+
+ dprintk("%s\n", __func__);
+
+ while ((si21_readreg(state, LNB_CTRL_REG_1) & 0x8) == 8) {
+ if (jiffies - start > timeout) {
+ dprintk("%s: timeout!!\n", __func__);
+ return -ETIMEDOUT;
+ }
+ msleep(10);
+ };
+
+ return 0;
+}
+
+static int si21xx_set_symbolrate(struct dvb_frontend *fe, u32 srate)
+{
+ struct si21xx_state *state = fe->demodulator_priv;
+ u32 sym_rate, data_rate;
+ int i;
+ u8 sym_rate_bytes[3];
+
+ dprintk("%s : srate = %i\n", __func__ , srate);
+
+ if ((srate < 1000000) || (srate > 45000000))
+ return -EINVAL;
+
+ data_rate = srate;
+ sym_rate = 0;
+
+ for (i = 0; i < 4; ++i) {
+ sym_rate /= 100;
+ sym_rate = sym_rate + ((data_rate % 100) * 0x800000) /
+ state->fs;
+ data_rate /= 100;
+ }
+ for (i = 0; i < 3; ++i)
+ sym_rate_bytes[i] = (u8)((sym_rate >> (i * 8)) & 0xff);
+
+ si21_writeregs(state, SYM_RATE_REG_L, sym_rate_bytes, 0x03);
+
+ return 0;
+}
+
+static int si21xx_send_diseqc_msg(struct dvb_frontend *fe,
+ struct dvb_diseqc_master_cmd *m)
+{
+ struct si21xx_state *state = fe->demodulator_priv;
+#if 0
+ u8 val;
+ int i;
+
+ if (si21xx_wait_diseqc_idle(state, 100) < 0)
+ return -ETIMEDOUT;
+
+ val = si21_readreg(state, 0x08);
+ /* DiSEqC mode */
+ if (si21_writereg(state, 0x08, (val & ~0x7) | 0x6))
+ return -EREMOTEIO;
+
+ for (i = 0; i < m->msg_len; i++) {
+ if (si21xx_wait_diseqc_fifo(state, 100) < 0)
+ return -ETIMEDOUT;
+
+ if (si21_writereg(state, 0x09, m->msg[i]))
+ return -EREMOTEIO;
+ }
+
+ if (si21xx_wait_diseqc_idle(state, 100) < 0)
+ return -ETIMEDOUT;
+
+ return 0;
+ }
+
+int si21xx_set_lnb_msg(state, lnb_cmd)
+{
+#endif
+ u8 lnb_status;
+ u8 LNB_CTRL_1;
+ int status;
+
+ dprintk("%s\n", __func__);
+
+ status = PASS;
+ LNB_CTRL_1 = 0;
+
+ status |= si21_readregs(state, LNB_CTRL_STATUS_REG, &lnb_status, 0x01);
+ status |= si21_readregs(state, LNB_CTRL_REG_1, &lnb_status, 0x01);
+
+ /*fill the FIFO*/
+ status |= si21_writeregs(state, LNB_FIFO_REGS_0, m->msg, m->msg_len);
+
+ LNB_CTRL_1 = (lnb_status & 0x70);
+#if 0
+ LNB_CTRL_1 |= voltage << 6; /*voltage select*/
+ LNB_CTRL_1 |= tone << 5; /*continuous tone selection*/
+ LNB_CTRL_1 |= burst << 4; /*tone burst selection*/
+ LNB_CTRL_1 |= mmsg << 3; /*more messages indicator*/
+#endif
+ LNB_CTRL_1 |= m->msg_len;
+
+ LNB_CTRL_1 |= 0x80; /* begin LNB signaling */
+
+ status |= si21_writeregs(state, LNB_CTRL_REG_1, &LNB_CTRL_1, 0x01);
+
+ return status;
+}
+
+static int si21xx_send_diseqc_burst(struct dvb_frontend *fe,
+ fe_sec_mini_cmd_t burst)
+{
+ struct si21xx_state *state = fe->demodulator_priv;
+ u8 val;
+
+ dprintk("%s\n", __func__);
+
+ if (si21xx_wait_diseqc_idle(state, 100) < 0)
+ return -ETIMEDOUT;
+
+ val = (0x80 | si21_readreg(state, 0xc1));
+#if 0
+ /* burst mode */
+ if (si21_writereg(state, LNB_CTRL_REG_1, (val & ~0x10))
+ return -EREMOTEIO;
+#endif
+ if (si21_writereg(state, LNB_CTRL_REG_1,
+ burst == SEC_MINI_A ? (val & ~0x10) : (val | 0x10)))
+ return -EREMOTEIO;
+
+ if (si21xx_wait_diseqc_idle(state, 100) < 0)
+ return -ETIMEDOUT;
+
+ if (si21_writereg(state, LNB_CTRL_REG_1, val))
+ return -EREMOTEIO;
+
+ return 0;
+}
+/* 30.06.2008 */
+static int si21xx_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone)
+{
+ struct si21xx_state *state = fe->demodulator_priv;
+ u8 val;
+
+ dprintk("%s\n", __func__);
+#if 0
+ if (si21xx_wait_diseqc_idle(state, 100) < 0)
+ return -ETIMEDOUT;
+#endif
+ val = (0x80 | si21_readreg(state, LNB_CTRL_REG_1));
+
+ switch (tone) {
+ case SEC_TONE_ON:
+ return si21_writereg(state, LNB_CTRL_REG_1, val | 0x20);
+
+ case SEC_TONE_OFF:
+ return si21_writereg(state, LNB_CTRL_REG_1, (val & ~0x20));
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int si21xx_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t volt)
+{
+ struct si21xx_state *state = fe->demodulator_priv;
+
+ u8 val;
+ dprintk("%s: %s\n", __func__,
+ volt == SEC_VOLTAGE_13 ? "SEC_VOLTAGE_13" :
+ volt == SEC_VOLTAGE_18 ? "SEC_VOLTAGE_18" : "??");
+
+
+ val = (0x80 | si21_readreg(state, LNB_CTRL_REG_1));
+
+ switch (volt) {
+ case SEC_VOLTAGE_18:
+ return si21_writereg(state, LNB_CTRL_REG_1, val | 0x40);
+ break;
+ case SEC_VOLTAGE_13:
+ return si21_writereg(state, LNB_CTRL_REG_1, (val & ~0x40));
+ break;
+ default:
+ return -EINVAL;
+ };
+}
+
+static int si21xx_init(struct dvb_frontend *fe)
+{
+ struct si21xx_state *state = fe->demodulator_priv;
+ int i;
+ int status = 0;
+ u8 reg1;
+ u8 val;
+ u8 reg2[2];
+
+ dprintk("%s\n", __func__);
+
+ for (i = 0; ; i += 2) {
+ reg1 = serit_sp1511lhb_inittab[i];
+ val = serit_sp1511lhb_inittab[i+1];
+ if (reg1 == 0xff && val == 0xff)
+ break;
+ si21_writeregs(state, reg1, &val, 1);
+ }
+
+ /*DVB QPSK SYSTEM MODE REG*/
+ reg1 = 0x08;
+ si21_writeregs(state, SYSTEM_MODE_REG, &reg1, 0x01);
+
+ /*transport stream config*/
+ /*
+ mode = PARALLEL;
+ sdata_form = LSB_FIRST;
+ clk_edge = FALLING_EDGE;
+ clk_mode = CLK_GAPPED_MODE;
+ strt_len = BYTE_WIDE;
+ sync_pol = ACTIVE_HIGH;
+ val_pol = ACTIVE_HIGH;
+ err_pol = ACTIVE_HIGH;
+ sclk_rate = 0x00;
+ parity = 0x00 ;
+ data_delay = 0x00;
+ clk_delay = 0x00;
+ pclk_smooth = 0x00;
+ */
+ reg2[0] =
+ PARALLEL + (LSB_FIRST << 1)
+ + (FALLING_EDGE << 2) + (CLK_GAPPED_MODE << 3)
+ + (BYTE_WIDE << 4) + (ACTIVE_HIGH << 5)
+ + (ACTIVE_HIGH << 6) + (ACTIVE_HIGH << 7);
+
+ reg2[1] = 0;
+ /* sclk_rate + (parity << 2)
+ + (data_delay << 3) + (clk_delay << 4)
+ + (pclk_smooth << 5);
+ */
+ status |= si21_writeregs(state, TS_CTRL_REG_1, reg2, 0x02);
+ if (status != 0)
+ dprintk(" %s : TS Set Error\n", __func__);
+
+#if 0
+ lnb_cmd.tone = ON; /* 22khz continuous */
+ lnb_cmd.mmsg = OFF; /* diseqc more message */
+ /* diseqc command */
+ lnb_cmd.msg[6] = { "0xE0", "0x10", "0x38", "0xF0" };
+ lnb_cmd.msg_len = OFF; /* diseqc command length */
+ lnb_cmd.burst = OFF; /* tone burst a,b */
+ lnb_cmd.volt = OFF; /* 13v 18v select */
+
+ status |= si21xx_set_lnb_msg(state, lnb_cmd);
+ if (status != PASS)
+ dprintk("%s LNB Set Error\n", __func__);
+#endif
+ return 0;
+
+}
+
+static int si21_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+ struct si21xx_state *state = fe->demodulator_priv;
+ u8 regs_read[2];
+ u8 reg_read;
+ u8 i;
+ u8 lock;
+ u8 signal = si21_readreg(state, ANALOG_AGC_POWER_LEVEL_REG);
+
+ si21_readregs(state, LOCK_STATUS_REG_1, regs_read, 0x02);
+ reg_read = 0;
+
+ for (i = 0; i < 7; ++i)
+ reg_read |= ((regs_read[0] >> i) & 0x01) << (6 - i);
+
+ lock = ((reg_read & 0x7f) | (regs_read[1] & 0x80));
+
+ dprintk("%s : FE_READ_STATUS : VSTATUS: 0x%02x\n", __func__, lock);
+ *status = 0;
+
+ if (signal > 10)
+ *status |= FE_HAS_SIGNAL;
+
+ if (lock & 0x2)
+ *status |= FE_HAS_CARRIER;
+
+ if (lock & 0x20)
+ *status |= FE_HAS_VITERBI;
+
+ if (lock & 0x40)
+ *status |= FE_HAS_SYNC;
+
+ if ((lock & 0x7b) == 0x7b)
+ *status |= FE_HAS_LOCK;
+
+ return 0;
+}
+
+static int si21_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
+{
+ struct si21xx_state *state = fe->demodulator_priv;
+
+ /*status = si21_readreg(state, ANALOG_AGC_POWER_LEVEL_REG,
+ (u8*)agclevel, 0x01);*/
+
+ u16 signal = (3 * si21_readreg(state, 0x27) *
+ si21_readreg(state, 0x28));
+
+ dprintk("%s : AGCPWR: 0x%02x%02x, signal=0x%04x\n", __func__,
+ si21_readreg(state, 0x27),
+ si21_readreg(state, 0x28), (int) signal);
+
+ signal <<= 4;
+ *strength = signal;
+
+ return 0;
+}
+
+static int si21_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ struct si21xx_state *state = fe->demodulator_priv;
+
+ dprintk("%s\n", __func__);
+
+ if (state->errmode != STATUS_BER)
+ return 0;
+
+ *ber = (si21_readreg(state, 0x1d) << 8) |
+ si21_readreg(state, 0x1e);
+
+ return 0;
+}
+
+static int si21_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct si21xx_state *state = fe->demodulator_priv;
+
+ s32 xsnr = 0xffff - ((si21_readreg(state, 0x24) << 8) |
+ si21_readreg(state, 0x25));
+ xsnr = 3 * (xsnr - 0xa100);
+ *snr = (xsnr > 0xffff) ? 0xffff : (xsnr < 0) ? 0 : xsnr;
+
+ dprintk("%s\n", __func__);
+
+ return 0;
+}
+
+static int si21_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ struct si21xx_state *state = fe->demodulator_priv;
+
+ dprintk("%s\n", __func__);
+
+ if (state->errmode != STATUS_UCBLOCKS)
+ *ucblocks = 0;
+ else
+ *ucblocks = (si21_readreg(state, 0x1d) << 8) |
+ si21_readreg(state, 0x1e);
+
+ return 0;
+}
+
+/* initiates a channel acquisition sequence
+ using the specified symbol rate and code rate */
+static int si21xx_setacquire(struct dvb_frontend *fe, int symbrate,
+ fe_code_rate_t crate)
+{
+
+ struct si21xx_state *state = fe->demodulator_priv;
+ u8 coderates[] = {
+ 0x0, 0x01, 0x02, 0x04, 0x00,
+ 0x8, 0x10, 0x20, 0x00, 0x3f
+ };
+
+ u8 coderate_ptr;
+ int status;
+ u8 start_acq = 0x80;
+ u8 reg, regs[3];
+
+ dprintk("%s\n", __func__);
+
+ status = PASS;
+ coderate_ptr = coderates[crate];
+
+ si21xx_set_symbolrate(fe, symbrate);
+
+ /* write code rates to use in the Viterbi search */
+ status |= si21_writeregs(state,
+ VIT_SRCH_CTRL_REG_1,
+ &coderate_ptr, 0x01);
+
+ /* clear acq_start bit */
+ status |= si21_readregs(state, ACQ_CTRL_REG_2, &reg, 0x01);
+ reg &= ~start_acq;
+ status |= si21_writeregs(state, ACQ_CTRL_REG_2, &reg, 0x01);
+
+ /* use new Carrier Frequency Offset Estimator (QuickLock) */
+ regs[0] = 0xCB;
+ regs[1] = 0x40;
+ regs[2] = 0xCB;
+
+ status |= si21_writeregs(state,
+ TWO_DB_BNDWDTH_THRSHLD_REG,
+ &regs[0], 0x03);
+ reg = 0x56;
+ status |= si21_writeregs(state,
+ LSA_CTRL_REG_1, &reg, 1);
+ reg = 0x05;
+ status |= si21_writeregs(state,
+ BLIND_SCAN_CTRL_REG, &reg, 1);
+ /* start automatic acq */
+ status |= si21_writeregs(state,
+ ACQ_CTRL_REG_2, &start_acq, 0x01);
+
+ return status;
+}
+
+static int si21xx_set_property(struct dvb_frontend *fe, struct dtv_property *p)
+{
+ dprintk("%s(..)\n", __func__);
+ return 0;
+}
+
+static int si21xx_get_property(struct dvb_frontend *fe, struct dtv_property *p)
+{
+ dprintk("%s(..)\n", __func__);
+ return 0;
+}
+
+static int si21xx_set_frontend(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *dfp)
+{
+ struct si21xx_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+
+ /* freq Channel carrier frequency in KHz (i.e. 1550000 KHz)
+ datarate Channel symbol rate in Sps (i.e. 22500000 Sps)*/
+
+ /* in MHz */
+ unsigned char coarse_tune_freq;
+ int fine_tune_freq;
+ unsigned char sample_rate = 0;
+ /* boolean */
+ unsigned int inband_interferer_ind;
+
+ /* INTERMEDIATE VALUES */
+ int icoarse_tune_freq; /* MHz */
+ int ifine_tune_freq; /* MHz */
+ unsigned int band_high;
+ unsigned int band_low;
+ unsigned int x1;
+ unsigned int x2;
+ int i;
+ unsigned int inband_interferer_div2[ALLOWABLE_FS_COUNT] = {
+ FALSE, FALSE, FALSE, FALSE, FALSE,
+ FALSE, FALSE, FALSE, FALSE, FALSE
+ };
+ unsigned int inband_interferer_div4[ALLOWABLE_FS_COUNT] = {
+ FALSE, FALSE, FALSE, FALSE, FALSE,
+ FALSE, FALSE, FALSE, FALSE, FALSE
+ };
+
+ int status;
+
+ /* allowable sample rates for ADC in MHz */
+ int afs[ALLOWABLE_FS_COUNT] = { 200, 192, 193, 194, 195,
+ 196, 204, 205, 206, 207
+ };
+ /* in MHz */
+ int if_limit_high;
+ int if_limit_low;
+ int lnb_lo;
+ int lnb_uncertanity;
+
+ int rf_freq;
+ int data_rate;
+ unsigned char regs[4];
+
+ dprintk("%s : FE_SET_FRONTEND\n", __func__);
+
+ if (c->delivery_system != SYS_DVBS) {
+ dprintk("%s: unsupported delivery system selected (%d)\n",
+ __func__, c->delivery_system);
+ return -EOPNOTSUPP;
+ }
+
+ for (i = 0; i < ALLOWABLE_FS_COUNT; ++i)
+ inband_interferer_div2[i] = inband_interferer_div4[i] = FALSE;
+
+ if_limit_high = -700000;
+ if_limit_low = -100000;
+ /* in MHz */
+ lnb_lo = 0;
+ lnb_uncertanity = 0;
+
+ rf_freq = 10 * c->frequency ;
+ data_rate = c->symbol_rate / 100;
+
+ status = PASS;
+
+ band_low = (rf_freq - lnb_lo) - ((lnb_uncertanity * 200)
+ + (data_rate * 135)) / 200;
+
+ band_high = (rf_freq - lnb_lo) + ((lnb_uncertanity * 200)
+ + (data_rate * 135)) / 200;
+
+
+ icoarse_tune_freq = 100000 *
+ (((rf_freq - lnb_lo) -
+ (if_limit_low + if_limit_high) / 2)
+ / 100000);
+
+ ifine_tune_freq = (rf_freq - lnb_lo) - icoarse_tune_freq ;
+
+ for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) {
+ x1 = ((rf_freq - lnb_lo) / (afs[i] * 2500)) *
+ (afs[i] * 2500) + afs[i] * 2500;
+
+ x2 = ((rf_freq - lnb_lo) / (afs[i] * 2500)) *
+ (afs[i] * 2500);
+
+ if (((band_low < x1) && (x1 < band_high)) ||
+ ((band_low < x2) && (x2 < band_high)))
+ inband_interferer_div4[i] = TRUE;
+
+ }
+
+ for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) {
+ x1 = ((rf_freq - lnb_lo) / (afs[i] * 5000)) *
+ (afs[i] * 5000) + afs[i] * 5000;
+
+ x2 = ((rf_freq - lnb_lo) / (afs[i] * 5000)) *
+ (afs[i] * 5000);
+
+ if (((band_low < x1) && (x1 < band_high)) ||
+ ((band_low < x2) && (x2 < band_high)))
+ inband_interferer_div2[i] = TRUE;
+ }
+
+ inband_interferer_ind = TRUE;
+ for (i = 0; i < ALLOWABLE_FS_COUNT; ++i)
+ inband_interferer_ind &= inband_interferer_div2[i] |
+ inband_interferer_div4[i];
+
+ if (inband_interferer_ind) {
+ for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) {
+ if (inband_interferer_div2[i] == FALSE) {
+ sample_rate = (u8) afs[i];
+ break;
+ }
+ }
+ } else {
+ for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) {
+ if ((inband_interferer_div2[i] |
+ inband_interferer_div4[i]) == FALSE) {
+ sample_rate = (u8) afs[i];
+ break;
+ }
+ }
+
+ }
+
+ if (sample_rate > 207 || sample_rate < 192)
+ sample_rate = 200;
+
+ fine_tune_freq = ((0x4000 * (ifine_tune_freq / 10)) /
+ ((sample_rate) * 1000));
+
+ coarse_tune_freq = (u8)(icoarse_tune_freq / 100000);
+
+ regs[0] = sample_rate;
+ regs[1] = coarse_tune_freq;
+ regs[2] = fine_tune_freq & 0xFF;
+ regs[3] = fine_tune_freq >> 8 & 0xFF;
+
+ status |= si21_writeregs(state, PLL_DIVISOR_REG, &regs[0], 0x04);
+
+ state->fs = sample_rate;/*ADC MHz*/
+ si21xx_setacquire(fe, c->symbol_rate, c->fec_inner);
+
+ return 0;
+}
+
+static int si21xx_sleep(struct dvb_frontend *fe)
+{
+ struct si21xx_state *state = fe->demodulator_priv;
+ u8 regdata;
+
+ dprintk("%s\n", __func__);
+
+ si21_readregs(state, SYSTEM_MODE_REG, &regdata, 0x01);
+ regdata |= 1 << 6;
+ si21_writeregs(state, SYSTEM_MODE_REG, &regdata, 0x01);
+ state->initialised = 0;
+
+ return 0;
+}
+
+static void si21xx_release(struct dvb_frontend *fe)
+{
+ struct si21xx_state *state = fe->demodulator_priv;
+
+ dprintk("%s\n", __func__);
+
+ kfree(state);
+}
+
+static struct dvb_frontend_ops si21xx_ops = {
+
+ .info = {
+ .name = "SL SI21XX DVB-S",
+ .type = FE_QPSK,
+ .frequency_min = 950000,
+ .frequency_max = 2150000,
+ .frequency_stepsize = 125, /* kHz for QPSK frontends */
+ .frequency_tolerance = 0,
+ .symbol_rate_min = 1000000,
+ .symbol_rate_max = 45000000,
+ .symbol_rate_tolerance = 500, /* ppm */
+ .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+ FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 |
+ FE_CAN_QPSK |
+ FE_CAN_FEC_AUTO
+ },
+
+ .release = si21xx_release,
+ .init = si21xx_init,
+ .sleep = si21xx_sleep,
+ .write = si21_write,
+ .read_status = si21_read_status,
+ .read_ber = si21_read_ber,
+ .read_signal_strength = si21_read_signal_strength,
+ .read_snr = si21_read_snr,
+ .read_ucblocks = si21_read_ucblocks,
+ .diseqc_send_master_cmd = si21xx_send_diseqc_msg,
+ .diseqc_send_burst = si21xx_send_diseqc_burst,
+ .set_tone = si21xx_set_tone,
+ .set_voltage = si21xx_set_voltage,
+
+ .set_property = si21xx_set_property,
+ .get_property = si21xx_get_property,
+ .set_frontend = si21xx_set_frontend,
+};
+
+struct dvb_frontend *si21xx_attach(const struct si21xx_config *config,
+ struct i2c_adapter *i2c)
+{
+ struct si21xx_state *state = NULL;
+ int id;
+
+ dprintk("%s\n", __func__);
+
+ /* allocate memory for the internal state */
+ state = kmalloc(sizeof(struct si21xx_state), GFP_KERNEL);
+ if (state == NULL)
+ goto error;
+
+ /* setup the state */
+ state->config = config;
+ state->i2c = i2c;
+ state->initialised = 0;
+ state->errmode = STATUS_BER;
+
+ /* check if the demod is there */
+ id = si21_readreg(state, SYSTEM_MODE_REG);
+ si21_writereg(state, SYSTEM_MODE_REG, id | 0x40); /* standby off */
+ msleep(200);
+ id = si21_readreg(state, 0x00);
+
+ /* register 0x00 contains:
+ 0x34 for SI2107
+ 0x24 for SI2108
+ 0x14 for SI2109
+ 0x04 for SI2110
+ */
+ if (id != 0x04 && id != 0x14)
+ goto error;
+
+ /* create dvb_frontend */
+ memcpy(&state->frontend.ops, &si21xx_ops,
+ sizeof(struct dvb_frontend_ops));
+ state->frontend.demodulator_priv = state;
+ return &state->frontend;
+
+error:
+ kfree(state);
+ return NULL;
+}
+EXPORT_SYMBOL(si21xx_attach);
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_DESCRIPTION("SL SI21XX DVB Demodulator driver");
+MODULE_AUTHOR("Igor M. Liplianin");
+MODULE_LICENSE("GPL");
diff --git a/linux/drivers/media/dvb/frontends/si21xx.h b/linux/drivers/media/dvb/frontends/si21xx.h
new file mode 100644
index 000000000..141b5b8a5
--- /dev/null
+++ b/linux/drivers/media/dvb/frontends/si21xx.h
@@ -0,0 +1,37 @@
+#ifndef SI21XX_H
+#define SI21XX_H
+
+#include <linux/dvb/frontend.h>
+#include "dvb_frontend.h"
+
+struct si21xx_config {
+ /* the demodulator's i2c address */
+ u8 demod_address;
+
+ /* minimum delay before retuning */
+ int min_delay_ms;
+};
+
+#if defined(CONFIG_DVB_SI21XX) || \
+ (defined(CONFIG_DVB_SI21XX_MODULE) && defined(MODULE))
+extern struct dvb_frontend *si21xx_attach(const struct si21xx_config *config,
+ struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *si21xx_attach(
+ const struct si21xx_config *config, struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif
+
+static inline int si21xx_writeregister(struct dvb_frontend *fe, u8 reg, u8 val)
+{
+ int r = 0;
+ u8 buf[] = {reg, val};
+ if (fe->ops.write)
+ r = fe->ops.write(fe, buf, 2);
+ return r;
+}
+
+#endif
diff --git a/linux/drivers/media/dvb/frontends/stb6000.c b/linux/drivers/media/dvb/frontends/stb6000.c
new file mode 100644
index 000000000..a0ece4278
--- /dev/null
+++ b/linux/drivers/media/dvb/frontends/stb6000.c
@@ -0,0 +1,255 @@
+ /*
+ Driver for ST STB6000 DVBS Silicon tuner
+
+ Copyright (C) 2008 Igor M. Liplianin (liplianin@me.by)
+
+ 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
+ (at your option) 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ */
+
+#include <linux/module.h>
+#include "compat.h"
+#include <linux/dvb/frontend.h>
+#include <asm/types.h>
+
+#include "stb6000.h"
+
+static int debug;
+#define dprintk(args...) \
+ do { \
+ if (debug) \
+ printk(KERN_DEBUG "stb6000: " args); \
+ } while (0)
+
+struct stb6000_priv {
+ /* i2c details */
+ int i2c_address;
+ struct i2c_adapter *i2c;
+ u32 frequency;
+};
+
+static int stb6000_release(struct dvb_frontend *fe)
+{
+ kfree(fe->tuner_priv);
+ fe->tuner_priv = NULL;
+ return 0;
+}
+
+static int stb6000_sleep(struct dvb_frontend *fe)
+{
+ struct stb6000_priv *priv = fe->tuner_priv;
+ int ret;
+ u8 buf[] = { 10, 0 };
+ struct i2c_msg msg = {
+ .addr = priv->i2c_address,
+ .flags = 0,
+ .buf = buf,
+ .len = 2
+ };
+
+ dprintk("%s:\n", __func__);
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+
+ ret = i2c_transfer(priv->i2c, &msg, 1);
+ if (ret != 1)
+ dprintk("%s: i2c error\n", __func__);
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ return (ret == 1) ? 0 : ret;
+}
+
+static int stb6000_set_params(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *params)
+{
+ struct stb6000_priv *priv = fe->tuner_priv;
+ unsigned int n, m;
+ int ret;
+ u32 freq_mhz;
+ int bandwidth;
+ u8 buf[12];
+ struct i2c_msg msg = {
+ .addr = priv->i2c_address,
+ .flags = 0,
+ .buf = buf,
+ .len = 12
+ };
+
+ dprintk("%s:\n", __func__);
+
+ freq_mhz = params->frequency / 1000;
+ bandwidth = params->u.qpsk.symbol_rate / 1000000;
+
+ if (bandwidth > 31)
+ bandwidth = 31;
+
+ if ((freq_mhz > 949) && (freq_mhz < 2151)) {
+ buf[0] = 0x01;
+ buf[1] = 0xac;
+ if (freq_mhz < 1950)
+ buf[1] = 0xaa;
+ if (freq_mhz < 1800)
+ buf[1] = 0xa8;
+ if (freq_mhz < 1650)
+ buf[1] = 0xa6;
+ if (freq_mhz < 1530)
+ buf[1] = 0xa5;
+ if (freq_mhz < 1470)
+ buf[1] = 0xa4;
+ if (freq_mhz < 1370)
+ buf[1] = 0xa2;
+ if (freq_mhz < 1300)
+ buf[1] = 0xa1;
+ if (freq_mhz < 1200)
+ buf[1] = 0xa0;
+ if (freq_mhz < 1075)
+ buf[1] = 0xbc;
+ if (freq_mhz < 1000)
+ buf[1] = 0xba;
+ if (freq_mhz < 1075) {
+ n = freq_mhz / 8; /* vco=lo*4 */
+ m = 2;
+ } else {
+ n = freq_mhz / 16; /* vco=lo*2 */
+ m = 1;
+ }
+ buf[2] = n >> 1;
+ buf[3] = (unsigned char)(((n & 1) << 7) |
+ (m * freq_mhz - n * 16) | 0x60);
+ buf[4] = 0x04;
+ buf[5] = 0x0e;
+
+ buf[6] = (unsigned char)(bandwidth);
+
+ buf[7] = 0xd8;
+ buf[8] = 0xd0;
+ buf[9] = 0x50;
+ buf[10] = 0xeb;
+ buf[11] = 0x4f;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+
+ ret = i2c_transfer(priv->i2c, &msg, 1);
+ if (ret != 1)
+ dprintk("%s: i2c error\n", __func__);
+
+ udelay(10);
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ buf[0] = 0x07;
+ buf[1] = 0xdf;
+ buf[2] = 0xd0;
+ buf[3] = 0x50;
+ buf[4] = 0xfb;
+ msg.len = 5;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+
+ ret = i2c_transfer(priv->i2c, &msg, 1);
+ if (ret != 1)
+ dprintk("%s: i2c error\n", __func__);
+
+ udelay(10);
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ priv->frequency = freq_mhz * 1000;
+
+ return (ret == 1) ? 0 : ret;
+ }
+ return -1;
+}
+
+static int stb6000_get_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+ struct stb6000_priv *priv = fe->tuner_priv;
+ *frequency = priv->frequency;
+ return 0;
+}
+
+static struct dvb_tuner_ops stb6000_tuner_ops = {
+ .info = {
+ .name = "ST STB6000",
+ .frequency_min = 950000,
+ .frequency_max = 2150000
+ },
+ .release = stb6000_release,
+ .sleep = stb6000_sleep,
+ .set_params = stb6000_set_params,
+ .get_frequency = stb6000_get_frequency,
+};
+
+struct dvb_frontend *stb6000_attach(struct dvb_frontend *fe, int addr,
+ struct i2c_adapter *i2c)
+{
+ struct stb6000_priv *priv = NULL;
+ u8 b1[] = { 0, 0 };
+ struct i2c_msg msg[2] = {
+ {
+ .addr = addr,
+ .flags = 0,
+ .buf = NULL,
+ .len = 0
+ }, {
+ .addr = addr,
+ .flags = I2C_M_RD,
+ .buf = b1,
+ .len = 2
+ }
+ };
+ int ret;
+
+ dprintk("%s:\n", __func__);
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+
+ /* is some i2c device here ? */
+ ret = i2c_transfer(i2c, msg, 2);
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ if (ret != 2)
+ return NULL;
+
+ priv = kzalloc(sizeof(struct stb6000_priv), GFP_KERNEL);
+ if (priv == NULL)
+ return NULL;
+
+ priv->i2c_address = addr;
+ priv->i2c = i2c;
+
+ memcpy(&fe->ops.tuner_ops, &stb6000_tuner_ops,
+ sizeof(struct dvb_tuner_ops));
+
+ fe->tuner_priv = priv;
+
+ return fe;
+}
+EXPORT_SYMBOL(stb6000_attach);
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_DESCRIPTION("DVB STB6000 driver");
+MODULE_AUTHOR("Igor M. Liplianin <liplianin@me.by>");
+MODULE_LICENSE("GPL");
diff --git a/linux/drivers/media/dvb/frontends/stb6000.h b/linux/drivers/media/dvb/frontends/stb6000.h
new file mode 100644
index 000000000..7be479c22
--- /dev/null
+++ b/linux/drivers/media/dvb/frontends/stb6000.h
@@ -0,0 +1,51 @@
+ /*
+ Driver for ST stb6000 DVBS Silicon tuner
+
+ Copyright (C) 2008 Igor M. Liplianin (liplianin@me.by)
+
+ 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
+ (at your option) 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ */
+
+#ifndef __DVB_STB6000_H__
+#define __DVB_STB6000_H__
+
+#include <linux/i2c.h>
+#include "dvb_frontend.h"
+
+/**
+ * Attach a stb6000 tuner to the supplied frontend structure.
+ *
+ * @param fe Frontend to attach to.
+ * @param addr i2c address of the tuner.
+ * @param i2c i2c adapter to use.
+ * @return FE pointer on success, NULL on failure.
+ */
+#if defined(CONFIG_DVB_STB6000) || (defined(CONFIG_DVB_STB6000_MODULE) \
+ && defined(MODULE))
+extern struct dvb_frontend *stb6000_attach(struct dvb_frontend *fe, int addr,
+ struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *stb6000_attach(struct dvb_frontend *fe,
+ int addr,
+ struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif /* CONFIG_DVB_STB6000 */
+
+#endif /* __DVB_STB6000_H__ */
diff --git a/linux/drivers/media/dvb/frontends/stv0288.c b/linux/drivers/media/dvb/frontends/stv0288.c
new file mode 100644
index 000000000..90e72e771
--- /dev/null
+++ b/linux/drivers/media/dvb/frontends/stv0288.c
@@ -0,0 +1,606 @@
+/*
+ Driver for ST STV0288 demodulator
+ Copyright (C) 2006 Georg Acher, BayCom GmbH, acher (at) baycom (dot) de
+ for Reel Multimedia
+ Copyright (C) 2008 TurboSight.com, Bob Liu <bob@turbosight.com>
+ Copyright (C) 2008 Igor M. Liplianin <liplianin@me.by>
+ Removed stb6000 specific tuner code and revised some
+ procedures.
+
+ 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
+ (at your option) 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <asm/div64.h>
+
+#include "dvb_frontend.h"
+#include "stv0288.h"
+
+struct stv0288_state {
+ struct i2c_adapter *i2c;
+ const struct stv0288_config *config;
+ struct dvb_frontend frontend;
+
+ u8 initialised:1;
+ u32 tuner_frequency;
+ u32 symbol_rate;
+ fe_code_rate_t fec_inner;
+ int errmode;
+};
+
+#define STATUS_BER 0
+#define STATUS_UCBLOCKS 1
+
+static int debug;
+static int debug_legacy_dish_switch;
+#define dprintk(args...) \
+ do { \
+ if (debug) \
+ printk(KERN_DEBUG "stv0288: " args); \
+ } while (0)
+
+
+static int stv0288_writeregI(struct stv0288_state *state, u8 reg, u8 data)
+{
+ int ret;
+ u8 buf[] = { reg, data };
+ struct i2c_msg msg = {
+ .addr = state->config->demod_address,
+ .flags = 0,
+ .buf = buf,
+ .len = 2
+ };
+
+ ret = i2c_transfer(state->i2c, &msg, 1);
+
+ if (ret != 1)
+ dprintk("%s: writereg error (reg == 0x%02x, val == 0x%02x, "
+ "ret == %i)\n", __func__, reg, data, ret);
+
+ return (ret != 1) ? -EREMOTEIO : 0;
+}
+
+static int stv0288_write(struct dvb_frontend *fe, u8 *buf, int len)
+{
+ struct stv0288_state *state = fe->demodulator_priv;
+
+ if (len != 2)
+ return -EINVAL;
+
+ return stv0288_writeregI(state, buf[0], buf[1]);
+}
+
+static u8 stv0288_readreg(struct stv0288_state *state, u8 reg)
+{
+ int ret;
+ u8 b0[] = { reg };
+ u8 b1[] = { 0 };
+ struct i2c_msg msg[] = {
+ {
+ .addr = state->config->demod_address,
+ .flags = 0,
+ .buf = b0,
+ .len = 1
+ }, {
+ .addr = state->config->demod_address,
+ .flags = I2C_M_RD,
+ .buf = b1,
+ .len = 1
+ }
+ };
+
+ ret = i2c_transfer(state->i2c, msg, 2);
+
+ if (ret != 2)
+ dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n",
+ __func__, reg, ret);
+
+ return b1[0];
+}
+
+static int stv0288_set_symbolrate(struct dvb_frontend *fe, u32 srate)
+{
+ struct stv0288_state *state = fe->demodulator_priv;
+ unsigned int temp;
+ unsigned char b[3];
+
+ if ((srate < 1000000) || (srate > 45000000))
+ return -EINVAL;
+
+ temp = (unsigned int)srate / 1000;
+
+ temp = temp * 32768;
+ temp = temp / 25;
+ temp = temp / 125;
+ b[0] = (unsigned char)((temp >> 12) & 0xff);
+ b[1] = (unsigned char)((temp >> 4) & 0xff);
+ b[2] = (unsigned char)((temp << 4) & 0xf0);
+ stv0288_writeregI(state, 0x28, 0x80); /* SFRH */
+ stv0288_writeregI(state, 0x29, 0); /* SFRM */
+ stv0288_writeregI(state, 0x2a, 0); /* SFRL */
+
+ stv0288_writeregI(state, 0x28, b[0]);
+ stv0288_writeregI(state, 0x29, b[1]);
+ stv0288_writeregI(state, 0x2a, b[2]);
+ dprintk("stv0288: stv0288_set_symbolrate\n");
+
+ return 0;
+}
+
+static int stv0288_send_diseqc_msg(struct dvb_frontend *fe,
+ struct dvb_diseqc_master_cmd *m)
+{
+ struct stv0288_state *state = fe->demodulator_priv;
+
+ int i;
+
+ dprintk("%s\n", __func__);
+
+ stv0288_writeregI(state, 0x09, 0);
+ msleep(30);
+ stv0288_writeregI(state, 0x05, 0x16);
+
+ for (i = 0; i < m->msg_len; i++) {
+ if (stv0288_writeregI(state, 0x06, m->msg[i]))
+ return -EREMOTEIO;
+ msleep(12);
+ }
+
+ return 0;
+}
+
+static int stv0288_send_diseqc_burst(struct dvb_frontend *fe,
+ fe_sec_mini_cmd_t burst)
+{
+ struct stv0288_state *state = fe->demodulator_priv;
+
+ dprintk("%s\n", __func__);
+
+ if (stv0288_writeregI(state, 0x05, 0x16))/* burst mode */
+ return -EREMOTEIO;
+
+ if (stv0288_writeregI(state, 0x06, burst == SEC_MINI_A ? 0x00 : 0xff))
+ return -EREMOTEIO;
+
+ if (stv0288_writeregI(state, 0x06, 0x12))
+ return -EREMOTEIO;
+
+ return 0;
+}
+
+static int stv0288_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone)
+{
+ struct stv0288_state *state = fe->demodulator_priv;
+
+ switch (tone) {
+ case SEC_TONE_ON:
+ if (stv0288_writeregI(state, 0x05, 0x10))/* burst mode */
+ return -EREMOTEIO;
+ return stv0288_writeregI(state, 0x06, 0xff);
+
+ case SEC_TONE_OFF:
+ if (stv0288_writeregI(state, 0x05, 0x13))/* burst mode */
+ return -EREMOTEIO;
+ return stv0288_writeregI(state, 0x06, 0x00);
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static u8 stv0288_inittab[] = {
+ 0x01, 0x15,
+ 0x02, 0x20,
+ 0x09, 0x0,
+ 0x0a, 0x4,
+ 0x0b, 0x0,
+ 0x0c, 0x0,
+ 0x0d, 0x0,
+ 0x0e, 0xd4,
+ 0x0f, 0x30,
+ 0x11, 0x80,
+ 0x12, 0x03,
+ 0x13, 0x48,
+ 0x14, 0x84,
+ 0x15, 0x45,
+ 0x16, 0xb7,
+ 0x17, 0x9c,
+ 0x18, 0x0,
+ 0x19, 0xa6,
+ 0x1a, 0x88,
+ 0x1b, 0x8f,
+ 0x1c, 0xf0,
+ 0x20, 0x0b,
+ 0x21, 0x54,
+ 0x22, 0x0,
+ 0x23, 0x0,
+ 0x2b, 0xff,
+ 0x2c, 0xf7,
+ 0x30, 0x0,
+ 0x31, 0x1e,
+ 0x32, 0x14,
+ 0x33, 0x0f,
+ 0x34, 0x09,
+ 0x35, 0x0c,
+ 0x36, 0x05,
+ 0x37, 0x2f,
+ 0x38, 0x16,
+ 0x39, 0xbe,
+ 0x3a, 0x0,
+ 0x3b, 0x13,
+ 0x3c, 0x11,
+ 0x3d, 0x30,
+ 0x40, 0x63,
+ 0x41, 0x04,
+ 0x42, 0x60,
+ 0x43, 0x00,
+ 0x44, 0x00,
+ 0x45, 0x00,
+ 0x46, 0x00,
+ 0x47, 0x00,
+ 0x4a, 0x00,
+ 0x50, 0x10,
+ 0x51, 0x38,
+ 0x52, 0x21,
+ 0x58, 0x54,
+ 0x59, 0x86,
+ 0x5a, 0x0,
+ 0x5b, 0x9b,
+ 0x5c, 0x08,
+ 0x5d, 0x7f,
+ 0x5e, 0x0,
+ 0x5f, 0xff,
+ 0x70, 0x0,
+ 0x71, 0x0,
+ 0x72, 0x0,
+ 0x74, 0x0,
+ 0x75, 0x0,
+ 0x76, 0x0,
+ 0x81, 0x0,
+ 0x82, 0x3f,
+ 0x83, 0x3f,
+ 0x84, 0x0,
+ 0x85, 0x0,
+ 0x88, 0x0,
+ 0x89, 0x0,
+ 0x8a, 0x0,
+ 0x8b, 0x0,
+ 0x8c, 0x0,
+ 0x90, 0x0,
+ 0x91, 0x0,
+ 0x92, 0x0,
+ 0x93, 0x0,
+ 0x94, 0x1c,
+ 0x97, 0x0,
+ 0xa0, 0x48,
+ 0xa1, 0x0,
+ 0xb0, 0xb8,
+ 0xb1, 0x3a,
+ 0xb2, 0x10,
+ 0xb3, 0x82,
+ 0xb4, 0x80,
+ 0xb5, 0x82,
+ 0xb6, 0x82,
+ 0xb7, 0x82,
+ 0xb8, 0x20,
+ 0xb9, 0x0,
+ 0xf0, 0x0,
+ 0xf1, 0x0,
+ 0xf2, 0xc0,
+ 0x51, 0x36,
+ 0x52, 0x09,
+ 0x53, 0x94,
+ 0x54, 0x62,
+ 0x55, 0x29,
+ 0x56, 0x64,
+ 0x57, 0x2b,
+ 0xff, 0xff,
+};
+
+static int stv0288_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t volt)
+{
+ dprintk("%s: %s\n", __func__,
+ volt == SEC_VOLTAGE_13 ? "SEC_VOLTAGE_13" :
+ volt == SEC_VOLTAGE_18 ? "SEC_VOLTAGE_18" : "??");
+
+ return 0;
+}
+
+static int stv0288_init(struct dvb_frontend *fe)
+{
+ struct stv0288_state *state = fe->demodulator_priv;
+ int i;
+
+ dprintk("stv0288: init chip\n");
+ stv0288_writeregI(state, 0x41, 0x04);
+ msleep(50);
+
+ for (i = 0; !(stv0288_inittab[i] == 0xff &&
+ stv0288_inittab[i + 1] == 0xff); i += 2)
+ stv0288_writeregI(state, stv0288_inittab[i],
+ stv0288_inittab[i + 1]);
+
+ return 0;
+}
+
+static int stv0288_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+ struct stv0288_state *state = fe->demodulator_priv;
+
+ u8 sync = stv0288_readreg(state, 0x24);
+ if (sync == 255)
+ sync = 0;
+
+ dprintk("%s : FE_READ_STATUS : VSTATUS: 0x%02x\n", __func__, sync);
+
+ *status = 0;
+
+ if ((sync & 0x08) == 0x08) {
+ *status |= FE_HAS_LOCK;
+ dprintk("stv0288 has locked\n");
+ }
+
+ return 0;
+}
+
+static int stv0288_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ struct stv0288_state *state = fe->demodulator_priv;
+
+ if (state->errmode != STATUS_BER)
+ return 0;
+ *ber = (stv0288_readreg(state, 0x26) << 8) |
+ stv0288_readreg(state, 0x27);
+ dprintk("stv0288_read_ber %d\n", *ber);
+
+ return 0;
+}
+
+
+static int stv0288_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
+{
+ struct stv0288_state *state = fe->demodulator_priv;
+
+ s32 signal = 0xffff - ((stv0288_readreg(state, 0x10) << 8));
+
+
+ signal = signal * 5 / 4;
+ *strength = (signal > 0xffff) ? 0xffff : (signal < 0) ? 0 : signal;
+ dprintk("stv0288_read_signal_strength %d\n", *strength);
+
+ return 0;
+}
+static int stv0288_sleep(struct dvb_frontend *fe)
+{
+ struct stv0288_state *state = fe->demodulator_priv;
+
+ stv0288_writeregI(state, 0x41, 0x84);
+ state->initialised = 0;
+
+ return 0;
+}
+static int stv0288_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct stv0288_state *state = fe->demodulator_priv;
+
+ s32 xsnr = 0xffff - ((stv0288_readreg(state, 0x2d) << 8)
+ | stv0288_readreg(state, 0x2e));
+ xsnr = 3 * (xsnr - 0xa100);
+ *snr = (xsnr > 0xffff) ? 0xffff : (xsnr < 0) ? 0 : xsnr;
+ dprintk("stv0288_read_snr %d\n", *snr);
+
+ return 0;
+}
+
+static int stv0288_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ struct stv0288_state *state = fe->demodulator_priv;
+
+ if (state->errmode != STATUS_BER)
+ return 0;
+ *ucblocks = (stv0288_readreg(state, 0x26) << 8) |
+ stv0288_readreg(state, 0x27);
+ dprintk("stv0288_read_ber %d\n", *ucblocks);
+
+ return 0;
+}
+
+static int stv0288_set_property(struct dvb_frontend *fe, struct dtv_property *p)
+{
+ dprintk("%s(..)\n", __func__);
+ return 0;
+}
+
+static int stv0288_get_property(struct dvb_frontend *fe, struct dtv_property *p)
+{
+ dprintk("%s(..)\n", __func__);
+ return 0;
+}
+
+static int stv0288_set_frontend(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *dfp)
+{
+ struct stv0288_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+
+ char tm;
+ unsigned char tda[3];
+
+ dprintk("%s : FE_SET_FRONTEND\n", __func__);
+
+ if (c->delivery_system != SYS_DVBS) {
+ dprintk("%s: unsupported delivery "
+ "system selected (%d)\n",
+ __func__, c->delivery_system);
+ return -EOPNOTSUPP;
+ }
+
+ if (state->config->set_ts_params)
+ state->config->set_ts_params(fe, 0);
+
+ /* only frequency & symbol_rate are used for tuner*/
+ dfp->frequency = c->frequency;
+ dfp->u.qpsk.symbol_rate = c->symbol_rate;
+ if (fe->ops.tuner_ops.set_params) {
+ fe->ops.tuner_ops.set_params(fe, dfp);
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+ }
+
+ udelay(10);
+ stv0288_set_symbolrate(fe, c->symbol_rate);
+ /* Carrier lock control register */
+ stv0288_writeregI(state, 0x15, 0xc5);
+
+ tda[0] = 0x2b; /* CFRM */
+ tda[2] = 0x0; /* CFRL */
+ for (tm = -6; tm < 7;) {
+ /* Viterbi status */
+ if (stv0288_readreg(state, 0x24) & 0x80)
+ break;
+
+ tda[2] += 40;
+ if (tda[2] < 40)
+ tm++;
+ tda[1] = (unsigned char)tm;
+ stv0288_writeregI(state, 0x2b, tda[1]);
+ stv0288_writeregI(state, 0x2c, tda[2]);
+ udelay(30);
+ }
+
+ state->tuner_frequency = c->frequency;
+ state->fec_inner = FEC_AUTO;
+ state->symbol_rate = c->symbol_rate;
+
+ return 0;
+}
+
+static int stv0288_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+ struct stv0288_state *state = fe->demodulator_priv;
+
+ if (enable)
+ stv0288_writeregI(state, 0x01, 0xb5);
+ else
+ stv0288_writeregI(state, 0x01, 0x35);
+
+ udelay(1);
+
+ return 0;
+}
+
+static void stv0288_release(struct dvb_frontend *fe)
+{
+ struct stv0288_state *state = fe->demodulator_priv;
+ kfree(state);
+}
+
+static struct dvb_frontend_ops stv0288_ops = {
+
+ .info = {
+ .name = "ST STV0288 DVB-S",
+ .type = FE_QPSK,
+ .frequency_min = 950000,
+ .frequency_max = 2150000,
+ .frequency_stepsize = 1000, /* kHz for QPSK frontends */
+ .frequency_tolerance = 0,
+ .symbol_rate_min = 1000000,
+ .symbol_rate_max = 45000000,
+ .symbol_rate_tolerance = 500, /* ppm */
+ .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+ FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 |
+ FE_CAN_QPSK |
+ FE_CAN_FEC_AUTO
+ },
+
+ .release = stv0288_release,
+ .init = stv0288_init,
+ .sleep = stv0288_sleep,
+ .write = stv0288_write,
+ .i2c_gate_ctrl = stv0288_i2c_gate_ctrl,
+ .read_status = stv0288_read_status,
+ .read_ber = stv0288_read_ber,
+ .read_signal_strength = stv0288_read_signal_strength,
+ .read_snr = stv0288_read_snr,
+ .read_ucblocks = stv0288_read_ucblocks,
+ .diseqc_send_master_cmd = stv0288_send_diseqc_msg,
+ .diseqc_send_burst = stv0288_send_diseqc_burst,
+ .set_tone = stv0288_set_tone,
+ .set_voltage = stv0288_set_voltage,
+
+ .set_property = stv0288_set_property,
+ .get_property = stv0288_get_property,
+ .set_frontend = stv0288_set_frontend,
+};
+
+struct dvb_frontend *stv0288_attach(const struct stv0288_config *config,
+ struct i2c_adapter *i2c)
+{
+ struct stv0288_state *state = NULL;
+ int id;
+
+ /* allocate memory for the internal state */
+ state = kmalloc(sizeof(struct stv0288_state), GFP_KERNEL);
+ if (state == NULL)
+ goto error;
+
+ /* setup the state */
+ state->config = config;
+ state->i2c = i2c;
+ state->initialised = 0;
+ state->tuner_frequency = 0;
+ state->symbol_rate = 0;
+ state->fec_inner = 0;
+ state->errmode = STATUS_BER;
+
+ stv0288_writeregI(state, 0x41, 0x04);
+ msleep(200);
+ id = stv0288_readreg(state, 0x00);
+ dprintk("stv0288 id %x\n", id);
+
+ /* register 0x00 contains 0x11 for STV0288 */
+ if (id != 0x11)
+ goto error;
+
+ /* create dvb_frontend */
+ memcpy(&state->frontend.ops, &stv0288_ops,
+ sizeof(struct dvb_frontend_ops));
+ state->frontend.demodulator_priv = state;
+ return &state->frontend;
+
+error:
+ kfree(state);
+
+ return NULL;
+}
+EXPORT_SYMBOL(stv0288_attach);
+
+module_param(debug_legacy_dish_switch, int, 0444);
+MODULE_PARM_DESC(debug_legacy_dish_switch,
+ "Enable timing analysis for Dish Network legacy switches");
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_DESCRIPTION("ST STV0288 DVB Demodulator driver");
+MODULE_AUTHOR("Georg Acher, Bob Liu, Igor liplianin");
+MODULE_LICENSE("GPL");
+
diff --git a/linux/drivers/media/dvb/frontends/stv0288.h b/linux/drivers/media/dvb/frontends/stv0288.h
new file mode 100644
index 000000000..aa0cdd273
--- /dev/null
+++ b/linux/drivers/media/dvb/frontends/stv0288.h
@@ -0,0 +1,65 @@
+/*
+ Driver for ST STV0288 demodulator
+
+ Copyright (C) 2006 Georg Acher, BayCom GmbH, acher (at) baycom (dot) de
+ for Reel Multimedia
+ Copyright (C) 2008 TurboSight.com, <bob@turbosight.com>
+ Copyright (C) 2008 Igor M. Liplianin <liplianin@me.by>
+ Removed stb6000 specific tuner code and revised some
+ procedures.
+
+ 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
+ (at your option) 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef STV0288_H
+#define STV0288_H
+
+#include <linux/dvb/frontend.h>
+#include "dvb_frontend.h"
+
+struct stv0288_config {
+ /* the demodulator's i2c address */
+ u8 demod_address;
+
+ /* minimum delay before retuning */
+ int min_delay_ms;
+
+ int (*set_ts_params)(struct dvb_frontend *fe, int is_punctured);
+};
+
+#if defined(CONFIG_DVB_STV0288) || (defined(CONFIG_DVB_STV0288_MODULE) && \
+ defined(MODULE))
+extern struct dvb_frontend *stv0288_attach(const struct stv0288_config *config,
+ struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *stv0288_attach(const struct stv0288_config *config,
+ struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif /* CONFIG_DVB_STV0288 */
+
+static inline int stv0288_writereg(struct dvb_frontend *fe, u8 reg, u8 val)
+{
+ int r = 0;
+ u8 buf[] = { reg, val };
+ if (fe->ops.write)
+ r = fe->ops.write(fe, buf, 2);
+ return r;
+}
+
+#endif /* STV0288_H */
diff --git a/linux/drivers/media/dvb/frontends/stv0299.c b/linux/drivers/media/dvb/frontends/stv0299.c
index 35435bef8..6c1cb1973 100644
--- a/linux/drivers/media/dvb/frontends/stv0299.c
+++ b/linux/drivers/media/dvb/frontends/stv0299.c
@@ -559,6 +559,8 @@ static int stv0299_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_par
int invval = 0;
dprintk ("%s : FE_SET_FRONTEND\n", __func__);
+ if (state->config->set_ts_params)
+ state->config->set_ts_params(fe, 0);
// set the inversion
if (p->inversion == INVERSION_OFF) invval = 0;
diff --git a/linux/drivers/media/dvb/frontends/stv0299.h b/linux/drivers/media/dvb/frontends/stv0299.h
index 3282f4302..0fd96e22b 100644
--- a/linux/drivers/media/dvb/frontends/stv0299.h
+++ b/linux/drivers/media/dvb/frontends/stv0299.h
@@ -89,15 +89,18 @@ struct stv0299_config
int min_delay_ms;
/* Set the symbol rate */
- int (*set_symbol_rate)(struct dvb_frontend* fe, u32 srate, u32 ratio);
+ int (*set_symbol_rate)(struct dvb_frontend *fe, u32 srate, u32 ratio);
+
+ /* Set device param to start dma */
+ int (*set_ts_params)(struct dvb_frontend *fe, int is_punctured);
};
#if defined(CONFIG_DVB_STV0299) || (defined(CONFIG_DVB_STV0299_MODULE) && defined(MODULE))
-extern struct dvb_frontend* stv0299_attach(const struct stv0299_config* config,
- struct i2c_adapter* i2c);
+extern struct dvb_frontend *stv0299_attach(const struct stv0299_config *config,
+ struct i2c_adapter *i2c);
#else
-static inline struct dvb_frontend* stv0299_attach(const struct stv0299_config* config,
- struct i2c_adapter* i2c)
+static inline struct dvb_frontend *stv0299_attach(const struct stv0299_config *config,
+ struct i2c_adapter *i2c)
{
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
return NULL;
diff --git a/linux/drivers/media/dvb/siano/sms-cards.c b/linux/drivers/media/dvb/siano/sms-cards.c
index cc5efb643..9da260fe3 100644
--- a/linux/drivers/media/dvb/siano/sms-cards.c
+++ b/linux/drivers/media/dvb/siano/sms-cards.c
@@ -40,6 +40,8 @@ struct usb_device_id smsusb_id_table[] = {
.driver_info = SMS1XXX_BOARD_HAUPPAUGE_OKEMO_B },
{ USB_DEVICE(0x2040, 0x5500),
.driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
+ { USB_DEVICE(0x2040, 0x5510),
+ .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
{ USB_DEVICE(0x2040, 0x5580),
.driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
{ USB_DEVICE(0x2040, 0x5590),
@@ -87,7 +89,7 @@ static struct sms_board sms_boards[] = {
.fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-nova-b-dvbt-01.fw",
},
[SMS1XXX_BOARD_HAUPPAUGE_WINDHAM] = {
- .name = "Hauppauge WinTV-Nova-T-MiniStick",
+ .name = "Hauppauge WinTV MiniStick",
.type = SMS_NOVA_B0,
.fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-hcw-55xxx-dvbt-01.fw",
},
diff --git a/linux/drivers/media/dvb/ttpci/budget-av.c b/linux/drivers/media/dvb/ttpci/budget-av.c
index 839c94101..1032ea778 100644
--- a/linux/drivers/media/dvb/ttpci/budget-av.c
+++ b/linux/drivers/media/dvb/ttpci/budget-av.c
@@ -57,6 +57,8 @@
#define SLOTSTATUS_READY 8
#define SLOTSTATUS_OCCUPIED (SLOTSTATUS_PRESENT|SLOTSTATUS_RESET|SLOTSTATUS_READY)
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
struct budget_av {
struct budget budget;
struct video_device *vd;
@@ -1127,7 +1129,9 @@ static int budget_av_attach(struct saa7146_dev *dev, struct saa7146_pci_extensio
dev->ext_priv = budget_av;
- if ((err = ttpci_budget_init(&budget_av->budget, dev, info, THIS_MODULE))) {
+ err = ttpci_budget_init(&budget_av->budget, dev, info, THIS_MODULE,
+ adapter_nr);
+ if (err) {
kfree(budget_av);
return err;
}
diff --git a/linux/drivers/media/dvb/ttpci/budget-ci.c b/linux/drivers/media/dvb/ttpci/budget-ci.c
index 0ccc9db6e..969877f58 100644
--- a/linux/drivers/media/dvb/ttpci/budget-ci.c
+++ b/linux/drivers/media/dvb/ttpci/budget-ci.c
@@ -96,6 +96,8 @@ static int ir_debug;
module_param(ir_debug, int, 0644);
MODULE_PARM_DESC(ir_debug, "enable debugging information for IR decoding");
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
struct budget_ci_ir {
struct input_dev *dev;
struct tasklet_struct msp430_irq_tasklet;
@@ -1191,7 +1193,8 @@ static int budget_ci_attach(struct saa7146_dev *dev, struct saa7146_pci_extensio
dev->ext_priv = budget_ci;
- err = ttpci_budget_init(&budget_ci->budget, dev, info, THIS_MODULE);
+ err = ttpci_budget_init(&budget_ci->budget, dev, info, THIS_MODULE,
+ adapter_nr);
if (err)
goto out2;
diff --git a/linux/drivers/media/dvb/ttpci/budget-core.c b/linux/drivers/media/dvb/ttpci/budget-core.c
index 6f4ddb643..ba18e56d5 100644
--- a/linux/drivers/media/dvb/ttpci/budget-core.c
+++ b/linux/drivers/media/dvb/ttpci/budget-core.c
@@ -57,8 +57,6 @@ module_param_named(bufsize, dma_buffer_size, int, 0444);
MODULE_PARM_DESC(debug, "Turn on/off budget debugging (default:off).");
MODULE_PARM_DESC(bufsize, "DMA buffer size in KB, default: 188, min: 188, max: 1410 (Activy: 564)");
-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
-
/****************************************************************************
* TT budget / WinTV Nova
****************************************************************************/
@@ -411,7 +409,7 @@ static void budget_unregister(struct budget *budget)
int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev,
struct saa7146_pci_extension_data *info,
- struct module *owner)
+ struct module *owner, short *adapter_nums)
{
int ret = 0;
struct budget_info *bi = info->ext_priv;
@@ -474,7 +472,7 @@ int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev,
printk("%s: dma buffer size %u\n", budget->dev->name, budget->buffer_size);
ret = dvb_register_adapter(&budget->dvb_adapter, budget->card->name,
- owner, &budget->dev->pci->dev, adapter_nr);
+ owner, &budget->dev->pci->dev, adapter_nums);
if (ret < 0)
return ret;
diff --git a/linux/drivers/media/dvb/ttpci/budget-patch.c b/linux/drivers/media/dvb/ttpci/budget-patch.c
index b53b4b6d9..0f0bfcad1 100644
--- a/linux/drivers/media/dvb/ttpci/budget-patch.c
+++ b/linux/drivers/media/dvb/ttpci/budget-patch.c
@@ -39,6 +39,8 @@
#include "bsru6.h"
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
#define budget_patch budget
static struct saa7146_extension budget_extension;
@@ -592,8 +594,9 @@ static int budget_patch_attach (struct saa7146_dev* dev, struct saa7146_pci_exte
dprintk(2, "budget: %p\n", budget);
- if ((err = ttpci_budget_init (budget, dev, info, THIS_MODULE))) {
- kfree (budget);
+ err = ttpci_budget_init(budget, dev, info, THIS_MODULE, adapter_nr);
+ if (err) {
+ kfree(budget);
return err;
}
diff --git a/linux/drivers/media/dvb/ttpci/budget.c b/linux/drivers/media/dvb/ttpci/budget.c
index d95203d55..1638e1d9f 100644
--- a/linux/drivers/media/dvb/ttpci/budget.c
+++ b/linux/drivers/media/dvb/ttpci/budget.c
@@ -52,6 +52,8 @@ static int diseqc_method;
module_param(diseqc_method, int, 0444);
MODULE_PARM_DESC(diseqc_method, "Select DiSEqC method for subsystem id 13c2:1003, 0: default, 1: more reliable (for newer revisions only)");
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
static void Set22K (struct budget *budget, int state)
{
struct saa7146_dev *dev=budget->dev;
@@ -598,7 +600,8 @@ static int budget_attach (struct saa7146_dev* dev, struct saa7146_pci_extension_
dev->ext_priv = budget;
- if ((err = ttpci_budget_init (budget, dev, info, THIS_MODULE))) {
+ err = ttpci_budget_init(budget, dev, info, THIS_MODULE, adapter_nr);
+ if (err) {
printk("==> failed\n");
kfree (budget);
return err;
diff --git a/linux/drivers/media/dvb/ttpci/budget.h b/linux/drivers/media/dvb/ttpci/budget.h
index 6539c0171..6129196b8 100644
--- a/linux/drivers/media/dvb/ttpci/budget.h
+++ b/linux/drivers/media/dvb/ttpci/budget.h
@@ -110,7 +110,7 @@ static struct saa7146_pci_extension_data x_var = { \
extern int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev,
struct saa7146_pci_extension_data *info,
- struct module *owner);
+ struct module *owner, short *adapter_nums);
extern void ttpci_budget_init_hooks(struct budget *budget);
extern int ttpci_budget_deinit(struct budget *budget);
extern void ttpci_budget_irq10_handler(struct saa7146_dev *dev, u32 * isr);
diff --git a/linux/drivers/media/dvb/ttusb-dec/ttusbdecfe.c b/linux/drivers/media/dvb/ttusb-dec/ttusbdecfe.c
index 443af2409..21260aad1 100644
--- a/linux/drivers/media/dvb/ttusb-dec/ttusbdecfe.c
+++ b/linux/drivers/media/dvb/ttusb-dec/ttusbdecfe.c
@@ -38,7 +38,17 @@ struct ttusbdecfe_state {
};
-static int ttusbdecfe_read_status(struct dvb_frontend* fe, fe_status_t* status)
+static int ttusbdecfe_dvbs_read_status(struct dvb_frontend *fe,
+ fe_status_t *status)
+{
+ *status = FE_HAS_SIGNAL | FE_HAS_VITERBI |
+ FE_HAS_SYNC | FE_HAS_CARRIER | FE_HAS_LOCK;
+ return 0;
+}
+
+
+static int ttusbdecfe_dvbt_read_status(struct dvb_frontend *fe,
+ fe_status_t *status)
{
struct ttusbdecfe_state* state = fe->demodulator_priv;
u8 b[] = { 0x00, 0x00, 0x00, 0x00,
@@ -251,7 +261,7 @@ static struct dvb_frontend_ops ttusbdecfe_dvbt_ops = {
.get_tune_settings = ttusbdecfe_dvbt_get_tune_settings,
- .read_status = ttusbdecfe_read_status,
+ .read_status = ttusbdecfe_dvbt_read_status,
};
static struct dvb_frontend_ops ttusbdecfe_dvbs_ops = {
@@ -273,7 +283,7 @@ static struct dvb_frontend_ops ttusbdecfe_dvbs_ops = {
.set_frontend = ttusbdecfe_dvbs_set_frontend,
- .read_status = ttusbdecfe_read_status,
+ .read_status = ttusbdecfe_dvbs_read_status,
.diseqc_send_master_cmd = ttusbdecfe_dvbs_diseqc_send_master_cmd,
.set_voltage = ttusbdecfe_dvbs_set_voltage,
diff --git a/linux/drivers/media/radio/Kconfig b/linux/drivers/media/radio/Kconfig
index 1b41b3f77..e51d707e5 100644
--- a/linux/drivers/media/radio/Kconfig
+++ b/linux/drivers/media/radio/Kconfig
@@ -361,4 +361,16 @@ config USB_SI470X
To compile this driver as a module, choose M here: the
module will be called radio-silabs.
+config USB_MR800
+ tristate "AverMedia MR 800 USB FM radio support"
+ depends on USB && VIDEO_V4L2
+ ---help---
+ Say Y here if you want to connect this type of radio to your
+ computer's USB port. Note that the audio is not digital, and
+ you must connect the line out connector to a sound card or a
+ set of speakers.
+
+ To compile this driver as a module, choose M here: the
+ module will be called radio-mr800.
+
endif # RADIO_ADAPTERS
diff --git a/linux/drivers/media/radio/Makefile b/linux/drivers/media/radio/Makefile
index 7ca71ab96..240ec63cd 100644
--- a/linux/drivers/media/radio/Makefile
+++ b/linux/drivers/media/radio/Makefile
@@ -18,5 +18,6 @@ obj-$(CONFIG_RADIO_TRUST) += radio-trust.o
obj-$(CONFIG_RADIO_MAESTRO) += radio-maestro.o
obj-$(CONFIG_USB_DSBR) += dsbr100.o
obj-$(CONFIG_USB_SI470X) += radio-si470x.o
+obj-$(CONFIG_USB_MR800) += radio-mr800.o
EXTRA_CFLAGS += -Isound
diff --git a/linux/drivers/media/radio/radio-mr800.c b/linux/drivers/media/radio/radio-mr800.c
new file mode 100644
index 000000000..a33717c48
--- /dev/null
+++ b/linux/drivers/media/radio/radio-mr800.c
@@ -0,0 +1,628 @@
+/*
+ * A driver for the AverMedia MR 800 USB FM radio. This device plugs
+ * into both the USB and an analog audio input, so this thing
+ * only deals with initialization and frequency setting, the
+ * audio data has to be handled by a sound driver.
+ *
+ * Copyright (c) 2008 Alexey Klimov <klimov.linux@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
+ * (at your option) 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
+ */
+
+/*
+ * Big thanks to authors of dsbr100.c and radio-si470x.c
+ *
+ * When work was looked pretty good, i discover this:
+ * http://av-usbradio.sourceforge.net/index.php
+ * http://sourceforge.net/projects/av-usbradio/
+ * Latest release of theirs project was in 2005.
+ * Probably, this driver could be improved trough using their
+ * achievements (specifications given).
+ * So, we have smth to begin with.
+ *
+ * History:
+ * Version 0.01: First working version.
+ * It's required to blacklist AverMedia USB Radio
+ * in usbhid/hid-quirks.c
+ *
+ * Many things to do:
+ * - Correct power managment of device (suspend & resume)
+ * - Make x86 independance (little-endian and big-endian stuff)
+ * - Add code for scanning and smooth tuning
+ * - Checked and add stereo&mono stuff
+ * - Add code for sensitivity value
+ * - Correct mistakes
+ * - In Japan another FREQ_MIN and FREQ_MAX
+ */
+
+/* kernel includes */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <linux/usb.h>
+#include <linux/version.h> /* for KERNEL_VERSION MACRO */
+
+/* driver and module definitions */
+#define DRIVER_AUTHOR "Alexey Klimov <klimov.linux@gmail.com>"
+#define DRIVER_DESC "AverMedia MR 800 USB FM radio driver"
+#define DRIVER_VERSION "0.01"
+#define RADIO_VERSION KERNEL_VERSION(0, 0, 1)
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+#define USB_AMRADIO_VENDOR 0x07ca
+#define USB_AMRADIO_PRODUCT 0xb800
+
+/* Probably USB_TIMEOUT should be modified in module parameter */
+#define BUFFER_LENGTH 8
+#define USB_TIMEOUT 500
+
+/* Frequency limits in MHz -- these are European values. For Japanese
+devices, that would be 76 and 91. */
+#define FREQ_MIN 87.5
+#define FREQ_MAX 108.0
+#define FREQ_MUL 16000
+
+/* module parameter */
+static int radio_nr = -1;
+module_param(radio_nr, int, 0);
+MODULE_PARM_DESC(radio_nr, "Radio Nr");
+
+static struct v4l2_queryctrl radio_qctrl[] = {
+ {
+ .id = V4L2_CID_AUDIO_MUTE,
+ .name = "Mute",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ },
+/* HINT: the disabled controls are only here to satify kradio and such apps */
+ { .id = V4L2_CID_AUDIO_VOLUME,
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+ },
+ {
+ .id = V4L2_CID_AUDIO_BALANCE,
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+ },
+ {
+ .id = V4L2_CID_AUDIO_BASS,
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+ },
+ {
+ .id = V4L2_CID_AUDIO_TREBLE,
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+ },
+ {
+ .id = V4L2_CID_AUDIO_LOUDNESS,
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+ },
+};
+
+static int usb_amradio_probe(struct usb_interface *intf,
+ const struct usb_device_id *id);
+static void usb_amradio_disconnect(struct usb_interface *intf);
+static int usb_amradio_open(struct inode *inode, struct file *file);
+static int usb_amradio_close(struct inode *inode, struct file *file);
+static int usb_amradio_suspend(struct usb_interface *intf,
+ pm_message_t message);
+static int usb_amradio_resume(struct usb_interface *intf);
+
+/* Data for one (physical) device */
+struct amradio_device {
+ /* reference to USB and video device */
+ struct usb_device *usbdev;
+ struct video_device *videodev;
+
+ unsigned char *buffer;
+ struct mutex lock; /* buffer locking */
+ int curfreq;
+ int stereo;
+ int users;
+ int removed;
+ int muted;
+};
+
+/* USB Device ID List */
+static struct usb_device_id usb_amradio_device_table[] = {
+ {USB_DEVICE_AND_INTERFACE_INFO(USB_AMRADIO_VENDOR, USB_AMRADIO_PRODUCT,
+ USB_CLASS_HID, 0, 0) },
+ { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, usb_amradio_device_table);
+
+/* USB subsystem interface */
+static struct usb_driver usb_amradio_driver = {
+ .name = "radio-mr800",
+ .probe = usb_amradio_probe,
+ .disconnect = usb_amradio_disconnect,
+ .suspend = usb_amradio_suspend,
+ .resume = usb_amradio_resume,
+ .reset_resume = usb_amradio_resume,
+ .id_table = usb_amradio_device_table,
+ .supports_autosuspend = 1,
+};
+
+/* switch on radio. Send 8 bytes to device. */
+static int amradio_start(struct amradio_device *radio)
+{
+ int retval;
+ int size;
+
+ mutex_lock(&radio->lock);
+
+ radio->buffer[0] = 0x00;
+ radio->buffer[1] = 0x55;
+ radio->buffer[2] = 0xaa;
+ radio->buffer[3] = 0x00;
+ radio->buffer[4] = 0xab;
+ radio->buffer[5] = 0x00;
+ radio->buffer[6] = 0x00;
+ radio->buffer[7] = 0x00;
+
+ retval = usb_bulk_msg(radio->usbdev, usb_sndintpipe(radio->usbdev, 2),
+ (void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT);
+
+ if (retval) {
+ mutex_unlock(&radio->lock);
+ return retval;
+ }
+
+ mutex_unlock(&radio->lock);
+
+ radio->muted = 0;
+
+ return retval;
+}
+
+/* switch off radio */
+static int amradio_stop(struct amradio_device *radio)
+{
+ int retval;
+ int size;
+
+ mutex_lock(&radio->lock);
+
+ radio->buffer[0] = 0x00;
+ radio->buffer[1] = 0x55;
+ radio->buffer[2] = 0xaa;
+ radio->buffer[3] = 0x00;
+ radio->buffer[4] = 0xab;
+ radio->buffer[5] = 0x01;
+ radio->buffer[6] = 0x00;
+ radio->buffer[7] = 0x00;
+
+ retval = usb_bulk_msg(radio->usbdev, usb_sndintpipe(radio->usbdev, 2),
+ (void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT);
+
+ if (retval) {
+ mutex_unlock(&radio->lock);
+ return retval;
+ }
+
+ mutex_unlock(&radio->lock);
+
+ radio->muted = 1;
+
+ return retval;
+}
+
+/* set a frequency, freq is defined by v4l's TUNER_LOW, i.e. 1/16th kHz */
+static int amradio_setfreq(struct amradio_device *radio, int freq)
+{
+ int retval;
+ int size;
+ unsigned short freq_send = 0x13 + (freq >> 3) / 25;
+
+ mutex_lock(&radio->lock);
+
+ radio->buffer[0] = 0x00;
+ radio->buffer[1] = 0x55;
+ radio->buffer[2] = 0xaa;
+ radio->buffer[3] = 0x03;
+ radio->buffer[4] = 0xa4;
+ radio->buffer[5] = 0x00;
+ radio->buffer[6] = 0x00;
+ radio->buffer[7] = 0x08;
+
+ retval = usb_bulk_msg(radio->usbdev, usb_sndintpipe(radio->usbdev, 2),
+ (void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT);
+
+ if (retval) {
+ mutex_unlock(&radio->lock);
+ return retval;
+ }
+
+ /* frequency is calculated from freq_send and placed in first 2 bytes */
+ radio->buffer[0] = (freq_send >> 8) & 0xff;
+ radio->buffer[1] = freq_send & 0xff;
+ radio->buffer[2] = 0x01;
+ radio->buffer[3] = 0x00;
+ radio->buffer[4] = 0x00;
+ /* 5 and 6 bytes of buffer already = 0x00 */
+ radio->buffer[7] = 0x00;
+
+ retval = usb_bulk_msg(radio->usbdev, usb_sndintpipe(radio->usbdev, 2),
+ (void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT);
+
+ if (retval) {
+ mutex_unlock(&radio->lock);
+ return retval;
+ }
+
+ mutex_unlock(&radio->lock);
+
+ radio->stereo = 0;
+
+ return retval;
+}
+
+/* USB subsystem interface begins here */
+
+/* handle unplugging of the device, release data structures
+if nothing keeps us from doing it. If something is still
+keeping us busy, the release callback of v4l will take care
+of releasing it. */
+static void usb_amradio_disconnect(struct usb_interface *intf)
+{
+ struct amradio_device *radio = usb_get_intfdata(intf);
+
+ usb_set_intfdata(intf, NULL);
+
+ if (radio) {
+ video_unregister_device(radio->videodev);
+ radio->videodev = NULL;
+ if (radio->users) {
+ kfree(radio->buffer);
+ kfree(radio);
+ } else {
+ radio->removed = 1;
+ }
+ }
+}
+
+/* vidioc_querycap - query device capabilities */
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *v)
+{
+ strlcpy(v->driver, "radio-mr800", sizeof(v->driver));
+ strlcpy(v->card, "AverMedia MR 800 USB FM Radio", sizeof(v->card));
+ sprintf(v->bus_info, "USB");
+ v->version = RADIO_VERSION;
+ v->capabilities = V4L2_CAP_TUNER;
+ return 0;
+}
+
+/* vidioc_g_tuner - get tuner attributes */
+static int vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
+{
+ struct amradio_device *radio = video_get_drvdata(video_devdata(file));
+
+ if (v->index > 0)
+ return -EINVAL;
+
+/* TODO: Add function which look is signal stereo or not
+ * amradio_getstat(radio);
+ */
+ radio->stereo = -1;
+ strcpy(v->name, "FM");
+ v->type = V4L2_TUNER_RADIO;
+ v->rangelow = FREQ_MIN * FREQ_MUL;
+ v->rangehigh = FREQ_MAX * FREQ_MUL;
+ v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
+ v->capability = V4L2_TUNER_CAP_LOW;
+ if (radio->stereo)
+ v->audmode = V4L2_TUNER_MODE_STEREO;
+ else
+ v->audmode = V4L2_TUNER_MODE_MONO;
+ v->signal = 0xffff; /* Can't get the signal strength, sad.. */
+ v->afc = 0; /* Don't know what is this */
+ return 0;
+}
+
+/* vidioc_s_tuner - set tuner attributes */
+static int vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
+{
+ if (v->index > 0)
+ return -EINVAL;
+ return 0;
+}
+
+/* vidioc_s_frequency - set tuner radio frequency */
+static int vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct amradio_device *radio = video_get_drvdata(video_devdata(file));
+
+ radio->curfreq = f->frequency;
+ if (amradio_setfreq(radio, radio->curfreq) < 0)
+ warn("Set frequency failed");
+ return 0;
+}
+
+/* vidioc_g_frequency - get tuner radio frequency */
+static int vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct amradio_device *radio = video_get_drvdata(video_devdata(file));
+
+ f->type = V4L2_TUNER_RADIO;
+ f->frequency = radio->curfreq;
+ return 0;
+}
+
+/* vidioc_queryctrl - enumerate control items */
+static int vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
+ if (qc->id && qc->id == radio_qctrl[i].id) {
+ memcpy(qc, &(radio_qctrl[i]),
+ sizeof(*qc));
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+/* vidioc_g_ctrl - get the value of a control */
+static int vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct amradio_device *radio = video_get_drvdata(video_devdata(file));
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ ctrl->value = radio->muted;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+/* vidioc_s_ctrl - set the value of a control */
+static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct amradio_device *radio = video_get_drvdata(video_devdata(file));
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ if (ctrl->value) {
+ if (amradio_stop(radio) < 0) {
+ warn("amradio_stop() failed");
+ return -1;
+ }
+ } else {
+ if (amradio_start(radio) < 0) {
+ warn("amradio_start() failed");
+ return -1;
+ }
+ }
+ return 0;
+ }
+ return -EINVAL;
+}
+
+/* vidioc_g_audio - get audio attributes */
+static int vidioc_g_audio(struct file *file, void *priv,
+ struct v4l2_audio *a)
+{
+ if (a->index > 1)
+ return -EINVAL;
+
+ strcpy(a->name, "Radio");
+ a->capability = V4L2_AUDCAP_STEREO;
+ return 0;
+}
+
+/* vidioc_s_audio - set audio attributes */
+static int vidioc_s_audio(struct file *file, void *priv,
+ struct v4l2_audio *a)
+{
+ if (a->index != 0)
+ return -EINVAL;
+ return 0;
+}
+
+/* vidioc_g_input - get input */
+static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
+{
+ *i = 0;
+ return 0;
+}
+
+/* vidioc_s_input - set input */
+static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
+{
+ if (i != 0)
+ return -EINVAL;
+ return 0;
+}
+
+/* open device - amradio_start() and amradio_setfreq() */
+static int usb_amradio_open(struct inode *inode, struct file *file)
+{
+ struct amradio_device *radio = video_get_drvdata(video_devdata(file));
+
+ radio->users = 1;
+ radio->muted = 1;
+
+ if (amradio_start(radio) < 0) {
+ warn("Radio did not start up properly");
+ radio->users = 0;
+ return -EIO;
+ }
+ if (amradio_setfreq(radio, radio->curfreq) < 0)
+ warn("Set frequency failed");
+ return 0;
+}
+
+/*close device - free driver structures */
+static int usb_amradio_close(struct inode *inode, struct file *file)
+{
+ struct amradio_device *radio = video_get_drvdata(video_devdata(file));
+
+ if (!radio)
+ return -ENODEV;
+ radio->users = 0;
+ if (radio->removed) {
+ kfree(radio->buffer);
+ kfree(radio);
+ }
+ return 0;
+}
+
+/* Suspend device - stop device. Need to be checked and fixed */
+static int usb_amradio_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct amradio_device *radio = usb_get_intfdata(intf);
+
+ if (amradio_stop(radio) < 0)
+ warn("amradio_stop() failed");
+
+ info("radio-mr800: Going into suspend..");
+
+ return 0;
+}
+
+/* Resume device - start device. Need to be checked and fixed */
+static int usb_amradio_resume(struct usb_interface *intf)
+{
+ struct amradio_device *radio = usb_get_intfdata(intf);
+
+ if (amradio_start(radio) < 0)
+ warn("amradio_start() failed");
+
+ info("radio-mr800: Coming out of suspend..");
+
+ return 0;
+}
+
+/* File system interface */
+static const struct file_operations usb_amradio_fops = {
+ .owner = THIS_MODULE,
+ .open = usb_amradio_open,
+ .release = usb_amradio_close,
+ .ioctl = video_ioctl2,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = v4l_compat_ioctl32,
+#endif
+ .llseek = no_llseek,
+};
+
+static const struct v4l2_ioctl_ops usb_amradio_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_g_tuner = vidioc_g_tuner,
+ .vidioc_s_tuner = vidioc_s_tuner,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_g_audio = vidioc_g_audio,
+ .vidioc_s_audio = vidioc_s_audio,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+};
+
+/* V4L2 interface */
+static struct video_device amradio_videodev_template = {
+ .name = "AverMedia MR 800 USB FM Radio",
+ .fops = &usb_amradio_fops,
+ .ioctl_ops = &usb_amradio_ioctl_ops,
+ .release = video_device_release,
+};
+
+/* check if the device is present and register with v4l and
+usb if it is */
+static int usb_amradio_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct amradio_device *radio;
+
+ radio = kmalloc(sizeof(struct amradio_device), GFP_KERNEL);
+
+ if (!(radio))
+ return -ENOMEM;
+
+ radio->buffer = kmalloc(BUFFER_LENGTH, GFP_KERNEL);
+
+ if (!(radio->buffer)) {
+ kfree(radio);
+ return -ENOMEM;
+ }
+
+ radio->videodev = video_device_alloc();
+
+ if (!(radio->videodev)) {
+ kfree(radio->buffer);
+ kfree(radio);
+ return -ENOMEM;
+ }
+
+ memcpy(radio->videodev, &amradio_videodev_template,
+ sizeof(amradio_videodev_template));
+
+ radio->removed = 0;
+ radio->users = 0;
+ radio->usbdev = interface_to_usbdev(intf);
+ radio->curfreq = 95.16 * FREQ_MUL;
+
+ mutex_init(&radio->lock);
+
+ video_set_drvdata(radio->videodev, radio);
+ if (video_register_device(radio->videodev, VFL_TYPE_RADIO, radio_nr)) {
+ warn("Could not register video device");
+ video_device_release(radio->videodev);
+ kfree(radio->buffer);
+ kfree(radio);
+ return -EIO;
+ }
+
+ usb_set_intfdata(intf, radio);
+ return 0;
+}
+
+static int __init amradio_init(void)
+{
+ int retval = usb_register(&usb_amradio_driver);
+
+ info(DRIVER_VERSION " " DRIVER_DESC);
+ if (retval)
+ err("usb_register failed. Error number %d", retval);
+ return retval;
+}
+
+static void __exit amradio_exit(void)
+{
+ usb_deregister(&usb_amradio_driver);
+}
+
+module_init(amradio_init);
+module_exit(amradio_exit);
+
diff --git a/linux/drivers/media/video/au0828/au0828-cards.c b/linux/drivers/media/video/au0828/au0828-cards.c
index ed48908a9..5f07a8a07 100644
--- a/linux/drivers/media/video/au0828/au0828-cards.c
+++ b/linux/drivers/media/video/au0828/au0828-cards.c
@@ -46,7 +46,7 @@ struct au0828_board au0828_boards[] = {
/* Tuner callback function for au0828 boards. Currently only needed
* for HVR1500Q, which has an xc5000 tuner.
*/
-int au0828_tuner_callback(void *priv, int command, int arg)
+int au0828_tuner_callback(void *priv, int component, int command, int arg)
{
struct au0828_dev *dev = priv;
diff --git a/linux/drivers/media/video/au0828/au0828-dvb.c b/linux/drivers/media/video/au0828/au0828-dvb.c
index 24160ca59..82474a4a8 100644
--- a/linux/drivers/media/video/au0828/au0828-dvb.c
+++ b/linux/drivers/media/video/au0828/au0828-dvb.c
@@ -54,7 +54,6 @@ static struct au8522_config hauppauge_woodbury_config = {
static struct xc5000_config hauppauge_hvr950q_tunerconfig = {
.i2c_address = 0x61,
.if_khz = 6000,
- .tuner_callback = au0828_tuner_callback
};
static struct mxl5007t_config mxl5007t_hvr950q_config = {
@@ -394,6 +393,8 @@ int au0828_dvb_register(struct au0828_dev *dev)
__func__);
return -1;
}
+ /* define general-purpose callback pointer */
+ dvb->frontend->callback = au0828_tuner_callback;
/* register everything */
ret = dvb_register(dev);
diff --git a/linux/drivers/media/video/au0828/au0828.h b/linux/drivers/media/video/au0828/au0828.h
index 4f10ff300..9d6a1161d 100644
--- a/linux/drivers/media/video/au0828/au0828.h
+++ b/linux/drivers/media/video/au0828/au0828.h
@@ -103,7 +103,8 @@ extern int au0828_debug;
extern struct au0828_board au0828_boards[];
extern struct usb_device_id au0828_usb_id_table[];
extern void au0828_gpio_setup(struct au0828_dev *dev);
-extern int au0828_tuner_callback(void *priv, int command, int arg);
+extern int au0828_tuner_callback(void *priv, int component,
+ int command, int arg);
extern void au0828_card_setup(struct au0828_dev *dev);
/* ----------------------------------------------------------- */
diff --git a/linux/drivers/media/video/cx18/cx18-gpio.c b/linux/drivers/media/video/cx18/cx18-gpio.c
index 3bdffbf7a..0e5604219 100644
--- a/linux/drivers/media/video/cx18/cx18-gpio.c
+++ b/linux/drivers/media/video/cx18/cx18-gpio.c
@@ -152,7 +152,7 @@ void cx18_gpio_init(struct cx18 *cx)
}
/* Xceive tuner reset function */
-int cx18_reset_tuner_gpio(void *dev, int cmd, int value)
+int cx18_reset_tuner_gpio(void *dev, int component, int cmd, int value)
{
struct i2c_algo_bit_data *algo = dev;
struct cx18_i2c_algo_callback_data *cb_data = algo->data;
diff --git a/linux/drivers/media/video/cx18/cx18-gpio.h b/linux/drivers/media/video/cx18/cx18-gpio.h
index 22cd7ddf8..beb7424b9 100644
--- a/linux/drivers/media/video/cx18/cx18-gpio.h
+++ b/linux/drivers/media/video/cx18/cx18-gpio.h
@@ -23,5 +23,5 @@
void cx18_gpio_init(struct cx18 *cx);
void cx18_reset_i2c_slaves_gpio(struct cx18 *cx);
void cx18_reset_ir_gpio(void *data);
-int cx18_reset_tuner_gpio(void *dev, int cmd, int value);
+int cx18_reset_tuner_gpio(void *dev, int component, int cmd, int value);
int cx18_gpio(struct cx18 *cx, unsigned int command, void *arg);
diff --git a/linux/drivers/media/video/cx23885/cx23885-cards.c b/linux/drivers/media/video/cx23885/cx23885-cards.c
index be9a6d8ec..2b497b28b 100644
--- a/linux/drivers/media/video/cx23885/cx23885-cards.c
+++ b/linux/drivers/media/video/cx23885/cx23885-cards.c
@@ -338,7 +338,7 @@ static void hauppauge_eeprom(struct cx23885_dev *dev, u8 *eeprom_data)
dev->name, tv.model);
}
-int cx23885_tuner_callback(void *priv, int command, int arg)
+int cx23885_tuner_callback(void *priv, int component, int command, int arg)
{
struct cx23885_tsport *port = priv;
struct cx23885_dev *dev = port->dev;
diff --git a/linux/drivers/media/video/cx23885/cx23885-dvb.c b/linux/drivers/media/video/cx23885/cx23885-dvb.c
index 663954988..1909ef6e3 100644
--- a/linux/drivers/media/video/cx23885/cx23885-dvb.c
+++ b/linux/drivers/media/video/cx23885/cx23885-dvb.c
@@ -190,13 +190,11 @@ static struct s5h1411_config dvico_s5h1411_config = {
static struct xc5000_config hauppauge_hvr1500q_tunerconfig = {
.i2c_address = 0x61,
.if_khz = 5380,
- .tuner_callback = cx23885_tuner_callback,
};
static struct xc5000_config dvico_xc5000_tunerconfig = {
.i2c_address = 0x64,
.if_khz = 5380,
- .tuner_callback = cx23885_tuner_callback,
};
static struct tda829x_config tda829x_no_probe = {
@@ -404,11 +402,9 @@ static int dvb_register(struct cx23885_tsport *port)
struct xc2028_config cfg = {
.i2c_adap = &i2c_bus->i2c_adap,
.i2c_addr = 0x61,
- .video_dev = port,
- .callback = cx23885_tuner_callback,
};
static struct xc2028_ctrl ctl = {
- .fname = "xc3028-v27.fw",
+ .fname = XC2028_DEFAULT_FIRMWARE,
.max_len = 64,
.scode_table = XC3028_FE_OREN538,
};
@@ -444,14 +440,13 @@ static int dvb_register(struct cx23885_tsport *port)
struct xc2028_config cfg = {
.i2c_adap = &dev->i2c_bus[1].i2c_adap,
.i2c_addr = 0x64,
- .video_dev = port,
- .callback = cx23885_tuner_callback,
};
static struct xc2028_ctrl ctl = {
- .fname = "xc3028L-v36.fw",
+ .fname = XC3028L_DEFAULT_FIRMWARE,
.max_len = 64,
.demod = 5000,
- .d2633 = 1
+ /* This is true for all demods with v36 firmware? */
+ .type = XC2028_D2633,
};
fe = dvb_attach(xc2028_attach,
@@ -486,11 +481,9 @@ static int dvb_register(struct cx23885_tsport *port)
struct xc2028_config cfg = {
.i2c_adap = &i2c_bus->i2c_adap,
.i2c_addr = 0x61,
- .video_dev = port,
- .callback = cx23885_tuner_callback,
};
static struct xc2028_ctrl ctl = {
- .fname = "xc3028-v27.fw",
+ .fname = XC2028_DEFAULT_FIRMWARE,
.max_len = 64,
.demod = XC3028_FE_ZARLINK456,
};
@@ -513,11 +506,9 @@ static int dvb_register(struct cx23885_tsport *port)
struct xc2028_config cfg = {
.i2c_adap = &dev->i2c_bus[1].i2c_adap,
.i2c_addr = 0x61,
- .video_dev = port,
- .callback = cx23885_tuner_callback,
};
static struct xc2028_ctrl ctl = {
- .fname = "xc3028-v27.fw",
+ .fname = XC2028_DEFAULT_FIRMWARE,
.max_len = 64,
.demod = XC3028_FE_ZARLINK456,
};
@@ -537,6 +528,8 @@ static int dvb_register(struct cx23885_tsport *port)
printk("%s: frontend initialization failed\n", dev->name);
return -1;
}
+ /* define general-purpose callback pointer */
+ port->dvb.frontend->callback = cx23885_tuner_callback;
/* Put the analog decoder in standby to keep it quiet */
cx23885_call_i2c_clients(i2c_bus, TUNER_SET_STANDBY, NULL);
diff --git a/linux/drivers/media/video/cx23885/cx23885.h b/linux/drivers/media/video/cx23885/cx23885.h
index 49e3eaec7..dea1798f7 100644
--- a/linux/drivers/media/video/cx23885/cx23885.h
+++ b/linux/drivers/media/video/cx23885/cx23885.h
@@ -412,7 +412,7 @@ extern const unsigned int cx23885_bcount;
extern struct cx23885_subid cx23885_subids[];
extern const unsigned int cx23885_idcount;
-extern int cx23885_tuner_callback(void *priv, int command, int arg);
+extern int cx23885_tuner_callback(void *priv, int component, int command, int arg);
extern void cx23885_card_list(struct cx23885_dev *dev);
extern int cx23885_ir_init(struct cx23885_dev *dev);
extern void cx23885_gpio_setup(struct cx23885_dev *dev);
diff --git a/linux/drivers/media/video/cx88/Kconfig b/linux/drivers/media/video/cx88/Kconfig
index 9dd7bdf65..0b9e5fac6 100644
--- a/linux/drivers/media/video/cx88/Kconfig
+++ b/linux/drivers/media/video/cx88/Kconfig
@@ -58,6 +58,10 @@ config VIDEO_CX88_DVB
select DVB_ISL6421 if !DVB_FE_CUSTOMISE
select MEDIA_TUNER_SIMPLE if !DVB_FE_CUSTOMISE
select DVB_S5H1411 if !DVB_FE_CUSTOMISE
+ select DVB_CX24116 if !DVB_FE_CUSTOMISE
+ select DVB_STV0299 if !DVB_FE_CUSTOMISE
+ select DVB_STV0288 if !DVB_FE_CUSTOMISE
+ select DVB_STB6000 if !DVB_FE_CUSTOMISE
---help---
This adds support for DVB/ATSC cards based on the
Conexant 2388x chip.
diff --git a/linux/drivers/media/video/cx88/cx88-cards.c b/linux/drivers/media/video/cx88/cx88-cards.c
index 871ba1992..215a724ce 100644
--- a/linux/drivers/media/video/cx88/cx88-cards.c
+++ b/linux/drivers/media/video/cx88/cx88-cards.c
@@ -1382,27 +1382,30 @@ static const struct cx88_board cx88_boards[] = {
.radio_addr = ADDR_UNSET,
.tda9887_conf = TDA9887_PRESENT,
.audio_chip = V4L2_IDENT_WM8775,
+ /*
+ * gpio0 as reported by Mike Crash <mike AT mikecrash.com>
+ */
.input = {{
.type = CX88_VMUX_TELEVISION,
.vmux = 0,
- .gpio0 = 0xe780,
+ .gpio0 = 0xef88,
.audioroute = 1,
},{
.type = CX88_VMUX_COMPOSITE1,
.vmux = 1,
- .gpio0 = 0xe780,
+ .gpio0 = 0xef88,
.audioroute = 2,
},{
.type = CX88_VMUX_SVIDEO,
.vmux = 2,
- .gpio0 = 0xe780,
+ .gpio0 = 0xef88,
.audioroute = 2,
}},
/* fixme: Add radio support */
.mpeg = CX88_MPEG_DVB | CX88_MPEG_BLACKBIRD,
.radio = {
.type = CX88_RADIO,
- .gpio0 = 0xe780,
+ .gpio0 = 0xef88,
},
},
[CX88_BOARD_ADSTECH_PTV_390] = {
@@ -1479,15 +1482,26 @@ static const struct cx88_board cx88_boards[] = {
.name = "Pinnacle Hybrid PCTV",
.tuner_type = TUNER_XC2028,
.tuner_addr = 0x61,
+ .radio_type = TUNER_XC2028,
+ .radio_addr = 0x61,
.input = { {
.type = CX88_VMUX_TELEVISION,
.vmux = 0,
+ .gpio0 = 0x004ff,
+ .gpio1 = 0x010ff,
+ .gpio2 = 0x00001,
}, {
.type = CX88_VMUX_COMPOSITE1,
.vmux = 1,
+ .gpio0 = 0x004fb,
+ .gpio1 = 0x010ef,
+ .audioroute = 1,
}, {
.type = CX88_VMUX_SVIDEO,
.vmux = 2,
+ .gpio0 = 0x004fb,
+ .gpio1 = 0x010ef,
+ .audioroute = 1,
} },
.radio = {
.type = CX88_RADIO,
@@ -1495,10 +1509,7 @@ static const struct cx88_board cx88_boards[] = {
.gpio1 = 0x010ff,
.gpio2 = 0x0ff,
},
-#if 0
- /* needs some more GPIO work */
.mpeg = CX88_MPEG_DVB,
-#endif
},
[CX88_BOARD_WINFAST_TV2000_XP_GLOBAL] = {
.name = "Winfast TV2000 XP Global",
@@ -1662,6 +1673,36 @@ static const struct cx88_board cx88_boards[] = {
.gpio2 = 0x0cfb,
},
},
+ [CX88_BOARD_PROLINK_PV_GLOBAL_XTREME] = {
+ .name = "Prolink Pixelview Global Extreme",
+ .tuner_type = TUNER_XC2028,
+ .tuner_addr = 0x61,
+ .input = { {
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x04fb,
+ .gpio1 = 0x04080,
+ .gpio2 = 0x0cf7,
+ }, {
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x04fb,
+ .gpio1 = 0x04080,
+ .gpio2 = 0x0cfb,
+ }, {
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x04fb,
+ .gpio1 = 0x04080,
+ .gpio2 = 0x0cfb,
+ } },
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0x04ff,
+ .gpio1 = 0x04080,
+ .gpio2 = 0x0cf7,
+ },
+ },
/* Both radio, analog and ATSC work with this board.
However, for analog to work, s5h1409 gate should be open,
otherwise, tuner-xc3028 won't be detected.
@@ -1701,6 +1742,119 @@ static const struct cx88_board cx88_boards[] = {
},
.mpeg = CX88_MPEG_DVB,
},
+ [CX88_BOARD_HAUPPAUGE_HVR4000] = {
+ .name = "Hauppauge WinTV-HVR4000 DVB-S/S2/T/Hybrid",
+ .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ /*
+ * GPIO0 (WINTV2000)
+ *
+ * Analogue SAT DVB-T
+ * Antenna 0xc4bf 0xc4bb
+ * Composite 0xc4bf 0xc4bb
+ * S-Video 0xc4bf 0xc4bb
+ * Composite1 0xc4ff 0xc4fb
+ * S-Video1 0xc4ff 0xc4fb
+ *
+ * BIT VALUE FUNCTION GP{x}_IO
+ * 0 1 I:?
+ * 1 1 I:?
+ * 2 1 O:DVB-T DEMOD ENABLE LOW/ANALOG DEMOD ENABLE HIGH
+ * 3 1 I:?
+ * 4 1 I:?
+ * 5 1 I:?
+ * 6 0 O:INPUT SELECTOR 0=INTERNAL 1=EXPANSION
+ * 7 1 O:DVB-T DEMOD RESET LOW
+ *
+ * BIT VALUE FUNCTION GP{x}_OE
+ * 8 0 I
+ * 9 0 I
+ * a 1 O
+ * b 0 I
+ * c 0 I
+ * d 0 I
+ * e 1 O
+ * f 1 O
+ */
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0xc4bf,
+ }, {
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0xc4bf,
+ }, {
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0xc4bf,
+ } },
+ /* fixme: Add radio support */
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_HAUPPAUGE_HVR4000LITE] = {
+ .name = "Hauppauge WinTV-HVR4000(Lite) DVB-S/S2",
+ .tuner_type = UNSET,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_DVB,
+ .vmux = 0,
+ } },
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_TEVII_S420] = {
+ .name = "TeVii S420 DVB-S",
+ .tuner_type = UNSET,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_DVB,
+ .vmux = 0,
+ } },
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_TEVII_S460] = {
+ .name = "TeVii S460 DVB-S/S2",
+ .tuner_type = UNSET,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_DVB,
+ .vmux = 0,
+ } },
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_OMICOM_SS4_PCI] = {
+ .name = "Omicom SS4 DVB-S/S2 PCI",
+ .tuner_type = UNSET,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_DVB,
+ .vmux = 0,
+ } },
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_TBS_8920] = {
+ .name = "TBS 8920 DVB-S/S2",
+ .tuner_type = TUNER_ABSENT,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_DVB,
+ .vmux = 1,
+ } },
+ .mpeg = CX88_MPEG_DVB,
+ },
};
/* ------------------------------------------------------------------ */
@@ -2047,9 +2201,49 @@ static const struct cx88_subid cx88_subids[] = {
.subdevice = 0x4935,
.card = CX88_BOARD_PROLINK_PV_8000GT,
}, {
+ .subvendor = 0x1554,
+ .subdevice = 0x4976,
+ .card = CX88_BOARD_PROLINK_PV_GLOBAL_XTREME,
+ }, {
.subvendor = 0x17de,
.subdevice = 0x08c1,
.card = CX88_BOARD_KWORLD_ATSC_120,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x6900,
+ .card = CX88_BOARD_HAUPPAUGE_HVR4000,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x6904,
+ .card = CX88_BOARD_HAUPPAUGE_HVR4000,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x6902,
+ .card = CX88_BOARD_HAUPPAUGE_HVR4000,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x6905,
+ .card = CX88_BOARD_HAUPPAUGE_HVR4000LITE,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x6906,
+ .card = CX88_BOARD_HAUPPAUGE_HVR4000LITE,
+ }, {
+ .subvendor = 0xd420,
+ .subdevice = 0x9022,
+ .card = CX88_BOARD_TEVII_S420,
+ }, {
+ .subvendor = 0xd460,
+ .subdevice = 0x9022,
+ .card = CX88_BOARD_TEVII_S460,
+ }, {
+ .subvendor = 0xA044,
+ .subdevice = 0x2011,
+ .card = CX88_BOARD_OMICOM_SS4_PCI,
+ }, {
+ .subvendor = 0x8920,
+ .subdevice = 0x8888,
+ .card = CX88_BOARD_TBS_8920,
},
};
@@ -2102,6 +2296,13 @@ static void hauppauge_eeprom(struct cx88_core *core, u8 *eeprom_data)
case 14669: /* WinTV-HVR3000 (OEM, no IR, no b/panel video - Low profile) */
case 28552: /* WinTV-PVR 'Roslyn' (No IR) */
case 34519: /* WinTV-PCI-FM */
+ case 69009:
+ /* WinTV-HVR4000 (DVBS/S2/T, Video and IR, back panel inputs) */
+ case 69100: /* WinTV-HVR4000LITE (DVBS/S2, IR) */
+ case 69500: /* WinTV-HVR4000LITE (DVBS/S2, No IR) */
+ case 69559:
+ /* WinTV-HVR4000 (DVBS/S2/T, Video no IR, back panel inputs) */
+ case 69569: /* WinTV-HVR4000 (DVBS/S2/T, Video no IR) */
case 90002: /* Nova-T-PCI (9002) */
case 92001: /* Nova-S-Plus (Video and IR) */
case 92002: /* Nova-S-Plus (Video and IR) */
@@ -2186,9 +2387,21 @@ static int cx88_dvico_xc2028_callback(struct cx88_core *core,
{
switch (command) {
case XC2028_TUNER_RESET:
- cx_write(MO_GP0_IO, 0x101000);
- mdelay(5);
- cx_set(MO_GP0_IO, 0x101010);
+ switch (core->boardnr) {
+ case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO:
+ /* GPIO-4 xc3028 tuner */
+
+ cx_set(MO_GP0_IO, 0x00001000);
+ cx_clear(MO_GP0_IO, 0x00000010);
+ msleep(100);
+ cx_set(MO_GP0_IO, 0x00000010);
+ msleep(100);
+ break;
+ default:
+ cx_write(MO_GP0_IO, 0x101000);
+ mdelay(5);
+ cx_set(MO_GP0_IO, 0x101010);
+ }
break;
default:
return -EINVAL;
@@ -2295,8 +2508,10 @@ static int cx88_xc2028_tuner_callback(struct cx88_core *core,
return cx88_xc3028_geniatech_tuner_callback(core,
command, arg);
case CX88_BOARD_PROLINK_PV_8000GT:
+ case CX88_BOARD_PROLINK_PV_GLOBAL_XTREME:
return cx88_pv_8000gt_callback(core, command, arg);
case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO:
+ case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO:
return cx88_dvico_xc2028_callback(core, command, arg);
}
@@ -2364,7 +2579,7 @@ static int cx88_xc5000_tuner_callback(struct cx88_core *core,
return 0; /* Should never be here */
}
-int cx88_tuner_callback(void *priv, int command, int arg)
+int cx88_tuner_callback(void *priv, int component, int command, int arg)
{
struct i2c_algo_bit_data *i2c_algo = priv;
struct cx88_core *core;
@@ -2381,6 +2596,9 @@ int cx88_tuner_callback(void *priv, int command, int arg)
return -EINVAL;
}
+ if (component != DVB_FRONTEND_COMPONENT_TUNER)
+ return -EINVAL;
+
switch (core->board.tuner_type) {
case TUNER_XC2028:
info_printk(core, "Calling XC2028/3028 callback\n");
@@ -2429,16 +2647,22 @@ static void cx88_card_setup_pre_i2c(struct cx88_core *core)
{
switch (core->boardnr) {
case CX88_BOARD_HAUPPAUGE_HVR1300:
- /* Bring the 702 demod up before i2c scanning/attach or devices are hidden */
- /* We leave here with the 702 on the bus */
- cx_write(MO_GP0_IO, 0x0000e780);
+ /*
+ * Bring the 702 demod up before i2c scanning/attach or devices are hidden
+ * We leave here with the 702 on the bus
+ *
+ * "reset the IR receiver on GPIO[3]"
+ * Reported by Mike Crash <mike AT mikecrash.com>
+ */
+ cx_write(MO_GP0_IO, 0x0000ef88);
udelay(1000);
- cx_clear(MO_GP0_IO, 0x00000080);
+ cx_clear(MO_GP0_IO, 0x00000088);
udelay(50);
- cx_set(MO_GP0_IO, 0x00000080); /* 702 out of reset */
+ cx_set(MO_GP0_IO, 0x00000088); /* 702 out of reset */
udelay(1000);
break;
+ case CX88_BOARD_PROLINK_PV_GLOBAL_XTREME:
case CX88_BOARD_PROLINK_PV_8000GT:
cx_write(MO_GP2_IO, 0xcf7);
mdelay(50);
@@ -2448,10 +2672,18 @@ static void cx88_card_setup_pre_i2c(struct cx88_core *core)
msleep(10);
break;
- case CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD:
+ case CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD:
/* Enable the xc5000 tuner */
cx_set(MO_GP0_IO, 0x00001010);
break;
+
+ case CX88_BOARD_HAUPPAUGE_HVR3000:
+ case CX88_BOARD_HAUPPAUGE_HVR4000:
+ case CX88_BOARD_HAUPPAUGE_HVR4000LITE:
+ /* Init GPIO */
+ cx_write(MO_GP0_IO, core->board.input[0].gpio0);
+ udelay(1000);
+ break;
}
}
@@ -2478,11 +2710,16 @@ void cx88_setup_xc3028(struct cx88_core *core, struct xc2028_ctrl *ctl)
case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO:
ctl->demod = XC3028_FE_OREN538;
break;
+ case CX88_BOARD_PROLINK_PV_GLOBAL_XTREME:
case CX88_BOARD_PROLINK_PV_8000GT:
/*
- * This board uses non-MTS firmware
+ * Those boards uses non-MTS firmware
*/
break;
+ case CX88_BOARD_PINNACLE_HYBRID_PCTV:
+ ctl->demod = XC3028_FE_ZARLINK456;
+ ctl->mts = 1;
+ break;
default:
ctl->demod = XC3028_FE_OREN538;
ctl->mts = 1;
@@ -2526,6 +2763,8 @@ static void cx88_card_setup(struct cx88_core *core)
case CX88_BOARD_HAUPPAUGE_HVR1100LP:
case CX88_BOARD_HAUPPAUGE_HVR3000:
case CX88_BOARD_HAUPPAUGE_HVR1300:
+ case CX88_BOARD_HAUPPAUGE_HVR4000:
+ case CX88_BOARD_HAUPPAUGE_HVR4000LITE:
if (0 == core->i2c_rc)
hauppauge_eeprom(core, eeprom);
break;
@@ -2607,7 +2846,27 @@ static void cx88_card_setup(struct cx88_core *core)
tea5767_cfg.priv = &ctl;
cx88_call_i2c_clients(core, TUNER_SET_CONFIG, &tea5767_cfg);
+ break;
}
+ case CX88_BOARD_TEVII_S420:
+ case CX88_BOARD_TEVII_S460:
+ cx_write(MO_SRST_IO, 0);
+ msleep(100);
+ cx_write(MO_SRST_IO, 1);
+ msleep(100);
+ break;
+ case CX88_BOARD_OMICOM_SS4_PCI:
+ cx_write(MO_SRST_IO, 0);
+ msleep(100);
+ cx_write(MO_SRST_IO, 1);
+ msleep(100);
+ break;
+ case CX88_BOARD_TBS_8920:
+ cx_write(MO_SRST_IO, 0);
+ msleep(100);
+ cx_write(MO_SRST_IO, 1);
+ msleep(100);
+ break;
} /*end switch() */
diff --git a/linux/drivers/media/video/cx88/cx88-dvb.c b/linux/drivers/media/video/cx88/cx88-dvb.c
index 68e56be1f..da1ad2b6b 100644
--- a/linux/drivers/media/video/cx88/cx88-dvb.c
+++ b/linux/drivers/media/video/cx88/cx88-dvb.c
@@ -49,6 +49,11 @@
#include "tuner-simple.h"
#include "tda9887.h"
#include "s5h1411.h"
+#include "stv0299.h"
+#include "z0194a.h"
+#include "stv0288.h"
+#include "stb6000.h"
+#include "cx24116.h"
MODULE_DESCRIPTION("driver for cx2388x based DVB cards");
MODULE_AUTHOR("Chris Pascoe <c.pascoe@itee.uq.edu.au>");
@@ -376,37 +381,28 @@ static int geniatech_dvbs_set_voltage(struct dvb_frontend *fe,
return 0;
}
-static int cx88_pci_nano_callback(void *ptr, int command, int arg)
+static int tevii_dvbs_set_voltage(struct dvb_frontend *fe,
+ fe_sec_voltage_t voltage)
{
- struct cx88_core *core = ptr;
-
- switch (command) {
- case XC2028_TUNER_RESET:
- /* Send the tuner in then out of reset */
- dprintk(1, "%s: XC2028_TUNER_RESET %d\n", __func__, arg);
-
- switch (core->boardnr) {
- case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO:
- /* GPIO-4 xc3028 tuner */
-
- cx_set(MO_GP0_IO, 0x00001000);
- cx_clear(MO_GP0_IO, 0x00000010);
- msleep(100);
- cx_set(MO_GP0_IO, 0x00000010);
- msleep(100);
- break;
- }
+ struct cx8802_dev *dev= fe->dvb->priv;
+ struct cx88_core *core = dev->core;
- break;
- case XC2028_RESET_CLK:
- dprintk(1, "%s: XC2028_RESET_CLK %d\n", __func__, arg);
- break;
- default:
- dprintk(1, "%s: unknown command %d, arg %d\n", __func__,
- command, arg);
- return -EINVAL;
+ switch (voltage) {
+ case SEC_VOLTAGE_13:
+ printk("LNB Voltage SEC_VOLTAGE_13\n");
+ cx_write(MO_GP0_IO, 0x00006040);
+ break;
+ case SEC_VOLTAGE_18:
+ printk("LNB Voltage SEC_VOLTAGE_18\n");
+ cx_write(MO_GP0_IO, 0x00006060);
+ break;
+ case SEC_VOLTAGE_OFF:
+ printk("LNB Voltage SEC_VOLTAGE_off\n");
+ break;
}
+ if (core->prev_set_voltage)
+ return core->prev_set_voltage(fe, voltage);
return 0;
}
@@ -457,7 +453,12 @@ static struct s5h1409_config kworld_atsc_120_config = {
static struct xc5000_config pinnacle_pctv_hd_800i_tuner_config = {
.i2c_address = 0x64,
.if_khz = 5380,
- .tuner_callback = cx88_tuner_callback,
+};
+
+static struct zl10353_config cx88_pinnacle_hybrid_pctv = {
+ .demod_address = (0x1e >> 1),
+ .no_tuner = 1,
+ .if2 = 45600,
};
static struct zl10353_config cx88_geniatech_x8000_mt = {
@@ -484,7 +485,6 @@ static struct s5h1411_config dvico_fusionhdtv7_config = {
static struct xc5000_config dvico_fusionhdtv7_tuner_config = {
.i2c_address = 0xc2 >> 1,
.if_khz = 5380,
- .tuner_callback = cx88_tuner_callback,
};
static int attach_xc3028(u8 addr, struct cx8802_dev *dev)
@@ -495,7 +495,6 @@ static int attach_xc3028(u8 addr, struct cx8802_dev *dev)
.i2c_adap = &dev->core->i2c_adap,
.i2c_addr = addr,
.ctrl = &ctl,
- .callback = cx88_tuner_callback,
};
if (!dev->dvb.frontend) {
@@ -525,6 +524,60 @@ static int attach_xc3028(u8 addr, struct cx8802_dev *dev)
return 0;
}
+static int cx24116_set_ts_param(struct dvb_frontend *fe,
+ int is_punctured)
+{
+ struct cx8802_dev *dev = fe->dvb->priv;
+ dev->ts_gen_cntrl = 0x2;
+
+ return 0;
+}
+
+static int cx24116_reset_device(struct dvb_frontend *fe)
+{
+ struct cx8802_dev *dev = fe->dvb->priv;
+ struct cx88_core *core = dev->core;
+
+ /* Reset the part */
+ cx_write(MO_SRST_IO, 0);
+ msleep(10);
+ cx_write(MO_SRST_IO, 1);
+ msleep(10);
+
+ return 0;
+}
+
+static struct cx24116_config hauppauge_hvr4000_config = {
+ .demod_address = 0x05,
+ .set_ts_params = cx24116_set_ts_param,
+ .reset_device = cx24116_reset_device,
+};
+
+static struct cx24116_config tevii_s460_config = {
+ .demod_address = 0x55,
+ .set_ts_params = cx24116_set_ts_param,
+ .reset_device = cx24116_reset_device,
+};
+
+static struct stv0299_config tevii_tuner_sharp_config = {
+ .demod_address = 0x68,
+ .inittab = sharp_z0194a__inittab,
+ .mclk = 88000000UL,
+ .invert = 1,
+ .skip_reinit = 0,
+ .lock_output = 1,
+ .volt13_op0_op1 = STV0299_VOLT13_OP1,
+ .min_delay_ms = 100,
+ .set_symbol_rate = sharp_z0194a__set_symbol_rate,
+ .set_ts_params = cx24116_set_ts_param,
+};
+
+static struct stv0288_config tevii_tuner_earda_config = {
+ .demod_address = 0x68,
+ .min_delay_ms = 100,
+ .set_ts_params = cx24116_set_ts_param,
+};
+
static int dvb_register(struct cx8802_dev *dev)
{
struct cx88_core *core = dev->core;
@@ -793,7 +846,7 @@ static int dvb_register(struct cx8802_dev *dev)
&core->i2c_adap);
if (dev->dvb.frontend) {
if (!dvb_attach(isl6421_attach, dev->dvb.frontend,
- &core->i2c_adap, 0x08, 0x00, 0x00))
+ &core->i2c_adap, 0x08, ISL6421_DCL, 0x00))
goto frontend_detach;
}
break;
@@ -835,10 +888,9 @@ static int dvb_register(struct cx8802_dev *dev)
struct xc2028_config cfg = {
.i2c_adap = &core->i2c_adap,
.i2c_addr = 0x61,
- .callback = cx88_pci_nano_callback,
};
static struct xc2028_ctrl ctl = {
- .fname = "xc3028-v27.fw",
+ .fname = XC2028_DEFAULT_FIRMWARE,
.max_len = 64,
.scode_table = XC3028_FE_OREN538,
};
@@ -851,10 +903,13 @@ static int dvb_register(struct cx8802_dev *dev)
break;
case CX88_BOARD_PINNACLE_HYBRID_PCTV:
dev->dvb.frontend = dvb_attach(zl10353_attach,
- &cx88_geniatech_x8000_mt,
+ &cx88_pinnacle_hybrid_pctv,
&core->i2c_adap);
- if (attach_xc3028(0x61, dev) < 0)
- goto frontend_detach;
+ if (dev->dvb.frontend) {
+ dev->dvb.frontend->ops.i2c_gate_ctrl = NULL;
+ if (attach_xc3028(0x61, dev) < 0)
+ goto frontend_detach;
+ }
break;
case CX88_BOARD_GENIATECH_X8000_MT:
dev->ts_gen_cntrl = 0x00;
@@ -883,6 +938,70 @@ static int dvb_register(struct cx8802_dev *dev)
goto frontend_detach;
}
break;
+ case CX88_BOARD_HAUPPAUGE_HVR4000:
+ case CX88_BOARD_HAUPPAUGE_HVR4000LITE:
+ /* Support for DVB-S only, not DVB-T support */
+ dev->dvb.frontend = dvb_attach(cx24116_attach,
+ &hauppauge_hvr4000_config,
+ &dev->core->i2c_adap);
+ if (dev->dvb.frontend) {
+ dvb_attach(isl6421_attach, dev->dvb.frontend,
+ &dev->core->i2c_adap,
+ 0x08, ISL6421_DCL, 0x00);
+ }
+ break;
+ case CX88_BOARD_TEVII_S420:
+ dev->dvb.frontend = dvb_attach(stv0299_attach,
+ &tevii_tuner_sharp_config,
+ &core->i2c_adap);
+ if (dev->dvb.frontend != NULL) {
+ if (!dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x60,
+ &core->i2c_adap, DVB_PLL_OPERA1))
+ goto frontend_detach;
+ core->prev_set_voltage = dev->dvb.frontend->ops.set_voltage;
+ dev->dvb.frontend->ops.set_voltage = tevii_dvbs_set_voltage;
+
+ } else {
+ dev->dvb.frontend = dvb_attach(stv0288_attach,
+ &tevii_tuner_earda_config,
+ &core->i2c_adap);
+ if (dev->dvb.frontend != NULL) {
+ if (!dvb_attach(stb6000_attach, dev->dvb.frontend, 0x61,
+ &core->i2c_adap))
+ goto frontend_detach;
+ core->prev_set_voltage = dev->dvb.frontend->ops.set_voltage;
+ dev->dvb.frontend->ops.set_voltage = tevii_dvbs_set_voltage;
+
+ }
+ }
+ break;
+ case CX88_BOARD_TEVII_S460:
+ dev->dvb.frontend = dvb_attach(cx24116_attach,
+ &tevii_s460_config,
+ &core->i2c_adap);
+ if (dev->dvb.frontend != NULL) {
+ core->prev_set_voltage = dev->dvb.frontend->ops.set_voltage;
+ dev->dvb.frontend->ops.set_voltage = tevii_dvbs_set_voltage;
+ }
+ break;
+ case CX88_BOARD_OMICOM_SS4_PCI:
+ dev->dvb.frontend = dvb_attach(cx24116_attach,
+ &hauppauge_hvr4000_config,
+ &core->i2c_adap);
+ if (dev->dvb.frontend != NULL) {
+ core->prev_set_voltage = dev->dvb.frontend->ops.set_voltage;
+ dev->dvb.frontend->ops.set_voltage = tevii_dvbs_set_voltage;
+ }
+ break;
+ case CX88_BOARD_TBS_8920:
+ dev->dvb.frontend = dvb_attach(cx24116_attach,
+ &hauppauge_hvr4000_config,
+ &core->i2c_adap);
+ if (dev->dvb.frontend != NULL) {
+ core->prev_set_voltage = dev->dvb.frontend->ops.set_voltage;
+ dev->dvb.frontend->ops.set_voltage = tevii_dvbs_set_voltage;
+ }
+ break;
default:
printk(KERN_ERR "%s/2: The frontend of your DVB/ATSC card isn't supported yet\n",
core->name);
@@ -894,6 +1013,8 @@ static int dvb_register(struct cx8802_dev *dev)
core->name);
return -EINVAL;
}
+ /* define general-purpose callback pointer */
+ dev->dvb.frontend->callback = cx88_tuner_callback;
/* Ensure all frontends negotiate bus access */
dev->dvb.frontend->ops.ts_bus_ctrl = cx88_dvb_bus_ctrl;
diff --git a/linux/drivers/media/video/cx88/cx88-i2c.c b/linux/drivers/media/video/cx88/cx88-i2c.c
index d7406a994..8e74d64fd 100644
--- a/linux/drivers/media/video/cx88/cx88-i2c.c
+++ b/linux/drivers/media/video/cx88/cx88-i2c.c
@@ -201,7 +201,23 @@ int cx88_i2c_init(struct cx88_core *core, struct pci_dev *pci)
core->i2c_rc = i2c_bit_add_bus(&core->i2c_adap);
if (0 == core->i2c_rc) {
+ static u8 tuner_data[] =
+ { 0x0b, 0xdc, 0x86, 0x52 };
+ static struct i2c_msg tuner_msg =
+ { .flags = 0, .addr = 0xc2 >> 1, .buf = tuner_data, .len = 4 };
+
dprintk(1, "i2c register ok\n");
+ switch( core->boardnr ) {
+ case CX88_BOARD_HAUPPAUGE_HVR1300:
+ case CX88_BOARD_HAUPPAUGE_HVR3000:
+ case CX88_BOARD_HAUPPAUGE_HVR4000:
+ printk("%s: i2c init: enabling analog demod on HVR1300/3000/4000 tuner\n",
+ core->name);
+ i2c_transfer(core->i2c_client.adapter, &tuner_msg, 1);
+ break;
+ default:
+ break;
+ }
if (i2c_scan)
do_i2c_scan(core->name,&core->i2c_client);
} else
diff --git a/linux/drivers/media/video/cx88/cx88-input.c b/linux/drivers/media/video/cx88/cx88-input.c
index 7d174382e..bcecb5bbd 100644
--- a/linux/drivers/media/video/cx88/cx88-input.c
+++ b/linux/drivers/media/video/cx88/cx88-input.c
@@ -237,6 +237,8 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci)
case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1:
case CX88_BOARD_HAUPPAUGE_HVR1100:
case CX88_BOARD_HAUPPAUGE_HVR3000:
+ case CX88_BOARD_HAUPPAUGE_HVR4000:
+ case CX88_BOARD_HAUPPAUGE_HVR4000LITE:
ir_codes = ir_codes_hauppauge_new;
ir_type = IR_TYPE_RC5;
ir->sampling = 1;
@@ -272,6 +274,7 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci)
ir->polling = 1; /* ms */
break;
case CX88_BOARD_PROLINK_PV_8000GT:
+ case CX88_BOARD_PROLINK_PV_GLOBAL_XTREME:
ir_codes = ir_codes_pixelview_new;
ir->gpio_addr = MO_GP1_IO;
ir->mask_keycode = 0x3f;
@@ -409,7 +412,7 @@ void cx88_ir_irq(struct cx88_core *core)
{
struct cx88_IR *ir = core->ir;
u32 samples, ircode;
- int i;
+ int i, start, range, toggle, dev, code;
if (NULL == ir)
return;
@@ -478,6 +481,34 @@ void cx88_ir_irq(struct cx88_core *core)
case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1:
case CX88_BOARD_HAUPPAUGE_HVR1100:
case CX88_BOARD_HAUPPAUGE_HVR3000:
+ case CX88_BOARD_HAUPPAUGE_HVR4000:
+ case CX88_BOARD_HAUPPAUGE_HVR4000LITE:
+ ircode = ir_decode_biphase(ir->samples, ir->scount, 5, 7);
+ ir_dprintk("biphase decoded: %x\n", ircode);
+ /*
+ * RC5 has an extension bit which adds a new range
+ * of available codes, this is detected here. Also
+ * hauppauge remotes (black/silver) always use
+ * specific device ids. If we do not filter the
+ * device ids then messages destined for devices
+ * such as TVs (id=0) will get through to the
+ * device causing mis-fired events.
+ */
+ /* split rc5 data block ... */
+ start = (ircode & 0x2000) >> 13;
+ range = (ircode & 0x1000) >> 12;
+ toggle= (ircode & 0x0800) >> 11;
+ dev = (ircode & 0x07c0) >> 6;
+ code = (ircode & 0x003f) | ((range << 6) ^ 0x0040);
+ if( start != 1)
+ /* no key pressed */
+ break;
+ if ( dev != 0x1e && dev != 0x1f )
+ /* not a hauppauge remote */
+ break;
+ ir_input_keydown(ir->input, &ir->ir, code, ircode);
+ ir->release = jiffies + msecs_to_jiffies(120);
+ break;
case CX88_BOARD_PINNACLE_PCTV_HD_800i:
ircode = ir_decode_biphase(ir->samples, ir->scount, 5, 7);
ir_dprintk("biphase decoded: %x\n", ircode);
diff --git a/linux/drivers/media/video/cx88/cx88-video.c b/linux/drivers/media/video/cx88/cx88-video.c
index 3a7a9ed17..c4fdb6da2 100644
--- a/linux/drivers/media/video/cx88/cx88-video.c
+++ b/linux/drivers/media/video/cx88/cx88-video.c
@@ -1065,6 +1065,8 @@ static int video_open(struct inode *inode, struct file *file)
}
unlock_kernel();
+ atomic_inc(&core->users);
+
return 0;
}
@@ -1152,7 +1154,8 @@ static int video_release(struct inode *inode, struct file *file)
file->private_data = NULL;
kfree(fh);
- cx88_call_i2c_clients (dev->core, TUNER_SET_STANDBY, NULL);
+ if(atomic_dec_and_test(&dev->core->users))
+ cx88_call_i2c_clients (dev->core, TUNER_SET_STANDBY, NULL);
return 0;
}
diff --git a/linux/drivers/media/video/cx88/cx88.h b/linux/drivers/media/video/cx88/cx88.h
index 751176e7a..e42ce0016 100644
--- a/linux/drivers/media/video/cx88/cx88.h
+++ b/linux/drivers/media/video/cx88/cx88.h
@@ -222,6 +222,13 @@ extern struct sram_channel cx88_sram_channels[];
#define CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD 65
#define CX88_BOARD_PROLINK_PV_8000GT 66
#define CX88_BOARD_KWORLD_ATSC_120 67
+#define CX88_BOARD_HAUPPAUGE_HVR4000 68
+#define CX88_BOARD_HAUPPAUGE_HVR4000LITE 69
+#define CX88_BOARD_TEVII_S460 70
+#define CX88_BOARD_OMICOM_SS4_PCI 71
+#define CX88_BOARD_TBS_8920 72
+#define CX88_BOARD_TEVII_S420 73
+#define CX88_BOARD_PROLINK_PV_GLOBAL_XTREME 74
enum cx88_itype {
CX88_VMUX_COMPOSITE1 = 1,
@@ -346,6 +353,7 @@ struct cx88_core {
struct mutex lock;
/* various v4l controls */
u32 freq;
+ atomic_t users;
/* cx88-video needs to access cx8802 for hybrid tuner pll access. */
struct cx8802_dev *dvbdev;
@@ -622,7 +630,7 @@ extern void cx88_call_i2c_clients(struct cx88_core *core,
/* ----------------------------------------------------------- */
/* cx88-cards.c */
-extern int cx88_tuner_callback(void *dev, int command, int arg);
+extern int cx88_tuner_callback(void *dev, int component, int command, int arg);
extern int cx88_get_resources(const struct cx88_core *core,
struct pci_dev *pci);
extern struct cx88_core *cx88_core_create(struct pci_dev *pci, int nr);
diff --git a/linux/drivers/media/video/em28xx/em28xx-audio.c b/linux/drivers/media/video/em28xx/em28xx-audio.c
index 14ef4da24..d6856b331 100644
--- a/linux/drivers/media/video/em28xx/em28xx-audio.c
+++ b/linux/drivers/media/video/em28xx/em28xx-audio.c
@@ -127,10 +127,10 @@ static void em28xx_audio_isocirq(struct urb *urb)
if (oldptr + length >= runtime->buffer_size) {
unsigned int cnt =
- runtime->buffer_size - oldptr - 1;
+ runtime->buffer_size - oldptr;
memcpy(runtime->dma_area + oldptr * stride, cp,
cnt * stride);
- memcpy(runtime->dma_area, cp + cnt,
+ memcpy(runtime->dma_area, cp + cnt * stride,
length * stride - cnt * stride);
} else {
memcpy(runtime->dma_area + oldptr * stride, cp,
diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c
index a4a071114..4f078da31 100644
--- a/linux/drivers/media/video/em28xx/em28xx-cards.c
+++ b/linux/drivers/media/video/em28xx/em28xx-cards.c
@@ -578,7 +578,7 @@ struct em28xx_board em28xx_boards[] = {
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = TVP5150_COMPOSITE1,
- .amux = 1,
+ .amux = 3,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = TVP5150_SVIDEO,
@@ -931,22 +931,23 @@ struct em28xx_board em28xx_boards[] = {
},
[EM2880_BOARD_KWORLD_DVB_310U] = {
.name = "KWorld DVB-T 310U",
- .valid = EM28XX_BOARD_NOT_VALIDATED,
.vchannels = 3,
.tuner_type = TUNER_XC2028,
+ .has_dvb = 1,
+ .mts_firmware = 1,
.decoder = EM28XX_TVP5150,
.input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = TVP5150_COMPOSITE0,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = TVP5150_COMPOSITE1,
- .amux = 1,
- }, {
+ .amux = EM28XX_AMUX_AC97_LINE_IN,
+ }, { /* S-video has not been tested yet */
.type = EM28XX_VMUX_SVIDEO,
.vmux = TVP5150_SVIDEO,
- .amux = 1,
+ .amux = EM28XX_AMUX_AC97_LINE_IN,
} },
},
[EM2881_BOARD_DNT_DA2_HYBRID] = {
@@ -1118,6 +1119,10 @@ struct usb_device_id em28xx_id_table [] = {
.driver_info = EM2880_BOARD_KWORLD_DVB_305U },
{ USB_DEVICE(0xeb1a, 0xe310),
.driver_info = EM2880_BOARD_MSI_DIGIVOX_AD },
+#if 0
+ { USB_DEVICE(0xeb1a, 0xe310),
+ .driver_info = EM2880_BOARD_KWORLD_DVB_310U },
+#endif
{ USB_DEVICE(0xeb1a, 0xa316),
.driver_info = EM2883_BOARD_KWORLD_HYBRID_A316 },
{ USB_DEVICE(0xeb1a, 0xe320),
@@ -1184,9 +1189,6 @@ struct usb_device_id em28xx_id_table [] = {
.driver_info = EM2800_BOARD_LEADTEK_WINFAST_USBII },
{ USB_DEVICE(0x093b, 0xa005),
.driver_info = EM2861_BOARD_PLEXTOR_PX_TV100U },
-#if 0
- EM2880_BOARD_KWORLD_DVB_310U },
-#endif
{ },
};
MODULE_DEVICE_TABLE(usb, em28xx_id_table);
@@ -1264,6 +1266,7 @@ static struct em28xx_reg_seq em2882_terratec_hybrid_xs_digital[] = {
static struct em28xx_hash_table em28xx_eeprom_hash [] = {
/* P/N: SA 60002070465 Tuner: TVF7533-MF */
{0x6ce05a8f, EM2820_BOARD_PROLINK_PLAYTV_USB2, TUNER_YMEC_TVF_5533MF},
+ {0x966a0441, EM2880_BOARD_KWORLD_DVB_310U, TUNER_XC2028},
};
/* I2C devicelist hash table for devices with generic USB IDs */
@@ -1273,7 +1276,7 @@ static struct em28xx_hash_table em28xx_i2c_hash[] = {
{0x1ba50080, EM2860_BOARD_POINTNIX_INTRAORAL_CAMERA, TUNER_ABSENT},
};
-int em28xx_tuner_callback(void *ptr, int command, int arg)
+int em28xx_tuner_callback(void *ptr, int component, int command, int arg)
{
int rc = 0;
struct em28xx *dev = ptr;
@@ -1534,9 +1537,12 @@ static void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl)
/* djh - Not sure which demod we need here */
ctl->demod = XC3028_FE_DEFAULT;
break;
+ case EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600:
+ ctl->demod = XC3028_FE_DEFAULT;
+ ctl->fname = XC3028L_DEFAULT_FIRMWARE;
+ break;
case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950:
case EM2880_BOARD_PINNACLE_PCTV_HD_PRO:
- case EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600:
/* FIXME: Better to specify the needed IF */
ctl->demod = XC3028_FE_DEFAULT;
break;
@@ -1746,6 +1752,20 @@ void em28xx_card_setup(struct em28xx *dev)
break;
case EM2820_BOARD_UNKNOWN:
case EM2800_BOARD_UNKNOWN:
+ /*
+ * The K-WORLD DVB-T 310U is detected as an MSI Digivox AD.
+ *
+ * This occurs because they share identical USB vendor and
+ * product IDs.
+ *
+ * What we do here is look up the EEPROM hash of the K-WORLD
+ * and if it is found then we decide that we do not have
+ * a DIGIVOX and reset the device to the K-WORLD instead.
+ *
+ * This solution is only valid if they do not share eeprom
+ * hash identities which has not been determined as yet.
+ */
+ case EM2880_BOARD_MSI_DIGIVOX_AD:
if (!em28xx_hint_board(dev))
em28xx_set_model(dev);
break;
diff --git a/linux/drivers/media/video/em28xx/em28xx-dvb.c b/linux/drivers/media/video/em28xx/em28xx-dvb.c
index 79c11b1d0..b98749b8d 100644
--- a/linux/drivers/media/video/em28xx/em28xx-dvb.c
+++ b/linux/drivers/media/video/em28xx/em28xx-dvb.c
@@ -250,7 +250,6 @@ static int attach_xc3028(u8 addr, struct em28xx *dev)
memset(&cfg, 0, sizeof(cfg));
cfg.i2c_adap = &dev->i2c_adap;
cfg.i2c_addr = addr;
- cfg.callback = em28xx_tuner_callback;
if (!dev->dvb->frontend) {
printk(KERN_ERR "%s/2: dvb frontend not attached. "
@@ -453,6 +452,15 @@ static int dvb_init(struct em28xx *dev)
goto out_free;
}
break;
+ case EM2880_BOARD_KWORLD_DVB_310U:
+ dvb->frontend = dvb_attach(zl10353_attach,
+ &em28xx_zl10353_with_xc3028,
+ &dev->i2c_adap);
+ if (attach_xc3028(0x61, dev) < 0) {
+ result = -EINVAL;
+ goto out_free;
+ }
+ break;
default:
printk(KERN_ERR "%s/2: The frontend of your DVB/ATSC card"
" isn't supported yet\n",
@@ -466,6 +474,8 @@ static int dvb_init(struct em28xx *dev)
result = -EINVAL;
goto out_free;
}
+ /* define general-purpose callback pointer */
+ dvb->frontend->callback = em28xx_tuner_callback;
/* register everything */
result = register_dvb(dvb, THIS_MODULE, dev, &dev->udev->dev);
diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h
index 4bdff9619..d73b8c983 100644
--- a/linux/drivers/media/video/em28xx/em28xx.h
+++ b/linux/drivers/media/video/em28xx/em28xx.h
@@ -541,7 +541,7 @@ extern struct em28xx_board em28xx_boards[];
extern struct usb_device_id em28xx_id_table[];
extern const unsigned int em28xx_bcount;
void em28xx_set_ir(struct em28xx *dev, struct IR_i2c *ir);
-int em28xx_tuner_callback(void *ptr, int command, int arg);
+int em28xx_tuner_callback(void *ptr, int component, int command, int arg);
/* Provided by em28xx-input.c */
/* TODO: Check if the standard get_key handlers on ir-common can be used */
diff --git a/linux/drivers/media/video/gspca/Kconfig b/linux/drivers/media/video/gspca/Kconfig
index 42b90742b..aa7f3eb7e 100644
--- a/linux/drivers/media/video/gspca/Kconfig
+++ b/linux/drivers/media/video/gspca/Kconfig
@@ -1,13 +1,203 @@
-config USB_GSPCA
- tristate "USB GSPCA driver"
+menuconfig USB_GSPCA
+ tristate "GSPCA based webcams"
depends on VIDEO_V4L2
+ default m
---help---
- Say Y here if you want support for various USB webcams.
+ Say Y here if you want to enable selecting webcams based
+ on the GSPCA framework.
- See <file:Documentation/video4linux/gspca.txt> for more info.
+ See <file:Documentation/video4linux/gspca.txt> for more info.
- This driver uses the Video For Linux API. You must say Y or M to
- "Video For Linux" to use this driver.
+ This driver uses the Video For Linux API. You must say Y or M to
+ "Video For Linux" to use this driver.
- To compile this driver as modules, choose M here: the
- modules will be called gspca_xxxx.
+ To compile this driver as modules, choose M here: the
+ modules will be called gspca_main.
+
+
+if USB_GSPCA && VIDEO_V4L2
+
+source "drivers/media/video/gspca/m5602/Kconfig"
+
+config USB_GSPCA_CONEX
+ tristate "Conexant Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the Conexant chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_conex.
+
+config USB_GSPCA_ETOMS
+ tristate "Etoms USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the Etoms chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_etoms.
+
+config USB_GSPCA_MARS
+ tristate "Mars USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the Mars chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_mars.
+
+config USB_GSPCA_OV519
+ tristate "OV519 USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the OV519 chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_ov519.
+
+config USB_GSPCA_PAC207
+ tristate "Pixart PAC207 USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the PAC207 chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_pac207.
+
+config USB_GSPCA_PAC7311
+ tristate "Pixart PAC7311 USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the PAC7311 chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_pac7311.
+
+config USB_GSPCA_SONIXB
+ tristate "SN9C102 USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the SONIXB chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_sonixb.
+
+config USB_GSPCA_SONIXJ
+ tristate "SONIX JPEG USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the SONIXJ chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_sonixj
+
+config USB_GSPCA_SPCA500
+ tristate "SPCA500 USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the SPCA500 chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_spca500.
+
+config USB_GSPCA_SPCA501
+ tristate "SPCA501 USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the SPCA501 chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_spca501.
+
+config USB_GSPCA_SPCA505
+ tristate "SPCA505 USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the SPCA505 chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_spca505.
+
+config USB_GSPCA_SPCA506
+ tristate "SPCA506 USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the SPCA506 chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_spca506.
+
+config USB_GSPCA_SPCA508
+ tristate "SPCA508 USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the SPCA508 chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_spca508.
+
+config USB_GSPCA_SPCA561
+ tristate "SPCA561 USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the SPCA561 chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_spca561.
+
+config USB_GSPCA_STK014
+ tristate "Syntek DV4000 (STK014) USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the STK014 chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_stk014.
+
+config USB_GSPCA_SPCA5XX
+ tristate "SPCA5xx USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the Sunplus
+ SPCA504(abc) SPCA533 SPCA536 chips.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_spca5xx.
+
+config USB_GSPCA_T613
+ tristate "T613 (JPEG Compliance) USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the T613 chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_t613.
+
+config USB_GSPCA_TV8531
+ tristate "TV8532 USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the TV8531 chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_tv8532.
+
+config USB_GSPCA_VC032X
+ tristate "VC032X USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the VC032X chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_vc032x.
+
+config USB_GSPCA_ZC3XX
+ tristate "VC3xx USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the ZC3XX chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_zc3xx.
+
+endif
diff --git a/linux/drivers/media/video/gspca/Makefile b/linux/drivers/media/video/gspca/Makefile
index e68a89652..b87322f4a 100644
--- a/linux/drivers/media/video/gspca/Makefile
+++ b/linux/drivers/media/video/gspca/Makefile
@@ -1,29 +1,46 @@
-obj-$(CONFIG_USB_GSPCA) += gspca_main.o \
- gspca_conex.o gspca_etoms.o gspca_mars.o \
- gspca_ov519.o gspca_pac207.o gspca_pac7311.o \
- gspca_sonixb.o gspca_sonixj.o gspca_spca500.o gspca_spca501.o \
- gspca_spca505.o gspca_spca506.o gspca_spca508.o gspca_spca561.o \
- gspca_sunplus.o gspca_stk014.o gspca_t613.o gspca_tv8532.o \
- gspca_vc032x.o gspca_zc3xx.o
+obj-$(CONFIG_USB_GSPCA) += gspca_main.o
+obj-$(CONFIG_USB_GSPCA_CONEX) += gspca_conex.o
+obj-$(CONFIG_USB_GSPCA_ETOMS) += gspca_etoms.o
+obj-$(CONFIG_USB_GSPCA_MARS) += gspca_mars.o
+obj-$(CONFIG_USB_GSPCA_OV519) += gspca_ov519.o
+obj-$(CONFIG_USB_GSPCA_PAC207) += gspca_pac207.o
+obj-$(CONFIG_USB_GSPCA_PAC7311) += gspca_pac7311.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
+obj-$(CONFIG_USB_GSPCA_SPCA501) += gspca_spca501.o
+obj-$(CONFIG_USB_GSPCA_SPCA505) += gspca_spca505.o
+obj-$(CONFIG_USB_GSPCA_SPCA506) += gspca_spca506.o
+obj-$(CONFIG_USB_GSPCA_SPCA508) += gspca_spca508.o
+obj-$(CONFIG_USB_GSPCA_SPCA561) += gspca_spca561.o
+obj-$(CONFIG_USB_GSPCA_SUNPLUS) += gspca_sunplus.o
+obj-$(CONFIG_USB_GSPCA_STK014) += gspca_stk014.o
+obj-$(CONFIG_USB_GSPCA_T613) += gspca_t613.o
+obj-$(CONFIG_USB_GSPCA_TV8532) += gspca_tv8532.o
+obj-$(CONFIG_USB_GSPCA_VC032X) += gspca_vc032x.o
+obj-$(CONFIG_USB_GSPCA_ZC3XX) += gspca_zc3xx.o
+
+gspca_main-objs := gspca.o
+gspca_conex-objs := conex.o
+gspca_etoms-objs := etoms.o
+gspca_mars-objs := mars.o
+gspca_ov519-objs := ov519.o
+gspca_pac207-objs := pac207.o
+gspca_pac7311-objs := pac7311.o
+gspca_sonixb-objs := sonixb.o
+gspca_sonixj-objs := sonixj.o
+gspca_spca500-objs := spca500.o
+gspca_spca501-objs := spca501.o
+gspca_spca505-objs := spca505.o
+gspca_spca506-objs := spca506.o
+gspca_spca508-objs := spca508.o
+gspca_spca561-objs := spca561.o
+gspca_stk014-objs := stk014.o
+gspca_sunplus-objs := sunplus.o
+gspca_t613-objs := t613.o
+gspca_tv8532-objs := tv8532.o
+gspca_vc032x-objs := vc032x.o
+gspca_zc3xx-objs := zc3xx.o
+
+obj-$(CONFIG_USB_M5602) += m5602/
-gspca_main-objs := gspca.o
-gspca_conex-objs := conex.o
-gspca_etoms-objs := etoms.o
-gspca_mars-objs := mars.o
-gspca_ov519-objs := ov519.o
-gspca_pac207-objs := pac207.o
-gspca_pac7311-objs := pac7311.o
-gspca_sonixb-objs := sonixb.o
-gspca_sonixj-objs := sonixj.o
-gspca_spca500-objs := spca500.o
-gspca_spca501-objs := spca501.o
-gspca_spca505-objs := spca505.o
-gspca_spca506-objs := spca506.o
-gspca_spca508-objs := spca508.o
-gspca_spca561-objs := spca561.o
-gspca_stk014-objs := stk014.o
-gspca_sunplus-objs := sunplus.o
-gspca_t613-objs := t613.o
-gspca_tv8532-objs := tv8532.o
-gspca_vc032x-objs := vc032x.o
-gspca_zc3xx-objs := zc3xx.o
diff --git a/linux/drivers/media/video/gspca/conex.c b/linux/drivers/media/video/gspca/conex.c
index 4d9f4cc25..a9d51ba7c 100644
--- a/linux/drivers/media/video/gspca/conex.c
+++ b/linux/drivers/media/video/gspca/conex.c
@@ -837,12 +837,13 @@ static int sd_init(struct gspca_dev *gspca_dev)
return 0;
}
-static void sd_start(struct gspca_dev *gspca_dev)
+static int sd_start(struct gspca_dev *gspca_dev)
{
cx11646_initsize(gspca_dev);
cx11646_fw(gspca_dev);
cx_sensor(gspca_dev);
cx11646_jpeg(gspca_dev);
+ return 0;
}
static void sd_stop0(struct gspca_dev *gspca_dev)
diff --git a/linux/drivers/media/video/gspca/etoms.c b/linux/drivers/media/video/gspca/etoms.c
index 86f3932c2..28869ef26 100644
--- a/linux/drivers/media/video/gspca/etoms.c
+++ b/linux/drivers/media/video/gspca/etoms.c
@@ -691,7 +691,7 @@ static int sd_init(struct gspca_dev *gspca_dev)
}
/* -- start the camera -- */
-static void sd_start(struct gspca_dev *gspca_dev)
+static int sd_start(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
@@ -704,6 +704,7 @@ static void sd_start(struct gspca_dev *gspca_dev)
reg_w_val(gspca_dev, ET_RESET_ALL, 0x08);
et_video(gspca_dev, 1); /* video on */
+ return 0;
}
static void sd_stopN(struct gspca_dev *gspca_dev)
diff --git a/linux/drivers/media/video/gspca/finepix.c b/linux/drivers/media/video/gspca/finepix.c
new file mode 100644
index 000000000..65d3cbfe6
--- /dev/null
+++ b/linux/drivers/media/video/gspca/finepix.c
@@ -0,0 +1,466 @@
+/*
+ * Fujifilm Finepix subdriver
+ *
+ * Copyright (C) 2008 Frank Zago
+ *
+ * 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
+ */
+
+#define MODULE_NAME "finepix"
+
+#include "gspca.h"
+
+MODULE_AUTHOR("Frank Zago <frank@zago.net>");
+MODULE_DESCRIPTION("Fujifilm FinePix USB V4L2 driver");
+MODULE_LICENSE("GPL");
+
+/* Default timeout, in ms */
+#define FPIX_TIMEOUT (HZ / 10)
+
+/* Maximum transfer size to use. The windows driver reads by chunks of
+ * 0x2000 bytes, so do the same. Note: reading more seems to work
+ * too. */
+#define FPIX_MAX_TRANSFER 0x2000
+
+/* Structure to hold all of our device specific stuff */
+struct usb_fpix {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+
+ /*
+ * USB stuff
+ */
+ struct usb_ctrlrequest ctrlreq;
+ struct urb *control_urb;
+ struct timer_list bulk_timer;
+
+ enum {
+ FPIX_NOP, /* inactive, else streaming */
+ FPIX_RESET, /* must reset */
+ FPIX_REQ_FRAME, /* requesting a frame */
+ FPIX_READ_FRAME, /* reading frame */
+ } state;
+
+ /*
+ * Driver stuff
+ */
+ struct delayed_work wqe;
+ struct completion can_close;
+ int streaming;
+};
+
+/* Delay after which claim the next frame. If the delay is too small,
+ * the camera will return old frames. On the 4800Z, 20ms is bad, 25ms
+ * will fail every 4 or 5 frames, but 30ms is perfect. */
+#define NEXT_FRAME_DELAY (((HZ * 30) + 999) / 1000)
+
+#define dev_new_state(new_state) { \
+ PDEBUG(D_STREAM, "new state from %d to %d at %s:%d", \
+ dev->state, new_state, __func__, __LINE__); \
+ dev->state = new_state; \
+}
+
+/* These cameras only support 320x200. */
+static struct v4l2_pix_format fpix_mode[1] = {
+ { 320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0}
+};
+
+/* Reads part of a frame */
+static void read_frame_part(struct usb_fpix *dev)
+{
+ int ret;
+
+ PDEBUG(D_STREAM, "read_frame_part");
+
+ /* Reads part of a frame */
+ ret = usb_submit_urb(dev->gspca_dev.urb[0], GFP_ATOMIC);
+ if (ret) {
+ dev_new_state(FPIX_RESET);
+ schedule_delayed_work(&dev->wqe, 1);
+ PDEBUG(D_STREAM, "usb_submit_urb failed with %d",
+ ret);
+ } else {
+ /* Sometimes we never get a callback, so use a timer.
+ * Is this masking a bug somewhere else? */
+ dev->bulk_timer.expires = jiffies + msecs_to_jiffies(150);
+ add_timer(&dev->bulk_timer);
+ }
+}
+
+/* Callback for URBs. */
+static void urb_callback(struct urb *urb)
+{
+ struct gspca_dev *gspca_dev = urb->context;
+ struct usb_fpix *dev = (struct usb_fpix *) gspca_dev;
+
+ PDEBUG(D_PACK,
+ "enter urb_callback - status=%d, length=%d",
+ urb->status, urb->actual_length);
+
+ if (dev->state == FPIX_READ_FRAME)
+ del_timer(&dev->bulk_timer);
+
+ if (urb->status != 0) {
+ /* We kill a stuck urb every 50 frames on average, so don't
+ * display a log message for that. */
+ if (urb->status != -ECONNRESET)
+ PDEBUG(D_STREAM, "bad URB status %d", urb->status);
+ dev_new_state(FPIX_RESET);
+ schedule_delayed_work(&dev->wqe, 1);
+ }
+
+ switch (dev->state) {
+ case FPIX_REQ_FRAME:
+ dev_new_state(FPIX_READ_FRAME);
+ read_frame_part(dev);
+ break;
+
+ case FPIX_READ_FRAME: {
+ unsigned char *data = urb->transfer_buffer;
+ struct gspca_frame *frame;
+
+ frame = gspca_get_i_frame(&dev->gspca_dev);
+ if (frame == NULL)
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ if (urb->actual_length < FPIX_MAX_TRANSFER ||
+ (data[urb->actual_length-2] == 0xff &&
+ data[urb->actual_length-1] == 0xd9)) {
+
+ /* If the result is less than what was asked
+ * for, then it's the end of the
+ * frame. Sometime the jpeg is not complete,
+ * but there's nothing we can do. We also end
+ * here if the the jpeg ends right at the end
+ * of the frame. */
+ if (frame)
+ gspca_frame_add(gspca_dev, LAST_PACKET,
+ frame,
+ data, urb->actual_length);
+ dev_new_state(FPIX_REQ_FRAME);
+ schedule_delayed_work(&dev->wqe, NEXT_FRAME_DELAY);
+ } else {
+
+ /* got a partial image */
+ if (frame)
+ gspca_frame_add(gspca_dev,
+ gspca_dev->last_packet_type
+ == LAST_PACKET
+ ? FIRST_PACKET : INTER_PACKET,
+ frame,
+ data, urb->actual_length);
+ read_frame_part(dev);
+ }
+ break;
+ }
+
+ case FPIX_NOP:
+ case FPIX_RESET:
+ PDEBUG(D_STREAM, "invalid state %d", dev->state);
+ break;
+ }
+}
+
+/* Request a new frame */
+static void request_frame(struct usb_fpix *dev)
+{
+ int ret;
+ struct gspca_dev *gspca_dev = &dev->gspca_dev;
+
+ /* Setup command packet */
+ memset(gspca_dev->usb_buf, 0, 12);
+ gspca_dev->usb_buf[0] = 0xd3;
+ gspca_dev->usb_buf[7] = 0x01;
+
+ /* Request a frame */
+ dev->ctrlreq.bRequestType =
+ USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
+ dev->ctrlreq.bRequest = USB_REQ_GET_STATUS;
+ dev->ctrlreq.wValue = 0;
+ dev->ctrlreq.wIndex = 0;
+ dev->ctrlreq.wLength = cpu_to_le16(12);
+
+ usb_fill_control_urb(dev->control_urb,
+ gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ (unsigned char *) &dev->ctrlreq,
+ gspca_dev->usb_buf,
+ 12, urb_callback, gspca_dev);
+
+ ret = usb_submit_urb(dev->control_urb, GFP_ATOMIC);
+ if (ret) {
+ dev_new_state(FPIX_RESET);
+ schedule_delayed_work(&dev->wqe, 1);
+ PDEBUG(D_STREAM, "usb_submit_urb failed with %d", ret);
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+
+/* State machine. */
+static void fpix_sm(struct work_struct *work)
+{
+ struct usb_fpix *dev = container_of(work, struct usb_fpix, wqe.work);
+
+ PDEBUG(D_STREAM, "fpix_sm state %d", dev->state);
+
+ /* verify that the device wasn't unplugged */
+ if (!dev->gspca_dev.present) {
+ PDEBUG(D_STREAM, "device is gone");
+ dev_new_state(FPIX_NOP);
+ complete(&dev->can_close);
+ return;
+ }
+
+ if (!dev->streaming) {
+ PDEBUG(D_STREAM, "stopping state machine");
+ dev_new_state(FPIX_NOP);
+ complete(&dev->can_close);
+ return;
+ }
+
+ switch (dev->state) {
+ case FPIX_RESET:
+ dev_new_state(FPIX_REQ_FRAME);
+ schedule_delayed_work(&dev->wqe, HZ / 10);
+ break;
+
+ case FPIX_REQ_FRAME:
+ /* get an image */
+ request_frame(dev);
+ break;
+
+ case FPIX_NOP:
+ case FPIX_READ_FRAME:
+ PDEBUG(D_STREAM, "invalid state %d", dev->state);
+ break;
+ }
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct cam *cam = &gspca_dev->cam;
+
+ cam->cam_mode = fpix_mode;
+ cam->nmodes = 1;
+ cam->epaddr = 0x01; /* todo: correct for all cams? */
+ cam->bulk_size = FPIX_MAX_TRANSFER;
+
+/* gspca_dev->nbalt = 1; * use bulk transfer */
+ return 0;
+}
+
+/* Stop streaming and free the ressources allocated by sd_start. */
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+ struct usb_fpix *dev = (struct usb_fpix *) gspca_dev;
+
+ dev->streaming = 0;
+
+ /* Stop the state machine */
+ if (dev->state != FPIX_NOP)
+ wait_for_completion(&dev->can_close);
+
+ usb_free_urb(dev->control_urb);
+ dev->control_urb = NULL;
+}
+
+/* Kill an URB that hasn't completed. */
+static void timeout_kill(unsigned long data)
+{
+ struct urb *urb = (struct urb *) data;
+
+ usb_unlink_urb(urb);
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ struct usb_fpix *dev = (struct usb_fpix *) gspca_dev;
+
+ INIT_DELAYED_WORK(&dev->wqe, fpix_sm);
+
+ init_timer(&dev->bulk_timer);
+ dev->bulk_timer.function = timeout_kill;
+
+ return 0;
+}
+
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct usb_fpix *dev = (struct usb_fpix *) gspca_dev;
+ int ret;
+ int size_ret;
+
+ /* Reset bulk in endpoint */
+ usb_clear_halt(gspca_dev->dev, gspca_dev->cam.epaddr);
+
+ /* Init the device */
+ memset(gspca_dev->usb_buf, 0, 12);
+ gspca_dev->usb_buf[0] = 0xc6;
+ gspca_dev->usb_buf[8] = 0x20;
+
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ USB_REQ_GET_STATUS,
+ USB_DIR_OUT | USB_TYPE_CLASS |
+ USB_RECIP_INTERFACE, 0, 0, gspca_dev->usb_buf,
+ 12, FPIX_TIMEOUT);
+
+ if (ret != 12) {
+ PDEBUG(D_STREAM, "usb_control_msg failed (%d)", ret);
+ ret = -EIO;
+ goto error;
+ }
+
+ /* Read the result of the command. Ignore the result, for it
+ * varies with the device. */
+ ret = usb_bulk_msg(gspca_dev->dev,
+ usb_rcvbulkpipe(gspca_dev->dev,
+ gspca_dev->cam.epaddr),
+ gspca_dev->usb_buf, FPIX_MAX_TRANSFER, &size_ret,
+ FPIX_TIMEOUT);
+ if (ret != 0) {
+ PDEBUG(D_STREAM, "usb_bulk_msg failed (%d)", ret);
+ ret = -EIO;
+ goto error;
+ }
+
+ /* Request a frame, but don't read it */
+ memset(gspca_dev->usb_buf, 0, 12);
+ gspca_dev->usb_buf[0] = 0xd3;
+ gspca_dev->usb_buf[7] = 0x01;
+
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ USB_REQ_GET_STATUS,
+ USB_DIR_OUT | USB_TYPE_CLASS |
+ USB_RECIP_INTERFACE, 0, 0, gspca_dev->usb_buf,
+ 12, FPIX_TIMEOUT);
+ if (ret != 12) {
+ PDEBUG(D_STREAM, "usb_control_msg failed (%d)", ret);
+ ret = -EIO;
+ goto error;
+ }
+
+ /* Again, reset bulk in endpoint */
+ usb_clear_halt(gspca_dev->dev, gspca_dev->cam.epaddr);
+
+ /* Allocate a control URB */
+ dev->control_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!dev->control_urb) {
+ PDEBUG(D_STREAM, "No free urbs available");
+ ret = -EIO;
+ goto error;
+ }
+
+ /* Various initializations. */
+ init_completion(&dev->can_close);
+ dev->bulk_timer.data = (unsigned long)dev->gspca_dev.urb[0];
+ dev->gspca_dev.urb[0]->complete = urb_callback;
+ dev->streaming = 1;
+
+ /* Schedule a frame request. */
+ dev_new_state(FPIX_REQ_FRAME);
+ schedule_delayed_work(&dev->wqe, 1);
+
+ return 0;
+
+error:
+ /* Free the ressources */
+ sd_stopN(gspca_dev);
+ return ret;
+}
+
+/* Table of supported USB devices */
+static const __devinitdata struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x04cb, 0x0104)},
+ {USB_DEVICE(0x04cb, 0x0109)},
+ {USB_DEVICE(0x04cb, 0x010b)},
+ {USB_DEVICE(0x04cb, 0x010f)},
+ {USB_DEVICE(0x04cb, 0x0111)},
+ {USB_DEVICE(0x04cb, 0x0113)},
+ {USB_DEVICE(0x04cb, 0x0115)},
+ {USB_DEVICE(0x04cb, 0x0117)},
+ {USB_DEVICE(0x04cb, 0x0119)},
+ {USB_DEVICE(0x04cb, 0x011b)},
+ {USB_DEVICE(0x04cb, 0x011d)},
+ {USB_DEVICE(0x04cb, 0x0121)},
+ {USB_DEVICE(0x04cb, 0x0123)},
+ {USB_DEVICE(0x04cb, 0x0125)},
+ {USB_DEVICE(0x04cb, 0x0127)},
+ {USB_DEVICE(0x04cb, 0x0129)},
+ {USB_DEVICE(0x04cb, 0x012b)},
+ {USB_DEVICE(0x04cb, 0x012d)},
+ {USB_DEVICE(0x04cb, 0x012f)},
+ {USB_DEVICE(0x04cb, 0x0131)},
+ {USB_DEVICE(0x04cb, 0x013b)},
+ {USB_DEVICE(0x04cb, 0x013d)},
+ {USB_DEVICE(0x04cb, 0x013f)},
+ {}
+};
+
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .config = sd_config,
+ .init = sd_init,
+ .start = sd_start,
+ .stopN = sd_stopN,
+};
+
+/* -- 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 usb_fpix),
+ THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+ .name = MODULE_NAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+#endif
+};
+
+/* -- module insert / remove -- */
+static int __init sd_mod_init(void)
+{
+ if (usb_register(&sd_driver) < 0)
+ return -1;
+ PDEBUG(D_PROBE, "registered");
+ return 0;
+}
+static void __exit sd_mod_exit(void)
+{
+ usb_deregister(&sd_driver);
+ PDEBUG(D_PROBE, "deregistered");
+}
+
+module_init(sd_mod_init);
+module_exit(sd_mod_exit);
diff --git a/linux/drivers/media/video/gspca/gspca.c b/linux/drivers/media/video/gspca/gspca.c
index 8c98c7a05..12e3a4360 100644
--- a/linux/drivers/media/video/gspca/gspca.c
+++ b/linux/drivers/media/video/gspca/gspca.c
@@ -29,6 +29,7 @@
#include <linux/string.h>
#include <linux/pagemap.h>
#include <linux/io.h>
+#include <linux/kref.h>
#include <asm/page.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
#include <asm/uaccess.h>
@@ -106,6 +107,22 @@ static struct vm_operations_struct gspca_vm_ops = {
.close = gspca_vm_close,
};
+/* get the current input frame buffer */
+struct gspca_frame *gspca_get_i_frame(struct gspca_dev *gspca_dev)
+{
+ struct gspca_frame *frame;
+ int i;
+
+ i = gspca_dev->fr_i;
+ i = gspca_dev->fr_queue[i];
+ frame = &gspca_dev->frame[i];
+ if ((frame->v4l2_buf.flags & BUF_ALL_FLAGS)
+ != V4L2_BUF_FLAG_QUEUED)
+ return NULL;
+ return frame;
+}
+EXPORT_SYMBOL(gspca_get_i_frame);
+
/*
* fill a video frame from an URB and resubmit
*/
@@ -114,7 +131,7 @@ static void fill_frame(struct gspca_dev *gspca_dev,
{
struct gspca_frame *frame;
__u8 *data; /* address of data in the iso message */
- int i, j, len, st;
+ int i, len, st;
cam_pkt_op pkt_scan;
if (urb->status != 0) {
@@ -128,11 +145,8 @@ static void fill_frame(struct gspca_dev *gspca_dev,
for (i = 0; i < urb->number_of_packets; i++) {
/* check the availability of the frame buffer */
- j = gspca_dev->fr_i;
- j = gspca_dev->fr_queue[j];
- frame = &gspca_dev->frame[j];
- if ((frame->v4l2_buf.flags & BUF_ALL_FLAGS)
- != V4L2_BUF_FLAG_QUEUED) {
+ frame = gspca_get_i_frame(gspca_dev);
+ if (!frame) {
gspca_dev->last_packet_type = DISCARD_PACKET;
break;
}
@@ -195,7 +209,6 @@ static void bulk_irq(struct urb *urb
{
struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context;
struct gspca_frame *frame;
- int j;
PDEBUG(D_PACK, "bulk irq");
if (!gspca_dev->streaming)
@@ -209,11 +222,8 @@ static void bulk_irq(struct urb *urb
}
/* check the availability of the frame buffer */
- j = gspca_dev->fr_i;
- j = gspca_dev->fr_queue[j];
- frame = &gspca_dev->frame[j];
- if ((frame->v4l2_buf.flags & BUF_ALL_FLAGS)
- != V4L2_BUF_FLAG_QUEUED) {
+ frame = gspca_get_i_frame(gspca_dev);
+ if (!frame) {
gspca_dev->last_packet_type = DISCARD_PACKET;
} else {
PDEBUG(D_PACK, "packet l:%d", urb->actual_length);
@@ -237,7 +247,7 @@ static void bulk_irq(struct urb *urb
* On LAST_PACKET, a new frame is returned.
*/
struct gspca_frame *gspca_frame_add(struct gspca_dev *gspca_dev,
- int packet_type,
+ enum gspca_packet_type packet_type,
struct gspca_frame *frame,
const __u8 *data,
int len)
@@ -279,7 +289,7 @@ struct gspca_frame *gspca_frame_add(struct gspca_dev *gspca_dev,
}
gspca_dev->last_packet_type = packet_type;
- /* if last packet, wake the application and advance in the queue */
+ /* if last packet, wake up the application and advance in the queue */
if (packet_type == LAST_PACKET) {
frame->v4l2_buf.bytesused = frame->data_end - frame->data;
frame->v4l2_buf.flags &= ~V4L2_BUF_FLAG_QUEUED;
@@ -317,7 +327,6 @@ static void *rvmalloc(unsigned long size)
void *mem;
unsigned long adr;
-/* size = PAGE_ALIGN(size); (already done) */
mem = vmalloc_32(size);
if (mem != NULL) {
adr = (unsigned long) mem;
@@ -421,7 +430,7 @@ static void destroy_urbs(struct gspca_dev *gspca_dev)
}
/*
- * search an input transfer endpoint in an alternate setting
+ * look for an input transfer endpoint in an alternate setting
*/
static struct usb_host_endpoint *alt_xfer(struct usb_host_interface *alt,
__u8 epaddr,
@@ -445,7 +454,7 @@ static struct usb_host_endpoint *alt_xfer(struct usb_host_interface *alt,
}
/*
- * search an input (isoc or bulk) endpoint
+ * look for an input (isoc or bulk) endpoint
*
* The endpoint is defined by the subdriver.
* Use only the first isoc (some Zoran - 0x0572:0x0001 - have two such ep).
@@ -461,25 +470,34 @@ static struct usb_host_endpoint *get_ep(struct gspca_dev *gspca_dev)
intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface);
ep = NULL;
i = gspca_dev->alt; /* previous alt setting */
+
+ /* try isoc */
while (--i > 0) { /* alt 0 is unusable */
ep = alt_xfer(&intf->altsetting[i],
gspca_dev->cam.epaddr,
- gspca_dev->bulk
- ? USB_ENDPOINT_XFER_BULK
- : USB_ENDPOINT_XFER_ISOC);
+ USB_ENDPOINT_XFER_ISOC);
if (ep)
break;
}
+
+ /* if no isoc, try bulk */
if (ep == NULL) {
- err("no transfer endpoint found");
- return NULL;
+ ep = alt_xfer(&intf->altsetting[0],
+ gspca_dev->cam.epaddr,
+ USB_ENDPOINT_XFER_BULK);
+ if (ep == NULL) {
+ err("no transfer endpoint found");
+ return NULL;
+ }
}
PDEBUG(D_STREAM, "use alt %d ep 0x%02x",
i, ep->desc.bEndpointAddress);
- ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, i);
- if (ret < 0) {
- err("set interface err %d", ret);
- return NULL;
+ if (i > 0) {
+ ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, i);
+ if (ret < 0) {
+ err("set interface err %d", ret);
+ return NULL;
+ }
}
gspca_dev->alt = i; /* memorize the current alt setting */
return ep;
@@ -497,9 +515,10 @@ static int create_urbs(struct gspca_dev *gspca_dev,
/* calculate the packet size and the number of packets */
psize = le16_to_cpu(ep->desc.wMaxPacketSize);
- /* See paragraph 5.9 / table 5-11 of the usb 2.0 spec. */
- psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3));
- if (!gspca_dev->bulk) {
+ if (gspca_dev->alt != 0) { /* isoc */
+
+ /* See paragraph 5.9 / table 5-11 of the usb 2.0 spec. */
+ psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3));
npkt = ISO_MAX_SIZE / psize;
if (npkt > ISO_MAX_PKT)
npkt = ISO_MAX_PKT;
@@ -508,9 +527,11 @@ static int create_urbs(struct gspca_dev *gspca_dev,
"isoc %d pkts size %d = bsize:%d",
npkt, psize, bsize);
nurbs = DEF_NURBS;
- } else {
+ } else { /* bulk */
npkt = 0;
- bsize = psize;
+ bsize = gspca_dev->cam. bulk_size;
+ if (bsize == 0)
+ bsize = psize;
PDEBUG(D_STREAM, "bulk bsize:%d", bsize);
nurbs = 1;
}
@@ -542,7 +563,7 @@ static int create_urbs(struct gspca_dev *gspca_dev,
urb->pipe = usb_rcvisocpipe(gspca_dev->dev,
ep->desc.bEndpointAddress);
urb->transfer_flags = URB_ISO_ASAP
- | URB_NO_TRANSFER_DMA_MAP;
+ | URB_NO_TRANSFER_DMA_MAP;
urb->interval = ep->desc.bInterval;
urb->complete = isoc_irq;
urb->number_of_packets = npkt;
@@ -553,6 +574,7 @@ static int create_urbs(struct gspca_dev *gspca_dev,
} else { /* bulk */
urb->pipe = usb_rcvbulkpipe(gspca_dev->dev,
ep->desc.bEndpointAddress),
+ urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
urb->complete = bulk_irq;
}
}
@@ -585,12 +607,16 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev)
goto out;
/* start the cam */
- gspca_dev->sd_desc->start(gspca_dev);
+ ret = gspca_dev->sd_desc->start(gspca_dev);
+ if (ret < 0) {
+ destroy_urbs(gspca_dev);
+ goto out;
+ }
gspca_dev->streaming = 1;
atomic_set(&gspca_dev->nevent, 0);
- /* start the bulk transfer is done by the subdriver */
- if (gspca_dev->bulk)
+ /* bulk transfers are started by the subdriver */
+ if (gspca_dev->alt == 0)
break;
/* submit the URBs */
@@ -624,7 +650,7 @@ static int gspca_set_alt0(struct gspca_dev *gspca_dev)
return ret;
}
-/* Note both the queue and the usb lock should be hold when calling this */
+/* Note: both the queue and the usb locks should be held when calling this */
static void gspca_stream_off(struct gspca_dev *gspca_dev)
{
gspca_dev->streaming = 0;
@@ -830,6 +856,16 @@ out:
return ret;
}
+static void gspca_delete(struct kref *kref)
+{
+ struct gspca_dev *gspca_dev = container_of(kref, struct gspca_dev, kref);
+
+ PDEBUG(D_STREAM, "device deleted");
+
+ kfree(gspca_dev->usb_buf);
+ kfree(gspca_dev);
+}
+
static int dev_open(struct inode *inode, struct file *file)
{
struct gspca_dev *gspca_dev;
@@ -849,13 +885,19 @@ static int dev_open(struct inode *inode, struct file *file)
goto out;
}
gspca_dev->users++;
+
+ /* one more user */
+ kref_get(&gspca_dev->kref);
+
file->private_data = gspca_dev;
#ifdef GSPCA_DEBUG
/* activate the v4l2 debug */
if (gspca_debug & D_V4L2)
- gspca_dev->vdev.debug |= 3;
+ gspca_dev->vdev.debug |= V4L2_DEBUG_IOCTL
+ | V4L2_DEBUG_IOCTL_ARG;
else
- gspca_dev->vdev.debug &= ~3;
+ gspca_dev->vdev.debug &= ~(V4L2_DEBUG_IOCTL
+ | V4L2_DEBUG_IOCTL_ARG);
#endif
ret = 0;
out:
@@ -889,7 +931,11 @@ static int dev_close(struct inode *inode, struct file *file)
}
file->private_data = NULL;
mutex_unlock(&gspca_dev->queue_lock);
+
PDEBUG(D_STREAM, "close done");
+
+ kref_put(&gspca_dev->kref, gspca_delete);
+
return 0;
}
@@ -900,7 +946,6 @@ static int vidioc_querycap(struct file *file, void *priv,
memset(cap, 0, sizeof *cap);
strncpy(cap->driver, gspca_dev->sd_desc->name, sizeof cap->driver);
-/* strncpy(cap->card, gspca_dev->cam.dev_name, sizeof cap->card); */
if (gspca_dev->dev->product != NULL) {
strncpy(cap->card, gspca_dev->dev->product,
sizeof cap->card);
@@ -1534,7 +1579,6 @@ static int vidioc_qbuf(struct file *file, void *priv,
}
frame->v4l2_buf.flags |= V4L2_BUF_FLAG_QUEUED;
-/* frame->v4l2_buf.flags &= ~V4L2_BUF_FLAG_DONE; */
if (frame->v4l2_buf.memory == V4L2_MEMORY_USERPTR) {
frame->v4l2_buf.m.userptr = v4l2_buf->m.userptr;
@@ -1681,7 +1725,7 @@ static ssize_t dev_read(struct file *file, char __user *data,
}
/* if the process slept for more than 1 second,
- * get anewer frame */
+ * get a newer frame */
frame = &gspca_dev->frame[v4l2_buf.index];
if (--n < 0)
break; /* avoid infinite loop */
@@ -1799,21 +1843,21 @@ int gspca_dev_probe(struct usb_interface *intf,
if (dev_size < sizeof *gspca_dev)
dev_size = sizeof *gspca_dev;
gspca_dev = kzalloc(dev_size, GFP_KERNEL);
- if (gspca_dev == NULL) {
+ if (!gspca_dev) {
err("couldn't kzalloc gspca struct");
- return -EIO;
+ return -ENOMEM;
}
+ kref_init(&gspca_dev->kref);
gspca_dev->usb_buf = kmalloc(USB_BUF_SZ, GFP_KERNEL);
if (!gspca_dev->usb_buf) {
err("out of memory");
- ret = -EIO;
+ ret = -ENOMEM;
goto out;
}
gspca_dev->dev = dev;
gspca_dev->iface = interface->bInterfaceNumber;
gspca_dev->nbalt = intf->num_altsetting;
gspca_dev->sd_desc = sd_desc;
-/* gspca_dev->users = 0; (done by kzalloc) */
gspca_dev->nbufread = 2;
/* configure the subdriver and initialize the USB device */
@@ -1852,8 +1896,7 @@ int gspca_dev_probe(struct usb_interface *intf,
PDEBUG(D_PROBE, "probe ok");
return 0;
out:
- kfree(gspca_dev->usb_buf);
- kfree(gspca_dev);
+ kref_put(&gspca_dev->kref, gspca_delete);
return ret;
}
EXPORT_SYMBOL(gspca_dev_probe);
@@ -1868,8 +1911,8 @@ void gspca_disconnect(struct usb_interface *intf)
{
struct gspca_dev *gspca_dev = usb_get_intfdata(intf);
- if (!gspca_dev)
- return;
+ usb_set_intfdata(intf, NULL);
+
gspca_dev->present = 0;
mutex_lock(&gspca_dev->queue_lock);
mutex_lock(&gspca_dev->usb_lock);
@@ -1877,16 +1920,12 @@ void gspca_disconnect(struct usb_interface *intf)
destroy_urbs(gspca_dev);
mutex_unlock(&gspca_dev->usb_lock);
mutex_unlock(&gspca_dev->queue_lock);
- while (gspca_dev->users != 0) { /* wait until fully closed */
- atomic_inc(&gspca_dev->nevent);
- wake_up_interruptible(&gspca_dev->wq); /* wake processes */
- schedule();
- }
+
/* We don't want people trying to open up the device */
video_unregister_device(&gspca_dev->vdev);
-/* Free the memory */
- kfree(gspca_dev->usb_buf);
- kfree(gspca_dev);
+
+ kref_put(&gspca_dev->kref, gspca_delete);
+
PDEBUG(D_PROBE, "disconnect complete");
}
EXPORT_SYMBOL(gspca_disconnect);
diff --git a/linux/drivers/media/video/gspca/gspca.h b/linux/drivers/media/video/gspca/gspca.h
index 78a35a88c..4779dd0b0 100644
--- a/linux/drivers/media/video/gspca/gspca.h
+++ b/linux/drivers/media/video/gspca/gspca.h
@@ -56,6 +56,7 @@ extern int gspca_debug;
/* device information - set at probe time */
struct cam {
+ int bulk_size; /* buffer size when image transfer by bulk */
struct v4l2_pix_format *cam_mode; /* size nmodes */
char nmodes;
__u8 epaddr;
@@ -93,7 +94,7 @@ struct sd_desc {
/* mandatory operations */
cam_cf_op config; /* called on probe */
cam_op init; /* called on probe and resume */
- cam_v_op start; /* called on stream on */
+ cam_op start; /* called on stream on */
cam_pkt_op pkt_scan;
/* optional operations */
cam_v_op stopN; /* called on stream off - main alt */
@@ -105,10 +106,12 @@ struct sd_desc {
};
/* packet types when moving from iso buf to frame buf */
-#define DISCARD_PACKET 0
-#define FIRST_PACKET 1
-#define INTER_PACKET 2
-#define LAST_PACKET 3
+enum gspca_packet_type {
+ DISCARD_PACKET,
+ FIRST_PACKET,
+ INTER_PACKET,
+ LAST_PACKET
+};
struct gspca_frame {
__u8 *data; /* frame buffer */
@@ -121,6 +124,7 @@ struct gspca_dev {
struct video_device vdev; /* !! must be the first item */
struct file_operations fops;
struct usb_device *dev;
+ struct kref kref;
struct file *capt_file; /* file doing video capture */
struct cam cam; /* device information */
@@ -143,7 +147,6 @@ struct gspca_dev {
__u8 iface; /* USB interface number */
__u8 alt; /* USB alternate setting */
- __u8 bulk; /* image transfer by isoc (0) or bulk (1) */
__u8 curr_mode; /* current camera mode */
__u32 pixfmt; /* current mode parameters */
__u16 width;
@@ -174,10 +177,11 @@ int gspca_dev_probe(struct usb_interface *intf,
struct module *module);
void gspca_disconnect(struct usb_interface *intf);
struct gspca_frame *gspca_frame_add(struct gspca_dev *gspca_dev,
- int packet_type,
+ enum gspca_packet_type packet_type,
struct gspca_frame *frame,
const __u8 *data,
int len);
+struct gspca_frame *gspca_get_i_frame(struct gspca_dev *gspca_dev);
#ifdef CONFIG_PM
int gspca_suspend(struct usb_interface *intf, pm_message_t message);
int gspca_resume(struct usb_interface *intf);
diff --git a/linux/drivers/media/video/gspca/m5602/Kconfig b/linux/drivers/media/video/gspca/m5602/Kconfig
new file mode 100644
index 000000000..5a69016ed
--- /dev/null
+++ b/linux/drivers/media/video/gspca/m5602/Kconfig
@@ -0,0 +1,11 @@
+config USB_M5602
+ tristate "ALi USB m5602 Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the
+ ALi m5602 connected to various image sensors.
+
+ See <file:Documentation/video4linux/m5602.txt> for more info.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_m5602.
diff --git a/linux/drivers/media/video/gspca/m5602/Makefile b/linux/drivers/media/video/gspca/m5602/Makefile
new file mode 100644
index 000000000..672fb6bb5
--- /dev/null
+++ b/linux/drivers/media/video/gspca/m5602/Makefile
@@ -0,0 +1,8 @@
+obj-$(CONFIG_USB_M5602) += gspca_m5602.o
+
+gspca_m5602-objs := m5602_core.o \
+ m5602_ov9650.o \
+ m5602_mt9m111.o \
+ m5602_po1030.o \
+ m5602_s5k83a.o \
+ m5602_s5k4aa.o
diff --git a/linux/drivers/media/video/gspca/m5602/m5602_bridge.h b/linux/drivers/media/video/gspca/m5602/m5602_bridge.h
new file mode 100644
index 000000000..6b2441e84
--- /dev/null
+++ b/linux/drivers/media/video/gspca/m5602/m5602_bridge.h
@@ -0,0 +1,182 @@
+/*
+ * USB Driver for ALi m5602 based webcams
+ *
+ * Copyright (C) 2008 Erik Andren
+ * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * 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, version 2.
+ *
+ */
+
+#ifndef M5602_BRIDGE_H_
+#define M5602_BRIDGE_H_
+
+#include "gspca.h"
+
+#define MODULE_NAME "ALi m5602"
+
+/*****************************************************************************/
+
+#undef PDEBUG
+#undef info
+#undef err
+
+#define err(format, arg...) printk(KERN_ERR KBUILD_MODNAME ": " \
+ format "\n" , ## arg)
+#define info(format, arg...) printk(KERN_INFO KBUILD_MODNAME ": " \
+ format "\n" , ## arg)
+
+/* Debug parameters */
+#define DBG_INIT 0x1
+#define DBG_PROBE 0x2
+#define DBG_V4L2 0x4
+#define DBG_TRACE 0x8
+#define DBG_DATA 0x10
+#define DBG_V4L2_CID 0x20
+#define DBG_GSPCA 0x40
+
+#define PDEBUG(level, fmt, args...) \
+ do { \
+ if (debug & level) \
+ info("[%s:%d] " fmt, __func__, __LINE__ , \
+ ## args); \
+ } while (0)
+
+/*****************************************************************************/
+
+#define M5602_XB_SENSOR_TYPE 0x00
+#define M5602_XB_SENSOR_CTRL 0x01
+#define M5602_XB_LINE_OF_FRAME_H 0x02
+#define M5602_XB_LINE_OF_FRAME_L 0x03
+#define M5602_XB_PIX_OF_LINE_H 0x04
+#define M5602_XB_PIX_OF_LINE_L 0x05
+#define M5602_XB_VSYNC_PARA 0x06
+#define M5602_XB_HSYNC_PARA 0x07
+#define M5602_XB_TEST_MODE_1 0x08
+#define M5602_XB_TEST_MODE_2 0x09
+#define M5602_XB_SIG_INI 0x0a
+#define M5602_XB_DS_PARA 0x0e
+#define M5602_XB_TRIG_PARA 0x0f
+#define M5602_XB_CLK_PD 0x10
+#define M5602_XB_MCU_CLK_CTRL 0x12
+#define M5602_XB_MCU_CLK_DIV 0x13
+#define M5602_XB_SEN_CLK_CTRL 0x14
+#define M5602_XB_SEN_CLK_DIV 0x15
+#define M5602_XB_AUD_CLK_CTRL 0x16
+#define M5602_XB_AUD_CLK_DIV 0x17
+#define M5602_XB_DEVCTR1 0x41
+#define M5602_XB_EPSETR0 0x42
+#define M5602_XB_EPAFCTR 0x47
+#define M5602_XB_EPBFCTR 0x49
+#define M5602_XB_EPEFCTR 0x4f
+#define M5602_XB_TEST_REG 0x53
+#define M5602_XB_ALT2SIZE 0x54
+#define M5602_XB_ALT3SIZE 0x55
+#define M5602_XB_OBSFRAME 0x56
+#define M5602_XB_PWR_CTL 0x59
+#define M5602_XB_ADC_CTRL 0x60
+#define M5602_XB_ADC_DATA 0x61
+#define M5602_XB_MISC_CTRL 0x62
+#define M5602_XB_SNAPSHOT 0x63
+#define M5602_XB_SCRATCH_1 0x64
+#define M5602_XB_SCRATCH_2 0x65
+#define M5602_XB_SCRATCH_3 0x66
+#define M5602_XB_SCRATCH_4 0x67
+#define M5602_XB_I2C_CTRL 0x68
+#define M5602_XB_I2C_CLK_DIV 0x69
+#define M5602_XB_I2C_DEV_ADDR 0x6a
+#define M5602_XB_I2C_REG_ADDR 0x6b
+#define M5602_XB_I2C_DATA 0x6c
+#define M5602_XB_I2C_STATUS 0x6d
+#define M5602_XB_GPIO_DAT_H 0x70
+#define M5602_XB_GPIO_DAT_L 0x71
+#define M5602_XB_GPIO_DIR_H 0x72
+#define M5602_XB_GPIO_DIR_L 0x73
+#define M5602_XB_GPIO_EN_H 0x74
+#define M5602_XB_GPIO_EN_L 0x75
+#define M5602_XB_GPIO_DAT 0x76
+#define M5602_XB_GPIO_DIR 0x77
+#define M5602_XB_MISC_CTL 0x70
+
+#define I2C_BUSY 0x80
+
+/*****************************************************************************/
+
+/* Driver info */
+#define DRIVER_AUTHOR "ALi m5602 Linux Driver Project"
+#define DRIVER_DESC "ALi m5602 webcam driver"
+
+#define M5602_ISOC_ENDPOINT_ADDR 0x81
+#define M5602_INTR_ENDPOINT_ADDR 0x82
+
+#define M5602_MAX_FRAMES 32
+#define M5602_URBS 2
+#define M5602_ISOC_PACKETS 14
+
+#define M5602_URB_TIMEOUT msecs_to_jiffies(2 * M5602_ISOC_PACKETS)
+#define M5602_URB_MSG_TIMEOUT 5000
+#define M5602_FRAME_TIMEOUT 2
+
+/*****************************************************************************/
+
+/* A skeleton used for sending messages to the m5602 bridge */
+static const unsigned char bridge_urb_skeleton[] = {
+ 0x13, 0x00, 0x81, 0x00
+};
+
+/* A skeleton used for sending messages to the sensor */
+static const unsigned char sensor_urb_skeleton[] = {
+ 0x23, M5602_XB_GPIO_EN_H, 0x81, 0x06,
+ 0x23, M5602_XB_MISC_CTRL, 0x81, 0x80,
+ 0x13, M5602_XB_I2C_DEV_ADDR, 0x81, 0x00,
+ 0x13, M5602_XB_I2C_REG_ADDR, 0x81, 0x00,
+ 0x13, M5602_XB_I2C_DATA, 0x81, 0x00,
+ 0x13, M5602_XB_I2C_CTRL, 0x81, 0x11
+};
+
+/* m5602 device descriptor, currently it just wraps the m5602_camera struct */
+struct sd {
+ struct gspca_dev gspca_dev;
+
+ /* The name of the m5602 camera */
+ char *name;
+
+ /* A pointer to the currently connected sensor */
+ struct m5602_sensor *sensor;
+
+ struct sd_desc *desc;
+
+ /* The current frame's id, used to detect frame boundaries */
+ u8 frame_id;
+
+ /* The current frame count */
+ u32 frame_count;
+};
+
+int m5602_read_bridge(
+ struct sd *sd, u8 address, u8 *i2c_data);
+
+int m5602_write_bridge(
+ struct sd *sd, u8 address, u8 i2c_data);
+
+int m5602_configure(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id);
+
+int m5602_init(struct gspca_dev *gspca_dev);
+
+void m5602_start_transfer(struct gspca_dev *gspca_dev);
+
+void m5602_stop_transfer(struct gspca_dev *gspca_dev);
+
+void m5602_urb_complete(struct gspca_dev *gspca_dev, struct gspca_frame *frame,
+ __u8 *data, int len);
+
+#endif
diff --git a/linux/drivers/media/video/gspca/m5602/m5602_core.c b/linux/drivers/media/video/gspca/m5602/m5602_core.c
new file mode 100644
index 000000000..30efe7d4b
--- /dev/null
+++ b/linux/drivers/media/video/gspca/m5602/m5602_core.c
@@ -0,0 +1,308 @@
+/*
+ * USB Driver for ALi m5602 based webcams
+ *
+ * Copyright (C) 2008 Erik Andren
+ * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * 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, version 2.
+ *
+ */
+
+#include "m5602_ov9650.h"
+#include "m5602_mt9m111.h"
+#include "m5602_po1030.h"
+#include "m5602_s5k83a.h"
+#include "m5602_s5k4aa.h"
+
+/* Kernel module parameters */
+int force_sensor;
+int dump_bridge;
+int dump_sensor;
+unsigned int debug;
+
+static const __devinitdata struct usb_device_id m5602_table[] = {
+ {USB_DEVICE(0x0402, 0x5602)},
+ {}
+};
+
+MODULE_DEVICE_TABLE(usb, m5602_table);
+
+/* sub-driver description, the ctrl and nctrl is filled at probe time */
+static struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .config = m5602_configure,
+ .init = m5602_init,
+ .start = m5602_start_transfer,
+ .stopN = m5602_stop_transfer,
+ .pkt_scan = m5602_urb_complete
+};
+
+/* Reads a byte from the m5602 */
+int m5602_read_bridge(struct sd *sd, u8 address, u8 *i2c_data)
+{
+ int err;
+ struct usb_device *udev = sd->gspca_dev.dev;
+ __u8 *buf = sd->gspca_dev.usb_buf;
+
+ err = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+ 0x04, 0xc0, 0x14,
+ 0x8100 + address, buf,
+ 1, M5602_URB_MSG_TIMEOUT);
+ *i2c_data = buf[0];
+
+ PDEBUG(DBG_TRACE, "Reading bridge register 0x%x containing 0x%x",
+ address, *i2c_data);
+
+ /* usb_control_msg(...) returns the number of bytes sent upon success,
+ mask that and return zero upon success instead*/
+ return (err < 0) ? err : 0;
+}
+
+/* Writes a byte to to the m5602 */
+int m5602_write_bridge(struct sd *sd, u8 address, u8 i2c_data)
+{
+ int err;
+ struct usb_device *udev = sd->gspca_dev.dev;
+ __u8 *buf = sd->gspca_dev.usb_buf;
+
+ PDEBUG(DBG_TRACE, "Writing bridge register 0x%x with 0x%x",
+ address, i2c_data);
+
+ memcpy(buf, bridge_urb_skeleton,
+ sizeof(bridge_urb_skeleton));
+ buf[1] = address;
+ buf[3] = i2c_data;
+
+ err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ 0x04, 0x40, 0x19,
+ 0x0000, buf,
+ 4, M5602_URB_MSG_TIMEOUT);
+
+ /* usb_control_msg(...) returns the number of bytes sent upon success,
+ mask that and return zero upon success instead */
+ return (err < 0) ? err : 0;
+}
+
+/* Dump all the registers of the m5602 bridge,
+ unfortunately this breaks the camera until it's power cycled */
+static void m5602_dump_bridge(struct sd *sd)
+{
+ int i;
+ for (i = 0; i < 0x80; i++) {
+ unsigned char val = 0;
+ m5602_read_bridge(sd, i, &val);
+ info("ALi m5602 address 0x%x contains 0x%x", i, val);
+ }
+ info("Warning: The camera probably won't work until it's power cycled");
+}
+
+int m5602_probe_sensor(struct sd *sd)
+{
+ /* Try the po1030 */
+ sd->sensor = &po1030;
+ if (!sd->sensor->probe(sd))
+ return 0;
+
+ /* Try the mt9m111 sensor */
+ sd->sensor = &mt9m111;
+ if (!sd->sensor->probe(sd))
+ return 0;
+
+ /* Try the s5k4aa */
+ sd->sensor = &s5k4aa;
+ if (!sd->sensor->probe(sd))
+ return 0;
+
+ /* Try the ov9650 */
+ sd->sensor = &ov9650;
+ if (!sd->sensor->probe(sd))
+ return 0;
+
+ /* Try the s5k83a */
+ sd->sensor = &s5k83a;
+ if (!sd->sensor->probe(sd))
+ return 0;
+
+ /* More sensor probe function goes here */
+ info("Failed to find a sensor");
+ sd->sensor = NULL;
+ return -ENODEV;
+}
+
+int m5602_init(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int err;
+
+ PDEBUG(DBG_TRACE, "Initializing ALi m5602 webcam");
+ /* Run the init sequence */
+ err = sd->sensor->init(sd);
+
+ return err;
+}
+
+void m5602_start_transfer(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ __u8 *buf = sd->gspca_dev.usb_buf;
+
+ /* Send start command to the camera */
+ const u8 buffer[4] = {0x13, 0xf9, 0x0f, 0x01};
+ memcpy(buf, buffer, sizeof(buffer));
+ usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0),
+ 0x04, 0x40, 0x19, 0x0000, buf,
+ 4, M5602_URB_MSG_TIMEOUT);
+
+ PDEBUG(DBG_V4L2, "Transfer started");
+}
+
+void m5602_urb_complete(struct gspca_dev *gspca_dev, struct gspca_frame *frame,
+ __u8 *data, int len)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (len < 6) {
+ PDEBUG(DBG_DATA, "Packet is less than 6 bytes");
+ return;
+ }
+
+ /* Frame delimiter: ff xx xx xx ff ff */
+ if (data[0] == 0xff && data[4] == 0xff && data[5] == 0xff &&
+ data[2] != sd->frame_id) {
+ PDEBUG(DBG_DATA, "Frame delimiter detected");
+ sd->frame_id = data[2];
+
+ /* Remove the extra fluff appended on each header */
+ data += 6;
+ len -= 6;
+
+ /* Complete the last frame (if any) */
+ frame = gspca_frame_add(gspca_dev, LAST_PACKET,
+ frame, data, 0);
+ sd->frame_count++;
+
+ /* Create a new frame */
+ gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, len);
+
+ PDEBUG(DBG_V4L2, "Starting new frame %d",
+ sd->frame_count);
+
+ } else {
+ int cur_frame_len = frame->data_end - frame->data;
+
+ /* Remove urb header */
+ data += 4;
+ len -= 4;
+
+ if (cur_frame_len + len <= frame->v4l2_buf.length) {
+ PDEBUG(DBG_DATA, "Continuing frame %d copying %d bytes",
+ sd->frame_count, len);
+
+ gspca_frame_add(gspca_dev, INTER_PACKET, frame,
+ data, len);
+ } else if (frame->v4l2_buf.length - cur_frame_len > 0) {
+ /* Add the remaining data up to frame size */
+ gspca_frame_add(gspca_dev, INTER_PACKET, frame, data,
+ frame->v4l2_buf.length - cur_frame_len);
+ }
+ }
+}
+
+void m5602_stop_transfer(struct gspca_dev *gspca_dev)
+{
+ /* Is there are a command to stop a data transfer? */
+}
+
+/* this function is called at probe time */
+int m5602_configure(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct cam *cam;
+ int err;
+
+ PDEBUG(DBG_GSPCA, "m5602_configure start");
+
+ cam = &gspca_dev->cam;
+ cam->epaddr = M5602_ISOC_ENDPOINT_ADDR;
+ sd->desc = &sd_desc;
+
+ if (dump_bridge)
+ m5602_dump_bridge(sd);
+
+ /* Probe sensor */
+ err = m5602_probe_sensor(sd);
+ if (err)
+ goto fail;
+
+ PDEBUG(DBG_GSPCA, "m5602_configure end");
+ return 0;
+
+fail:
+ PDEBUG(DBG_GSPCA, "m5602_configure failed");
+ cam->cam_mode = NULL;
+ cam->nmodes = 0;
+
+ return err;
+}
+
+static int m5602_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+ THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+ .name = MODULE_NAME,
+ .id_table = m5602_table,
+ .probe = m5602_probe,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+#endif
+ .disconnect = gspca_disconnect
+};
+
+/* -- module insert / remove -- */
+static int __init mod_m5602_init(void)
+{
+ if (usb_register(&sd_driver) < 0)
+ return -1;
+ PDEBUG(D_PROBE, "m5602 module registered");
+ return 0;
+}
+static void __exit mod_m5602_exit(void)
+{
+ usb_deregister(&sd_driver);
+ PDEBUG(D_PROBE, "m5602 module deregistered");
+}
+
+module_init(mod_m5602_init);
+module_exit(mod_m5602_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+module_param(debug, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "toggles debug on/off");
+
+module_param(force_sensor, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(force_sensor,
+ "force detection of sensor, "
+ "1 = OV9650, 2 = S5K83A, 3 = S5K4AA, 4 = MT9M111, 5 = PO1030");
+
+module_param(dump_bridge, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(dump_bridge, "Dumps all usb bridge registers at startup");
+
+module_param(dump_sensor, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(dump_sensor, "Dumps all usb sensor registers "
+ "at startup providing a sensor is found");
diff --git a/linux/drivers/media/video/gspca/m5602/m5602_mt9m111.c b/linux/drivers/media/video/gspca/m5602/m5602_mt9m111.c
new file mode 100644
index 000000000..ea2250217
--- /dev/null
+++ b/linux/drivers/media/video/gspca/m5602/m5602_mt9m111.c
@@ -0,0 +1,345 @@
+/*
+ * Driver for the mt9m111 sensor
+ *
+ * Copyright (C) 2008 Erik Andren
+ * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * 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, version 2.
+ *
+ */
+
+#include "m5602_mt9m111.h"
+
+int mt9m111_probe(struct sd *sd)
+{
+ u8 data[2] = {0x00, 0x00};
+ int i;
+
+ if (force_sensor) {
+ if (force_sensor == MT9M111_SENSOR) {
+ info("Forcing a %s sensor", mt9m111.name);
+ goto sensor_found;
+ }
+ /* If we want to force another sensor, don't try to probe this
+ * one */
+ return -ENODEV;
+ }
+
+ info("Probing for a mt9m111 sensor");
+
+ /* Do the preinit */
+ for (i = 0; i < ARRAY_SIZE(preinit_mt9m111); i++) {
+ if (preinit_mt9m111[i][0] == BRIDGE) {
+ m5602_write_bridge(sd,
+ preinit_mt9m111[i][1],
+ preinit_mt9m111[i][2]);
+ } else {
+ data[0] = preinit_mt9m111[i][2];
+ data[1] = preinit_mt9m111[i][3];
+ mt9m111_write_sensor(sd,
+ preinit_mt9m111[i][1], data, 2);
+ }
+ }
+
+ if (mt9m111_read_sensor(sd, MT9M111_SC_CHIPVER, data, 2))
+ return -ENODEV;
+
+ if ((data[0] == 0x14) && (data[1] == 0x3a)) {
+ info("Detected a mt9m111 sensor");
+ goto sensor_found;
+ }
+
+ return -ENODEV;
+
+sensor_found:
+ sd->gspca_dev.cam.cam_mode = mt9m111.modes;
+ sd->gspca_dev.cam.nmodes = mt9m111.nmodes;
+ sd->desc->ctrls = mt9m111.ctrls;
+ sd->desc->nctrls = mt9m111.nctrls;
+ return 0;
+}
+
+int mt9m111_init(struct sd *sd)
+{
+ int i, err;
+
+ /* Init the sensor */
+ for (i = 0; i < ARRAY_SIZE(init_mt9m111); i++) {
+ u8 data[2];
+
+ if (init_mt9m111[i][0] == BRIDGE) {
+ err = m5602_write_bridge(sd,
+ init_mt9m111[i][1],
+ init_mt9m111[i][2]);
+ } else {
+ data[0] = init_mt9m111[i][2];
+ data[1] = init_mt9m111[i][3];
+ err = mt9m111_write_sensor(sd,
+ init_mt9m111[i][1], data, 2);
+ }
+ }
+
+ if (dump_sensor)
+ mt9m111_dump_registers(sd);
+
+ return (err < 0) ? err : 0;
+}
+
+int mt9m111_power_down(struct sd *sd)
+{
+ return 0;
+}
+
+int mt9m111_get_vflip(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ int err;
+ u8 data[2] = {0x00, 0x00};
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ err = mt9m111_read_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B,
+ data, 2);
+ *val = data[0] & MT9M111_RMB_MIRROR_ROWS;
+ PDEBUG(DBG_V4L2_CID, "Read vertical flip %d", *val);
+
+ return (err < 0) ? err : 0;
+}
+
+int mt9m111_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ u8 data[2] = {0x00, 0x00};
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ PDEBUG(DBG_V4L2_CID, "Set vertical flip to %d", val);
+
+ /* Set the correct page map */
+ err = mt9m111_write_sensor(sd, MT9M111_PAGE_MAP, data, 2);
+ if (err < 0)
+ goto out;
+
+ err = mt9m111_read_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B, data, 2);
+ if (err < 0)
+ goto out;
+
+ data[0] = (data[0] & 0xfe) | val;
+ err = mt9m111_write_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B,
+ data, 2);
+out:
+ return (err < 0) ? err : 0;
+}
+
+int mt9m111_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ int err;
+ u8 data[2] = {0x00, 0x00};
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ err = mt9m111_read_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B,
+ data, 2);
+ *val = data[0] & MT9M111_RMB_MIRROR_COLS;
+ PDEBUG(DBG_V4L2_CID, "Read horizontal flip %d", *val);
+
+ return (err < 0) ? err : 0;
+}
+
+int mt9m111_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ u8 data[2] = {0x00, 0x00};
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ PDEBUG(DBG_V4L2_CID, "Set horizontal flip to %d", val);
+
+ /* Set the correct page map */
+ err = mt9m111_write_sensor(sd, MT9M111_PAGE_MAP, data, 2);
+ if (err < 0)
+ goto out;
+
+ err = mt9m111_read_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B, data, 2);
+ if (err < 0)
+ goto out;
+
+ data[0] = (data[0] & 0xfd) | ((val << 1) & 0x02);
+ err = mt9m111_write_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B,
+ data, 2);
+out:
+ return (err < 0) ? err : 0;
+}
+
+int mt9m111_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ int err, tmp;
+ u8 data[2] = {0x00, 0x00};
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ err = mt9m111_read_sensor(sd, MT9M111_SC_GLOBAL_GAIN, data, 2);
+ tmp = ((data[1] << 8) | data[0]);
+
+ *val = ((tmp & (1 << 10)) * 2) |
+ ((tmp & (1 << 9)) * 2) |
+ ((tmp & (1 << 8)) * 2) |
+ (tmp & 0x7f);
+
+ PDEBUG(DBG_V4L2_CID, "Read gain %d", *val);
+
+ return (err < 0) ? err : 0;
+}
+
+int mt9m111_set_gain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err, tmp;
+ u8 data[2] = {0x00, 0x00};
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ /* Set the correct page map */
+ err = mt9m111_write_sensor(sd, MT9M111_PAGE_MAP, data, 2);
+ if (err < 0)
+ goto out;
+
+ if (val >= INITIAL_MAX_GAIN * 2 * 2 * 2)
+ return -EINVAL;
+
+ if ((val >= INITIAL_MAX_GAIN * 2 * 2) &&
+ (val < (INITIAL_MAX_GAIN - 1) * 2 * 2 * 2))
+ tmp = (1 << 10) | (val << 9) |
+ (val << 8) | (val / 8);
+ else if ((val >= INITIAL_MAX_GAIN * 2) &&
+ (val < INITIAL_MAX_GAIN * 2 * 2))
+ tmp = (1 << 9) | (1 << 8) | (val / 4);
+ else if ((val >= INITIAL_MAX_GAIN) &&
+ (val < INITIAL_MAX_GAIN * 2))
+ tmp = (1 << 8) | (val / 2);
+ else
+ tmp = val;
+
+ data[1] = (tmp & 0xff00) >> 8;
+ data[0] = (tmp & 0xff);
+ PDEBUG(DBG_V4L2_CID, "tmp=%d, data[1]=%d, data[0]=%d", tmp,
+ data[1], data[0]);
+
+ err = mt9m111_write_sensor(sd, MT9M111_SC_GLOBAL_GAIN,
+ data, 2);
+out:
+ return (err < 0) ? err : 0;
+}
+
+int mt9m111_read_sensor(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len) {
+ int err, i;
+
+ do {
+ err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, i2c_data);
+ } while ((*i2c_data & I2C_BUSY) && !err);
+ if (err < 0)
+ goto out;
+
+ err = m5602_write_bridge(sd, M5602_XB_I2C_DEV_ADDR,
+ sd->sensor->i2c_slave_id);
+ if (err < 0)
+ goto out;
+
+ err = m5602_write_bridge(sd, M5602_XB_I2C_REG_ADDR, address);
+ if (err < 0)
+ goto out;
+
+ err = m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x1a);
+ if (err < 0)
+ goto out;
+
+ for (i = 0; i < len && !err; i++) {
+ err = m5602_read_bridge(sd, M5602_XB_I2C_DATA, &(i2c_data[i]));
+
+ PDEBUG(DBG_TRACE, "Reading sensor register "
+ "0x%x contains 0x%x ", address, *i2c_data);
+ }
+out:
+ return (err < 0) ? err : 0;
+}
+
+int mt9m111_write_sensor(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len)
+{
+ int err, i;
+ u8 *p;
+ struct usb_device *udev = sd->gspca_dev.dev;
+ __u8 *buf = sd->gspca_dev.usb_buf;
+
+ /* No sensor with a data width larger
+ than 16 bits has yet been seen, nor with 0 :p*/
+ if (len > 2 || !len)
+ return -EINVAL;
+
+ memcpy(buf, sensor_urb_skeleton,
+ sizeof(sensor_urb_skeleton));
+
+ buf[11] = sd->sensor->i2c_slave_id;
+ buf[15] = address;
+
+ p = buf + 16;
+
+ /* Copy a four byte write sequence for each byte to be written to */
+ for (i = 0; i < len; i++) {
+ memcpy(p, sensor_urb_skeleton + 16, 4);
+ p[3] = i2c_data[i];
+ p += 4;
+ PDEBUG(DBG_TRACE, "Writing sensor register 0x%x with 0x%x",
+ address, i2c_data[i]);
+ }
+
+ /* Copy the tailer */
+ memcpy(p, sensor_urb_skeleton + 20, 4);
+
+ /* Set the total length */
+ p[3] = 0x10 + len;
+
+ err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ 0x04, 0x40, 0x19,
+ 0x0000, buf,
+ 20 + len * 4, M5602_URB_MSG_TIMEOUT);
+
+ return (err < 0) ? err : 0;
+}
+
+void mt9m111_dump_registers(struct sd *sd)
+{
+ u8 address, value[2] = {0x00, 0x00};
+
+ info("Dumping the mt9m111 register state");
+
+ info("Dumping the mt9m111 sensor core registers");
+ value[1] = MT9M111_SENSOR_CORE;
+ mt9m111_write_sensor(sd, MT9M111_PAGE_MAP, value, 2);
+ for (address = 0; address < 0xff; address++) {
+ mt9m111_read_sensor(sd, address, value, 2);
+ info("register 0x%x contains 0x%x%x",
+ address, value[0], value[1]);
+ }
+
+ info("Dumping the mt9m111 color pipeline registers");
+ value[1] = MT9M111_COLORPIPE;
+ mt9m111_write_sensor(sd, MT9M111_PAGE_MAP, value, 2);
+ for (address = 0; address < 0xff; address++) {
+ mt9m111_read_sensor(sd, address, value, 2);
+ info("register 0x%x contains 0x%x%x",
+ address, value[0], value[1]);
+ }
+
+ info("Dumping the mt9m111 camera control registers");
+ value[1] = MT9M111_CAMERA_CONTROL;
+ mt9m111_write_sensor(sd, MT9M111_PAGE_MAP, value, 2);
+ for (address = 0; address < 0xff; address++) {
+ mt9m111_read_sensor(sd, address, value, 2);
+ info("register 0x%x contains 0x%x%x",
+ address, value[0], value[1]);
+ }
+
+ info("mt9m111 register state dump complete");
+}
diff --git a/linux/drivers/media/video/gspca/m5602/m5602_mt9m111.h b/linux/drivers/media/video/gspca/m5602/m5602_mt9m111.h
new file mode 100644
index 000000000..f914547b9
--- /dev/null
+++ b/linux/drivers/media/video/gspca/m5602/m5602_mt9m111.h
@@ -0,0 +1,1020 @@
+/*
+ * Driver for the mt9m111 sensor
+ *
+ * Copyright (C) 2008 Erik Andren
+ * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * Some defines taken from the mt9m111 sensor driver
+ * Copyright (C) 2008, Robert Jarzmik <robert.jarzmik@free.fr>
+ *
+ * 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, version 2.
+ *
+ */
+
+#ifndef M5602_MT9M111_H_
+#define M5602_MT9M111_H_
+
+#include "m5602_sensor.h"
+
+/*****************************************************************************/
+
+#define MT9M111_SC_CHIPVER 0x00
+#define MT9M111_SC_ROWSTART 0x01
+#define MT9M111_SC_COLSTART 0x02
+#define MT9M111_SC_WINDOW_HEIGHT 0x03
+#define MT9M111_SC_WINDOW_WIDTH 0x04
+#define MT9M111_SC_HBLANK_CONTEXT_B 0x05
+#define MT9M111_SC_VBLANK_CONTEXT_B 0x06
+#define MT9M111_SC_HBLANK_CONTEXT_A 0x07
+#define MT9M111_SC_VBLANK_CONTEXT_A 0x08
+#define MT9M111_SC_SHUTTER_WIDTH 0x09
+#define MT9M111_SC_ROW_SPEED 0x0a
+
+#define MT9M111_SC_EXTRA_DELAY 0x0b
+#define MT9M111_SC_SHUTTER_DELAY 0x0c
+#define MT9M111_SC_RESET 0x0d
+#define MT9M111_SC_R_MODE_CONTEXT_B 0x20
+#define MT9M111_SC_R_MODE_CONTEXT_A 0x21
+#define MT9M111_SC_FLASH_CONTROL 0x23
+#define MT9M111_SC_GREEN_1_GAIN 0x2b
+#define MT9M111_SC_BLUE_GAIN 0x2c
+#define MT9M111_SC_RED_GAIN 0x2d
+#define MT9M111_SC_GREEN_2_GAIN 0x2e
+#define MT9M111_SC_GLOBAL_GAIN 0x2f
+
+#define MT9M111_RMB_MIRROR_ROWS (1 << 0)
+#define MT9M111_RMB_MIRROR_COLS (1 << 1)
+
+#define MT9M111_CONTEXT_CONTROL 0xc8
+#define MT9M111_PAGE_MAP 0xf0
+#define MT9M111_BYTEWISE_ADDRESS 0xf1
+
+#define MT9M111_CP_OPERATING_MODE_CTL 0x06
+#define MT9M111_CP_LUMA_OFFSET 0x34
+#define MT9M111_CP_LUMA_CLIP 0x35
+#define MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_A 0x3a
+#define MT9M111_CP_LENS_CORRECTION_1 0x3b
+#define MT9M111_CP_DEFECT_CORR_CONTEXT_A 0x4c
+#define MT9M111_CP_DEFECT_CORR_CONTEXT_B 0x4d
+#define MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_B 0x9b
+#define MT9M111_CP_GLOBAL_CLK_CONTROL 0xb3
+
+#define MT9M111_CC_AUTO_EXPOSURE_PARAMETER_18 0x65
+#define MT9M111_CC_AWB_PARAMETER_7 0x28
+
+#define MT9M111_SENSOR_CORE 0x00
+#define MT9M111_COLORPIPE 0x01
+#define MT9M111_CAMERA_CONTROL 0x02
+
+#define INITIAL_MAX_GAIN 64
+#define DEFAULT_GAIN 283
+
+/*****************************************************************************/
+
+/* Kernel module parameters */
+extern int force_sensor;
+extern int dump_sensor;
+extern unsigned int debug;
+
+int mt9m111_probe(struct sd *sd);
+int mt9m111_init(struct sd *sd);
+int mt9m111_power_down(struct sd *sd);
+
+int mt9m111_read_sensor(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len);
+
+int mt9m111_write_sensor(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len);
+
+void mt9m111_dump_registers(struct sd *sd);
+
+int mt9m111_set_vflip(struct gspca_dev *gspca_dev, __s32 val);
+int mt9m111_get_vflip(struct gspca_dev *gspca_dev, __s32 *val);
+int mt9m111_get_hflip(struct gspca_dev *gspca_dev, __s32 *val);
+int mt9m111_set_hflip(struct gspca_dev *gspca_dev, __s32 val);
+int mt9m111_get_gain(struct gspca_dev *gspca_dev, __s32 *val);
+int mt9m111_set_gain(struct gspca_dev *gspca_dev, __s32 val);
+
+static struct m5602_sensor mt9m111 = {
+ .name = "MT9M111",
+
+ .i2c_slave_id = 0xba,
+
+ .probe = mt9m111_probe,
+ .init = mt9m111_init,
+ .power_down = mt9m111_power_down,
+
+ .read_sensor = mt9m111_read_sensor,
+ .write_sensor = mt9m111_write_sensor,
+
+ .nctrls = 3,
+ .ctrls = {
+ {
+ {
+ .id = V4L2_CID_VFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "vertical flip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0
+ },
+ .set = mt9m111_set_vflip,
+ .get = mt9m111_get_vflip
+ }, {
+ {
+ .id = V4L2_CID_HFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "horizontal flip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0
+ },
+ .set = mt9m111_set_hflip,
+ .get = mt9m111_get_hflip
+ }, {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "gain",
+ .minimum = 0,
+ .maximum = (INITIAL_MAX_GAIN - 1) * 2 * 2 * 2,
+ .step = 1,
+ .default_value = DEFAULT_GAIN,
+ .flags = V4L2_CTRL_FLAG_SLIDER
+ },
+ .set = mt9m111_set_hflip,
+ .get = mt9m111_get_hflip
+ }
+ },
+
+ .nmodes = 1,
+ .modes = {
+ {
+ M5602_DEFAULT_FRAME_WIDTH,
+ M5602_DEFAULT_FRAME_HEIGHT,
+ V4L2_PIX_FMT_SBGGR8,
+ V4L2_FIELD_NONE,
+ .sizeimage =
+ M5602_DEFAULT_FRAME_WIDTH * M5602_DEFAULT_FRAME_HEIGHT,
+ .bytesperline = M5602_DEFAULT_FRAME_WIDTH,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1
+ }
+ }
+};
+
+static const unsigned char preinit_mt9m111[][4] =
+{
+ {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
+ {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+ {SENSOR, MT9M111_SC_RESET, 0xff, 0xf7},
+
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x05, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x04, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x3e, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3e, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
+
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x07, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x0b, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
+
+ {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a, 0x00}
+};
+
+static const unsigned char init_mt9m111[][4] =
+{
+ {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
+ {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+ {SENSOR, MT9M111_SC_RESET, 0xff, 0xff},
+ {SENSOR, MT9M111_SC_RESET, 0xff, 0xff},
+ {SENSOR, MT9M111_SC_RESET, 0xff, 0xde},
+ {SENSOR, MT9M111_SC_RESET, 0xff, 0xff},
+ {SENSOR, MT9M111_SC_RESET, 0xff, 0xf7},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x01},
+
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0xb3, 0x00},
+
+ {SENSOR, MT9M111_CP_GLOBAL_CLK_CONTROL, 0xff, 0xff},
+
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x05, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x04, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x3e, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3e, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x07, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x0b, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
+ {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a, 0x00},
+
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x05},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x29},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x08},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x01},
+ {SENSOR, MT9M111_CP_OPERATING_MODE_CTL, 0x00, 0x10},
+ {SENSOR, MT9M111_CP_LENS_CORRECTION_1, 0x04, 0x2a},
+ {SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_A, 0x00, 0x01},
+ {SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_B, 0x00, 0x01},
+ {SENSOR, MT9M111_CP_LUMA_OFFSET, 0x00, 0x00},
+ {SENSOR, MT9M111_CP_LUMA_CLIP, 0xff, 0x00},
+ {SENSOR, MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_A, 0x14, 0x00},
+ {SENSOR, MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_B, 0x14, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0xcd, 0x00},
+
+ {SENSOR, 0xcd, 0x00, 0x0e},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0xd0, 0x00},
+ {SENSOR, 0xd0, 0x00, 0x40},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x02},
+ {SENSOR, MT9M111_CC_AUTO_EXPOSURE_PARAMETER_18, 0x00, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x28, 0x00},
+ {SENSOR, MT9M111_CC_AWB_PARAMETER_7, 0xef, 0x07},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x28, 0x00},
+ {SENSOR, MT9M111_CC_AWB_PARAMETER_7, 0xef, 0x03},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x33, 0x00},
+ {SENSOR, 0x33, 0x03, 0x49},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x33, 0x00},
+
+ {SENSOR, 0x33, 0x03, 0x49},
+ {SENSOR, 0x34, 0xc0, 0x19},
+ {SENSOR, 0x3f, 0x20, 0x20},
+ {SENSOR, 0x40, 0x20, 0x20},
+ {SENSOR, 0x5a, 0xc0, 0x0a},
+ {SENSOR, 0x70, 0x7b, 0x0a},
+ {SENSOR, 0x71, 0xff, 0x00},
+ {SENSOR, 0x72, 0x19, 0x0e},
+ {SENSOR, 0x73, 0x18, 0x0f},
+ {SENSOR, 0x74, 0x57, 0x32},
+ {SENSOR, 0x75, 0x56, 0x34},
+ {SENSOR, 0x76, 0x73, 0x35},
+ {SENSOR, 0x77, 0x30, 0x12},
+ {SENSOR, 0x78, 0x79, 0x02},
+ {SENSOR, 0x79, 0x75, 0x06},
+ {SENSOR, 0x7a, 0x77, 0x0a},
+ {SENSOR, 0x7b, 0x78, 0x09},
+ {SENSOR, 0x7c, 0x7d, 0x06},
+ {SENSOR, 0x7d, 0x31, 0x10},
+ {SENSOR, 0x7e, 0x00, 0x7e},
+ {SENSOR, 0x80, 0x59, 0x04},
+ {SENSOR, 0x81, 0x59, 0x04},
+ {SENSOR, 0x82, 0x57, 0x0a},
+ {SENSOR, 0x83, 0x58, 0x0b},
+ {SENSOR, 0x84, 0x47, 0x0c},
+ {SENSOR, 0x85, 0x48, 0x0e},
+ {SENSOR, 0x86, 0x5b, 0x02},
+ {SENSOR, 0x87, 0x00, 0x5c},
+ {SENSOR, MT9M111_CONTEXT_CONTROL, 0x00, 0x08},
+ {SENSOR, 0x60, 0x00, 0x80},
+ {SENSOR, 0x61, 0x00, 0x00},
+ {SENSOR, 0x62, 0x00, 0x00},
+ {SENSOR, 0x63, 0x00, 0x00},
+ {SENSOR, 0x64, 0x00, 0x00},
+
+ {SENSOR, MT9M111_SC_ROWSTART, 0x00, 0x0d},
+ {SENSOR, MT9M111_SC_COLSTART, 0x00, 0x18},
+ {SENSOR, MT9M111_SC_WINDOW_HEIGHT, 0x04, 0x04},
+ {SENSOR, MT9M111_SC_WINDOW_WIDTH, 0x05, 0x08},
+ {SENSOR, MT9M111_SC_HBLANK_CONTEXT_B, 0x01, 0x38},
+ {SENSOR, MT9M111_SC_VBLANK_CONTEXT_B, 0x00, 0x11},
+ {SENSOR, MT9M111_SC_HBLANK_CONTEXT_A, 0x01, 0x38},
+ {SENSOR, MT9M111_SC_VBLANK_CONTEXT_A, 0x00, 0x11},
+ {SENSOR, MT9M111_SC_R_MODE_CONTEXT_B, 0x01, 0x03},
+ {SENSOR, MT9M111_SC_R_MODE_CONTEXT_A, 0x01, 0x03},
+ {SENSOR, 0x30, 0x04, 0x00},
+
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+ {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00},
+ {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x04, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x04, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x05, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x07, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xa0, 0x00},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+ {SENSOR, MT9M111_SC_SHUTTER_WIDTH, 0x01, 0xf4},
+ {SENSOR, MT9M111_SC_GLOBAL_GAIN, 0x00, 0xea},
+
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+ {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00},
+ {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x04, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x04, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x05, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x07, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x09},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x29},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x08},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x0c},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x04},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x01},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0xb3, 0x00},
+ {SENSOR, MT9M111_CP_GLOBAL_CLK_CONTROL, 0x00, 0x03},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x05, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x04, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x3e, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3e, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x07, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x0b, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
+ {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a, 0x00},
+
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x05},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x29},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x08},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x01},
+ {SENSOR, MT9M111_CP_OPERATING_MODE_CTL, 0x00, 0x10},
+ {SENSOR, MT9M111_CP_LENS_CORRECTION_1, 0x04, 0x2a},
+ {SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_A, 0x00, 0x01},
+ {SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_B, 0x00, 0x01},
+ {SENSOR, MT9M111_CP_LUMA_OFFSET, 0x00, 0x00},
+ {SENSOR, MT9M111_CP_LUMA_CLIP, 0xff, 0x00},
+ {SENSOR, MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_A, 0x14, 0x00},
+ {SENSOR, MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_B, 0x14, 0x00},
+
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0xcd, 0x00},
+ {SENSOR, 0xcd, 0x00, 0x0e},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0xd0, 0x00},
+ {SENSOR, 0xd0, 0x00, 0x40},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x02},
+ {SENSOR, MT9M111_CC_AUTO_EXPOSURE_PARAMETER_18, 0x00, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x28, 0x00},
+ {SENSOR, MT9M111_CC_AWB_PARAMETER_7, 0xef, 0x07},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x28, 0x00},
+ {SENSOR, MT9M111_CC_AWB_PARAMETER_7, 0xef, 0x03},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x33, 0x00},
+ {SENSOR, 0x33, 0x03, 0x49},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x33, 0x00},
+
+ {SENSOR, 0x33, 0x03, 0x49},
+ {SENSOR, 0x34, 0xc0, 0x19},
+ {SENSOR, 0x3f, 0x20, 0x20},
+ {SENSOR, 0x40, 0x20, 0x20},
+ {SENSOR, 0x5a, 0xc0, 0x0a},
+ {SENSOR, 0x70, 0x7b, 0x0a},
+ {SENSOR, 0x71, 0xff, 0x00},
+ {SENSOR, 0x72, 0x19, 0x0e},
+ {SENSOR, 0x73, 0x18, 0x0f},
+ {SENSOR, 0x74, 0x57, 0x32},
+ {SENSOR, 0x75, 0x56, 0x34},
+ {SENSOR, 0x76, 0x73, 0x35},
+ {SENSOR, 0x77, 0x30, 0x12},
+ {SENSOR, 0x78, 0x79, 0x02},
+ {SENSOR, 0x79, 0x75, 0x06},
+ {SENSOR, 0x7a, 0x77, 0x0a},
+ {SENSOR, 0x7b, 0x78, 0x09},
+ {SENSOR, 0x7c, 0x7d, 0x06},
+ {SENSOR, 0x7d, 0x31, 0x10},
+ {SENSOR, 0x7e, 0x00, 0x7e},
+ {SENSOR, 0x80, 0x59, 0x04},
+ {SENSOR, 0x81, 0x59, 0x04},
+ {SENSOR, 0x82, 0x57, 0x0a},
+ {SENSOR, 0x83, 0x58, 0x0b},
+ {SENSOR, 0x84, 0x47, 0x0c},
+ {SENSOR, 0x85, 0x48, 0x0e},
+ {SENSOR, 0x86, 0x5b, 0x02},
+ {SENSOR, 0x87, 0x00, 0x5c},
+ {SENSOR, MT9M111_CONTEXT_CONTROL, 0x00, 0x08},
+ {SENSOR, 0x60, 0x00, 0x80},
+ {SENSOR, 0x61, 0x00, 0x00},
+ {SENSOR, 0x62, 0x00, 0x00},
+ {SENSOR, 0x63, 0x00, 0x00},
+ {SENSOR, 0x64, 0x00, 0x00},
+
+ {SENSOR, MT9M111_SC_ROWSTART, 0x00, 0x0d},
+ {SENSOR, MT9M111_SC_COLSTART, 0x00, 0x18},
+ {SENSOR, MT9M111_SC_WINDOW_HEIGHT, 0x04, 0x04},
+ {SENSOR, MT9M111_SC_WINDOW_WIDTH, 0x05, 0x08},
+ {SENSOR, MT9M111_SC_HBLANK_CONTEXT_B, 0x01, 0x38},
+ {SENSOR, MT9M111_SC_VBLANK_CONTEXT_B, 0x00, 0x11},
+ {SENSOR, MT9M111_SC_HBLANK_CONTEXT_A, 0x01, 0x38},
+ {SENSOR, MT9M111_SC_VBLANK_CONTEXT_A, 0x00, 0x11},
+ {SENSOR, MT9M111_SC_R_MODE_CONTEXT_B, 0x01, 0x03},
+ {SENSOR, MT9M111_SC_R_MODE_CONTEXT_A, 0x01, 0x03},
+ {SENSOR, 0x30, 0x04, 0x00},
+
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+ {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00},
+ {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x04, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x04, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x05, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x07, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xa0, 0x00},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+ {SENSOR, MT9M111_SC_SHUTTER_WIDTH, 0x01, 0xf4},
+ {SENSOR, MT9M111_SC_GLOBAL_GAIN, 0x00, 0xea},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x09},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x29},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x08},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x0c},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x04},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x01},
+
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0xb3, 0x00},
+ {SENSOR, MT9M111_CP_GLOBAL_CLK_CONTROL, 0x00, 0x03},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x05, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x04, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x3e, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3e, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x07, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x0b, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
+ {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a, 0x00},
+
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x05},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x29},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x08},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x01},
+ {SENSOR, MT9M111_CP_OPERATING_MODE_CTL, 0x00, 0x10},
+ {SENSOR, MT9M111_CP_LENS_CORRECTION_1, 0x04, 0x2a},
+ {SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_A, 0x00, 0x01},
+ {SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_B, 0x00, 0x01},
+ {SENSOR, MT9M111_CP_LUMA_OFFSET, 0x00, 0x00},
+ {SENSOR, MT9M111_CP_LUMA_CLIP, 0xff, 0x00},
+ {SENSOR, MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_A, 0x14, 0x00},
+ {SENSOR, MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_B, 0x14, 0x00},
+
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0xcd, 0x00},
+ {SENSOR, 0xcd, 0x00, 0x0e},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0xd0, 0x00},
+ {SENSOR, 0xd0, 0x00, 0x40},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x02},
+ {SENSOR, MT9M111_CC_AUTO_EXPOSURE_PARAMETER_18, 0x00, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x28, 0x00},
+ {SENSOR, MT9M111_CC_AWB_PARAMETER_7, 0xef, 0x07},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x28, 0x00},
+ {SENSOR, MT9M111_CC_AWB_PARAMETER_7, 0xef, 0x03},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x33, 0x00},
+ {SENSOR, 0x33, 0x03, 0x49},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x33, 0x00},
+
+ {SENSOR, 0x33, 0x03, 0x49},
+ {SENSOR, 0x34, 0xc0, 0x19},
+ {SENSOR, 0x3f, 0x20, 0x20},
+ {SENSOR, 0x40, 0x20, 0x20},
+ {SENSOR, 0x5a, 0xc0, 0x0a},
+ {SENSOR, 0x70, 0x7b, 0x0a},
+ {SENSOR, 0x71, 0xff, 0x00},
+ {SENSOR, 0x72, 0x19, 0x0e},
+ {SENSOR, 0x73, 0x18, 0x0f},
+ {SENSOR, 0x74, 0x57, 0x32},
+ {SENSOR, 0x75, 0x56, 0x34},
+ {SENSOR, 0x76, 0x73, 0x35},
+ {SENSOR, 0x77, 0x30, 0x12},
+ {SENSOR, 0x78, 0x79, 0x02},
+ {SENSOR, 0x79, 0x75, 0x06},
+ {SENSOR, 0x7a, 0x77, 0x0a},
+ {SENSOR, 0x7b, 0x78, 0x09},
+ {SENSOR, 0x7c, 0x7d, 0x06},
+ {SENSOR, 0x7d, 0x31, 0x10},
+ {SENSOR, 0x7e, 0x00, 0x7e},
+ {SENSOR, 0x80, 0x59, 0x04},
+ {SENSOR, 0x81, 0x59, 0x04},
+ {SENSOR, 0x82, 0x57, 0x0a},
+ {SENSOR, 0x83, 0x58, 0x0b},
+ {SENSOR, 0x84, 0x47, 0x0c},
+ {SENSOR, 0x85, 0x48, 0x0e},
+ {SENSOR, 0x86, 0x5b, 0x02},
+ {SENSOR, 0x87, 0x00, 0x5c},
+ {SENSOR, MT9M111_CONTEXT_CONTROL, 0x00, 0x08},
+ {SENSOR, 0x60, 0x00, 0x80},
+ {SENSOR, 0x61, 0x00, 0x00},
+ {SENSOR, 0x62, 0x00, 0x00},
+ {SENSOR, 0x63, 0x00, 0x00},
+ {SENSOR, 0x64, 0x00, 0x00},
+
+ {SENSOR, MT9M111_SC_ROWSTART, 0x00, 0x0d},
+ {SENSOR, MT9M111_SC_COLSTART, 0x00, 0x18},
+ {SENSOR, MT9M111_SC_WINDOW_HEIGHT, 0x04, 0x04},
+ {SENSOR, MT9M111_SC_WINDOW_WIDTH, 0x05, 0x08},
+ {SENSOR, MT9M111_SC_HBLANK_CONTEXT_B, 0x01, 0x38},
+ {SENSOR, MT9M111_SC_VBLANK_CONTEXT_B, 0x00, 0x11},
+ {SENSOR, MT9M111_SC_HBLANK_CONTEXT_A, 0x01, 0x38},
+ {SENSOR, MT9M111_SC_VBLANK_CONTEXT_A, 0x00, 0x11},
+ {SENSOR, MT9M111_SC_R_MODE_CONTEXT_B, 0x01, 0x03},
+ {SENSOR, MT9M111_SC_R_MODE_CONTEXT_A, 0x01, 0x03},
+ {SENSOR, 0x30, 0x04, 0x00},
+
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+ {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00},
+ {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x04, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x04, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x05, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x07, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xa0, 0x00},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+ {SENSOR, MT9M111_SC_SHUTTER_WIDTH, 0x01, 0xf4},
+ {SENSOR, MT9M111_SC_GLOBAL_GAIN, 0x00, 0xea},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+ {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00},
+ {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x09},
+ {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00},
+ {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x29},
+ {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00},
+ {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x08},
+ {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00},
+ {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x0c},
+ {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00},
+ {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x04},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x01},
+ {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0xb3, 0x00},
+ {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+ {SENSOR, MT9M111_CP_GLOBAL_CLK_CONTROL, 0x00, 0x03},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x05, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x04, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x3e, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3e, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x07, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x0b, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
+ {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a, 0x00},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+ {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00},
+ {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x05},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+ {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00},
+ {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x29},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+ {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00},
+ {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x08},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x01},
+ {SENSOR, MT9M111_CP_OPERATING_MODE_CTL, 0x00, 0x10},
+ {SENSOR, MT9M111_CP_LENS_CORRECTION_1, 0x04, 0x2a},
+ {SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_A, 0x00, 0x01},
+ {SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_B, 0x00, 0x01},
+ {SENSOR, MT9M111_CP_LUMA_OFFSET, 0x00, 0x00},
+ {SENSOR, MT9M111_CP_LUMA_CLIP, 0xff, 0x00},
+ {SENSOR, MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_A, 0x14, 0x00},
+ {SENSOR, MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_B, 0x14, 0x00},
+
+ {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0xcd, 0x00},
+ {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+ {SENSOR, 0xcd, 0x00, 0x0e},
+ {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0xd0, 0x00},
+ {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+ {SENSOR, 0xd0, 0x00, 0x40},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x02},
+ {SENSOR, MT9M111_CC_AUTO_EXPOSURE_PARAMETER_18, 0x00, 0x00},
+ {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x28, 0x00},
+ {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+ {SENSOR, MT9M111_CC_AWB_PARAMETER_7, 0xef, 0x07},
+ {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x28, 0x00},
+ {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+ {SENSOR, MT9M111_CC_AWB_PARAMETER_7, 0xef, 0x03},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+ {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x33, 0x00},
+ {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+ {SENSOR, 0x33, 0x03, 0x49},
+ {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x33, 0x00},
+ {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+
+ {SENSOR, 0x33, 0x03, 0x49},
+ {SENSOR, 0x34, 0xc0, 0x19},
+ {SENSOR, 0x3f, 0x20, 0x20},
+ {SENSOR, 0x40, 0x20, 0x20},
+ {SENSOR, 0x5a, 0xc0, 0x0a},
+ {SENSOR, 0x70, 0x7b, 0x0a},
+ {SENSOR, 0x71, 0xff, 0x00},
+ {SENSOR, 0x72, 0x19, 0x0e},
+ {SENSOR, 0x73, 0x18, 0x0f},
+ {SENSOR, 0x74, 0x57, 0x32},
+ {SENSOR, 0x75, 0x56, 0x34},
+ {SENSOR, 0x76, 0x73, 0x35},
+ {SENSOR, 0x77, 0x30, 0x12},
+ {SENSOR, 0x78, 0x79, 0x02},
+ {SENSOR, 0x79, 0x75, 0x06},
+ {SENSOR, 0x7a, 0x77, 0x0a},
+ {SENSOR, 0x7b, 0x78, 0x09},
+ {SENSOR, 0x7c, 0x7d, 0x06},
+ {SENSOR, 0x7d, 0x31, 0x10},
+ {SENSOR, 0x7e, 0x00, 0x7e},
+ {SENSOR, 0x80, 0x59, 0x04},
+ {SENSOR, 0x81, 0x59, 0x04},
+ {SENSOR, 0x82, 0x57, 0x0a},
+ {SENSOR, 0x83, 0x58, 0x0b},
+ {SENSOR, 0x84, 0x47, 0x0c},
+ {SENSOR, 0x85, 0x48, 0x0e},
+ {SENSOR, 0x86, 0x5b, 0x02},
+ {SENSOR, 0x87, 0x00, 0x5c},
+ {SENSOR, MT9M111_CONTEXT_CONTROL, 0x00, 0x08},
+ {SENSOR, 0x60, 0x00, 0x80},
+ {SENSOR, 0x61, 0x00, 0x00},
+ {SENSOR, 0x62, 0x00, 0x00},
+ {SENSOR, 0x63, 0x00, 0x00},
+ {SENSOR, 0x64, 0x00, 0x00},
+ {SENSOR, MT9M111_SC_ROWSTART, 0x00, 0x0d},
+ {SENSOR, MT9M111_SC_COLSTART, 0x00, 0x12},
+ {SENSOR, MT9M111_SC_WINDOW_HEIGHT, 0x04, 0x00},
+ {SENSOR, MT9M111_SC_WINDOW_WIDTH, 0x05, 0x10},
+ {SENSOR, MT9M111_SC_HBLANK_CONTEXT_B, 0x01, 0x60},
+ {SENSOR, MT9M111_SC_VBLANK_CONTEXT_B, 0x00, 0x11},
+ {SENSOR, MT9M111_SC_HBLANK_CONTEXT_A, 0x01, 0x60},
+ {SENSOR, MT9M111_SC_VBLANK_CONTEXT_A, 0x00, 0x11},
+ {SENSOR, MT9M111_SC_R_MODE_CONTEXT_B, 0x01, 0x0f},
+ {SENSOR, MT9M111_SC_R_MODE_CONTEXT_A, 0x01, 0x0f},
+ {SENSOR, 0x30, 0x04, 0x00},
+
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+ {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00},
+ {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x01, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0xe3, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x02, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x87, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+ {SENSOR, MT9M111_SC_SHUTTER_WIDTH, 0x01, 0x90},
+ {SENSOR, MT9M111_SC_GLOBAL_GAIN, 0x00, 0xe6},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+ {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00},
+ {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x09},
+ {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00},
+ {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x29},
+ {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00},
+ {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x08},
+ {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00},
+ {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x0c},
+ {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00},
+ {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x04},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x01},
+ {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0xb3, 0x00},
+ {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+ {SENSOR, MT9M111_CP_GLOBAL_CLK_CONTROL, 0x00, 0x03},
+
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x05, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x04, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x3e, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3e, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x07, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x0b, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
+ {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a, 0x00},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+ {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00},
+ {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x05},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+ {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00},
+ {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x29},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+ {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00},
+ {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x08},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x01},
+ {SENSOR, MT9M111_CP_OPERATING_MODE_CTL, 0x00, 0x10},
+ {SENSOR, MT9M111_CP_LENS_CORRECTION_1, 0x04, 0x2a},
+ {SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_A, 0x00, 0x01},
+ {SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_B, 0x00, 0x01},
+ {SENSOR, MT9M111_CP_LUMA_OFFSET, 0x00, 0x00},
+ {SENSOR, MT9M111_CP_LUMA_CLIP, 0xff, 0x00},
+ {SENSOR, MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_A, 0x14, 0x00},
+ {SENSOR, MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_B, 0x14, 0x00},
+
+ {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0xcd, 0x00},
+ {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+ {SENSOR, 0xcd, 0x00, 0x0e},
+ {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0xd0, 0x00},
+ {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+ {SENSOR, 0xd0, 0x00, 0x40},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x02},
+ {SENSOR, MT9M111_CC_AUTO_EXPOSURE_PARAMETER_18, 0x00, 0x00},
+ {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x28, 0x00},
+ {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+ {SENSOR, MT9M111_CC_AWB_PARAMETER_7, 0xef, 0x07},
+ {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x28, 0x00},
+ {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+ {SENSOR, MT9M111_CC_AWB_PARAMETER_7, 0xef, 0x03},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+
+ {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x33, 0x00},
+ {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+ {SENSOR, 0x33, 0x03, 0x49},
+ {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x33, 0x00},
+ {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+
+ {SENSOR, 0x33, 0x03, 0x49},
+ {SENSOR, 0x34, 0xc0, 0x19},
+ {SENSOR, 0x3f, 0x20, 0x20},
+ {SENSOR, 0x40, 0x20, 0x20},
+ {SENSOR, 0x5a, 0xc0, 0x0a},
+ {SENSOR, 0x70, 0x7b, 0x0a},
+ {SENSOR, 0x71, 0xff, 0x00},
+ {SENSOR, 0x72, 0x19, 0x0e},
+ {SENSOR, 0x73, 0x18, 0x0f},
+ {SENSOR, 0x74, 0x57, 0x32},
+ {SENSOR, 0x75, 0x56, 0x34},
+ {SENSOR, 0x76, 0x73, 0x35},
+ {SENSOR, 0x77, 0x30, 0x12},
+ {SENSOR, 0x78, 0x79, 0x02},
+ {SENSOR, 0x79, 0x75, 0x06},
+ {SENSOR, 0x7a, 0x77, 0x0a},
+ {SENSOR, 0x7b, 0x78, 0x09},
+ {SENSOR, 0x7c, 0x7d, 0x06},
+ {SENSOR, 0x7d, 0x31, 0x10},
+ {SENSOR, 0x7e, 0x00, 0x7e},
+ {SENSOR, 0x80, 0x59, 0x04},
+ {SENSOR, 0x81, 0x59, 0x04},
+ {SENSOR, 0x82, 0x57, 0x0a},
+ {SENSOR, 0x83, 0x58, 0x0b},
+ {SENSOR, 0x84, 0x47, 0x0c},
+ {SENSOR, 0x85, 0x48, 0x0e},
+ {SENSOR, 0x86, 0x5b, 0x02},
+ {SENSOR, 0x87, 0x00, 0x5c},
+ {SENSOR, MT9M111_CONTEXT_CONTROL, 0x00, 0x08},
+ {SENSOR, 0x60, 0x00, 0x80},
+ {SENSOR, 0x61, 0x00, 0x00},
+ {SENSOR, 0x62, 0x00, 0x00},
+ {SENSOR, 0x63, 0x00, 0x00},
+ {SENSOR, 0x64, 0x00, 0x00},
+
+ {SENSOR, MT9M111_SC_ROWSTART, 0x00, 0x0d},
+ {SENSOR, MT9M111_SC_COLSTART, 0x00, 0x12},
+ {SENSOR, MT9M111_SC_WINDOW_HEIGHT, 0x04, 0x00},
+ {SENSOR, MT9M111_SC_WINDOW_WIDTH, 0x05, 0x10},
+ {SENSOR, MT9M111_SC_HBLANK_CONTEXT_B, 0x01, 0x60},
+ {SENSOR, MT9M111_SC_VBLANK_CONTEXT_B, 0x00, 0x11},
+ {SENSOR, MT9M111_SC_HBLANK_CONTEXT_A, 0x01, 0x60},
+ {SENSOR, MT9M111_SC_VBLANK_CONTEXT_A, 0x00, 0x11},
+ {SENSOR, MT9M111_SC_R_MODE_CONTEXT_B, 0x01, 0x0f},
+ {SENSOR, MT9M111_SC_R_MODE_CONTEXT_A, 0x01, 0x0f},
+ {SENSOR, 0x30, 0x04, 0x00},
+
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+ {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00},
+ {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x01, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0xe0, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x02, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x7f, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+ /* Set number of blank rows chosen to 400 */
+ {SENSOR, MT9M111_SC_SHUTTER_WIDTH, 0x01, 0x90},
+ /* Set the global gain to 283 (of 512) */
+ {SENSOR, MT9M111_SC_GLOBAL_GAIN, 0x03, 0x63}
+};
+
+#endif
diff --git a/linux/drivers/media/video/gspca/m5602/m5602_ov9650.c b/linux/drivers/media/video/gspca/m5602/m5602_ov9650.c
new file mode 100644
index 000000000..31c589625
--- /dev/null
+++ b/linux/drivers/media/video/gspca/m5602/m5602_ov9650.c
@@ -0,0 +1,546 @@
+/*
+ * Driver for the ov9650 sensor
+ *
+ * Copyright (C) 2008 Erik Andren
+ * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * 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, version 2.
+ *
+ */
+
+#include "m5602_ov9650.h"
+
+int ov9650_read_sensor(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len)
+{
+ int err, i;
+
+ /* The ov9650 registers have a max depth of one byte */
+ if (len > 1 || !len)
+ return -EINVAL;
+
+ do {
+ err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, i2c_data);
+ } while ((*i2c_data & I2C_BUSY) && !err);
+
+ m5602_write_bridge(sd, M5602_XB_I2C_DEV_ADDR,
+ ov9650.i2c_slave_id);
+ m5602_write_bridge(sd, M5602_XB_I2C_REG_ADDR, address);
+ m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x10 + len);
+ m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x08);
+
+ for (i = 0; i < len; i++) {
+ err = m5602_read_bridge(sd, M5602_XB_I2C_DATA, &(i2c_data[i]));
+
+ PDEBUG(DBG_TRACE, "Reading sensor register "
+ "0x%x containing 0x%x ", address, *i2c_data);
+ }
+ return (err < 0) ? err : 0;
+}
+
+int ov9650_write_sensor(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len)
+{
+ int err, i;
+ u8 *p;
+ struct usb_device *udev = sd->gspca_dev.dev;
+ __u8 *buf = sd->gspca_dev.usb_buf;
+
+ /* The ov9650 only supports one byte writes */
+ if (len > 1 || !len)
+ return -EINVAL;
+
+ memcpy(buf, sensor_urb_skeleton,
+ sizeof(sensor_urb_skeleton));
+
+ buf[11] = sd->sensor->i2c_slave_id;
+ buf[15] = address;
+
+ /* Special case larger sensor writes */
+ p = buf + 16;
+
+ /* Copy a four byte write sequence for each byte to be written to */
+ for (i = 0; i < len; i++) {
+ memcpy(p, sensor_urb_skeleton + 16, 4);
+ p[3] = i2c_data[i];
+ p += 4;
+ PDEBUG(DBG_TRACE, "Writing sensor register 0x%x with 0x%x",
+ address, i2c_data[i]);
+ }
+
+ /* Copy the tailer */
+ memcpy(p, sensor_urb_skeleton + 20, 4);
+
+ /* Set the total length */
+ p[3] = 0x10 + len;
+
+ err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ 0x04, 0x40, 0x19,
+ 0x0000, buf,
+ 20 + len * 4, M5602_URB_MSG_TIMEOUT);
+
+ return (err < 0) ? err : 0;
+}
+
+int ov9650_probe(struct sd *sd)
+{
+ u8 prod_id = 0, ver_id = 0, i;
+
+ if (force_sensor) {
+ if (force_sensor == OV9650_SENSOR) {
+ info("Forcing an %s sensor", ov9650.name);
+ goto sensor_found;
+ }
+ /* If we want to force another sensor,
+ don't try to probe this one */
+ return -ENODEV;
+ }
+
+ info("Probing for an ov9650 sensor");
+
+ /* Run the pre-init to actually probe the unit */
+ for (i = 0; i < ARRAY_SIZE(preinit_ov9650); i++) {
+ u8 data = preinit_ov9650[i][2];
+ if (preinit_ov9650[i][0] == SENSOR)
+ ov9650_write_sensor(sd,
+ preinit_ov9650[i][1], &data, 1);
+ else
+ m5602_write_bridge(sd, preinit_ov9650[i][1], data);
+ }
+
+ if (ov9650_read_sensor(sd, OV9650_PID, &prod_id, 1))
+ return -ENODEV;
+
+ if (ov9650_read_sensor(sd, OV9650_VER, &ver_id, 1))
+ return -ENODEV;
+
+ if ((prod_id == 0x96) && (ver_id == 0x52)) {
+ info("Detected an ov9650 sensor");
+ goto sensor_found;
+ }
+
+ return -ENODEV;
+
+sensor_found:
+ sd->gspca_dev.cam.cam_mode = ov9650.modes;
+ sd->gspca_dev.cam.nmodes = ov9650.nmodes;
+ sd->desc->ctrls = ov9650.ctrls;
+ sd->desc->nctrls = ov9650.nctrls;
+ return 0;
+}
+
+int ov9650_init(struct sd *sd)
+{
+ int i, err = 0;
+ u8 data;
+
+ if (dump_sensor)
+ ov9650_dump_registers(sd);
+
+ for (i = 0; i < ARRAY_SIZE(init_ov9650) && !err; i++) {
+ data = init_ov9650[i][2];
+ if (init_ov9650[i][0] == SENSOR)
+ err = ov9650_write_sensor(sd, init_ov9650[i][1],
+ &data, 1);
+ else
+ err = m5602_write_bridge(sd, init_ov9650[i][1], data);
+ }
+
+ if (!err && dmi_check_system(ov9650_flip_dmi_table)) {
+ info("vflip quirk active");
+ data = 0x30;
+ err = ov9650_write_sensor(sd, OV9650_MVFP, &data, 1);
+ }
+
+ return (err < 0) ? err : 0;
+}
+
+int ov9650_power_down(struct sd *sd)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(power_down_ov9650); i++) {
+ u8 data = power_down_ov9650[i][2];
+ if (power_down_ov9650[i][0] == SENSOR)
+ ov9650_write_sensor(sd,
+ power_down_ov9650[i][1], &data, 1);
+ else
+ m5602_write_bridge(sd, power_down_ov9650[i][1], data);
+ }
+
+ return 0;
+}
+
+int ov9650_get_exposure(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 i2c_data;
+ int err;
+
+ err = ov9650_read_sensor(sd, OV9650_COM1, &i2c_data, 1);
+ if (err < 0)
+ goto out;
+ *val = i2c_data & 0x03;
+
+ err = ov9650_read_sensor(sd, OV9650_AECH, &i2c_data, 1);
+ if (err < 0)
+ goto out;
+ *val |= (i2c_data << 2);
+
+ err = ov9650_read_sensor(sd, OV9650_AECHM, &i2c_data, 1);
+ if (err < 0)
+ goto out;
+ *val |= (i2c_data & 0x3f) << 10;
+
+ PDEBUG(DBG_V4L2_CID, "Read exposure %d", *val);
+out:
+ return (err < 0) ? err : 0;
+}
+
+int ov9650_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 i2c_data;
+ int err;
+
+ PDEBUG(DBG_V4L2_CID, "Set exposure to %d",
+ val & 0xffff);
+
+ /* The 6 MSBs */
+ i2c_data = (val >> 10) & 0x3f;
+ err = ov9650_write_sensor(sd, OV9650_AECHM,
+ &i2c_data, 1);
+ if (err < 0)
+ goto out;
+
+ /* The 8 middle bits */
+ i2c_data = (val >> 2) & 0xff;
+ err = ov9650_write_sensor(sd, OV9650_AECH,
+ &i2c_data, 1);
+ if (err < 0)
+ goto out;
+
+ /* The 2 LSBs */
+ i2c_data = val & 0x03;
+ err = ov9650_write_sensor(sd, OV9650_COM1, &i2c_data, 1);
+
+out:
+ return (err < 0) ? err : 0;
+}
+
+int ov9650_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ int err;
+ u8 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ ov9650_read_sensor(sd, OV9650_VREF, &i2c_data, 1);
+ *val = (i2c_data & 0x03) << 8;
+
+ err = ov9650_read_sensor(sd, OV9650_GAIN, &i2c_data, 1);
+ *val |= i2c_data;
+ PDEBUG(DBG_V4L2_CID, "Read gain %d", *val);
+ return (err < 0) ? err : 0;
+}
+
+int ov9650_set_gain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ u8 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ /* The 2 MSB */
+ /* Read the OV9650_VREF register first to avoid
+ corrupting the VREF high and low bits */
+ ov9650_read_sensor(sd, OV9650_VREF, &i2c_data, 1);
+ /* Mask away all uninteresting bits */
+ i2c_data = ((val & 0x0300) >> 2) |
+ (i2c_data & 0x3F);
+ err = ov9650_write_sensor(sd, OV9650_VREF, &i2c_data, 1);
+
+ /* The 8 LSBs */
+ i2c_data = val & 0xff;
+ err = ov9650_write_sensor(sd, OV9650_GAIN, &i2c_data, 1);
+ return (err < 0) ? err : 0;
+}
+
+int ov9650_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ int err;
+ u8 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ err = ov9650_read_sensor(sd, OV9650_RED, &i2c_data, 1);
+ *val = i2c_data;
+
+ PDEBUG(DBG_V4L2_CID, "Read red gain %d", *val);
+
+ return (err < 0) ? err : 0;
+}
+
+int ov9650_set_red_balance(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ u8 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ PDEBUG(DBG_V4L2_CID, "Set red gain to %d",
+ val & 0xff);
+
+ i2c_data = val & 0xff;
+ err = ov9650_write_sensor(sd, OV9650_RED, &i2c_data, 1);
+
+ return (err < 0) ? err : 0;
+}
+
+int ov9650_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ int err;
+ u8 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ err = ov9650_read_sensor(sd, OV9650_BLUE, &i2c_data, 1);
+ *val = i2c_data;
+
+ PDEBUG(DBG_V4L2_CID, "Read blue gain %d", *val);
+
+ return (err < 0) ? err : 0;
+}
+
+int ov9650_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ u8 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ PDEBUG(DBG_V4L2_CID, "Set blue gain to %d",
+ val & 0xff);
+
+ i2c_data = val & 0xff;
+ err = ov9650_write_sensor(sd, OV9650_BLUE, &i2c_data, 1);
+
+ return (err < 0) ? err : 0;
+}
+
+int ov9650_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ int err;
+ u8 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ err = ov9650_read_sensor(sd, OV9650_MVFP, &i2c_data, 1);
+ if (dmi_check_system(ov9650_flip_dmi_table))
+ *val = ((i2c_data & OV9650_HFLIP) >> 5) ? 0 : 1;
+ else
+ *val = (i2c_data & OV9650_HFLIP) >> 5;
+ PDEBUG(DBG_V4L2_CID, "Read horizontal flip %d", *val);
+
+ return (err < 0) ? err : 0;
+}
+
+int ov9650_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ u8 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ PDEBUG(DBG_V4L2_CID, "Set horizontal flip to %d", val);
+ err = ov9650_read_sensor(sd, OV9650_MVFP, &i2c_data, 1);
+ if (err < 0)
+ goto out;
+
+ if (dmi_check_system(ov9650_flip_dmi_table))
+ i2c_data = ((i2c_data & 0xdf) |
+ (((val ? 0 : 1) & 0x01) << 5));
+ else
+ i2c_data = ((i2c_data & 0xdf) |
+ ((val & 0x01) << 5));
+
+ err = ov9650_write_sensor(sd, OV9650_MVFP, &i2c_data, 1);
+out:
+ return (err < 0) ? err : 0;
+}
+
+int ov9650_get_vflip(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ int err;
+ u8 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ err = ov9650_read_sensor(sd, OV9650_MVFP, &i2c_data, 1);
+ if (dmi_check_system(ov9650_flip_dmi_table))
+ *val = ((i2c_data & 0x10) >> 4) ? 0 : 1;
+ else
+ *val = (i2c_data & 0x10) >> 4;
+ PDEBUG(DBG_V4L2_CID, "Read vertical flip %d", *val);
+
+ return (err < 0) ? err : 0;
+}
+
+int ov9650_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ u8 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ PDEBUG(DBG_V4L2_CID, "Set vertical flip to %d", val);
+ err = ov9650_read_sensor(sd, OV9650_MVFP, &i2c_data, 1);
+ if (err < 0)
+ goto out;
+
+ if (dmi_check_system(ov9650_flip_dmi_table))
+ i2c_data = ((i2c_data & 0xef) |
+ (((val ? 0 : 1) & 0x01) << 4));
+ else
+ i2c_data = ((i2c_data & 0xef) |
+ ((val & 0x01) << 4));
+
+ err = ov9650_write_sensor(sd, OV9650_MVFP, &i2c_data, 1);
+out:
+ return (err < 0) ? err : 0;
+}
+
+int ov9650_get_brightness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ int err;
+ u8 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ err = ov9650_read_sensor(sd, OV9650_VREF, &i2c_data, 1);
+ if (err < 0)
+ goto out;
+ *val = (i2c_data & 0x03) << 8;
+
+ err = ov9650_read_sensor(sd, OV9650_GAIN, &i2c_data, 1);
+ *val |= i2c_data;
+ PDEBUG(DBG_V4L2_CID, "Read gain %d", *val);
+out:
+ return (err < 0) ? err : 0;
+}
+
+int ov9650_set_brightness(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ u8 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ PDEBUG(DBG_V4L2_CID, "Set gain to %d", val & 0x3ff);
+
+ /* Read the OV9650_VREF register first to avoid
+ corrupting the VREF high and low bits */
+ err = ov9650_read_sensor(sd, OV9650_VREF, &i2c_data, 1);
+ if (err < 0)
+ goto out;
+
+ /* Mask away all uninteresting bits */
+ i2c_data = ((val & 0x0300) >> 2) | (i2c_data & 0x3F);
+ err = ov9650_write_sensor(sd, OV9650_VREF, &i2c_data, 1);
+ if (err < 0)
+ goto out;
+
+ /* The 8 LSBs */
+ i2c_data = val & 0xff;
+ err = ov9650_write_sensor(sd, OV9650_GAIN, &i2c_data, 1);
+
+out:
+ return (err < 0) ? err : 0;
+}
+
+int ov9650_get_auto_white_balance(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ int err;
+ u8 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ err = ov9650_read_sensor(sd, OV9650_COM8, &i2c_data, 1);
+ *val = (i2c_data & OV9650_AWB_EN) >> 1;
+ PDEBUG(DBG_V4L2_CID, "Read auto white balance %d", *val);
+
+ return (err < 0) ? err : 0;
+}
+
+int ov9650_set_auto_white_balance(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ u8 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ PDEBUG(DBG_V4L2_CID, "Set auto white balance to %d", val);
+ err = ov9650_read_sensor(sd, OV9650_COM8, &i2c_data, 1);
+ if (err < 0)
+ goto out;
+
+ i2c_data = ((i2c_data & 0xfd) | ((val & 0x01) << 1));
+ err = ov9650_write_sensor(sd, OV9650_COM8, &i2c_data, 1);
+out:
+ return (err < 0) ? err : 0;
+}
+
+int ov9650_get_auto_gain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ int err;
+ u8 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ err = ov9650_read_sensor(sd, OV9650_COM8, &i2c_data, 1);
+ *val = (i2c_data & OV9650_AGC_EN) >> 2;
+ PDEBUG(DBG_V4L2_CID, "Read auto gain control %d", *val);
+
+ return (err < 0) ? err : 0;
+}
+
+int ov9650_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ u8 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ PDEBUG(DBG_V4L2_CID, "Set auto gain control to %d", val);
+ err = ov9650_read_sensor(sd, OV9650_COM8, &i2c_data, 1);
+ if (err < 0)
+ goto out;
+
+ i2c_data = ((i2c_data & 0xfb) | ((val & 0x01) << 2));
+ err = ov9650_write_sensor(sd, OV9650_COM8, &i2c_data, 1);
+out:
+ return (err < 0) ? err : 0;
+}
+
+void ov9650_dump_registers(struct sd *sd)
+{
+ int address;
+ info("Dumping the ov9650 register state");
+ for (address = 0; address < 0xa9; address++) {
+ u8 value;
+ ov9650_read_sensor(sd, address, &value, 1);
+ info("register 0x%x contains 0x%x",
+ address, value);
+ }
+
+ info("ov9650 register state dump complete");
+
+ info("Probing for which registers that are read/write");
+ for (address = 0; address < 0xff; address++) {
+ u8 old_value, ctrl_value;
+ u8 test_value[2] = {0xff, 0xff};
+
+ ov9650_read_sensor(sd, address, &old_value, 1);
+ ov9650_write_sensor(sd, address, test_value, 1);
+ ov9650_read_sensor(sd, address, &ctrl_value, 1);
+
+ if (ctrl_value == test_value[0])
+ info("register 0x%x is writeable", address);
+ else
+ info("register 0x%x is read only", address);
+
+ /* Restore original value */
+ ov9650_write_sensor(sd, address, &old_value, 1);
+ }
+}
diff --git a/linux/drivers/media/video/gspca/m5602/m5602_ov9650.h b/linux/drivers/media/video/gspca/m5602/m5602_ov9650.h
new file mode 100644
index 000000000..70429b411
--- /dev/null
+++ b/linux/drivers/media/video/gspca/m5602/m5602_ov9650.h
@@ -0,0 +1,501 @@
+/*
+ * Driver for the ov9650 sensor
+ *
+ * Copyright (C) 2008 Erik Andren
+ * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * 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, version 2.
+ *
+ */
+
+#ifndef M5602_OV9650_H_
+#define M5602_OV9650_H_
+
+#include <linux/dmi.h>
+
+#include "m5602_sensor.h"
+
+/*****************************************************************************/
+
+#define OV9650_GAIN 0x00
+#define OV9650_BLUE 0x01
+#define OV9650_RED 0x02
+#define OV9650_VREF 0x03
+#define OV9650_COM1 0x04
+#define OV9650_BAVE 0x05
+#define OV9650_GEAVE 0x06
+#define OV9650_RSVD7 0x07
+#define OV9650_PID 0x0a
+#define OV9650_VER 0x0b
+#define OV9650_COM3 0x0c
+#define OV9650_COM5 0x0e
+#define OV9650_COM6 0x0f
+#define OV9650_AECH 0x10
+#define OV9650_CLKRC 0x11
+#define OV9650_COM7 0x12
+#define OV9650_COM8 0x13
+#define OV9650_COM9 0x14
+#define OV9650_COM10 0x15
+#define OV9650_RSVD16 0x16
+#define OV9650_HSTART 0x17
+#define OV9650_HSTOP 0x18
+#define OV9650_VSTRT 0x19
+#define OV9650_VSTOP 0x1a
+#define OV9650_PSHFT 0x1b
+#define OV9650_MVFP 0x1e
+#define OV9650_AEW 0x24
+#define OV9650_AEB 0x25
+#define OV9650_VPT 0x26
+#define OV9650_BBIAS 0x27
+#define OV9650_GbBIAS 0x28
+#define OV9650_Gr_COM 0x29
+#define OV9650_RBIAS 0x2c
+#define OV9650_HREF 0x32
+#define OV9650_CHLF 0x33
+#define OV9650_ARBLM 0x34
+#define OV9650_RSVD35 0x35
+#define OV9650_RSVD36 0x36
+#define OV9650_ADC 0x37
+#define OV9650_ACOM38 0x38
+#define OV9650_OFON 0x39
+#define OV9650_TSLB 0x3a
+#define OV9650_COM12 0x3c
+#define OV9650_COM13 0x3d
+#define OV9650_COM15 0x40
+#define OV9650_COM16 0x41
+#define OV9650_LCC1 0x62
+#define OV9650_LCC2 0x63
+#define OV9650_LCC3 0x64
+#define OV9650_LCC4 0x65
+#define OV9650_LCC5 0x66
+#define OV9650_HV 0x69
+#define OV9650_DBLV 0x6b
+#define OV9650_COM21 0x8b
+#define OV9650_COM22 0x8c
+#define OV9650_COM24 0x8e
+#define OV9650_DBLC1 0x8f
+#define OV9650_RSVD94 0x94
+#define OV9650_RSVD95 0x95
+#define OV9650_RSVD96 0x96
+#define OV9650_LCCFB 0x9d
+#define OV9650_LCCFR 0x9e
+#define OV9650_AECHM 0xa1
+#define OV9650_COM26 0xa5
+#define OV9650_ACOMA8 0xa8
+#define OV9650_ACOMA9 0xa9
+
+#define OV9650_REGISTER_RESET (1 << 7)
+#define OV9650_VGA_SELECT (1 << 6)
+#define OV9650_RGB_SELECT (1 << 2)
+#define OV9650_RAW_RGB_SELECT (1 << 0)
+
+#define OV9650_FAST_AGC_AEC (1 << 7)
+#define OV9650_AEC_UNLIM_STEP_SIZE (1 << 6)
+#define OV9650_BANDING (1 << 5)
+#define OV9650_AGC_EN (1 << 2)
+#define OV9650_AWB_EN (1 << 1)
+#define OV9650_AEC_EN (1 << 0)
+
+#define OV9650_VARIOPIXEL (1 << 2)
+#define OV9650_SYSTEM_CLK_SEL (1 << 7)
+#define OV9650_SLAM_MODE (1 << 4)
+
+#define OV9650_VFLIP (1 << 4)
+#define OV9650_HFLIP (1 << 5)
+
+#define GAIN_DEFAULT 0x14
+#define RED_GAIN_DEFAULT 0x70
+#define BLUE_GAIN_DEFAULT 0x20
+#define EXPOSURE_DEFAULT 0x5003
+
+/*****************************************************************************/
+
+/* Kernel module parameters */
+extern int force_sensor;
+extern int dump_sensor;
+extern unsigned int debug;
+
+int ov9650_probe(struct sd *sd);
+int ov9650_init(struct sd *sd);
+int ov9650_power_down(struct sd *sd);
+
+int ov9650_read_sensor(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len);
+int ov9650_write_sensor(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len);
+
+void ov9650_dump_registers(struct sd *sd);
+
+int ov9650_set_exposure(struct gspca_dev *gspca_dev, __s32 val);
+int ov9650_get_exposure(struct gspca_dev *gspca_dev, __s32 *val);
+int ov9650_get_gain(struct gspca_dev *gspca_dev, __s32 *val);
+int ov9650_set_gain(struct gspca_dev *gspca_dev, __s32 val);
+int ov9650_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val);
+int ov9650_set_red_balance(struct gspca_dev *gspca_dev, __s32 val);
+int ov9650_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val);
+int ov9650_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val);
+int ov9650_get_hflip(struct gspca_dev *gspca_dev, __s32 *val);
+int ov9650_set_hflip(struct gspca_dev *gspca_dev, __s32 val);
+int ov9650_get_vflip(struct gspca_dev *gspca_dev, __s32 *val);
+int ov9650_set_vflip(struct gspca_dev *gspca_dev, __s32 val);
+int ov9650_get_brightness(struct gspca_dev *gspca_dev, __s32 *val);
+int ov9650_set_brightness(struct gspca_dev *gspca_dev, __s32 val);
+int ov9650_get_auto_white_balance(struct gspca_dev *gspca_dev, __s32 *val);
+int ov9650_set_auto_white_balance(struct gspca_dev *gspca_dev, __s32 val);
+int ov9650_get_auto_gain(struct gspca_dev *gspca_dev, __s32 *val);
+int ov9650_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val);
+
+static struct m5602_sensor ov9650 = {
+ .name = "OV9650",
+ .i2c_slave_id = 0x60,
+ .probe = ov9650_probe,
+ .init = ov9650_init,
+ .power_down = ov9650_power_down,
+ .read_sensor = ov9650_read_sensor,
+ .write_sensor = ov9650_write_sensor,
+
+ .nctrls = 8,
+ .ctrls = {
+ {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "exposure",
+ .minimum = 0x00,
+ .maximum = 0xffff,
+ .step = 0x1,
+ .default_value = EXPOSURE_DEFAULT,
+ .flags = V4L2_CTRL_FLAG_SLIDER
+ },
+ .set = ov9650_set_exposure,
+ .get = ov9650_get_exposure
+ }, {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "gain",
+ .minimum = 0x00,
+ .maximum = 0x3ff,
+ .step = 0x1,
+ .default_value = GAIN_DEFAULT,
+ .flags = V4L2_CTRL_FLAG_SLIDER
+ },
+ .set = ov9650_set_gain,
+ .get = ov9650_get_gain
+ }, {
+ {
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "red balance",
+ .minimum = 0x00,
+ .maximum = 0xff,
+ .step = 0x1,
+ .default_value = RED_GAIN_DEFAULT,
+ .flags = V4L2_CTRL_FLAG_SLIDER
+ },
+ .set = ov9650_set_red_balance,
+ .get = ov9650_get_red_balance
+ }, {
+ {
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "blue balance",
+ .minimum = 0x00,
+ .maximum = 0xff,
+ .step = 0x1,
+ .default_value = BLUE_GAIN_DEFAULT,
+ .flags = V4L2_CTRL_FLAG_SLIDER
+ },
+ .set = ov9650_set_blue_balance,
+ .get = ov9650_get_blue_balance
+ }, {
+ {
+ .id = V4L2_CID_HFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "horizontal flip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0
+ },
+ .set = ov9650_set_hflip,
+ .get = ov9650_get_hflip
+ }, {
+ {
+ .id = V4L2_CID_VFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "vertical flip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0
+ },
+ .set = ov9650_set_vflip,
+ .get = ov9650_get_vflip
+ }, {
+ {
+ .id = V4L2_CID_AUTO_WHITE_BALANCE,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "auto white balance",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0
+ },
+ .set = ov9650_set_auto_white_balance,
+ .get = ov9650_get_auto_white_balance
+ }, {
+ {
+ .id = V4L2_CID_AUTOGAIN,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "auto gain control",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0
+ },
+ .set = ov9650_set_auto_gain,
+ .get = ov9650_get_auto_gain
+ }
+ },
+
+ .nmodes = 1,
+ .modes = {
+ {
+ M5602_DEFAULT_FRAME_WIDTH,
+ M5602_DEFAULT_FRAME_HEIGHT,
+ V4L2_PIX_FMT_SBGGR8,
+ V4L2_FIELD_NONE,
+ .sizeimage =
+ M5602_DEFAULT_FRAME_WIDTH * M5602_DEFAULT_FRAME_HEIGHT,
+ .bytesperline = M5602_DEFAULT_FRAME_WIDTH,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1
+ }
+ }
+};
+
+static const unsigned char preinit_ov9650[][3] =
+{
+ /* [INITCAM] */
+ {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02},
+ {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+ {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00},
+
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x08},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x05},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x04},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
+ {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
+ {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x00},
+ {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a},
+ /* Reset chip */
+ {SENSOR, OV9650_COM7, OV9650_REGISTER_RESET},
+ /* Enable double clock */
+ {SENSOR, OV9650_CLKRC, 0x80},
+ /* Do something out of spec with the power */
+ {SENSOR, OV9650_OFON, 0x40}
+};
+
+static const unsigned char init_ov9650[][3] =
+{
+ /* [INITCAM] */
+ {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02},
+ {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+ {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00},
+
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x08},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x05},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x04},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
+ {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
+ {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x00},
+ {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a},
+ /* Reset chip */
+ {SENSOR, OV9650_COM7, OV9650_REGISTER_RESET},
+ /* Enable double clock */
+ {SENSOR, OV9650_CLKRC, 0x80},
+ /* Do something out of spec with the power */
+ {SENSOR, OV9650_OFON, 0x40},
+
+ /* Set QQVGA */
+ {SENSOR, OV9650_COM1, 0x20},
+ /* Set fast AGC/AEC algorithm with unlimited step size */
+ {SENSOR, OV9650_COM8, OV9650_FAST_AGC_AEC |
+ OV9650_AEC_UNLIM_STEP_SIZE |
+ OV9650_AWB_EN | OV9650_AGC_EN},
+
+ {SENSOR, OV9650_CHLF, 0x10},
+ {SENSOR, OV9650_ARBLM, 0xbf},
+ {SENSOR, OV9650_ACOM38, 0x81},
+ /* Turn off color matrix coefficient double option */
+ {SENSOR, OV9650_COM16, 0x00},
+ /* Enable color matrix for RGB/YUV, Delay Y channel,
+ set output Y/UV delay to 1 */
+ {SENSOR, OV9650_COM13, 0x19},
+ /* Enable digital BLC, Set output mode to U Y V Y */
+ {SENSOR, OV9650_TSLB, 0x0c},
+ /* Limit the AGC/AEC stable upper region */
+ {SENSOR, OV9650_COM24, 0x00},
+ /* Enable HREF and some out of spec things */
+ {SENSOR, OV9650_COM12, 0x73},
+ /* Set all DBLC offset signs to positive and
+ do some out of spec stuff */
+ {SENSOR, OV9650_DBLC1, 0xdf},
+ {SENSOR, OV9650_COM21, 0x06},
+ {SENSOR, OV9650_RSVD35, 0x91},
+ /* Necessary, no camera stream without it */
+ {SENSOR, OV9650_RSVD16, 0x06},
+ {SENSOR, OV9650_RSVD94, 0x99},
+ {SENSOR, OV9650_RSVD95, 0x99},
+ {SENSOR, OV9650_RSVD96, 0x04},
+ /* Enable full range output */
+ {SENSOR, OV9650_COM15, 0x0},
+ /* Enable HREF at optical black, enable ADBLC bias,
+ enable ADBLC, reset timings at format change */
+ {SENSOR, OV9650_COM6, 0x4b},
+ /* Subtract 32 from the B channel bias */
+ {SENSOR, OV9650_BBIAS, 0xa0},
+ /* Subtract 32 from the Gb channel bias */
+ {SENSOR, OV9650_GbBIAS, 0xa0},
+ /* Do not bypass the analog BLC and to some out of spec stuff */
+ {SENSOR, OV9650_Gr_COM, 0x00},
+ /* Subtract 32 from the R channel bias */
+ {SENSOR, OV9650_RBIAS, 0xa0},
+ /* Subtract 32 from the R channel bias */
+ {SENSOR, OV9650_RBIAS, 0x0},
+ {SENSOR, OV9650_COM26, 0x80},
+ {SENSOR, OV9650_ACOMA9, 0x98},
+ /* Set the AGC/AEC stable region upper limit */
+ {SENSOR, OV9650_AEW, 0x68},
+ /* Set the AGC/AEC stable region lower limit */
+ {SENSOR, OV9650_AEB, 0x5c},
+ /* Set the high and low limit nibbles to 3 */
+ {SENSOR, OV9650_VPT, 0xc3},
+ /* Set the Automatic Gain Ceiling (AGC) to 128x,
+ drop VSYNC at frame drop,
+ limit exposure timing,
+ drop frame when the AEC step is larger than the exposure gap */
+ {SENSOR, OV9650_COM9, 0x6e},
+ /* Set VSYNC negative, Set RESET to SLHS (slave mode horizontal sync)
+ and set PWDN to SLVS (slave mode vertical sync) */
+ {SENSOR, OV9650_COM10, 0x42},
+ /* Set horizontal column start high to default value */
+ {SENSOR, OV9650_HSTART, 0x1a},
+ /* Set horizontal column end */
+ {SENSOR, OV9650_HSTOP, 0xbf},
+ /* Complementing register to the two writes above */
+ {SENSOR, OV9650_HREF, 0xb2},
+ /* Set vertical row start high bits */
+ {SENSOR, OV9650_VSTRT, 0x02},
+ /* Set vertical row end low bits */
+ {SENSOR, OV9650_VSTOP, 0x7e},
+ /* Set complementing vertical frame control */
+ {SENSOR, OV9650_VREF, 0x10},
+ /* Set raw RGB output format with VGA resolution */
+ {SENSOR, OV9650_COM7, OV9650_VGA_SELECT |
+ OV9650_RGB_SELECT |
+ OV9650_RAW_RGB_SELECT},
+ {SENSOR, OV9650_ADC, 0x04},
+ {SENSOR, OV9650_HV, 0x40},
+ /* Enable denoise, and white-pixel erase */
+ {SENSOR, OV9650_COM22, 0x23},
+
+ /* Set the high bits of the exposure value */
+ {SENSOR, OV9650_AECH, ((EXPOSURE_DEFAULT & 0xff00) >> 8)},
+
+ /* Set the low bits of the exposure value */
+ {SENSOR, OV9650_COM1, (EXPOSURE_DEFAULT & 0xff)},
+ {SENSOR, OV9650_GAIN, GAIN_DEFAULT},
+ {SENSOR, OV9650_BLUE, BLUE_GAIN_DEFAULT},
+ {SENSOR, OV9650_RED, RED_GAIN_DEFAULT},
+
+ {SENSOR, OV9650_COM3, OV9650_VARIOPIXEL},
+ {SENSOR, OV9650_COM5, OV9650_SYSTEM_CLK_SEL},
+
+ {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x82},
+ {BRIDGE, M5602_XB_LINE_OF_FRAME_L, 0x00},
+ {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82},
+ {BRIDGE, M5602_XB_PIX_OF_LINE_L, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x01},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x09},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x01},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0xe0},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x5e},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x02},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0xde}
+};
+
+static const unsigned char power_down_ov9650[][3] =
+{
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+ {SENSOR, OV9650_COM7, 0x80},
+ {SENSOR, OV9650_OFON, 0xf4},
+ {SENSOR, OV9650_MVFP, 0x80},
+ {SENSOR, OV9650_DBLV, 0x3f},
+ {SENSOR, OV9650_RSVD36, 0x49},
+ {SENSOR, OV9650_COM7, 0x05},
+
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x05},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x04},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0x06},
+ {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}
+};
+
+/* Vertically and horizontally flips the image if matched, needed for machines
+ where the sensor is mounted upside down */
+static const struct dmi_system_id ov9650_flip_dmi_table[] = {
+ {
+ .ident = "ASUS A6VC",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "A6VC")
+ }
+ },
+ {
+ .ident = "ASUS A6VM",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "A6VM")
+ }
+ },
+ {
+ .ident = "ASUS A6JC",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "A6JC")
+ }
+ },
+ {
+ .ident = "ASUS A6Kt",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "A6Kt")
+ }
+ },
+ { }
+};
+
+#endif
diff --git a/linux/drivers/media/video/gspca/m5602/m5602_po1030.c b/linux/drivers/media/video/gspca/m5602/m5602_po1030.c
new file mode 100644
index 000000000..08c015bde
--- /dev/null
+++ b/linux/drivers/media/video/gspca/m5602/m5602_po1030.c
@@ -0,0 +1,336 @@
+/*
+ * Driver for the po1030 sensor
+ *
+ * Copyright (c) 2008 Erik Andren
+ * Copyright (c) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (c) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * 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, version 2.
+ *
+ */
+
+#include "m5602_po1030.h"
+
+int po1030_probe(struct sd *sd)
+{
+ u8 prod_id = 0, ver_id = 0, i;
+
+ if (force_sensor) {
+ if (force_sensor == PO1030_SENSOR) {
+ info("Forcing a %s sensor", po1030.name);
+ goto sensor_found;
+ }
+ /* If we want to force another sensor, don't try to probe this
+ * one */
+ return -ENODEV;
+ }
+
+ info("Probing for a po1030 sensor");
+
+ /* Run the pre-init to actually probe the unit */
+ for (i = 0; i < ARRAY_SIZE(preinit_po1030); i++) {
+ u8 data = preinit_po1030[i][2];
+ if (preinit_po1030[i][0] == SENSOR)
+ po1030_write_sensor(sd,
+ preinit_po1030[i][1], &data, 1);
+ else
+ m5602_write_bridge(sd, preinit_po1030[i][1], data);
+ }
+
+ if (po1030_read_sensor(sd, 0x3, &prod_id, 1))
+ return -ENODEV;
+
+ if (po1030_read_sensor(sd, 0x4, &ver_id, 1))
+ return -ENODEV;
+
+ if ((prod_id == 0x02) && (ver_id == 0xef)) {
+ info("Detected a po1030 sensor");
+ goto sensor_found;
+ }
+ return -ENODEV;
+
+sensor_found:
+ sd->gspca_dev.cam.cam_mode = po1030.modes;
+ sd->gspca_dev.cam.nmodes = po1030.nmodes;
+ sd->desc->ctrls = po1030.ctrls;
+ sd->desc->nctrls = po1030.nctrls;
+ return 0;
+}
+
+int po1030_read_sensor(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len)
+{
+ int err, i;
+
+ do {
+ err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, i2c_data);
+ } while ((*i2c_data & I2C_BUSY) && !err);
+
+ m5602_write_bridge(sd, M5602_XB_I2C_DEV_ADDR,
+ sd->sensor->i2c_slave_id);
+ m5602_write_bridge(sd, M5602_XB_I2C_REG_ADDR, address);
+ m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x10 + len);
+ m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x08);
+
+ for (i = 0; i < len; i++) {
+ err = m5602_read_bridge(sd, M5602_XB_I2C_DATA, &(i2c_data[i]));
+
+ PDEBUG(DBG_TRACE, "Reading sensor register "
+ "0x%x containing 0x%x ", address, *i2c_data);
+ }
+ return (err < 0) ? err : 0;
+}
+
+int po1030_write_sensor(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len)
+{
+ int err, i;
+ u8 *p;
+ struct usb_device *udev = sd->gspca_dev.dev;
+ __u8 *buf = sd->gspca_dev.usb_buf;
+
+ /* The po1030 only supports one byte writes */
+ if (len > 1 || !len)
+ return -EINVAL;
+
+ memcpy(buf, sensor_urb_skeleton, sizeof(sensor_urb_skeleton));
+
+ buf[11] = sd->sensor->i2c_slave_id;
+ buf[15] = address;
+
+ p = buf + 16;
+
+ /* Copy a four byte write sequence for each byte to be written to */
+ for (i = 0; i < len; i++) {
+ memcpy(p, sensor_urb_skeleton + 16, 4);
+ p[3] = i2c_data[i];
+ p += 4;
+ PDEBUG(DBG_TRACE, "Writing sensor register 0x%x with 0x%x",
+ address, i2c_data[i]);
+ }
+
+ /* Copy the footer */
+ memcpy(p, sensor_urb_skeleton + 20, 4);
+
+ /* Set the total length */
+ p[3] = 0x10 + len;
+
+ err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ 0x04, 0x40, 0x19,
+ 0x0000, buf,
+ 20 + len * 4, M5602_URB_MSG_TIMEOUT);
+
+ return (err < 0) ? err : 0;
+}
+
+int po1030_init(struct sd *sd)
+{
+ int i, err = 0;
+
+ /* Init the sensor */
+ for (i = 0; i < ARRAY_SIZE(init_po1030); i++) {
+ u8 data[2] = {0x00, 0x00};
+
+ switch (init_po1030[i][0]) {
+ case BRIDGE:
+ err = m5602_write_bridge(sd,
+ init_po1030[i][1],
+ init_po1030[i][2]);
+ break;
+
+ case SENSOR:
+ data[0] = init_po1030[i][2];
+ err = po1030_write_sensor(sd,
+ init_po1030[i][1], data, 1);
+ break;
+
+ case SENSOR_LONG:
+ data[0] = init_po1030[i][2];
+ data[1] = init_po1030[i][3];
+ err = po1030_write_sensor(sd,
+ init_po1030[i][1], data, 2);
+ break;
+ default:
+ info("Invalid stream command, exiting init");
+ return -EINVAL;
+ }
+ }
+
+ if (dump_sensor)
+ po1030_dump_registers(sd);
+
+ return (err < 0) ? err : 0;
+}
+
+int po1030_get_exposure(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 i2c_data;
+ int err;
+
+ err = po1030_read_sensor(sd, PO1030_REG_INTEGLINES_H,
+ &i2c_data, 1);
+ if (err < 0)
+ goto out;
+ *val = (i2c_data << 8);
+
+ err = po1030_read_sensor(sd, PO1030_REG_INTEGLINES_M,
+ &i2c_data, 1);
+ *val |= i2c_data;
+
+ PDEBUG(DBG_V4L2_CID, "Exposure read as %d", *val);
+out:
+ return (err < 0) ? err : 0;
+}
+
+int po1030_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 i2c_data;
+ int err;
+
+ PDEBUG(DBG_V4L2, "Set exposure to %d", val & 0xffff);
+
+ i2c_data = ((val & 0xff00) >> 8);
+ PDEBUG(DBG_V4L2, "Set exposure to high byte to 0x%x",
+ i2c_data);
+
+ err = po1030_write_sensor(sd, PO1030_REG_INTEGLINES_H,
+ &i2c_data, 1);
+ if (err < 0)
+ goto out;
+
+ i2c_data = (val & 0xff);
+ PDEBUG(DBG_V4L2, "Set exposure to low byte to 0x%x",
+ i2c_data);
+ err = po1030_write_sensor(sd, PO1030_REG_INTEGLINES_M,
+ &i2c_data, 1);
+
+out:
+ return (err < 0) ? err : 0;
+}
+
+int po1030_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 i2c_data;
+ int err;
+
+ err = po1030_read_sensor(sd, PO1030_REG_GLOBALGAIN,
+ &i2c_data, 1);
+ *val = i2c_data;
+ PDEBUG(DBG_V4L2_CID, "Read global gain %d", *val);
+
+ return (err < 0) ? err : 0;
+}
+
+int po1030_set_gain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 i2c_data;
+ int err;
+
+ i2c_data = val & 0xff;
+ PDEBUG(DBG_V4L2, "Set global gain to %d", i2c_data);
+ err = po1030_write_sensor(sd, PO1030_REG_GLOBALGAIN,
+ &i2c_data, 1);
+ return (err < 0) ? err : 0;
+}
+
+int po1030_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 i2c_data;
+ int err;
+
+ err = po1030_read_sensor(sd, PO1030_REG_RED_GAIN,
+ &i2c_data, 1);
+ *val = i2c_data;
+ PDEBUG(DBG_V4L2_CID, "Read red gain %d", *val);
+ return (err < 0) ? err : 0;
+}
+
+int po1030_set_red_balance(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 i2c_data;
+ int err;
+
+ i2c_data = val & 0xff;
+ PDEBUG(DBG_V4L2, "Set red gain to %d", i2c_data);
+ err = po1030_write_sensor(sd, PO1030_REG_RED_GAIN,
+ &i2c_data, 1);
+ return (err < 0) ? err : 0;
+}
+
+int po1030_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 i2c_data;
+ int err;
+
+ err = po1030_read_sensor(sd, PO1030_REG_BLUE_GAIN,
+ &i2c_data, 1);
+ *val = i2c_data;
+ PDEBUG(DBG_V4L2_CID, "Read blue gain %d", *val);
+
+ return (err < 0) ? err : 0;
+}
+
+int po1030_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 i2c_data;
+ int err;
+ i2c_data = val & 0xff;
+ PDEBUG(DBG_V4L2, "Set blue gain to %d", i2c_data);
+ err = po1030_write_sensor(sd, PO1030_REG_BLUE_GAIN,
+ &i2c_data, 1);
+
+ return (err < 0) ? err : 0;
+}
+
+int po1030_power_down(struct sd *sd)
+{
+ return 0;
+}
+
+void po1030_dump_registers(struct sd *sd)
+{
+ int address;
+ u8 value = 0;
+
+ info("Dumping the po1030 sensor core registers");
+ for (address = 0; address < 0x7f; address++) {
+ po1030_read_sensor(sd, address, &value, 1);
+ info("register 0x%x contains 0x%x",
+ address, value);
+ }
+
+ info("po1030 register state dump complete");
+
+ info("Probing for which registers that are read/write");
+ for (address = 0; address < 0xff; address++) {
+ u8 old_value, ctrl_value;
+ u8 test_value[2] = {0xff, 0xff};
+
+ po1030_read_sensor(sd, address, &old_value, 1);
+ po1030_write_sensor(sd, address, test_value, 1);
+ po1030_read_sensor(sd, address, &ctrl_value, 1);
+
+ if (ctrl_value == test_value[0])
+ info("register 0x%x is writeable", address);
+ else
+ info("register 0x%x is read only", address);
+
+ /* Restore original value */
+ po1030_write_sensor(sd, address, &old_value, 1);
+ }
+}
diff --git a/linux/drivers/media/video/gspca/m5602/m5602_po1030.h b/linux/drivers/media/video/gspca/m5602/m5602_po1030.h
new file mode 100644
index 000000000..db86a9287
--- /dev/null
+++ b/linux/drivers/media/video/gspca/m5602/m5602_po1030.h
@@ -0,0 +1,478 @@
+/*
+ * Driver for the po1030 sensor.
+ * This is probably a pixel plus sensor but we haven't identified it yet
+ *
+ * Copyright (c) 2008 Erik Andren
+ * Copyright (c) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (c) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * Register defines taken from Pascal Stangs Proxycon Armlib
+ *
+ * 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, version 2.
+ *
+ */
+
+#ifndef M5602_PO1030_H_
+#define M5602_PO1030_H_
+
+#include "m5602_sensor.h"
+
+/*****************************************************************************/
+
+#define PO1030_REG_DEVID_H 0x00
+#define PO1030_REG_DEVID_L 0x01
+#define PO1030_REG_FRAMEWIDTH_H 0x04
+#define PO1030_REG_FRAMEWIDTH_L 0x05
+#define PO1030_REG_FRAMEHEIGHT_H 0x06
+#define PO1030_REG_FRAMEHEIGHT_L 0x07
+#define PO1030_REG_WINDOWX_H 0x08
+#define PO1030_REG_WINDOWX_L 0x09
+#define PO1030_REG_WINDOWY_H 0x0a
+#define PO1030_REG_WINDOWY_L 0x0b
+#define PO1030_REG_WINDOWWIDTH_H 0x0c
+#define PO1030_REG_WINDOWWIDTH_L 0x0d
+#define PO1030_REG_WINDOWHEIGHT_H 0x0e
+#define PO1030_REG_WINDOWHEIGHT_L 0x0f
+
+#define PO1030_REG_GLOBALIBIAS 0x12
+#define PO1030_REG_PIXELIBIAS 0x13
+
+#define PO1030_REG_GLOBALGAIN 0x15
+#define PO1030_REG_RED_GAIN 0x16
+#define PO1030_REG_GREEN_1_GAIN 0x17
+#define PO1030_REG_BLUE_GAIN 0x18
+#define PO1030_REG_GREEN_2_GAIN 0x19
+
+#define PO1030_REG_INTEGLINES_H 0x1a
+#define PO1030_REG_INTEGLINES_M 0x1b
+#define PO1030_REG_INTEGLINES_L 0x1c
+
+#define PO1030_REG_CONTROL1 0x1d
+#define PO1030_REG_CONTROL2 0x1e
+#define PO1030_REG_CONTROL3 0x1f
+#define PO1030_REG_CONTROL4 0x20
+
+#define PO1030_REG_PERIOD50_H 0x23
+#define PO1030_REG_PERIOD50_L 0x24
+#define PO1030_REG_PERIOD60_H 0x25
+#define PO1030_REG_PERIOD60_L 0x26
+#define PO1030_REG_REGCLK167 0x27
+#define PO1030_REG_DELTA50 0x28
+#define PO1030_REG_DELTA60 0x29
+
+#define PO1030_REG_ADCOFFSET 0x2c
+
+/* Gamma Correction Coeffs */
+#define PO1030_REG_GC0 0x2d
+#define PO1030_REG_GC1 0x2e
+#define PO1030_REG_GC2 0x2f
+#define PO1030_REG_GC3 0x30
+#define PO1030_REG_GC4 0x31
+#define PO1030_REG_GC5 0x32
+#define PO1030_REG_GC6 0x33
+#define PO1030_REG_GC7 0x34
+
+/* Color Transform Matrix */
+#define PO1030_REG_CT0 0x35
+#define PO1030_REG_CT1 0x36
+#define PO1030_REG_CT2 0x37
+#define PO1030_REG_CT3 0x38
+#define PO1030_REG_CT4 0x39
+#define PO1030_REG_CT5 0x3a
+#define PO1030_REG_CT6 0x3b
+#define PO1030_REG_CT7 0x3c
+#define PO1030_REG_CT8 0x3d
+
+#define PO1030_REG_AUTOCTRL1 0x3e
+#define PO1030_REG_AUTOCTRL2 0x3f
+
+#define PO1030_REG_YTARGET 0x40
+#define PO1030_REG_GLOBALGAINMIN 0x41
+#define PO1030_REG_GLOBALGAINMAX 0x42
+
+/* Output format control */
+#define PO1030_REG_OUTFORMCTRL1 0x5a
+#define PO1030_REG_OUTFORMCTRL2 0x5b
+#define PO1030_REG_OUTFORMCTRL3 0x5c
+#define PO1030_REG_OUTFORMCTRL4 0x5d
+#define PO1030_REG_OUTFORMCTRL5 0x5e
+
+/* Imaging coefficients */
+#define PO1030_REG_YBRIGHT 0x73
+#define PO1030_REG_YCONTRAST 0x74
+#define PO1030_REG_YSATURATION 0x75
+
+/*****************************************************************************/
+
+#define PO1030_GLOBAL_GAIN_DEFAULT 0x12
+#define PO1030_EXPOSURE_DEFAULT 0xf0ff
+#define PO1030_BLUE_GAIN_DEFAULT 0x40
+#define PO1030_RED_GAIN_DEFAULT 0x40
+
+/*****************************************************************************/
+
+/* Kernel module parameters */
+extern int force_sensor;
+extern int dump_sensor;
+extern unsigned int debug;
+
+int po1030_probe(struct sd *sd);
+int po1030_init(struct sd *sd);
+int po1030_power_down(struct sd *sd);
+
+void po1030_dump_registers(struct sd *sd);
+
+int po1030_read_sensor(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len);
+int po1030_write_sensor(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len);
+
+int po1030_get_exposure(struct gspca_dev *gspca_dev, __s32 *val);
+int po1030_set_exposure(struct gspca_dev *gspca_dev, __s32 val);
+int po1030_get_gain(struct gspca_dev *gspca_dev, __s32 *val);
+int po1030_set_gain(struct gspca_dev *gspca_dev, __s32 val);
+int po1030_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val);
+int po1030_set_red_balance(struct gspca_dev *gspca_dev, __s32 val);
+int po1030_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val);
+int po1030_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val);
+
+static struct m5602_sensor po1030 = {
+ .name = "PO1030",
+
+ .i2c_slave_id = 0xdc,
+
+ .probe = po1030_probe,
+ .init = po1030_init,
+ .power_down = po1030_power_down,
+
+ .nctrls = 4,
+ .ctrls = {
+ {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "gain",
+ .minimum = 0x00,
+ .maximum = 0xff,
+ .step = 0x1,
+ .default_value = PO1030_GLOBAL_GAIN_DEFAULT,
+ .flags = V4L2_CTRL_FLAG_SLIDER
+ },
+ .set = po1030_set_gain,
+ .get = po1030_get_gain
+ }, {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "exposure",
+ .minimum = 0x00,
+ .maximum = 0xffff,
+ .step = 0x1,
+ .default_value = PO1030_EXPOSURE_DEFAULT,
+ .flags = V4L2_CTRL_FLAG_SLIDER
+ },
+ .set = po1030_set_exposure,
+ .get = po1030_get_exposure
+ }, {
+ {
+ .id = V4L2_CID_RED_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "red balance",
+ .minimum = 0x00,
+ .maximum = 0xff,
+ .step = 0x1,
+ .default_value = PO1030_RED_GAIN_DEFAULT,
+ .flags = V4L2_CTRL_FLAG_SLIDER
+ },
+ .set = po1030_set_red_balance,
+ .get = po1030_get_red_balance
+ }, {
+ {
+ .id = V4L2_CID_BLUE_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "blue balance",
+ .minimum = 0x00,
+ .maximum = 0xff,
+ .step = 0x1,
+ .default_value = PO1030_BLUE_GAIN_DEFAULT,
+ .flags = V4L2_CTRL_FLAG_SLIDER
+ },
+ .set = po1030_set_blue_balance,
+ .get = po1030_get_blue_balance
+ }
+ },
+ .nmodes = 1,
+ .modes = {
+ {
+ M5602_DEFAULT_FRAME_WIDTH,
+ M5602_DEFAULT_FRAME_HEIGHT,
+ V4L2_PIX_FMT_SBGGR8,
+ V4L2_FIELD_NONE,
+ .sizeimage =
+ M5602_DEFAULT_FRAME_WIDTH * M5602_DEFAULT_FRAME_HEIGHT,
+ .bytesperline = M5602_DEFAULT_FRAME_WIDTH,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1
+ }
+ }
+};
+
+static const unsigned char preinit_po1030[][3] =
+{
+ {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02},
+ {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d},
+ {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
+
+ {SENSOR, PO1030_REG_AUTOCTRL2, 0x24},
+
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x05},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x04},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
+ {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
+ {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
+ {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81},
+ {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82},
+ {BRIDGE, M5602_XB_SIG_INI, 0x01},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x02},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x01},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0xec},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x02},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x02},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x87},
+ {BRIDGE, M5602_XB_SIG_INI, 0x00},
+
+ {SENSOR, PO1030_REG_AUTOCTRL2, 0x24},
+
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x05},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x04},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
+ {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
+ {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x05},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x00}
+};
+
+static const unsigned char init_po1030[][4] =
+{
+ {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02},
+ {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0},
+ /*sequence 1*/
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d},
+
+ {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
+ /*end of sequence 1*/
+
+ /*sequence 2 (same as stop sequence)*/
+ {SENSOR, PO1030_REG_AUTOCTRL2, 0x24},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x05},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x04},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
+ {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
+ {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02},
+
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+ /*end of sequence 2*/
+
+ /*sequence 5*/
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
+ {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81},
+ {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82},
+ {BRIDGE, M5602_XB_SIG_INI, 0x01},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x02},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x01},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0xec},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x02},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x02},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x87},
+ {BRIDGE, M5602_XB_SIG_INI, 0x00},
+ /*end of sequence 5*/
+
+ /*sequence 2 stop */
+ {SENSOR, PO1030_REG_AUTOCTRL2, 0x24},
+
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x05},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x04},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
+ {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
+ {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+ /*end of sequence 2 stop */
+
+/* ---------------------------------
+ * end of init - begin of start
+ * --------------------------------- */
+
+ /*sequence 3*/
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
+ /*end of sequence 3*/
+ /*sequence 4*/
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x05},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0x00},
+
+ {SENSOR, PO1030_REG_AUTOCTRL2, 0x04},
+
+ /* Set the width to 751 */
+ {SENSOR, PO1030_REG_FRAMEWIDTH_H, 0x02},
+ {SENSOR, PO1030_REG_FRAMEWIDTH_L, 0xef},
+
+ /* Set the height to 540 */
+ {SENSOR, PO1030_REG_FRAMEHEIGHT_H, 0x02},
+ {SENSOR, PO1030_REG_FRAMEHEIGHT_L, 0x1c},
+
+ /* Set the x window to 1 */
+ {SENSOR, PO1030_REG_WINDOWX_H, 0x00},
+ {SENSOR, PO1030_REG_WINDOWX_L, 0x01},
+
+ /* Set the y window to 1 */
+ {SENSOR, PO1030_REG_WINDOWY_H, 0x00},
+ {SENSOR, PO1030_REG_WINDOWX_L, 0x01},
+
+ {SENSOR, PO1030_REG_WINDOWWIDTH_H, 0x02},
+ {SENSOR, PO1030_REG_WINDOWWIDTH_L, 0x87},
+ {SENSOR, PO1030_REG_WINDOWHEIGHT_H, 0x01},
+ {SENSOR, PO1030_REG_WINDOWHEIGHT_L, 0xe3},
+
+ {SENSOR, PO1030_REG_OUTFORMCTRL2, 0x04},
+ {SENSOR, PO1030_REG_OUTFORMCTRL2, 0x04},
+ {SENSOR, PO1030_REG_AUTOCTRL1, 0x08},
+ {SENSOR, PO1030_REG_CONTROL2, 0x03},
+ {SENSOR, 0x21, 0x90},
+ {SENSOR, PO1030_REG_YTARGET, 0x60},
+ {SENSOR, 0x59, 0x13},
+ {SENSOR, PO1030_REG_OUTFORMCTRL1, 0x40},
+ {SENSOR, 0x5f, 0x00},
+ {SENSOR, 0x60, 0x80},
+ {SENSOR, 0x78, 0x14},
+ {SENSOR, 0x6f, 0x01},
+ {SENSOR, PO1030_REG_CONTROL1, 0x18},
+ {SENSOR, PO1030_REG_GLOBALGAINMAX, 0x14},
+ {SENSOR, 0x63, 0x38},
+ {SENSOR, 0x64, 0x38},
+ {SENSOR, PO1030_REG_CONTROL1, 0x58},
+ {SENSOR, PO1030_REG_RED_GAIN, 0x30},
+ {SENSOR, PO1030_REG_GREEN_1_GAIN, 0x30},
+ {SENSOR, PO1030_REG_BLUE_GAIN, 0x30},
+ {SENSOR, PO1030_REG_GREEN_2_GAIN, 0x30},
+ {SENSOR, PO1030_REG_GC0, 0x10},
+ {SENSOR, PO1030_REG_GC1, 0x20},
+ {SENSOR, PO1030_REG_GC2, 0x40},
+ {SENSOR, PO1030_REG_GC3, 0x60},
+ {SENSOR, PO1030_REG_GC4, 0x80},
+ {SENSOR, PO1030_REG_GC5, 0xa0},
+ {SENSOR, PO1030_REG_GC6, 0xc0},
+ {SENSOR, PO1030_REG_GC7, 0xff},
+ /*end of sequence 4*/
+ /*sequence 5*/
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
+ {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81},
+ {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82},
+ {BRIDGE, M5602_XB_SIG_INI, 0x01},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x02},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x01},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0xec},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x02},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x7e},
+ {BRIDGE, M5602_XB_SIG_INI, 0x00},
+ /*end of sequence 5*/
+
+ /*sequence 6*/
+ /* Changing 40 in f0 the image becomes green in bayer mode and red in
+ * rgb mode */
+ {SENSOR, PO1030_REG_RED_GAIN, PO1030_RED_GAIN_DEFAULT},
+ /* in changing 40 in f0 the image becomes green in bayer mode and red in
+ * rgb mode */
+ {SENSOR, PO1030_REG_BLUE_GAIN, PO1030_BLUE_GAIN_DEFAULT},
+
+ /* with a very low lighted environment increase the exposure but
+ * decrease the FPS (Frame Per Second) */
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+
+ /* Controls high exposure more than SENSOR_LOW_EXPOSURE, use only in
+ * low lighted environment (f0 is more than ff ?)*/
+ {SENSOR, PO1030_REG_INTEGLINES_H, ((PO1030_EXPOSURE_DEFAULT >> 2)
+ & 0xff)},
+
+ /* Controls middle exposure, use only in high lighted environment */
+ {SENSOR, PO1030_REG_INTEGLINES_M, PO1030_EXPOSURE_DEFAULT & 0xff},
+
+ /* Controls clarity (not sure) */
+ {SENSOR, PO1030_REG_INTEGLINES_L, 0x00},
+ /* Controls gain (the image is more lighted) */
+ {SENSOR, PO1030_REG_GLOBALGAIN, PO1030_GLOBAL_GAIN_DEFAULT},
+
+ /* Sets the width */
+ {SENSOR, PO1030_REG_FRAMEWIDTH_H, 0x02},
+ {SENSOR, PO1030_REG_FRAMEWIDTH_L, 0xef}
+ /*end of sequence 6*/
+};
+
+#endif
diff --git a/linux/drivers/media/video/gspca/m5602/m5602_s5k4aa.c b/linux/drivers/media/video/gspca/m5602/m5602_s5k4aa.c
new file mode 100644
index 000000000..682025653
--- /dev/null
+++ b/linux/drivers/media/video/gspca/m5602/m5602_s5k4aa.c
@@ -0,0 +1,463 @@
+/*
+ * Driver for the s5k4aa sensor
+ *
+ * Copyright (C) 2008 Erik Andren
+ * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * 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, version 2.
+ *
+ */
+
+#include "m5602_s5k4aa.h"
+
+int s5k4aa_probe(struct sd *sd)
+{
+ u8 prod_id[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+ const u8 expected_prod_id[6] = {0x00, 0x10, 0x00, 0x4b, 0x33, 0x75};
+ int i, err = 0;
+
+ if (force_sensor) {
+ if (force_sensor == S5K4AA_SENSOR) {
+ info("Forcing a %s sensor", s5k4aa.name);
+ goto sensor_found;
+ }
+ /* If we want to force another sensor, don't try to probe this
+ * one */
+ return -ENODEV;
+ }
+
+ info("Probing for a s5k4aa sensor");
+
+ /* Preinit the sensor */
+ for (i = 0; i < ARRAY_SIZE(preinit_s5k4aa) && !err; i++) {
+ u8 data[2] = {0x00, 0x00};
+
+ switch (preinit_s5k4aa[i][0]) {
+ case BRIDGE:
+ err = m5602_write_bridge(sd,
+ preinit_s5k4aa[i][1],
+ preinit_s5k4aa[i][2]);
+ break;
+
+ case SENSOR:
+ data[0] = preinit_s5k4aa[i][2];
+ err = s5k4aa_write_sensor(sd,
+ preinit_s5k4aa[i][1],
+ data, 1);
+ break;
+
+ case SENSOR_LONG:
+ data[0] = preinit_s5k4aa[i][2];
+ data[1] = preinit_s5k4aa[i][3];
+ err = s5k4aa_write_sensor(sd,
+ preinit_s5k4aa[i][1],
+ data, 2);
+ break;
+ default:
+ info("Invalid stream command, exiting init");
+ return -EINVAL;
+ }
+ }
+
+ /* Test some registers, but we don't know their exact meaning yet */
+ if (s5k4aa_read_sensor(sd, 0x00, prod_id, sizeof(prod_id)))
+ return -ENODEV;
+
+ if (memcmp(prod_id, expected_prod_id, sizeof(prod_id)))
+ return -ENODEV;
+ else
+ info("Detected a s5k4aa sensor");
+sensor_found:
+ sd->gspca_dev.cam.cam_mode = s5k4aa.modes;
+ sd->gspca_dev.cam.nmodes = s5k4aa.nmodes;
+ sd->desc->ctrls = s5k4aa.ctrls;
+ sd->desc->nctrls = s5k4aa.nctrls;
+
+ return 0;
+}
+
+int s5k4aa_read_sensor(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len)
+{
+ int err, i;
+
+ do {
+ err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, i2c_data);
+ } while ((*i2c_data & I2C_BUSY) && !err);
+ if (err < 0)
+ goto out;
+
+ err = m5602_write_bridge(sd, M5602_XB_I2C_DEV_ADDR,
+ sd->sensor->i2c_slave_id);
+ if (err < 0)
+ goto out;
+
+ err = m5602_write_bridge(sd, M5602_XB_I2C_REG_ADDR, address);
+ if (err < 0)
+ goto out;
+
+ err = m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x18 + len);
+ if (err < 0)
+ goto out;
+
+ do {
+ err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, i2c_data);
+ } while ((*i2c_data & I2C_BUSY) && !err);
+ if (err < 0)
+ goto out;
+
+ for (i = 0; (i < len) & !err; i++) {
+ err = m5602_read_bridge(sd, M5602_XB_I2C_DATA, &(i2c_data[i]));
+
+ PDEBUG(DBG_TRACE, "Reading sensor register "
+ "0x%x containing 0x%x ", address, *i2c_data);
+ }
+out:
+ return (err < 0) ? err : 0;
+}
+
+int s5k4aa_write_sensor(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len)
+{
+ int err, i;
+ u8 *p;
+ struct usb_device *udev = sd->gspca_dev.dev;
+ __u8 *buf = sd->gspca_dev.usb_buf;
+
+ /* No sensor with a data width larger than 16 bits has yet been seen */
+ if (len > 2 || !len)
+ return -EINVAL;
+
+ memcpy(buf, sensor_urb_skeleton,
+ sizeof(sensor_urb_skeleton));
+
+ buf[11] = sd->sensor->i2c_slave_id;
+ buf[15] = address;
+
+ /* Special case larger sensor writes */
+ p = buf + 16;
+
+ /* Copy a four byte write sequence for each byte to be written to */
+ for (i = 0; i < len; i++) {
+ memcpy(p, sensor_urb_skeleton + 16, 4);
+ p[3] = i2c_data[i];
+ p += 4;
+ PDEBUG(DBG_TRACE, "Writing sensor register 0x%x with 0x%x",
+ address, i2c_data[i]);
+ }
+
+ /* Copy the tailer */
+ memcpy(p, sensor_urb_skeleton + 20, 4);
+
+ /* Set the total length */
+ p[3] = 0x10 + len;
+
+ err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ 0x04, 0x40, 0x19,
+ 0x0000, buf,
+ 20 + len * 4, M5602_URB_MSG_TIMEOUT);
+
+ return (err < 0) ? err : 0;
+}
+
+int s5k4aa_init(struct sd *sd)
+{
+ int i, err = 0;
+
+ for (i = 0; i < ARRAY_SIZE(init_s5k4aa) && !err; i++) {
+ u8 data[2] = {0x00, 0x00};
+
+ switch (init_s5k4aa[i][0]) {
+ case BRIDGE:
+ err = m5602_write_bridge(sd,
+ init_s5k4aa[i][1],
+ init_s5k4aa[i][2]);
+ break;
+
+ case SENSOR:
+ data[0] = init_s5k4aa[i][2];
+ err = s5k4aa_write_sensor(sd,
+ init_s5k4aa[i][1], data, 1);
+ break;
+
+ case SENSOR_LONG:
+ data[0] = init_s5k4aa[i][2];
+ data[1] = init_s5k4aa[i][3];
+ err = s5k4aa_write_sensor(sd,
+ init_s5k4aa[i][1], data, 2);
+ break;
+ default:
+ info("Invalid stream command, exiting init");
+ return -EINVAL;
+ }
+ }
+
+ if (dump_sensor)
+ s5k4aa_dump_registers(sd);
+
+ if (!err && dmi_check_system(s5k4aa_vflip_dmi_table)) {
+ u8 data = 0x02;
+ info("vertical flip quirk active");
+ s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+ s5k4aa_read_sensor(sd, S5K4AA_READ_MODE, &data, 1);
+ data |= S5K4AA_RM_V_FLIP;
+ data &= ~S5K4AA_RM_H_FLIP;
+ s5k4aa_write_sensor(sd, S5K4AA_READ_MODE, &data, 1);
+
+ /* Decrement COLSTART to preserve color order (BGGR) */
+ s5k4aa_read_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
+ data--;
+ s5k4aa_write_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
+
+ /* Increment ROWSTART to preserve color order (BGGR) */
+ s5k4aa_read_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
+ data++;
+ s5k4aa_write_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
+ }
+
+ return (err < 0) ? err : 0;
+}
+
+int s5k4aa_power_down(struct sd *sd)
+{
+ return 0;
+}
+
+int s5k4aa_get_exposure(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 data = S5K4AA_PAGE_MAP_2;
+ int err;
+
+ err = s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+ if (err < 0)
+ goto out;
+
+ err = s5k4aa_read_sensor(sd, S5K4AA_EXPOSURE_HI, &data, 1);
+ if (err < 0)
+ goto out;
+
+ *val = data << 8;
+ err = s5k4aa_read_sensor(sd, S5K4AA_EXPOSURE_LO, &data, 1);
+ *val |= data;
+ PDEBUG(DBG_V4L2_CID, "Read exposure %d", *val);
+out:
+ return (err < 0) ? err : 0;
+}
+
+int s5k4aa_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 data = S5K4AA_PAGE_MAP_2;
+ int err;
+
+ PDEBUG(DBG_V4L2_CID, "Set exposure to %d", val);
+ err = s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+ if (err < 0)
+ goto out;
+ data = (val >> 8) & 0xff;
+ err = s5k4aa_write_sensor(sd, S5K4AA_EXPOSURE_HI, &data, 1);
+ if (err < 0)
+ goto out;
+ data = val & 0xff;
+ err = s5k4aa_write_sensor(sd, S5K4AA_EXPOSURE_LO, &data, 1);
+out:
+ return (err < 0) ? err : 0;
+}
+
+int s5k4aa_get_vflip(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 data = S5K4AA_PAGE_MAP_2;
+ int err;
+
+ err = s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+ if (err < 0)
+ goto out;
+
+ err = s5k4aa_read_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+ *val = (data & S5K4AA_RM_V_FLIP) >> 7;
+ PDEBUG(DBG_V4L2_CID, "Read vertical flip %d", *val);
+
+out:
+ return (err < 0) ? err : 0;
+}
+
+int s5k4aa_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 data = S5K4AA_PAGE_MAP_2;
+ int err;
+
+ PDEBUG(DBG_V4L2_CID, "Set vertical flip to %d", val);
+ err = s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+ if (err < 0)
+ goto out;
+ err = s5k4aa_write_sensor(sd, S5K4AA_READ_MODE, &data, 1);
+ if (err < 0)
+ goto out;
+ data = ((data & ~S5K4AA_RM_V_FLIP)
+ | ((val & 0x01) << 7));
+ err = s5k4aa_write_sensor(sd, S5K4AA_READ_MODE, &data, 1);
+ if (err < 0)
+ goto out;
+
+ if (val) {
+ err = s5k4aa_read_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
+ if (err < 0)
+ goto out;
+
+ data++;
+ err = s5k4aa_write_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
+ } else {
+ err = s5k4aa_read_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
+ if (err < 0)
+ goto out;
+
+ data--;
+ err = s5k4aa_write_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
+ }
+out:
+ return (err < 0) ? err : 0;
+}
+
+int s5k4aa_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 data = S5K4AA_PAGE_MAP_2;
+ int err;
+
+ err = s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+ if (err < 0)
+ goto out;
+
+ err = s5k4aa_read_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+ *val = (data & S5K4AA_RM_H_FLIP) >> 6;
+ PDEBUG(DBG_V4L2_CID, "Read horizontal flip %d", *val);
+out:
+ return (err < 0) ? err : 0;
+}
+
+int s5k4aa_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 data = S5K4AA_PAGE_MAP_2;
+ int err;
+
+ PDEBUG(DBG_V4L2_CID, "Set horizontal flip to %d",
+ val);
+ err = s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+ if (err < 0)
+ goto out;
+ err = s5k4aa_write_sensor(sd, S5K4AA_READ_MODE, &data, 1);
+ if (err < 0)
+ goto out;
+
+ data = ((data & ~S5K4AA_RM_H_FLIP) | ((val & 0x01) << 6));
+ err = s5k4aa_write_sensor(sd, S5K4AA_READ_MODE, &data, 1);
+ if (err < 0)
+ goto out;
+
+ if (val) {
+ err = s5k4aa_read_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
+ if (err < 0)
+ goto out;
+ data++;
+ err = s5k4aa_write_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
+ if (err < 0)
+ goto out;
+ } else {
+ err = s5k4aa_read_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
+ if (err < 0)
+ goto out;
+ data--;
+ err = s5k4aa_write_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
+ }
+out:
+ return (err < 0) ? err : 0;
+}
+
+int s5k4aa_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 data = S5K4AA_PAGE_MAP_2;
+ int err;
+
+ err = s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+ if (err < 0)
+ goto out;
+
+ err = s5k4aa_read_sensor(sd, S5K4AA_GAIN_2, &data, 1);
+ *val = data;
+ PDEBUG(DBG_V4L2_CID, "Read gain %d", *val);
+
+out:
+ return (err < 0) ? err : 0;
+}
+
+int s5k4aa_set_gain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 data = S5K4AA_PAGE_MAP_2;
+ int err;
+
+ PDEBUG(DBG_V4L2_CID, "Set gain to %d", val);
+ err = s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+ if (err < 0)
+ goto out;
+
+ data = val & 0xff;
+ err = s5k4aa_write_sensor(sd, S5K4AA_GAIN_2, &data, 1);
+
+out:
+ return (err < 0) ? err : 0;
+}
+
+void s5k4aa_dump_registers(struct sd *sd)
+{
+ int address;
+ u8 page, old_page;
+ s5k4aa_read_sensor(sd, S5K4AA_PAGE_MAP, &old_page, 1);
+ for (page = 0; page < 16; page++) {
+ s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &page, 1);
+ info("Dumping the s5k4aa register state for page 0x%x", page);
+ for (address = 0; address <= 0xff; address++) {
+ u8 value = 0;
+ s5k4aa_read_sensor(sd, address, &value, 1);
+ info("register 0x%x contains 0x%x",
+ address, value);
+ }
+ }
+ info("s5k4aa register state dump complete");
+
+ for (page = 0; page < 16; page++) {
+ s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &page, 1);
+ info("Probing for which registers that are "
+ "read/write for page 0x%x", page);
+ for (address = 0; address <= 0xff; address++) {
+ u8 old_value, ctrl_value, test_value = 0xff;
+
+ s5k4aa_read_sensor(sd, address, &old_value, 1);
+ s5k4aa_write_sensor(sd, address, &test_value, 1);
+ s5k4aa_read_sensor(sd, address, &ctrl_value, 1);
+
+ if (ctrl_value == test_value)
+ info("register 0x%x is writeable", address);
+ else
+ info("register 0x%x is read only", address);
+
+ /* Restore original value */
+ s5k4aa_write_sensor(sd, address, &old_value, 1);
+ }
+ }
+ info("Read/write register probing complete");
+ s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &old_page, 1);
+}
diff --git a/linux/drivers/media/video/gspca/m5602/m5602_s5k4aa.h b/linux/drivers/media/video/gspca/m5602/m5602_s5k4aa.h
new file mode 100644
index 000000000..5d808a750
--- /dev/null
+++ b/linux/drivers/media/video/gspca/m5602/m5602_s5k4aa.h
@@ -0,0 +1,368 @@
+/*
+ * Driver for the s5k4aa sensor
+ *
+ * Copyright (C) 2008 Erik Andren
+ * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * 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, version 2.
+ *
+ */
+
+#ifndef M5602_S5K4AA_H_
+#define M5602_S5K4AA_H_
+
+#include <linux/dmi.h>
+
+#include "m5602_sensor.h"
+
+/*****************************************************************************/
+
+#define S5K4AA_PAGE_MAP 0xec
+
+#define S5K4AA_PAGE_MAP_0 0x00
+#define S5K4AA_PAGE_MAP_1 0x01
+#define S5K4AA_PAGE_MAP_2 0x02
+
+/* Sensor register definitions for page 0x02 */
+#define S5K4AA_READ_MODE 0x03
+#define S5K4AA_ROWSTART_HI 0x04
+#define S5K4AA_ROWSTART_LO 0x05
+#define S5K4AA_COLSTART_HI 0x06
+#define S5K4AA_COLSTART_LO 0x07
+#define S5K4AA_WINDOW_HEIGHT_HI 0x08
+#define S5K4AA_WINDOW_HEIGHT_LO 0x09
+#define S5K4AA_WINDOW_WIDTH_HI 0x0a
+#define S5K4AA_WINDOW_WIDTH_LO 0x0b
+#define S5K4AA_GLOBAL_GAIN__ 0x0f /* Only a guess ATM !!! */
+#define S5K4AA_H_BLANK_HI__ 0x1d /* Only a guess ATM !!! sync lost
+ if too low, reduces frame rate
+ if too high */
+#define S5K4AA_H_BLANK_LO__ 0x1e /* Only a guess ATM !!! */
+#define S5K4AA_EXPOSURE_HI 0x17
+#define S5K4AA_EXPOSURE_LO 0x18
+#define S5K4AA_GAIN_1 0x1f /* (digital?) gain : 5 bits */
+#define S5K4AA_GAIN_2 0x20 /* (analogue?) gain : 7 bits */
+
+#define S5K4AA_RM_ROW_SKIP_4X 0x08
+#define S5K4AA_RM_ROW_SKIP_2X 0x04
+#define S5K4AA_RM_COL_SKIP_4X 0x02
+#define S5K4AA_RM_COL_SKIP_2X 0x01
+#define S5K4AA_RM_H_FLIP 0x40
+#define S5K4AA_RM_V_FLIP 0x80
+
+/*****************************************************************************/
+
+/* Kernel module parameters */
+extern int force_sensor;
+extern int dump_sensor;
+extern unsigned int debug;
+
+int s5k4aa_probe(struct sd *sd);
+int s5k4aa_init(struct sd *sd);
+int s5k4aa_power_down(struct sd *sd);
+
+void s5k4aa_dump_registers(struct sd *sd);
+
+int s5k4aa_read_sensor(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len);
+int s5k4aa_write_sensor(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len);
+
+int s5k4aa_get_exposure(struct gspca_dev *gspca_dev, __s32 *val);
+int s5k4aa_set_exposure(struct gspca_dev *gspca_dev, __s32 val);
+int s5k4aa_get_vflip(struct gspca_dev *gspca_dev, __s32 *val);
+int s5k4aa_set_vflip(struct gspca_dev *gspca_dev, __s32 val);
+int s5k4aa_get_hflip(struct gspca_dev *gspca_dev, __s32 *val);
+int s5k4aa_set_hflip(struct gspca_dev *gspca_dev, __s32 val);
+int s5k4aa_get_gain(struct gspca_dev *gspca_dev, __s32 *val);
+int s5k4aa_set_gain(struct gspca_dev *gspca_dev, __s32 val);
+
+static struct m5602_sensor s5k4aa = {
+ .name = "S5K4AA",
+ .probe = s5k4aa_probe,
+ .init = s5k4aa_init,
+ .power_down = s5k4aa_power_down,
+ .read_sensor = s5k4aa_read_sensor,
+ .write_sensor = s5k4aa_write_sensor,
+ .i2c_slave_id = 0x5a,
+ .nctrls = 4,
+ .ctrls = {
+ {
+ {
+ .id = V4L2_CID_VFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "vertical flip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0
+ },
+ .set = s5k4aa_set_vflip,
+ .get = s5k4aa_get_vflip
+
+ }, {
+ {
+ .id = V4L2_CID_HFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "horizontal flip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0
+ },
+ .set = s5k4aa_set_hflip,
+ .get = s5k4aa_get_hflip
+
+ }, {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain",
+ .minimum = 0,
+ .maximum = 127,
+ .step = 1,
+ .default_value = 0xa0,
+ .flags = V4L2_CTRL_FLAG_SLIDER
+ },
+ .set = s5k4aa_set_gain,
+ .get = s5k4aa_get_gain
+ }, {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Exposure",
+ .minimum = 13,
+ .maximum = 0xfff,
+ .step = 1,
+ .default_value = 0x100,
+ .flags = V4L2_CTRL_FLAG_SLIDER
+ },
+ .set = s5k4aa_set_exposure,
+ .get = s5k4aa_get_exposure
+ }
+ },
+
+ .nmodes = 1,
+ .modes = {
+ {
+ M5602_DEFAULT_FRAME_WIDTH,
+ M5602_DEFAULT_FRAME_HEIGHT,
+ V4L2_PIX_FMT_SBGGR8,
+ V4L2_FIELD_NONE,
+ .sizeimage =
+ M5602_DEFAULT_FRAME_WIDTH * M5602_DEFAULT_FRAME_HEIGHT,
+ .bytesperline = M5602_DEFAULT_FRAME_WIDTH,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1
+ }
+ }
+};
+
+static const unsigned char preinit_s5k4aa[][4] =
+{
+ {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
+ {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00, 0x00},
+
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0x80, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x3f, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3f, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x00, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x08, 0x00},
+
+ {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
+ {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x14, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xf0, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x1c, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
+ {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x20, 0x00},
+
+ {SENSOR, S5K4AA_PAGE_MAP, 0x00, 0x00}
+};
+
+static const unsigned char init_s5k4aa[][4] =
+{
+ {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
+ {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00, 0x00},
+
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0x80, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x3f, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3f, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x00, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x08, 0x00},
+
+ {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
+ {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x14, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xf0, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x1c, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
+ {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x20, 0x00},
+
+ {SENSOR, S5K4AA_PAGE_MAP, 0x07, 0x00},
+ {SENSOR, 0x36, 0x01, 0x00},
+ {SENSOR, S5K4AA_PAGE_MAP, 0x00, 0x00},
+ {SENSOR, 0x7b, 0xff, 0x00},
+ {SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00},
+ {SENSOR, 0x0c, 0x05, 0x00},
+ {SENSOR, 0x02, 0x0e, 0x00},
+ {SENSOR, S5K4AA_GAIN_1, 0x0f, 0x00},
+ {SENSOR, S5K4AA_GAIN_2, 0x00, 0x00},
+ {SENSOR, S5K4AA_GLOBAL_GAIN__, 0x01, 0x00},
+ {SENSOR, 0x11, 0x00, 0x00},
+ {SENSOR, 0x12, 0x00, 0x00},
+ {SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00},
+ {SENSOR, S5K4AA_READ_MODE, 0xa0, 0x00},
+ {SENSOR, 0x37, 0x00, 0x00},
+ {SENSOR, S5K4AA_ROWSTART_HI, 0x00, 0x00},
+ {SENSOR, S5K4AA_ROWSTART_LO, 0x2a, 0x00},
+ {SENSOR, S5K4AA_COLSTART_HI, 0x00, 0x00},
+ {SENSOR, S5K4AA_COLSTART_LO, 0x0b, 0x00},
+ {SENSOR, S5K4AA_WINDOW_HEIGHT_HI, 0x03, 0x00},
+ {SENSOR, S5K4AA_WINDOW_HEIGHT_LO, 0xc4, 0x00},
+ {SENSOR, S5K4AA_WINDOW_WIDTH_HI, 0x05, 0x00},
+ {SENSOR, S5K4AA_WINDOW_WIDTH_LO, 0x08, 0x00},
+ {SENSOR, S5K4AA_H_BLANK_HI__, 0x00, 0x00},
+ {SENSOR, S5K4AA_H_BLANK_LO__, 0x48, 0x00},
+ {SENSOR, S5K4AA_EXPOSURE_HI, 0x00, 0x00},
+ {SENSOR, S5K4AA_EXPOSURE_LO, 0x43, 0x00},
+ {SENSOR, 0x11, 0x04, 0x00},
+ {SENSOR, 0x12, 0xc3, 0x00},
+ {SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00},
+
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x08, 0x00},
+ {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00},
+ {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ /* VSYNC_PARA, VSYNC_PARA : img height 480 = 0x01e0 */
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x01, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0xe0, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+ /* HSYNC_PARA, HSYNC_PARA : img width 640 = 0x0280 */
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x02, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x80, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xa0, 0x00}, /* 48 MHz */
+
+ {SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00},
+ {SENSOR, S5K4AA_READ_MODE, S5K4AA_RM_H_FLIP | S5K4AA_RM_ROW_SKIP_2X
+ | S5K4AA_RM_COL_SKIP_2X, 0x00},
+ /* 0x37 : Fix image stability when light is too bright and improves
+ * image quality in 640x480, but worsens it in 1280x1024 */
+ {SENSOR, 0x37, 0x01, 0x00},
+ /* ROWSTART_HI, ROWSTART_LO : 10 + (1024-960)/2 = 42 = 0x002a */
+ {SENSOR, S5K4AA_ROWSTART_HI, 0x00, 0x00},
+ {SENSOR, S5K4AA_ROWSTART_LO, 0x2a, 0x00},
+ {SENSOR, S5K4AA_COLSTART_HI, 0x00, 0x00},
+ {SENSOR, S5K4AA_COLSTART_LO, 0x0c, 0x00},
+ /* window_height_hi, window_height_lo : 960 = 0x03c0 */
+ {SENSOR, S5K4AA_WINDOW_HEIGHT_HI, 0x03, 0x00},
+ {SENSOR, S5K4AA_WINDOW_HEIGHT_LO, 0xc0, 0x00},
+ /* window_width_hi, window_width_lo : 1280 = 0x0500 */
+ {SENSOR, S5K4AA_WINDOW_WIDTH_HI, 0x05, 0x00},
+ {SENSOR, S5K4AA_WINDOW_WIDTH_LO, 0x00, 0x00},
+ {SENSOR, S5K4AA_H_BLANK_HI__, 0x00, 0x00},
+ {SENSOR, S5K4AA_H_BLANK_LO__, 0xa8, 0x00}, /* helps to sync... */
+ {SENSOR, S5K4AA_EXPOSURE_HI, 0x01, 0x00},
+ {SENSOR, S5K4AA_EXPOSURE_LO, 0x00, 0x00},
+ {SENSOR, 0x11, 0x04, 0x00},
+ {SENSOR, 0x12, 0xc3, 0x00},
+ {SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00},
+ {SENSOR, 0x02, 0x0e, 0x00},
+ {SENSOR_LONG, S5K4AA_GLOBAL_GAIN__, 0x0f, 0x00},
+ {SENSOR, S5K4AA_GAIN_1, 0x0b, 0x00},
+ {SENSOR, S5K4AA_GAIN_2, 0xa0, 0x00}
+};
+
+static const struct dmi_system_id s5k4aa_vflip_dmi_table[] = {
+ {
+ .ident = "Fujitsu-Siemens Amilo Xa 2528",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xa 2528")
+ }
+ },
+ {
+ .ident = "Fujitsu-Siemens Amilo Xi 2550",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xi 2550")
+ }
+ },
+ {
+ .ident = "MSI GX700",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "GX700"),
+ DMI_MATCH(DMI_BIOS_DATE, "07/26/2007")
+ }
+ },
+ { }
+};
+
+#endif
diff --git a/linux/drivers/media/video/gspca/m5602/m5602_s5k83a.c b/linux/drivers/media/video/gspca/m5602/m5602_s5k83a.c
new file mode 100644
index 000000000..c1ff967b1
--- /dev/null
+++ b/linux/drivers/media/video/gspca/m5602/m5602_s5k83a.c
@@ -0,0 +1,333 @@
+/*
+ * Driver for the s5k83a sensor
+ *
+ * Copyright (C) 2008 Erik Andren
+ * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * 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, version 2.
+ *
+ */
+
+#include "m5602_s5k83a.h"
+
+int s5k83a_probe(struct sd *sd)
+{
+ u8 prod_id = 0, ver_id = 0;
+ int i, err = 0;
+
+ if (force_sensor) {
+ if (force_sensor == S5K83A_SENSOR) {
+ info("Forcing a %s sensor", s5k83a.name);
+ goto sensor_found;
+ }
+ /* If we want to force another sensor, don't try to probe this
+ * one */
+ return -ENODEV;
+ }
+
+ info("Probing for a s5k83a sensor");
+
+ /* Preinit the sensor */
+ for (i = 0; i < ARRAY_SIZE(preinit_s5k83a) && !err; i++) {
+ u8 data[2] = {preinit_s5k83a[i][2], preinit_s5k83a[i][3]};
+ if (preinit_s5k83a[i][0] == SENSOR)
+ err = s5k83a_write_sensor(sd, preinit_s5k83a[i][1],
+ data, 2);
+ else
+ err = m5602_write_bridge(sd, preinit_s5k83a[i][1],
+ data[0]);
+ }
+
+ /* We don't know what register (if any) that contain the product id
+ * Just pick the first addresses that seem to produce the same results
+ * on multiple machines */
+ if (s5k83a_read_sensor(sd, 0x00, &prod_id, 1))
+ return -ENODEV;
+
+ if (s5k83a_read_sensor(sd, 0x01, &ver_id, 1))
+ return -ENODEV;
+
+ if ((prod_id == 0xff) || (ver_id == 0xff))
+ return -ENODEV;
+ else
+ info("Detected a s5k83a sensor");
+
+sensor_found:
+ sd->gspca_dev.cam.cam_mode = s5k83a.modes;
+ sd->gspca_dev.cam.nmodes = s5k83a.nmodes;
+ sd->desc->ctrls = s5k83a.ctrls;
+ sd->desc->nctrls = s5k83a.nctrls;
+ return 0;
+}
+
+int s5k83a_read_sensor(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len)
+{
+ int err, i;
+
+ do {
+ err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, i2c_data);
+ } while ((*i2c_data & I2C_BUSY) && !err);
+ if (err < 0)
+ goto out;
+
+ err = m5602_write_bridge(sd, M5602_XB_I2C_DEV_ADDR,
+ sd->sensor->i2c_slave_id);
+ if (err < 0)
+ goto out;
+
+ err = m5602_write_bridge(sd, M5602_XB_I2C_REG_ADDR, address);
+ if (err < 0)
+ goto out;
+
+ err = m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x18 + len);
+ if (err < 0)
+ goto out;
+
+ do {
+ err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, i2c_data);
+ } while ((*i2c_data & I2C_BUSY) && !err);
+
+ if (err < 0)
+ goto out;
+ for (i = 0; i < len && !len; i++) {
+ err = m5602_read_bridge(sd, M5602_XB_I2C_DATA, &(i2c_data[i]));
+
+ PDEBUG(DBG_TRACE, "Reading sensor register "
+ "0x%x containing 0x%x ", address, *i2c_data);
+ }
+
+out:
+ return (err < 0) ? err : 0;
+}
+
+int s5k83a_write_sensor(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len)
+{
+ int err, i;
+ u8 *p;
+ struct usb_device *udev = sd->gspca_dev.dev;
+ __u8 *buf = sd->gspca_dev.usb_buf;
+
+ /* No sensor with a data width larger than 16 bits has yet been seen */
+ if (len > 2 || !len)
+ return -EINVAL;
+
+ memcpy(buf, sensor_urb_skeleton,
+ sizeof(sensor_urb_skeleton));
+
+ buf[11] = sd->sensor->i2c_slave_id;
+ buf[15] = address;
+
+ /* Special case larger sensor writes */
+ p = buf + 16;
+
+ /* Copy a four byte write sequence for each byte to be written to */
+ for (i = 0; i < len; i++) {
+ memcpy(p, sensor_urb_skeleton + 16, 4);
+ p[3] = i2c_data[i];
+ p += 4;
+ PDEBUG(DBG_TRACE, "Writing sensor register 0x%x with 0x%x",
+ address, i2c_data[i]);
+ }
+
+ /* Copy the tailer */
+ memcpy(p, sensor_urb_skeleton + 20, 4);
+
+ /* Set the total length */
+ p[3] = 0x10 + len;
+
+ err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ 0x04, 0x40, 0x19,
+ 0x0000, buf,
+ 20 + len * 4, M5602_URB_MSG_TIMEOUT);
+
+ return (err < 0) ? err : 0;
+}
+
+int s5k83a_init(struct sd *sd)
+{
+ int i, err = 0;
+
+ for (i = 0; i < ARRAY_SIZE(init_s5k83a) && !err; i++) {
+ u8 data[2] = {0x00, 0x00};
+
+ switch (init_s5k83a[i][0]) {
+ case BRIDGE:
+ err = m5602_write_bridge(sd,
+ init_s5k83a[i][1],
+ init_s5k83a[i][2]);
+ break;
+
+ case SENSOR:
+ data[0] = init_s5k83a[i][2];
+ err = s5k83a_write_sensor(sd,
+ init_s5k83a[i][1], data, 1);
+ break;
+
+ case SENSOR_LONG:
+ data[0] = init_s5k83a[i][2];
+ data[1] = init_s5k83a[i][3];
+ err = s5k83a_write_sensor(sd,
+ init_s5k83a[i][1], data, 2);
+ break;
+ default:
+ info("Invalid stream command, exiting init");
+ return -EINVAL;
+ }
+ }
+
+ if (dump_sensor)
+ s5k83a_dump_registers(sd);
+
+ return (err < 0) ? err : 0;
+}
+
+int s5k83a_power_down(struct sd *sd)
+{
+ return 0;
+}
+
+void s5k83a_dump_registers(struct sd *sd)
+{
+ int address;
+ u8 page, old_page;
+ s5k83a_read_sensor(sd, S5K83A_PAGE_MAP, &old_page, 1);
+
+ for (page = 0; page < 16; page++) {
+ s5k83a_write_sensor(sd, S5K83A_PAGE_MAP, &page, 1);
+ info("Dumping the s5k83a register state for page 0x%x", page);
+ for (address = 0; address <= 0xff; address++) {
+ u8 val = 0;
+ s5k83a_read_sensor(sd, address, &val, 1);
+ info("register 0x%x contains 0x%x",
+ address, val);
+ }
+ }
+ info("s5k83a register state dump complete");
+
+ for (page = 0; page < 16; page++) {
+ s5k83a_write_sensor(sd, S5K83A_PAGE_MAP, &page, 1);
+ info("Probing for which registers that are read/write "
+ "for page 0x%x", page);
+ for (address = 0; address <= 0xff; address++) {
+ u8 old_val, ctrl_val, test_val = 0xff;
+
+ s5k83a_read_sensor(sd, address, &old_val, 1);
+ s5k83a_write_sensor(sd, address, &test_val, 1);
+ s5k83a_read_sensor(sd, address, &ctrl_val, 1);
+
+ if (ctrl_val == test_val)
+ info("register 0x%x is writeable", address);
+ else
+ info("register 0x%x is read only", address);
+
+ /* Restore original val */
+ s5k83a_write_sensor(sd, address, &old_val, 1);
+ }
+ }
+ info("Read/write register probing complete");
+ s5k83a_write_sensor(sd, S5K83A_PAGE_MAP, &old_page, 1);
+}
+
+int s5k83a_get_brightness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ int err;
+ u8 data[2];
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ err = s5k83a_read_sensor(sd, S5K83A_BRIGHTNESS, data, 2);
+ data[1] = data[1] << 1;
+ *val = data[1];
+
+ return (err < 0) ? err : 0;
+}
+
+int s5k83a_set_brightness(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ u8 data[2];
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ data[0] = 0x00;
+ data[1] = 0x20;
+ err = s5k83a_write_sensor(sd, 0x14, data, 2);
+ if (err < 0)
+ return err;
+
+ data[0] = 0x01;
+ data[1] = 0x00;
+ err = s5k83a_write_sensor(sd, 0x0d, data, 2);
+ if (err < 0)
+ return err;
+
+ /* FIXME: This is not sane, we need to figure out the composition
+ of these registers */
+ data[0] = val >> 3; /* brightness, high 5 bits */
+ data[1] = val >> 1; /* brightness, high 7 bits */
+ err = s5k83a_write_sensor(sd, S5K83A_BRIGHTNESS, data, 2);
+
+ return (err < 0) ? err : 0;
+}
+
+int s5k83a_get_whiteness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ int err;
+ u8 data;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ err = s5k83a_read_sensor(sd, S5K83A_WHITENESS, &data, 1);
+
+ *val = data;
+ return (err < 0) ? err : 0;
+}
+
+int s5k83a_set_whiteness(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ u8 data[1];
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ data[0] = val;
+ err = s5k83a_write_sensor(sd, S5K83A_WHITENESS, data, 1);
+
+ return (err < 0) ? err : 0;
+}
+
+int s5k83a_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ int err;
+ u8 data[2];
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ err = s5k83a_read_sensor(sd, S5K83A_GAIN, data, 2);
+
+ data[1] = data[1] & 0x3f;
+ if (data[1] > S5K83A_MAXIMUM_GAIN)
+ data[1] = S5K83A_MAXIMUM_GAIN;
+
+ *val = data[1];
+
+ return (err < 0) ? err : 0;
+}
+
+int s5k83a_set_gain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err = 0;
+ u8 data[2];
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ data[0] = 0;
+ data[1] = val;
+ err = s5k83a_write_sensor(sd, S5K83A_GAIN, data, 2);
+
+ return (err < 0) ? err : 0;
+}
diff --git a/linux/drivers/media/video/gspca/m5602/m5602_s5k83a.h b/linux/drivers/media/video/gspca/m5602/m5602_s5k83a.h
new file mode 100644
index 000000000..67432b5a5
--- /dev/null
+++ b/linux/drivers/media/video/gspca/m5602/m5602_s5k83a.h
@@ -0,0 +1,444 @@
+/*
+ * Driver for the s5k83a sensor
+ *
+ * Copyright (C) 2008 Erik Andren
+ * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * 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, version 2.
+ *
+ */
+
+#ifndef M5602_S5K83A_H_
+#define M5602_S5K83A_H_
+
+#include "m5602_sensor.h"
+
+#define S5K83A_PAGE_MAP 0xec
+#define S5K83A_GAIN 0x18
+#define S5K83A_WHITENESS 0x0a
+#define S5K83A_BRIGHTNESS 0x1b
+
+#define S5K83A_DEFAULT_BRIGHTNESS 0x71
+#define S5K83A_DEFAULT_WHITENESS 0x7e
+#define S5K83A_DEFAULT_GAIN 0x00
+#define S5K83A_MAXIMUM_GAIN 0x3c
+
+/*****************************************************************************/
+
+/* Kernel module parameters */
+extern int force_sensor;
+extern int dump_sensor;
+extern unsigned int debug;
+
+
+int s5k83a_probe(struct sd *sd);
+int s5k83a_init(struct sd *sd);
+int s5k83a_power_down(struct sd *sd);
+
+void s5k83a_dump_registers(struct sd *sd);
+
+int s5k83a_read_sensor(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len);
+int s5k83a_write_sensor(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len);
+
+int s5k83a_set_brightness(struct gspca_dev *gspca_dev, __s32 val);
+int s5k83a_get_brightness(struct gspca_dev *gspca_dev, __s32 *val);
+int s5k83a_set_whiteness(struct gspca_dev *gspca_dev, __s32 val);
+int s5k83a_get_whiteness(struct gspca_dev *gspca_dev, __s32 *val);
+int s5k83a_set_gain(struct gspca_dev *gspca_dev, __s32 val);
+int s5k83a_get_gain(struct gspca_dev *gspca_dev, __s32 *val);
+
+static struct m5602_sensor s5k83a = {
+ .name = "S5K83A",
+ .probe = s5k83a_probe,
+ .init = s5k83a_init,
+ .power_down = s5k83a_power_down,
+ .read_sensor = s5k83a_read_sensor,
+ .write_sensor = s5k83a_write_sensor,
+ .i2c_slave_id = 0x5a,
+ .nctrls = 3,
+ .ctrls = {
+ {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "brightness",
+ .minimum = 0x00,
+ .maximum = 0xff,
+ .step = 0x01,
+ .default_value = S5K83A_DEFAULT_BRIGHTNESS,
+ .flags = V4L2_CTRL_FLAG_SLIDER
+ },
+ .set = s5k83a_set_brightness,
+ .get = s5k83a_get_brightness
+
+ }, {
+ {
+ .id = V4L2_CID_WHITENESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "whiteness",
+ .minimum = 0x00,
+ .maximum = 0xff,
+ .step = 0x01,
+ .default_value = S5K83A_DEFAULT_WHITENESS,
+ .flags = V4L2_CTRL_FLAG_SLIDER
+ },
+ .set = s5k83a_set_whiteness,
+ .get = s5k83a_get_whiteness,
+ }, {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "gain",
+ .minimum = 0x00,
+ .maximum = S5K83A_MAXIMUM_GAIN,
+ .step = 0x01,
+ .default_value = S5K83A_DEFAULT_GAIN,
+ .flags = V4L2_CTRL_FLAG_SLIDER
+ },
+ .set = s5k83a_set_gain,
+ .get = s5k83a_get_gain
+ }
+ },
+ .nmodes = 1,
+ .modes = {
+ {
+ M5602_DEFAULT_FRAME_WIDTH,
+ M5602_DEFAULT_FRAME_HEIGHT,
+ V4L2_PIX_FMT_SBGGR8,
+ V4L2_FIELD_NONE,
+ .sizeimage =
+ M5602_DEFAULT_FRAME_WIDTH * M5602_DEFAULT_FRAME_HEIGHT,
+ .bytesperline = M5602_DEFAULT_FRAME_WIDTH,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1
+
+ }
+ }
+};
+
+static const unsigned char preinit_s5k83a[][4] =
+{
+ {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
+ {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00, 0x00},
+
+ {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x3f, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3f, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0x80, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+ {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
+ {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xf0, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x1c, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
+ {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x20, 0x00},
+
+ {SENSOR, S5K83A_PAGE_MAP, 0x00, 0x00}
+};
+
+/* This could probably be considerably shortened.
+ I don't have the hardware to experiment with it, patches welcome
+*/
+static const unsigned char init_s5k83a[][4] =
+{
+ {SENSOR, S5K83A_PAGE_MAP, 0x04, 0x00},
+ {SENSOR, 0xaf, 0x01, 0x00},
+ {SENSOR, S5K83A_PAGE_MAP, 0x00, 0x00},
+ {SENSOR, 0x7b, 0xff, 0x00},
+ {SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00},
+ {SENSOR, 0x01, 0x50, 0x00},
+ {SENSOR, 0x12, 0x20, 0x00},
+ {SENSOR, 0x17, 0x40, 0x00},
+ {SENSOR, S5K83A_BRIGHTNESS, 0x0f, 0x00},
+ {SENSOR, 0x1c, 0x00, 0x00},
+ {SENSOR, 0x02, 0x70, 0x00},
+ {SENSOR, 0x03, 0x0b, 0x00},
+ {SENSOR, 0x04, 0xf0, 0x00},
+ {SENSOR, 0x05, 0x0b, 0x00},
+ {SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00},
+
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+ {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00},
+ {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x01, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0xe4, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x02, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x87, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+
+ {SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00},
+ {SENSOR, 0x06, 0x71, 0x00},
+ {SENSOR, 0x07, 0xe8, 0x00},
+ {SENSOR, 0x08, 0x02, 0x00},
+ {SENSOR, 0x09, 0x88, 0x00},
+ {SENSOR, 0x14, 0x00, 0x00},
+ {SENSOR, 0x15, 0x20, 0x00},
+ {SENSOR, 0x19, 0x00, 0x00},
+ {SENSOR, 0x1a, 0x98, 0x00},
+ {SENSOR, 0x0f, 0x02, 0x00},
+ {SENSOR, 0x10, 0xe5, 0x00},
+ {SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00},
+ {SENSOR_LONG, 0x14, 0x00, 0x20},
+ {SENSOR_LONG, 0x0d, 0x00, 0x7d},
+ {SENSOR_LONG, 0x1b, 0x0d, 0x05},
+
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+ {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00},
+ {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x01, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0xe4, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x02, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x87, 0x00},
+
+ {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x3f, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3f, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0x80, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+ {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
+ {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xf0, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x1c, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
+ {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x20, 0x00},
+
+ {SENSOR, S5K83A_PAGE_MAP, 0x04, 0x00},
+ {SENSOR, 0xaf, 0x01, 0x00},
+ {SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00},
+ /* ff ( init value )is very dark) || 71 and f0 better */
+ {SENSOR, 0x7b, 0xff, 0x00},
+ {SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00},
+ {SENSOR, 0x01, 0x50, 0x00},
+ {SENSOR, 0x12, 0x20, 0x00},
+ {SENSOR, 0x17, 0x40, 0x00},
+ {SENSOR, S5K83A_BRIGHTNESS, 0x0f, 0x00},
+ {SENSOR, 0x1c, 0x00, 0x00},
+ {SENSOR, 0x02, 0x70, 0x00},
+ /* some values like 0x10 give a blue-purple image */
+ {SENSOR, 0x03, 0x0b, 0x00},
+ {SENSOR, 0x04, 0xf0, 0x00},
+ {SENSOR, 0x05, 0x0b, 0x00},
+ {SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00},
+
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},
+ /* under 80 don't work, highter depend on value */
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+ {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00},
+ {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x01, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0xe4, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x02, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x7f, 0x00},
+
+ {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+
+ {SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00},
+ {SENSOR, 0x06, 0x71, 0x00},
+ {SENSOR, 0x07, 0xe8, 0x00},
+ {SENSOR, 0x08, 0x02, 0x00},
+ {SENSOR, 0x09, 0x88, 0x00},
+ {SENSOR, 0x14, 0x00, 0x00},
+ {SENSOR, 0x15, 0x20, 0x00},
+ {SENSOR, 0x19, 0x00, 0x00},
+ {SENSOR, 0x1a, 0x98, 0x00},
+ {SENSOR, 0x0f, 0x02, 0x00},
+ {SENSOR, 0x10, 0xe5, 0x00},
+ {SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00},
+ {SENSOR_LONG, 0x14, 0x00, 0x20},
+ {SENSOR_LONG, 0x0d, 0x00, 0x7d},
+ {SENSOR_LONG, 0x1b, 0x0d, 0x05},
+
+ /* The following sequence is useless after a clean boot
+ but is necessary after resume from suspend */
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x3f, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3f, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0x80, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+ {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
+ {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xf0, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x1c, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
+ {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x20, 0x00},
+
+ {SENSOR, S5K83A_PAGE_MAP, 0x04, 0x00},
+ {SENSOR, 0xaf, 0x01, 0x00},
+ {SENSOR, S5K83A_PAGE_MAP, 0x00, 0x00},
+ {SENSOR, 0x7b, 0xff, 0x00},
+ {SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00},
+ {SENSOR, 0x01, 0x50, 0x00},
+ {SENSOR, 0x12, 0x20, 0x00},
+ {SENSOR, 0x17, 0x40, 0x00},
+ {SENSOR, S5K83A_BRIGHTNESS, 0x0f, 0x00},
+ {SENSOR, 0x1c, 0x00, 0x00},
+ {SENSOR, 0x02, 0x70, 0x00},
+ {SENSOR, 0x03, 0x0b, 0x00},
+ {SENSOR, 0x04, 0xf0, 0x00},
+ {SENSOR, 0x05, 0x0b, 0x00},
+ {SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00},
+
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+ {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00},
+ {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x01, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0xe4, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x02, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x7f, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+
+ {SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00},
+ {SENSOR, 0x06, 0x71, 0x00},
+ {SENSOR, 0x07, 0xe8, 0x00},
+ {SENSOR, 0x08, 0x02, 0x00},
+ {SENSOR, 0x09, 0x88, 0x00},
+ {SENSOR, 0x14, 0x00, 0x00},
+ {SENSOR, 0x15, 0x20, 0x00},
+ {SENSOR, 0x19, 0x00, 0x00},
+ {SENSOR, 0x1a, 0x98, 0x00},
+ {SENSOR, 0x0f, 0x02, 0x00},
+
+ {SENSOR, 0x10, 0xe5, 0x00},
+ {SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00},
+ {SENSOR_LONG, 0x14, 0x00, 0x20},
+ {SENSOR_LONG, 0x0d, 0x00, 0x7d},
+ {SENSOR_LONG, 0x1b, 0x0d, 0x05},
+
+ /* normal colors
+ (this is value after boot, but after tries can be different) */
+ {SENSOR, 0x00, 0x06, 0x00},
+
+ /* set default brightness */
+ {SENSOR_LONG, 0x14, 0x00, 0x20},
+ {SENSOR_LONG, 0x0d, 0x01, 0x00},
+ {SENSOR_LONG, 0x1b, S5K83A_DEFAULT_BRIGHTNESS >> 3,
+ S5K83A_DEFAULT_BRIGHTNESS >> 1},
+
+ /* set default whiteness */
+ {SENSOR, S5K83A_WHITENESS, S5K83A_DEFAULT_WHITENESS, 0x00},
+
+ /* set default gain */
+ {SENSOR_LONG, 0x18, 0x00, S5K83A_DEFAULT_GAIN}
+};
+
+#endif
diff --git a/linux/drivers/media/video/gspca/m5602/m5602_sensor.h b/linux/drivers/media/video/gspca/m5602/m5602_sensor.h
new file mode 100644
index 000000000..930fcaab4
--- /dev/null
+++ b/linux/drivers/media/video/gspca/m5602/m5602_sensor.h
@@ -0,0 +1,76 @@
+/*
+ * USB Driver for ALi m5602 based webcams
+ *
+ * Copyright (C) 2008 Erik Andren
+ * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * 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, version 2.
+ *
+ */
+
+#ifndef M5602_SENSOR_H_
+#define M5602_SENSOR_H_
+
+#include "m5602_bridge.h"
+
+#define M5602_DEFAULT_FRAME_WIDTH 640
+#define M5602_DEFAULT_FRAME_HEIGHT 480
+
+#define M5602_MAX_CTRLS (V4L2_CID_LASTP1 - V4L2_CID_BASE + 10)
+
+/* Enumerates all supported sensors */
+enum sensors {
+ OV9650_SENSOR = 1,
+ S5K83A_SENSOR = 2,
+ S5K4AA_SENSOR = 3,
+ MT9M111_SENSOR = 4,
+ PO1030_SENSOR = 5
+};
+
+/* Enumerates all possible instruction types */
+enum instruction {
+ BRIDGE,
+ SENSOR,
+ SENSOR_LONG
+};
+
+struct m5602_sensor {
+ /* Defines the name of a sensor */
+ char name[32];
+
+ /* What i2c address the sensor is connected to */
+ u8 i2c_slave_id;
+
+ /* Probes if the sensor is connected */
+ int (*probe)(struct sd *sd);
+
+ /* Performs a initialization sequence */
+ int (*init)(struct sd *sd);
+
+ /* Performs a power down sequence */
+ int (*power_down)(struct sd *sd);
+
+ /* Reads a sensor register */
+ int (*read_sensor)(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len);
+
+ /* Writes to a sensor register */
+ int (*write_sensor)(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len);
+
+ int nctrls;
+ struct ctrl ctrls[M5602_MAX_CTRLS];
+
+ char nmodes;
+ struct v4l2_pix_format modes[];
+};
+
+#endif
diff --git a/linux/drivers/media/video/gspca/mars.c b/linux/drivers/media/video/gspca/mars.c
index 4d5db47ba..277ca34a8 100644
--- a/linux/drivers/media/video/gspca/mars.c
+++ b/linux/drivers/media/video/gspca/mars.c
@@ -134,7 +134,7 @@ static int sd_init(struct gspca_dev *gspca_dev)
return 0;
}
-static void sd_start(struct gspca_dev *gspca_dev)
+static int sd_start(struct gspca_dev *gspca_dev)
{
int err_code;
__u8 *data;
@@ -143,9 +143,10 @@ static void sd_start(struct gspca_dev *gspca_dev)
int intpipe;
PDEBUG(D_STREAM, "camera start, iface %d, alt 8", gspca_dev->iface);
- if (usb_set_interface(gspca_dev->dev, gspca_dev->iface, 8) < 0) {
+ err_code = usb_set_interface(gspca_dev->dev, gspca_dev->iface, 8);
+ if (err_code < 0) {
PDEBUG(D_ERR|D_STREAM, "Set packet size: set interface error");
- return;
+ return err_code;
}
data = gspca_dev->usb_buf;
@@ -154,7 +155,7 @@ static void sd_start(struct gspca_dev *gspca_dev)
err_code = reg_w(gspca_dev, data[0], 2);
if (err_code < 0)
- return;
+ return err_code;
/*
Initialize the MR97113 chip register
@@ -180,14 +181,14 @@ static void sd_start(struct gspca_dev *gspca_dev)
err_code = reg_w(gspca_dev, data[0], 11);
if (err_code < 0)
- return;
+ return err_code;
data[0] = 0x23; /* address */
data[1] = 0x09; /* reg 35, append frame header */
err_code = reg_w(gspca_dev, data[0], 2);
if (err_code < 0)
- return;
+ return err_code;
data[0] = 0x3c; /* address */
/* if (gspca_dev->width == 1280) */
@@ -198,7 +199,7 @@ static void sd_start(struct gspca_dev *gspca_dev)
* (unit: 4KB) 200KB */
err_code = reg_w(gspca_dev, data[0], 2);
if (err_code < 0)
- return;
+ return err_code;
if (0) { /* fixed dark-gain */
data[1] = 0; /* reg 94, Y Gain (1.75) */
@@ -240,13 +241,13 @@ static void sd_start(struct gspca_dev *gspca_dev)
err_code = reg_w(gspca_dev, data[0], 6);
if (err_code < 0)
- return;
+ return err_code;
data[0] = 0x67;
data[1] = 0x13; /* reg 103, first pixel B, disable sharpness */
err_code = reg_w(gspca_dev, data[0], 2);
if (err_code < 0)
- return;
+ return err_code;
/*
* initialize the value of MI sensor...
@@ -326,6 +327,7 @@ static void sd_start(struct gspca_dev *gspca_dev)
data[0] = 0x00;
data[1] = 0x4d; /* ISOC transfering enable... */
reg_w(gspca_dev, data[0], 2);
+ return err_code;
}
static void sd_stopN(struct gspca_dev *gspca_dev)
diff --git a/linux/drivers/media/video/gspca/ov519.c b/linux/drivers/media/video/gspca/ov519.c
index bb2842114..895bcf082 100644
--- a/linux/drivers/media/video/gspca/ov519.c
+++ b/linux/drivers/media/video/gspca/ov519.c
@@ -1879,7 +1879,7 @@ static int set_ov_sensor_window(struct sd *sd)
}
/* -- start the camera -- */
-static void sd_start(struct gspca_dev *gspca_dev)
+static int sd_start(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
int ret;
@@ -1896,9 +1896,10 @@ static void sd_start(struct gspca_dev *gspca_dev)
goto out;
PDEBUG(D_STREAM, "camera started alt: 0x%02x", gspca_dev->alt);
ov51x_led_control(sd, 1);
- return;
+ return 0;
out:
PDEBUG(D_ERR, "camera start error:%d", ret);
+ return ret;
}
static void sd_stopN(struct gspca_dev *gspca_dev)
diff --git a/linux/drivers/media/video/gspca/pac207.c b/linux/drivers/media/video/gspca/pac207.c
index 34e923d00..0b0c573d0 100644
--- a/linux/drivers/media/video/gspca/pac207.c
+++ b/linux/drivers/media/video/gspca/pac207.c
@@ -281,7 +281,7 @@ static int sd_init(struct gspca_dev *gspca_dev)
}
/* -- start the camera -- */
-static void sd_start(struct gspca_dev *gspca_dev)
+static int sd_start(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
__u8 mode;
@@ -323,6 +323,7 @@ static void sd_start(struct gspca_dev *gspca_dev)
sd->sof_read = 0;
sd->autogain_ignore_frames = 0;
atomic_set(&sd->avg_lum, -1);
+ return 0;
}
static void sd_stopN(struct gspca_dev *gspca_dev)
diff --git a/linux/drivers/media/video/gspca/pac7311.c b/linux/drivers/media/video/gspca/pac7311.c
index 296ba82e6..a122634e0 100644
--- a/linux/drivers/media/video/gspca/pac7311.c
+++ b/linux/drivers/media/video/gspca/pac7311.c
@@ -689,7 +689,7 @@ static int sd_init(struct gspca_dev *gspca_dev)
return 0;
}
-static void sd_start(struct gspca_dev *gspca_dev)
+static int sd_start(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
@@ -738,6 +738,7 @@ static void sd_start(struct gspca_dev *gspca_dev)
reg_w(gspca_dev, 0x78, 0x01);
else
reg_w(gspca_dev, 0x78, 0x05);
+ return 0;
}
static void sd_stopN(struct gspca_dev *gspca_dev)
diff --git a/linux/drivers/media/video/gspca/sonixb.c b/linux/drivers/media/video/gspca/sonixb.c
index 53cb724bf..4bae911da 100644
--- a/linux/drivers/media/video/gspca/sonixb.c
+++ b/linux/drivers/media/video/gspca/sonixb.c
@@ -922,7 +922,7 @@ static int sd_init(struct gspca_dev *gspca_dev)
}
/* -- start the camera -- */
-static void sd_start(struct gspca_dev *gspca_dev)
+static int sd_start(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
struct cam *cam = &gspca_dev->cam;
@@ -1006,6 +1006,7 @@ static void sd_start(struct gspca_dev *gspca_dev)
sd->frames_to_drop = 0;
sd->autogain_ignore_frames = 0;
atomic_set(&sd->avg_lum, -1);
+ return 0;
}
static void sd_stopN(struct gspca_dev *gspca_dev)
diff --git a/linux/drivers/media/video/gspca/sonixj.c b/linux/drivers/media/video/gspca/sonixj.c
index a77164581..f68faf6bd 100644
--- a/linux/drivers/media/video/gspca/sonixj.c
+++ b/linux/drivers/media/video/gspca/sonixj.c
@@ -144,7 +144,7 @@ static struct ctrl sd_ctrls[] = {
.minimum = 0,
.maximum = 1,
.step = 1,
-#define VFLIP_DEF 0
+#define VFLIP_DEF 1
.default_value = VFLIP_DEF,
},
.set = sd_setvflip,
@@ -241,17 +241,6 @@ static const __u8 sn_ov7648[] = {
};
static const __u8 sn_ov7660[] = {
-#if 0
-/*jfm: from win trace */
-/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
- 0x00, 0x61, 0x40, 0x00, 0x1a, 0x00, 0x00, 0x00,
-/* reg8 reg9 rega regb regc regd rege regf */
- 0x81, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
- 0x03, 0x00, 0x01, 0x01, 0x14, 0x28, 0x1e, 0x00,
-/* reg18 reg19 reg1a reg1b reg1c reg1d reg1e reg1f */
- 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-#else
/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
0x00, 0x61, 0x40, 0x00, 0x1a, 0x20, 0x20, 0x20,
/* reg8 reg9 rega regb regc regd rege regf */
@@ -260,7 +249,6 @@ static const __u8 sn_ov7660[] = {
0x03, 0x00, 0x01, 0x01, 0x08, 0x28, 0x1e, 0x20,
/* reg18 reg19 reg1a reg1b reg1c reg1d reg1e reg1f */
0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-#endif
};
/* sequence specific to the sensors - !! index = SENSOR_xxx */
@@ -279,14 +267,12 @@ static const __u8 gamma_def[] = {
0xa6, 0xb2, 0xbf, 0xca, 0xd5, 0xe0, 0xeb, 0xf5, 0xff
};
+/* color matrix and offsets */
static const __u8 reg84[] = {
- 0x14, 0x00, 0x27, 0x00, 0x07, 0x00, 0xe5, 0x0f,
- 0xe4, 0x0f, 0x38, 0x00, 0x3e, 0x00, 0xc3, 0x0f,
-#if 1
- 0xf7, 0x0f, 0x00, 0x00, 0x00
-#else
- 0xf7, 0x0f, 0x0a, 0x00, 0x00
-#endif
+ 0x14, 0x00, 0x27, 0x00, 0x07, 0x00, /* YR YG YB gains */
+ 0xe8, 0x0f, 0xda, 0x0f, 0x40, 0x00, /* UR UG UB */
+ 0x3e, 0x00, 0xcd, 0x0f, 0xf7, 0x0f, /* VR VG VB */
+ 0x00, 0x00, 0x00 /* YUV offsets */
};
static const __u8 hv7131r_sensor_init[][8] = {
{0xC1, 0x11, 0x01, 0x08, 0x01, 0x00, 0x00, 0x10},
@@ -743,6 +729,7 @@ static void i2c_w8(struct gspca_dev *gspca_dev,
0x08, 0, /* value, index */
gspca_dev->usb_buf, 8,
500);
+ msleep(2);
}
/* read 5 bytes in gspca_dev->usb_buf */
@@ -1022,13 +1009,13 @@ static int sd_init(struct gspca_dev *gspca_dev)
case BRIDGE_SN9C105:
if (regF1 != 0x11)
return -ENODEV;
- reg_w(gspca_dev, 0x02, regGpio, 2);
+ reg_w(gspca_dev, 0x01, regGpio, 2);
break;
case BRIDGE_SN9C120:
if (regF1 != 0x12)
return -ENODEV;
regGpio[1] = 0x70;
- reg_w(gspca_dev, 0x02, regGpio, 2);
+ reg_w(gspca_dev, 0x01, regGpio, 2);
break;
default:
/* case BRIDGE_SN9C110: */
@@ -1130,20 +1117,17 @@ static unsigned int setexposure(struct gspca_dev *gspca_dev,
static void setbrightcont(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
- unsigned val;
+ int val;
__u8 reg84_full[0x15];
- memset(reg84_full, 0, sizeof reg84_full);
- val = sd->contrast * 0x20 / CONTRAST_MAX + 0x10; /* 10..30 */
- reg84_full[2] = val;
- reg84_full[0] = (val + 1) / 2;
- reg84_full[4] = (val + 1) / 5;
- if (val > BRIGHTNESS_DEF)
- val = (sd->brightness - BRIGHTNESS_DEF) * 0x20
+ memcpy(reg84_full, reg84, sizeof reg84_full);
+ val = sd->contrast * 0x30 / CONTRAST_MAX + 0x10; /* 10..40 */
+ reg84_full[0] = (val + 1) / 2; /* red */
+ reg84_full[2] = val; /* green */
+ reg84_full[4] = (val + 1) / 5; /* blue */
+ val = (sd->brightness - BRIGHTNESS_DEF) * 0x10
/ BRIGHTNESS_MAX;
- else
- val = 0;
- reg84_full[0x12] = val; /* 00..1f */
+ reg84_full[0x12] = val & 0x1f; /* 5:0 signed value */
reg_w(gspca_dev, 0x84, reg84_full, sizeof reg84_full);
}
@@ -1231,7 +1215,7 @@ static void setvflip(struct sd *sd)
}
/* -- start the camera -- */
-static void sd_start(struct gspca_dev *gspca_dev)
+static int sd_start(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
int i;
@@ -1242,7 +1226,7 @@ static void sd_start(struct gspca_dev *gspca_dev)
static const __u8 CA[] = { 0x28, 0xd8, 0x14, 0xec };
static const __u8 CE[] = { 0x32, 0xdd, 0x2d, 0xdd }; /* MI0360 */
static const __u8 CE_ov76xx[] =
- { 0x32, 0xdd, 0x32, 0xdd }; /* OV7630/48 */
+ { 0x32, 0xdd, 0x32, 0xdd };
sn9c1xx = sn_tb[(int) sd->sensor];
configure_gpio(gspca_dev, sn9c1xx);
@@ -1284,20 +1268,15 @@ static void sd_start(struct gspca_dev *gspca_dev)
reg_w(gspca_dev, 0x20, gamma_def, sizeof gamma_def);
for (i = 0; i < 8; i++)
reg_w(gspca_dev, 0x84, reg84, sizeof reg84);
-#if 0
- switch (sd->bridge) {
- case BRIDGE_SN9C325:
- reg_w1(gspca_dev, 0x9a, 0x0a);
- reg_w1(gspca_dev, 0x99, 0x60);
+ switch (sd->sensor) {
+ case SENSOR_OV7660:
+ reg_w1(gspca_dev, 0x9a, 0x05);
break;
default:
-#endif
reg_w1(gspca_dev, 0x9a, 0x08);
reg_w1(gspca_dev, 0x99, 0x59);
-#if 0
break;
}
-#endif
mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv;
if (mode)
@@ -1349,14 +1328,8 @@ static void sd_start(struct gspca_dev *gspca_dev)
/* reg1 = 0x44; */
/* reg1 = 0x46; (done) */
} else {
-#if 0
-/*jfm: from win trace */
reg17 = 0xa2; /* 640 */
- reg1 = 0x40;
-#else
- reg17 = 0x22; /* 640 MCKSIZE */
- reg1 = 0x06;
-#endif
+ reg1 = 0x44;
}
break;
}
@@ -1365,6 +1338,7 @@ static void sd_start(struct gspca_dev *gspca_dev)
switch (sd->sensor) {
case SENSOR_OV7630:
case SENSOR_OV7648:
+ case SENSOR_OV7660:
reg_w(gspca_dev, 0xce, CE_ov76xx, 4);
break;
default:
@@ -1397,6 +1371,7 @@ static void sd_start(struct gspca_dev *gspca_dev)
}
setautogain(gspca_dev);
reg_w1(gspca_dev, 0x01, reg1);
+ return 0;
}
static void sd_stopN(struct gspca_dev *gspca_dev)
@@ -1677,7 +1652,9 @@ static const __devinitdata struct usb_device_id device_table[] = {
/* {USB_DEVICE(0x0c45, 0x60fa), BSI(SN9C105, OV7648, 0x??)}, */
{USB_DEVICE(0x0c45, 0x60fb), BSI(SN9C105, OV7660, 0x21)},
{USB_DEVICE(0x0c45, 0x60fc), BSI(SN9C105, HV7131R, 0x11)},
-/* {USB_DEVICE(0x0c45, 0x60fe), BSI(SN9C105, OV7630, 0x??)}, */
+#if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE
+ {USB_DEVICE(0x0c45, 0x60fe), BSI(SN9C105, OV7630, 0x21)},
+#endif
/* {USB_DEVICE(0x0c45, 0x6108), BSI(SN9C120, OM6801, 0x??)}, */
/* {USB_DEVICE(0x0c45, 0x6122), BSI(SN9C110, ICM105C, 0x??)}, */
/* {USB_DEVICE(0x0c45, 0x6123), BSI(SN9C110, SanyoCCD, 0x??)}, */
diff --git a/linux/drivers/media/video/gspca/spca500.c b/linux/drivers/media/video/gspca/spca500.c
index c33bfe48c..a3f616709 100644
--- a/linux/drivers/media/video/gspca/spca500.c
+++ b/linux/drivers/media/video/gspca/spca500.c
@@ -677,7 +677,7 @@ static int sd_init(struct gspca_dev *gspca_dev)
return 0;
}
-static void sd_start(struct gspca_dev *gspca_dev)
+static int sd_start(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
int err;
@@ -884,6 +884,7 @@ static void sd_start(struct gspca_dev *gspca_dev)
write_vector(gspca_dev, Clicksmart510_defaults);
break;
}
+ return 0;
}
static void sd_stopN(struct gspca_dev *gspca_dev)
diff --git a/linux/drivers/media/video/gspca/spca501.c b/linux/drivers/media/video/gspca/spca501.c
index 49e512cac..979344907 100644
--- a/linux/drivers/media/video/gspca/spca501.c
+++ b/linux/drivers/media/video/gspca/spca501.c
@@ -1991,7 +1991,7 @@ static int sd_init(struct gspca_dev *gspca_dev)
return 0;
}
-static void sd_start(struct gspca_dev *gspca_dev)
+static int sd_start(struct gspca_dev *gspca_dev)
{
struct usb_device *dev = gspca_dev->dev;
int mode;
@@ -2023,6 +2023,7 @@ static void sd_start(struct gspca_dev *gspca_dev)
setbrightness(gspca_dev);
setcontrast(gspca_dev);
setcolors(gspca_dev);
+ return 0;
}
static void sd_stopN(struct gspca_dev *gspca_dev)
diff --git a/linux/drivers/media/video/gspca/spca505.c b/linux/drivers/media/video/gspca/spca505.c
index e33666df4..62bab3cc1 100644
--- a/linux/drivers/media/video/gspca/spca505.c
+++ b/linux/drivers/media/video/gspca/spca505.c
@@ -757,7 +757,7 @@ static int sd_init(struct gspca_dev *gspca_dev)
return 0;
}
-static void sd_start(struct gspca_dev *gspca_dev)
+static int sd_start(struct gspca_dev *gspca_dev)
{
struct usb_device *dev = gspca_dev->dev;
int ret;
@@ -802,6 +802,7 @@ static void sd_start(struct gspca_dev *gspca_dev)
/* reg_write(dev, 0x5, 0x0, 0x0); */
/* reg_write(dev, 0x5, 0x0, 0x1); */
/* reg_write(dev, 0x5, 0x11, 0x2); */
+ return ret;
}
static void sd_stopN(struct gspca_dev *gspca_dev)
diff --git a/linux/drivers/media/video/gspca/spca506.c b/linux/drivers/media/video/gspca/spca506.c
index 195dce96e..645ee9d44 100644
--- a/linux/drivers/media/video/gspca/spca506.c
+++ b/linux/drivers/media/video/gspca/spca506.c
@@ -422,7 +422,7 @@ static int sd_init(struct gspca_dev *gspca_dev)
return 0;
}
-static void sd_start(struct gspca_dev *gspca_dev)
+static int sd_start(struct gspca_dev *gspca_dev)
{
struct usb_device *dev = gspca_dev->dev;
__u16 norme;
@@ -549,6 +549,7 @@ static void sd_start(struct gspca_dev *gspca_dev)
PDEBUG(D_STREAM, "webcam started");
spca506_GetNormeInput(gspca_dev, &norme, &channel);
spca506_SetNormeInput(gspca_dev, norme, channel);
+ return 0;
}
static void sd_stopN(struct gspca_dev *gspca_dev)
diff --git a/linux/drivers/media/video/gspca/spca508.c b/linux/drivers/media/video/gspca/spca508.c
index 5e3f66ae2..c9360cdb8 100644
--- a/linux/drivers/media/video/gspca/spca508.c
+++ b/linux/drivers/media/video/gspca/spca508.c
@@ -1566,7 +1566,7 @@ static int sd_init(struct gspca_dev *gspca_dev)
return 0;
}
-static void sd_start(struct gspca_dev *gspca_dev)
+static int sd_start(struct gspca_dev *gspca_dev)
{
int mode;
@@ -1584,6 +1584,7 @@ static void sd_start(struct gspca_dev *gspca_dev)
break;
}
reg_write(gspca_dev->dev, 0x8112, 0x10 | 0x20);
+ return 0;
}
static void sd_stopN(struct gspca_dev *gspca_dev)
diff --git a/linux/drivers/media/video/gspca/spca561.c b/linux/drivers/media/video/gspca/spca561.c
index b1329a149..5b65dd66b 100644
--- a/linux/drivers/media/video/gspca/spca561.c
+++ b/linux/drivers/media/video/gspca/spca561.c
@@ -152,7 +152,7 @@ static void reg_w_val(struct usb_device *dev, __u16 index, __u8 value)
ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
0, /* request */
- USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
value, index, NULL, 0, 500);
PDEBUG(D_USBO, "reg write: 0x%02x:0x%02x", index, value);
if (ret < 0)
@@ -225,7 +225,7 @@ static int i2c_read(struct gspca_dev *gspca_dev, __u16 reg, __u8 mode)
reg_w_val(gspca_dev->dev, 0x8802, (mode | 0x01));
do {
reg_r(gspca_dev, 0x8803, 1);
- if (!gspca_dev->usb_buf)
+ if (!gspca_dev->usb_buf[0])
break;
} while (--retry);
if (retry == 0)
@@ -713,7 +713,7 @@ static void setautogain(struct gspca_dev *gspca_dev)
sd->ag_cnt = -1;
}
-static void sd_start_12a(struct gspca_dev *gspca_dev)
+static int sd_start_12a(struct gspca_dev *gspca_dev)
{
struct usb_device *dev = gspca_dev->dev;
int Clck = 0x8a; /* lower 0x8X values lead to fps > 30 */
@@ -739,8 +739,9 @@ static void sd_start_12a(struct gspca_dev *gspca_dev)
setwhite(gspca_dev);
setautogain(gspca_dev);
setexposure(gspca_dev);
+ return 0;
}
-static void sd_start_72a(struct gspca_dev *gspca_dev)
+static int sd_start_72a(struct gspca_dev *gspca_dev)
{
struct usb_device *dev = gspca_dev->dev;
int Clck;
@@ -764,6 +765,7 @@ static void sd_start_72a(struct gspca_dev *gspca_dev)
reg_w_val(dev, 0x8700, Clck); /* 0x27 clock */
reg_w_val(dev, 0x8112, 0x10 | 0x20);
setautogain(gspca_dev);
+ return 0;
}
static void sd_stopN(struct gspca_dev *gspca_dev)
@@ -1078,7 +1080,7 @@ static struct ctrl sd_ctrls_12a[] = {
{
.id = V4L2_CID_DO_WHITE_BALANCE,
.type = V4L2_CTRL_TYPE_INTEGER,
- .name = "While Balance",
+ .name = "White Balance",
.minimum = WHITE_MIN,
.maximum = WHITE_MAX,
.step = 1,
diff --git a/linux/drivers/media/video/gspca/stk014.c b/linux/drivers/media/video/gspca/stk014.c
index 2f2de429e..d9d64911f 100644
--- a/linux/drivers/media/video/gspca/stk014.c
+++ b/linux/drivers/media/video/gspca/stk014.c
@@ -324,7 +324,7 @@ static int sd_init(struct gspca_dev *gspca_dev)
}
/* -- start the camera -- */
-static void sd_start(struct gspca_dev *gspca_dev)
+static int sd_start(struct gspca_dev *gspca_dev)
{
int ret, value;
@@ -374,9 +374,10 @@ static void sd_start(struct gspca_dev *gspca_dev)
set_par(gspca_dev, 0x01000000);
set_par(gspca_dev, 0x01000000);
PDEBUG(D_STREAM, "camera started alt: 0x%02x", gspca_dev->alt);
- return;
+ return 0;
out:
PDEBUG(D_ERR|D_STREAM, "camera start err %d", ret);
+ return ret;
}
static void sd_stopN(struct gspca_dev *gspca_dev)
diff --git a/linux/drivers/media/video/gspca/sunplus.c b/linux/drivers/media/video/gspca/sunplus.c
index e8e5c47ba..3a972ff1c 100644
--- a/linux/drivers/media/video/gspca/sunplus.c
+++ b/linux/drivers/media/video/gspca/sunplus.c
@@ -992,7 +992,7 @@ static int sd_init(struct gspca_dev *gspca_dev)
return 0;
}
-static void sd_start(struct gspca_dev *gspca_dev)
+static int sd_start(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
struct usb_device *dev = gspca_dev->dev;
@@ -1073,6 +1073,7 @@ static void sd_start(struct gspca_dev *gspca_dev)
break;
}
sp5xx_initContBrigHueRegisters(gspca_dev);
+ return 0;
}
static void sd_stopN(struct gspca_dev *gspca_dev)
diff --git a/linux/drivers/media/video/gspca/t613.c b/linux/drivers/media/video/gspca/t613.c
index c7673661a..06f3b8718 100644
--- a/linux/drivers/media/video/gspca/t613.c
+++ b/linux/drivers/media/video/gspca/t613.c
@@ -868,7 +868,7 @@ static int sd_getlowlight(struct gspca_dev *gspca_dev, __s32 *val)
return 0;
}
-static void sd_start(struct gspca_dev *gspca_dev)
+static int sd_start(struct gspca_dev *gspca_dev)
{
int mode;
@@ -921,6 +921,7 @@ static void sd_start(struct gspca_dev *gspca_dev)
seteffect(gspca_dev);
setflip(gspca_dev);
#endif
+ return 0;
}
static void sd_pkt_scan(struct gspca_dev *gspca_dev,
@@ -938,7 +939,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
return;
}
- if (data[len - 1] == 0xff && data[len] == 0xd9) {
+ if (data[len - 2] == 0xff && data[len - 1] == 0xd9) {
/* Just in case, i have seen packets with the marker,
* other's do not include it... */
data += 2;
diff --git a/linux/drivers/media/video/gspca/tv8532.c b/linux/drivers/media/video/gspca/tv8532.c
index 01de48d4e..8204dabb1 100644
--- a/linux/drivers/media/video/gspca/tv8532.c
+++ b/linux/drivers/media/video/gspca/tv8532.c
@@ -390,7 +390,7 @@ static void setbrightness(struct gspca_dev *gspca_dev)
}
/* -- start the camera -- */
-static void sd_start(struct gspca_dev *gspca_dev)
+static int sd_start(struct gspca_dev *gspca_dev)
{
reg_w_1(gspca_dev, TV8532_AD_SLOPE, 0x32);
reg_w_1(gspca_dev, TV8532_AD_BITCTRL, 0x00);
@@ -443,6 +443,7 @@ static void sd_start(struct gspca_dev *gspca_dev)
/************************************************/
tv_8532_PollReg(gspca_dev);
reg_w_1(gspca_dev, TV8532_UDP_UPDATE, 0x00); /* 0x31 */
+ return 0;
}
static void sd_stopN(struct gspca_dev *gspca_dev)
diff --git a/linux/drivers/media/video/gspca/vc032x.c b/linux/drivers/media/video/gspca/vc032x.c
index cfb66c257..95531138e 100644
--- a/linux/drivers/media/video/gspca/vc032x.c
+++ b/linux/drivers/media/video/gspca/vc032x.c
@@ -1698,7 +1698,7 @@ static void setlightfreq(struct gspca_dev *gspca_dev)
usb_exchange(gspca_dev, ov7660_freq_tb[sd->lightfreq]);
}
-static void sd_start(struct gspca_dev *gspca_dev)
+static int sd_start(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
const __u8 *GammaT = NULL;
@@ -1782,7 +1782,7 @@ static void sd_start(struct gspca_dev *gspca_dev)
break;
default:
PDEBUG(D_PROBE, "Damned !! no sensor found Bye");
- return;
+ return -EMEDIUMTYPE;
}
if (GammaT && MatrixT) {
put_tab_to_reg(gspca_dev, GammaT, 17, 0xb84a);
@@ -1818,6 +1818,7 @@ static void sd_start(struct gspca_dev *gspca_dev)
setautogain(gspca_dev);
setlightfreq(gspca_dev);
}
+ return 0;
}
static void sd_stopN(struct gspca_dev *gspca_dev)
diff --git a/linux/drivers/media/video/gspca/zc3xx.c b/linux/drivers/media/video/gspca/zc3xx.c
index 3153bd4cd..0b1871cec 100644
--- a/linux/drivers/media/video/gspca/zc3xx.c
+++ b/linux/drivers/media/video/gspca/zc3xx.c
@@ -7183,7 +7183,7 @@ static int sd_init(struct gspca_dev *gspca_dev)
return 0;
}
-static void sd_start(struct gspca_dev *gspca_dev)
+static int sd_start(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
struct usb_device *dev = gspca_dev->dev;
@@ -7336,6 +7336,7 @@ static void sd_start(struct gspca_dev *gspca_dev)
reg_w(dev, 0x02, 0x0008);
break;
}
+ return 0;
}
static void sd_stop0(struct gspca_dev *gspca_dev)
diff --git a/linux/drivers/media/video/ir-kbd-i2c.c b/linux/drivers/media/video/ir-kbd-i2c.c
index 73fcfddce..038a5c04b 100644
--- a/linux/drivers/media/video/ir-kbd-i2c.c
+++ b/linux/drivers/media/video/ir-kbd-i2c.c
@@ -66,7 +66,7 @@ static int get_key_haup_common(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw,
int size, int offset)
{
unsigned char buf[6];
- int start, range, toggle, dev, code;
+ int start, range, toggle, dev, code, ircode;
/* poll IR chip */
if (size != i2c_master_recv(&ir->c,buf,size))
@@ -86,6 +86,24 @@ static int get_key_haup_common(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw,
if (!start)
/* no key pressed */
return 0;
+ /*
+ * Hauppauge remotes (black/silver) always use
+ * specific device ids. If we do not filter the
+ * device ids then messages destined for devices
+ * such as TVs (id=0) will get through causing
+ * mis-fired events.
+ *
+ * We also filter out invalid key presses which
+ * produce annoying debug log entries.
+ */
+ ircode= (start << 12) | (toggle << 11) | (dev << 6) | code;
+ if ((ircode & 0x1fff)==0x1fff)
+ /* invalid key press */
+ return 0;
+
+ if (dev!=0x1e && dev!=0x1f)
+ /* not a hauppauge remote */
+ return 0;
if (!range)
code += 64;
@@ -95,7 +113,7 @@ static int get_key_haup_common(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw,
/* return key */
*ir_key = code;
- *ir_raw = (start << 12) | (toggle << 11) | (dev << 6) | code;
+ *ir_raw = ircode;
return 1;
}
diff --git a/linux/drivers/media/video/ivtv/ivtv-gpio.c b/linux/drivers/media/video/ivtv/ivtv-gpio.c
index bc22905ea..74a44844c 100644
--- a/linux/drivers/media/video/ivtv/ivtv-gpio.c
+++ b/linux/drivers/media/video/ivtv/ivtv-gpio.c
@@ -124,7 +124,7 @@ void ivtv_reset_ir_gpio(struct ivtv *itv)
}
/* Xceive tuner reset function */
-int ivtv_reset_tuner_gpio(void *dev, int cmd, int value)
+int ivtv_reset_tuner_gpio(void *dev, int component, int cmd, int value)
{
struct i2c_algo_bit_data *algo = dev;
struct ivtv *itv = algo->data;
diff --git a/linux/drivers/media/video/ivtv/ivtv-gpio.h b/linux/drivers/media/video/ivtv/ivtv-gpio.h
index 964a265d9..48b629161 100644
--- a/linux/drivers/media/video/ivtv/ivtv-gpio.h
+++ b/linux/drivers/media/video/ivtv/ivtv-gpio.h
@@ -24,7 +24,7 @@
/* GPIO stuff */
void ivtv_gpio_init(struct ivtv *itv);
void ivtv_reset_ir_gpio(struct ivtv *itv);
-int ivtv_reset_tuner_gpio(void *dev, int cmd, int value);
+int ivtv_reset_tuner_gpio(void *dev, int component, int cmd, int value);
int ivtv_gpio(struct ivtv *itv, unsigned int command, void *arg);
#endif
diff --git a/linux/drivers/media/video/pxa_camera.c b/linux/drivers/media/video/pxa_camera.c
index ee2fb7baa..00aa421fe 100644
--- a/linux/drivers/media/video/pxa_camera.c
+++ b/linux/drivers/media/video/pxa_camera.c
@@ -1125,31 +1125,31 @@ static int pxa_camera_probe(struct platform_device *pdev)
pcdev->dev = &pdev->dev;
/* request dma */
- pcdev->dma_chans[0] = pxa_request_dma("CI_Y", DMA_PRIO_HIGH,
- pxa_camera_dma_irq_y, pcdev);
- if (pcdev->dma_chans[0] < 0) {
+ err = pxa_request_dma("CI_Y", DMA_PRIO_HIGH,
+ pxa_camera_dma_irq_y, pcdev);
+ if (err < 0) {
dev_err(pcdev->dev, "Can't request DMA for Y\n");
- err = -ENOMEM;
goto exit_iounmap;
}
+ pcdev->dma_chans[0] = err;
dev_dbg(pcdev->dev, "got DMA channel %d\n", pcdev->dma_chans[0]);
- pcdev->dma_chans[1] = pxa_request_dma("CI_U", DMA_PRIO_HIGH,
- pxa_camera_dma_irq_u, pcdev);
- if (pcdev->dma_chans[1] < 0) {
+ err = pxa_request_dma("CI_U", DMA_PRIO_HIGH,
+ pxa_camera_dma_irq_u, pcdev);
+ if (err < 0) {
dev_err(pcdev->dev, "Can't request DMA for U\n");
- err = -ENOMEM;
goto exit_free_dma_y;
}
+ pcdev->dma_chans[1] = err;
dev_dbg(pcdev->dev, "got DMA channel (U) %d\n", pcdev->dma_chans[1]);
- pcdev->dma_chans[2] = pxa_request_dma("CI_V", DMA_PRIO_HIGH,
- pxa_camera_dma_irq_v, pcdev);
- if (pcdev->dma_chans[0] < 0) {
+ err = pxa_request_dma("CI_V", DMA_PRIO_HIGH,
+ pxa_camera_dma_irq_v, pcdev);
+ if (err < 0) {
dev_err(pcdev->dev, "Can't request DMA for V\n");
- err = -ENOMEM;
goto exit_free_dma_u;
}
+ pcdev->dma_chans[2] = err;
dev_dbg(pcdev->dev, "got DMA channel (V) %d\n", pcdev->dma_chans[2]);
DRCMR68 = pcdev->dma_chans[0] | DRCMR_MAPVLD;
diff --git a/linux/drivers/media/video/saa7134/saa7134-cards.c b/linux/drivers/media/video/saa7134/saa7134-cards.c
index dfe4f7169..135250cf5 100644
--- a/linux/drivers/media/video/saa7134/saa7134-cards.c
+++ b/linux/drivers/media/video/saa7134/saa7134-cards.c
@@ -3299,6 +3299,7 @@ struct saa7134_board saa7134_boards[] = {
},
[SAA7134_BOARD_HAUPPAUGE_HVR1110] = {
/* Thomas Genty <tomlohave@gmail.com> */
+ /* David Bentham <db260179@hotmail.com> */
.name = "Hauppauge WinTV-HVR1110 DVB-T/Hybrid",
.audio_clock = 0x00187de7,
.tuner_type = TUNER_PHILIPS_TDA8290,
@@ -3307,23 +3308,26 @@ struct saa7134_board saa7134_boards[] = {
.radio_addr = ADDR_UNSET,
.tuner_config = 1,
.mpeg = SAA7134_MPEG_DVB,
+ .gpiomask = 0x0200100,
.inputs = {{
.name = name_tv,
.vmux = 1,
.amux = TV,
.tv = 1,
- },{
- .name = name_comp1,
- .vmux = 3,
- .amux = LINE2, /* FIXME: audio doesn't work on svideo/composite */
- },{
- .name = name_svideo,
- .vmux = 8,
- .amux = LINE2, /* FIXME: audio doesn't work on svideo/composite */
- }},
+ .gpio = 0x0000100,
+ }, {
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ } },
.radio = {
.name = name_radio,
- .amux = TV,
+ .amux = TV,
+ .gpio = 0x0200100,
},
},
[SAA7134_BOARD_CINERGY_HT_PCMCIA] = {
@@ -3427,6 +3431,42 @@ struct saa7134_board saa7134_boards[] = {
.amux = 0,
},
},
+ [SAA7134_BOARD_ENCORE_ENLTV_FM53] = {
+ .name = "Encore ENLTV-FM v5.3",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_TNF_5335MF,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .gpiomask = 0x7000,
+ .inputs = { {
+ .name = name_tv,
+ .vmux = 1,
+ .amux = 1,
+ .tv = 1,
+ .gpio = 0x50000,
+ }, {
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = 2,
+ .gpio = 0x2000,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = 2,
+ .gpio = 0x2000,
+ } },
+ .radio = {
+ .name = name_radio,
+ .vmux = 1,
+ .amux = 1,
+ },
+ .mute = {
+ .name = name_mute,
+ .gpio = 0xf000,
+ .amux = 0,
+ },
+ },
[SAA7134_BOARD_CINERGY_HT_PCI] = {
.name = "Terratec Cinergy HT PCI",
.audio_clock = 0x00187de7,
@@ -3670,6 +3710,40 @@ struct saa7134_board saa7134_boards[] = {
.tv = 1,
}},
},
+ [SAA7134_BOARD_AVERMEDIA_M135A] = {
+ .name = "Avermedia PCI pure analog (M135A)",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tuner_config = 2,
+ .gpiomask = 0x020200000,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ }, {
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ } },
+ .radio = {
+ .name = name_radio,
+ .amux = TV,
+ .gpio = 0x00200000,
+ },
+ .mute = {
+ .name = name_mute,
+ .amux = TV,
+ .gpio = 0x01,
+ },
+ },
[SAA7134_BOARD_BEHOLD_401] = {
/* Beholder Intl. Ltd. 2008 */
/*Dmitry Belimov <d.belimov@gmail.com> */
@@ -4448,6 +4522,95 @@ struct saa7134_board saa7134_boards[] = {
/* no DVB support for now */
/* .mpeg = SAA7134_MPEG_DVB, */
},
+ [SAA7134_BOARD_ASUSTeK_TIGER_3IN1] = {
+ .name = "Asus Tiger 3in1",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tuner_config = 2,
+ .gpiomask = 1 << 21,
+ .mpeg = SAA7134_MPEG_DVB,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ }, {
+ .name = name_comp,
+ .vmux = 0,
+ .amux = LINE2,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ } },
+ .radio = {
+ .name = name_radio,
+ .amux = TV,
+ .gpio = 0x0200000,
+ },
+ },
+ [SAA7134_BOARD_REAL_ANGEL_220] = {
+ .name = "Zogis Real Angel 220",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_TNF_5335MF,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .gpiomask = 0x801a8087,
+ .inputs = { {
+ .name = name_tv,
+ .vmux = 3,
+ .amux = LINE2,
+ .tv = 1,
+ .gpio = 0x624000,
+ }, {
+ .name = name_comp1,
+ .vmux = 1,
+ .amux = LINE1,
+ .gpio = 0x624000,
+ }, {
+ .name = name_svideo,
+ .vmux = 1,
+ .amux = LINE1,
+ .gpio = 0x624000,
+ } },
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ .gpio = 0x624001,
+ },
+ .mute = {
+ .name = name_mute,
+ .amux = TV,
+ },
+ },
+ [SAA7134_BOARD_ADS_INSTANT_HDTV_PCI] = {
+ .name = "ADS Tech Instant HDTV",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TUV1236D,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .mpeg = SAA7134_MPEG_DVB,
+ .inputs = { {
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ }, {
+ .name = name_comp,
+ .vmux = 4,
+ .amux = LINE1,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ } },
+ },
};
const unsigned int saa7134_bcount = ARRAY_SIZE(saa7134_boards);
@@ -4816,6 +4979,12 @@ struct pci_device_id saa7134_pci_tbl[] = {
},{
.vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x1461, /* Avermedia Technologies Inc */
+ .subdevice = 0xf11d,
+ .driver_data = SAA7134_BOARD_AVERMEDIA_M135A,
+ }, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
.device = PCI_DEVICE_ID_PHILIPS_SAA7130,
.subvendor = PCI_VENDOR_ID_PHILIPS,
.subdevice = 0x2004,
@@ -5196,6 +5365,12 @@ struct pci_device_id saa7134_pci_tbl[] = {
.driver_data = SAA7134_BOARD_ENCORE_ENLTV_FM,
},{
.vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7130,
+ .subvendor = 0x1a7f,
+ .subdevice = 0x2008,
+ .driver_data = SAA7134_BOARD_ENCORE_ENLTV_FM53,
+ }, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
.device = PCI_DEVICE_ID_PHILIPS_SAA7133,
.subvendor = 0x153b,
.subdevice = 0x1175,
@@ -5454,6 +5629,12 @@ struct pci_device_id saa7134_pci_tbl[] = {
.driver_data = SAA7134_BOARD_VIDEOMATE_T750,
}, {
.vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, /* SAA7135HL */
+ .subvendor = 0x1421,
+ .subdevice = 0x0380,
+ .driver_data = SAA7134_BOARD_ADS_INSTANT_HDTV_PCI,
+ }, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
.device = PCI_DEVICE_ID_PHILIPS_SAA7133,
.subvendor = 0x5169,
.subdevice = 0x1502,
@@ -5471,6 +5652,12 @@ struct pci_device_id saa7134_pci_tbl[] = {
.subdevice = 0xf636,
.driver_data = SAA7134_BOARD_AVERMEDIA_M103,
}, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x1043,
+ .subdevice = 0x4878, /* REV:1.02G */
+ .driver_data = SAA7134_BOARD_ASUSTeK_TIGER_3IN1,
+ }, {
/* --- boards without eeprom + subsystem ID --- */
.vendor = PCI_VENDOR_ID_PHILIPS,
.device = PCI_DEVICE_ID_PHILIPS_SAA7134,
@@ -5635,7 +5822,7 @@ static int saa7134_tda8290_callback(struct saa7134_dev *dev,
return 0;
}
-int saa7134_tuner_callback(void *priv, int command, int arg)
+int saa7134_tuner_callback(void *priv, int component, int command, int arg)
{
struct saa7134_dev *dev = priv;
if (dev != NULL) {
@@ -5719,6 +5906,7 @@ int saa7134_board_init1(struct saa7134_dev *dev)
case SAA7134_BOARD_AVERMEDIA_STUDIO_507:
case SAA7134_BOARD_AVERMEDIA_GO_007_FM:
case SAA7134_BOARD_AVERMEDIA_777:
+ case SAA7134_BOARD_AVERMEDIA_M135A:
/* case SAA7134_BOARD_SABRENT_SBTTVFM: */ /* not finished yet */
case SAA7134_BOARD_VIDEOMATE_TV_PVR:
case SAA7134_BOARD_VIDEOMATE_GOLD_PLUS:
@@ -5743,6 +5931,7 @@ int saa7134_board_init1(struct saa7134_dev *dev)
case SAA7134_BOARD_AVERMEDIA_A16AR:
case SAA7134_BOARD_ENCORE_ENLTV:
case SAA7134_BOARD_ENCORE_ENLTV_FM:
+ case SAA7134_BOARD_ENCORE_ENLTV_FM53:
case SAA7134_BOARD_10MOONSTVMASTER3:
case SAA7134_BOARD_BEHOLD_401:
case SAA7134_BOARD_BEHOLD_403:
@@ -5755,6 +5944,7 @@ int saa7134_board_init1(struct saa7134_dev *dev)
case SAA7134_BOARD_BEHOLD_505FM:
case SAA7134_BOARD_BEHOLD_507_9FM:
case SAA7134_BOARD_GENIUS_TVGO_A11MCE:
+ case SAA7134_BOARD_REAL_ANGEL_220:
dev->has_remote = SAA7134_REMOTE_GPIO;
break;
case SAA7134_BOARD_FLYDVBS_LR300:
@@ -6105,6 +6295,14 @@ int saa7134_board_init2(struct saa7134_dev *dev)
i2c_transfer(&dev->i2c_adap, &msg, 1);
break;
}
+ case SAA7134_BOARD_ASUSTeK_TIGER_3IN1:
+ {
+ u8 data[] = { 0x3c, 0x33, 0x60};
+ struct i2c_msg msg = {.addr = 0x0b, .flags = 0, .buf = data,
+ .len = sizeof(data)};
+ i2c_transfer(&dev->i2c_adap, &msg, 1);
+ break;
+ }
case SAA7134_BOARD_FLYDVB_TRIO:
{
u8 data[] = { 0x3c, 0x33, 0x62};
@@ -6130,6 +6328,7 @@ int saa7134_board_init2(struct saa7134_dev *dev)
i2c_transfer(&dev->i2c_adap, &msg, 1);
break;
}
+ case SAA7134_BOARD_ADS_INSTANT_HDTV_PCI:
case SAA7134_BOARD_KWORLD_ATSC110:
{
/* enable tuner */
diff --git a/linux/drivers/media/video/saa7134/saa7134-core.c b/linux/drivers/media/video/saa7134/saa7134-core.c
index ce8bcc967..c839114a0 100644
--- a/linux/drivers/media/video/saa7134/saa7134-core.c
+++ b/linux/drivers/media/video/saa7134/saa7134-core.c
@@ -760,6 +760,10 @@ static int saa7134_hw_enable2(struct saa7134_dev *dev)
irq2_mask |= SAA7134_IRQ2_INTE_GPIO18A;
}
+ if (dev->has_remote == SAA7134_REMOTE_I2C) {
+ request_module("ir-kbd-i2c");
+ }
+
saa_writel(SAA7134_IRQ1, 0);
saa_writel(SAA7134_IRQ2, irq2_mask);
diff --git a/linux/drivers/media/video/saa7134/saa7134-dvb.c b/linux/drivers/media/video/saa7134/saa7134-dvb.c
index ef8baf5fe..5222f1728 100644
--- a/linux/drivers/media/video/saa7134/saa7134-dvb.c
+++ b/linux/drivers/media/video/saa7134/saa7134-dvb.c
@@ -553,7 +553,6 @@ static int configure_tda827x_fe(struct saa7134_dev *dev,
/* ------------------------------------------------------------------ */
static struct tda827x_config tda827x_cfg_0 = {
- .tuner_callback = saa7134_tuner_callback,
.init = philips_tda827x_tuner_init,
.sleep = philips_tda827x_tuner_sleep,
.config = 0,
@@ -561,7 +560,6 @@ static struct tda827x_config tda827x_cfg_0 = {
};
static struct tda827x_config tda827x_cfg_1 = {
- .tuner_callback = saa7134_tuner_callback,
.init = philips_tda827x_tuner_init,
.sleep = philips_tda827x_tuner_sleep,
.config = 1,
@@ -569,7 +567,6 @@ static struct tda827x_config tda827x_cfg_1 = {
};
static struct tda827x_config tda827x_cfg_2 = {
- .tuner_callback = saa7134_tuner_callback,
.init = philips_tda827x_tuner_init,
.sleep = philips_tda827x_tuner_sleep,
.config = 2,
@@ -577,7 +574,6 @@ static struct tda827x_config tda827x_cfg_2 = {
};
static struct tda827x_config tda827x_cfg_2_sw42 = {
- .tuner_callback = saa7134_tuner_callback,
.init = philips_tda827x_tuner_init,
.sleep = philips_tda827x_tuner_sleep,
.config = 2,
@@ -799,6 +795,20 @@ static struct tda1004x_config twinhan_dtv_dvb_3056_config = {
.request_firmware = philips_tda1004x_request_firmware
};
+static struct tda1004x_config asus_tiger_3in1_config = {
+ .demod_address = 0x0b,
+ .invert = 1,
+ .invert_oclk = 0,
+ .xtal_freq = TDA10046_XTAL_16M,
+ .agc_config = TDA10046_AGC_TDA827X,
+ .gpio_config = TDA10046_GP11_I,
+ .if_freq = TDA10046_FREQ_045,
+ .i2c_gate = 0x4b,
+ .tuner_address = 0x61,
+ .antenna_switch = 1,
+ .request_firmware = philips_tda1004x_request_firmware
+};
+
/* ------------------------------------------------------------------
* special case: this card uses saa713x GPIO22 for the mode switch
*/
@@ -822,7 +832,6 @@ static int ads_duo_tuner_sleep(struct dvb_frontend *fe)
}
static struct tda827x_config ads_duo_cfg = {
- .tuner_callback = saa7134_tuner_callback,
.init = ads_duo_tuner_init,
.sleep = ads_duo_tuner_sleep,
.config = 0
@@ -1147,6 +1156,7 @@ static int dvb_init(struct saa7134_dev *dev)
dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61,
NULL, DVB_PLL_TDHU2);
break;
+ case SAA7134_BOARD_ADS_INSTANT_HDTV_PCI:
case SAA7134_BOARD_KWORLD_ATSC110:
dev->dvb.frontend = dvb_attach(nxt200x_attach, &kworldatsc110,
&dev->i2c_adap);
@@ -1304,6 +1314,31 @@ static int dvb_init(struct saa7134_dev *dev)
&dev->i2c_adap);
attach_xc3028 = 1;
break;
+ case SAA7134_BOARD_ASUSTeK_TIGER_3IN1:
+ if (!use_frontend) { /* terrestrial */
+ if (configure_tda827x_fe(dev, &asus_tiger_3in1_config,
+ &tda827x_cfg_2) < 0)
+ goto dettach_frontend;
+ } else { /* satellite */
+ dev->dvb.frontend = dvb_attach(tda10086_attach,
+ &flydvbs, &dev->i2c_adap);
+ if (dev->dvb.frontend) {
+ if (dvb_attach(tda826x_attach,
+ dev->dvb.frontend, 0x60,
+ &dev->i2c_adap, 0) == NULL) {
+ wprintk("%s: Asus Tiger 3in1, no "
+ "tda826x found!\n", __func__);
+ goto dettach_frontend;
+ }
+ if (dvb_attach(lnbp21_attach, dev->dvb.frontend,
+ &dev->i2c_adap, 0, 0) == NULL) {
+ wprintk("%s: Asus Tiger 3in1, no lnbp21"
+ " found!\n", __func__);
+ goto dettach_frontend;
+ }
+ }
+ }
+ break;
default:
wprintk("Huh? unknown DVB card?\n");
break;
@@ -1331,6 +1366,8 @@ static int dvb_init(struct saa7134_dev *dev)
printk(KERN_ERR "%s/dvb: frontend initialization failed\n", dev->name);
return -1;
}
+ /* define general-purpose callback pointer */
+ dev->dvb.frontend->callback = saa7134_tuner_callback;
/* register everything else */
ret = videobuf_dvb_register(&dev->dvb, THIS_MODULE, dev, &dev->pci->dev,
diff --git a/linux/drivers/media/video/saa7134/saa7134-input.c b/linux/drivers/media/video/saa7134/saa7134-input.c
index a9f6df2dd..cb93661fb 100644
--- a/linux/drivers/media/video/saa7134/saa7134-input.c
+++ b/linux/drivers/media/video/saa7134/saa7134-input.c
@@ -401,6 +401,12 @@ int saa7134_input_init1(struct saa7134_dev *dev)
saa_setb(SAA7134_GPIO_GPMODE0, 0x4);
saa_setb(SAA7134_GPIO_GPSTATUS0, 0x4);
break;
+ case SAA7134_BOARD_AVERMEDIA_M135A:
+ ir_codes = ir_codes_avermedia_m135a;
+ mask_keydown = 0x0040000;
+ mask_keycode = 0x00013f;
+ nec_gpio = 1;
+ break;
case SAA7134_BOARD_AVERMEDIA_777:
case SAA7134_BOARD_AVERMEDIA_A16AR:
ir_codes = ir_codes_avermedia;
@@ -509,6 +515,12 @@ int saa7134_input_init1(struct saa7134_dev *dev)
mask_keyup = 0x040000;
polling = 50; // ms
break;
+ case SAA7134_BOARD_ENCORE_ENLTV_FM53:
+ ir_codes = ir_codes_encore_enltv_fm53;
+ mask_keydown = 0x0040000;
+ mask_keycode = 0x00007f;
+ nec_gpio = 1;
+ break;
case SAA7134_BOARD_10MOONSTVMASTER3:
ir_codes = ir_codes_encore_enltv;
mask_keycode = 0x5f80000;
@@ -521,6 +533,12 @@ int saa7134_input_init1(struct saa7134_dev *dev)
mask_keydown = 0xf00000;
polling = 50; /* ms */
break;
+ case SAA7134_BOARD_REAL_ANGEL_220:
+ ir_codes = ir_codes_real_audio_220_32_keys;
+ mask_keycode = 0x3f00;
+ mask_keyup = 0x4000;
+ polling = 50; /* ms */
+ break;
}
if (NULL == ir_codes) {
printk("%s: Oops: IR config error [card=%d]\n",
diff --git a/linux/drivers/media/video/saa7134/saa7134.h b/linux/drivers/media/video/saa7134/saa7134.h
index 50319493d..b704b5f40 100644
--- a/linux/drivers/media/video/saa7134/saa7134.h
+++ b/linux/drivers/media/video/saa7134/saa7134.h
@@ -270,6 +270,11 @@ struct saa7134_format {
#define SAA7134_BOARD_BEHOLD_M6_EXTRA 144
#define SAA7134_BOARD_AVERMEDIA_M103 145
#define SAA7134_BOARD_ASUSTeK_P7131_ANALOG 146
+#define SAA7134_BOARD_ASUSTeK_TIGER_3IN1 147
+#define SAA7134_BOARD_ENCORE_ENLTV_FM53 148
+#define SAA7134_BOARD_AVERMEDIA_M135A 149
+#define SAA7134_BOARD_REAL_ANGEL_220 150
+#define SAA7134_BOARD_ADS_INSTANT_HDTV_PCI 151
#define SAA7134_MAXBOARDS 8
#define SAA7134_INPUT_MAX 8
@@ -657,7 +662,7 @@ extern struct pci_device_id __devinitdata saa7134_pci_tbl[];
extern int saa7134_board_init1(struct saa7134_dev *dev);
extern int saa7134_board_init2(struct saa7134_dev *dev);
-int saa7134_tuner_callback(void *priv, int command, int arg);
+int saa7134_tuner_callback(void *priv, int component, int command, int arg);
/* ----------------------------------------------------------- */
diff --git a/linux/drivers/media/video/stk-webcam.c b/linux/drivers/media/video/stk-webcam.c
index 055e92549..8c1817a47 100644
--- a/linux/drivers/media/video/stk-webcam.c
+++ b/linux/drivers/media/video/stk-webcam.c
@@ -66,22 +66,6 @@ static struct usb_device_id stkwebcam_table[] = {
};
MODULE_DEVICE_TABLE(usb, stkwebcam_table);
-static void stk_camera_cleanup(struct kref *kref)
-{
- struct stk_camera *dev = to_stk_camera(kref);
-
- STK_INFO("Syntek USB2.0 Camera release resources"
- " video device /dev/video%d\n", dev->vdev.minor);
- video_unregister_device(&dev->vdev);
- video_set_drvdata(&dev->vdev, NULL);
-
- if (dev->sio_bufs != NULL || dev->isobufs != NULL)
- STK_ERROR("We are leaking memory\n");
- usb_put_intf(dev->interface);
- kfree(dev);
-}
-
-
/*
* Basic stuff
*/
@@ -695,8 +679,7 @@ static int v4l_stk_open(struct inode *inode, struct file *fp)
unlock_kernel();
return -ENXIO;
}
- fp->private_data = vdev;
- kref_get(&dev->kref);
+ fp->private_data = dev;
usb_autopm_get_interface(dev->interface);
unlock_kernel();
@@ -705,23 +688,10 @@ static int v4l_stk_open(struct inode *inode, struct file *fp)
static int v4l_stk_release(struct inode *inode, struct file *fp)
{
- struct stk_camera *dev;
- struct video_device *vdev;
-
- vdev = video_devdata(fp);
- if (vdev == NULL) {
- STK_ERROR("v4l_release called w/o video devdata\n");
- return -EFAULT;
- }
- dev = vdev_to_camera(vdev);
- if (dev == NULL) {
- STK_ERROR("v4l_release called on removed device\n");
- return -ENODEV;
- }
+ struct stk_camera *dev = fp->private_data;
if (dev->owner != fp) {
usb_autopm_put_interface(dev->interface);
- kref_put(&dev->kref, stk_camera_cleanup);
return 0;
}
@@ -732,7 +702,6 @@ static int v4l_stk_release(struct inode *inode, struct file *fp)
dev->owner = NULL;
usb_autopm_put_interface(dev->interface);
- kref_put(&dev->kref, stk_camera_cleanup);
return 0;
}
@@ -743,14 +712,8 @@ static ssize_t v4l_stk_read(struct file *fp, char __user *buf,
int i;
int ret;
unsigned long flags;
- struct stk_camera *dev;
- struct video_device *vdev;
struct stk_sio_buffer *sbuf;
-
- vdev = video_devdata(fp);
- if (vdev == NULL)
- return -EFAULT;
- dev = vdev_to_camera(vdev);
+ struct stk_camera *dev = fp->private_data;
if (dev == NULL)
return -EIO;
@@ -809,15 +772,8 @@ static ssize_t v4l_stk_read(struct file *fp, char __user *buf,
static unsigned int v4l_stk_poll(struct file *fp, poll_table *wait)
{
- struct stk_camera *dev;
- struct video_device *vdev;
-
- vdev = video_devdata(fp);
-
- if (vdev == NULL)
- return -EFAULT;
+ struct stk_camera *dev = fp->private_data;
- dev = vdev_to_camera(vdev);
if (dev == NULL)
return -ENODEV;
@@ -855,16 +811,12 @@ static int v4l_stk_mmap(struct file *fp, struct vm_area_struct *vma)
unsigned int i;
int ret;
unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
- struct stk_camera *dev;
- struct video_device *vdev;
+ struct stk_camera *dev = fp->private_data;
struct stk_sio_buffer *sbuf = NULL;
if (!(vma->vm_flags & VM_WRITE) || !(vma->vm_flags & VM_SHARED))
return -EINVAL;
- vdev = video_devdata(fp);
- dev = vdev_to_camera(vdev);
-
for (i = 0; i < dev->n_sbufs; i++) {
if (dev->sio_bufs[i].v4lbuf.m.offset == offset) {
sbuf = dev->sio_bufs + i;
@@ -1360,6 +1312,12 @@ static const struct v4l2_ioctl_ops v4l_stk_ioctl_ops = {
static void stk_v4l_dev_release(struct video_device *vd)
{
+ struct stk_camera *dev = vdev_to_camera(vd);
+
+ if (dev->sio_bufs != NULL || dev->isobufs != NULL)
+ STK_ERROR("We are leaking memory\n");
+ usb_put_intf(dev->interface);
+ kfree(dev);
}
static struct video_device stk_v4l_data = {
@@ -1380,7 +1338,6 @@ static int stk_register_video_device(struct stk_camera *dev)
dev->vdev = stk_v4l_data;
dev->vdev.debug = debug;
dev->vdev.parent = &dev->interface->dev;
- video_set_drvdata(&dev->vdev, dev);
err = video_register_device(&dev->vdev, VFL_TYPE_GRABBER, -1);
if (err)
STK_ERROR("v4l registration failed\n");
@@ -1397,7 +1354,7 @@ static int stk_camera_probe(struct usb_interface *interface,
const struct usb_device_id *id)
{
int i;
- int err;
+ int err = 0;
struct stk_camera *dev = NULL;
struct usb_device *udev = interface_to_usbdev(interface);
@@ -1410,7 +1367,6 @@ static int stk_camera_probe(struct usb_interface *interface,
return -ENOMEM;
}
- kref_init(&dev->kref);
spin_lock_init(&dev->spinlock);
init_waitqueue_head(&dev->wait_frame);
@@ -1443,8 +1399,8 @@ static int stk_camera_probe(struct usb_interface *interface,
}
if (!dev->isoc_ep) {
STK_ERROR("Could not find isoc-in endpoint");
- kref_put(&dev->kref, stk_camera_cleanup);
- return -ENODEV;
+ err = -ENODEV;
+ goto error;
}
dev->vsettings.brightness = 0x7fff;
dev->vsettings.palette = V4L2_PIX_FMT_RGB565;
@@ -1458,14 +1414,17 @@ static int stk_camera_probe(struct usb_interface *interface,
err = stk_register_video_device(dev);
if (err) {
- kref_put(&dev->kref, stk_camera_cleanup);
- return err;
+ goto error;
}
stk_create_sysfs_files(&dev->vdev);
usb_autopm_enable(dev->interface);
return 0;
+
+error:
+ kfree(dev);
+ return err;
}
static void stk_camera_disconnect(struct usb_interface *interface)
@@ -1478,7 +1437,10 @@ static void stk_camera_disconnect(struct usb_interface *interface)
wake_up_interruptible(&dev->wait_frame);
stk_remove_sysfs_files(&dev->vdev);
- kref_put(&dev->kref, stk_camera_cleanup);
+ STK_INFO("Syntek USB2.0 Camera release resources"
+ "video device /dev/video%d\n", dev->vdev.minor);
+
+ video_unregister_device(&dev->vdev);
}
#ifdef CONFIG_PM
diff --git a/linux/drivers/media/video/stk-webcam.h b/linux/drivers/media/video/stk-webcam.h
index df4dfefc5..084a85bdd 100644
--- a/linux/drivers/media/video/stk-webcam.h
+++ b/linux/drivers/media/video/stk-webcam.h
@@ -99,7 +99,6 @@ struct stk_camera {
u8 isoc_ep;
- struct kref kref;
/* Not sure if this is right */
atomic_t urbs_used;
@@ -121,7 +120,6 @@ struct stk_camera {
unsigned sequence;
};
-#define to_stk_camera(d) container_of(d, struct stk_camera, kref)
#define vdev_to_camera(d) container_of(d, struct stk_camera, vdev)
void stk_camera_delete(struct kref *);
diff --git a/linux/drivers/media/video/tda9840.c b/linux/drivers/media/video/tda9840.c
index 72adcb450..31dde7516 100644
--- a/linux/drivers/media/video/tda9840.c
+++ b/linux/drivers/media/video/tda9840.c
@@ -72,7 +72,6 @@ static void tda9840_write(struct i2c_client *client, u8 reg, u8 val)
static int tda9840_command(struct i2c_client *client, unsigned cmd, void *arg)
{
- int result = 0;
int byte = *(int *)arg;
switch (cmd) {
@@ -177,9 +176,6 @@ static int tda9840_command(struct i2c_client *client, unsigned cmd, void *arg)
return -ENOIOCTLCMD;
}
- if (result)
- return -EIO;
-
return 0;
}
diff --git a/linux/drivers/media/video/tuner-core.c b/linux/drivers/media/video/tuner-core.c
index d6ce6f6a5..5dd4bd660 100644
--- a/linux/drivers/media/video/tuner-core.c
+++ b/linux/drivers/media/video/tuner-core.c
@@ -93,7 +93,6 @@ struct tuner {
unsigned int type; /* chip type id */
unsigned int config;
- int (*tuner_callback) (void *dev, int command, int arg);
const char *name;
};
@@ -347,7 +346,7 @@ static struct xc5000_config xc5000_cfg;
static void set_type(struct i2c_client *c, unsigned int type,
unsigned int new_mode_mask, unsigned int new_config,
- int (*tuner_callback) (void *dev, int command,int arg))
+ int (*tuner_callback) (void *dev, int component, int cmd, int arg))
{
struct tuner *t = i2c_get_clientdata(c);
struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
@@ -363,7 +362,7 @@ static void set_type(struct i2c_client *c, unsigned int type,
t->config = new_config;
if (tuner_callback != NULL) {
tuner_dbg("defining GPIO callback\n");
- t->tuner_callback = tuner_callback;
+ t->fe.callback = tuner_callback;
}
if (t->mode == T_UNINITIALIZED) {
@@ -386,7 +385,6 @@ static void set_type(struct i2c_client *c, unsigned int type,
{
struct tda829x_config cfg = {
.lna_cfg = t->config,
- .tuner_callback = t->tuner_callback,
};
if (!dvb_attach(tda829x_attach, &t->fe, t->i2c->adapter,
t->i2c->addr, &cfg))
@@ -434,7 +432,6 @@ static void set_type(struct i2c_client *c, unsigned int type,
struct xc2028_config cfg = {
.i2c_adap = t->i2c->adapter,
.i2c_addr = t->i2c->addr,
- .callback = t->tuner_callback,
};
if (!dvb_attach(xc2028_attach, &t->fe, &cfg))
goto attach_failed;
@@ -451,7 +448,6 @@ static void set_type(struct i2c_client *c, unsigned int type,
xc5000_cfg.i2c_address = t->i2c->addr;
xc5000_cfg.if_khz = 5380;
- xc5000_cfg.tuner_callback = t->tuner_callback;
if (!dvb_attach(xc5000_attach,
&t->fe, t->i2c->adapter, &xc5000_cfg))
goto attach_failed;
@@ -1234,7 +1230,7 @@ register_client:
} else {
t->mode = V4L2_TUNER_DIGITAL_TV;
}
- set_type(client, t->type, t->mode_mask, t->config, t->tuner_callback);
+ set_type(client, t->type, t->mode_mask, t->config, t->fe.callback);
list_add_tail(&t->list, &tuner_list);
return 0;
}
diff --git a/linux/drivers/media/video/uvc/uvc_ctrl.c b/linux/drivers/media/video/uvc/uvc_ctrl.c
index 6ef3e5297..f16aafe9c 100644
--- a/linux/drivers/media/video/uvc/uvc_ctrl.c
+++ b/linux/drivers/media/video/uvc/uvc_ctrl.c
@@ -83,6 +83,22 @@ static struct uvc_control_info uvc_ctrls[] = {
},
{
.entity = UVC_GUID_UVC_PROCESSING,
+ .selector = PU_WHITE_BALANCE_TEMPERATURE_CONTROL,
+ .index = 6,
+ .size = 2,
+ .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE
+ | UVC_CONTROL_RESTORE | UVC_CONTROL_AUTO_UPDATE,
+ },
+ {
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = PU_WHITE_BALANCE_COMPONENT_CONTROL,
+ .index = 7,
+ .size = 4,
+ .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE
+ | UVC_CONTROL_RESTORE | UVC_CONTROL_AUTO_UPDATE,
+ },
+ {
+ .entity = UVC_GUID_UVC_PROCESSING,
.selector = PU_BACKLIGHT_COMPENSATION_CONTROL,
.index = 8,
.size = 2,
@@ -114,6 +130,60 @@ static struct uvc_control_info uvc_ctrls[] = {
| UVC_CONTROL_GET_DEF | UVC_CONTROL_RESTORE,
},
{
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL,
+ .index = 12,
+ .size = 1,
+ .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR
+ | UVC_CONTROL_GET_DEF | UVC_CONTROL_RESTORE,
+ },
+ {
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL,
+ .index = 13,
+ .size = 1,
+ .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR
+ | UVC_CONTROL_GET_DEF | UVC_CONTROL_RESTORE,
+ },
+ {
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = PU_DIGITAL_MULTIPLIER_CONTROL,
+ .index = 14,
+ .size = 2,
+ .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE
+ | UVC_CONTROL_RESTORE,
+ },
+ {
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = PU_DIGITAL_MULTIPLIER_LIMIT_CONTROL,
+ .index = 15,
+ .size = 2,
+ .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE
+ | UVC_CONTROL_RESTORE,
+ },
+ {
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = PU_ANALOG_VIDEO_STANDARD_CONTROL,
+ .index = 16,
+ .size = 1,
+ .flags = UVC_CONTROL_GET_CUR,
+ },
+ {
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = PU_ANALOG_LOCK_STATUS_CONTROL,
+ .index = 17,
+ .size = 1,
+ .flags = UVC_CONTROL_GET_CUR,
+ },
+ {
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = CT_SCANNING_MODE_CONTROL,
+ .index = 0,
+ .size = 1,
+ .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR
+ | UVC_CONTROL_RESTORE,
+ },
+ {
.entity = UVC_GUID_UVC_CAMERA,
.selector = CT_AE_MODE_CONTROL,
.index = 1,
@@ -140,6 +210,14 @@ static struct uvc_control_info uvc_ctrls[] = {
},
{
.entity = UVC_GUID_UVC_CAMERA,
+ .selector = CT_EXPOSURE_TIME_RELATIVE_CONTROL,
+ .index = 4,
+ .size = 1,
+ .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR
+ | UVC_CONTROL_RESTORE,
+ },
+ {
+ .entity = UVC_GUID_UVC_CAMERA,
.selector = CT_FOCUS_ABSOLUTE_CONTROL,
.index = 5,
.size = 2,
@@ -148,42 +226,90 @@ static struct uvc_control_info uvc_ctrls[] = {
},
{
.entity = UVC_GUID_UVC_CAMERA,
- .selector = CT_FOCUS_AUTO_CONTROL,
- .index = 17,
- .size = 1,
- .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR
- | UVC_CONTROL_GET_DEF | UVC_CONTROL_RESTORE,
+ .selector = CT_FOCUS_RELATIVE_CONTROL,
+ .index = 6,
+ .size = 2,
+ .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE
+ | UVC_CONTROL_AUTO_UPDATE,
},
{
- .entity = UVC_GUID_UVC_PROCESSING,
- .selector = PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL,
- .index = 12,
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = CT_IRIS_ABSOLUTE_CONTROL,
+ .index = 7,
+ .size = 2,
+ .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE
+ | UVC_CONTROL_RESTORE | UVC_CONTROL_AUTO_UPDATE,
+ },
+ {
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = CT_IRIS_RELATIVE_CONTROL,
+ .index = 8,
.size = 1,
.flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR
- | UVC_CONTROL_GET_DEF | UVC_CONTROL_RESTORE,
+ | UVC_CONTROL_AUTO_UPDATE,
},
{
- .entity = UVC_GUID_UVC_PROCESSING,
- .selector = PU_WHITE_BALANCE_TEMPERATURE_CONTROL,
- .index = 6,
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = CT_ZOOM_ABSOLUTE_CONTROL,
+ .index = 9,
.size = 2,
.flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE
| UVC_CONTROL_RESTORE | UVC_CONTROL_AUTO_UPDATE,
},
{
- .entity = UVC_GUID_UVC_PROCESSING,
- .selector = PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL,
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = CT_ZOOM_RELATIVE_CONTROL,
+ .index = 10,
+ .size = 3,
+ .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE
+ | UVC_CONTROL_AUTO_UPDATE,
+ },
+ {
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = CT_PANTILT_ABSOLUTE_CONTROL,
+ .index = 11,
+ .size = 8,
+ .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE
+ | UVC_CONTROL_RESTORE | UVC_CONTROL_AUTO_UPDATE,
+ },
+ {
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = CT_PANTILT_RELATIVE_CONTROL,
+ .index = 12,
+ .size = 4,
+ .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE
+ | UVC_CONTROL_AUTO_UPDATE,
+ },
+ {
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = CT_ROLL_ABSOLUTE_CONTROL,
.index = 13,
+ .size = 2,
+ .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE
+ | UVC_CONTROL_RESTORE | UVC_CONTROL_AUTO_UPDATE,
+ },
+ {
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = CT_ROLL_RELATIVE_CONTROL,
+ .index = 14,
+ .size = 2,
+ .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE
+ | UVC_CONTROL_AUTO_UPDATE,
+ },
+ {
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = CT_FOCUS_AUTO_CONTROL,
+ .index = 17,
.size = 1,
.flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR
| UVC_CONTROL_GET_DEF | UVC_CONTROL_RESTORE,
},
{
- .entity = UVC_GUID_UVC_PROCESSING,
- .selector = PU_WHITE_BALANCE_COMPONENT_CONTROL,
- .index = 7,
- .size = 4,
- .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = CT_PRIVACY_CONTROL,
+ .index = 18,
+ .size = 1,
+ .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR
| UVC_CONTROL_RESTORE | UVC_CONTROL_AUTO_UPDATE,
},
};
@@ -592,7 +718,7 @@ int uvc_query_v4l2_ctrl(struct uvc_video_device *video,
if (ctrl == NULL)
return -EINVAL;
- data = kmalloc(8, GFP_KERNEL);
+ data = kmalloc(ctrl->info->size, GFP_KERNEL);
if (data == NULL)
return -ENOMEM;
@@ -711,7 +837,17 @@ static int uvc_ctrl_commit_entity(struct uvc_device *dev,
for (i = 0; i < entity->ncontrols; ++i) {
ctrl = &entity->controls[i];
- if (ctrl->info == NULL || !ctrl->dirty)
+ if (ctrl->info == NULL)
+ continue;
+
+ /* Reset the loaded flag for auto-update controls that were
+ * marked as loaded in uvc_ctrl_get/uvc_ctrl_set to prevent
+ * uvc_ctrl_get from using the cached value.
+ */
+ if (ctrl->info->flags & UVC_CONTROL_AUTO_UPDATE)
+ ctrl->loaded = 0;
+
+ if (!ctrl->dirty)
continue;
if (!rollback)
@@ -727,9 +863,6 @@ static int uvc_ctrl_commit_entity(struct uvc_device *dev,
uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP),
ctrl->info->size);
- if ((ctrl->info->flags & UVC_CONTROL_GET_CUR) == 0)
- ctrl->loaded = 0;
-
ctrl->dirty = 0;
if (ret < 0)
@@ -787,8 +920,7 @@ int uvc_ctrl_get(struct uvc_video_device *video,
if (ret < 0)
return ret;
- if ((ctrl->info->flags & UVC_CONTROL_AUTO_UPDATE) == 0)
- ctrl->loaded = 1;
+ ctrl->loaded = 1;
}
xctrl->value = uvc_get_le_value(
@@ -839,8 +971,7 @@ int uvc_ctrl_set(struct uvc_video_device *video,
return ret;
}
- if ((ctrl->info->flags & UVC_CONTROL_AUTO_UPDATE) == 0)
- ctrl->loaded = 1;
+ ctrl->loaded = 1;
}
if (!ctrl->dirty) {
diff --git a/linux/drivers/media/video/uvc/uvc_driver.c b/linux/drivers/media/video/uvc/uvc_driver.c
index b2482502a..5364417de 100644
--- a/linux/drivers/media/video/uvc/uvc_driver.c
+++ b/linux/drivers/media/video/uvc/uvc_driver.c
@@ -1941,6 +1941,15 @@ static struct usb_device_id uvc_ids[] = {
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_PROBE_MINMAX },
+ /* Advent 4211 - Bison Electronics */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x5986,
+ .idProduct = 0x0203,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_PROBE_MINMAX },
/* Bison Electronics */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
diff --git a/linux/drivers/media/video/uvc/uvc_status.c b/linux/drivers/media/video/uvc/uvc_status.c
index 75e678ac5..5d60b264d 100644
--- a/linux/drivers/media/video/uvc/uvc_status.c
+++ b/linux/drivers/media/video/uvc/uvc_status.c
@@ -177,9 +177,15 @@ int uvc_status_init(struct uvc_device *dev)
uvc_input_init(dev);
+ dev->status = kzalloc(UVC_MAX_STATUS_SIZE, GFP_KERNEL);
+ if (dev->status == NULL)
+ return -ENOMEM;
+
dev->int_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (dev->int_urb == NULL)
+ if (dev->int_urb == NULL) {
+ kfree(dev->status);
return -ENOMEM;
+ }
pipe = usb_rcvintpipe(dev->udev, ep->desc.bEndpointAddress);
@@ -192,7 +198,7 @@ int uvc_status_init(struct uvc_device *dev)
interval = fls(interval) - 1;
usb_fill_int_urb(dev->int_urb, dev->udev, pipe,
- dev->status, sizeof dev->status, uvc_status_complete,
+ dev->status, UVC_MAX_STATUS_SIZE, uvc_status_complete,
dev, interval);
return usb_submit_urb(dev->int_urb, GFP_KERNEL);
@@ -202,6 +208,7 @@ void uvc_status_cleanup(struct uvc_device *dev)
{
usb_kill_urb(dev->int_urb);
usb_free_urb(dev->int_urb);
+ kfree(dev->status);
uvc_input_cleanup(dev);
}
diff --git a/linux/drivers/media/video/uvc/uvc_v4l2.c b/linux/drivers/media/video/uvc/uvc_v4l2.c
index d4758c8e1..78e4c4e09 100644
--- a/linux/drivers/media/video/uvc/uvc_v4l2.c
+++ b/linux/drivers/media/video/uvc/uvc_v4l2.c
@@ -842,10 +842,6 @@ static int uvc_v4l2_do_ioctl(struct inode *inode, struct file *file,
if (ret < 0)
return ret;
- if (!(video->streaming->cur_format->flags &
- UVC_FMT_FLAG_COMPRESSED))
- video->queue.flags |= UVC_QUEUE_DROP_INCOMPLETE;
-
rb->count = ret;
ret = 0;
break;
diff --git a/linux/drivers/media/video/uvc/uvc_video.c b/linux/drivers/media/video/uvc/uvc_video.c
index 593aebffe..b7bb23820 100644
--- a/linux/drivers/media/video/uvc/uvc_video.c
+++ b/linux/drivers/media/video/uvc/uvc_video.c
@@ -971,6 +971,11 @@ int uvc_video_enable(struct uvc_video_device *video, int enable)
return 0;
}
+ if (video->streaming->cur_format->flags & UVC_FMT_FLAG_COMPRESSED)
+ video->queue.flags &= ~UVC_QUEUE_DROP_INCOMPLETE;
+ else
+ video->queue.flags |= UVC_QUEUE_DROP_INCOMPLETE;
+
if ((ret = uvc_queue_enable(&video->queue, 1)) < 0)
return ret;
diff --git a/linux/drivers/media/video/uvc/uvcvideo.h b/linux/drivers/media/video/uvc/uvcvideo.h
index bafe3406e..9a6bc1aaf 100644
--- a/linux/drivers/media/video/uvc/uvcvideo.h
+++ b/linux/drivers/media/video/uvc/uvcvideo.h
@@ -303,6 +303,8 @@ struct uvc_xu_control {
#define UVC_MAX_FRAME_SIZE (16*1024*1024)
/* Maximum number of video buffers. */
#define UVC_MAX_VIDEO_BUFFERS 32
+/* Maximum status buffer size in bytes of interrupt URB. */
+#define UVC_MAX_STATUS_SIZE 16
#define UVC_CTRL_CONTROL_TIMEOUT 300
#define UVC_CTRL_STREAMING_TIMEOUT 1000
@@ -634,7 +636,7 @@ struct uvc_device {
/* Status Interrupt Endpoint */
struct usb_host_endpoint *int_ep;
struct urb *int_urb;
- __u8 status[16];
+ __u8 *status;
struct input_dev *input;
/* Video Streaming interfaces */