From 353330c3ebf01c8f436b41690d95ec1548b6f364 Mon Sep 17 00:00:00 2001 From: Jose Alberto Reguero Date: Sat, 12 Sep 2009 09:51:36 -0300 Subject: V4L/DVB (12753): af9015: [1/2] fix USB TS configuration Fix wrongly configured USB2.0 TS EP. Signed-off-by: Jose Alberto Reguero Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/af9015.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/media/dvb') diff --git a/drivers/media/dvb/dvb-usb/af9015.c b/drivers/media/dvb/dvb-usb/af9015.c index 99cdd0d101ca..03838e629e06 100644 --- a/drivers/media/dvb/dvb-usb/af9015.c +++ b/drivers/media/dvb/dvb-usb/af9015.c @@ -874,7 +874,7 @@ static int af9015_read_config(struct usb_device *udev) af9015_config.dual_mode = 0; } else { af9015_properties[i].adapter[0].stream.u.bulk.buffersize - = TS_USB20_MAX_PACKET_SIZE; + = TS_USB20_FRAME_SIZE; } } @@ -1310,7 +1310,7 @@ static struct dvb_usb_device_properties af9015_properties[] = { .u = { .bulk = { .buffersize = - TS_USB20_MAX_PACKET_SIZE, + TS_USB20_FRAME_SIZE, } } }, @@ -1416,7 +1416,7 @@ static struct dvb_usb_device_properties af9015_properties[] = { .u = { .bulk = { .buffersize = - TS_USB20_MAX_PACKET_SIZE, + TS_USB20_FRAME_SIZE, } } }, @@ -1522,7 +1522,7 @@ static struct dvb_usb_device_properties af9015_properties[] = { .u = { .bulk = { .buffersize = - TS_USB20_MAX_PACKET_SIZE, + TS_USB20_FRAME_SIZE, } } }, -- cgit v1.2.2 From 9c863278097a4905a78ee0d70d417641aecfac2e Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sat, 12 Sep 2009 13:35:29 -0300 Subject: V4L/DVB (12754): af9015: [2/2] fix USB TS configuration Fix wrongly configured USB1.1 TS EP. Patch serie also increases USB URB size to reduce wakeups. USB1.1 URB size from 64 to 940 bytes USB2.0 URB size from 512 to 16356 bytes Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/af9015.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers/media/dvb') diff --git a/drivers/media/dvb/dvb-usb/af9015.c b/drivers/media/dvb/dvb-usb/af9015.c index 03838e629e06..fdfa0f54291c 100644 --- a/drivers/media/dvb/dvb-usb/af9015.c +++ b/drivers/media/dvb/dvb-usb/af9015.c @@ -369,12 +369,14 @@ static int af9015_init_endpoint(struct dvb_usb_device *d) u8 packet_size; deb_info("%s: USB speed:%d\n", __func__, d->udev->speed); + /* Windows driver uses packet count 21 for USB1.1 and 348 for USB2.0. + We use smaller - about 1/4 from the original, 5 and 87. */ #define TS_PACKET_SIZE 188 -#define TS_USB20_PACKET_COUNT 348 +#define TS_USB20_PACKET_COUNT 87 #define TS_USB20_FRAME_SIZE (TS_PACKET_SIZE*TS_USB20_PACKET_COUNT) -#define TS_USB11_PACKET_COUNT 21 +#define TS_USB11_PACKET_COUNT 5 #define TS_USB11_FRAME_SIZE (TS_PACKET_SIZE*TS_USB11_PACKET_COUNT) #define TS_USB20_MAX_PACKET_SIZE 512 @@ -868,7 +870,7 @@ static int af9015_read_config(struct usb_device *udev) /* USB1.1 set smaller buffersize and disable 2nd adapter */ if (udev->speed == USB_SPEED_FULL) { af9015_properties[i].adapter[0].stream.u.bulk.buffersize - = TS_USB11_MAX_PACKET_SIZE; + = TS_USB11_FRAME_SIZE; /* disable 2nd adapter because we don't have PID-filters */ af9015_config.dual_mode = 0; -- cgit v1.2.2 From 06565d7a4231b77a1181f5b853b076f374535649 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sat, 12 Sep 2009 20:46:30 -0300 Subject: V4L/DVB (12755): af9015: improve usb control message function slightly * define names for few values * decrease buffer len by one byte which was not used * add check for buffer overflow for sure * indentation fixes * remove useless 0 len check from memcpy It should not happen and if it happens memcpy should not do anything. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/af9015.c | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) (limited to 'drivers/media/dvb') diff --git a/drivers/media/dvb/dvb-usb/af9015.c b/drivers/media/dvb/dvb-usb/af9015.c index fdfa0f54291c..ebb9981eb89e 100644 --- a/drivers/media/dvb/dvb-usb/af9015.c +++ b/drivers/media/dvb/dvb-usb/af9015.c @@ -61,10 +61,13 @@ static struct af9013_config af9015_af9013_config[] = { static int af9015_rw_udev(struct usb_device *udev, struct req_t *req) { +#define BUF_LEN 63 +#define REQ_HDR_LEN 8 /* send header size */ +#define ACK_HDR_LEN 2 /* rece header size */ int act_len, ret; - u8 buf[64]; + u8 buf[BUF_LEN]; u8 write = 1; - u8 msg_len = 8; + u8 msg_len = REQ_HDR_LEN; static u8 seq; /* packet sequence number */ if (mutex_lock_interruptible(&af9015_usb_mutex) < 0) @@ -107,17 +110,26 @@ static int af9015_rw_udev(struct usb_device *udev, struct req_t *req) goto error_unlock; } + /* buffer overflow check */ + if ((write && (req->data_len > BUF_LEN - REQ_HDR_LEN)) || + (!write && (req->data_len > BUF_LEN - ACK_HDR_LEN))) { + err("too much data; cmd:%d len:%d", req->cmd, req->data_len); + ret = -EINVAL; + goto error_unlock; + } + /* write requested */ if (write) { - memcpy(&buf[8], req->data, req->data_len); + memcpy(&buf[REQ_HDR_LEN], 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); + &act_len, AF9015_USB_TIMEOUT); if (ret) err("bulk message failed:%d (%d/%d)", ret, msg_len, act_len); else @@ -130,10 +142,14 @@ static int af9015_rw_udev(struct usb_device *udev, struct req_t *req) 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 */ + /* write receives seq + status = 2 bytes + read receives seq + status + data = 2 + N bytes */ + msg_len = ACK_HDR_LEN; + if (!write) + msg_len += req->data_len; + ret = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, 0x81), buf, msg_len, - &act_len, AF9015_USB_TIMEOUT); + &act_len, AF9015_USB_TIMEOUT); if (ret) { err("recv bulk message failed:%d", ret); ret = -1; @@ -159,7 +175,7 @@ static int af9015_rw_udev(struct usb_device *udev, struct req_t *req) /* read request, copy returned data to return buf */ if (!write) - memcpy(req->data, &buf[2], req->data_len); + memcpy(req->data, &buf[ACK_HDR_LEN], req->data_len); error_unlock: exit_unlock: -- cgit v1.2.2 From f4e96deb4513d044653027d4921fd7592195503a Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sat, 12 Sep 2009 21:25:59 -0300 Subject: V4L/DVB (12756): af9015: fix typo in register compare And mask should be 0xff00 instead of 0xae00. Thanks to Jochen Friedrich for pointing this. Cc: Jochen Friedrich Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/af9015.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media/dvb') diff --git a/drivers/media/dvb/dvb-usb/af9015.c b/drivers/media/dvb/dvb-usb/af9015.c index ebb9981eb89e..cf042b309b46 100644 --- a/drivers/media/dvb/dvb-usb/af9015.c +++ b/drivers/media/dvb/dvb-usb/af9015.c @@ -97,7 +97,7 @@ static int af9015_rw_udev(struct usb_device *udev, struct req_t *req) break; case WRITE_MEMORY: if (((req->addr & 0xff00) == 0xff00) || - ((req->addr & 0xae00) == 0xae00)) + ((req->addr & 0xff00) == 0xae00)) buf[0] = WRITE_VIRTUAL_MEMORY; case WRITE_VIRTUAL_MEMORY: case COPY_FIRMWARE: -- cgit v1.2.2 From 74b76f213640b4ebde9134d94a8013dbfecfcd93 Mon Sep 17 00:00:00 2001 From: Olivier Grenie Date: Wed, 2 Sep 2009 08:19:19 -0300 Subject: V4L/DVB (12886): Added new Pinnacle USB devices Added Pinnacle PCTV USB devices based on PCTV 73e. Signed-off-by: Olivier Grenie Signed-off-by: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/dib0700_devices.c | 40 ++++++++++++++++++++++++++++- drivers/media/dvb/dvb-usb/dvb-usb-ids.h | 3 +++ 2 files changed, 42 insertions(+), 1 deletion(-) (limited to 'drivers/media/dvb') diff --git a/drivers/media/dvb/dvb-usb/dib0700_devices.c b/drivers/media/dvb/dvb-usb/dib0700_devices.c index d1d6f4491403..ec0dd255c0ec 100644 --- a/drivers/media/dvb/dvb-usb/dib0700_devices.c +++ b/drivers/media/dvb/dvb-usb/dib0700_devices.c @@ -1500,7 +1500,10 @@ struct usb_device_id dib0700_usb_id_table[] = { { USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_T3) }, { USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_T5) }, { USB_DEVICE(USB_VID_YUAN, USB_PID_YUAN_STK7700D) }, - { USB_DEVICE(USB_VID_YUAN, USB_PID_YUAN_STK7700D_2) }, +/* 55 */{ USB_DEVICE(USB_VID_YUAN, USB_PID_YUAN_STK7700D_2) }, + { USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV73A) }, + { USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV73ESE) }, + { USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV282E) }, { 0 } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, dib0700_usb_id_table); @@ -1762,6 +1765,41 @@ struct dvb_usb_device_properties dib0700_devices[] = { .rc_key_map_size = ARRAY_SIZE(dib0700_rc_keys), .rc_query = dib0700_rc_query + }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, + + .num_adapters = 1, + .adapter = { + { + .frontend_attach = stk7070p_frontend_attach, + .tuner_attach = dib7070p_tuner_attach, + + DIB0700_DEFAULT_STREAMING_CONFIG(0x02), + + .size_of_priv = sizeof(struct dib0700_adapter_state), + }, + }, + + .num_device_descs = 3, + .devices = { + { "Pinnacle PCTV 73A", + { &dib0700_usb_id_table[56], NULL }, + { NULL }, + }, + { "Pinnacle PCTV 73e SE", + { &dib0700_usb_id_table[57], NULL }, + { NULL }, + }, + { "Pinnacle PCTV 282e", + { &dib0700_usb_id_table[58], 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 = 2, diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h index 185a5069b10b..eec5604dcdd0 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h +++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h @@ -195,6 +195,9 @@ #define USB_PID_PINNACLE_PCTV73E 0x0237 #define USB_PID_PINNACLE_PCTV801E 0x023a #define USB_PID_PINNACLE_PCTV801E_SE 0x023b +#define USB_PID_PINNACLE_PCTV73A 0x0243 +#define USB_PID_PINNACLE_PCTV73ESE 0x0245 +#define USB_PID_PINNACLE_PCTV282E 0x0248 #define USB_PID_PCTV_200E 0x020e #define USB_PID_PCTV_400E 0x020f #define USB_PID_PCTV_450E 0x0222 -- cgit v1.2.2 From ef80196490d6533e74a49509112804aa88a21c6f Mon Sep 17 00:00:00 2001 From: Olivier Grenie Date: Tue, 15 Sep 2009 06:46:52 -0300 Subject: V4L/DVB (12887): DIB7000P: SNR calcuation forr DiB7000P Add the SNR monitoring for the dib7000p. The result is in dB. Signed-off-by: Olivier Grenie Signed-off-by: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/dib7000p.c | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) (limited to 'drivers/media/dvb') diff --git a/drivers/media/dvb/frontends/dib7000p.c b/drivers/media/dvb/frontends/dib7000p.c index fc96fbf03d6d..55ef6eeb0769 100644 --- a/drivers/media/dvb/frontends/dib7000p.c +++ b/drivers/media/dvb/frontends/dib7000p.c @@ -10,6 +10,7 @@ #include #include +#include "dvb_math.h" #include "dvb_frontend.h" #include "dib7000p.h" @@ -1217,7 +1218,37 @@ static int dib7000p_read_signal_strength(struct dvb_frontend *fe, u16 *strength) static int dib7000p_read_snr(struct dvb_frontend* fe, u16 *snr) { - *snr = 0x0000; + struct dib7000p_state *state = fe->demodulator_priv; + u16 val; + s32 signal_mant, signal_exp, noise_mant, noise_exp; + u32 result = 0; + + val = dib7000p_read_word(state, 479); + noise_mant = (val >> 4) & 0xff; + noise_exp = ((val & 0xf) << 2); + val = dib7000p_read_word(state, 480); + noise_exp += ((val >> 14) & 0x3); + if ((noise_exp & 0x20) != 0) + noise_exp -= 0x40; + + signal_mant = (val >> 6) & 0xFF; + signal_exp = (val & 0x3F); + if ((signal_exp & 0x20) != 0) + signal_exp -= 0x40; + + if (signal_mant != 0) + result = intlog10(2) * 10 * signal_exp + 10 * + intlog10(signal_mant); + else + result = intlog10(2) * 10 * signal_exp - 100; + + if (noise_mant != 0) + result -= intlog10(2) * 10 * noise_exp + 10 * + intlog10(noise_mant); + else + result -= intlog10(2) * 10 * noise_exp - 100; + + *snr = result / ((1 << 24) / 10); return 0; } -- cgit v1.2.2 From d300bd691464ffb87342e375793ac2418e66c3f3 Mon Sep 17 00:00:00 2001 From: Olivier Grenie Date: Tue, 15 Sep 2009 06:55:35 -0300 Subject: V4L/DVB (12888): STK7770P: Add support for STK7770P Added support for the dib7770P and the STK7770P Signed-off-by: Olivier Grenie Signed-off-by: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/dib0700_devices.c | 76 +++++++++++++++++++++++++++++ drivers/media/dvb/dvb-usb/dvb-usb-ids.h | 1 + 2 files changed, 77 insertions(+) (limited to 'drivers/media/dvb') diff --git a/drivers/media/dvb/dvb-usb/dib0700_devices.c b/drivers/media/dvb/dvb-usb/dib0700_devices.c index ec0dd255c0ec..dd0294e5ff58 100644 --- a/drivers/media/dvb/dvb-usb/dib0700_devices.c +++ b/drivers/media/dvb/dvb-usb/dib0700_devices.c @@ -1122,6 +1122,15 @@ static struct dib0070_config dib7070p_dib0070_config[2] = { } }; +static struct dib0070_config dib7770p_dib0070_config = { + .i2c_address = DEFAULT_DIB0070_I2C_ADDRESS, + .reset = dib7070_tuner_reset, + .sleep = dib7070_tuner_sleep, + .clock_khz = 12000, + .clock_pad_drive = 0, + .flip_chip = 1, +}; + static int dib7070_set_param_override(struct dvb_frontend *fe, struct dvb_frontend_parameters *fep) { struct dvb_usb_adapter *adap = fe->dvb->priv; @@ -1139,6 +1148,45 @@ static int dib7070_set_param_override(struct dvb_frontend *fe, struct dvb_fronte return state->set_param_save(fe, fep); } +static int dib7770_set_param_override(struct dvb_frontend *fe, + struct dvb_frontend_parameters *fep) +{ + struct dvb_usb_adapter *adap = fe->dvb->priv; + struct dib0700_adapter_state *state = adap->priv; + + u16 offset; + u8 band = BAND_OF_FREQUENCY(fep->frequency/1000); + switch (band) { + case BAND_VHF: + dib7000p_set_gpio(fe, 0, 0, 1); + offset = 850; + break; + case BAND_UHF: + default: + dib7000p_set_gpio(fe, 0, 0, 0); + offset = 250; + break; + } + deb_info("WBD for DiB7000P: %d\n", offset + dib0070_wbd_offset(fe)); + dib7000p_set_wbd_ref(fe, offset + dib0070_wbd_offset(fe)); + return state->set_param_save(fe, fep); +} + +static int dib7770p_tuner_attach(struct dvb_usb_adapter *adap) +{ + struct dib0700_adapter_state *st = adap->priv; + struct i2c_adapter *tun_i2c = dib7000p_get_i2c_master(adap->fe, + DIBX000_I2C_INTERFACE_TUNER, 1); + + if (dvb_attach(dib0070_attach, adap->fe, tun_i2c, + &dib7770p_dib0070_config) == NULL) + return -ENODEV; + + st->set_param_save = adap->fe->ops.tuner_ops.set_params; + adap->fe->ops.tuner_ops.set_params = dib7770_set_param_override; + return 0; +} + static int dib7070p_tuner_attach(struct dvb_usb_adapter *adap) { struct dib0700_adapter_state *st = adap->priv; @@ -1504,6 +1552,7 @@ struct usb_device_id dib0700_usb_id_table[] = { { USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV73A) }, { USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV73ESE) }, { USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV282E) }, + { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_STK7770P) }, { 0 } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, dib0700_usb_id_table); @@ -1965,6 +2014,33 @@ struct dvb_usb_device_properties dib0700_devices[] = { { NULL }, }, }, + }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, + + .num_adapters = 1, + .adapter = { + { + .frontend_attach = stk7070p_frontend_attach, + .tuner_attach = dib7770p_tuner_attach, + + DIB0700_DEFAULT_STREAMING_CONFIG(0x02), + + .size_of_priv = + sizeof(struct dib0700_adapter_state), + }, + }, + + .num_device_descs = 1, + .devices = { + { "DiBcom STK7770P reference design", + { &dib0700_usb_id_table[59], 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 }, }; diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h index eec5604dcdd0..a77d305e30d0 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h +++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h @@ -96,6 +96,7 @@ #define USB_PID_DIBCOM_STK7070P 0x1ebc #define USB_PID_DIBCOM_STK7070PD 0x1ebe #define USB_PID_DIBCOM_ANCHOR_2135_COLD 0x2131 +#define USB_PID_DIBCOM_STK7770P 0x1e80 #define USB_PID_DPOSH_M9206_COLD 0x9206 #define USB_PID_DPOSH_M9206_WARM 0xa090 #define USB_PID_UNIWILL_STK7700P 0x6003 -- cgit v1.2.2 From db48138f6bc4d2b613b08731ffc8b319432ebc25 Mon Sep 17 00:00:00 2001 From: Patrick Boettcher Date: Tue, 15 Sep 2009 07:16:51 -0300 Subject: V4L/DVB (12889): DIB0700: added USB IDs for a Terratec DVB-T XXS Since there is now correct support for the DiB7770 the support for the 'Terratec Cinergy T USB XXS (HD)' can be added. One USB-ID has been moved, another one has been added. Signed-off-by: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/dib0700_devices.c | 12 ++++++++---- drivers/media/dvb/dvb-usb/dvb-usb-ids.h | 3 ++- 2 files changed, 10 insertions(+), 5 deletions(-) (limited to 'drivers/media/dvb') diff --git a/drivers/media/dvb/dvb-usb/dib0700_devices.c b/drivers/media/dvb/dvb-usb/dib0700_devices.c index dd0294e5ff58..10a89b0e5189 100644 --- a/drivers/media/dvb/dvb-usb/dib0700_devices.c +++ b/drivers/media/dvb/dvb-usb/dib0700_devices.c @@ -1553,6 +1553,7 @@ struct usb_device_id dib0700_usb_id_table[] = { { USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV73ESE) }, { USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV282E) }, { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_STK7770P) }, +/* 60 */{ USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_T_XXS_2) }, { 0 } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, dib0700_usb_id_table); @@ -1617,7 +1618,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { { NULL }, }, { "Leadtek Winfast DTV Dongle (STK7700P based)", - { &dib0700_usb_id_table[8], &dib0700_usb_id_table[34] }, + { &dib0700_usb_id_table[8] }, { NULL }, }, { "AVerMedia AVerTV DVB-T Express", @@ -2025,18 +2026,21 @@ struct dvb_usb_device_properties dib0700_devices[] = { DIB0700_DEFAULT_STREAMING_CONFIG(0x02), .size_of_priv = - sizeof(struct dib0700_adapter_state), + sizeof(struct dib0700_adapter_state), }, }, - .num_device_descs = 1, + .num_device_descs = 2, .devices = { { "DiBcom STK7770P reference design", { &dib0700_usb_id_table[59], NULL }, { NULL }, }, + { "Terratec Cinergy T USB XXS (HD)", + { &dib0700_usb_id_table[34], &dib0700_usb_id_table[60] }, + { NULL }, + }, }, - .rc_interval = DEFAULT_RC_INTERVAL, .rc_key_map = dib0700_rc_keys, .rc_key_map_size = ARRAY_SIZE(dib0700_rc_keys), diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h index a77d305e30d0..a07959c1c065 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h +++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h @@ -96,7 +96,7 @@ #define USB_PID_DIBCOM_STK7070P 0x1ebc #define USB_PID_DIBCOM_STK7070PD 0x1ebe #define USB_PID_DIBCOM_ANCHOR_2135_COLD 0x2131 -#define USB_PID_DIBCOM_STK7770P 0x1e80 +#define USB_PID_DIBCOM_STK7770P 0x1e80 #define USB_PID_DPOSH_M9206_COLD 0x9206 #define USB_PID_DPOSH_M9206_WARM 0xa090 #define USB_PID_UNIWILL_STK7700P 0x6003 @@ -185,6 +185,7 @@ #define USB_PID_TERRATEC_CINERGY_HT_EXPRESS 0x0060 #define USB_PID_TERRATEC_CINERGY_T_EXPRESS 0x0062 #define USB_PID_TERRATEC_CINERGY_T_XXS 0x0078 +#define USB_PID_TERRATEC_CINERGY_T_XXS_2 0x00ab #define USB_PID_TERRATEC_T3 0x10a0 #define USB_PID_TERRATEC_T5 0x10a1 #define USB_PID_PINNACLE_EXPRESSCARD_320CX 0x022e -- cgit v1.2.2 From b6e760f3097501e60e76fbcb7a313d42da930c1f Mon Sep 17 00:00:00 2001 From: Patrick Boettcher Date: Mon, 3 Aug 2009 14:39:15 -0300 Subject: V4L/DVB (12892): DVB-API: add support for ISDB-T and ISDB-Tsb (version 5.1) This patch increments the DVB-API to version 5.1 in order to reflect the addition of ISDB-T and ISDB-Tsb on Linux' DVB-API. Changes in detail: - added a small document to describe how to use the API to tune to an ISDB-T or ISDB-Tsb channel - added necessary fields to dtv_frontend_cache - added a smarter clear-cache function which resets all fields of the dtv_frontend_cache - added a TRANSMISSION_MODE_4K to fe_transmit_mode_t Signed-off-by: Olivier Grenie Signed-off-by: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-core/dvb_frontend.c | 202 +++++++++++++++++++++++++++++- drivers/media/dvb/dvb-core/dvb_frontend.h | 14 +++ 2 files changed, 211 insertions(+), 5 deletions(-) (limited to 'drivers/media/dvb') diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c index d13ebcb0c6b6..826080416c93 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -850,6 +850,49 @@ static int dvb_frontend_check_parameters(struct dvb_frontend *fe, return 0; } +static int dvb_frontend_clear_cache(struct dvb_frontend *fe) +{ + int i; + + memset(&(fe->dtv_property_cache), 0, + sizeof(struct dtv_frontend_properties)); + + fe->dtv_property_cache.state = DTV_CLEAR; + fe->dtv_property_cache.delivery_system = SYS_UNDEFINED; + fe->dtv_property_cache.inversion = INVERSION_AUTO; + fe->dtv_property_cache.fec_inner = FEC_AUTO; + fe->dtv_property_cache.transmission_mode = TRANSMISSION_MODE_AUTO; + fe->dtv_property_cache.bandwidth_hz = BANDWIDTH_AUTO; + fe->dtv_property_cache.guard_interval = GUARD_INTERVAL_AUTO; + fe->dtv_property_cache.hierarchy = HIERARCHY_AUTO; + fe->dtv_property_cache.symbol_rate = QAM_AUTO; + fe->dtv_property_cache.code_rate_HP = FEC_AUTO; + fe->dtv_property_cache.code_rate_LP = FEC_AUTO; + + fe->dtv_property_cache.isdbt_partial_reception = -1; + fe->dtv_property_cache.isdbt_sb_mode = -1; + fe->dtv_property_cache.isdbt_sb_subchannel = -1; + fe->dtv_property_cache.isdbt_sb_segment_idx = -1; + fe->dtv_property_cache.isdbt_sb_segment_count = -1; + fe->dtv_property_cache.isdbt_layer_enabled = 0x7; + for (i = 0; i < 3; i++) { + fe->dtv_property_cache.layer[i].fec = FEC_AUTO; + fe->dtv_property_cache.layer[i].modulation = QAM_AUTO; + fe->dtv_property_cache.layer[i].interleaving = -1; + fe->dtv_property_cache.layer[i].segment_count = -1; + } + + return 0; +} + +#define _DTV_CMD(n, s, b) \ +[n] = { \ + .name = #n, \ + .cmd = n, \ + .set = s,\ + .buffer = b \ +} + static struct dtv_cmds_h dtv_cmds[] = { [DTV_TUNE] = { .name = "DTV_TUNE", @@ -949,6 +992,43 @@ static struct dtv_cmds_h dtv_cmds[] = { .cmd = DTV_TRANSMISSION_MODE, .set = 1, }, + + _DTV_CMD(DTV_ISDBT_PARTIAL_RECEPTION, 1, 0), + _DTV_CMD(DTV_ISDBT_SOUND_BROADCASTING, 1, 0), + _DTV_CMD(DTV_ISDBT_SB_SUBCHANNEL_ID, 1, 0), + _DTV_CMD(DTV_ISDBT_SB_SEGMENT_IDX, 1, 0), + _DTV_CMD(DTV_ISDBT_SB_SEGMENT_COUNT, 1, 0), + _DTV_CMD(DTV_ISDBT_LAYERA_FEC, 1, 0), + _DTV_CMD(DTV_ISDBT_LAYERA_MODULATION, 1, 0), + _DTV_CMD(DTV_ISDBT_LAYERA_SEGMENT_COUNT, 1, 0), + _DTV_CMD(DTV_ISDBT_LAYERA_TIME_INTERLEAVING, 1, 0), + _DTV_CMD(DTV_ISDBT_LAYERB_FEC, 1, 0), + _DTV_CMD(DTV_ISDBT_LAYERB_MODULATION, 1, 0), + _DTV_CMD(DTV_ISDBT_LAYERB_SEGMENT_COUNT, 1, 0), + _DTV_CMD(DTV_ISDBT_LAYERB_TIME_INTERLEAVING, 1, 0), + _DTV_CMD(DTV_ISDBT_LAYERC_FEC, 1, 0), + _DTV_CMD(DTV_ISDBT_LAYERC_MODULATION, 1, 0), + _DTV_CMD(DTV_ISDBT_LAYERC_SEGMENT_COUNT, 1, 0), + _DTV_CMD(DTV_ISDBT_LAYERC_TIME_INTERLEAVING, 1, 0), + + _DTV_CMD(DTV_ISDBT_PARTIAL_RECEPTION, 0, 0), + _DTV_CMD(DTV_ISDBT_SOUND_BROADCASTING, 0, 0), + _DTV_CMD(DTV_ISDBT_SB_SUBCHANNEL_ID, 0, 0), + _DTV_CMD(DTV_ISDBT_SB_SEGMENT_IDX, 0, 0), + _DTV_CMD(DTV_ISDBT_SB_SEGMENT_COUNT, 0, 0), + _DTV_CMD(DTV_ISDBT_LAYERA_FEC, 0, 0), + _DTV_CMD(DTV_ISDBT_LAYERA_MODULATION, 0, 0), + _DTV_CMD(DTV_ISDBT_LAYERA_SEGMENT_COUNT, 0, 0), + _DTV_CMD(DTV_ISDBT_LAYERA_TIME_INTERLEAVING, 0, 0), + _DTV_CMD(DTV_ISDBT_LAYERB_FEC, 0, 0), + _DTV_CMD(DTV_ISDBT_LAYERB_MODULATION, 0, 0), + _DTV_CMD(DTV_ISDBT_LAYERB_SEGMENT_COUNT, 0, 0), + _DTV_CMD(DTV_ISDBT_LAYERB_TIME_INTERLEAVING, 0, 0), + _DTV_CMD(DTV_ISDBT_LAYERC_FEC, 0, 0), + _DTV_CMD(DTV_ISDBT_LAYERC_MODULATION, 0, 0), + _DTV_CMD(DTV_ISDBT_LAYERC_SEGMENT_COUNT, 0, 0), + _DTV_CMD(DTV_ISDBT_LAYERC_TIME_INTERLEAVING, 0, 0), + /* Get */ [DTV_DISEQC_SLAVE_REPLY] = { .name = "DTV_DISEQC_SLAVE_REPLY", @@ -956,6 +1036,7 @@ static struct dtv_cmds_h dtv_cmds[] = { .set = 0, .buffer = 1, }, + [DTV_API_VERSION] = { .name = "DTV_API_VERSION", .cmd = DTV_API_VERSION, @@ -1165,14 +1246,21 @@ static void dtv_property_adv_params_sync(struct dvb_frontend *fe) 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->inversion = c->inversion; 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; + if (c->bandwidth_hz == 8000000) + p->u.ofdm.bandwidth = BANDWIDTH_8_MHZ; + else if (c->bandwidth_hz == 7000000) + p->u.ofdm.bandwidth = BANDWIDTH_7_MHZ; + else if (c->bandwidth_hz == 6000000) + p->u.ofdm.bandwidth = BANDWIDTH_6_MHZ; + else + p->u.ofdm.bandwidth = BANDWIDTH_AUTO; } } @@ -1274,6 +1362,59 @@ static int dtv_property_process_get(struct dvb_frontend *fe, case DTV_HIERARCHY: tvp->u.data = fe->dtv_property_cache.hierarchy; break; + + /* ISDB-T Support here */ + case DTV_ISDBT_PARTIAL_RECEPTION: + tvp->u.data = fe->dtv_property_cache.isdbt_partial_reception; + break; + case DTV_ISDBT_SOUND_BROADCASTING: + tvp->u.data = fe->dtv_property_cache.isdbt_sb_mode; + break; + case DTV_ISDBT_SB_SUBCHANNEL_ID: + tvp->u.data = fe->dtv_property_cache.isdbt_sb_subchannel; + break; + case DTV_ISDBT_SB_SEGMENT_IDX: + tvp->u.data = fe->dtv_property_cache.isdbt_sb_segment_idx; + break; + case DTV_ISDBT_SB_SEGMENT_COUNT: + tvp->u.data = fe->dtv_property_cache.isdbt_sb_segment_count; + break; + case DTV_ISDBT_LAYERA_FEC: + tvp->u.data = fe->dtv_property_cache.layer[0].fec; + break; + case DTV_ISDBT_LAYERA_MODULATION: + tvp->u.data = fe->dtv_property_cache.layer[0].modulation; + break; + case DTV_ISDBT_LAYERA_SEGMENT_COUNT: + tvp->u.data = fe->dtv_property_cache.layer[0].segment_count; + break; + case DTV_ISDBT_LAYERA_TIME_INTERLEAVING: + tvp->u.data = fe->dtv_property_cache.layer[0].interleaving; + break; + case DTV_ISDBT_LAYERB_FEC: + tvp->u.data = fe->dtv_property_cache.layer[1].fec; + break; + case DTV_ISDBT_LAYERB_MODULATION: + tvp->u.data = fe->dtv_property_cache.layer[1].modulation; + break; + case DTV_ISDBT_LAYERB_SEGMENT_COUNT: + tvp->u.data = fe->dtv_property_cache.layer[1].segment_count; + break; + case DTV_ISDBT_LAYERB_TIME_INTERLEAVING: + tvp->u.data = fe->dtv_property_cache.layer[1].interleaving; + break; + case DTV_ISDBT_LAYERC_FEC: + tvp->u.data = fe->dtv_property_cache.layer[2].fec; + break; + case DTV_ISDBT_LAYERC_MODULATION: + tvp->u.data = fe->dtv_property_cache.layer[2].modulation; + break; + case DTV_ISDBT_LAYERC_SEGMENT_COUNT: + tvp->u.data = fe->dtv_property_cache.layer[2].segment_count; + break; + case DTV_ISDBT_LAYERC_TIME_INTERLEAVING: + tvp->u.data = fe->dtv_property_cache.layer[2].interleaving; + break; default: r = -1; } @@ -1302,10 +1443,8 @@ static int dtv_property_process_set(struct dvb_frontend *fe, /* Reset a cache of data specific to the frontend here. This does * not effect hardware. */ + dvb_frontend_clear_cache(fe); dprintk("%s() Flushing property cache\n", __func__); - 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 @@ -1371,6 +1510,59 @@ static int dtv_property_process_set(struct dvb_frontend *fe, case DTV_HIERARCHY: fe->dtv_property_cache.hierarchy = tvp->u.data; break; + + /* ISDB-T Support here */ + case DTV_ISDBT_PARTIAL_RECEPTION: + fe->dtv_property_cache.isdbt_partial_reception = tvp->u.data; + break; + case DTV_ISDBT_SOUND_BROADCASTING: + fe->dtv_property_cache.isdbt_sb_mode = tvp->u.data; + break; + case DTV_ISDBT_SB_SUBCHANNEL_ID: + fe->dtv_property_cache.isdbt_sb_subchannel = tvp->u.data; + break; + case DTV_ISDBT_SB_SEGMENT_IDX: + fe->dtv_property_cache.isdbt_sb_segment_idx = tvp->u.data; + break; + case DTV_ISDBT_SB_SEGMENT_COUNT: + fe->dtv_property_cache.isdbt_sb_segment_count = tvp->u.data; + break; + case DTV_ISDBT_LAYERA_FEC: + fe->dtv_property_cache.layer[0].fec = tvp->u.data; + break; + case DTV_ISDBT_LAYERA_MODULATION: + fe->dtv_property_cache.layer[0].modulation = tvp->u.data; + break; + case DTV_ISDBT_LAYERA_SEGMENT_COUNT: + fe->dtv_property_cache.layer[0].segment_count = tvp->u.data; + break; + case DTV_ISDBT_LAYERA_TIME_INTERLEAVING: + fe->dtv_property_cache.layer[0].interleaving = tvp->u.data; + break; + case DTV_ISDBT_LAYERB_FEC: + fe->dtv_property_cache.layer[1].fec = tvp->u.data; + break; + case DTV_ISDBT_LAYERB_MODULATION: + fe->dtv_property_cache.layer[1].modulation = tvp->u.data; + break; + case DTV_ISDBT_LAYERB_SEGMENT_COUNT: + fe->dtv_property_cache.layer[1].segment_count = tvp->u.data; + break; + case DTV_ISDBT_LAYERB_TIME_INTERLEAVING: + fe->dtv_property_cache.layer[1].interleaving = tvp->u.data; + break; + case DTV_ISDBT_LAYERC_FEC: + fe->dtv_property_cache.layer[2].fec = tvp->u.data; + break; + case DTV_ISDBT_LAYERC_MODULATION: + fe->dtv_property_cache.layer[2].modulation = tvp->u.data; + break; + case DTV_ISDBT_LAYERC_SEGMENT_COUNT: + fe->dtv_property_cache.layer[2].segment_count = tvp->u.data; + break; + case DTV_ISDBT_LAYERC_TIME_INTERLEAVING: + fe->dtv_property_cache.layer[2].interleaving = tvp->u.data; + break; default: r = -1; } diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.h b/drivers/media/dvb/dvb-core/dvb_frontend.h index e176da472d7a..9e46f1772c54 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.h +++ b/drivers/media/dvb/dvb-core/dvb_frontend.h @@ -341,6 +341,20 @@ struct dtv_frontend_properties { fe_rolloff_t rolloff; fe_delivery_system_t delivery_system; + + /* ISDB-T specifics */ + u8 isdbt_partial_reception; + u8 isdbt_sb_mode; + u8 isdbt_sb_subchannel; + u32 isdbt_sb_segment_idx; + u32 isdbt_sb_segment_count; + u8 isdbt_layer_enabled; + struct { + u8 segment_count; + fe_code_rate_t fec; + fe_modulation_t modulation; + u8 interleaving; + } layer[3]; }; struct dvb_frontend { -- cgit v1.2.2 From e7b7949a95441affe937fa25f4d6d8f3df0ca271 Mon Sep 17 00:00:00 2001 From: Patrick Boettcher Date: Fri, 14 Aug 2009 05:24:19 -0300 Subject: V4L/DVB (12896): ISDB-T: add mapping of LAYER_ENABLED to frontend-cache It was forgotten to map the LAYER_ENABLED ioctl to the frontend-cache and back. Signed-off-by: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-core/dvb_frontend.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/media/dvb') diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c index 826080416c93..3c9482660eac 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -998,6 +998,7 @@ static struct dtv_cmds_h dtv_cmds[] = { _DTV_CMD(DTV_ISDBT_SB_SUBCHANNEL_ID, 1, 0), _DTV_CMD(DTV_ISDBT_SB_SEGMENT_IDX, 1, 0), _DTV_CMD(DTV_ISDBT_SB_SEGMENT_COUNT, 1, 0), + _DTV_CMD(DTV_ISDBT_LAYER_ENABLED, 1, 0), _DTV_CMD(DTV_ISDBT_LAYERA_FEC, 1, 0), _DTV_CMD(DTV_ISDBT_LAYERA_MODULATION, 1, 0), _DTV_CMD(DTV_ISDBT_LAYERA_SEGMENT_COUNT, 1, 0), @@ -1016,6 +1017,7 @@ static struct dtv_cmds_h dtv_cmds[] = { _DTV_CMD(DTV_ISDBT_SB_SUBCHANNEL_ID, 0, 0), _DTV_CMD(DTV_ISDBT_SB_SEGMENT_IDX, 0, 0), _DTV_CMD(DTV_ISDBT_SB_SEGMENT_COUNT, 0, 0), + _DTV_CMD(DTV_ISDBT_LAYER_ENABLED, 0, 0), _DTV_CMD(DTV_ISDBT_LAYERA_FEC, 0, 0), _DTV_CMD(DTV_ISDBT_LAYERA_MODULATION, 0, 0), _DTV_CMD(DTV_ISDBT_LAYERA_SEGMENT_COUNT, 0, 0), @@ -1379,6 +1381,9 @@ static int dtv_property_process_get(struct dvb_frontend *fe, case DTV_ISDBT_SB_SEGMENT_COUNT: tvp->u.data = fe->dtv_property_cache.isdbt_sb_segment_count; break; + case DTV_ISDBT_LAYER_ENABLED: + tvp->u.data = fe->dtv_property_cache.isdbt_layer_enabled; + break; case DTV_ISDBT_LAYERA_FEC: tvp->u.data = fe->dtv_property_cache.layer[0].fec; break; @@ -1527,6 +1532,9 @@ static int dtv_property_process_set(struct dvb_frontend *fe, case DTV_ISDBT_SB_SEGMENT_COUNT: fe->dtv_property_cache.isdbt_sb_segment_count = tvp->u.data; break; + case DTV_ISDBT_LAYER_ENABLED: + fe->dtv_property_cache.isdbt_layer_enabled = tvp->u.data; + break; case DTV_ISDBT_LAYERA_FEC: fe->dtv_property_cache.layer[0].fec = tvp->u.data; break; -- cgit v1.2.2 From 7e5ce6515d0deb76a49dcb4112a6dff5d950bfb6 Mon Sep 17 00:00:00 2001 From: Patrick Boettcher Date: Mon, 3 Aug 2009 13:43:40 -0300 Subject: V4L/DVB (12898): DiB0070: Update to latest internal release General update of the dib0070-driver based on DiBcom's latest release. New driver features can enable better performance in some reception situations. Signed-off-by: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/dib0700_devices.c | 7 +- drivers/media/dvb/frontends/dib0070.c | 673 ++++++++++++++++------------ drivers/media/dvb/frontends/dib0070.h | 11 + 3 files changed, 407 insertions(+), 284 deletions(-) (limited to 'drivers/media/dvb') diff --git a/drivers/media/dvb/dvb-usb/dib0700_devices.c b/drivers/media/dvb/dvb-usb/dib0700_devices.c index 10a89b0e5189..10ade261b0ab 100644 --- a/drivers/media/dvb/dvb-usb/dib0700_devices.c +++ b/drivers/media/dvb/dvb-usb/dib0700_devices.c @@ -1098,11 +1098,13 @@ static struct dibx000_agc_config dib7070_agc_config = { static int dib7070_tuner_reset(struct dvb_frontend *fe, int onoff) { + deb_info("reset: %d", onoff); return dib7000p_set_gpio(fe, 8, 0, !onoff); } static int dib7070_tuner_sleep(struct dvb_frontend *fe, int onoff) { + deb_info("sleep: %d", onoff); return dib7000p_set_gpio(fe, 9, 0, onoff); } @@ -1112,13 +1114,14 @@ static struct dib0070_config dib7070p_dib0070_config[2] = { .reset = dib7070_tuner_reset, .sleep = dib7070_tuner_sleep, .clock_khz = 12000, - .clock_pad_drive = 4 + .clock_pad_drive = 4, + .charge_pump = 2, }, { .i2c_address = DEFAULT_DIB0070_I2C_ADDRESS, .reset = dib7070_tuner_reset, .sleep = dib7070_tuner_sleep, .clock_khz = 12000, - + .charge_pump = 2, } }; diff --git a/drivers/media/dvb/frontends/dib0070.c b/drivers/media/dvb/frontends/dib0070.c index da92cbe1b8ea..2eb9bdb4dbb3 100644 --- a/drivers/media/dvb/frontends/dib0070.c +++ b/drivers/media/dvb/frontends/dib0070.c @@ -1,12 +1,29 @@ /* * Linux-DVB Driver for DiBcom's DiB0070 base-band RF Tuner. * - * Copyright (C) 2005-7 DiBcom (http://www.dibcom.fr/) + * Copyright (C) 2005-9 DiBcom (http://www.dibcom.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. + * 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. + * + * + * This code is more or less generated from another driver, please + * excuse some codingstyle oddities. + * */ + #include #include @@ -19,19 +36,57 @@ static int debug; module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "turn on debugging (default: 0)"); -#define dprintk(args...) do { if (debug) { printk(KERN_DEBUG "DiB0070: "); printk(args); printk("\n"); } } while (0) +#define dprintk(args...) do { \ + if (debug) { \ + printk(KERN_DEBUG "DiB0070: "); \ + printk(args); \ + printk("\n"); \ + } \ +} while (0) #define DIB0070_P1D 0x00 #define DIB0070_P1F 0x01 #define DIB0070_P1G 0x03 #define DIB0070S_P1A 0x02 +enum frontend_tune_state { + CT_TUNER_START = 10, + CT_TUNER_STEP_0, + CT_TUNER_STEP_1, + CT_TUNER_STEP_2, + CT_TUNER_STEP_3, + CT_TUNER_STEP_4, + CT_TUNER_STEP_5, + CT_TUNER_STEP_6, + CT_TUNER_STEP_7, + CT_TUNER_STOP, +}; + +#define FE_CALLBACK_TIME_NEVER 0xffffffff + struct dib0070_state { struct i2c_adapter *i2c; struct dvb_frontend *fe; const struct dib0070_config *cfg; u16 wbd_ff_offset; u8 revision; + + enum frontend_tune_state tune_state; + u32 current_rf; + + /* for the captrim binary search */ + s8 step; + u16 adc_diff; + + s8 captrim; + s8 fcaptrim; + u16 lo4; + + const struct dib0070_tuning *current_tune_table_index; + const struct dib0070_lna_match *lna_match; + + u8 wbd_gain_current; + u16 wbd_offset_3_3[2]; }; static uint16_t dib0070_read_reg(struct dib0070_state *state, u8 reg) @@ -59,55 +114,71 @@ static int dib0070_write_reg(struct dib0070_state *state, u8 reg, u16 val) return 0; } -#define HARD_RESET(state) do { if (state->cfg->reset) { state->cfg->reset(state->fe,1); msleep(10); state->cfg->reset(state->fe,0); msleep(10); } } while (0) +#define HARD_RESET(state) do { \ + state->cfg->sleep(state->fe, 0); \ + if (state->cfg->reset) { \ + state->cfg->reset(state->fe,1); msleep(10); \ + state->cfg->reset(state->fe,0); msleep(10); \ + } \ +} while (0) static int dib0070_set_bandwidth(struct dvb_frontend *fe, struct dvb_frontend_parameters *ch) { - struct dib0070_state *st = fe->tuner_priv; - u16 tmp = 0; - tmp = dib0070_read_reg(st, 0x02) & 0x3fff; - - switch(BANDWIDTH_TO_KHZ(ch->u.ofdm.bandwidth)) { - case 8000: - tmp |= (0 << 14); - break; - case 7000: - tmp |= (1 << 14); - break; - case 6000: - tmp |= (2 << 14); - break; - case 5000: - default: - tmp |= (3 << 14); - break; - } - dib0070_write_reg(st, 0x02, tmp); + struct dib0070_state *st = fe->tuner_priv; + u16 tmp = dib0070_read_reg(st, 0x02) & 0x3fff; + + if (fe->dtv_property_cache.bandwidth_hz/1000 > 7000) + tmp |= (0 << 14); + else if (fe->dtv_property_cache.bandwidth_hz/1000 > 6000) + tmp |= (1 << 14); + else if (fe->dtv_property_cache.bandwidth_hz/1000 > 5000) + tmp |= (2 << 14); + else + tmp |= (3 << 14); + + dib0070_write_reg(st, 0x02, tmp); + + /* sharpen the BB filter in ISDB-T to have higher immunity to adjacent channels */ + if (fe->dtv_property_cache.delivery_system == SYS_ISDBT) { + u16 value = dib0070_read_reg(st, 0x17); + + dib0070_write_reg(st, 0x17, value & 0xfffc); + tmp = dib0070_read_reg(st, 0x01) & 0x01ff; + dib0070_write_reg(st, 0x01, tmp | (60 << 9)); + + dib0070_write_reg(st, 0x17, value); + } return 0; } -static void dib0070_captrim(struct dib0070_state *st, u16 LO4) +static int dib0070_captrim(struct dib0070_state *st, enum frontend_tune_state *tune_state) { - int8_t captrim, fcaptrim, step_sign, step; - u16 adc, adc_diff = 3000; + int8_t step_sign; + u16 adc; + int ret = 0; + if (*tune_state == CT_TUNER_STEP_0) { + dib0070_write_reg(st, 0x0f, 0xed10); + dib0070_write_reg(st, 0x17, 0x0034); - dib0070_write_reg(st, 0x0f, 0xed10); - dib0070_write_reg(st, 0x17, 0x0034); + dib0070_write_reg(st, 0x18, 0x0032); + st->step = st->captrim = st->fcaptrim = 64; + st->adc_diff = 3000; + ret = 20; - dib0070_write_reg(st, 0x18, 0x0032); - msleep(2); + *tune_state = CT_TUNER_STEP_1; + } else if (*tune_state == CT_TUNER_STEP_1) { + st->step /= 2; + dib0070_write_reg(st, 0x14, st->lo4 | st->captrim); + ret = 15; - step = captrim = fcaptrim = 64; + *tune_state = CT_TUNER_STEP_2; + } else if (*tune_state == CT_TUNER_STEP_2) { - do { - step /= 2; - dib0070_write_reg(st, 0x14, LO4 | captrim); - msleep(1); adc = dib0070_read_reg(st, 0x19); - dprintk( "CAPTRIM=%hd; ADC = %hd (ADC) & %dmV", captrim, adc, (u32) adc*(u32)1800/(u32)1024); + dprintk( "CAPTRIM=%hd; ADC = %hd (ADC) & %dmV", st->captrim, adc, (u32) adc*(u32)1800/(u32)1024); if (adc >= 400) { adc -= 400; @@ -117,266 +188,305 @@ static void dib0070_captrim(struct dib0070_state *st, u16 LO4) step_sign = 1; } - if (adc < adc_diff) { - dprintk( "CAPTRIM=%hd is closer to target (%hd/%hd)", captrim, adc, adc_diff); - adc_diff = adc; - fcaptrim = captrim; + if (adc < st->adc_diff) { + dprintk( "CAPTRIM=%hd is closer to target (%hd/%hd)", st->captrim, adc, st->adc_diff); + st->adc_diff = adc; + st->fcaptrim = st->captrim; } - captrim += (step_sign * step); - } while (step >= 1); + st->captrim += (step_sign * st->step); + + if (st->step >= 1) + *tune_state = CT_TUNER_STEP_1; + else + *tune_state = CT_TUNER_STEP_3; + + } else if (*tune_state == CT_TUNER_STEP_3) { + dib0070_write_reg(st, 0x14, st->lo4 | st->fcaptrim); + dib0070_write_reg(st, 0x18, 0x07ff); + *tune_state = CT_TUNER_STEP_4; + } - dib0070_write_reg(st, 0x14, LO4 | fcaptrim); - dib0070_write_reg(st, 0x18, 0x07ff); + return ret; } -#define LPF 100 // define for the loop filter 100kHz by default 16-07-06 -#define LO4_SET_VCO_HFDIV(l, v, h) l |= ((v) << 11) | ((h) << 7) -#define LO4_SET_SD(l, s) l |= ((s) << 14) | ((s) << 12) -#define LO4_SET_CTRIM(l, c) l |= (c) << 10 -static int dib0070_tune_digital(struct dvb_frontend *fe, struct dvb_frontend_parameters *ch) +static int dib0070_set_ctrl_lo5(struct dvb_frontend *fe, u8 vco_bias_trim, u8 hf_div_trim, u8 cp_current, u8 third_order_filt) { - struct dib0070_state *st = fe->tuner_priv; - u32 freq = ch->frequency/1000 + (BAND_OF_FREQUENCY(ch->frequency/1000) == BAND_VHF ? st->cfg->freq_offset_khz_vhf : st->cfg->freq_offset_khz_uhf); + struct dib0070_state *state = fe->tuner_priv; + u16 lo5 = (third_order_filt << 14) | (0 << 13) | (1 << 12) | (3 << 9) | (cp_current << 6) | (hf_div_trim << 3) | (vco_bias_trim << 0); + dprintk( "CTRL_LO5: 0x%x", lo5); + return dib0070_write_reg(state, 0x15, lo5); +} - u8 band = BAND_OF_FREQUENCY(freq), c; +struct dib0070_tuning +{ + u32 max_freq; /* for every frequency less than or equal to that field: this information is correct */ + u8 switch_trim; + u8 vco_band; + u8 hfdiv; + u8 vco_multi; + u8 presc; + u8 wbdmux; + u16 tuner_enable; +}; - /*******************VCO***********************************/ - u16 lo4 = 0; +struct dib0070_lna_match +{ + u32 max_freq; /* for every frequency less than or equal to that field: this information is correct */ + u8 lna_band; +}; - u8 REFDIV, PRESC = 2; - u32 FBDiv, Rest, FREF, VCOF_kHz; - u16 Num, Den; - /*******************FrontEnd******************************/ - u16 value = 0; +static const struct dib0070_tuning dib0070s_tuning_table[] = - dprintk( "Tuning for Band: %hd (%d kHz)", band, freq); +{ + { 570000, 2, 1, 3, 6, 6, 2, 0x4000 | 0x0800 }, /* UHF */ + { 700000, 2, 0, 2, 4, 2, 2, 0x4000 | 0x0800 }, + { 863999, 2, 1, 2, 4, 2, 2, 0x4000 | 0x0800 }, + { 1500000, 0, 1, 1, 2, 2, 4, 0x2000 | 0x0400 }, /* LBAND */ + { 1600000, 0, 1, 1, 2, 2, 4, 0x2000 | 0x0400 }, + { 2000000, 0, 1, 1, 2, 2, 4, 0x2000 | 0x0400 }, + { 0xffffffff, 0, 0, 8, 1, 2, 1, 0x8000 | 0x1000 }, /* SBAND */ +}; +static const struct dib0070_tuning dib0070_tuning_table[] = - dib0070_write_reg(st, 0x17, 0x30); +{ + { 115000, 1, 0, 7, 24, 2, 1, 0x8000 | 0x1000 }, /* FM below 92MHz cannot be tuned */ + { 179500, 1, 0, 3, 16, 2, 1, 0x8000 | 0x1000 }, /* VHF */ + { 189999, 1, 1, 3, 16, 2, 1, 0x8000 | 0x1000 }, + { 250000, 1, 0, 6, 12, 2, 1, 0x8000 | 0x1000 }, + { 569999, 2, 1, 5, 6, 2, 2, 0x4000 | 0x0800 }, /* UHF */ + { 699999, 2, 0 ,1, 4, 2, 2, 0x4000 | 0x0800 }, + { 863999, 2, 1, 1, 4, 2, 2, 0x4000 | 0x0800 }, + { 0xffffffff, 0, 1, 0, 2, 2, 4, 0x2000 | 0x0400 }, /* LBAND or everything higher than UHF */ +}; - dib0070_set_bandwidth(fe, ch); /* c is used as HF */ - switch (st->revision) { - case DIB0070S_P1A: - switch (band) { - case BAND_LBAND: - LO4_SET_VCO_HFDIV(lo4, 1, 1); - c = 2; - break; - case BAND_SBAND: - LO4_SET_VCO_HFDIV(lo4, 0, 0); - LO4_SET_CTRIM(lo4, 1); - c = 1; - break; - case BAND_UHF: - default: - if (freq < 570000) { - LO4_SET_VCO_HFDIV(lo4, 1, 3); - PRESC = 6; c = 6; - } else if (freq < 680000) { - LO4_SET_VCO_HFDIV(lo4, 0, 2); - c = 4; - } else { - LO4_SET_VCO_HFDIV(lo4, 1, 2); - c = 4; - } - break; - } break; - - case DIB0070_P1G: - case DIB0070_P1F: - default: - switch (band) { - case BAND_FM: - LO4_SET_VCO_HFDIV(lo4, 0, 7); - c = 24; - break; - case BAND_LBAND: - LO4_SET_VCO_HFDIV(lo4, 1, 0); - c = 2; - break; - case BAND_VHF: - if (freq < 180000) { - LO4_SET_VCO_HFDIV(lo4, 0, 3); - c = 16; - } else if (freq < 190000) { - LO4_SET_VCO_HFDIV(lo4, 1, 3); - c = 16; - } else { - LO4_SET_VCO_HFDIV(lo4, 0, 6); - c = 12; - } - break; - - case BAND_UHF: - default: - if (freq < 570000) { - LO4_SET_VCO_HFDIV(lo4, 1, 5); - c = 6; - } else if (freq < 700000) { - LO4_SET_VCO_HFDIV(lo4, 0, 1); - c = 4; - } else { - LO4_SET_VCO_HFDIV(lo4, 1, 1); - c = 4; - } - break; - } - break; - } +static const struct dib0070_lna_match dib0070_lna_flip_chip[] = - dprintk( "HFDIV code: %hd", (lo4 >> 7) & 0xf); - dprintk( "VCO = %hd", (lo4 >> 11) & 0x3); +{ + { 180000, 0 }, /* VHF */ + { 188000, 1 }, + { 196400, 2 }, + { 250000, 3 }, + { 550000, 0 }, /* UHF */ + { 590000, 1 }, + { 666000, 3 }, + { 864000, 5 }, + { 1500000, 0 }, /* LBAND or everything higher than UHF */ + { 1600000, 1 }, + { 2000000, 3 }, + { 0xffffffff, 7 }, +}; +static const struct dib0070_lna_match dib0070_lna[] = - VCOF_kHz = (c * freq) * 2; - dprintk( "VCOF in kHz: %d ((%hd*%d) << 1))",VCOF_kHz, c, freq); +{ + { 180000, 0 }, /* VHF */ + { 188000, 1 }, + { 196400, 2 }, + { 250000, 3 }, + { 550000, 2 }, /* UHF */ + { 650000, 3 }, + { 750000, 5 }, + { 850000, 6 }, + { 864000, 7 }, + { 1500000, 0 }, /* LBAND or everything higher than UHF */ + { 1600000, 1 }, + { 2000000, 3 }, + { 0xffffffff, 7 }, +}; - switch (band) { - case BAND_VHF: - REFDIV = (u8) ((st->cfg->clock_khz + 9999) / 10000); - break; - case BAND_FM: - REFDIV = (u8) ((st->cfg->clock_khz) / 1000); - break; - default: - REFDIV = (u8) ( st->cfg->clock_khz / 10000); - break; - } - FREF = st->cfg->clock_khz / REFDIV; +#define LPF 100 // define for the loop filter 100kHz by default 16-07-06 +static int dib0070_tune_digital(struct dvb_frontend *fe, struct dvb_frontend_parameters *ch) +{ + struct dib0070_state *st = fe->tuner_priv; + + const struct dib0070_tuning *tune; + const struct dib0070_lna_match *lna_match; + + enum frontend_tune_state *tune_state = &st->tune_state; + int ret = 10; /* 1ms is the default delay most of the time */ - dprintk( "REFDIV: %hd, FREF: %d", REFDIV, FREF); + u8 band = (u8)BAND_OF_FREQUENCY(ch->frequency/1000); + u32 freq = ch->frequency/1000 + (band == BAND_VHF ? st->cfg->freq_offset_khz_vhf : st->cfg->freq_offset_khz_uhf); + + +#ifdef CONFIG_STANDARD_ISDBT + if (fe->dtv_property_cache.delivery_system == SYS_ISDBT && ch->u.isdbt.sb_mode == 1) + if ( ( (ch->u.isdbt.sb_conn_total_seg % 2) && (ch->u.isdbt.sb_wanted_seg == ((ch->u.isdbt.sb_conn_total_seg/2) + 1) ) ) || + ( ( (ch->u.isdbt.sb_conn_total_seg % 2) == 0) && (ch->u.isdbt.sb_wanted_seg == (ch->u.isdbt.sb_conn_total_seg/2) ) ) || + ( ( (ch->u.isdbt.sb_conn_total_seg % 2) == 0) && (ch->u.isdbt.sb_wanted_seg == ((ch->u.isdbt.sb_conn_total_seg/2)+1))) ) + freq += 850; +#endif + if (st->current_rf != freq) { + switch (st->revision) { - case DIB0070S_P1A: - FBDiv = (VCOF_kHz / PRESC / FREF); - Rest = (VCOF_kHz / PRESC) - FBDiv * FREF; - break; - - case DIB0070_P1G: - case DIB0070_P1F: - default: - FBDiv = (freq / (FREF / 2)); - Rest = 2 * freq - FBDiv * FREF; - break; + case DIB0070S_P1A: + tune = dib0070s_tuning_table; + lna_match = dib0070_lna; + break; + default: + tune = dib0070_tuning_table; + if (st->cfg->flip_chip) + lna_match = dib0070_lna_flip_chip; + else + lna_match = dib0070_lna; + break; } + while (freq > tune->max_freq) /* find the right one */ + tune++; + while (freq > lna_match->max_freq) /* find the right one */ + lna_match++; + st->current_tune_table_index = tune; + st->lna_match = lna_match; + } - if (Rest < LPF) Rest = 0; - else if (Rest < 2 * LPF) Rest = 2 * LPF; - else if (Rest > (FREF - LPF)) { Rest = 0 ; FBDiv += 1; } - else if (Rest > (FREF - 2 * LPF)) Rest = FREF - 2 * LPF; - Rest = (Rest * 6528) / (FREF / 10); - dprintk( "FBDIV: %d, Rest: %d", FBDiv, Rest); + if (*tune_state == CT_TUNER_START) { + dprintk( "Tuning for Band: %hd (%d kHz)", band, freq); + if (st->current_rf != freq) { + u8 REFDIV; + u32 FBDiv, Rest, FREF, VCOF_kHz; + u8 Den; - Num = 0; - Den = 1; + st->current_rf = freq; + st->lo4 = (st->current_tune_table_index->vco_band << 11) | (st->current_tune_table_index->hfdiv << 7); - if (Rest > 0) { - LO4_SET_SD(lo4, 1); - Den = 255; - Num = (u16)Rest; - } - dprintk( "Num: %hd, Den: %hd, SD: %hd",Num, Den, (lo4 >> 12) & 0x1); + dib0070_write_reg(st, 0x17, 0x30); - dib0070_write_reg(st, 0x11, (u16)FBDiv); + VCOF_kHz = st->current_tune_table_index->vco_multi * freq * 2; + switch (band) { + case BAND_VHF: + REFDIV = (u8) ((st->cfg->clock_khz + 9999) / 10000); + break; + case BAND_FM: + REFDIV = (u8) ((st->cfg->clock_khz) / 1000); + break; + default: + REFDIV = (u8) ( st->cfg->clock_khz / 10000); + break; + } + FREF = st->cfg->clock_khz / REFDIV; - dib0070_write_reg(st, 0x12, (Den << 8) | REFDIV); - dib0070_write_reg(st, 0x13, Num); + switch (st->revision) { + case DIB0070S_P1A: + FBDiv = (VCOF_kHz / st->current_tune_table_index->presc / FREF); + Rest = (VCOF_kHz / st->current_tune_table_index->presc) - FBDiv * FREF; + break; + case DIB0070_P1G: + case DIB0070_P1F: + default: + FBDiv = (freq / (FREF / 2)); + Rest = 2 * freq - FBDiv * FREF; + break; + } - value = 0x0040 | 0x0020 | 0x0010 | 0x0008 | 0x0002 | 0x0001; - switch (band) { - case BAND_UHF: value |= 0x4000 | 0x0800; break; - case BAND_LBAND: value |= 0x2000 | 0x0400; break; - default: value |= 0x8000 | 0x1000; break; - } - dib0070_write_reg(st, 0x20, value); + if (Rest < LPF) Rest = 0; + else if (Rest < 2 * LPF) Rest = 2 * LPF; + else if (Rest > (FREF - LPF)) { Rest = 0 ; FBDiv += 1; } + else if (Rest > (FREF - 2 * LPF)) Rest = FREF - 2 * LPF; + Rest = (Rest * 6528) / (FREF / 10); - dib0070_captrim(st, lo4); - if (st->revision == DIB0070S_P1A) { - if (band == BAND_SBAND) - dib0070_write_reg(st, 0x15, 0x16e2); - else - dib0070_write_reg(st, 0x15, 0x56e5); - } + Den = 1; + if (Rest > 0) { + st->lo4 |= (1 << 14) | (1 << 12); + Den = 255; + } + dib0070_write_reg(st, 0x11, (u16)FBDiv); + dib0070_write_reg(st, 0x12, (Den << 8) | REFDIV); + dib0070_write_reg(st, 0x13, (u16) Rest); - switch (band) { - case BAND_UHF: value = 0x7c82; break; - case BAND_LBAND: value = 0x7c84; break; - default: value = 0x7c81; break; - } - dib0070_write_reg(st, 0x0f, value); - dib0070_write_reg(st, 0x06, 0x3fff); + if (st->revision == DIB0070S_P1A) { - /* Front End */ - /* c == TUNE, value = SWITCH */ - c = 0; - value = 0; - switch (band) { - case BAND_FM: - c = 0; value = 1; - break; + if (band == BAND_SBAND) { + dib0070_set_ctrl_lo5(fe, 2, 4, 3, 0); + dib0070_write_reg(st, 0x1d,0xFFFF); + } else + dib0070_set_ctrl_lo5(fe, 5, 4, 3, 1); + } - case BAND_VHF: - if (freq <= 180000) c = 0; - else if (freq <= 188200) c = 1; - else if (freq <= 196400) c = 2; - else c = 3; - value = 1; - break; - case BAND_LBAND: - if (freq <= 1500000) c = 0; - else if (freq <= 1600000) c = 1; - else c = 3; - break; + dib0070_write_reg(st, 0x20, 0x0040 | 0x0020 | 0x0010 | 0x0008 | 0x0002 | 0x0001 | st->current_tune_table_index->tuner_enable); - case BAND_SBAND: - c = 7; - dib0070_write_reg(st, 0x1d,0xFFFF); - break; + dprintk( "REFDIV: %hd, FREF: %d", REFDIV, FREF); + dprintk( "FBDIV: %d, Rest: %d", FBDiv, Rest); + dprintk( "Num: %hd, Den: %hd, SD: %hd",(u16) Rest, Den, (st->lo4 >> 12) & 0x1); + dprintk( "HFDIV code: %hd", st->current_tune_table_index->hfdiv); + dprintk( "VCO = %hd", st->current_tune_table_index->vco_band); + dprintk( "VCOF: ((%hd*%d) << 1))", st->current_tune_table_index->vco_multi, freq); - case BAND_UHF: - default: - if (st->cfg->flip_chip) { - if (freq <= 550000) c = 0; - else if (freq <= 590000) c = 1; - else if (freq <= 666000) c = 3; - else c = 5; - } else { - if (freq <= 550000) c = 2; - else if (freq <= 650000) c = 3; - else if (freq <= 750000) c = 5; - else if (freq <= 850000) c = 6; - else c = 7; - } - value = 2; - break; + *tune_state = CT_TUNER_STEP_0; + } else { /* we are already tuned to this frequency - the configuration is correct */ + ret = 50; /* wakeup time */ + *tune_state = CT_TUNER_STEP_5; + } + } else if ((*tune_state > CT_TUNER_START) && (*tune_state < CT_TUNER_STEP_4)) { + + ret = dib0070_captrim(st, tune_state); + + } else if (*tune_state == CT_TUNER_STEP_4) { + const struct dib0070_wbd_gain_cfg *tmp = st->cfg->wbd_gain; + if (tmp != NULL) { + while (freq/1000 > tmp->freq) /* find the right one */ + tmp++; + dib0070_write_reg(st, 0x0f, (0 << 15) | (1 << 14) | (3 << 12) | (tmp->wbd_gain_val << 9) | (0 << 8) | (1 << 7) | (st->current_tune_table_index->wbdmux << 0)); + st->wbd_gain_current = tmp->wbd_gain_val; + } else { + dib0070_write_reg(st, 0x0f, (0 << 15) | (1 << 14) | (3 << 12) | (6 << 9) | (0 << 8) | (1 << 7) | (st->current_tune_table_index->wbdmux << 0)); + st->wbd_gain_current = 6; } - /* default: LNA_MATCH=7, BIAS=3 */ - dib0070_write_reg(st, 0x07, (value << 11) | (7 << 8) | (c << 3) | (3 << 0)); - dib0070_write_reg(st, 0x08, (c << 10) | (3 << 7) | (127)); + dib0070_write_reg(st, 0x06, 0x3fff); + dib0070_write_reg(st, 0x07, (st->current_tune_table_index->switch_trim << 11) | (7 << 8) | (st->lna_match->lna_band << 3) | (3 << 0)); + dib0070_write_reg(st, 0x08, (st->lna_match->lna_band << 10) | (3 << 7) | (127)); dib0070_write_reg(st, 0x0d, 0x0d80); dib0070_write_reg(st, 0x18, 0x07ff); dib0070_write_reg(st, 0x17, 0x0033); - return 0; + + *tune_state = CT_TUNER_STEP_5; + } else if (*tune_state == CT_TUNER_STEP_5) { + dib0070_set_bandwidth(fe, ch); + *tune_state = CT_TUNER_STOP; + } else { + ret = FE_CALLBACK_TIME_NEVER; /* tuner finished, time to call again infinite */ + } + return ret; +} + + +static int dib0070_tune(struct dvb_frontend *fe, struct dvb_frontend_parameters *p) +{ + struct dib0070_state *state = fe->tuner_priv; + uint32_t ret; + + state->tune_state = CT_TUNER_START; + + do { + ret = dib0070_tune_digital(fe, p); + if (ret != FE_CALLBACK_TIME_NEVER) + msleep(ret/10); + else + break; + } while (state->tune_state != CT_TUNER_STOP); + + return 0; } static int dib0070_wakeup(struct dvb_frontend *fe) @@ -395,7 +505,7 @@ static int dib0070_sleep(struct dvb_frontend *fe) return 0; } -static u16 dib0070_p1f_defaults[] = +static const u16 dib0070_p1f_defaults[] = { 7, 0x02, @@ -434,45 +544,40 @@ static u16 dib0070_p1f_defaults[] = 0, }; -static void dib0070_wbd_calibration(struct dvb_frontend *fe) +static u16 dib0070_read_wbd_offset(struct dib0070_state *state, u8 gain) { - u16 wbd_offs; - struct dib0070_state *state = fe->tuner_priv; - - if (state->cfg->sleep) - state->cfg->sleep(fe, 0); - - dib0070_write_reg(state, 0x0f, 0x6d81); - dib0070_write_reg(state, 0x20, 0x0040 | 0x0020 | 0x0010 | 0x0008 | 0x0002 | 0x0001); - msleep(9); - wbd_offs = dib0070_read_reg(state, 0x19); - dib0070_write_reg(state, 0x20, 0); - state->wbd_ff_offset = ((wbd_offs * 8 * 18 / 33 + 1) / 2); - dprintk( "WBDStart = %d (Vargen) - FF = %hd", (u32) wbd_offs * 1800/1024, state->wbd_ff_offset); - - if (state->cfg->sleep) - state->cfg->sleep(fe, 1); + u16 tuner_en = dib0070_read_reg(state, 0x20); + u16 offset; + + dib0070_write_reg(state, 0x18, 0x07ff); + dib0070_write_reg(state, 0x20, 0x0800 | 0x4000 | 0x0040 | 0x0020 | 0x0010 | 0x0008 | 0x0002 | 0x0001); + dib0070_write_reg(state, 0x0f, (1 << 14) | (2 << 12) | (gain << 9) | (1 << 8) | (1 << 7) | (0 << 0)); + msleep(9); + offset = dib0070_read_reg(state, 0x19); + dib0070_write_reg(state, 0x20, tuner_en); + return offset; +} +static void dib0070_wbd_offset_calibration(struct dib0070_state *state) +{ + u8 gain; + for (gain = 6; gain < 8; gain++) { + state->wbd_offset_3_3[gain - 6] = ((dib0070_read_wbd_offset(state, gain) * 8 * 18 / 33 + 1) / 2); + dprintk( "Gain: %d, WBDOffset (3.3V) = %hd", gain, state->wbd_offset_3_3[gain-6]); + } } u16 dib0070_wbd_offset(struct dvb_frontend *fe) { struct dib0070_state *st = fe->tuner_priv; - return st->wbd_ff_offset; + return st->wbd_offset_3_3[st->wbd_gain_current - 6]; } EXPORT_SYMBOL(dib0070_wbd_offset); -static int dib0070_set_ctrl_lo5(struct dvb_frontend *fe, u8 vco_bias_trim, u8 hf_div_trim, u8 cp_current, u8 third_order_filt) -{ - struct dib0070_state *state = fe->tuner_priv; - u16 lo5 = (third_order_filt << 14) | (0 << 13) | (1 << 12) | (3 << 9) | (cp_current << 6) | (hf_div_trim << 3) | (vco_bias_trim << 0); - dprintk( "CTRL_LO5: 0x%x", lo5); - return dib0070_write_reg(state, 0x15, lo5); -} - #define pgm_read_word(w) (*w) -static int dib0070_reset(struct dib0070_state *state) +static int dib0070_reset(struct dvb_frontend *fe) { + struct dib0070_state *state = fe->tuner_priv; u16 l, r, *n; HARD_RESET(state); @@ -482,6 +587,8 @@ static int dib0070_reset(struct dib0070_state *state) if ((dib0070_read_reg(state, 0x22) >> 9) & 0x1) state->revision = (dib0070_read_reg(state, 0x1f) >> 8) & 0xff; else +#else +#warning forcing SBAND #endif state->revision = DIB0070S_P1A; @@ -511,24 +618,27 @@ static int dib0070_reset(struct dib0070_state *state) else r = 2; + r |= state->cfg->osc_buffer_state << 3; dib0070_write_reg(state, 0x10, r); - dib0070_write_reg(state, 0x1f, (1 << 8) | ((state->cfg->clock_pad_drive & 0xf) << 4)); + dib0070_write_reg(state, 0x1f, (1 << 8) | ((state->cfg->clock_pad_drive & 0xf) << 5)); if (state->cfg->invert_iq) { r = dib0070_read_reg(state, 0x02) & 0xffdf; dib0070_write_reg(state, 0x02, r | (1 << 5)); } - - if (state->revision == DIB0070S_P1A) - dib0070_set_ctrl_lo5(state->fe, 4, 7, 3, 1); - else - dib0070_set_ctrl_lo5(state->fe, 4, 4, 2, 0); + if (state->revision == DIB0070S_P1A) + dib0070_set_ctrl_lo5(fe, 2, 4, 3, 0); + else + dib0070_set_ctrl_lo5(fe, 5, 4, state->cfg->charge_pump, state->cfg->enable_third_order_filter); dib0070_write_reg(state, 0x01, (54 << 9) | 0xc8); - return 0; + + dib0070_wbd_offset_calibration(state); + + return 0; } @@ -539,7 +649,7 @@ static int dib0070_release(struct dvb_frontend *fe) return 0; } -static struct dvb_tuner_ops dib0070_ops = { +static const struct dvb_tuner_ops dib0070_ops = { .info = { .name = "DiBcom DiB0070", .frequency_min = 45000000, @@ -550,7 +660,8 @@ static struct dvb_tuner_ops dib0070_ops = { .init = dib0070_wakeup, .sleep = dib0070_sleep, - .set_params = dib0070_tune_digital, + .set_params = dib0070_tune, + // .get_frequency = dib0070_get_frequency, // .get_bandwidth = dib0070_get_bandwidth }; @@ -566,11 +677,9 @@ struct dvb_frontend * dib0070_attach(struct dvb_frontend *fe, struct i2c_adapter state->fe = fe; fe->tuner_priv = state; - if (dib0070_reset(state) != 0) + if (dib0070_reset(fe) != 0) goto free_mem; - dib0070_wbd_calibration(fe); - printk(KERN_INFO "DiB0070: successfully identified\n"); memcpy(&fe->ops.tuner_ops, &dib0070_ops, sizeof(struct dvb_tuner_ops)); diff --git a/drivers/media/dvb/frontends/dib0070.h b/drivers/media/dvb/frontends/dib0070.h index 9670f5d20cfb..8c1c51cfaff1 100644 --- a/drivers/media/dvb/frontends/dib0070.h +++ b/drivers/media/dvb/frontends/dib0070.h @@ -15,6 +15,11 @@ struct i2c_adapter; #define DEFAULT_DIB0070_I2C_ADDRESS 0x60 +struct dib0070_wbd_gain_cfg { + u16 freq; + u16 wbd_gain_val; +}; + struct dib0070_config { u8 i2c_address; @@ -35,6 +40,12 @@ struct dib0070_config { u8 force_crystal_mode; /* if == 0 -> decision is made in the driver default: <24 -> 2, >=24 -> 1 */ u8 flip_chip; + u8 enable_third_order_filter; + u8 charge_pump; + + const struct dib0070_wbd_gain_cfg * wbd_gain; + + u8 vga_filter; }; #if defined(CONFIG_DVB_TUNER_DIB0070) || (defined(CONFIG_DVB_TUNER_DIB0070_MODULE) && defined(MODULE)) -- cgit v1.2.2 From 2a6a30e05cc4afa4aa4da406ece75e6846d5b408 Mon Sep 17 00:00:00 2001 From: Patrick Boettcher Date: Mon, 17 Aug 2009 05:13:28 -0300 Subject: V4L/DVB (12899): DiB0070: Indenting driver with indent -linux In order to follow a little bit the kernel coding style from now on after the generation of that driver file and indent -linux call is emitted. Signed-off-by: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/dib0070.c | 768 +++++++++++++++++----------------- drivers/media/dvb/frontends/dib0070.h | 31 +- 2 files changed, 405 insertions(+), 394 deletions(-) (limited to 'drivers/media/dvb') diff --git a/drivers/media/dvb/frontends/dib0070.c b/drivers/media/dvb/frontends/dib0070.c index 2eb9bdb4dbb3..2be17b93e0bd 100644 --- a/drivers/media/dvb/frontends/dib0070.c +++ b/drivers/media/dvb/frontends/dib0070.c @@ -50,16 +50,16 @@ MODULE_PARM_DESC(debug, "turn on debugging (default: 0)"); #define DIB0070S_P1A 0x02 enum frontend_tune_state { - CT_TUNER_START = 10, - CT_TUNER_STEP_0, - CT_TUNER_STEP_1, - CT_TUNER_STEP_2, - CT_TUNER_STEP_3, - CT_TUNER_STEP_4, - CT_TUNER_STEP_5, - CT_TUNER_STEP_6, - CT_TUNER_STEP_7, - CT_TUNER_STOP, + CT_TUNER_START = 10, + CT_TUNER_STEP_0, + CT_TUNER_STEP_1, + CT_TUNER_STEP_2, + CT_TUNER_STEP_3, + CT_TUNER_STEP_4, + CT_TUNER_STEP_5, + CT_TUNER_STEP_6, + CT_TUNER_STEP_7, + CT_TUNER_STOP, }; #define FE_CALLBACK_TIME_NEVER 0xffffffff @@ -71,10 +71,10 @@ struct dib0070_state { u16 wbd_ff_offset; u8 revision; - enum frontend_tune_state tune_state; - u32 current_rf; + enum frontend_tune_state tune_state; + u32 current_rf; - /* for the captrim binary search */ + /* for the captrim binary search */ s8 step; u16 adc_diff; @@ -85,7 +85,7 @@ struct dib0070_state { const struct dib0070_tuning *current_tune_table_index; const struct dib0070_lna_match *lna_match; - u8 wbd_gain_current; + u8 wbd_gain_current; u16 wbd_offset_3_3[2]; }; @@ -93,8 +93,8 @@ static uint16_t dib0070_read_reg(struct dib0070_state *state, u8 reg) { u8 b[2]; struct i2c_msg msg[2] = { - { .addr = state->cfg->i2c_address, .flags = 0, .buf = ®, .len = 1 }, - { .addr = state->cfg->i2c_address, .flags = I2C_M_RD, .buf = b, .len = 2 }, + {.addr = state->cfg->i2c_address,.flags = 0,.buf = ®,.len = 1}, + {.addr = state->cfg->i2c_address,.flags = I2C_M_RD,.buf = b,.len = 2}, }; if (i2c_transfer(state->i2c, msg, 2) != 2) { printk(KERN_WARNING "DiB0070 I2C read failed\n"); @@ -106,7 +106,7 @@ static uint16_t dib0070_read_reg(struct dib0070_state *state, u8 reg) static int dib0070_write_reg(struct dib0070_state *state, u8 reg, u16 val) { u8 b[3] = { reg, val >> 8, val & 0xff }; - struct i2c_msg msg = { .addr = state->cfg->i2c_address, .flags = 0, .buf = b, .len = 3 }; + struct i2c_msg msg = {.addr = state->cfg->i2c_address,.flags = 0,.buf = b,.len = 3 }; if (i2c_transfer(state->i2c, &msg, 1) != 1) { printk(KERN_WARNING "DiB0070 I2C write failed\n"); return -EREMOTEIO; @@ -124,34 +124,34 @@ static int dib0070_write_reg(struct dib0070_state *state, u8 reg, u16 val) static int dib0070_set_bandwidth(struct dvb_frontend *fe, struct dvb_frontend_parameters *ch) { - struct dib0070_state *st = fe->tuner_priv; - u16 tmp = dib0070_read_reg(st, 0x02) & 0x3fff; - - if (fe->dtv_property_cache.bandwidth_hz/1000 > 7000) - tmp |= (0 << 14); - else if (fe->dtv_property_cache.bandwidth_hz/1000 > 6000) - tmp |= (1 << 14); - else if (fe->dtv_property_cache.bandwidth_hz/1000 > 5000) - tmp |= (2 << 14); - else - tmp |= (3 << 14); - - dib0070_write_reg(st, 0x02, tmp); - - /* sharpen the BB filter in ISDB-T to have higher immunity to adjacent channels */ - if (fe->dtv_property_cache.delivery_system == SYS_ISDBT) { - u16 value = dib0070_read_reg(st, 0x17); - - dib0070_write_reg(st, 0x17, value & 0xfffc); - tmp = dib0070_read_reg(st, 0x01) & 0x01ff; - dib0070_write_reg(st, 0x01, tmp | (60 << 9)); - - dib0070_write_reg(st, 0x17, value); - } + struct dib0070_state *state = fe->tuner_priv; + u16 tmp = dib0070_read_reg(state, 0x02) & 0x3fff; + + if (state->fe->dtv_property_cache.bandwidth_hz / 1000 > 7000) + tmp |= (0 << 14); + else if (state->fe->dtv_property_cache.bandwidth_hz / 1000 > 6000) + tmp |= (1 << 14); + else if (state->fe->dtv_property_cache.bandwidth_hz / 1000 > 5000) + tmp |= (2 << 14); + else + tmp |= (3 << 14); + + dib0070_write_reg(state, 0x02, tmp); + + /* sharpen the BB filter in ISDB-T to have higher immunity to adjacent channels */ + if (state->fe->dtv_property_cache.delivery_system == SYS_ISDBT) { + u16 value = dib0070_read_reg(state, 0x17); + + dib0070_write_reg(state, 0x17, value & 0xfffc); + tmp = dib0070_read_reg(state, 0x01) & 0x01ff; + dib0070_write_reg(state, 0x01, tmp | (60 << 9)); + + dib0070_write_reg(state, 0x17, value); + } return 0; } -static int dib0070_captrim(struct dib0070_state *st, enum frontend_tune_state *tune_state) +static int dib0070_captrim(struct dib0070_state *state, enum frontend_tune_state *tune_state) { int8_t step_sign; u16 adc; @@ -159,26 +159,26 @@ static int dib0070_captrim(struct dib0070_state *st, enum frontend_tune_state *t if (*tune_state == CT_TUNER_STEP_0) { - dib0070_write_reg(st, 0x0f, 0xed10); - dib0070_write_reg(st, 0x17, 0x0034); + dib0070_write_reg(state, 0x0f, 0xed10); + dib0070_write_reg(state, 0x17, 0x0034); - dib0070_write_reg(st, 0x18, 0x0032); - st->step = st->captrim = st->fcaptrim = 64; - st->adc_diff = 3000; + dib0070_write_reg(state, 0x18, 0x0032); + state->step = state->captrim = state->fcaptrim = 64; + state->adc_diff = 3000; ret = 20; - *tune_state = CT_TUNER_STEP_1; + *tune_state = CT_TUNER_STEP_1; } else if (*tune_state == CT_TUNER_STEP_1) { - st->step /= 2; - dib0070_write_reg(st, 0x14, st->lo4 | st->captrim); + state->step /= 2; + dib0070_write_reg(state, 0x14, state->lo4 | state->captrim); ret = 15; *tune_state = CT_TUNER_STEP_2; } else if (*tune_state == CT_TUNER_STEP_2) { - adc = dib0070_read_reg(st, 0x19); + adc = dib0070_read_reg(state, 0x19); - dprintk( "CAPTRIM=%hd; ADC = %hd (ADC) & %dmV", st->captrim, adc, (u32) adc*(u32)1800/(u32)1024); + dprintk("CAPTRIM=%hd; ADC = %hd (ADC) & %dmV", state->captrim, adc, (u32) adc * (u32) 1800 / (u32) 1024); if (adc >= 400) { adc -= 400; @@ -188,24 +188,22 @@ static int dib0070_captrim(struct dib0070_state *st, enum frontend_tune_state *t step_sign = 1; } - if (adc < st->adc_diff) { - dprintk( "CAPTRIM=%hd is closer to target (%hd/%hd)", st->captrim, adc, st->adc_diff); - st->adc_diff = adc; - st->fcaptrim = st->captrim; - - + if (adc < state->adc_diff) { + dprintk("CAPTRIM=%hd is closer to target (%hd/%hd)", state->captrim, adc, state->adc_diff); + state->adc_diff = adc; + state->fcaptrim = state->captrim; } - st->captrim += (step_sign * st->step); + state->captrim += (step_sign * state->step); - if (st->step >= 1) + if (state->step >= 1) *tune_state = CT_TUNER_STEP_1; else *tune_state = CT_TUNER_STEP_3; } else if (*tune_state == CT_TUNER_STEP_3) { - dib0070_write_reg(st, 0x14, st->lo4 | st->fcaptrim); - dib0070_write_reg(st, 0x18, 0x07ff); + dib0070_write_reg(state, 0x14, state->lo4 | state->fcaptrim); + dib0070_write_reg(state, 0x18, 0x07ff); *tune_state = CT_TUNER_STEP_4; } @@ -215,374 +213,391 @@ static int dib0070_captrim(struct dib0070_state *st, enum frontend_tune_state *t static int dib0070_set_ctrl_lo5(struct dvb_frontend *fe, u8 vco_bias_trim, u8 hf_div_trim, u8 cp_current, u8 third_order_filt) { struct dib0070_state *state = fe->tuner_priv; - u16 lo5 = (third_order_filt << 14) | (0 << 13) | (1 << 12) | (3 << 9) | (cp_current << 6) | (hf_div_trim << 3) | (vco_bias_trim << 0); - dprintk( "CTRL_LO5: 0x%x", lo5); + u16 lo5 = (third_order_filt << 14) | (0 << 13) | (1 << 12) | (3 << 9) | (cp_current << 6) | (hf_div_trim << 3) | (vco_bias_trim << 0); + dprintk("CTRL_LO5: 0x%x", lo5); return dib0070_write_reg(state, 0x15, lo5); } -struct dib0070_tuning -{ - u32 max_freq; /* for every frequency less than or equal to that field: this information is correct */ - u8 switch_trim; - u8 vco_band; - u8 hfdiv; - u8 vco_multi; - u8 presc; - u8 wbdmux; - u16 tuner_enable; -}; - -struct dib0070_lna_match +void dib0070_ctrl_agc_filter(struct dvb_frontend *fe, u8 open) { - u32 max_freq; /* for every frequency less than or equal to that field: this information is correct */ - u8 lna_band; -}; + struct dib0070_state *state = fe->tuner_priv; -static const struct dib0070_tuning dib0070s_tuning_table[] = + if (open) { + dib0070_write_reg(state, 0x1b, 0xff00); + dib0070_write_reg(state, 0x1a, 0x0000); + } else { + dib0070_write_reg(state, 0x1b, 0x4112); + if (state->cfg->vga_filter != 0) { + dib0070_write_reg(state, 0x1a, state->cfg->vga_filter); + dprintk("vga filter register is set to %x", state->cfg->vga_filter); + } else + dib0070_write_reg(state, 0x1a, 0x0009); + } +} -{ - { 570000, 2, 1, 3, 6, 6, 2, 0x4000 | 0x0800 }, /* UHF */ - { 700000, 2, 0, 2, 4, 2, 2, 0x4000 | 0x0800 }, - { 863999, 2, 1, 2, 4, 2, 2, 0x4000 | 0x0800 }, - { 1500000, 0, 1, 1, 2, 2, 4, 0x2000 | 0x0400 }, /* LBAND */ - { 1600000, 0, 1, 1, 2, 2, 4, 0x2000 | 0x0400 }, - { 2000000, 0, 1, 1, 2, 2, 4, 0x2000 | 0x0400 }, - { 0xffffffff, 0, 0, 8, 1, 2, 1, 0x8000 | 0x1000 }, /* SBAND */ +EXPORT_SYMBOL(dib0070_ctrl_agc_filter); +struct dib0070_tuning { + u32 max_freq; /* for every frequency less than or equal to that field: this information is correct */ + u8 switch_trim; + u8 vco_band; + u8 hfdiv; + u8 vco_multi; + u8 presc; + u8 wbdmux; + u16 tuner_enable; }; -static const struct dib0070_tuning dib0070_tuning_table[] = - -{ - { 115000, 1, 0, 7, 24, 2, 1, 0x8000 | 0x1000 }, /* FM below 92MHz cannot be tuned */ - { 179500, 1, 0, 3, 16, 2, 1, 0x8000 | 0x1000 }, /* VHF */ - { 189999, 1, 1, 3, 16, 2, 1, 0x8000 | 0x1000 }, - { 250000, 1, 0, 6, 12, 2, 1, 0x8000 | 0x1000 }, - { 569999, 2, 1, 5, 6, 2, 2, 0x4000 | 0x0800 }, /* UHF */ - { 699999, 2, 0 ,1, 4, 2, 2, 0x4000 | 0x0800 }, - { 863999, 2, 1, 1, 4, 2, 2, 0x4000 | 0x0800 }, - { 0xffffffff, 0, 1, 0, 2, 2, 4, 0x2000 | 0x0400 }, /* LBAND or everything higher than UHF */ +struct dib0070_lna_match { + u32 max_freq; /* for every frequency less than or equal to that field: this information is correct */ + u8 lna_band; }; -static const struct dib0070_lna_match dib0070_lna_flip_chip[] = +static const struct dib0070_tuning dib0070s_tuning_table[] = { + {570000, 2, 1, 3, 6, 6, 2, 0x4000 | 0x0800}, /* UHF */ + {700000, 2, 0, 2, 4, 2, 2, 0x4000 | 0x0800}, + {863999, 2, 1, 2, 4, 2, 2, 0x4000 | 0x0800}, + {1500000, 0, 1, 1, 2, 2, 4, 0x2000 | 0x0400}, /* LBAND */ + {1600000, 0, 1, 1, 2, 2, 4, 0x2000 | 0x0400}, + {2000000, 0, 1, 1, 2, 2, 4, 0x2000 | 0x0400}, + {0xffffffff, 0, 0, 8, 1, 2, 1, 0x8000 | 0x1000}, /* SBAND */ +}; -{ - { 180000, 0 }, /* VHF */ - { 188000, 1 }, - { 196400, 2 }, - { 250000, 3 }, - { 550000, 0 }, /* UHF */ - { 590000, 1 }, - { 666000, 3 }, - { 864000, 5 }, - { 1500000, 0 }, /* LBAND or everything higher than UHF */ - { 1600000, 1 }, - { 2000000, 3 }, - { 0xffffffff, 7 }, +static const struct dib0070_tuning dib0070_tuning_table[] = { + {115000, 1, 0, 7, 24, 2, 1, 0x8000 | 0x1000}, /* FM below 92MHz cannot be tuned */ + {179500, 1, 0, 3, 16, 2, 1, 0x8000 | 0x1000}, /* VHF */ + {189999, 1, 1, 3, 16, 2, 1, 0x8000 | 0x1000}, + {250000, 1, 0, 6, 12, 2, 1, 0x8000 | 0x1000}, + {569999, 2, 1, 5, 6, 2, 2, 0x4000 | 0x0800}, /* UHF */ + {699999, 2, 0, 1, 4, 2, 2, 0x4000 | 0x0800}, + {863999, 2, 1, 1, 4, 2, 2, 0x4000 | 0x0800}, + {0xffffffff, 0, 1, 0, 2, 2, 4, 0x2000 | 0x0400}, /* LBAND or everything higher than UHF */ }; -static const struct dib0070_lna_match dib0070_lna[] = +static const struct dib0070_lna_match dib0070_lna_flip_chip[] = { + {180000, 0}, /* VHF */ + {188000, 1}, + {196400, 2}, + {250000, 3}, + {550000, 0}, /* UHF */ + {590000, 1}, + {666000, 3}, + {864000, 5}, + {1500000, 0}, /* LBAND or everything higher than UHF */ + {1600000, 1}, + {2000000, 3}, + {0xffffffff, 7}, +}; -{ - { 180000, 0 }, /* VHF */ - { 188000, 1 }, - { 196400, 2 }, - { 250000, 3 }, - { 550000, 2 }, /* UHF */ - { 650000, 3 }, - { 750000, 5 }, - { 850000, 6 }, - { 864000, 7 }, - { 1500000, 0 }, /* LBAND or everything higher than UHF */ - { 1600000, 1 }, - { 2000000, 3 }, - { 0xffffffff, 7 }, +static const struct dib0070_lna_match dib0070_lna[] = { + {180000, 0}, /* VHF */ + {188000, 1}, + {196400, 2}, + {250000, 3}, + {550000, 2}, /* UHF */ + {650000, 3}, + {750000, 5}, + {850000, 6}, + {864000, 7}, + {1500000, 0}, /* LBAND or everything higher than UHF */ + {1600000, 1}, + {2000000, 3}, + {0xffffffff, 7}, }; -#define LPF 100 // define for the loop filter 100kHz by default 16-07-06 +#define LPF 100 // define for the loop filter 100kHz by default 16-07-06 static int dib0070_tune_digital(struct dvb_frontend *fe, struct dvb_frontend_parameters *ch) { - struct dib0070_state *st = fe->tuner_priv; - - const struct dib0070_tuning *tune; - const struct dib0070_lna_match *lna_match; - - enum frontend_tune_state *tune_state = &st->tune_state; - int ret = 10; /* 1ms is the default delay most of the time */ - - u8 band = (u8)BAND_OF_FREQUENCY(ch->frequency/1000); - u32 freq = ch->frequency/1000 + (band == BAND_VHF ? st->cfg->freq_offset_khz_vhf : st->cfg->freq_offset_khz_uhf); - - - + struct dib0070_state *state = fe->tuner_priv; + const struct dib0070_tuning *tune; + const struct dib0070_lna_match *lna_match; -#ifdef CONFIG_STANDARD_ISDBT - if (fe->dtv_property_cache.delivery_system == SYS_ISDBT && ch->u.isdbt.sb_mode == 1) - if ( ( (ch->u.isdbt.sb_conn_total_seg % 2) && (ch->u.isdbt.sb_wanted_seg == ((ch->u.isdbt.sb_conn_total_seg/2) + 1) ) ) || - ( ( (ch->u.isdbt.sb_conn_total_seg % 2) == 0) && (ch->u.isdbt.sb_wanted_seg == (ch->u.isdbt.sb_conn_total_seg/2) ) ) || - ( ( (ch->u.isdbt.sb_conn_total_seg % 2) == 0) && (ch->u.isdbt.sb_wanted_seg == ((ch->u.isdbt.sb_conn_total_seg/2)+1))) ) - freq += 850; + enum frontend_tune_state *tune_state = &state->tune_state; + int ret = 10; /* 1ms is the default delay most of the time */ + + u8 band = (u8) BAND_OF_FREQUENCY(fe->dtv_property_cache.frequency / 1000); + u32 freq = fe->dtv_property_cache.frequency / 1000 + (band == BAND_VHF ? state->cfg->freq_offset_khz_vhf : state->cfg->freq_offset_khz_uhf); + +#ifdef CONFIG_SYS_ISDBT + if (state->fe->dtv_property_cache.delivery_system == SYS_ISDBT && state->fe->dtv_property_cache.isdbt_sb_mode == 1) + if (((state->fe->dtv_property_cache.isdbt_sb_segment_count % 2) + && (state->fe->dtv_property_cache.isdbt_sb_segment_idx == ((state->fe->dtv_property_cache.isdbt_sb_segment_count / 2) + 1))) + || (((state->fe->dtv_property_cache.isdbt_sb_segment_count % 2) == 0) + && (state->fe->dtv_property_cache.isdbt_sb_segment_idx == (state->fe->dtv_property_cache.isdbt_sb_segment_count / 2))) + || (((state->fe->dtv_property_cache.isdbt_sb_segment_count % 2) == 0) + && (state->fe->dtv_property_cache.isdbt_sb_segment_idx == ((state->fe->dtv_property_cache.isdbt_sb_segment_count / 2) + 1)))) + freq += 850; #endif - if (st->current_rf != freq) { - - switch (st->revision) { - case DIB0070S_P1A: - tune = dib0070s_tuning_table; - lna_match = dib0070_lna; - break; - default: - tune = dib0070_tuning_table; - if (st->cfg->flip_chip) - lna_match = dib0070_lna_flip_chip; - else - lna_match = dib0070_lna; - break; - } - while (freq > tune->max_freq) /* find the right one */ - tune++; - while (freq > lna_match->max_freq) /* find the right one */ - lna_match++; - - st->current_tune_table_index = tune; - st->lna_match = lna_match; - } - - if (*tune_state == CT_TUNER_START) { - dprintk( "Tuning for Band: %hd (%d kHz)", band, freq); - if (st->current_rf != freq) { - u8 REFDIV; - u32 FBDiv, Rest, FREF, VCOF_kHz; - u8 Den; - - st->current_rf = freq; - st->lo4 = (st->current_tune_table_index->vco_band << 11) | (st->current_tune_table_index->hfdiv << 7); - - - dib0070_write_reg(st, 0x17, 0x30); - - - VCOF_kHz = st->current_tune_table_index->vco_multi * freq * 2; - - switch (band) { - case BAND_VHF: - REFDIV = (u8) ((st->cfg->clock_khz + 9999) / 10000); - break; - case BAND_FM: - REFDIV = (u8) ((st->cfg->clock_khz) / 1000); - break; - default: - REFDIV = (u8) ( st->cfg->clock_khz / 10000); - break; - } - FREF = st->cfg->clock_khz / REFDIV; - - - - switch (st->revision) { - case DIB0070S_P1A: - FBDiv = (VCOF_kHz / st->current_tune_table_index->presc / FREF); - Rest = (VCOF_kHz / st->current_tune_table_index->presc) - FBDiv * FREF; - break; - - case DIB0070_P1G: - case DIB0070_P1F: - default: - FBDiv = (freq / (FREF / 2)); - Rest = 2 * freq - FBDiv * FREF; - break; - } - - - if (Rest < LPF) Rest = 0; - else if (Rest < 2 * LPF) Rest = 2 * LPF; - else if (Rest > (FREF - LPF)) { Rest = 0 ; FBDiv += 1; } - else if (Rest > (FREF - 2 * LPF)) Rest = FREF - 2 * LPF; - Rest = (Rest * 6528) / (FREF / 10); - - Den = 1; - if (Rest > 0) { - st->lo4 |= (1 << 14) | (1 << 12); - Den = 255; - } - - - dib0070_write_reg(st, 0x11, (u16)FBDiv); - dib0070_write_reg(st, 0x12, (Den << 8) | REFDIV); - dib0070_write_reg(st, 0x13, (u16) Rest); - - if (st->revision == DIB0070S_P1A) { - - if (band == BAND_SBAND) { - dib0070_set_ctrl_lo5(fe, 2, 4, 3, 0); - dib0070_write_reg(st, 0x1d,0xFFFF); - } else - dib0070_set_ctrl_lo5(fe, 5, 4, 3, 1); - } - + if (state->current_rf != freq) { + + switch (state->revision) { + case DIB0070S_P1A: + tune = dib0070s_tuning_table; + lna_match = dib0070_lna; + break; + default: + tune = dib0070_tuning_table; + if (state->cfg->flip_chip) + lna_match = dib0070_lna_flip_chip; + else + lna_match = dib0070_lna; + break; + } + while (freq > tune->max_freq) /* find the right one */ + tune++; + while (freq > lna_match->max_freq) /* find the right one */ + lna_match++; - dib0070_write_reg(st, 0x20, 0x0040 | 0x0020 | 0x0010 | 0x0008 | 0x0002 | 0x0001 | st->current_tune_table_index->tuner_enable); + state->current_tune_table_index = tune; + state->lna_match = lna_match; + } - dprintk( "REFDIV: %hd, FREF: %d", REFDIV, FREF); - dprintk( "FBDIV: %d, Rest: %d", FBDiv, Rest); - dprintk( "Num: %hd, Den: %hd, SD: %hd",(u16) Rest, Den, (st->lo4 >> 12) & 0x1); - dprintk( "HFDIV code: %hd", st->current_tune_table_index->hfdiv); - dprintk( "VCO = %hd", st->current_tune_table_index->vco_band); - dprintk( "VCOF: ((%hd*%d) << 1))", st->current_tune_table_index->vco_multi, freq); + if (*tune_state == CT_TUNER_START) { + dprintk("Tuning for Band: %hd (%d kHz)", band, freq); + if (state->current_rf != freq) { + u8 REFDIV; + u32 FBDiv, Rest, FREF, VCOF_kHz; + u8 Den; + + state->current_rf = freq; + state->lo4 = (state->current_tune_table_index->vco_band << 11) | (state->current_tune_table_index->hfdiv << 7); + + dib0070_write_reg(state, 0x17, 0x30); + + VCOF_kHz = state->current_tune_table_index->vco_multi * freq * 2; + + switch (band) { + case BAND_VHF: + REFDIV = (u8) ((state->cfg->clock_khz + 9999) / 10000); + break; + case BAND_FM: + REFDIV = (u8) ((state->cfg->clock_khz) / 1000); + break; + default: + REFDIV = (u8) (state->cfg->clock_khz / 10000); + break; + } + FREF = state->cfg->clock_khz / REFDIV; + + switch (state->revision) { + case DIB0070S_P1A: + FBDiv = (VCOF_kHz / state->current_tune_table_index->presc / FREF); + Rest = (VCOF_kHz / state->current_tune_table_index->presc) - FBDiv * FREF; + break; + + case DIB0070_P1G: + case DIB0070_P1F: + default: + FBDiv = (freq / (FREF / 2)); + Rest = 2 * freq - FBDiv * FREF; + break; + } + + if (Rest < LPF) + Rest = 0; + else if (Rest < 2 * LPF) + Rest = 2 * LPF; + else if (Rest > (FREF - LPF)) { + Rest = 0; + FBDiv += 1; + } else if (Rest > (FREF - 2 * LPF)) + Rest = FREF - 2 * LPF; + Rest = (Rest * 6528) / (FREF / 10); + + Den = 1; + if (Rest > 0) { + state->lo4 |= (1 << 14) | (1 << 12); + Den = 255; + } + + dib0070_write_reg(state, 0x11, (u16) FBDiv); + dib0070_write_reg(state, 0x12, (Den << 8) | REFDIV); + dib0070_write_reg(state, 0x13, (u16) Rest); + + if (state->revision == DIB0070S_P1A) { + + if (band == BAND_SBAND) { + dib0070_set_ctrl_lo5(fe, 2, 4, 3, 0); + dib0070_write_reg(state, 0x1d, 0xFFFF); + } else + dib0070_set_ctrl_lo5(fe, 5, 4, 3, 1); + } + + dib0070_write_reg(state, 0x20, + 0x0040 | 0x0020 | 0x0010 | 0x0008 | 0x0002 | 0x0001 | state->current_tune_table_index->tuner_enable); + + dprintk("REFDIV: %hd, FREF: %d", REFDIV, FREF); + dprintk("FBDIV: %d, Rest: %d", FBDiv, Rest); + dprintk("Num: %hd, Den: %hd, SD: %hd", (u16) Rest, Den, (state->lo4 >> 12) & 0x1); + dprintk("HFDIV code: %hd", state->current_tune_table_index->hfdiv); + dprintk("VCO = %hd", state->current_tune_table_index->vco_band); + dprintk("VCOF: ((%hd*%d) << 1))", state->current_tune_table_index->vco_multi, freq); + + *tune_state = CT_TUNER_STEP_0; + } else { /* we are already tuned to this frequency - the configuration is correct */ + ret = 50; /* wakeup time */ + *tune_state = CT_TUNER_STEP_5; + } + } else if ((*tune_state > CT_TUNER_START) && (*tune_state < CT_TUNER_STEP_4)) { + + ret = dib0070_captrim(state, tune_state); + + } else if (*tune_state == CT_TUNER_STEP_4) { + const struct dib0070_wbd_gain_cfg *tmp = state->cfg->wbd_gain; + if (tmp != NULL) { + while (freq / 1000 > tmp->freq) /* find the right one */ + tmp++; + dib0070_write_reg(state, 0x0f, + (0 << 15) | (1 << 14) | (3 << 12) | (tmp->wbd_gain_val << 9) | (0 << 8) | (1 << 7) | (state-> + current_tune_table_index-> + wbdmux << 0)); + state->wbd_gain_current = tmp->wbd_gain_val; + } else { + dib0070_write_reg(state, 0x0f, + (0 << 15) | (1 << 14) | (3 << 12) | (6 << 9) | (0 << 8) | (1 << 7) | (state->current_tune_table_index-> + wbdmux << 0)); + state->wbd_gain_current = 6; + } - *tune_state = CT_TUNER_STEP_0; - } else { /* we are already tuned to this frequency - the configuration is correct */ - ret = 50; /* wakeup time */ - *tune_state = CT_TUNER_STEP_5; - } - } else if ((*tune_state > CT_TUNER_START) && (*tune_state < CT_TUNER_STEP_4)) { + dib0070_write_reg(state, 0x06, 0x3fff); + dib0070_write_reg(state, 0x07, + (state->current_tune_table_index->switch_trim << 11) | (7 << 8) | (state->lna_match->lna_band << 3) | (3 << 0)); + dib0070_write_reg(state, 0x08, (state->lna_match->lna_band << 10) | (3 << 7) | (127)); + dib0070_write_reg(state, 0x0d, 0x0d80); - ret = dib0070_captrim(st, tune_state); + dib0070_write_reg(state, 0x18, 0x07ff); + dib0070_write_reg(state, 0x17, 0x0033); - } else if (*tune_state == CT_TUNER_STEP_4) { - const struct dib0070_wbd_gain_cfg *tmp = st->cfg->wbd_gain; - if (tmp != NULL) { - while (freq/1000 > tmp->freq) /* find the right one */ - tmp++; - dib0070_write_reg(st, 0x0f, (0 << 15) | (1 << 14) | (3 << 12) | (tmp->wbd_gain_val << 9) | (0 << 8) | (1 << 7) | (st->current_tune_table_index->wbdmux << 0)); - st->wbd_gain_current = tmp->wbd_gain_val; + *tune_state = CT_TUNER_STEP_5; + } else if (*tune_state == CT_TUNER_STEP_5) { + dib0070_set_bandwidth(fe, ch); + *tune_state = CT_TUNER_STOP; } else { - dib0070_write_reg(st, 0x0f, (0 << 15) | (1 << 14) | (3 << 12) | (6 << 9) | (0 << 8) | (1 << 7) | (st->current_tune_table_index->wbdmux << 0)); - st->wbd_gain_current = 6; + ret = FE_CALLBACK_TIME_NEVER; /* tuner finished, time to call again infinite */ } - - dib0070_write_reg(st, 0x06, 0x3fff); - dib0070_write_reg(st, 0x07, (st->current_tune_table_index->switch_trim << 11) | (7 << 8) | (st->lna_match->lna_band << 3) | (3 << 0)); - dib0070_write_reg(st, 0x08, (st->lna_match->lna_band << 10) | (3 << 7) | (127)); - dib0070_write_reg(st, 0x0d, 0x0d80); - - - dib0070_write_reg(st, 0x18, 0x07ff); - dib0070_write_reg(st, 0x17, 0x0033); - - - *tune_state = CT_TUNER_STEP_5; - } else if (*tune_state == CT_TUNER_STEP_5) { - dib0070_set_bandwidth(fe, ch); - *tune_state = CT_TUNER_STOP; - } else { - ret = FE_CALLBACK_TIME_NEVER; /* tuner finished, time to call again infinite */ - } - return ret; + return ret; } - static int dib0070_tune(struct dvb_frontend *fe, struct dvb_frontend_parameters *p) { - struct dib0070_state *state = fe->tuner_priv; - uint32_t ret; + struct dib0070_state *state = fe->tuner_priv; + uint32_t ret; - state->tune_state = CT_TUNER_START; + state->tune_state = CT_TUNER_START; - do { - ret = dib0070_tune_digital(fe, p); - if (ret != FE_CALLBACK_TIME_NEVER) - msleep(ret/10); - else - break; - } while (state->tune_state != CT_TUNER_STOP); + do { + ret = dib0070_tune_digital(fe, p); + if (ret != FE_CALLBACK_TIME_NEVER) + msleep(ret / 10); + else + break; + } while (state->tune_state != CT_TUNER_STOP); - return 0; + return 0; } static int dib0070_wakeup(struct dvb_frontend *fe) { - struct dib0070_state *st = fe->tuner_priv; - if (st->cfg->sleep) - st->cfg->sleep(fe, 0); + struct dib0070_state *state = fe->tuner_priv; + if (state->cfg->sleep) + state->cfg->sleep(fe, 0); return 0; } static int dib0070_sleep(struct dvb_frontend *fe) { - struct dib0070_state *st = fe->tuner_priv; - if (st->cfg->sleep) - st->cfg->sleep(fe, 1); + struct dib0070_state *state = fe->tuner_priv; + if (state->cfg->sleep) + state->cfg->sleep(fe, 1); return 0; } -static const u16 dib0070_p1f_defaults[] = - -{ +static const u16 dib0070_p1f_defaults[] = { 7, 0x02, - 0x0008, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0002, - 0x0100, + 0x0008, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0002, + 0x0100, 3, 0x0d, - 0x0d80, - 0x0001, - 0x0000, + 0x0d80, + 0x0001, + 0x0000, 4, 0x11, - 0x0000, - 0x0103, - 0x0000, - 0x0000, + 0x0000, + 0x0103, + 0x0000, + 0x0000, 3, 0x16, - 0x0004 | 0x0040, - 0x0030, - 0x07ff, + 0x0004 | 0x0040, + 0x0030, + 0x07ff, 6, 0x1b, - 0x4112, - 0xff00, - 0xc07f, - 0x0000, - 0x0180, - 0x4000 | 0x0800 | 0x0040 | 0x0020 | 0x0010 | 0x0008 | 0x0002 | 0x0001, + 0x4112, + 0xff00, + 0xc07f, + 0x0000, + 0x0180, + 0x4000 | 0x0800 | 0x0040 | 0x0020 | 0x0010 | 0x0008 | 0x0002 | 0x0001, 0, }; static u16 dib0070_read_wbd_offset(struct dib0070_state *state, u8 gain) { - u16 tuner_en = dib0070_read_reg(state, 0x20); - u16 offset; - - dib0070_write_reg(state, 0x18, 0x07ff); - dib0070_write_reg(state, 0x20, 0x0800 | 0x4000 | 0x0040 | 0x0020 | 0x0010 | 0x0008 | 0x0002 | 0x0001); - dib0070_write_reg(state, 0x0f, (1 << 14) | (2 << 12) | (gain << 9) | (1 << 8) | (1 << 7) | (0 << 0)); - msleep(9); - offset = dib0070_read_reg(state, 0x19); - dib0070_write_reg(state, 0x20, tuner_en); - return offset; + u16 tuner_en = dib0070_read_reg(state, 0x20); + u16 offset; + + dib0070_write_reg(state, 0x18, 0x07ff); + dib0070_write_reg(state, 0x20, 0x0800 | 0x4000 | 0x0040 | 0x0020 | 0x0010 | 0x0008 | 0x0002 | 0x0001); + dib0070_write_reg(state, 0x0f, (1 << 14) | (2 << 12) | (gain << 9) | (1 << 8) | (1 << 7) | (0 << 0)); + msleep(9); + offset = dib0070_read_reg(state, 0x19); + dib0070_write_reg(state, 0x20, tuner_en); + return offset; } static void dib0070_wbd_offset_calibration(struct dib0070_state *state) { - u8 gain; - for (gain = 6; gain < 8; gain++) { - state->wbd_offset_3_3[gain - 6] = ((dib0070_read_wbd_offset(state, gain) * 8 * 18 / 33 + 1) / 2); - dprintk( "Gain: %d, WBDOffset (3.3V) = %hd", gain, state->wbd_offset_3_3[gain-6]); - } + u8 gain; + for (gain = 6; gain < 8; gain++) { + state->wbd_offset_3_3[gain - 6] = ((dib0070_read_wbd_offset(state, gain) * 8 * 18 / 33 + 1) / 2); + dprintk("Gain: %d, WBDOffset (3.3V) = %hd", gain, state->wbd_offset_3_3[gain - 6]); + } } u16 dib0070_wbd_offset(struct dvb_frontend *fe) { - struct dib0070_state *st = fe->tuner_priv; - return st->wbd_offset_3_3[st->wbd_gain_current - 6]; + struct dib0070_state *state = fe->tuner_priv; + const struct dib0070_wbd_gain_cfg *tmp = state->cfg->wbd_gain; + u32 freq = fe->dtv_property_cache.frequency / 1000; + + if (tmp != NULL) { + while (freq / 1000 > tmp->freq) /* find the right one */ + tmp++; + state->wbd_gain_current = tmp->wbd_gain_val; + } else + state->wbd_gain_current = 6; + + return state->wbd_offset_3_3[state->wbd_gain_current - 6]; } EXPORT_SYMBOL(dib0070_wbd_offset); + #define pgm_read_word(w) (*w) static int dib0070_reset(struct dvb_frontend *fe) { - struct dib0070_state *state = fe->tuner_priv; + struct dib0070_state *state = fe->tuner_priv; u16 l, r, *n; HARD_RESET(state); - #ifndef FORCE_SBAND_TUNER if ((dib0070_read_reg(state, 0x22) >> 9) & 0x1) state->revision = (dib0070_read_reg(state, 0x1f) >> 8) & 0xff; @@ -590,13 +605,13 @@ static int dib0070_reset(struct dvb_frontend *fe) #else #warning forcing SBAND #endif - state->revision = DIB0070S_P1A; + state->revision = DIB0070S_P1A; /* P1F or not */ - dprintk( "Revision: %x", state->revision); + dprintk("Revision: %x", state->revision); if (state->revision == DIB0070_P1D) { - dprintk( "Error: this driver is not to be used meant for P1D or earlier"); + dprintk("Error: this driver is not to be used meant for P1D or earlier"); return -EINVAL; } @@ -605,7 +620,7 @@ static int dib0070_reset(struct dvb_frontend *fe) while (l) { r = pgm_read_word(n++); do { - dib0070_write_reg(state, (u8)r, pgm_read_word(n++)); + dib0070_write_reg(state, (u8) r, pgm_read_word(n++)); r++; } while (--l); l = pgm_read_word(n++); @@ -618,7 +633,6 @@ static int dib0070_reset(struct dvb_frontend *fe) else r = 2; - r |= state->cfg->osc_buffer_state << 3; dib0070_write_reg(state, 0x10, r); @@ -629,19 +643,18 @@ static int dib0070_reset(struct dvb_frontend *fe) dib0070_write_reg(state, 0x02, r | (1 << 5)); } - if (state->revision == DIB0070S_P1A) - dib0070_set_ctrl_lo5(fe, 2, 4, 3, 0); - else + if (state->revision == DIB0070S_P1A) + dib0070_set_ctrl_lo5(fe, 2, 4, 3, 0); + else dib0070_set_ctrl_lo5(fe, 5, 4, state->cfg->charge_pump, state->cfg->enable_third_order_filter); dib0070_write_reg(state, 0x01, (54 << 9) | 0xc8); - dib0070_wbd_offset_calibration(state); + dib0070_wbd_offset_calibration(state); - return 0; + return 0; } - static int dib0070_release(struct dvb_frontend *fe) { kfree(fe->tuner_priv); @@ -651,22 +664,22 @@ static int dib0070_release(struct dvb_frontend *fe) static const struct dvb_tuner_ops dib0070_ops = { .info = { - .name = "DiBcom DiB0070", - .frequency_min = 45000000, - .frequency_max = 860000000, - .frequency_step = 1000, - }, - .release = dib0070_release, - - .init = dib0070_wakeup, - .sleep = dib0070_sleep, - .set_params = dib0070_tune, - -// .get_frequency = dib0070_get_frequency, -// .get_bandwidth = dib0070_get_bandwidth + .name = "DiBcom DiB0070", + .frequency_min = 45000000, + .frequency_max = 860000000, + .frequency_step = 1000, + }, + .release = dib0070_release, + + .init = dib0070_wakeup, + .sleep = dib0070_sleep, + .set_params = dib0070_tune, + +// .get_frequency = dib0070_get_frequency, +// .get_bandwidth = dib0070_get_bandwidth }; -struct dvb_frontend * dib0070_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct dib0070_config *cfg) +struct dvb_frontend *dib0070_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct dib0070_config *cfg) { struct dib0070_state *state = kzalloc(sizeof(struct dib0070_state), GFP_KERNEL); if (state == NULL) @@ -674,7 +687,7 @@ struct dvb_frontend * dib0070_attach(struct dvb_frontend *fe, struct i2c_adapter state->cfg = cfg; state->i2c = i2c; - state->fe = fe; + state->fe = fe; fe->tuner_priv = state; if (dib0070_reset(fe) != 0) @@ -686,11 +699,12 @@ struct dvb_frontend * dib0070_attach(struct dvb_frontend *fe, struct i2c_adapter fe->tuner_priv = state; return fe; -free_mem: + free_mem: kfree(state); fe->tuner_priv = NULL; return NULL; } + EXPORT_SYMBOL(dib0070_attach); MODULE_AUTHOR("Patrick Boettcher "); diff --git a/drivers/media/dvb/frontends/dib0070.h b/drivers/media/dvb/frontends/dib0070.h index 8c1c51cfaff1..8a2e1e710adb 100644 --- a/drivers/media/dvb/frontends/dib0070.h +++ b/drivers/media/dvb/frontends/dib0070.h @@ -16,8 +16,8 @@ struct i2c_adapter; #define DEFAULT_DIB0070_I2C_ADDRESS 0x60 struct dib0070_wbd_gain_cfg { - u16 freq; - u16 wbd_gain_val; + u16 freq; + u16 wbd_gain_val; }; struct dib0070_config { @@ -31,32 +31,28 @@ struct dib0070_config { int freq_offset_khz_uhf; int freq_offset_khz_vhf; - u8 osc_buffer_state; /* 0= normal, 1= tri-state */ - u32 clock_khz; - u8 clock_pad_drive; /* (Drive + 1) * 2mA */ + u8 osc_buffer_state; /* 0= normal, 1= tri-state */ + u32 clock_khz; + u8 clock_pad_drive; /* (Drive + 1) * 2mA */ - u8 invert_iq; /* invert Q - in case I or Q is inverted on the board */ + u8 invert_iq; /* invert Q - in case I or Q is inverted on the board */ - u8 force_crystal_mode; /* if == 0 -> decision is made in the driver default: <24 -> 2, >=24 -> 1 */ + u8 force_crystal_mode; /* if == 0 -> decision is made in the driver default: <24 -> 2, >=24 -> 1 */ u8 flip_chip; - u8 enable_third_order_filter; - u8 charge_pump; + u8 enable_third_order_filter; + u8 charge_pump; - const struct dib0070_wbd_gain_cfg * wbd_gain; + const struct dib0070_wbd_gain_cfg *wbd_gain; - u8 vga_filter; + u8 vga_filter; }; #if defined(CONFIG_DVB_TUNER_DIB0070) || (defined(CONFIG_DVB_TUNER_DIB0070_MODULE) && defined(MODULE)) -extern struct dvb_frontend *dib0070_attach(struct dvb_frontend *fe, - struct i2c_adapter *i2c, - struct dib0070_config *cfg); +extern struct dvb_frontend *dib0070_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct dib0070_config *cfg); extern u16 dib0070_wbd_offset(struct dvb_frontend *); #else -static inline struct dvb_frontend *dib0070_attach(struct dvb_frontend *fe, - struct i2c_adapter *i2c, - struct dib0070_config *cfg) +static inline struct dvb_frontend *dib0070_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct dib0070_config *cfg) { printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); return NULL; @@ -68,5 +64,6 @@ static inline u16 dib0070_wbd_offset(struct dvb_frontend *fe) return -ENODEV; } #endif +extern void dib0070_ctrl_agc_filter(struct dvb_frontend *, u8 open); #endif -- cgit v1.2.2 From 77e2c0f5d471e2b14140f0695a1b6a718f318dd7 Mon Sep 17 00:00:00 2001 From: Patrick Boettcher Date: Mon, 17 Aug 2009 07:01:10 -0300 Subject: V4L/DVB (12900): DiB8000: added support for DiBcom ISDB-T/ISDB-Tsb demodulator DiB8000 This commit adds support for the DiB8000 ISDB-T demodulator made by DiBcom. Signed-off-by: Olivier Grenie Signed-off-by: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/Kconfig | 3 +- drivers/media/dvb/frontends/Kconfig | 8 + drivers/media/dvb/frontends/Makefile | 1 + drivers/media/dvb/frontends/dib8000.c | 2279 ++++++++++++++++++++++++++ drivers/media/dvb/frontends/dib8000.h | 79 + drivers/media/dvb/frontends/dibx000_common.c | 95 +- drivers/media/dvb/frontends/dibx000_common.h | 31 +- 7 files changed, 2449 insertions(+), 47 deletions(-) create mode 100644 drivers/media/dvb/frontends/dib8000.c create mode 100644 drivers/media/dvb/frontends/dib8000.h (limited to 'drivers/media/dvb') diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig index 8b8bc04ee980..c5ec9a5f3b3f 100644 --- a/drivers/media/dvb/dvb-usb/Kconfig +++ b/drivers/media/dvb/dvb-usb/Kconfig @@ -71,6 +71,7 @@ config DVB_USB_DIB0700 depends on DVB_USB select DVB_DIB7000P if !DVB_FE_CUSTOMISE select DVB_DIB7000M if !DVB_FE_CUSTOMISE + select DVB_DIB8000 if !DVB_FE_CUSTOMISE select DVB_DIB3000MC if !DVB_FE_CUSTOMISE select DVB_S5H1411 if !DVB_FE_CUSTOMISE select DVB_LGDT3305 if !DVB_FE_CUSTOMISE @@ -87,7 +88,7 @@ config DVB_USB_DIB0700 Avermedia and other big and small companies. For an up-to-date list of devices supported by this driver, have a look - on the Linux-DVB Wiki at www.linuxtv.org. + on the LinuxTV Wiki at www.linuxtv.org. Say Y if you own such a device and want to use it. You should build it as a module. diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig index b794e860b4e2..d7c4837fa71c 100644 --- a/drivers/media/dvb/frontends/Kconfig +++ b/drivers/media/dvb/frontends/Kconfig @@ -484,6 +484,14 @@ config DVB_S921 AN ISDB-T DQPSK, QPSK, 16QAM and 64QAM 1seg tuner module. Say Y when you want to support this frontend. +config DVB_DIB8000 + tristate "DiBcom 8000MB/MC" + depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE + help + A driver for DiBcom's DiB8000 ISDB-T/ISDB-Tsb demodulator. + Say Y when you want to support this frontend. + comment "Digital terrestrial only tuners/PLL" depends on DVB_CORE diff --git a/drivers/media/dvb/frontends/Makefile b/drivers/media/dvb/frontends/Makefile index 3b49d37ab5fa..3523767e7a76 100644 --- a/drivers/media/dvb/frontends/Makefile +++ b/drivers/media/dvb/frontends/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_DVB_DIB3000MB) += dib3000mb.o obj-$(CONFIG_DVB_DIB3000MC) += dib3000mc.o dibx000_common.o obj-$(CONFIG_DVB_DIB7000M) += dib7000m.o dibx000_common.o obj-$(CONFIG_DVB_DIB7000P) += dib7000p.o dibx000_common.o +obj-$(CONFIG_DVB_DIB8000) += dib8000.o dibx000_common.o obj-$(CONFIG_DVB_MT312) += mt312.o obj-$(CONFIG_DVB_VES1820) += ves1820.o obj-$(CONFIG_DVB_VES1X93) += ves1x93.o diff --git a/drivers/media/dvb/frontends/dib8000.c b/drivers/media/dvb/frontends/dib8000.c new file mode 100644 index 000000000000..99ee6b09ce1d --- /dev/null +++ b/drivers/media/dvb/frontends/dib8000.c @@ -0,0 +1,2279 @@ +/* + * Linux-DVB Driver for DiBcom's DiB8000 chip (ISDB-T). + * + * Copyright (C) 2009 DiBcom (http://www.dibcom.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. + */ +#include +#include +#include "dvb_math.h" + +#include "dvb_frontend.h" + +#include "dib8000.h" + +#define LAYER_ALL -1 +#define LAYER_A 1 +#define LAYER_B 2 +#define LAYER_C 3 + +#define FE_CALLBACK_TIME_NEVER 0xffffffff + +static int debug = 0; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "turn on debugging (default: 0)"); + +#define dprintk(args...) do { if (debug) { printk(KERN_DEBUG "DiB8000: "); printk(args); printk("\n"); } } while (0) + +enum frontend_tune_state { + CT_AGC_START = 20, + CT_AGC_STEP_0, + CT_AGC_STEP_1, + CT_AGC_STEP_2, + CT_AGC_STEP_3, + CT_AGC_STEP_4, + CT_AGC_STOP, + + CT_DEMOD_START = 30, +}; + +#define FE_STATUS_TUNE_FAILED 0 + +struct i2c_device { + struct i2c_adapter *adap; + u8 addr; +}; + +struct dib8000_state { + struct dvb_frontend fe; + struct dib8000_config cfg; + + struct i2c_device i2c; + + struct dibx000_i2c_master i2c_master; + + u16 wbd_ref; + + u8 current_band; + u32 current_bandwidth; + struct dibx000_agc_config *current_agc; + u32 timf; + u32 timf_default; + + u8 div_force_off:1; + u8 div_state:1; + u16 div_sync_wait; + + u8 agc_state; + u8 differential_constellation; + u8 diversity_onoff; + + s16 ber_monitored_layer; + u16 gpio_dir; + u16 gpio_val; + + u16 revision; + u8 isdbt_cfg_loaded; + enum frontend_tune_state tune_state; + u32 status; +}; + +enum dib8000_power_mode { + DIB8000M_POWER_ALL = 0, + DIB8000M_POWER_INTERFACE_ONLY, +}; + +static u16 dib8000_i2c_read16(struct i2c_device *i2c, u16 reg) +{ + u8 wb[2] = { reg >> 8, reg & 0xff }; + u8 rb[2]; + struct i2c_msg msg[2] = { + {.addr = i2c->addr >> 1,.flags = 0,.buf = wb,.len = 2}, + {.addr = i2c->addr >> 1,.flags = I2C_M_RD,.buf = rb,.len = 2}, + }; + + if (i2c_transfer(i2c->adap, msg, 2) != 2) + dprintk("i2c read error on %d", reg); + + return (rb[0] << 8) | rb[1]; +} + +static u16 dib8000_read_word(struct dib8000_state *state, u16 reg) +{ + return dib8000_i2c_read16(&state->i2c, reg); +} + +static u32 dib8000_read32(struct dib8000_state *state, u16 reg) +{ + u16 rw[2]; + + rw[0] = dib8000_read_word(state, reg + 0); + rw[1] = dib8000_read_word(state, reg + 1); + + return ((rw[0] << 16) | (rw[1])); +} + +static int dib8000_i2c_write16(struct i2c_device *i2c, u16 reg, u16 val) +{ + u8 b[4] = { + (reg >> 8) & 0xff, reg & 0xff, + (val >> 8) & 0xff, val & 0xff, + }; + struct i2c_msg msg = { + .addr = i2c->addr >> 1,.flags = 0,.buf = b,.len = 4 + }; + return i2c_transfer(i2c->adap, &msg, 1) != 1 ? -EREMOTEIO : 0; +} + +static int dib8000_write_word(struct dib8000_state *state, u16 reg, u16 val) +{ + return dib8000_i2c_write16(&state->i2c, reg, val); +} + +const int16_t coeff_2k_sb_1seg_dqpsk[8] = { + (769 << 5) | 0x0a, (745 << 5) | 0x03, (595 << 5) | 0x0d, (769 << 5) | 0x0a, (920 << 5) | 0x09, (784 << 5) | 0x02, (519 << 5) | 0x0c, + (920 << 5) | 0x09 +}; + +const int16_t coeff_2k_sb_1seg[8] = { + (692 << 5) | 0x0b, (683 << 5) | 0x01, (519 << 5) | 0x09, (692 << 5) | 0x0b, 0 | 0x1f, 0 | 0x1f, 0 | 0x1f, 0 | 0x1f +}; + +const int16_t coeff_2k_sb_3seg_0dqpsk_1dqpsk[8] = { + (832 << 5) | 0x10, (912 << 5) | 0x05, (900 << 5) | 0x12, (832 << 5) | 0x10, (-931 << 5) | 0x0f, (912 << 5) | 0x04, (807 << 5) | 0x11, + (-931 << 5) | 0x0f +}; + +const int16_t coeff_2k_sb_3seg_0dqpsk[8] = { + (622 << 5) | 0x0c, (941 << 5) | 0x04, (796 << 5) | 0x10, (622 << 5) | 0x0c, (982 << 5) | 0x0c, (519 << 5) | 0x02, (572 << 5) | 0x0e, + (982 << 5) | 0x0c +}; + +const int16_t coeff_2k_sb_3seg_1dqpsk[8] = { + (699 << 5) | 0x14, (607 << 5) | 0x04, (944 << 5) | 0x13, (699 << 5) | 0x14, (-720 << 5) | 0x0d, (640 << 5) | 0x03, (866 << 5) | 0x12, + (-720 << 5) | 0x0d +}; + +const int16_t coeff_2k_sb_3seg[8] = { + (664 << 5) | 0x0c, (925 << 5) | 0x03, (937 << 5) | 0x10, (664 << 5) | 0x0c, (-610 << 5) | 0x0a, (697 << 5) | 0x01, (836 << 5) | 0x0e, + (-610 << 5) | 0x0a +}; + +const int16_t coeff_4k_sb_1seg_dqpsk[8] = { + (-955 << 5) | 0x0e, (687 << 5) | 0x04, (818 << 5) | 0x10, (-955 << 5) | 0x0e, (-922 << 5) | 0x0d, (750 << 5) | 0x03, (665 << 5) | 0x0f, + (-922 << 5) | 0x0d +}; + +const int16_t coeff_4k_sb_1seg[8] = { + (638 << 5) | 0x0d, (683 << 5) | 0x02, (638 << 5) | 0x0d, (638 << 5) | 0x0d, (-655 << 5) | 0x0a, (517 << 5) | 0x00, (698 << 5) | 0x0d, + (-655 << 5) | 0x0a +}; + +const int16_t coeff_4k_sb_3seg_0dqpsk_1dqpsk[8] = { + (-707 << 5) | 0x14, (910 << 5) | 0x06, (889 << 5) | 0x16, (-707 << 5) | 0x14, (-958 << 5) | 0x13, (993 << 5) | 0x05, (523 << 5) | 0x14, + (-958 << 5) | 0x13 +}; + +const int16_t coeff_4k_sb_3seg_0dqpsk[8] = { + (-723 << 5) | 0x13, (910 << 5) | 0x05, (777 << 5) | 0x14, (-723 << 5) | 0x13, (-568 << 5) | 0x0f, (547 << 5) | 0x03, (696 << 5) | 0x12, + (-568 << 5) | 0x0f +}; + +const int16_t coeff_4k_sb_3seg_1dqpsk[8] = { + (-940 << 5) | 0x15, (607 << 5) | 0x05, (915 << 5) | 0x16, (-940 << 5) | 0x15, (-848 << 5) | 0x13, (683 << 5) | 0x04, (543 << 5) | 0x14, + (-848 << 5) | 0x13 +}; + +const int16_t coeff_4k_sb_3seg[8] = { + (612 << 5) | 0x12, (910 << 5) | 0x04, (864 << 5) | 0x14, (612 << 5) | 0x12, (-869 << 5) | 0x13, (683 << 5) | 0x02, (869 << 5) | 0x12, + (-869 << 5) | 0x13 +}; + +const int16_t coeff_8k_sb_1seg_dqpsk[8] = { + (-835 << 5) | 0x12, (684 << 5) | 0x05, (735 << 5) | 0x14, (-835 << 5) | 0x12, (-598 << 5) | 0x10, (781 << 5) | 0x04, (739 << 5) | 0x13, + (-598 << 5) | 0x10 +}; + +const int16_t coeff_8k_sb_1seg[8] = { + (673 << 5) | 0x0f, (683 << 5) | 0x03, (808 << 5) | 0x12, (673 << 5) | 0x0f, (585 << 5) | 0x0f, (512 << 5) | 0x01, (780 << 5) | 0x0f, + (585 << 5) | 0x0f +}; + +const int16_t coeff_8k_sb_3seg_0dqpsk_1dqpsk[8] = { + (863 << 5) | 0x17, (930 << 5) | 0x07, (878 << 5) | 0x19, (863 << 5) | 0x17, (0 << 5) | 0x14, (521 << 5) | 0x05, (980 << 5) | 0x18, + (0 << 5) | 0x14 +}; + +const int16_t coeff_8k_sb_3seg_0dqpsk[8] = { + (-924 << 5) | 0x17, (910 << 5) | 0x06, (774 << 5) | 0x17, (-924 << 5) | 0x17, (-877 << 5) | 0x15, (565 << 5) | 0x04, (553 << 5) | 0x15, + (-877 << 5) | 0x15 +}; + +const int16_t coeff_8k_sb_3seg_1dqpsk[8] = { + (-921 << 5) | 0x19, (607 << 5) | 0x06, (881 << 5) | 0x19, (-921 << 5) | 0x19, (-921 << 5) | 0x14, (713 << 5) | 0x05, (1018 << 5) | 0x18, + (-921 << 5) | 0x14 +}; + +const int16_t coeff_8k_sb_3seg[8] = { + (514 << 5) | 0x14, (910 << 5) | 0x05, (861 << 5) | 0x17, (514 << 5) | 0x14, (690 << 5) | 0x14, (683 << 5) | 0x03, (662 << 5) | 0x15, + (690 << 5) | 0x14 +}; + +const int16_t ana_fe_coeff_3seg[24] = { + 81, 80, 78, 74, 68, 61, 54, 45, 37, 28, 19, 11, 4, 1022, 1017, 1013, 1010, 1008, 1008, 1008, 1008, 1010, 1014, 1017 +}; + +const int16_t ana_fe_coeff_1seg[24] = { + 249, 226, 164, 82, 5, 981, 970, 988, 1018, 20, 31, 26, 8, 1012, 1000, 1018, 1012, 8, 15, 14, 9, 3, 1017, 1003 +}; + +const int16_t ana_fe_coeff_13seg[24] = { + 396, 305, 105, -51, -77, -12, 41, 31, -11, -30, -11, 14, 15, -2, -13, -7, 5, 8, 1, -6, -7, -3, 0, 1 +}; + +static u16 fft_to_mode(struct dib8000_state *state) +{ + u16 mode; + switch (state->fe.dtv_property_cache.transmission_mode) { + case TRANSMISSION_MODE_2K: + mode = 1; + break; + case TRANSMISSION_MODE_4K: + mode = 2; + break; + default: + case TRANSMISSION_MODE_AUTO: + case TRANSMISSION_MODE_8K: + mode = 3; + break; + } + return mode; +} + +static void dib8000_set_acquisition_mode(struct dib8000_state *state) +{ + u16 nud = dib8000_read_word(state, 298); + nud |= (1 << 3) | (1 << 0); + dprintk("acquisition mode activated"); + dib8000_write_word(state, 298, nud); +} + +static int dib8000_set_output_mode(struct dib8000_state *state, int mode) +{ + u16 outreg, fifo_threshold, smo_mode, sram = 0x0205; /* by default SDRAM deintlv is enabled */ + + outreg = 0; + fifo_threshold = 1792; + smo_mode = (dib8000_read_word(state, 299) & 0x0050) | (1 << 1); + + dprintk("-I- Setting output mode for demod %p to %d", &state->fe, mode); + + switch (mode) { + case OUTMODE_MPEG2_PAR_GATED_CLK: // STBs with parallel gated clock + outreg = (1 << 10); /* 0x0400 */ + break; + case OUTMODE_MPEG2_PAR_CONT_CLK: // STBs with parallel continues clock + outreg = (1 << 10) | (1 << 6); /* 0x0440 */ + break; + case OUTMODE_MPEG2_SERIAL: // STBs with serial input + outreg = (1 << 10) | (2 << 6) | (0 << 1); /* 0x0482 */ + break; + case OUTMODE_DIVERSITY: + if (state->cfg.hostbus_diversity) { + outreg = (1 << 10) | (4 << 6); /* 0x0500 */ + sram &= 0xfdff; + } else + sram |= 0x0c00; + break; + case OUTMODE_MPEG2_FIFO: // e.g. USB feeding + smo_mode |= (3 << 1); + fifo_threshold = 512; + outreg = (1 << 10) | (5 << 6); + break; + case OUTMODE_HIGH_Z: // disable + outreg = 0; + break; + + case OUTMODE_ANALOG_ADC: + outreg = (1 << 10) | (3 << 6); + dib8000_set_acquisition_mode(state); + break; + + default: + dprintk("Unhandled output_mode passed to be set for demod %p", &state->fe); + return -EINVAL; + } + + if (state->cfg.output_mpeg2_in_188_bytes) + smo_mode |= (1 << 5); + + dib8000_write_word(state, 299, smo_mode); + dib8000_write_word(state, 300, fifo_threshold); /* synchronous fread */ + dib8000_write_word(state, 1286, outreg); + dib8000_write_word(state, 1291, sram); + + return 0; +} + +static int dib8000_set_diversity_in(struct dvb_frontend *fe, int onoff) +{ + struct dib8000_state *state = fe->demodulator_priv; + u16 sync_wait = dib8000_read_word(state, 273) & 0xfff0; + + if (!state->differential_constellation) { + dib8000_write_word(state, 272, 1 << 9); //dvsy_off_lmod4 = 1 + dib8000_write_word(state, 273, sync_wait | (1 << 2) | 2); // sync_enable = 1; comb_mode = 2 + } else { + dib8000_write_word(state, 272, 0); //dvsy_off_lmod4 = 0 + dib8000_write_word(state, 273, sync_wait); // sync_enable = 0; comb_mode = 0 + } + state->diversity_onoff = onoff; + + switch (onoff) { + case 0: /* only use the internal way - not the diversity input */ + dib8000_write_word(state, 270, 1); + dib8000_write_word(state, 271, 0); + break; + case 1: /* both ways */ + dib8000_write_word(state, 270, 6); + dib8000_write_word(state, 271, 6); + break; + case 2: /* only the diversity input */ + dib8000_write_word(state, 270, 0); + dib8000_write_word(state, 271, 1); + break; + } + return 0; +} + +static void dib8000_set_power_mode(struct dib8000_state *state, enum dib8000_power_mode mode) +{ + /* by default everything is going to be powered off */ + u16 reg_774 = 0x3fff, reg_775 = 0xffff, reg_776 = 0xffff, + reg_900 = (dib8000_read_word(state, 900) & 0xfffc) | 0x3, reg_1280 = (dib8000_read_word(state, 1280) & 0x00ff) | 0xff00; + + /* now, depending on the requested mode, we power on */ + switch (mode) { + /* power up everything in the demod */ + case DIB8000M_POWER_ALL: + reg_774 = 0x0000; + reg_775 = 0x0000; + reg_776 = 0x0000; + reg_900 &= 0xfffc; + reg_1280 &= 0x00ff; + break; + case DIB8000M_POWER_INTERFACE_ONLY: + reg_1280 &= 0x00ff; + break; + } + + dprintk("powermode : 774 : %x ; 775 : %x; 776 : %x ; 900 : %x; 1280 : %x", reg_774, reg_775, reg_776, reg_900, reg_1280); + dib8000_write_word(state, 774, reg_774); + dib8000_write_word(state, 775, reg_775); + dib8000_write_word(state, 776, reg_776); + dib8000_write_word(state, 900, reg_900); + dib8000_write_word(state, 1280, reg_1280); +} + +static int dib8000_set_adc_state(struct dib8000_state *state, enum dibx000_adc_states no) +{ + int ret = 0; + u16 reg_907 = dib8000_read_word(state, 907), reg_908 = dib8000_read_word(state, 908); + + switch (no) { + case DIBX000_SLOW_ADC_ON: + reg_908 |= (1 << 1) | (1 << 0); + ret |= dib8000_write_word(state, 908, reg_908); + reg_908 &= ~(1 << 1); + break; + + case DIBX000_SLOW_ADC_OFF: + reg_908 |= (1 << 1) | (1 << 0); + break; + + case DIBX000_ADC_ON: + reg_907 &= 0x0fff; + reg_908 &= 0x0003; + break; + + case DIBX000_ADC_OFF: // leave the VBG voltage on + reg_907 |= (1 << 14) | (1 << 13) | (1 << 12); + reg_908 |= (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2); + break; + + case DIBX000_VBG_ENABLE: + reg_907 &= ~(1 << 15); + break; + + case DIBX000_VBG_DISABLE: + reg_907 |= (1 << 15); + break; + + default: + break; + } + + ret |= dib8000_write_word(state, 907, reg_907); + ret |= dib8000_write_word(state, 908, reg_908); + + return ret; +} + +static int dib8000_set_bandwidth(struct dib8000_state *state, u32 bw) +{ + u32 timf; + + if (bw == 0) + bw = 6000; + + if (state->timf == 0) { + dprintk("using default timf"); + timf = state->timf_default; + } else { + dprintk("using updated timf"); + timf = state->timf; + } + + dib8000_write_word(state, 29, (u16) ((timf >> 16) & 0xffff)); + dib8000_write_word(state, 30, (u16) ((timf) & 0xffff)); + + return 0; +} + +static int dib8000_sad_calib(struct dib8000_state *state) +{ +/* internal */ + dib8000_write_word(state, 923, (0 << 1) | (0 << 0)); + dib8000_write_word(state, 924, 776); // 0.625*3.3 / 4096 + + /* do the calibration */ + dib8000_write_word(state, 923, (1 << 0)); + dib8000_write_word(state, 923, (0 << 0)); + + msleep(1); + return 0; +} + +int dib8000_set_wbd_ref(struct dvb_frontend *fe, u16 value) +{ + struct dib8000_state *state = fe->demodulator_priv; + if (value > 4095) + value = 4095; + state->wbd_ref = value; + return dib8000_write_word(state, 106, value); +} + +EXPORT_SYMBOL(dib8000_set_wbd_ref); +static void dib8000_reset_pll_common(struct dib8000_state *state, const struct dibx000_bandwidth_config *bw) +{ + dprintk("ifreq: %d %x, inversion: %d", bw->ifreq, bw->ifreq, bw->ifreq >> 25); + dib8000_write_word(state, 23, (u16) (((bw->internal * 1000) >> 16) & 0xffff)); /* P_sec_len */ + dib8000_write_word(state, 24, (u16) ((bw->internal * 1000) & 0xffff)); + dib8000_write_word(state, 27, (u16) ((bw->ifreq >> 16) & 0x01ff)); + dib8000_write_word(state, 28, (u16) (bw->ifreq & 0xffff)); + dib8000_write_word(state, 26, (u16) ((bw->ifreq >> 25) & 0x0003)); + + dib8000_write_word(state, 922, bw->sad_cfg); +} + +static void dib8000_reset_pll(struct dib8000_state *state) +{ + const struct dibx000_bandwidth_config *pll = state->cfg.pll; + u16 clk_cfg1; + + // clk_cfg0 + dib8000_write_word(state, 901, (pll->pll_prediv << 8) | (pll->pll_ratio << 0)); + + // clk_cfg1 + clk_cfg1 = (1 << 10) | (0 << 9) | (pll->IO_CLK_en_core << 8) | + (pll->bypclk_div << 5) | (pll->enable_refdiv << 4) | (1 << 3) | (pll->pll_range << 1) | (pll->pll_reset << 0); + + dib8000_write_word(state, 902, clk_cfg1); + clk_cfg1 = (clk_cfg1 & 0xfff7) | (pll->pll_bypass << 3); + dib8000_write_word(state, 902, clk_cfg1); + + dprintk("clk_cfg1: 0x%04x", clk_cfg1); /* 0x507 1 0 1 000 0 0 11 1 */ + + /* smpl_cfg: P_refclksel=2, P_ensmplsel=1 nodivsmpl=1 */ + if (state->cfg.pll->ADClkSrc == 0) + dib8000_write_word(state, 904, (0 << 15) | (0 << 12) | (0 << 10) | (pll->modulo << 8) | (pll->ADClkSrc << 7) | (0 << 1)); + else if (state->cfg.refclksel != 0) + dib8000_write_word(state, 904, + (0 << 15) | (1 << 12) | ((state->cfg.refclksel & 0x3) << 10) | (pll->modulo << 8) | (pll-> + ADClkSrc << 7) | (0 << 1)); + else + dib8000_write_word(state, 904, (0 << 15) | (1 << 12) | (3 << 10) | (pll->modulo << 8) | (pll->ADClkSrc << 7) | (0 << 1)); + + dib8000_reset_pll_common(state, pll); +} + +static int dib8000_reset_gpio(struct dib8000_state *st) +{ + /* reset the GPIOs */ + dib8000_write_word(st, 1029, st->cfg.gpio_dir); + dib8000_write_word(st, 1030, st->cfg.gpio_val); + + /* TODO 782 is P_gpio_od */ + + dib8000_write_word(st, 1032, st->cfg.gpio_pwm_pos); + + dib8000_write_word(st, 1037, st->cfg.pwm_freq_div); + return 0; +} + +static int dib8000_cfg_gpio(struct dib8000_state *st, u8 num, u8 dir, u8 val) +{ + st->cfg.gpio_dir = dib8000_read_word(st, 1029); + st->cfg.gpio_dir &= ~(1 << num); /* reset the direction bit */ + st->cfg.gpio_dir |= (dir & 0x1) << num; /* set the new direction */ + dib8000_write_word(st, 1029, st->cfg.gpio_dir); + + st->cfg.gpio_val = dib8000_read_word(st, 1030); + st->cfg.gpio_val &= ~(1 << num); /* reset the direction bit */ + st->cfg.gpio_val |= (val & 0x01) << num; /* set the new value */ + dib8000_write_word(st, 1030, st->cfg.gpio_val); + + dprintk("gpio dir: %x: gpio val: %x", st->cfg.gpio_dir, st->cfg.gpio_val); + + return 0; +} + +int dib8000_set_gpio(struct dvb_frontend *fe, u8 num, u8 dir, u8 val) +{ + struct dib8000_state *state = fe->demodulator_priv; + return dib8000_cfg_gpio(state, num, dir, val); +} + +EXPORT_SYMBOL(dib8000_set_gpio); +static const u16 dib8000_defaults[] = { + /* auto search configuration - lock0 by default waiting + * for cpil_lock; lock1 cpil_lock; lock2 tmcc_sync_lock */ + 3, 7, + 0x0004, + 0x0400, + 0x0814, + + 12, 11, + 0x001b, + 0x7740, + 0x005b, + 0x8d80, + 0x01c9, + 0xc380, + 0x0000, + 0x0080, + 0x0000, + 0x0090, + 0x0001, + 0xd4c0, + + /*1, 32, + 0x6680 // P_corm_thres Lock algorithms configuration */ + + 11, 80, /* set ADC level to -16 */ + (1 << 13) - 825 - 117, + (1 << 13) - 837 - 117, + (1 << 13) - 811 - 117, + (1 << 13) - 766 - 117, + (1 << 13) - 737 - 117, + (1 << 13) - 693 - 117, + (1 << 13) - 648 - 117, + (1 << 13) - 619 - 117, + (1 << 13) - 575 - 117, + (1 << 13) - 531 - 117, + (1 << 13) - 501 - 117, + + 4, 108, + 0, + 0, + 0, + 0, + + 1, 175, + 0x0410, + 1, 179, + 8192, // P_fft_nb_to_cut + + 6, 181, + 0x2800, // P_coff_corthres_ ( 2k 4k 8k ) 0x2800 + 0x2800, + 0x2800, + 0x2800, // P_coff_cpilthres_ ( 2k 4k 8k ) 0x2800 + 0x2800, + 0x2800, + + 2, 193, + 0x0666, // P_pha3_thres + 0x0000, // P_cti_use_cpe, P_cti_use_prog + + 2, 205, + 0x200f, // P_cspu_regul, P_cspu_win_cut + 0x000f, // P_des_shift_work + + 5, 215, + 0x023d, // P_adp_regul_cnt + 0x00a4, // P_adp_noise_cnt + 0x00a4, // P_adp_regul_ext + 0x7ff0, // P_adp_noise_ext + 0x3ccc, // P_adp_fil + + 1, 230, + 0x0000, // P_2d_byp_ti_num + + 1, 263, + 0x800, //P_equal_thres_wgn + + 1, 268, + (2 << 9) | 39, // P_equal_ctrl_synchro, P_equal_speedmode + + 1, 270, + 0x0001, // P_div_lock0_wait + 1, 285, + 0x0020, //p_fec_ + 1, 299, + 0x0062, // P_smo_mode, P_smo_rs_discard, P_smo_fifo_flush, P_smo_pid_parse, P_smo_error_discard + + 1, 338, + (1 << 12) | // P_ctrl_corm_thres4pre_freq_inh=1 + (1 << 10) | // P_ctrl_pre_freq_mode_sat=1 + (0 << 9) | // P_ctrl_pre_freq_inh=0 + (3 << 5) | // P_ctrl_pre_freq_step=3 + (1 << 0), // P_pre_freq_win_len=1 + + 1, 903, + (0 << 4) | 2, // P_divclksel=0 P_divbitsel=2 (was clk=3,bit=1 for MPW) + + 0, +}; + +static u16 dib8000_identify(struct i2c_device *client) +{ + u16 value; + + //because of glitches sometimes + value = dib8000_i2c_read16(client, 896); + + if ((value = dib8000_i2c_read16(client, 896)) != 0x01b3) { + dprintk("wrong Vendor ID (read=0x%x)", value); + return 0; + } + + value = dib8000_i2c_read16(client, 897); + if (value != 0x8000 && value != 0x8001 && value != 0x8002) { + dprintk("wrong Device ID (%x)", value); + return 0; + } + + switch (value) { + case 0x8000: + dprintk("found DiB8000A"); + break; + case 0x8001: + dprintk("found DiB8000B"); + break; + case 0x8002: + dprintk("found DiB8000C"); + break; + } + return value; +} + +static int dib8000_reset(struct dvb_frontend *fe) +{ + struct dib8000_state *state = fe->demodulator_priv; + + dib8000_write_word(state, 1287, 0x0003); /* sram lead in, rdy */ + + if ((state->revision = dib8000_identify(&state->i2c)) == 0) + return -EINVAL; + + if (state->revision == 0x8000) + dprintk("error : dib8000 MA not supported"); + + dibx000_reset_i2c_master(&state->i2c_master); + + dib8000_set_power_mode(state, DIB8000M_POWER_ALL); + + /* always leave the VBG voltage on - it consumes almost nothing but takes a long time to start */ + dib8000_set_adc_state(state, DIBX000_VBG_ENABLE); + + /* restart all parts */ + dib8000_write_word(state, 770, 0xffff); + dib8000_write_word(state, 771, 0xffff); + dib8000_write_word(state, 772, 0xfffc); + dib8000_write_word(state, 898, 0x000c); // sad + dib8000_write_word(state, 1280, 0x004d); + dib8000_write_word(state, 1281, 0x000c); + + dib8000_write_word(state, 770, 0x0000); + dib8000_write_word(state, 771, 0x0000); + dib8000_write_word(state, 772, 0x0000); + dib8000_write_word(state, 898, 0x0004); // sad + dib8000_write_word(state, 1280, 0x0000); + dib8000_write_word(state, 1281, 0x0000); + + /* drives */ + if (state->cfg.drives) + dib8000_write_word(state, 906, state->cfg.drives); + else { + dprintk("using standard PAD-drive-settings, please adjust settings in config-struct to be optimal."); + dib8000_write_word(state, 906, 0x2d98); // min drive SDRAM - not optimal - adjust + } + + dib8000_reset_pll(state); + + if (dib8000_reset_gpio(state) != 0) + dprintk("GPIO reset was not successful."); + + if (dib8000_set_output_mode(state, OUTMODE_HIGH_Z) != 0) + dprintk("OUTPUT_MODE could not be resetted."); + + state->current_agc = NULL; + + // P_iqc_alpha_pha, P_iqc_alpha_amp, P_iqc_dcc_alpha, ... + /* P_iqc_ca2 = 0; P_iqc_impnc_on = 0; P_iqc_mode = 0; */ + if (state->cfg.pll->ifreq == 0) + dib8000_write_word(state, 40, 0x0755); /* P_iqc_corr_inh = 0 enable IQcorr block */ + else + dib8000_write_word(state, 40, 0x1f55); /* P_iqc_corr_inh = 1 disable IQcorr block */ + + { + u16 l = 0, r; + const u16 *n; + n = dib8000_defaults; + l = *n++; + while (l) { + r = *n++; + do { + dib8000_write_word(state, r, *n++); + r++; + } while (--l); + l = *n++; + } + } + state->isdbt_cfg_loaded = 0; + + //div_cfg override for special configs + if (state->cfg.div_cfg != 0) + dib8000_write_word(state, 903, state->cfg.div_cfg); + + /* unforce divstr regardless whether i2c enumeration was done or not */ + dib8000_write_word(state, 1285, dib8000_read_word(state, 1285) & ~(1 << 1)); + + dib8000_set_bandwidth(state, 6000); + + dib8000_set_adc_state(state, DIBX000_SLOW_ADC_ON); + dib8000_sad_calib(state); + dib8000_set_adc_state(state, DIBX000_SLOW_ADC_OFF); + + dib8000_set_power_mode(state, DIB8000M_POWER_INTERFACE_ONLY); + + return 0; +} + +static void dib8000_restart_agc(struct dib8000_state *state) +{ + // P_restart_iqc & P_restart_agc + dib8000_write_word(state, 770, 0x0a00); + dib8000_write_word(state, 770, 0x0000); +} + +static int dib8000_update_lna(struct dib8000_state *state) +{ + u16 dyn_gain; + + if (state->cfg.update_lna) { + // read dyn_gain here (because it is demod-dependent and not tuner) + dyn_gain = dib8000_read_word(state, 390); + + if (state->cfg.update_lna(&state->fe, dyn_gain)) { // LNA has changed + dib8000_restart_agc(state); + return 1; + } + } + return 0; +} + +static int dib8000_set_agc_config(struct dib8000_state *state, u8 band) +{ + struct dibx000_agc_config *agc = NULL; + int i; + if (state->current_band == band && state->current_agc != NULL) + return 0; + state->current_band = band; + + for (i = 0; i < state->cfg.agc_config_count; i++) + if (state->cfg.agc[i].band_caps & band) { + agc = &state->cfg.agc[i]; + break; + } + + if (agc == NULL) { + dprintk("no valid AGC configuration found for band 0x%02x", band); + return -EINVAL; + } + + state->current_agc = agc; + + /* AGC */ + dib8000_write_word(state, 76, agc->setup); + dib8000_write_word(state, 77, agc->inv_gain); + dib8000_write_word(state, 78, agc->time_stabiliz); + dib8000_write_word(state, 101, (agc->alpha_level << 12) | agc->thlock); + + // Demod AGC loop configuration + dib8000_write_word(state, 102, (agc->alpha_mant << 5) | agc->alpha_exp); + dib8000_write_word(state, 103, (agc->beta_mant << 6) | agc->beta_exp); + + dprintk("WBD: ref: %d, sel: %d, active: %d, alpha: %d", + state->wbd_ref != 0 ? state->wbd_ref : agc->wbd_ref, agc->wbd_sel, !agc->perform_agc_softsplit, agc->wbd_sel); + + /* AGC continued */ + if (state->wbd_ref != 0) + dib8000_write_word(state, 106, state->wbd_ref); + else // use default + dib8000_write_word(state, 106, agc->wbd_ref); + dib8000_write_word(state, 107, (agc->wbd_alpha << 9) | (agc->perform_agc_softsplit << 8)); + dib8000_write_word(state, 108, agc->agc1_max); + dib8000_write_word(state, 109, agc->agc1_min); + dib8000_write_word(state, 110, agc->agc2_max); + dib8000_write_word(state, 111, agc->agc2_min); + dib8000_write_word(state, 112, (agc->agc1_pt1 << 8) | agc->agc1_pt2); + dib8000_write_word(state, 113, (agc->agc1_slope1 << 8) | agc->agc1_slope2); + dib8000_write_word(state, 114, (agc->agc2_pt1 << 8) | agc->agc2_pt2); + dib8000_write_word(state, 115, (agc->agc2_slope1 << 8) | agc->agc2_slope2); + + dib8000_write_word(state, 75, agc->agc1_pt3); + dib8000_write_word(state, 923, (dib8000_read_word(state, 923) & 0xffe3) | (agc->wbd_inv << 4) | (agc->wbd_sel << 2)); /*LB : 929 -> 923 */ + + return 0; +} + +static int dib8000_agc_soft_split(struct dib8000_state *state) +{ + u16 agc, split_offset; + + if (!state->current_agc || !state->current_agc->perform_agc_softsplit || state->current_agc->split.max == 0) + return FE_CALLBACK_TIME_NEVER; + + // n_agc_global + agc = dib8000_read_word(state, 390); + + if (agc > state->current_agc->split.min_thres) + split_offset = state->current_agc->split.min; + else if (agc < state->current_agc->split.max_thres) + split_offset = state->current_agc->split.max; + else + split_offset = state->current_agc->split.max * + (agc - state->current_agc->split.min_thres) / (state->current_agc->split.max_thres - state->current_agc->split.min_thres); + + dprintk("AGC split_offset: %d", split_offset); + + // P_agc_force_split and P_agc_split_offset + dib8000_write_word(state, 107, (dib8000_read_word(state, 107) & 0xff00) | split_offset); + return 5000; +} + +static int dib8000_agc_startup(struct dvb_frontend *fe) +{ + struct dib8000_state *state = fe->demodulator_priv; + enum frontend_tune_state *tune_state = &state->tune_state; + + int ret = 0; + + switch (*tune_state) { + case CT_AGC_START: + // set power-up level: interf+analog+AGC + + dib8000_set_adc_state(state, DIBX000_ADC_ON); + + if (dib8000_set_agc_config(state, (unsigned char)(BAND_OF_FREQUENCY(fe->dtv_property_cache.frequency / 1000))) != 0) { + *tune_state = CT_AGC_STOP; + state->status = FE_STATUS_TUNE_FAILED; + break; + } + + ret = 70; + *tune_state = CT_AGC_STEP_0; + break; + + case CT_AGC_STEP_0: + //AGC initialization + if (state->cfg.agc_control) + state->cfg.agc_control(&state->fe, 1); + + dib8000_restart_agc(state); + + // wait AGC rough lock time + ret = 50; + *tune_state = CT_AGC_STEP_1; + break; + + case CT_AGC_STEP_1: + // wait AGC accurate lock time + ret = 70; + + if (dib8000_update_lna(state)) + // wait only AGC rough lock time + ret = 50; + else + *tune_state = CT_AGC_STEP_2; + break; + + case CT_AGC_STEP_2: + dib8000_agc_soft_split(state); + + if (state->cfg.agc_control) + state->cfg.agc_control(&state->fe, 0); + + *tune_state = CT_AGC_STOP; + break; + default: + ret = dib8000_agc_soft_split(state); + break; + } + return ret; + +} + +static void dib8000_update_timf(struct dib8000_state *state) +{ + u32 timf = state->timf = dib8000_read32(state, 435); + + dib8000_write_word(state, 29, (u16) (timf >> 16)); + dib8000_write_word(state, 30, (u16) (timf & 0xffff)); + dprintk("Updated timing frequency: %d (default: %d)", state->timf, state->timf_default); +} + +static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosearching) +{ + u16 mode, max_constellation, seg_diff_mask = 0, nbseg_diff = 0; + u8 guard, crate, constellation, timeI; + u8 permu_seg[] = { 6, 5, 7, 4, 8, 3, 9, 2, 10, 1, 11, 0, 12 }; + u16 i, coeff[4], P_cfr_left_edge = 0, P_cfr_right_edge = 0, seg_mask13 = 0x1fff; // All 13 segments enabled + const s16 *ncoeff, *ana_fe; + u16 tmcc_pow = 0; + u16 coff_pow = 0x2800; + u16 init_prbs = 0xfff; + u16 ana_gain = 0; + u16 adc_target_16dB[11] = { + (1 << 13) - 825 - 117, + (1 << 13) - 837 - 117, + (1 << 13) - 811 - 117, + (1 << 13) - 766 - 117, + (1 << 13) - 737 - 117, + (1 << 13) - 693 - 117, + (1 << 13) - 648 - 117, + (1 << 13) - 619 - 117, + (1 << 13) - 575 - 117, + (1 << 13) - 531 - 117, + (1 << 13) - 501 - 117 + }; + + if (state->ber_monitored_layer != LAYER_ALL) + dib8000_write_word(state, 285, (dib8000_read_word(state, 285) & 0x60) | state->ber_monitored_layer); + else + dib8000_write_word(state, 285, dib8000_read_word(state, 285) & 0x60); + + i = dib8000_read_word(state, 26) & 1; // P_dds_invspec + dib8000_write_word(state, 26, state->fe.dtv_property_cache.inversion ^ i); + + if (state->fe.dtv_property_cache.isdbt_sb_mode) { + //compute new dds_freq for the seg and adjust prbs + int seg_offset = + state->fe.dtv_property_cache.isdbt_sb_segment_idx - (state->fe.dtv_property_cache.isdbt_sb_segment_count / 2) - + (state->fe.dtv_property_cache.isdbt_sb_segment_count % 2); + int clk = state->cfg.pll->internal; + u32 segtodds = ((u32) (430 << 23) / clk) << 3; // segtodds = SegBW / Fclk * pow(2,26) + int dds_offset = seg_offset * segtodds; + int new_dds, sub_channel; + if ((state->fe.dtv_property_cache.isdbt_sb_segment_count % 2) == 0) // if even + dds_offset -= (int)(segtodds / 2); + + if (state->cfg.pll->ifreq == 0) { + if ((state->fe.dtv_property_cache.inversion ^ i) == 0) { + dib8000_write_word(state, 26, dib8000_read_word(state, 26) | 1); + new_dds = dds_offset; + } else + new_dds = dds_offset; + + // We shift tuning frequency if the wanted segment is : + // - the segment of center frequency with an odd total number of segments + // - the segment to the left of center frequency with an even total number of segments + // - the segment to the right of center frequency with an even total number of segments + if ((state->fe.dtv_property_cache.delivery_system == SYS_ISDBT) && (state->fe.dtv_property_cache.isdbt_sb_mode == 1) + && + (((state->fe.dtv_property_cache.isdbt_sb_segment_count % 2) + && (state->fe.dtv_property_cache.isdbt_sb_segment_idx == + ((state->fe.dtv_property_cache.isdbt_sb_segment_count / 2) + 1))) + || (((state->fe.dtv_property_cache.isdbt_sb_segment_count % 2) == 0) + && (state->fe.dtv_property_cache.isdbt_sb_segment_idx == (state->fe.dtv_property_cache.isdbt_sb_segment_count / 2))) + || (((state->fe.dtv_property_cache.isdbt_sb_segment_count % 2) == 0) + && (state->fe.dtv_property_cache.isdbt_sb_segment_idx == + ((state->fe.dtv_property_cache.isdbt_sb_segment_count / 2) + 1))) + )) { + new_dds -= ((u32) (850 << 22) / clk) << 4; // new_dds = 850 (freq shift in KHz) / Fclk * pow(2,26) + } + } else { + if ((state->fe.dtv_property_cache.inversion ^ i) == 0) + new_dds = state->cfg.pll->ifreq - dds_offset; + else + new_dds = state->cfg.pll->ifreq + dds_offset; + } + dib8000_write_word(state, 27, (u16) ((new_dds >> 16) & 0x01ff)); + dib8000_write_word(state, 28, (u16) (new_dds & 0xffff)); + if (state->fe.dtv_property_cache.isdbt_sb_segment_count % 2) // if odd + sub_channel = ((state->fe.dtv_property_cache.isdbt_sb_subchannel + (3 * seg_offset) + 1) % 41) / 3; + else // if even + sub_channel = ((state->fe.dtv_property_cache.isdbt_sb_subchannel + (3 * seg_offset)) % 41) / 3; + sub_channel -= 6; + + if (state->fe.dtv_property_cache.transmission_mode == TRANSMISSION_MODE_2K + || state->fe.dtv_property_cache.transmission_mode == TRANSMISSION_MODE_4K) { + dib8000_write_word(state, 219, dib8000_read_word(state, 219) | 0x1); //adp_pass =1 + dib8000_write_word(state, 190, dib8000_read_word(state, 190) | (0x1 << 14)); //pha3_force_pha_shift = 1 + } else { + dib8000_write_word(state, 219, dib8000_read_word(state, 219) & 0xfffe); //adp_pass =0 + dib8000_write_word(state, 190, dib8000_read_word(state, 190) & 0xbfff); //pha3_force_pha_shift = 0 + } + + switch (state->fe.dtv_property_cache.transmission_mode) { + case TRANSMISSION_MODE_2K: + switch (sub_channel) { + case -6: + init_prbs = 0x0; + break; // 41, 0, 1 + case -5: + init_prbs = 0x423; + break; // 02~04 + case -4: + init_prbs = 0x9; + break; // 05~07 + case -3: + init_prbs = 0x5C7; + break; // 08~10 + case -2: + init_prbs = 0x7A6; + break; // 11~13 + case -1: + init_prbs = 0x3D8; + break; // 14~16 + case 0: + init_prbs = 0x527; + break; // 17~19 + case 1: + init_prbs = 0x7FF; + break; // 20~22 + case 2: + init_prbs = 0x79B; + break; // 23~25 + case 3: + init_prbs = 0x3D6; + break; // 26~28 + case 4: + init_prbs = 0x3A2; + break; // 29~31 + case 5: + init_prbs = 0x53B; + break; // 32~34 + case 6: + init_prbs = 0x2F4; + break; // 35~37 + default: + case 7: + init_prbs = 0x213; + break; // 38~40 + } + break; + + case TRANSMISSION_MODE_4K: + switch (sub_channel) { + case -6: + init_prbs = 0x0; + break; // 41, 0, 1 + case -5: + init_prbs = 0x208; + break; // 02~04 + case -4: + init_prbs = 0xC3; + break; // 05~07 + case -3: + init_prbs = 0x7B9; + break; // 08~10 + case -2: + init_prbs = 0x423; + break; // 11~13 + case -1: + init_prbs = 0x5C7; + break; // 14~16 + case 0: + init_prbs = 0x3D8; + break; // 17~19 + case 1: + init_prbs = 0x7FF; + break; // 20~22 + case 2: + init_prbs = 0x3D6; + break; // 23~25 + case 3: + init_prbs = 0x53B; + break; // 26~28 + case 4: + init_prbs = 0x213; + break; // 29~31 + case 5: + init_prbs = 0x29; + break; // 32~34 + case 6: + init_prbs = 0xD0; + break; // 35~37 + default: + case 7: + init_prbs = 0x48E; + break; // 38~40 + } + break; + + default: + case TRANSMISSION_MODE_8K: + switch (sub_channel) { + case -6: + init_prbs = 0x0; + break; // 41, 0, 1 + case -5: + init_prbs = 0x740; + break; // 02~04 + case -4: + init_prbs = 0x069; + break; // 05~07 + case -3: + init_prbs = 0x7DD; + break; // 08~10 + case -2: + init_prbs = 0x208; + break; // 11~13 + case -1: + init_prbs = 0x7B9; + break; // 14~16 + case 0: + init_prbs = 0x5C7; + break; // 17~19 + case 1: + init_prbs = 0x7FF; + break; // 20~22 + case 2: + init_prbs = 0x53B; + break; // 23~25 + case 3: + init_prbs = 0x29; + break; // 26~28 + case 4: + init_prbs = 0x48E; + break; // 29~31 + case 5: + init_prbs = 0x4C4; + break; // 32~34 + case 6: + init_prbs = 0x367; + break; // 33~37 + default: + case 7: + init_prbs = 0x684; + break; // 38~40 + } + break; + } + } else { // if not state->fe.dtv_property_cache.isdbt_sb_mode + dib8000_write_word(state, 27, (u16) ((state->cfg.pll->ifreq >> 16) & 0x01ff)); + dib8000_write_word(state, 28, (u16) (state->cfg.pll->ifreq & 0xffff)); + dib8000_write_word(state, 26, (u16) ((state->cfg.pll->ifreq >> 25) & 0x0003)); + } + /*P_mode == ?? */ + dib8000_write_word(state, 10, (seq << 4)); + // dib8000_write_word(state, 287, (dib8000_read_word(state, 287) & 0xe000) | 0x1000); + + switch (state->fe.dtv_property_cache.guard_interval) { + case GUARD_INTERVAL_1_32: + guard = 0; + break; + case GUARD_INTERVAL_1_16: + guard = 1; + break; + case GUARD_INTERVAL_1_8: + guard = 2; + break; + case GUARD_INTERVAL_1_4: + default: + guard = 3; + break; + } + + dib8000_write_word(state, 1, (init_prbs << 2) | (guard & 0x3)); // ADDR 1 + + max_constellation = DQPSK; + for (i = 0; i < 3; i++) { + switch (state->fe.dtv_property_cache.layer[i].modulation) { + case DQPSK: + constellation = 0; + break; + case QPSK: + constellation = 1; + break; + case QAM_16: + constellation = 2; + break; + case QAM_64: + default: + constellation = 3; + break; + } + + switch (state->fe.dtv_property_cache.layer[i].fec) { + case FEC_1_2: + crate = 1; + break; + case FEC_2_3: + crate = 2; + break; + case FEC_3_4: + crate = 3; + break; + case FEC_5_6: + crate = 5; + break; + case FEC_7_8: + default: + crate = 7; + break; + } + + if ((state->fe.dtv_property_cache.layer[i].interleaving > 0) && + ((state->fe.dtv_property_cache.layer[i].interleaving <= 3) || + (state->fe.dtv_property_cache.layer[i].interleaving == 4 && state->fe.dtv_property_cache.isdbt_sb_mode == 1)) + ) + timeI = state->fe.dtv_property_cache.layer[i].interleaving; + else + timeI = 0; + dib8000_write_word(state, 2 + i, (constellation << 10) | ((state->fe.dtv_property_cache.layer[i].segment_count & 0xf) << 6) | + (crate << 3) | timeI); + if (state->fe.dtv_property_cache.layer[i].segment_count > 0) { + switch (max_constellation) { + case DQPSK: + case QPSK: + if (state->fe.dtv_property_cache.layer[i].modulation == QAM_16 || + state->fe.dtv_property_cache.layer[i].modulation == QAM_64) + max_constellation = state->fe.dtv_property_cache.layer[i].modulation; + break; + case QAM_16: + if (state->fe.dtv_property_cache.layer[i].modulation == QAM_64) + max_constellation = state->fe.dtv_property_cache.layer[i].modulation; + break; + } + } + } + + mode = fft_to_mode(state); + + //dib8000_write_word(state, 5, 13); /*p_last_seg = 13*/ + + dib8000_write_word(state, 274, (dib8000_read_word(state, 274) & 0xffcf) | + ((state->fe.dtv_property_cache.isdbt_partial_reception & 1) << 5) | ((state->fe.dtv_property_cache. + isdbt_sb_mode & 1) << 4)); + + dprintk("mode = %d ; guard = %d", mode, state->fe.dtv_property_cache.guard_interval); + + /* signal optimization parameter */ + + if (state->fe.dtv_property_cache.isdbt_partial_reception) { + seg_diff_mask = (state->fe.dtv_property_cache.layer[0].modulation == DQPSK) << permu_seg[0]; + for (i = 1; i < 3; i++) + nbseg_diff += + (state->fe.dtv_property_cache.layer[i].modulation == DQPSK) * state->fe.dtv_property_cache.layer[i].segment_count; + for (i = 0; i < nbseg_diff; i++) + seg_diff_mask |= 1 << permu_seg[i + 1]; + } else { + for (i = 0; i < 3; i++) + nbseg_diff += + (state->fe.dtv_property_cache.layer[i].modulation == DQPSK) * state->fe.dtv_property_cache.layer[i].segment_count; + for (i = 0; i < nbseg_diff; i++) + seg_diff_mask |= 1 << permu_seg[i]; + } + dprintk("nbseg_diff = %X (%d)", seg_diff_mask, seg_diff_mask); + + state->differential_constellation = (seg_diff_mask != 0); + dib8000_set_diversity_in(&state->fe, state->diversity_onoff); + + if (state->fe.dtv_property_cache.isdbt_sb_mode == 1) { // ISDB-Tsb + if (state->fe.dtv_property_cache.isdbt_partial_reception == 1) // 3-segments + seg_mask13 = 0x00E0; + else // 1-segment + seg_mask13 = 0x0040; + } else + seg_mask13 = 0x1fff; + + // WRITE: Mode & Diff mask + dib8000_write_word(state, 0, (mode << 13) | seg_diff_mask); + + if ((seg_diff_mask) || (state->fe.dtv_property_cache.isdbt_sb_mode)) + dib8000_write_word(state, 268, (dib8000_read_word(state, 268) & 0xF9FF) | 0x0200); + else + dib8000_write_word(state, 268, (2 << 9) | 39); //init value + + // ---- SMALL ---- + // P_small_seg_diff + dib8000_write_word(state, 352, seg_diff_mask); // ADDR 352 + + dib8000_write_word(state, 353, seg_mask13); // ADDR 353 + +/* // P_small_narrow_band=0, P_small_last_seg=13, P_small_offset_num_car=5 */ + // dib8000_write_word(state, 351, (state->fe.dtv_property_cache.isdbt_sb_mode << 8) | (13 << 4) | 5 ); + + // ---- SMALL ---- + if (state->fe.dtv_property_cache.isdbt_sb_mode == 1) { + switch (state->fe.dtv_property_cache.transmission_mode) { + case TRANSMISSION_MODE_2K: + if (state->fe.dtv_property_cache.isdbt_partial_reception == 0) { // 1-seg + if (state->fe.dtv_property_cache.layer[0].modulation == DQPSK) // DQPSK + ncoeff = coeff_2k_sb_1seg_dqpsk; + else // QPSK or QAM + ncoeff = coeff_2k_sb_1seg; + } else { // 3-segments + if (state->fe.dtv_property_cache.layer[0].modulation == DQPSK) { // DQPSK on central segment + if (state->fe.dtv_property_cache.layer[1].modulation == DQPSK) // DQPSK on external segments + ncoeff = coeff_2k_sb_3seg_0dqpsk_1dqpsk; + else // QPSK or QAM on external segments + ncoeff = coeff_2k_sb_3seg_0dqpsk; + } else { // QPSK or QAM on central segment + if (state->fe.dtv_property_cache.layer[1].modulation == DQPSK) // DQPSK on external segments + ncoeff = coeff_2k_sb_3seg_1dqpsk; + else // QPSK or QAM on external segments + ncoeff = coeff_2k_sb_3seg; + } + } + break; + + case TRANSMISSION_MODE_4K: + if (state->fe.dtv_property_cache.isdbt_partial_reception == 0) { // 1-seg + if (state->fe.dtv_property_cache.layer[0].modulation == DQPSK) // DQPSK + ncoeff = coeff_4k_sb_1seg_dqpsk; + else // QPSK or QAM + ncoeff = coeff_4k_sb_1seg; + } else { // 3-segments + if (state->fe.dtv_property_cache.layer[0].modulation == DQPSK) { // DQPSK on central segment + if (state->fe.dtv_property_cache.layer[1].modulation == DQPSK) { // DQPSK on external segments + ncoeff = coeff_4k_sb_3seg_0dqpsk_1dqpsk; + } else { // QPSK or QAM on external segments + ncoeff = coeff_4k_sb_3seg_0dqpsk; + } + } else { // QPSK or QAM on central segment + if (state->fe.dtv_property_cache.layer[1].modulation == DQPSK) { // DQPSK on external segments + ncoeff = coeff_4k_sb_3seg_1dqpsk; + } else // QPSK or QAM on external segments + ncoeff = coeff_4k_sb_3seg; + } + } + break; + + case TRANSMISSION_MODE_AUTO: + case TRANSMISSION_MODE_8K: + default: + if (state->fe.dtv_property_cache.isdbt_partial_reception == 0) { // 1-seg + if (state->fe.dtv_property_cache.layer[0].modulation == DQPSK) // DQPSK + ncoeff = coeff_8k_sb_1seg_dqpsk; + else // QPSK or QAM + ncoeff = coeff_8k_sb_1seg; + } else { // 3-segments + if (state->fe.dtv_property_cache.layer[0].modulation == DQPSK) { // DQPSK on central segment + if (state->fe.dtv_property_cache.layer[1].modulation == DQPSK) { // DQPSK on external segments + ncoeff = coeff_8k_sb_3seg_0dqpsk_1dqpsk; + } else { // QPSK or QAM on external segments + ncoeff = coeff_8k_sb_3seg_0dqpsk; + } + } else { // QPSK or QAM on central segment + if (state->fe.dtv_property_cache.layer[1].modulation == DQPSK) { // DQPSK on external segments + ncoeff = coeff_8k_sb_3seg_1dqpsk; + } else // QPSK or QAM on external segments + ncoeff = coeff_8k_sb_3seg; + } + } + break; + } + } + if (state->fe.dtv_property_cache.isdbt_sb_mode == 1) + for (i = 0; i < 8; i++) + dib8000_write_word(state, 343 + i, ncoeff[i]); + + // P_small_coef_ext_enable=ISDB-Tsb, P_small_narrow_band=ISDB-Tsb, P_small_last_seg=13, P_small_offset_num_car=5 + dib8000_write_word(state, 351, + (state->fe.dtv_property_cache.isdbt_sb_mode << 9) | (state->fe.dtv_property_cache.isdbt_sb_mode << 8) | (13 << 4) | 5); + + // ---- COFF ---- + // Carloff, the most robust + if (state->fe.dtv_property_cache.isdbt_sb_mode == 1) { // Sound Broadcasting mode - use both TMCC and AC pilots + + // P_coff_cpil_alpha=4, P_coff_inh=0, P_coff_cpil_winlen=64 + // P_coff_narrow_band=1, P_coff_square_val=1, P_coff_one_seg=~partial_rcpt, P_coff_use_tmcc=1, P_coff_use_ac=1 + dib8000_write_word(state, 187, + (4 << 12) | (0 << 11) | (63 << 5) | (0x3 << 3) | ((~state->fe.dtv_property_cache.isdbt_partial_reception & 1) << 2) + | 0x3); + +/* // P_small_coef_ext_enable = 1 */ +/* dib8000_write_word(state, 351, dib8000_read_word(state, 351) | 0x200); */ + + if (state->fe.dtv_property_cache.isdbt_partial_reception == 0) { // Sound Broadcasting mode 1 seg + + // P_coff_winlen=63, P_coff_thres_lock=15, P_coff_one_seg_width= (P_mode == 3) , P_coff_one_seg_sym= (P_mode-1) + if (mode == 3) + dib8000_write_word(state, 180, 0x1fcf | ((mode - 1) << 14)); + else + dib8000_write_word(state, 180, 0x0fcf | ((mode - 1) << 14)); + // P_ctrl_corm_thres4pre_freq_inh=1,P_ctrl_pre_freq_mode_sat=1, + // P_ctrl_pre_freq_inh=0, P_ctrl_pre_freq_step = 5, P_pre_freq_win_len=4 + dib8000_write_word(state, 338, (1 << 12) | (1 << 10) | (0 << 9) | (5 << 5) | 4); + // P_ctrl_pre_freq_win_len=16, P_ctrl_pre_freq_thres_lockin=8 + dib8000_write_word(state, 340, (16 << 6) | (8 << 0)); + // P_ctrl_pre_freq_thres_lockout=6, P_small_use_tmcc/ac/cp=1 + dib8000_write_word(state, 341, (6 << 3) | (1 << 2) | (1 << 1) | (1 << 0)); + + // P_coff_corthres_8k, 4k, 2k and P_coff_cpilthres_8k, 4k, 2k + dib8000_write_word(state, 181, 300); + dib8000_write_word(state, 182, 150); + dib8000_write_word(state, 183, 80); + dib8000_write_word(state, 184, 300); + dib8000_write_word(state, 185, 150); + dib8000_write_word(state, 186, 80); + } else { // Sound Broadcasting mode 3 seg + // P_coff_one_seg_sym= 1, P_coff_one_seg_width= 1, P_coff_winlen=63, P_coff_thres_lock=15 + /* if (mode == 3) */ + /* dib8000_write_word(state, 180, 0x2fca | ((0) << 14)); */ + /* else */ + /* dib8000_write_word(state, 180, 0x2fca | ((1) << 14)); */ + dib8000_write_word(state, 180, 0x1fcf | (1 << 14)); + + // P_ctrl_corm_thres4pre_freq_inh = 1, P_ctrl_pre_freq_mode_sat=1, + // P_ctrl_pre_freq_inh=0, P_ctrl_pre_freq_step = 4, P_pre_freq_win_len=4 + dib8000_write_word(state, 338, (1 << 12) | (1 << 10) | (0 << 9) | (4 << 5) | 4); + // P_ctrl_pre_freq_win_len=16, P_ctrl_pre_freq_thres_lockin=8 + dib8000_write_word(state, 340, (16 << 6) | (8 << 0)); + //P_ctrl_pre_freq_thres_lockout=6, P_small_use_tmcc/ac/cp=1 + dib8000_write_word(state, 341, (6 << 3) | (1 << 2) | (1 << 1) | (1 << 0)); + + // P_coff_corthres_8k, 4k, 2k and P_coff_cpilthres_8k, 4k, 2k + dib8000_write_word(state, 181, 350); + dib8000_write_word(state, 182, 300); + dib8000_write_word(state, 183, 250); + dib8000_write_word(state, 184, 350); + dib8000_write_word(state, 185, 300); + dib8000_write_word(state, 186, 250); + } + + } else if (state->isdbt_cfg_loaded == 0) { // if not Sound Broadcasting mode : put default values for 13 segments + dib8000_write_word(state, 180, (16 << 6) | 9); + dib8000_write_word(state, 187, (4 << 12) | (8 << 5) | 0x2); + coff_pow = 0x2800; + for (i = 0; i < 6; i++) + dib8000_write_word(state, 181 + i, coff_pow); + + // P_ctrl_corm_thres4pre_freq_inh=1, P_ctrl_pre_freq_mode_sat=1, + // P_ctrl_pre_freq_mode_sat=1, P_ctrl_pre_freq_inh=0, P_ctrl_pre_freq_step = 3, P_pre_freq_win_len=1 + dib8000_write_word(state, 338, (1 << 12) | (1 << 10) | (0 << 9) | (3 << 5) | 1); + + // P_ctrl_pre_freq_win_len=8, P_ctrl_pre_freq_thres_lockin=6 + dib8000_write_word(state, 340, (8 << 6) | (6 << 0)); + // P_ctrl_pre_freq_thres_lockout=4, P_small_use_tmcc/ac/cp=1 + dib8000_write_word(state, 341, (4 << 3) | (1 << 2) | (1 << 1) | (1 << 0)); + } + // ---- FFT ---- + if (state->fe.dtv_property_cache.isdbt_sb_mode == 1 && state->fe.dtv_property_cache.isdbt_partial_reception == 0) // 1-seg + dib8000_write_word(state, 178, 64); // P_fft_powrange=64 + else + dib8000_write_word(state, 178, 32); // P_fft_powrange=32 + + /* make the cpil_coff_lock more robust but slower p_coff_winlen + * 6bits; p_coff_thres_lock 6bits (for coff lock if needed) + */ + /* if ( ( nbseg_diff>0)&&(nbseg_diff<13)) + dib8000_write_word(state, 187, (dib8000_read_word(state, 187) & 0xfffb) | (1 << 3)); */ + + dib8000_write_word(state, 189, ~seg_mask13 | seg_diff_mask); /* P_lmod4_seg_inh */ + dib8000_write_word(state, 192, ~seg_mask13 | seg_diff_mask); /* P_pha3_seg_inh */ + dib8000_write_word(state, 225, ~seg_mask13 | seg_diff_mask); /* P_tac_seg_inh */ + if ((!state->fe.dtv_property_cache.isdbt_sb_mode) && (state->cfg.pll->ifreq == 0)) + dib8000_write_word(state, 266, ~seg_mask13 | seg_diff_mask | 0x40); /* P_equal_noise_seg_inh */ + else + dib8000_write_word(state, 266, ~seg_mask13 | seg_diff_mask); /* P_equal_noise_seg_inh */ + dib8000_write_word(state, 287, ~seg_mask13 | 0x1000); /* P_tmcc_seg_inh */ + //dib8000_write_word(state, 288, ~seg_mask13 | seg_diff_mask); /* P_tmcc_seg_eq_inh */ + if (!autosearching) + dib8000_write_word(state, 288, (~seg_mask13 | seg_diff_mask) & 0x1fff); /* P_tmcc_seg_eq_inh */ + else + dib8000_write_word(state, 288, 0x1fff); //disable equalisation of the tmcc when autosearch to be able to find the DQPSK channels. + dprintk("287 = %X (%d)", ~seg_mask13 | 0x1000, ~seg_mask13 | 0x1000); + + dib8000_write_word(state, 211, seg_mask13 & (~seg_diff_mask)); /* P_des_seg_enabled */ + + /* offset loop parameters */ + if (state->fe.dtv_property_cache.isdbt_sb_mode == 1) { + if (state->fe.dtv_property_cache.isdbt_partial_reception == 0) // Sound Broadcasting mode 1 seg + /* P_timf_alpha = (11-P_mode), P_corm_alpha=6, P_corm_thres=0x80 */ + dib8000_write_word(state, 32, ((11 - mode) << 12) | (6 << 8) | 0x40); + + else // Sound Broadcasting mode 3 seg + /* P_timf_alpha = (10-P_mode), P_corm_alpha=6, P_corm_thres=0x80 */ + dib8000_write_word(state, 32, ((10 - mode) << 12) | (6 << 8) | 0x60); + } else + // TODO in 13 seg, timf_alpha can always be the same or not ? + /* P_timf_alpha = (9-P_mode, P_corm_alpha=6, P_corm_thres=0x80 */ + dib8000_write_word(state, 32, ((9 - mode) << 12) | (6 << 8) | 0x80); + + if (state->fe.dtv_property_cache.isdbt_sb_mode == 1) { + if (state->fe.dtv_property_cache.isdbt_partial_reception == 0) // Sound Broadcasting mode 1 seg + /* P_ctrl_pha_off_max=3 P_ctrl_sfreq_inh =0 P_ctrl_sfreq_step = (11-P_mode) */ + dib8000_write_word(state, 37, (3 << 5) | (0 << 4) | (10 - mode)); + + else // Sound Broadcasting mode 3 seg + /* P_ctrl_pha_off_max=3 P_ctrl_sfreq_inh =0 P_ctrl_sfreq_step = (10-P_mode) */ + dib8000_write_word(state, 37, (3 << 5) | (0 << 4) | (9 - mode)); + } else + /* P_ctrl_pha_off_max=3 P_ctrl_sfreq_inh =0 P_ctrl_sfreq_step = 9 */ + dib8000_write_word(state, 37, (3 << 5) | (0 << 4) | (8 - mode)); + + /* P_dvsy_sync_wait - reuse mode */ + switch (state->fe.dtv_property_cache.transmission_mode) { + case TRANSMISSION_MODE_8K: + mode = 256; + break; + case TRANSMISSION_MODE_4K: + mode = 128; + break; + default: + case TRANSMISSION_MODE_2K: + mode = 64; + break; + } + if (state->cfg.diversity_delay == 0) + mode = (mode * (1 << (guard)) * 3) / 2 + 48; // add 50% SFN margin + compensate for one DVSY-fifo + else + mode = (mode * (1 << (guard)) * 3) / 2 + state->cfg.diversity_delay; // add 50% SFN margin + compensate for DVSY-fifo + mode <<= 4; + dib8000_write_word(state, 273, (dib8000_read_word(state, 273) & 0x000f) | mode); + + /* channel estimation fine configuration */ + switch (max_constellation) { + case QAM_64: + ana_gain = 0x7; // -1 : avoid def_est saturation when ADC target is -16dB + coeff[0] = 0x0148; /* P_adp_regul_cnt 0.04 */ + coeff[1] = 0xfff0; /* P_adp_noise_cnt -0.002 */ + coeff[2] = 0x00a4; /* P_adp_regul_ext 0.02 */ + coeff[3] = 0xfff8; /* P_adp_noise_ext -0.001 */ + //if (!state->cfg.hostbus_diversity) //if diversity, we should prehaps use the configuration of the max_constallation -1 + break; + case QAM_16: + ana_gain = 0x7; // -1 : avoid def_est saturation when ADC target is -16dB + coeff[0] = 0x023d; /* P_adp_regul_cnt 0.07 */ + coeff[1] = 0xffdf; /* P_adp_noise_cnt -0.004 */ + coeff[2] = 0x00a4; /* P_adp_regul_ext 0.02 */ + coeff[3] = 0xfff0; /* P_adp_noise_ext -0.002 */ + //if (!((state->cfg.hostbus_diversity) && (max_constellation == QAM_16))) + break; + default: + ana_gain = 0; // 0 : goes along with ADC target at -22dB to keep good mobile performance and lock at sensitivity level + coeff[0] = 0x099a; /* P_adp_regul_cnt 0.3 */ + coeff[1] = 0xffae; /* P_adp_noise_cnt -0.01 */ + coeff[2] = 0x0333; /* P_adp_regul_ext 0.1 */ + coeff[3] = 0xfff8; /* P_adp_noise_ext -0.002 */ + break; + } + for (mode = 0; mode < 4; mode++) + dib8000_write_word(state, 215 + mode, coeff[mode]); + + // update ana_gain depending on max constellation + dib8000_write_word(state, 116, ana_gain); + // update ADC target depending on ana_gain + if (ana_gain) { // set -16dB ADC target for ana_gain=-1 + for (i = 0; i < 10; i++) + dib8000_write_word(state, 80 + i, adc_target_16dB[i]); + } else { // set -22dB ADC target for ana_gain=0 + for (i = 0; i < 10; i++) + dib8000_write_word(state, 80 + i, adc_target_16dB[i] - 355); + } + + // ---- ANA_FE ---- + if (state->fe.dtv_property_cache.isdbt_sb_mode) { + if (state->fe.dtv_property_cache.isdbt_partial_reception == 1) // 3-segments + ana_fe = ana_fe_coeff_3seg; + else // 1-segment + ana_fe = ana_fe_coeff_1seg; + } else + ana_fe = ana_fe_coeff_13seg; + + if (state->fe.dtv_property_cache.isdbt_sb_mode == 1 || state->isdbt_cfg_loaded == 0) + for (mode = 0; mode < 24; mode++) + dib8000_write_word(state, 117 + mode, ana_fe[mode]); + + // ---- CHAN_BLK ---- + for (i = 0; i < 13; i++) { + if ((((~seg_diff_mask) >> i) & 1) == 1) { + P_cfr_left_edge += (1 << i) * ((i == 0) || ((((seg_mask13 & (~seg_diff_mask)) >> (i - 1)) & 1) == 0)); + P_cfr_right_edge += (1 << i) * ((i == 12) || ((((seg_mask13 & (~seg_diff_mask)) >> (i + 1)) & 1) == 0)); + } + } + dib8000_write_word(state, 222, P_cfr_left_edge); // P_cfr_left_edge + dib8000_write_word(state, 223, P_cfr_right_edge); // P_cfr_right_edge + // "P_cspu_left_edge" not used => do not care + // "P_cspu_right_edge" not used => do not care + + if (state->fe.dtv_property_cache.isdbt_sb_mode == 1) { // ISDB-Tsb + dib8000_write_word(state, 228, 1); // P_2d_mode_byp=1 + dib8000_write_word(state, 205, dib8000_read_word(state, 205) & 0xfff0); // P_cspu_win_cut = 0 + if (state->fe.dtv_property_cache.isdbt_partial_reception == 0 // 1-segment + && state->fe.dtv_property_cache.transmission_mode == TRANSMISSION_MODE_2K) { + //dib8000_write_word(state, 219, dib8000_read_word(state, 219) & 0xfffe); // P_adp_pass = 0 + dib8000_write_word(state, 265, 15); // P_equal_noise_sel = 15 + } + } else if (state->isdbt_cfg_loaded == 0) { + dib8000_write_word(state, 228, 0); // default value + dib8000_write_word(state, 265, 31); // default value + dib8000_write_word(state, 205, 0x200f); // init value + } + // ---- TMCC ---- + for (i = 0; i < 3; i++) + tmcc_pow += + (((state->fe.dtv_property_cache.layer[i].modulation == DQPSK) * 4 + 1) * state->fe.dtv_property_cache.layer[i].segment_count); + // Quantif of "P_tmcc_dec_thres_?k" is (0, 5+mode, 9); + // Threshold is set at 1/4 of max power. + tmcc_pow *= (1 << (9 - 2)); + + dib8000_write_word(state, 290, tmcc_pow); // P_tmcc_dec_thres_2k + dib8000_write_word(state, 291, tmcc_pow); // P_tmcc_dec_thres_4k + dib8000_write_word(state, 292, tmcc_pow); // P_tmcc_dec_thres_8k + //dib8000_write_word(state, 287, (1 << 13) | 0x1000 ); + // ---- PHA3 ---- + + if (state->isdbt_cfg_loaded == 0) + dib8000_write_word(state, 250, 3285); /*p_2d_hspeed_thr0 */ + + if (state->fe.dtv_property_cache.isdbt_sb_mode == 1) + state->isdbt_cfg_loaded = 0; + else + state->isdbt_cfg_loaded = 1; + +} + +static int dib8000_autosearch_start(struct dvb_frontend *fe) +{ + u8 factor; + u32 value; + struct dib8000_state *state = fe->demodulator_priv; + + int slist = 0; + + state->fe.dtv_property_cache.transmission_mode = TRANSMISSION_MODE_8K; + state->fe.dtv_property_cache.guard_interval = GUARD_INTERVAL_1_8; + //state->fe.dtv_property_cache.isdbt_sb_mode = 0; + //state->fe.dtv_property_cache.isdbt_partial_reception = 0; + state->fe.dtv_property_cache.inversion = 0; + if (!state->fe.dtv_property_cache.isdbt_sb_mode) + state->fe.dtv_property_cache.layer[0].segment_count = 13; + state->fe.dtv_property_cache.layer[0].modulation = QAM_64; + state->fe.dtv_property_cache.layer[0].fec = FEC_2_3; + state->fe.dtv_property_cache.layer[0].interleaving = 0; + + //choose the right list, in sb, always do everything + if (state->fe.dtv_property_cache.isdbt_sb_mode) { + slist = 7; + dib8000_write_word(state, 0, (dib8000_read_word(state, 0) & 0x9fff) | (1 << 13)); + } else { + if (state->fe.dtv_property_cache.guard_interval == GUARD_INTERVAL_AUTO) { + if (state->fe.dtv_property_cache.transmission_mode == TRANSMISSION_MODE_AUTO) { + slist = 7; + dib8000_write_word(state, 0, (dib8000_read_word(state, 0) & 0x9fff) | (1 << 13)); // P_mode = 1 to have autosearch start ok with mode2 + } else { + slist = 3; + state->fe.dtv_property_cache.transmission_mode = state->fe.dtv_property_cache.transmission_mode; + } + } else { + if (state->fe.dtv_property_cache.transmission_mode == TRANSMISSION_MODE_AUTO) { + slist = 2; + state->fe.dtv_property_cache.guard_interval = state->fe.dtv_property_cache.guard_interval; + dib8000_write_word(state, 0, (dib8000_read_word(state, 0) & 0x9fff) | (1 << 13)); // P_mode = 1 + } else { + slist = 0; + state->fe.dtv_property_cache.transmission_mode = state->fe.dtv_property_cache.transmission_mode; + state->fe.dtv_property_cache.guard_interval = state->fe.dtv_property_cache.guard_interval; + } + } + + dprintk("using list for autosearch : %d", slist); + dib8000_set_channel(state, (unsigned char)slist, 1); + //dib8000_write_word(state, 0, (dib8000_read_word(state, 0) & 0x9fff) | (1 << 13)); // P_mode = 1 + + factor = 1; + + //set lock_mask values + dib8000_write_word(state, 6, 0x4); + dib8000_write_word(state, 7, 0x8); + dib8000_write_word(state, 8, 0x1000); + + //set lock_mask wait time values + value = 50 * state->cfg.pll->internal * factor; + dib8000_write_word(state, 11, (u16) ((value >> 16) & 0xffff)); // lock0 wait time + dib8000_write_word(state, 12, (u16) (value & 0xffff)); // lock0 wait time + value = 100 * state->cfg.pll->internal * factor; + dib8000_write_word(state, 13, (u16) ((value >> 16) & 0xffff)); // lock1 wait time + dib8000_write_word(state, 14, (u16) (value & 0xffff)); // lock1 wait time + value = 1000 * state->cfg.pll->internal * factor; + dib8000_write_word(state, 15, (u16) ((value >> 16) & 0xffff)); // lock2 wait time + dib8000_write_word(state, 16, (u16) (value & 0xffff)); // lock2 wait time + + value = dib8000_read_word(state, 0); + dib8000_write_word(state, 0, (u16) ((1 << 15) | value)); + dib8000_read_word(state, 1284); // reset the INT. n_irq_pending + dib8000_write_word(state, 0, (u16) value); + } + + return 0; +} + +static int dib8000_autosearch_irq(struct dvb_frontend *fe) +{ + struct dib8000_state *state = fe->demodulator_priv; + u16 irq_pending = dib8000_read_word(state, 1284); + + if (irq_pending & 0x1) { // failed + dprintk("dib8000_autosearch_irq failed"); + return 1; + } + + if (irq_pending & 0x2) { // succeeded + dprintk("dib8000_autosearch_irq succeeded"); + return 2; + } + + return 0; // still pending +} + +static int dib8000_tune(struct dvb_frontend *fe) +{ + struct dib8000_state *state = fe->demodulator_priv; + int ret = 0; + u16 value, mode = fft_to_mode(state); + + // we are already tuned - just resuming from suspend + if (state == NULL) + return -EINVAL; + + dib8000_set_bandwidth(state, state->fe.dtv_property_cache.bandwidth_hz / 1000); + dib8000_set_channel(state, 0, 0); + + // restart demod + ret |= dib8000_write_word(state, 770, 0x4000); + ret |= dib8000_write_word(state, 770, 0x0000); + msleep(45); + + /* P_ctrl_inh_cor=0, P_ctrl_alpha_cor=4, P_ctrl_inh_isi=0, P_ctrl_alpha_isi=3 */ + /* ret |= dib8000_write_word(state, 29, (0 << 9) | (4 << 5) | (0 << 4) | (3 << 0) ); workaround inh_isi stays at 1 */ + + // never achieved a lock before - wait for timfreq to update + if (state->timf == 0) { + if (state->fe.dtv_property_cache.isdbt_sb_mode == 1) { + if (state->fe.dtv_property_cache.isdbt_partial_reception == 0) // Sound Broadcasting mode 1 seg + msleep(300); + else // Sound Broadcasting mode 3 seg + msleep(500); + } else // 13 seg + msleep(200); + } + //dump_reg(state); + if (state->fe.dtv_property_cache.isdbt_sb_mode == 1) { + if (state->fe.dtv_property_cache.isdbt_partial_reception == 0) { // Sound Broadcasting mode 1 seg + + /* P_timf_alpha = (13-P_mode) , P_corm_alpha=6, P_corm_thres=0x40 alpha to check on board */ + dib8000_write_word(state, 32, ((13 - mode) << 12) | (6 << 8) | 0x40); + //dib8000_write_word(state, 32, (8 << 12) | (6 << 8) | 0x80); + + /* P_ctrl_sfreq_step= (12-P_mode) P_ctrl_sfreq_inh =0 P_ctrl_pha_off_max */ + ret |= dib8000_write_word(state, 37, (12 - mode) | ((5 + mode) << 5)); + + } else { // Sound Broadcasting mode 3 seg + + /* P_timf_alpha = (12-P_mode) , P_corm_alpha=6, P_corm_thres=0x60 alpha to check on board */ + dib8000_write_word(state, 32, ((12 - mode) << 12) | (6 << 8) | 0x60); + + ret |= dib8000_write_word(state, 37, (11 - mode) | ((5 + mode) << 5)); + } + + } else { // 13 seg + /* P_timf_alpha = 8 , P_corm_alpha=6, P_corm_thres=0x80 alpha to check on board */ + dib8000_write_word(state, 32, ((11 - mode) << 12) | (6 << 8) | 0x80); + + ret |= dib8000_write_word(state, 37, (10 - mode) | ((5 + mode) << 5)); + + } + + // we achieved a coff_cpil_lock - it's time to update the timf + if ((dib8000_read_word(state, 568) >> 11) & 0x1) + dib8000_update_timf(state); + + //now that tune is finished, lock0 should lock on fec_mpeg to output this lock on MP_LOCK. It's changed in autosearch start + dib8000_write_word(state, 6, 0x200); + + if (state->revision == 0x8002) { + value = dib8000_read_word(state, 903); + dib8000_write_word(state, 903, value & ~(1 << 3)); + msleep(1); + dib8000_write_word(state, 903, value | (1 << 3)); + } + + return ret; +} + +static int dib8000_wakeup(struct dvb_frontend *fe) +{ + struct dib8000_state *state = fe->demodulator_priv; + + dib8000_set_power_mode(state, DIB8000M_POWER_ALL); + dib8000_set_adc_state(state, DIBX000_ADC_ON); + if (dib8000_set_adc_state(state, DIBX000_SLOW_ADC_ON) != 0) + dprintk("could not start Slow ADC"); + + return 0; +} + +static int dib8000_sleep(struct dvb_frontend *fe) +{ + struct dib8000_state *st = fe->demodulator_priv; + if (1) { + dib8000_set_output_mode(st, OUTMODE_HIGH_Z); + dib8000_set_power_mode(st, DIB8000M_POWER_INTERFACE_ONLY); + return dib8000_set_adc_state(st, DIBX000_SLOW_ADC_OFF) | dib8000_set_adc_state(st, DIBX000_ADC_OFF); + } else { + + return 0; + } +} + +static int dib8000_get_frontend(struct dvb_frontend *fe, struct dvb_frontend_parameters *fep) +{ + struct dib8000_state *state = fe->demodulator_priv; + u16 i, val = 0; + + fe->dtv_property_cache.bandwidth_hz = 6000000; + + fe->dtv_property_cache.isdbt_sb_mode = dib8000_read_word(state, 508) & 0x1; + + val = dib8000_read_word(state, 570); + fe->dtv_property_cache.inversion = (val & 0x40) >> 6; + switch ((val & 0x30) >> 4) { + case 1: + fe->dtv_property_cache.transmission_mode = TRANSMISSION_MODE_2K; + break; + case 3: + default: + fe->dtv_property_cache.transmission_mode = TRANSMISSION_MODE_8K; + break; + } + + switch (val & 0x3) { + case 0: + fe->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_32; + dprintk("dib8000_get_frontend GI = 1/32 "); + break; + case 1: + fe->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_16; + dprintk("dib8000_get_frontend GI = 1/16 "); + break; + case 2: + dprintk("dib8000_get_frontend GI = 1/8 "); + fe->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_8; + break; + case 3: + dprintk("dib8000_get_frontend GI = 1/4 "); + fe->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_4; + break; + } + + val = dib8000_read_word(state, 505); + fe->dtv_property_cache.isdbt_partial_reception = val & 1; + dprintk("dib8000_get_frontend : partial_reception = %d ", fe->dtv_property_cache.isdbt_partial_reception); + + for (i = 0; i < 3; i++) { + val = dib8000_read_word(state, 493 + i); + fe->dtv_property_cache.layer[i].segment_count = val & 0x0F; + dprintk("dib8000_get_frontend : Layer %d segments = %d ", i, fe->dtv_property_cache.layer[i].segment_count); + + val = dib8000_read_word(state, 499 + i); + fe->dtv_property_cache.layer[i].interleaving = val & 0x3; + dprintk("dib8000_get_frontend : Layer %d time_intlv = %d ", i, fe->dtv_property_cache.layer[i].interleaving); + + val = dib8000_read_word(state, 481 + i); + switch (val & 0x7) { + case 1: + fe->dtv_property_cache.layer[i].fec = FEC_1_2; + dprintk("dib8000_get_frontend : Layer %d Code Rate = 1/2 ", i); + break; + case 2: + fe->dtv_property_cache.layer[i].fec = FEC_2_3; + dprintk("dib8000_get_frontend : Layer %d Code Rate = 2/3 ", i); + break; + case 3: + fe->dtv_property_cache.layer[i].fec = FEC_3_4; + dprintk("dib8000_get_frontend : Layer %d Code Rate = 3/4 ", i); + break; + case 5: + fe->dtv_property_cache.layer[i].fec = FEC_5_6; + dprintk("dib8000_get_frontend : Layer %d Code Rate = 5/6 ", i); + break; + default: + fe->dtv_property_cache.layer[i].fec = FEC_7_8; + dprintk("dib8000_get_frontend : Layer %d Code Rate = 7/8 ", i); + break; + } + + val = dib8000_read_word(state, 487 + i); + switch (val & 0x3) { + case 0: + dprintk("dib8000_get_frontend : Layer %d DQPSK ", i); + fe->dtv_property_cache.layer[i].modulation = DQPSK; + break; + case 1: + fe->dtv_property_cache.layer[i].modulation = QPSK; + dprintk("dib8000_get_frontend : Layer %d QPSK ", i); + break; + case 2: + fe->dtv_property_cache.layer[i].modulation = QAM_16; + dprintk("dib8000_get_frontend : Layer %d QAM16 ", i); + break; + case 3: + default: + dprintk("dib8000_get_frontend : Layer %d QAM64 ", i); + fe->dtv_property_cache.layer[i].modulation = QAM_64; + break; + } + } + return 0; +} + +static int dib8000_set_frontend(struct dvb_frontend *fe, struct dvb_frontend_parameters *fep) +{ + struct dib8000_state *state = fe->demodulator_priv; + int time, ret; + + dib8000_set_output_mode(state, OUTMODE_HIGH_Z); + + if (fe->ops.tuner_ops.set_params) + fe->ops.tuner_ops.set_params(fe, fep); + + /* start up the AGC */ + state->tune_state = CT_AGC_START; + do { + time = dib8000_agc_startup(fe); + if (time != FE_CALLBACK_TIME_NEVER) + msleep(time / 10); + else + break; + } while (state->tune_state != CT_AGC_STOP); + + if (state->fe.dtv_property_cache.frequency == 0) { + dprintk("dib8000: must at least specify frequency "); + return 0; + } + + if (state->fe.dtv_property_cache.bandwidth_hz == 0) { + dprintk("dib8000: no bandwidth specified, set to default "); + state->fe.dtv_property_cache.bandwidth_hz = 6000000; + } + + state->tune_state = CT_DEMOD_START; + + if ((state->fe.dtv_property_cache.delivery_system != SYS_ISDBT) || + (state->fe.dtv_property_cache.inversion == INVERSION_AUTO) || + (state->fe.dtv_property_cache.transmission_mode == TRANSMISSION_MODE_AUTO) || + (state->fe.dtv_property_cache.guard_interval == GUARD_INTERVAL_AUTO) || + (((state->fe.dtv_property_cache.isdbt_layer_enabled & (1 << 0)) != 0) && + (state->fe.dtv_property_cache.layer[0].segment_count != 0xff) && + (state->fe.dtv_property_cache.layer[0].segment_count != 0) && + ((state->fe.dtv_property_cache.layer[0].modulation == QAM_AUTO) || + (state->fe.dtv_property_cache.layer[0].fec == FEC_AUTO))) || + (((state->fe.dtv_property_cache.isdbt_layer_enabled & (1 << 1)) != 0) && + (state->fe.dtv_property_cache.layer[1].segment_count != 0xff) && + (state->fe.dtv_property_cache.layer[1].segment_count != 0) && + ((state->fe.dtv_property_cache.layer[1].modulation == QAM_AUTO) || + (state->fe.dtv_property_cache.layer[1].fec == FEC_AUTO))) || + (((state->fe.dtv_property_cache.isdbt_layer_enabled & (1 << 2)) != 0) && + (state->fe.dtv_property_cache.layer[2].segment_count != 0xff) && + (state->fe.dtv_property_cache.layer[2].segment_count != 0) && + ((state->fe.dtv_property_cache.layer[2].modulation == QAM_AUTO) || + (state->fe.dtv_property_cache.layer[2].fec == FEC_AUTO))) || + (((state->fe.dtv_property_cache.layer[0].segment_count == 0) || + ((state->fe.dtv_property_cache.isdbt_layer_enabled & (1 << 0)) == 0)) && + ((state->fe.dtv_property_cache.layer[1].segment_count == 0) || + ((state->fe.dtv_property_cache.isdbt_layer_enabled & (2 << 0)) == 0)) && + ((state->fe.dtv_property_cache.layer[2].segment_count == 0) || ((state->fe.dtv_property_cache.isdbt_layer_enabled & (3 << 0)) == 0)))) { + int i = 800, found; + + dib8000_set_bandwidth(state, fe->dtv_property_cache.bandwidth_hz / 1000); + dib8000_autosearch_start(fe); + do { + msleep(10); + found = dib8000_autosearch_irq(fe); + } while (found == 0 && i--); + + dprintk("Frequency %d Hz, autosearch returns: %d", fep->frequency, found); + + if (found == 0 || found == 1) + return 0; // no channel found + + dib8000_get_frontend(fe, fep); + } + + ret = dib8000_tune(fe); + + /* make this a config parameter */ + dib8000_set_output_mode(state, state->cfg.output_mode); + + return ret; +} + +static int dib8000_read_status(struct dvb_frontend *fe, fe_status_t * stat) +{ + struct dib8000_state *state = fe->demodulator_priv; + u16 lock = dib8000_read_word(state, 568); + + *stat = 0; + + if ((lock >> 14) & 1) // AGC + *stat |= FE_HAS_SIGNAL; + + if ((lock >> 8) & 1) // Equal + *stat |= FE_HAS_CARRIER; + + if ((lock >> 3) & 1) // TMCC_SYNC + *stat |= FE_HAS_SYNC; + + if ((lock >> 5) & 7) // FEC MPEG + *stat |= FE_HAS_LOCK; + + lock = dib8000_read_word(state, 554); // Viterbi Layer A + if (lock & 0x01) + *stat |= FE_HAS_VITERBI; + + lock = dib8000_read_word(state, 555); // Viterbi Layer B + if (lock & 0x01) + *stat |= FE_HAS_VITERBI; + + lock = dib8000_read_word(state, 556); // Viterbi Layer C + if (lock & 0x01) + *stat |= FE_HAS_VITERBI; + + return 0; +} + +static int dib8000_read_ber(struct dvb_frontend *fe, u32 * ber) +{ + struct dib8000_state *state = fe->demodulator_priv; + *ber = (dib8000_read_word(state, 560) << 16) | dib8000_read_word(state, 561); // 13 segments + return 0; +} + +static int dib8000_read_unc_blocks(struct dvb_frontend *fe, u32 * unc) +{ + struct dib8000_state *state = fe->demodulator_priv; + *unc = dib8000_read_word(state, 565); // packet error on 13 seg + return 0; +} + +static int dib8000_read_signal_strength(struct dvb_frontend *fe, u16 * strength) +{ + struct dib8000_state *state = fe->demodulator_priv; + u16 val = dib8000_read_word(state, 390); + *strength = 65535 - val; + return 0; +} + +static int dib8000_read_snr(struct dvb_frontend *fe, u16 * snr) +{ + struct dib8000_state *state = fe->demodulator_priv; + u16 val; + s32 signal_mant, signal_exp, noise_mant, noise_exp; + u32 result = 0; + + val = dib8000_read_word(state, 542); + noise_mant = (val >> 6) & 0xff; + noise_exp = (val & 0x3f); + + val = dib8000_read_word(state, 543); + signal_mant = (val >> 6) & 0xff; + signal_exp = (val & 0x3f); + + if ((noise_exp & 0x20) != 0) + noise_exp -= 0x40; + if ((signal_exp & 0x20) != 0) + signal_exp -= 0x40; + + if (signal_mant != 0) + result = intlog10(2) * 10 * signal_exp + 10 * intlog10(signal_mant); + else + result = intlog10(2) * 10 * signal_exp - 100; + if (noise_mant != 0) + result -= intlog10(2) * 10 * noise_exp + 10 * intlog10(noise_mant); + else + result -= intlog10(2) * 10 * noise_exp - 100; + + *snr = result / (1 << 24); + return 0; +} + +int dib8000_i2c_enumeration(struct i2c_adapter *host, int no_of_demods, u8 default_addr, u8 first_addr) +{ + int k = 0; + u8 new_addr = 0; + struct i2c_device client = {.adap = host }; + + for (k = no_of_demods - 1; k >= 0; k--) { + /* designated i2c address */ + new_addr = first_addr + (k << 1); + + client.addr = new_addr; + dib8000_i2c_write16(&client, 1287, 0x0003); /* sram lead in, rdy */ + if (dib8000_identify(&client) == 0) { + dib8000_i2c_write16(&client, 1287, 0x0003); /* sram lead in, rdy */ + client.addr = default_addr; + if (dib8000_identify(&client) == 0) { + dprintk("#%d: not identified", k); + return -EINVAL; + } + } + + /* start diversity to pull_down div_str - just for i2c-enumeration */ + dib8000_i2c_write16(&client, 1286, (1 << 10) | (4 << 6)); + + /* set new i2c address and force divstart */ + dib8000_i2c_write16(&client, 1285, (new_addr << 2) | 0x2); + client.addr = new_addr; + dib8000_identify(&client); + + dprintk("IC %d initialized (to i2c_address 0x%x)", k, new_addr); + } + + for (k = 0; k < no_of_demods; k++) { + new_addr = first_addr | (k << 1); + client.addr = new_addr; + + // unforce divstr + dib8000_i2c_write16(&client, 1285, new_addr << 2); + + /* deactivate div - it was just for i2c-enumeration */ + dib8000_i2c_write16(&client, 1286, 0); + } + + return 0; +} + +EXPORT_SYMBOL(dib8000_i2c_enumeration); +static int dib8000_fe_get_tune_settings(struct dvb_frontend *fe, struct dvb_frontend_tune_settings *tune) +{ + tune->min_delay_ms = 1000; + tune->step_size = 0; + tune->max_drift = 0; + return 0; +} + +static void dib8000_release(struct dvb_frontend *fe) +{ + struct dib8000_state *st = fe->demodulator_priv; + dibx000_exit_i2c_master(&st->i2c_master); + kfree(st); +} + +struct i2c_adapter *dib8000_get_i2c_master(struct dvb_frontend *fe, enum dibx000_i2c_interface intf, int gating) +{ + struct dib8000_state *st = fe->demodulator_priv; + return dibx000_get_i2c_adapter(&st->i2c_master, intf, gating); +} + +EXPORT_SYMBOL(dib8000_get_i2c_master); + +static const struct dvb_frontend_ops dib8000_ops = { + .info = { + .name = "DiBcom 8000 ISDB-T", + .type = FE_OFDM, + .frequency_min = 44250000, + .frequency_max = 867250000, + .frequency_stepsize = 62500, + .caps = FE_CAN_INVERSION_AUTO | + 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_RECOVER | FE_CAN_HIERARCHY_AUTO, + }, + + .release = dib8000_release, + + .init = dib8000_wakeup, + .sleep = dib8000_sleep, + + .set_frontend = dib8000_set_frontend, + .get_tune_settings = dib8000_fe_get_tune_settings, + .get_frontend = dib8000_get_frontend, + + .read_status = dib8000_read_status, + .read_ber = dib8000_read_ber, + .read_signal_strength = dib8000_read_signal_strength, + .read_snr = dib8000_read_snr, + .read_ucblocks = dib8000_read_unc_blocks, +}; + +struct dvb_frontend *dib8000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib8000_config *cfg) +{ + struct dvb_frontend *fe; + struct dib8000_state *state; + + dprintk("dib8000_attach"); + + state = kzalloc(sizeof(struct dib8000_state), GFP_KERNEL); + if (state == NULL) + return NULL; + + memcpy(&state->cfg, cfg, sizeof(struct dib8000_config)); + state->i2c.adap = i2c_adap; + state->i2c.addr = i2c_addr; + state->gpio_val = cfg->gpio_val; + state->gpio_dir = cfg->gpio_dir; + + /* Ensure the output mode remains at the previous default if it's + * not specifically set by the caller. + */ + if ((state->cfg.output_mode != OUTMODE_MPEG2_SERIAL) && (state->cfg.output_mode != OUTMODE_MPEG2_PAR_GATED_CLK)) + state->cfg.output_mode = OUTMODE_MPEG2_FIFO; + + fe = &state->fe; + fe->demodulator_priv = state; + memcpy(&state->fe.ops, &dib8000_ops, sizeof(struct dvb_frontend_ops)); + + state->timf_default = cfg->pll->timf; + + if (dib8000_identify(&state->i2c) == 0) + goto error; + + dibx000_init_i2c_master(&state->i2c_master, DIB8000, state->i2c.adap, state->i2c.addr); + + dib8000_reset(fe); + + dib8000_write_word(state, 285, (dib8000_read_word(state, 285) & ~0x60) | (3 << 5)); /* ber_rs_len = 3 */ + + return fe; + + error: + kfree(state); + return NULL; +} + +EXPORT_SYMBOL(dib8000_attach); + +MODULE_AUTHOR("Olivier Grenie "); +MODULE_DESCRIPTION("Driver for the DiBcom 8000 ISDB-T demodulator"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/frontends/dib8000.h b/drivers/media/dvb/frontends/dib8000.h new file mode 100644 index 000000000000..a86de340dd54 --- /dev/null +++ b/drivers/media/dvb/frontends/dib8000.h @@ -0,0 +1,79 @@ +#ifndef DIB8000_H +#define DIB8000_H + +#include "dibx000_common.h" + +struct dib8000_config { + u8 output_mpeg2_in_188_bytes; + u8 hostbus_diversity; + u8 tuner_is_baseband; + int (*update_lna) (struct dvb_frontend *, u16 agc_global); + + u8 agc_config_count; + struct dibx000_agc_config *agc; + struct dibx000_bandwidth_config *pll; + +#define DIB8000_GPIO_DEFAULT_DIRECTIONS 0xffff + u16 gpio_dir; +#define DIB8000_GPIO_DEFAULT_VALUES 0x0000 + u16 gpio_val; +#define DIB8000_GPIO_PWM_POS0(v) ((v & 0xf) << 12) +#define DIB8000_GPIO_PWM_POS1(v) ((v & 0xf) << 8 ) +#define DIB8000_GPIO_PWM_POS2(v) ((v & 0xf) << 4 ) +#define DIB8000_GPIO_PWM_POS3(v) (v & 0xf) +#define DIB8000_GPIO_DEFAULT_PWM_POS 0xffff + u16 gpio_pwm_pos; + u16 pwm_freq_div; + + void (*agc_control) (struct dvb_frontend *, u8 before); + + u16 drives; + u16 diversity_delay; + u8 div_cfg; + u8 output_mode; + u8 refclksel; +}; + +#define DEFAULT_DIB8000_I2C_ADDRESS 18 + +#if defined(CONFIG_DVB_DIB8000) || (defined(CONFIG_DVB_DIB8000_MODULE) && defined(MODULE)) +extern struct dvb_frontend *dib8000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib8000_config *cfg); +extern struct i2c_adapter *dib8000_get_i2c_master(struct dvb_frontend *, enum dibx000_i2c_interface, int); + +extern int dib8000_i2c_enumeration(struct i2c_adapter *host, int no_of_demods, u8 default_addr, u8 first_addr); + +extern int dib8000_set_gpio(struct dvb_frontend *, u8 num, u8 dir, u8 val); +extern int dib8000_set_wbd_ref(struct dvb_frontend *, u16 value); +#else +static inline struct dvb_frontend *dib8000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib8000_config *cfg) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} + +static inline struct i2c_adapter *dib8000_get_i2c_master(struct dvb_frontend *fe, enum dibx000_i2c_interface i, int x) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} + +int dib8000_i2c_enumeration(struct i2c_adapter *host, int no_of_demods, u8 default_addr, u8 first_addr) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return -ENODEV; +} + +int dib8000_set_gpio(struct dvb_frontend *fe, u8 num, u8 dir, u8 val) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return -ENODEV; +} + +int dib8000_set_wbd_ref(struct dvb_frontend *fe, u16 value) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return -ENODEV; +} +#endif + +#endif diff --git a/drivers/media/dvb/frontends/dibx000_common.c b/drivers/media/dvb/frontends/dibx000_common.c index 315e09e95b0c..4efca30d2127 100644 --- a/drivers/media/dvb/frontends/dibx000_common.c +++ b/drivers/media/dvb/frontends/dibx000_common.c @@ -15,29 +15,31 @@ static int dibx000_write_word(struct dibx000_i2c_master *mst, u16 reg, u16 val) (val >> 8) & 0xff, val & 0xff, }; struct i2c_msg msg = { - .addr = mst->i2c_addr, .flags = 0, .buf = b, .len = 4 + .addr = mst->i2c_addr,.flags = 0,.buf = b,.len = 4 }; return i2c_transfer(mst->i2c_adap, &msg, 1) != 1 ? -EREMOTEIO : 0; } -static int dibx000_i2c_select_interface(struct dibx000_i2c_master *mst, enum dibx000_i2c_interface intf) +static int dibx000_i2c_select_interface(struct dibx000_i2c_master *mst, + enum dibx000_i2c_interface intf) { if (mst->device_rev > DIB3000MC && mst->selected_interface != intf) { - dprintk("selecting interface: %d\n",intf); + dprintk("selecting interface: %d\n", intf); mst->selected_interface = intf; return dibx000_write_word(mst, mst->base_reg + 4, intf); } return 0; } -static int dibx000_i2c_gate_ctrl(struct dibx000_i2c_master *mst, u8 tx[4], u8 addr, int onoff) +static int dibx000_i2c_gate_ctrl(struct dibx000_i2c_master *mst, u8 tx[4], + u8 addr, int onoff) { u16 val; if (onoff) - val = addr << 8; // bit 7 = use master or not, if 0, the gate is open + val = addr << 8; // bit 7 = use master or not, if 0, the gate is open else val = 1 << 7; @@ -45,7 +47,7 @@ static int dibx000_i2c_gate_ctrl(struct dibx000_i2c_master *mst, u8 tx[4], u8 ad val <<= 1; tx[0] = (((mst->base_reg + 1) >> 8) & 0xff); - tx[1] = ( (mst->base_reg + 1) & 0xff); + tx[1] = ((mst->base_reg + 1) & 0xff); tx[2] = val >> 8; tx[3] = val & 0xff; @@ -57,59 +59,78 @@ static u32 dibx000_i2c_func(struct i2c_adapter *adapter) return I2C_FUNC_I2C; } -static int dibx000_i2c_gated_tuner_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msg[], int num) +static int dibx000_i2c_gated_tuner_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg msg[], int num) { struct dibx000_i2c_master *mst = i2c_get_adapdata(i2c_adap); struct i2c_msg m[2 + num]; u8 tx_open[4], tx_close[4]; - memset(m,0, sizeof(struct i2c_msg) * (2 + num)); + memset(m, 0, sizeof(struct i2c_msg) * (2 + num)); dibx000_i2c_select_interface(mst, DIBX000_I2C_INTERFACE_TUNER); - dibx000_i2c_gate_ctrl(mst, tx_open, msg[0].addr, 1); + dibx000_i2c_gate_ctrl(mst, tx_open, msg[0].addr, 1); m[0].addr = mst->i2c_addr; - m[0].buf = tx_open; - m[0].len = 4; + m[0].buf = tx_open; + m[0].len = 4; memcpy(&m[1], msg, sizeof(struct i2c_msg) * num); dibx000_i2c_gate_ctrl(mst, tx_close, 0, 0); - m[num+1].addr = mst->i2c_addr; - m[num+1].buf = tx_close; - m[num+1].len = 4; + m[num + 1].addr = mst->i2c_addr; + m[num + 1].buf = tx_close; + m[num + 1].len = 4; - return i2c_transfer(mst->i2c_adap, m, 2+num) == 2 + num ? num : -EIO; + return i2c_transfer(mst->i2c_adap, m, 2 + num) == 2 + num ? num : -EIO; } static struct i2c_algorithm dibx000_i2c_gated_tuner_algo = { - .master_xfer = dibx000_i2c_gated_tuner_xfer, + .master_xfer = dibx000_i2c_gated_tuner_xfer, .functionality = dibx000_i2c_func, }; -struct i2c_adapter * dibx000_get_i2c_adapter(struct dibx000_i2c_master *mst, enum dibx000_i2c_interface intf, int gating) +struct i2c_adapter *dibx000_get_i2c_adapter(struct dibx000_i2c_master *mst, + enum dibx000_i2c_interface intf, + int gating) { struct i2c_adapter *i2c = NULL; switch (intf) { - case DIBX000_I2C_INTERFACE_TUNER: - if (gating) - i2c = &mst->gated_tuner_i2c_adap; - break; - default: - printk(KERN_ERR "DiBX000: incorrect I2C interface selected\n"); - break; + case DIBX000_I2C_INTERFACE_TUNER: + if (gating) + i2c = &mst->gated_tuner_i2c_adap; + break; + default: + printk(KERN_ERR "DiBX000: incorrect I2C interface selected\n"); + break; } return i2c; } + EXPORT_SYMBOL(dibx000_get_i2c_adapter); -static int i2c_adapter_init(struct i2c_adapter *i2c_adap, struct i2c_algorithm *algo, const char *name, struct dibx000_i2c_master *mst) +void dibx000_reset_i2c_master(struct dibx000_i2c_master *mst) +{ + /* initialize the i2c-master by closing the gate */ + u8 tx[4]; + struct i2c_msg m = {.addr = mst->i2c_addr,.buf = tx,.len = 4 }; + + dibx000_i2c_gate_ctrl(mst, tx, 0, 0); + i2c_transfer(mst->i2c_adap, &m, 1); + mst->selected_interface = 0xff; // the first time force a select of the I2C + dibx000_i2c_select_interface(mst, DIBX000_I2C_INTERFACE_TUNER); +} + +EXPORT_SYMBOL(dibx000_reset_i2c_master); + +static int i2c_adapter_init(struct i2c_adapter *i2c_adap, + struct i2c_algorithm *algo, const char *name, + struct dibx000_i2c_master *mst) { strncpy(i2c_adap->name, name, sizeof(i2c_adap->name)); - i2c_adap->class = I2C_CLASS_TV_DIGITAL, - i2c_adap->algo = algo; + i2c_adap->class = I2C_CLASS_TV_DIGITAL, i2c_adap->algo = algo; i2c_adap->algo_data = NULL; i2c_set_adapdata(i2c_adap, mst); if (i2c_add_adapter(i2c_adap) < 0) @@ -117,34 +138,40 @@ static int i2c_adapter_init(struct i2c_adapter *i2c_adap, struct i2c_algorithm * return 0; } -int dibx000_init_i2c_master(struct dibx000_i2c_master *mst, u16 device_rev, struct i2c_adapter *i2c_adap, u8 i2c_addr) +int dibx000_init_i2c_master(struct dibx000_i2c_master *mst, u16 device_rev, + struct i2c_adapter *i2c_adap, u8 i2c_addr) { u8 tx[4]; - struct i2c_msg m = { .addr = i2c_addr >> 1, .buf = tx, .len = 4 }; + struct i2c_msg m = {.addr = i2c_addr >> 1,.buf = tx,.len = 4 }; mst->device_rev = device_rev; - mst->i2c_adap = i2c_adap; - mst->i2c_addr = i2c_addr >> 1; + mst->i2c_adap = i2c_adap; + mst->i2c_addr = i2c_addr >> 1; - if (device_rev == DIB7000P) + if (device_rev == DIB7000P || device_rev == DIB8000) mst->base_reg = 1024; else mst->base_reg = 768; - if (i2c_adapter_init(&mst->gated_tuner_i2c_adap, &dibx000_i2c_gated_tuner_algo, "DiBX000 tuner I2C bus", mst) != 0) - printk(KERN_ERR "DiBX000: could not initialize the tuner i2c_adapter\n"); + if (i2c_adapter_init + (&mst->gated_tuner_i2c_adap, &dibx000_i2c_gated_tuner_algo, + "DiBX000 tuner I2C bus", mst) != 0) + printk(KERN_ERR + "DiBX000: could not initialize the tuner i2c_adapter\n"); /* initialize the i2c-master by closing the gate */ dibx000_i2c_gate_ctrl(mst, tx, 0, 0); return i2c_transfer(i2c_adap, &m, 1) == 1; } + EXPORT_SYMBOL(dibx000_init_i2c_master); void dibx000_exit_i2c_master(struct dibx000_i2c_master *mst) { i2c_del_adapter(&mst->gated_tuner_i2c_adap); } + EXPORT_SYMBOL(dibx000_exit_i2c_master); MODULE_AUTHOR("Patrick Boettcher "); diff --git a/drivers/media/dvb/frontends/dibx000_common.h b/drivers/media/dvb/frontends/dibx000_common.h index 84e4d5362922..5be10eca07c0 100644 --- a/drivers/media/dvb/frontends/dibx000_common.h +++ b/drivers/media/dvb/frontends/dibx000_common.h @@ -2,7 +2,7 @@ #define DIBX000_COMMON_H enum dibx000_i2c_interface { - DIBX000_I2C_INTERFACE_TUNER = 0, + DIBX000_I2C_INTERFACE_TUNER = 0, DIBX000_I2C_INTERFACE_GPIO_1_2 = 1, DIBX000_I2C_INTERFACE_GPIO_3_4 = 2 }; @@ -12,22 +12,29 @@ struct dibx000_i2c_master { #define DIB7000 2 #define DIB7000P 11 #define DIB7000MC 12 +#define DIB8000 13 u16 device_rev; enum dibx000_i2c_interface selected_interface; -// struct i2c_adapter tuner_i2c_adap; - struct i2c_adapter gated_tuner_i2c_adap; +// struct i2c_adapter tuner_i2c_adap; + struct i2c_adapter gated_tuner_i2c_adap; struct i2c_adapter *i2c_adap; - u8 i2c_addr; + u8 i2c_addr; u16 base_reg; }; -extern int dibx000_init_i2c_master(struct dibx000_i2c_master *mst, u16 device_rev, struct i2c_adapter *i2c_adap, u8 i2c_addr); -extern struct i2c_adapter * dibx000_get_i2c_adapter(struct dibx000_i2c_master *mst, enum dibx000_i2c_interface intf, int gating); +extern int dibx000_init_i2c_master(struct dibx000_i2c_master *mst, + u16 device_rev, struct i2c_adapter *i2c_adap, + u8 i2c_addr); +extern struct i2c_adapter *dibx000_get_i2c_adapter(struct dibx000_i2c_master + *mst, + enum dibx000_i2c_interface + intf, int gating); extern void dibx000_exit_i2c_master(struct dibx000_i2c_master *mst); +extern void dibx000_reset_i2c_master(struct dibx000_i2c_master *mst); #define BAND_LBAND 0x01 #define BAND_UHF 0x02 @@ -41,18 +48,18 @@ extern void dibx000_exit_i2c_master(struct dibx000_i2c_master *mst); (freq_kHz) <= 2000000 ? BAND_LBAND : BAND_SBAND ) struct dibx000_agc_config { - /* defines the capabilities of this AGC-setting - using the BAND_-defines*/ - u8 band_caps; + /* defines the capabilities of this AGC-setting - using the BAND_-defines */ + u8 band_caps; u16 setup; u16 inv_gain; u16 time_stabiliz; - u8 alpha_level; + u8 alpha_level; u16 thlock; - u8 wbd_inv; + u8 wbd_inv; u16 wbd_ref; u8 wbd_sel; u8 wbd_alpha; @@ -92,8 +99,8 @@ struct dibx000_agc_config { }; struct dibx000_bandwidth_config { - u32 internal; - u32 sampling; + u32 internal; + u32 sampling; u8 pll_prediv; u8 pll_ratio; -- cgit v1.2.2 From ba3fe3a96374ff209f532a4924743bb1fa4d57f6 Mon Sep 17 00:00:00 2001 From: Patrick Boettcher Date: Wed, 16 Sep 2009 09:51:30 -0300 Subject: V4L/DVB (12901): DiB0700: add support for STK807XP and STK807XPVR This patchs adds support for the DiB8000 based devices STK807xP and STK807xPVR to the dib0700-device-tree. [mchehab@redhat.com: Fix merge conflicts] Signed-off-by: Olivier Grenie Signed-off-by: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/dib0700_devices.c | 367 +++++++++++++++++++++++++++- drivers/media/dvb/dvb-usb/dvb-usb-ids.h | 2 + 2 files changed, 368 insertions(+), 1 deletion(-) (limited to 'drivers/media/dvb') diff --git a/drivers/media/dvb/dvb-usb/dib0700_devices.c b/drivers/media/dvb/dvb-usb/dib0700_devices.c index 10ade261b0ab..442afbaabeef 100644 --- a/drivers/media/dvb/dvb-usb/dib0700_devices.c +++ b/drivers/media/dvb/dvb-usb/dib0700_devices.c @@ -4,13 +4,14 @@ * under the terms of the GNU General Public License as published by the Free * Software Foundation, version 2. * - * Copyright (C) 2005-7 DiBcom, SA + * Copyright (C) 2005-9 DiBcom, SA et al */ #include "dib0700.h" #include "dib3000mc.h" #include "dib7000m.h" #include "dib7000p.h" +#include "dib8000.h" #include "mt2060.h" #include "mt2266.h" #include "tuner-xc2028.h" @@ -1268,6 +1269,306 @@ static int stk7070p_frontend_attach(struct dvb_usb_adapter *adap) return adap->fe == NULL ? -ENODEV : 0; } +/* DIB807x generic */ +static struct dibx000_agc_config dib807x_agc_config[2] = { + { + BAND_VHF, + /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, + * P_agc_freq_pwm_div=1, P_agc_inv_pwm1=0, + * P_agc_inv_pwm2=0,P_agc_inh_dc_rv_est=0, + * P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=5, + * P_agc_write=0 */ + (0 << 15) | (0 << 14) | (7 << 11) | (0 << 10) | (0 << 9) | + (0 << 8) | (3 << 5) | (0 << 4) | (5 << 1) | + (0 << 0), /* setup*/ + + 600, /* inv_gain*/ + 10, /* time_stabiliz*/ + + 0, /* alpha_level*/ + 118, /* thlock*/ + + 0, /* wbd_inv*/ + 3530, /* wbd_ref*/ + 1, /* wbd_sel*/ + 5, /* wbd_alpha*/ + + 65535, /* agc1_max*/ + 0, /* agc1_min*/ + + 65535, /* agc2_max*/ + 0, /* agc2_min*/ + + 0, /* agc1_pt1*/ + 40, /* agc1_pt2*/ + 183, /* agc1_pt3*/ + 206, /* agc1_slope1*/ + 255, /* agc1_slope2*/ + 72, /* agc2_pt1*/ + 152, /* agc2_pt2*/ + 88, /* agc2_slope1*/ + 90, /* agc2_slope2*/ + + 17, /* alpha_mant*/ + 27, /* alpha_exp*/ + 23, /* beta_mant*/ + 51, /* beta_exp*/ + + 0, /* perform_agc_softsplit*/ + }, { + BAND_UHF, + /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, + * P_agc_freq_pwm_div=1, P_agc_inv_pwm1=0, + * P_agc_inv_pwm2=0, P_agc_inh_dc_rv_est=0, + * P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=5, + * P_agc_write=0 */ + (0 << 15) | (0 << 14) | (1 << 11) | (0 << 10) | (0 << 9) | + (0 << 8) | (3 << 5) | (0 << 4) | (5 << 1) | + (0 << 0), /* setup */ + + 600, /* inv_gain*/ + 10, /* time_stabiliz*/ + + 0, /* alpha_level*/ + 118, /* thlock*/ + + 0, /* wbd_inv*/ + 3530, /* wbd_ref*/ + 1, /* wbd_sel*/ + 5, /* wbd_alpha*/ + + 65535, /* agc1_max*/ + 0, /* agc1_min*/ + + 65535, /* agc2_max*/ + 0, /* agc2_min*/ + + 0, /* agc1_pt1*/ + 40, /* agc1_pt2*/ + 183, /* agc1_pt3*/ + 206, /* agc1_slope1*/ + 255, /* agc1_slope2*/ + 72, /* agc2_pt1*/ + 152, /* agc2_pt2*/ + 88, /* agc2_slope1*/ + 90, /* agc2_slope2*/ + + 17, /* alpha_mant*/ + 27, /* alpha_exp*/ + 23, /* beta_mant*/ + 51, /* beta_exp*/ + + 0, /* perform_agc_softsplit*/ + } +}; + +static struct dibx000_bandwidth_config dib807x_bw_config_12_mhz = { + 60000, 15000, /* internal, sampling*/ + 1, 20, 3, 1, 0, /* pll_cfg: prediv, ratio, range, reset, bypass*/ + 0, 0, 1, 1, 2, /* misc: refdiv, bypclk_div, IO_CLK_en_core, + ADClkSrc, modulo */ + (3 << 14) | (1 << 12) | (599 << 0), /* sad_cfg: refsel, sel, freq_15k*/ + (0 << 25) | 0, /* ifreq = 0.000000 MHz*/ + 18179755, /* timf*/ + 12000000, /* xtal_hz*/ +}; + +static struct dib8000_config dib807x_dib8000_config[2] = { + { + .output_mpeg2_in_188_bytes = 1, + + .agc_config_count = 2, + .agc = dib807x_agc_config, + .pll = &dib807x_bw_config_12_mhz, + .tuner_is_baseband = 1, + + .gpio_dir = DIB8000_GPIO_DEFAULT_DIRECTIONS, + .gpio_val = DIB8000_GPIO_DEFAULT_VALUES, + .gpio_pwm_pos = DIB8000_GPIO_DEFAULT_PWM_POS, + + .hostbus_diversity = 1, + .div_cfg = 1, + .agc_control = &dib0070_ctrl_agc_filter, + .output_mode = OUTMODE_MPEG2_FIFO, + .drives = 0x2d98, + }, { + .output_mpeg2_in_188_bytes = 1, + + .agc_config_count = 2, + .agc = dib807x_agc_config, + .pll = &dib807x_bw_config_12_mhz, + .tuner_is_baseband = 1, + + .gpio_dir = DIB8000_GPIO_DEFAULT_DIRECTIONS, + .gpio_val = DIB8000_GPIO_DEFAULT_VALUES, + .gpio_pwm_pos = DIB8000_GPIO_DEFAULT_PWM_POS, + + .hostbus_diversity = 1, + .agc_control = &dib0070_ctrl_agc_filter, + .output_mode = OUTMODE_MPEG2_FIFO, + .drives = 0x2d98, + } +}; + +static int dib807x_tuner_reset(struct dvb_frontend *fe, int onoff) +{ + return dib8000_set_gpio(fe, 5, 0, !onoff); +} + +static int dib807x_tuner_sleep(struct dvb_frontend *fe, int onoff) +{ + return dib8000_set_gpio(fe, 0, 0, onoff); +} + +static const struct dib0070_wbd_gain_cfg dib8070_wbd_gain_cfg[] = { + { 240, 7}, + { 0xffff, 6}, +}; + +static struct dib0070_config dib807x_dib0070_config[2] = { + { + .i2c_address = DEFAULT_DIB0070_I2C_ADDRESS, + .reset = dib807x_tuner_reset, + .sleep = dib807x_tuner_sleep, + .clock_khz = 12000, + .clock_pad_drive = 4, + .vga_filter = 1, + .force_crystal_mode = 1, + .enable_third_order_filter = 1, + .charge_pump = 0, + .wbd_gain = dib8070_wbd_gain_cfg, + .osc_buffer_state = 0, + .freq_offset_khz_uhf = -100, + .freq_offset_khz_vhf = -100, + }, { + .i2c_address = DEFAULT_DIB0070_I2C_ADDRESS, + .reset = dib807x_tuner_reset, + .sleep = dib807x_tuner_sleep, + .clock_khz = 12000, + .clock_pad_drive = 2, + .vga_filter = 1, + .force_crystal_mode = 1, + .enable_third_order_filter = 1, + .charge_pump = 0, + .wbd_gain = dib8070_wbd_gain_cfg, + .osc_buffer_state = 0, + .freq_offset_khz_uhf = -25, + .freq_offset_khz_vhf = -25, + } +}; + +static int dib807x_set_param_override(struct dvb_frontend *fe, + struct dvb_frontend_parameters *fep) +{ + struct dvb_usb_adapter *adap = fe->dvb->priv; + struct dib0700_adapter_state *state = adap->priv; + + u16 offset = dib0070_wbd_offset(fe); + u8 band = BAND_OF_FREQUENCY(fep->frequency/1000); + switch (band) { + case BAND_VHF: + offset += 750; + break; + case BAND_UHF: /* fall-thru wanted */ + default: + offset += 250; break; + } + deb_info("WBD for DiB8000: %d\n", offset); + dib8000_set_wbd_ref(fe, offset); + + return state->set_param_save(fe, fep); +} + +static int dib807x_tuner_attach(struct dvb_usb_adapter *adap) +{ + struct dib0700_adapter_state *st = adap->priv; + struct i2c_adapter *tun_i2c = dib8000_get_i2c_master(adap->fe, + DIBX000_I2C_INTERFACE_TUNER, 1); + + if (adap->id == 0) { + if (dvb_attach(dib0070_attach, adap->fe, tun_i2c, + &dib807x_dib0070_config[0]) == NULL) + return -ENODEV; + } else { + if (dvb_attach(dib0070_attach, adap->fe, tun_i2c, + &dib807x_dib0070_config[1]) == NULL) + return -ENODEV; + } + + st->set_param_save = adap->fe->ops.tuner_ops.set_params; + adap->fe->ops.tuner_ops.set_params = dib807x_set_param_override; + return 0; +} + + +/* STK807x */ +static int stk807x_frontend_attach(struct dvb_usb_adapter *adap) +{ + dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); + msleep(10); + 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, GPIO10, GPIO_OUT, 0); + + dib0700_ctrl_clock(adap->dev, 72, 1); + + msleep(10); + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); + msleep(10); + dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); + + dib8000_i2c_enumeration(&adap->dev->i2c_adap, 1, 18, + 0x80); + + adap->fe = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x80, + &dib807x_dib8000_config[0]); + + return adap->fe == NULL ? -ENODEV : 0; +} + +/* STK807xPVR */ +static int stk807xpvr_frontend_attach0(struct dvb_usb_adapter *adap) +{ + dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 0); + msleep(30); + dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); + msleep(500); + 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, GPIO10, GPIO_OUT, 0); + + dib0700_ctrl_clock(adap->dev, 72, 1); + + msleep(10); + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); + msleep(10); + dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); + + /* initialize IC 0 */ + dib8000_i2c_enumeration(&adap->dev->i2c_adap, 1, 0x12, 0x80); + + adap->fe = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x80, + &dib807x_dib8000_config[0]); + + return adap->fe == NULL ? -ENODEV : 0; +} + +static int stk807xpvr_frontend_attach1(struct dvb_usb_adapter *adap) +{ + /* initialize IC 1 */ + dib8000_i2c_enumeration(&adap->dev->i2c_adap, 1, 0x22, 0x82); + + adap->fe = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x82, + &dib807x_dib8000_config[1]); + + return adap->fe == NULL ? -ENODEV : 0; +} + + /* STK7070PD */ static struct dib7000p_config stk7070pd_dib7000p_config[2] = { { @@ -1557,6 +1858,8 @@ struct usb_device_id dib0700_usb_id_table[] = { { USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV282E) }, { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_STK7770P) }, /* 60 */{ USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_T_XXS_2) }, + { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_STK807XPVR) }, + { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_STK807XP) }, { 0 } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, dib0700_usb_id_table); @@ -2044,6 +2347,68 @@ struct dvb_usb_device_properties dib0700_devices[] = { { 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 = stk807x_frontend_attach, + .tuner_attach = dib807x_tuner_attach, + + DIB0700_DEFAULT_STREAMING_CONFIG(0x02), + + .size_of_priv = + sizeof(struct dib0700_adapter_state), + }, + }, + + .num_device_descs = 1, + .devices = { + { "DiBcom STK807xP reference design", + { &dib0700_usb_id_table[62], 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 = 2, + .adapter = { + { + .frontend_attach = stk807xpvr_frontend_attach0, + .tuner_attach = dib807x_tuner_attach, + + DIB0700_DEFAULT_STREAMING_CONFIG(0x02), + + .size_of_priv = + sizeof(struct dib0700_adapter_state), + }, + { + .frontend_attach = stk807xpvr_frontend_attach1, + .tuner_attach = dib807x_tuner_attach, + + DIB0700_DEFAULT_STREAMING_CONFIG(0x03), + + .size_of_priv = + sizeof(struct dib0700_adapter_state), + }, + }, + + .num_device_descs = 1, + .devices = { + { "DiBcom STK807xPVR reference design", + { &dib0700_usb_id_table[61], NULL }, + { NULL }, + }, + }, + .rc_interval = DEFAULT_RC_INTERVAL, .rc_key_map = dib0700_rc_keys, .rc_key_map_size = ARRAY_SIZE(dib0700_rc_keys), diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h index a07959c1c065..7c0e3124a69b 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h +++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h @@ -95,6 +95,8 @@ #define USB_PID_DIBCOM_STK7700_U7000 0x7001 #define USB_PID_DIBCOM_STK7070P 0x1ebc #define USB_PID_DIBCOM_STK7070PD 0x1ebe +#define USB_PID_DIBCOM_STK807XP 0x1f90 +#define USB_PID_DIBCOM_STK807XPVR 0x1f98 #define USB_PID_DIBCOM_ANCHOR_2135_COLD 0x2131 #define USB_PID_DIBCOM_STK7770P 0x1e80 #define USB_PID_DPOSH_M9206_COLD 0x9206 -- cgit v1.2.2 From 78f3bc631f39fbfa4eaf039f0664265e71f725fb Mon Sep 17 00:00:00 2001 From: Patrick Boettcher Date: Mon, 17 Aug 2009 12:53:51 -0300 Subject: V4L/DVB (12903): DiB8000: fix channel search parameter initialization This patch is fixing the initialization of the channel search parameters. Signed-off-by: Olivier Grenie Signed-off-by: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/dib8000.c | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) (limited to 'drivers/media/dvb') diff --git a/drivers/media/dvb/frontends/dib8000.c b/drivers/media/dvb/frontends/dib8000.c index 99ee6b09ce1d..852c790d09d9 100644 --- a/drivers/media/dvb/frontends/dib8000.c +++ b/drivers/media/dvb/frontends/dib8000.c @@ -22,7 +22,7 @@ #define FE_CALLBACK_TIME_NEVER 0xffffffff -static int debug = 0; +static int debug; module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "turn on debugging (default: 0)"); @@ -1671,10 +1671,6 @@ static int dib8000_autosearch_start(struct dvb_frontend *fe) int slist = 0; - state->fe.dtv_property_cache.transmission_mode = TRANSMISSION_MODE_8K; - state->fe.dtv_property_cache.guard_interval = GUARD_INTERVAL_1_8; - //state->fe.dtv_property_cache.isdbt_sb_mode = 0; - //state->fe.dtv_property_cache.isdbt_partial_reception = 0; state->fe.dtv_property_cache.inversion = 0; if (!state->fe.dtv_property_cache.isdbt_sb_mode) state->fe.dtv_property_cache.layer[0].segment_count = 13; @@ -1684,6 +1680,8 @@ static int dib8000_autosearch_start(struct dvb_frontend *fe) //choose the right list, in sb, always do everything if (state->fe.dtv_property_cache.isdbt_sb_mode) { + state->fe.dtv_property_cache.transmission_mode = TRANSMISSION_MODE_8K; + state->fe.dtv_property_cache.guard_interval = GUARD_INTERVAL_1_8; slist = 7; dib8000_write_word(state, 0, (dib8000_read_word(state, 0) & 0x9fff) | (1 << 13)); } else { @@ -1691,22 +1689,21 @@ static int dib8000_autosearch_start(struct dvb_frontend *fe) if (state->fe.dtv_property_cache.transmission_mode == TRANSMISSION_MODE_AUTO) { slist = 7; dib8000_write_word(state, 0, (dib8000_read_word(state, 0) & 0x9fff) | (1 << 13)); // P_mode = 1 to have autosearch start ok with mode2 - } else { + } else slist = 3; - state->fe.dtv_property_cache.transmission_mode = state->fe.dtv_property_cache.transmission_mode; - } } else { if (state->fe.dtv_property_cache.transmission_mode == TRANSMISSION_MODE_AUTO) { slist = 2; - state->fe.dtv_property_cache.guard_interval = state->fe.dtv_property_cache.guard_interval; dib8000_write_word(state, 0, (dib8000_read_word(state, 0) & 0x9fff) | (1 << 13)); // P_mode = 1 - } else { + } else slist = 0; - state->fe.dtv_property_cache.transmission_mode = state->fe.dtv_property_cache.transmission_mode; - state->fe.dtv_property_cache.guard_interval = state->fe.dtv_property_cache.guard_interval; - } } + if (state->fe.dtv_property_cache.transmission_mode == TRANSMISSION_MODE_AUTO) + state->fe.dtv_property_cache.transmission_mode = TRANSMISSION_MODE_8K; + if (state->fe.dtv_property_cache.guard_interval == GUARD_INTERVAL_AUTO) + state->fe.dtv_property_cache.guard_interval = GUARD_INTERVAL_1_8; + dprintk("using list for autosearch : %d", slist); dib8000_set_channel(state, (unsigned char)slist, 1); //dib8000_write_word(state, 0, (dib8000_read_word(state, 0) & 0x9fff) | (1 << 13)); // P_mode = 1 @@ -1733,6 +1730,7 @@ static int dib8000_autosearch_start(struct dvb_frontend *fe) dib8000_write_word(state, 0, (u16) ((1 << 15) | value)); dib8000_read_word(state, 1284); // reset the INT. n_irq_pending dib8000_write_word(state, 0, (u16) value); + } return 0; -- cgit v1.2.2 From aaeab30f753d9499e04979e2a5a7feadba18c39d Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 16 Sep 2009 09:18:26 -0300 Subject: V4L/DVB (12906): dib0700: Add support for Prolink SBTVD Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/dib0700_devices.c | 7 ++++++- drivers/media/dvb/dvb-usb/dvb-usb-ids.h | 2 ++ 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'drivers/media/dvb') diff --git a/drivers/media/dvb/dvb-usb/dib0700_devices.c b/drivers/media/dvb/dvb-usb/dib0700_devices.c index 442afbaabeef..0b2812aa30a4 100644 --- a/drivers/media/dvb/dvb-usb/dib0700_devices.c +++ b/drivers/media/dvb/dvb-usb/dib0700_devices.c @@ -1860,6 +1860,7 @@ struct usb_device_id dib0700_usb_id_table[] = { /* 60 */{ USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_T_XXS_2) }, { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_STK807XPVR) }, { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_STK807XP) }, + { USB_DEVICE(USB_VID_PIXELVIEW, USB_PID_PIXELVIEW_SBTVD) }, { 0 } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, dib0700_usb_id_table); @@ -2365,12 +2366,16 @@ struct dvb_usb_device_properties dib0700_devices[] = { }, }, - .num_device_descs = 1, + .num_device_descs = 2, .devices = { { "DiBcom STK807xP reference design", { &dib0700_usb_id_table[62], NULL }, { NULL }, }, + { "Prolink Pixelview SBTVD", + { &dib0700_usb_id_table[63], NULL }, + { NULL }, + }, }, .rc_interval = DEFAULT_RC_INTERVAL, diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h index 7c0e3124a69b..2d51e3c28207 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h +++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h @@ -46,6 +46,7 @@ #define USB_VID_MSI_2 0x1462 #define USB_VID_OPERA1 0x695c #define USB_VID_PINNACLE 0x2304 +#define USB_VID_PIXELVIEW 0x1554 #define USB_VID_TECHNOTREND 0x0b48 #define USB_VID_TERRATEC 0x0ccd #define USB_VID_TELESTAR 0x10b9 @@ -202,6 +203,7 @@ #define USB_PID_PINNACLE_PCTV73A 0x0243 #define USB_PID_PINNACLE_PCTV73ESE 0x0245 #define USB_PID_PINNACLE_PCTV282E 0x0248 +#define USB_PID_PIXELVIEW_SBTVD 0x5010 #define USB_PID_PCTV_200E 0x020e #define USB_PID_PCTV_400E 0x020f #define USB_PID_PCTV_450E 0x0222 -- cgit v1.2.2 From ab69333690c9e0f278d5ca1dab4e76015f7ecc66 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Wed, 16 Sep 2009 19:47:01 -0300 Subject: V4L/DVB (12959): anysee: increase BULK transfer size from 512 to 8192 increase BULK transfer size from 512 to 8192 to increase wakeups Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/anysee.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media/dvb') diff --git a/drivers/media/dvb/dvb-usb/anysee.c b/drivers/media/dvb/dvb-usb/anysee.c index 7381aff4dcf6..f9d3e063626b 100644 --- a/drivers/media/dvb/dvb-usb/anysee.c +++ b/drivers/media/dvb/dvb-usb/anysee.c @@ -511,7 +511,7 @@ static struct dvb_usb_device_properties anysee_properties = { .endpoint = 0x82, .u = { .bulk = { - .buffersize = 512, + .buffersize = (16*512), } } }, -- cgit v1.2.2 From ae3745f63ad28233d23f3a2f7407b95717d0dba8 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Wed, 16 Sep 2009 19:50:25 -0300 Subject: V4L/DVB (12960): anysee: coding style fix Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/anysee.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/media/dvb') diff --git a/drivers/media/dvb/dvb-usb/anysee.c b/drivers/media/dvb/dvb-usb/anysee.c index f9d3e063626b..2ae7f648effe 100644 --- a/drivers/media/dvb/dvb-usb/anysee.c +++ b/drivers/media/dvb/dvb-usb/anysee.c @@ -203,11 +203,11 @@ static struct i2c_algorithm anysee_i2c_algo = { static int anysee_mt352_demod_init(struct dvb_frontend *fe) { - static u8 clock_config [] = { CLOCK_CTL, 0x38, 0x28 }; - static u8 reset [] = { RESET, 0x80 }; - static u8 adc_ctl_1_cfg [] = { ADC_CTL_1, 0x40 }; - static u8 agc_cfg [] = { AGC_TARGET, 0x28, 0x20 }; - static u8 gpp_ctl_cfg [] = { GPP_CTL, 0x33 }; + static u8 clock_config[] = { CLOCK_CTL, 0x38, 0x28 }; + static u8 reset[] = { RESET, 0x80 }; + static u8 adc_ctl_1_cfg[] = { ADC_CTL_1, 0x40 }; + static u8 agc_cfg[] = { AGC_TARGET, 0x28, 0x20 }; + static u8 gpp_ctl_cfg[] = { GPP_CTL, 0x33 }; static u8 capt_range_cfg[] = { CAPT_RANGE, 0x32 }; mt352_write(fe, clock_config, sizeof(clock_config)); @@ -485,7 +485,7 @@ static int anysee_probe(struct usb_interface *intf, return ret; } -static struct usb_device_id anysee_table [] = { +static struct usb_device_id anysee_table[] = { { USB_DEVICE(USB_VID_CYPRESS, USB_PID_ANYSEE) }, { USB_DEVICE(USB_VID_AMT, USB_PID_ANYSEE) }, { } /* Terminating entry */ -- cgit v1.2.2 From eb3b2d89bcd7bbdcff46f427d0f6f85c9e88701d Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Wed, 16 Sep 2009 20:21:52 -0300 Subject: V4L/DVB (12962): ce6230: increase BULK transfer size from 512 to 8192 Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/ce6230.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media/dvb') diff --git a/drivers/media/dvb/dvb-usb/ce6230.c b/drivers/media/dvb/dvb-usb/ce6230.c index 52badc00e673..0737c6377892 100644 --- a/drivers/media/dvb/dvb-usb/ce6230.c +++ b/drivers/media/dvb/dvb-usb/ce6230.c @@ -274,7 +274,7 @@ static struct dvb_usb_device_properties ce6230_properties = { .endpoint = 0x82, .u = { .bulk = { - .buffersize = 512, + .buffersize = (16*512), } } }, -- cgit v1.2.2 From 98293ef3e54f9f2175f11b4d14c119a2ff753d61 Mon Sep 17 00:00:00 2001 From: HIRANO Takahito Date: Fri, 18 Sep 2009 11:17:54 -0300 Subject: V4L/DVB (12997): Add the DTV_ISDB_TS_ID property for ISDB_S In ISDB-S, time-devision duplex is used to multiplexing several waves in the same frequency. Each wave is identified by its own transport stream ID, or TS ID. We need to provide some way to specify this ID from user applications to handle ISDB-S frontends. This code has been tested with the Earthsoft PT1 driver. [mchehab@infradead.org: Fix merge conflicts with isdbt and rename the new parameter to DTV_ISDBS_TS_ID] Signed-off-by: HIRANO Takahito Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-core/dvb_frontend.c | 8 ++++++++ drivers/media/dvb/dvb-core/dvb_frontend.h | 3 +++ 2 files changed, 11 insertions(+) (limited to 'drivers/media/dvb') diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c index 3c9482660eac..ddf639ed2fd8 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -1031,6 +1031,8 @@ static struct dtv_cmds_h dtv_cmds[] = { _DTV_CMD(DTV_ISDBT_LAYERC_SEGMENT_COUNT, 0, 0), _DTV_CMD(DTV_ISDBT_LAYERC_TIME_INTERLEAVING, 0, 0), + _DTV_CMD(DTV_ISDBS_TS_ID, 1, 0), + /* Get */ [DTV_DISEQC_SLAVE_REPLY] = { .name = "DTV_DISEQC_SLAVE_REPLY", @@ -1420,6 +1422,9 @@ static int dtv_property_process_get(struct dvb_frontend *fe, case DTV_ISDBT_LAYERC_TIME_INTERLEAVING: tvp->u.data = fe->dtv_property_cache.layer[2].interleaving; break; + case DTV_ISDBS_TS_ID: + tvp->u.data = fe->dtv_property_cache.isdbs_ts_id; + break; default: r = -1; } @@ -1571,6 +1576,9 @@ static int dtv_property_process_set(struct dvb_frontend *fe, case DTV_ISDBT_LAYERC_TIME_INTERLEAVING: fe->dtv_property_cache.layer[2].interleaving = tvp->u.data; break; + case DTV_ISDBS_TS_ID: + fe->dtv_property_cache.isdbs_ts_id = tvp->u.data; + break; default: r = -1; } diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.h b/drivers/media/dvb/dvb-core/dvb_frontend.h index 9e46f1772c54..810f07d63246 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.h +++ b/drivers/media/dvb/dvb-core/dvb_frontend.h @@ -355,6 +355,9 @@ struct dtv_frontend_properties { fe_modulation_t modulation; u8 interleaving; } layer[3]; + + /* ISDB-T specifics */ + u32 isdbs_ts_id; }; struct dvb_frontend { -- cgit v1.2.2 From 3d17fb1be937c8c025fc9f54b4e17e91081e7a4f Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 23 Aug 2009 00:51:22 -0300 Subject: V4L/DVB (12999): Add a driver for Earthsoft PT1 Add a driver for Earthsoft PT1 Eearthsoft PT1 is a PCI card for Japanese broadcasting with two ISDB-S and ISDB-T demodulators. This card has neither MPEG decoder nor conditional access module onboard. It transmits only compressed and possibly encrypted MPEG data over the PCI bus, so you need an external software decoder and a decrypter to watch TV on your computer. This driver is originally developed by Tomoaki Ishikawa by reverse engineering. [mchehab@redhat.com: renamed isdb_ts to isdbs_ts to use the current standard] Signed-off-by: HIRANO Takahito Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/Kconfig | 4 + drivers/media/dvb/Makefile | 2 +- drivers/media/dvb/pt1/Kconfig | 12 + drivers/media/dvb/pt1/Makefile | 5 + drivers/media/dvb/pt1/pt1.c | 1056 ++++++++++++++++++++++++++++++++++ drivers/media/dvb/pt1/va1j5jf8007s.c | 658 +++++++++++++++++++++ drivers/media/dvb/pt1/va1j5jf8007s.h | 40 ++ drivers/media/dvb/pt1/va1j5jf8007t.c | 468 +++++++++++++++ drivers/media/dvb/pt1/va1j5jf8007t.h | 40 ++ 9 files changed, 2284 insertions(+), 1 deletion(-) create mode 100644 drivers/media/dvb/pt1/Kconfig create mode 100644 drivers/media/dvb/pt1/Makefile create mode 100644 drivers/media/dvb/pt1/pt1.c create mode 100644 drivers/media/dvb/pt1/va1j5jf8007s.c create mode 100644 drivers/media/dvb/pt1/va1j5jf8007s.h create mode 100644 drivers/media/dvb/pt1/va1j5jf8007t.c create mode 100644 drivers/media/dvb/pt1/va1j5jf8007t.h (limited to 'drivers/media/dvb') diff --git a/drivers/media/dvb/Kconfig b/drivers/media/dvb/Kconfig index 1d0e4b1ef10c..35d0817126e9 100644 --- a/drivers/media/dvb/Kconfig +++ b/drivers/media/dvb/Kconfig @@ -68,6 +68,10 @@ comment "Supported FireWire (IEEE 1394) Adapters" depends on DVB_CORE && IEEE1394 source "drivers/media/dvb/firewire/Kconfig" +comment "Supported Earthsoft PT1 Adapters" + depends on DVB_CORE && PCI && I2C +source "drivers/media/dvb/pt1/Kconfig" + comment "Supported DVB Frontends" depends on DVB_CORE source "drivers/media/dvb/frontends/Kconfig" diff --git a/drivers/media/dvb/Makefile b/drivers/media/dvb/Makefile index 6092a5bb5a7d..16d262ddb45d 100644 --- a/drivers/media/dvb/Makefile +++ b/drivers/media/dvb/Makefile @@ -2,6 +2,6 @@ # Makefile for the kernel multimedia device drivers. # -obj-y := dvb-core/ frontends/ ttpci/ ttusb-dec/ ttusb-budget/ b2c2/ bt8xx/ dvb-usb/ pluto2/ siano/ dm1105/ +obj-y := dvb-core/ frontends/ ttpci/ ttusb-dec/ ttusb-budget/ b2c2/ bt8xx/ dvb-usb/ pluto2/ siano/ dm1105/ pt1/ obj-$(CONFIG_DVB_FIREDTV) += firewire/ diff --git a/drivers/media/dvb/pt1/Kconfig b/drivers/media/dvb/pt1/Kconfig new file mode 100644 index 000000000000..24501d5bf70d --- /dev/null +++ b/drivers/media/dvb/pt1/Kconfig @@ -0,0 +1,12 @@ +config DVB_PT1 + tristate "PT1 cards" + depends on DVB_CORE && PCI && I2C + help + Support for Earthsoft PT1 PCI cards. + + 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/drivers/media/dvb/pt1/Makefile b/drivers/media/dvb/pt1/Makefile new file mode 100644 index 000000000000..a66da17bbe31 --- /dev/null +++ b/drivers/media/dvb/pt1/Makefile @@ -0,0 +1,5 @@ +earth-pt1-objs := pt1.o va1j5jf8007s.o va1j5jf8007t.o + +obj-$(CONFIG_DVB_PT1) += earth-pt1.o + +EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core -Idrivers/media/dvb/frontends diff --git a/drivers/media/dvb/pt1/pt1.c b/drivers/media/dvb/pt1/pt1.c new file mode 100644 index 000000000000..ef0f7d235e2a --- /dev/null +++ b/drivers/media/dvb/pt1/pt1.c @@ -0,0 +1,1056 @@ +/* + * driver for Earthsoft PT1 + * + * Copyright (C) 2009 HIRANO Takahito + * + * based on pt1dvr - http://pt1dvr.sourceforge.jp/ + * by Tomoaki Ishikawa + * + * 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 +#include +#include +#include +#include + +#include "dvbdev.h" +#include "dvb_demux.h" +#include "dmxdev.h" +#include "dvb_net.h" +#include "dvb_frontend.h" + +#include "va1j5jf8007t.h" +#include "va1j5jf8007s.h" + +#define DRIVER_NAME "earth-pt1" + +#define PT1_PAGE_SHIFT 12 +#define PT1_PAGE_SIZE (1 << PT1_PAGE_SHIFT) +#define PT1_NR_UPACKETS 1024 +#define PT1_NR_BUFS 511 + +struct pt1_buffer_page { + __le32 upackets[PT1_NR_UPACKETS]; +}; + +struct pt1_table_page { + __le32 next_pfn; + __le32 buf_pfns[PT1_NR_BUFS]; +}; + +struct pt1_buffer { + struct pt1_buffer_page *page; + dma_addr_t addr; +}; + +struct pt1_table { + struct pt1_table_page *page; + dma_addr_t addr; + struct pt1_buffer bufs[PT1_NR_BUFS]; +}; + +#define PT1_NR_ADAPS 4 + +struct pt1_adapter; + +struct pt1 { + struct pci_dev *pdev; + void __iomem *regs; + struct i2c_adapter i2c_adap; + int i2c_running; + struct pt1_adapter *adaps[PT1_NR_ADAPS]; + struct pt1_table *tables; + struct task_struct *kthread; +}; + +struct pt1_adapter { + struct pt1 *pt1; + int index; + + u8 *buf; + int upacket_count; + int packet_count; + + struct dvb_adapter adap; + struct dvb_demux demux; + int users; + struct dmxdev dmxdev; + struct dvb_net net; + struct dvb_frontend *fe; + int (*orig_set_voltage)(struct dvb_frontend *fe, + fe_sec_voltage_t voltage); +}; + +#define pt1_printk(level, pt1, format, arg...) \ + dev_printk(level, &(pt1)->pdev->dev, format, ##arg) + +static void pt1_write_reg(struct pt1 *pt1, int reg, u32 data) +{ + writel(data, pt1->regs + reg * 4); +} + +static u32 pt1_read_reg(struct pt1 *pt1, int reg) +{ + return readl(pt1->regs + reg * 4); +} + +static int pt1_nr_tables = 64; +module_param_named(nr_tables, pt1_nr_tables, int, 0); + +static void pt1_increment_table_count(struct pt1 *pt1) +{ + pt1_write_reg(pt1, 0, 0x00000020); +} + +static void pt1_init_table_count(struct pt1 *pt1) +{ + pt1_write_reg(pt1, 0, 0x00000010); +} + +static void pt1_register_tables(struct pt1 *pt1, u32 first_pfn) +{ + pt1_write_reg(pt1, 5, first_pfn); + pt1_write_reg(pt1, 0, 0x0c000040); +} + +static void pt1_unregister_tables(struct pt1 *pt1) +{ + pt1_write_reg(pt1, 0, 0x08080000); +} + +static int pt1_sync(struct pt1 *pt1) +{ + int i; + for (i = 0; i < 57; i++) { + if (pt1_read_reg(pt1, 0) & 0x20000000) + return 0; + pt1_write_reg(pt1, 0, 0x00000008); + } + pt1_printk(KERN_ERR, pt1, "could not sync\n"); + return -EIO; +} + +static u64 pt1_identify(struct pt1 *pt1) +{ + int i; + u64 id; + id = 0; + for (i = 0; i < 57; i++) { + id |= (u64)(pt1_read_reg(pt1, 0) >> 30 & 1) << i; + pt1_write_reg(pt1, 0, 0x00000008); + } + return id; +} + +static int pt1_unlock(struct pt1 *pt1) +{ + int i; + pt1_write_reg(pt1, 0, 0x00000008); + for (i = 0; i < 3; i++) { + if (pt1_read_reg(pt1, 0) & 0x80000000) + return 0; + schedule_timeout_uninterruptible((HZ + 999) / 1000); + } + pt1_printk(KERN_ERR, pt1, "could not unlock\n"); + return -EIO; +} + +static int pt1_reset_pci(struct pt1 *pt1) +{ + int i; + pt1_write_reg(pt1, 0, 0x01010000); + pt1_write_reg(pt1, 0, 0x01000000); + for (i = 0; i < 10; i++) { + if (pt1_read_reg(pt1, 0) & 0x00000001) + return 0; + schedule_timeout_uninterruptible((HZ + 999) / 1000); + } + pt1_printk(KERN_ERR, pt1, "could not reset PCI\n"); + return -EIO; +} + +static int pt1_reset_ram(struct pt1 *pt1) +{ + int i; + pt1_write_reg(pt1, 0, 0x02020000); + pt1_write_reg(pt1, 0, 0x02000000); + for (i = 0; i < 10; i++) { + if (pt1_read_reg(pt1, 0) & 0x00000002) + return 0; + schedule_timeout_uninterruptible((HZ + 999) / 1000); + } + pt1_printk(KERN_ERR, pt1, "could not reset RAM\n"); + return -EIO; +} + +static int pt1_do_enable_ram(struct pt1 *pt1) +{ + int i, j; + u32 status; + status = pt1_read_reg(pt1, 0) & 0x00000004; + pt1_write_reg(pt1, 0, 0x00000002); + for (i = 0; i < 10; i++) { + for (j = 0; j < 1024; j++) { + if ((pt1_read_reg(pt1, 0) & 0x00000004) != status) + return 0; + } + schedule_timeout_uninterruptible((HZ + 999) / 1000); + } + pt1_printk(KERN_ERR, pt1, "could not enable RAM\n"); + return -EIO; +} + +static int pt1_enable_ram(struct pt1 *pt1) +{ + int i, ret; + schedule_timeout_uninterruptible((HZ + 999) / 1000); + for (i = 0; i < 10; i++) { + ret = pt1_do_enable_ram(pt1); + if (ret < 0) + return ret; + } + return 0; +} + +static void pt1_disable_ram(struct pt1 *pt1) +{ + pt1_write_reg(pt1, 0, 0x0b0b0000); +} + +static void pt1_set_stream(struct pt1 *pt1, int index, int enabled) +{ + pt1_write_reg(pt1, 2, 1 << (index + 8) | enabled << index); +} + +static void pt1_init_streams(struct pt1 *pt1) +{ + int i; + for (i = 0; i < PT1_NR_ADAPS; i++) + pt1_set_stream(pt1, i, 0); +} + +static int pt1_filter(struct pt1 *pt1, struct pt1_buffer_page *page) +{ + u32 upacket; + int i; + int index; + struct pt1_adapter *adap; + int offset; + u8 *buf; + + if (!page->upackets[PT1_NR_UPACKETS - 1]) + return 0; + + for (i = 0; i < PT1_NR_UPACKETS; i++) { + upacket = le32_to_cpu(page->upackets[i]); + index = (upacket >> 29) - 1; + if (index < 0 || index >= PT1_NR_ADAPS) + continue; + + adap = pt1->adaps[index]; + if (upacket >> 25 & 1) + adap->upacket_count = 0; + else if (!adap->upacket_count) + continue; + + buf = adap->buf; + offset = adap->packet_count * 188 + adap->upacket_count * 3; + buf[offset] = upacket >> 16; + buf[offset + 1] = upacket >> 8; + if (adap->upacket_count != 62) + buf[offset + 2] = upacket; + + if (++adap->upacket_count >= 63) { + adap->upacket_count = 0; + if (++adap->packet_count >= 21) { + dvb_dmx_swfilter_packets(&adap->demux, buf, 21); + adap->packet_count = 0; + } + } + } + + page->upackets[PT1_NR_UPACKETS - 1] = 0; + return 1; +} + +static int pt1_thread(void *data) +{ + struct pt1 *pt1; + int table_index; + int buf_index; + struct pt1_buffer_page *page; + + pt1 = data; + set_freezable(); + + table_index = 0; + buf_index = 0; + + while (!kthread_should_stop()) { + try_to_freeze(); + + page = pt1->tables[table_index].bufs[buf_index].page; + if (!pt1_filter(pt1, page)) { + schedule_timeout_interruptible((HZ + 999) / 1000); + continue; + } + + if (++buf_index >= PT1_NR_BUFS) { + pt1_increment_table_count(pt1); + buf_index = 0; + if (++table_index >= pt1_nr_tables) + table_index = 0; + } + } + + return 0; +} + +static void pt1_free_page(struct pt1 *pt1, void *page, dma_addr_t addr) +{ + dma_free_coherent(&pt1->pdev->dev, PT1_PAGE_SIZE, page, addr); +} + +static void *pt1_alloc_page(struct pt1 *pt1, dma_addr_t *addrp, u32 *pfnp) +{ + void *page; + dma_addr_t addr; + + page = dma_alloc_coherent(&pt1->pdev->dev, PT1_PAGE_SIZE, &addr, + GFP_KERNEL); + if (page == NULL) + return NULL; + + BUG_ON(addr & (PT1_PAGE_SIZE - 1)); + BUG_ON(addr >> PT1_PAGE_SHIFT >> 31 >> 1); + + *addrp = addr; + *pfnp = addr >> PT1_PAGE_SHIFT; + return page; +} + +static void pt1_cleanup_buffer(struct pt1 *pt1, struct pt1_buffer *buf) +{ + pt1_free_page(pt1, buf->page, buf->addr); +} + +static int +pt1_init_buffer(struct pt1 *pt1, struct pt1_buffer *buf, u32 *pfnp) +{ + struct pt1_buffer_page *page; + dma_addr_t addr; + + page = pt1_alloc_page(pt1, &addr, pfnp); + if (page == NULL) + return -ENOMEM; + + page->upackets[PT1_NR_UPACKETS - 1] = 0; + + buf->page = page; + buf->addr = addr; + return 0; +} + +static void pt1_cleanup_table(struct pt1 *pt1, struct pt1_table *table) +{ + int i; + + for (i = 0; i < PT1_NR_BUFS; i++) + pt1_cleanup_buffer(pt1, &table->bufs[i]); + + pt1_free_page(pt1, table->page, table->addr); +} + +static int +pt1_init_table(struct pt1 *pt1, struct pt1_table *table, u32 *pfnp) +{ + struct pt1_table_page *page; + dma_addr_t addr; + int i, ret; + u32 buf_pfn; + + page = pt1_alloc_page(pt1, &addr, pfnp); + if (page == NULL) + return -ENOMEM; + + for (i = 0; i < PT1_NR_BUFS; i++) { + ret = pt1_init_buffer(pt1, &table->bufs[i], &buf_pfn); + if (ret < 0) + goto err; + + page->buf_pfns[i] = cpu_to_le32(buf_pfn); + } + + pt1_increment_table_count(pt1); + table->page = page; + table->addr = addr; + return 0; + +err: + while (i--) + pt1_cleanup_buffer(pt1, &table->bufs[i]); + + pt1_free_page(pt1, page, addr); + return ret; +} + +static void pt1_cleanup_tables(struct pt1 *pt1) +{ + struct pt1_table *tables; + int i; + + tables = pt1->tables; + pt1_unregister_tables(pt1); + + for (i = 0; i < pt1_nr_tables; i++) + pt1_cleanup_table(pt1, &tables[i]); + + vfree(tables); +} + +static int pt1_init_tables(struct pt1 *pt1) +{ + struct pt1_table *tables; + int i, ret; + u32 first_pfn, pfn; + + tables = vmalloc(sizeof(struct pt1_table) * pt1_nr_tables); + if (tables == NULL) + return -ENOMEM; + + pt1_init_table_count(pt1); + + i = 0; + if (pt1_nr_tables) { + ret = pt1_init_table(pt1, &tables[0], &first_pfn); + if (ret) + goto err; + i++; + } + + while (i < pt1_nr_tables) { + ret = pt1_init_table(pt1, &tables[i], &pfn); + if (ret) + goto err; + tables[i - 1].page->next_pfn = cpu_to_le32(pfn); + i++; + } + + tables[pt1_nr_tables - 1].page->next_pfn = cpu_to_le32(first_pfn); + + pt1_register_tables(pt1, first_pfn); + pt1->tables = tables; + return 0; + +err: + while (i--) + pt1_cleanup_table(pt1, &tables[i]); + + vfree(tables); + return ret; +} + +static int pt1_start_feed(struct dvb_demux_feed *feed) +{ + struct pt1_adapter *adap; + adap = container_of(feed->demux, struct pt1_adapter, demux); + if (!adap->users++) + pt1_set_stream(adap->pt1, adap->index, 1); + return 0; +} + +static int pt1_stop_feed(struct dvb_demux_feed *feed) +{ + struct pt1_adapter *adap; + adap = container_of(feed->demux, struct pt1_adapter, demux); + if (!--adap->users) + pt1_set_stream(adap->pt1, adap->index, 0); + return 0; +} + +static void +pt1_set_power(struct pt1 *pt1, int power, int lnb, int reset) +{ + pt1_write_reg(pt1, 1, power | lnb << 1 | !reset << 3); +} + +static int pt1_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +{ + struct pt1_adapter *adap; + int lnb; + + adap = container_of(fe->dvb, struct pt1_adapter, adap); + + switch (voltage) { + case SEC_VOLTAGE_13: /* actually 11V */ + lnb = 2; + break; + case SEC_VOLTAGE_18: /* actually 15V */ + lnb = 3; + break; + case SEC_VOLTAGE_OFF: + lnb = 0; + break; + default: + return -EINVAL; + } + + pt1_set_power(adap->pt1, 1, lnb, 0); + + if (adap->orig_set_voltage) + return adap->orig_set_voltage(fe, voltage); + else + return 0; +} + +static void pt1_free_adapter(struct pt1_adapter *adap) +{ + dvb_unregister_frontend(adap->fe); + dvb_net_release(&adap->net); + adap->demux.dmx.close(&adap->demux.dmx); + dvb_dmxdev_release(&adap->dmxdev); + dvb_dmx_release(&adap->demux); + dvb_unregister_adapter(&adap->adap); + free_page((unsigned long)adap->buf); + kfree(adap); +} + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +static struct pt1_adapter * +pt1_alloc_adapter(struct pt1 *pt1, struct dvb_frontend *fe) +{ + struct pt1_adapter *adap; + void *buf; + struct dvb_adapter *dvb_adap; + struct dvb_demux *demux; + struct dmxdev *dmxdev; + int ret; + + adap = kzalloc(sizeof(struct pt1_adapter), GFP_KERNEL); + if (!adap) { + ret = -ENOMEM; + goto err; + } + + adap->pt1 = pt1; + + adap->orig_set_voltage = fe->ops.set_voltage; + fe->ops.set_voltage = pt1_set_voltage; + + buf = (u8 *)__get_free_page(GFP_KERNEL); + if (!buf) { + ret = -ENOMEM; + goto err_kfree; + } + + adap->buf = buf; + adap->upacket_count = 0; + adap->packet_count = 0; + + dvb_adap = &adap->adap; + dvb_adap->priv = adap; + ret = dvb_register_adapter(dvb_adap, DRIVER_NAME, THIS_MODULE, + &pt1->pdev->dev, adapter_nr); + if (ret < 0) + goto err_free_page; + + demux = &adap->demux; + demux->dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING; + demux->priv = adap; + demux->feednum = 256; + demux->filternum = 256; + demux->start_feed = pt1_start_feed; + demux->stop_feed = pt1_stop_feed; + demux->write_to_decoder = NULL; + ret = dvb_dmx_init(demux); + if (ret < 0) + goto err_unregister_adapter; + + dmxdev = &adap->dmxdev; + dmxdev->filternum = 256; + dmxdev->demux = &demux->dmx; + dmxdev->capabilities = 0; + ret = dvb_dmxdev_init(dmxdev, dvb_adap); + if (ret < 0) + goto err_dmx_release; + + dvb_net_init(dvb_adap, &adap->net, &demux->dmx); + + ret = dvb_register_frontend(dvb_adap, fe); + if (ret < 0) + goto err_net_release; + adap->fe = fe; + + return adap; + +err_net_release: + dvb_net_release(&adap->net); + adap->demux.dmx.close(&adap->demux.dmx); + dvb_dmxdev_release(&adap->dmxdev); +err_dmx_release: + dvb_dmx_release(demux); +err_unregister_adapter: + dvb_unregister_adapter(dvb_adap); +err_free_page: + free_page((unsigned long)buf); +err_kfree: + kfree(adap); +err: + return ERR_PTR(ret); +} + +static void pt1_cleanup_adapters(struct pt1 *pt1) +{ + int i; + for (i = 0; i < PT1_NR_ADAPS; i++) + pt1_free_adapter(pt1->adaps[i]); +} + +struct pt1_config { + struct va1j5jf8007s_config va1j5jf8007s_config; + struct va1j5jf8007t_config va1j5jf8007t_config; +}; + +static const struct pt1_config pt1_configs[2] = { + { + { .demod_address = 0x1b }, + { .demod_address = 0x1a }, + }, { + { .demod_address = 0x19 }, + { .demod_address = 0x18 }, + }, +}; + +static int pt1_init_adapters(struct pt1 *pt1) +{ + int i, j; + struct i2c_adapter *i2c_adap; + const struct pt1_config *config; + struct dvb_frontend *fe[4]; + struct pt1_adapter *adap; + int ret; + + i = 0; + j = 0; + + i2c_adap = &pt1->i2c_adap; + do { + config = &pt1_configs[i / 2]; + + fe[i] = va1j5jf8007s_attach(&config->va1j5jf8007s_config, + i2c_adap); + if (!fe[i]) { + ret = -ENODEV; /* This does not sound nice... */ + goto err; + } + i++; + + fe[i] = va1j5jf8007t_attach(&config->va1j5jf8007t_config, + i2c_adap); + if (!fe[i]) { + ret = -ENODEV; + goto err; + } + i++; + + ret = va1j5jf8007s_prepare(fe[i - 2]); + if (ret < 0) + goto err; + + ret = va1j5jf8007t_prepare(fe[i - 1]); + if (ret < 0) + goto err; + + } while (i < 4); + + do { + adap = pt1_alloc_adapter(pt1, fe[j]); + if (IS_ERR(adap)) + goto err; + adap->index = j; + pt1->adaps[j] = adap; + } while (++j < 4); + + return 0; + +err: + while (i-- > j) + fe[i]->ops.release(fe[i]); + + while (j--) + pt1_free_adapter(pt1->adaps[j]); + + return ret; +} + +static void pt1_i2c_emit(struct pt1 *pt1, int addr, int busy, int read_enable, + int clock, int data, int next_addr) +{ + pt1_write_reg(pt1, 4, addr << 18 | busy << 13 | read_enable << 12 | + !clock << 11 | !data << 10 | next_addr); +} + +static void pt1_i2c_write_bit(struct pt1 *pt1, int addr, int *addrp, int data) +{ + pt1_i2c_emit(pt1, addr, 1, 0, 0, data, addr + 1); + pt1_i2c_emit(pt1, addr + 1, 1, 0, 1, data, addr + 2); + pt1_i2c_emit(pt1, addr + 2, 1, 0, 0, data, addr + 3); + *addrp = addr + 3; +} + +static void pt1_i2c_read_bit(struct pt1 *pt1, int addr, int *addrp) +{ + pt1_i2c_emit(pt1, addr, 1, 0, 0, 1, addr + 1); + pt1_i2c_emit(pt1, addr + 1, 1, 0, 1, 1, addr + 2); + pt1_i2c_emit(pt1, addr + 2, 1, 1, 1, 1, addr + 3); + pt1_i2c_emit(pt1, addr + 3, 1, 0, 0, 1, addr + 4); + *addrp = addr + 4; +} + +static void pt1_i2c_write_byte(struct pt1 *pt1, int addr, int *addrp, int data) +{ + int i; + for (i = 0; i < 8; i++) + pt1_i2c_write_bit(pt1, addr, &addr, data >> (7 - i) & 1); + pt1_i2c_write_bit(pt1, addr, &addr, 1); + *addrp = addr; +} + +static void pt1_i2c_read_byte(struct pt1 *pt1, int addr, int *addrp, int last) +{ + int i; + for (i = 0; i < 8; i++) + pt1_i2c_read_bit(pt1, addr, &addr); + pt1_i2c_write_bit(pt1, addr, &addr, last); + *addrp = addr; +} + +static void pt1_i2c_prepare(struct pt1 *pt1, int addr, int *addrp) +{ + pt1_i2c_emit(pt1, addr, 1, 0, 1, 1, addr + 1); + pt1_i2c_emit(pt1, addr + 1, 1, 0, 1, 0, addr + 2); + pt1_i2c_emit(pt1, addr + 2, 1, 0, 0, 0, addr + 3); + *addrp = addr + 3; +} + +static void +pt1_i2c_write_msg(struct pt1 *pt1, int addr, int *addrp, struct i2c_msg *msg) +{ + int i; + pt1_i2c_prepare(pt1, addr, &addr); + pt1_i2c_write_byte(pt1, addr, &addr, msg->addr << 1); + for (i = 0; i < msg->len; i++) + pt1_i2c_write_byte(pt1, addr, &addr, msg->buf[i]); + *addrp = addr; +} + +static void +pt1_i2c_read_msg(struct pt1 *pt1, int addr, int *addrp, struct i2c_msg *msg) +{ + int i; + pt1_i2c_prepare(pt1, addr, &addr); + pt1_i2c_write_byte(pt1, addr, &addr, msg->addr << 1 | 1); + for (i = 0; i < msg->len; i++) + pt1_i2c_read_byte(pt1, addr, &addr, i == msg->len - 1); + *addrp = addr; +} + +static int pt1_i2c_end(struct pt1 *pt1, int addr) +{ + pt1_i2c_emit(pt1, addr, 1, 0, 0, 0, addr + 1); + pt1_i2c_emit(pt1, addr + 1, 1, 0, 1, 0, addr + 2); + pt1_i2c_emit(pt1, addr + 2, 1, 0, 1, 1, 0); + + pt1_write_reg(pt1, 0, 0x00000004); + do { + if (signal_pending(current)) + return -EINTR; + schedule_timeout_interruptible((HZ + 999) / 1000); + } while (pt1_read_reg(pt1, 0) & 0x00000080); + return 0; +} + +static void pt1_i2c_begin(struct pt1 *pt1, int *addrp) +{ + int addr; + addr = 0; + + pt1_i2c_emit(pt1, addr, 0, 0, 1, 1, addr /* itself */); + addr = addr + 1; + + if (!pt1->i2c_running) { + pt1_i2c_emit(pt1, addr, 1, 0, 1, 1, addr + 1); + pt1_i2c_emit(pt1, addr + 1, 1, 0, 1, 0, addr + 2); + addr = addr + 2; + pt1->i2c_running = 1; + } + *addrp = addr; +} + +static int pt1_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +{ + struct pt1 *pt1; + int i; + struct i2c_msg *msg, *next_msg; + int addr, ret; + u16 len; + u32 word; + + pt1 = i2c_get_adapdata(adap); + + for (i = 0; i < num; i++) { + msg = &msgs[i]; + if (msg->flags & I2C_M_RD) + return -ENOTSUPP; + + if (i + 1 < num) + next_msg = &msgs[i + 1]; + else + next_msg = NULL; + + if (next_msg && next_msg->flags & I2C_M_RD) { + i++; + + len = next_msg->len; + if (len > 4) + return -ENOTSUPP; + + pt1_i2c_begin(pt1, &addr); + pt1_i2c_write_msg(pt1, addr, &addr, msg); + pt1_i2c_read_msg(pt1, addr, &addr, next_msg); + ret = pt1_i2c_end(pt1, addr); + if (ret < 0) + return ret; + + word = pt1_read_reg(pt1, 2); + while (len--) { + next_msg->buf[len] = word; + word >>= 8; + } + } else { + pt1_i2c_begin(pt1, &addr); + pt1_i2c_write_msg(pt1, addr, &addr, msg); + ret = pt1_i2c_end(pt1, addr); + if (ret < 0) + return ret; + } + } + + return num; +} + +static u32 pt1_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C; +} + +static const struct i2c_algorithm pt1_i2c_algo = { + .master_xfer = pt1_i2c_xfer, + .functionality = pt1_i2c_func, +}; + +static void pt1_i2c_wait(struct pt1 *pt1) +{ + int i; + for (i = 0; i < 128; i++) + pt1_i2c_emit(pt1, 0, 0, 0, 1, 1, 0); +} + +static void pt1_i2c_init(struct pt1 *pt1) +{ + int i; + for (i = 0; i < 1024; i++) + pt1_i2c_emit(pt1, i, 0, 0, 1, 1, 0); +} + +static void __devexit pt1_remove(struct pci_dev *pdev) +{ + struct pt1 *pt1; + void __iomem *regs; + + pt1 = pci_get_drvdata(pdev); + regs = pt1->regs; + + kthread_stop(pt1->kthread); + pt1_cleanup_tables(pt1); + pt1_cleanup_adapters(pt1); + pt1_disable_ram(pt1); + pt1_set_power(pt1, 0, 0, 1); + i2c_del_adapter(&pt1->i2c_adap); + pci_set_drvdata(pdev, NULL); + kfree(pt1); + pci_iounmap(pdev, regs); + pci_release_regions(pdev); + pci_disable_device(pdev); +} + +static int __devinit +pt1_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + int ret; + void __iomem *regs; + struct pt1 *pt1; + struct i2c_adapter *i2c_adap; + struct task_struct *kthread; + + ret = pci_enable_device(pdev); + if (ret < 0) + goto err; + + 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; + + regs = pci_iomap(pdev, 0, 0); + if (!regs) { + ret = -EIO; + goto err_pci_release_regions; + } + + pt1 = kzalloc(sizeof(struct pt1), GFP_KERNEL); + if (!pt1) { + ret = -ENOMEM; + goto err_pci_iounmap; + } + + pt1->pdev = pdev; + pt1->regs = regs; + pci_set_drvdata(pdev, pt1); + + i2c_adap = &pt1->i2c_adap; + i2c_adap->class = I2C_CLASS_TV_DIGITAL; + i2c_adap->algo = &pt1_i2c_algo; + i2c_adap->algo_data = NULL; + i2c_adap->dev.parent = &pdev->dev; + i2c_set_adapdata(i2c_adap, pt1); + ret = i2c_add_adapter(i2c_adap); + if (ret < 0) + goto err_kfree; + + pt1_set_power(pt1, 0, 0, 1); + + pt1_i2c_init(pt1); + pt1_i2c_wait(pt1); + + ret = pt1_sync(pt1); + if (ret < 0) + goto err_i2c_del_adapter; + + pt1_identify(pt1); + + ret = pt1_unlock(pt1); + if (ret < 0) + goto err_i2c_del_adapter; + + ret = pt1_reset_pci(pt1); + if (ret < 0) + goto err_i2c_del_adapter; + + ret = pt1_reset_ram(pt1); + if (ret < 0) + goto err_i2c_del_adapter; + + ret = pt1_enable_ram(pt1); + if (ret < 0) + goto err_i2c_del_adapter; + + pt1_init_streams(pt1); + + pt1_set_power(pt1, 1, 0, 1); + schedule_timeout_uninterruptible((HZ + 49) / 50); + + pt1_set_power(pt1, 1, 0, 0); + schedule_timeout_uninterruptible((HZ + 999) / 1000); + + ret = pt1_init_adapters(pt1); + if (ret < 0) + goto err_pt1_disable_ram; + + ret = pt1_init_tables(pt1); + if (ret < 0) + goto err_pt1_cleanup_adapters; + + kthread = kthread_run(pt1_thread, pt1, "pt1"); + if (IS_ERR(kthread)) { + ret = PTR_ERR(kthread); + goto err_pt1_cleanup_tables; + } + + pt1->kthread = kthread; + return 0; + +err_pt1_cleanup_tables: + pt1_cleanup_tables(pt1); +err_pt1_cleanup_adapters: + pt1_cleanup_adapters(pt1); +err_pt1_disable_ram: + pt1_disable_ram(pt1); + pt1_set_power(pt1, 0, 0, 1); +err_i2c_del_adapter: + i2c_del_adapter(i2c_adap); +err_kfree: + pci_set_drvdata(pdev, NULL); + kfree(pt1); +err_pci_iounmap: + pci_iounmap(pdev, regs); +err_pci_release_regions: + pci_release_regions(pdev); +err_pci_disable_device: + pci_disable_device(pdev); +err: + return ret; + +} + +static struct pci_device_id pt1_id_table[] = { + { PCI_DEVICE(0x10ee, 0x211a) }, + { }, +}; +MODULE_DEVICE_TABLE(pci, pt1_id_table); + +static struct pci_driver pt1_driver = { + .name = DRIVER_NAME, + .probe = pt1_probe, + .remove = __devexit_p(pt1_remove), + .id_table = pt1_id_table, +}; + + +static int __init pt1_init(void) +{ + return pci_register_driver(&pt1_driver); +} + + +static void __exit pt1_cleanup(void) +{ + pci_unregister_driver(&pt1_driver); +} + +module_init(pt1_init); +module_exit(pt1_cleanup); + +MODULE_AUTHOR("Takahito HIRANO "); +MODULE_DESCRIPTION("Earthsoft PT1 Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/pt1/va1j5jf8007s.c b/drivers/media/dvb/pt1/va1j5jf8007s.c new file mode 100644 index 000000000000..2db940f8635f --- /dev/null +++ b/drivers/media/dvb/pt1/va1j5jf8007s.c @@ -0,0 +1,658 @@ +/* + * ISDB-S driver for VA1J5JF8007 + * + * Copyright (C) 2009 HIRANO Takahito + * + * based on pt1dvr - http://pt1dvr.sourceforge.jp/ + * by Tomoaki Ishikawa + * + * 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 +#include +#include +#include +#include "dvb_frontend.h" +#include "va1j5jf8007s.h" + +enum va1j5jf8007s_tune_state { + VA1J5JF8007S_IDLE, + VA1J5JF8007S_SET_FREQUENCY_1, + VA1J5JF8007S_SET_FREQUENCY_2, + VA1J5JF8007S_SET_FREQUENCY_3, + VA1J5JF8007S_CHECK_FREQUENCY, + VA1J5JF8007S_SET_MODULATION, + VA1J5JF8007S_CHECK_MODULATION, + VA1J5JF8007S_SET_TS_ID, + VA1J5JF8007S_CHECK_TS_ID, + VA1J5JF8007S_TRACK, +}; + +struct va1j5jf8007s_state { + const struct va1j5jf8007s_config *config; + struct i2c_adapter *adap; + struct dvb_frontend fe; + enum va1j5jf8007s_tune_state tune_state; +}; + +static int va1j5jf8007s_get_frontend_algo(struct dvb_frontend *fe) +{ + return DVBFE_ALGO_HW; +} + +static int +va1j5jf8007s_read_status(struct dvb_frontend *fe, fe_status_t *status) +{ + struct va1j5jf8007s_state *state; + + state = fe->demodulator_priv; + + switch (state->tune_state) { + case VA1J5JF8007S_IDLE: + case VA1J5JF8007S_SET_FREQUENCY_1: + case VA1J5JF8007S_SET_FREQUENCY_2: + case VA1J5JF8007S_SET_FREQUENCY_3: + case VA1J5JF8007S_CHECK_FREQUENCY: + *status = 0; + return 0; + + + case VA1J5JF8007S_SET_MODULATION: + case VA1J5JF8007S_CHECK_MODULATION: + *status |= FE_HAS_SIGNAL; + return 0; + + case VA1J5JF8007S_SET_TS_ID: + case VA1J5JF8007S_CHECK_TS_ID: + *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER; + return 0; + + case VA1J5JF8007S_TRACK: + *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_LOCK; + return 0; + } + + BUG(); +} + +struct va1j5jf8007s_cb_map { + u32 frequency; + u8 cb; +}; + +static const struct va1j5jf8007s_cb_map va1j5jf8007s_cb_maps[] = { + { 986000, 0xb2 }, + { 1072000, 0xd2 }, + { 1154000, 0xe2 }, + { 1291000, 0x20 }, + { 1447000, 0x40 }, + { 1615000, 0x60 }, + { 1791000, 0x80 }, + { 1972000, 0xa0 }, +}; + +static u8 va1j5jf8007s_lookup_cb(u32 frequency) +{ + int i; + const struct va1j5jf8007s_cb_map *map; + + for (i = 0; i < ARRAY_SIZE(va1j5jf8007s_cb_maps); i++) { + map = &va1j5jf8007s_cb_maps[i]; + if (frequency < map->frequency) + return map->cb; + } + return 0xc0; +} + +static int va1j5jf8007s_set_frequency_1(struct va1j5jf8007s_state *state) +{ + u32 frequency; + u16 word; + u8 buf[6]; + struct i2c_msg msg; + + frequency = state->fe.dtv_property_cache.frequency; + + word = (frequency + 500) / 1000; + if (frequency < 1072000) + word = (word << 1 & ~0x1f) | (word & 0x0f); + + buf[0] = 0xfe; + buf[1] = 0xc0; + buf[2] = 0x40 | word >> 8; + buf[3] = word; + buf[4] = 0xe0; + buf[5] = va1j5jf8007s_lookup_cb(frequency); + + msg.addr = state->config->demod_address; + msg.flags = 0; + msg.len = sizeof(buf); + msg.buf = buf; + + if (i2c_transfer(state->adap, &msg, 1) != 1) + return -EREMOTEIO; + + return 0; +} + +static int va1j5jf8007s_set_frequency_2(struct va1j5jf8007s_state *state) +{ + u8 buf[3]; + struct i2c_msg msg; + + buf[0] = 0xfe; + buf[1] = 0xc0; + buf[2] = 0xe4; + + msg.addr = state->config->demod_address; + msg.flags = 0; + msg.len = sizeof(buf); + msg.buf = buf; + + if (i2c_transfer(state->adap, &msg, 1) != 1) + return -EREMOTEIO; + + return 0; +} + +static int va1j5jf8007s_set_frequency_3(struct va1j5jf8007s_state *state) +{ + u32 frequency; + u8 buf[4]; + struct i2c_msg msg; + + frequency = state->fe.dtv_property_cache.frequency; + + buf[0] = 0xfe; + buf[1] = 0xc0; + buf[2] = 0xf4; + buf[3] = va1j5jf8007s_lookup_cb(frequency) | 0x4; + + msg.addr = state->config->demod_address; + msg.flags = 0; + msg.len = sizeof(buf); + msg.buf = buf; + + if (i2c_transfer(state->adap, &msg, 1) != 1) + return -EREMOTEIO; + + return 0; +} + +static int +va1j5jf8007s_check_frequency(struct va1j5jf8007s_state *state, int *lock) +{ + u8 addr; + u8 write_buf[2], read_buf[1]; + struct i2c_msg msgs[2]; + + addr = state->config->demod_address; + + write_buf[0] = 0xfe; + write_buf[1] = 0xc1; + + msgs[0].addr = addr; + msgs[0].flags = 0; + msgs[0].len = sizeof(write_buf); + msgs[0].buf = write_buf; + + msgs[1].addr = addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = sizeof(read_buf); + msgs[1].buf = read_buf; + + if (i2c_transfer(state->adap, msgs, 2) != 2) + return -EREMOTEIO; + + *lock = read_buf[0] & 0x40; + return 0; +} + +static int va1j5jf8007s_set_modulation(struct va1j5jf8007s_state *state) +{ + u8 buf[2]; + struct i2c_msg msg; + + buf[0] = 0x03; + buf[1] = 0x01; + + msg.addr = state->config->demod_address; + msg.flags = 0; + msg.len = sizeof(buf); + msg.buf = buf; + + if (i2c_transfer(state->adap, &msg, 1) != 1) + return -EREMOTEIO; + + return 0; +} + +static int +va1j5jf8007s_check_modulation(struct va1j5jf8007s_state *state, int *lock) +{ + u8 addr; + u8 write_buf[1], read_buf[1]; + struct i2c_msg msgs[2]; + + addr = state->config->demod_address; + + write_buf[0] = 0xc3; + + msgs[0].addr = addr; + msgs[0].flags = 0; + msgs[0].len = sizeof(write_buf); + msgs[0].buf = write_buf; + + msgs[1].addr = addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = sizeof(read_buf); + msgs[1].buf = read_buf; + + if (i2c_transfer(state->adap, msgs, 2) != 2) + return -EREMOTEIO; + + *lock = !(read_buf[0] & 0x10); + return 0; +} + +static int +va1j5jf8007s_set_ts_id(struct va1j5jf8007s_state *state) +{ + u32 ts_id; + u8 buf[3]; + struct i2c_msg msg; + + ts_id = state->fe.dtv_property_cache.isdbs_ts_id; + if (!ts_id) + return 0; + + buf[0] = 0x8f; + buf[1] = ts_id >> 8; + buf[2] = ts_id; + + msg.addr = state->config->demod_address; + msg.flags = 0; + msg.len = sizeof(buf); + msg.buf = buf; + + if (i2c_transfer(state->adap, &msg, 1) != 1) + return -EREMOTEIO; + + return 0; +} + +static int +va1j5jf8007s_check_ts_id(struct va1j5jf8007s_state *state, int *lock) +{ + u8 addr; + u8 write_buf[1], read_buf[2]; + struct i2c_msg msgs[2]; + u32 ts_id; + + ts_id = state->fe.dtv_property_cache.isdbs_ts_id; + if (!ts_id) { + *lock = 1; + return 0; + } + + addr = state->config->demod_address; + + write_buf[0] = 0xe6; + + msgs[0].addr = addr; + msgs[0].flags = 0; + msgs[0].len = sizeof(write_buf); + msgs[0].buf = write_buf; + + msgs[1].addr = addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = sizeof(read_buf); + msgs[1].buf = read_buf; + + if (i2c_transfer(state->adap, msgs, 2) != 2) + return -EREMOTEIO; + + *lock = (read_buf[0] << 8 | read_buf[1]) == ts_id; + return 0; +} + +static int +va1j5jf8007s_tune(struct dvb_frontend *fe, + struct dvb_frontend_parameters *params, + unsigned int mode_flags, unsigned int *delay, + fe_status_t *status) +{ + struct va1j5jf8007s_state *state; + int ret; + int lock; + + state = fe->demodulator_priv; + + if (params != NULL) + state->tune_state = VA1J5JF8007S_SET_FREQUENCY_1; + + switch (state->tune_state) { + case VA1J5JF8007S_IDLE: + *delay = 3 * HZ; + *status = 0; + return 0; + + case VA1J5JF8007S_SET_FREQUENCY_1: + ret = va1j5jf8007s_set_frequency_1(state); + if (ret < 0) + return ret; + + state->tune_state = VA1J5JF8007S_SET_FREQUENCY_2; + *delay = 0; + *status = 0; + return 0; + + case VA1J5JF8007S_SET_FREQUENCY_2: + ret = va1j5jf8007s_set_frequency_2(state); + if (ret < 0) + return ret; + + state->tune_state = VA1J5JF8007S_SET_FREQUENCY_3; + *delay = (HZ + 99) / 100; + *status = 0; + return 0; + + case VA1J5JF8007S_SET_FREQUENCY_3: + ret = va1j5jf8007s_set_frequency_3(state); + if (ret < 0) + return ret; + + state->tune_state = VA1J5JF8007S_CHECK_FREQUENCY; + *delay = 0; + *status = 0; + return 0; + + case VA1J5JF8007S_CHECK_FREQUENCY: + ret = va1j5jf8007s_check_frequency(state, &lock); + if (ret < 0) + return ret; + + if (!lock) { + *delay = (HZ + 999) / 1000; + *status = 0; + return 0; + } + + state->tune_state = VA1J5JF8007S_SET_MODULATION; + *delay = 0; + *status = FE_HAS_SIGNAL; + return 0; + + case VA1J5JF8007S_SET_MODULATION: + ret = va1j5jf8007s_set_modulation(state); + if (ret < 0) + return ret; + + state->tune_state = VA1J5JF8007S_CHECK_MODULATION; + *delay = 0; + *status = FE_HAS_SIGNAL; + return 0; + + case VA1J5JF8007S_CHECK_MODULATION: + ret = va1j5jf8007s_check_modulation(state, &lock); + if (ret < 0) + return ret; + + if (!lock) { + *delay = (HZ + 49) / 50; + *status = FE_HAS_SIGNAL; + return 0; + } + + state->tune_state = VA1J5JF8007S_SET_TS_ID; + *delay = 0; + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER; + return 0; + + case VA1J5JF8007S_SET_TS_ID: + ret = va1j5jf8007s_set_ts_id(state); + if (ret < 0) + return ret; + + state->tune_state = VA1J5JF8007S_CHECK_TS_ID; + return 0; + + case VA1J5JF8007S_CHECK_TS_ID: + ret = va1j5jf8007s_check_ts_id(state, &lock); + if (ret < 0) + return ret; + + if (!lock) { + *delay = (HZ + 99) / 100; + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER; + return 0; + } + + state->tune_state = VA1J5JF8007S_TRACK; + /* fall through */ + + case VA1J5JF8007S_TRACK: + *delay = 3 * HZ; + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_LOCK; + return 0; + } + + BUG(); +} + +static int va1j5jf8007s_init_frequency(struct va1j5jf8007s_state *state) +{ + u8 buf[4]; + struct i2c_msg msg; + + buf[0] = 0xfe; + buf[1] = 0xc0; + buf[2] = 0xf0; + buf[3] = 0x04; + + msg.addr = state->config->demod_address; + msg.flags = 0; + msg.len = sizeof(buf); + msg.buf = buf; + + if (i2c_transfer(state->adap, &msg, 1) != 1) + return -EREMOTEIO; + + return 0; +} + +static int va1j5jf8007s_set_sleep(struct va1j5jf8007s_state *state, int sleep) +{ + u8 buf[2]; + struct i2c_msg msg; + + buf[0] = 0x17; + buf[1] = sleep ? 0x01 : 0x00; + + msg.addr = state->config->demod_address; + msg.flags = 0; + msg.len = sizeof(buf); + msg.buf = buf; + + if (i2c_transfer(state->adap, &msg, 1) != 1) + return -EREMOTEIO; + + return 0; +} + +static int va1j5jf8007s_sleep(struct dvb_frontend *fe) +{ + struct va1j5jf8007s_state *state; + int ret; + + state = fe->demodulator_priv; + + ret = va1j5jf8007s_init_frequency(state); + if (ret < 0) + return ret; + + return va1j5jf8007s_set_sleep(state, 1); +} + +static int va1j5jf8007s_init(struct dvb_frontend *fe) +{ + struct va1j5jf8007s_state *state; + + state = fe->demodulator_priv; + state->tune_state = VA1J5JF8007S_IDLE; + + return va1j5jf8007s_set_sleep(state, 0); +} + +static void va1j5jf8007s_release(struct dvb_frontend *fe) +{ + struct va1j5jf8007s_state *state; + state = fe->demodulator_priv; + kfree(state); +} + +static struct dvb_frontend_ops va1j5jf8007s_ops = { + .info = { + .name = "VA1J5JF8007 ISDB-S", + .type = FE_QPSK, + .frequency_min = 950000, + .frequency_max = 2150000, + .frequency_stepsize = 1000, + .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_AUTO | + FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_HIERARCHY_AUTO, + }, + + .get_frontend_algo = va1j5jf8007s_get_frontend_algo, + .read_status = va1j5jf8007s_read_status, + .tune = va1j5jf8007s_tune, + .sleep = va1j5jf8007s_sleep, + .init = va1j5jf8007s_init, + .release = va1j5jf8007s_release, +}; + +static int va1j5jf8007s_prepare_1(struct va1j5jf8007s_state *state) +{ + u8 addr; + u8 write_buf[1], read_buf[1]; + struct i2c_msg msgs[2]; + + addr = state->config->demod_address; + + write_buf[0] = 0x07; + + msgs[0].addr = addr; + msgs[0].flags = 0; + msgs[0].len = sizeof(write_buf); + msgs[0].buf = write_buf; + + msgs[1].addr = addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = sizeof(read_buf); + msgs[1].buf = read_buf; + + if (i2c_transfer(state->adap, msgs, 2) != 2) + return -EREMOTEIO; + + if (read_buf[0] != 0x41) + return -EIO; + + return 0; +} + +static const u8 va1j5jf8007s_prepare_bufs[][2] = { + {0x04, 0x02}, {0x0d, 0x55}, {0x11, 0x40}, {0x13, 0x80}, {0x17, 0x01}, + {0x1c, 0x0a}, {0x1d, 0xaa}, {0x1e, 0x20}, {0x1f, 0x88}, {0x51, 0xb0}, + {0x52, 0x89}, {0x53, 0xb3}, {0x5a, 0x2d}, {0x5b, 0xd3}, {0x85, 0x69}, + {0x87, 0x04}, {0x8e, 0x02}, {0xa3, 0xf7}, {0xa5, 0xc0}, +}; + +static int va1j5jf8007s_prepare_2(struct va1j5jf8007s_state *state) +{ + u8 addr; + u8 buf[2]; + struct i2c_msg msg; + int i; + + addr = state->config->demod_address; + + msg.addr = addr; + msg.flags = 0; + msg.len = 2; + msg.buf = buf; + for (i = 0; i < ARRAY_SIZE(va1j5jf8007s_prepare_bufs); i++) { + memcpy(buf, va1j5jf8007s_prepare_bufs[i], sizeof(buf)); + if (i2c_transfer(state->adap, &msg, 1) != 1) + return -EREMOTEIO; + } + + return 0; +} + +/* must be called after va1j5jf8007t_attach */ +int va1j5jf8007s_prepare(struct dvb_frontend *fe) +{ + struct va1j5jf8007s_state *state; + int ret; + + state = fe->demodulator_priv; + + ret = va1j5jf8007s_prepare_1(state); + if (ret < 0) + return ret; + + ret = va1j5jf8007s_prepare_2(state); + if (ret < 0) + return ret; + + return va1j5jf8007s_init_frequency(state); +} + +struct dvb_frontend * +va1j5jf8007s_attach(const struct va1j5jf8007s_config *config, + struct i2c_adapter *adap) +{ + struct va1j5jf8007s_state *state; + struct dvb_frontend *fe; + u8 buf[2]; + struct i2c_msg msg; + + state = kzalloc(sizeof(struct va1j5jf8007s_state), GFP_KERNEL); + if (!state) + return NULL; + + state->config = config; + state->adap = adap; + + fe = &state->fe; + memcpy(&fe->ops, &va1j5jf8007s_ops, sizeof(struct dvb_frontend_ops)); + fe->demodulator_priv = state; + + buf[0] = 0x01; + buf[1] = 0x80; + + msg.addr = state->config->demod_address; + msg.flags = 0; + msg.len = sizeof(buf); + msg.buf = buf; + + if (i2c_transfer(state->adap, &msg, 1) != 1) { + kfree(state); + return NULL; + } + + return fe; +} diff --git a/drivers/media/dvb/pt1/va1j5jf8007s.h b/drivers/media/dvb/pt1/va1j5jf8007s.h new file mode 100644 index 000000000000..aa228a816353 --- /dev/null +++ b/drivers/media/dvb/pt1/va1j5jf8007s.h @@ -0,0 +1,40 @@ +/* + * ISDB-S driver for VA1J5JF8007 + * + * Copyright (C) 2009 HIRANO Takahito + * + * based on pt1dvr - http://pt1dvr.sourceforge.jp/ + * by Tomoaki Ishikawa + * + * 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 VA1J5JF8007S_H +#define VA1J5JF8007S_H + +struct va1j5jf8007s_config { + u8 demod_address; +}; + +struct i2c_adapter; + +struct dvb_frontend * +va1j5jf8007s_attach(const struct va1j5jf8007s_config *config, + struct i2c_adapter *adap); + +/* must be called after va1j5jf8007t_attach */ +int va1j5jf8007s_prepare(struct dvb_frontend *fe); + +#endif diff --git a/drivers/media/dvb/pt1/va1j5jf8007t.c b/drivers/media/dvb/pt1/va1j5jf8007t.c new file mode 100644 index 000000000000..71117f4ca7e6 --- /dev/null +++ b/drivers/media/dvb/pt1/va1j5jf8007t.c @@ -0,0 +1,468 @@ +/* + * ISDB-T driver for VA1J5JF8007 + * + * Copyright (C) 2009 HIRANO Takahito + * + * based on pt1dvr - http://pt1dvr.sourceforge.jp/ + * by Tomoaki Ishikawa + * + * 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 +#include +#include +#include +#include "dvb_frontend.h" +#include "dvb_math.h" +#include "va1j5jf8007t.h" + +enum va1j5jf8007t_tune_state { + VA1J5JF8007T_IDLE, + VA1J5JF8007T_SET_FREQUENCY, + VA1J5JF8007T_CHECK_FREQUENCY, + VA1J5JF8007T_SET_MODULATION, + VA1J5JF8007T_CHECK_MODULATION, + VA1J5JF8007T_TRACK, + VA1J5JF8007T_ABORT, +}; + +struct va1j5jf8007t_state { + const struct va1j5jf8007t_config *config; + struct i2c_adapter *adap; + struct dvb_frontend fe; + enum va1j5jf8007t_tune_state tune_state; +}; + +static int va1j5jf8007t_get_frontend_algo(struct dvb_frontend *fe) +{ + return DVBFE_ALGO_HW; +} + +static int +va1j5jf8007t_read_status(struct dvb_frontend *fe, fe_status_t *status) +{ + struct va1j5jf8007t_state *state; + + state = fe->demodulator_priv; + + switch (state->tune_state) { + case VA1J5JF8007T_IDLE: + case VA1J5JF8007T_SET_FREQUENCY: + case VA1J5JF8007T_CHECK_FREQUENCY: + *status = 0; + return 0; + + + case VA1J5JF8007T_SET_MODULATION: + case VA1J5JF8007T_CHECK_MODULATION: + case VA1J5JF8007T_ABORT: + *status |= FE_HAS_SIGNAL; + return 0; + + case VA1J5JF8007T_TRACK: + *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_LOCK; + return 0; + } + + BUG(); +} + +struct va1j5jf8007t_cb_map { + u32 frequency; + u8 cb; +}; + +static const struct va1j5jf8007t_cb_map va1j5jf8007t_cb_maps[] = { + { 90000000, 0x80 }, + { 140000000, 0x81 }, + { 170000000, 0xa1 }, + { 220000000, 0x62 }, + { 330000000, 0xa2 }, + { 402000000, 0xe2 }, + { 450000000, 0x64 }, + { 550000000, 0x84 }, + { 600000000, 0xa4 }, + { 700000000, 0xc4 }, +}; + +static u8 va1j5jf8007t_lookup_cb(u32 frequency) +{ + int i; + const struct va1j5jf8007t_cb_map *map; + + for (i = 0; i < ARRAY_SIZE(va1j5jf8007t_cb_maps); i++) { + map = &va1j5jf8007t_cb_maps[i]; + if (frequency < map->frequency) + return map->cb; + } + return 0xe4; +} + +static int va1j5jf8007t_set_frequency(struct va1j5jf8007t_state *state) +{ + u32 frequency; + u16 word; + u8 buf[6]; + struct i2c_msg msg; + + frequency = state->fe.dtv_property_cache.frequency; + + word = (frequency + 71428) / 142857 + 399; + buf[0] = 0xfe; + buf[1] = 0xc2; + buf[2] = word >> 8; + buf[3] = word; + buf[4] = 0x80; + buf[5] = va1j5jf8007t_lookup_cb(frequency); + + msg.addr = state->config->demod_address; + msg.flags = 0; + msg.len = sizeof(buf); + msg.buf = buf; + + if (i2c_transfer(state->adap, &msg, 1) != 1) + return -EREMOTEIO; + + return 0; +} + +static int +va1j5jf8007t_check_frequency(struct va1j5jf8007t_state *state, int *lock) +{ + u8 addr; + u8 write_buf[2], read_buf[1]; + struct i2c_msg msgs[2]; + + addr = state->config->demod_address; + + write_buf[0] = 0xfe; + write_buf[1] = 0xc3; + + msgs[0].addr = addr; + msgs[0].flags = 0; + msgs[0].len = sizeof(write_buf); + msgs[0].buf = write_buf; + + msgs[1].addr = addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = sizeof(read_buf); + msgs[1].buf = read_buf; + + if (i2c_transfer(state->adap, msgs, 2) != 2) + return -EREMOTEIO; + + *lock = read_buf[0] & 0x40; + return 0; +} + +static int va1j5jf8007t_set_modulation(struct va1j5jf8007t_state *state) +{ + u8 buf[2]; + struct i2c_msg msg; + + buf[0] = 0x01; + buf[1] = 0x40; + + msg.addr = state->config->demod_address; + msg.flags = 0; + msg.len = sizeof(buf); + msg.buf = buf; + + if (i2c_transfer(state->adap, &msg, 1) != 1) + return -EREMOTEIO; + + return 0; +} + +static int va1j5jf8007t_check_modulation(struct va1j5jf8007t_state *state, + int *lock, int *retry) +{ + u8 addr; + u8 write_buf[1], read_buf[1]; + struct i2c_msg msgs[2]; + + addr = state->config->demod_address; + + write_buf[0] = 0x80; + + msgs[0].addr = addr; + msgs[0].flags = 0; + msgs[0].len = sizeof(write_buf); + msgs[0].buf = write_buf; + + msgs[1].addr = addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = sizeof(read_buf); + msgs[1].buf = read_buf; + + if (i2c_transfer(state->adap, msgs, 2) != 2) + return -EREMOTEIO; + + *lock = !(read_buf[0] & 0x10); + *retry = read_buf[0] & 0x80; + return 0; +} + +static int +va1j5jf8007t_tune(struct dvb_frontend *fe, + struct dvb_frontend_parameters *params, + unsigned int mode_flags, unsigned int *delay, + fe_status_t *status) +{ + struct va1j5jf8007t_state *state; + int ret; + int lock, retry; + + state = fe->demodulator_priv; + + if (params != NULL) + state->tune_state = VA1J5JF8007T_SET_FREQUENCY; + + switch (state->tune_state) { + case VA1J5JF8007T_IDLE: + *delay = 3 * HZ; + *status = 0; + return 0; + + case VA1J5JF8007T_SET_FREQUENCY: + ret = va1j5jf8007t_set_frequency(state); + if (ret < 0) + return ret; + + state->tune_state = VA1J5JF8007T_CHECK_FREQUENCY; + *delay = 0; + *status = 0; + return 0; + + case VA1J5JF8007T_CHECK_FREQUENCY: + ret = va1j5jf8007t_check_frequency(state, &lock); + if (ret < 0) + return ret; + + if (!lock) { + *delay = (HZ + 999) / 1000; + *status = 0; + return 0; + } + + state->tune_state = VA1J5JF8007T_SET_MODULATION; + *delay = 0; + *status = FE_HAS_SIGNAL; + return 0; + + case VA1J5JF8007T_SET_MODULATION: + ret = va1j5jf8007t_set_modulation(state); + if (ret < 0) + return ret; + + state->tune_state = VA1J5JF8007T_CHECK_MODULATION; + *delay = 0; + *status = FE_HAS_SIGNAL; + return 0; + + case VA1J5JF8007T_CHECK_MODULATION: + ret = va1j5jf8007t_check_modulation(state, &lock, &retry); + if (ret < 0) + return ret; + + if (!lock) { + if (!retry) { + state->tune_state = VA1J5JF8007T_ABORT; + *delay = 3 * HZ; + *status = FE_HAS_SIGNAL; + return 0; + } + *delay = (HZ + 999) / 1000; + *status = FE_HAS_SIGNAL; + return 0; + } + + state->tune_state = VA1J5JF8007T_TRACK; + /* fall through */ + + case VA1J5JF8007T_TRACK: + *delay = 3 * HZ; + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_LOCK; + return 0; + + case VA1J5JF8007T_ABORT: + *delay = 3 * HZ; + *status = FE_HAS_SIGNAL; + return 0; + } + + BUG(); +} + +static int va1j5jf8007t_init_frequency(struct va1j5jf8007t_state *state) +{ + u8 buf[7]; + struct i2c_msg msg; + + buf[0] = 0xfe; + buf[1] = 0xc2; + buf[2] = 0x01; + buf[3] = 0x8f; + buf[4] = 0xc1; + buf[5] = 0x80; + buf[6] = 0x80; + + msg.addr = state->config->demod_address; + msg.flags = 0; + msg.len = sizeof(buf); + msg.buf = buf; + + if (i2c_transfer(state->adap, &msg, 1) != 1) + return -EREMOTEIO; + + return 0; +} + +static int va1j5jf8007t_set_sleep(struct va1j5jf8007t_state *state, int sleep) +{ + u8 buf[2]; + struct i2c_msg msg; + + buf[0] = 0x03; + buf[1] = sleep ? 0x90 : 0x80; + + msg.addr = state->config->demod_address; + msg.flags = 0; + msg.len = sizeof(buf); + msg.buf = buf; + + if (i2c_transfer(state->adap, &msg, 1) != 1) + return -EREMOTEIO; + + return 0; +} + +static int va1j5jf8007t_sleep(struct dvb_frontend *fe) +{ + struct va1j5jf8007t_state *state; + int ret; + + state = fe->demodulator_priv; + + ret = va1j5jf8007t_init_frequency(state); + if (ret < 0) + return ret; + + return va1j5jf8007t_set_sleep(state, 1); +} + +static int va1j5jf8007t_init(struct dvb_frontend *fe) +{ + struct va1j5jf8007t_state *state; + + state = fe->demodulator_priv; + state->tune_state = VA1J5JF8007T_IDLE; + + return va1j5jf8007t_set_sleep(state, 0); +} + +static void va1j5jf8007t_release(struct dvb_frontend *fe) +{ + struct va1j5jf8007t_state *state; + state = fe->demodulator_priv; + kfree(state); +} + +static struct dvb_frontend_ops va1j5jf8007t_ops = { + .info = { + .name = "VA1J5JF8007 ISDB-T", + .type = FE_OFDM, + .frequency_min = 90000000, + .frequency_max = 770000000, + .frequency_stepsize = 142857, + .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_AUTO | + FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_HIERARCHY_AUTO, + }, + + .get_frontend_algo = va1j5jf8007t_get_frontend_algo, + .read_status = va1j5jf8007t_read_status, + .tune = va1j5jf8007t_tune, + .sleep = va1j5jf8007t_sleep, + .init = va1j5jf8007t_init, + .release = va1j5jf8007t_release, +}; + +static const u8 va1j5jf8007t_prepare_bufs[][2] = { + {0x03, 0x90}, {0x14, 0x8f}, {0x1c, 0x2a}, {0x1d, 0xa8}, {0x1e, 0xa2}, + {0x22, 0x83}, {0x31, 0x0d}, {0x32, 0xe0}, {0x39, 0xd3}, {0x3a, 0x00}, + {0x5c, 0x40}, {0x5f, 0x80}, {0x75, 0x02}, {0x76, 0x4e}, {0x77, 0x03}, + {0xef, 0x01} +}; + +int va1j5jf8007t_prepare(struct dvb_frontend *fe) +{ + struct va1j5jf8007t_state *state; + u8 buf[2]; + struct i2c_msg msg; + int i; + + state = fe->demodulator_priv; + + msg.addr = state->config->demod_address; + msg.flags = 0; + msg.len = sizeof(buf); + msg.buf = buf; + + for (i = 0; i < ARRAY_SIZE(va1j5jf8007t_prepare_bufs); i++) { + memcpy(buf, va1j5jf8007t_prepare_bufs[i], sizeof(buf)); + if (i2c_transfer(state->adap, &msg, 1) != 1) + return -EREMOTEIO; + } + + return va1j5jf8007t_init_frequency(state); +} + +struct dvb_frontend * +va1j5jf8007t_attach(const struct va1j5jf8007t_config *config, + struct i2c_adapter *adap) +{ + struct va1j5jf8007t_state *state; + struct dvb_frontend *fe; + u8 buf[2]; + struct i2c_msg msg; + + state = kzalloc(sizeof(struct va1j5jf8007t_state), GFP_KERNEL); + if (!state) + return NULL; + + state->config = config; + state->adap = adap; + + fe = &state->fe; + memcpy(&fe->ops, &va1j5jf8007t_ops, sizeof(struct dvb_frontend_ops)); + fe->demodulator_priv = state; + + buf[0] = 0x01; + buf[1] = 0x80; + + msg.addr = state->config->demod_address; + msg.flags = 0; + msg.len = sizeof(buf); + msg.buf = buf; + + if (i2c_transfer(state->adap, &msg, 1) != 1) { + kfree(state); + return NULL; + } + + return fe; +} diff --git a/drivers/media/dvb/pt1/va1j5jf8007t.h b/drivers/media/dvb/pt1/va1j5jf8007t.h new file mode 100644 index 000000000000..ed49906f7769 --- /dev/null +++ b/drivers/media/dvb/pt1/va1j5jf8007t.h @@ -0,0 +1,40 @@ +/* + * ISDB-T driver for VA1J5JF8007 + * + * Copyright (C) 2009 HIRANO Takahito + * + * based on pt1dvr - http://pt1dvr.sourceforge.jp/ + * by Tomoaki Ishikawa + * + * 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 VA1J5JF8007T_H +#define VA1J5JF8007T_H + +struct va1j5jf8007t_config { + u8 demod_address; +}; + +struct i2c_adapter; + +struct dvb_frontend * +va1j5jf8007t_attach(const struct va1j5jf8007t_config *config, + struct i2c_adapter *adap); + +/* must be called after va1j5jf8007s_attach */ +int va1j5jf8007t_prepare(struct dvb_frontend *fe); + +#endif -- cgit v1.2.2 From 5eca4823ea8f99a7109779f68cedb00535aa6834 Mon Sep 17 00:00:00 2001 From: Akihiro Tsukada Date: Tue, 25 Aug 2009 02:39:51 -0300 Subject: V4L/DVB (13000): add driver for 774 Friio White USB ISDB-T receiver This patch adds driver for 774 Friio White, ISDB-T USB receiver Friio White is an USB 2.0 ISDB-T receiver. (http://www.friio.com/) The device has a GL861 chip and a Comtech JDVBT90502 canned tuner module. This driver ignores all the frontend_parameters except frequency, as ISDB-T shares the same parameter configuration across the country and thus the device can work like an intelligent one. As this device does not include a CAM nor hardware descrambling feature, the driver passes through scrambled TS streams. There is Friio Black, a variant for ISDB-S, which shares the same USB Vendor/Product ID with White, but it is not supported in this driver. They should be identified in the initialization sequence, but this feature is not tested. Signed-off-by: Akihiro Tsukada Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/Kconfig | 6 + drivers/media/dvb/dvb-usb/Makefile | 3 + drivers/media/dvb/dvb-usb/dvb-usb-ids.h | 2 + drivers/media/dvb/dvb-usb/friio-fe.c | 483 +++++++++++++++++++++++++++++ drivers/media/dvb/dvb-usb/friio.c | 525 ++++++++++++++++++++++++++++++++ drivers/media/dvb/dvb-usb/friio.h | 99 ++++++ 6 files changed, 1118 insertions(+) create mode 100644 drivers/media/dvb/dvb-usb/friio-fe.c create mode 100644 drivers/media/dvb/dvb-usb/friio.c create mode 100644 drivers/media/dvb/dvb-usb/friio.h (limited to 'drivers/media/dvb') diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig index c5ec9a5f3b3f..0e4b97fba384 100644 --- a/drivers/media/dvb/dvb-usb/Kconfig +++ b/drivers/media/dvb/dvb-usb/Kconfig @@ -316,3 +316,9 @@ config DVB_USB_CE6230 select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMISE help Say Y here to support the Intel CE6230 DVB-T USB2.0 receiver + +config DVB_USB_FRIIO + tristate "Friio ISDB-T USB2.0 Receiver support" + depends on DVB_USB + help + Say Y here to support the Japanese DTV receiver Friio. diff --git a/drivers/media/dvb/dvb-usb/Makefile b/drivers/media/dvb/dvb-usb/Makefile index f92734ed777a..85b83a43d55d 100644 --- a/drivers/media/dvb/dvb-usb/Makefile +++ b/drivers/media/dvb/dvb-usb/Makefile @@ -79,6 +79,9 @@ obj-$(CONFIG_DVB_USB_CINERGY_T2) += dvb-usb-cinergyT2.o dvb-usb-ce6230-objs = ce6230.o obj-$(CONFIG_DVB_USB_CE6230) += dvb-usb-ce6230.o +dvb-usb-friio-objs = friio.o friio-fe.o +obj-$(CONFIG_DVB_USB_FRIIO) += dvb-usb-friio.o + EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/ # due to tuner-xc3028 EXTRA_CFLAGS += -Idrivers/media/common/tuners diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h index 2d51e3c28207..a548c14c1944 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h +++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h @@ -60,6 +60,7 @@ #define USB_VID_YUAN 0x1164 #define USB_VID_XTENSIONS 0x1ae7 #define USB_VID_HUMAX_COEX 0x10b9 +#define USB_VID_774 0x7a69 /* Product IDs */ #define USB_PID_ADSTECH_USB2_COLD 0xa333 @@ -274,5 +275,6 @@ #define USB_PID_ELGATO_EYETV_DTT_Dlx 0x0020 #define USB_PID_DVB_T_USB_STICK_HIGH_SPEED_COLD 0x5000 #define USB_PID_DVB_T_USB_STICK_HIGH_SPEED_WARM 0x5001 +#define USB_PID_FRIIO_WHITE 0x0001 #endif diff --git a/drivers/media/dvb/dvb-usb/friio-fe.c b/drivers/media/dvb/dvb-usb/friio-fe.c new file mode 100644 index 000000000000..c4dfe25cf60d --- /dev/null +++ b/drivers/media/dvb/dvb-usb/friio-fe.c @@ -0,0 +1,483 @@ +/* DVB USB compliant Linux driver for the Friio USB2.0 ISDB-T receiver. + * + * Copyright (C) 2009 Akihiro Tsukada + * + * This module is based off the the gl861 and vp702x modules. + * + * 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. + * + * see Documentation/dvb/README.dvb-usb for more information + */ +#include +#include +#include + +#include "friio.h" + +struct jdvbt90502_state { + struct i2c_adapter *i2c; + struct dvb_frontend frontend; + struct jdvbt90502_config config; +}; + +/* NOTE: TC90502 has 16bit register-address? */ +/* register 0x0100 is used for reading PLL status, so reg is u16 here */ +static int jdvbt90502_reg_read(struct jdvbt90502_state *state, + const u16 reg, u8 *buf, const size_t count) +{ + int ret; + u8 wbuf[3]; + struct i2c_msg msg[2]; + + wbuf[0] = reg & 0xFF; + wbuf[1] = 0; + wbuf[2] = reg >> 8; + + msg[0].addr = state->config.demod_address; + msg[0].flags = 0; + msg[0].buf = wbuf; + msg[0].len = sizeof(wbuf); + + msg[1].addr = msg[0].addr; + msg[1].flags = I2C_M_RD; + msg[1].buf = buf; + msg[1].len = count; + + ret = i2c_transfer(state->i2c, msg, 2); + if (ret != 2) { + deb_fe(" reg read failed.\n"); + return -EREMOTEIO; + } + return 0; +} + +/* currently 16bit register-address is not used, so reg is u8 here */ +static int jdvbt90502_single_reg_write(struct jdvbt90502_state *state, + const u8 reg, const u8 val) +{ + struct i2c_msg msg; + u8 wbuf[2]; + + wbuf[0] = reg; + wbuf[1] = val; + + msg.addr = state->config.demod_address; + msg.flags = 0; + msg.buf = wbuf; + msg.len = sizeof(wbuf); + + if (i2c_transfer(state->i2c, &msg, 1) != 1) { + deb_fe(" reg write failed."); + return -EREMOTEIO; + } + return 0; +} + +static int _jdvbt90502_write(struct dvb_frontend *fe, u8 *buf, int len) +{ + struct jdvbt90502_state *state = fe->demodulator_priv; + int err, i; + for (i = 0; i < len - 1; i++) { + err = jdvbt90502_single_reg_write(state, + buf[0] + i, buf[i + 1]); + if (err) + return err; + } + + return 0; +} + +/* read pll status byte via the demodulator's I2C register */ +/* note: Win box reads it by 8B block at the I2C addr 0x30 from reg:0x80 */ +static int jdvbt90502_pll_read(struct jdvbt90502_state *state, u8 *result) +{ + int ret; + + /* +1 for reading */ + u8 pll_addr_byte = (state->config.pll_address << 1) + 1; + + *result = 0; + + ret = jdvbt90502_single_reg_write(state, JDVBT90502_2ND_I2C_REG, + pll_addr_byte); + if (ret) + goto error; + + ret = jdvbt90502_reg_read(state, 0x0100, result, 1); + if (ret) + goto error; + + deb_fe("PLL read val:%02x\n", *result); + return 0; + +error: + deb_fe("%s:ret == %d\n", __func__, ret); + return -EREMOTEIO; +} + + +/* set pll frequency via the demodulator's I2C register */ +static int jdvbt90502_pll_set_freq(struct jdvbt90502_state *state, u32 freq) +{ + int ret; + int retry; + u8 res1; + u8 res2[9]; + + u8 pll_freq_cmd[PLL_CMD_LEN]; + u8 pll_agc_cmd[PLL_CMD_LEN]; + struct i2c_msg msg[2]; + u32 f; + + deb_fe("%s: freq=%d, step=%d\n", __func__, freq, + state->frontend.ops.info.frequency_stepsize); + /* freq -> oscilator frequency conversion. */ + /* freq: 473,000,000 + n*6,000,000 (no 1/7MHz shift to center freq) */ + /* add 400[1/7 MHZ] = 57.142857MHz. 57MHz for the IF, */ + /* 1/7MHz for center freq shift */ + f = freq / state->frontend.ops.info.frequency_stepsize; + f += 400; + pll_freq_cmd[DEMOD_REDIRECT_REG] = JDVBT90502_2ND_I2C_REG; /* 0xFE */ + pll_freq_cmd[ADDRESS_BYTE] = state->config.pll_address << 1; + pll_freq_cmd[DIVIDER_BYTE1] = (f >> 8) & 0x7F; + pll_freq_cmd[DIVIDER_BYTE2] = f & 0xFF; + pll_freq_cmd[CONTROL_BYTE] = 0xB2; /* ref.divider:28, 4MHz/28=1/7MHz */ + pll_freq_cmd[BANDSWITCH_BYTE] = 0x08; /* UHF band */ + + msg[0].addr = state->config.demod_address; + msg[0].flags = 0; + msg[0].buf = pll_freq_cmd; + msg[0].len = sizeof(pll_freq_cmd); + + ret = i2c_transfer(state->i2c, &msg[0], 1); + if (ret != 1) + goto error; + + udelay(50); + + pll_agc_cmd[DEMOD_REDIRECT_REG] = pll_freq_cmd[DEMOD_REDIRECT_REG]; + pll_agc_cmd[ADDRESS_BYTE] = pll_freq_cmd[ADDRESS_BYTE]; + pll_agc_cmd[DIVIDER_BYTE1] = pll_freq_cmd[DIVIDER_BYTE1]; + pll_agc_cmd[DIVIDER_BYTE2] = pll_freq_cmd[DIVIDER_BYTE2]; + pll_agc_cmd[CONTROL_BYTE] = 0x9A; /* AGC_CTRL instead of BANDSWITCH */ + pll_agc_cmd[AGC_CTRL_BYTE] = 0x50; + /* AGC Time Constant 2s, AGC take-over point:103dBuV(lowest) */ + + msg[1].addr = msg[0].addr; + msg[1].flags = 0; + msg[1].buf = pll_agc_cmd; + msg[1].len = sizeof(pll_agc_cmd); + + ret = i2c_transfer(state->i2c, &msg[1], 1); + if (ret != 1) + goto error; + + /* I don't know what these cmds are for, */ + /* but the USB log on a windows box contains them */ + ret = jdvbt90502_single_reg_write(state, 0x01, 0x40); + ret |= jdvbt90502_single_reg_write(state, 0x01, 0x00); + if (ret) + goto error; + udelay(100); + + /* wait for the demod to be ready? */ +#define RETRY_COUNT 5 + for (retry = 0; retry < RETRY_COUNT; retry++) { + ret = jdvbt90502_reg_read(state, 0x0096, &res1, 1); + if (ret) + goto error; + /* if (res1 != 0x00) goto error; */ + ret = jdvbt90502_reg_read(state, 0x00B0, res2, sizeof(res2)); + if (ret) + goto error; + if (res2[0] >= 0xA7) + break; + msleep(100); + } + if (retry >= RETRY_COUNT) { + deb_fe("%s: FE does not get ready after freq setting.\n", + __func__); + return -EREMOTEIO; + } + + return 0; +error: + deb_fe("%s:ret == %d\n", __func__, ret); + return -EREMOTEIO; +} + +static int jdvbt90502_read_status(struct dvb_frontend *fe, fe_status_t *state) +{ + u8 result; + int ret; + + *state = FE_HAS_SIGNAL; + + ret = jdvbt90502_pll_read(fe->demodulator_priv, &result); + if (ret) { + deb_fe("%s:ret == %d\n", __func__, ret); + return -EREMOTEIO; + } + + *state = FE_HAS_SIGNAL + | FE_HAS_CARRIER + | FE_HAS_VITERBI + | FE_HAS_SYNC; + + if (result & PLL_STATUS_LOCKED) + *state |= FE_HAS_LOCK; + + return 0; +} + +static int jdvbt90502_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + *ber = 0; + return 0; +} + +static int jdvbt90502_read_signal_strength(struct dvb_frontend *fe, + u16 *strength) +{ + int ret; + u8 rbuf[37]; + + *strength = 0; + + /* status register (incl. signal strength) : 0x89 */ + /* TODO: read just the necessary registers [0x8B..0x8D]? */ + ret = jdvbt90502_reg_read(fe->demodulator_priv, 0x0089, + rbuf, sizeof(rbuf)); + + if (ret) { + deb_fe("%s:ret == %d\n", __func__, ret); + return -EREMOTEIO; + } + + /* signal_strength: rbuf[2-4] (24bit BE), use lower 16bit for now. */ + *strength = (rbuf[3] << 8) + rbuf[4]; + if (rbuf[2]) + *strength = 0xffff; + + return 0; +} + +static int jdvbt90502_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + *snr = 0x0101; + return 0; +} + +static int jdvbt90502_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + *ucblocks = 0; + return 0; +} + +static int jdvbt90502_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *fs) +{ + fs->min_delay_ms = 500; + fs->step_size = 0; + fs->max_drift = 0; + + return 0; +} + +static int jdvbt90502_get_frontend(struct dvb_frontend *fe, + struct dvb_frontend_parameters *p) +{ + p->inversion = INVERSION_AUTO; + p->u.ofdm.bandwidth = BANDWIDTH_6_MHZ; + p->u.ofdm.code_rate_HP = FEC_AUTO; + p->u.ofdm.code_rate_LP = FEC_AUTO; + p->u.ofdm.constellation = QAM_64; + p->u.ofdm.transmission_mode = TRANSMISSION_MODE_AUTO; + p->u.ofdm.guard_interval = GUARD_INTERVAL_AUTO; + p->u.ofdm.hierarchy_information = HIERARCHY_AUTO; + return 0; +} + +static int jdvbt90502_set_frontend(struct dvb_frontend *fe, + struct dvb_frontend_parameters *p) +{ + /** + * NOTE: ignore all the paramters except frequency. + * others should be fixed to the proper value for ISDB-T, + * but don't check here. + */ + + struct jdvbt90502_state *state = fe->demodulator_priv; + int ret; + + deb_fe("%s: Freq:%d\n", __func__, p->frequency); + + ret = jdvbt90502_pll_set_freq(state, p->frequency); + if (ret) { + deb_fe("%s:ret == %d\n", __func__, ret); + return -EREMOTEIO; + } + + return 0; +} + +static int jdvbt90502_sleep(struct dvb_frontend *fe) +{ + deb_fe("%s called.\n", __func__); + return 0; +} + + +/** + * (reg, val) commad list to initialize this module. + * captured on a Windows box. + */ +static u8 init_code[][2] = { + {0x01, 0x40}, + {0x04, 0x38}, + {0x05, 0x40}, + {0x07, 0x40}, + {0x0F, 0x4F}, + {0x11, 0x21}, + {0x12, 0x0B}, + {0x13, 0x2F}, + {0x14, 0x31}, + {0x16, 0x02}, + {0x21, 0xC4}, + {0x22, 0x20}, + {0x2C, 0x79}, + {0x2D, 0x34}, + {0x2F, 0x00}, + {0x30, 0x28}, + {0x31, 0x31}, + {0x32, 0xDF}, + {0x38, 0x01}, + {0x39, 0x78}, + {0x3B, 0x33}, + {0x3C, 0x33}, + {0x48, 0x90}, + {0x51, 0x68}, + {0x5E, 0x38}, + {0x71, 0x00}, + {0x72, 0x08}, + {0x77, 0x00}, + {0xC0, 0x21}, + {0xC1, 0x10}, + {0xE4, 0x1A}, + {0xEA, 0x1F}, + {0x77, 0x00}, + {0x71, 0x00}, + {0x71, 0x00}, + {0x76, 0x0C}, +}; + +const static int init_code_len = sizeof(init_code) / sizeof(u8[2]); + +static int jdvbt90502_init(struct dvb_frontend *fe) +{ + int i = -1; + int ret; + struct i2c_msg msg; + + struct jdvbt90502_state *state = fe->demodulator_priv; + + deb_fe("%s called.\n", __func__); + + msg.addr = state->config.demod_address; + msg.flags = 0; + msg.len = 2; + for (i = 0; i < init_code_len; i++) { + msg.buf = init_code[i]; + ret = i2c_transfer(state->i2c, &msg, 1); + if (ret != 1) + goto error; + } + msleep(100); + + return 0; + +error: + deb_fe("%s: init_code[%d] failed. ret==%d\n", __func__, i, ret); + return -EREMOTEIO; +} + + +static void jdvbt90502_release(struct dvb_frontend *fe) +{ + struct jdvbt90502_state *state = fe->demodulator_priv; + kfree(state); +} + + +static struct dvb_frontend_ops jdvbt90502_ops; + +struct dvb_frontend *jdvbt90502_attach(struct dvb_usb_device *d) +{ + struct jdvbt90502_state *state = NULL; + + deb_info("%s called.\n", __func__); + + /* allocate memory for the internal state */ + state = kzalloc(sizeof(struct jdvbt90502_state), GFP_KERNEL); + if (state == NULL) + goto error; + + /* setup the state */ + state->i2c = &d->i2c_adap; + memcpy(&state->config, &friio_fe_config, sizeof(friio_fe_config)); + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, &jdvbt90502_ops, + sizeof(jdvbt90502_ops)); + state->frontend.demodulator_priv = state; + + if (jdvbt90502_init(&state->frontend) < 0) + goto error; + + return &state->frontend; + +error: + kfree(state); + return NULL; +} + +static struct dvb_frontend_ops jdvbt90502_ops = { + + .info = { + .name = "Comtech JDVBT90502 ISDB-T", + .type = FE_OFDM, + .frequency_min = 473000000, /* UHF 13ch, center */ + .frequency_max = 767142857, /* UHF 62ch, center */ + .frequency_stepsize = JDVBT90502_PLL_CLK / + JDVBT90502_PLL_DIVIDER, + .frequency_tolerance = 0, + + /* NOTE: this driver ignores all parameters but frequency. */ + .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_8_9 | FE_CAN_FEC_AUTO | + 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, + }, + + .release = jdvbt90502_release, + + .init = jdvbt90502_init, + .sleep = jdvbt90502_sleep, + .write = _jdvbt90502_write, + + .set_frontend = jdvbt90502_set_frontend, + .get_frontend = jdvbt90502_get_frontend, + .get_tune_settings = jdvbt90502_get_tune_settings, + + .read_status = jdvbt90502_read_status, + .read_ber = jdvbt90502_read_ber, + .read_signal_strength = jdvbt90502_read_signal_strength, + .read_snr = jdvbt90502_read_snr, + .read_ucblocks = jdvbt90502_read_ucblocks, +}; diff --git a/drivers/media/dvb/dvb-usb/friio.c b/drivers/media/dvb/dvb-usb/friio.c new file mode 100644 index 000000000000..14a65b4aec07 --- /dev/null +++ b/drivers/media/dvb/dvb-usb/friio.c @@ -0,0 +1,525 @@ +/* DVB USB compliant Linux driver for the Friio USB2.0 ISDB-T receiver. + * + * Copyright (C) 2009 Akihiro Tsukada + * + * This module is based off the the gl861 and vp702x modules. + * + * 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. + * + * see Documentation/dvb/README.dvb-usb for more information + */ +#include "friio.h" + +/* debug */ +int dvb_usb_friio_debug; +module_param_named(debug, dvb_usb_friio_debug, int, 0644); +MODULE_PARM_DESC(debug, + "set debugging level (1=info,2=xfer,4=rc,8=fe (or-able))." + DVB_USB_DEBUG_STATUS); + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +/** + * Indirect I2C access to the PLL via FE. + * whole I2C protocol data to the PLL is sent via the FE's I2C register. + * This is done by a control msg to the FE with the I2C data accompanied, and + * a specific USB request number is assigned for that purpose. + * + * this func sends wbuf[1..] to the I2C register wbuf[0] at addr (= at FE). + * TODO: refoctored, smarter i2c functions. + */ +static int gl861_i2c_ctrlmsg_data(struct dvb_usb_device *d, u8 addr, + u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen) +{ + u16 index = wbuf[0]; /* must be JDVBT90502_2ND_I2C_REG(=0xFE) */ + u16 value = addr << (8 + 1); + int wo = (rbuf == NULL || rlen == 0); /* write only */ + u8 req, type; + + deb_xfer("write to PLL:0x%02x via FE reg:0x%02x, len:%d\n", + wbuf[1], wbuf[0], wlen - 1); + + if (wo && wlen >= 2) { + req = GL861_REQ_I2C_DATA_CTRL_WRITE; + type = GL861_WRITE; + udelay(20); + return usb_control_msg(d->udev, usb_sndctrlpipe(d->udev, 0), + req, type, value, index, + &wbuf[1], wlen - 1, 2000); + } + + deb_xfer("not supported ctrl-msg, aborting."); + return -EINVAL; +} + +/* normal I2C access (without extra data arguments). + * write to the register wbuf[0] at I2C address addr with the value wbuf[1], + * or read from the register wbuf[0]. + * register address can be 16bit (wbuf[2]<<8 | wbuf[0]) if wlen==3 + */ +static int gl861_i2c_msg(struct dvb_usb_device *d, u8 addr, + u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen) +{ + u16 index; + u16 value = addr << (8 + 1); + int wo = (rbuf == NULL || rlen == 0); /* write-only */ + u8 req, type; + unsigned int pipe; + + /* special case for the indirect I2C access to the PLL via FE, */ + if (addr == friio_fe_config.demod_address && + wbuf[0] == JDVBT90502_2ND_I2C_REG) + return gl861_i2c_ctrlmsg_data(d, addr, wbuf, wlen, rbuf, rlen); + + if (wo) { + req = GL861_REQ_I2C_WRITE; + type = GL861_WRITE; + pipe = usb_sndctrlpipe(d->udev, 0); + } else { /* rw */ + req = GL861_REQ_I2C_READ; + type = GL861_READ; + pipe = usb_rcvctrlpipe(d->udev, 0); + } + + switch (wlen) { + case 1: + index = wbuf[0]; + break; + case 2: + index = wbuf[0]; + value = value + wbuf[1]; + break; + case 3: + /* special case for 16bit register-address */ + index = (wbuf[2] << 8) | wbuf[0]; + value = value + wbuf[1]; + break; + default: + deb_xfer("wlen = %x, aborting.", wlen); + return -EINVAL; + } + msleep(1); + return usb_control_msg(d->udev, pipe, req, type, + value, index, rbuf, rlen, 2000); +} + +/* I2C */ +static int gl861_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], + int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + int i; + + + if (num > 2) + return -EINVAL; + + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + for (i = 0; i < num; i++) { + /* write/read request */ + if (i + 1 < num && (msg[i + 1].flags & I2C_M_RD)) { + if (gl861_i2c_msg(d, msg[i].addr, + msg[i].buf, msg[i].len, + msg[i + 1].buf, msg[i + 1].len) < 0) + break; + i++; + } else + if (gl861_i2c_msg(d, msg[i].addr, msg[i].buf, + msg[i].len, NULL, 0) < 0) + break; + } + + mutex_unlock(&d->i2c_mutex); + return i; +} + +static u32 gl861_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + + +static int friio_ext_ctl(struct dvb_usb_adapter *adap, + u32 sat_color, int lnb_on) +{ + int i; + int ret; + struct i2c_msg msg; + u8 buf[2]; + u32 mask; + u8 lnb = (lnb_on) ? FRIIO_CTL_LNB : 0; + + msg.addr = 0x00; + msg.flags = 0; + msg.len = 2; + msg.buf = buf; + + buf[0] = 0x00; + + /* send 2bit header (&B10) */ + buf[1] = lnb | FRIIO_CTL_LED | FRIIO_CTL_STROBE; + ret = gl861_i2c_xfer(&adap->dev->i2c_adap, &msg, 1); + buf[1] |= FRIIO_CTL_CLK; + ret += gl861_i2c_xfer(&adap->dev->i2c_adap, &msg, 1); + + buf[1] = lnb | FRIIO_CTL_STROBE; + ret += gl861_i2c_xfer(&adap->dev->i2c_adap, &msg, 1); + buf[1] |= FRIIO_CTL_CLK; + ret += gl861_i2c_xfer(&adap->dev->i2c_adap, &msg, 1); + + /* send 32bit(satur, R, G, B) data in serial */ + mask = 1 << 31; + for (i = 0; i < 32; i++) { + buf[1] = lnb | FRIIO_CTL_STROBE; + if (sat_color & mask) + buf[1] |= FRIIO_CTL_LED; + ret += gl861_i2c_xfer(&adap->dev->i2c_adap, &msg, 1); + buf[1] |= FRIIO_CTL_CLK; + ret += gl861_i2c_xfer(&adap->dev->i2c_adap, &msg, 1); + mask >>= 1; + } + + /* set the strobe off */ + buf[1] = lnb; + ret += gl861_i2c_xfer(&adap->dev->i2c_adap, &msg, 1); + buf[1] |= FRIIO_CTL_CLK; + ret += gl861_i2c_xfer(&adap->dev->i2c_adap, &msg, 1); + + return (ret == 70); +} + + +static int friio_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff); + +/* TODO: move these init cmds to the FE's init routine? */ +static u8 streaming_init_cmds[][2] = { + {0x33, 0x08}, + {0x37, 0x40}, + {0x3A, 0x1F}, + {0x3B, 0xFF}, + {0x3C, 0x1F}, + {0x3D, 0xFF}, + {0x38, 0x00}, + {0x35, 0x00}, + {0x39, 0x00}, + {0x36, 0x00}, +}; +static int cmdlen = sizeof(streaming_init_cmds) / 2; + +/* + * Command sequence in this init function is a replay + * of the captured USB commands from the Windows proprietary driver. + */ +static int friio_initialize(struct dvb_usb_device *d) +{ + int ret; + int i; + int retry = 0; + u8 rbuf[2]; + u8 wbuf[3]; + + deb_info("%s called.\n", __func__); + + /* use gl861_i2c_msg instead of gl861_i2c_xfer(), */ + /* because the i2c device is not set up yet. */ + wbuf[0] = 0x11; + wbuf[1] = 0x02; + ret = gl861_i2c_msg(d, 0x00, wbuf, 2, NULL, 0); + if (ret < 0) + goto error; + msleep(2); + + wbuf[0] = 0x11; + wbuf[1] = 0x00; + ret = gl861_i2c_msg(d, 0x00, wbuf, 2, NULL, 0); + if (ret < 0) + goto error; + msleep(1); + + /* following msgs should be in the FE's init code? */ + /* cmd sequence to identify the device type? (friio black/white) */ + wbuf[0] = 0x03; + wbuf[1] = 0x80; + /* can't use gl861_i2c_cmd, as the register-addr is 16bit(0x0100) */ + ret = usb_control_msg(d->udev, usb_sndctrlpipe(d->udev, 0), + GL861_REQ_I2C_DATA_CTRL_WRITE, GL861_WRITE, + 0x1200, 0x0100, wbuf, 2, 2000); + if (ret < 0) + goto error; + + msleep(2); + wbuf[0] = 0x00; + wbuf[2] = 0x01; /* reg.0x0100 */ + wbuf[1] = 0x00; + ret = gl861_i2c_msg(d, 0x12 >> 1, wbuf, 3, rbuf, 2); + /* my Friio White returns 0xffff. */ + if (ret < 0 || rbuf[0] != 0xff || rbuf[1] != 0xff) + goto error; + + msleep(2); + wbuf[0] = 0x03; + wbuf[1] = 0x80; + ret = usb_control_msg(d->udev, usb_sndctrlpipe(d->udev, 0), + GL861_REQ_I2C_DATA_CTRL_WRITE, GL861_WRITE, + 0x9000, 0x0100, wbuf, 2, 2000); + if (ret < 0) + goto error; + + msleep(2); + wbuf[0] = 0x00; + wbuf[2] = 0x01; /* reg.0x0100 */ + wbuf[1] = 0x00; + ret = gl861_i2c_msg(d, 0x90 >> 1, wbuf, 3, rbuf, 2); + /* my Friio White returns 0xffff again. */ + if (ret < 0 || rbuf[0] != 0xff || rbuf[1] != 0xff) + goto error; + + msleep(1); + +restart: + /* ============ start DEMOD init cmds ================== */ + /* read PLL status to clear the POR bit */ + wbuf[0] = JDVBT90502_2ND_I2C_REG; + wbuf[1] = (FRIIO_PLL_ADDR << 1) + 1; /* +1 for reading */ + ret = gl861_i2c_msg(d, FRIIO_DEMOD_ADDR, wbuf, 2, NULL, 0); + if (ret < 0) + goto error; + + msleep(5); + /* note: DEMODULATOR has 16bit register-address. */ + wbuf[0] = 0x00; + wbuf[2] = 0x01; /* reg addr: 0x0100 */ + wbuf[1] = 0x00; /* val: not used */ + ret = gl861_i2c_msg(d, FRIIO_DEMOD_ADDR, wbuf, 3, rbuf, 1); + if (ret < 0) + goto error; +/* + msleep(1); + wbuf[0] = 0x80; + wbuf[1] = 0x00; + ret = gl861_i2c_msg(d, FRIIO_DEMOD_ADDR, wbuf, 2, rbuf, 1); + if (ret < 0) + goto error; + */ + if (rbuf[0] & 0x80) { /* still in PowerOnReset state? */ + if (++retry > 3) { + deb_info("failed to get the correct" + " FE demod status:0x%02x\n", rbuf[0]); + goto error; + } + msleep(100); + goto restart; + } + + /* TODO: check return value in rbuf */ + /* =========== end DEMOD init cmds ===================== */ + msleep(1); + + wbuf[0] = 0x30; + wbuf[1] = 0x04; + ret = gl861_i2c_msg(d, 0x00, wbuf, 2, NULL, 0); + if (ret < 0) + goto error; + + msleep(2); + /* following 2 cmds unnecessary? */ + wbuf[0] = 0x00; + wbuf[1] = 0x01; + ret = gl861_i2c_msg(d, 0x00, wbuf, 2, NULL, 0); + if (ret < 0) + goto error; + + wbuf[0] = 0x06; + wbuf[1] = 0x0F; + ret = gl861_i2c_msg(d, 0x00, wbuf, 2, NULL, 0); + if (ret < 0) + goto error; + + /* some streaming ctl cmds (maybe) */ + msleep(10); + for (i = 0; i < cmdlen; i++) { + ret = gl861_i2c_msg(d, 0x00, streaming_init_cmds[i], 2, + NULL, 0); + if (ret < 0) + goto error; + msleep(1); + } + msleep(20); + + /* change the LED color etc. */ + ret = friio_streaming_ctrl(&d->adapter[0], 0); + if (ret < 0) + goto error; + + return 0; + +error: + deb_info("%s:ret == %d\n", __func__, ret); + return -EIO; +} + +/* Callbacks for DVB USB */ + +static int friio_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) +{ + int ret; + + deb_info("%s called.(%d)\n", __func__, onoff); + + /* set the LED color and saturation (and LNB on) */ + if (onoff) + ret = friio_ext_ctl(adap, 0x6400ff64, 1); + else + ret = friio_ext_ctl(adap, 0x96ff00ff, 1); + + if (ret != 1) { + deb_info("%s failed to send cmdx. ret==%d\n", __func__, ret); + return -EREMOTEIO; + } + return 0; +} + +static int friio_frontend_attach(struct dvb_usb_adapter *adap) +{ + if (friio_initialize(adap->dev) < 0) + return -EIO; + + adap->fe = jdvbt90502_attach(adap->dev); + if (adap->fe == NULL) + return -EIO; + + return 0; +} + +/* DVB USB Driver stuff */ +static struct dvb_usb_device_properties friio_properties; + +static int friio_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct dvb_usb_device *d; + struct usb_host_interface *alt; + int ret; + + if (intf->num_altsetting < GL861_ALTSETTING_COUNT) + return -ENODEV; + + alt = usb_altnum_to_altsetting(intf, FRIIO_BULK_ALTSETTING); + if (alt == NULL) { + deb_rc("not alt found!\n"); + return -ENODEV; + } + ret = usb_set_interface(interface_to_usbdev(intf), + alt->desc.bInterfaceNumber, + alt->desc.bAlternateSetting); + if (ret != 0) { + deb_rc("failed to set alt-setting!\n"); + return ret; + } + + ret = dvb_usb_device_init(intf, &friio_properties, + THIS_MODULE, &d, adapter_nr); + if (ret == 0) + friio_streaming_ctrl(&d->adapter[0], 1); + + return ret; +} + + +struct jdvbt90502_config friio_fe_config = { + .demod_address = FRIIO_DEMOD_ADDR, + .pll_address = FRIIO_PLL_ADDR, +}; + +static struct i2c_algorithm gl861_i2c_algo = { + .master_xfer = gl861_i2c_xfer, + .functionality = gl861_i2c_func, +}; + +static struct usb_device_id friio_table[] = { + { USB_DEVICE(USB_VID_774, USB_PID_FRIIO_WHITE) }, + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(usb, friio_table); + + +static struct dvb_usb_device_properties friio_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + .usb_ctrl = DEVICE_SPECIFIC, + + .size_of_priv = 0, + + .num_adapters = 1, + .adapter = { + /* caps:0 => no pid filter, 188B TS packet */ + /* GL861 has a HW pid filter, but no info available. */ + { + .caps = 0, + + .frontend_attach = friio_frontend_attach, + .streaming_ctrl = friio_streaming_ctrl, + + .stream = { + .type = USB_BULK, + /* count <= MAX_NO_URBS_FOR_DATA_STREAM(10) */ + .count = 8, + .endpoint = 0x01, + .u = { + /* GL861 has 6KB buf inside */ + .bulk = { + .buffersize = 16384, + } + } + }, + } + }, + .i2c_algo = &gl861_i2c_algo, + + .num_device_descs = 1, + .devices = { + { + .name = "774 Friio ISDB-T USB2.0", + .cold_ids = { NULL }, + .warm_ids = { &friio_table[0], NULL }, + }, + } +}; + +static struct usb_driver friio_driver = { + .name = "dvb_usb_friio", + .probe = friio_probe, + .disconnect = dvb_usb_device_exit, + .id_table = friio_table, +}; + + +/* module stuff */ +static int __init friio_module_init(void) +{ + int ret; + + ret = usb_register(&friio_driver); + if (ret) + err("usb_register failed. Error number %d", ret); + + return ret; +} + + +static void __exit friio_module_exit(void) +{ + /* deregister this driver from the USB subsystem */ + usb_deregister(&friio_driver); +} + +module_init(friio_module_init); +module_exit(friio_module_exit); + +MODULE_AUTHOR("Akihiro Tsukada "); +MODULE_DESCRIPTION("Driver for Friio ISDB-T USB2.0 Receiver"); +MODULE_VERSION("0.2"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/dvb-usb/friio.h b/drivers/media/dvb/dvb-usb/friio.h new file mode 100644 index 000000000000..af8d55e390fb --- /dev/null +++ b/drivers/media/dvb/dvb-usb/friio.h @@ -0,0 +1,99 @@ +/* DVB USB compliant Linux driver for the Friio USB2.0 ISDB-T receiver. + * + * Copyright (C) 2009 Akihiro Tsukada + * + * This module is based off the the gl861 and vp702x modules. + * + * 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. + * + * see Documentation/dvb/README.dvb-usb for more information + */ +#ifndef _DVB_USB_FRIIO_H_ +#define _DVB_USB_FRIIO_H_ + +/** + * Friio Components + * USB hub: AU4254 + * USB controller(+ TS dmx & streaming): GL861 + * Frontend: comtech JDVBT-90502 + * (tuner PLL: tua6034, I2C addr:(0xC0 >> 1)) + * (OFDM demodulator: TC90502, I2C addr:(0x30 >> 1)) + * LED x3 (+LNB) controll: PIC 16F676 + * EEPROM: 24C08 + * + * (USB smart card reader: AU9522) + * + */ + +#define DVB_USB_LOG_PREFIX "friio" +#include "dvb-usb.h" + +extern int dvb_usb_friio_debug; +#define deb_info(args...) dprintk(dvb_usb_friio_debug, 0x01, args) +#define deb_xfer(args...) dprintk(dvb_usb_friio_debug, 0x02, args) +#define deb_rc(args...) dprintk(dvb_usb_friio_debug, 0x04, args) +#define deb_fe(args...) dprintk(dvb_usb_friio_debug, 0x08, args) + +/* Vendor requests */ +#define GL861_WRITE 0x40 +#define GL861_READ 0xc0 + +/* command bytes */ +#define GL861_REQ_I2C_WRITE 0x01 +#define GL861_REQ_I2C_READ 0x02 +/* For control msg with data argument */ +/* Used for accessing the PLL on the secondary I2C bus of FE via GL861 */ +#define GL861_REQ_I2C_DATA_CTRL_WRITE 0x03 + +#define GL861_ALTSETTING_COUNT 2 +#define FRIIO_BULK_ALTSETTING 0 +#define FRIIO_ISOC_ALTSETTING 1 + +/* LED & LNB control via PIC. */ +/* basically, it's serial control with clock and strobe. */ +/* write the below 4bit control data to the reg 0x00 at the I2C addr 0x00 */ +/* when controlling the LEDs, 32bit(saturation, R, G, B) is sent on the bit3*/ +#define FRIIO_CTL_LNB (1 << 0) +#define FRIIO_CTL_STROBE (1 << 1) +#define FRIIO_CTL_CLK (1 << 2) +#define FRIIO_CTL_LED (1 << 3) + +/* Front End related */ + +#define FRIIO_DEMOD_ADDR (0x30 >> 1) +#define FRIIO_PLL_ADDR (0xC0 >> 1) + +#define JDVBT90502_PLL_CLK 4000000 +#define JDVBT90502_PLL_DIVIDER 28 + +#define JDVBT90502_2ND_I2C_REG 0xFE + +/* byte index for pll i2c command data structure*/ +/* see datasheet for tua6034 */ +#define DEMOD_REDIRECT_REG 0 +#define ADDRESS_BYTE 1 +#define DIVIDER_BYTE1 2 +#define DIVIDER_BYTE2 3 +#define CONTROL_BYTE 4 +#define BANDSWITCH_BYTE 5 +#define AGC_CTRL_BYTE 5 +#define PLL_CMD_LEN 6 + +/* bit masks for PLL STATUS response */ +#define PLL_STATUS_POR_MODE 0x80 /* 1: Power on Reset (test) Mode */ +#define PLL_STATUS_LOCKED 0x40 /* 1: locked */ +#define PLL_STATUS_AGC_ACTIVE 0x08 /* 1:active */ +#define PLL_STATUS_TESTMODE 0x07 /* digital output level (5 level) */ + /* 0.15Vcc step 0x00: < 0.15Vcc, ..., 0x04: >= 0.6Vcc (<= 1Vcc) */ + + +struct jdvbt90502_config { + u8 demod_address; /* i2c addr for demodulator IC */ + u8 pll_address; /* PLL addr on the secondary i2c*/ +}; +extern struct jdvbt90502_config friio_fe_config; + +extern struct dvb_frontend *jdvbt90502_attach(struct dvb_usb_device *d); +#endif -- cgit v1.2.2 From 1601fb14980b1295e00087460d57256a87f334bf Mon Sep 17 00:00:00 2001 From: Roel Kluin Date: Fri, 18 Sep 2009 20:09:52 -0300 Subject: V4L/DVB (13015): kmalloc failure ignored in m920x_firmware_download() Prevent NULL dereference if kmalloc() fails. Signed-off-by: Roel Kluin Signed-off-by: Douglas Schilling Landgraf Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/m920x.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/media/dvb') diff --git a/drivers/media/dvb/dvb-usb/m920x.c b/drivers/media/dvb/dvb-usb/m920x.c index aec7a1943b66..ef9b7bed13ff 100644 --- a/drivers/media/dvb/dvb-usb/m920x.c +++ b/drivers/media/dvb/dvb-usb/m920x.c @@ -337,6 +337,8 @@ static int m920x_firmware_download(struct usb_device *udev, const struct firmwar int i, pass, ret = 0; buff = kmalloc(65536, GFP_KERNEL); + if (buff == NULL) + return -ENOMEM; if ((ret = m920x_read(udev, M9206_FILTER, 0x0, 0x8000, read, 4)) != 0) goto done; -- cgit v1.2.2 From 8d04df4997b530d978d48a66655a99714af197ca Mon Sep 17 00:00:00 2001 From: Roel Kluin Date: Fri, 18 Sep 2009 20:26:53 -0300 Subject: V4L/DVB (13016): kmalloc failure ignored in lgdt3304_attach() and s921_attach() Prevent NULL dereference if kmalloc() fails. Cc: Markus Rechberger Signed-off-by: Roel Kluin Signed-off-by: Douglas Schilling Landgraf Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/lgdt3304.c | 2 ++ drivers/media/dvb/frontends/s921_module.c | 2 ++ 2 files changed, 4 insertions(+) (limited to 'drivers/media/dvb') diff --git a/drivers/media/dvb/frontends/lgdt3304.c b/drivers/media/dvb/frontends/lgdt3304.c index eb72a9866c93..e334b5d4e578 100644 --- a/drivers/media/dvb/frontends/lgdt3304.c +++ b/drivers/media/dvb/frontends/lgdt3304.c @@ -363,6 +363,8 @@ struct dvb_frontend* lgdt3304_attach(const struct lgdt3304_config *config, struct lgdt3304_state *state; state = kzalloc(sizeof(struct lgdt3304_state), GFP_KERNEL); + if (state == NULL) + return NULL; state->addr = config->i2c_address; state->i2c = i2c; diff --git a/drivers/media/dvb/frontends/s921_module.c b/drivers/media/dvb/frontends/s921_module.c index 3f5a0e1dfdf5..3156b64cfc96 100644 --- a/drivers/media/dvb/frontends/s921_module.c +++ b/drivers/media/dvb/frontends/s921_module.c @@ -169,6 +169,8 @@ struct dvb_frontend* s921_attach(const struct s921_config *config, struct s921_state *state; state = kzalloc(sizeof(struct s921_state), GFP_KERNEL); + if (state == NULL) + return NULL; state->addr = config->i2c_address; state->i2c = i2c; -- cgit v1.2.2 From 40d2951758d788a0375d27062caf9cc75de735a9 Mon Sep 17 00:00:00 2001 From: Roel Kluin Date: Fri, 18 Sep 2009 21:03:34 -0300 Subject: V4L/DVB (13018): kzalloc failure ignored in au8522_probe() Prevent NULL dereference if kzalloc() fails. Cc: Devin Heitmueller Signed-off-by: Roel Kluin Signed-off-by: Douglas Schilling Landgraf Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/au8522_decoder.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/media/dvb') diff --git a/drivers/media/dvb/frontends/au8522_decoder.c b/drivers/media/dvb/frontends/au8522_decoder.c index 9e9a75576a1d..74981ee923c8 100644 --- a/drivers/media/dvb/frontends/au8522_decoder.c +++ b/drivers/media/dvb/frontends/au8522_decoder.c @@ -792,6 +792,11 @@ static int au8522_probe(struct i2c_client *client, } demod_config = kzalloc(sizeof(struct au8522_config), GFP_KERNEL); + if (demod_config == NULL) { + if (instance == 1) + kfree(state); + return -ENOMEM; + } demod_config->demod_address = 0x8e >> 1; state->config = demod_config; -- cgit v1.2.2 From 84d6ae431f315e8973aac3c3fe1d550fc9240ef3 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 19 Sep 2009 01:01:26 -0300 Subject: V4L/DVB (13033): pt1: Don't use a deprecated DMA_BIT_MASK macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit drivers/media/dvb/pt1/pt1.c: In function ‘pt1_probe’: drivers/media/dvb/pt1/pt1.c:915: warning: ‘DMA_nnBIT_MASK’ is deprecated Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/pt1/pt1.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media/dvb') diff --git a/drivers/media/dvb/pt1/pt1.c b/drivers/media/dvb/pt1/pt1.c index ef0f7d235e2a..8ffbcecad931 100644 --- a/drivers/media/dvb/pt1/pt1.c +++ b/drivers/media/dvb/pt1/pt1.c @@ -912,7 +912,7 @@ pt1_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (ret < 0) goto err; - ret = pci_set_dma_mask(pdev, DMA_32BIT_MASK); + ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); if (ret < 0) goto err_pci_disable_device; -- cgit v1.2.2