diff options
author | Ingo Molnar <mingo@elte.hu> | 2008-07-31 12:38:04 +0200 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2008-07-31 12:38:04 +0200 |
commit | 5fbf24659b75356e2142e1f1b88f67b34cbc3e75 (patch) | |
tree | 0e470e4cc89b72e3d74939f8c39396a6a0bebb3a /drivers/media/dvb | |
parent | d89961e2dc87b6e30b8e3f60bd2af5cd92cf4643 (diff) | |
parent | 6e86841d05f371b5b9b86ce76c02aaee83352298 (diff) | |
download | op-kernel-dev-5fbf24659b75356e2142e1f1b88f67b34cbc3e75.zip op-kernel-dev-5fbf24659b75356e2142e1f1b88f67b34cbc3e75.tar.gz |
Merge branch 'linus' into x86/xen
Diffstat (limited to 'drivers/media/dvb')
-rw-r--r-- | drivers/media/dvb/bt8xx/Kconfig | 2 | ||||
-rw-r--r-- | drivers/media/dvb/dvb-usb/Kconfig | 10 | ||||
-rw-r--r-- | drivers/media/dvb/dvb-usb/Makefile | 3 | ||||
-rw-r--r-- | drivers/media/dvb/dvb-usb/anysee.c | 2 | ||||
-rw-r--r-- | drivers/media/dvb/dvb-usb/dvb-usb-ids.h | 1 | ||||
-rw-r--r-- | drivers/media/dvb/dvb-usb/dw2102.c | 425 | ||||
-rw-r--r-- | drivers/media/dvb/dvb-usb/dw2102.h | 9 | ||||
-rw-r--r-- | drivers/media/dvb/frontends/Kconfig | 38 | ||||
-rw-r--r-- | drivers/media/dvb/frontends/Makefile | 1 | ||||
-rw-r--r-- | drivers/media/dvb/frontends/drx397xD.c | 1504 | ||||
-rw-r--r-- | drivers/media/dvb/frontends/drx397xD.h | 130 | ||||
-rw-r--r-- | drivers/media/dvb/frontends/drx397xD_fw.h | 40 | ||||
-rw-r--r-- | drivers/media/dvb/frontends/z0194a.h | 97 | ||||
-rw-r--r-- | drivers/media/dvb/siano/smscoreapi.c | 14 | ||||
-rw-r--r-- | drivers/media/dvb/siano/smsdvb.c | 4 | ||||
-rw-r--r-- | drivers/media/dvb/ttpci/Kconfig | 4 | ||||
-rw-r--r-- | drivers/media/dvb/ttusb-dec/Kconfig | 2 |
17 files changed, 2250 insertions, 36 deletions
diff --git a/drivers/media/dvb/bt8xx/Kconfig b/drivers/media/dvb/bt8xx/Kconfig index 7588db1..7e9c090 100644 --- a/drivers/media/dvb/bt8xx/Kconfig +++ b/drivers/media/dvb/bt8xx/Kconfig @@ -1,7 +1,6 @@ config DVB_BT8XX tristate "BT8xx based PCI cards" depends on DVB_CORE && PCI && I2C && VIDEO_BT848 - depends on HOTPLUG # due to FW_LOADER select DVB_MT352 if !DVB_FE_CUSTOMISE select DVB_SP887X if !DVB_FE_CUSTOMISE select DVB_NXT6000 if !DVB_FE_CUSTOMISE @@ -10,7 +9,6 @@ config DVB_BT8XX select DVB_LGDT330X if !DVB_FE_CUSTOMISE select DVB_ZL10353 if !DVB_FE_CUSTOMISE select MEDIA_TUNER_SIMPLE if !DVB_FE_CUSTOMISE - select FW_LOADER help Support for PCI cards based on the Bt8xx PCI bridge. Examples are the Nebula cards, the Pinnacle PCTV cards, the Twinhan DST cards, diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig index a577c0f..e84152b 100644 --- a/drivers/media/dvb/dvb-usb/Kconfig +++ b/drivers/media/dvb/dvb-usb/Kconfig @@ -1,8 +1,6 @@ config DVB_USB tristate "Support for various USB DVB devices" depends on DVB_CORE && USB && I2C && INPUT - depends on HOTPLUG # due to FW_LOADER - select FW_LOADER help By enabling this you will be able to choose the various supported USB1.1 and USB2.0 DVB devices. @@ -246,6 +244,14 @@ config DVB_USB_AF9005_REMOTE Say Y here to support the default remote control decoding for the Afatech AF9005 based receiver. +config DVB_USB_DW2102 + tristate "DvbWorld 2102 DVB-S USB2.0 receiver" + depends on DVB_USB + select DVB_STV0299 if !DVB_FE_CUSTOMISE + select DVB_PLL if !DVB_FE_CUSTOMISE + help + Say Y here to support the DvbWorld 2102 DVB-S USB2.0 receiver. + config DVB_USB_ANYSEE tristate "Anysee DVB-T/C USB2.0 support" depends on DVB_USB diff --git a/drivers/media/dvb/dvb-usb/Makefile b/drivers/media/dvb/dvb-usb/Makefile index 44c11e4..e206f1e 100644 --- a/drivers/media/dvb/dvb-usb/Makefile +++ b/drivers/media/dvb/dvb-usb/Makefile @@ -64,6 +64,9 @@ obj-$(CONFIG_DVB_USB_AF9005_REMOTE) += dvb-usb-af9005-remote.o dvb-usb-anysee-objs = anysee.o obj-$(CONFIG_DVB_USB_ANYSEE) += dvb-usb-anysee.o +dvb-usb-dw2102-objs = dw2102.o +obj-$(CONFIG_DVB_USB_DW2102) += dvb-usb-dw2102.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/anysee.c b/drivers/media/dvb/dvb-usb/anysee.c index adfd4fc..2f408d2 100644 --- a/drivers/media/dvb/dvb-usb/anysee.c +++ b/drivers/media/dvb/dvb-usb/anysee.c @@ -43,7 +43,7 @@ module_param_named(debug, dvb_usb_anysee_debug, int, 0644); MODULE_PARM_DESC(debug, "set debugging level" DVB_USB_DEBUG_STATUS); DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); -struct mutex anysee_usb_mutex; +static struct mutex anysee_usb_mutex; static int anysee_ctrl_msg(struct dvb_usb_device *d, u8 *sbuf, u8 slen, u8 *rbuf, u8 rlen) diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h index e5238b3..029b437 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h +++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h @@ -204,5 +204,6 @@ #define USB_PID_ASUS_U3000 0x171f #define USB_PID_ASUS_U3100 0x173f #define USB_PID_YUAN_EC372S 0x1edc +#define USB_PID_DW2102 0x2102 #endif diff --git a/drivers/media/dvb/dvb-usb/dw2102.c b/drivers/media/dvb/dvb-usb/dw2102.c new file mode 100644 index 0000000..a4d898b --- /dev/null +++ b/drivers/media/dvb/dvb-usb/dw2102.c @@ -0,0 +1,425 @@ +/* DVB USB framework compliant Linux driver for the DVBWorld DVB-S 2102 Card +* +* Copyright (C) 2008 Igor M. Liplianin (liplianin@me.by) +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation, version 2. +* +* see Documentation/dvb/README.dvb-usb for more information +*/ +#include <linux/version.h> +#include "dw2102.h" +#include "stv0299.h" +#include "z0194a.h" + +#ifndef USB_PID_DW2102 +#define USB_PID_DW2102 0x2102 +#endif + +#define DW2102_READ_MSG 0 +#define DW2102_WRITE_MSG 1 + +#define REG_1F_SYMBOLRATE_BYTE0 0x1f +#define REG_20_SYMBOLRATE_BYTE1 0x20 +#define REG_21_SYMBOLRATE_BYTE2 0x21 + +#define DW2102_VOLTAGE_CTRL (0x1800) +#define DW2102_RC_QUERY (0x1a00) + +struct dw2102_state { + u32 last_key_pressed; +}; +struct dw2102_rc_keys { + u32 keycode; + u32 event; +}; + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +static int dw2102_op_rw(struct usb_device *dev, u8 request, u16 value, + u8 *data, u16 len, int flags) +{ + int ret; + u8 u8buf[len]; + + unsigned int pipe = (flags == DW2102_READ_MSG) ? + usb_rcvctrlpipe(dev, 0) : usb_sndctrlpipe(dev, 0); + u8 request_type = (flags == DW2102_READ_MSG) ? USB_DIR_IN : USB_DIR_OUT; + + if (flags == DW2102_WRITE_MSG) + memcpy(u8buf, data, len); + ret = usb_control_msg(dev, pipe, request, + request_type | USB_TYPE_VENDOR, value, 0 , u8buf, len, 2000); + + if (flags == DW2102_READ_MSG) + memcpy(data, u8buf, len); + return ret; +} + +/* I2C */ + +static int dw2102_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], + int num) +{ +struct dvb_usb_device *d = i2c_get_adapdata(adap); + int i = 0, ret = 0; + u8 buf6[] = {0x2c, 0x05, 0xc0, 0, 0, 0, 0}; + u8 request; + u16 value; + + if (!d) + return -ENODEV; + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + switch (num) { + case 2: + /* read stv0299 register */ + request = 0xb5; + value = msg[0].buf[0];/* register */ + for (i = 0; i < msg[1].len; i++) { + value = value + i; + ret = dw2102_op_rw(d->udev, 0xb5, + value, buf6, 2, DW2102_READ_MSG); + msg[1].buf[i] = buf6[0]; + + } + break; + case 1: + switch (msg[0].addr) { + case 0x68: + /* write to stv0299 register */ + buf6[0] = 0x2a; + buf6[1] = msg[0].buf[0]; + buf6[2] = msg[0].buf[1]; + ret = dw2102_op_rw(d->udev, 0xb2, + 0, buf6, 3, DW2102_WRITE_MSG); + break; + case 0x60: + if (msg[0].flags == 0) { + /* write to tuner pll */ + buf6[0] = 0x2c; + buf6[1] = 5; + buf6[2] = 0xc0; + buf6[3] = msg[0].buf[0]; + buf6[4] = msg[0].buf[1]; + buf6[5] = msg[0].buf[2]; + buf6[6] = msg[0].buf[3]; + ret = dw2102_op_rw(d->udev, 0xb2, + 0, buf6, 7, DW2102_WRITE_MSG); + } else { + /* write to tuner pll */ + ret = dw2102_op_rw(d->udev, 0xb5, + 0, buf6, 1, DW2102_READ_MSG); + msg[0].buf[0] = buf6[0]; + } + break; + case (DW2102_RC_QUERY): + ret = dw2102_op_rw(d->udev, 0xb8, + 0, buf6, 2, DW2102_READ_MSG); + msg[0].buf[0] = buf6[0]; + msg[0].buf[1] = buf6[1]; + break; + case (DW2102_VOLTAGE_CTRL): + buf6[0] = 0x30; + buf6[1] = msg[0].buf[0]; + ret = dw2102_op_rw(d->udev, 0xb2, + 0, buf6, 2, DW2102_WRITE_MSG); + break; + } + + break; + } + + mutex_unlock(&d->i2c_mutex); + return num; +} + +static u32 dw2102_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static struct i2c_algorithm dw2102_i2c_algo = { + .master_xfer = dw2102_i2c_transfer, + .functionality = dw2102_i2c_func, +}; + +static int dw2102_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +{ + static u8 command_13v[1] = {0x00}; + static u8 command_18v[1] = {0x01}; + struct i2c_msg msg[] = { + {.addr = DW2102_VOLTAGE_CTRL, .flags = 0, + .buf = command_13v, .len = 1}, + }; + + struct dvb_usb_adapter *udev_adap = + (struct dvb_usb_adapter *)(fe->dvb->priv); + if (voltage == SEC_VOLTAGE_18) + msg[0].buf = command_18v; + i2c_transfer(&udev_adap->dev->i2c_adap, msg, 1); + return 0; +} + +static int dw2102_frontend_attach(struct dvb_usb_adapter *d) +{ + d->fe = dvb_attach(stv0299_attach, &sharp_z0194a_config, + &d->dev->i2c_adap); + if (d->fe != NULL) { + d->fe->ops.set_voltage = dw2102_set_voltage; + info("Attached stv0299!\n"); + return 0; + } + return -EIO; +} + +static int dw2102_tuner_attach(struct dvb_usb_adapter *adap) +{ + dvb_attach(dvb_pll_attach, adap->fe, 0x60, + &adap->dev->i2c_adap, DVB_PLL_OPERA1); + return 0; +} + +static struct dvb_usb_rc_key dw2102_rc_keys[] = { + { 0xf8, 0x0a, KEY_Q }, /*power*/ + { 0xf8, 0x0c, KEY_M }, /*mute*/ + { 0xf8, 0x11, KEY_1 }, + { 0xf8, 0x12, KEY_2 }, + { 0xf8, 0x13, KEY_3 }, + { 0xf8, 0x14, KEY_4 }, + { 0xf8, 0x15, KEY_5 }, + { 0xf8, 0x16, KEY_6 }, + { 0xf8, 0x17, KEY_7 }, + { 0xf8, 0x18, KEY_8 }, + { 0xf8, 0x19, KEY_9 }, + { 0xf8, 0x10, KEY_0 }, + { 0xf8, 0x1c, KEY_PAGEUP }, /*ch+*/ + { 0xf8, 0x0f, KEY_PAGEDOWN }, /*ch-*/ + { 0xf8, 0x1a, KEY_O }, /*vol+*/ + { 0xf8, 0x0e, KEY_Z }, /*vol-*/ + { 0xf8, 0x04, KEY_R }, /*rec*/ + { 0xf8, 0x09, KEY_D }, /*fav*/ + { 0xf8, 0x08, KEY_BACKSPACE }, /*rewind*/ + { 0xf8, 0x07, KEY_A }, /*fast*/ + { 0xf8, 0x0b, KEY_P }, /*pause*/ + { 0xf8, 0x02, KEY_ESC }, /*cancel*/ + { 0xf8, 0x03, KEY_G }, /*tab*/ + { 0xf8, 0x00, KEY_UP }, /*up*/ + { 0xf8, 0x1f, KEY_ENTER }, /*ok*/ + { 0xf8, 0x01, KEY_DOWN }, /*down*/ + { 0xf8, 0x05, KEY_C }, /*cap*/ + { 0xf8, 0x06, KEY_S }, /*stop*/ + { 0xf8, 0x40, KEY_F }, /*full*/ + { 0xf8, 0x1e, KEY_W }, /*tvmode*/ + { 0xf8, 0x1b, KEY_B }, /*recall*/ + +}; + + + +static int dw2102_rc_query(struct dvb_usb_device *d, u32 *event, int *state) +{ + struct dw2102_state *st = d->priv; + u8 key[2]; + struct i2c_msg msg[] = { + {.addr = DW2102_RC_QUERY, .flags = I2C_M_RD, .buf = key, + .len = 2}, + }; + int i; + + *state = REMOTE_NO_KEY_PRESSED; + if (dw2102_i2c_transfer(&d->i2c_adap, msg, 1) == 1) { + for (i = 0; i < ARRAY_SIZE(dw2102_rc_keys); i++) { + if (dw2102_rc_keys[i].data == msg[0].buf[0]) { + *state = REMOTE_KEY_PRESSED; + *event = dw2102_rc_keys[i].event; + st->last_key_pressed = + dw2102_rc_keys[i].event; + break; + } + st->last_key_pressed = 0; + } + } + /* info("key: %x %x\n",key[0],key[1]); */ + return 0; +} + +static struct usb_device_id dw2102_table[] = { + {USB_DEVICE(USB_VID_CYPRESS, USB_PID_DW2102)}, + {USB_DEVICE(USB_VID_CYPRESS, 0x2101)}, + { } +}; + +MODULE_DEVICE_TABLE(usb, dw2102_table); + +static int dw2102_load_firmware(struct usb_device *dev, + const struct firmware *frmwr) +{ + u8 *b, *p; + int ret = 0, i; + u8 reset; + u8 reset16 [] = {0, 0, 0, 0, 0, 0, 0}; + const struct firmware *fw; + const char *filename = "dvb-usb-dw2101.fw"; + switch (dev->descriptor.idProduct) { + case 0x2101: + ret = request_firmware(&fw, filename, &dev->dev); + if (ret != 0) { + err("did not find the firmware file. (%s) " + "Please see linux/Documentation/dvb/ for more details " + "on firmware-problems.", filename); + return ret; + } + break; + case USB_PID_DW2102: + fw = frmwr; + break; + } + info("start downloading DW2102 firmware"); + p = kmalloc(fw->size, GFP_KERNEL); + reset = 1; + /*stop the CPU*/ + dw2102_op_rw(dev, 0xa0, 0x7f92, &reset, 1, DW2102_WRITE_MSG); + dw2102_op_rw(dev, 0xa0, 0xe600, &reset, 1, DW2102_WRITE_MSG); + + if (p != NULL) { + memcpy(p, fw->data, fw->size); + for (i = 0; i < fw->size; i += 0x40) { + b = (u8 *) p + i; + if (dw2102_op_rw + (dev, 0xa0, i, b , 0x40, + DW2102_WRITE_MSG) != 0x40 + ) { + err("error while transferring firmware"); + ret = -EINVAL; + break; + } + } + /* restart the CPU */ + reset = 0; + if (ret || dw2102_op_rw + (dev, 0xa0, 0x7f92, &reset, 1, + DW2102_WRITE_MSG) != 1) { + err("could not restart the USB controller CPU."); + ret = -EINVAL; + } + if (ret || dw2102_op_rw + (dev, 0xa0, 0xe600, &reset, 1, + DW2102_WRITE_MSG) != 1) { + err("could not restart the USB controller CPU."); + ret = -EINVAL; + } + /* init registers */ + switch (dev->descriptor.idProduct) { + case USB_PID_DW2102: + dw2102_op_rw + (dev, 0xbf, 0x0040, &reset, 0, + DW2102_WRITE_MSG); + dw2102_op_rw + (dev, 0xb9, 0x0000, &reset16[0], 2, + DW2102_READ_MSG); + break; + case 0x2101: + dw2102_op_rw + (dev, 0xbc, 0x0030, &reset16[0], 2, + DW2102_READ_MSG); + dw2102_op_rw + (dev, 0xba, 0x0000, &reset16[0], 7, + DW2102_READ_MSG); + dw2102_op_rw + (dev, 0xba, 0x0000, &reset16[0], 7, + DW2102_READ_MSG); + dw2102_op_rw + (dev, 0xb9, 0x0000, &reset16[0], 2, + DW2102_READ_MSG); + break; + } + kfree(p); + } + return ret; +} + +static struct dvb_usb_device_properties dw2102_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + .usb_ctrl = DEVICE_SPECIFIC, + .firmware = "dvb-usb-dw2102.fw", + .size_of_priv = sizeof(struct dw2102_state), + .no_reconnect = 1, + + .i2c_algo = &dw2102_i2c_algo, + .rc_key_map = dw2102_rc_keys, + .rc_key_map_size = ARRAY_SIZE(dw2102_rc_keys), + .rc_interval = 150, + .rc_query = dw2102_rc_query, + + .generic_bulk_ctrl_endpoint = 0x81, + /* parameter for the MPEG2-data transfer */ + .num_adapters = 1, + .download_firmware = dw2102_load_firmware, + .adapter = { + { + .frontend_attach = dw2102_frontend_attach, + .streaming_ctrl = NULL, + .tuner_attach = dw2102_tuner_attach, + .stream = { + .type = USB_BULK, + .count = 8, + .endpoint = 0x82, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, + } + }, + .num_device_descs = 2, + .devices = { + {"DVBWorld DVB-S 2102 USB2.0", + {&dw2102_table[0], NULL}, + {NULL}, + }, + {"DVBWorld DVB-S 2101 USB2.0", + {&dw2102_table[1], NULL}, + {NULL}, + }, + } +}; + +static int dw2102_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return dvb_usb_device_init(intf, &dw2102_properties, + THIS_MODULE, NULL, adapter_nr); +} + +static struct usb_driver dw2102_driver = { + .name = "dw2102", + .probe = dw2102_probe, + .disconnect = dvb_usb_device_exit, + .id_table = dw2102_table, +}; + +static int __init dw2102_module_init(void) +{ + int ret = usb_register(&dw2102_driver); + if (ret) + err("usb_register failed. Error number %d", ret); + + return ret; +} + +static void __exit dw2102_module_exit(void) +{ + usb_deregister(&dw2102_driver); +} + +module_init(dw2102_module_init); +module_exit(dw2102_module_exit); + +MODULE_AUTHOR("Igor M. Liplianin (c) liplianin@me.by"); +MODULE_DESCRIPTION("Driver for DVBWorld DVB-S 2101 2102 USB2.0 device"); +MODULE_VERSION("0.1"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/dvb-usb/dw2102.h b/drivers/media/dvb/dvb-usb/dw2102.h new file mode 100644 index 0000000..7a310f9 --- /dev/null +++ b/drivers/media/dvb/dvb-usb/dw2102.h @@ -0,0 +1,9 @@ +#ifndef _DW2102_H_ +#define _DW2102_H_ + +#define DVB_USB_LOG_PREFIX "dw2102" +#include "dvb-usb.h" + +extern int dvb_usb_dw2102_debug; +#define deb_xfer(args...) dprintk(dvb_usb_dw2102_debug, 0x02, args) +#endif diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig index c20553c..574dffe 100644 --- a/drivers/media/dvb/frontends/Kconfig +++ b/drivers/media/dvb/frontends/Kconfig @@ -97,9 +97,8 @@ comment "DVB-T (terrestrial) frontends" config DVB_SP8870 tristate "Spase sp8870 based" - depends on DVB_CORE && I2C && HOTPLUG + depends on DVB_CORE && I2C default m if DVB_FE_CUSTOMISE - select FW_LOADER help A DVB-T tuner module. Say Y when you want to support this frontend. @@ -110,9 +109,8 @@ config DVB_SP8870 config DVB_SP887X tristate "Spase sp887x based" - depends on DVB_CORE && I2C && HOTPLUG + depends on DVB_CORE && I2C default m if DVB_FE_CUSTOMISE - select FW_LOADER help A DVB-T tuner module. Say Y when you want to support this frontend. @@ -135,6 +133,20 @@ config DVB_CX22702 help A DVB-T tuner module. Say Y when you want to support this frontend. +config DVB_DRX397XD + tristate "Micronas DRX3975D/DRX3977D based" + depends on DVB_CORE && I2C && HOTPLUG + default m if DVB_FE_CUSTOMISE + select FW_LOADER + help + A DVB-T tuner module. Say Y when you want to support this frontend. + + TODO: + This driver needs external firmware. Please use the command + "<kerneldir>/Documentation/dvb/get_dvb_firmware drx397xD" to + download/extract them, and then copy them to /usr/lib/hotplug/firmware + or /lib/firmware (depending on configuration of firmware hotplug). + config DVB_L64781 tristate "LSI L64781" depends on DVB_CORE && I2C @@ -144,9 +156,8 @@ config DVB_L64781 config DVB_TDA1004X tristate "Philips TDA10045H/TDA10046H based" - depends on DVB_CORE && I2C && HOTPLUG + depends on DVB_CORE && I2C default m if DVB_FE_CUSTOMISE - select FW_LOADER help A DVB-T tuner module. Say Y when you want to support this frontend. @@ -211,9 +222,8 @@ config DVB_DIB7000P config DVB_TDA10048 tristate "Philips TDA10048HN based" - depends on DVB_CORE && I2C && HOTPLUG + depends on DVB_CORE && I2C default m if DVB_FE_CUSTOMISE - select FW_LOADER help A DVB-T tuner module. Say Y when you want to support this frontend. @@ -253,9 +263,8 @@ comment "ATSC (North American/Korean Terrestrial/Cable DTV) frontends" config DVB_NXT200X tristate "NxtWave Communications NXT2002/NXT2004 based" - depends on DVB_CORE && I2C && HOTPLUG + depends on DVB_CORE && I2C default m if DVB_FE_CUSTOMISE - select FW_LOADER help An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want to support this frontend. @@ -268,9 +277,8 @@ config DVB_NXT200X config DVB_OR51211 tristate "Oren OR51211 based" - depends on DVB_CORE && I2C && HOTPLUG + depends on DVB_CORE && I2C default m if DVB_FE_CUSTOMISE - select FW_LOADER help An ATSC 8VSB tuner module. Say Y when you want to support this frontend. @@ -281,9 +289,8 @@ config DVB_OR51211 config DVB_OR51132 tristate "Oren OR51132 based" - depends on DVB_CORE && I2C && HOTPLUG + depends on DVB_CORE && I2C default m if DVB_FE_CUSTOMISE - select FW_LOADER help An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want to support this frontend. @@ -297,9 +304,8 @@ config DVB_OR51132 config DVB_BCM3510 tristate "Broadcom BCM3510" - depends on DVB_CORE && I2C && HOTPLUG + depends on DVB_CORE && I2C default m if DVB_FE_CUSTOMISE - select FW_LOADER help An ATSC 8VSB/16VSB and QAM64/256 tuner module. Say Y when you want to support this frontend. diff --git a/drivers/media/dvb/frontends/Makefile b/drivers/media/dvb/frontends/Makefile index a89dc0f..028da55 100644 --- a/drivers/media/dvb/frontends/Makefile +++ b/drivers/media/dvb/frontends/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_DVB_NXT6000) += nxt6000.o obj-$(CONFIG_DVB_MT352) += mt352.o obj-$(CONFIG_DVB_ZL10353) += zl10353.o obj-$(CONFIG_DVB_CX22702) += cx22702.o +obj-$(CONFIG_DVB_DRX397XD) += drx397xD.o obj-$(CONFIG_DVB_TDA10021) += tda10021.o obj-$(CONFIG_DVB_TDA10023) += tda10023.o obj-$(CONFIG_DVB_STV0297) += stv0297.o diff --git a/drivers/media/dvb/frontends/drx397xD.c b/drivers/media/dvb/frontends/drx397xD.c new file mode 100644 index 0000000..3cbed87 --- /dev/null +++ b/drivers/media/dvb/frontends/drx397xD.c @@ -0,0 +1,1504 @@ +/* + * Driver for Micronas drx397xD demodulator + * + * Copyright (C) 2007 Henk Vergonet <Henk.Vergonet@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; If not, see <http://www.gnu.org/licenses/>. + */ + +#define DEBUG /* uncomment if you want debugging output */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/delay.h> +#include <linux/string.h> +#include <linux/firmware.h> +#include <asm/div64.h> + +#include "dvb_frontend.h" +#include "drx397xD.h" + +static const char mod_name[] = "drx397xD"; + +#define MAX_CLOCK_DRIFT 200 /* maximal 200 PPM allowed */ + +#define F_SET_0D0h 1 +#define F_SET_0D4h 2 + +typedef enum fw_ix { +#define _FW_ENTRY(a, b) b +#include "drx397xD_fw.h" +} fw_ix_t; + +/* chip specifics */ +struct drx397xD_state { + struct i2c_adapter *i2c; + struct dvb_frontend frontend; + struct drx397xD_config config; + fw_ix_t chip_rev; + int flags; + u32 bandwidth_parm; /* internal bandwidth conversions */ + u32 f_osc; /* w90: actual osc frequency [Hz] */ +}; + +/******************************************************************************* + * Firmware + ******************************************************************************/ + +static const char *blob_name[] = { +#define _BLOB_ENTRY(a, b) a +#include "drx397xD_fw.h" +}; + +typedef enum blob_ix { +#define _BLOB_ENTRY(a, b) b +#include "drx397xD_fw.h" +} blob_ix_t; + +static struct { + const char *name; + const struct firmware *file; + rwlock_t lock; + int refcnt; + const u8 *data[ARRAY_SIZE(blob_name)]; +} fw[] = { +#define _FW_ENTRY(a, b) { \ + .name = a, \ + .file = 0, \ + .lock = RW_LOCK_UNLOCKED, \ + .refcnt = 0, \ + .data = { } } +#include "drx397xD_fw.h" +}; + +/* use only with writer lock aquired */ +static void _drx_release_fw(struct drx397xD_state *s, fw_ix_t ix) +{ + memset(&fw[ix].data[0], 0, sizeof(fw[0].data)); + if (fw[ix].file) + release_firmware(fw[ix].file); +} + +static void drx_release_fw(struct drx397xD_state *s) +{ + fw_ix_t ix = s->chip_rev; + + pr_debug("%s\n", __FUNCTION__); + + write_lock(&fw[ix].lock); + if (fw[ix].refcnt) { + fw[ix].refcnt--; + if (fw[ix].refcnt == 0) + _drx_release_fw(s, ix); + } + write_unlock(&fw[ix].lock); +} + +static int drx_load_fw(struct drx397xD_state *s, fw_ix_t ix) +{ + const u8 *data; + size_t size, len; + int i = 0, j, rc = -EINVAL; + + pr_debug("%s\n", __FUNCTION__); + + if (ix < 0 || ix >= ARRAY_SIZE(fw)) + return -EINVAL; + s->chip_rev = ix; + + write_lock(&fw[ix].lock); + if (fw[ix].file) { + rc = 0; + goto exit_ok; + } + memset(&fw[ix].data[0], 0, sizeof(fw[0].data)); + + if (request_firmware(&fw[ix].file, fw[ix].name, &s->i2c->dev) != 0) { + printk(KERN_ERR "%s: Firmware \"%s\" not available\n", + mod_name, fw[ix].name); + rc = -ENOENT; + goto exit_err; + } + + if (!fw[ix].file->data || fw[ix].file->size < 10) + goto exit_corrupt; + + data = fw[ix].file->data; + size = fw[ix].file->size; + + if (data[i++] != 2) /* check firmware version */ + goto exit_corrupt; + + do { + switch (data[i++]) { + case 0x00: /* bytecode */ + if (i >= size) + break; + i += data[i]; + case 0x01: /* reset */ + case 0x02: /* sleep */ + i++; + break; + case 0xfe: /* name */ + len = strnlen(&data[i], size - i); + if (i + len + 1 >= size) + goto exit_corrupt; + if (data[i + len + 1] != 0) + goto exit_corrupt; + for (j = 0; j < ARRAY_SIZE(blob_name); j++) { + if (strcmp(blob_name[j], &data[i]) == 0) { + fw[ix].data[j] = &data[i + len + 1]; + pr_debug("Loading %s\n", blob_name[j]); + } + } + i += len + 1; + break; + case 0xff: /* file terminator */ + if (i == size) { + rc = 0; + goto exit_ok; + } + default: + goto exit_corrupt; + } + } while (i < size); + exit_corrupt: + printk(KERN_ERR "%s: Firmware is corrupt\n", mod_name); + exit_err: + _drx_release_fw(s, ix); + fw[ix].refcnt--; + exit_ok: + fw[ix].refcnt++; + write_unlock(&fw[ix].lock); + return rc; +} + +/******************************************************************************* + * i2c bus IO + ******************************************************************************/ + +static int write_fw(struct drx397xD_state *s, blob_ix_t ix) +{ + struct i2c_msg msg = {.addr = s->config.demod_address,.flags = 0 }; + const u8 *data; + int len, rc = 0, i = 0; + + if (ix < 0 || ix >= ARRAY_SIZE(blob_name)) { + pr_debug("%s drx_fw_ix_t out of range\n", __FUNCTION__); + return -EINVAL; + } + pr_debug("%s %s\n", __FUNCTION__, blob_name[ix]); + + read_lock(&fw[s->chip_rev].lock); + data = fw[s->chip_rev].data[ix]; + if (!data) { + rc = -EINVAL; + goto exit_rc; + } + + for (;;) { + switch (data[i++]) { + case 0: /* bytecode */ + len = data[i++]; + msg.len = len; + msg.buf = (__u8 *) &data[i]; + if (i2c_transfer(s->i2c, &msg, 1) != 1) { + rc = -EIO; + goto exit_rc; + } + i += len; + break; + case 1: /* reset */ + case 2: /* sleep */ + i++; + break; + default: + goto exit_rc; + } + } + exit_rc: + read_unlock(&fw[s->chip_rev].lock); + return 0; +} + +/* Function is not endian safe, use the RD16 wrapper below */ +static int _read16(struct drx397xD_state *s, u32 i2c_adr) +{ + int rc; + u8 a[4]; + u16 v; + struct i2c_msg msg[2] = { + { + .addr = s->config.demod_address, + .flags = 0, + .buf = a, + .len = sizeof(a) + } + , { + .addr = s->config.demod_address, + .flags = I2C_M_RD, + .buf = (u8 *) & v, + .len = sizeof(v) + } + }; + + *(u32 *) a = i2c_adr; + + rc = i2c_transfer(s->i2c, msg, 2); + if (rc != 2) + return -EIO; + + return le16_to_cpu(v); +} + +/* Function is not endian safe, use the WR16.. wrappers below */ +static int _write16(struct drx397xD_state *s, u32 i2c_adr, u16 val) +{ + u8 a[6]; + int rc; + struct i2c_msg msg = { + .addr = s->config.demod_address, + .flags = 0, + .buf = a, + .len = sizeof(a) + }; + + *(u32 *) a = i2c_adr; + *(u16 *) & a[4] = val; + + rc = i2c_transfer(s->i2c, &msg, 1); + if (rc != 1) + return -EIO; + return 0; +} + +#define WR16(ss,adr, val) \ + _write16(ss, I2C_ADR_C0(adr), cpu_to_le16(val)) +#define WR16_E0(ss,adr, val) \ + _write16(ss, I2C_ADR_E0(adr), cpu_to_le16(val)) +#define RD16(ss,adr) \ + _read16(ss, I2C_ADR_C0(adr)) + +#define EXIT_RC( cmd ) if ( (rc = (cmd)) < 0) goto exit_rc + +/******************************************************************************* + * Tuner callback + ******************************************************************************/ + +static int PLL_Set(struct drx397xD_state *s, + struct dvb_frontend_parameters *fep, int *df_tuner) +{ + struct dvb_frontend *fe = &s->frontend; + u32 f_tuner, f = fep->frequency; + int rc; + + pr_debug("%s\n", __FUNCTION__); + + if ((f > s->frontend.ops.tuner_ops.info.frequency_max) || + (f < s->frontend.ops.tuner_ops.info.frequency_min)) + return -EINVAL; + + *df_tuner = 0; + if (!s->frontend.ops.tuner_ops.set_params || + !s->frontend.ops.tuner_ops.get_frequency) + return -ENOSYS; + + rc = s->frontend.ops.tuner_ops.set_params(fe, fep); + if (rc < 0) + return rc; + + rc = s->frontend.ops.tuner_ops.get_frequency(fe, &f_tuner); + if (rc < 0) + return rc; + + *df_tuner = f_tuner - f; + pr_debug("%s requested %d [Hz] tuner %d [Hz]\n", __FUNCTION__, f, + f_tuner); + + return 0; +} + +/******************************************************************************* + * Demodulator helper functions + ******************************************************************************/ + +static int SC_WaitForReady(struct drx397xD_state *s) +{ + int cnt = 1000; + int rc; + + pr_debug("%s\n", __FUNCTION__); + + while (cnt--) { + rc = RD16(s, 0x820043); + if (rc == 0) + return 0; + } + return -1; +} + +static int SC_SendCommand(struct drx397xD_state *s, int cmd) +{ + int rc; + + pr_debug("%s\n", __FUNCTION__); + + WR16(s, 0x820043, cmd); + SC_WaitForReady(s); + rc = RD16(s, 0x820042); + if ((rc & 0xffff) == 0xffff) + return -1; + return 0; +} + +static int HI_Command(struct drx397xD_state *s, u16 cmd) +{ + int rc, cnt = 1000; + + pr_debug("%s\n", __FUNCTION__); + + rc = WR16(s, 0x420032, cmd); + if (rc < 0) + return rc; + + do { + rc = RD16(s, 0x420032); + if (rc == 0) { + rc = RD16(s, 0x420031); + return rc; + } + if (rc < 0) + return rc; + } while (--cnt); + return rc; +} + +static int HI_CfgCommand(struct drx397xD_state *s) +{ + + pr_debug("%s\n", __FUNCTION__); + + WR16(s, 0x420033, 0x3973); + WR16(s, 0x420034, s->config.w50); // code 4, log 4 + WR16(s, 0x420035, s->config.w52); // code 15, log 9 + WR16(s, 0x420036, s->config.demod_address << 1); + WR16(s, 0x420037, s->config.w56); // code (set_i2c ?? initX 1 ), log 1 +// WR16(s, 0x420033, 0x3973); + if ((s->config.w56 & 8) == 0) + return HI_Command(s, 3); + return WR16(s, 0x420032, 0x3); +} + +static const u8 fastIncrDecLUT_15273[] = { + 0x0e, 0x0f, 0x0f, 0x10, 0x11, 0x12, 0x12, 0x13, 0x14, + 0x15, 0x16, 0x17, 0x18, 0x1a, 0x1b, 0x1c, 0x1d, 0x1f +}; + +static const u8 slowIncrDecLUT_15272[] = { + 3, 4, 4, 5, 6 +}; + +static int SetCfgIfAgc(struct drx397xD_state *s, struct drx397xD_CfgIfAgc *agc) +{ + u16 w06 = agc->w06; + u16 w08 = agc->w08; + u16 w0A = agc->w0A; + u16 w0C = agc->w0C; + int quot, rem, i, rc = -EINVAL; + + pr_debug("%s\n", __FUNCTION__); + + if (agc->w04 > 0x3ff) + goto exit_rc; + + if (agc->d00 == 1) { + EXIT_RC(RD16(s, 0x0c20010)); + rc &= ~0x10; + EXIT_RC(WR16(s, 0x0c20010, rc)); + return WR16(s, 0x0c20030, agc->w04 & 0x7ff); + } + + if (agc->d00 != 0) + goto exit_rc; + if (w0A < w08) + goto exit_rc; + if (w0A > 0x3ff) + goto exit_rc; + if (w0C > 0x3ff) + goto exit_rc; + if (w06 > 0x3ff) + goto exit_rc; + + EXIT_RC(RD16(s, 0x0c20010)); + rc |= 0x10; + EXIT_RC(WR16(s, 0x0c20010, rc)); + + EXIT_RC(WR16(s, 0x0c20025, (w06 >> 1) & 0x1ff)); + EXIT_RC(WR16(s, 0x0c20031, (w0A - w08) >> 1)); + EXIT_RC(WR16(s, 0x0c20032, ((w0A + w08) >> 1) - 0x1ff)); + + quot = w0C / 113; + rem = w0C % 113; + if (quot <= 8) { + quot = 8 - quot; + } else { + quot = 0; + rem += 113; + } + + EXIT_RC(WR16(s, 0x0c20024, quot)); + + i = fastIncrDecLUT_15273[rem / 8]; + EXIT_RC(WR16(s, 0x0c2002d, i)); + EXIT_RC(WR16(s, 0x0c2002e, i)); + + i = slowIncrDecLUT_15272[rem / 28]; + EXIT_RC(WR16(s, 0x0c2002b, i)); + rc = WR16(s, 0x0c2002c, i); + exit_rc: + return rc; +} + +static int SetCfgRfAgc(struct drx397xD_state *s, struct drx397xD_CfgRfAgc *agc) +{ + u16 w04 = agc->w04; + u16 w06 = agc->w06; + int rc = -1; + + pr_debug("%s %d 0x%x 0x%x\n", __FUNCTION__, agc->d00, w04, w06); + + if (w04 > 0x3ff) + goto exit_rc; + + switch (agc->d00) { + case 1: + if (w04 == 0x3ff) + w04 = 0x400; + + EXIT_RC(WR16(s, 0x0c20036, w04)); + s->config.w9C &= ~2; + EXIT_RC(WR16(s, 0x0c20015, s->config.w9C)); + EXIT_RC(RD16(s, 0x0c20010)); + rc &= 0xbfdf; + EXIT_RC(WR16(s, 0x0c20010, rc)); + EXIT_RC(RD16(s, 0x0c20013)); + rc &= ~2; + break; + case 0: + // loc_8000659 + s->config.w9C &= ~2; + EXIT_RC(WR16(s, 0x0c20015, s->config.w9C)); + EXIT_RC(RD16(s, 0x0c20010)); + rc &= 0xbfdf; + rc |= 0x4000; + EXIT_RC(WR16(s, 0x0c20010, rc)); + EXIT_RC(WR16(s, 0x0c20051, (w06 >> 4) & 0x3f)); + EXIT_RC(RD16(s, 0x0c20013)); + rc &= ~2; + break; + default: + s->config.w9C |= 2; + EXIT_RC(WR16(s, 0x0c20015, s->config.w9C)); + EXIT_RC(RD16(s, 0x0c20010)); + rc &= 0xbfdf; + EXIT_RC(WR16(s, 0x0c20010, rc)); + + EXIT_RC(WR16(s, 0x0c20036, 0)); + + EXIT_RC(RD16(s, 0x0c20013)); + rc |= 2; + } + rc = WR16(s, 0x0c20013, rc); + exit_rc: + return rc; +} + +static int GetLockStatus(struct drx397xD_state *s, int *lockstat) +{ + int rc; + + *lockstat = 0; + + rc = RD16(s, 0x082004b); + if (rc < 0) + return rc; + + if (s->config.d60 != 2) + return 0; + + if ((rc & 7) == 7) + *lockstat |= 1; + if ((rc & 3) == 3) + *lockstat |= 2; + if (rc & 1) + *lockstat |= 4; + return 0; +} + +static int CorrectSysClockDeviation(struct drx397xD_state *s) +{ + int rc = -EINVAL; + int lockstat; + u32 clk, clk_limit; + + pr_debug("%s\n", __FUNCTION__); + + if (s->config.d5C == 0) { + EXIT_RC(WR16(s, 0x08200e8, 0x010)); + EXIT_RC(WR16(s, 0x08200e9, 0x113)); + s->config.d5C = 1; + return rc; + } + if (s->config.d5C != 1) + goto exit_rc; + + rc = RD16(s, 0x0820048); + + rc = GetLockStatus(s, &lockstat); + if (rc < 0) + goto exit_rc; + if ((lockstat & 1) == 0) + goto exit_rc; + + EXIT_RC(WR16(s, 0x0420033, 0x200)); + EXIT_RC(WR16(s, 0x0420034, 0xc5)); + EXIT_RC(WR16(s, 0x0420035, 0x10)); + EXIT_RC(WR16(s, 0x0420036, 0x1)); + EXIT_RC(WR16(s, 0x0420037, 0xa)); + EXIT_RC(HI_Command(s, 6)); + EXIT_RC(RD16(s, 0x0420040)); + clk = rc; + EXIT_RC(RD16(s, 0x0420041)); + clk |= rc << 16; + + if (clk <= 0x26ffff) + goto exit_rc; + if (clk > 0x610000) + goto exit_rc; + + if (!s->bandwidth_parm) + return -EINVAL; + + /* round & convert to Hz */ + clk = ((u64) (clk + 0x800000) * s->bandwidth_parm + (1 << 20)) >> 21; + clk_limit = s->config.f_osc * MAX_CLOCK_DRIFT / 1000; + + if (clk - s->config.f_osc * 1000 + clk_limit <= 2 * clk_limit) { + s->f_osc = clk; + pr_debug("%s: osc %d %d [Hz]\n", __FUNCTION__, + s->config.f_osc * 1000, clk - s->config.f_osc * 1000); + } + rc = WR16(s, 0x08200e8, 0); + exit_rc: + return rc; +} + +static int ConfigureMPEGOutput(struct drx397xD_state *s, int type) +{ + int rc, si, bp; + + pr_debug("%s\n", __FUNCTION__); + + si = s->config.wA0; + if (s->config.w98 == 0) { + si |= 1; + bp = 0; + } else { + si &= ~1; + bp = 0x200; + } + if (s->config.w9A == 0) { + si |= 0x80; + } else { + si &= ~0x80; + } + + EXIT_RC(WR16(s, 0x2150045, 0)); + EXIT_RC(WR16(s, 0x2150010, si)); + EXIT_RC(WR16(s, 0x2150011, bp)); + rc = WR16(s, 0x2150012, (type == 0 ? 0xfff : 0)); + exit_rc: + return rc; +} + +static int drx_tune(struct drx397xD_state *s, + struct dvb_frontend_parameters *fep) +{ + u16 v22 = 0; + u16 v1C = 0; + u16 v1A = 0; + u16 v18 = 0; + u32 edi = 0, ebx = 0, ebp = 0, edx = 0; + u16 v20 = 0, v1E = 0, v16 = 0, v14 = 0, v12 = 0, v10 = 0, v0E = 0; + + int rc, df_tuner; + int a, b, c, d; + pr_debug("%s %d\n", __FUNCTION__, s->config.d60); + + if (s->config.d60 != 2) + goto set_tuner; + rc = CorrectSysClockDeviation(s); + if (rc < 0) + goto set_tuner; + + s->config.d60 = 1; + rc = ConfigureMPEGOutput(s, 0); + if (rc < 0) + goto set_tuner; + set_tuner: + + rc = PLL_Set(s, fep, &df_tuner); + if (rc < 0) { + printk(KERN_ERR "Error in pll_set\n"); + goto exit_rc; + } + msleep(200); + + a = rc = RD16(s, 0x2150016); + if (rc < 0) + goto exit_rc; + b = rc = RD16(s, 0x2150010); + if (rc < 0) + goto exit_rc; + c = rc = RD16(s, 0x2150034); + if (rc < 0) + goto exit_rc; + d = rc = RD16(s, 0x2150035); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x2150014, c); + rc = WR16(s, 0x2150015, d); + rc = WR16(s, 0x2150010, 0); + rc = WR16(s, 0x2150000, 2); + rc = WR16(s, 0x2150036, 0x0fff); + rc = WR16(s, 0x2150016, a); + + rc = WR16(s, 0x2150010, 2); + rc = WR16(s, 0x2150007, 0); + rc = WR16(s, 0x2150000, 1); + rc = WR16(s, 0x2110000, 0); + rc = WR16(s, 0x0800000, 0); + rc = WR16(s, 0x2800000, 0); + rc = WR16(s, 0x2110010, 0x664); + + rc = write_fw(s, DRXD_ResetECRAM); + rc = WR16(s, 0x2110000, 1); + + rc = write_fw(s, DRXD_InitSC); + if (rc < 0) + goto exit_rc; + + rc = SetCfgIfAgc(s, &s->config.ifagc); + if (rc < 0) + goto exit_rc; + + rc = SetCfgRfAgc(s, &s->config.rfagc); + if (rc < 0) + goto exit_rc; + + if (fep->u.ofdm.transmission_mode != TRANSMISSION_MODE_2K) + v22 = 1; + switch (fep->u.ofdm.transmission_mode) { + case TRANSMISSION_MODE_8K: + edi = 1; + if (s->chip_rev == DRXD_FW_B1) + break; + + rc = WR16(s, 0x2010010, 0); + if (rc < 0) + break; + v1C = 0x63; + v1A = 0x53; + v18 = 0x43; + break; + default: + edi = 0; + if (s->chip_rev == DRXD_FW_B1) + break; + + rc = WR16(s, 0x2010010, 1); + if (rc < 0) + break; + + v1C = 0x61; + v1A = 0x47; + v18 = 0x41; + } + + switch (fep->u.ofdm.guard_interval) { + case GUARD_INTERVAL_1_4: + edi |= 0x0c; + break; + case GUARD_INTERVAL_1_8: + edi |= 0x08; + break; + case GUARD_INTERVAL_1_16: + edi |= 0x04; + break; + case GUARD_INTERVAL_1_32: + break; + default: + v22 |= 2; + } + + ebx = 0; + ebp = 0; + v20 = 0; + v1E = 0; + v16 = 0; + v14 = 0; + v12 = 0; + v10 = 0; + v0E = 0; + + switch (fep->u.ofdm.hierarchy_information) { + case HIERARCHY_1: + edi |= 0x40; + if (s->chip_rev == DRXD_FW_B1) + break; + rc = WR16(s, 0x1c10047, 1); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x2010012, 1); + if (rc < 0) + goto exit_rc; + ebx = 0x19f; + ebp = 0x1fb; + v20 = 0x0c0; + v1E = 0x195; + v16 = 0x1d6; + v14 = 0x1ef; + v12 = 4; + v10 = 5; + v0E = 5; + break; + case HIERARCHY_2: + edi |= 0x80; + if (s->chip_rev == DRXD_FW_B1) + break; + rc = WR16(s, 0x1c10047, 2); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x2010012, 2); + if (rc < 0) + goto exit_rc; + ebx = 0x08f; + ebp = 0x12f; + v20 = 0x0c0; + v1E = 0x11e; + v16 = 0x1d6; + v14 = 0x15e; + v12 = 4; + v10 = 5; + v0E = 5; + break; + case HIERARCHY_4: + edi |= 0xc0; + if (s->chip_rev == DRXD_FW_B1) + break; + rc = WR16(s, 0x1c10047, 3); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x2010012, 3); + if (rc < 0) + goto exit_rc; + ebx = 0x14d; + ebp = 0x197; + v20 = 0x0c0; + v1E = 0x1ce; + v16 = 0x1d6; + v14 = 0x11a; + v12 = 4; + v10 = 6; + v0E = 5; + break; + default: + v22 |= 8; + if (s->chip_rev == DRXD_FW_B1) + break; + rc = WR16(s, 0x1c10047, 0); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x2010012, 0); + if (rc < 0) + goto exit_rc; + // QPSK QAM16 QAM64 + ebx = 0x19f; // 62 + ebp = 0x1fb; // 15 + v20 = 0x16a; // 62 + v1E = 0x195; // 62 + v16 = 0x1bb; // 15 + v14 = 0x1ef; // 15 + v12 = 5; // 16 + v10 = 5; // 16 + v0E = 5; // 16 + } + + switch (fep->u.ofdm.constellation) { + default: + v22 |= 4; + case QPSK: + if (s->chip_rev == DRXD_FW_B1) + break; + + rc = WR16(s, 0x1c10046, 0); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x2010011, 0); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x201001a, 0x10); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x201001b, 0); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x201001c, 0); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x1c10062, v20); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x1c1002a, v1C); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x1c10015, v16); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x1c10016, v12); + if (rc < 0) + goto exit_rc; + break; + case QAM_16: + edi |= 0x10; + if (s->chip_rev == DRXD_FW_B1) + break; + + rc = WR16(s, 0x1c10046, 1); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x2010011, 1); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x201001a, 0x10); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x201001b, 4); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x201001c, 0); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x1c10062, v1E); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x1c1002a, v1A); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x1c10015, v14); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x1c10016, v10); + if (rc < 0) + goto exit_rc; + break; + case QAM_64: + edi |= 0x20; + rc = WR16(s, 0x1c10046, 2); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x2010011, 2); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x201001a, 0x20); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x201001b, 8); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x201001c, 2); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x1c10062, ebx); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x1c1002a, v18); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x1c10015, ebp); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x1c10016, v0E); + if (rc < 0) + goto exit_rc; + break; + } + + if (s->config.s20d24 == 1) { + rc = WR16(s, 0x2010013, 0); + } else { + rc = WR16(s, 0x2010013, 1); + edi |= 0x1000; + } + + switch (fep->u.ofdm.code_rate_HP) { + default: + v22 |= 0x10; + case FEC_1_2: + if (s->chip_rev == DRXD_FW_B1) + break; + rc = WR16(s, 0x2090011, 0); + break; + case FEC_2_3: + edi |= 0x200; + if (s->chip_rev == DRXD_FW_B1) + break; + rc = WR16(s, 0x2090011, 1); + break; + case FEC_3_4: + edi |= 0x400; + if (s->chip_rev == DRXD_FW_B1) + break; + rc = WR16(s, 0x2090011, 2); + break; + case FEC_5_6: /* 5 */ + edi |= 0x600; + if (s->chip_rev == DRXD_FW_B1) + break; + rc = WR16(s, 0x2090011, 3); + break; + case FEC_7_8: /* 7 */ + edi |= 0x800; + if (s->chip_rev == DRXD_FW_B1) + break; + rc = WR16(s, 0x2090011, 4); + break; + }; + if (rc < 0) + goto exit_rc; + + switch (fep->u.ofdm.bandwidth) { + default: + rc = -EINVAL; + goto exit_rc; + case BANDWIDTH_8_MHZ: /* 0 */ + case BANDWIDTH_AUTO: + rc = WR16(s, 0x0c2003f, 0x32); + s->bandwidth_parm = ebx = 0x8b8249; // 9142857 + edx = 0; + break; + case BANDWIDTH_7_MHZ: + rc = WR16(s, 0x0c2003f, 0x3b); + s->bandwidth_parm = ebx = 0x7a1200; // 8000000 + edx = 0x4807; + break; + case BANDWIDTH_6_MHZ: + rc = WR16(s, 0x0c2003f, 0x47); + s->bandwidth_parm = ebx = 0x68a1b6; // 6857142 + edx = 0x0f07; + break; + }; + + if (rc < 0) + goto exit_rc; + + rc = WR16(s, 0x08200ec, edx); + if (rc < 0) + goto exit_rc; + + rc = RD16(s, 0x0820050); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x0820050, rc); + + { + /* Configure bandwidth specific factor */ + ebx = div64_u64(((u64) (s->f_osc) << 21) + (ebx >> 1), + (u64)ebx) - 0x800000; + EXIT_RC(WR16(s, 0x0c50010, ebx & 0xffff)); + EXIT_RC(WR16(s, 0x0c50011, ebx >> 16)); + + /* drx397xD oscillator calibration */ + ebx = div64_u64(((u64) (s->config.f_if + df_tuner) << 28) + + (s->f_osc >> 1), (u64)s->f_osc); + } + ebx &= 0xfffffff; + if (fep->inversion == INVERSION_ON) + ebx = 0x10000000 - ebx; + + EXIT_RC(WR16(s, 0x0c30010, ebx & 0xffff)); + EXIT_RC(WR16(s, 0x0c30011, ebx >> 16)); + + EXIT_RC(WR16(s, 0x0800000, 1)); + EXIT_RC(RD16(s, 0x0800000)); + + + EXIT_RC(SC_WaitForReady(s)); + EXIT_RC(WR16(s, 0x0820042, 0)); + EXIT_RC(WR16(s, 0x0820041, v22)); + EXIT_RC(WR16(s, 0x0820040, edi)); + EXIT_RC(SC_SendCommand(s, 3)); + + rc = RD16(s, 0x0800000); + + SC_WaitForReady(s); + WR16(s, 0x0820042, 0); + WR16(s, 0x0820041, 1); + WR16(s, 0x0820040, 1); + SC_SendCommand(s, 1); + +// rc = WR16(s, 0x2150000, 1); +// if (rc < 0) goto exit_rc; + + rc = WR16(s, 0x2150000, 2); + rc = WR16(s, 0x2150016, a); + rc = WR16(s, 0x2150010, 4); + rc = WR16(s, 0x2150036, 0); + rc = WR16(s, 0x2150000, 1); + s->config.d60 = 2; + exit_rc: + return rc; +} + +/******************************************************************************* + * DVB interface + ******************************************************************************/ + +static int drx397x_init(struct dvb_frontend *fe) +{ + struct drx397xD_state *s = fe->demodulator_priv; + int rc; + + pr_debug("%s\n", __FUNCTION__); + + s->config.rfagc.d00 = 2; /* 0x7c */ + s->config.rfagc.w04 = 0; + s->config.rfagc.w06 = 0x3ff; + + s->config.ifagc.d00 = 0; /* 0x68 */ + s->config.ifagc.w04 = 0; + s->config.ifagc.w06 = 140; + s->config.ifagc.w08 = 0; + s->config.ifagc.w0A = 0x3ff; + s->config.ifagc.w0C = 0x388; + + /* for signal strenght calculations */ + s->config.ss76 = 820; + s->config.ss78 = 2200; + s->config.ss7A = 150; + + /* HI_CfgCommand */ + s->config.w50 = 4; + s->config.w52 = 9; // 0xf; + + s->config.f_if = 42800000; /* d14: intermediate frequency [Hz] */ + s->config.f_osc = 48000; /* s66 : oscillator frequency [kHz] */ + s->config.w92 = 12000; // 20000; + + s->config.w9C = 0x000e; + s->config.w9E = 0x0000; + + /* ConfigureMPEGOutput params */ + s->config.wA0 = 4; + s->config.w98 = 1; // 0; + s->config.w9A = 1; + + /* get chip revision */ + rc = RD16(s, 0x2410019); + if (rc < 0) + return -ENODEV; + + if (rc == 0) { + printk(KERN_INFO "%s: chip revision A2\n", mod_name); + rc = drx_load_fw(s, DRXD_FW_A2); + } else { + + rc = (rc >> 12) - 3; + switch (rc) { + case 1: + s->flags |= F_SET_0D4h; + case 0: + case 4: + s->flags |= F_SET_0D0h; + break; + case 2: + case 5: + break; + case 3: + s->flags |= F_SET_0D4h; + break; + default: + return -ENODEV; + }; + printk(KERN_INFO "%s: chip revision B1.%d\n", mod_name, rc); + rc = drx_load_fw(s, DRXD_FW_B1); + } + if (rc < 0) + goto error; + + rc = WR16(s, 0x0420033, 0x3973); + if (rc < 0) + goto error; + + rc = HI_Command(s, 2); + + msleep(1); + + if (s->chip_rev == DRXD_FW_A2) { + rc = WR16(s, 0x043012d, 0x47F); + if (rc < 0) + goto error; + } + rc = WR16_E0(s, 0x0400000, 0); + if (rc < 0) + goto error; + + if (s->config.w92 > 20000 || s->config.w92 % 4000) { + printk(KERN_ERR "%s: invalid osc frequency\n", mod_name); + rc = -1; + goto error; + } + + rc = WR16(s, 0x2410010, 1); + if (rc < 0) + goto error; + rc = WR16(s, 0x2410011, 0x15); + if (rc < 0) + goto error; + rc = WR16(s, 0x2410012, s->config.w92 / 4000); + if (rc < 0) + goto error; +#ifdef ORIG_FW + rc = WR16(s, 0x2410015, 2); + if (rc < 0) + goto error; +#endif + rc = WR16(s, 0x2410017, 0x3973); + if (rc < 0) + goto error; + + s->f_osc = s->config.f_osc * 1000; /* initial estimator */ + + s->config.w56 = 1; + + rc = HI_CfgCommand(s); + if (rc < 0) + goto error; + + rc = write_fw(s, DRXD_InitAtomicRead); + if (rc < 0) + goto error; + + if (s->chip_rev == DRXD_FW_A2) { + rc = WR16(s, 0x2150013, 0); + if (rc < 0) + goto error; + } + + rc = WR16_E0(s, 0x0400002, 0); + if (rc < 0) + goto error; + rc = WR16(s, 0x0400002, 0); + if (rc < 0) + goto error; + + if (s->chip_rev == DRXD_FW_A2) { + rc = write_fw(s, DRXD_ResetCEFR); + if (rc < 0) + goto error; + } + rc = write_fw(s, DRXD_microcode); + if (rc < 0) + goto error; + + s->config.w9C = 0x0e; + if (s->flags & F_SET_0D0h) { + s->config.w9C = 0; + rc = RD16(s, 0x0c20010); + if (rc < 0) + goto write_DRXD_InitFE_1; + + rc &= ~0x1000; + rc = WR16(s, 0x0c20010, rc); + if (rc < 0) + goto write_DRXD_InitFE_1; + + rc = RD16(s, 0x0c20011); + if (rc < 0) + goto write_DRXD_InitFE_1; + + rc &= ~0x8; + rc = WR16(s, 0x0c20011, rc); + if (rc < 0) + goto write_DRXD_InitFE_1; + + rc = WR16(s, 0x0c20012, 1); + } + + write_DRXD_InitFE_1: + + rc = write_fw(s, DRXD_InitFE_1); + if (rc < 0) + goto error; + + rc = 1; + if (s->chip_rev == DRXD_FW_B1) { + if (s->flags & F_SET_0D0h) + rc = 0; + } else { + if (s->flags & F_SET_0D0h) + rc = 4; + } + + rc = WR16(s, 0x0C20012, rc); + if (rc < 0) + goto error; + + rc = WR16(s, 0x0C20013, s->config.w9E); + if (rc < 0) + goto error; + rc = WR16(s, 0x0C20015, s->config.w9C); + if (rc < 0) + goto error; + + rc = write_fw(s, DRXD_InitFE_2); + if (rc < 0) + goto error; + rc = write_fw(s, DRXD_InitFT); + if (rc < 0) + goto error; + rc = write_fw(s, DRXD_InitCP); + if (rc < 0) + goto error; + rc = write_fw(s, DRXD_InitCE); + if (rc < 0) + goto error; + rc = write_fw(s, DRXD_InitEQ); + if (rc < 0) + goto error; + rc = write_fw(s, DRXD_InitEC); + if (rc < 0) + goto error; + rc = write_fw(s, DRXD_InitSC); + if (rc < 0) + goto error; + + rc = SetCfgIfAgc(s, &s->config.ifagc); + if (rc < 0) + goto error; + + rc = SetCfgRfAgc(s, &s->config.rfagc); + if (rc < 0) + goto error; + + rc = ConfigureMPEGOutput(s, 1); + rc = WR16(s, 0x08201fe, 0x0017); + rc = WR16(s, 0x08201ff, 0x0101); + + s->config.d5C = 0; + s->config.d60 = 1; + s->config.d48 = 1; + error: + return rc; +} + +static int drx397x_get_frontend(struct dvb_frontend *fe, + struct dvb_frontend_parameters *params) +{ + return 0; +} + +static int drx397x_set_frontend(struct dvb_frontend *fe, + struct dvb_frontend_parameters *params) +{ + struct drx397xD_state *s = fe->demodulator_priv; + + s->config.s20d24 = 1; // 0; + return drx_tune(s, params); +} + +static int drx397x_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings + *fe_tune_settings) +{ + fe_tune_settings->min_delay_ms = 10000; + fe_tune_settings->step_size = 0; + fe_tune_settings->max_drift = 0; + return 0; +} + +static int drx397x_read_status(struct dvb_frontend *fe, fe_status_t * status) +{ + struct drx397xD_state *s = fe->demodulator_priv; + int lockstat; + + GetLockStatus(s, &lockstat); + /* TODO */ +// if (lockstat & 1) +// CorrectSysClockDeviation(s); + + *status = 0; + if (lockstat & 2) { + CorrectSysClockDeviation(s); + ConfigureMPEGOutput(s, 1); + *status = FE_HAS_LOCK | FE_HAS_SYNC | FE_HAS_VITERBI; + } + if (lockstat & 4) { + *status |= FE_HAS_CARRIER | FE_HAS_SIGNAL; + } + + return 0; +} + +static int drx397x_read_ber(struct dvb_frontend *fe, unsigned int *ber) +{ + *ber = 0; + return 0; +} + +static int drx397x_read_snr(struct dvb_frontend *fe, u16 * snr) +{ + *snr = 0; + return 0; +} + +static int drx397x_read_signal_strength(struct dvb_frontend *fe, u16 * strength) +{ + struct drx397xD_state *s = fe->demodulator_priv; + int rc; + + if (s->config.ifagc.d00 == 2) { + *strength = 0xffff; + return 0; + } + rc = RD16(s, 0x0c20035); + if (rc < 0) { + *strength = 0; + return 0; + } + rc &= 0x3ff; + /* Signal strength is calculated using the following formula: + * + * a = 2200 * 150 / (2200 + 150); + * a = a * 3300 / (a + 820); + * b = 2200 * 3300 / (2200 + 820); + * c = (((b-a) * rc) >> 10 + a) << 4; + * strength = ~c & 0xffff; + * + * The following does the same but with less rounding errors: + */ + *strength = ~(7720 + (rc * 30744 >> 10)); + return 0; +} + +static int drx397x_read_ucblocks(struct dvb_frontend *fe, + unsigned int *ucblocks) +{ + *ucblocks = 0; + return 0; +} + +static int drx397x_sleep(struct dvb_frontend *fe) +{ + return 0; +} + +static void drx397x_release(struct dvb_frontend *fe) +{ + struct drx397xD_state *s = fe->demodulator_priv; + printk(KERN_INFO "%s: release demodulator\n", mod_name); + if (s) { + drx_release_fw(s); + kfree(s); + } + +} + +static struct dvb_frontend_ops drx397x_ops = { + + .info = { + .name = "Micronas DRX397xD DVB-T Frontend", + .type = FE_OFDM, + .frequency_min = 47125000, + .frequency_max = 855250000, + .frequency_stepsize = 166667, + .frequency_tolerance = 0, + .caps = /* 0x0C01B2EAE */ + FE_CAN_FEC_1_2 | // = 0x2, + FE_CAN_FEC_2_3 | // = 0x4, + FE_CAN_FEC_3_4 | // = 0x8, + FE_CAN_FEC_5_6 | // = 0x20, + FE_CAN_FEC_7_8 | // = 0x80, + FE_CAN_FEC_AUTO | // = 0x200, + FE_CAN_QPSK | // = 0x400, + FE_CAN_QAM_16 | // = 0x800, + FE_CAN_QAM_64 | // = 0x2000, + FE_CAN_QAM_AUTO | // = 0x10000, + FE_CAN_TRANSMISSION_MODE_AUTO | // = 0x20000, + FE_CAN_GUARD_INTERVAL_AUTO | // = 0x80000, + FE_CAN_HIERARCHY_AUTO | // = 0x100000, + FE_CAN_RECOVER | // = 0x40000000, + FE_CAN_MUTE_TS // = 0x80000000 + }, + + .release = drx397x_release, + .init = drx397x_init, + .sleep = drx397x_sleep, + + .set_frontend = drx397x_set_frontend, + .get_tune_settings = drx397x_get_tune_settings, + .get_frontend = drx397x_get_frontend, + + .read_status = drx397x_read_status, + .read_snr = drx397x_read_snr, + .read_signal_strength = drx397x_read_signal_strength, + .read_ber = drx397x_read_ber, + .read_ucblocks = drx397x_read_ucblocks, +}; + +struct dvb_frontend *drx397xD_attach(const struct drx397xD_config *config, + struct i2c_adapter *i2c) +{ + struct drx397xD_state *s = NULL; + + /* allocate memory for the internal state */ + s = kzalloc(sizeof(struct drx397xD_state), GFP_KERNEL); + if (s == NULL) + goto error; + + /* setup the state */ + s->i2c = i2c; + memcpy(&s->config, config, sizeof(struct drx397xD_config)); + + /* check if the demod is there */ + if (RD16(s, 0x2410019) < 0) + goto error; + + /* create dvb_frontend */ + memcpy(&s->frontend.ops, &drx397x_ops, sizeof(struct dvb_frontend_ops)); + s->frontend.demodulator_priv = s; + + return &s->frontend; + error: + kfree(s); + return NULL; +} + +MODULE_DESCRIPTION("Micronas DRX397xD DVB-T Frontend"); +MODULE_AUTHOR("Henk Vergonet"); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL(drx397xD_attach); diff --git a/drivers/media/dvb/frontends/drx397xD.h b/drivers/media/dvb/frontends/drx397xD.h new file mode 100644 index 0000000..ddc7a07 --- /dev/null +++ b/drivers/media/dvb/frontends/drx397xD.h @@ -0,0 +1,130 @@ +/* + * Driver for Micronas DVB-T drx397xD demodulator + * + * Copyright (C) 2007 Henk vergonet <Henk.Vergonet@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.= + */ + +#ifndef _DRX397XD_H_INCLUDED +#define _DRX397XD_H_INCLUDED + +#include <linux/dvb/frontend.h> + +#define DRX_F_STEPSIZE 166667 +#define DRX_F_OFFSET 36000000 + +#define I2C_ADR_C0(x) \ +( (u32)cpu_to_le32( \ + (u32)( \ + (((u32)(x) & (u32)0x000000ffUL) ) | \ + (((u32)(x) & (u32)0x0000ff00UL) << 16) | \ + (((u32)(x) & (u32)0x0fff0000UL) >> 8) | \ + ( (u32)0x00c00000UL) \ + )) \ +) + +#define I2C_ADR_E0(x) \ +( (u32)cpu_to_le32( \ + (u32)( \ + (((u32)(x) & (u32)0x000000ffUL) ) | \ + (((u32)(x) & (u32)0x0000ff00UL) << 16) | \ + (((u32)(x) & (u32)0x0fff0000UL) >> 8) | \ + ( (u32)0x00e00000UL) \ + )) \ +) + +struct drx397xD_CfgRfAgc /* 0x7c */ +{ + int d00; /* 2 */ + u16 w04; + u16 w06; +}; + +struct drx397xD_CfgIfAgc /* 0x68 */ +{ + int d00; /* 0 */ + u16 w04; /* 0 */ + u16 w06; + u16 w08; + u16 w0A; + u16 w0C; +}; + +struct drx397xD_s20 { + int d04; + u32 d18; + u32 d1C; + u32 d20; + u32 d14; + u32 d24; + u32 d0C; + u32 d08; +}; + +struct drx397xD_config +{ + /* demodulator's I2C address */ + u8 demod_address; /* 0x0f */ + + struct drx397xD_CfgIfAgc ifagc; /* 0x68 */ + struct drx397xD_CfgRfAgc rfagc; /* 0x7c */ + u32 s20d24; + + /* HI_CfgCommand parameters */ + u16 w50, w52, /* w54, */ w56; + + int d5C; + int d60; + int d48; + int d28; + + u32 f_if; /* d14: intermediate frequency [Hz] */ + /* 36000000 on Cinergy 2400i DT */ + /* 42800000 on Pinnacle Hybrid PRO 330e */ + + u16 f_osc; /* s66: 48000 oscillator frequency [kHz] */ + + u16 w92; /* 20000 */ + + u16 wA0; + u16 w98; + u16 w9A; + + u16 w9C; /* 0xe0 */ + u16 w9E; /* 0x00 */ + + /* used for signal strength calculations in + drx397x_read_signal_strength + */ + u16 ss78; // 2200 + u16 ss7A; // 150 + u16 ss76; // 820 +}; + +#if defined(CONFIG_DVB_DRX397XD) || (defined(CONFIG_DVB_DRX397XD_MODULE) && defined(MODULE)) +extern struct dvb_frontend* drx397xD_attach(const struct drx397xD_config *config, + struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend* drx397xD_attach(const struct drx397xD_config *config, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__); + return NULL; +} +#endif /* CONFIG_DVB_DRX397XD */ + +#endif /* _DRX397XD_H_INCLUDED */ diff --git a/drivers/media/dvb/frontends/drx397xD_fw.h b/drivers/media/dvb/frontends/drx397xD_fw.h new file mode 100644 index 0000000..01de02a --- /dev/null +++ b/drivers/media/dvb/frontends/drx397xD_fw.h @@ -0,0 +1,40 @@ +/* + * Firmware definitions for Micronas drx397xD + * + * Copyright (C) 2007 Henk Vergonet <Henk.Vergonet@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; If not, see <http://www.gnu.org/licenses/>. + */ + +#ifdef _FW_ENTRY + _FW_ENTRY("drx397xD.A2.fw", DRXD_FW_A2 = 0 ), + _FW_ENTRY("drx397xD.B1.fw", DRXD_FW_B1 ), +#undef _FW_ENTRY +#endif /* _FW_ENTRY */ + +#ifdef _BLOB_ENTRY + _BLOB_ENTRY("InitAtomicRead", DRXD_InitAtomicRead = 0 ), + _BLOB_ENTRY("InitCE", DRXD_InitCE ), + _BLOB_ENTRY("InitCP", DRXD_InitCP ), + _BLOB_ENTRY("InitEC", DRXD_InitEC ), + _BLOB_ENTRY("InitEQ", DRXD_InitEQ ), + _BLOB_ENTRY("InitFE_1", DRXD_InitFE_1 ), + _BLOB_ENTRY("InitFE_2", DRXD_InitFE_2 ), + _BLOB_ENTRY("InitFT", DRXD_InitFT ), + _BLOB_ENTRY("InitSC", DRXD_InitSC ), + _BLOB_ENTRY("ResetCEFR", DRXD_ResetCEFR ), + _BLOB_ENTRY("ResetECRAM", DRXD_ResetECRAM ), + _BLOB_ENTRY("microcode", DRXD_microcode ), +#undef _BLOB_ENTRY +#endif /* _BLOB_ENTRY */ diff --git a/drivers/media/dvb/frontends/z0194a.h b/drivers/media/dvb/frontends/z0194a.h new file mode 100644 index 0000000..d2876d2 --- /dev/null +++ b/drivers/media/dvb/frontends/z0194a.h @@ -0,0 +1,97 @@ +/* z0194a.h Sharp z0194a tuner support +* +* Copyright (C) 2008 Igor M. Liplianin (liplianin@me.by) +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation, version 2. +* +* see Documentation/dvb/README.dvb-usb for more information +*/ + +#ifndef Z0194A +#define Z0194A + +static int sharp_z0194a__set_symbol_rate(struct dvb_frontend *fe, + u32 srate, u32 ratio) +{ + u8 aclk = 0; + u8 bclk = 0; + + if (srate < 1500000) { + aclk = 0xb7; bclk = 0x47; } + else if (srate < 3000000) { + aclk = 0xb7; bclk = 0x4b; } + else if (srate < 7000000) { + aclk = 0xb7; bclk = 0x4f; } + else if (srate < 14000000) { + aclk = 0xb7; bclk = 0x53; } + else if (srate < 30000000) { + aclk = 0xb6; bclk = 0x53; } + else if (srate < 45000000) { + aclk = 0xb4; bclk = 0x51; } + + stv0299_writereg(fe, 0x13, aclk); + stv0299_writereg(fe, 0x14, bclk); + stv0299_writereg(fe, 0x1f, (ratio >> 16) & 0xff); + stv0299_writereg(fe, 0x20, (ratio >> 8) & 0xff); + stv0299_writereg(fe, 0x21, (ratio) & 0xf0); + + return 0; +} + +static u8 sharp_z0194a__inittab[] = { + 0x01, 0x15, + 0x02, 0x00, + 0x03, 0x00, + 0x04, 0x7d, /* F22FR = 0x7d, F22 = f_VCO / 128 / 0x7d = 22 kHz */ + 0x05, 0x35, /* I2CT = 0, SCLT = 1, SDAT = 1 */ + 0x06, 0x40, /* DAC not used, set to high impendance mode */ + 0x07, 0x00, /* DAC LSB */ + 0x08, 0x40, /* DiSEqC off, LNB power on OP2/LOCK pin on */ + 0x09, 0x00, /* FIFO */ + 0x0c, 0x51, /* OP1 ctl = Normal, OP1 val = 1 (LNB Power ON) */ + 0x0d, 0x82, /* DC offset compensation = ON, beta_agc1 = 2 */ + 0x0e, 0x23, /* alpha_tmg = 2, beta_tmg = 3 */ + 0x10, 0x3f, /* AGC2 0x3d */ + 0x11, 0x84, + 0x12, 0xb9, + 0x15, 0xc9, /* lock detector threshold */ + 0x16, 0x00, + 0x17, 0x00, + 0x18, 0x00, + 0x19, 0x00, + 0x1a, 0x00, + 0x1f, 0x50, + 0x20, 0x00, + 0x21, 0x00, + 0x22, 0x00, + 0x23, 0x00, + 0x28, 0x00, /* out imp: normal out type: parallel FEC mode:0 */ + 0x29, 0x1e, /* 1/2 threshold */ + 0x2a, 0x14, /* 2/3 threshold */ + 0x2b, 0x0f, /* 3/4 threshold */ + 0x2c, 0x09, /* 5/6 threshold */ + 0x2d, 0x05, /* 7/8 threshold */ + 0x2e, 0x01, + 0x31, 0x1f, /* test all FECs */ + 0x32, 0x19, /* viterbi and synchro search */ + 0x33, 0xfc, /* rs control */ + 0x34, 0x93, /* error control */ + 0x0f, 0x52, + 0xff, 0xff +}; + +static struct stv0299_config sharp_z0194a_config = { + .demod_address = 0x68, + .inittab = sharp_z0194a__inittab, + .mclk = 88000000UL, + .invert = 1, + .skip_reinit = 0, + .lock_output = STV0299_LOCKOUTPUT_1, + .volt13_op0_op1 = STV0299_VOLT13_OP1, + .min_delay_ms = 100, + .set_symbol_rate = sharp_z0194a__set_symbol_rate, +}; + +#endif diff --git a/drivers/media/dvb/siano/smscoreapi.c b/drivers/media/dvb/siano/smscoreapi.c index b4b8ed7..c5f45fe 100644 --- a/drivers/media/dvb/siano/smscoreapi.c +++ b/drivers/media/dvb/siano/smscoreapi.c @@ -110,12 +110,12 @@ struct smscore_registry_entry_t { enum sms_device_type_st type; }; -struct list_head g_smscore_notifyees; -struct list_head g_smscore_devices; -struct mutex g_smscore_deviceslock; +static struct list_head g_smscore_notifyees; +static struct list_head g_smscore_devices; +static struct mutex g_smscore_deviceslock; -struct list_head g_smscore_registry; -struct mutex g_smscore_registrylock; +static struct list_head g_smscore_registry; +static struct mutex g_smscore_registrylock; static int default_mode = 4; @@ -1187,7 +1187,7 @@ int smsclient_sendrequest(struct smscore_client_t *client, } -int smscore_module_init(void) +static int __init smscore_module_init(void) { int rc = 0; @@ -1209,7 +1209,7 @@ int smscore_module_init(void) return rc; } -void smscore_module_exit(void) +static void __exit smscore_module_exit(void) { kmutex_lock(&g_smscore_deviceslock); diff --git a/drivers/media/dvb/siano/smsdvb.c b/drivers/media/dvb/siano/smsdvb.c index 6f9c185..229274a 100644 --- a/drivers/media/dvb/siano/smsdvb.c +++ b/drivers/media/dvb/siano/smsdvb.c @@ -27,8 +27,8 @@ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); -struct list_head g_smsdvb_clients; -struct mutex g_smsdvb_clientslock; +static struct list_head g_smsdvb_clients; +static struct mutex g_smsdvb_clientslock; static int smsdvb_onresponse(void *context, struct smscore_buffer_t *cb) { diff --git a/drivers/media/dvb/ttpci/Kconfig b/drivers/media/dvb/ttpci/Kconfig index 87c973a..41b5a98 100644 --- a/drivers/media/dvb/ttpci/Kconfig +++ b/drivers/media/dvb/ttpci/Kconfig @@ -5,8 +5,6 @@ config TTPCI_EEPROM config DVB_AV7110 tristate "AV7110 cards" depends on DVB_CORE && PCI && I2C - depends on HOTPLUG - select FW_LOADER if !DVB_AV7110_FIRMWARE select TTPCI_EEPROM select VIDEO_SAA7146_VV depends on VIDEO_DEV # dependencies of VIDEO_SAA7146_VV @@ -127,14 +125,12 @@ config DVB_BUDGET_AV depends on DVB_BUDGET_CORE && I2C select VIDEO_SAA7146_VV depends on VIDEO_DEV # dependencies of VIDEO_SAA7146_VV - depends on HOTPLUG # dependency of FW_LOADER select DVB_PLL if !DVB_FE_CUSTOMISE select DVB_STV0299 if !DVB_FE_CUSTOMISE select DVB_TDA1004X if !DVB_FE_CUSTOMISE select DVB_TDA10021 if !DVB_FE_CUSTOMISE select DVB_TDA10023 if !DVB_FE_CUSTOMISE select DVB_TUA6100 if !DVB_FE_CUSTOMISE - select FW_LOADER help Support for simple SAA7146 based DVB cards (so called Budget- or Nova-PCI cards) without onboard diff --git a/drivers/media/dvb/ttusb-dec/Kconfig b/drivers/media/dvb/ttusb-dec/Kconfig index a23cc0a..d5f48a3 100644 --- a/drivers/media/dvb/ttusb-dec/Kconfig +++ b/drivers/media/dvb/ttusb-dec/Kconfig @@ -1,8 +1,6 @@ config DVB_TTUSB_DEC tristate "Technotrend/Hauppauge USB DEC devices" depends on DVB_CORE && USB && INPUT - depends on HOTPLUG # due to FW_LOADER - select FW_LOADER select CRC32 help Support for external USB adapters designed by Technotrend and |