diff options
Diffstat (limited to 'drivers/media/dvb/dibusb')
-rw-r--r-- | drivers/media/dvb/dibusb/Kconfig | 62 | ||||
-rw-r--r-- | drivers/media/dvb/dibusb/Makefile | 11 | ||||
-rw-r--r-- | drivers/media/dvb/dibusb/dvb-dibusb-core.c | 558 | ||||
-rw-r--r-- | drivers/media/dvb/dibusb/dvb-dibusb-dvb.c | 185 | ||||
-rw-r--r-- | drivers/media/dvb/dibusb/dvb-dibusb-fe-i2c.c | 582 | ||||
-rw-r--r-- | drivers/media/dvb/dibusb/dvb-dibusb-firmware.c | 87 | ||||
-rw-r--r-- | drivers/media/dvb/dibusb/dvb-dibusb-remote.c | 316 | ||||
-rw-r--r-- | drivers/media/dvb/dibusb/dvb-dibusb-usb.c | 303 | ||||
-rw-r--r-- | drivers/media/dvb/dibusb/dvb-dibusb.h | 327 | ||||
-rw-r--r-- | drivers/media/dvb/dibusb/dvb-fe-dtt200u.c | 263 |
10 files changed, 2694 insertions, 0 deletions
diff --git a/drivers/media/dvb/dibusb/Kconfig b/drivers/media/dvb/dibusb/Kconfig new file mode 100644 index 00000000000..74dfc73ae5b --- /dev/null +++ b/drivers/media/dvb/dibusb/Kconfig @@ -0,0 +1,62 @@ +config DVB_DIBUSB + tristate "DiBcom USB DVB-T devices (see help for a complete device list)" + depends on DVB_CORE && USB + select FW_LOADER + select DVB_DIB3000MB + select DVB_DIB3000MC + select DVB_MT352 + help + Support for USB 1.1 and 2.0 DVB-T devices based on reference designs made by + DiBcom (http://www.dibcom.fr) and C&E. + + Devices supported by this driver: + + TwinhanDTV USB-Ter (VP7041) + TwinhanDTV Magic Box (VP7041e) + KWorld/JetWay/ADSTech V-Stream XPERT DTV - DVB-T USB1.1 and USB2.0 + Hama DVB-T USB-Box + DiBcom reference devices (non-public) + Ultima Electronic/Artec T1 USB TVBOX + Compro Videomate DVB-U2000 - DVB-T USB + Grandtec DVB-T USB + Avermedia AverTV DVBT USB + Artec T1 USB1.1 and USB2.0 boxes + Yakumo/Typhoon DVB-T USB2.0 + Hanftek UMT-010 USB2.0 + Hauppauge WinTV NOVA-T USB2 + + The VP7041 seems to be identical to "CTS Portable" (Chinese + Television System). + + These devices can be understood as budget ones, they "only" deliver + (a part of) the MPEG2 transport stream. + + A firmware is needed to get the device working. See Documentation/dvb/README.dibusb + details. + + Say Y if you own such a device and want to use it. You should build it as + a module. + +config DVB_DIBUSB_MISDESIGNED_DEVICES + bool "Enable support for some misdesigned (see help) devices, which identify with wrong IDs" + depends on DVB_DIBUSB + help + Somehow Artec/Ultima Electronic forgot to program the eeprom of some of their + USB1.1/USB2.0 devices. + So comes that they identify with the default Vendor and Product ID of the Cypress + CY7C64613 (AN2235) or Cypress FX2. + + Affected device IDs: + 0x0574:0x2235 (Artec T1 USB1.1, cold) + 0x04b4:0x8613 (Artec T1 USB2.0, cold) + 0x0574:0x1002 (Artec T1 USB2.0, warm) + 0x0574:0x2131 (aged DiBcom USB1.1 test device) + + Say Y if your device has one of the mentioned IDs. + +config DVB_DIBCOM_DEBUG + bool "Enable extended debug support for DiBcom USB device" + depends on DVB_DIBUSB + help + Say Y if you want to enable debuging. See modinfo dvb-dibusb for + debug levels. diff --git a/drivers/media/dvb/dibusb/Makefile b/drivers/media/dvb/dibusb/Makefile new file mode 100644 index 00000000000..e941c508624 --- /dev/null +++ b/drivers/media/dvb/dibusb/Makefile @@ -0,0 +1,11 @@ +dvb-dibusb-objs = dvb-dibusb-core.o \ + dvb-dibusb-dvb.o \ + dvb-dibusb-fe-i2c.o \ + dvb-dibusb-firmware.o \ + dvb-dibusb-remote.o \ + dvb-dibusb-usb.o \ + dvb-fe-dtt200u.o + +obj-$(CONFIG_DVB_DIBUSB) += dvb-dibusb.o + +EXTRA_CFLAGS = -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/ diff --git a/drivers/media/dvb/dibusb/dvb-dibusb-core.c b/drivers/media/dvb/dibusb/dvb-dibusb-core.c new file mode 100644 index 00000000000..26235f9247e --- /dev/null +++ b/drivers/media/dvb/dibusb/dvb-dibusb-core.c @@ -0,0 +1,558 @@ +/* + * Driver for mobile USB Budget DVB-T devices based on reference + * design made by DiBcom (http://www.dibcom.fr/) + * + * dvb-dibusb-core.c + * + * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) + * + * based on GPL code from DiBcom, which has + * Copyright (C) 2004 Amaury Demol for DiBcom (ademol@dibcom.fr) + * + * Remote control code added by David Matthews (dm@prolingua.co.uk) + * + * 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. + * + * Acknowledgements + * + * Amaury Demol (ademol@dibcom.fr) from DiBcom for providing specs and driver + * sources, on which this driver (and the dib3000mb/mc/p frontends) are based. + * + * see Documentation/dvb/README.dibusb for more information + */ +#include "dvb-dibusb.h" + +#include <linux/moduleparam.h> + +/* debug */ +int dvb_dibusb_debug; +module_param_named(debug, dvb_dibusb_debug, int, 0644); + +#ifdef CONFIG_DVB_DIBCOM_DEBUG +#define DBSTATUS "" +#else +#define DBSTATUS " (debugging is not enabled)" +#endif +MODULE_PARM_DESC(debug, "set debugging level (1=info,2=xfer,4=alotmore,8=ts,16=err,32=rc (|-able))." DBSTATUS); +#undef DBSTATUS + +static int pid_parse; +module_param(pid_parse, int, 0644); +MODULE_PARM_DESC(pid_parse, "enable pid parsing (filtering) when running at USB2.0"); + +static int rc_query_interval = 100; +module_param(rc_query_interval, int, 0644); +MODULE_PARM_DESC(rc_query_interval, "interval in msecs for remote control query (default: 100; min: 40)"); + +static int rc_key_repeat_count = 2; +module_param(rc_key_repeat_count, int, 0644); +MODULE_PARM_DESC(rc_key_repeat_count, "how many key repeats will be dropped before passing the key event again (default: 2)"); + +/* Vendor IDs */ +#define USB_VID_ADSTECH 0x06e1 +#define USB_VID_ANCHOR 0x0547 +#define USB_VID_AVERMEDIA 0x14aa +#define USB_VID_COMPRO 0x185b +#define USB_VID_COMPRO_UNK 0x145f +#define USB_VID_CYPRESS 0x04b4 +#define USB_VID_DIBCOM 0x10b8 +#define USB_VID_EMPIA 0xeb1a +#define USB_VID_GRANDTEC 0x5032 +#define USB_VID_HANFTEK 0x15f4 +#define USB_VID_HAUPPAUGE 0x2040 +#define USB_VID_HYPER_PALTEK 0x1025 +#define USB_VID_IMC_NETWORKS 0x13d3 +#define USB_VID_TWINHAN 0x1822 +#define USB_VID_ULTIMA_ELECTRONIC 0x05d8 + +/* Product IDs */ +#define USB_PID_ADSTECH_USB2_COLD 0xa333 +#define USB_PID_ADSTECH_USB2_WARM 0xa334 +#define USB_PID_AVERMEDIA_DVBT_USB_COLD 0x0001 +#define USB_PID_AVERMEDIA_DVBT_USB_WARM 0x0002 +#define USB_PID_COMPRO_DVBU2000_COLD 0xd000 +#define USB_PID_COMPRO_DVBU2000_WARM 0xd001 +#define USB_PID_COMPRO_DVBU2000_UNK_COLD 0x010c +#define USB_PID_COMPRO_DVBU2000_UNK_WARM 0x010d +#define USB_PID_DIBCOM_MOD3000_COLD 0x0bb8 +#define USB_PID_DIBCOM_MOD3000_WARM 0x0bb9 +#define USB_PID_DIBCOM_MOD3001_COLD 0x0bc6 +#define USB_PID_DIBCOM_MOD3001_WARM 0x0bc7 +#define USB_PID_DIBCOM_ANCHOR_2135_COLD 0x2131 +#define USB_PID_GRANDTEC_DVBT_USB_COLD 0x0fa0 +#define USB_PID_GRANDTEC_DVBT_USB_WARM 0x0fa1 +#define USB_PID_KWORLD_VSTREAM_COLD 0x17de +#define USB_PID_KWORLD_VSTREAM_WARM 0x17df +#define USB_PID_TWINHAN_VP7041_COLD 0x3201 +#define USB_PID_TWINHAN_VP7041_WARM 0x3202 +#define USB_PID_ULTIMA_TVBOX_COLD 0x8105 +#define USB_PID_ULTIMA_TVBOX_WARM 0x8106 +#define USB_PID_ULTIMA_TVBOX_AN2235_COLD 0x8107 +#define USB_PID_ULTIMA_TVBOX_AN2235_WARM 0x8108 +#define USB_PID_ULTIMA_TVBOX_ANCHOR_COLD 0x2235 +#define USB_PID_ULTIMA_TVBOX_USB2_COLD 0x8109 +#define USB_PID_ULTIMA_TVBOX_USB2_FX_COLD 0x8613 +#define USB_PID_ULTIMA_TVBOX_USB2_FX_WARM 0x1002 +#define USB_PID_UNK_HYPER_PALTEK_COLD 0x005e +#define USB_PID_UNK_HYPER_PALTEK_WARM 0x005f +#define USB_PID_HANFTEK_UMT_010_COLD 0x0001 +#define USB_PID_HANFTEK_UMT_010_WARM 0x0015 +#define USB_PID_YAKUMO_DTT200U_COLD 0x0201 +#define USB_PID_YAKUMO_DTT200U_WARM 0x0301 +#define USB_PID_WINTV_NOVA_T_USB2_COLD 0x9300 +#define USB_PID_WINTV_NOVA_T_USB2_WARM 0x9301 + +/* USB Driver stuff + * table of devices that this driver is working with + * + * ATTENTION: Never ever change the order of this table, the particular + * devices depend on this order + * + * Each entry is used as a reference in the device_struct. Currently this is + * the only non-redundant way of assigning USB ids to actual devices I'm aware + * of, because there is only one place in the code where the assignment of + * vendor and product id is done, here. + */ +static struct usb_device_id dib_table [] = { +/* 00 */ { USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_DVBT_USB_COLD)}, +/* 01 */ { USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_DVBT_USB_WARM)}, +/* 02 */ { USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_YAKUMO_DTT200U_COLD) }, +/* 03 */ { USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_YAKUMO_DTT200U_WARM) }, + +/* 04 */ { USB_DEVICE(USB_VID_COMPRO, USB_PID_COMPRO_DVBU2000_COLD) }, +/* 05 */ { USB_DEVICE(USB_VID_COMPRO, USB_PID_COMPRO_DVBU2000_WARM) }, +/* 06 */ { USB_DEVICE(USB_VID_COMPRO_UNK, USB_PID_COMPRO_DVBU2000_UNK_COLD) }, +/* 07 */ { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_MOD3000_COLD) }, +/* 08 */ { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_MOD3000_WARM) }, +/* 09 */ { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_MOD3001_COLD) }, +/* 10 */ { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_MOD3001_WARM) }, +/* 11 */ { USB_DEVICE(USB_VID_EMPIA, USB_PID_KWORLD_VSTREAM_COLD) }, +/* 12 */ { USB_DEVICE(USB_VID_EMPIA, USB_PID_KWORLD_VSTREAM_WARM) }, +/* 13 */ { USB_DEVICE(USB_VID_GRANDTEC, USB_PID_GRANDTEC_DVBT_USB_COLD) }, +/* 14 */ { USB_DEVICE(USB_VID_GRANDTEC, USB_PID_GRANDTEC_DVBT_USB_WARM) }, +/* 15 */ { USB_DEVICE(USB_VID_GRANDTEC, USB_PID_DIBCOM_MOD3000_COLD) }, +/* 16 */ { USB_DEVICE(USB_VID_GRANDTEC, USB_PID_DIBCOM_MOD3000_WARM) }, +/* 17 */ { USB_DEVICE(USB_VID_HYPER_PALTEK, USB_PID_UNK_HYPER_PALTEK_COLD) }, +/* 18 */ { USB_DEVICE(USB_VID_HYPER_PALTEK, USB_PID_UNK_HYPER_PALTEK_WARM) }, +/* 19 */ { USB_DEVICE(USB_VID_IMC_NETWORKS, USB_PID_TWINHAN_VP7041_COLD) }, +/* 20 */ { USB_DEVICE(USB_VID_IMC_NETWORKS, USB_PID_TWINHAN_VP7041_WARM) }, +/* 21 */ { USB_DEVICE(USB_VID_TWINHAN, USB_PID_TWINHAN_VP7041_COLD) }, +/* 22 */ { USB_DEVICE(USB_VID_TWINHAN, USB_PID_TWINHAN_VP7041_WARM) }, +/* 23 */ { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_COLD) }, +/* 24 */ { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_WARM) }, +/* 25 */ { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_AN2235_COLD) }, +/* 26 */ { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_AN2235_WARM) }, +/* 27 */ { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_USB2_COLD) }, + +/* 28 */ { USB_DEVICE(USB_VID_HANFTEK, USB_PID_HANFTEK_UMT_010_COLD) }, +/* 29 */ { USB_DEVICE(USB_VID_HANFTEK, USB_PID_HANFTEK_UMT_010_WARM) }, + +/* 30 */ { USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_WINTV_NOVA_T_USB2_COLD) }, +/* 31 */ { USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_WINTV_NOVA_T_USB2_WARM) }, +/* 32 */ { USB_DEVICE(USB_VID_ADSTECH, USB_PID_ADSTECH_USB2_COLD) }, +/* 33 */ { USB_DEVICE(USB_VID_ADSTECH, USB_PID_ADSTECH_USB2_WARM) }, +/* + * activate the following define when you have one of the devices and want to + * build it from build-2.6 in dvb-kernel + */ +// #define CONFIG_DVB_DIBUSB_MISDESIGNED_DEVICES +#ifdef CONFIG_DVB_DIBUSB_MISDESIGNED_DEVICES +/* 34 */ { USB_DEVICE(USB_VID_ANCHOR, USB_PID_ULTIMA_TVBOX_ANCHOR_COLD) }, +/* 35 */ { USB_DEVICE(USB_VID_CYPRESS, USB_PID_ULTIMA_TVBOX_USB2_FX_COLD) }, +/* 36 */ { USB_DEVICE(USB_VID_ANCHOR, USB_PID_ULTIMA_TVBOX_USB2_FX_WARM) }, +/* 37 */ { USB_DEVICE(USB_VID_ANCHOR, USB_PID_DIBCOM_ANCHOR_2135_COLD) }, +#endif + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE (usb, dib_table); + +static struct dibusb_usb_controller dibusb_usb_ctrl[] = { + { .name = "Cypress AN2135", .cpu_cs_register = 0x7f92 }, + { .name = "Cypress AN2235", .cpu_cs_register = 0x7f92 }, + { .name = "Cypress FX2", .cpu_cs_register = 0xe600 }, +}; + +struct dibusb_tuner dibusb_tuner[] = { + { DIBUSB_TUNER_CABLE_THOMSON, + 0x61 + }, + { DIBUSB_TUNER_COFDM_PANASONIC_ENV57H1XD5, + 0x60 + }, + { DIBUSB_TUNER_CABLE_LG_TDTP_E102P, + 0x61 + }, + { DIBUSB_TUNER_COFDM_PANASONIC_ENV77H11D5, + 0x60 + }, +}; + +static struct dibusb_demod dibusb_demod[] = { + { DIBUSB_DIB3000MB, + 16, + { 0x8, 0 }, + }, + { DIBUSB_DIB3000MC, + 32, + { 0x9, 0xa, 0xb, 0xc }, + }, + { DIBUSB_MT352, + 254, + { 0xf, 0 }, + }, + { DTT200U_FE, + 8, + { 0xff,0 }, /* there is no i2c bus in this device */ + } +}; + +static struct dibusb_device_class dibusb_device_classes[] = { + { .id = DIBUSB1_1, .usb_ctrl = &dibusb_usb_ctrl[0], + .firmware = "dvb-dibusb-5.0.0.11.fw", + .pipe_cmd = 0x01, .pipe_data = 0x02, + .urb_count = 7, .urb_buffer_size = 4096, + DIBUSB_RC_NEC_PROTOCOL, + &dibusb_demod[DIBUSB_DIB3000MB], + &dibusb_tuner[DIBUSB_TUNER_CABLE_THOMSON], + }, + { DIBUSB1_1_AN2235, &dibusb_usb_ctrl[1], + "dvb-dibusb-an2235-1.fw", + 0x01, 0x02, + 7, 4096, + DIBUSB_RC_NEC_PROTOCOL, + &dibusb_demod[DIBUSB_DIB3000MB], + &dibusb_tuner[DIBUSB_TUNER_CABLE_THOMSON], + }, + { DIBUSB2_0,&dibusb_usb_ctrl[2], + "dvb-dibusb-6.0.0.5.fw", + 0x01, 0x06, + 7, 4096, + DIBUSB_RC_NEC_PROTOCOL, + &dibusb_demod[DIBUSB_DIB3000MC], + &dibusb_tuner[DIBUSB_TUNER_COFDM_PANASONIC_ENV57H1XD5], + }, + { UMT2_0, &dibusb_usb_ctrl[2], + "dvb-dibusb-umt-2.fw", + 0x01, 0x06, + 20, 512, + DIBUSB_RC_NO, + &dibusb_demod[DIBUSB_MT352], + &dibusb_tuner[DIBUSB_TUNER_CABLE_LG_TDTP_E102P], + }, + { DIBUSB2_0B,&dibusb_usb_ctrl[2], + "dvb-dibusb-adstech-usb2-1.fw", + 0x01, 0x06, + 7, 4096, + DIBUSB_RC_NEC_PROTOCOL, + &dibusb_demod[DIBUSB_DIB3000MB], + &dibusb_tuner[DIBUSB_TUNER_CABLE_THOMSON], + }, + { NOVAT_USB2,&dibusb_usb_ctrl[2], + "dvb-dibusb-nova-t-1.fw", + 0x01, 0x06, + 7, 4096, + DIBUSB_RC_HAUPPAUGE_PROTO, + &dibusb_demod[DIBUSB_DIB3000MC], + &dibusb_tuner[DIBUSB_TUNER_COFDM_PANASONIC_ENV57H1XD5], + }, + { DTT200U,&dibusb_usb_ctrl[2], + "dvb-dtt200u-1.fw", + 0x01, 0x02, + 7, 4096, + DIBUSB_RC_NO, + &dibusb_demod[DTT200U_FE], + NULL, /* no explicit tuner/pll-programming necessary (it has the ENV57H1XD5) */ + }, +}; + +static struct dibusb_usb_device dibusb_devices[] = { + { "TwinhanDTV USB1.1 / Magic Box / HAMA USB1.1 DVB-T device", + &dibusb_device_classes[DIBUSB1_1], + { &dib_table[19], &dib_table[21], NULL}, + { &dib_table[20], &dib_table[22], NULL}, + }, + { "KWorld V-Stream XPERT DTV - DVB-T USB1.1", + &dibusb_device_classes[DIBUSB1_1], + { &dib_table[11], NULL }, + { &dib_table[12], NULL }, + }, + { "Grandtec USB1.1 DVB-T", + &dibusb_device_classes[DIBUSB1_1], + { &dib_table[13], &dib_table[15], NULL }, + { &dib_table[14], &dib_table[16], NULL }, + }, + { "DiBcom USB1.1 DVB-T reference design (MOD3000)", + &dibusb_device_classes[DIBUSB1_1], + { &dib_table[7], NULL }, + { &dib_table[8], NULL }, + }, + { "Artec T1 USB1.1 TVBOX with AN2135", + &dibusb_device_classes[DIBUSB1_1], + { &dib_table[23], NULL }, + { &dib_table[24], NULL }, + }, + { "Artec T1 USB1.1 TVBOX with AN2235", + &dibusb_device_classes[DIBUSB1_1_AN2235], + { &dib_table[25], NULL }, + { &dib_table[26], NULL }, + }, + { "Avermedia AverTV DVBT USB1.1", + &dibusb_device_classes[DIBUSB1_1], + { &dib_table[0], NULL }, + { &dib_table[1], NULL }, + }, + { "Compro Videomate DVB-U2000 - DVB-T USB1.1 (please confirm to linux-dvb)", + &dibusb_device_classes[DIBUSB1_1], + { &dib_table[4], &dib_table[6], NULL}, + { &dib_table[5], NULL }, + }, + { "Unkown USB1.1 DVB-T device ???? please report the name to the author", + &dibusb_device_classes[DIBUSB1_1], + { &dib_table[17], NULL }, + { &dib_table[18], NULL }, + }, + { "DiBcom USB2.0 DVB-T reference design (MOD3000P)", + &dibusb_device_classes[DIBUSB2_0], + { &dib_table[9], NULL }, + { &dib_table[10], NULL }, + }, + { "Artec T1 USB2.0 TVBOX (please report the warm ID)", + &dibusb_device_classes[DIBUSB2_0], + { &dib_table[27], NULL }, + { NULL }, + }, + { "Hauppauge WinTV NOVA-T USB2", + &dibusb_device_classes[NOVAT_USB2], + { &dib_table[30], NULL }, + { &dib_table[31], NULL }, + }, + { "DTT200U (Yakumo/Hama/Typhoon) DVB-T USB2.0", + &dibusb_device_classes[DTT200U], + { &dib_table[2], NULL }, + { &dib_table[3], NULL }, + }, + { "Hanftek UMT-010 DVB-T USB2.0", + &dibusb_device_classes[UMT2_0], + { &dib_table[28], NULL }, + { &dib_table[29], NULL }, + }, + { "KWorld/ADSTech Instant DVB-T USB 2.0", + &dibusb_device_classes[DIBUSB2_0B], + { &dib_table[32], NULL }, + { &dib_table[33], NULL }, /* device ID with default DIBUSB2_0-firmware */ + }, +#ifdef CONFIG_DVB_DIBUSB_MISDESIGNED_DEVICES + { "Artec T1 USB1.1 TVBOX with AN2235 (misdesigned)", + &dibusb_device_classes[DIBUSB1_1_AN2235], + { &dib_table[34], NULL }, + { NULL }, + }, + { "Artec T1 USB2.0 TVBOX with FX2 IDs (misdesigned, please report the warm ID)", + &dibusb_device_classes[DTT200U], + { &dib_table[35], NULL }, + { &dib_table[36], NULL }, /* undefined, it could be that the device will get another USB ID in warm state */ + }, + { "DiBcom USB1.1 DVB-T reference design (MOD3000) with AN2135 default IDs", + &dibusb_device_classes[DIBUSB1_1], + { &dib_table[37], NULL }, + { NULL }, + }, +#endif +}; + +static int dibusb_exit(struct usb_dibusb *dib) +{ + deb_info("init_state before exiting everything: %x\n",dib->init_state); + dibusb_remote_exit(dib); + dibusb_fe_exit(dib); + dibusb_i2c_exit(dib); + dibusb_dvb_exit(dib); + dibusb_urb_exit(dib); + deb_info("init_state should be zero now: %x\n",dib->init_state); + dib->init_state = DIBUSB_STATE_INIT; + kfree(dib); + return 0; +} + +static int dibusb_init(struct usb_dibusb *dib) +{ + int ret = 0; + sema_init(&dib->usb_sem, 1); + sema_init(&dib->i2c_sem, 1); + + dib->init_state = DIBUSB_STATE_INIT; + + if ((ret = dibusb_urb_init(dib)) || + (ret = dibusb_dvb_init(dib)) || + (ret = dibusb_i2c_init(dib))) { + dibusb_exit(dib); + return ret; + } + + if ((ret = dibusb_fe_init(dib))) + err("could not initialize a frontend."); + + if ((ret = dibusb_remote_init(dib))) + err("could not initialize remote control."); + + return 0; +} + +static struct dibusb_usb_device * dibusb_device_class_quirk(struct usb_device *udev, struct dibusb_usb_device *dev) +{ + int i; + + /* Quirk for the Kworld/ADSTech Instant USB2.0 device. It has the same USB + * IDs like the USB1.1 KWorld after loading the firmware. Which is a bad + * idea and make this quirk necessary. + */ + if (dev->dev_cl->id == DIBUSB1_1 && udev->speed == USB_SPEED_HIGH) { + info("this seems to be the Kworld/ADSTech Instant USB2.0 device or equal."); + for (i = 0; i < sizeof(dibusb_devices)/sizeof(struct dibusb_usb_device); i++) { + if (dibusb_devices[i].dev_cl->id == DIBUSB2_0B) { + dev = &dibusb_devices[i]; + break; + } + } + } + + return dev; +} + +static struct dibusb_usb_device * dibusb_find_device (struct usb_device *udev,int *cold) +{ + int i,j; + struct dibusb_usb_device *dev = NULL; + *cold = -1; + + for (i = 0; i < sizeof(dibusb_devices)/sizeof(struct dibusb_usb_device); i++) { + for (j = 0; j < DIBUSB_ID_MAX_NUM && dibusb_devices[i].cold_ids[j] != NULL; j++) { + deb_info("check for cold %x %x\n",dibusb_devices[i].cold_ids[j]->idVendor, dibusb_devices[i].cold_ids[j]->idProduct); + if (dibusb_devices[i].cold_ids[j]->idVendor == le16_to_cpu(udev->descriptor.idVendor) && + dibusb_devices[i].cold_ids[j]->idProduct == le16_to_cpu(udev->descriptor.idProduct)) { + *cold = 1; + dev = &dibusb_devices[i]; + break; + } + } + + if (dev != NULL) + break; + + for (j = 0; j < DIBUSB_ID_MAX_NUM && dibusb_devices[i].warm_ids[j] != NULL; j++) { + deb_info("check for warm %x %x\n",dibusb_devices[i].warm_ids[j]->idVendor, dibusb_devices[i].warm_ids[j]->idProduct); + if (dibusb_devices[i].warm_ids[j]->idVendor == le16_to_cpu(udev->descriptor.idVendor) && + dibusb_devices[i].warm_ids[j]->idProduct == le16_to_cpu(udev->descriptor.idProduct)) { + *cold = 0; + dev = &dibusb_devices[i]; + break; + } + } + } + + if (dev != NULL) + dev = dibusb_device_class_quirk(udev,dev); + + return dev; +} + +/* + * USB + */ +static int dibusb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct usb_dibusb *dib = NULL; + struct dibusb_usb_device *dibdev = NULL; + + int ret = -ENOMEM,cold=0; + + if ((dibdev = dibusb_find_device(udev,&cold)) == NULL) { + err("something went very wrong, " + "unknown product ID: %.4x",le16_to_cpu(udev->descriptor.idProduct)); + return -ENODEV; + } + + if (cold == 1) { + info("found a '%s' in cold state, will try to load a firmware",dibdev->name); + ret = dibusb_loadfirmware(udev,dibdev); + } else { + info("found a '%s' in warm state.",dibdev->name); + dib = kmalloc(sizeof(struct usb_dibusb),GFP_KERNEL); + if (dib == NULL) { + err("no memory"); + return ret; + } + memset(dib,0,sizeof(struct usb_dibusb)); + + dib->udev = udev; + dib->dibdev = dibdev; + + /* store parameters to structures */ + dib->rc_query_interval = rc_query_interval; + dib->pid_parse = pid_parse; + dib->rc_key_repeat_count = rc_key_repeat_count; + + usb_set_intfdata(intf, dib); + + ret = dibusb_init(dib); + } + + if (ret == 0) + info("%s successfully initialized and connected.",dibdev->name); + else + info("%s error while loading driver (%d)",dibdev->name,ret); + return ret; +} + +static void dibusb_disconnect(struct usb_interface *intf) +{ + struct usb_dibusb *dib = usb_get_intfdata(intf); + const char *name = DRIVER_DESC; + + usb_set_intfdata(intf,NULL); + if (dib != NULL && dib->dibdev != NULL) { + name = dib->dibdev->name; + dibusb_exit(dib); + } + info("%s successfully deinitialized and disconnected.",name); + +} + +/* usb specific object needed to register this driver with the usb subsystem */ +static struct usb_driver dibusb_driver = { + .owner = THIS_MODULE, + .name = DRIVER_DESC, + .probe = dibusb_probe, + .disconnect = dibusb_disconnect, + .id_table = dib_table, +}; + +/* module stuff */ +static int __init usb_dibusb_init(void) +{ + int result; + if ((result = usb_register(&dibusb_driver))) { + err("usb_register failed. Error number %d",result); + return result; + } + + return 0; +} + +static void __exit usb_dibusb_exit(void) +{ + /* deregister this driver from the USB subsystem */ + usb_deregister(&dibusb_driver); +} + +module_init (usb_dibusb_init); +module_exit (usb_dibusb_exit); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/dibusb/dvb-dibusb-dvb.c b/drivers/media/dvb/dibusb/dvb-dibusb-dvb.c new file mode 100644 index 00000000000..04e54ec093f --- /dev/null +++ b/drivers/media/dvb/dibusb/dvb-dibusb-dvb.c @@ -0,0 +1,185 @@ +/* + * dvb-dibusb-dvb.c is part of the driver for mobile USB Budget DVB-T devices + * based on reference design made by DiBcom (http://www.dibcom.fr/) + * + * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) + * + * see dvb-dibusb-core.c for more copyright details. + * + * This file contains functions for initializing and handling the + * linux-dvb API. + */ +#include "dvb-dibusb.h" + +#include <linux/usb.h> +#include <linux/version.h> + +static u32 urb_compl_count; + +/* + * MPEG2 TS DVB stuff + */ +void dibusb_urb_complete(struct urb *urb, struct pt_regs *ptregs) +{ + struct usb_dibusb *dib = urb->context; + + deb_ts("urb complete feedcount: %d, status: %d, length: %d\n",dib->feedcount,urb->status, + urb->actual_length); + + urb_compl_count++; + if (urb_compl_count % 1000 == 0) + deb_info("%d urbs completed so far.\n",urb_compl_count); + + switch (urb->status) { + case 0: /* success */ + case -ETIMEDOUT: /* NAK */ + break; + case -ECONNRESET: /* kill */ + case -ENOENT: + case -ESHUTDOWN: + return; + default: /* error */ + deb_ts("urb completition error %d.", urb->status); + break; + } + + if (dib->feedcount > 0 && urb->actual_length > 0) { + if (dib->init_state & DIBUSB_STATE_DVB) + dvb_dmx_swfilter(&dib->demux, (u8*) urb->transfer_buffer,urb->actual_length); + } else + deb_ts("URB dropped because of feedcount.\n"); + + usb_submit_urb(urb,GFP_ATOMIC); +} + +static int dibusb_ctrl_feed(struct dvb_demux_feed *dvbdmxfeed, int onoff) +{ + struct usb_dibusb *dib = dvbdmxfeed->demux->priv; + int newfeedcount; + + if (dib == NULL) + return -ENODEV; + + newfeedcount = dib->feedcount + (onoff ? 1 : -1); + + /* + * stop feed before setting a new pid if there will be no pid anymore + */ + if (newfeedcount == 0) { + deb_ts("stop feeding\n"); + if (dib->xfer_ops.fifo_ctrl != NULL) { + if (dib->xfer_ops.fifo_ctrl(dib->fe,0)) { + err("error while inhibiting fifo."); + return -ENODEV; + } + } + dibusb_streaming(dib,0); + } + + dib->feedcount = newfeedcount; + + /* activate the pid on the device specific pid_filter */ + deb_ts("setting pid: %5d %04x at index %d '%s'\n",dvbdmxfeed->pid,dvbdmxfeed->pid,dvbdmxfeed->index,onoff ? "on" : "off"); + if (dib->pid_parse && dib->xfer_ops.pid_ctrl != NULL) + dib->xfer_ops.pid_ctrl(dib->fe,dvbdmxfeed->index,dvbdmxfeed->pid,onoff); + + /* + * start the feed if this was the first pid to set and there is still a pid + * for reception. + */ + if (dib->feedcount == onoff && dib->feedcount > 0) { + + deb_ts("controlling pid parser\n"); + if (dib->xfer_ops.pid_parse != NULL) { + if (dib->xfer_ops.pid_parse(dib->fe,dib->pid_parse) < 0) { + err("could not handle pid_parser"); + } + } + + deb_ts("start feeding\n"); + if (dib->xfer_ops.fifo_ctrl != NULL) { + if (dib->xfer_ops.fifo_ctrl(dib->fe,1)) { + err("error while enabling fifo."); + return -ENODEV; + } + } + dibusb_streaming(dib,1); + } + return 0; +} + +static int dibusb_start_feed(struct dvb_demux_feed *dvbdmxfeed) +{ + deb_ts("start pid: 0x%04x, feedtype: %d\n", dvbdmxfeed->pid,dvbdmxfeed->type); + return dibusb_ctrl_feed(dvbdmxfeed,1); +} + +static int dibusb_stop_feed(struct dvb_demux_feed *dvbdmxfeed) +{ + deb_ts("stop pid: 0x%04x, feedtype: %d\n", dvbdmxfeed->pid, dvbdmxfeed->type); + return dibusb_ctrl_feed(dvbdmxfeed,0); +} + +int dibusb_dvb_init(struct usb_dibusb *dib) +{ + int ret; + + urb_compl_count = 0; + + if ((ret = dvb_register_adapter(&dib->adapter, DRIVER_DESC, + THIS_MODULE)) < 0) { + deb_info("dvb_register_adapter failed: error %d", ret); + goto err; + } + dib->adapter->priv = dib; + +/* i2c is done in dibusb_i2c_init */ + + dib->demux.dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING; + + dib->demux.priv = (void *)dib; + /* get pidcount from demod */ + dib->demux.feednum = dib->demux.filternum = 255; + dib->demux.start_feed = dibusb_start_feed; + dib->demux.stop_feed = dibusb_stop_feed; + dib->demux.write_to_decoder = NULL; + if ((ret = dvb_dmx_init(&dib->demux)) < 0) { + err("dvb_dmx_init failed: error %d",ret); + goto err_dmx; + } + + dib->dmxdev.filternum = dib->demux.filternum; + dib->dmxdev.demux = &dib->demux.dmx; + dib->dmxdev.capabilities = 0; + if ((ret = dvb_dmxdev_init(&dib->dmxdev, dib->adapter)) < 0) { + err("dvb_dmxdev_init failed: error %d",ret); + goto err_dmx_dev; + } + + dvb_net_init(dib->adapter, &dib->dvb_net, &dib->demux.dmx); + + goto success; +err_dmx_dev: + dvb_dmx_release(&dib->demux); +err_dmx: + dvb_unregister_adapter(dib->adapter); +err: + return ret; +success: + dib->init_state |= DIBUSB_STATE_DVB; + return 0; +} + +int dibusb_dvb_exit(struct usb_dibusb *dib) +{ + if (dib->init_state & DIBUSB_STATE_DVB) { + dib->init_state &= ~DIBUSB_STATE_DVB; + deb_info("unregistering DVB part\n"); + dvb_net_release(&dib->dvb_net); + dib->demux.dmx.close(&dib->demux.dmx); + dvb_dmxdev_release(&dib->dmxdev); + dvb_dmx_release(&dib->demux); + dvb_unregister_adapter(dib->adapter); + } + return 0; +} diff --git a/drivers/media/dvb/dibusb/dvb-dibusb-fe-i2c.c b/drivers/media/dvb/dibusb/dvb-dibusb-fe-i2c.c new file mode 100644 index 00000000000..2ed89488c7c --- /dev/null +++ b/drivers/media/dvb/dibusb/dvb-dibusb-fe-i2c.c @@ -0,0 +1,582 @@ +/* + * dvb-dibusb-fe-i2c.c is part of the driver for mobile USB Budget DVB-T devices + * based on reference design made by DiBcom (http://www.dibcom.fr/) + * + * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) + * + * see dvb-dibusb-core.c for more copyright details. + * + * This file contains functions for attaching, initializing of an appropriate + * demodulator/frontend. I2C-stuff is also located here. + * + */ +#include "dvb-dibusb.h" + +#include <linux/usb.h> + +static int dibusb_i2c_msg(struct usb_dibusb *dib, u8 addr, + u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen) +{ + u8 sndbuf[wlen+4]; /* lead(1) devaddr,direction(1) addr(2) data(wlen) (len(2) (when reading)) */ + /* write only ? */ + int wo = (rbuf == NULL || rlen == 0), + len = 2 + wlen + (wo ? 0 : 2); + + sndbuf[0] = wo ? DIBUSB_REQ_I2C_WRITE : DIBUSB_REQ_I2C_READ; + sndbuf[1] = (addr << 1) | (wo ? 0 : 1); + + memcpy(&sndbuf[2],wbuf,wlen); + + if (!wo) { + sndbuf[wlen+2] = (rlen >> 8) & 0xff; + sndbuf[wlen+3] = rlen & 0xff; + } + + return dibusb_readwrite_usb(dib,sndbuf,len,rbuf,rlen); +} + +/* + * I2C master xfer function + */ +static int dibusb_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg *msg,int num) +{ + struct usb_dibusb *dib = i2c_get_adapdata(adap); + int i; + + if (down_interruptible(&dib->i2c_sem) < 0) + return -EAGAIN; + + if (num > 2) + warn("more than 2 i2c messages at a time is not handled yet. TODO."); + + for (i = 0; i < num; i++) { + /* write/read request */ + if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) { + if (dibusb_i2c_msg(dib, msg[i].addr, msg[i].buf,msg[i].len, + msg[i+1].buf,msg[i+1].len) < 0) + break; + i++; + } else + if (dibusb_i2c_msg(dib, msg[i].addr, msg[i].buf,msg[i].len,NULL,0) < 0) + break; + } + + up(&dib->i2c_sem); + return i; +} + +static u32 dibusb_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static struct i2c_algorithm dibusb_algo = { + .name = "DiBcom USB i2c algorithm", + .id = I2C_ALGO_BIT, + .master_xfer = dibusb_i2c_xfer, + .functionality = dibusb_i2c_func, +}; + +static int dibusb_general_demod_init(struct dvb_frontend *fe); +static u8 dibusb_general_pll_addr(struct dvb_frontend *fe); +static int dibusb_general_pll_init(struct dvb_frontend *fe, u8 pll_buf[5]); +static int dibusb_general_pll_set(struct dvb_frontend *fe, + struct dvb_frontend_parameters* params, u8 pll_buf[5]); + +static struct mt352_config mt352_hanftek_umt_010_config = { + .demod_address = 0x1e, + .demod_init = dibusb_general_demod_init, + .pll_set = dibusb_general_pll_set, +}; + +static int dibusb_tuner_quirk(struct usb_dibusb *dib) +{ + switch (dib->dibdev->dev_cl->id) { + case DIBUSB1_1: /* some these device have the ENV77H11D5 and some the THOMSON CABLE */ + case DIBUSB1_1_AN2235: { /* actually its this device, but in warm state they are indistinguishable */ + struct dibusb_tuner *t; + u8 b[2] = { 0,0 } ,b2[1]; + struct i2c_msg msg[2] = { + { .flags = 0, .buf = b, .len = 2 }, + { .flags = I2C_M_RD, .buf = b2, .len = 1}, + }; + + t = &dibusb_tuner[DIBUSB_TUNER_COFDM_PANASONIC_ENV77H11D5]; + + msg[0].addr = msg[1].addr = t->pll_addr; + + if (dib->xfer_ops.tuner_pass_ctrl != NULL) + dib->xfer_ops.tuner_pass_ctrl(dib->fe,1,t->pll_addr); + dibusb_i2c_xfer(&dib->i2c_adap,msg,2); + if (dib->xfer_ops.tuner_pass_ctrl != NULL) + dib->xfer_ops.tuner_pass_ctrl(dib->fe,0,t->pll_addr); + + if (b2[0] == 0xfe) + info("this device has the Thomson Cable onboard. Which is default."); + else { + dib->tuner = t; + info("this device has the Panasonic ENV77H11D5 onboard."); + } + break; + } + default: + break; + } + return 0; +} + +int dibusb_fe_init(struct usb_dibusb* dib) +{ + struct dib3000_config demod_cfg; + int i; + + if (dib->init_state & DIBUSB_STATE_I2C) { + for (i = 0; i < sizeof(dib->dibdev->dev_cl->demod->i2c_addrs) / sizeof(unsigned char) && + dib->dibdev->dev_cl->demod->i2c_addrs[i] != 0; i++) { + + demod_cfg.demod_address = dib->dibdev->dev_cl->demod->i2c_addrs[i]; + demod_cfg.pll_addr = dibusb_general_pll_addr; + demod_cfg.pll_set = dibusb_general_pll_set; + demod_cfg.pll_init = dibusb_general_pll_init; + + deb_info("demod id: %d %d\n",dib->dibdev->dev_cl->demod->id,DTT200U_FE); + + switch (dib->dibdev->dev_cl->demod->id) { + case DIBUSB_DIB3000MB: + dib->fe = dib3000mb_attach(&demod_cfg,&dib->i2c_adap,&dib->xfer_ops); + break; + case DIBUSB_DIB3000MC: + dib->fe = dib3000mc_attach(&demod_cfg,&dib->i2c_adap,&dib->xfer_ops); + break; + case DIBUSB_MT352: + mt352_hanftek_umt_010_config.demod_address = dib->dibdev->dev_cl->demod->i2c_addrs[i]; + dib->fe = mt352_attach(&mt352_hanftek_umt_010_config, &dib->i2c_adap); + break; + case DTT200U_FE: + dib->fe = dtt200u_fe_attach(dib,&dib->xfer_ops); + break; + } + if (dib->fe != NULL) { + info("found demodulator at i2c address 0x%x",dib->dibdev->dev_cl->demod->i2c_addrs[i]); + break; + } + } + /* if a frontend was found */ + if (dib->fe != NULL) { + if (dib->fe->ops->sleep != NULL) + dib->fe_sleep = dib->fe->ops->sleep; + dib->fe->ops->sleep = dibusb_hw_sleep; + + if (dib->fe->ops->init != NULL ) + dib->fe_init = dib->fe->ops->init; + dib->fe->ops->init = dibusb_hw_wakeup; + + /* setting the default tuner */ + dib->tuner = dib->dibdev->dev_cl->tuner; + + /* check which tuner is mounted on this device, in case this is unsure */ + dibusb_tuner_quirk(dib); + } + } + if (dib->fe == NULL) { + err("A frontend driver was not found for device '%s'.", + dib->dibdev->name); + return -ENODEV; + } else { + if (dvb_register_frontend(dib->adapter, dib->fe)) { + err("Frontend registration failed."); + if (dib->fe->ops->release) + dib->fe->ops->release(dib->fe); + dib->fe = NULL; + return -ENODEV; + } + } + + return 0; +} + +int dibusb_fe_exit(struct usb_dibusb *dib) +{ + if (dib->fe != NULL) + dvb_unregister_frontend(dib->fe); + return 0; +} + +int dibusb_i2c_init(struct usb_dibusb *dib) +{ + int ret = 0; + + dib->adapter->priv = dib; + + strncpy(dib->i2c_adap.name,dib->dibdev->name,I2C_NAME_SIZE); +#ifdef I2C_ADAP_CLASS_TV_DIGITAL + dib->i2c_adap.class = I2C_ADAP_CLASS_TV_DIGITAL, +#else + dib->i2c_adap.class = I2C_CLASS_TV_DIGITAL, +#endif + dib->i2c_adap.algo = &dibusb_algo; + dib->i2c_adap.algo_data = NULL; + dib->i2c_adap.id = I2C_ALGO_BIT; + + i2c_set_adapdata(&dib->i2c_adap, dib); + + if ((ret = i2c_add_adapter(&dib->i2c_adap)) < 0) + err("could not add i2c adapter"); + + dib->init_state |= DIBUSB_STATE_I2C; + + return ret; +} + +int dibusb_i2c_exit(struct usb_dibusb *dib) +{ + if (dib->init_state & DIBUSB_STATE_I2C) + i2c_del_adapter(&dib->i2c_adap); + dib->init_state &= ~DIBUSB_STATE_I2C; + return 0; +} + + +/* pll stuff, maybe removed soon (thx to Gerd/Andrew in advance) */ +static int thomson_cable_eu_pll_set(struct dvb_frontend_parameters *fep, u8 pllbuf[4]) +{ + u32 tfreq = (fep->frequency + 36125000) / 62500; + int vu,p0,p1,p2; + + if (fep->frequency > 403250000) + vu = 1, p2 = 1, p1 = 0, p0 = 1; + else if (fep->frequency > 115750000) + vu = 0, p2 = 1, p1 = 1, p0 = 0; + else if (fep->frequency > 44250000) + vu = 0, p2 = 0, p1 = 1, p0 = 1; + else + return -EINVAL; + + pllbuf[0] = (tfreq >> 8) & 0x7f; + pllbuf[1] = tfreq & 0xff; + pllbuf[2] = 0x8e; + pllbuf[3] = (vu << 7) | (p2 << 2) | (p1 << 1) | p0; + return 0; +} + +static int panasonic_cofdm_env57h1xd5_pll_set(struct dvb_frontend_parameters *fep, u8 pllbuf[4]) +{ + u32 freq_khz = fep->frequency / 1000; + u32 tfreq = ((freq_khz + 36125)*6 + 500) / 1000; + u8 TA, T210, R210, ctrl1, cp210, p4321; + if (freq_khz > 858000) { + err("frequency cannot be larger than 858 MHz."); + return -EINVAL; + } + + // contol data 1 : 1 | T/A=1 | T2,T1,T0 = 0,0,0 | R2,R1,R0 = 0,1,0 + TA = 1; + T210 = 0; + R210 = 0x2; + ctrl1 = (1 << 7) | (TA << 6) | (T210 << 3) | R210; + +// ******** CHARGE PUMP CONFIG vs RF FREQUENCIES ***************** + if (freq_khz < 470000) + cp210 = 2; // VHF Low and High band ch E12 to E4 to E12 + else if (freq_khz < 526000) + cp210 = 4; // UHF band Ch E21 to E27 + else // if (freq < 862000000) + cp210 = 5; // UHF band ch E28 to E69 + +//********************* BW select ******************************* + if (freq_khz < 153000) + p4321 = 1; // BW selected for VHF low + else if (freq_khz < 470000) + p4321 = 2; // BW selected for VHF high E5 to E12 + else // if (freq < 862000000) + p4321 = 4; // BW selection for UHF E21 to E69 + + pllbuf[0] = (tfreq >> 8) & 0xff; + pllbuf[1] = (tfreq >> 0) & 0xff; + pllbuf[2] = 0xff & ctrl1; + pllbuf[3] = (cp210 << 5) | (p4321); + + return 0; +} + +/* + * 7 6 5 4 3 2 1 0 + * Address Byte 1 1 0 0 0 MA1 MA0 R/~W=0 + * + * Program divider byte 1 0 n14 n13 n12 n11 n10 n9 n8 + * Program divider byte 2 n7 n6 n5 n4 n3 n2 n1 n0 + * + * Control byte 1 1 T/A=1 T2 T1 T0 R2 R1 R0 + * 1 T/A=0 0 0 ATC AL2 AL1 AL0 + * + * Control byte 2 CP2 CP1 CP0 BS5 BS4 BS3 BS2 BS1 + * + * MA0/1 = programmable address bits + * R/~W = read/write bit (0 for writing) + * N14-0 = programmable LO frequency + * + * T/A = test AGC bit (0 = next 6 bits AGC setting, + * 1 = next 6 bits test and reference divider ratio settings) + * T2-0 = test bits + * R2-0 = reference divider ratio and programmable frequency step + * ATC = AGC current setting and time constant + * ATC = 0: AGC current = 220nA, AGC time constant = 2s + * ATC = 1: AGC current = 9uA, AGC time constant = 50ms + * AL2-0 = AGC take-over point bits + * CP2-0 = charge pump current + * BS5-1 = PMOS ports control bits; + * BSn = 0 corresponding port is off, high-impedance state (at power-on) + * BSn = 1 corresponding port is on + */ +static int panasonic_cofdm_env77h11d5_tda6650_init(struct dvb_frontend *fe, u8 pllbuf[4]) +{ + pllbuf[0] = 0x0b; + pllbuf[1] = 0xf5; + pllbuf[2] = 0x85; + pllbuf[3] = 0xab; + return 0; +} + +static int panasonic_cofdm_env77h11d5_tda6650_set (struct dvb_frontend_parameters *fep,u8 pllbuf[4]) +{ + int tuner_frequency = 0; + u8 band, cp, filter; + + // determine charge pump + tuner_frequency = fep->frequency + 36166000; + if (tuner_frequency < 87000000) + return -EINVAL; + else if (tuner_frequency < 130000000) + cp = 3; + else if (tuner_frequency < 160000000) + cp = 5; + else if (tuner_frequency < 200000000) + cp = 6; + else if (tuner_frequency < 290000000) + cp = 3; + else if (tuner_frequency < 420000000) + cp = 5; + else if (tuner_frequency < 480000000) + cp = 6; + else if (tuner_frequency < 620000000) + cp = 3; + else if (tuner_frequency < 830000000) + cp = 5; + else if (tuner_frequency < 895000000) + cp = 7; + else + return -EINVAL; + + // determine band + if (fep->frequency < 49000000) + return -EINVAL; + else if (fep->frequency < 161000000) + band = 1; + else if (fep->frequency < 444000000) + band = 2; + else if (fep->frequency < 861000000) + band = 4; + else + return -EINVAL; + + // setup PLL filter + switch (fep->u.ofdm.bandwidth) { + case BANDWIDTH_6_MHZ: + case BANDWIDTH_7_MHZ: + filter = 0; + break; + case BANDWIDTH_8_MHZ: + filter = 1; + break; + default: + return -EINVAL; + } + + // calculate divisor + // ((36166000+((1000000/6)/2)) + Finput)/(1000000/6) + tuner_frequency = (((fep->frequency / 1000) * 6) + 217496) / 1000; + + // setup tuner buffer + pllbuf[0] = (tuner_frequency >> 8) & 0x7f; + pllbuf[1] = tuner_frequency & 0xff; + pllbuf[2] = 0xca; + pllbuf[3] = (cp << 5) | (filter << 3) | band; + return 0; +} + +/* + * 7 6 5 4 3 2 1 0 + * Address Byte 1 1 0 0 0 MA1 MA0 R/~W=0 + * + * Program divider byte 1 0 n14 n13 n12 n11 n10 n9 n8 + * Program divider byte 2 n7 n6 n5 n4 n3 n2 n1 n0 + * + * Control byte 1 CP T2 T1 T0 RSA RSB OS + * + * Band Switch byte X X X P4 P3 P2 P1 P0 + * + * Auxiliary byte ATC AL2 AL1 AL0 0 0 0 0 + * + * Address: MA1 MA0 Address + * 0 0 c0 + * 0 1 c2 (always valid) + * 1 0 c4 + * 1 1 c6 + */ +static int lg_tdtp_e102p_tua6034(struct dvb_frontend_parameters* fep, u8 pllbuf[4]) +{ + u32 div; + u8 p210, p3; + +#define TUNER_MUL 62500 + + div = (fep->frequency + 36125000 + TUNER_MUL / 2) / TUNER_MUL; +// div = ((fep->frequency/1000 + 36166) * 6) / 1000; + + if (fep->frequency < 174500000) + p210 = 1; // not supported by the tdtp_e102p + else if (fep->frequency < 230000000) // VHF + p210 = 2; + else + p210 = 4; + + if (fep->u.ofdm.bandwidth == BANDWIDTH_7_MHZ) + p3 = 0; + else + p3 = 1; + + pllbuf[0] = (div >> 8) & 0x7f; + pllbuf[1] = div & 0xff; + pllbuf[2] = 0xce; +// pllbuf[2] = 0xcc; + pllbuf[3] = (p3 << 3) | p210; + + return 0; +} + +static int lg_tdtp_e102p_mt352_demod_init(struct dvb_frontend *fe) +{ + static u8 mt352_clock_config[] = { 0x89, 0xb8, 0x2d }; + static u8 mt352_reset[] = { 0x50, 0x80 }; + static u8 mt352_mclk_ratio[] = { 0x8b, 0x00 }; + static u8 mt352_adc_ctl_1_cfg[] = { 0x8E, 0x40 }; + static u8 mt352_agc_cfg[] = { 0x67, 0x10, 0xa0 }; + + static u8 mt352_sec_agc_cfg1[] = { 0x6a, 0xff }; + static u8 mt352_sec_agc_cfg2[] = { 0x6d, 0xff }; + static u8 mt352_sec_agc_cfg3[] = { 0x70, 0x40 }; + static u8 mt352_sec_agc_cfg4[] = { 0x7b, 0x03 }; + static u8 mt352_sec_agc_cfg5[] = { 0x7d, 0x0f }; + + static u8 mt352_acq_ctl[] = { 0x53, 0x50 }; + static u8 mt352_input_freq_1[] = { 0x56, 0x31, 0x06 }; + + mt352_write(fe, mt352_clock_config, sizeof(mt352_clock_config)); + udelay(2000); + mt352_write(fe, mt352_reset, sizeof(mt352_reset)); + mt352_write(fe, mt352_mclk_ratio, sizeof(mt352_mclk_ratio)); + + mt352_write(fe, mt352_adc_ctl_1_cfg, sizeof(mt352_adc_ctl_1_cfg)); + mt352_write(fe, mt352_agc_cfg, sizeof(mt352_agc_cfg)); + + mt352_write(fe, mt352_sec_agc_cfg1, sizeof(mt352_sec_agc_cfg1)); + mt352_write(fe, mt352_sec_agc_cfg2, sizeof(mt352_sec_agc_cfg2)); + mt352_write(fe, mt352_sec_agc_cfg3, sizeof(mt352_sec_agc_cfg3)); + mt352_write(fe, mt352_sec_agc_cfg4, sizeof(mt352_sec_agc_cfg4)); + mt352_write(fe, mt352_sec_agc_cfg5, sizeof(mt352_sec_agc_cfg5)); + + mt352_write(fe, mt352_acq_ctl, sizeof(mt352_acq_ctl)); + mt352_write(fe, mt352_input_freq_1, sizeof(mt352_input_freq_1)); + + return 0; +} + +static int dibusb_general_demod_init(struct dvb_frontend *fe) +{ + struct usb_dibusb* dib = (struct usb_dibusb*) fe->dvb->priv; + switch (dib->dibdev->dev_cl->id) { + case UMT2_0: + return lg_tdtp_e102p_mt352_demod_init(fe); + default: /* other device classes do not have device specific demod inits */ + break; + } + return 0; +} + +static u8 dibusb_general_pll_addr(struct dvb_frontend *fe) +{ + struct usb_dibusb* dib = (struct usb_dibusb*) fe->dvb->priv; + return dib->tuner->pll_addr; +} + +static int dibusb_pll_i2c_helper(struct usb_dibusb *dib, u8 pll_buf[5], u8 buf[4]) +{ + if (pll_buf == NULL) { + struct i2c_msg msg = { + .addr = dib->tuner->pll_addr, + .flags = 0, + .buf = buf, + .len = sizeof(buf) + }; + if (i2c_transfer (&dib->i2c_adap, &msg, 1) != 1) + return -EIO; + msleep(1); + } else { + pll_buf[0] = dib->tuner->pll_addr << 1; + memcpy(&pll_buf[1],buf,4); + } + + return 0; +} + +static int dibusb_general_pll_init(struct dvb_frontend *fe, + u8 pll_buf[5]) +{ + struct usb_dibusb* dib = (struct usb_dibusb*) fe->dvb->priv; + u8 buf[4]; + int ret=0; + switch (dib->tuner->id) { + case DIBUSB_TUNER_COFDM_PANASONIC_ENV77H11D5: + ret = panasonic_cofdm_env77h11d5_tda6650_init(fe,buf); + break; + default: + break; + } + + if (ret) + return ret; + + return dibusb_pll_i2c_helper(dib,pll_buf,buf); +} + +static int dibusb_general_pll_set(struct dvb_frontend *fe, + struct dvb_frontend_parameters *fep, u8 pll_buf[5]) +{ + struct usb_dibusb* dib = (struct usb_dibusb*) fe->dvb->priv; + u8 buf[4]; + int ret=0; + + switch (dib->tuner->id) { + case DIBUSB_TUNER_CABLE_THOMSON: + ret = thomson_cable_eu_pll_set(fep, buf); + break; + case DIBUSB_TUNER_COFDM_PANASONIC_ENV57H1XD5: + ret = panasonic_cofdm_env57h1xd5_pll_set(fep, buf); + break; + case DIBUSB_TUNER_CABLE_LG_TDTP_E102P: + ret = lg_tdtp_e102p_tua6034(fep, buf); + break; + case DIBUSB_TUNER_COFDM_PANASONIC_ENV77H11D5: + ret = panasonic_cofdm_env77h11d5_tda6650_set(fep,buf); + break; + default: + warn("no pll programming routine found for tuner %d.\n",dib->tuner->id); + ret = -ENODEV; + break; + } + + if (ret) + return ret; + + return dibusb_pll_i2c_helper(dib,pll_buf,buf); +} diff --git a/drivers/media/dvb/dibusb/dvb-dibusb-firmware.c b/drivers/media/dvb/dibusb/dvb-dibusb-firmware.c new file mode 100644 index 00000000000..504ba47afdf --- /dev/null +++ b/drivers/media/dvb/dibusb/dvb-dibusb-firmware.c @@ -0,0 +1,87 @@ +/* + * dvb-dibusb-firmware.c is part of the driver for mobile USB Budget DVB-T devices + * based on reference design made by DiBcom (http://www.dibcom.fr/) + * + * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) + * + * see dvb-dibusb-core.c for more copyright details. + * + * This file contains functions for downloading the firmware to the device. + */ +#include "dvb-dibusb.h" + +#include <linux/firmware.h> +#include <linux/usb.h> + +/* + * load a firmware packet to the device + */ +static int dibusb_writemem(struct usb_device *udev,u16 addr,u8 *data, u8 len) +{ + return usb_control_msg(udev, usb_sndctrlpipe(udev,0), + 0xa0, USB_TYPE_VENDOR, addr, 0x00, data, len, 5000); +} + +int dibusb_loadfirmware(struct usb_device *udev, struct dibusb_usb_device *dibdev) +{ + const struct firmware *fw = NULL; + u16 addr; + u8 *b,*p; + int ret = 0,i; + + if ((ret = request_firmware(&fw, dibdev->dev_cl->firmware, &udev->dev)) != 0) { + err("did not find the firmware file. (%s) " + "Please see linux/Documentation/dvb/ for more details on firmware-problems.", + dibdev->dev_cl->firmware); + return ret; + } + + info("downloading firmware from file '%s'.",dibdev->dev_cl->firmware); + + p = kmalloc(fw->size,GFP_KERNEL); + if (p != NULL) { + u8 reset; + /* + * you cannot use the fw->data as buffer for + * usb_control_msg, a new buffer has to be + * created + */ + memcpy(p,fw->data,fw->size); + + /* stop the CPU */ + reset = 1; + if ((ret = dibusb_writemem(udev,dibdev->dev_cl->usb_ctrl->cpu_cs_register,&reset,1)) != 1) + err("could not stop the USB controller CPU."); + for(i = 0; p[i+3] == 0 && i < fw->size; ) { + b = (u8 *) &p[i]; + addr = *((u16 *) &b[1]); + + ret = dibusb_writemem(udev,addr,&b[4],b[0]); + + if (ret != b[0]) { + err("error while transferring firmware " + "(transferred size: %d, block size: %d)", + ret,b[0]); + ret = -EINVAL; + break; + } + i += 5 + b[0]; + } + /* length in ret */ + if (ret > 0) + ret = 0; + /* restart the CPU */ + reset = 0; + if (ret || dibusb_writemem(udev,dibdev->dev_cl->usb_ctrl->cpu_cs_register,&reset,1) != 1) { + err("could not restart the USB controller CPU."); + ret = -EINVAL; + } + + kfree(p); + } else { + ret = -ENOMEM; + } + release_firmware(fw); + + return ret; +} diff --git a/drivers/media/dvb/dibusb/dvb-dibusb-remote.c b/drivers/media/dvb/dibusb/dvb-dibusb-remote.c new file mode 100644 index 00000000000..9dc8b15517b --- /dev/null +++ b/drivers/media/dvb/dibusb/dvb-dibusb-remote.c @@ -0,0 +1,316 @@ +/* + * dvb-dibusb-remote.c is part of the driver for mobile USB Budget DVB-T devices + * based on reference design made by DiBcom (http://www.dibcom.fr/) + * + * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) + * + * see dvb-dibusb-core.c for more copyright details. + * + * This file contains functions for handling the event device on the software + * side and the remote control on the hardware side. + */ +#include "dvb-dibusb.h" + +/* Table to map raw key codes to key events. This should not be hard-wired + into the kernel. */ +static const struct { u8 c0, c1, c2; uint32_t key; } nec_rc_keys [] = +{ + /* Key codes for the little Artec T1/Twinhan/HAMA/ remote. */ + { 0x00, 0xff, 0x16, KEY_POWER }, + { 0x00, 0xff, 0x10, KEY_MUTE }, + { 0x00, 0xff, 0x03, KEY_1 }, + { 0x00, 0xff, 0x01, KEY_2 }, + { 0x00, 0xff, 0x06, KEY_3 }, + { 0x00, 0xff, 0x09, KEY_4 }, + { 0x00, 0xff, 0x1d, KEY_5 }, + { 0x00, 0xff, 0x1f, KEY_6 }, + { 0x00, 0xff, 0x0d, KEY_7 }, + { 0x00, 0xff, 0x19, KEY_8 }, + { 0x00, 0xff, 0x1b, KEY_9 }, + { 0x00, 0xff, 0x15, KEY_0 }, + { 0x00, 0xff, 0x05, KEY_CHANNELUP }, + { 0x00, 0xff, 0x02, KEY_CHANNELDOWN }, + { 0x00, 0xff, 0x1e, KEY_VOLUMEUP }, + { 0x00, 0xff, 0x0a, KEY_VOLUMEDOWN }, + { 0x00, 0xff, 0x11, KEY_RECORD }, + { 0x00, 0xff, 0x17, KEY_FAVORITES }, /* Heart symbol - Channel list. */ + { 0x00, 0xff, 0x14, KEY_PLAY }, + { 0x00, 0xff, 0x1a, KEY_STOP }, + { 0x00, 0xff, 0x40, KEY_REWIND }, + { 0x00, 0xff, 0x12, KEY_FASTFORWARD }, + { 0x00, 0xff, 0x0e, KEY_PREVIOUS }, /* Recall - Previous channel. */ + { 0x00, 0xff, 0x4c, KEY_PAUSE }, + { 0x00, 0xff, 0x4d, KEY_SCREEN }, /* Full screen mode. */ + { 0x00, 0xff, 0x54, KEY_AUDIO }, /* MTS - Switch to secondary audio. */ + /* additional keys TwinHan VisionPlus, the Artec seemingly not have */ + { 0x00, 0xff, 0x0c, KEY_CANCEL }, /* Cancel */ + { 0x00, 0xff, 0x1c, KEY_EPG }, /* EPG */ + { 0x00, 0xff, 0x00, KEY_TAB }, /* Tab */ + { 0x00, 0xff, 0x48, KEY_INFO }, /* Preview */ + { 0x00, 0xff, 0x04, KEY_LIST }, /* RecordList */ + { 0x00, 0xff, 0x0f, KEY_TEXT }, /* Teletext */ + /* Key codes for the KWorld/ADSTech/JetWay remote. */ + { 0x86, 0x6b, 0x12, KEY_POWER }, + { 0x86, 0x6b, 0x0f, KEY_SELECT }, /* source */ + { 0x86, 0x6b, 0x0c, KEY_UNKNOWN }, /* scan */ + { 0x86, 0x6b, 0x0b, KEY_EPG }, + { 0x86, 0x6b, 0x10, KEY_MUTE }, + { 0x86, 0x6b, 0x01, KEY_1 }, + { 0x86, 0x6b, 0x02, KEY_2 }, + { 0x86, 0x6b, 0x03, KEY_3 }, + { 0x86, 0x6b, 0x04, KEY_4 }, + { 0x86, 0x6b, 0x05, KEY_5 }, + { 0x86, 0x6b, 0x06, KEY_6 }, + { 0x86, 0x6b, 0x07, KEY_7 }, + { 0x86, 0x6b, 0x08, KEY_8 }, + { 0x86, 0x6b, 0x09, KEY_9 }, + { 0x86, 0x6b, 0x0a, KEY_0 }, + { 0x86, 0x6b, 0x18, KEY_ZOOM }, + { 0x86, 0x6b, 0x1c, KEY_UNKNOWN }, /* preview */ + { 0x86, 0x6b, 0x13, KEY_UNKNOWN }, /* snap */ + { 0x86, 0x6b, 0x00, KEY_UNDO }, + { 0x86, 0x6b, 0x1d, KEY_RECORD }, + { 0x86, 0x6b, 0x0d, KEY_STOP }, + { 0x86, 0x6b, 0x0e, KEY_PAUSE }, + { 0x86, 0x6b, 0x16, KEY_PLAY }, + { 0x86, 0x6b, 0x11, KEY_BACK }, + { 0x86, 0x6b, 0x19, KEY_FORWARD }, + { 0x86, 0x6b, 0x14, KEY_UNKNOWN }, /* pip */ + { 0x86, 0x6b, 0x15, KEY_ESC }, + { 0x86, 0x6b, 0x1a, KEY_UP }, + { 0x86, 0x6b, 0x1e, KEY_DOWN }, + { 0x86, 0x6b, 0x1f, KEY_LEFT }, + { 0x86, 0x6b, 0x1b, KEY_RIGHT }, +}; + +/* Hauppauge NOVA-T USB2 keys */ +static const struct { u16 raw; uint32_t key; } haupp_rc_keys [] = { + { 0xddf, KEY_GOTO }, + { 0xdef, KEY_POWER }, + { 0xce7, KEY_TV }, + { 0xcc7, KEY_VIDEO }, + { 0xccf, KEY_AUDIO }, + { 0xcd7, KEY_MEDIA }, + { 0xcdf, KEY_EPG }, + { 0xca7, KEY_UP }, + { 0xc67, KEY_RADIO }, + { 0xcb7, KEY_LEFT }, + { 0xd2f, KEY_OK }, + { 0xcbf, KEY_RIGHT }, + { 0xcff, KEY_BACK }, + { 0xcaf, KEY_DOWN }, + { 0xc6f, KEY_MENU }, + { 0xc87, KEY_VOLUMEUP }, + { 0xc8f, KEY_VOLUMEDOWN }, + { 0xc97, KEY_CHANNEL }, + { 0xc7f, KEY_MUTE }, + { 0xd07, KEY_CHANNELUP }, + { 0xd0f, KEY_CHANNELDOWN }, + { 0xdbf, KEY_RECORD }, + { 0xdb7, KEY_STOP }, + { 0xd97, KEY_REWIND }, + { 0xdaf, KEY_PLAY }, + { 0xda7, KEY_FASTFORWARD }, + { 0xd27, KEY_LAST }, /* Skip backwards */ + { 0xd87, KEY_PAUSE }, + { 0xcf7, KEY_NEXT }, + { 0xc07, KEY_0 }, + { 0xc0f, KEY_1 }, + { 0xc17, KEY_2 }, + { 0xc1f, KEY_3 }, + { 0xc27, KEY_4 }, + { 0xc2f, KEY_5 }, + { 0xc37, KEY_6 }, + { 0xc3f, KEY_7 }, + { 0xc47, KEY_8 }, + { 0xc4f, KEY_9 }, + { 0xc57, KEY_KPASTERISK }, + { 0xc77, KEY_GRAVE }, /* # */ + { 0xc5f, KEY_RED }, + { 0xd77, KEY_GREEN }, + { 0xdc7, KEY_YELLOW }, + { 0xd4f, KEY_BLUE}, +}; + +static int dibusb_key2event_nec(struct usb_dibusb *dib,u8 rb[5]) +{ + int i; + switch (rb[0]) { + case DIBUSB_RC_NEC_KEY_PRESSED: + /* rb[1-3] is the actual key, rb[4] is a checksum */ + deb_rc("raw key code 0x%02x, 0x%02x, 0x%02x, 0x%02x\n", + rb[1], rb[2], rb[3], rb[4]); + + if ((0xff - rb[3]) != rb[4]) { + deb_rc("remote control checksum failed.\n"); + break; + } + + /* See if we can match the raw key code. */ + for (i = 0; i < sizeof(nec_rc_keys)/sizeof(nec_rc_keys[0]); i++) { + if (nec_rc_keys[i].c0 == rb[1] && + nec_rc_keys[i].c1 == rb[2] && + nec_rc_keys[i].c2 == rb[3]) { + + dib->last_event = nec_rc_keys[i].key; + return 1; + } + } + break; + case DIBUSB_RC_NEC_KEY_REPEATED: + /* rb[1]..rb[4] are always zero.*/ + /* Repeats often seem to occur so for the moment just ignore this. */ + return 0; + case DIBUSB_RC_NEC_EMPTY: /* No (more) remote control keys. */ + default: + break; + } + return -1; +} + +static int dibusb_key2event_hauppauge(struct usb_dibusb *dib,u8 rb[4]) +{ + u16 raw; + int i,state; + switch (rb[0]) { + case DIBUSB_RC_HAUPPAUGE_KEY_PRESSED: + raw = ((rb[1] & 0x0f) << 8) | rb[2]; + + state = !!(rb[1] & 0x40); + + deb_rc("raw key code 0x%02x, 0x%02x, 0x%02x to %04x state: %d\n",rb[1],rb[2],rb[3],raw,state); + for (i = 0; i < sizeof(haupp_rc_keys)/sizeof(haupp_rc_keys[0]); i++) { + if (haupp_rc_keys[i].raw == raw) { + if (dib->last_event == haupp_rc_keys[i].key && + dib->last_state == state) { + deb_rc("key repeat\n"); + return 0; + } else { + dib->last_event = haupp_rc_keys[i].key; + dib->last_state = state; + return 1; + } + } + } + + break; + case DIBUSB_RC_HAUPPAUGE_KEY_EMPTY: + default: + break; + } + return -1; +} + +/* + * Read the remote control and feed the appropriate event. + * NEC protocol is used for remote controls + */ +static int dibusb_read_remote_control(struct usb_dibusb *dib) +{ + u8 b[1] = { DIBUSB_REQ_POLL_REMOTE }, rb[5]; + int ret,event = 0; + + if ((ret = dibusb_readwrite_usb(dib,b,1,rb,5))) + return ret; + + switch (dib->dibdev->dev_cl->remote_type) { + case DIBUSB_RC_NEC_PROTOCOL: + event = dibusb_key2event_nec(dib,rb); + break; + case DIBUSB_RC_HAUPPAUGE_PROTO: + event = dibusb_key2event_hauppauge(dib,rb); + default: + break; + } + + /* key repeat */ + if (event == 0) + if (++dib->repeat_key_count < dib->rc_key_repeat_count) { + deb_rc("key repeat dropped. (%d)\n",dib->repeat_key_count); + event = -1; /* skip this key repeat */ + } + + if (event == 1 || event == 0) { + deb_rc("Translated key 0x%04x\n",event); + + /* Signal down and up events for this key. */ + input_report_key(&dib->rc_input_dev, dib->last_event, 1); + input_report_key(&dib->rc_input_dev, dib->last_event, 0); + input_sync(&dib->rc_input_dev); + + if (event == 1) + dib->repeat_key_count = 0; + } + return 0; +} + +/* Remote-control poll function - called every dib->rc_query_interval ms to see + whether the remote control has received anything. */ +static void dibusb_remote_query(void *data) +{ + struct usb_dibusb *dib = (struct usb_dibusb *) data; + /* TODO: need a lock here. We can simply skip checking for the remote control + if we're busy. */ + dibusb_read_remote_control(dib); + schedule_delayed_work(&dib->rc_query_work, + msecs_to_jiffies(dib->rc_query_interval)); +} + +int dibusb_remote_init(struct usb_dibusb *dib) +{ + int i; + + if (dib->dibdev->dev_cl->remote_type == DIBUSB_RC_NO) + return 0; + + /* Initialise the remote-control structures.*/ + init_input_dev(&dib->rc_input_dev); + + dib->rc_input_dev.evbit[0] = BIT(EV_KEY); + dib->rc_input_dev.keycodesize = sizeof(unsigned char); + dib->rc_input_dev.keycodemax = KEY_MAX; + dib->rc_input_dev.name = DRIVER_DESC " remote control"; + + switch (dib->dibdev->dev_cl->remote_type) { + case DIBUSB_RC_NEC_PROTOCOL: + for (i=0; i<sizeof(nec_rc_keys)/sizeof(nec_rc_keys[0]); i++) + set_bit(nec_rc_keys[i].key, dib->rc_input_dev.keybit); + break; + case DIBUSB_RC_HAUPPAUGE_PROTO: + for (i=0; i<sizeof(haupp_rc_keys)/sizeof(haupp_rc_keys[0]); i++) + set_bit(haupp_rc_keys[i].key, dib->rc_input_dev.keybit); + break; + default: + break; + } + + + input_register_device(&dib->rc_input_dev); + + INIT_WORK(&dib->rc_query_work, dibusb_remote_query, dib); + + /* Start the remote-control polling. */ + if (dib->rc_query_interval < 40) + dib->rc_query_interval = 100; /* default */ + + info("schedule remote query interval to %d msecs.",dib->rc_query_interval); + schedule_delayed_work(&dib->rc_query_work,msecs_to_jiffies(dib->rc_query_interval)); + + dib->init_state |= DIBUSB_STATE_REMOTE; + + return 0; +} + +int dibusb_remote_exit(struct usb_dibusb *dib) +{ + if (dib->dibdev->dev_cl->remote_type == DIBUSB_RC_NO) + return 0; + + if (dib->init_state & DIBUSB_STATE_REMOTE) { + cancel_delayed_work(&dib->rc_query_work); + flush_scheduled_work(); + input_unregister_device(&dib->rc_input_dev); + } + dib->init_state &= ~DIBUSB_STATE_REMOTE; + return 0; +} diff --git a/drivers/media/dvb/dibusb/dvb-dibusb-usb.c b/drivers/media/dvb/dibusb/dvb-dibusb-usb.c new file mode 100644 index 00000000000..642f0596a5b --- /dev/null +++ b/drivers/media/dvb/dibusb/dvb-dibusb-usb.c @@ -0,0 +1,303 @@ +/* + * dvb-dibusb-usb.c is part of the driver for mobile USB Budget DVB-T devices + * based on reference design made by DiBcom (http://www.dibcom.fr/) + * + * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) + * + * see dvb-dibusb-core.c for more copyright details. + * + * This file contains functions for initializing and handling the + * usb specific stuff. + */ +#include "dvb-dibusb.h" + +#include <linux/version.h> +#include <linux/pci.h> + +int dibusb_readwrite_usb(struct usb_dibusb *dib, u8 *wbuf, u16 wlen, u8 *rbuf, + u16 rlen) +{ + int actlen,ret = -ENOMEM; + + if (wbuf == NULL || wlen == 0) + return -EINVAL; + + if ((ret = down_interruptible(&dib->usb_sem))) + return ret; + + debug_dump(wbuf,wlen); + + ret = usb_bulk_msg(dib->udev,usb_sndbulkpipe(dib->udev, + dib->dibdev->dev_cl->pipe_cmd), wbuf,wlen,&actlen, + DIBUSB_I2C_TIMEOUT); + + if (ret) + err("bulk message failed: %d (%d/%d)",ret,wlen,actlen); + else + ret = actlen != wlen ? -1 : 0; + + /* an answer is expected, and no error before */ + if (!ret && rbuf && rlen) { + ret = usb_bulk_msg(dib->udev,usb_rcvbulkpipe(dib->udev, + dib->dibdev->dev_cl->pipe_cmd),rbuf,rlen,&actlen, + DIBUSB_I2C_TIMEOUT); + + if (ret) + err("recv bulk message failed: %d",ret); + else { + deb_alot("rlen: %d\n",rlen); + debug_dump(rbuf,actlen); + } + } + + up(&dib->usb_sem); + return ret; +} + +/* + * Cypress controls + */ +int dibusb_write_usb(struct usb_dibusb *dib, u8 *buf, u16 len) +{ + return dibusb_readwrite_usb(dib,buf,len,NULL,0); +} + +#if 0 +/* + * #if 0'ing the following functions as they are not in use _now_, + * but probably will be sometime. + */ +/* + * do not use this, just a workaround for a bug, + * which will hopefully never occur :). + */ +int dibusb_interrupt_read_loop(struct usb_dibusb *dib) +{ + u8 b[1] = { DIBUSB_REQ_INTR_READ }; + return dibusb_write_usb(dib,b,1); +} +#endif + +/* + * ioctl for the firmware + */ +static int dibusb_ioctl_cmd(struct usb_dibusb *dib, u8 cmd, u8 *param, int plen) +{ + u8 b[34]; + int size = plen > 32 ? 32 : plen; + memset(b,0,34); + b[0] = DIBUSB_REQ_SET_IOCTL; + b[1] = cmd; + + if (size > 0) + memcpy(&b[2],param,size); + + return dibusb_write_usb(dib,b,34); //2+size); +} + +/* + * ioctl for power control + */ +int dibusb_hw_wakeup(struct dvb_frontend *fe) +{ + struct usb_dibusb *dib = (struct usb_dibusb *) fe->dvb->priv; + u8 b[1] = { DIBUSB_IOCTL_POWER_WAKEUP }; + deb_info("dibusb-device is getting up.\n"); + + switch (dib->dibdev->dev_cl->id) { + case DTT200U: + break; + default: + dibusb_ioctl_cmd(dib,DIBUSB_IOCTL_CMD_POWER_MODE, b,1); + break; + } + + if (dib->fe_init) + return dib->fe_init(fe); + + return 0; +} + +int dibusb_hw_sleep(struct dvb_frontend *fe) +{ + struct usb_dibusb *dib = (struct usb_dibusb *) fe->dvb->priv; + u8 b[1] = { DIBUSB_IOCTL_POWER_SLEEP }; + deb_info("dibusb-device is going to bed.\n"); + /* workaround, something is wrong, when dibusb 1.1 device are going to bed too late */ + switch (dib->dibdev->dev_cl->id) { + case DIBUSB1_1: + case NOVAT_USB2: + case DTT200U: + break; + default: + dibusb_ioctl_cmd(dib,DIBUSB_IOCTL_CMD_POWER_MODE, b,1); + break; + } + if (dib->fe_sleep) + return dib->fe_sleep(fe); + + return 0; +} + +int dibusb_set_streaming_mode(struct usb_dibusb *dib,u8 mode) +{ + u8 b[2] = { DIBUSB_REQ_SET_STREAMING_MODE, mode }; + return dibusb_readwrite_usb(dib,b,2,NULL,0); +} + +static int dibusb_urb_kill(struct usb_dibusb *dib) +{ + int i; +deb_info("trying to kill urbs\n"); + if (dib->init_state & DIBUSB_STATE_URB_SUBMIT) { + for (i = 0; i < dib->dibdev->dev_cl->urb_count; i++) { + deb_info("killing URB no. %d.\n",i); + + /* stop the URB */ + usb_kill_urb(dib->urb_list[i]); + } + } else + deb_info(" URBs not killed.\n"); + dib->init_state &= ~DIBUSB_STATE_URB_SUBMIT; + return 0; +} + +static int dibusb_urb_submit(struct usb_dibusb *dib) +{ + int i,ret; + if (dib->init_state & DIBUSB_STATE_URB_INIT) { + for (i = 0; i < dib->dibdev->dev_cl->urb_count; i++) { + deb_info("submitting URB no. %d\n",i); + if ((ret = usb_submit_urb(dib->urb_list[i],GFP_ATOMIC))) { + err("could not submit buffer urb no. %d - get them all back\n",i); + dibusb_urb_kill(dib); + return ret; + } + dib->init_state |= DIBUSB_STATE_URB_SUBMIT; + } + } + return 0; +} + +int dibusb_streaming(struct usb_dibusb *dib,int onoff) +{ + if (onoff) + dibusb_urb_submit(dib); + else + dibusb_urb_kill(dib); + + switch (dib->dibdev->dev_cl->id) { + case DIBUSB2_0: + case DIBUSB2_0B: + case NOVAT_USB2: + case UMT2_0: + if (onoff) + return dibusb_ioctl_cmd(dib,DIBUSB_IOCTL_CMD_ENABLE_STREAM,NULL,0); + else + return dibusb_ioctl_cmd(dib,DIBUSB_IOCTL_CMD_DISABLE_STREAM,NULL,0); + break; + default: + break; + } + return 0; +} + +int dibusb_urb_init(struct usb_dibusb *dib) +{ + int i,bufsize,def_pid_parse = 1; + + /* + * when reloading the driver w/o replugging the device + * a timeout occures, this helps + */ + usb_clear_halt(dib->udev,usb_sndbulkpipe(dib->udev,dib->dibdev->dev_cl->pipe_cmd)); + usb_clear_halt(dib->udev,usb_rcvbulkpipe(dib->udev,dib->dibdev->dev_cl->pipe_cmd)); + usb_clear_halt(dib->udev,usb_rcvbulkpipe(dib->udev,dib->dibdev->dev_cl->pipe_data)); + + /* allocate the array for the data transfer URBs */ + dib->urb_list = kmalloc(dib->dibdev->dev_cl->urb_count*sizeof(struct urb *),GFP_KERNEL); + if (dib->urb_list == NULL) + return -ENOMEM; + memset(dib->urb_list,0,dib->dibdev->dev_cl->urb_count*sizeof(struct urb *)); + + dib->init_state |= DIBUSB_STATE_URB_LIST; + + bufsize = dib->dibdev->dev_cl->urb_count*dib->dibdev->dev_cl->urb_buffer_size; + deb_info("allocate %d bytes as buffersize for all URBs\n",bufsize); + /* allocate the actual buffer for the URBs */ + if ((dib->buffer = pci_alloc_consistent(NULL,bufsize,&dib->dma_handle)) == NULL) { + deb_info("not enough memory.\n"); + return -ENOMEM; + } + deb_info("allocation complete\n"); + memset(dib->buffer,0,bufsize); + + dib->init_state |= DIBUSB_STATE_URB_BUF; + + /* allocate and submit the URBs */ + for (i = 0; i < dib->dibdev->dev_cl->urb_count; i++) { + if (!(dib->urb_list[i] = usb_alloc_urb(0,GFP_ATOMIC))) { + return -ENOMEM; + } + + usb_fill_bulk_urb( dib->urb_list[i], dib->udev, + usb_rcvbulkpipe(dib->udev,dib->dibdev->dev_cl->pipe_data), + &dib->buffer[i*dib->dibdev->dev_cl->urb_buffer_size], + dib->dibdev->dev_cl->urb_buffer_size, + dibusb_urb_complete, dib); + + dib->urb_list[i]->transfer_flags = 0; + + dib->init_state |= DIBUSB_STATE_URB_INIT; + } + + /* dib->pid_parse here contains the value of the module parameter */ + /* decide if pid parsing can be deactivated: + * is possible (by device type) and wanted (by user) + */ + switch (dib->dibdev->dev_cl->id) { + case DIBUSB2_0: + case DIBUSB2_0B: + if (dib->udev->speed == USB_SPEED_HIGH && !dib->pid_parse) { + def_pid_parse = 0; + info("running at HIGH speed, will deliver the complete TS."); + } else + info("will use pid_parsing."); + break; + default: + break; + } + /* from here on it contains the device and user decision */ + dib->pid_parse = def_pid_parse; + + return 0; +} + +int dibusb_urb_exit(struct usb_dibusb *dib) +{ + int i; + + dibusb_urb_kill(dib); + + if (dib->init_state & DIBUSB_STATE_URB_LIST) { + for (i = 0; i < dib->dibdev->dev_cl->urb_count; i++) { + if (dib->urb_list[i] != NULL) { + deb_info("freeing URB no. %d.\n",i); + /* free the URBs */ + usb_free_urb(dib->urb_list[i]); + } + } + /* free the urb array */ + kfree(dib->urb_list); + dib->init_state &= ~DIBUSB_STATE_URB_LIST; + } + + if (dib->init_state & DIBUSB_STATE_URB_BUF) + pci_free_consistent(NULL, + dib->dibdev->dev_cl->urb_buffer_size*dib->dibdev->dev_cl->urb_count, + dib->buffer,dib->dma_handle); + + dib->init_state &= ~DIBUSB_STATE_URB_BUF; + dib->init_state &= ~DIBUSB_STATE_URB_INIT; + return 0; +} diff --git a/drivers/media/dvb/dibusb/dvb-dibusb.h b/drivers/media/dvb/dibusb/dvb-dibusb.h new file mode 100644 index 00000000000..52cd35dd9d8 --- /dev/null +++ b/drivers/media/dvb/dibusb/dvb-dibusb.h @@ -0,0 +1,327 @@ +/* + * dvb-dibusb.h + * + * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) + * + * 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. + * + * for more information see dvb-dibusb-core.c . + */ +#ifndef __DVB_DIBUSB_H__ +#define __DVB_DIBUSB_H__ + +#include <linux/input.h> +#include <linux/config.h> +#include <linux/usb.h> + +#include "dvb_frontend.h" +#include "dvb_demux.h" +#include "dvb_net.h" +#include "dmxdev.h" + +#include "dib3000.h" +#include "mt352.h" + +/* debug */ +#ifdef CONFIG_DVB_DIBCOM_DEBUG +#define dprintk(level,args...) \ + do { if ((dvb_dibusb_debug & level)) { printk(args); } } while (0) + +#define debug_dump(b,l) {\ + int i; \ + for (i = 0; i < l; i++) deb_xfer("%02x ", b[i]); \ + deb_xfer("\n");\ +} + +#else +#define dprintk(args...) +#define debug_dump(b,l) +#endif + +extern int dvb_dibusb_debug; + +/* Version information */ +#define DRIVER_VERSION "0.3" +#define DRIVER_DESC "DiBcom based USB Budget DVB-T device" +#define DRIVER_AUTHOR "Patrick Boettcher, patrick.boettcher@desy.de" + +#define deb_info(args...) dprintk(0x01,args) +#define deb_xfer(args...) dprintk(0x02,args) +#define deb_alot(args...) dprintk(0x04,args) +#define deb_ts(args...) dprintk(0x08,args) +#define deb_err(args...) dprintk(0x10,args) +#define deb_rc(args...) dprintk(0x20,args) + +/* generic log methods - taken from usb.h */ +#undef err +#define err(format, arg...) printk(KERN_ERR "dvb-dibusb: " format "\n" , ## arg) +#undef info +#define info(format, arg...) printk(KERN_INFO "dvb-dibusb: " format "\n" , ## arg) +#undef warn +#define warn(format, arg...) printk(KERN_WARNING "dvb-dibusb: " format "\n" , ## arg) + +struct dibusb_usb_controller { + const char *name; /* name of the usb controller */ + u16 cpu_cs_register; /* needs to be restarted, when the firmware has been downloaded. */ +}; + +typedef enum { + DIBUSB1_1 = 0, + DIBUSB1_1_AN2235, + DIBUSB2_0, + UMT2_0, + DIBUSB2_0B, + NOVAT_USB2, + DTT200U, +} dibusb_class_t; + +typedef enum { + DIBUSB_TUNER_CABLE_THOMSON = 0, + DIBUSB_TUNER_COFDM_PANASONIC_ENV57H1XD5, + DIBUSB_TUNER_CABLE_LG_TDTP_E102P, + DIBUSB_TUNER_COFDM_PANASONIC_ENV77H11D5, +} dibusb_tuner_t; + +typedef enum { + DIBUSB_DIB3000MB = 0, + DIBUSB_DIB3000MC, + DIBUSB_MT352, + DTT200U_FE, +} dibusb_demodulator_t; + +typedef enum { + DIBUSB_RC_NO = 0, + DIBUSB_RC_NEC_PROTOCOL, + DIBUSB_RC_HAUPPAUGE_PROTO, +} dibusb_remote_t; + +struct dibusb_tuner { + dibusb_tuner_t id; + + u8 pll_addr; /* tuner i2c address */ +}; +extern struct dibusb_tuner dibusb_tuner[]; + +#define DIBUSB_POSSIBLE_I2C_ADDR_NUM 4 +struct dibusb_demod { + dibusb_demodulator_t id; + + int pid_filter_count; /* counter of the internal pid_filter */ + u8 i2c_addrs[DIBUSB_POSSIBLE_I2C_ADDR_NUM]; /* list of possible i2c addresses of the demod */ +}; + +#define DIBUSB_MAX_TUNER_NUM 2 +struct dibusb_device_class { + dibusb_class_t id; + + const struct dibusb_usb_controller *usb_ctrl; /* usb controller */ + const char *firmware; /* valid firmware filenames */ + + int pipe_cmd; /* command pipe (read/write) */ + int pipe_data; /* data pipe */ + + int urb_count; /* number of data URBs to be submitted */ + int urb_buffer_size; /* the size of the buffer for each URB */ + + dibusb_remote_t remote_type; /* does this device have a ir-receiver */ + + struct dibusb_demod *demod; /* which demodulator is mount */ + struct dibusb_tuner *tuner; /* which tuner can be found here */ +}; + +#define DIBUSB_ID_MAX_NUM 15 +struct dibusb_usb_device { + const char *name; /* real name of the box */ + struct dibusb_device_class *dev_cl; /* which dibusb_device_class is this device part of */ + + struct usb_device_id *cold_ids[DIBUSB_ID_MAX_NUM]; /* list of USB ids when this device is at pre firmware state */ + struct usb_device_id *warm_ids[DIBUSB_ID_MAX_NUM]; /* list of USB ids when this device is at post firmware state */ +}; + +/* a PID for the pid_filter list, when in use */ +struct dibusb_pid +{ + int index; + u16 pid; + int active; +}; + +struct usb_dibusb { + /* usb */ + struct usb_device * udev; + + struct dibusb_usb_device * dibdev; + +#define DIBUSB_STATE_INIT 0x000 +#define DIBUSB_STATE_URB_LIST 0x001 +#define DIBUSB_STATE_URB_BUF 0x002 +#define DIBUSB_STATE_URB_INIT 0x004 +#define DIBUSB_STATE_DVB 0x008 +#define DIBUSB_STATE_I2C 0x010 +#define DIBUSB_STATE_REMOTE 0x020 +#define DIBUSB_STATE_URB_SUBMIT 0x040 + int init_state; + + int feedcount; + struct dib_fe_xfer_ops xfer_ops; + + struct dibusb_tuner *tuner; + + struct urb **urb_list; + u8 *buffer; + dma_addr_t dma_handle; + + /* I2C */ + struct i2c_adapter i2c_adap; + + /* locking */ + struct semaphore usb_sem; + struct semaphore i2c_sem; + + /* dvb */ + struct dvb_adapter *adapter; + struct dmxdev dmxdev; + struct dvb_demux demux; + struct dvb_net dvb_net; + struct dvb_frontend* fe; + + int (*fe_sleep) (struct dvb_frontend *); + int (*fe_init) (struct dvb_frontend *); + + /* remote control */ + struct input_dev rc_input_dev; + struct work_struct rc_query_work; + int last_event; + int last_state; /* for Hauppauge RC protocol */ + int repeat_key_count; + int rc_key_repeat_count; /* module parameter */ + + /* module parameters */ + int pid_parse; + int rc_query_interval; +}; + +/* commonly used functions in the separated files */ + +/* dvb-dibusb-firmware.c */ +int dibusb_loadfirmware(struct usb_device *udev, struct dibusb_usb_device *dibdev); + +/* dvb-dibusb-remote.c */ +int dibusb_remote_exit(struct usb_dibusb *dib); +int dibusb_remote_init(struct usb_dibusb *dib); + +/* dvb-dibusb-fe-i2c.c */ +int dibusb_fe_init(struct usb_dibusb* dib); +int dibusb_fe_exit(struct usb_dibusb *dib); +int dibusb_i2c_init(struct usb_dibusb *dib); +int dibusb_i2c_exit(struct usb_dibusb *dib); + +/* dvb-dibusb-dvb.c */ +void dibusb_urb_complete(struct urb *urb, struct pt_regs *ptregs); +int dibusb_dvb_init(struct usb_dibusb *dib); +int dibusb_dvb_exit(struct usb_dibusb *dib); + +/* dvb-dibusb-usb.c */ +int dibusb_readwrite_usb(struct usb_dibusb *dib, u8 *wbuf, u16 wlen, u8 *rbuf, + u16 rlen); +int dibusb_write_usb(struct usb_dibusb *dib, u8 *buf, u16 len); + +int dibusb_hw_wakeup(struct dvb_frontend *); +int dibusb_hw_sleep(struct dvb_frontend *); +int dibusb_set_streaming_mode(struct usb_dibusb *,u8); +int dibusb_streaming(struct usb_dibusb *,int); + +int dibusb_urb_init(struct usb_dibusb *); +int dibusb_urb_exit(struct usb_dibusb *); + +/* dvb-fe-dtt200u.c */ +struct dvb_frontend* dtt200u_fe_attach(struct usb_dibusb *,struct dib_fe_xfer_ops *); + +/* i2c and transfer stuff */ +#define DIBUSB_I2C_TIMEOUT 5000 + +/* + * protocol of all dibusb related devices + */ + +/* + * bulk msg to/from endpoint 0x01 + * + * general structure: + * request_byte parameter_bytes + */ + +#define DIBUSB_REQ_START_READ 0x00 +#define DIBUSB_REQ_START_DEMOD 0x01 + +/* + * i2c read + * bulk write: 0x02 ((7bit i2c_addr << 1) & 0x01) register_bytes length_word + * bulk read: byte_buffer (length_word bytes) + */ +#define DIBUSB_REQ_I2C_READ 0x02 + +/* + * i2c write + * bulk write: 0x03 (7bit i2c_addr << 1) register_bytes value_bytes + */ +#define DIBUSB_REQ_I2C_WRITE 0x03 + +/* + * polling the value of the remote control + * bulk write: 0x04 + * bulk read: byte_buffer (5 bytes) + * + * first byte of byte_buffer shows the status (0x00, 0x01, 0x02) + */ +#define DIBUSB_REQ_POLL_REMOTE 0x04 + +#define DIBUSB_RC_NEC_EMPTY 0x00 +#define DIBUSB_RC_NEC_KEY_PRESSED 0x01 +#define DIBUSB_RC_NEC_KEY_REPEATED 0x02 + +/* additional status values for Hauppauge Remote Control Protocol */ +#define DIBUSB_RC_HAUPPAUGE_KEY_PRESSED 0x01 +#define DIBUSB_RC_HAUPPAUGE_KEY_EMPTY 0x03 + +/* streaming mode: + * bulk write: 0x05 mode_byte + * + * mode_byte is mostly 0x00 + */ +#define DIBUSB_REQ_SET_STREAMING_MODE 0x05 + +/* interrupt the internal read loop, when blocking */ +#define DIBUSB_REQ_INTR_READ 0x06 + +/* io control + * 0x07 cmd_byte param_bytes + * + * param_bytes can be up to 32 bytes + * + * cmd_byte function parameter name + * 0x00 power mode + * 0x00 sleep + * 0x01 wakeup + * + * 0x01 enable streaming + * 0x02 disable streaming + * + * + */ +#define DIBUSB_REQ_SET_IOCTL 0x07 + +/* IOCTL commands */ + +/* change the power mode in firmware */ +#define DIBUSB_IOCTL_CMD_POWER_MODE 0x00 +#define DIBUSB_IOCTL_POWER_SLEEP 0x00 +#define DIBUSB_IOCTL_POWER_WAKEUP 0x01 + +/* modify streaming of the FX2 */ +#define DIBUSB_IOCTL_CMD_ENABLE_STREAM 0x01 +#define DIBUSB_IOCTL_CMD_DISABLE_STREAM 0x02 + +#endif diff --git a/drivers/media/dvb/dibusb/dvb-fe-dtt200u.c b/drivers/media/dvb/dibusb/dvb-fe-dtt200u.c new file mode 100644 index 00000000000..1872aa6d200 --- /dev/null +++ b/drivers/media/dvb/dibusb/dvb-fe-dtt200u.c @@ -0,0 +1,263 @@ +/* + * dvb-dtt200u-fe.c is a driver which implements the frontend-part of the + * Yakumo/Typhoon/Hama USB2.0 boxes. It is hard-wired to the dibusb-driver as + * it uses the usb-transfer functions directly (maybe creating a + * generic-dvb-usb-lib for all usb-drivers will be reduce some more code.) + * + * Copyright (C) 2005 Patrick Boettcher <patrick.boettcher@desy.de> + * + * see dvb-dibusb-core.c for copyright details. + */ + +/* guessed protocol description (reverse engineered): + * read + * 00 - USB type 0x02 for usb2.0, 0x01 for usb1.1 + * 81 - <TS_LOCK> <current frequency divided by 250000> + * 82 - crash - do not touch + * 83 - crash - do not touch + * 84 - remote control + * 85 - crash - do not touch (OK, stop testing here) + * 88 - locking 2 bytes (0x80 0x40 == no signal, 0x89 0x20 == nice signal) + * 89 - noise-to-signal + * 8a - unkown 1 byte - signal_strength + * 8c - ber ??? + * 8d - ber + * 8e - unc + * + * write + * 02 - bandwidth + * 03 - frequency (divided by 250000) + * 04 - pid table (index pid(7:0) pid(12:8)) + * 05 - reset the pid table + * 08 - demod transfer enabled or not (FX2 transfer is enabled by default) + */ + +#include "dvb-dibusb.h" +#include "dvb_frontend.h" + +struct dtt200u_fe_state { + struct usb_dibusb *dib; + + struct dvb_frontend_parameters fep; + struct dvb_frontend frontend; +}; + +#define moan(which,what) info("unexpected value in '%s' for cmd '%02x' - please report to linux-dvb@linuxtv.org",which,what) + +static int dtt200u_fe_read_status(struct dvb_frontend* fe, fe_status_t *stat) +{ + struct dtt200u_fe_state *state = fe->demodulator_priv; + u8 bw[1] = { 0x81 }; + u8 br[3] = { 0 }; +// u8 bdeb[5] = { 0 }; + + dibusb_readwrite_usb(state->dib,bw,1,br,3); + switch (br[0]) { + case 0x01: + *stat = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; + break; + case 0x00: + *stat = 0; + break; + default: + moan("br[0]",0x81); + break; + } + +// bw[0] = 0x88; +// dibusb_readwrite_usb(state->dib,bw,1,bdeb,5); + +// deb_info("%02x: %02x %02x %02x %02x %02x\n",bw[0],bdeb[0],bdeb[1],bdeb[2],bdeb[3],bdeb[4]); + + return 0; +} +static int dtt200u_fe_read_ber(struct dvb_frontend* fe, u32 *ber) +{ + struct dtt200u_fe_state *state = fe->demodulator_priv; + u8 bw[1] = { 0x8d }; + *ber = 0; + dibusb_readwrite_usb(state->dib,bw,1,(u8*) ber, 3); + return 0; +} + +static int dtt200u_fe_read_unc_blocks(struct dvb_frontend* fe, u32 *unc) +{ + struct dtt200u_fe_state *state = fe->demodulator_priv; + u8 bw[1] = { 0x8c }; + *unc = 0; + dibusb_readwrite_usb(state->dib,bw,1,(u8*) unc, 3); + return 0; +} + +static int dtt200u_fe_read_signal_strength(struct dvb_frontend* fe, u16 *strength) +{ + struct dtt200u_fe_state *state = fe->demodulator_priv; + u8 bw[1] = { 0x8a }; + u8 b; + dibusb_readwrite_usb(state->dib,bw,1,&b, 1); + *strength = (b << 8) | b; + return 0; +} + +static int dtt200u_fe_read_snr(struct dvb_frontend* fe, u16 *snr) +{ + struct dtt200u_fe_state *state = fe->demodulator_priv; + u8 bw[1] = { 0x89 }; + u8 br[1] = { 0 }; + dibusb_readwrite_usb(state->dib,bw,1,br,1); + *snr = ((0xff - br[0]) << 8) | (0xff - br[0]); + return 0; +} + +static int dtt200u_fe_init(struct dvb_frontend* fe) +{ + struct dtt200u_fe_state *state = fe->demodulator_priv; + u8 b[] = { 0x01 }; + return dibusb_write_usb(state->dib,b,1); +} + +static int dtt200u_fe_sleep(struct dvb_frontend* fe) +{ + return dtt200u_fe_init(fe); +} + +static int dtt200u_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune) +{ + tune->min_delay_ms = 1500; + tune->step_size = 166667; + tune->max_drift = 166667 * 2; + return 0; +} + +static int dtt200u_fe_set_frontend(struct dvb_frontend* fe, + struct dvb_frontend_parameters *fep) +{ + struct dtt200u_fe_state *state = fe->demodulator_priv; + u16 freq = fep->frequency / 250000; + u8 bw,bwbuf[2] = { 0x03, 0 }, freqbuf[3] = { 0x02, 0, 0 }; + + switch (fep->u.ofdm.bandwidth) { + case BANDWIDTH_8_MHZ: bw = 8; break; + case BANDWIDTH_7_MHZ: bw = 7; break; + case BANDWIDTH_6_MHZ: bw = 6; break; + case BANDWIDTH_AUTO: return -EOPNOTSUPP; + default: + return -EINVAL; + } + deb_info("set_frontend\n"); + + bwbuf[1] = bw; + dibusb_write_usb(state->dib,bwbuf,2); + + freqbuf[1] = freq & 0xff; + freqbuf[2] = (freq >> 8) & 0xff; + dibusb_write_usb(state->dib,freqbuf,3); + + memcpy(&state->fep,fep,sizeof(struct dvb_frontend_parameters)); + + return 0; +} + +static int dtt200u_fe_get_frontend(struct dvb_frontend* fe, + struct dvb_frontend_parameters *fep) +{ + struct dtt200u_fe_state *state = fe->demodulator_priv; + memcpy(fep,&state->fep,sizeof(struct dvb_frontend_parameters)); + return 0; +} + +static void dtt200u_fe_release(struct dvb_frontend* fe) +{ + struct dtt200u_fe_state *state = (struct dtt200u_fe_state*) fe->demodulator_priv; + kfree(state); +} + +static int dtt200u_pid_control(struct dvb_frontend *fe,int index, int pid,int onoff) +{ + struct dtt200u_fe_state *state = (struct dtt200u_fe_state*) fe->demodulator_priv; + u8 b_pid[4]; + pid = onoff ? pid : 0; + + b_pid[0] = 0x04; + b_pid[1] = index; + b_pid[2] = pid & 0xff; + b_pid[3] = (pid >> 8) & 0xff; + + dibusb_write_usb(state->dib,b_pid,4); + return 0; +} + +static int dtt200u_fifo_control(struct dvb_frontend *fe, int onoff) +{ + struct dtt200u_fe_state *state = (struct dtt200u_fe_state*) fe->demodulator_priv; + u8 b_streaming[2] = { 0x08, onoff }; + u8 b_rst_pid[1] = { 0x05 }; + + dibusb_write_usb(state->dib,b_streaming,2); + + if (!onoff) + dibusb_write_usb(state->dib,b_rst_pid,1); + return 0; +} + +static struct dvb_frontend_ops dtt200u_fe_ops; + +struct dvb_frontend* dtt200u_fe_attach(struct usb_dibusb *dib, struct dib_fe_xfer_ops *xfer_ops) +{ + struct dtt200u_fe_state* state = NULL; + + /* allocate memory for the internal state */ + state = (struct dtt200u_fe_state*) kmalloc(sizeof(struct dtt200u_fe_state), GFP_KERNEL); + if (state == NULL) + goto error; + memset(state,0,sizeof(struct dtt200u_fe_state)); + + deb_info("attaching frontend dtt200u\n"); + + state->dib = dib; + + state->frontend.ops = &dtt200u_fe_ops; + state->frontend.demodulator_priv = state; + + xfer_ops->fifo_ctrl = dtt200u_fifo_control; + xfer_ops->pid_ctrl = dtt200u_pid_control; + + goto success; +error: + return NULL; +success: + return &state->frontend; +} + +static struct dvb_frontend_ops dtt200u_fe_ops = { + .info = { + .name = "DTT200U (Yakumo/Typhoon/Hama) DVB-T", + .type = FE_OFDM, + .frequency_min = 44250000, + .frequency_max = 867250000, + .frequency_stepsize = 250000, + .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 = dtt200u_fe_release, + + .init = dtt200u_fe_init, + .sleep = dtt200u_fe_sleep, + + .set_frontend = dtt200u_fe_set_frontend, + .get_frontend = dtt200u_fe_get_frontend, + .get_tune_settings = dtt200u_fe_get_tune_settings, + + .read_status = dtt200u_fe_read_status, + .read_ber = dtt200u_fe_read_ber, + .read_signal_strength = dtt200u_fe_read_signal_strength, + .read_snr = dtt200u_fe_read_snr, + .read_ucblocks = dtt200u_fe_read_unc_blocks, +}; |