diff options
author | John W. Linville <linville@tuxdriver.com> | 2011-11-22 20:05:46 +0100 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2011-11-22 20:05:46 +0100 |
commit | 515db09338704a6ad7d27b5f1e33820d3052edd2 (patch) | |
tree | 74f915531710303397d34069b325c2be7a5ac93c /drivers/media/dvb/dvb-usb | |
parent | mac80211: fix RCU warnings in mesh (diff) | |
parent | Linux 3.2-rc2 (diff) | |
download | linux-515db09338704a6ad7d27b5f1e33820d3052edd2.tar.xz linux-515db09338704a6ad7d27b5f1e33820d3052edd2.zip |
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux
Conflicts:
drivers/net/wireless/iwlegacy/iwl-debugfs.c
drivers/net/wireless/iwlegacy/iwl-rx.c
drivers/net/wireless/iwlegacy/iwl-scan.c
drivers/net/wireless/iwlegacy/iwl-tx.c
include/net/bluetooth/bluetooth.h
Diffstat (limited to 'drivers/media/dvb/dvb-usb')
57 files changed, 8163 insertions, 626 deletions
diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig index 5d73dec8ac07..58257165761e 100644 --- a/drivers/media/dvb/dvb-usb/Kconfig +++ b/drivers/media/dvb/dvb-usb/Kconfig @@ -258,6 +258,19 @@ config DVB_USB_AF9005_REMOTE Say Y here to support the default remote control decoding for the Afatech AF9005 based receiver. +config DVB_USB_PCTV452E + tristate "Pinnacle PCTV HDTV Pro USB device/TT Connect S2-3600" + depends on DVB_USB + select TTPCI_EEPROM + select DVB_LNBP22 if !DVB_FE_CUSTOMISE + select DVB_STB0899 if !DVB_FE_CUSTOMISE + select DVB_STB6100 if !DVB_FE_CUSTOMISE + help + Support for external USB adapter designed by Pinnacle, + shipped under the brand name 'PCTV HDTV Pro USB'. + Also supports TT Connect S2-3600/3650 cards. + Say Y if you own such a device and want to use it. + config DVB_USB_DW2102 tristate "DvbWorld & TeVii DVB-S/S2 USB2.0 support" depends on DVB_USB @@ -374,3 +387,18 @@ config DVB_USB_TECHNISAT_USB2 select DVB_STV6110x if !DVB_FE_CUSTOMISE help Say Y here to support the Technisat USB2 DVB-S/S2 device + +config DVB_USB_IT913X + tristate "it913x driver" + depends on DVB_USB + select DVB_IT913X_FE + help + Say Y here to support the it913x device + +config DVB_USB_MXL111SF + tristate "MxL111SF DTV USB2.0 support" + depends on DVB_USB + select DVB_LGDT3305 if !DVB_FE_CUSTOMISE + select VIDEO_TVEEPROM + help + Say Y here to support the MxL111SF USB2.0 DTV receiver. diff --git a/drivers/media/dvb/dvb-usb/Makefile b/drivers/media/dvb/dvb-usb/Makefile index 4bac13da0c39..26c8b9e57050 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-pctv452e-objs = pctv452e.o +obj-$(CONFIG_DVB_USB_PCTV452E) += dvb-usb-pctv452e.o + dvb-usb-dw2102-objs = dw2102.o obj-$(CONFIG_DVB_USB_DW2102) += dvb-usb-dw2102.o @@ -94,7 +97,16 @@ obj-$(CONFIG_DVB_USB_LME2510) += dvb-usb-lmedm04.o dvb-usb-technisat-usb2-objs = technisat-usb2.o obj-$(CONFIG_DVB_USB_TECHNISAT_USB2) += dvb-usb-technisat-usb2.o -EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/ +dvb-usb-it913x-objs := it913x.o +obj-$(CONFIG_DVB_USB_IT913X) += dvb-usb-it913x.o + +dvb-usb-mxl111sf-objs = mxl111sf.o mxl111sf-phy.o mxl111sf-i2c.o mxl111sf-gpio.o +obj-$(CONFIG_DVB_USB_MXL111SF) += dvb-usb-mxl111sf.o +obj-$(CONFIG_DVB_USB_MXL111SF) += mxl111sf-demod.o +obj-$(CONFIG_DVB_USB_MXL111SF) += mxl111sf-tuner.o + +ccflags-y += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/ # due to tuner-xc3028 -EXTRA_CFLAGS += -Idrivers/media/common/tuners +ccflags-y += -Idrivers/media/common/tuners +EXTRA_CFLAGS += -Idrivers/media/dvb/ttpci diff --git a/drivers/media/dvb/dvb-usb/a800.c b/drivers/media/dvb/dvb-usb/a800.c index b95a95e17840..2aef3c89e9fa 100644 --- a/drivers/media/dvb/dvb-usb/a800.c +++ b/drivers/media/dvb/dvb-usb/a800.c @@ -127,6 +127,8 @@ static struct dvb_usb_device_properties a800_properties = { .num_adapters = 1, .adapter = { { + .num_frontends = 1, + .fe = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, .pid_filter_count = 32, .streaming_ctrl = dibusb2_0_streaming_ctrl, @@ -147,7 +149,7 @@ static struct dvb_usb_device_properties a800_properties = { } } }, - + }}, .size_of_priv = sizeof(struct dibusb_state), }, }, diff --git a/drivers/media/dvb/dvb-usb/af9005-fe.c b/drivers/media/dvb/dvb-usb/af9005-fe.c index 6ad94745bbdd..3263e9749d09 100644 --- a/drivers/media/dvb/dvb-usb/af9005-fe.c +++ b/drivers/media/dvb/dvb-usb/af9005-fe.c @@ -63,11 +63,9 @@ static int af9005_write_word_agc(struct dvb_usb_device *d, u16 reghi, u16 reglo, u8 pos, u8 len, u16 value) { int ret; - u8 temp; if ((ret = af9005_write_ofdm_register(d, reglo, (u8) (value & 0xff)))) return ret; - temp = (u8) ((value & 0x0300) >> 8); return af9005_write_register_bits(d, reghi, pos, len, (u8) ((value & 0x300) >> 8)); } diff --git a/drivers/media/dvb/dvb-usb/af9005.c b/drivers/media/dvb/dvb-usb/af9005.c index 0351c0e52dd2..bd51a764351b 100644 --- a/drivers/media/dvb/dvb-usb/af9005.c +++ b/drivers/media/dvb/dvb-usb/af9005.c @@ -815,7 +815,7 @@ static int af9005_frontend_attach(struct dvb_usb_adapter *adap) debug_dump(buf, 8, printk); } } - adap->fe = af9005_fe_attach(adap->dev); + adap->fe_adap[0].fe = af9005_fe_attach(adap->dev); return 0; } @@ -999,6 +999,8 @@ static struct dvb_usb_device_properties af9005_properties = { .num_adapters = 1, .adapter = { { + .num_frontends = 1, + .fe = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, @@ -1018,6 +1020,7 @@ static struct dvb_usb_device_properties af9005_properties = { } } }, + }}, } }, .power_ctrl = af9005_power_ctrl, diff --git a/drivers/media/dvb/dvb-usb/af9015.c b/drivers/media/dvb/dvb-usb/af9015.c index d7ad05fc383b..c6c275bac08e 100644 --- a/drivers/media/dvb/dvb-usb/af9015.c +++ b/drivers/media/dvb/dvb-usb/af9015.c @@ -744,29 +744,31 @@ static const struct af9015_rc_setup af9015_rc_setup_hashes[] = { }; static const struct af9015_rc_setup af9015_rc_setup_usbids[] = { - { (USB_VID_TERRATEC << 16) + USB_PID_TERRATEC_CINERGY_T_STICK_RC, + { (USB_VID_TERRATEC << 16) | USB_PID_TERRATEC_CINERGY_T_STICK_RC, RC_MAP_TERRATEC_SLIM_2 }, - { (USB_VID_TERRATEC << 16) + USB_PID_TERRATEC_CINERGY_T_STICK_DUAL_RC, + { (USB_VID_TERRATEC << 16) | USB_PID_TERRATEC_CINERGY_T_STICK_DUAL_RC, RC_MAP_TERRATEC_SLIM }, - { (USB_VID_VISIONPLUS << 16) + USB_PID_AZUREWAVE_AD_TU700, + { (USB_VID_VISIONPLUS << 16) | USB_PID_AZUREWAVE_AD_TU700, RC_MAP_AZUREWAVE_AD_TU700 }, - { (USB_VID_VISIONPLUS << 16) + USB_PID_TINYTWIN, + { (USB_VID_VISIONPLUS << 16) | USB_PID_TINYTWIN, RC_MAP_AZUREWAVE_AD_TU700 }, - { (USB_VID_MSI_2 << 16) + USB_PID_MSI_DIGI_VOX_MINI_III, + { (USB_VID_MSI_2 << 16) | USB_PID_MSI_DIGI_VOX_MINI_III, RC_MAP_MSI_DIGIVOX_III }, - { (USB_VID_MSI_2 << 16) + USB_PID_MSI_DIGIVOX_DUO, + { (USB_VID_MSI_2 << 16) | USB_PID_MSI_DIGIVOX_DUO, RC_MAP_MSI_DIGIVOX_III }, - { (USB_VID_LEADTEK << 16) + USB_PID_WINFAST_DTV_DONGLE_GOLD, + { (USB_VID_LEADTEK << 16) | USB_PID_WINFAST_DTV_DONGLE_GOLD, RC_MAP_LEADTEK_Y04G0051 }, - { (USB_VID_AVERMEDIA << 16) + USB_PID_AVERMEDIA_VOLAR_X, + { (USB_VID_LEADTEK << 16) | USB_PID_WINFAST_DTV2000DS, + RC_MAP_LEADTEK_Y04G0051 }, + { (USB_VID_AVERMEDIA << 16) | USB_PID_AVERMEDIA_VOLAR_X, RC_MAP_AVERMEDIA_M135A }, - { (USB_VID_AFATECH << 16) + USB_PID_TREKSTOR_DVBT, + { (USB_VID_AFATECH << 16) | USB_PID_TREKSTOR_DVBT, RC_MAP_TREKSTOR }, - { (USB_VID_KWORLD_2 << 16) + USB_PID_TINYTWIN_2, + { (USB_VID_KWORLD_2 << 16) | USB_PID_TINYTWIN_2, RC_MAP_DIGITALNOW_TINYTWIN }, - { (USB_VID_GTEK << 16) + USB_PID_TINYTWIN_3, + { (USB_VID_GTEK << 16) | USB_PID_TINYTWIN_3, RC_MAP_DIGITALNOW_TINYTWIN }, - { (USB_VID_KWORLD_2 << 16) + USB_PID_SVEON_STV22, + { (USB_VID_KWORLD_2 << 16) | USB_PID_SVEON_STV22, RC_MAP_MSI_DIGIVOX_III }, { } }; @@ -859,13 +861,13 @@ static int af9015_read_config(struct usb_device *udev) for (i = 0; i < af9015_properties_count; i++) { /* USB1.1 set smaller buffersize and disable 2nd adapter */ if (udev->speed == USB_SPEED_FULL) { - af9015_properties[i].adapter[0].stream.u.bulk.buffersize + af9015_properties[i].adapter[0].fe[0].stream.u.bulk.buffersize = TS_USB11_FRAME_SIZE; /* disable 2nd adapter because we don't have PID-filters */ af9015_config.dual_mode = 0; } else { - af9015_properties[i].adapter[0].stream.u.bulk.buffersize + af9015_properties[i].adapter[0].fe[0].stream.u.bulk.buffersize = TS_USB20_FRAME_SIZE; } } @@ -1111,10 +1113,10 @@ static int af9015_af9013_frontend_attach(struct dvb_usb_adapter *adap) } /* attach demodulator */ - adap->fe = dvb_attach(af9013_attach, &af9015_af9013_config[adap->id], + adap->fe_adap[0].fe = dvb_attach(af9013_attach, &af9015_af9013_config[adap->id], &adap->dev->i2c_adap); - return adap->fe == NULL ? -ENODEV : 0; + return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; } static struct mt2060_config af9015_mt2060_config = { @@ -1188,49 +1190,49 @@ static int af9015_tuner_attach(struct dvb_usb_adapter *adap) switch (af9015_af9013_config[adap->id].tuner) { case AF9013_TUNER_MT2060: case AF9013_TUNER_MT2060_2: - ret = dvb_attach(mt2060_attach, adap->fe, &adap->dev->i2c_adap, + ret = dvb_attach(mt2060_attach, adap->fe_adap[0].fe, &adap->dev->i2c_adap, &af9015_mt2060_config, af9015_config.mt2060_if1[adap->id]) == NULL ? -ENODEV : 0; break; case AF9013_TUNER_QT1010: case AF9013_TUNER_QT1010A: - ret = dvb_attach(qt1010_attach, adap->fe, &adap->dev->i2c_adap, + ret = dvb_attach(qt1010_attach, adap->fe_adap[0].fe, &adap->dev->i2c_adap, &af9015_qt1010_config) == NULL ? -ENODEV : 0; break; case AF9013_TUNER_TDA18271: - ret = dvb_attach(tda18271_attach, adap->fe, 0xc0, + ret = dvb_attach(tda18271_attach, adap->fe_adap[0].fe, 0xc0, &adap->dev->i2c_adap, &af9015_tda18271_config) == NULL ? -ENODEV : 0; break; case AF9013_TUNER_TDA18218: - ret = dvb_attach(tda18218_attach, adap->fe, + ret = dvb_attach(tda18218_attach, adap->fe_adap[0].fe, &adap->dev->i2c_adap, &af9015_tda18218_config) == NULL ? -ENODEV : 0; break; case AF9013_TUNER_MXL5003D: - ret = dvb_attach(mxl5005s_attach, adap->fe, + ret = dvb_attach(mxl5005s_attach, adap->fe_adap[0].fe, &adap->dev->i2c_adap, &af9015_mxl5003_config) == NULL ? -ENODEV : 0; break; case AF9013_TUNER_MXL5005D: case AF9013_TUNER_MXL5005R: - ret = dvb_attach(mxl5005s_attach, adap->fe, + ret = dvb_attach(mxl5005s_attach, adap->fe_adap[0].fe, &adap->dev->i2c_adap, &af9015_mxl5005_config) == NULL ? -ENODEV : 0; break; case AF9013_TUNER_ENV77H11D5: - ret = dvb_attach(dvb_pll_attach, adap->fe, 0xc0, + ret = dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0xc0, &adap->dev->i2c_adap, DVB_PLL_TDA665X) == NULL ? -ENODEV : 0; break; case AF9013_TUNER_MC44S803: - ret = dvb_attach(mc44s803_attach, adap->fe, + ret = dvb_attach(mc44s803_attach, adap->fe_adap[0].fe, &adap->dev->i2c_adap, &af9015_mc44s803_config) == NULL ? -ENODEV : 0; break; case AF9013_TUNER_MXL5007T: - ret = dvb_attach(mxl5007t_attach, adap->fe, + ret = dvb_attach(mxl5007t_attach, adap->fe_adap[0].fe, &adap->dev->i2c_adap, 0xc0, &af9015_mxl5007t_config) == NULL ? -ENODEV : 0; break; @@ -1304,6 +1306,8 @@ static struct dvb_usb_device_properties af9015_properties[] = { .num_adapters = 2, .adapter = { { + .num_frontends = 1, + .fe = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, @@ -1319,8 +1323,11 @@ static struct dvb_usb_device_properties af9015_properties[] = { .count = 6, .endpoint = 0x84, }, + }}, }, { + .num_frontends = 1, + .fe = {{ .frontend_attach = af9015_af9013_frontend_attach, .tuner_attach = af9015_tuner_attach, @@ -1335,6 +1342,7 @@ static struct dvb_usb_device_properties af9015_properties[] = { } } }, + }}, } }, @@ -1432,6 +1440,8 @@ static struct dvb_usb_device_properties af9015_properties[] = { .num_adapters = 2, .adapter = { { + .num_frontends = 1, + .fe = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, @@ -1447,8 +1457,11 @@ static struct dvb_usb_device_properties af9015_properties[] = { .count = 6, .endpoint = 0x84, }, + }}, }, { + .num_frontends = 1, + .fe = {{ .frontend_attach = af9015_af9013_frontend_attach, .tuner_attach = af9015_tuner_attach, @@ -1463,6 +1476,7 @@ static struct dvb_usb_device_properties af9015_properties[] = { } } }, + }}, } }, @@ -1549,6 +1563,8 @@ static struct dvb_usb_device_properties af9015_properties[] = { .num_adapters = 2, .adapter = { { + .num_frontends = 1, + .fe = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, @@ -1564,8 +1580,11 @@ static struct dvb_usb_device_properties af9015_properties[] = { .count = 6, .endpoint = 0x84, }, + }}, }, { + .num_frontends = 1, + .fe = {{ .frontend_attach = af9015_af9013_frontend_attach, .tuner_attach = af9015_tuner_attach, @@ -1580,6 +1599,7 @@ static struct dvb_usb_device_properties af9015_properties[] = { } } }, + }}, } }, diff --git a/drivers/media/dvb/dvb-usb/anysee.c b/drivers/media/dvb/dvb-usb/anysee.c index 2cbf19a52e38..5f2278b73ee9 100644 --- a/drivers/media/dvb/dvb-usb/anysee.c +++ b/drivers/media/dvb/dvb-usb/anysee.c @@ -446,6 +446,114 @@ static struct isl6423_config anysee_isl6423_config = { * IOE[5] STV0903 1=enabled */ +static int anysee_frontend_ctrl(struct dvb_frontend *fe, int onoff) +{ + struct dvb_usb_adapter *adap = fe->dvb->priv; + struct anysee_state *state = adap->dev->priv; + int ret; + + deb_info("%s: fe=%d onoff=%d\n", __func__, fe->id, onoff); + + /* no frontend sleep control */ + if (onoff == 0) + return 0; + + switch (state->hw) { + case ANYSEE_HW_507FA: /* 15 */ + /* E30 Combo Plus */ + /* E30 C Plus */ + + if ((fe->id ^ dvb_usb_anysee_delsys) == 0) { + /* disable DVB-T demod on IOD[0] */ + ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (0 << 0), + 0x01); + if (ret) + goto error; + + /* enable DVB-C demod on IOD[5] */ + ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 5), + 0x20); + if (ret) + goto error; + + /* enable DVB-C tuner on IOE[0] */ + ret = anysee_wr_reg_mask(adap->dev, REG_IOE, (1 << 0), + 0x01); + if (ret) + goto error; + } else { + /* disable DVB-C demod on IOD[5] */ + ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (0 << 5), + 0x20); + if (ret) + goto error; + + /* enable DVB-T demod on IOD[0] */ + ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 0), + 0x01); + if (ret) + goto error; + + /* enable DVB-T tuner on IOE[0] */ + ret = anysee_wr_reg_mask(adap->dev, REG_IOE, (0 << 0), + 0x01); + if (ret) + goto error; + } + + break; + case ANYSEE_HW_508TC: /* 18 */ + case ANYSEE_HW_508PTC: /* 21 */ + /* E7 TC */ + /* E7 PTC */ + + if ((fe->id ^ dvb_usb_anysee_delsys) == 0) { + /* disable DVB-T demod on IOD[6] */ + ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (0 << 6), + 0x40); + if (ret) + goto error; + + /* enable DVB-C demod on IOD[5] */ + ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 5), + 0x20); + if (ret) + goto error; + + /* enable IF route on IOE[0] */ + ret = anysee_wr_reg_mask(adap->dev, REG_IOE, (1 << 0), + 0x01); + if (ret) + goto error; + } else { + /* disable DVB-C demod on IOD[5] */ + ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (0 << 5), + 0x20); + if (ret) + goto error; + + /* enable DVB-T demod on IOD[6] */ + ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 6), + 0x40); + if (ret) + goto error; + + /* enable IF route on IOE[0] */ + ret = anysee_wr_reg_mask(adap->dev, REG_IOE, (0 << 0), + 0x01); + if (ret) + goto error; + } + + break; + default: + ret = 0; + } + +error: + return ret; +} + static int anysee_frontend_attach(struct dvb_usb_adapter *adap) { int ret; @@ -466,41 +574,54 @@ static int anysee_frontend_attach(struct dvb_usb_adapter *adap) } }; - /* Check which hardware we have. - * We must do this call two times to get reliable values (hw bug). - */ - ret = anysee_get_hw_info(adap->dev, hw_info); - if (ret) - goto error; + /* detect hardware only once */ + if (adap->fe_adap[0].fe == NULL) { + /* Check which hardware we have. + * We must do this call two times to get reliable values (hw bug). + */ + ret = anysee_get_hw_info(adap->dev, hw_info); + if (ret) + goto error; - ret = anysee_get_hw_info(adap->dev, hw_info); - if (ret) - goto error; + ret = anysee_get_hw_info(adap->dev, hw_info); + if (ret) + goto error; - /* Meaning of these info bytes are guessed. */ - info("firmware version:%d.%d hardware id:%d", - hw_info[1], hw_info[2], hw_info[0]); + /* Meaning of these info bytes are guessed. */ + info("firmware version:%d.%d hardware id:%d", + hw_info[1], hw_info[2], hw_info[0]); - state->hw = hw_info[0]; + state->hw = hw_info[0]; + } + + /* set current frondend ID for devices having two frondends */ + if (adap->fe_adap[0].fe) + state->fe_id++; switch (state->hw) { case ANYSEE_HW_507T: /* 2 */ /* E30 */ + if (state->fe_id) + break; + /* attach demod */ - adap->fe = dvb_attach(mt352_attach, &anysee_mt352_config, + adap->fe_adap[0].fe = dvb_attach(mt352_attach, &anysee_mt352_config, &adap->dev->i2c_adap); - if (adap->fe) + if (adap->fe_adap[0].fe) break; /* attach demod */ - adap->fe = dvb_attach(zl10353_attach, &anysee_zl10353_config, + adap->fe_adap[0].fe = dvb_attach(zl10353_attach, &anysee_zl10353_config, &adap->dev->i2c_adap); break; case ANYSEE_HW_507CD: /* 6 */ /* E30 Plus */ + if (state->fe_id) + break; + /* enable DVB-T demod on IOD[0] */ ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 0), 0x01); if (ret) @@ -512,33 +633,39 @@ static int anysee_frontend_attach(struct dvb_usb_adapter *adap) goto error; /* attach demod */ - adap->fe = dvb_attach(zl10353_attach, &anysee_zl10353_config, - &adap->dev->i2c_adap); + adap->fe_adap[0].fe = dvb_attach(zl10353_attach, + &anysee_zl10353_config, &adap->dev->i2c_adap); break; case ANYSEE_HW_507DC: /* 10 */ /* E30 C Plus */ + if (state->fe_id) + break; + /* enable DVB-C demod on IOD[0] */ ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 0), 0x01); if (ret) goto error; /* attach demod */ - adap->fe = dvb_attach(tda10023_attach, &anysee_tda10023_config, - &adap->dev->i2c_adap, 0x48); + adap->fe_adap[0].fe = dvb_attach(tda10023_attach, + &anysee_tda10023_config, &adap->dev->i2c_adap, 0x48); break; case ANYSEE_HW_507SI: /* 11 */ /* E30 S2 Plus */ + if (state->fe_id) + break; + /* enable DVB-S/S2 demod on IOD[0] */ ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 0), 0x01); if (ret) goto error; /* attach demod */ - adap->fe = dvb_attach(cx24116_attach, &anysee_cx24116_config, + adap->fe_adap[0].fe = dvb_attach(cx24116_attach, &anysee_cx24116_config, &adap->dev->i2c_adap); break; @@ -564,55 +691,59 @@ static int anysee_frontend_attach(struct dvb_usb_adapter *adap) if (ret) goto error; - if (dvb_usb_anysee_delsys) { - /* disable DVB-C demod on IOD[5] */ - ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (0 << 5), - 0x20); + if ((state->fe_id ^ dvb_usb_anysee_delsys) == 0) { + /* disable DVB-T demod on IOD[0] */ + ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (0 << 0), + 0x01); if (ret) goto error; - /* enable DVB-T demod on IOD[0] */ - ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 0), - 0x01); + /* enable DVB-C demod on IOD[5] */ + ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 5), + 0x20); if (ret) goto error; /* attach demod */ if (tmp == 0xc7) { /* TDA18212 config */ - adap->fe = dvb_attach(zl10353_attach, - &anysee_zl10353_tda18212_config2, - &adap->dev->i2c_adap); + adap->fe_adap[state->fe_id].fe = dvb_attach( + tda10023_attach, + &anysee_tda10023_tda18212_config, + &adap->dev->i2c_adap, 0x48); } else { /* PLL config */ - adap->fe = dvb_attach(zl10353_attach, - &anysee_zl10353_config, - &adap->dev->i2c_adap); + adap->fe_adap[state->fe_id].fe = dvb_attach( + tda10023_attach, + &anysee_tda10023_config, + &adap->dev->i2c_adap, 0x48); } } else { - /* disable DVB-T demod on IOD[0] */ - ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (0 << 0), - 0x01); + /* disable DVB-C demod on IOD[5] */ + ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (0 << 5), + 0x20); if (ret) goto error; - /* enable DVB-C demod on IOD[5] */ - ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 5), - 0x20); + /* enable DVB-T demod on IOD[0] */ + ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 0), + 0x01); if (ret) goto error; /* attach demod */ if (tmp == 0xc7) { /* TDA18212 config */ - adap->fe = dvb_attach(tda10023_attach, - &anysee_tda10023_tda18212_config, - &adap->dev->i2c_adap, 0x48); + adap->fe_adap[state->fe_id].fe = dvb_attach( + zl10353_attach, + &anysee_zl10353_tda18212_config2, + &adap->dev->i2c_adap); } else { /* PLL config */ - adap->fe = dvb_attach(tda10023_attach, - &anysee_tda10023_config, - &adap->dev->i2c_adap, 0x48); + adap->fe_adap[state->fe_id].fe = dvb_attach( + zl10353_attach, + &anysee_zl10353_config, + &adap->dev->i2c_adap); } } @@ -627,52 +758,40 @@ static int anysee_frontend_attach(struct dvb_usb_adapter *adap) if (ret) goto error; - if (dvb_usb_anysee_delsys) { - /* disable DVB-C demod on IOD[5] */ - ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (0 << 5), - 0x20); - if (ret) - goto error; - - /* enable DVB-T demod on IOD[6] */ - ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 6), + if ((state->fe_id ^ dvb_usb_anysee_delsys) == 0) { + /* disable DVB-T demod on IOD[6] */ + ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (0 << 6), 0x40); if (ret) goto error; - /* enable IF route on IOE[0] */ - ret = anysee_wr_reg_mask(adap->dev, REG_IOE, (0 << 0), - 0x01); + /* enable DVB-C demod on IOD[5] */ + ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 5), + 0x20); if (ret) goto error; /* attach demod */ - adap->fe = dvb_attach(zl10353_attach, - &anysee_zl10353_tda18212_config, - &adap->dev->i2c_adap); + adap->fe_adap[state->fe_id].fe = dvb_attach(tda10023_attach, + &anysee_tda10023_tda18212_config, + &adap->dev->i2c_adap, 0x48); } else { - /* disable DVB-T demod on IOD[6] */ - ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (0 << 6), - 0x40); - if (ret) - goto error; - - /* enable DVB-C demod on IOD[5] */ - ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 5), + /* disable DVB-C demod on IOD[5] */ + ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (0 << 5), 0x20); if (ret) goto error; - /* enable IF route on IOE[0] */ - ret = anysee_wr_reg_mask(adap->dev, REG_IOE, (1 << 0), - 0x01); + /* enable DVB-T demod on IOD[6] */ + ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 6), + 0x40); if (ret) goto error; /* attach demod */ - adap->fe = dvb_attach(tda10023_attach, - &anysee_tda10023_tda18212_config, - &adap->dev->i2c_adap, 0x48); + adap->fe_adap[state->fe_id].fe = dvb_attach(zl10353_attach, + &anysee_zl10353_tda18212_config, + &adap->dev->i2c_adap); } break; @@ -681,6 +800,9 @@ static int anysee_frontend_attach(struct dvb_usb_adapter *adap) /* E7 S2 */ /* E7 PS2 */ + if (state->fe_id) + break; + /* enable transport stream on IOA[7] */ ret = anysee_wr_reg_mask(adap->dev, REG_IOA, (1 << 7), 0x80); if (ret) @@ -692,13 +814,13 @@ static int anysee_frontend_attach(struct dvb_usb_adapter *adap) goto error; /* attach demod */ - adap->fe = dvb_attach(stv0900_attach, &anysee_stv0900_config, + adap->fe_adap[0].fe = dvb_attach(stv0900_attach, &anysee_stv0900_config, &adap->dev->i2c_adap, 0); break; } - if (!adap->fe) { + if (!adap->fe_adap[0].fe) { /* we have no frontend :-( */ ret = -ENODEV; err("Unsupported Anysee version. " \ @@ -713,14 +835,14 @@ static int anysee_tuner_attach(struct dvb_usb_adapter *adap) struct anysee_state *state = adap->dev->priv; struct dvb_frontend *fe; int ret; - deb_info("%s:\n", __func__); + deb_info("%s: fe=%d\n", __func__, state->fe_id); switch (state->hw) { case ANYSEE_HW_507T: /* 2 */ /* E30 */ /* attach tuner */ - fe = dvb_attach(dvb_pll_attach, adap->fe, (0xc2 >> 1), + fe = dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, (0xc2 >> 1), NULL, DVB_PLL_THOMSON_DTT7579); break; @@ -728,7 +850,7 @@ static int anysee_tuner_attach(struct dvb_usb_adapter *adap) /* E30 Plus */ /* attach tuner */ - fe = dvb_attach(dvb_pll_attach, adap->fe, (0xc2 >> 1), + fe = dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, (0xc2 >> 1), &adap->dev->i2c_adap, DVB_PLL_THOMSON_DTT7579); break; @@ -736,7 +858,7 @@ static int anysee_tuner_attach(struct dvb_usb_adapter *adap) /* E30 C Plus */ /* attach tuner */ - fe = dvb_attach(dvb_pll_attach, adap->fe, (0xc0 >> 1), + fe = dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, (0xc0 >> 1), &adap->dev->i2c_adap, DVB_PLL_SAMSUNG_DTOS403IH102A); break; @@ -744,28 +866,14 @@ static int anysee_tuner_attach(struct dvb_usb_adapter *adap) /* E30 S2 Plus */ /* attach LNB controller */ - fe = dvb_attach(isl6423_attach, adap->fe, &adap->dev->i2c_adap, - &anysee_isl6423_config); + fe = dvb_attach(isl6423_attach, adap->fe_adap[0].fe, + &adap->dev->i2c_adap, &anysee_isl6423_config); break; case ANYSEE_HW_507FA: /* 15 */ /* E30 Combo Plus */ /* E30 C Plus */ - if (dvb_usb_anysee_delsys) { - /* enable DVB-T tuner on IOE[0] */ - ret = anysee_wr_reg_mask(adap->dev, REG_IOE, (0 << 0), - 0x01); - if (ret) - goto error; - } else { - /* enable DVB-C tuner on IOE[0] */ - ret = anysee_wr_reg_mask(adap->dev, REG_IOE, (1 << 0), - 0x01); - if (ret) - goto error; - } - /* Try first attach TDA18212 silicon tuner on IOE[4], if that * fails attach old simple PLL. */ @@ -775,8 +883,8 @@ static int anysee_tuner_attach(struct dvb_usb_adapter *adap) goto error; /* attach tuner */ - fe = dvb_attach(tda18212_attach, adap->fe, &adap->dev->i2c_adap, - &anysee_tda18212_config); + fe = dvb_attach(tda18212_attach, adap->fe_adap[state->fe_id].fe, + &adap->dev->i2c_adap, &anysee_tda18212_config); if (fe) break; @@ -786,8 +894,9 @@ static int anysee_tuner_attach(struct dvb_usb_adapter *adap) goto error; /* attach tuner */ - fe = dvb_attach(dvb_pll_attach, adap->fe, (0xc0 >> 1), - &adap->dev->i2c_adap, DVB_PLL_SAMSUNG_DTOS403IH102A); + fe = dvb_attach(dvb_pll_attach, adap->fe_adap[state->fe_id].fe, + (0xc0 >> 1), &adap->dev->i2c_adap, + DVB_PLL_SAMSUNG_DTOS403IH102A); break; case ANYSEE_HW_508TC: /* 18 */ @@ -801,8 +910,8 @@ static int anysee_tuner_attach(struct dvb_usb_adapter *adap) goto error; /* attach tuner */ - fe = dvb_attach(tda18212_attach, adap->fe, &adap->dev->i2c_adap, - &anysee_tda18212_config); + fe = dvb_attach(tda18212_attach, adap->fe_adap[state->fe_id].fe, + &adap->dev->i2c_adap, &anysee_tda18212_config); break; case ANYSEE_HW_508S2: /* 19 */ @@ -811,12 +920,12 @@ static int anysee_tuner_attach(struct dvb_usb_adapter *adap) /* E7 PS2 */ /* attach tuner */ - fe = dvb_attach(stv6110_attach, adap->fe, + fe = dvb_attach(stv6110_attach, adap->fe_adap[0].fe, &anysee_stv6110_config, &adap->dev->i2c_adap); if (fe) { /* attach LNB controller */ - fe = dvb_attach(isl6423_attach, adap->fe, + fe = dvb_attach(isl6423_attach, adap->fe_adap[0].fe, &adap->dev->i2c_adap, &anysee_isl6423_config); } @@ -918,6 +1027,23 @@ static struct dvb_usb_device_properties anysee_properties = { .num_adapters = 1, .adapter = { { + .num_frontends = 2, + .frontend_ctrl = anysee_frontend_ctrl, + .fe = {{ + .streaming_ctrl = anysee_streaming_ctrl, + .frontend_attach = anysee_frontend_attach, + .tuner_attach = anysee_tuner_attach, + .stream = { + .type = USB_BULK, + .count = 8, + .endpoint = 0x82, + .u = { + .bulk = { + .buffersize = (16*512), + } + } + }, + }, { .streaming_ctrl = anysee_streaming_ctrl, .frontend_attach = anysee_frontend_attach, .tuner_attach = anysee_tuner_attach, @@ -931,6 +1057,7 @@ static struct dvb_usb_device_properties anysee_properties = { } } }, + }}, } }, diff --git a/drivers/media/dvb/dvb-usb/anysee.h b/drivers/media/dvb/dvb-usb/anysee.h index ad6ccd1ea2d9..57ee500b8c0e 100644 --- a/drivers/media/dvb/dvb-usb/anysee.h +++ b/drivers/media/dvb/dvb-usb/anysee.h @@ -59,6 +59,7 @@ enum cmd { struct anysee_state { u8 hw; /* PCB ID */ u8 seq; + u8 fe_id:1; /* frondend ID */ }; #define ANYSEE_HW_507T 2 /* E30 */ diff --git a/drivers/media/dvb/dvb-usb/au6610.c b/drivers/media/dvb/dvb-usb/au6610.c index 2351077ff2b3..b77994967b9b 100644 --- a/drivers/media/dvb/dvb-usb/au6610.c +++ b/drivers/media/dvb/dvb-usb/au6610.c @@ -140,9 +140,9 @@ static struct zl10353_config au6610_zl10353_config = { static int au6610_zl10353_frontend_attach(struct dvb_usb_adapter *adap) { - adap->fe = dvb_attach(zl10353_attach, &au6610_zl10353_config, + adap->fe_adap[0].fe = dvb_attach(zl10353_attach, &au6610_zl10353_config, &adap->dev->i2c_adap); - if (adap->fe == NULL) + if (adap->fe_adap[0].fe == NULL) return -ENODEV; return 0; @@ -155,7 +155,7 @@ static struct qt1010_config au6610_qt1010_config = { static int au6610_qt1010_tuner_attach(struct dvb_usb_adapter *adap) { return dvb_attach(qt1010_attach, - adap->fe, &adap->dev->i2c_adap, + adap->fe_adap[0].fe, &adap->dev->i2c_adap, &au6610_qt1010_config) == NULL ? -ENODEV : 0; } @@ -204,6 +204,8 @@ static struct dvb_usb_device_properties au6610_properties = { .num_adapters = 1, .adapter = { { + .num_frontends = 1, + .fe = {{ .frontend_attach = au6610_zl10353_frontend_attach, .tuner_attach = au6610_qt1010_tuner_attach, @@ -219,6 +221,7 @@ static struct dvb_usb_device_properties au6610_properties = { } } }, + }}, } }, diff --git a/drivers/media/dvb/dvb-usb/az6027.c b/drivers/media/dvb/dvb-usb/az6027.c index 57e2444d51ab..bf67b4dfd82b 100644 --- a/drivers/media/dvb/dvb-usb/az6027.c +++ b/drivers/media/dvb/dvb-usb/az6027.c @@ -40,7 +40,6 @@ static const struct stb0899_s1_reg az6027_stb0899_s1_init_1[] = { { STB0899_DISRX_ST0 , 0x04 }, { STB0899_DISRX_ST1 , 0x00 }, { STB0899_DISPARITY , 0x00 }, - { STB0899_DISFIFO , 0x00 }, { STB0899_DISSTATUS , 0x20 }, { STB0899_DISF22 , 0x99 }, { STB0899_DISF22RX , 0xa8 }, @@ -782,7 +781,6 @@ static int az6027_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) { u8 buf; - int ret; struct dvb_usb_adapter *adap = fe->dvb->priv; struct i2c_msg i2c_msg = { @@ -800,17 +798,17 @@ static int az6027_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) switch (voltage) { case SEC_VOLTAGE_13: buf = 1; - ret = i2c_transfer(&adap->dev->i2c_adap, &i2c_msg, 1); + i2c_transfer(&adap->dev->i2c_adap, &i2c_msg, 1); break; case SEC_VOLTAGE_18: buf = 2; - ret = i2c_transfer(&adap->dev->i2c_adap, &i2c_msg, 1); + i2c_transfer(&adap->dev->i2c_adap, &i2c_msg, 1); break; case SEC_VOLTAGE_OFF: buf = 0; - ret = i2c_transfer(&adap->dev->i2c_adap, &i2c_msg, 1); + i2c_transfer(&adap->dev->i2c_adap, &i2c_msg, 1); break; default: @@ -910,16 +908,16 @@ static int az6027_frontend_attach(struct dvb_usb_adapter *adap) az6027_frontend_reset(adap); deb_info("adap = %p, dev = %p\n", adap, adap->dev); - adap->fe = stb0899_attach(&az6027_stb0899_config, &adap->dev->i2c_adap); + adap->fe_adap[0].fe = stb0899_attach(&az6027_stb0899_config, &adap->dev->i2c_adap); - if (adap->fe) { + if (adap->fe_adap[0].fe) { deb_info("found STB0899 DVB-S/DVB-S2 frontend @0x%02x", az6027_stb0899_config.demod_address); - if (stb6100_attach(adap->fe, &az6027_stb6100_config, &adap->dev->i2c_adap)) { + if (stb6100_attach(adap->fe_adap[0].fe, &az6027_stb6100_config, &adap->dev->i2c_adap)) { deb_info("found STB6100 DVB-S/DVB-S2 frontend @0x%02x", az6027_stb6100_config.tuner_address); - adap->fe->ops.set_voltage = az6027_set_voltage; + adap->fe_adap[0].fe->ops.set_voltage = az6027_set_voltage; az6027_ci_init(adap); } else { - adap->fe = NULL; + adap->fe_adap[0].fe = NULL; } } else warn("no front-end attached\n"); @@ -954,7 +952,6 @@ static int az6027_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], int n { struct dvb_usb_device *d = i2c_get_adapdata(adap); int i = 0, j = 0, len = 0; - int ret; u16 index; u16 value; int length; @@ -990,7 +987,7 @@ static int az6027_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], int n index = (((msg[i].buf[0] << 8) & 0xff00) | (msg[i].buf[1] & 0x00ff)); value = msg[i].addr + (msg[i].len << 8); length = msg[i + 1].len + 6; - ret = az6027_usb_in_op(d, req, value, index, data, length); + az6027_usb_in_op(d, req, value, index, data, length); len = msg[i + 1].len; for (j = 0; j < len; j++) msg[i + 1].buf[j] = data[j + 5]; @@ -1017,7 +1014,7 @@ static int az6027_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], int n index = 0x0; value = msg[i].addr; length = msg[i].len + 6; - ret = az6027_usb_in_op(d, req, value, index, data, length); + az6027_usb_in_op(d, req, value, index, data, length); len = msg[i].len; for (j = 0; j < len; j++) msg[i].buf[j] = data[j + 5]; @@ -1106,6 +1103,8 @@ static struct dvb_usb_device_properties az6027_properties = { .num_adapters = 1, .adapter = { { + .num_frontends = 1, + .fe = {{ .streaming_ctrl = az6027_streaming_ctrl, .frontend_attach = az6027_frontend_attach, @@ -1120,6 +1119,7 @@ static struct dvb_usb_device_properties az6027_properties = { } } }, + }}, } }, /* diff --git a/drivers/media/dvb/dvb-usb/ce6230.c b/drivers/media/dvb/dvb-usb/ce6230.c index 6d1a3041540d..57afb5a9157e 100644 --- a/drivers/media/dvb/dvb-usb/ce6230.c +++ b/drivers/media/dvb/dvb-usb/ce6230.c @@ -186,9 +186,9 @@ static struct zl10353_config ce6230_zl10353_config = { static int ce6230_zl10353_frontend_attach(struct dvb_usb_adapter *adap) { deb_info("%s:\n", __func__); - adap->fe = dvb_attach(zl10353_attach, &ce6230_zl10353_config, + adap->fe_adap[0].fe = dvb_attach(zl10353_attach, &ce6230_zl10353_config, &adap->dev->i2c_adap); - if (adap->fe == NULL) + if (adap->fe_adap[0].fe == NULL) return -ENODEV; return 0; } @@ -214,7 +214,7 @@ static int ce6230_mxl5003s_tuner_attach(struct dvb_usb_adapter *adap) { int ret; deb_info("%s:\n", __func__); - ret = dvb_attach(mxl5005s_attach, adap->fe, &adap->dev->i2c_adap, + ret = dvb_attach(mxl5005s_attach, adap->fe_adap[0].fe, &adap->dev->i2c_adap, &ce6230_mxl5003s_config) == NULL ? -ENODEV : 0; return ret; } @@ -273,6 +273,8 @@ static struct dvb_usb_device_properties ce6230_properties = { .num_adapters = 1, .adapter = { { + .num_frontends = 1, + .fe = {{ .frontend_attach = ce6230_zl10353_frontend_attach, .tuner_attach = ce6230_mxl5003s_tuner_attach, .stream = { @@ -285,6 +287,7 @@ static struct dvb_usb_device_properties ce6230_properties = { } } }, + }}, } }, diff --git a/drivers/media/dvb/dvb-usb/cinergyT2-core.c b/drivers/media/dvb/dvb-usb/cinergyT2-core.c index 16f2ce2bc15a..f9d905002ec9 100644 --- a/drivers/media/dvb/dvb-usb/cinergyT2-core.c +++ b/drivers/media/dvb/dvb-usb/cinergyT2-core.c @@ -69,7 +69,7 @@ static int cinergyt2_frontend_attach(struct dvb_usb_adapter *adap) char state[3]; int ret; - adap->fe = cinergyt2_fe_attach(adap->dev); + adap->fe_adap[0].fe = cinergyt2_fe_attach(adap->dev); ret = dvb_usb_generic_rw(adap->dev, query, sizeof(query), state, sizeof(state), 0); @@ -198,6 +198,8 @@ static struct dvb_usb_device_properties cinergyt2_properties = { .num_adapters = 1, .adapter = { { + .num_frontends = 1, + .fe = {{ .streaming_ctrl = cinergyt2_streaming_ctrl, .frontend_attach = cinergyt2_frontend_attach, @@ -212,6 +214,7 @@ static struct dvb_usb_device_properties cinergyt2_properties = { } } }, + }}, } }, diff --git a/drivers/media/dvb/dvb-usb/cxusb.c b/drivers/media/dvb/dvb-usb/cxusb.c index acb5fb2d2e73..9f2a02c48377 100644 --- a/drivers/media/dvb/dvb-usb/cxusb.c +++ b/drivers/media/dvb/dvb-usb/cxusb.c @@ -347,7 +347,7 @@ static void cxusb_d680_dmb_drain_message(struct dvb_usb_device *d) static void cxusb_d680_dmb_drain_video(struct dvb_usb_device *d) { - struct usb_data_stream_properties *p = &d->props.adapter[0].stream; + struct usb_data_stream_properties *p = &d->props.adapter[0].fe[0].stream; const int timeout = 100; const int junk_len = p->u.bulk.buffersize; u8 *junk; @@ -725,7 +725,7 @@ static struct max2165_config mygica_d689_max2165_cfg = { /* Callbacks for DVB USB */ static int cxusb_fmd1216me_tuner_attach(struct dvb_usb_adapter *adap) { - dvb_attach(simple_tuner_attach, adap->fe, + dvb_attach(simple_tuner_attach, adap->fe_adap[0].fe, &adap->dev->i2c_adap, 0x61, TUNER_PHILIPS_FMD1216ME_MK3); return 0; @@ -733,27 +733,27 @@ static int cxusb_fmd1216me_tuner_attach(struct dvb_usb_adapter *adap) static int cxusb_dee1601_tuner_attach(struct dvb_usb_adapter *adap) { - dvb_attach(dvb_pll_attach, adap->fe, 0x61, + dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x61, NULL, DVB_PLL_THOMSON_DTT7579); return 0; } static int cxusb_lgz201_tuner_attach(struct dvb_usb_adapter *adap) { - dvb_attach(dvb_pll_attach, adap->fe, 0x61, NULL, DVB_PLL_LG_Z201); + dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x61, NULL, DVB_PLL_LG_Z201); return 0; } static int cxusb_dtt7579_tuner_attach(struct dvb_usb_adapter *adap) { - dvb_attach(dvb_pll_attach, adap->fe, 0x60, + dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x60, NULL, DVB_PLL_THOMSON_DTT7579); return 0; } static int cxusb_lgh064f_tuner_attach(struct dvb_usb_adapter *adap) { - dvb_attach(simple_tuner_attach, adap->fe, + dvb_attach(simple_tuner_attach, adap->fe_adap[0].fe, &adap->dev->i2c_adap, 0x61, TUNER_LG_TDVS_H06XF); return 0; } @@ -795,9 +795,9 @@ static int cxusb_dvico_xc3028_tuner_attach(struct dvb_usb_adapter *adap) }; /* FIXME: generalize & move to common area */ - adap->fe->callback = dvico_bluebird_xc2028_callback; + adap->fe_adap[0].fe->callback = dvico_bluebird_xc2028_callback; - fe = dvb_attach(xc2028_attach, adap->fe, &cfg); + fe = dvb_attach(xc2028_attach, adap->fe_adap[0].fe, &cfg); if (fe == NULL || fe->ops.tuner_ops.set_config == NULL) return -EIO; @@ -808,7 +808,7 @@ static int cxusb_dvico_xc3028_tuner_attach(struct dvb_usb_adapter *adap) static int cxusb_mxl5003s_tuner_attach(struct dvb_usb_adapter *adap) { - dvb_attach(mxl5005s_attach, adap->fe, + dvb_attach(mxl5005s_attach, adap->fe_adap[0].fe, &adap->dev->i2c_adap, &aver_a868r_tuner); return 0; } @@ -816,7 +816,7 @@ static int cxusb_mxl5003s_tuner_attach(struct dvb_usb_adapter *adap) static int cxusb_d680_dmb_tuner_attach(struct dvb_usb_adapter *adap) { struct dvb_frontend *fe; - fe = dvb_attach(mxl5005s_attach, adap->fe, + fe = dvb_attach(mxl5005s_attach, adap->fe_adap[0].fe, &adap->dev->i2c_adap, &d680_dmb_tuner); return (fe == NULL) ? -EIO : 0; } @@ -824,7 +824,7 @@ static int cxusb_d680_dmb_tuner_attach(struct dvb_usb_adapter *adap) static int cxusb_mygica_d689_tuner_attach(struct dvb_usb_adapter *adap) { struct dvb_frontend *fe; - fe = dvb_attach(max2165_attach, adap->fe, + fe = dvb_attach(max2165_attach, adap->fe_adap[0].fe, &adap->dev->i2c_adap, &mygica_d689_max2165_cfg); return (fe == NULL) ? -EIO : 0; } @@ -837,8 +837,9 @@ static int cxusb_cx22702_frontend_attach(struct dvb_usb_adapter *adap) cxusb_ctrl_msg(adap->dev, CMD_DIGITAL, NULL, 0, &b, 1); - if ((adap->fe = dvb_attach(cx22702_attach, &cxusb_cx22702_config, - &adap->dev->i2c_adap)) != NULL) + adap->fe_adap[0].fe = dvb_attach(cx22702_attach, &cxusb_cx22702_config, + &adap->dev->i2c_adap); + if ((adap->fe_adap[0].fe) != NULL) return 0; return -EIO; @@ -851,8 +852,10 @@ static int cxusb_lgdt3303_frontend_attach(struct dvb_usb_adapter *adap) cxusb_ctrl_msg(adap->dev, CMD_DIGITAL, NULL, 0, NULL, 0); - if ((adap->fe = dvb_attach(lgdt330x_attach, &cxusb_lgdt3303_config, - &adap->dev->i2c_adap)) != NULL) + adap->fe_adap[0].fe = dvb_attach(lgdt330x_attach, + &cxusb_lgdt3303_config, + &adap->dev->i2c_adap); + if ((adap->fe_adap[0].fe) != NULL) return 0; return -EIO; @@ -860,9 +863,9 @@ static int cxusb_lgdt3303_frontend_attach(struct dvb_usb_adapter *adap) static int cxusb_aver_lgdt3303_frontend_attach(struct dvb_usb_adapter *adap) { - adap->fe = dvb_attach(lgdt330x_attach, &cxusb_aver_lgdt3303_config, + adap->fe_adap[0].fe = dvb_attach(lgdt330x_attach, &cxusb_aver_lgdt3303_config, &adap->dev->i2c_adap); - if (adap->fe != NULL) + if (adap->fe_adap[0].fe != NULL) return 0; return -EIO; @@ -876,8 +879,9 @@ static int cxusb_mt352_frontend_attach(struct dvb_usb_adapter *adap) cxusb_ctrl_msg(adap->dev, CMD_DIGITAL, NULL, 0, NULL, 0); - if ((adap->fe = dvb_attach(mt352_attach, &cxusb_mt352_config, - &adap->dev->i2c_adap)) != NULL) + adap->fe_adap[0].fe = dvb_attach(mt352_attach, &cxusb_mt352_config, + &adap->dev->i2c_adap); + if ((adap->fe_adap[0].fe) != NULL) return 0; return -EIO; @@ -890,11 +894,15 @@ static int cxusb_dee1601_frontend_attach(struct dvb_usb_adapter *adap) cxusb_ctrl_msg(adap->dev, CMD_DIGITAL, NULL, 0, NULL, 0); - if (((adap->fe = dvb_attach(mt352_attach, &cxusb_dee1601_config, - &adap->dev->i2c_adap)) != NULL) || - ((adap->fe = dvb_attach(zl10353_attach, - &cxusb_zl10353_dee1601_config, - &adap->dev->i2c_adap)) != NULL)) + adap->fe_adap[0].fe = dvb_attach(mt352_attach, &cxusb_dee1601_config, + &adap->dev->i2c_adap); + if ((adap->fe_adap[0].fe) != NULL) + return 0; + + adap->fe_adap[0].fe = dvb_attach(zl10353_attach, + &cxusb_zl10353_dee1601_config, + &adap->dev->i2c_adap); + if ((adap->fe_adap[0].fe) != NULL) return 0; return -EIO; @@ -917,9 +925,11 @@ static int cxusb_dualdig4_frontend_attach(struct dvb_usb_adapter *adap) cxusb_bluebird_gpio_pulse(adap->dev, 0x01, 1); cxusb_bluebird_gpio_pulse(adap->dev, 0x02, 1); - if ((adap->fe = dvb_attach(zl10353_attach, - &cxusb_zl10353_xc3028_config_no_i2c_gate, - &adap->dev->i2c_adap)) == NULL) + adap->fe_adap[0].fe = + dvb_attach(zl10353_attach, + &cxusb_zl10353_xc3028_config_no_i2c_gate, + &adap->dev->i2c_adap); + if ((adap->fe_adap[0].fe) == NULL) return -EIO; /* try to determine if there is no IR decoder on the I2C bus */ @@ -1031,9 +1041,9 @@ static int cxusb_dualdig4_rev2_frontend_attach(struct dvb_usb_adapter *adap) return -ENODEV; } - adap->fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x80, + adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x80, &cxusb_dualdig4_rev2_config); - if (adap->fe == NULL) + if (adap->fe_adap[0].fe == NULL) return -EIO; return 0; @@ -1084,15 +1094,15 @@ static int cxusb_dualdig4_rev2_tuner_attach(struct dvb_usb_adapter *adap) { struct dib0700_adapter_state *st = adap->priv; struct i2c_adapter *tun_i2c = - dib7000p_get_i2c_master(adap->fe, + dib7000p_get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_TUNER, 1); - if (dvb_attach(dib0070_attach, adap->fe, tun_i2c, + if (dvb_attach(dib0070_attach, adap->fe_adap[0].fe, tun_i2c, &dib7070p_dib0070_config) == NULL) return -ENODEV; - st->set_param_save = adap->fe->ops.tuner_ops.set_params; - adap->fe->ops.tuner_ops.set_params = dib7070_set_param_override; + st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params; + adap->fe_adap[0].fe->ops.tuner_ops.set_params = dib7070_set_param_override; return 0; } @@ -1108,14 +1118,16 @@ static int cxusb_nano2_frontend_attach(struct dvb_usb_adapter *adap) cxusb_bluebird_gpio_pulse(adap->dev, 0x01, 1); cxusb_bluebird_gpio_pulse(adap->dev, 0x02, 1); - if ((adap->fe = dvb_attach(zl10353_attach, - &cxusb_zl10353_xc3028_config, - &adap->dev->i2c_adap)) != NULL) + adap->fe_adap[0].fe = dvb_attach(zl10353_attach, + &cxusb_zl10353_xc3028_config, + &adap->dev->i2c_adap); + if ((adap->fe_adap[0].fe) != NULL) return 0; - if ((adap->fe = dvb_attach(mt352_attach, - &cxusb_mt352_xc3028_config, - &adap->dev->i2c_adap)) != NULL) + adap->fe_adap[0].fe = dvb_attach(mt352_attach, + &cxusb_mt352_xc3028_config, + &adap->dev->i2c_adap); + if ((adap->fe_adap[0].fe) != NULL) return 0; return -EIO; @@ -1150,7 +1162,7 @@ static int cxusb_d680_dmb_frontend_attach(struct dvb_usb_adapter *adap) usb_clear_halt(d->udev, usb_rcvbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint)); usb_clear_halt(d->udev, - usb_rcvbulkpipe(d->udev, d->props.adapter[0].stream.endpoint)); + usb_rcvbulkpipe(d->udev, d->props.adapter[0].fe[0].stream.endpoint)); /* Drain USB pipes to avoid hang after reboot */ for (n = 0; n < 5; n++) { @@ -1172,8 +1184,8 @@ static int cxusb_d680_dmb_frontend_attach(struct dvb_usb_adapter *adap) msleep(100); /* Attach frontend */ - adap->fe = dvb_attach(lgs8gxx_attach, &d680_lgs8gl5_cfg, &d->i2c_adap); - if (adap->fe == NULL) + adap->fe_adap[0].fe = dvb_attach(lgs8gxx_attach, &d680_lgs8gl5_cfg, &d->i2c_adap); + if (adap->fe_adap[0].fe == NULL) return -EIO; return 0; @@ -1207,7 +1219,7 @@ static int cxusb_mygica_d689_frontend_attach(struct dvb_usb_adapter *adap) usb_clear_halt(d->udev, usb_rcvbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint)); usb_clear_halt(d->udev, - usb_rcvbulkpipe(d->udev, d->props.adapter[0].stream.endpoint)); + usb_rcvbulkpipe(d->udev, d->props.adapter[0].fe[0].stream.endpoint)); /* Reset the tuner */ @@ -1223,9 +1235,9 @@ static int cxusb_mygica_d689_frontend_attach(struct dvb_usb_adapter *adap) msleep(100); /* Attach frontend */ - adap->fe = dvb_attach(atbm8830_attach, &mygica_d689_atbm8830_cfg, + adap->fe_adap[0].fe = dvb_attach(atbm8830_attach, &mygica_d689_atbm8830_cfg, &d->i2c_adap); - if (adap->fe == NULL) + if (adap->fe_adap[0].fe == NULL) return -EIO; return 0; @@ -1383,6 +1395,8 @@ static struct dvb_usb_device_properties cxusb_medion_properties = { .num_adapters = 1, .adapter = { { + .num_frontends = 1, + .fe = {{ .streaming_ctrl = cxusb_streaming_ctrl, .frontend_attach = cxusb_cx22702_frontend_attach, .tuner_attach = cxusb_fmd1216me_tuner_attach, @@ -1397,7 +1411,7 @@ static struct dvb_usb_device_properties cxusb_medion_properties = { } } }, - + }}, }, }, .power_ctrl = cxusb_power_ctrl, @@ -1429,6 +1443,8 @@ static struct dvb_usb_device_properties cxusb_bluebird_lgh064f_properties = { .num_adapters = 1, .adapter = { { + .num_frontends = 1, + .fe = {{ .streaming_ctrl = cxusb_streaming_ctrl, .frontend_attach = cxusb_lgdt3303_frontend_attach, .tuner_attach = cxusb_lgh064f_tuner_attach, @@ -1444,6 +1460,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_lgh064f_properties = { } } }, + }}, }, }, @@ -1483,6 +1500,8 @@ static struct dvb_usb_device_properties cxusb_bluebird_dee1601_properties = { .num_adapters = 1, .adapter = { { + .num_frontends = 1, + .fe = {{ .streaming_ctrl = cxusb_streaming_ctrl, .frontend_attach = cxusb_dee1601_frontend_attach, .tuner_attach = cxusb_dee1601_tuner_attach, @@ -1497,6 +1516,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_dee1601_properties = { } } }, + }}, }, }, @@ -1544,6 +1564,8 @@ static struct dvb_usb_device_properties cxusb_bluebird_lgz201_properties = { .num_adapters = 2, .adapter = { { + .num_frontends = 1, + .fe = {{ .streaming_ctrl = cxusb_streaming_ctrl, .frontend_attach = cxusb_mt352_frontend_attach, .tuner_attach = cxusb_lgz201_tuner_attach, @@ -1559,6 +1581,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_lgz201_properties = { } } }, + }}, }, }, .power_ctrl = cxusb_bluebird_power_ctrl, @@ -1596,6 +1619,8 @@ static struct dvb_usb_device_properties cxusb_bluebird_dtt7579_properties = { .num_adapters = 1, .adapter = { { + .num_frontends = 1, + .fe = {{ .streaming_ctrl = cxusb_streaming_ctrl, .frontend_attach = cxusb_mt352_frontend_attach, .tuner_attach = cxusb_dtt7579_tuner_attach, @@ -1611,6 +1636,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_dtt7579_properties = { } } }, + }}, }, }, .power_ctrl = cxusb_bluebird_power_ctrl, @@ -1645,6 +1671,8 @@ static struct dvb_usb_device_properties cxusb_bluebird_dualdig4_properties = { .num_adapters = 1, .adapter = { { + .num_frontends = 1, + .fe = {{ .streaming_ctrl = cxusb_streaming_ctrl, .frontend_attach = cxusb_dualdig4_frontend_attach, .tuner_attach = cxusb_dvico_xc3028_tuner_attach, @@ -1659,6 +1687,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_dualdig4_properties = { } } }, + }}, }, }, @@ -1695,6 +1724,8 @@ static struct dvb_usb_device_properties cxusb_bluebird_nano2_properties = { .num_adapters = 1, .adapter = { { + .num_frontends = 1, + .fe = {{ .streaming_ctrl = cxusb_streaming_ctrl, .frontend_attach = cxusb_nano2_frontend_attach, .tuner_attach = cxusb_dvico_xc3028_tuner_attach, @@ -1709,6 +1740,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_nano2_properties = { } } }, + }}, }, }, @@ -1747,6 +1779,8 @@ static struct dvb_usb_device_properties cxusb_bluebird_nano2_needsfirmware_prope .num_adapters = 1, .adapter = { { + .num_frontends = 1, + .fe = {{ .streaming_ctrl = cxusb_streaming_ctrl, .frontend_attach = cxusb_nano2_frontend_attach, .tuner_attach = cxusb_dvico_xc3028_tuner_attach, @@ -1761,6 +1795,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_nano2_needsfirmware_prope } } }, + }}, }, }, @@ -1796,6 +1831,8 @@ static struct dvb_usb_device_properties cxusb_aver_a868r_properties = { .num_adapters = 1, .adapter = { { + .num_frontends = 1, + .fe = {{ .streaming_ctrl = cxusb_aver_streaming_ctrl, .frontend_attach = cxusb_aver_lgdt3303_frontend_attach, .tuner_attach = cxusb_mxl5003s_tuner_attach, @@ -1810,7 +1847,7 @@ static struct dvb_usb_device_properties cxusb_aver_a868r_properties = { } } }, - + }}, }, }, .power_ctrl = cxusb_aver_power_ctrl, @@ -1839,10 +1876,12 @@ struct dvb_usb_device_properties cxusb_bluebird_dualdig4_rev2_properties = { .num_adapters = 1, .adapter = { { + .size_of_priv = sizeof(struct dib0700_adapter_state), + .num_frontends = 1, + .fe = {{ .streaming_ctrl = cxusb_streaming_ctrl, .frontend_attach = cxusb_dualdig4_rev2_frontend_attach, .tuner_attach = cxusb_dualdig4_rev2_tuner_attach, - .size_of_priv = sizeof(struct dib0700_adapter_state), /* parameter for the MPEG2-data transfer */ .stream = { .type = USB_BULK, @@ -1854,6 +1893,7 @@ struct dvb_usb_device_properties cxusb_bluebird_dualdig4_rev2_properties = { } } }, + }}, }, }, @@ -1889,6 +1929,8 @@ static struct dvb_usb_device_properties cxusb_d680_dmb_properties = { .num_adapters = 1, .adapter = { { + .num_frontends = 1, + .fe = {{ .streaming_ctrl = cxusb_d680_dmb_streaming_ctrl, .frontend_attach = cxusb_d680_dmb_frontend_attach, .tuner_attach = cxusb_d680_dmb_tuner_attach, @@ -1904,6 +1946,7 @@ static struct dvb_usb_device_properties cxusb_d680_dmb_properties = { } } }, + }}, }, }, @@ -1940,6 +1983,8 @@ static struct dvb_usb_device_properties cxusb_mygica_d689_properties = { .num_adapters = 1, .adapter = { { + .num_frontends = 1, + .fe = {{ .streaming_ctrl = cxusb_d680_dmb_streaming_ctrl, .frontend_attach = cxusb_mygica_d689_frontend_attach, .tuner_attach = cxusb_mygica_d689_tuner_attach, @@ -1955,6 +2000,7 @@ static struct dvb_usb_device_properties cxusb_mygica_d689_properties = { } } }, + }}, }, }, diff --git a/drivers/media/dvb/dvb-usb/dib0700_core.c b/drivers/media/dvb/dvb-usb/dib0700_core.c index 5eb91b4f8fd0..156cbfc9c79d 100644 --- a/drivers/media/dvb/dvb-usb/dib0700_core.c +++ b/drivers/media/dvb/dvb-usb/dib0700_core.c @@ -30,6 +30,11 @@ int dib0700_get_version(struct dvb_usb_device *d, u32 *hwversion, struct dib0700_state *st = d->priv; int ret; + if (mutex_lock_interruptible(&d->usb_mutex) < 0) { + err("could not acquire lock"); + return 0; + } + ret = usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0), REQUEST_GET_VERSION, USB_TYPE_VENDOR | USB_DIR_IN, 0, 0, @@ -46,6 +51,7 @@ int dib0700_get_version(struct dvb_usb_device *d, u32 *hwversion, if (fwtype != NULL) *fwtype = (st->buf[12] << 24) | (st->buf[13] << 16) | (st->buf[14] << 8) | st->buf[15]; + mutex_unlock(&d->usb_mutex); return ret; } @@ -108,7 +114,12 @@ int dib0700_ctrl_rd(struct dvb_usb_device *d, u8 *tx, u8 txlen, u8 *rx, u8 rxlen int dib0700_set_gpio(struct dvb_usb_device *d, enum dib07x0_gpios gpio, u8 gpio_dir, u8 gpio_val) { struct dib0700_state *st = d->priv; - s16 ret; + int ret; + + if (mutex_lock_interruptible(&d->usb_mutex) < 0) { + err("could not acquire lock"); + return 0; + } st->buf[0] = REQUEST_SET_GPIO; st->buf[1] = gpio; @@ -116,6 +127,7 @@ int dib0700_set_gpio(struct dvb_usb_device *d, enum dib07x0_gpios gpio, u8 gpio_ ret = dib0700_ctrl_wr(d, st->buf, 3); + mutex_unlock(&d->usb_mutex); return ret; } @@ -125,6 +137,11 @@ static int dib0700_set_usb_xfer_len(struct dvb_usb_device *d, u16 nb_ts_packets) int ret; if (st->fw_version >= 0x10201) { + if (mutex_lock_interruptible(&d->usb_mutex) < 0) { + err("could not acquire lock"); + return 0; + } + st->buf[0] = REQUEST_SET_USB_XFER_LEN; st->buf[1] = (nb_ts_packets >> 8) & 0xff; st->buf[2] = nb_ts_packets & 0xff; @@ -132,6 +149,7 @@ static int dib0700_set_usb_xfer_len(struct dvb_usb_device *d, u16 nb_ts_packets) deb_info("set the USB xfer len to %i Ts packet\n", nb_ts_packets); ret = dib0700_ctrl_wr(d, st->buf, 3); + mutex_unlock(&d->usb_mutex); } else { deb_info("this firmware does not allow to change the USB xfer len\n"); ret = -EIO; @@ -208,6 +226,10 @@ static int dib0700_i2c_xfer_new(struct i2c_adapter *adap, struct i2c_msg *msg, } else { /* Write request */ + if (mutex_lock_interruptible(&d->usb_mutex) < 0) { + err("could not acquire lock"); + return 0; + } st->buf[0] = REQUEST_NEW_I2C_WRITE; st->buf[1] = msg[i].addr << 1; st->buf[2] = (en_start << 7) | (en_stop << 6) | @@ -227,6 +249,7 @@ static int dib0700_i2c_xfer_new(struct i2c_adapter *adap, struct i2c_msg *msg, USB_TYPE_VENDOR | USB_DIR_OUT, 0, 0, st->buf, msg[i].len + 4, USB_CTRL_GET_TIMEOUT); + mutex_unlock(&d->usb_mutex); if (result < 0) { deb_info("i2c write error (status = %d)\n", result); break; @@ -249,6 +272,10 @@ static int dib0700_i2c_xfer_legacy(struct i2c_adapter *adap, if (mutex_lock_interruptible(&d->i2c_mutex) < 0) return -EAGAIN; + if (mutex_lock_interruptible(&d->usb_mutex) < 0) { + err("could not acquire lock"); + return 0; + } for (i = 0; i < num; i++) { /* fill in the address */ @@ -279,6 +306,7 @@ static int dib0700_i2c_xfer_legacy(struct i2c_adapter *adap, break; } } + mutex_unlock(&d->usb_mutex); mutex_unlock(&d->i2c_mutex); return i; @@ -337,7 +365,12 @@ static int dib0700_set_clock(struct dvb_usb_device *d, u8 en_pll, u16 pll_loopdiv, u16 free_div, u16 dsuScaler) { struct dib0700_state *st = d->priv; - s16 ret; + int ret; + + if (mutex_lock_interruptible(&d->usb_mutex) < 0) { + err("could not acquire lock"); + return 0; + } st->buf[0] = REQUEST_SET_CLOCK; st->buf[1] = (en_pll << 7) | (pll_src << 6) | @@ -352,6 +385,7 @@ static int dib0700_set_clock(struct dvb_usb_device *d, u8 en_pll, st->buf[9] = dsuScaler & 0xff; /* LSB */ ret = dib0700_ctrl_wr(d, st->buf, 10); + mutex_unlock(&d->usb_mutex); return ret; } @@ -360,10 +394,16 @@ int dib0700_set_i2c_speed(struct dvb_usb_device *d, u16 scl_kHz) { struct dib0700_state *st = d->priv; u16 divider; + int ret; if (scl_kHz == 0) return -EINVAL; + if (mutex_lock_interruptible(&d->usb_mutex) < 0) { + err("could not acquire lock"); + return 0; + } + st->buf[0] = REQUEST_SET_I2C_PARAM; divider = (u16) (30000 / scl_kHz); st->buf[1] = 0; @@ -379,7 +419,11 @@ int dib0700_set_i2c_speed(struct dvb_usb_device *d, u16 scl_kHz) deb_info("setting I2C speed: %04x %04x %04x (%d kHz).", (st->buf[2] << 8) | (st->buf[3]), (st->buf[4] << 8) | st->buf[5], (st->buf[6] << 8) | st->buf[7], scl_kHz); - return dib0700_ctrl_wr(d, st->buf, 8); + + ret = dib0700_ctrl_wr(d, st->buf, 8); + mutex_unlock(&d->usb_mutex); + + return ret; } @@ -484,13 +528,13 @@ int dib0700_download_firmware(struct usb_device *udev, const struct firmware *fw for (adap_num = 0; adap_num < dib0700_devices[i].num_adapters; adap_num++) { if (fw_version >= 0x10201) { - dib0700_devices[i].adapter[adap_num].stream.u.bulk.buffersize = 188*nb_packet_buffer_size; + dib0700_devices[i].adapter[adap_num].fe[0].stream.u.bulk.buffersize = 188*nb_packet_buffer_size; } else { /* for fw version older than 1.20.1, * the buffersize has to be n times 512 */ - dib0700_devices[i].adapter[adap_num].stream.u.bulk.buffersize = ((188*nb_packet_buffer_size+188/2)/512)*512; - if (dib0700_devices[i].adapter[adap_num].stream.u.bulk.buffersize < 512) - dib0700_devices[i].adapter[adap_num].stream.u.bulk.buffersize = 512; + dib0700_devices[i].adapter[adap_num].fe[0].stream.u.bulk.buffersize = ((188*nb_packet_buffer_size+188/2)/512)*512; + if (dib0700_devices[i].adapter[adap_num].fe[0].stream.u.bulk.buffersize < 512) + dib0700_devices[i].adapter[adap_num].fe[0].stream.u.bulk.buffersize = 512; } } } @@ -515,6 +559,11 @@ int dib0700_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) } } + if (mutex_lock_interruptible(&adap->dev->usb_mutex) < 0) { + err("could not acquire lock"); + return 0; + } + st->buf[0] = REQUEST_ENABLE_VIDEO; /* this bit gives a kind of command, * rather than enabling something or not */ @@ -530,25 +579,28 @@ int dib0700_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) deb_info("modifying (%d) streaming state for %d\n", onoff, adap->id); st->channel_state &= ~0x3; - if ((adap->stream.props.endpoint != 2) - && (adap->stream.props.endpoint != 3)) { - deb_info("the endpoint number (%i) is not correct, use the adapter id instead", adap->stream.props.endpoint); + if ((adap->fe_adap[0].stream.props.endpoint != 2) + && (adap->fe_adap[0].stream.props.endpoint != 3)) { + deb_info("the endpoint number (%i) is not correct, use the adapter id instead", adap->fe_adap[0].stream.props.endpoint); if (onoff) st->channel_state |= 1 << (adap->id); else st->channel_state |= 1 << ~(adap->id); } else { if (onoff) - st->channel_state |= 1 << (adap->stream.props.endpoint-2); + st->channel_state |= 1 << (adap->fe_adap[0].stream.props.endpoint-2); else - st->channel_state |= 1 << (3-adap->stream.props.endpoint); + st->channel_state |= 1 << (3-adap->fe_adap[0].stream.props.endpoint); } st->buf[2] |= st->channel_state; deb_info("data for streaming: %x %x\n", st->buf[1], st->buf[2]); - return dib0700_ctrl_wr(adap->dev, st->buf, 4); + ret = dib0700_ctrl_wr(adap->dev, st->buf, 4); + mutex_unlock(&adap->dev->usb_mutex); + + return ret; } int dib0700_change_protocol(struct rc_dev *rc, u64 rc_type) @@ -557,6 +609,11 @@ int dib0700_change_protocol(struct rc_dev *rc, u64 rc_type) struct dib0700_state *st = d->priv; int new_proto, ret; + if (mutex_lock_interruptible(&d->usb_mutex) < 0) { + err("could not acquire lock"); + return 0; + } + st->buf[0] = REQUEST_SET_RC; st->buf[1] = 0; st->buf[2] = 0; @@ -567,23 +624,29 @@ int dib0700_change_protocol(struct rc_dev *rc, u64 rc_type) else if (rc_type == RC_TYPE_NEC) new_proto = 0; else if (rc_type == RC_TYPE_RC6) { - if (st->fw_version < 0x10200) - return -EINVAL; + if (st->fw_version < 0x10200) { + ret = -EINVAL; + goto out; + } new_proto = 2; - } else - return -EINVAL; + } else { + ret = -EINVAL; + goto out; + } st->buf[1] = new_proto; ret = dib0700_ctrl_wr(d, st->buf, 3); if (ret < 0) { err("ir protocol setup failed"); - return ret; + goto out; } d->props.rc.core.protocol = rc_type; +out: + mutex_unlock(&d->usb_mutex); return ret; } diff --git a/drivers/media/dvb/dvb-usb/dib0700_devices.c b/drivers/media/dvb/dvb-usb/dib0700_devices.c index d0ea5b64f6b4..f313182eb9d5 100644 --- a/drivers/media/dvb/dvb-usb/dib0700_devices.c +++ b/drivers/media/dvb/dvb-usb/dib0700_devices.c @@ -101,7 +101,7 @@ static int bristol_frontend_attach(struct dvb_usb_adapter *adap) } } st->mt2060_if1[adap->id] = 1220; - return (adap->fe = dvb_attach(dib3000mc_attach, &adap->dev->i2c_adap, + return (adap->fe_adap[0].fe = dvb_attach(dib3000mc_attach, &adap->dev->i2c_adap, (10 + adap->id) << 1, &bristol_dib3000mc_config[adap->id])) == NULL ? -ENODEV : 0; } @@ -118,15 +118,16 @@ static int eeprom_read(struct i2c_adapter *adap,u8 adrs,u8 *pval) static int bristol_tuner_attach(struct dvb_usb_adapter *adap) { struct i2c_adapter *prim_i2c = &adap->dev->i2c_adap; - struct i2c_adapter *tun_i2c = dib3000mc_get_tuner_i2c_master(adap->fe, 1); + struct i2c_adapter *tun_i2c = dib3000mc_get_tuner_i2c_master(adap->fe_adap[0].fe, 1); s8 a; int if1=1220; if (adap->dev->udev->descriptor.idVendor == cpu_to_le16(USB_VID_HAUPPAUGE) && adap->dev->udev->descriptor.idProduct == cpu_to_le16(USB_PID_HAUPPAUGE_NOVA_T_500_2)) { if (!eeprom_read(prim_i2c,0x59 + adap->id,&a)) if1=1220+a; } - return dvb_attach(mt2060_attach,adap->fe, tun_i2c,&bristol_mt2060_config[adap->id], - if1) == NULL ? -ENODEV : 0; + return dvb_attach(mt2060_attach, adap->fe_adap[0].fe, tun_i2c, + &bristol_mt2060_config[adap->id], if1) == NULL ? + -ENODEV : 0; } /* STK7700D: Pinnacle/Terratec/Hauppauge Dual DVB-T Diversity */ @@ -279,10 +280,12 @@ static int stk7700P2_frontend_attach(struct dvb_usb_adapter *adap) } } - adap->fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap,0x80+(adap->id << 1), - &stk7700d_dib7000p_mt2266_config[adap->id]); + adap->fe_adap[0].fe = + dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, + 0x80 + (adap->id << 1), + &stk7700d_dib7000p_mt2266_config[adap->id]); - return adap->fe == NULL ? -ENODEV : 0; + return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; } static int stk7700d_frontend_attach(struct dvb_usb_adapter *adap) @@ -306,17 +309,19 @@ static int stk7700d_frontend_attach(struct dvb_usb_adapter *adap) } } - adap->fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap,0x80+(adap->id << 1), - &stk7700d_dib7000p_mt2266_config[adap->id]); + adap->fe_adap[0].fe = + dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, + 0x80 + (adap->id << 1), + &stk7700d_dib7000p_mt2266_config[adap->id]); - return adap->fe == NULL ? -ENODEV : 0; + return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; } static int stk7700d_tuner_attach(struct dvb_usb_adapter *adap) { struct i2c_adapter *tun_i2c; - tun_i2c = dib7000p_get_i2c_master(adap->fe, DIBX000_I2C_INTERFACE_TUNER, 1); - return dvb_attach(mt2266_attach, adap->fe, tun_i2c, + tun_i2c = dib7000p_get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_TUNER, 1); + return dvb_attach(mt2266_attach, adap->fe_adap[0].fe, tun_i2c, &stk7700d_mt2266_config[adap->id]) == NULL ? -ENODEV : 0; } @@ -396,8 +401,8 @@ static int stk7700ph_xc3028_callback(void *ptr, int component, switch (command) { case XC2028_TUNER_RESET: /* Send the tuner in then out of reset */ - dib7000p_set_gpio(adap->fe, 8, 0, 0); msleep(10); - dib7000p_set_gpio(adap->fe, 8, 0, 1); + dib7000p_set_gpio(adap->fe_adap[0].fe, 8, 0, 0); msleep(10); + dib7000p_set_gpio(adap->fe_adap[0].fe, 8, 0, 1); break; case XC2028_RESET_CLK: break; @@ -447,25 +452,25 @@ static int stk7700ph_frontend_attach(struct dvb_usb_adapter *adap) return -ENODEV; } - adap->fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x80, + adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x80, &stk7700ph_dib7700_xc3028_config); - return adap->fe == NULL ? -ENODEV : 0; + return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; } static int stk7700ph_tuner_attach(struct dvb_usb_adapter *adap) { struct i2c_adapter *tun_i2c; - tun_i2c = dib7000p_get_i2c_master(adap->fe, + tun_i2c = dib7000p_get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_TUNER, 1); stk7700ph_xc3028_config.i2c_adap = tun_i2c; /* FIXME: generalize & move to common area */ - adap->fe->callback = stk7700ph_xc3028_callback; + adap->fe_adap[0].fe->callback = stk7700ph_xc3028_callback; - return dvb_attach(xc2028_attach, adap->fe, &stk7700ph_xc3028_config) + return dvb_attach(xc2028_attach, adap->fe_adap[0].fe, &stk7700ph_xc3028_config) == NULL ? -ENODEV : 0; } @@ -685,12 +690,12 @@ static int stk7700p_frontend_attach(struct dvb_usb_adapter *adap) st->mt2060_if1[0] = 1220; if (dib7000pc_detection(&adap->dev->i2c_adap)) { - adap->fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 18, &stk7700p_dib7000p_config); + adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 18, &stk7700p_dib7000p_config); st->is_dib7000pc = 1; } else - adap->fe = dvb_attach(dib7000m_attach, &adap->dev->i2c_adap, 18, &stk7700p_dib7000m_config); + adap->fe_adap[0].fe = dvb_attach(dib7000m_attach, &adap->dev->i2c_adap, 18, &stk7700p_dib7000m_config); - return adap->fe == NULL ? -ENODEV : 0; + return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; } static struct mt2060_config stk7700p_mt2060_config = { @@ -709,11 +714,11 @@ static int stk7700p_tuner_attach(struct dvb_usb_adapter *adap) if (!eeprom_read(prim_i2c,0x58,&a)) if1=1220+a; } if (st->is_dib7000pc) - tun_i2c = dib7000p_get_i2c_master(adap->fe, DIBX000_I2C_INTERFACE_TUNER, 1); + tun_i2c = dib7000p_get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_TUNER, 1); else - tun_i2c = dib7000m_get_i2c_master(adap->fe, DIBX000_I2C_INTERFACE_TUNER, 1); + tun_i2c = dib7000m_get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_TUNER, 1); - return dvb_attach(mt2060_attach, adap->fe, tun_i2c, &stk7700p_mt2060_config, + return dvb_attach(mt2060_attach, adap->fe_adap[0].fe, tun_i2c, &stk7700p_mt2060_config, if1) == NULL ? -ENODEV : 0; } @@ -843,33 +848,33 @@ static int dib7770_set_param_override(struct dvb_frontend *fe, static int dib7770p_tuner_attach(struct dvb_usb_adapter *adap) { struct dib0700_adapter_state *st = adap->priv; - struct i2c_adapter *tun_i2c = dib7000p_get_i2c_master(adap->fe, + struct i2c_adapter *tun_i2c = dib7000p_get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_TUNER, 1); - if (dvb_attach(dib0070_attach, adap->fe, tun_i2c, - &dib7770p_dib0070_config) == NULL) + if (dvb_attach(dib0070_attach, adap->fe_adap[0].fe, tun_i2c, + &dib7770p_dib0070_config) == NULL) return -ENODEV; - st->set_param_save = adap->fe->ops.tuner_ops.set_params; - adap->fe->ops.tuner_ops.set_params = dib7770_set_param_override; + st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params; + adap->fe_adap[0].fe->ops.tuner_ops.set_params = dib7770_set_param_override; return 0; } static int dib7070p_tuner_attach(struct dvb_usb_adapter *adap) { struct dib0700_adapter_state *st = adap->priv; - struct i2c_adapter *tun_i2c = dib7000p_get_i2c_master(adap->fe, DIBX000_I2C_INTERFACE_TUNER, 1); + struct i2c_adapter *tun_i2c = dib7000p_get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_TUNER, 1); if (adap->id == 0) { - if (dvb_attach(dib0070_attach, adap->fe, tun_i2c, &dib7070p_dib0070_config[0]) == NULL) + if (dvb_attach(dib0070_attach, adap->fe_adap[0].fe, tun_i2c, &dib7070p_dib0070_config[0]) == NULL) return -ENODEV; } else { - if (dvb_attach(dib0070_attach, adap->fe, tun_i2c, &dib7070p_dib0070_config[1]) == NULL) + if (dvb_attach(dib0070_attach, adap->fe_adap[0].fe, tun_i2c, &dib7070p_dib0070_config[1]) == NULL) return -ENODEV; } - st->set_param_save = adap->fe->ops.tuner_ops.set_params; - adap->fe->ops.tuner_ops.set_params = dib7070_set_param_override; + st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params; + adap->fe_adap[0].fe->ops.tuner_ops.set_params = dib7070_set_param_override; return 0; } @@ -878,26 +883,26 @@ static int stk7700p_pid_filter(struct dvb_usb_adapter *adapter, int index, { struct dib0700_state *st = adapter->dev->priv; if (st->is_dib7000pc) - return dib7000p_pid_filter(adapter->fe, index, pid, onoff); - return dib7000m_pid_filter(adapter->fe, index, pid, onoff); + return dib7000p_pid_filter(adapter->fe_adap[0].fe, index, pid, onoff); + return dib7000m_pid_filter(adapter->fe_adap[0].fe, index, pid, onoff); } static int stk7700p_pid_filter_ctrl(struct dvb_usb_adapter *adapter, int onoff) { struct dib0700_state *st = adapter->dev->priv; if (st->is_dib7000pc) - return dib7000p_pid_filter_ctrl(adapter->fe, onoff); - return dib7000m_pid_filter_ctrl(adapter->fe, onoff); + return dib7000p_pid_filter_ctrl(adapter->fe_adap[0].fe, onoff); + return dib7000m_pid_filter_ctrl(adapter->fe_adap[0].fe, onoff); } static int stk70x0p_pid_filter(struct dvb_usb_adapter *adapter, int index, u16 pid, int onoff) { - return dib7000p_pid_filter(adapter->fe, index, pid, onoff); + return dib7000p_pid_filter(adapter->fe_adap[0].fe, index, pid, onoff); } static int stk70x0p_pid_filter_ctrl(struct dvb_usb_adapter *adapter, int onoff) { - return dib7000p_pid_filter_ctrl(adapter->fe, onoff); + return dib7000p_pid_filter_ctrl(adapter->fe_adap[0].fe, onoff); } static struct dibx000_bandwidth_config dib7070_bw_config_12_mhz = { @@ -955,9 +960,9 @@ static int stk7070p_frontend_attach(struct dvb_usb_adapter *adap) return -ENODEV; } - adap->fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x80, + adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x80, &dib7070p_dib7000p_config); - return adap->fe == NULL ? -ENODEV : 0; + return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; } /* STK7770P */ @@ -1007,9 +1012,9 @@ static int stk7770p_frontend_attach(struct dvb_usb_adapter *adap) return -ENODEV; } - adap->fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x80, + adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x80, &dib7770p_dib7000p_config); - return adap->fe == NULL ? -ENODEV : 0; + return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; } /* DIB807x generic */ @@ -1225,34 +1230,34 @@ static int dib807x_set_param_override(struct dvb_frontend *fe, static int dib807x_tuner_attach(struct dvb_usb_adapter *adap) { struct dib0700_adapter_state *st = adap->priv; - struct i2c_adapter *tun_i2c = dib8000_get_i2c_master(adap->fe, + struct i2c_adapter *tun_i2c = dib8000_get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_TUNER, 1); if (adap->id == 0) { - if (dvb_attach(dib0070_attach, adap->fe, tun_i2c, + if (dvb_attach(dib0070_attach, adap->fe_adap[0].fe, tun_i2c, &dib807x_dib0070_config[0]) == NULL) return -ENODEV; } else { - if (dvb_attach(dib0070_attach, adap->fe, tun_i2c, + if (dvb_attach(dib0070_attach, adap->fe_adap[0].fe, tun_i2c, &dib807x_dib0070_config[1]) == NULL) return -ENODEV; } - st->set_param_save = adap->fe->ops.tuner_ops.set_params; - adap->fe->ops.tuner_ops.set_params = dib807x_set_param_override; + st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params; + adap->fe_adap[0].fe->ops.tuner_ops.set_params = dib807x_set_param_override; return 0; } static int stk80xx_pid_filter(struct dvb_usb_adapter *adapter, int index, u16 pid, int onoff) { - return dib8000_pid_filter(adapter->fe, index, pid, onoff); + return dib8000_pid_filter(adapter->fe_adap[0].fe, index, pid, onoff); } static int stk80xx_pid_filter_ctrl(struct dvb_usb_adapter *adapter, int onoff) { - return dib8000_pid_filter_ctrl(adapter->fe, onoff); + return dib8000_pid_filter_ctrl(adapter->fe_adap[0].fe, onoff); } /* STK807x */ @@ -1276,10 +1281,10 @@ static int stk807x_frontend_attach(struct dvb_usb_adapter *adap) dib8000_i2c_enumeration(&adap->dev->i2c_adap, 1, 18, 0x80); - adap->fe = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x80, + adap->fe_adap[0].fe = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x80, &dib807x_dib8000_config[0]); - return adap->fe == NULL ? -ENODEV : 0; + return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; } /* STK807xPVR */ @@ -1305,10 +1310,10 @@ static int stk807xpvr_frontend_attach0(struct dvb_usb_adapter *adap) /* initialize IC 0 */ dib8000_i2c_enumeration(&adap->dev->i2c_adap, 1, 0x22, 0x80); - adap->fe = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x80, + adap->fe_adap[0].fe = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x80, &dib807x_dib8000_config[0]); - return adap->fe == NULL ? -ENODEV : 0; + return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; } static int stk807xpvr_frontend_attach1(struct dvb_usb_adapter *adap) @@ -1316,10 +1321,10 @@ static int stk807xpvr_frontend_attach1(struct dvb_usb_adapter *adap) /* initialize IC 1 */ dib8000_i2c_enumeration(&adap->dev->i2c_adap, 1, 0x12, 0x82); - adap->fe = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x82, + adap->fe_adap[0].fe = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x82, &dib807x_dib8000_config[1]); - return adap->fe == NULL ? -ENODEV : 0; + return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; } /* STK8096GP */ @@ -1546,13 +1551,13 @@ static int dib8096_set_param_override(struct dvb_frontend *fe, static int dib809x_tuner_attach(struct dvb_usb_adapter *adap) { struct dib0700_adapter_state *st = adap->priv; - struct i2c_adapter *tun_i2c = dib8000_get_i2c_master(adap->fe, DIBX000_I2C_INTERFACE_TUNER, 1); + struct i2c_adapter *tun_i2c = dib8000_get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_TUNER, 1); - if (dvb_attach(dib0090_register, adap->fe, tun_i2c, &dib809x_dib0090_config) == NULL) + if (dvb_attach(dib0090_register, adap->fe_adap[0].fe, tun_i2c, &dib809x_dib0090_config) == NULL) return -ENODEV; - st->set_param_save = adap->fe->ops.tuner_ops.set_params; - adap->fe->ops.tuner_ops.set_params = dib8096_set_param_override; + st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params; + adap->fe_adap[0].fe->ops.tuner_ops.set_params = dib8096_set_param_override; return 0; } @@ -1575,30 +1580,30 @@ static int stk809x_frontend_attach(struct dvb_usb_adapter *adap) dib8000_i2c_enumeration(&adap->dev->i2c_adap, 1, 18, 0x80); - adap->fe = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x80, &dib809x_dib8000_config[0]); + adap->fe_adap[0].fe = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x80, &dib809x_dib8000_config[0]); - return adap->fe == NULL ? -ENODEV : 0; + return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; } static int nim8096md_tuner_attach(struct dvb_usb_adapter *adap) { struct dib0700_adapter_state *st = adap->priv; struct i2c_adapter *tun_i2c; - struct dvb_frontend *fe_slave = dib8000_get_slave_frontend(adap->fe, 1); + struct dvb_frontend *fe_slave = dib8000_get_slave_frontend(adap->fe_adap[0].fe, 1); if (fe_slave) { tun_i2c = dib8000_get_i2c_master(fe_slave, DIBX000_I2C_INTERFACE_TUNER, 1); if (dvb_attach(dib0090_register, fe_slave, tun_i2c, &dib809x_dib0090_config) == NULL) return -ENODEV; - fe_slave->dvb = adap->fe->dvb; + fe_slave->dvb = adap->fe_adap[0].fe->dvb; fe_slave->ops.tuner_ops.set_params = dib8096_set_param_override; } - tun_i2c = dib8000_get_i2c_master(adap->fe, DIBX000_I2C_INTERFACE_TUNER, 1); - if (dvb_attach(dib0090_register, adap->fe, tun_i2c, &dib809x_dib0090_config) == NULL) + tun_i2c = dib8000_get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_TUNER, 1); + if (dvb_attach(dib0090_register, adap->fe_adap[0].fe, tun_i2c, &dib809x_dib0090_config) == NULL) return -ENODEV; - st->set_param_save = adap->fe->ops.tuner_ops.set_params; - adap->fe->ops.tuner_ops.set_params = dib8096_set_param_override; + st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params; + adap->fe_adap[0].fe->ops.tuner_ops.set_params = dib8096_set_param_override; return 0; } @@ -1626,12 +1631,12 @@ static int nim8096md_frontend_attach(struct dvb_usb_adapter *adap) dib8000_i2c_enumeration(&adap->dev->i2c_adap, 2, 18, 0x80); - adap->fe = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x80, &dib809x_dib8000_config[0]); - if (adap->fe == NULL) + adap->fe_adap[0].fe = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x80, &dib809x_dib8000_config[0]); + if (adap->fe_adap[0].fe == NULL) return -ENODEV; fe_slave = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x82, &dib809x_dib8000_config[1]); - dib8000_set_slave_frontend(adap->fe, fe_slave); + dib8000_set_slave_frontend(adap->fe_adap[0].fe, fe_slave); return fe_slave == NULL ? -ENODEV : 0; } @@ -1639,12 +1644,12 @@ static int nim8096md_frontend_attach(struct dvb_usb_adapter *adap) /* STK9090M */ static int dib90x0_pid_filter(struct dvb_usb_adapter *adapter, int index, u16 pid, int onoff) { - return dib9000_fw_pid_filter(adapter->fe, index, pid, onoff); + return dib9000_fw_pid_filter(adapter->fe_adap[0].fe, index, pid, onoff); } static int dib90x0_pid_filter_ctrl(struct dvb_usb_adapter *adapter, int onoff) { - return dib9000_fw_pid_filter_ctrl(adapter->fe, onoff); + return dib9000_fw_pid_filter_ctrl(adapter->fe_adap[0].fe, onoff); } static int dib90x0_tuner_reset(struct dvb_frontend *fe, int onoff) @@ -1856,15 +1861,15 @@ static int stk9090m_frontend_attach(struct dvb_usb_adapter *adap) stk9090m_config.microcode_B_fe_size = state->frontend_firmware->size; stk9090m_config.microcode_B_fe_buffer = state->frontend_firmware->data; - adap->fe = dvb_attach(dib9000_attach, &adap->dev->i2c_adap, 0x80, &stk9090m_config); + adap->fe_adap[0].fe = dvb_attach(dib9000_attach, &adap->dev->i2c_adap, 0x80, &stk9090m_config); - return adap->fe == NULL ? -ENODEV : 0; + return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; } static int dib9090_tuner_attach(struct dvb_usb_adapter *adap) { struct dib0700_adapter_state *state = adap->priv; - struct i2c_adapter *i2c = dib9000_get_tuner_interface(adap->fe); + struct i2c_adapter *i2c = dib9000_get_tuner_interface(adap->fe_adap[0].fe); u16 data_dib190[10] = { 1, 0x1374, 2, 0x01a2, @@ -1873,13 +1878,13 @@ static int dib9090_tuner_attach(struct dvb_usb_adapter *adap) 8, 0x0486, }; - if (dvb_attach(dib0090_fw_register, adap->fe, i2c, &dib9090_dib0090_config) == NULL) + if (dvb_attach(dib0090_fw_register, adap->fe_adap[0].fe, i2c, &dib9090_dib0090_config) == NULL) return -ENODEV; - i2c = dib9000_get_i2c_master(adap->fe, DIBX000_I2C_INTERFACE_GPIO_1_2, 0); + i2c = dib9000_get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_GPIO_1_2, 0); if (dib01x0_pmu_update(i2c, data_dib190, 10) != 0) return -ENODEV; dib0700_set_i2c_speed(adap->dev, 2000); - if (dib9000_firmware_post_pll_init(adap->fe) < 0) + if (dib9000_firmware_post_pll_init(adap->fe_adap[0].fe) < 0) return -ENODEV; release_firmware(state->frontend_firmware); return 0; @@ -1925,16 +1930,16 @@ static int nim9090md_frontend_attach(struct dvb_usb_adapter *adap) nim9090md_config[1].microcode_B_fe_buffer = state->frontend_firmware->data; dib9000_i2c_enumeration(&adap->dev->i2c_adap, 1, 0x20, 0x80); - adap->fe = dvb_attach(dib9000_attach, &adap->dev->i2c_adap, 0x80, &nim9090md_config[0]); + adap->fe_adap[0].fe = dvb_attach(dib9000_attach, &adap->dev->i2c_adap, 0x80, &nim9090md_config[0]); - if (adap->fe == NULL) + if (adap->fe_adap[0].fe == NULL) return -ENODEV; - i2c = dib9000_get_i2c_master(adap->fe, DIBX000_I2C_INTERFACE_GPIO_3_4, 0); + i2c = dib9000_get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_GPIO_3_4, 0); dib9000_i2c_enumeration(i2c, 1, 0x12, 0x82); fe_slave = dvb_attach(dib9000_attach, i2c, 0x82, &nim9090md_config[1]); - dib9000_set_slave_frontend(adap->fe, fe_slave); + dib9000_set_slave_frontend(adap->fe_adap[0].fe, fe_slave); return fe_slave == NULL ? -ENODEV : 0; } @@ -1951,26 +1956,26 @@ static int nim9090md_tuner_attach(struct dvb_usb_adapter *adap) 0, 0x00ef, 8, 0x0406, }; - i2c = dib9000_get_tuner_interface(adap->fe); - if (dvb_attach(dib0090_fw_register, adap->fe, i2c, &nim9090md_dib0090_config[0]) == NULL) + i2c = dib9000_get_tuner_interface(adap->fe_adap[0].fe); + if (dvb_attach(dib0090_fw_register, adap->fe_adap[0].fe, i2c, &nim9090md_dib0090_config[0]) == NULL) return -ENODEV; - i2c = dib9000_get_i2c_master(adap->fe, DIBX000_I2C_INTERFACE_GPIO_1_2, 0); + i2c = dib9000_get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_GPIO_1_2, 0); if (dib01x0_pmu_update(i2c, data_dib190, 10) < 0) return -ENODEV; dib0700_set_i2c_speed(adap->dev, 2000); - if (dib9000_firmware_post_pll_init(adap->fe) < 0) + if (dib9000_firmware_post_pll_init(adap->fe_adap[0].fe) < 0) return -ENODEV; - fe_slave = dib9000_get_slave_frontend(adap->fe, 1); + fe_slave = dib9000_get_slave_frontend(adap->fe_adap[0].fe, 1); if (fe_slave != NULL) { - i2c = dib9000_get_component_bus_interface(adap->fe); + i2c = dib9000_get_component_bus_interface(adap->fe_adap[0].fe); dib9000_set_i2c_adapter(fe_slave, i2c); i2c = dib9000_get_tuner_interface(fe_slave); if (dvb_attach(dib0090_fw_register, fe_slave, i2c, &nim9090md_dib0090_config[1]) == NULL) return -ENODEV; - fe_slave->dvb = adap->fe->dvb; - dib9000_fw_set_component_bus_speed(adap->fe, 2000); + fe_slave->dvb = adap->fe_adap[0].fe->dvb; + dib9000_fw_set_component_bus_speed(adap->fe_adap[0].fe, 2000); if (dib9000_firmware_post_pll_init(fe_slave) < 0) return -ENODEV; } @@ -2393,23 +2398,23 @@ static int nim7090_frontend_attach(struct dvb_usb_adapter *adap) err("%s: dib7000p_i2c_enumeration failed. Cannot continue\n", __func__); return -ENODEV; } - adap->fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x80, &nim7090_dib7000p_config); + adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x80, &nim7090_dib7000p_config); - return adap->fe == NULL ? -ENODEV : 0; + return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; } static int nim7090_tuner_attach(struct dvb_usb_adapter *adap) { struct dib0700_adapter_state *st = adap->priv; - struct i2c_adapter *tun_i2c = dib7090_get_i2c_tuner(adap->fe); + struct i2c_adapter *tun_i2c = dib7090_get_i2c_tuner(adap->fe_adap[0].fe); - if (dvb_attach(dib0090_register, adap->fe, tun_i2c, &nim7090_dib0090_config) == NULL) + if (dvb_attach(dib0090_register, adap->fe_adap[0].fe, tun_i2c, &nim7090_dib0090_config) == NULL) return -ENODEV; - dib7000p_set_gpio(adap->fe, 8, 0, 1); + dib7000p_set_gpio(adap->fe_adap[0].fe, 8, 0, 1); - st->set_param_save = adap->fe->ops.tuner_ops.set_params; - adap->fe->ops.tuner_ops.set_params = dib7090_agc_startup; + st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params; + adap->fe_adap[0].fe->ops.tuner_ops.set_params = dib7090_agc_startup; return 0; } @@ -2439,11 +2444,11 @@ static int tfe7090pvr_frontend0_attach(struct dvb_usb_adapter *adap) } dib0700_set_i2c_speed(adap->dev, 340); - adap->fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x90, &tfe7090pvr_dib7000p_config[0]); - if (adap->fe == NULL) + adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x90, &tfe7090pvr_dib7000p_config[0]); + if (adap->fe_adap[0].fe == NULL) return -ENODEV; - dib7090_slave_reset(adap->fe); + dib7090_slave_reset(adap->fe_adap[0].fe); return 0; } @@ -2452,50 +2457,50 @@ static int tfe7090pvr_frontend1_attach(struct dvb_usb_adapter *adap) { struct i2c_adapter *i2c; - if (adap->dev->adapter[0].fe == NULL) { + if (adap->dev->adapter[0].fe_adap[0].fe == NULL) { err("the master dib7090 has to be initialized first"); return -ENODEV; /* the master device has not been initialized */ } - i2c = dib7000p_get_i2c_master(adap->dev->adapter[0].fe, DIBX000_I2C_INTERFACE_GPIO_6_7, 1); + i2c = dib7000p_get_i2c_master(adap->dev->adapter[0].fe_adap[0].fe, DIBX000_I2C_INTERFACE_GPIO_6_7, 1); if (dib7000p_i2c_enumeration(i2c, 1, 0x10, &tfe7090pvr_dib7000p_config[1]) != 0) { err("%s: dib7000p_i2c_enumeration failed. Cannot continue\n", __func__); return -ENODEV; } - adap->fe = dvb_attach(dib7000p_attach, i2c, 0x92, &tfe7090pvr_dib7000p_config[1]); + adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, i2c, 0x92, &tfe7090pvr_dib7000p_config[1]); dib0700_set_i2c_speed(adap->dev, 200); - return adap->fe == NULL ? -ENODEV : 0; + return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; } static int tfe7090pvr_tuner0_attach(struct dvb_usb_adapter *adap) { struct dib0700_adapter_state *st = adap->priv; - struct i2c_adapter *tun_i2c = dib7090_get_i2c_tuner(adap->fe); + struct i2c_adapter *tun_i2c = dib7090_get_i2c_tuner(adap->fe_adap[0].fe); - if (dvb_attach(dib0090_register, adap->fe, tun_i2c, &tfe7090pvr_dib0090_config[0]) == NULL) + if (dvb_attach(dib0090_register, adap->fe_adap[0].fe, tun_i2c, &tfe7090pvr_dib0090_config[0]) == NULL) return -ENODEV; - dib7000p_set_gpio(adap->fe, 8, 0, 1); + dib7000p_set_gpio(adap->fe_adap[0].fe, 8, 0, 1); - st->set_param_save = adap->fe->ops.tuner_ops.set_params; - adap->fe->ops.tuner_ops.set_params = dib7090_agc_startup; + st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params; + adap->fe_adap[0].fe->ops.tuner_ops.set_params = dib7090_agc_startup; return 0; } static int tfe7090pvr_tuner1_attach(struct dvb_usb_adapter *adap) { struct dib0700_adapter_state *st = adap->priv; - struct i2c_adapter *tun_i2c = dib7090_get_i2c_tuner(adap->fe); + struct i2c_adapter *tun_i2c = dib7090_get_i2c_tuner(adap->fe_adap[0].fe); - if (dvb_attach(dib0090_register, adap->fe, tun_i2c, &tfe7090pvr_dib0090_config[1]) == NULL) + if (dvb_attach(dib0090_register, adap->fe_adap[0].fe, tun_i2c, &tfe7090pvr_dib0090_config[1]) == NULL) return -ENODEV; - dib7000p_set_gpio(adap->fe, 8, 0, 1); + dib7000p_set_gpio(adap->fe_adap[0].fe, 8, 0, 1); - st->set_param_save = adap->fe->ops.tuner_ops.set_params; - adap->fe->ops.tuner_ops.set_params = dib7090_agc_startup; + st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params; + adap->fe_adap[0].fe->ops.tuner_ops.set_params = dib7090_agc_startup; return 0; } @@ -2555,14 +2560,14 @@ static int stk7070pd_frontend_attach0(struct dvb_usb_adapter *adap) return -ENODEV; } - adap->fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x80, &stk7070pd_dib7000p_config[0]); - return adap->fe == NULL ? -ENODEV : 0; + adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x80, &stk7070pd_dib7000p_config[0]); + return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; } static int stk7070pd_frontend_attach1(struct dvb_usb_adapter *adap) { - adap->fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x82, &stk7070pd_dib7000p_config[1]); - return adap->fe == NULL ? -ENODEV : 0; + adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x82, &stk7070pd_dib7000p_config[1]); + return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; } /* S5H1411 */ @@ -2617,9 +2622,9 @@ static int s5h1411_frontend_attach(struct dvb_usb_adapter *adap) dib0700_set_gpio(adap->dev, GPIO2, GPIO_OUT, 1); /* GPIOs are initialized, do the attach */ - adap->fe = dvb_attach(s5h1411_attach, &pinnacle_801e_config, + adap->fe_adap[0].fe = dvb_attach(s5h1411_attach, &pinnacle_801e_config, &adap->dev->i2c_adap); - return adap->fe == NULL ? -ENODEV : 0; + return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; } static int dib0700_xc5000_tuner_callback(void *priv, int component, @@ -2649,9 +2654,9 @@ static struct xc5000_config s5h1411_xc5000_tunerconfig = { static int xc5000_tuner_attach(struct dvb_usb_adapter *adap) { /* FIXME: generalize & move to common area */ - adap->fe->callback = dib0700_xc5000_tuner_callback; + adap->fe_adap[0].fe->callback = dib0700_xc5000_tuner_callback; - return dvb_attach(xc5000_attach, adap->fe, &adap->dev->i2c_adap, + return dvb_attach(xc5000_attach, adap->fe_adap[0].fe, &adap->dev->i2c_adap, &s5h1411_xc5000_tunerconfig) == NULL ? -ENODEV : 0; } @@ -2663,9 +2668,9 @@ static int dib0700_xc4000_tuner_callback(void *priv, int component, if (command == XC4000_TUNER_RESET) { /* Reset the tuner */ - dib7000p_set_gpio(adap->fe, 8, 0, 0); + dib7000p_set_gpio(adap->fe_adap[0].fe, 8, 0, 0); msleep(10); - dib7000p_set_gpio(adap->fe, 8, 0, 1); + dib7000p_set_gpio(adap->fe_adap[0].fe, 8, 0, 1); } else { err("xc4000: unknown tuner callback command: %d\n", command); return -EINVAL; @@ -2771,11 +2776,11 @@ static int pctv340e_frontend_attach(struct dvb_usb_adapter *adap) return -ENODEV; } - adap->fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x12, + adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x12, &pctv_340e_config); st->is_dib7000pc = 1; - return adap->fe == NULL ? -ENODEV : 0; + return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; } static struct xc4000_config dib7000p_xc4000_tunerconfig = { @@ -2791,7 +2796,7 @@ static int xc4000_tuner_attach(struct dvb_usb_adapter *adap) struct i2c_adapter *tun_i2c; /* The xc4000 is not on the main i2c bus */ - tun_i2c = dib7000p_get_i2c_master(adap->fe, + tun_i2c = dib7000p_get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_TUNER, 1); if (tun_i2c == NULL) { printk(KERN_ERR "Could not reach tuner i2c bus\n"); @@ -2799,9 +2804,9 @@ static int xc4000_tuner_attach(struct dvb_usb_adapter *adap) } /* Setup the reset callback */ - adap->fe->callback = dib0700_xc4000_tuner_callback; + adap->fe_adap[0].fe->callback = dib0700_xc4000_tuner_callback; - return dvb_attach(xc4000_attach, adap->fe, tun_i2c, + return dvb_attach(xc4000_attach, adap->fe_adap[0].fe, tun_i2c, &dib7000p_xc4000_tunerconfig) == NULL ? -ENODEV : 0; } @@ -2857,16 +2862,16 @@ static int lgdt3305_frontend_attach(struct dvb_usb_adapter *adap) dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); msleep(30); - adap->fe = dvb_attach(lgdt3305_attach, + adap->fe_adap[0].fe = dvb_attach(lgdt3305_attach, &hcw_lgdt3305_config, &adap->dev->i2c_adap); - return adap->fe == NULL ? -ENODEV : 0; + return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; } static int mxl5007t_tuner_attach(struct dvb_usb_adapter *adap) { - return dvb_attach(mxl5007t_attach, adap->fe, + return dvb_attach(mxl5007t_attach, adap->fe_adap[0].fe, &adap->dev->i2c_adap, 0x60, &hcw_mxl5007t_config) == NULL ? -ENODEV : 0; } @@ -2989,6 +2994,8 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 1, .adapter = { { + .num_frontends = 1, + .fe = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, .pid_filter_count = 32, .pid_filter = stk7700p_pid_filter, @@ -2997,6 +3004,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { .tuner_attach = stk7700p_tuner_attach, DIB0700_DEFAULT_STREAMING_CONFIG(0x02), + }}, }, }, @@ -3050,15 +3058,21 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 2, .adapter = { { + .num_frontends = 1, + .fe = {{ .frontend_attach = bristol_frontend_attach, .tuner_attach = bristol_tuner_attach, DIB0700_DEFAULT_STREAMING_CONFIG(0x02), + }}, }, { + .num_frontends = 1, + .fe = {{ .frontend_attach = bristol_frontend_attach, .tuner_attach = bristol_tuner_attach, DIB0700_DEFAULT_STREAMING_CONFIG(0x03), + }}, } }, @@ -3084,6 +3098,8 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 2, .adapter = { { + .num_frontends = 1, + .fe = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, .pid_filter_count = 32, .pid_filter = stk70x0p_pid_filter, @@ -3092,7 +3108,10 @@ struct dvb_usb_device_properties dib0700_devices[] = { .tuner_attach = stk7700d_tuner_attach, DIB0700_DEFAULT_STREAMING_CONFIG(0x02), + }}, }, { + .num_frontends = 1, + .fe = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, .pid_filter_count = 32, .pid_filter = stk70x0p_pid_filter, @@ -3101,6 +3120,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { .tuner_attach = stk7700d_tuner_attach, DIB0700_DEFAULT_STREAMING_CONFIG(0x03), + }}, } }, @@ -3143,6 +3163,8 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 1, .adapter = { { + .num_frontends = 1, + .fe = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, .pid_filter_count = 32, .pid_filter = stk70x0p_pid_filter, @@ -3151,6 +3173,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { .tuner_attach = stk7700d_tuner_attach, DIB0700_DEFAULT_STREAMING_CONFIG(0x02), + }}, }, }, @@ -3185,6 +3208,8 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 1, .adapter = { { + .num_frontends = 1, + .fe = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, .pid_filter_count = 32, .pid_filter = stk70x0p_pid_filter, @@ -3193,7 +3218,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { .tuner_attach = dib7070p_tuner_attach, DIB0700_DEFAULT_STREAMING_CONFIG(0x02), - + }}, .size_of_priv = sizeof(struct dib0700_adapter_state), }, }, @@ -3261,6 +3286,8 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 1, .adapter = { { + .num_frontends = 1, + .fe = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, .pid_filter_count = 32, .pid_filter = stk70x0p_pid_filter, @@ -3269,7 +3296,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { .tuner_attach = dib7070p_tuner_attach, DIB0700_DEFAULT_STREAMING_CONFIG(0x02), - + }}, .size_of_priv = sizeof(struct dib0700_adapter_state), }, }, @@ -3305,6 +3332,8 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 2, .adapter = { { + .num_frontends = 1, + .fe = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, .pid_filter_count = 32, .pid_filter = stk70x0p_pid_filter, @@ -3313,9 +3342,11 @@ struct dvb_usb_device_properties dib0700_devices[] = { .tuner_attach = dib7070p_tuner_attach, DIB0700_DEFAULT_STREAMING_CONFIG(0x02), - + }}, .size_of_priv = sizeof(struct dib0700_adapter_state), }, { + .num_frontends = 1, + .fe = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, .pid_filter_count = 32, .pid_filter = stk70x0p_pid_filter, @@ -3324,7 +3355,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { .tuner_attach = dib7070p_tuner_attach, DIB0700_DEFAULT_STREAMING_CONFIG(0x03), - + }}, .size_of_priv = sizeof(struct dib0700_adapter_state), } }, @@ -3373,6 +3404,8 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 2, .adapter = { { + .num_frontends = 1, + .fe = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, .pid_filter_count = 32, .pid_filter = stk70x0p_pid_filter, @@ -3381,9 +3414,11 @@ struct dvb_usb_device_properties dib0700_devices[] = { .tuner_attach = dib7070p_tuner_attach, DIB0700_DEFAULT_STREAMING_CONFIG(0x02), - + }}, .size_of_priv = sizeof(struct dib0700_adapter_state), }, { + .num_frontends = 1, + .fe = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, .pid_filter_count = 32, .pid_filter = stk70x0p_pid_filter, @@ -3392,7 +3427,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { .tuner_attach = dib7070p_tuner_attach, DIB0700_DEFAULT_STREAMING_CONFIG(0x03), - + }}, .size_of_priv = sizeof(struct dib0700_adapter_state), } }, @@ -3420,6 +3455,8 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 1, .adapter = { { + .num_frontends = 1, + .fe = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, .pid_filter_count = 32, .pid_filter = stk70x0p_pid_filter, @@ -3428,7 +3465,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { .tuner_attach = stk7700ph_tuner_attach, DIB0700_DEFAULT_STREAMING_CONFIG(0x02), - + }}, .size_of_priv = sizeof(struct dib0700_adapter_state), }, @@ -3488,11 +3525,13 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 1, .adapter = { { + .num_frontends = 1, + .fe = {{ .frontend_attach = s5h1411_frontend_attach, .tuner_attach = xc5000_tuner_attach, DIB0700_DEFAULT_STREAMING_CONFIG(0x02), - + }}, .size_of_priv = sizeof(struct dib0700_adapter_state), }, @@ -3524,11 +3563,13 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 1, .adapter = { { + .num_frontends = 1, + .fe = {{ .frontend_attach = lgdt3305_frontend_attach, .tuner_attach = mxl5007t_tuner_attach, DIB0700_DEFAULT_STREAMING_CONFIG(0x02), - + }}, .size_of_priv = sizeof(struct dib0700_adapter_state), }, @@ -3550,6 +3591,8 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 1, .adapter = { { + .num_frontends = 1, + .fe = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, .pid_filter_count = 32, .pid_filter = stk70x0p_pid_filter, @@ -3558,7 +3601,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { .tuner_attach = dib7770p_tuner_attach, DIB0700_DEFAULT_STREAMING_CONFIG(0x02), - + }}, .size_of_priv = sizeof(struct dib0700_adapter_state), }, @@ -3600,6 +3643,8 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 1, .adapter = { { + .num_frontends = 1, + .fe = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, .pid_filter_count = 32, .pid_filter = stk80xx_pid_filter, @@ -3608,7 +3653,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { .tuner_attach = dib807x_tuner_attach, DIB0700_DEFAULT_STREAMING_CONFIG(0x02), - + }}, .size_of_priv = sizeof(struct dib0700_adapter_state), }, @@ -3644,6 +3689,8 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 2, .adapter = { { + .num_frontends = 1, + .fe = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, .pid_filter_count = 32, .pid_filter = stk80xx_pid_filter, @@ -3652,11 +3699,13 @@ struct dvb_usb_device_properties dib0700_devices[] = { .tuner_attach = dib807x_tuner_attach, DIB0700_DEFAULT_STREAMING_CONFIG(0x02), - + }}, .size_of_priv = sizeof(struct dib0700_adapter_state), }, { + .num_frontends = 1, + .fe = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, .pid_filter_count = 32, .pid_filter = stk80xx_pid_filter, @@ -3665,7 +3714,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { .tuner_attach = dib807x_tuner_attach, DIB0700_DEFAULT_STREAMING_CONFIG(0x03), - + }}, .size_of_priv = sizeof(struct dib0700_adapter_state), }, @@ -3693,6 +3742,8 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 1, .adapter = { { + .num_frontends = 1, + .fe = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, .pid_filter_count = 32, @@ -3702,7 +3753,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { .tuner_attach = dib809x_tuner_attach, DIB0700_DEFAULT_STREAMING_CONFIG(0x02), - + }}, .size_of_priv = sizeof(struct dib0700_adapter_state), }, @@ -3730,6 +3781,8 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 1, .adapter = { { + .num_frontends = 1, + .fe = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, .pid_filter_count = 32, @@ -3739,7 +3792,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { .tuner_attach = dib9090_tuner_attach, DIB0700_DEFAULT_STREAMING_CONFIG(0x02), - + }}, .size_of_priv = sizeof(struct dib0700_adapter_state), }, @@ -3767,6 +3820,8 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 1, .adapter = { { + .num_frontends = 1, + .fe = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, .pid_filter_count = 32, @@ -3776,7 +3831,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { .tuner_attach = nim8096md_tuner_attach, DIB0700_DEFAULT_STREAMING_CONFIG(0x02), - + }}, .size_of_priv = sizeof(struct dib0700_adapter_state), }, @@ -3804,6 +3859,8 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 1, .adapter = { { + .num_frontends = 1, + .fe = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, .pid_filter_count = 32, @@ -3813,7 +3870,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { .tuner_attach = nim9090md_tuner_attach, DIB0700_DEFAULT_STREAMING_CONFIG(0x02), - + }}, .size_of_priv = sizeof(struct dib0700_adapter_state), }, @@ -3841,6 +3898,8 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 1, .adapter = { { + .num_frontends = 1, + .fe = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, .pid_filter_count = 32, @@ -3850,7 +3909,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { .tuner_attach = nim7090_tuner_attach, DIB0700_DEFAULT_STREAMING_CONFIG(0x02), - + }}, .size_of_priv = sizeof(struct dib0700_adapter_state), }, @@ -3878,6 +3937,8 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 2, .adapter = { { + .num_frontends = 1, + .fe = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, .pid_filter_count = 32, @@ -3887,11 +3948,13 @@ struct dvb_usb_device_properties dib0700_devices[] = { .tuner_attach = tfe7090pvr_tuner0_attach, DIB0700_DEFAULT_STREAMING_CONFIG(0x03), - + }}, .size_of_priv = sizeof(struct dib0700_adapter_state), }, { + .num_frontends = 1, + .fe = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, .pid_filter_count = 32, @@ -3901,7 +3964,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { .tuner_attach = tfe7090pvr_tuner1_attach, DIB0700_DEFAULT_STREAMING_CONFIG(0x02), - + }}, .size_of_priv = sizeof(struct dib0700_adapter_state), }, @@ -3929,11 +3992,13 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 1, .adapter = { { + .num_frontends = 1, + .fe = {{ .frontend_attach = pctv340e_frontend_attach, .tuner_attach = xc4000_tuner_attach, DIB0700_DEFAULT_STREAMING_CONFIG(0x02), - + }}, .size_of_priv = sizeof(struct dib0700_adapter_state), }, diff --git a/drivers/media/dvb/dvb-usb/dibusb-common.c b/drivers/media/dvb/dvb-usb/dibusb-common.c index 4c2a689c820e..a76bbb29ca36 100644 --- a/drivers/media/dvb/dvb-usb/dibusb-common.c +++ b/drivers/media/dvb/dvb-usb/dibusb-common.c @@ -23,7 +23,7 @@ int dibusb_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) if (adap->priv != NULL) { struct dibusb_state *st = adap->priv; if (st->ops.fifo_ctrl != NULL) - if (st->ops.fifo_ctrl(adap->fe,onoff)) { + if (st->ops.fifo_ctrl(adap->fe_adap[0].fe, onoff)) { err("error while controlling the fifo of the demod."); return -ENODEV; } @@ -37,7 +37,8 @@ int dibusb_pid_filter(struct dvb_usb_adapter *adap, int index, u16 pid, int onof if (adap->priv != NULL) { struct dibusb_state *st = adap->priv; if (st->ops.pid_ctrl != NULL) - st->ops.pid_ctrl(adap->fe,index,pid,onoff); + st->ops.pid_ctrl(adap->fe_adap[0].fe, + index, pid, onoff); } return 0; } @@ -48,7 +49,7 @@ int dibusb_pid_filter_ctrl(struct dvb_usb_adapter *adap, int onoff) if (adap->priv != NULL) { struct dibusb_state *st = adap->priv; if (st->ops.pid_parse != NULL) - if (st->ops.pid_parse(adap->fe,onoff) < 0) + if (st->ops.pid_parse(adap->fe_adap[0].fe, onoff) < 0) err("could not handle pid_parser"); } return 0; @@ -254,8 +255,16 @@ int dibusb_dib3000mc_frontend_attach(struct dvb_usb_adapter *adap) msleep(1000); } - if ((adap->fe = dvb_attach(dib3000mc_attach, &adap->dev->i2c_adap, DEFAULT_DIB3000P_I2C_ADDRESS, &mod3000p_dib3000p_config)) != NULL || - (adap->fe = dvb_attach(dib3000mc_attach, &adap->dev->i2c_adap, DEFAULT_DIB3000MC_I2C_ADDRESS, &mod3000p_dib3000p_config)) != NULL) { + adap->fe_adap[0].fe = dvb_attach(dib3000mc_attach, + &adap->dev->i2c_adap, + DEFAULT_DIB3000P_I2C_ADDRESS, + &mod3000p_dib3000p_config); + if ((adap->fe_adap[0].fe) == NULL) + adap->fe_adap[0].fe = dvb_attach(dib3000mc_attach, + &adap->dev->i2c_adap, + DEFAULT_DIB3000MC_I2C_ADDRESS, + &mod3000p_dib3000p_config); + if ((adap->fe_adap[0].fe) != NULL) { if (adap->priv != NULL) { struct dibusb_state *st = adap->priv; st->ops.pid_parse = dib3000mc_pid_parse; @@ -309,15 +318,15 @@ int dibusb_dib3000mc_tuner_attach(struct dvb_usb_adapter *adap) } } - tun_i2c = dib3000mc_get_tuner_i2c_master(adap->fe, 1); - if (dvb_attach(mt2060_attach, adap->fe, tun_i2c, &stk3000p_mt2060_config, if1) == NULL) { + tun_i2c = dib3000mc_get_tuner_i2c_master(adap->fe_adap[0].fe, 1); + if (dvb_attach(mt2060_attach, adap->fe_adap[0].fe, tun_i2c, &stk3000p_mt2060_config, if1) == NULL) { /* not found - use panasonic pll parameters */ - if (dvb_attach(dvb_pll_attach, adap->fe, 0x60, tun_i2c, DVB_PLL_ENV57H1XD5) == NULL) + if (dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x60, tun_i2c, DVB_PLL_ENV57H1XD5) == NULL) return -ENOMEM; } else { st->mt2060_present = 1; /* set the correct parameters for the dib3000p */ - dib3000mc_set_config(adap->fe, &stk3000p_dib3000p_config); + dib3000mc_set_config(adap->fe_adap[0].fe, &stk3000p_dib3000p_config); } return 0; } diff --git a/drivers/media/dvb/dvb-usb/dibusb-mb.c b/drivers/media/dvb/dvb-usb/dibusb-mb.c index 04d91bdd3562..7270791f8340 100644 --- a/drivers/media/dvb/dvb-usb/dibusb-mb.c +++ b/drivers/media/dvb/dvb-usb/dibusb-mb.c @@ -31,11 +31,12 @@ static int dibusb_dib3000mb_frontend_attach(struct dvb_usb_adapter *adap) demod_cfg.demod_address = 0x8; - if ((adap->fe = dvb_attach(dib3000mb_attach, &demod_cfg, - &adap->dev->i2c_adap, &st->ops)) == NULL) + adap->fe_adap[0].fe = dvb_attach(dib3000mb_attach, &demod_cfg, + &adap->dev->i2c_adap, &st->ops); + if ((adap->fe_adap[0].fe) == NULL) return -ENODEV; - adap->fe->ops.i2c_gate_ctrl = dib3000mb_i2c_gate_ctrl; + adap->fe_adap[0].fe->ops.i2c_gate_ctrl = dib3000mb_i2c_gate_ctrl; return 0; } @@ -46,7 +47,7 @@ static int dibusb_thomson_tuner_attach(struct dvb_usb_adapter *adap) st->tuner_addr = 0x61; - dvb_attach(dvb_pll_attach, adap->fe, 0x61, &adap->dev->i2c_adap, + dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x61, &adap->dev->i2c_adap, DVB_PLL_TUA6010XS); return 0; } @@ -57,7 +58,7 @@ static int dibusb_panasonic_tuner_attach(struct dvb_usb_adapter *adap) st->tuner_addr = 0x60; - dvb_attach(dvb_pll_attach, adap->fe, 0x60, &adap->dev->i2c_adap, + dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x60, &adap->dev->i2c_adap, DVB_PLL_TDA665X); return 0; } @@ -78,16 +79,16 @@ static int dibusb_tuner_probe_and_attach(struct dvb_usb_adapter *adap) /* the Panasonic sits on I2C addrass 0x60, the Thomson on 0x61 */ msg[0].addr = msg[1].addr = st->tuner_addr = 0x60; - if (adap->fe->ops.i2c_gate_ctrl) - adap->fe->ops.i2c_gate_ctrl(adap->fe,1); + if (adap->fe_adap[0].fe->ops.i2c_gate_ctrl) + adap->fe_adap[0].fe->ops.i2c_gate_ctrl(adap->fe_adap[0].fe, 1); if (i2c_transfer(&adap->dev->i2c_adap, msg, 2) != 2) { err("tuner i2c write failed."); ret = -EREMOTEIO; } - if (adap->fe->ops.i2c_gate_ctrl) - adap->fe->ops.i2c_gate_ctrl(adap->fe,0); + if (adap->fe_adap[0].fe->ops.i2c_gate_ctrl) + adap->fe_adap[0].fe->ops.i2c_gate_ctrl(adap->fe_adap[0].fe, 0); if (b2[0] == 0xfe) { info("This device has the Thomson Cable onboard. Which is default."); @@ -185,6 +186,8 @@ static struct dvb_usb_device_properties dibusb1_1_properties = { .num_adapters = 1, .adapter = { { + .num_frontends = 1, + .fe = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, .pid_filter_count = 16, @@ -205,6 +208,7 @@ static struct dvb_usb_device_properties dibusb1_1_properties = { } } }, + }}, .size_of_priv = sizeof(struct dibusb_state), } }, @@ -272,6 +276,8 @@ static struct dvb_usb_device_properties dibusb1_1_an2235_properties = { .num_adapters = 1, .adapter = { { + .num_frontends = 1, + .fe = {{ .caps = DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF | DVB_USB_ADAP_HAS_PID_FILTER, .pid_filter_count = 16, @@ -292,6 +298,7 @@ static struct dvb_usb_device_properties dibusb1_1_an2235_properties = { } } }, + }}, .size_of_priv = sizeof(struct dibusb_state), }, }, @@ -338,6 +345,8 @@ static struct dvb_usb_device_properties dibusb2_0b_properties = { .num_adapters = 1, .adapter = { { + .num_frontends = 1, + .fe = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, .pid_filter_count = 16, @@ -358,6 +367,7 @@ static struct dvb_usb_device_properties dibusb2_0b_properties = { } } }, + }}, .size_of_priv = sizeof(struct dibusb_state), } }, @@ -398,6 +408,8 @@ static struct dvb_usb_device_properties artec_t1_usb2_properties = { .num_adapters = 1, .adapter = { { + .num_frontends = 1, + .fe = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, .pid_filter_count = 16, @@ -417,6 +429,7 @@ static struct dvb_usb_device_properties artec_t1_usb2_properties = { } } }, + }}, .size_of_priv = sizeof(struct dibusb_state), } }, diff --git a/drivers/media/dvb/dvb-usb/dibusb-mc.c b/drivers/media/dvb/dvb-usb/dibusb-mc.c index c1d9094b61e5..9c165e2569d4 100644 --- a/drivers/media/dvb/dvb-usb/dibusb-mc.c +++ b/drivers/media/dvb/dvb-usb/dibusb-mc.c @@ -57,6 +57,8 @@ static struct dvb_usb_device_properties dibusb_mc_properties = { .num_adapters = 1, .adapter = { { + .num_frontends = 1, + .fe = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, .pid_filter_count = 32, .streaming_ctrl = dibusb2_0_streaming_ctrl, @@ -76,6 +78,7 @@ static struct dvb_usb_device_properties dibusb_mc_properties = { } } }, + }}, .size_of_priv = sizeof(struct dibusb_state), } }, diff --git a/drivers/media/dvb/dvb-usb/digitv.c b/drivers/media/dvb/dvb-usb/digitv.c index f6344cdd360f..f7184111aa64 100644 --- a/drivers/media/dvb/dvb-usb/digitv.c +++ b/drivers/media/dvb/dvb-usb/digitv.c @@ -137,11 +137,16 @@ static int digitv_frontend_attach(struct dvb_usb_adapter *adap) { struct digitv_state *st = adap->dev->priv; - if ((adap->fe = dvb_attach(mt352_attach, &digitv_mt352_config, &adap->dev->i2c_adap)) != NULL) { + adap->fe_adap[0].fe = dvb_attach(mt352_attach, &digitv_mt352_config, + &adap->dev->i2c_adap); + if ((adap->fe_adap[0].fe) != NULL) { st->is_nxt6000 = 0; return 0; } - if ((adap->fe = dvb_attach(nxt6000_attach, &digitv_nxt6000_config, &adap->dev->i2c_adap)) != NULL) { + adap->fe_adap[0].fe = dvb_attach(nxt6000_attach, + &digitv_nxt6000_config, + &adap->dev->i2c_adap); + if ((adap->fe_adap[0].fe) != NULL) { st->is_nxt6000 = 1; return 0; } @@ -152,11 +157,11 @@ static int digitv_tuner_attach(struct dvb_usb_adapter *adap) { struct digitv_state *st = adap->dev->priv; - if (!dvb_attach(dvb_pll_attach, adap->fe, 0x60, NULL, DVB_PLL_TDED4)) + if (!dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x60, NULL, DVB_PLL_TDED4)) return -ENODEV; if (st->is_nxt6000) - adap->fe->ops.tuner_ops.set_params = digitv_nxt6000_tuner_set_params; + adap->fe_adap[0].fe->ops.tuner_ops.set_params = digitv_nxt6000_tuner_set_params; return 0; } @@ -292,6 +297,8 @@ static struct dvb_usb_device_properties digitv_properties = { .num_adapters = 1, .adapter = { { + .num_frontends = 1, + .fe = {{ .frontend_attach = digitv_frontend_attach, .tuner_attach = digitv_tuner_attach, @@ -306,6 +313,7 @@ static struct dvb_usb_device_properties digitv_properties = { } } }, + }}, } }, .identify_state = digitv_identify_state, diff --git a/drivers/media/dvb/dvb-usb/dtt200u.c b/drivers/media/dvb/dvb-usb/dtt200u.c index ecd86eca2548..106dfd55ff9c 100644 --- a/drivers/media/dvb/dvb-usb/dtt200u.c +++ b/drivers/media/dvb/dvb-usb/dtt200u.c @@ -90,7 +90,7 @@ static int dtt200u_rc_query(struct dvb_usb_device *d, u32 *event, int *state) static int dtt200u_frontend_attach(struct dvb_usb_adapter *adap) { - adap->fe = dtt200u_fe_attach(adap->dev); + adap->fe_adap[0].fe = dtt200u_fe_attach(adap->dev); return 0; } @@ -140,6 +140,8 @@ static struct dvb_usb_device_properties dtt200u_properties = { .num_adapters = 1, .adapter = { { + .num_frontends = 1, + .fe = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_NEED_PID_FILTERING, .pid_filter_count = 15, @@ -157,6 +159,7 @@ static struct dvb_usb_device_properties dtt200u_properties = { } } }, + }}, } }, .power_ctrl = dtt200u_power_ctrl, @@ -187,6 +190,8 @@ static struct dvb_usb_device_properties wt220u_properties = { .num_adapters = 1, .adapter = { { + .num_frontends = 1, + .fe = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_NEED_PID_FILTERING, .pid_filter_count = 15, @@ -204,6 +209,7 @@ static struct dvb_usb_device_properties wt220u_properties = { } } }, + }}, } }, .power_ctrl = dtt200u_power_ctrl, @@ -234,6 +240,8 @@ static struct dvb_usb_device_properties wt220u_fc_properties = { .num_adapters = 1, .adapter = { { + .num_frontends = 1, + .fe = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_NEED_PID_FILTERING, .pid_filter_count = 15, @@ -251,6 +259,7 @@ static struct dvb_usb_device_properties wt220u_fc_properties = { } } }, + }}, } }, .power_ctrl = dtt200u_power_ctrl, @@ -281,6 +290,8 @@ static struct dvb_usb_device_properties wt220u_zl0353_properties = { .num_adapters = 1, .adapter = { { + .num_frontends = 1, + .fe = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_NEED_PID_FILTERING, .pid_filter_count = 15, @@ -298,6 +309,7 @@ static struct dvb_usb_device_properties wt220u_zl0353_properties = { } } }, + }}, } }, .power_ctrl = dtt200u_power_ctrl, diff --git a/drivers/media/dvb/dvb-usb/dtv5100.c b/drivers/media/dvb/dvb-usb/dtv5100.c index 078ce92ca436..7373132163d2 100644 --- a/drivers/media/dvb/dvb-usb/dtv5100.c +++ b/drivers/media/dvb/dvb-usb/dtv5100.c @@ -115,13 +115,13 @@ static struct zl10353_config dtv5100_zl10353_config = { static int dtv5100_frontend_attach(struct dvb_usb_adapter *adap) { - adap->fe = dvb_attach(zl10353_attach, &dtv5100_zl10353_config, + adap->fe_adap[0].fe = dvb_attach(zl10353_attach, &dtv5100_zl10353_config, &adap->dev->i2c_adap); - if (adap->fe == NULL) + if (adap->fe_adap[0].fe == NULL) return -EIO; /* disable i2c gate, or it won't work... is this safe? */ - adap->fe->ops.i2c_gate_ctrl = NULL; + adap->fe_adap[0].fe->ops.i2c_gate_ctrl = NULL; return 0; } @@ -133,7 +133,7 @@ static struct qt1010_config dtv5100_qt1010_config = { static int dtv5100_tuner_attach(struct dvb_usb_adapter *adap) { return dvb_attach(qt1010_attach, - adap->fe, &adap->dev->i2c_adap, + adap->fe_adap[0].fe, &adap->dev->i2c_adap, &dtv5100_qt1010_config) == NULL ? -ENODEV : 0; } @@ -180,6 +180,8 @@ static struct dvb_usb_device_properties dtv5100_properties = { .num_adapters = 1, .adapter = {{ + .num_frontends = 1, + .fe = {{ .frontend_attach = dtv5100_frontend_attach, .tuner_attach = dtv5100_tuner_attach, @@ -193,6 +195,7 @@ static struct dvb_usb_device_properties dtv5100_properties = { } } }, + }}, } }, .i2c_algo = &dtv5100_i2c_algo, diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-dvb.c b/drivers/media/dvb/dvb-usb/dvb-usb-dvb.c index b3cb626ed56e..ba4a7517354f 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-dvb.c +++ b/drivers/media/dvb/dvb-usb/dvb-usb-dvb.c @@ -17,15 +17,20 @@ static int dvb_usb_ctrl_feed(struct dvb_demux_feed *dvbdmxfeed, int onoff) if (adap == NULL) return -ENODEV; + if ((adap->active_fe < 0) || + (adap->active_fe >= adap->num_frontends_initialized)) { + return -EINVAL; + } + newfeedcount = adap->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"); - usb_urb_kill(&adap->stream); + usb_urb_kill(&adap->fe_adap[adap->active_fe].stream); - if (adap->props.streaming_ctrl != NULL) { - ret = adap->props.streaming_ctrl(adap, 0); + if (adap->props.fe[adap->active_fe].streaming_ctrl != NULL) { + ret = adap->props.fe[adap->active_fe].streaming_ctrl(adap, 0); if (ret < 0) { err("error while stopping stream."); return ret; @@ -36,36 +41,37 @@ static int dvb_usb_ctrl_feed(struct dvb_demux_feed *dvbdmxfeed, int onoff) adap->feedcount = newfeedcount; /* activate the pid on the device specific pid_filter */ - deb_ts("setting pid (%s): %5d %04x at index %d '%s'\n",adap->pid_filtering ? - "yes" : "no", dvbdmxfeed->pid,dvbdmxfeed->pid,dvbdmxfeed->index,onoff ? - "on" : "off"); - if (adap->props.caps & DVB_USB_ADAP_HAS_PID_FILTER && - adap->pid_filtering && - adap->props.pid_filter != NULL) - adap->props.pid_filter(adap, dvbdmxfeed->index, dvbdmxfeed->pid,onoff); + deb_ts("setting pid (%s): %5d %04x at index %d '%s'\n", + adap->fe_adap[adap->active_fe].pid_filtering ? + "yes" : "no", dvbdmxfeed->pid, dvbdmxfeed->pid, + dvbdmxfeed->index, onoff ? "on" : "off"); + if (adap->props.fe[adap->active_fe].caps & DVB_USB_ADAP_HAS_PID_FILTER && + adap->fe_adap[adap->active_fe].pid_filtering && + adap->props.fe[adap->active_fe].pid_filter != NULL) + adap->props.fe[adap->active_fe].pid_filter(adap, dvbdmxfeed->index, dvbdmxfeed->pid, onoff); /* start the feed if this was the first feed and there is still a feed * for reception. */ if (adap->feedcount == onoff && adap->feedcount > 0) { deb_ts("submitting all URBs\n"); - usb_urb_submit(&adap->stream); + usb_urb_submit(&adap->fe_adap[adap->active_fe].stream); deb_ts("controlling pid parser\n"); - if (adap->props.caps & DVB_USB_ADAP_HAS_PID_FILTER && - adap->props.caps & + if (adap->props.fe[adap->active_fe].caps & DVB_USB_ADAP_HAS_PID_FILTER && + adap->props.fe[adap->active_fe].caps & DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF && - adap->props.pid_filter_ctrl != NULL) { - ret = adap->props.pid_filter_ctrl(adap, - adap->pid_filtering); + adap->props.fe[adap->active_fe].pid_filter_ctrl != NULL) { + ret = adap->props.fe[adap->active_fe].pid_filter_ctrl(adap, + adap->fe_adap[adap->active_fe].pid_filtering); if (ret < 0) { err("could not handle pid_parser"); return ret; } } deb_ts("start feeding\n"); - if (adap->props.streaming_ctrl != NULL) { - ret = adap->props.streaming_ctrl(adap, 1); + if (adap->props.fe[adap->active_fe].streaming_ctrl != NULL) { + ret = adap->props.fe[adap->active_fe].streaming_ctrl(adap, 1); if (ret < 0) { err("error while enabling fifo."); return ret; @@ -90,6 +96,7 @@ static int dvb_usb_stop_feed(struct dvb_demux_feed *dvbdmxfeed) int dvb_usb_adapter_dvb_init(struct dvb_usb_adapter *adap, short *adapter_nums) { + int i; int ret = dvb_register_adapter(&adap->dvb_adap, adap->dev->desc->name, adap->dev->owner, &adap->dev->udev->dev, adapter_nums); @@ -112,7 +119,12 @@ int dvb_usb_adapter_dvb_init(struct dvb_usb_adapter *adap, short *adapter_nums) adap->demux.dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING; adap->demux.priv = adap; - adap->demux.feednum = adap->demux.filternum = adap->max_feed_count; + adap->demux.filternum = 0; + for (i = 0; i < adap->props.num_frontends; i++) { + if (adap->demux.filternum < adap->fe_adap[i].max_feed_count) + adap->demux.filternum = adap->fe_adap[i].max_feed_count; + } + adap->demux.feednum = adap->demux.filternum; adap->demux.start_feed = dvb_usb_start_feed; adap->demux.stop_feed = dvb_usb_stop_feed; adap->demux.write_to_decoder = NULL; @@ -156,14 +168,33 @@ int dvb_usb_adapter_dvb_exit(struct dvb_usb_adapter *adap) return 0; } +static int dvb_usb_set_active_fe(struct dvb_frontend *fe, int onoff) +{ + struct dvb_usb_adapter *adap = fe->dvb->priv; + + int ret = (adap->props.frontend_ctrl) ? + adap->props.frontend_ctrl(fe, onoff) : 0; + + if (ret < 0) { + err("frontend_ctrl request failed"); + return ret; + } + if (onoff) + adap->active_fe = fe->id; + + return 0; +} + static int dvb_usb_fe_wakeup(struct dvb_frontend *fe) { struct dvb_usb_adapter *adap = fe->dvb->priv; dvb_usb_device_power_ctrl(adap->dev, 1); - if (adap->fe_init) - adap->fe_init(fe); + dvb_usb_set_active_fe(fe, 1); + + if (adap->fe_adap[fe->id].fe_init) + adap->fe_adap[fe->id].fe_init(fe); return 0; } @@ -172,45 +203,81 @@ static int dvb_usb_fe_sleep(struct dvb_frontend *fe) { struct dvb_usb_adapter *adap = fe->dvb->priv; - if (adap->fe_sleep) - adap->fe_sleep(fe); + if (adap->fe_adap[fe->id].fe_sleep) + adap->fe_adap[fe->id].fe_sleep(fe); + + dvb_usb_set_active_fe(fe, 0); return dvb_usb_device_power_ctrl(adap->dev, 0); } int dvb_usb_adapter_frontend_init(struct dvb_usb_adapter *adap) { - if (adap->props.frontend_attach == NULL) { - err("strange: '%s' #%d doesn't want to attach a frontend.",adap->dev->desc->name, adap->id); - return 0; - } + int ret, i; - /* re-assign sleep and wakeup functions */ - if (adap->props.frontend_attach(adap) == 0 && adap->fe != NULL) { - adap->fe_init = adap->fe->ops.init; adap->fe->ops.init = dvb_usb_fe_wakeup; - adap->fe_sleep = adap->fe->ops.sleep; adap->fe->ops.sleep = dvb_usb_fe_sleep; + /* register all given adapter frontends */ + for (i = 0; i < adap->props.num_frontends; i++) { - if (dvb_register_frontend(&adap->dvb_adap, adap->fe)) { - err("Frontend registration failed."); - dvb_frontend_detach(adap->fe); - adap->fe = NULL; - return -ENODEV; + if (adap->props.fe[i].frontend_attach == NULL) { + err("strange: '%s' #%d,%d " + "doesn't want to attach a frontend.", + adap->dev->desc->name, adap->id, i); + + return 0; + } + + ret = adap->props.fe[i].frontend_attach(adap); + if (ret || adap->fe_adap[i].fe == NULL) { + /* only print error when there is no FE at all */ + if (i == 0) + err("no frontend was attached by '%s'", + adap->dev->desc->name); + + return 0; + } + + adap->fe_adap[i].fe->id = i; + + /* re-assign sleep and wakeup functions */ + adap->fe_adap[i].fe_init = adap->fe_adap[i].fe->ops.init; + adap->fe_adap[i].fe->ops.init = dvb_usb_fe_wakeup; + adap->fe_adap[i].fe_sleep = adap->fe_adap[i].fe->ops.sleep; + adap->fe_adap[i].fe->ops.sleep = dvb_usb_fe_sleep; + + if (dvb_register_frontend(&adap->dvb_adap, adap->fe_adap[i].fe)) { + err("Frontend %d registration failed.", i); + dvb_frontend_detach(adap->fe_adap[i].fe); + adap->fe_adap[i].fe = NULL; + /* In error case, do not try register more FEs, + * still leaving already registered FEs alive. */ + if (i == 0) + return -ENODEV; + else + return 0; } /* only attach the tuner if the demod is there */ - if (adap->props.tuner_attach != NULL) - adap->props.tuner_attach(adap); - } else - err("no frontend was attached by '%s'",adap->dev->desc->name); + if (adap->props.fe[i].tuner_attach != NULL) + adap->props.fe[i].tuner_attach(adap); + + adap->num_frontends_initialized++; + } return 0; } int dvb_usb_adapter_frontend_exit(struct dvb_usb_adapter *adap) { - if (adap->fe != NULL) { - dvb_unregister_frontend(adap->fe); - dvb_frontend_detach(adap->fe); + int i = adap->num_frontends_initialized - 1; + + /* unregister all given adapter frontends */ + for (; i >= 0; i--) { + if (adap->fe_adap[i].fe != NULL) { + dvb_unregister_frontend(adap->fe_adap[i].fe); + dvb_frontend_detach(adap->fe_adap[i].fe); + } } + adap->num_frontends_initialized = 0; + return 0; } diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h index 2a79b8fb3e8d..2d08c9b5128a 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h +++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h @@ -37,6 +37,7 @@ #define USB_VID_HAUPPAUGE 0x2040 #define USB_VID_HYPER_PALTEK 0x1025 #define USB_VID_INTEL 0x8086 +#define USB_VID_ITETECH 0x048d #define USB_VID_KWORLD 0xeb2a #define USB_VID_KWORLD_2 0x1b80 #define USB_VID_KYE 0x0458 @@ -126,6 +127,7 @@ #define USB_PID_GRANDTEC_DVBT_USB_COLD 0x0fa0 #define USB_PID_GRANDTEC_DVBT_USB_WARM 0x0fa1 #define USB_PID_INTEL_CE9500 0x9500 +#define USB_PID_ITETECH_IT9135 0x9135 #define USB_PID_KWORLD_399U 0xe399 #define USB_PID_KWORLD_399U_2 0xe400 #define USB_PID_KWORLD_395U 0xe396 @@ -136,6 +138,7 @@ #define USB_PID_KWORLD_PC160_2T 0xc160 #define USB_PID_KWORLD_PC160_T 0xc161 #define USB_PID_KWORLD_UB383_T 0xe383 +#define USB_PID_KWORLD_UB499_2T_T09 0xe409 #define USB_PID_KWORLD_VSTREAM_COLD 0x17de #define USB_PID_KWORLD_VSTREAM_WARM 0x17df #define USB_PID_TERRATEC_CINERGY_T_USB_XE 0x0055 @@ -240,6 +243,9 @@ #define USB_PID_PCTV_200E 0x020e #define USB_PID_PCTV_400E 0x020f #define USB_PID_PCTV_450E 0x0222 +#define USB_PID_PCTV_452E 0x021f +#define USB_PID_TECHNOTREND_CONNECT_S2_3600 0x3007 +#define USB_PID_TECHNOTREND_CONNECT_S2_3650_CI 0x300a #define USB_PID_NEBULA_DIGITV 0x0201 #define USB_PID_DVICO_BLUEBIRD_LGDT 0xd820 #define USB_PID_DVICO_BLUEBIRD_LG064F_COLD 0xd500 diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-init.c b/drivers/media/dvb/dvb-usb/dvb-usb-init.c index 2e3ea0fa28e0..169196ec2d4e 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-init.c +++ b/drivers/media/dvb/dvb-usb/dvb-usb-init.c @@ -29,7 +29,7 @@ MODULE_PARM_DESC(force_pid_filter_usage, "force all dvb-usb-devices to use a PID static int dvb_usb_adapter_init(struct dvb_usb_device *d, short *adapter_nrs) { struct dvb_usb_adapter *adap; - int ret, n; + int ret, n, o; for (n = 0; n < d->props.num_adapters; n++) { adap = &d->adapter[n]; @@ -38,31 +38,42 @@ static int dvb_usb_adapter_init(struct dvb_usb_device *d, short *adapter_nrs) memcpy(&adap->props, &d->props.adapter[n], sizeof(struct dvb_usb_adapter_properties)); + for (o = 0; o < adap->props.num_frontends; o++) { + struct dvb_usb_adapter_fe_properties *props = &adap->props.fe[o]; /* speed - when running at FULL speed we need a HW PID filter */ - if (d->udev->speed == USB_SPEED_FULL && !(adap->props.caps & DVB_USB_ADAP_HAS_PID_FILTER)) { + if (d->udev->speed == USB_SPEED_FULL && !(props->caps & DVB_USB_ADAP_HAS_PID_FILTER)) { err("This USB2.0 device cannot be run on a USB1.1 port. (it lacks a hardware PID filter)"); return -ENODEV; } - if ((d->udev->speed == USB_SPEED_FULL && adap->props.caps & DVB_USB_ADAP_HAS_PID_FILTER) || - (adap->props.caps & DVB_USB_ADAP_NEED_PID_FILTERING)) { - info("will use the device's hardware PID filter (table count: %d).", adap->props.pid_filter_count); - adap->pid_filtering = 1; - adap->max_feed_count = adap->props.pid_filter_count; + if ((d->udev->speed == USB_SPEED_FULL && props->caps & DVB_USB_ADAP_HAS_PID_FILTER) || + (props->caps & DVB_USB_ADAP_NEED_PID_FILTERING)) { + info("will use the device's hardware PID filter (table count: %d).", props->pid_filter_count); + adap->fe_adap[o].pid_filtering = 1; + adap->fe_adap[o].max_feed_count = props->pid_filter_count; } else { info("will pass the complete MPEG2 transport stream to the software demuxer."); - adap->pid_filtering = 0; - adap->max_feed_count = 255; + adap->fe_adap[o].pid_filtering = 0; + adap->fe_adap[o].max_feed_count = 255; } - if (!adap->pid_filtering && + if (!adap->fe_adap[o].pid_filtering && dvb_usb_force_pid_filter_usage && - adap->props.caps & DVB_USB_ADAP_HAS_PID_FILTER) { + props->caps & DVB_USB_ADAP_HAS_PID_FILTER) { info("pid filter enabled by module option."); - adap->pid_filtering = 1; - adap->max_feed_count = adap->props.pid_filter_count; + adap->fe_adap[o].pid_filtering = 1; + adap->fe_adap[o].max_feed_count = props->pid_filter_count; } + if (props->size_of_priv > 0) { + adap->fe_adap[o].priv = kzalloc(props->size_of_priv, GFP_KERNEL); + if (adap->fe_adap[o].priv == NULL) { + err("no memory for priv for adapter %d fe %d.", n, o); + return -ENOMEM; + } + } + } + if (adap->props.size_of_priv > 0) { adap->priv = kzalloc(adap->props.size_of_priv, GFP_KERNEL); if (adap->priv == NULL) { @@ -77,6 +88,10 @@ static int dvb_usb_adapter_init(struct dvb_usb_device *d, short *adapter_nrs) return ret; } + /* use exclusive FE lock if there is multiple shared FEs */ + if (adap->fe_adap[1].fe) + adap->dvb_adap.mfe_shared = 1; + d->num_adapters_initialized++; d->state |= DVB_USB_STATE_DVB; } diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-urb.c b/drivers/media/dvb/dvb-usb/dvb-usb-urb.c index bb46ba6a3573..53a5c30b51b2 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-urb.c +++ b/drivers/media/dvb/dvb-usb/dvb-usb-urb.c @@ -82,16 +82,28 @@ static void dvb_usb_data_complete_204(struct usb_data_stream *stream, u8 *buffer int dvb_usb_adapter_stream_init(struct dvb_usb_adapter *adap) { - adap->stream.udev = adap->dev->udev; - if (adap->props.caps & DVB_USB_ADAP_RECEIVES_204_BYTE_TS) - adap->stream.complete = dvb_usb_data_complete_204; - else - adap->stream.complete = dvb_usb_data_complete; - adap->stream.user_priv = adap; - return usb_urb_init(&adap->stream, &adap->props.stream); + int i, ret = 0; + for (i = 0; i < adap->props.num_frontends; i++) { + + adap->fe_adap[i].stream.udev = adap->dev->udev; + if (adap->props.fe[i].caps & DVB_USB_ADAP_RECEIVES_204_BYTE_TS) + adap->fe_adap[i].stream.complete = + dvb_usb_data_complete_204; + else + adap->fe_adap[i].stream.complete = dvb_usb_data_complete; + adap->fe_adap[i].stream.user_priv = adap; + ret = usb_urb_init(&adap->fe_adap[i].stream, + &adap->props.fe[i].stream); + if (ret < 0) + break; + } + return ret; } int dvb_usb_adapter_stream_exit(struct dvb_usb_adapter *adap) { - return usb_urb_exit(&adap->stream); + int i; + for (i = 0; i < adap->props.num_frontends; i++) + usb_urb_exit(&adap->fe_adap[i].stream); + return 0; } diff --git a/drivers/media/dvb/dvb-usb/dvb-usb.h b/drivers/media/dvb/dvb-usb/dvb-usb.h index 7d35d078342b..6d7d13f9ce68 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb.h +++ b/drivers/media/dvb/dvb-usb/dvb-usb.h @@ -124,6 +124,8 @@ struct usb_data_stream_properties { * @caps: capabilities of the DVB USB device. * @pid_filter_count: number of PID filter position in the optional hardware * PID-filter. + * @num_frontends: number of frontends of the DVB USB adapter. + * @frontend_ctrl: called to power on/off active frontend. * @streaming_ctrl: called to start and stop the MPEG2-TS streaming of the * device (not URB submitting/killing). * @pid_filter_ctrl: called to en/disable the PID filter, if any. @@ -134,7 +136,7 @@ struct usb_data_stream_properties { * pll_desc and pll_init_buf of struct dvb_usb_device). * @stream: configuration of the USB streaming */ -struct dvb_usb_adapter_properties { +struct dvb_usb_adapter_fe_properties { #define DVB_USB_ADAP_HAS_PID_FILTER 0x01 #define DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF 0x02 #define DVB_USB_ADAP_NEED_PID_FILTERING 0x04 @@ -152,9 +154,18 @@ struct dvb_usb_adapter_properties { struct usb_data_stream_properties stream; int size_of_priv; +}; + +#define MAX_NO_OF_FE_PER_ADAP 2 +struct dvb_usb_adapter_properties { + int size_of_priv; + int (*frontend_ctrl) (struct dvb_frontend *, int); int (*fe_ioctl_override) (struct dvb_frontend *, unsigned int, void *, unsigned int); + + int num_frontends; + struct dvb_usb_adapter_fe_properties fe[MAX_NO_OF_FE_PER_ADAP]; }; /** @@ -345,6 +356,20 @@ struct usb_data_stream { * * @stream: the usb data stream. */ +struct dvb_usb_fe_adapter { + struct dvb_frontend *fe; + + int (*fe_init) (struct dvb_frontend *); + int (*fe_sleep) (struct dvb_frontend *); + + struct usb_data_stream stream; + + int pid_filtering; + int max_feed_count; + + void *priv; +}; + struct dvb_usb_adapter { struct dvb_usb_device *dev; struct dvb_usb_adapter_properties props; @@ -356,20 +381,16 @@ struct dvb_usb_adapter { u8 id; int feedcount; - int pid_filtering; /* dvb */ struct dvb_adapter dvb_adap; struct dmxdev dmxdev; struct dvb_demux demux; struct dvb_net dvb_net; - struct dvb_frontend *fe; - int max_feed_count; - - int (*fe_init) (struct dvb_frontend *); - int (*fe_sleep) (struct dvb_frontend *); - struct usb_data_stream stream; + struct dvb_usb_fe_adapter fe_adap[MAX_NO_OF_FE_PER_ADAP]; + int active_fe; + int num_frontends_initialized; void *priv; }; diff --git a/drivers/media/dvb/dvb-usb/dw2102.c b/drivers/media/dvb/dvb-usb/dw2102.c index 058b2318abed..f103ec1fe82e 100644 --- a/drivers/media/dvb/dvb-usb/dw2102.c +++ b/drivers/media/dvb/dvb-usb/dw2102.c @@ -992,18 +992,18 @@ static int dw2104_frontend_attach(struct dvb_usb_adapter *d) struct dvb_tuner_ops *tuner_ops = NULL; if (demod_probe & 4) { - d->fe = dvb_attach(stv0900_attach, &dw2104a_stv0900_config, + d->fe_adap[0].fe = dvb_attach(stv0900_attach, &dw2104a_stv0900_config, &d->dev->i2c_adap, 0); - if (d->fe != NULL) { - if (dvb_attach(stb6100_attach, d->fe, + if (d->fe_adap[0].fe != NULL) { + if (dvb_attach(stb6100_attach, d->fe_adap[0].fe, &dw2104a_stb6100_config, &d->dev->i2c_adap)) { - tuner_ops = &d->fe->ops.tuner_ops; + tuner_ops = &d->fe_adap[0].fe->ops.tuner_ops; tuner_ops->set_frequency = stb6100_set_freq; tuner_ops->get_frequency = stb6100_get_freq; tuner_ops->set_bandwidth = stb6100_set_bandw; tuner_ops->get_bandwidth = stb6100_get_bandw; - d->fe->ops.set_voltage = dw210x_set_voltage; + d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage; info("Attached STV0900+STB6100!\n"); return 0; } @@ -1011,13 +1011,13 @@ static int dw2104_frontend_attach(struct dvb_usb_adapter *d) } if (demod_probe & 2) { - d->fe = dvb_attach(stv0900_attach, &dw2104_stv0900_config, + d->fe_adap[0].fe = dvb_attach(stv0900_attach, &dw2104_stv0900_config, &d->dev->i2c_adap, 0); - if (d->fe != NULL) { - if (dvb_attach(stv6110_attach, d->fe, + if (d->fe_adap[0].fe != NULL) { + if (dvb_attach(stv6110_attach, d->fe_adap[0].fe, &dw2104_stv6110_config, &d->dev->i2c_adap)) { - d->fe->ops.set_voltage = dw210x_set_voltage; + d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage; info("Attached STV0900+STV6110A!\n"); return 0; } @@ -1025,19 +1025,19 @@ static int dw2104_frontend_attach(struct dvb_usb_adapter *d) } if (demod_probe & 1) { - d->fe = dvb_attach(cx24116_attach, &dw2104_config, + d->fe_adap[0].fe = dvb_attach(cx24116_attach, &dw2104_config, &d->dev->i2c_adap); - if (d->fe != NULL) { - d->fe->ops.set_voltage = dw210x_set_voltage; + if (d->fe_adap[0].fe != NULL) { + d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage; info("Attached cx24116!\n"); return 0; } } - d->fe = dvb_attach(ds3000_attach, &dw2104_ds3000_config, + d->fe_adap[0].fe = dvb_attach(ds3000_attach, &dw2104_ds3000_config, &d->dev->i2c_adap); - if (d->fe != NULL) { - d->fe->ops.set_voltage = dw210x_set_voltage; + if (d->fe_adap[0].fe != NULL) { + d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage; info("Attached DS3000!\n"); return 0; } @@ -1053,22 +1053,22 @@ static int dw2102_frontend_attach(struct dvb_usb_adapter *d) { if (dw2102_properties.i2c_algo == &dw2102_serit_i2c_algo) { /*dw2102_properties.adapter->tuner_attach = NULL;*/ - d->fe = dvb_attach(si21xx_attach, &serit_sp1511lhb_config, + d->fe_adap[0].fe = dvb_attach(si21xx_attach, &serit_sp1511lhb_config, &d->dev->i2c_adap); - if (d->fe != NULL) { - d->fe->ops.set_voltage = dw210x_set_voltage; + if (d->fe_adap[0].fe != NULL) { + d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage; info("Attached si21xx!\n"); return 0; } } if (dw2102_properties.i2c_algo == &dw2102_earda_i2c_algo) { - d->fe = dvb_attach(stv0288_attach, &earda_config, + d->fe_adap[0].fe = dvb_attach(stv0288_attach, &earda_config, &d->dev->i2c_adap); - if (d->fe != NULL) { - if (dvb_attach(stb6000_attach, d->fe, 0x61, + if (d->fe_adap[0].fe != NULL) { + if (dvb_attach(stb6000_attach, d->fe_adap[0].fe, 0x61, &d->dev->i2c_adap)) { - d->fe->ops.set_voltage = dw210x_set_voltage; + d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage; info("Attached stv0288!\n"); return 0; } @@ -1077,10 +1077,10 @@ static int dw2102_frontend_attach(struct dvb_usb_adapter *d) if (dw2102_properties.i2c_algo == &dw2102_i2c_algo) { /*dw2102_properties.adapter->tuner_attach = dw2102_tuner_attach;*/ - d->fe = dvb_attach(stv0299_attach, &sharp_z0194a_config, + d->fe_adap[0].fe = dvb_attach(stv0299_attach, &sharp_z0194a_config, &d->dev->i2c_adap); - if (d->fe != NULL) { - d->fe->ops.set_voltage = dw210x_set_voltage; + if (d->fe_adap[0].fe != NULL) { + d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage; info("Attached stv0299!\n"); return 0; } @@ -1090,9 +1090,9 @@ static int dw2102_frontend_attach(struct dvb_usb_adapter *d) static int dw3101_frontend_attach(struct dvb_usb_adapter *d) { - d->fe = dvb_attach(tda10023_attach, &dw3101_tda10023_config, + d->fe_adap[0].fe = dvb_attach(tda10023_attach, &dw3101_tda10023_config, &d->dev->i2c_adap, 0x48); - if (d->fe != NULL) { + if (d->fe_adap[0].fe != NULL) { info("Attached tda10023!\n"); return 0; } @@ -1101,12 +1101,12 @@ static int dw3101_frontend_attach(struct dvb_usb_adapter *d) static int zl100313_frontend_attach(struct dvb_usb_adapter *d) { - d->fe = dvb_attach(mt312_attach, &zl313_config, + d->fe_adap[0].fe = dvb_attach(mt312_attach, &zl313_config, &d->dev->i2c_adap); - if (d->fe != NULL) { - if (dvb_attach(zl10039_attach, d->fe, 0x60, + if (d->fe_adap[0].fe != NULL) { + if (dvb_attach(zl10039_attach, d->fe_adap[0].fe, 0x60, &d->dev->i2c_adap)) { - d->fe->ops.set_voltage = dw210x_set_voltage; + d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage; info("Attached zl100313+zl10039!\n"); return 0; } @@ -1119,16 +1119,16 @@ static int stv0288_frontend_attach(struct dvb_usb_adapter *d) { u8 obuf[] = {7, 1}; - d->fe = dvb_attach(stv0288_attach, &earda_config, + d->fe_adap[0].fe = dvb_attach(stv0288_attach, &earda_config, &d->dev->i2c_adap); - if (d->fe == NULL) + if (d->fe_adap[0].fe == NULL) return -EIO; - if (NULL == dvb_attach(stb6000_attach, d->fe, 0x61, &d->dev->i2c_adap)) + if (NULL == dvb_attach(stb6000_attach, d->fe_adap[0].fe, 0x61, &d->dev->i2c_adap)) return -EIO; - d->fe->ops.set_voltage = dw210x_set_voltage; + d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage; dw210x_op_rw(d->dev->udev, 0x8a, 0, 0, obuf, 2, DW210X_WRITE_MSG); @@ -1143,14 +1143,14 @@ static int ds3000_frontend_attach(struct dvb_usb_adapter *d) struct s6x0_state *st = (struct s6x0_state *)d->dev->priv; u8 obuf[] = {7, 1}; - d->fe = dvb_attach(ds3000_attach, &dw2104_ds3000_config, + d->fe_adap[0].fe = dvb_attach(ds3000_attach, &dw2104_ds3000_config, &d->dev->i2c_adap); - if (d->fe == NULL) + if (d->fe_adap[0].fe == NULL) return -EIO; - st->old_set_voltage = d->fe->ops.set_voltage; - d->fe->ops.set_voltage = s660_set_voltage; + st->old_set_voltage = d->fe_adap[0].fe->ops.set_voltage; + d->fe_adap[0].fe->ops.set_voltage = s660_set_voltage; dw210x_op_rw(d->dev->udev, 0x8a, 0, 0, obuf, 2, DW210X_WRITE_MSG); @@ -1163,12 +1163,12 @@ static int prof_7500_frontend_attach(struct dvb_usb_adapter *d) { u8 obuf[] = {7, 1}; - d->fe = dvb_attach(stv0900_attach, &prof_7500_stv0900_config, + d->fe_adap[0].fe = dvb_attach(stv0900_attach, &prof_7500_stv0900_config, &d->dev->i2c_adap, 0); - if (d->fe == NULL) + if (d->fe_adap[0].fe == NULL) return -EIO; - d->fe->ops.set_voltage = dw210x_set_voltage; + d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage; dw210x_op_rw(d->dev->udev, 0x8a, 0, 0, obuf, 2, DW210X_WRITE_MSG); @@ -1204,9 +1204,9 @@ static int su3000_frontend_attach(struct dvb_usb_adapter *d) if (dvb_usb_generic_rw(d->dev, obuf, 1, ibuf, 1, 0) < 0) err("command 0x51 transfer failed."); - d->fe = dvb_attach(ds3000_attach, &su3000_ds3000_config, + d->fe_adap[0].fe = dvb_attach(ds3000_attach, &su3000_ds3000_config, &d->dev->i2c_adap); - if (d->fe == NULL) + if (d->fe_adap[0].fe == NULL) return -EIO; info("Attached DS3000!\n"); @@ -1216,14 +1216,14 @@ static int su3000_frontend_attach(struct dvb_usb_adapter *d) static int dw2102_tuner_attach(struct dvb_usb_adapter *adap) { - dvb_attach(dvb_pll_attach, adap->fe, 0x60, + dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x60, &adap->dev->i2c_adap, DVB_PLL_OPERA1); return 0; } static int dw3101_tuner_attach(struct dvb_usb_adapter *adap) { - dvb_attach(dvb_pll_attach, adap->fe, 0x60, + dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x60, &adap->dev->i2c_adap, DVB_PLL_TUA6034); return 0; @@ -1535,7 +1535,7 @@ static int dw2102_load_firmware(struct usb_device *dev, DW210X_READ_MSG); if ((reset16[0] == 0xa1) || (reset16[0] == 0x80)) { dw2102_properties.i2c_algo = &dw2102_i2c_algo; - dw2102_properties.adapter->tuner_attach = &dw2102_tuner_attach; + dw2102_properties.adapter->fe[0].tuner_attach = &dw2102_tuner_attach; break; } else { /* check STV0288 frontend */ @@ -1591,6 +1591,8 @@ static struct dvb_usb_device_properties dw2102_properties = { .read_mac_address = dw210x_read_mac_address, .adapter = { { + .num_frontends = 1, + .fe = {{ .frontend_attach = dw2102_frontend_attach, .stream = { .type = USB_BULK, @@ -1602,6 +1604,7 @@ static struct dvb_usb_device_properties dw2102_properties = { } } }, + }}, } }, .num_device_descs = 3, @@ -1642,6 +1645,8 @@ static struct dvb_usb_device_properties dw2104_properties = { .read_mac_address = dw210x_read_mac_address, .adapter = { { + .num_frontends = 1, + .fe = {{ .frontend_attach = dw2104_frontend_attach, .stream = { .type = USB_BULK, @@ -1653,6 +1658,7 @@ static struct dvb_usb_device_properties dw2104_properties = { } } }, + }}, } }, .num_device_descs = 2, @@ -1689,6 +1695,8 @@ static struct dvb_usb_device_properties dw3101_properties = { .read_mac_address = dw210x_read_mac_address, .adapter = { { + .num_frontends = 1, + .fe = {{ .frontend_attach = dw3101_frontend_attach, .tuner_attach = dw3101_tuner_attach, .stream = { @@ -1701,6 +1709,7 @@ static struct dvb_usb_device_properties dw3101_properties = { } } }, + }}, } }, .num_device_descs = 1, @@ -1733,6 +1742,8 @@ static struct dvb_usb_device_properties s6x0_properties = { .read_mac_address = s6x0_read_mac_address, .adapter = { { + .num_frontends = 1, + .fe = {{ .frontend_attach = zl100313_frontend_attach, .stream = { .type = USB_BULK, @@ -1744,6 +1755,7 @@ static struct dvb_usb_device_properties s6x0_properties = { } } }, + }}, } }, .num_device_descs = 1, @@ -1810,6 +1822,8 @@ static struct dvb_usb_device_properties su3000_properties = { .adapter = { { + .num_frontends = 1, + .fe = {{ .streaming_ctrl = su3000_streaming_ctrl, .frontend_attach = su3000_frontend_attach, .stream = { @@ -1822,6 +1836,7 @@ static struct dvb_usb_device_properties su3000_properties = { } } } + }}, } }, .num_device_descs = 3, @@ -1855,7 +1870,7 @@ static int dw2102_probe(struct usb_interface *intf, p1100->devices[0] = d1100; p1100->rc.legacy.rc_map_table = rc_map_tbs_table; p1100->rc.legacy.rc_map_size = ARRAY_SIZE(rc_map_tbs_table); - p1100->adapter->frontend_attach = stv0288_frontend_attach; + p1100->adapter->fe[0].frontend_attach = stv0288_frontend_attach; s660 = kzalloc(sizeof(struct dvb_usb_device_properties), GFP_KERNEL); if (!s660) { @@ -1869,7 +1884,7 @@ static int dw2102_probe(struct usb_interface *intf, s660->devices[0] = d660; s660->devices[1] = d480_1; s660->devices[2] = d480_2; - s660->adapter->frontend_attach = ds3000_frontend_attach; + s660->adapter->fe[0].frontend_attach = ds3000_frontend_attach; p7500 = kzalloc(sizeof(struct dvb_usb_device_properties), GFP_KERNEL); if (!p7500) { @@ -1883,7 +1898,7 @@ static int dw2102_probe(struct usb_interface *intf, p7500->devices[0] = d7500; p7500->rc.legacy.rc_map_table = rc_map_tbs_table; p7500->rc.legacy.rc_map_size = ARRAY_SIZE(rc_map_tbs_table); - p7500->adapter->frontend_attach = prof_7500_frontend_attach; + p7500->adapter->fe[0].frontend_attach = prof_7500_frontend_attach; if (0 == dvb_usb_device_init(intf, &dw2102_properties, THIS_MODULE, NULL, adapter_nr) || diff --git a/drivers/media/dvb/dvb-usb/ec168.c b/drivers/media/dvb/dvb-usb/ec168.c index 1ba3e5dbee10..78442fe4aa5e 100644 --- a/drivers/media/dvb/dvb-usb/ec168.c +++ b/drivers/media/dvb/dvb-usb/ec168.c @@ -200,9 +200,9 @@ static struct ec100_config ec168_ec100_config = { static int ec168_ec100_frontend_attach(struct dvb_usb_adapter *adap) { deb_info("%s:\n", __func__); - adap->fe = dvb_attach(ec100_attach, &ec168_ec100_config, + adap->fe_adap[0].fe = dvb_attach(ec100_attach, &ec168_ec100_config, &adap->dev->i2c_adap); - if (adap->fe == NULL) + if (adap->fe_adap[0].fe == NULL) return -ENODEV; return 0; @@ -228,7 +228,7 @@ static struct mxl5005s_config ec168_mxl5003s_config = { static int ec168_mxl5003s_tuner_attach(struct dvb_usb_adapter *adap) { deb_info("%s:\n", __func__); - return dvb_attach(mxl5005s_attach, adap->fe, &adap->dev->i2c_adap, + return dvb_attach(mxl5005s_attach, adap->fe_adap[0].fe, &adap->dev->i2c_adap, &ec168_mxl5003s_config) == NULL ? -ENODEV : 0; } @@ -382,6 +382,8 @@ static struct dvb_usb_device_properties ec168_properties = { .num_adapters = 1, .adapter = { { + .num_frontends = 1, + .fe = {{ .streaming_ctrl = ec168_streaming_ctrl, .frontend_attach = ec168_ec100_frontend_attach, .tuner_attach = ec168_mxl5003s_tuner_attach, @@ -395,6 +397,7 @@ static struct dvb_usb_device_properties ec168_properties = { } } }, + }}, } }, diff --git a/drivers/media/dvb/dvb-usb/friio.c b/drivers/media/dvb/dvb-usb/friio.c index 76159aed9bb0..b092dc2137cd 100644 --- a/drivers/media/dvb/dvb-usb/friio.c +++ b/drivers/media/dvb/dvb-usb/friio.c @@ -403,8 +403,8 @@ static int friio_frontend_attach(struct dvb_usb_adapter *adap) if (friio_initialize(adap->dev) < 0) return -EIO; - adap->fe = jdvbt90502_attach(adap->dev); - if (adap->fe == NULL) + adap->fe_adap[0].fe = jdvbt90502_attach(adap->dev); + if (adap->fe_adap[0].fe == NULL) return -EIO; return 0; @@ -473,6 +473,8 @@ static struct dvb_usb_device_properties friio_properties = { /* caps:0 => no pid filter, 188B TS packet */ /* GL861 has a HW pid filter, but no info available. */ { + .num_frontends = 1, + .fe = {{ .caps = 0, .frontend_attach = friio_frontend_attach, @@ -490,6 +492,7 @@ static struct dvb_usb_device_properties friio_properties = { } } }, + }}, } }, .i2c_algo = &gl861_i2c_algo, diff --git a/drivers/media/dvb/dvb-usb/gl861.c b/drivers/media/dvb/dvb-usb/gl861.c index 6f596ed41761..63681df244c4 100644 --- a/drivers/media/dvb/dvb-usb/gl861.c +++ b/drivers/media/dvb/dvb-usb/gl861.c @@ -103,9 +103,9 @@ static struct zl10353_config gl861_zl10353_config = { static int gl861_frontend_attach(struct dvb_usb_adapter *adap) { - adap->fe = dvb_attach(zl10353_attach, &gl861_zl10353_config, + adap->fe_adap[0].fe = dvb_attach(zl10353_attach, &gl861_zl10353_config, &adap->dev->i2c_adap); - if (adap->fe == NULL) + if (adap->fe_adap[0].fe == NULL) return -EIO; return 0; @@ -118,7 +118,7 @@ static struct qt1010_config gl861_qt1010_config = { static int gl861_tuner_attach(struct dvb_usb_adapter *adap) { return dvb_attach(qt1010_attach, - adap->fe, &adap->dev->i2c_adap, + adap->fe_adap[0].fe, &adap->dev->i2c_adap, &gl861_qt1010_config) == NULL ? -ENODEV : 0; } @@ -167,6 +167,8 @@ static struct dvb_usb_device_properties gl861_properties = { .num_adapters = 1, .adapter = {{ + .num_frontends = 1, + .fe = {{ .frontend_attach = gl861_frontend_attach, .tuner_attach = gl861_tuner_attach, @@ -181,6 +183,7 @@ static struct dvb_usb_device_properties gl861_properties = { } } }, + }}, } }, .i2c_algo = &gl861_i2c_algo, diff --git a/drivers/media/dvb/dvb-usb/gp8psk-fe.c b/drivers/media/dvb/dvb-usb/gp8psk-fe.c index 60d11e57e7d0..5426267980c7 100644 --- a/drivers/media/dvb/dvb-usb/gp8psk-fe.c +++ b/drivers/media/dvb/dvb-usb/gp8psk-fe.c @@ -144,19 +144,25 @@ static int gp8psk_fe_set_frontend(struct dvb_frontend* fe, cmd[6] = (freq >> 16) & 0xff; cmd[7] = (freq >> 24) & 0xff; + /* backwards compatibility: DVB-S + 8-PSK were used for Turbo-FEC */ + if (c->delivery_system == SYS_DVBS && c->modulation == PSK_8) + c->delivery_system = SYS_TURBO; + switch (c->delivery_system) { case SYS_DVBS: - /* Allow QPSK and 8PSK (even for DVB-S) */ - if (c->modulation != QPSK && c->modulation != PSK_8) { + if (c->modulation != QPSK) { deb_fe("%s: unsupported modulation selected (%d)\n", __func__, c->modulation); return -EOPNOTSUPP; } c->fec_inner = FEC_AUTO; break; - case SYS_DVBS2: + case SYS_DVBS2: /* kept for backwards compatibility */ deb_fe("%s: DVB-S2 delivery system selected\n", __func__); break; + case SYS_TURBO: + deb_fe("%s: Turbo-FEC delivery system selected\n", __func__); + break; default: deb_fe("%s: unsupported delivery system selected (%d)\n", @@ -189,7 +195,10 @@ static int gp8psk_fe_set_frontend(struct dvb_frontend* fe, default: cmd[9] = 5; break; } - cmd[8] = ADV_MOD_DVB_QPSK; + if (c->delivery_system == SYS_TURBO) + cmd[8] = ADV_MOD_TURBO_QPSK; + else + cmd[8] = ADV_MOD_DVB_QPSK; break; case PSK_8: /* PSK_8 is for compatibility with DN */ cmd[8] = ADV_MOD_TURBO_8PSK; diff --git a/drivers/media/dvb/dvb-usb/gp8psk.c b/drivers/media/dvb/dvb-usb/gp8psk.c index 1cb3d9a66e02..5f71284703d0 100644 --- a/drivers/media/dvb/dvb-usb/gp8psk.c +++ b/drivers/media/dvb/dvb-usb/gp8psk.c @@ -230,7 +230,7 @@ static int gp8psk_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) static int gp8psk_frontend_attach(struct dvb_usb_adapter *adap) { - adap->fe = gp8psk_fe_attach(adap->dev); + adap->fe_adap[0].fe = gp8psk_fe_attach(adap->dev); return 0; } @@ -268,6 +268,8 @@ static struct dvb_usb_device_properties gp8psk_properties = { .num_adapters = 1, .adapter = { { + .num_frontends = 1, + .fe = {{ .streaming_ctrl = gp8psk_streaming_ctrl, .frontend_attach = gp8psk_frontend_attach, /* parameter for the MPEG2-data transfer */ @@ -281,6 +283,7 @@ static struct dvb_usb_device_properties gp8psk_properties = { } } }, + }}, } }, .power_ctrl = gp8psk_power_ctrl, diff --git a/drivers/media/dvb/dvb-usb/it913x.c b/drivers/media/dvb/dvb-usb/it913x.c new file mode 100644 index 000000000000..c46226187143 --- /dev/null +++ b/drivers/media/dvb/dvb-usb/it913x.c @@ -0,0 +1,702 @@ +/* DVB USB compliant linux driver for IT9137 + * + * Copyright (C) 2011 Malcolm Priestley (tvboxspy@gmail.com) + * IT9137 (C) ITE Tech Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2, as + * published by the Free Software Foundation. + * + * 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. + * + * + * see Documentation/dvb/README.dvb-usb for more information + * see Documentation/dvb/it9137.txt for firmware information + * + */ +#define DVB_USB_LOG_PREFIX "it913x" + +#include <linux/usb.h> +#include <linux/usb/input.h> +#include <media/rc-core.h> + +#include "dvb-usb.h" +#include "it913x-fe.h" + +/* debug */ +static int dvb_usb_it913x_debug; +#define l_dprintk(var, level, args...) do { \ + if ((var >= level)) \ + printk(KERN_DEBUG DVB_USB_LOG_PREFIX ": " args); \ +} while (0) + +#define deb_info(level, args...) l_dprintk(dvb_usb_it913x_debug, level, args) +#define debug_data_snipet(level, name, p) \ + deb_info(level, name" (%02x%02x%02x%02x%02x%02x%02x%02x)", \ + *p, *(p+1), *(p+2), *(p+3), *(p+4), \ + *(p+5), *(p+6), *(p+7)); + + +module_param_named(debug, dvb_usb_it913x_debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able))." + DVB_USB_DEBUG_STATUS); + +static int pid_filter; +module_param_named(pid, pid_filter, int, 0644); +MODULE_PARM_DESC(pid, "set default 0=on 1=off"); + +int cmd_counter; + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +struct it913x_state { + u8 id; +}; + +struct ite_config { + u8 chip_ver; + u16 chip_type; + u32 firmware; + u8 tuner_id_0; + u8 tuner_id_1; + u8 dual_mode; +}; + +struct ite_config it913x_config; + +static int it913x_bulk_write(struct usb_device *dev, + u8 *snd, int len, u8 pipe) +{ + int ret, actual_l; + + ret = usb_bulk_msg(dev, usb_sndbulkpipe(dev, pipe), + snd, len , &actual_l, 100); + return ret; +} + +static int it913x_bulk_read(struct usb_device *dev, + u8 *rev, int len, u8 pipe) +{ + int ret, actual_l; + + ret = usb_bulk_msg(dev, usb_rcvbulkpipe(dev, pipe), + rev, len , &actual_l, 200); + return ret; +} + +static u16 check_sum(u8 *p, u8 len) +{ + u16 sum = 0; + u8 i = 1; + while (i < len) + sum += (i++ & 1) ? (*p++) << 8 : *p++; + return ~sum; +} + +static int it913x_io(struct usb_device *udev, u8 mode, u8 pro, + u8 cmd, u32 reg, u8 addr, u8 *data, u8 len) +{ + int ret = 0, i, buf_size = 1; + u8 *buff; + u8 rlen; + u16 chk_sum; + + buff = kzalloc(256, GFP_KERNEL); + if (!buff) { + info("USB Buffer Failed"); + return -ENOMEM; + } + + buff[buf_size++] = pro; + buff[buf_size++] = cmd; + buff[buf_size++] = cmd_counter; + + switch (mode) { + case READ_LONG: + case WRITE_LONG: + buff[buf_size++] = len; + buff[buf_size++] = 2; + buff[buf_size++] = (reg >> 24); + buff[buf_size++] = (reg >> 16) & 0xff; + buff[buf_size++] = (reg >> 8) & 0xff; + buff[buf_size++] = reg & 0xff; + break; + case READ_SHORT: + buff[buf_size++] = addr; + break; + case WRITE_SHORT: + buff[buf_size++] = len; + buff[buf_size++] = addr; + buff[buf_size++] = (reg >> 8) & 0xff; + buff[buf_size++] = reg & 0xff; + break; + case READ_DATA: + case WRITE_DATA: + break; + case WRITE_CMD: + mode = 7; + break; + default: + kfree(buff); + return -EINVAL; + } + + if (mode & 1) { + for (i = 0; i < len ; i++) + buff[buf_size++] = data[i]; + } + chk_sum = check_sum(&buff[1], buf_size); + + buff[buf_size++] = chk_sum >> 8; + buff[0] = buf_size; + buff[buf_size++] = (chk_sum & 0xff); + + ret = it913x_bulk_write(udev, buff, buf_size , 0x02); + + ret |= it913x_bulk_read(udev, buff, (mode & 1) ? + 5 : len + 5 , 0x01); + + rlen = (mode & 0x1) ? 0x1 : len; + + if (mode & 1) + ret |= buff[2]; + else + memcpy(data, &buff[3], rlen); + + cmd_counter++; + + kfree(buff); + + return (ret < 0) ? -ENODEV : 0; +} + +static int it913x_wr_reg(struct usb_device *udev, u8 pro, u32 reg , u8 data) +{ + int ret; + u8 b[1]; + b[0] = data; + ret = it913x_io(udev, WRITE_LONG, pro, + CMD_DEMOD_WRITE, reg, 0, b, sizeof(b)); + + return ret; +} + +static int it913x_read_reg(struct usb_device *udev, u32 reg) +{ + int ret; + u8 data[1]; + + ret = it913x_io(udev, READ_LONG, DEV_0, + CMD_DEMOD_READ, reg, 0, &data[0], 1); + + return (ret < 0) ? ret : data[0]; +} + +static u32 it913x_query(struct usb_device *udev, u8 pro) +{ + int ret; + u8 data[4]; + ret = it913x_io(udev, READ_LONG, pro, CMD_DEMOD_READ, + 0x1222, 0, &data[0], 3); + + it913x_config.chip_ver = data[0]; + it913x_config.chip_type = (u16)(data[2] << 8) + data[1]; + + info("Chip Version=%02x Chip Type=%04x", it913x_config.chip_ver, + it913x_config.chip_type); + + ret |= it913x_io(udev, READ_SHORT, pro, + CMD_QUERYINFO, 0, 0x1, &data[0], 4); + + it913x_config.firmware = (data[0] << 24) + (data[1] << 16) + + (data[2] << 8) + data[3]; + + return (ret < 0) ? 0 : it913x_config.firmware; +} + +static int it913x_pid_filter_ctrl(struct dvb_usb_adapter *adap, int onoff) +{ + int ret = 0; + u8 pro = (adap->id == 0) ? DEV_0_DMOD : DEV_1_DMOD; + + if (mutex_lock_interruptible(&adap->dev->i2c_mutex) < 0) + return -EAGAIN; + deb_info(1, "PID_C (%02x)", onoff); + + if (!onoff) + ret = it913x_wr_reg(adap->dev->udev, pro, PID_RST, 0x1); + + mutex_unlock(&adap->dev->i2c_mutex); + return ret; +} + +static int it913x_pid_filter(struct dvb_usb_adapter *adap, + int index, u16 pid, int onoff) +{ + struct usb_device *udev = adap->dev->udev; + int ret = 0; + u8 pro = (adap->id == 0) ? DEV_0_DMOD : DEV_1_DMOD; + + if (pid_filter > 0) + return 0; + + if (mutex_lock_interruptible(&adap->dev->i2c_mutex) < 0) + return -EAGAIN; + deb_info(1, "PID_F (%02x)", onoff); + if (onoff) { + ret = it913x_wr_reg(udev, pro, PID_EN, 0x1); + + ret |= it913x_wr_reg(udev, pro, PID_LSB, (u8)(pid & 0xff)); + + ret |= it913x_wr_reg(udev, pro, PID_MSB, (u8)(pid >> 8)); + + ret |= it913x_wr_reg(udev, pro, PID_INX_EN, (u8)onoff); + + ret |= it913x_wr_reg(udev, pro, PID_INX, (u8)(index & 0x1f)); + + } + + mutex_unlock(&adap->dev->i2c_mutex); + return 0; +} + + +static int it913x_return_status(struct usb_device *udev) +{ + u32 firm = 0; + + firm = it913x_query(udev, DEV_0); + if (firm > 0) + info("Firmware Version %d", firm); + + return (firm > 0) ? firm : 0; +} + +static int it913x_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], + int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + static u8 data[256]; + int ret; + u32 reg; + u8 pro; + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + debug_data_snipet(1, "Message out", msg[0].buf); + deb_info(2, "num of messages %d address %02x", num, msg[0].addr); + + pro = (msg[0].addr & 0x2) ? DEV_0_DMOD : 0x0; + pro |= (msg[0].addr & 0x20) ? DEV_1 : DEV_0; + memcpy(data, msg[0].buf, msg[0].len); + reg = (data[0] << 24) + (data[1] << 16) + + (data[2] << 8) + data[3]; + if (num == 2) { + ret = it913x_io(d->udev, READ_LONG, pro, + CMD_DEMOD_READ, reg, 0, data, msg[1].len); + memcpy(msg[1].buf, data, msg[1].len); + } else + ret = it913x_io(d->udev, WRITE_LONG, pro, CMD_DEMOD_WRITE, + reg, 0, &data[4], msg[0].len - 4); + + mutex_unlock(&d->i2c_mutex); + + return ret; +} + +static u32 it913x_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static struct i2c_algorithm it913x_i2c_algo = { + .master_xfer = it913x_i2c_xfer, + .functionality = it913x_i2c_func, +}; + +/* Callbacks for DVB USB */ +#define IT913X_POLL 250 +static int it913x_rc_query(struct dvb_usb_device *d) +{ + u8 ibuf[4]; + int ret; + u32 key; + /* Avoid conflict with frontends*/ + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + ret = it913x_io(d->udev, READ_LONG, PRO_LINK, CMD_IR_GET, + 0, 0, &ibuf[0], sizeof(ibuf)); + + if ((ibuf[2] + ibuf[3]) == 0xff) { + key = ibuf[2]; + key += ibuf[0] << 8; + deb_info(1, "INT Key =%08x", key); + if (d->rc_dev != NULL) + rc_keydown(d->rc_dev, key, 0); + } + mutex_unlock(&d->i2c_mutex); + + return ret; +} +static int it913x_identify_state(struct usb_device *udev, + struct dvb_usb_device_properties *props, + struct dvb_usb_device_description **desc, + int *cold) +{ + int ret = 0, firm_no; + u8 reg, remote; + + firm_no = it913x_return_status(udev); + + /* checnk for dual mode */ + it913x_config.dual_mode = it913x_read_reg(udev, 0x49c5); + + /* TODO different remotes */ + remote = it913x_read_reg(udev, 0x49ac); /* Remote */ + if (remote == 0) + props->rc.core.rc_codes = NULL; + + /* TODO at the moment tuner_id is always assigned to 0x38 */ + it913x_config.tuner_id_0 = it913x_read_reg(udev, 0x49d0); + + info("Dual mode=%x Remote=%x Tuner Type=%x", it913x_config.dual_mode + , remote, it913x_config.tuner_id_0); + + if (firm_no > 0) { + *cold = 0; + return 0; + } + + if (it913x_config.dual_mode) { + it913x_config.tuner_id_1 = it913x_read_reg(udev, 0x49e0); + ret = it913x_wr_reg(udev, DEV_0, GPIOH1_EN, 0x1); + ret |= it913x_wr_reg(udev, DEV_0, GPIOH1_ON, 0x1); + ret |= it913x_wr_reg(udev, DEV_0, GPIOH1_O, 0x1); + msleep(50); + ret |= it913x_wr_reg(udev, DEV_0, GPIOH1_O, 0x0); + msleep(50); + reg = it913x_read_reg(udev, GPIOH1_O); + if (reg == 0) { + ret |= it913x_wr_reg(udev, DEV_0, GPIOH1_O, 0x1); + ret |= it913x_return_status(udev); + if (ret != 0) + ret = it913x_wr_reg(udev, DEV_0, + GPIOH1_O, 0x0); + } + props->num_adapters = 2; + } else + props->num_adapters = 1; + + reg = it913x_read_reg(udev, IO_MUX_POWER_CLK); + + if (it913x_config.dual_mode) { + ret |= it913x_wr_reg(udev, DEV_0, 0x4bfb, CHIP2_I2C_ADDR); + ret |= it913x_wr_reg(udev, DEV_0, CLK_O_EN, 0x1); + } else { + ret |= it913x_wr_reg(udev, DEV_0, 0x4bfb, 0x0); + ret |= it913x_wr_reg(udev, DEV_0, CLK_O_EN, 0x0); + } + + *cold = 1; + + return (ret < 0) ? -ENODEV : 0; +} + +static int it913x_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) +{ + int ret = 0; + u8 pro = (adap->id == 0) ? DEV_0_DMOD : DEV_1_DMOD; + + if (mutex_lock_interruptible(&adap->dev->i2c_mutex) < 0) + return -EAGAIN; + deb_info(1, "STM (%02x)", onoff); + + if (!onoff) + ret = it913x_wr_reg(adap->dev->udev, pro, PID_RST, 0x1); + + + mutex_unlock(&adap->dev->i2c_mutex); + + return ret; +} + + +static int it913x_download_firmware(struct usb_device *udev, + const struct firmware *fw) +{ + int ret = 0, i; + u8 packet_size, dlen; + u8 *fw_data; + + packet_size = 0x29; + + ret = it913x_wr_reg(udev, DEV_0, I2C_CLK, I2C_CLK_100); + + info("FRM Starting Firmware Download"); + /* This uses scatter write firmware headers follow */ + /* 03 XX 00 XX = chip number? */ + + for (i = 0; i < fw->size; i += packet_size) { + if (i > 0) + packet_size = 0x39; + fw_data = (u8 *)(fw->data + i); + dlen = ((i + packet_size) > fw->size) + ? (fw->size - i) : packet_size; + ret |= it913x_io(udev, WRITE_DATA, DEV_0, + CMD_SCATTER_WRITE, 0, 0, fw_data, dlen); + udelay(1000); + } + + ret |= it913x_io(udev, WRITE_CMD, DEV_0, + CMD_BOOT, 0, 0, NULL, 0); + + msleep(100); + + if (ret < 0) + info("FRM Firmware Download Failed (%04x)" , ret); + else + info("FRM Firmware Download Completed - Resetting Device"); + + ret |= it913x_return_status(udev); + + msleep(30); + + ret |= it913x_wr_reg(udev, DEV_0, I2C_CLK, I2C_CLK_400); + + /* Tuner function */ + if (it913x_config.dual_mode) + ret |= it913x_wr_reg(udev, DEV_0_DMOD , 0xec4c, 0xa0); + + ret |= it913x_wr_reg(udev, DEV_0, PADODPU, 0x0); + ret |= it913x_wr_reg(udev, DEV_0, AGC_O_D, 0x0); + if (it913x_config.dual_mode) { + ret |= it913x_wr_reg(udev, DEV_1, PADODPU, 0x0); + ret |= it913x_wr_reg(udev, DEV_1, AGC_O_D, 0x0); + } + + return (ret < 0) ? -ENODEV : 0; +} + +static int it913x_name(struct dvb_usb_adapter *adap) +{ + const char *desc = adap->dev->desc->name; + char *fe_name[] = {"_1", "_2", "_3", "_4"}; + char *name = adap->fe_adap[0].fe->ops.info.name; + + strlcpy(name, desc, 128); + strlcat(name, fe_name[adap->id], 128); + + return 0; +} + +static int it913x_frontend_attach(struct dvb_usb_adapter *adap) +{ + struct usb_device *udev = adap->dev->udev; + int ret = 0; + u8 adf = it913x_read_reg(udev, IO_MUX_POWER_CLK); + u8 adap_addr = I2C_BASE_ADDR + (adap->id << 5); + u16 ep_size = adap->props.fe[0].stream.u.bulk.buffersize; + u8 tuner_id, tuner_type; + + if (adap->id == 0) + tuner_id = it913x_config.tuner_id_0; + else + tuner_id = it913x_config.tuner_id_1; + + /* TODO we always use IT9137 possible references here*/ + /* Documentation suggests don't care */ + switch (tuner_id) { + case 0x51: + case 0x52: + case 0x60: + case 0x61: + case 0x62: + default: + case 0x38: + tuner_type = IT9137; + } + + adap->fe_adap[0].fe = dvb_attach(it913x_fe_attach, + &adap->dev->i2c_adap, adap_addr, adf, tuner_type); + + if (adap->id == 0 && adap->fe_adap[0].fe) { + ret = it913x_wr_reg(udev, DEV_0_DMOD, MP2_SW_RST, 0x1); + ret = it913x_wr_reg(udev, DEV_0_DMOD, MP2IF2_SW_RST, 0x1); + ret = it913x_wr_reg(udev, DEV_0, EP0_TX_EN, 0x0f); + ret = it913x_wr_reg(udev, DEV_0, EP0_TX_NAK, 0x1b); + ret = it913x_wr_reg(udev, DEV_0, EP0_TX_EN, 0x2f); + ret = it913x_wr_reg(udev, DEV_0, EP4_TX_LEN_LSB, + ep_size & 0xff); + ret = it913x_wr_reg(udev, DEV_0, EP4_TX_LEN_MSB, ep_size >> 8); + ret = it913x_wr_reg(udev, DEV_0, EP4_MAX_PKT, 0x80); + } else if (adap->id == 1 && adap->fe_adap[0].fe) { + ret = it913x_wr_reg(udev, DEV_0, EP0_TX_EN, 0x6f); + ret = it913x_wr_reg(udev, DEV_0, EP5_TX_LEN_LSB, + ep_size & 0xff); + ret = it913x_wr_reg(udev, DEV_0, EP5_TX_LEN_MSB, ep_size >> 8); + ret = it913x_wr_reg(udev, DEV_0, EP5_MAX_PKT, 0x80); + ret = it913x_wr_reg(udev, DEV_0_DMOD, MP2IF2_EN, 0x1); + ret = it913x_wr_reg(udev, DEV_1_DMOD, MP2IF_SERIAL, 0x1); + ret = it913x_wr_reg(udev, DEV_1, TOP_HOSTB_SER_MODE, 0x1); + ret = it913x_wr_reg(udev, DEV_0_DMOD, TSIS_ENABLE, 0x1); + ret = it913x_wr_reg(udev, DEV_0_DMOD, MP2_SW_RST, 0x0); + ret = it913x_wr_reg(udev, DEV_0_DMOD, MP2IF2_SW_RST, 0x0); + ret = it913x_wr_reg(udev, DEV_0_DMOD, MP2IF2_HALF_PSB, 0x0); + ret = it913x_wr_reg(udev, DEV_0_DMOD, MP2IF_STOP_EN, 0x1); + ret = it913x_wr_reg(udev, DEV_1_DMOD, MPEG_FULL_SPEED, 0x0); + ret = it913x_wr_reg(udev, DEV_1_DMOD, MP2IF_STOP_EN, 0x0); + } else + return -ENODEV; + + ret = it913x_name(adap); + + return ret; +} + +/* DVB USB Driver */ +static struct dvb_usb_device_properties it913x_properties; + +static int it913x_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + cmd_counter = 0; + if (0 == dvb_usb_device_init(intf, &it913x_properties, + THIS_MODULE, NULL, adapter_nr)) { + info("DEV registering device driver"); + return 0; + } + + info("DEV it913x Error"); + return -ENODEV; + +} + +static struct usb_device_id it913x_table[] = { + { USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_UB499_2T_T09) }, + { USB_DEVICE(USB_VID_ITETECH, USB_PID_ITETECH_IT9135) }, + {} /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, it913x_table); + +static struct dvb_usb_device_properties it913x_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + .usb_ctrl = DEVICE_SPECIFIC, + .download_firmware = it913x_download_firmware, + .firmware = "dvb-usb-it9137-01.fw", + .no_reconnect = 1, + .size_of_priv = sizeof(struct it913x_state), + .num_adapters = 2, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .caps = DVB_USB_ADAP_HAS_PID_FILTER| + DVB_USB_ADAP_NEED_PID_FILTERING| + DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .streaming_ctrl = it913x_streaming_ctrl, + .pid_filter_count = 31, + .pid_filter = it913x_pid_filter, + .pid_filter_ctrl = it913x_pid_filter_ctrl, + .frontend_attach = it913x_frontend_attach, + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_BULK, + .count = 10, + .endpoint = 0x04, + .u = {/* Keep Low if PID filter on */ + .bulk = { + .buffersize = 3584, + + } + } + } + }}, + }, + { + .num_frontends = 1, + .fe = {{ + .caps = DVB_USB_ADAP_HAS_PID_FILTER| + DVB_USB_ADAP_NEED_PID_FILTERING| + DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .streaming_ctrl = it913x_streaming_ctrl, + .pid_filter_count = 31, + .pid_filter = it913x_pid_filter, + .pid_filter_ctrl = it913x_pid_filter_ctrl, + .frontend_attach = it913x_frontend_attach, + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_BULK, + .count = 5, + .endpoint = 0x05, + .u = { + .bulk = { + .buffersize = 3584, + + } + } + } + }}, + } + }, + .identify_state = it913x_identify_state, + .rc.core = { + .protocol = RC_TYPE_NEC, + .module_name = "it913x", + .rc_query = it913x_rc_query, + .rc_interval = IT913X_POLL, + .allowed_protos = RC_TYPE_NEC, + .rc_codes = RC_MAP_KWORLD_315U, + }, + .i2c_algo = &it913x_i2c_algo, + .num_device_descs = 2, + .devices = { + { "Kworld UB499-2T T09(IT9137)", + { &it913x_table[0], NULL }, + }, + { "ITE 9135 Generic", + { &it913x_table[1], NULL }, + }, + } +}; + +static struct usb_driver it913x_driver = { + .name = "it913x", + .probe = it913x_probe, + .disconnect = dvb_usb_device_exit, + .id_table = it913x_table, +}; + +/* module stuff */ +static int __init it913x_module_init(void) +{ + int result = usb_register(&it913x_driver); + if (result) { + err("usb_register failed. Error number %d", result); + return result; + } + + return 0; +} + +static void __exit it913x_module_exit(void) +{ + /* deregister this driver from the USB subsystem */ + usb_deregister(&it913x_driver); +} + +module_init(it913x_module_init); +module_exit(it913x_module_exit); + +MODULE_AUTHOR("Malcolm Priestley <tvboxspy@gmail.com>"); +MODULE_DESCRIPTION("it913x USB 2 Driver"); +MODULE_VERSION("1.07"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/dvb-usb/lmedm04.c b/drivers/media/dvb/dvb-usb/lmedm04.c index 37b146961ae2..b9228240f5ce 100644 --- a/drivers/media/dvb/dvb-usb/lmedm04.c +++ b/drivers/media/dvb/dvb-usb/lmedm04.c @@ -162,7 +162,7 @@ static int lme2510_usb_talk(struct dvb_usb_device *d, int ret = 0; if (st->usb_buffer == NULL) { - st->usb_buffer = kmalloc(512, GFP_KERNEL); + st->usb_buffer = kmalloc(64, GFP_KERNEL); if (st->usb_buffer == NULL) { info("MEM Error no memory"); return -ENOMEM; @@ -175,8 +175,8 @@ static int lme2510_usb_talk(struct dvb_usb_device *d, if (ret < 0) return -EAGAIN; - /* the read/write capped at 512 */ - memcpy(buff, wbuf, (wlen > 512) ? 512 : wlen); + /* the read/write capped at 64 */ + memcpy(buff, wbuf, (wlen < 64) ? wlen : 64); ret |= usb_clear_halt(d->udev, usb_sndbulkpipe(d->udev, 0x01)); @@ -186,8 +186,8 @@ static int lme2510_usb_talk(struct dvb_usb_device *d, ret |= usb_clear_halt(d->udev, usb_rcvbulkpipe(d->udev, 0x01)); - ret |= lme2510_bulk_read(d->udev, buff, (rlen > 512) ? - 512 : rlen , 0x01); + ret |= lme2510_bulk_read(d->udev, buff, (rlen < 64) ? + rlen : 64 , 0x01); if (rlen > 0) memcpy(rbuf, buff, rlen); @@ -333,7 +333,7 @@ static int lme2510_int_read(struct dvb_usb_adapter *adap) if (lme_int->lme_urb == NULL) return -ENOMEM; - lme_int->buffer = usb_alloc_coherent(adap->dev->udev, 5000, GFP_ATOMIC, + lme_int->buffer = usb_alloc_coherent(adap->dev->udev, 128, GFP_ATOMIC, &lme_int->lme_urb->transfer_dma); if (lme_int->buffer == NULL) @@ -343,10 +343,10 @@ static int lme2510_int_read(struct dvb_usb_adapter *adap) adap->dev->udev, usb_rcvintpipe(adap->dev->udev, 0xa), lme_int->buffer, - 4096, + 128, lme2510_int_response, adap, - 11); + 8); lme_int->lme_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; @@ -580,7 +580,7 @@ static int lme2510_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], { struct dvb_usb_device *d = i2c_get_adapdata(adap); struct lme2510_state *st = d->priv; - static u8 obuf[64], ibuf[512]; + static u8 obuf[64], ibuf[64]; int i, read, read_o; u16 len; u8 gate = st->i2c_gate; @@ -621,7 +621,7 @@ static int lme2510_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], len = msg[i].len+3; } - if (lme2510_msg(d, obuf, len, ibuf, 512) < 0) { + if (lme2510_msg(d, obuf, len, ibuf, 64) < 0) { deb_info(1, "i2c transfer failed."); return -EAGAIN; } @@ -941,7 +941,7 @@ static int lme_name(struct dvb_usb_adapter *adap) const char *desc = adap->dev->desc->name; char *fe_name[] = {"", " LG TDQY-P001F", " SHARP:BS2F7HZ7395", " SHARP:BS2F7HZ0194"}; - char *name = adap->fe->ops.info.name; + char *name = adap->fe_adap[0].fe->ops.info.name; strlcpy(name, desc, 128); strlcat(name, fe_name[st->tuner_config], 128); @@ -958,10 +958,10 @@ static int dm04_lme2510_frontend_attach(struct dvb_usb_adapter *adap) st->i2c_talk_onoff = 1; st->i2c_gate = 4; - adap->fe = dvb_attach(tda10086_attach, &tda10086_config, + adap->fe_adap[0].fe = dvb_attach(tda10086_attach, &tda10086_config, &adap->dev->i2c_adap); - if (adap->fe) { + if (adap->fe_adap[0].fe) { info("TUN Found Frontend TDA10086"); st->i2c_tuner_gate_w = 4; st->i2c_tuner_gate_r = 4; @@ -975,9 +975,9 @@ static int dm04_lme2510_frontend_attach(struct dvb_usb_adapter *adap) } st->i2c_gate = 4; - adap->fe = dvb_attach(stv0299_attach, &sharp_z0194_config, + adap->fe_adap[0].fe = dvb_attach(stv0299_attach, &sharp_z0194_config, &adap->dev->i2c_adap); - if (adap->fe) { + if (adap->fe_adap[0].fe) { info("FE Found Stv0299"); st->i2c_tuner_gate_w = 4; st->i2c_tuner_gate_r = 5; @@ -991,9 +991,9 @@ static int dm04_lme2510_frontend_attach(struct dvb_usb_adapter *adap) } st->i2c_gate = 5; - adap->fe = dvb_attach(stv0288_attach, &lme_config, + adap->fe_adap[0].fe = dvb_attach(stv0288_attach, &lme_config, &adap->dev->i2c_adap); - if (adap->fe) { + if (adap->fe_adap[0].fe) { info("FE Found Stv0288"); st->i2c_tuner_gate_w = 4; st->i2c_tuner_gate_r = 5; @@ -1010,15 +1010,15 @@ static int dm04_lme2510_frontend_attach(struct dvb_usb_adapter *adap) end: if (ret) { - if (adap->fe) { - dvb_frontend_detach(adap->fe); - adap->fe = NULL; + if (adap->fe_adap[0].fe) { + dvb_frontend_detach(adap->fe_adap[0].fe); + adap->fe_adap[0].fe = NULL; } adap->dev->props.rc.core.rc_codes = NULL; return -ENODEV; } - adap->fe->ops.set_voltage = dm04_lme2510_set_voltage; + adap->fe_adap[0].fe->ops.set_voltage = dm04_lme2510_set_voltage; ret = lme_name(adap); return ret; } @@ -1031,17 +1031,17 @@ static int dm04_lme2510_tuner(struct dvb_usb_adapter *adap) switch (st->tuner_config) { case TUNER_LG: - if (dvb_attach(tda826x_attach, adap->fe, 0xc0, + if (dvb_attach(tda826x_attach, adap->fe_adap[0].fe, 0xc0, &adap->dev->i2c_adap, 1)) ret = st->tuner_config; break; case TUNER_S7395: - if (dvb_attach(ix2505v_attach , adap->fe, &lme_tuner, + if (dvb_attach(ix2505v_attach , adap->fe_adap[0].fe, &lme_tuner, &adap->dev->i2c_adap)) ret = st->tuner_config; break; case TUNER_S0194: - if (dvb_attach(dvb_pll_attach , adap->fe, 0xc0, + if (dvb_attach(dvb_pll_attach , adap->fe_adap[0].fe, 0xc0, &adap->dev->i2c_adap, DVB_PLL_OPERA1)) ret = st->tuner_config; break; @@ -1145,6 +1145,8 @@ static struct dvb_usb_device_properties lme2510_properties = { .num_adapters = 1, .adapter = { { + .num_frontends = 1, + .fe = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER| DVB_USB_ADAP_NEED_PID_FILTERING| DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, @@ -1166,6 +1168,7 @@ static struct dvb_usb_device_properties lme2510_properties = { } } } + }}, } }, .rc.core = { @@ -1193,6 +1196,8 @@ static struct dvb_usb_device_properties lme2510c_properties = { .num_adapters = 1, .adapter = { { + .num_frontends = 1, + .fe = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER| DVB_USB_ADAP_NEED_PID_FILTERING| DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, @@ -1214,6 +1219,7 @@ static struct dvb_usb_device_properties lme2510c_properties = { } } } + }}, } }, .rc.core = { @@ -1241,7 +1247,7 @@ static void *lme2510_exit_int(struct dvb_usb_device *d) void *buffer = NULL; if (adap != NULL) { - lme2510_kill_urb(&adap->stream); + lme2510_kill_urb(&adap->fe_adap[0].stream); adap->feedcount = 0; } @@ -1255,7 +1261,7 @@ static void *lme2510_exit_int(struct dvb_usb_device *d) if (st->lme_urb != NULL) { usb_kill_urb(st->lme_urb); - usb_free_coherent(d->udev, 5000, st->buffer, + usb_free_coherent(d->udev, 128, st->buffer, st->lme_urb->transfer_dma); info("Interrupt Service Stopped"); } @@ -1306,5 +1312,5 @@ module_exit(lme2510_module_exit); MODULE_AUTHOR("Malcolm Priestley <tvboxspy@gmail.com>"); MODULE_DESCRIPTION("LME2510(C) DVB-S USB2.0"); -MODULE_VERSION("1.88"); +MODULE_VERSION("1.90"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/dvb-usb/m920x.c b/drivers/media/dvb/dvb-usb/m920x.c index 9456792f219b..a1e1287c949e 100644 --- a/drivers/media/dvb/dvb-usb/m920x.c +++ b/drivers/media/dvb/dvb-usb/m920x.c @@ -86,12 +86,12 @@ static int m920x_init(struct dvb_usb_device *d, struct m920x_inits *rc_seq) } for (i = 0; i < d->props.num_adapters; i++) - flags |= d->adapter[i].props.caps; + flags |= d->adapter[i].props.fe[0].caps; /* Some devices(Dposh) might crash if we attempt touch at all. */ if (flags & DVB_USB_ADAP_HAS_PID_FILTER) { for (i = 0; i < d->props.num_adapters; i++) { - epi = d->adapter[i].props.stream.endpoint - 0x81; + epi = d->adapter[i].props.fe[0].stream.endpoint - 0x81; if (epi < 0 || epi >= M9206_MAX_ADAPTERS) { printk(KERN_INFO "m920x: Unexpected adapter endpoint!\n"); @@ -292,7 +292,7 @@ static int m920x_update_filters(struct dvb_usb_adapter *adap) struct m920x_state *m = adap->dev->priv; int enabled = m->filtering_enabled[adap->id]; int i, ret = 0, filter = 0; - int ep = adap->props.stream.endpoint; + int ep = adap->props.fe[0].stream.endpoint; for (i = 0; i < M9206_MAX_FILTERS; i++) if (m->filters[adap->id][i] == 8192) @@ -501,9 +501,10 @@ static int m920x_mt352_frontend_attach(struct dvb_usb_adapter *adap) { deb("%s\n",__func__); - if ((adap->fe = dvb_attach(mt352_attach, - &m920x_mt352_config, - &adap->dev->i2c_adap)) == NULL) + adap->fe_adap[0].fe = dvb_attach(mt352_attach, + &m920x_mt352_config, + &adap->dev->i2c_adap); + if ((adap->fe_adap[0].fe) == NULL) return -EIO; return 0; @@ -513,9 +514,10 @@ static int m920x_tda10046_08_frontend_attach(struct dvb_usb_adapter *adap) { deb("%s\n",__func__); - if ((adap->fe = dvb_attach(tda10046_attach, - &m920x_tda10046_08_config, - &adap->dev->i2c_adap)) == NULL) + adap->fe_adap[0].fe = dvb_attach(tda10046_attach, + &m920x_tda10046_08_config, + &adap->dev->i2c_adap); + if ((adap->fe_adap[0].fe) == NULL) return -EIO; return 0; @@ -525,9 +527,10 @@ static int m920x_tda10046_0b_frontend_attach(struct dvb_usb_adapter *adap) { deb("%s\n",__func__); - if ((adap->fe = dvb_attach(tda10046_attach, - &m920x_tda10046_0b_config, - &adap->dev->i2c_adap)) == NULL) + adap->fe_adap[0].fe = dvb_attach(tda10046_attach, + &m920x_tda10046_0b_config, + &adap->dev->i2c_adap); + if ((adap->fe_adap[0].fe) == NULL) return -EIO; return 0; @@ -537,7 +540,7 @@ static int m920x_qt1010_tuner_attach(struct dvb_usb_adapter *adap) { deb("%s\n",__func__); - if (dvb_attach(qt1010_attach, adap->fe, &adap->dev->i2c_adap, &m920x_qt1010_config) == NULL) + if (dvb_attach(qt1010_attach, adap->fe_adap[0].fe, &adap->dev->i2c_adap, &m920x_qt1010_config) == NULL) return -ENODEV; return 0; @@ -547,7 +550,7 @@ static int m920x_tda8275_60_tuner_attach(struct dvb_usb_adapter *adap) { deb("%s\n",__func__); - if (dvb_attach(tda827x_attach, adap->fe, 0x60, &adap->dev->i2c_adap, NULL) == NULL) + if (dvb_attach(tda827x_attach, adap->fe_adap[0].fe, 0x60, &adap->dev->i2c_adap, NULL) == NULL) return -ENODEV; return 0; @@ -557,7 +560,7 @@ static int m920x_tda8275_61_tuner_attach(struct dvb_usb_adapter *adap) { deb("%s\n",__func__); - if (dvb_attach(tda827x_attach, adap->fe, 0x61, &adap->dev->i2c_adap, NULL) == NULL) + if (dvb_attach(tda827x_attach, adap->fe_adap[0].fe, 0x61, &adap->dev->i2c_adap, NULL) == NULL) return -ENODEV; return 0; @@ -565,7 +568,7 @@ static int m920x_tda8275_61_tuner_attach(struct dvb_usb_adapter *adap) static int m920x_fmd1216me_tuner_attach(struct dvb_usb_adapter *adap) { - dvb_attach(simple_tuner_attach, adap->fe, + dvb_attach(simple_tuner_attach, adap->fe_adap[0].fe, &adap->dev->i2c_adap, 0x61, TUNER_PHILIPS_FMD1216ME_MK3); return 0; @@ -807,6 +810,9 @@ static struct dvb_usb_device_properties megasky_properties = { .identify_state = m920x_identify_state, .num_adapters = 1, .adapter = {{ + .num_frontends = 1, + .fe = {{ + .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, @@ -827,6 +833,7 @@ static struct dvb_usb_device_properties megasky_properties = { } } }, + }}, }}, .i2c_algo = &m920x_i2c_algo, @@ -851,6 +858,9 @@ static struct dvb_usb_device_properties digivox_mini_ii_properties = { .identify_state = m920x_identify_state, .num_adapters = 1, .adapter = {{ + .num_frontends = 1, + .fe = {{ + .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, @@ -871,6 +881,7 @@ static struct dvb_usb_device_properties digivox_mini_ii_properties = { } } }, + }}, }}, .i2c_algo = &m920x_i2c_algo, @@ -910,6 +921,9 @@ static struct dvb_usb_device_properties tvwalkertwin_properties = { .identify_state = m920x_identify_state, .num_adapters = 2, .adapter = {{ + .num_frontends = 1, + .fe = {{ + .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, @@ -929,7 +943,11 @@ static struct dvb_usb_device_properties tvwalkertwin_properties = { .buffersize = 512, } } + }}, }},{ + .num_frontends = 1, + .fe = {{ + .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, @@ -949,6 +967,7 @@ static struct dvb_usb_device_properties tvwalkertwin_properties = { .buffersize = 512, } } + }}, }, }}, .i2c_algo = &m920x_i2c_algo, @@ -974,6 +993,8 @@ static struct dvb_usb_device_properties dposh_properties = { .identify_state = m920x_identify_state, .num_adapters = 1, .adapter = {{ + .num_frontends = 1, + .fe = {{ /* Hardware pid filters don't work with this device/firmware */ .frontend_attach = m920x_mt352_frontend_attach, @@ -989,6 +1010,7 @@ static struct dvb_usb_device_properties dposh_properties = { } } }, + }}, }}, .i2c_algo = &m920x_i2c_algo, @@ -1019,6 +1041,9 @@ static struct dvb_usb_device_properties pinnacle_pctv310e_properties = { .identify_state = m920x_identify_state, .num_adapters = 1, .adapter = {{ + .num_frontends = 1, + .fe = {{ + .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, @@ -1041,6 +1066,7 @@ static struct dvb_usb_device_properties pinnacle_pctv310e_properties = { } } }, + }}, } }, .i2c_algo = &m920x_i2c_algo, diff --git a/drivers/media/dvb/dvb-usb/mxl111sf-demod.c b/drivers/media/dvb/dvb-usb/mxl111sf-demod.c new file mode 100644 index 000000000000..d1f58371c711 --- /dev/null +++ b/drivers/media/dvb/dvb-usb/mxl111sf-demod.c @@ -0,0 +1,614 @@ +/* + * mxl111sf-demod.c - driver for the MaxLinear MXL111SF DVB-T demodulator + * + * Copyright (C) 2010 Michael Krufky <mkrufky@kernellabs.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. + */ + +#include "mxl111sf-demod.h" +#include "mxl111sf-reg.h" + +/* debug */ +static int mxl111sf_demod_debug; +module_param_named(debug, mxl111sf_demod_debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able))."); + +#define mxl_dbg(fmt, arg...) \ + if (mxl111sf_demod_debug) \ + mxl_printk(KERN_DEBUG, fmt, ##arg) + +/* ------------------------------------------------------------------------ */ + +struct mxl111sf_demod_state { + struct mxl111sf_state *mxl_state; + + struct mxl111sf_demod_config *cfg; + + struct dvb_frontend fe; +}; + +/* ------------------------------------------------------------------------ */ + +static int mxl111sf_demod_read_reg(struct mxl111sf_demod_state *state, + u8 addr, u8 *data) +{ + return (state->cfg->read_reg) ? + state->cfg->read_reg(state->mxl_state, addr, data) : + -EINVAL; +} + +static int mxl111sf_demod_write_reg(struct mxl111sf_demod_state *state, + u8 addr, u8 data) +{ + return (state->cfg->write_reg) ? + state->cfg->write_reg(state->mxl_state, addr, data) : + -EINVAL; +} + +static +int mxl111sf_demod_program_regs(struct mxl111sf_demod_state *state, + struct mxl111sf_reg_ctrl_info *ctrl_reg_info) +{ + return (state->cfg->program_regs) ? + state->cfg->program_regs(state->mxl_state, ctrl_reg_info) : + -EINVAL; +} + +/* ------------------------------------------------------------------------ */ +/* TPS */ + +static +int mxl1x1sf_demod_get_tps_code_rate(struct mxl111sf_demod_state *state, + fe_code_rate_t *code_rate) +{ + u8 val; + int ret = mxl111sf_demod_read_reg(state, V6_CODE_RATE_TPS_REG, &val); + /* bit<2:0> - 000:1/2, 001:2/3, 010:3/4, 011:5/6, 100:7/8 */ + if (mxl_fail(ret)) + goto fail; + + switch (val & V6_CODE_RATE_TPS_MASK) { + case 0: + *code_rate = FEC_1_2; + break; + case 1: + *code_rate = FEC_2_3; + break; + case 2: + *code_rate = FEC_3_4; + break; + case 3: + *code_rate = FEC_5_6; + break; + case 4: + *code_rate = FEC_7_8; + break; + } +fail: + return ret; +} + +static +int mxl1x1sf_demod_get_tps_constellation(struct mxl111sf_demod_state *state, + fe_modulation_t *constellation) +{ + u8 val; + int ret = mxl111sf_demod_read_reg(state, V6_MODORDER_TPS_REG, &val); + /* Constellation, 00 : QPSK, 01 : 16QAM, 10:64QAM */ + if (mxl_fail(ret)) + goto fail; + + switch ((val & V6_PARAM_CONSTELLATION_MASK) >> 4) { + case 0: + *constellation = QPSK; + break; + case 1: + *constellation = QAM_16; + break; + case 2: + *constellation = QAM_64; + break; + } +fail: + return ret; +} + +static +int mxl1x1sf_demod_get_tps_guard_fft_mode(struct mxl111sf_demod_state *state, + fe_transmit_mode_t *fft_mode) +{ + u8 val; + int ret = mxl111sf_demod_read_reg(state, V6_MODE_TPS_REG, &val); + /* FFT Mode, 00:2K, 01:8K, 10:4K */ + if (mxl_fail(ret)) + goto fail; + + switch ((val & V6_PARAM_FFT_MODE_MASK) >> 2) { + case 0: + *fft_mode = TRANSMISSION_MODE_2K; + break; + case 1: + *fft_mode = TRANSMISSION_MODE_8K; + break; + case 2: + *fft_mode = TRANSMISSION_MODE_4K; + break; + } +fail: + return ret; +} + +static +int mxl1x1sf_demod_get_tps_guard_interval(struct mxl111sf_demod_state *state, + fe_guard_interval_t *guard) +{ + u8 val; + int ret = mxl111sf_demod_read_reg(state, V6_CP_TPS_REG, &val); + /* 00:1/32, 01:1/16, 10:1/8, 11:1/4 */ + if (mxl_fail(ret)) + goto fail; + + switch ((val & V6_PARAM_GI_MASK) >> 4) { + case 0: + *guard = GUARD_INTERVAL_1_32; + break; + case 1: + *guard = GUARD_INTERVAL_1_16; + break; + case 2: + *guard = GUARD_INTERVAL_1_8; + break; + case 3: + *guard = GUARD_INTERVAL_1_4; + break; + } +fail: + return ret; +} + +static +int mxl1x1sf_demod_get_tps_hierarchy(struct mxl111sf_demod_state *state, + fe_hierarchy_t *hierarchy) +{ + u8 val; + int ret = mxl111sf_demod_read_reg(state, V6_TPS_HIERACHY_REG, &val); + /* bit<6:4> - 000:Non hierarchy, 001:1, 010:2, 011:4 */ + if (mxl_fail(ret)) + goto fail; + + switch ((val & V6_TPS_HIERARCHY_INFO_MASK) >> 6) { + case 0: + *hierarchy = HIERARCHY_NONE; + break; + case 1: + *hierarchy = HIERARCHY_1; + break; + case 2: + *hierarchy = HIERARCHY_2; + break; + case 3: + *hierarchy = HIERARCHY_4; + break; + } +fail: + return ret; +} + +/* ------------------------------------------------------------------------ */ +/* LOCKS */ + +static +int mxl1x1sf_demod_get_sync_lock_status(struct mxl111sf_demod_state *state, + int *sync_lock) +{ + u8 val = 0; + int ret = mxl111sf_demod_read_reg(state, V6_SYNC_LOCK_REG, &val); + if (mxl_fail(ret)) + goto fail; + *sync_lock = (val & SYNC_LOCK_MASK) >> 4; +fail: + return ret; +} + +static +int mxl1x1sf_demod_get_rs_lock_status(struct mxl111sf_demod_state *state, + int *rs_lock) +{ + u8 val = 0; + int ret = mxl111sf_demod_read_reg(state, V6_RS_LOCK_DET_REG, &val); + if (mxl_fail(ret)) + goto fail; + *rs_lock = (val & RS_LOCK_DET_MASK) >> 3; +fail: + return ret; +} + +static +int mxl1x1sf_demod_get_tps_lock_status(struct mxl111sf_demod_state *state, + int *tps_lock) +{ + u8 val = 0; + int ret = mxl111sf_demod_read_reg(state, V6_TPS_LOCK_REG, &val); + if (mxl_fail(ret)) + goto fail; + *tps_lock = (val & V6_PARAM_TPS_LOCK_MASK) >> 6; +fail: + return ret; +} + +static +int mxl1x1sf_demod_get_fec_lock_status(struct mxl111sf_demod_state *state, + int *fec_lock) +{ + u8 val = 0; + int ret = mxl111sf_demod_read_reg(state, V6_IRQ_STATUS_REG, &val); + if (mxl_fail(ret)) + goto fail; + *fec_lock = (val & IRQ_MASK_FEC_LOCK) >> 4; +fail: + return ret; +} + +#if 0 +static +int mxl1x1sf_demod_get_cp_lock_status(struct mxl111sf_demod_state *state, + int *cp_lock) +{ + u8 val = 0; + int ret = mxl111sf_demod_read_reg(state, V6_CP_LOCK_DET_REG, &val); + if (mxl_fail(ret)) + goto fail; + *cp_lock = (val & V6_CP_LOCK_DET_MASK) >> 2; +fail: + return ret; +} +#endif + +static int mxl1x1sf_demod_reset_irq_status(struct mxl111sf_demod_state *state) +{ + return mxl111sf_demod_write_reg(state, 0x0e, 0xff); +} + +/* ------------------------------------------------------------------------ */ + +static int mxl111sf_demod_set_frontend(struct dvb_frontend *fe, + struct dvb_frontend_parameters *param) +{ + struct mxl111sf_demod_state *state = fe->demodulator_priv; + int ret = 0; + + struct mxl111sf_reg_ctrl_info phy_pll_patch[] = { + {0x00, 0xff, 0x01}, /* change page to 1 */ + {0x40, 0xff, 0x05}, + {0x40, 0xff, 0x01}, + {0x41, 0xff, 0xca}, + {0x41, 0xff, 0xc0}, + {0x00, 0xff, 0x00}, /* change page to 0 */ + {0, 0, 0} + }; + + mxl_dbg("()"); + + if (fe->ops.tuner_ops.set_params) { + ret = fe->ops.tuner_ops.set_params(fe, param); + if (mxl_fail(ret)) + goto fail; + msleep(50); + } + ret = mxl111sf_demod_program_regs(state, phy_pll_patch); + mxl_fail(ret); + msleep(50); + ret = mxl1x1sf_demod_reset_irq_status(state); + mxl_fail(ret); + msleep(100); +fail: + return ret; +} + +/* ------------------------------------------------------------------------ */ + +#if 0 +/* resets TS Packet error count */ +/* After setting 7th bit of V5_PER_COUNT_RESET_REG, it should be reset to 0. */ +static +int mxl1x1sf_demod_reset_packet_error_count(struct mxl111sf_demod_state *state) +{ + struct mxl111sf_reg_ctrl_info reset_per_count[] = { + {0x20, 0x01, 0x01}, + {0x20, 0x01, 0x00}, + {0, 0, 0} + }; + return mxl111sf_demod_program_regs(state, reset_per_count); +} +#endif + +/* returns TS Packet error count */ +/* PER Count = FEC_PER_COUNT * (2 ** (FEC_PER_SCALE * 4)) */ +static int mxl111sf_demod_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + struct mxl111sf_demod_state *state = fe->demodulator_priv; + u32 fec_per_count, fec_per_scale; + u8 val; + int ret; + + *ucblocks = 0; + + /* FEC_PER_COUNT Register */ + ret = mxl111sf_demod_read_reg(state, V6_FEC_PER_COUNT_REG, &val); + if (mxl_fail(ret)) + goto fail; + + fec_per_count = val; + + /* FEC_PER_SCALE Register */ + ret = mxl111sf_demod_read_reg(state, V6_FEC_PER_SCALE_REG, &val); + if (mxl_fail(ret)) + goto fail; + + val &= V6_FEC_PER_SCALE_MASK; + val *= 4; + + fec_per_scale = 1 << val; + + fec_per_count *= fec_per_scale; + + *ucblocks = fec_per_count; +fail: + return ret; +} + +#ifdef MXL111SF_DEMOD_ENABLE_CALCULATIONS +/* FIXME: leaving this enabled breaks the build on some architectures, + * and we shouldn't have any floating point math in the kernel, anyway. + * + * These macros need to be re-written, but it's harmless to simply + * return zero for now. */ +#define CALCULATE_BER(avg_errors, count) \ + ((u32)(avg_errors * 4)/(count*64*188*8)) +#define CALCULATE_SNR(data) \ + ((u32)((10 * (u32)data / 64) - 2.5)) +#else +#define CALCULATE_BER(avg_errors, count) 0 +#define CALCULATE_SNR(data) 0 +#endif + +static int mxl111sf_demod_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct mxl111sf_demod_state *state = fe->demodulator_priv; + u8 val1, val2, val3; + int ret; + + *ber = 0; + + ret = mxl111sf_demod_read_reg(state, V6_RS_AVG_ERRORS_LSB_REG, &val1); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_demod_read_reg(state, V6_RS_AVG_ERRORS_MSB_REG, &val2); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_demod_read_reg(state, V6_N_ACCUMULATE_REG, &val3); + if (mxl_fail(ret)) + goto fail; + + *ber = CALCULATE_BER((val1 | (val2 << 8)), val3); +fail: + return ret; +} + +static int mxl111sf_demod_calc_snr(struct mxl111sf_demod_state *state, + u16 *snr) +{ + u8 val1, val2; + int ret; + + *snr = 0; + + ret = mxl111sf_demod_read_reg(state, V6_SNR_RB_LSB_REG, &val1); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_demod_read_reg(state, V6_SNR_RB_MSB_REG, &val2); + if (mxl_fail(ret)) + goto fail; + + *snr = CALCULATE_SNR(val1 | ((val2 & 0x03) << 8)); +fail: + return ret; +} + +static int mxl111sf_demod_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct mxl111sf_demod_state *state = fe->demodulator_priv; + + int ret = mxl111sf_demod_calc_snr(state, snr); + if (mxl_fail(ret)) + goto fail; + + *snr /= 10; /* 0.1 dB */ +fail: + return ret; +} + +static int mxl111sf_demod_read_status(struct dvb_frontend *fe, + fe_status_t *status) +{ + struct mxl111sf_demod_state *state = fe->demodulator_priv; + int ret, locked, cr_lock, sync_lock, fec_lock; + + *status = 0; + + ret = mxl1x1sf_demod_get_rs_lock_status(state, &locked); + if (mxl_fail(ret)) + goto fail; + ret = mxl1x1sf_demod_get_tps_lock_status(state, &cr_lock); + if (mxl_fail(ret)) + goto fail; + ret = mxl1x1sf_demod_get_sync_lock_status(state, &sync_lock); + if (mxl_fail(ret)) + goto fail; + ret = mxl1x1sf_demod_get_fec_lock_status(state, &fec_lock); + if (mxl_fail(ret)) + goto fail; + + if (locked) + *status |= FE_HAS_SIGNAL; + if (cr_lock) + *status |= FE_HAS_CARRIER; + if (sync_lock) + *status |= FE_HAS_SYNC; + if (fec_lock) /* false positives? */ + *status |= FE_HAS_VITERBI; + + if ((locked) && (cr_lock) && (sync_lock)) + *status |= FE_HAS_LOCK; +fail: + return ret; +} + +static int mxl111sf_demod_read_signal_strength(struct dvb_frontend *fe, + u16 *signal_strength) +{ + struct mxl111sf_demod_state *state = fe->demodulator_priv; + fe_modulation_t constellation; + u16 snr; + + mxl111sf_demod_calc_snr(state, &snr); + mxl1x1sf_demod_get_tps_constellation(state, &constellation); + + switch (constellation) { + case QPSK: + *signal_strength = (snr >= 1300) ? + min(65535, snr * 44) : snr * 38; + break; + case QAM_16: + *signal_strength = (snr >= 1500) ? + min(65535, snr * 38) : snr * 33; + break; + case QAM_64: + *signal_strength = (snr >= 2000) ? + min(65535, snr * 29) : snr * 25; + break; + default: + *signal_strength = 0; + return -EINVAL; + } + + return 0; +} + +static int mxl111sf_demod_get_frontend(struct dvb_frontend *fe, + struct dvb_frontend_parameters *p) +{ + struct mxl111sf_demod_state *state = fe->demodulator_priv; + + mxl_dbg("()"); +#if 0 + p->inversion = /* FIXME */ ? INVERSION_ON : INVERSION_OFF; +#endif + if (fe->ops.tuner_ops.get_bandwidth) + fe->ops.tuner_ops.get_bandwidth(fe, &p->u.ofdm.bandwidth); + if (fe->ops.tuner_ops.get_frequency) + fe->ops.tuner_ops.get_frequency(fe, &p->frequency); + mxl1x1sf_demod_get_tps_code_rate(state, &p->u.ofdm.code_rate_HP); + mxl1x1sf_demod_get_tps_code_rate(state, &p->u.ofdm.code_rate_LP); + mxl1x1sf_demod_get_tps_constellation(state, &p->u.ofdm.constellation); + mxl1x1sf_demod_get_tps_guard_fft_mode(state, + &p->u.ofdm.transmission_mode); + mxl1x1sf_demod_get_tps_guard_interval(state, + &p->u.ofdm.guard_interval); + mxl1x1sf_demod_get_tps_hierarchy(state, + &p->u.ofdm.hierarchy_information); + + return 0; +} + +static +int mxl111sf_demod_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *tune) +{ + tune->min_delay_ms = 1000; + return 0; +} + +static void mxl111sf_demod_release(struct dvb_frontend *fe) +{ + struct mxl111sf_demod_state *state = fe->demodulator_priv; + mxl_dbg("()"); + kfree(state); + fe->demodulator_priv = NULL; +} + +static struct dvb_frontend_ops mxl111sf_demod_ops = { + + .info = { + .name = "MaxLinear MxL111SF DVB-T demodulator", + .type = FE_OFDM, + .frequency_min = 177000000, + .frequency_max = 858000000, + .frequency_stepsize = 166666, + .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | + FE_CAN_QAM_AUTO | + FE_CAN_HIERARCHY_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_RECOVER + }, + .release = mxl111sf_demod_release, +#if 0 + .init = mxl111sf_init, + .i2c_gate_ctrl = mxl111sf_i2c_gate_ctrl, +#endif + .set_frontend = mxl111sf_demod_set_frontend, + .get_frontend = mxl111sf_demod_get_frontend, + .get_tune_settings = mxl111sf_demod_get_tune_settings, + .read_status = mxl111sf_demod_read_status, + .read_signal_strength = mxl111sf_demod_read_signal_strength, + .read_ber = mxl111sf_demod_read_ber, + .read_snr = mxl111sf_demod_read_snr, + .read_ucblocks = mxl111sf_demod_read_ucblocks, +}; + +struct dvb_frontend *mxl111sf_demod_attach(struct mxl111sf_state *mxl_state, + struct mxl111sf_demod_config *cfg) +{ + struct mxl111sf_demod_state *state = NULL; + + mxl_dbg("()"); + + state = kzalloc(sizeof(struct mxl111sf_demod_state), GFP_KERNEL); + if (state == NULL) + return NULL; + + state->mxl_state = mxl_state; + state->cfg = cfg; + + memcpy(&state->fe.ops, &mxl111sf_demod_ops, + sizeof(struct dvb_frontend_ops)); + + state->fe.demodulator_priv = state; + return &state->fe; +} +EXPORT_SYMBOL_GPL(mxl111sf_demod_attach); + +MODULE_DESCRIPTION("MaxLinear MxL111SF DVB-T demodulator driver"); +MODULE_AUTHOR("Michael Krufky <mkrufky@kernellabs.com>"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("0.1"); + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/dvb/dvb-usb/mxl111sf-demod.h b/drivers/media/dvb/dvb-usb/mxl111sf-demod.h new file mode 100644 index 000000000000..432706ae5274 --- /dev/null +++ b/drivers/media/dvb/dvb-usb/mxl111sf-demod.h @@ -0,0 +1,55 @@ +/* + * mxl111sf-demod.h - driver for the MaxLinear MXL111SF DVB-T demodulator + * + * Copyright (C) 2010 Michael Krufky <mkrufky@kernellabs.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 __MXL111SF_DEMOD_H__ +#define __MXL111SF_DEMOD_H__ + +#include "dvb_frontend.h" +#include "mxl111sf.h" + +struct mxl111sf_demod_config { + int (*read_reg)(struct mxl111sf_state *state, u8 addr, u8 *data); + int (*write_reg)(struct mxl111sf_state *state, u8 addr, u8 data); + int (*program_regs)(struct mxl111sf_state *state, + struct mxl111sf_reg_ctrl_info *ctrl_reg_info); +}; + +#if defined(CONFIG_DVB_USB_MXL111SF) || \ + (defined(CONFIG_DVB_USB_MXL111SF_MODULE) && defined(MODULE)) +extern +struct dvb_frontend *mxl111sf_demod_attach(struct mxl111sf_state *mxl_state, + struct mxl111sf_demod_config *cfg); +#else +static inline +struct dvb_frontend *mxl111sf_demod_attach(struct mxl111sf_state *mxl_state, + struct mxl111sf_demod_config *cfg) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif /* CONFIG_DVB_USB_MXL111SF */ + +#endif /* __MXL111SF_DEMOD_H__ */ + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/dvb/dvb-usb/mxl111sf-gpio.c b/drivers/media/dvb/dvb-usb/mxl111sf-gpio.c new file mode 100644 index 000000000000..e4121cb8f5ef --- /dev/null +++ b/drivers/media/dvb/dvb-usb/mxl111sf-gpio.c @@ -0,0 +1,763 @@ +/* + * mxl111sf-gpio.c - driver for the MaxLinear MXL111SF + * + * Copyright (C) 2010 Michael Krufky <mkrufky@kernellabs.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. + */ + +#include "mxl111sf-gpio.h" +#include "mxl111sf-i2c.h" +#include "mxl111sf.h" + +/* ------------------------------------------------------------------------- */ + +#define MXL_GPIO_MUX_REG_0 0x84 +#define MXL_GPIO_MUX_REG_1 0x89 +#define MXL_GPIO_MUX_REG_2 0x82 + +#define MXL_GPIO_DIR_INPUT 0 +#define MXL_GPIO_DIR_OUTPUT 1 + + +static int mxl111sf_set_gpo_state(struct mxl111sf_state *state, u8 pin, u8 val) +{ + int ret; + u8 tmp; + + mxl_debug_adv("(%d, %d)", pin, val); + + if ((pin > 0) && (pin < 8)) { + ret = mxl111sf_read_reg(state, 0x19, &tmp); + if (mxl_fail(ret)) + goto fail; + tmp &= ~(1 << (pin - 1)); + tmp |= (val << (pin - 1)); + ret = mxl111sf_write_reg(state, 0x19, tmp); + if (mxl_fail(ret)) + goto fail; + } else if (pin <= 10) { + if (pin == 0) + pin += 7; + ret = mxl111sf_read_reg(state, 0x30, &tmp); + if (mxl_fail(ret)) + goto fail; + tmp &= ~(1 << (pin - 3)); + tmp |= (val << (pin - 3)); + ret = mxl111sf_write_reg(state, 0x30, tmp); + if (mxl_fail(ret)) + goto fail; + } else + ret = -EINVAL; +fail: + return ret; +} + +static int mxl111sf_get_gpi_state(struct mxl111sf_state *state, u8 pin, u8 *val) +{ + int ret; + u8 tmp; + + mxl_debug("(0x%02x)", pin); + + *val = 0; + + switch (pin) { + case 0: + case 1: + case 2: + case 3: + ret = mxl111sf_read_reg(state, 0x23, &tmp); + if (mxl_fail(ret)) + goto fail; + *val = (tmp >> (pin + 4)) & 0x01; + break; + case 4: + case 5: + case 6: + case 7: + ret = mxl111sf_read_reg(state, 0x2f, &tmp); + if (mxl_fail(ret)) + goto fail; + *val = (tmp >> pin) & 0x01; + break; + case 8: + case 9: + case 10: + ret = mxl111sf_read_reg(state, 0x22, &tmp); + if (mxl_fail(ret)) + goto fail; + *val = (tmp >> (pin - 3)) & 0x01; + break; + default: + return -EINVAL; /* invalid pin */ + } +fail: + return ret; +} + +struct mxl_gpio_cfg { + u8 pin; + u8 dir; + u8 val; +}; + +static int mxl111sf_config_gpio_pins(struct mxl111sf_state *state, + struct mxl_gpio_cfg *gpio_cfg) +{ + int ret; + u8 tmp; + + mxl_debug_adv("(%d, %d)", gpio_cfg->pin, gpio_cfg->dir); + + switch (gpio_cfg->pin) { + case 0: + case 1: + case 2: + case 3: + ret = mxl111sf_read_reg(state, MXL_GPIO_MUX_REG_0, &tmp); + if (mxl_fail(ret)) + goto fail; + tmp &= ~(1 << (gpio_cfg->pin + 4)); + tmp |= (gpio_cfg->dir << (gpio_cfg->pin + 4)); + ret = mxl111sf_write_reg(state, MXL_GPIO_MUX_REG_0, tmp); + if (mxl_fail(ret)) + goto fail; + break; + case 4: + case 5: + case 6: + case 7: + ret = mxl111sf_read_reg(state, MXL_GPIO_MUX_REG_1, &tmp); + if (mxl_fail(ret)) + goto fail; + tmp &= ~(1 << gpio_cfg->pin); + tmp |= (gpio_cfg->dir << gpio_cfg->pin); + ret = mxl111sf_write_reg(state, MXL_GPIO_MUX_REG_1, tmp); + if (mxl_fail(ret)) + goto fail; + break; + case 8: + case 9: + case 10: + ret = mxl111sf_read_reg(state, MXL_GPIO_MUX_REG_2, &tmp); + if (mxl_fail(ret)) + goto fail; + tmp &= ~(1 << (gpio_cfg->pin - 3)); + tmp |= (gpio_cfg->dir << (gpio_cfg->pin - 3)); + ret = mxl111sf_write_reg(state, MXL_GPIO_MUX_REG_2, tmp); + if (mxl_fail(ret)) + goto fail; + break; + default: + return -EINVAL; /* invalid pin */ + } + + ret = (MXL_GPIO_DIR_OUTPUT == gpio_cfg->dir) ? + mxl111sf_set_gpo_state(state, + gpio_cfg->pin, gpio_cfg->val) : + mxl111sf_get_gpi_state(state, + gpio_cfg->pin, &gpio_cfg->val); + mxl_fail(ret); +fail: + return ret; +} + +static int mxl111sf_hw_do_set_gpio(struct mxl111sf_state *state, + int gpio, int direction, int val) +{ + struct mxl_gpio_cfg gpio_config = { + .pin = gpio, + .dir = direction, + .val = val, + }; + + mxl_debug("(%d, %d, %d)", gpio, direction, val); + + return mxl111sf_config_gpio_pins(state, &gpio_config); +} + +/* ------------------------------------------------------------------------- */ + +#define PIN_MUX_MPEG_MODE_MASK 0x40 /* 0x17 <6> */ +#define PIN_MUX_MPEG_PAR_EN_MASK 0x01 /* 0x18 <0> */ +#define PIN_MUX_MPEG_SER_EN_MASK 0x02 /* 0x18 <1> */ +#define PIN_MUX_MPG_IN_MUX_MASK 0x80 /* 0x3D <7> */ +#define PIN_MUX_BT656_ENABLE_MASK 0x04 /* 0x12 <2> */ +#define PIN_MUX_I2S_ENABLE_MASK 0x40 /* 0x15 <6> */ +#define PIN_MUX_SPI_MODE_MASK 0x10 /* 0x3D <4> */ +#define PIN_MUX_MCLK_EN_CTRL_MASK 0x10 /* 0x82 <4> */ +#define PIN_MUX_MPSYN_EN_CTRL_MASK 0x20 /* 0x82 <5> */ +#define PIN_MUX_MDVAL_EN_CTRL_MASK 0x40 /* 0x82 <6> */ +#define PIN_MUX_MPERR_EN_CTRL_MASK 0x80 /* 0x82 <7> */ +#define PIN_MUX_MDAT_EN_0_MASK 0x10 /* 0x84 <4> */ +#define PIN_MUX_MDAT_EN_1_MASK 0x20 /* 0x84 <5> */ +#define PIN_MUX_MDAT_EN_2_MASK 0x40 /* 0x84 <6> */ +#define PIN_MUX_MDAT_EN_3_MASK 0x80 /* 0x84 <7> */ +#define PIN_MUX_MDAT_EN_4_MASK 0x10 /* 0x89 <4> */ +#define PIN_MUX_MDAT_EN_5_MASK 0x20 /* 0x89 <5> */ +#define PIN_MUX_MDAT_EN_6_MASK 0x40 /* 0x89 <6> */ +#define PIN_MUX_MDAT_EN_7_MASK 0x80 /* 0x89 <7> */ + +int mxl111sf_config_pin_mux_modes(struct mxl111sf_state *state, + enum mxl111sf_mux_config pin_mux_config) +{ + u8 r12, r15, r17, r18, r3D, r82, r84, r89; + int ret; + + mxl_debug("(%d)", pin_mux_config); + + ret = mxl111sf_read_reg(state, 0x17, &r17); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_read_reg(state, 0x18, &r18); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_read_reg(state, 0x12, &r12); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_read_reg(state, 0x15, &r15); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_read_reg(state, 0x82, &r82); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_read_reg(state, 0x84, &r84); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_read_reg(state, 0x89, &r89); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_read_reg(state, 0x3D, &r3D); + if (mxl_fail(ret)) + goto fail; + + switch (pin_mux_config) { + case PIN_MUX_TS_OUT_PARALLEL: + /* mpeg_mode = 1 */ + r17 |= PIN_MUX_MPEG_MODE_MASK; + /* mpeg_par_en = 1 */ + r18 |= PIN_MUX_MPEG_PAR_EN_MASK; + /* mpeg_ser_en = 0 */ + r18 &= ~PIN_MUX_MPEG_SER_EN_MASK; + /* mpg_in_mux = 0 */ + r3D &= ~PIN_MUX_MPG_IN_MUX_MASK; + /* bt656_enable = 0 */ + r12 &= ~PIN_MUX_BT656_ENABLE_MASK; + /* i2s_enable = 0 */ + r15 &= ~PIN_MUX_I2S_ENABLE_MASK; + /* spi_mode = 0 */ + r3D &= ~PIN_MUX_SPI_MODE_MASK; + /* mclk_en_ctrl = 1 */ + r82 |= PIN_MUX_MCLK_EN_CTRL_MASK; + /* mperr_en_ctrl = 1 */ + r82 |= PIN_MUX_MPERR_EN_CTRL_MASK; + /* mdval_en_ctrl = 1 */ + r82 |= PIN_MUX_MDVAL_EN_CTRL_MASK; + /* mpsyn_en_ctrl = 1 */ + r82 |= PIN_MUX_MPSYN_EN_CTRL_MASK; + /* mdat_en_ctrl[3:0] = 0xF */ + r84 |= 0xF0; + /* mdat_en_ctrl[7:4] = 0xF */ + r89 |= 0xF0; + break; + case PIN_MUX_TS_OUT_SERIAL: + /* mpeg_mode = 1 */ + r17 |= PIN_MUX_MPEG_MODE_MASK; + /* mpeg_par_en = 0 */ + r18 &= ~PIN_MUX_MPEG_PAR_EN_MASK; + /* mpeg_ser_en = 1 */ + r18 |= PIN_MUX_MPEG_SER_EN_MASK; + /* mpg_in_mux = 0 */ + r3D &= ~PIN_MUX_MPG_IN_MUX_MASK; + /* bt656_enable = 0 */ + r12 &= ~PIN_MUX_BT656_ENABLE_MASK; + /* i2s_enable = 0 */ + r15 &= ~PIN_MUX_I2S_ENABLE_MASK; + /* spi_mode = 0 */ + r3D &= ~PIN_MUX_SPI_MODE_MASK; + /* mclk_en_ctrl = 1 */ + r82 |= PIN_MUX_MCLK_EN_CTRL_MASK; + /* mperr_en_ctrl = 1 */ + r82 |= PIN_MUX_MPERR_EN_CTRL_MASK; + /* mdval_en_ctrl = 1 */ + r82 |= PIN_MUX_MDVAL_EN_CTRL_MASK; + /* mpsyn_en_ctrl = 1 */ + r82 |= PIN_MUX_MPSYN_EN_CTRL_MASK; + /* mdat_en_ctrl[3:0] = 0xF */ + r84 |= 0xF0; + /* mdat_en_ctrl[7:4] = 0xF */ + r89 |= 0xF0; + break; + case PIN_MUX_GPIO_MODE: + /* mpeg_mode = 0 */ + r17 &= ~PIN_MUX_MPEG_MODE_MASK; + /* mpeg_par_en = 0 */ + r18 &= ~PIN_MUX_MPEG_PAR_EN_MASK; + /* mpeg_ser_en = 0 */ + r18 &= ~PIN_MUX_MPEG_SER_EN_MASK; + /* mpg_in_mux = 0 */ + r3D &= ~PIN_MUX_MPG_IN_MUX_MASK; + /* bt656_enable = 0 */ + r12 &= ~PIN_MUX_BT656_ENABLE_MASK; + /* i2s_enable = 0 */ + r15 &= ~PIN_MUX_I2S_ENABLE_MASK; + /* spi_mode = 0 */ + r3D &= ~PIN_MUX_SPI_MODE_MASK; + /* mclk_en_ctrl = 0 */ + r82 &= ~PIN_MUX_MCLK_EN_CTRL_MASK; + /* mperr_en_ctrl = 0 */ + r82 &= ~PIN_MUX_MPERR_EN_CTRL_MASK; + /* mdval_en_ctrl = 0 */ + r82 &= ~PIN_MUX_MDVAL_EN_CTRL_MASK; + /* mpsyn_en_ctrl = 0 */ + r82 &= ~PIN_MUX_MPSYN_EN_CTRL_MASK; + /* mdat_en_ctrl[3:0] = 0x0 */ + r84 &= 0x0F; + /* mdat_en_ctrl[7:4] = 0x0 */ + r89 &= 0x0F; + break; + case PIN_MUX_TS_SERIAL_IN_MODE_0: + /* mpeg_mode = 0 */ + r17 &= ~PIN_MUX_MPEG_MODE_MASK; + /* mpeg_par_en = 0 */ + r18 &= ~PIN_MUX_MPEG_PAR_EN_MASK; + /* mpeg_ser_en = 1 */ + r18 |= PIN_MUX_MPEG_SER_EN_MASK; + /* mpg_in_mux = 0 */ + r3D &= ~PIN_MUX_MPG_IN_MUX_MASK; + /* bt656_enable = 0 */ + r12 &= ~PIN_MUX_BT656_ENABLE_MASK; + /* i2s_enable = 0 */ + r15 &= ~PIN_MUX_I2S_ENABLE_MASK; + /* spi_mode = 0 */ + r3D &= ~PIN_MUX_SPI_MODE_MASK; + /* mclk_en_ctrl = 0 */ + r82 &= ~PIN_MUX_MCLK_EN_CTRL_MASK; + /* mperr_en_ctrl = 0 */ + r82 &= ~PIN_MUX_MPERR_EN_CTRL_MASK; + /* mdval_en_ctrl = 0 */ + r82 &= ~PIN_MUX_MDVAL_EN_CTRL_MASK; + /* mpsyn_en_ctrl = 0 */ + r82 &= ~PIN_MUX_MPSYN_EN_CTRL_MASK; + /* mdat_en_ctrl[3:0] = 0x0 */ + r84 &= 0x0F; + /* mdat_en_ctrl[7:4] = 0x0 */ + r89 &= 0x0F; + break; + case PIN_MUX_TS_SERIAL_IN_MODE_1: + /* mpeg_mode = 0 */ + r17 &= ~PIN_MUX_MPEG_MODE_MASK; + /* mpeg_par_en = 0 */ + r18 &= ~PIN_MUX_MPEG_PAR_EN_MASK; + /* mpeg_ser_en = 1 */ + r18 |= PIN_MUX_MPEG_SER_EN_MASK; + /* mpg_in_mux = 1 */ + r3D |= PIN_MUX_MPG_IN_MUX_MASK; + /* bt656_enable = 0 */ + r12 &= ~PIN_MUX_BT656_ENABLE_MASK; + /* i2s_enable = 0 */ + r15 &= ~PIN_MUX_I2S_ENABLE_MASK; + /* spi_mode = 0 */ + r3D &= ~PIN_MUX_SPI_MODE_MASK; + /* mclk_en_ctrl = 0 */ + r82 &= ~PIN_MUX_MCLK_EN_CTRL_MASK; + /* mperr_en_ctrl = 0 */ + r82 &= ~PIN_MUX_MPERR_EN_CTRL_MASK; + /* mdval_en_ctrl = 0 */ + r82 &= ~PIN_MUX_MDVAL_EN_CTRL_MASK; + /* mpsyn_en_ctrl = 0 */ + r82 &= ~PIN_MUX_MPSYN_EN_CTRL_MASK; + /* mdat_en_ctrl[3:0] = 0x0 */ + r84 &= 0x0F; + /* mdat_en_ctrl[7:4] = 0x0 */ + r89 &= 0x0F; + break; + case PIN_MUX_TS_SPI_IN_MODE_1: + /* mpeg_mode = 0 */ + r17 &= ~PIN_MUX_MPEG_MODE_MASK; + /* mpeg_par_en = 0 */ + r18 &= ~PIN_MUX_MPEG_PAR_EN_MASK; + /* mpeg_ser_en = 1 */ + r18 |= PIN_MUX_MPEG_SER_EN_MASK; + /* mpg_in_mux = 1 */ + r3D |= PIN_MUX_MPG_IN_MUX_MASK; + /* bt656_enable = 0 */ + r12 &= ~PIN_MUX_BT656_ENABLE_MASK; + /* i2s_enable = 1 */ + r15 |= PIN_MUX_I2S_ENABLE_MASK; + /* spi_mode = 1 */ + r3D |= PIN_MUX_SPI_MODE_MASK; + /* mclk_en_ctrl = 0 */ + r82 &= ~PIN_MUX_MCLK_EN_CTRL_MASK; + /* mperr_en_ctrl = 0 */ + r82 &= ~PIN_MUX_MPERR_EN_CTRL_MASK; + /* mdval_en_ctrl = 0 */ + r82 &= ~PIN_MUX_MDVAL_EN_CTRL_MASK; + /* mpsyn_en_ctrl = 0 */ + r82 &= ~PIN_MUX_MPSYN_EN_CTRL_MASK; + /* mdat_en_ctrl[3:0] = 0x0 */ + r84 &= 0x0F; + /* mdat_en_ctrl[7:4] = 0x0 */ + r89 &= 0x0F; + break; + case PIN_MUX_TS_SPI_IN_MODE_0: + /* mpeg_mode = 0 */ + r17 &= ~PIN_MUX_MPEG_MODE_MASK; + /* mpeg_par_en = 0 */ + r18 &= ~PIN_MUX_MPEG_PAR_EN_MASK; + /* mpeg_ser_en = 1 */ + r18 |= PIN_MUX_MPEG_SER_EN_MASK; + /* mpg_in_mux = 0 */ + r3D &= ~PIN_MUX_MPG_IN_MUX_MASK; + /* bt656_enable = 0 */ + r12 &= ~PIN_MUX_BT656_ENABLE_MASK; + /* i2s_enable = 1 */ + r15 |= PIN_MUX_I2S_ENABLE_MASK; + /* spi_mode = 1 */ + r3D |= PIN_MUX_SPI_MODE_MASK; + /* mclk_en_ctrl = 0 */ + r82 &= ~PIN_MUX_MCLK_EN_CTRL_MASK; + /* mperr_en_ctrl = 0 */ + r82 &= ~PIN_MUX_MPERR_EN_CTRL_MASK; + /* mdval_en_ctrl = 0 */ + r82 &= ~PIN_MUX_MDVAL_EN_CTRL_MASK; + /* mpsyn_en_ctrl = 0 */ + r82 &= ~PIN_MUX_MPSYN_EN_CTRL_MASK; + /* mdat_en_ctrl[3:0] = 0x0 */ + r84 &= 0x0F; + /* mdat_en_ctrl[7:4] = 0x0 */ + r89 &= 0x0F; + break; + case PIN_MUX_TS_PARALLEL_IN: + /* mpeg_mode = 0 */ + r17 &= ~PIN_MUX_MPEG_MODE_MASK; + /* mpeg_par_en = 1 */ + r18 |= PIN_MUX_MPEG_PAR_EN_MASK; + /* mpeg_ser_en = 0 */ + r18 &= ~PIN_MUX_MPEG_SER_EN_MASK; + /* mpg_in_mux = 0 */ + r3D &= ~PIN_MUX_MPG_IN_MUX_MASK; + /* bt656_enable = 0 */ + r12 &= ~PIN_MUX_BT656_ENABLE_MASK; + /* i2s_enable = 0 */ + r15 &= ~PIN_MUX_I2S_ENABLE_MASK; + /* spi_mode = 0 */ + r3D &= ~PIN_MUX_SPI_MODE_MASK; + /* mclk_en_ctrl = 0 */ + r82 &= ~PIN_MUX_MCLK_EN_CTRL_MASK; + /* mperr_en_ctrl = 0 */ + r82 &= ~PIN_MUX_MPERR_EN_CTRL_MASK; + /* mdval_en_ctrl = 0 */ + r82 &= ~PIN_MUX_MDVAL_EN_CTRL_MASK; + /* mpsyn_en_ctrl = 0 */ + r82 &= ~PIN_MUX_MPSYN_EN_CTRL_MASK; + /* mdat_en_ctrl[3:0] = 0x0 */ + r84 &= 0x0F; + /* mdat_en_ctrl[7:4] = 0x0 */ + r89 &= 0x0F; + break; + case PIN_MUX_BT656_I2S_MODE: + /* mpeg_mode = 0 */ + r17 &= ~PIN_MUX_MPEG_MODE_MASK; + /* mpeg_par_en = 0 */ + r18 &= ~PIN_MUX_MPEG_PAR_EN_MASK; + /* mpeg_ser_en = 0 */ + r18 &= ~PIN_MUX_MPEG_SER_EN_MASK; + /* mpg_in_mux = 0 */ + r3D &= ~PIN_MUX_MPG_IN_MUX_MASK; + /* bt656_enable = 1 */ + r12 |= PIN_MUX_BT656_ENABLE_MASK; + /* i2s_enable = 1 */ + r15 |= PIN_MUX_I2S_ENABLE_MASK; + /* spi_mode = 0 */ + r3D &= ~PIN_MUX_SPI_MODE_MASK; + /* mclk_en_ctrl = 0 */ + r82 &= ~PIN_MUX_MCLK_EN_CTRL_MASK; + /* mperr_en_ctrl = 0 */ + r82 &= ~PIN_MUX_MPERR_EN_CTRL_MASK; + /* mdval_en_ctrl = 0 */ + r82 &= ~PIN_MUX_MDVAL_EN_CTRL_MASK; + /* mpsyn_en_ctrl = 0 */ + r82 &= ~PIN_MUX_MPSYN_EN_CTRL_MASK; + /* mdat_en_ctrl[3:0] = 0x0 */ + r84 &= 0x0F; + /* mdat_en_ctrl[7:4] = 0x0 */ + r89 &= 0x0F; + break; + case PIN_MUX_DEFAULT: + default: + /* mpeg_mode = 1 */ + r17 |= PIN_MUX_MPEG_MODE_MASK; + /* mpeg_par_en = 0 */ + r18 &= ~PIN_MUX_MPEG_PAR_EN_MASK; + /* mpeg_ser_en = 0 */ + r18 &= ~PIN_MUX_MPEG_SER_EN_MASK; + /* mpg_in_mux = 0 */ + r3D &= ~PIN_MUX_MPG_IN_MUX_MASK; + /* bt656_enable = 0 */ + r12 &= ~PIN_MUX_BT656_ENABLE_MASK; + /* i2s_enable = 0 */ + r15 &= ~PIN_MUX_I2S_ENABLE_MASK; + /* spi_mode = 0 */ + r3D &= ~PIN_MUX_SPI_MODE_MASK; + /* mclk_en_ctrl = 0 */ + r82 &= ~PIN_MUX_MCLK_EN_CTRL_MASK; + /* mperr_en_ctrl = 0 */ + r82 &= ~PIN_MUX_MPERR_EN_CTRL_MASK; + /* mdval_en_ctrl = 0 */ + r82 &= ~PIN_MUX_MDVAL_EN_CTRL_MASK; + /* mpsyn_en_ctrl = 0 */ + r82 &= ~PIN_MUX_MPSYN_EN_CTRL_MASK; + /* mdat_en_ctrl[3:0] = 0x0 */ + r84 &= 0x0F; + /* mdat_en_ctrl[7:4] = 0x0 */ + r89 &= 0x0F; + break; + } + + ret = mxl111sf_write_reg(state, 0x17, r17); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_write_reg(state, 0x18, r18); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_write_reg(state, 0x12, r12); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_write_reg(state, 0x15, r15); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_write_reg(state, 0x82, r82); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_write_reg(state, 0x84, r84); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_write_reg(state, 0x89, r89); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_write_reg(state, 0x3D, r3D); + if (mxl_fail(ret)) + goto fail; +fail: + return ret; +} + +/* ------------------------------------------------------------------------- */ + +static int mxl111sf_hw_set_gpio(struct mxl111sf_state *state, int gpio, int val) +{ + return mxl111sf_hw_do_set_gpio(state, gpio, MXL_GPIO_DIR_OUTPUT, val); +} + +static int mxl111sf_hw_gpio_initialize(struct mxl111sf_state *state) +{ + u8 gpioval = 0x07; /* write protect enabled, signal LEDs off */ + int i, ret; + + mxl_debug("()"); + + for (i = 3; i < 8; i++) { + ret = mxl111sf_hw_set_gpio(state, i, (gpioval >> i) & 0x01); + if (mxl_fail(ret)) + break; + } + + return ret; +} + +#define PCA9534_I2C_ADDR (0x40 >> 1) +static int pca9534_set_gpio(struct mxl111sf_state *state, int gpio, int val) +{ + u8 w[2] = { 1, 0 }; + u8 r = 0; + struct i2c_msg msg[] = { + { .addr = PCA9534_I2C_ADDR, + .flags = 0, .buf = w, .len = 1 }, + { .addr = PCA9534_I2C_ADDR, + .flags = I2C_M_RD, .buf = &r, .len = 1 }, + }; + + mxl_debug("(%d, %d)", gpio, val); + + /* read current GPIO levels from flip-flop */ + i2c_transfer(&state->d->i2c_adap, msg, 2); + + /* prepare write buffer with current GPIO levels */ + msg[0].len = 2; +#if 0 + w[0] = 1; +#endif + w[1] = r; + + /* clear the desired GPIO */ + w[1] &= ~(1 << gpio); + + /* set the desired GPIO value */ + w[1] |= ((val ? 1 : 0) << gpio); + + /* write new GPIO levels to flip-flop */ + i2c_transfer(&state->d->i2c_adap, &msg[0], 1); + + return 0; +} + +static int pca9534_init_port_expander(struct mxl111sf_state *state) +{ + u8 w[2] = { 1, 0x07 }; /* write protect enabled, signal LEDs off */ + + struct i2c_msg msg = { + .addr = PCA9534_I2C_ADDR, + .flags = 0, .buf = w, .len = 2 + }; + + mxl_debug("()"); + + i2c_transfer(&state->d->i2c_adap, &msg, 1); + + /* configure all pins as outputs */ + w[0] = 3; + w[1] = 0; + + i2c_transfer(&state->d->i2c_adap, &msg, 1); + + return 0; +} + +int mxl111sf_set_gpio(struct mxl111sf_state *state, int gpio, int val) +{ + mxl_debug("(%d, %d)", gpio, val); + + switch (state->gpio_port_expander) { + default: + mxl_printk(KERN_ERR, + "gpio_port_expander undefined, assuming PCA9534"); + /* fall-thru */ + case mxl111sf_PCA9534: + return pca9534_set_gpio(state, gpio, val); + case mxl111sf_gpio_hw: + return mxl111sf_hw_set_gpio(state, gpio, val); + } +} + +static int mxl111sf_probe_port_expander(struct mxl111sf_state *state) +{ + int ret; + u8 w = 1; + u8 r = 0; + struct i2c_msg msg[] = { + { .flags = 0, .buf = &w, .len = 1 }, + { .flags = I2C_M_RD, .buf = &r, .len = 1 }, + }; + + mxl_debug("()"); + + msg[0].addr = 0x70 >> 1; + msg[1].addr = 0x70 >> 1; + + /* read current GPIO levels from flip-flop */ + ret = i2c_transfer(&state->d->i2c_adap, msg, 2); + if (ret == 2) { + state->port_expander_addr = msg[0].addr; + state->gpio_port_expander = mxl111sf_PCA9534; + mxl_debug("found port expander at 0x%02x", + state->port_expander_addr); + return 0; + } + + msg[0].addr = 0x40 >> 1; + msg[1].addr = 0x40 >> 1; + + ret = i2c_transfer(&state->d->i2c_adap, msg, 2); + if (ret == 2) { + state->port_expander_addr = msg[0].addr; + state->gpio_port_expander = mxl111sf_PCA9534; + mxl_debug("found port expander at 0x%02x", + state->port_expander_addr); + return 0; + } + state->port_expander_addr = 0xff; + state->gpio_port_expander = mxl111sf_gpio_hw; + mxl_debug("using hardware gpio"); + return 0; +} + +int mxl111sf_init_port_expander(struct mxl111sf_state *state) +{ + mxl_debug("()"); + + if (0x00 == state->port_expander_addr) + mxl111sf_probe_port_expander(state); + + switch (state->gpio_port_expander) { + default: + mxl_printk(KERN_ERR, + "gpio_port_expander undefined, assuming PCA9534"); + /* fall-thru */ + case mxl111sf_PCA9534: + return pca9534_init_port_expander(state); + case mxl111sf_gpio_hw: + return mxl111sf_hw_gpio_initialize(state); + } +} + +/* ------------------------------------------------------------------------ */ + +int mxl111sf_gpio_mode_switch(struct mxl111sf_state *state, unsigned int mode) +{ +/* GPO: + * 3 - ATSC/MH# | 1 = ATSC transport, 0 = MH transport | default 0 + * 4 - ATSC_RST## | 1 = ATSC enable, 0 = ATSC Reset | default 0 + * 5 - ATSC_EN | 1 = ATSC power enable, 0 = ATSC power off | default 0 + * 6 - MH_RESET# | 1 = MH enable, 0 = MH Reset | default 0 + * 7 - MH_EN | 1 = MH power enable, 0 = MH power off | default 0 + */ + mxl_debug("(%d)", mode); + + switch (mode) { + case MXL111SF_GPIO_MOD_MH: + mxl111sf_set_gpio(state, 4, 0); + mxl111sf_set_gpio(state, 5, 0); + msleep(50); + mxl111sf_set_gpio(state, 7, 1); + msleep(50); + mxl111sf_set_gpio(state, 6, 1); + msleep(50); + + mxl111sf_set_gpio(state, 3, 0); + break; + case MXL111SF_GPIO_MOD_ATSC: + mxl111sf_set_gpio(state, 6, 0); + mxl111sf_set_gpio(state, 7, 0); + msleep(50); + mxl111sf_set_gpio(state, 5, 1); + msleep(50); + mxl111sf_set_gpio(state, 4, 1); + msleep(50); + mxl111sf_set_gpio(state, 3, 1); + break; + default: /* DVBT / STANDBY */ + mxl111sf_init_port_expander(state); + break; + } + return 0; +} + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/dvb/dvb-usb/mxl111sf-gpio.h b/drivers/media/dvb/dvb-usb/mxl111sf-gpio.h new file mode 100644 index 000000000000..0220f54299a5 --- /dev/null +++ b/drivers/media/dvb/dvb-usb/mxl111sf-gpio.h @@ -0,0 +1,56 @@ +/* + * mxl111sf-gpio.h - driver for the MaxLinear MXL111SF + * + * Copyright (C) 2010 Michael Krufky <mkrufky@kernellabs.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 _DVB_USB_MXL111SF_GPIO_H_ +#define _DVB_USB_MXL111SF_GPIO_H_ + +#include "mxl111sf.h" + +int mxl111sf_set_gpio(struct mxl111sf_state *state, int gpio, int val); +int mxl111sf_init_port_expander(struct mxl111sf_state *state); + +#define MXL111SF_GPIO_MOD_DVBT 0 +#define MXL111SF_GPIO_MOD_MH 1 +#define MXL111SF_GPIO_MOD_ATSC 2 +int mxl111sf_gpio_mode_switch(struct mxl111sf_state *state, unsigned int mode); + +enum mxl111sf_mux_config { + PIN_MUX_DEFAULT = 0, + PIN_MUX_TS_OUT_PARALLEL, + PIN_MUX_TS_OUT_SERIAL, + PIN_MUX_GPIO_MODE, + PIN_MUX_TS_SERIAL_IN_MODE_0, + PIN_MUX_TS_SERIAL_IN_MODE_1, + PIN_MUX_TS_SPI_IN_MODE_0, + PIN_MUX_TS_SPI_IN_MODE_1, + PIN_MUX_TS_PARALLEL_IN, + PIN_MUX_BT656_I2S_MODE, +}; + +int mxl111sf_config_pin_mux_modes(struct mxl111sf_state *state, + enum mxl111sf_mux_config pin_mux_config); + +#endif /* _DVB_USB_MXL111SF_GPIO_H_ */ + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/dvb/dvb-usb/mxl111sf-i2c.c b/drivers/media/dvb/dvb-usb/mxl111sf-i2c.c new file mode 100644 index 000000000000..34434557ef65 --- /dev/null +++ b/drivers/media/dvb/dvb-usb/mxl111sf-i2c.c @@ -0,0 +1,850 @@ +/* + * mxl111sf-i2c.c - driver for the MaxLinear MXL111SF + * + * Copyright (C) 2010 Michael Krufky <mkrufky@kernellabs.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. + */ + +#include "mxl111sf-i2c.h" +#include "mxl111sf.h" + +/* SW-I2C ----------------------------------------------------------------- */ + +#define SW_I2C_ADDR 0x1a +#define SW_I2C_EN 0x02 +#define SW_SCL_OUT 0x04 +#define SW_SDA_OUT 0x08 +#define SW_SDA_IN 0x04 + +#define SW_I2C_BUSY_ADDR 0x2f +#define SW_I2C_BUSY 0x02 + +static int mxl111sf_i2c_bitbang_sendbyte(struct mxl111sf_state *state, + u8 byte) +{ + int i, ret; + u8 data = 0; + + mxl_i2c("(0x%02x)", byte); + + ret = mxl111sf_read_reg(state, SW_I2C_BUSY_ADDR, &data); + if (mxl_fail(ret)) + goto fail; + + for (i = 0; i < 8; i++) { + + data = (byte & (0x80 >> i)) ? SW_SDA_OUT : 0; + + ret = mxl111sf_write_reg(state, SW_I2C_ADDR, + 0x10 | SW_I2C_EN | data); + if (mxl_fail(ret)) + goto fail; + + ret = mxl111sf_write_reg(state, SW_I2C_ADDR, + 0x10 | SW_I2C_EN | data | SW_SCL_OUT); + if (mxl_fail(ret)) + goto fail; + + ret = mxl111sf_write_reg(state, SW_I2C_ADDR, + 0x10 | SW_I2C_EN | data); + if (mxl_fail(ret)) + goto fail; + } + + /* last bit was 0 so we need to release SDA */ + if (!(byte & 1)) { + ret = mxl111sf_write_reg(state, SW_I2C_ADDR, + 0x10 | SW_I2C_EN | SW_SDA_OUT); + if (mxl_fail(ret)) + goto fail; + } + + /* CLK high for ACK readback */ + ret = mxl111sf_write_reg(state, SW_I2C_ADDR, + 0x10 | SW_I2C_EN | SW_SCL_OUT | SW_SDA_OUT); + if (mxl_fail(ret)) + goto fail; + + ret = mxl111sf_read_reg(state, SW_I2C_BUSY_ADDR, &data); + if (mxl_fail(ret)) + goto fail; + + /* drop the CLK after getting ACK, SDA will go high right away */ + ret = mxl111sf_write_reg(state, SW_I2C_ADDR, + 0x10 | SW_I2C_EN | SW_SDA_OUT); + if (mxl_fail(ret)) + goto fail; + + if (data & SW_SDA_IN) + ret = -EIO; +fail: + return ret; +} + +static int mxl111sf_i2c_bitbang_recvbyte(struct mxl111sf_state *state, + u8 *pbyte) +{ + int i, ret; + u8 byte = 0; + u8 data = 0; + + mxl_i2c("()"); + + *pbyte = 0; + + ret = mxl111sf_write_reg(state, SW_I2C_ADDR, + 0x10 | SW_I2C_EN | SW_SDA_OUT); + if (mxl_fail(ret)) + goto fail; + + for (i = 0; i < 8; i++) { + ret = mxl111sf_write_reg(state, SW_I2C_ADDR, + 0x10 | SW_I2C_EN | + SW_SCL_OUT | SW_SDA_OUT); + if (mxl_fail(ret)) + goto fail; + + ret = mxl111sf_read_reg(state, SW_I2C_BUSY_ADDR, &data); + if (mxl_fail(ret)) + goto fail; + + if (data & SW_SDA_IN) + byte |= (0x80 >> i); + + ret = mxl111sf_write_reg(state, SW_I2C_ADDR, + 0x10 | SW_I2C_EN | SW_SDA_OUT); + if (mxl_fail(ret)) + goto fail; + } + *pbyte = byte; +fail: + return ret; +} + +static int mxl111sf_i2c_start(struct mxl111sf_state *state) +{ + int ret; + + mxl_i2c("()"); + + ret = mxl111sf_write_reg(state, SW_I2C_ADDR, + 0x10 | SW_I2C_EN | SW_SCL_OUT | SW_SDA_OUT); + if (mxl_fail(ret)) + goto fail; + + ret = mxl111sf_write_reg(state, SW_I2C_ADDR, + 0x10 | SW_I2C_EN | SW_SCL_OUT); + if (mxl_fail(ret)) + goto fail; + + ret = mxl111sf_write_reg(state, SW_I2C_ADDR, + 0x10 | SW_I2C_EN); /* start */ + mxl_fail(ret); +fail: + return ret; +} + +static int mxl111sf_i2c_stop(struct mxl111sf_state *state) +{ + int ret; + + mxl_i2c("()"); + + ret = mxl111sf_write_reg(state, SW_I2C_ADDR, + 0x10 | SW_I2C_EN); /* stop */ + if (mxl_fail(ret)) + goto fail; + + ret = mxl111sf_write_reg(state, SW_I2C_ADDR, + 0x10 | SW_I2C_EN | SW_SCL_OUT); + if (mxl_fail(ret)) + goto fail; + + ret = mxl111sf_write_reg(state, SW_I2C_ADDR, + 0x10 | SW_I2C_EN | SW_SCL_OUT | SW_SDA_OUT); + if (mxl_fail(ret)) + goto fail; + + ret = mxl111sf_write_reg(state, SW_I2C_ADDR, + 0x10 | SW_SCL_OUT | SW_SDA_OUT); + mxl_fail(ret); +fail: + return ret; +} + +static int mxl111sf_i2c_ack(struct mxl111sf_state *state) +{ + int ret; + u8 b = 0; + + mxl_i2c("()"); + + ret = mxl111sf_read_reg(state, SW_I2C_BUSY_ADDR, &b); + if (mxl_fail(ret)) + goto fail; + + ret = mxl111sf_write_reg(state, SW_I2C_ADDR, + 0x10 | SW_I2C_EN); + if (mxl_fail(ret)) + goto fail; + + /* pull SDA low */ + ret = mxl111sf_write_reg(state, SW_I2C_ADDR, + 0x10 | SW_I2C_EN | SW_SCL_OUT); + if (mxl_fail(ret)) + goto fail; + + ret = mxl111sf_write_reg(state, SW_I2C_ADDR, + 0x10 | SW_I2C_EN | SW_SDA_OUT); + mxl_fail(ret); +fail: + return ret; +} + +static int mxl111sf_i2c_nack(struct mxl111sf_state *state) +{ + int ret; + + mxl_i2c("()"); + + /* SDA high to signal last byte read from slave */ + ret = mxl111sf_write_reg(state, SW_I2C_ADDR, + 0x10 | SW_I2C_EN | SW_SCL_OUT | SW_SDA_OUT); + if (mxl_fail(ret)) + goto fail; + + ret = mxl111sf_write_reg(state, SW_I2C_ADDR, + 0x10 | SW_I2C_EN | SW_SDA_OUT); + mxl_fail(ret); +fail: + return ret; +} + +/* ------------------------------------------------------------------------ */ + +static int mxl111sf_i2c_sw_xfer_msg(struct mxl111sf_state *state, + struct i2c_msg *msg) +{ + int i, ret; + + mxl_i2c("()"); + + if (msg->flags & I2C_M_RD) { + + ret = mxl111sf_i2c_start(state); + if (mxl_fail(ret)) + goto fail; + + ret = mxl111sf_i2c_bitbang_sendbyte(state, + (msg->addr << 1) | 0x01); + if (mxl_fail(ret)) { + mxl111sf_i2c_stop(state); + goto fail; + } + + for (i = 0; i < msg->len; i++) { + ret = mxl111sf_i2c_bitbang_recvbyte(state, + &msg->buf[i]); + if (mxl_fail(ret)) { + mxl111sf_i2c_stop(state); + goto fail; + } + + if (i < msg->len - 1) + mxl111sf_i2c_ack(state); + } + + mxl111sf_i2c_nack(state); + + ret = mxl111sf_i2c_stop(state); + if (mxl_fail(ret)) + goto fail; + + } else { + + ret = mxl111sf_i2c_start(state); + if (mxl_fail(ret)) + goto fail; + + ret = mxl111sf_i2c_bitbang_sendbyte(state, + (msg->addr << 1) & 0xfe); + if (mxl_fail(ret)) { + mxl111sf_i2c_stop(state); + goto fail; + } + + for (i = 0; i < msg->len; i++) { + ret = mxl111sf_i2c_bitbang_sendbyte(state, + msg->buf[i]); + if (mxl_fail(ret)) { + mxl111sf_i2c_stop(state); + goto fail; + } + } + + /* FIXME: we only want to do this on the last transaction */ + mxl111sf_i2c_stop(state); + } +fail: + return ret; +} + +/* HW-I2C ----------------------------------------------------------------- */ + +#define USB_WRITE_I2C_CMD 0x99 +#define USB_READ_I2C_CMD 0xdd +#define USB_END_I2C_CMD 0xfe + +#define USB_WRITE_I2C_CMD_LEN 26 +#define USB_READ_I2C_CMD_LEN 24 + +#define I2C_MUX_REG 0x30 +#define I2C_CONTROL_REG 0x00 +#define I2C_SLAVE_ADDR_REG 0x08 +#define I2C_DATA_REG 0x0c +#define I2C_INT_STATUS_REG 0x10 + +static int mxl111sf_i2c_send_data(struct mxl111sf_state *state, + u8 index, u8 *wdata) +{ + int ret = mxl111sf_ctrl_msg(state->d, wdata[0], + &wdata[1], 25, NULL, 0); + mxl_fail(ret); + + return ret; +} + +static int mxl111sf_i2c_get_data(struct mxl111sf_state *state, + u8 index, u8 *wdata, u8 *rdata) +{ + int ret = mxl111sf_ctrl_msg(state->d, wdata[0], + &wdata[1], 25, rdata, 24); + mxl_fail(ret); + + return ret; +} + +static u8 mxl111sf_i2c_check_status(struct mxl111sf_state *state) +{ + u8 status = 0; + u8 buf[26]; + + mxl_i2c_adv("()"); + + buf[0] = USB_READ_I2C_CMD; + buf[1] = 0x00; + + buf[2] = I2C_INT_STATUS_REG; + buf[3] = 0x00; + buf[4] = 0x00; + + buf[5] = USB_END_I2C_CMD; + + mxl111sf_i2c_get_data(state, 0, buf, buf); + + if (buf[1] & 0x04) + status = 1; + + return status; +} + +static u8 mxl111sf_i2c_check_fifo(struct mxl111sf_state *state) +{ + u8 status = 0; + u8 buf[26]; + + mxl_i2c("()"); + + buf[0] = USB_READ_I2C_CMD; + buf[1] = 0x00; + + buf[2] = I2C_MUX_REG; + buf[3] = 0x00; + buf[4] = 0x00; + + buf[5] = I2C_INT_STATUS_REG; + buf[6] = 0x00; + buf[7] = 0x00; + buf[8] = USB_END_I2C_CMD; + + mxl111sf_i2c_get_data(state, 0, buf, buf); + + if (0x08 == (buf[1] & 0x08)) + status = 1; + + if ((buf[5] & 0x02) == 0x02) + mxl_i2c("(buf[5] & 0x02) == 0x02"); /* FIXME */ + + return status; +} + +static int mxl111sf_i2c_readagain(struct mxl111sf_state *state, + u8 count, u8 *rbuf) +{ + u8 i2c_w_data[26]; + u8 i2c_r_data[24]; + u8 i = 0; + u8 fifo_status = 0; + int status = 0; + + mxl_i2c("read %d bytes", count); + + while ((fifo_status == 0) && (i++ < 5)) + fifo_status = mxl111sf_i2c_check_fifo(state); + + i2c_w_data[0] = 0xDD; + i2c_w_data[1] = 0x00; + + for (i = 2; i < 26; i++) + i2c_w_data[i] = 0xFE; + + for (i = 0; i < count; i++) { + i2c_w_data[2+(i*3)] = 0x0C; + i2c_w_data[3+(i*3)] = 0x00; + i2c_w_data[4+(i*3)] = 0x00; + } + + mxl111sf_i2c_get_data(state, 0, i2c_w_data, i2c_r_data); + + /* Check for I2C NACK status */ + if (mxl111sf_i2c_check_status(state) == 1) { + mxl_i2c("error!"); + } else { + for (i = 0; i < count; i++) { + rbuf[i] = i2c_r_data[(i*3)+1]; + mxl_i2c("%02x\t %02x", + i2c_r_data[(i*3)+1], + i2c_r_data[(i*3)+2]); + } + + status = 1; + } + + return status; +} + +#define HWI2C400 1 +static int mxl111sf_i2c_hw_xfer_msg(struct mxl111sf_state *state, + struct i2c_msg *msg) +{ + int i, k, ret = 0; + u16 index = 0; + u8 buf[26]; + u8 i2c_r_data[24]; + u16 block_len; + u16 left_over_len; + u8 rd_status[8]; + u8 ret_status; + u8 readbuff[26]; + + mxl_i2c("addr: 0x%02x, read buff len: %d, write buff len: %d", + msg->addr, (msg->flags & I2C_M_RD) ? msg->len : 0, + (!(msg->flags & I2C_M_RD)) ? msg->len : 0); + + for (index = 0; index < 26; index++) + buf[index] = USB_END_I2C_CMD; + + /* command to indicate data payload is destined for I2C interface */ + buf[0] = USB_WRITE_I2C_CMD; + buf[1] = 0x00; + + /* enable I2C interface */ + buf[2] = I2C_MUX_REG; + buf[3] = 0x80; + buf[4] = 0x00; + + /* enable I2C interface */ + buf[5] = I2C_MUX_REG; + buf[6] = 0x81; + buf[7] = 0x00; + + /* set Timeout register on I2C interface */ + buf[8] = 0x14; + buf[9] = 0xff; + buf[10] = 0x00; +#if 0 + /* enable Interrupts on I2C interface */ + buf[8] = 0x24; + buf[9] = 0xF7; + buf[10] = 0x00; +#endif + buf[11] = 0x24; + buf[12] = 0xF7; + buf[13] = 0x00; + + ret = mxl111sf_i2c_send_data(state, 0, buf); + + /* write data on I2C bus */ + if (!(msg->flags & I2C_M_RD) && (msg->len > 0)) { + mxl_i2c("%d\t%02x", msg->len, msg->buf[0]); + + /* control register on I2C interface to initialize I2C bus */ + buf[2] = I2C_CONTROL_REG; + buf[3] = 0x5E; + buf[4] = (HWI2C400) ? 0x03 : 0x0D; + + /* I2C Slave device Address */ + buf[5] = I2C_SLAVE_ADDR_REG; + buf[6] = (msg->addr); + buf[7] = 0x00; + buf[8] = USB_END_I2C_CMD; + ret = mxl111sf_i2c_send_data(state, 0, buf); + + /* check for slave device status */ + if (mxl111sf_i2c_check_status(state) == 1) { + mxl_i2c("NACK writing slave address %02x", + msg->addr); + /* if NACK, stop I2C bus and exit */ + buf[2] = I2C_CONTROL_REG; + buf[3] = 0x4E; + buf[4] = (HWI2C400) ? 0x03 : 0x0D; + ret = -EIO; + goto exit; + } + + /* I2C interface can do I2C operations in block of 8 bytes of + I2C data. calculation to figure out number of blocks of i2c + data required to program */ + block_len = (msg->len / 8); + left_over_len = (msg->len % 8); + index = 0; + + mxl_i2c("block_len %d, left_over_len %d", + block_len, left_over_len); + + for (index = 0; index < block_len; index++) { + for (i = 0; i < 8; i++) { + /* write data on I2C interface */ + buf[2+(i*3)] = I2C_DATA_REG; + buf[3+(i*3)] = msg->buf[(index*8)+i]; + buf[4+(i*3)] = 0x00; + } + + ret = mxl111sf_i2c_send_data(state, 0, buf); + + /* check for I2C NACK status */ + if (mxl111sf_i2c_check_status(state) == 1) { + mxl_i2c("NACK writing slave address %02x", + msg->addr); + + /* if NACK, stop I2C bus and exit */ + buf[2] = I2C_CONTROL_REG; + buf[3] = 0x4E; + buf[4] = (HWI2C400) ? 0x03 : 0x0D; + ret = -EIO; + goto exit; + } + + } + + if (left_over_len) { + for (k = 0; k < 26; k++) + buf[k] = USB_END_I2C_CMD; + + buf[0] = 0x99; + buf[1] = 0x00; + + for (i = 0; i < left_over_len; i++) { + buf[2+(i*3)] = I2C_DATA_REG; + buf[3+(i*3)] = msg->buf[(index*8)+i]; + mxl_i2c("index = %d %d data %d", + index, i, msg->buf[(index*8)+i]); + buf[4+(i*3)] = 0x00; + } + ret = mxl111sf_i2c_send_data(state, 0, buf); + + /* check for I2C NACK status */ + if (mxl111sf_i2c_check_status(state) == 1) { + mxl_i2c("NACK writing slave address %02x", + msg->addr); + + /* if NACK, stop I2C bus and exit */ + buf[2] = I2C_CONTROL_REG; + buf[3] = 0x4E; + buf[4] = (HWI2C400) ? 0x03 : 0x0D; + ret = -EIO; + goto exit; + } + + } + + /* issue I2C STOP after write */ + buf[2] = I2C_CONTROL_REG; + buf[3] = 0x4E; + buf[4] = (HWI2C400) ? 0x03 : 0x0D; + + } + + /* read data from I2C bus */ + if ((msg->flags & I2C_M_RD) && (msg->len > 0)) { + mxl_i2c("read buf len %d", msg->len); + + /* command to indicate data payload is + destined for I2C interface */ + buf[2] = I2C_CONTROL_REG; + buf[3] = 0xDF; + buf[4] = (HWI2C400) ? 0x03 : 0x0D; + + /* I2C xfer length */ + buf[5] = 0x14; + buf[6] = (msg->len & 0xFF); + buf[7] = 0; + + /* I2C slave device Address */ + buf[8] = I2C_SLAVE_ADDR_REG; + buf[9] = msg->addr; + buf[10] = 0x00; + buf[11] = USB_END_I2C_CMD; + ret = mxl111sf_i2c_send_data(state, 0, buf); + + /* check for I2C NACK status */ + if (mxl111sf_i2c_check_status(state) == 1) { + mxl_i2c("NACK reading slave address %02x", + msg->addr); + + /* if NACK, stop I2C bus and exit */ + buf[2] = I2C_CONTROL_REG; + buf[3] = 0xC7; + buf[4] = (HWI2C400) ? 0x03 : 0x0D; + ret = -EIO; + goto exit; + } + + /* I2C interface can do I2C operations in block of 8 bytes of + I2C data. calculation to figure out number of blocks of + i2c data required to program */ + block_len = ((msg->len) / 8); + left_over_len = ((msg->len) % 8); + index = 0; + + mxl_i2c("block_len %d, left_over_len %d", + block_len, left_over_len); + + /* command to read data from I2C interface */ + buf[0] = USB_READ_I2C_CMD; + buf[1] = 0x00; + + for (index = 0; index < block_len; index++) { + /* setup I2C read request packet on I2C interface */ + for (i = 0; i < 8; i++) { + buf[2+(i*3)] = I2C_DATA_REG; + buf[3+(i*3)] = 0x00; + buf[4+(i*3)] = 0x00; + } + + ret = mxl111sf_i2c_get_data(state, 0, buf, i2c_r_data); + + /* check for I2C NACK status */ + if (mxl111sf_i2c_check_status(state) == 1) { + mxl_i2c("NACK reading slave address %02x", + msg->addr); + + /* if NACK, stop I2C bus and exit */ + buf[2] = I2C_CONTROL_REG; + buf[3] = 0xC7; + buf[4] = (HWI2C400) ? 0x03 : 0x0D; + ret = -EIO; + goto exit; + } + + /* copy data from i2c data payload to read buffer */ + for (i = 0; i < 8; i++) { + rd_status[i] = i2c_r_data[(i*3)+2]; + + if (rd_status[i] == 0x04) { + if (i < 7) { + mxl_i2c("i2c fifo empty!" + " @ %d", i); + msg->buf[(index*8)+i] = + i2c_r_data[(i*3)+1]; + /* read again */ + ret_status = + mxl111sf_i2c_readagain( + state, 8-(i+1), + readbuff); + if (ret_status == 1) { + for (k = 0; + k < 8-(i+1); + k++) { + + msg->buf[(index*8)+(k+i+1)] = + readbuff[k]; + mxl_i2c("read data: %02x\t %02x", + msg->buf[(index*8)+(k+i)], + (index*8)+(k+i)); + mxl_i2c("read data: %02x\t %02x", + msg->buf[(index*8)+(k+i+1)], + readbuff[k]); + + } + goto stop_copy; + } else { + mxl_i2c("readagain " + "ERROR!"); + } + } else { + msg->buf[(index*8)+i] = + i2c_r_data[(i*3)+1]; + } + } else { + msg->buf[(index*8)+i] = + i2c_r_data[(i*3)+1]; + } + } +stop_copy: + ; + + } + + if (left_over_len) { + for (k = 0; k < 26; k++) + buf[k] = USB_END_I2C_CMD; + + buf[0] = 0xDD; + buf[1] = 0x00; + + for (i = 0; i < left_over_len; i++) { + buf[2+(i*3)] = I2C_DATA_REG; + buf[3+(i*3)] = 0x00; + buf[4+(i*3)] = 0x00; + } + ret = mxl111sf_i2c_get_data(state, 0, buf, + i2c_r_data); + + /* check for I2C NACK status */ + if (mxl111sf_i2c_check_status(state) == 1) { + mxl_i2c("NACK reading slave address %02x", + msg->addr); + + /* if NACK, stop I2C bus and exit */ + buf[2] = I2C_CONTROL_REG; + buf[3] = 0xC7; + buf[4] = (HWI2C400) ? 0x03 : 0x0D; + ret = -EIO; + goto exit; + } + + for (i = 0; i < left_over_len; i++) { + msg->buf[(block_len*8)+i] = + i2c_r_data[(i*3)+1]; + mxl_i2c("read data: %02x\t %02x", + i2c_r_data[(i*3)+1], + i2c_r_data[(i*3)+2]); + } + } + + /* indicate I2C interface to issue NACK + after next I2C read op */ + buf[0] = USB_WRITE_I2C_CMD; + buf[1] = 0x00; + + /* control register */ + buf[2] = I2C_CONTROL_REG; + buf[3] = 0x17; + buf[4] = (HWI2C400) ? 0x03 : 0x0D; + + buf[5] = USB_END_I2C_CMD; + ret = mxl111sf_i2c_send_data(state, 0, buf); + + /* control register */ + buf[2] = I2C_CONTROL_REG; + buf[3] = 0xC7; + buf[4] = (HWI2C400) ? 0x03 : 0x0D; + + } +exit: + /* STOP and disable I2C MUX */ + buf[0] = USB_WRITE_I2C_CMD; + buf[1] = 0x00; + + /* de-initilize I2C BUS */ + buf[5] = USB_END_I2C_CMD; + mxl111sf_i2c_send_data(state, 0, buf); + + /* Control Register */ + buf[2] = I2C_CONTROL_REG; + buf[3] = 0xDF; + buf[4] = 0x03; + + /* disable I2C interface */ + buf[5] = I2C_MUX_REG; + buf[6] = 0x00; + buf[7] = 0x00; + + /* de-initilize I2C BUS */ + buf[8] = USB_END_I2C_CMD; + mxl111sf_i2c_send_data(state, 0, buf); + + /* disable I2C interface */ + buf[2] = I2C_MUX_REG; + buf[3] = 0x81; + buf[4] = 0x00; + + /* disable I2C interface */ + buf[5] = I2C_MUX_REG; + buf[6] = 0x00; + buf[7] = 0x00; + + /* disable I2C interface */ + buf[8] = I2C_MUX_REG; + buf[9] = 0x00; + buf[10] = 0x00; + + buf[11] = USB_END_I2C_CMD; + mxl111sf_i2c_send_data(state, 0, buf); + + return ret; +} + +/* ------------------------------------------------------------------------ */ + +int mxl111sf_i2c_xfer(struct i2c_adapter *adap, + struct i2c_msg msg[], int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + struct mxl111sf_state *state = d->priv; + int hwi2c = (state->chip_rev > MXL111SF_V6); + int i, ret; + + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + for (i = 0; i < num; i++) { + ret = (hwi2c) ? + mxl111sf_i2c_hw_xfer_msg(state, &msg[i]) : + mxl111sf_i2c_sw_xfer_msg(state, &msg[i]); + if (mxl_fail(ret)) { + mxl_debug_adv("failed with error %d on i2c " + "transaction %d of %d, %sing %d bytes " + "to/from 0x%02x", ret, i+1, num, + (msg[i].flags & I2C_M_RD) ? + "read" : "writ", + msg[i].len, msg[i].addr); + + break; + } + } + + mutex_unlock(&d->i2c_mutex); + + return i == num ? num : -EREMOTEIO; +} + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/dvb/dvb-usb/mxl111sf-i2c.h b/drivers/media/dvb/dvb-usb/mxl111sf-i2c.h new file mode 100644 index 000000000000..a57a45ffb9e4 --- /dev/null +++ b/drivers/media/dvb/dvb-usb/mxl111sf-i2c.h @@ -0,0 +1,35 @@ +/* + * mxl111sf-i2c.h - driver for the MaxLinear MXL111SF + * + * Copyright (C) 2010 Michael Krufky <mkrufky@kernellabs.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 _DVB_USB_MXL111SF_I2C_H_ +#define _DVB_USB_MXL111SF_I2C_H_ + +#include <linux/i2c.h> + +int mxl111sf_i2c_xfer(struct i2c_adapter *adap, + struct i2c_msg msg[], int num); + +#endif /* _DVB_USB_MXL111SF_I2C_H_ */ + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/dvb/dvb-usb/mxl111sf-phy.c b/drivers/media/dvb/dvb-usb/mxl111sf-phy.c new file mode 100644 index 000000000000..b741b3a7a325 --- /dev/null +++ b/drivers/media/dvb/dvb-usb/mxl111sf-phy.c @@ -0,0 +1,343 @@ +/* + * mxl111sf-phy.c - driver for the MaxLinear MXL111SF + * + * Copyright (C) 2010 Michael Krufky <mkrufky@kernellabs.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. + */ + +#include "mxl111sf-phy.h" +#include "mxl111sf-reg.h" + +int mxl111sf_init_tuner_demod(struct mxl111sf_state *state) +{ + struct mxl111sf_reg_ctrl_info mxl_111_overwrite_default[] = { + {0x07, 0xff, 0x0c}, + {0x58, 0xff, 0x9d}, + {0x09, 0xff, 0x00}, + {0x06, 0xff, 0x06}, + {0xc8, 0xff, 0x40}, /* ED_LE_WIN_OLD = 0 */ + {0x8d, 0x01, 0x01}, /* NEGATE_Q */ + {0x32, 0xff, 0xac}, /* DIG_RFREFSELECT = 12 */ + {0x42, 0xff, 0x43}, /* DIG_REG_AMP = 4 */ + {0x74, 0xff, 0xc4}, /* SSPUR_FS_PRIO = 4 */ + {0x71, 0xff, 0xe6}, /* SPUR_ROT_PRIO_VAL = 1 */ + {0x83, 0xff, 0x64}, /* INF_FILT1_THD_SC = 100 */ + {0x85, 0xff, 0x64}, /* INF_FILT2_THD_SC = 100 */ + {0x88, 0xff, 0xf0}, /* INF_THD = 240 */ + {0x6f, 0xf0, 0xb0}, /* DFE_DLY = 11 */ + {0x00, 0xff, 0x01}, /* Change to page 1 */ + {0x81, 0xff, 0x11}, /* DSM_FERR_BYPASS = 1 */ + {0xf4, 0xff, 0x07}, /* DIG_FREQ_CORR = 1 */ + {0xd4, 0x1f, 0x0f}, /* SPUR_TEST_NOISE_TH = 15 */ + {0xd6, 0xff, 0x0c}, /* SPUR_TEST_NOISE_PAPR = 12 */ + {0x00, 0xff, 0x00}, /* Change to page 0 */ + {0, 0, 0} + }; + + mxl_debug("()"); + + return mxl111sf_ctrl_program_regs(state, mxl_111_overwrite_default); +} + +int mxl1x1sf_soft_reset(struct mxl111sf_state *state) +{ + int ret; + mxl_debug("()"); + + ret = mxl111sf_write_reg(state, 0xff, 0x00); /* AIC */ + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_write_reg(state, 0x02, 0x01); /* get out of reset */ + mxl_fail(ret); +fail: + return ret; +} + +int mxl1x1sf_set_device_mode(struct mxl111sf_state *state, int mode) +{ + int ret; + + mxl_debug("(%s)", MXL_SOC_MODE == mode ? + "MXL_SOC_MODE" : "MXL_TUNER_MODE"); + + /* set device mode */ + ret = mxl111sf_write_reg(state, 0x03, + MXL_SOC_MODE == mode ? 0x01 : 0x00); + if (mxl_fail(ret)) + goto fail; + + ret = mxl111sf_write_reg_mask(state, + 0x7d, 0x40, MXL_SOC_MODE == mode ? + 0x00 : /* enable impulse noise filter, + INF_BYP = 0 */ + 0x40); /* disable impulse noise filter, + INF_BYP = 1 */ + if (mxl_fail(ret)) + goto fail; + + state->device_mode = mode; +fail: + return ret; +} + +/* power up tuner */ +int mxl1x1sf_top_master_ctrl(struct mxl111sf_state *state, int onoff) +{ + mxl_debug("(%d)", onoff); + + return mxl111sf_write_reg(state, 0x01, onoff ? 0x01 : 0x00); +} + +int mxl111sf_disable_656_port(struct mxl111sf_state *state) +{ + mxl_debug("()"); + + return mxl111sf_write_reg_mask(state, 0x12, 0x04, 0x00); +} + +int mxl111sf_enable_usb_output(struct mxl111sf_state *state) +{ + mxl_debug("()"); + + return mxl111sf_write_reg_mask(state, 0x17, 0x40, 0x00); +} + +/* initialize TSIF as input port of MxL1X1SF for MPEG2 data transfer */ +int mxl111sf_config_mpeg_in(struct mxl111sf_state *state, + unsigned int parallel_serial, + unsigned int msb_lsb_1st, + unsigned int clock_phase, + unsigned int mpeg_valid_pol, + unsigned int mpeg_sync_pol) +{ + int ret; + u8 mode, tmp; + + mxl_debug("(%u,%u,%u,%u,%u)", parallel_serial, msb_lsb_1st, + clock_phase, mpeg_valid_pol, mpeg_sync_pol); + + /* Enable PIN MUX */ + ret = mxl111sf_write_reg(state, V6_PIN_MUX_MODE_REG, V6_ENABLE_PIN_MUX); + mxl_fail(ret); + + /* Configure MPEG Clock phase */ + mxl111sf_read_reg(state, V6_MPEG_IN_CLK_INV_REG, &mode); + + if (clock_phase == TSIF_NORMAL) + mode &= ~V6_INVERTED_CLK_PHASE; + else + mode |= V6_INVERTED_CLK_PHASE; + + ret = mxl111sf_write_reg(state, V6_MPEG_IN_CLK_INV_REG, mode); + mxl_fail(ret); + + /* Configure data input mode, MPEG Valid polarity, MPEG Sync polarity + * Get current configuration */ + ret = mxl111sf_read_reg(state, V6_MPEG_IN_CTRL_REG, &mode); + mxl_fail(ret); + + /* Data Input mode */ + if (parallel_serial == TSIF_INPUT_PARALLEL) { + /* Disable serial mode */ + mode &= ~V6_MPEG_IN_DATA_SERIAL; + + /* Enable Parallel mode */ + mode |= V6_MPEG_IN_DATA_PARALLEL; + } else { + /* Disable Parallel mode */ + mode &= ~V6_MPEG_IN_DATA_PARALLEL; + + /* Enable Serial Mode */ + mode |= V6_MPEG_IN_DATA_SERIAL; + + /* If serial interface is chosen, configure + MSB or LSB order in transmission */ + ret = mxl111sf_read_reg(state, + V6_MPEG_INOUT_BIT_ORDER_CTRL_REG, + &tmp); + mxl_fail(ret); + + if (msb_lsb_1st == MPEG_SER_MSB_FIRST_ENABLED) + tmp |= V6_MPEG_SER_MSB_FIRST; + else + tmp &= ~V6_MPEG_SER_MSB_FIRST; + + ret = mxl111sf_write_reg(state, + V6_MPEG_INOUT_BIT_ORDER_CTRL_REG, + tmp); + mxl_fail(ret); + } + + /* MPEG Sync polarity */ + if (mpeg_sync_pol == TSIF_NORMAL) + mode &= ~V6_INVERTED_MPEG_SYNC; + else + mode |= V6_INVERTED_MPEG_SYNC; + + /* MPEG Valid polarity */ + if (mpeg_valid_pol == 0) + mode &= ~V6_INVERTED_MPEG_VALID; + else + mode |= V6_INVERTED_MPEG_VALID; + + ret = mxl111sf_write_reg(state, V6_MPEG_IN_CTRL_REG, mode); + mxl_fail(ret); + + return ret; +} + +int mxl111sf_init_i2s_port(struct mxl111sf_state *state, u8 sample_size) +{ + static struct mxl111sf_reg_ctrl_info init_i2s[] = { + {0x1b, 0xff, 0x1e}, /* pin mux mode, Choose 656/I2S input */ + {0x15, 0x60, 0x60}, /* Enable I2S */ + {0x17, 0xe0, 0x20}, /* Input, MPEG MODE USB, + Inverted 656 Clock, I2S_SOFT_RESET, + 0 : Normal operation, 1 : Reset State */ +#if 0 + {0x12, 0x01, 0x00}, /* AUDIO_IRQ_CLR (Overflow Indicator) */ +#endif + {0x00, 0xff, 0x02}, /* Change to Control Page */ + {0x26, 0x0d, 0x0d}, /* I2S_MODE & BT656_SRC_SEL for FPGA only */ + {0x00, 0xff, 0x00}, + {0, 0, 0} + }; + int ret; + + mxl_debug("(0x%02x)", sample_size); + + ret = mxl111sf_ctrl_program_regs(state, init_i2s); + if (mxl_fail(ret)) + goto fail; + + ret = mxl111sf_write_reg(state, V6_I2S_NUM_SAMPLES_REG, sample_size); + mxl_fail(ret); +fail: + return ret; +} + +int mxl111sf_disable_i2s_port(struct mxl111sf_state *state) +{ + static struct mxl111sf_reg_ctrl_info disable_i2s[] = { + {0x15, 0x40, 0x00}, + {0, 0, 0} + }; + + mxl_debug("()"); + + return mxl111sf_ctrl_program_regs(state, disable_i2s); +} + +int mxl111sf_config_i2s(struct mxl111sf_state *state, + u8 msb_start_pos, u8 data_width) +{ + int ret; + u8 tmp; + + mxl_debug("(0x%02x, 0x%02x)", msb_start_pos, data_width); + + ret = mxl111sf_read_reg(state, V6_I2S_STREAM_START_BIT_REG, &tmp); + if (mxl_fail(ret)) + goto fail; + + tmp &= 0xe0; + tmp |= msb_start_pos; + ret = mxl111sf_write_reg(state, V6_I2S_STREAM_START_BIT_REG, tmp); + if (mxl_fail(ret)) + goto fail; + + ret = mxl111sf_read_reg(state, V6_I2S_STREAM_END_BIT_REG, &tmp); + if (mxl_fail(ret)) + goto fail; + + tmp &= 0xe0; + tmp |= data_width; + ret = mxl111sf_write_reg(state, V6_I2S_STREAM_END_BIT_REG, tmp); + mxl_fail(ret); +fail: + return ret; +} + +int mxl111sf_config_spi(struct mxl111sf_state *state, int onoff) +{ + u8 val; + int ret; + + mxl_debug("(%d)", onoff); + + ret = mxl111sf_write_reg(state, 0x00, 0x02); + if (mxl_fail(ret)) + goto fail; + + ret = mxl111sf_read_reg(state, V8_SPI_MODE_REG, &val); + if (mxl_fail(ret)) + goto fail; + + if (onoff) + val |= 0x04; + else + val &= ~0x04; + + ret = mxl111sf_write_reg(state, V8_SPI_MODE_REG, val); + if (mxl_fail(ret)) + goto fail; + + ret = mxl111sf_write_reg(state, 0x00, 0x00); + mxl_fail(ret); +fail: + return ret; +} + +int mxl111sf_idac_config(struct mxl111sf_state *state, + u8 control_mode, u8 current_setting, + u8 current_value, u8 hysteresis_value) +{ + int ret; + u8 val; + /* current value will be set for both automatic & manual IDAC control */ + val = current_value; + + if (control_mode == IDAC_MANUAL_CONTROL) { + /* enable manual control of IDAC */ + val |= IDAC_MANUAL_CONTROL_BIT_MASK; + + if (current_setting == IDAC_CURRENT_SINKING_ENABLE) + /* enable current sinking in manual mode */ + val |= IDAC_CURRENT_SINKING_BIT_MASK; + else + /* disable current sinking in manual mode */ + val &= ~IDAC_CURRENT_SINKING_BIT_MASK; + } else { + /* disable manual control of IDAC */ + val &= ~IDAC_MANUAL_CONTROL_BIT_MASK; + + /* set hysteresis value reg: 0x0B<5:0> */ + ret = mxl111sf_write_reg(state, V6_IDAC_HYSTERESIS_REG, + (hysteresis_value & 0x3F)); + mxl_fail(ret); + } + + ret = mxl111sf_write_reg(state, V6_IDAC_SETTINGS_REG, val); + mxl_fail(ret); + + return ret; +} + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/dvb/dvb-usb/mxl111sf-phy.h b/drivers/media/dvb/dvb-usb/mxl111sf-phy.h new file mode 100644 index 000000000000..f0756071d347 --- /dev/null +++ b/drivers/media/dvb/dvb-usb/mxl111sf-phy.h @@ -0,0 +1,53 @@ +/* + * mxl111sf-phy.h - driver for the MaxLinear MXL111SF + * + * Copyright (C) 2010 Michael Krufky <mkrufky@kernellabs.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 _DVB_USB_MXL111SF_PHY_H_ +#define _DVB_USB_MXL111SF_PHY_H_ + +#include "mxl111sf.h" + +int mxl1x1sf_soft_reset(struct mxl111sf_state *state); +int mxl1x1sf_set_device_mode(struct mxl111sf_state *state, int mode); +int mxl1x1sf_top_master_ctrl(struct mxl111sf_state *state, int onoff); +int mxl111sf_disable_656_port(struct mxl111sf_state *state); +int mxl111sf_init_tuner_demod(struct mxl111sf_state *state); +int mxl111sf_enable_usb_output(struct mxl111sf_state *state); +int mxl111sf_config_mpeg_in(struct mxl111sf_state *state, + unsigned int parallel_serial, + unsigned int msb_lsb_1st, + unsigned int clock_phase, + unsigned int mpeg_valid_pol, + unsigned int mpeg_sync_pol); +int mxl111sf_config_i2s(struct mxl111sf_state *state, + u8 msb_start_pos, u8 data_width); +int mxl111sf_init_i2s_port(struct mxl111sf_state *state, u8 sample_size); +int mxl111sf_disable_i2s_port(struct mxl111sf_state *state); +int mxl111sf_config_spi(struct mxl111sf_state *state, int onoff); +int mxl111sf_idac_config(struct mxl111sf_state *state, + u8 control_mode, u8 current_setting, + u8 current_value, u8 hysteresis_value); + +#endif /* _DVB_USB_MXL111SF_PHY_H_ */ + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/dvb/dvb-usb/mxl111sf-reg.h b/drivers/media/dvb/dvb-usb/mxl111sf-reg.h new file mode 100644 index 000000000000..17831b0fb9db --- /dev/null +++ b/drivers/media/dvb/dvb-usb/mxl111sf-reg.h @@ -0,0 +1,179 @@ +/* + * mxl111sf-reg.h - driver for the MaxLinear MXL111SF + * + * Copyright (C) 2010 Michael Krufky <mkrufky@kernellabs.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 _DVB_USB_MXL111SF_REG_H_ +#define _DVB_USB_MXL111SF_REG_H_ + +#define CHIP_ID_REG 0xFC +#define TOP_CHIP_REV_ID_REG 0xFA + +#define V6_SNR_RB_LSB_REG 0x27 +#define V6_SNR_RB_MSB_REG 0x28 + +#define V6_N_ACCUMULATE_REG 0x11 +#define V6_RS_AVG_ERRORS_LSB_REG 0x2C +#define V6_RS_AVG_ERRORS_MSB_REG 0x2D + +#define V6_IRQ_STATUS_REG 0x24 +#define IRQ_MASK_FEC_LOCK 0x10 + +#define V6_SYNC_LOCK_REG 0x28 +#define SYNC_LOCK_MASK 0x10 + +#define V6_RS_LOCK_DET_REG 0x28 +#define RS_LOCK_DET_MASK 0x08 + +#define V6_INITACQ_NODETECT_REG 0x20 +#define V6_FORCE_NFFT_CPSIZE_REG 0x20 + +#define V6_CODE_RATE_TPS_REG 0x29 +#define V6_CODE_RATE_TPS_MASK 0x07 + + +#define V6_CP_LOCK_DET_REG 0x28 +#define V6_CP_LOCK_DET_MASK 0x04 + +#define V6_TPS_HIERACHY_REG 0x29 +#define V6_TPS_HIERARCHY_INFO_MASK 0x40 + +#define V6_MODORDER_TPS_REG 0x2A +#define V6_PARAM_CONSTELLATION_MASK 0x30 + +#define V6_MODE_TPS_REG 0x2A +#define V6_PARAM_FFT_MODE_MASK 0x0C + + +#define V6_CP_TPS_REG 0x29 +#define V6_PARAM_GI_MASK 0x30 + +#define V6_TPS_LOCK_REG 0x2A +#define V6_PARAM_TPS_LOCK_MASK 0x40 + +#define V6_FEC_PER_COUNT_REG 0x2E +#define V6_FEC_PER_SCALE_REG 0x2B +#define V6_FEC_PER_SCALE_MASK 0x03 +#define V6_FEC_PER_CLR_REG 0x20 +#define V6_FEC_PER_CLR_MASK 0x01 + +#define V6_PIN_MUX_MODE_REG 0x1B +#define V6_ENABLE_PIN_MUX 0x1E + +#define V6_I2S_NUM_SAMPLES_REG 0x16 + +#define V6_MPEG_IN_CLK_INV_REG 0x17 +#define V6_MPEG_IN_CTRL_REG 0x18 + +#define V6_INVERTED_CLK_PHASE 0x20 +#define V6_MPEG_IN_DATA_PARALLEL 0x01 +#define V6_MPEG_IN_DATA_SERIAL 0x02 + +#define V6_INVERTED_MPEG_SYNC 0x04 +#define V6_INVERTED_MPEG_VALID 0x08 + +#define TSIF_INPUT_PARALLEL 0 +#define TSIF_INPUT_SERIAL 1 +#define TSIF_NORMAL 0 + +#define V6_MPEG_INOUT_BIT_ORDER_CTRL_REG 0x19 +#define V6_MPEG_SER_MSB_FIRST 0x80 +#define MPEG_SER_MSB_FIRST_ENABLED 0x01 + +#define V6_656_I2S_BUFF_STATUS_REG 0x2F +#define V6_656_OVERFLOW_MASK_BIT 0x08 +#define V6_I2S_OVERFLOW_MASK_BIT 0x01 + +#define V6_I2S_STREAM_START_BIT_REG 0x14 +#define V6_I2S_STREAM_END_BIT_REG 0x15 +#define I2S_RIGHT_JUSTIFIED 0 +#define I2S_LEFT_JUSTIFIED 1 +#define I2S_DATA_FORMAT 2 + +#define V6_TUNER_LOOP_THRU_CONTROL_REG 0x09 +#define V6_ENABLE_LOOP_THRU 0x01 + +#define TOTAL_NUM_IF_OUTPUT_FREQ 16 + +#define TUNER_NORMAL_IF_SPECTRUM 0x0 +#define TUNER_INVERT_IF_SPECTRUM 0x10 + +#define V6_TUNER_IF_SEL_REG 0x06 +#define V6_TUNER_IF_FCW_REG 0x3C +#define V6_TUNER_IF_FCW_BYP_REG 0x3D +#define V6_RF_LOCK_STATUS_REG 0x23 + +#define NUM_DIG_TV_CHANNEL 1000 + +#define V6_DIG_CLK_FREQ_SEL_REG 0x07 +#define V6_REF_SYNTH_INT_REG 0x5C +#define V6_REF_SYNTH_REMAIN_REG 0x58 +#define V6_DIG_RFREFSELECT_REG 0x32 +#define V6_XTAL_CLK_OUT_GAIN_REG 0x31 +#define V6_TUNER_LOOP_THRU_CTRL_REG 0x09 +#define V6_DIG_XTAL_ENABLE_REG 0x06 +#define V6_DIG_XTAL_BIAS_REG 0x66 +#define V6_XTAL_CAP_REG 0x08 + +#define V6_GPO_CTRL_REG 0x18 +#define MXL_GPO_0 0x00 +#define MXL_GPO_1 0x01 +#define V6_GPO_0_MASK 0x10 +#define V6_GPO_1_MASK 0x20 + +#define V6_111SF_GPO_CTRL_REG 0x19 +#define MXL_111SF_GPO_1 0x00 +#define MXL_111SF_GPO_2 0x01 +#define MXL_111SF_GPO_3 0x02 +#define MXL_111SF_GPO_4 0x03 +#define MXL_111SF_GPO_5 0x04 +#define MXL_111SF_GPO_6 0x05 +#define MXL_111SF_GPO_7 0x06 + +#define MXL_111SF_GPO_0_MASK 0x01 +#define MXL_111SF_GPO_1_MASK 0x02 +#define MXL_111SF_GPO_2_MASK 0x04 +#define MXL_111SF_GPO_3_MASK 0x08 +#define MXL_111SF_GPO_4_MASK 0x10 +#define MXL_111SF_GPO_5_MASK 0x20 +#define MXL_111SF_GPO_6_MASK 0x40 + +#define V6_ATSC_CONFIG_REG 0x0A + +#define MXL_MODE_REG 0x03 +#define START_TUNE_REG 0x1C + +#define V6_IDAC_HYSTERESIS_REG 0x0B +#define V6_IDAC_SETTINGS_REG 0x0C +#define IDAC_MANUAL_CONTROL 1 +#define IDAC_CURRENT_SINKING_ENABLE 1 +#define IDAC_MANUAL_CONTROL_BIT_MASK 0x80 +#define IDAC_CURRENT_SINKING_BIT_MASK 0x40 + +#define V8_SPI_MODE_REG 0xE9 + +#define V6_DIG_RF_PWR_LSB_REG 0x46 +#define V6_DIG_RF_PWR_MSB_REG 0x47 + +#endif /* _DVB_USB_MXL111SF_REG_H_ */ + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/dvb/dvb-usb/mxl111sf-tuner.c b/drivers/media/dvb/dvb-usb/mxl111sf-tuner.c new file mode 100644 index 000000000000..a6341058c4e7 --- /dev/null +++ b/drivers/media/dvb/dvb-usb/mxl111sf-tuner.c @@ -0,0 +1,476 @@ +/* + * mxl111sf-tuner.c - driver for the MaxLinear MXL111SF CMOS tuner + * + * Copyright (C) 2010 Michael Krufky <mkrufky@kernellabs.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. + */ + +#include "mxl111sf-tuner.h" +#include "mxl111sf-phy.h" +#include "mxl111sf-reg.h" + +/* debug */ +static int mxl111sf_tuner_debug; +module_param_named(debug, mxl111sf_tuner_debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able))."); + +#define mxl_dbg(fmt, arg...) \ + if (mxl111sf_tuner_debug) \ + mxl_printk(KERN_DEBUG, fmt, ##arg) + +/* ------------------------------------------------------------------------ */ + +struct mxl111sf_tuner_state { + struct mxl111sf_state *mxl_state; + + struct mxl111sf_tuner_config *cfg; + + u32 frequency; + u32 bandwidth; +}; + +static int mxl111sf_tuner_read_reg(struct mxl111sf_tuner_state *state, + u8 addr, u8 *data) +{ + return (state->cfg->read_reg) ? + state->cfg->read_reg(state->mxl_state, addr, data) : + -EINVAL; +} + +static int mxl111sf_tuner_write_reg(struct mxl111sf_tuner_state *state, + u8 addr, u8 data) +{ + return (state->cfg->write_reg) ? + state->cfg->write_reg(state->mxl_state, addr, data) : + -EINVAL; +} + +static int mxl111sf_tuner_program_regs(struct mxl111sf_tuner_state *state, + struct mxl111sf_reg_ctrl_info *ctrl_reg_info) +{ + return (state->cfg->program_regs) ? + state->cfg->program_regs(state->mxl_state, ctrl_reg_info) : + -EINVAL; +} + +static int mxl1x1sf_tuner_top_master_ctrl(struct mxl111sf_tuner_state *state, + int onoff) +{ + return (state->cfg->top_master_ctrl) ? + state->cfg->top_master_ctrl(state->mxl_state, onoff) : + -EINVAL; +} + +/* ------------------------------------------------------------------------ */ + +static struct mxl111sf_reg_ctrl_info mxl_phy_tune_rf[] = { + {0x1d, 0x7f, 0x00}, /* channel bandwidth section 1/2/3, + DIG_MODEINDEX, _A, _CSF, */ + {0x1e, 0xff, 0x00}, /* channel frequency (lo and fractional) */ + {0x1f, 0xff, 0x00}, /* channel frequency (hi for integer portion) */ + {0, 0, 0} +}; + +/* ------------------------------------------------------------------------ */ + +static struct mxl111sf_reg_ctrl_info *mxl111sf_calc_phy_tune_regs(u32 freq, + u8 bw) +{ + u8 filt_bw; + + /* set channel bandwidth */ + switch (bw) { + case 0: /* ATSC */ + filt_bw = 25; + break; + case 1: /* QAM */ + filt_bw = 69; + break; + case 6: + filt_bw = 21; + break; + case 7: + filt_bw = 42; + break; + case 8: + filt_bw = 63; + break; + default: + err("%s: invalid bandwidth setting!", __func__); + return NULL; + } + + /* calculate RF channel */ + freq /= 1000000; + + freq *= 64; +#if 0 + /* do round */ + freq += 0.5; +#endif + /* set bandwidth */ + mxl_phy_tune_rf[0].data = filt_bw; + + /* set RF */ + mxl_phy_tune_rf[1].data = (freq & 0xff); + mxl_phy_tune_rf[2].data = (freq >> 8) & 0xff; + + /* start tune */ + return mxl_phy_tune_rf; +} + +static int mxl1x1sf_tuner_set_if_output_freq(struct mxl111sf_tuner_state *state) +{ + int ret; + u8 ctrl; +#if 0 + u16 iffcw; + u32 if_freq; +#endif + mxl_dbg("(IF polarity = %d, IF freq = 0x%02x)", + state->cfg->invert_spectrum, state->cfg->if_freq); + + /* set IF polarity */ + ctrl = state->cfg->invert_spectrum; + + ctrl |= state->cfg->if_freq; + + ret = mxl111sf_tuner_write_reg(state, V6_TUNER_IF_SEL_REG, ctrl); + if (mxl_fail(ret)) + goto fail; + +#if 0 + if_freq /= 1000000; + + /* do round */ + if_freq += 0.5; + + if (MXL_IF_LO == state->cfg->if_freq) { + ctrl = 0x08; + iffcw = (u16)(if_freq / (108 * 4096)); + } else if (MXL_IF_HI == state->cfg->if_freq) { + ctrl = 0x08; + iffcw = (u16)(if_freq / (216 * 4096)); + } else { + ctrl = 0; + iffcw = 0; + } + + ctrl |= (iffcw >> 8); +#endif + ret = mxl111sf_tuner_read_reg(state, V6_TUNER_IF_FCW_BYP_REG, &ctrl); + if (mxl_fail(ret)) + goto fail; + + ctrl &= 0xf0; + ctrl |= 0x90; + + ret = mxl111sf_tuner_write_reg(state, V6_TUNER_IF_FCW_BYP_REG, ctrl); + if (mxl_fail(ret)) + goto fail; + +#if 0 + ctrl = iffcw & 0x00ff; +#endif + ret = mxl111sf_tuner_write_reg(state, V6_TUNER_IF_FCW_REG, ctrl); + mxl_fail(ret); +fail: + return ret; +} + +static int mxl1x1sf_tune_rf(struct dvb_frontend *fe, u32 freq, u8 bw) +{ + struct mxl111sf_tuner_state *state = fe->tuner_priv; + static struct mxl111sf_reg_ctrl_info *reg_ctrl_array; + int ret; + u8 mxl_mode; + + mxl_dbg("(freq = %d, bw = 0x%x)", freq, bw); + + /* stop tune */ + ret = mxl111sf_tuner_write_reg(state, START_TUNE_REG, 0); + if (mxl_fail(ret)) + goto fail; + + /* check device mode */ + ret = mxl111sf_tuner_read_reg(state, MXL_MODE_REG, &mxl_mode); + if (mxl_fail(ret)) + goto fail; + + /* Fill out registers for channel tune */ + reg_ctrl_array = mxl111sf_calc_phy_tune_regs(freq, bw); + if (!reg_ctrl_array) + return -EINVAL; + + ret = mxl111sf_tuner_program_regs(state, reg_ctrl_array); + if (mxl_fail(ret)) + goto fail; + + if ((mxl_mode & MXL_DEV_MODE_MASK) == MXL_TUNER_MODE) { + /* IF tuner mode only */ + mxl1x1sf_tuner_top_master_ctrl(state, 0); + mxl1x1sf_tuner_top_master_ctrl(state, 1); + mxl1x1sf_tuner_set_if_output_freq(state); + } + + ret = mxl111sf_tuner_write_reg(state, START_TUNE_REG, 1); + if (mxl_fail(ret)) + goto fail; + + if (state->cfg->ant_hunt) + state->cfg->ant_hunt(fe); +fail: + return ret; +} + +static int mxl1x1sf_tuner_get_lock_status(struct mxl111sf_tuner_state *state, + int *rf_synth_lock, + int *ref_synth_lock) +{ + int ret; + u8 data; + + *rf_synth_lock = 0; + *ref_synth_lock = 0; + + ret = mxl111sf_tuner_read_reg(state, V6_RF_LOCK_STATUS_REG, &data); + if (mxl_fail(ret)) + goto fail; + + *ref_synth_lock = ((data & 0x03) == 0x03) ? 1 : 0; + *rf_synth_lock = ((data & 0x0c) == 0x0c) ? 1 : 0; +fail: + return ret; +} + +#if 0 +static int mxl1x1sf_tuner_loop_thru_ctrl(struct mxl111sf_tuner_state *state, + int onoff) +{ + return mxl111sf_tuner_write_reg(state, V6_TUNER_LOOP_THRU_CTRL_REG, + onoff ? 1 : 0); +} +#endif + +/* ------------------------------------------------------------------------ */ + +static int mxl111sf_tuner_set_params(struct dvb_frontend *fe, + struct dvb_frontend_parameters *params) +{ + struct mxl111sf_tuner_state *state = fe->tuner_priv; + int ret; + u8 bw; + + mxl_dbg("()"); + + if (fe->ops.info.type == FE_ATSC) { + switch (params->u.vsb.modulation) { + case VSB_8: + case VSB_16: + bw = 0; /* ATSC */ + break; + case QAM_64: + case QAM_256: + bw = 1; /* US CABLE */ + break; + default: + err("%s: modulation not set!", __func__); + return -EINVAL; + } + } else if (fe->ops.info.type == FE_OFDM) { + switch (params->u.ofdm.bandwidth) { + case BANDWIDTH_6_MHZ: + bw = 6; + break; + case BANDWIDTH_7_MHZ: + bw = 7; + break; + case BANDWIDTH_8_MHZ: + bw = 8; + break; + default: + err("%s: bandwidth not set!", __func__); + return -EINVAL; + } + } else { + err("%s: modulation type not supported!", __func__); + return -EINVAL; + } + ret = mxl1x1sf_tune_rf(fe, params->frequency, bw); + if (mxl_fail(ret)) + goto fail; + + state->frequency = params->frequency; + state->bandwidth = (fe->ops.info.type == FE_OFDM) ? + params->u.ofdm.bandwidth : 0; +fail: + return ret; +} + +/* ------------------------------------------------------------------------ */ + +#if 0 +static int mxl111sf_tuner_init(struct dvb_frontend *fe) +{ + struct mxl111sf_tuner_state *state = fe->tuner_priv; + int ret; + + /* wake from standby handled by usb driver */ + + return ret; +} + +static int mxl111sf_tuner_sleep(struct dvb_frontend *fe) +{ + struct mxl111sf_tuner_state *state = fe->tuner_priv; + int ret; + + /* enter standby mode handled by usb driver */ + + return ret; +} +#endif + +/* ------------------------------------------------------------------------ */ + +static int mxl111sf_tuner_get_status(struct dvb_frontend *fe, u32 *status) +{ + struct mxl111sf_tuner_state *state = fe->tuner_priv; + int rf_locked, ref_locked, ret; + + *status = 0; + + ret = mxl1x1sf_tuner_get_lock_status(state, &rf_locked, &ref_locked); + if (mxl_fail(ret)) + goto fail; + mxl_info("%s%s", rf_locked ? "rf locked " : "", + ref_locked ? "ref locked" : ""); + + if ((rf_locked) || (ref_locked)) + *status |= TUNER_STATUS_LOCKED; +fail: + return ret; +} + +static int mxl111sf_get_rf_strength(struct dvb_frontend *fe, u16 *strength) +{ + struct mxl111sf_tuner_state *state = fe->tuner_priv; + u8 val1, val2; + int ret; + + *strength = 0; + + ret = mxl111sf_tuner_write_reg(state, 0x00, 0x02); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_tuner_read_reg(state, V6_DIG_RF_PWR_LSB_REG, &val1); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_tuner_read_reg(state, V6_DIG_RF_PWR_MSB_REG, &val2); + if (mxl_fail(ret)) + goto fail; + + *strength = val1 | ((val2 & 0x07) << 8); +fail: + ret = mxl111sf_tuner_write_reg(state, 0x00, 0x00); + mxl_fail(ret); + + return ret; +} + +/* ------------------------------------------------------------------------ */ + +static int mxl111sf_tuner_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct mxl111sf_tuner_state *state = fe->tuner_priv; + *frequency = state->frequency; + return 0; +} + +static int mxl111sf_tuner_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) +{ + struct mxl111sf_tuner_state *state = fe->tuner_priv; + *bandwidth = state->bandwidth; + return 0; +} + +static int mxl111sf_tuner_release(struct dvb_frontend *fe) +{ + struct mxl111sf_tuner_state *state = fe->tuner_priv; + mxl_dbg("()"); + kfree(state); + fe->tuner_priv = NULL; + return 0; +} + +/* ------------------------------------------------------------------------- */ + +static struct dvb_tuner_ops mxl111sf_tuner_tuner_ops = { + .info = { + .name = "MaxLinear MxL111SF", +#if 0 + .frequency_min = , + .frequency_max = , + .frequency_step = , +#endif + }, +#if 0 + .init = mxl111sf_tuner_init, + .sleep = mxl111sf_tuner_sleep, +#endif + .set_params = mxl111sf_tuner_set_params, + .get_status = mxl111sf_tuner_get_status, + .get_rf_strength = mxl111sf_get_rf_strength, + .get_frequency = mxl111sf_tuner_get_frequency, + .get_bandwidth = mxl111sf_tuner_get_bandwidth, + .release = mxl111sf_tuner_release, +}; + +struct dvb_frontend *mxl111sf_tuner_attach(struct dvb_frontend *fe, + struct mxl111sf_state *mxl_state, + struct mxl111sf_tuner_config *cfg) +{ + struct mxl111sf_tuner_state *state = NULL; + + mxl_dbg("()"); + + state = kzalloc(sizeof(struct mxl111sf_tuner_state), GFP_KERNEL); + if (state == NULL) + return NULL; + + state->mxl_state = mxl_state; + state->cfg = cfg; + + memcpy(&fe->ops.tuner_ops, &mxl111sf_tuner_tuner_ops, + sizeof(struct dvb_tuner_ops)); + + fe->tuner_priv = state; + return fe; +} +EXPORT_SYMBOL_GPL(mxl111sf_tuner_attach); + +MODULE_DESCRIPTION("MaxLinear MxL111SF CMOS tuner driver"); +MODULE_AUTHOR("Michael Krufky <mkrufky@kernellabs.com>"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("0.1"); + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * --------------------------------------------------------------------------- + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/dvb/dvb-usb/mxl111sf-tuner.h b/drivers/media/dvb/dvb-usb/mxl111sf-tuner.h new file mode 100644 index 000000000000..ff333960b184 --- /dev/null +++ b/drivers/media/dvb/dvb-usb/mxl111sf-tuner.h @@ -0,0 +1,89 @@ +/* + * mxl111sf-tuner.h - driver for the MaxLinear MXL111SF CMOS tuner + * + * Copyright (C) 2010 Michael Krufky <mkrufky@kernellabs.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 __MXL111SF_TUNER_H__ +#define __MXL111SF_TUNER_H__ + +#include "dvb_frontend.h" + +#include "mxl111sf.h" + +enum mxl_if_freq { +#if 0 + MXL_IF_LO = 0x00, /* other IF < 9MHz */ +#endif + MXL_IF_4_0 = 0x01, /* 4.0 MHz */ + MXL_IF_4_5 = 0x02, /* 4.5 MHz */ + MXL_IF_4_57 = 0x03, /* 4.57 MHz */ + MXL_IF_5_0 = 0x04, /* 5.0 MHz */ + MXL_IF_5_38 = 0x05, /* 5.38 MHz */ + MXL_IF_6_0 = 0x06, /* 6.0 MHz */ + MXL_IF_6_28 = 0x07, /* 6.28 MHz */ + MXL_IF_7_2 = 0x08, /* 7.2 MHz */ + MXL_IF_35_25 = 0x09, /* 35.25 MHz */ + MXL_IF_36 = 0x0a, /* 36 MHz */ + MXL_IF_36_15 = 0x0b, /* 36.15 MHz */ + MXL_IF_44 = 0x0c, /* 44 MHz */ +#if 0 + MXL_IF_HI = 0x0f, /* other IF > 35 MHz and < 45 MHz */ +#endif +}; + +struct mxl111sf_tuner_config { + enum mxl_if_freq if_freq; + unsigned int invert_spectrum:1; + + int (*read_reg)(struct mxl111sf_state *state, u8 addr, u8 *data); + int (*write_reg)(struct mxl111sf_state *state, u8 addr, u8 data); + int (*program_regs)(struct mxl111sf_state *state, + struct mxl111sf_reg_ctrl_info *ctrl_reg_info); + int (*top_master_ctrl)(struct mxl111sf_state *state, int onoff); + int (*ant_hunt)(struct dvb_frontend *fe); +}; + +/* ------------------------------------------------------------------------ */ + +#if defined(CONFIG_DVB_USB_MXL111SF) || \ + (defined(CONFIG_DVB_USB_MXL111SF_MODULE) && defined(MODULE)) +extern +struct dvb_frontend *mxl111sf_tuner_attach(struct dvb_frontend *fe, + struct mxl111sf_state *mxl_state, + struct mxl111sf_tuner_config *cfg); +#else +static inline +struct dvb_frontend *mxl111sf_tuner_attach(struct dvb_frontend *fe, + struct mxl111sf_state *mxl_state + struct mxl111sf_tuner_config *cfg) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif /* __MXL111SF_TUNER_H__ */ + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * --------------------------------------------------------------------------- + * Local variables: + * c-basic-offset: 8 + * End: + */ + diff --git a/drivers/media/dvb/dvb-usb/mxl111sf.c b/drivers/media/dvb/dvb-usb/mxl111sf.c new file mode 100644 index 000000000000..b5c98da5d9e2 --- /dev/null +++ b/drivers/media/dvb/dvb-usb/mxl111sf.c @@ -0,0 +1,1086 @@ +/* + * Copyright (C) 2010 Michael Krufky (mkrufky@kernellabs.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, version 2. + * + * see Documentation/dvb/README.dvb-usb for more information + */ + +#include <linux/vmalloc.h> +#include <linux/i2c.h> + +#include "mxl111sf.h" +#include "mxl111sf-reg.h" +#include "mxl111sf-phy.h" +#include "mxl111sf-i2c.h" +#include "mxl111sf-gpio.h" + +#include "mxl111sf-demod.h" +#include "mxl111sf-tuner.h" + +#include "lgdt3305.h" + +int dvb_usb_mxl111sf_debug; +module_param_named(debug, dvb_usb_mxl111sf_debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level " + "(1=info, 2=xfer, 4=i2c, 8=reg, 16=adv (or-able))."); + +int dvb_usb_mxl111sf_isoc; +module_param_named(isoc, dvb_usb_mxl111sf_isoc, int, 0644); +MODULE_PARM_DESC(isoc, "enable usb isoc xfer (0=bulk, 1=isoc)."); + +#define ANT_PATH_AUTO 0 +#define ANT_PATH_EXTERNAL 1 +#define ANT_PATH_INTERNAL 2 + +int dvb_usb_mxl111sf_rfswitch = +#if 0 + ANT_PATH_AUTO; +#else + ANT_PATH_EXTERNAL; +#endif + +module_param_named(rfswitch, dvb_usb_mxl111sf_rfswitch, int, 0644); +MODULE_PARM_DESC(rfswitch, "force rf switch position (0=auto, 1=ext, 2=int)."); + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +#define deb_info(args...) dprintk(dvb_usb_mxl111sf_debug, 0x13, args) +#define deb_reg(args...) dprintk(dvb_usb_mxl111sf_debug, 0x08, args) +#define deb_adv(args...) dprintk(dvb_usb_mxl111sf_debug, MXL_ADV_DBG, args) + +int mxl111sf_ctrl_msg(struct dvb_usb_device *d, + u8 cmd, u8 *wbuf, int wlen, u8 *rbuf, int rlen) +{ + int wo = (rbuf == NULL || rlen == 0); /* write-only */ + int ret; + u8 sndbuf[1+wlen]; + + deb_adv("%s(wlen = %d, rlen = %d)\n", __func__, wlen, rlen); + + memset(sndbuf, 0, 1+wlen); + + sndbuf[0] = cmd; + memcpy(&sndbuf[1], wbuf, wlen); + + ret = (wo) ? dvb_usb_generic_write(d, sndbuf, 1+wlen) : + dvb_usb_generic_rw(d, sndbuf, 1+wlen, rbuf, rlen, 0); + mxl_fail(ret); + + return ret; +} + +/* ------------------------------------------------------------------------ */ + +#define MXL_CMD_REG_READ 0xaa +#define MXL_CMD_REG_WRITE 0x55 + +int mxl111sf_read_reg(struct mxl111sf_state *state, u8 addr, u8 *data) +{ + u8 buf[2]; + int ret; + + ret = mxl111sf_ctrl_msg(state->d, MXL_CMD_REG_READ, &addr, 1, buf, 2); + if (mxl_fail(ret)) { + mxl_debug("error reading reg: 0x%02x", addr); + goto fail; + } + + if (buf[0] == addr) + *data = buf[1]; + else { + err("invalid response reading reg: 0x%02x != 0x%02x, 0x%02x", + addr, buf[0], buf[1]); + ret = -EINVAL; + } + + deb_reg("R: (0x%02x, 0x%02x)\n", addr, *data); +fail: + return ret; +} + +int mxl111sf_write_reg(struct mxl111sf_state *state, u8 addr, u8 data) +{ + u8 buf[] = { addr, data }; + int ret; + + deb_reg("W: (0x%02x, 0x%02x)\n", addr, data); + + ret = mxl111sf_ctrl_msg(state->d, MXL_CMD_REG_WRITE, buf, 2, NULL, 0); + if (mxl_fail(ret)) + err("error writing reg: 0x%02x, val: 0x%02x", addr, data); + return ret; +} + +/* ------------------------------------------------------------------------ */ + +int mxl111sf_write_reg_mask(struct mxl111sf_state *state, + u8 addr, u8 mask, u8 data) +{ + int ret; + u8 val; + + if (mask != 0xff) { + ret = mxl111sf_read_reg(state, addr, &val); +#if 1 + /* dont know why this usually errors out on the first try */ + if (mxl_fail(ret)) + err("error writing addr: 0x%02x, mask: 0x%02x, " + "data: 0x%02x, retrying...", addr, mask, data); + + ret = mxl111sf_read_reg(state, addr, &val); +#endif + if (mxl_fail(ret)) + goto fail; + } + val &= ~mask; + val |= data; + + ret = mxl111sf_write_reg(state, addr, val); + mxl_fail(ret); +fail: + return ret; +} + +/* ------------------------------------------------------------------------ */ + +int mxl111sf_ctrl_program_regs(struct mxl111sf_state *state, + struct mxl111sf_reg_ctrl_info *ctrl_reg_info) +{ + int i, ret = 0; + + for (i = 0; ctrl_reg_info[i].addr | + ctrl_reg_info[i].mask | + ctrl_reg_info[i].data; i++) { + + ret = mxl111sf_write_reg_mask(state, + ctrl_reg_info[i].addr, + ctrl_reg_info[i].mask, + ctrl_reg_info[i].data); + if (mxl_fail(ret)) { + err("failed on reg #%d (0x%02x)", i, + ctrl_reg_info[i].addr); + break; + } + } + return ret; +} + +/* ------------------------------------------------------------------------ */ + +static int mxl1x1sf_get_chip_info(struct mxl111sf_state *state) +{ + int ret; + u8 id, ver; + char *mxl_chip, *mxl_rev; + + if ((state->chip_id) && (state->chip_ver)) + return 0; + + ret = mxl111sf_read_reg(state, CHIP_ID_REG, &id); + if (mxl_fail(ret)) + goto fail; + state->chip_id = id; + + ret = mxl111sf_read_reg(state, TOP_CHIP_REV_ID_REG, &ver); + if (mxl_fail(ret)) + goto fail; + state->chip_ver = ver; + + switch (id) { + case 0x61: + mxl_chip = "MxL101SF"; + break; + case 0x63: + mxl_chip = "MxL111SF"; + break; + default: + mxl_chip = "UNKNOWN MxL1X1"; + break; + } + switch (ver) { + case 0x36: + state->chip_rev = MXL111SF_V6; + mxl_rev = "v6"; + break; + case 0x08: + state->chip_rev = MXL111SF_V8_100; + mxl_rev = "v8_100"; + break; + case 0x18: + state->chip_rev = MXL111SF_V8_200; + mxl_rev = "v8_200"; + break; + default: + state->chip_rev = 0; + mxl_rev = "UNKNOWN REVISION"; + break; + } + info("%s detected, %s (0x%x)", mxl_chip, mxl_rev, ver); +fail: + return ret; +} + +#define get_chip_info(state) \ +({ \ + int ___ret; \ + ___ret = mxl1x1sf_get_chip_info(state); \ + if (mxl_fail(___ret)) { \ + mxl_debug("failed to get chip info" \ + " on first probe attempt"); \ + ___ret = mxl1x1sf_get_chip_info(state); \ + if (mxl_fail(___ret)) \ + err("failed to get chip info during probe"); \ + else \ + mxl_debug("probe needed a retry " \ + "in order to succeed."); \ + } \ + ___ret; \ +}) + +/* ------------------------------------------------------------------------ */ + +static int mxl111sf_power_ctrl(struct dvb_usb_device *d, int onoff) +{ + /* power control depends on which adapter is being woken: + * save this for init, instead, via mxl111sf_adap_fe_init */ + return 0; +} + +static int mxl111sf_adap_fe_init(struct dvb_frontend *fe) +{ + struct dvb_usb_adapter *adap = fe->dvb->priv; + struct dvb_usb_device *d = adap->dev; + struct mxl111sf_state *state = d->priv; + struct mxl111sf_adap_state *adap_state = adap->fe_adap[fe->id].priv; + + int err; + + /* exit if we didnt initialize the driver yet */ + if (!state->chip_id) { + mxl_debug("driver not yet initialized, exit."); + goto fail; + } + + deb_info("%s()\n", __func__); + + mutex_lock(&state->fe_lock); + + state->alt_mode = adap_state->alt_mode; + + if (usb_set_interface(adap->dev->udev, 0, state->alt_mode) < 0) + err("set interface failed"); + + err = mxl1x1sf_soft_reset(state); + mxl_fail(err); + err = mxl111sf_init_tuner_demod(state); + mxl_fail(err); + err = mxl1x1sf_set_device_mode(state, adap_state->device_mode); + + mxl_fail(err); + mxl111sf_enable_usb_output(state); + mxl_fail(err); + mxl1x1sf_top_master_ctrl(state, 1); + mxl_fail(err); + + if ((MXL111SF_GPIO_MOD_DVBT != adap_state->gpio_mode) && + (state->chip_rev > MXL111SF_V6)) { + mxl111sf_config_pin_mux_modes(state, + PIN_MUX_TS_SPI_IN_MODE_1); + mxl_fail(err); + } + err = mxl111sf_init_port_expander(state); + if (!mxl_fail(err)) { + state->gpio_mode = adap_state->gpio_mode; + err = mxl111sf_gpio_mode_switch(state, state->gpio_mode); + mxl_fail(err); +#if 0 + err = fe->ops.init(fe); +#endif + msleep(100); /* add short delay after enabling + * the demod before touching it */ + } + + return (adap_state->fe_init) ? adap_state->fe_init(fe) : 0; +fail: + return -ENODEV; +} + +static int mxl111sf_adap_fe_sleep(struct dvb_frontend *fe) +{ + struct dvb_usb_adapter *adap = fe->dvb->priv; + struct dvb_usb_device *d = adap->dev; + struct mxl111sf_state *state = d->priv; + struct mxl111sf_adap_state *adap_state = adap->fe_adap[fe->id].priv; + int err; + + /* exit if we didnt initialize the driver yet */ + if (!state->chip_id) { + mxl_debug("driver not yet initialized, exit."); + goto fail; + } + + deb_info("%s()\n", __func__); + + err = (adap_state->fe_sleep) ? adap_state->fe_sleep(fe) : 0; + + mutex_unlock(&state->fe_lock); + + return err; +fail: + return -ENODEV; +} + + +static int mxl111sf_ep6_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) +{ + struct dvb_usb_device *d = adap->dev; + struct mxl111sf_state *state = d->priv; + struct mxl111sf_adap_state *adap_state = adap->fe_adap[adap->active_fe].priv; + int ret = 0; + u8 tmp; + + deb_info("%s(%d)\n", __func__, onoff); + + if (onoff) { + ret = mxl111sf_enable_usb_output(state); + mxl_fail(ret); + ret = mxl111sf_config_mpeg_in(state, 1, 1, + adap_state->ep6_clockphase, + 0, 0); + mxl_fail(ret); + } else { + ret = mxl111sf_disable_656_port(state); + mxl_fail(ret); + } + + mxl111sf_read_reg(state, 0x12, &tmp); + tmp &= ~0x04; + mxl111sf_write_reg(state, 0x12, tmp); + + return ret; +} + +static int mxl111sf_ep4_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) +{ + struct dvb_usb_device *d = adap->dev; + struct mxl111sf_state *state = d->priv; + int ret = 0; + + deb_info("%s(%d)\n", __func__, onoff); + + if (onoff) { + ret = mxl111sf_enable_usb_output(state); + mxl_fail(ret); + } + + return ret; +} + +/* ------------------------------------------------------------------------ */ + +static struct lgdt3305_config hauppauge_lgdt3305_config = { + .i2c_addr = 0xb2 >> 1, + .mpeg_mode = LGDT3305_MPEG_SERIAL, + .tpclk_edge = LGDT3305_TPCLK_RISING_EDGE, + .tpvalid_polarity = LGDT3305_TP_VALID_HIGH, + .deny_i2c_rptr = 1, + .spectral_inversion = 0, + .qam_if_khz = 6000, + .vsb_if_khz = 6000, +}; + +static int mxl111sf_lgdt3305_frontend_attach(struct dvb_usb_adapter *adap) +{ + struct dvb_usb_device *d = adap->dev; + struct mxl111sf_state *state = d->priv; + int fe_id = adap->num_frontends_initialized; + struct mxl111sf_adap_state *adap_state = adap->fe_adap[fe_id].priv; + int ret; + + deb_adv("%s()\n", __func__); + + /* save a pointer to the dvb_usb_device in device state */ + state->d = d; + adap_state->alt_mode = (dvb_usb_mxl111sf_isoc) ? 2 : 1; + state->alt_mode = adap_state->alt_mode; + + if (usb_set_interface(adap->dev->udev, 0, state->alt_mode) < 0) + err("set interface failed"); + + state->gpio_mode = MXL111SF_GPIO_MOD_ATSC; + adap_state->gpio_mode = state->gpio_mode; + adap_state->device_mode = MXL_TUNER_MODE; + adap_state->ep6_clockphase = 1; + + ret = mxl1x1sf_soft_reset(state); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_init_tuner_demod(state); + if (mxl_fail(ret)) + goto fail; + + ret = mxl1x1sf_set_device_mode(state, adap_state->device_mode); + if (mxl_fail(ret)) + goto fail; + + ret = mxl111sf_enable_usb_output(state); + if (mxl_fail(ret)) + goto fail; + ret = mxl1x1sf_top_master_ctrl(state, 1); + if (mxl_fail(ret)) + goto fail; + + ret = mxl111sf_init_port_expander(state); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_gpio_mode_switch(state, state->gpio_mode); + if (mxl_fail(ret)) + goto fail; + + adap->fe_adap[fe_id].fe = dvb_attach(lgdt3305_attach, + &hauppauge_lgdt3305_config, + &adap->dev->i2c_adap); + if (adap->fe_adap[fe_id].fe) { + adap_state->fe_init = adap->fe_adap[fe_id].fe->ops.init; + adap->fe_adap[fe_id].fe->ops.init = mxl111sf_adap_fe_init; + adap_state->fe_sleep = adap->fe_adap[fe_id].fe->ops.sleep; + adap->fe_adap[fe_id].fe->ops.sleep = mxl111sf_adap_fe_sleep; + return 0; + } + ret = -EIO; +fail: + return ret; +} + +static struct mxl111sf_demod_config mxl_demod_config = { + .read_reg = mxl111sf_read_reg, + .write_reg = mxl111sf_write_reg, + .program_regs = mxl111sf_ctrl_program_regs, +}; + +static int mxl111sf_attach_demod(struct dvb_usb_adapter *adap) +{ + struct dvb_usb_device *d = adap->dev; + struct mxl111sf_state *state = d->priv; + int fe_id = adap->num_frontends_initialized; + struct mxl111sf_adap_state *adap_state = adap->fe_adap[fe_id].priv; + int ret; + + deb_adv("%s()\n", __func__); + + /* save a pointer to the dvb_usb_device in device state */ + state->d = d; + adap_state->alt_mode = (dvb_usb_mxl111sf_isoc) ? 1 : 2; + state->alt_mode = adap_state->alt_mode; + + if (usb_set_interface(adap->dev->udev, 0, state->alt_mode) < 0) + err("set interface failed"); + + state->gpio_mode = MXL111SF_GPIO_MOD_DVBT; + adap_state->gpio_mode = state->gpio_mode; + adap_state->device_mode = MXL_SOC_MODE; + adap_state->ep6_clockphase = 1; + + ret = mxl1x1sf_soft_reset(state); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_init_tuner_demod(state); + if (mxl_fail(ret)) + goto fail; + + ret = mxl1x1sf_set_device_mode(state, adap_state->device_mode); + if (mxl_fail(ret)) + goto fail; + + ret = mxl111sf_enable_usb_output(state); + if (mxl_fail(ret)) + goto fail; + ret = mxl1x1sf_top_master_ctrl(state, 1); + if (mxl_fail(ret)) + goto fail; + + /* dont care if this fails */ + mxl111sf_init_port_expander(state); + + adap->fe_adap[fe_id].fe = dvb_attach(mxl111sf_demod_attach, state, + &mxl_demod_config); + if (adap->fe_adap[fe_id].fe) { + adap_state->fe_init = adap->fe_adap[fe_id].fe->ops.init; + adap->fe_adap[fe_id].fe->ops.init = mxl111sf_adap_fe_init; + adap_state->fe_sleep = adap->fe_adap[fe_id].fe->ops.sleep; + adap->fe_adap[fe_id].fe->ops.sleep = mxl111sf_adap_fe_sleep; + return 0; + } + ret = -EIO; +fail: + return ret; +} + +static inline int mxl111sf_set_ant_path(struct mxl111sf_state *state, + int antpath) +{ + return mxl111sf_idac_config(state, 1, 1, + (antpath == ANT_PATH_INTERNAL) ? + 0x3f : 0x00, 0); +} + +#define DbgAntHunt(x, pwr0, pwr1, pwr2, pwr3) \ + err("%s(%d) FINAL input set to %s rxPwr:%d|%d|%d|%d\n", \ + __func__, __LINE__, \ + (ANT_PATH_EXTERNAL == x) ? "EXTERNAL" : "INTERNAL", \ + pwr0, pwr1, pwr2, pwr3) + +#define ANT_HUNT_SLEEP 90 +#define ANT_EXT_TWEAK 0 + +static int mxl111sf_ant_hunt(struct dvb_frontend *fe) +{ + struct dvb_usb_adapter *adap = fe->dvb->priv; + struct dvb_usb_device *d = adap->dev; + struct mxl111sf_state *state = d->priv; + + int antctrl = dvb_usb_mxl111sf_rfswitch; + + u16 rxPwrA, rxPwr0, rxPwr1, rxPwr2; + + /* FIXME: must force EXTERNAL for QAM - done elsewhere */ + mxl111sf_set_ant_path(state, antctrl == ANT_PATH_AUTO ? + ANT_PATH_EXTERNAL : antctrl); + + if (antctrl == ANT_PATH_AUTO) { +#if 0 + msleep(ANT_HUNT_SLEEP); +#endif + fe->ops.tuner_ops.get_rf_strength(fe, &rxPwrA); + + mxl111sf_set_ant_path(state, ANT_PATH_EXTERNAL); + msleep(ANT_HUNT_SLEEP); + fe->ops.tuner_ops.get_rf_strength(fe, &rxPwr0); + + mxl111sf_set_ant_path(state, ANT_PATH_EXTERNAL); + msleep(ANT_HUNT_SLEEP); + fe->ops.tuner_ops.get_rf_strength(fe, &rxPwr1); + + mxl111sf_set_ant_path(state, ANT_PATH_INTERNAL); + msleep(ANT_HUNT_SLEEP); + fe->ops.tuner_ops.get_rf_strength(fe, &rxPwr2); + + if (rxPwr1+ANT_EXT_TWEAK >= rxPwr2) { + /* return with EXTERNAL enabled */ + mxl111sf_set_ant_path(state, ANT_PATH_EXTERNAL); + DbgAntHunt(ANT_PATH_EXTERNAL, rxPwrA, + rxPwr0, rxPwr1, rxPwr2); + } else { + /* return with INTERNAL enabled */ + DbgAntHunt(ANT_PATH_INTERNAL, rxPwrA, + rxPwr0, rxPwr1, rxPwr2); + } + } + return 0; +} + +static struct mxl111sf_tuner_config mxl_tuner_config = { + .if_freq = MXL_IF_6_0, /* applies to external IF output, only */ + .invert_spectrum = 0, + .read_reg = mxl111sf_read_reg, + .write_reg = mxl111sf_write_reg, + .program_regs = mxl111sf_ctrl_program_regs, + .top_master_ctrl = mxl1x1sf_top_master_ctrl, + .ant_hunt = mxl111sf_ant_hunt, +}; + +static int mxl111sf_attach_tuner(struct dvb_usb_adapter *adap) +{ + struct dvb_usb_device *d = adap->dev; + struct mxl111sf_state *state = d->priv; + int fe_id = adap->num_frontends_initialized; + + deb_adv("%s()\n", __func__); + + if (NULL != dvb_attach(mxl111sf_tuner_attach, + adap->fe_adap[fe_id].fe, state, + &mxl_tuner_config)) + return 0; + + return -EIO; +} + +static int mxl111sf_fe_ioctl_override(struct dvb_frontend *fe, + unsigned int cmd, void *parg, + unsigned int stage) +{ + int err = 0; + + switch (stage) { + case DVB_FE_IOCTL_PRE: + + switch (cmd) { + case FE_READ_SIGNAL_STRENGTH: + err = fe->ops.tuner_ops.get_rf_strength(fe, parg); + /* If no error occurs, prevent dvb-core from handling + * this IOCTL, otherwise return the error */ + if (0 == err) + err = 1; + break; + } + break; + + case DVB_FE_IOCTL_POST: + /* no post-ioctl handling required */ + break; + } + return err; +}; + +static u32 mxl111sf_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +struct i2c_algorithm mxl111sf_i2c_algo = { + .master_xfer = mxl111sf_i2c_xfer, + .functionality = mxl111sf_i2c_func, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif +}; + +static struct dvb_usb_device_properties mxl111sf_dvbt_bulk_properties; +static struct dvb_usb_device_properties mxl111sf_dvbt_isoc_properties; +static struct dvb_usb_device_properties mxl111sf_atsc_bulk_properties; +static struct dvb_usb_device_properties mxl111sf_atsc_isoc_properties; + +static int mxl111sf_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct dvb_usb_device *d = NULL; + + deb_adv("%s()\n", __func__); + + if (((dvb_usb_mxl111sf_isoc) && + (0 == dvb_usb_device_init(intf, + &mxl111sf_dvbt_isoc_properties, + THIS_MODULE, &d, adapter_nr) || + 0 == dvb_usb_device_init(intf, + &mxl111sf_atsc_isoc_properties, + THIS_MODULE, &d, adapter_nr))) || + 0 == dvb_usb_device_init(intf, + &mxl111sf_dvbt_bulk_properties, + THIS_MODULE, &d, adapter_nr) || + 0 == dvb_usb_device_init(intf, + &mxl111sf_atsc_bulk_properties, + THIS_MODULE, &d, adapter_nr) || 0) { + + struct mxl111sf_state *state = d->priv; + static u8 eeprom[256]; + struct i2c_client c; + int ret; + + ret = get_chip_info(state); + if (mxl_fail(ret)) + err("failed to get chip info during probe"); + + mutex_init(&state->fe_lock); + + if (state->chip_rev > MXL111SF_V6) + mxl111sf_config_pin_mux_modes(state, + PIN_MUX_TS_SPI_IN_MODE_1); + + c.adapter = &d->i2c_adap; + c.addr = 0xa0 >> 1; + + ret = tveeprom_read(&c, eeprom, sizeof(eeprom)); + if (mxl_fail(ret)) + return 0; + tveeprom_hauppauge_analog(&c, &state->tv, + (0x84 == eeprom[0xa0]) ? + eeprom + 0xa0 : eeprom + 0x80); +#if 0 + switch (state->tv.model) { + case 117001: + case 126001: + case 138001: + break; + default: + printk(KERN_WARNING "%s: warning: " + "unknown hauppauge model #%d\n", + __func__, state->tv.model); + } +#endif + return 0; + } + err("Your device is not yet supported by this driver. " + "See kernellabs.com for more info"); + return -EINVAL; +} + +static struct usb_device_id mxl111sf_table[] = { +/* 0 */ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc600) }, /* ATSC+ IR */ + { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc601) }, /* ATSC */ + { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc602) }, /* + */ + { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc603) }, /* ATSC+ */ + { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc604) }, /* DVBT */ +/* 5 */ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc609) }, /* ATSC IR */ + { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc60a) }, /* + IR */ + { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc60b) }, /* ATSC+ IR */ + { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc60c) }, /* DVBT IR */ + { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc653) }, /* ATSC+ */ +/*10 */ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc65b) }, /* ATSC+ IR */ + { USB_DEVICE(USB_VID_HAUPPAUGE, 0xb700) }, /* ATSC+ sw */ + { USB_DEVICE(USB_VID_HAUPPAUGE, 0xb701) }, /* ATSC sw */ + { USB_DEVICE(USB_VID_HAUPPAUGE, 0xb702) }, /* + sw */ + { USB_DEVICE(USB_VID_HAUPPAUGE, 0xb703) }, /* ATSC+ sw */ +/*15 */ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xb704) }, /* DVBT sw */ + { USB_DEVICE(USB_VID_HAUPPAUGE, 0xb753) }, /* ATSC+ sw */ + { USB_DEVICE(USB_VID_HAUPPAUGE, 0xb763) }, /* ATSC+ no */ + { USB_DEVICE(USB_VID_HAUPPAUGE, 0xb764) }, /* DVBT no */ + { USB_DEVICE(USB_VID_HAUPPAUGE, 0xd853) }, /* ATSC+ sw */ +/*20 */ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xd854) }, /* DVBT sw */ + { USB_DEVICE(USB_VID_HAUPPAUGE, 0xd863) }, /* ATSC+ no */ + { USB_DEVICE(USB_VID_HAUPPAUGE, 0xd864) }, /* DVBT no */ + { USB_DEVICE(USB_VID_HAUPPAUGE, 0xd8d3) }, /* ATSC+ sw */ + { USB_DEVICE(USB_VID_HAUPPAUGE, 0xd8d4) }, /* DVBT sw */ +/*25 */ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xd8e3) }, /* ATSC+ no */ + { USB_DEVICE(USB_VID_HAUPPAUGE, 0xd8e4) }, /* DVBT no */ + { USB_DEVICE(USB_VID_HAUPPAUGE, 0xd8ff) }, /* ATSC+ */ + { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc612) }, /* + */ + { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc613) }, /* ATSC+ */ +/*30 */ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc61a) }, /* + IR */ + { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc61b) }, /* ATSC+ IR */ + { USB_DEVICE(USB_VID_HAUPPAUGE, 0xb757) }, /* ATSC+DVBT sw */ + { USB_DEVICE(USB_VID_HAUPPAUGE, 0xb767) }, /* ATSC+DVBT no */ + {} /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(usb, mxl111sf_table); + + +#define MXL111SF_EP4_BULK_STREAMING_CONFIG \ + .streaming_ctrl = mxl111sf_ep4_streaming_ctrl, \ + .stream = { \ + .type = USB_BULK, \ + .count = 5, \ + .endpoint = 0x04, \ + .u = { \ + .bulk = { \ + .buffersize = 8192, \ + } \ + } \ + } + +/* FIXME: works for v6 but not v8 silicon */ +#define MXL111SF_EP4_ISOC_STREAMING_CONFIG \ + .streaming_ctrl = mxl111sf_ep4_streaming_ctrl, \ + .stream = { \ + .type = USB_ISOC, \ + .count = 5, \ + .endpoint = 0x04, \ + .u = { \ + .isoc = { \ + .framesperurb = 96, \ + /* FIXME: v6 SILICON: */ \ + .framesize = 564, \ + .interval = 1, \ + } \ + } \ + } + +#define MXL111SF_EP6_BULK_STREAMING_CONFIG \ + .streaming_ctrl = mxl111sf_ep6_streaming_ctrl, \ + .stream = { \ + .type = USB_BULK, \ + .count = 5, \ + .endpoint = 0x06, \ + .u = { \ + .bulk = { \ + .buffersize = 8192, \ + } \ + } \ + } + +/* FIXME */ +#define MXL111SF_EP6_ISOC_STREAMING_CONFIG \ + .streaming_ctrl = mxl111sf_ep6_streaming_ctrl, \ + .stream = { \ + .type = USB_ISOC, \ + .count = 5, \ + .endpoint = 0x06, \ + .u = { \ + .isoc = { \ + .framesperurb = 24, \ + .framesize = 3072, \ + .interval = 1, \ + } \ + } \ + } + +#define MXL111SF_DEFAULT_DEVICE_PROPERTIES \ + .caps = DVB_USB_IS_AN_I2C_ADAPTER, \ + .usb_ctrl = DEVICE_SPECIFIC, \ + /* use usb alt setting 1 for EP4 ISOC transfer (dvb-t), \ + EP6 BULK transfer (atsc/qam), \ + use usb alt setting 2 for EP4 BULK transfer (dvb-t), \ + EP6 ISOC transfer (atsc/qam), \ + */ \ + .power_ctrl = mxl111sf_power_ctrl, \ + .i2c_algo = &mxl111sf_i2c_algo, \ + .generic_bulk_ctrl_endpoint = MXL_EP2_REG_WRITE, \ + .generic_bulk_ctrl_endpoint_response = MXL_EP1_REG_READ, \ + .size_of_priv = sizeof(struct mxl111sf_state) + +static struct dvb_usb_device_properties mxl111sf_dvbt_bulk_properties = { + MXL111SF_DEFAULT_DEVICE_PROPERTIES, + + .num_adapters = 1, + .adapter = { + { + .fe_ioctl_override = mxl111sf_fe_ioctl_override, + .num_frontends = 1, + .fe = {{ + .size_of_priv = sizeof(struct mxl111sf_adap_state), + + .frontend_attach = mxl111sf_attach_demod, + .tuner_attach = mxl111sf_attach_tuner, + + MXL111SF_EP4_BULK_STREAMING_CONFIG, + } }, + }, + }, + .num_device_descs = 4, + .devices = { + { "Hauppauge 126xxx DVBT (bulk)", + { NULL }, + { &mxl111sf_table[4], &mxl111sf_table[8], + NULL }, + }, + { "Hauppauge 117xxx DVBT (bulk)", + { NULL }, + { &mxl111sf_table[15], &mxl111sf_table[18], + NULL }, + }, + { "Hauppauge 138xxx DVBT (bulk)", + { NULL }, + { &mxl111sf_table[20], &mxl111sf_table[22], + &mxl111sf_table[24], &mxl111sf_table[26], + NULL }, + }, + { "Hauppauge 126xxx (tp-bulk)", + { NULL }, + { &mxl111sf_table[28], &mxl111sf_table[30], + NULL }, + }, + } +}; + +static struct dvb_usb_device_properties mxl111sf_dvbt_isoc_properties = { + MXL111SF_DEFAULT_DEVICE_PROPERTIES, + + .num_adapters = 1, + .adapter = { + { + .fe_ioctl_override = mxl111sf_fe_ioctl_override, + .num_frontends = 1, + .fe = {{ + .size_of_priv = sizeof(struct mxl111sf_adap_state), + + .frontend_attach = mxl111sf_attach_demod, + .tuner_attach = mxl111sf_attach_tuner, + + MXL111SF_EP4_ISOC_STREAMING_CONFIG, + } }, + }, + }, + .num_device_descs = 4, + .devices = { + { "Hauppauge 126xxx DVBT (isoc)", + { NULL }, + { &mxl111sf_table[4], &mxl111sf_table[8], + NULL }, + }, + { "Hauppauge 117xxx DVBT (isoc)", + { NULL }, + { &mxl111sf_table[15], &mxl111sf_table[18], + NULL }, + }, + { "Hauppauge 138xxx DVBT (isoc)", + { NULL }, + { &mxl111sf_table[20], &mxl111sf_table[22], + &mxl111sf_table[24], &mxl111sf_table[26], + NULL }, + }, + { "Hauppauge 126xxx (tp-isoc)", + { NULL }, + { &mxl111sf_table[28], &mxl111sf_table[30], + NULL }, + }, + } +}; + +static struct dvb_usb_device_properties mxl111sf_atsc_bulk_properties = { + MXL111SF_DEFAULT_DEVICE_PROPERTIES, + + .num_adapters = 1, + .adapter = { + { + .fe_ioctl_override = mxl111sf_fe_ioctl_override, + .num_frontends = 2, + .fe = {{ + .size_of_priv = sizeof(struct mxl111sf_adap_state), + + .frontend_attach = mxl111sf_lgdt3305_frontend_attach, + .tuner_attach = mxl111sf_attach_tuner, + + MXL111SF_EP6_BULK_STREAMING_CONFIG, + }, + { + .size_of_priv = sizeof(struct mxl111sf_adap_state), + + .frontend_attach = mxl111sf_attach_demod, + .tuner_attach = mxl111sf_attach_tuner, + + MXL111SF_EP4_BULK_STREAMING_CONFIG, + }}, + }, + }, + .num_device_descs = 6, + .devices = { + { "Hauppauge 126xxx ATSC (bulk)", + { NULL }, + { &mxl111sf_table[1], &mxl111sf_table[5], + NULL }, + }, + { "Hauppauge 117xxx ATSC (bulk)", + { NULL }, + { &mxl111sf_table[12], + NULL }, + }, + { "Hauppauge 126xxx ATSC+ (bulk)", + { NULL }, + { &mxl111sf_table[0], &mxl111sf_table[3], + &mxl111sf_table[7], &mxl111sf_table[9], + &mxl111sf_table[10], NULL }, + }, + { "Hauppauge 117xxx ATSC+ (bulk)", + { NULL }, + { &mxl111sf_table[11], &mxl111sf_table[14], + &mxl111sf_table[16], &mxl111sf_table[17], + &mxl111sf_table[32], &mxl111sf_table[33], + NULL }, + }, + { "Hauppauge Mercury (tp-bulk)", + { NULL }, + { &mxl111sf_table[19], &mxl111sf_table[21], + &mxl111sf_table[23], &mxl111sf_table[25], + &mxl111sf_table[27], NULL }, + }, + { "Hauppauge WinTV-Aero-M", + { NULL }, + { &mxl111sf_table[29], &mxl111sf_table[31], + NULL }, + }, + } +}; + +static struct dvb_usb_device_properties mxl111sf_atsc_isoc_properties = { + MXL111SF_DEFAULT_DEVICE_PROPERTIES, + + .num_adapters = 1, + .adapter = { + { + .fe_ioctl_override = mxl111sf_fe_ioctl_override, + .num_frontends = 2, + .fe = {{ + .size_of_priv = sizeof(struct mxl111sf_adap_state), + + .frontend_attach = mxl111sf_lgdt3305_frontend_attach, + .tuner_attach = mxl111sf_attach_tuner, + + MXL111SF_EP6_ISOC_STREAMING_CONFIG, + }, + { + .size_of_priv = sizeof(struct mxl111sf_adap_state), + + .frontend_attach = mxl111sf_attach_demod, + .tuner_attach = mxl111sf_attach_tuner, + + MXL111SF_EP4_ISOC_STREAMING_CONFIG, + }}, + }, + }, + .num_device_descs = 6, + .devices = { + { "Hauppauge 126xxx ATSC (isoc)", + { NULL }, + { &mxl111sf_table[1], &mxl111sf_table[5], + NULL }, + }, + { "Hauppauge 117xxx ATSC (isoc)", + { NULL }, + { &mxl111sf_table[12], + NULL }, + }, + { "Hauppauge 126xxx ATSC+ (isoc)", + { NULL }, + { &mxl111sf_table[0], &mxl111sf_table[3], + &mxl111sf_table[7], &mxl111sf_table[9], + &mxl111sf_table[10], NULL }, + }, + { "Hauppauge 117xxx ATSC+ (isoc)", + { NULL }, + { &mxl111sf_table[11], &mxl111sf_table[14], + &mxl111sf_table[16], &mxl111sf_table[17], + &mxl111sf_table[32], &mxl111sf_table[33], + NULL }, + }, + { "Hauppauge Mercury (tp-isoc)", + { NULL }, + { &mxl111sf_table[19], &mxl111sf_table[21], + &mxl111sf_table[23], &mxl111sf_table[25], + &mxl111sf_table[27], NULL }, + }, + { "Hauppauge WinTV-Aero-M (tp-isoc)", + { NULL }, + { &mxl111sf_table[29], &mxl111sf_table[31], + NULL }, + }, + } +}; + +static struct usb_driver mxl111sf_driver = { + .name = "dvb_usb_mxl111sf", + .probe = mxl111sf_probe, + .disconnect = dvb_usb_device_exit, + .id_table = mxl111sf_table, +}; + +static int __init mxl111sf_module_init(void) +{ + int result = usb_register(&mxl111sf_driver); + if (result) { + err("usb_register failed. Error number %d", result); + return result; + } + + return 0; +} + +static void __exit mxl111sf_module_exit(void) +{ + usb_deregister(&mxl111sf_driver); +} + +module_init(mxl111sf_module_init); +module_exit(mxl111sf_module_exit); + +MODULE_AUTHOR("Michael Krufky <mkrufky@kernellabs.com>"); +MODULE_DESCRIPTION("Driver for MaxLinear MxL111SF"); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL"); + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/dvb/dvb-usb/mxl111sf.h b/drivers/media/dvb/dvb-usb/mxl111sf.h new file mode 100644 index 000000000000..364d89f826bd --- /dev/null +++ b/drivers/media/dvb/dvb-usb/mxl111sf.h @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2010 Michael Krufky (mkrufky@kernellabs.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, version 2. + * + * see Documentation/dvb/README.dvb-usb for more information + */ + +#ifndef _DVB_USB_MXL111SF_H_ +#define _DVB_USB_MXL111SF_H_ + +#ifdef DVB_USB_LOG_PREFIX +#undef DVB_USB_LOG_PREFIX +#endif +#define DVB_USB_LOG_PREFIX "mxl111sf" +#include "dvb-usb.h" +#include <media/tveeprom.h> + +#define MXL_EP1_REG_READ 1 +#define MXL_EP2_REG_WRITE 2 +#define MXL_EP3_INTERRUPT 3 +#define MXL_EP4_MPEG2 4 +#define MXL_EP5_I2S 5 +#define MXL_EP6_656 6 +#define MXL_EP6_MPEG2 6 + +#ifdef USING_ENUM_mxl111sf_current_mode +enum mxl111sf_current_mode { + mxl_mode_dvbt = MXL_EP4_MPEG2, + mxl_mode_mh = MXL_EP5_I2S, + mxl_mode_atsc = MXL_EP6_MPEG2, +}; +#endif + +enum mxl111sf_gpio_port_expander { + mxl111sf_gpio_hw, + mxl111sf_PCA9534, +}; + +struct mxl111sf_state { + struct dvb_usb_device *d; + + enum mxl111sf_gpio_port_expander gpio_port_expander; + u8 port_expander_addr; + + u8 chip_id; + u8 chip_ver; +#define MXL111SF_V6 1 +#define MXL111SF_V8_100 2 +#define MXL111SF_V8_200 3 + u8 chip_rev; + +#ifdef USING_ENUM_mxl111sf_current_mode + enum mxl111sf_current_mode current_mode; +#endif + +#define MXL_TUNER_MODE 0 +#define MXL_SOC_MODE 1 +#define MXL_DEV_MODE_MASK 0x01 +#if 1 + int device_mode; +#endif + /* use usb alt setting 1 for EP4 ISOC transfer (dvb-t), + EP5 BULK transfer (atsc-mh), + EP6 BULK transfer (atsc/qam), + use usb alt setting 2 for EP4 BULK transfer (dvb-t), + EP5 ISOC transfer (atsc-mh), + EP6 ISOC transfer (atsc/qam), + */ + int alt_mode; + int gpio_mode; + struct tveeprom tv; + + struct mutex fe_lock; +}; + +struct mxl111sf_adap_state { + int alt_mode; + int gpio_mode; + int device_mode; + int ep6_clockphase; + int (*fe_init)(struct dvb_frontend *); + int (*fe_sleep)(struct dvb_frontend *); +}; + +int mxl111sf_read_reg(struct mxl111sf_state *state, u8 addr, u8 *data); +int mxl111sf_write_reg(struct mxl111sf_state *state, u8 addr, u8 data); + +struct mxl111sf_reg_ctrl_info { + u8 addr; + u8 mask; + u8 data; +}; + +int mxl111sf_write_reg_mask(struct mxl111sf_state *state, + u8 addr, u8 mask, u8 data); +int mxl111sf_ctrl_program_regs(struct mxl111sf_state *state, + struct mxl111sf_reg_ctrl_info *ctrl_reg_info); + +/* needed for hardware i2c functions in mxl111sf-i2c.c: + * mxl111sf_i2c_send_data / mxl111sf_i2c_get_data */ +int mxl111sf_ctrl_msg(struct dvb_usb_device *d, + u8 cmd, u8 *wbuf, int wlen, u8 *rbuf, int rlen); + +#define mxl_printk(kern, fmt, arg...) \ + printk(kern "%s: " fmt "\n", __func__, ##arg) + +#define mxl_info(fmt, arg...) \ + mxl_printk(KERN_INFO, fmt, ##arg) + +extern int dvb_usb_mxl111sf_debug; +#define mxl_debug(fmt, arg...) \ + if (dvb_usb_mxl111sf_debug) \ + mxl_printk(KERN_DEBUG, fmt, ##arg) + +#define MXL_I2C_DBG 0x04 +#define MXL_ADV_DBG 0x10 +#define mxl_debug_adv(fmt, arg...) \ + if (dvb_usb_mxl111sf_debug & MXL_ADV_DBG) \ + mxl_printk(KERN_DEBUG, fmt, ##arg) + +#define mxl_i2c(fmt, arg...) \ + if (dvb_usb_mxl111sf_debug & MXL_I2C_DBG) \ + mxl_printk(KERN_DEBUG, fmt, ##arg) + +#define mxl_i2c_adv(fmt, arg...) \ + if ((dvb_usb_mxl111sf_debug & (MXL_I2C_DBG | MXL_ADV_DBG)) == \ + (MXL_I2C_DBG | MXL_ADV_DBG)) \ + mxl_printk(KERN_DEBUG, fmt, ##arg) + +/* The following allows the mxl_fail() macro defined below to work + * in externel modules, such as mxl111sf-tuner.ko, even though + * dvb_usb_mxl111sf_debug is not defined within those modules */ +#if (defined(__MXL111SF_TUNER_H__)) || (defined(__MXL111SF_DEMOD_H__)) +#define MXL_ADV_DEBUG_ENABLED MXL_ADV_DBG +#else +#define MXL_ADV_DEBUG_ENABLED dvb_usb_mxl111sf_debug +#endif + +#define mxl_fail(ret) \ +({ \ + int __ret; \ + __ret = (ret < 0); \ + if ((__ret) && (MXL_ADV_DEBUG_ENABLED & MXL_ADV_DBG)) \ + mxl_printk(KERN_ERR, "error %d on line %d", \ + ret, __LINE__); \ + __ret; \ +}) + +#endif /* _DVB_USB_MXL111SF_H_ */ + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/dvb/dvb-usb/nova-t-usb2.c b/drivers/media/dvb/dvb-usb/nova-t-usb2.c index bc350e982b72..21384da6570e 100644 --- a/drivers/media/dvb/dvb-usb/nova-t-usb2.c +++ b/drivers/media/dvb/dvb-usb/nova-t-usb2.c @@ -166,6 +166,8 @@ static struct dvb_usb_device_properties nova_t_properties = { .num_adapters = 1, .adapter = { { + .num_frontends = 1, + .fe = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, .pid_filter_count = 32, @@ -186,7 +188,7 @@ static struct dvb_usb_device_properties nova_t_properties = { } } }, - + }}, .size_of_priv = sizeof(struct dibusb_state), } }, diff --git a/drivers/media/dvb/dvb-usb/opera1.c b/drivers/media/dvb/dvb-usb/opera1.c index 2e4fab7215f5..98fd9a6092b7 100644 --- a/drivers/media/dvb/dvb-usb/opera1.c +++ b/drivers/media/dvb/dvb-usb/opera1.c @@ -263,10 +263,10 @@ static struct stv0299_config opera1_stv0299_config = { static int opera1_frontend_attach(struct dvb_usb_adapter *d) { - if ((d->fe = - dvb_attach(stv0299_attach, &opera1_stv0299_config, - &d->dev->i2c_adap)) != NULL) { - d->fe->ops.set_voltage = opera1_set_voltage; + d->fe_adap[0].fe = dvb_attach(stv0299_attach, &opera1_stv0299_config, + &d->dev->i2c_adap); + if ((d->fe_adap[0].fe) != NULL) { + d->fe_adap[0].fe->ops.set_voltage = opera1_set_voltage; return 0; } info("not attached stv0299"); @@ -276,7 +276,7 @@ static int opera1_frontend_attach(struct dvb_usb_adapter *d) static int opera1_tuner_attach(struct dvb_usb_adapter *adap) { dvb_attach( - dvb_pll_attach, adap->fe, 0xc0>>1, + dvb_pll_attach, adap->fe_adap[0].fe, 0xc0>>1, &adap->dev->i2c_adap, DVB_PLL_OPERA1 ); return 0; @@ -516,6 +516,8 @@ static struct dvb_usb_device_properties opera1_properties = { .num_adapters = 1, .adapter = { { + .num_frontends = 1, + .fe = {{ .frontend_attach = opera1_frontend_attach, .streaming_ctrl = opera1_streaming_ctrl, .tuner_attach = opera1_tuner_attach, @@ -535,6 +537,7 @@ static struct dvb_usb_device_properties opera1_properties = { } } }, + }}, } }, .num_device_descs = 1, diff --git a/drivers/media/dvb/dvb-usb/pctv452e.c b/drivers/media/dvb/dvb-usb/pctv452e.c new file mode 100644 index 000000000000..f9aec5cb6e77 --- /dev/null +++ b/drivers/media/dvb/dvb-usb/pctv452e.c @@ -0,0 +1,1079 @@ +/* + * PCTV 452e DVB driver + * + * Copyright (c) 2006-2008 Dominik Kuhlen <dkuhlen@gmx.net> + * + * TT connect S2-3650-CI Common Interface support, MAC readout + * Copyright (C) 2008 Michael H. Schimek <mschimek@gmx.at> + * + * 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. + */ + +/* dvb usb framework */ +#define DVB_USB_LOG_PREFIX "pctv452e" +#include "dvb-usb.h" + +/* Demodulator */ +#include "stb0899_drv.h" +#include "stb0899_reg.h" +#include "stb0899_cfg.h" +/* Tuner */ +#include "stb6100.h" +#include "stb6100_cfg.h" +/* FE Power */ +#include "lnbp22.h" + +#include "dvb_ca_en50221.h" +#include "ttpci-eeprom.h" + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +#define ISOC_INTERFACE_ALTERNATIVE 3 + +#define SYNC_BYTE_OUT 0xaa +#define SYNC_BYTE_IN 0x55 + +/* guessed: (copied from ttusb-budget) */ +#define PCTV_CMD_RESET 0x15 +/* command to poll IR receiver */ +#define PCTV_CMD_IR 0x1b +/* command to send I2C */ +#define PCTV_CMD_I2C 0x31 + +#define I2C_ADDR_STB0899 (0xd0 >> 1) +#define I2C_ADDR_STB6100 (0xc0 >> 1) +#define I2C_ADDR_LNBP22 (0x10 >> 1) +#define I2C_ADDR_24C16 (0xa0 >> 1) +#define I2C_ADDR_24C64 (0xa2 >> 1) + + +/* pctv452e sends us this amount of data for each issued usb-command */ +#define PCTV_ANSWER_LEN 64 +/* Wait up to 1000ms for device */ +#define PCTV_TIMEOUT 1000 + + +#define PCTV_LED_GPIO STB0899_GPIO01 +#define PCTV_LED_GREEN 0x82 +#define PCTV_LED_ORANGE 0x02 + +#define ci_dbg(format, arg...) \ +do { \ + if (0) \ + printk(KERN_DEBUG DVB_USB_LOG_PREFIX \ + ": " format "\n" , ## arg); \ +} while (0) + +enum { + TT3650_CMD_CI_TEST = 0x40, + TT3650_CMD_CI_RD_CTRL, + TT3650_CMD_CI_WR_CTRL, + TT3650_CMD_CI_RD_ATTR, + TT3650_CMD_CI_WR_ATTR, + TT3650_CMD_CI_RESET, + TT3650_CMD_CI_SET_VIDEO_PORT +}; + + +static struct stb0899_postproc pctv45e_postproc[] = { + { PCTV_LED_GPIO, STB0899_GPIOPULLUP }, + { 0, 0 } +}; + +/* + * stores all private variables for communication with the PCTV452e DVB-S2 + */ +struct pctv452e_state { + struct dvb_ca_en50221 ca; + struct mutex ca_mutex; + + u8 c; /* transaction counter, wraps around... */ + u8 initialized; /* set to 1 if 0x15 has been sent */ + u16 last_rc_key; +}; + +static int tt3650_ci_msg(struct dvb_usb_device *d, u8 cmd, u8 *data, + unsigned int write_len, unsigned int read_len) +{ + struct pctv452e_state *state = (struct pctv452e_state *)d->priv; + u8 buf[64]; + u8 id; + unsigned int rlen; + int ret; + + BUG_ON(NULL == data && 0 != (write_len | read_len)); + BUG_ON(write_len > 64 - 4); + BUG_ON(read_len > 64 - 4); + + id = state->c++; + + buf[0] = SYNC_BYTE_OUT; + buf[1] = id; + buf[2] = cmd; + buf[3] = write_len; + + memcpy(buf + 4, data, write_len); + + rlen = (read_len > 0) ? 64 : 0; + ret = dvb_usb_generic_rw(d, buf, 4 + write_len, + buf, rlen, /* delay_ms */ 0); + if (0 != ret) + goto failed; + + ret = -EIO; + if (SYNC_BYTE_IN != buf[0] || id != buf[1]) + goto failed; + + memcpy(data, buf + 4, read_len); + + return 0; + +failed: + err("CI error %d; %02X %02X %02X -> %02X %02X %02X.", + ret, SYNC_BYTE_OUT, id, cmd, buf[0], buf[1], buf[2]); + + return ret; +} + +static int tt3650_ci_msg_locked(struct dvb_ca_en50221 *ca, + u8 cmd, u8 *data, unsigned int write_len, + unsigned int read_len) +{ + struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data; + struct pctv452e_state *state = (struct pctv452e_state *)d->priv; + int ret; + + mutex_lock(&state->ca_mutex); + ret = tt3650_ci_msg(d, cmd, data, write_len, read_len); + mutex_unlock(&state->ca_mutex); + + return ret; +} + +static int tt3650_ci_read_attribute_mem(struct dvb_ca_en50221 *ca, + int slot, int address) +{ + u8 buf[3]; + int ret; + + if (0 != slot) + return -EINVAL; + + buf[0] = (address >> 8) & 0x0F; + buf[1] = address; + + ret = tt3650_ci_msg_locked(ca, TT3650_CMD_CI_RD_ATTR, buf, 2, 3); + + ci_dbg("%s %04x -> %d 0x%02x", + __func__, address, ret, buf[2]); + + if (ret < 0) + return ret; + + return buf[2]; +} + +static int tt3650_ci_write_attribute_mem(struct dvb_ca_en50221 *ca, + int slot, int address, u8 value) +{ + u8 buf[3]; + + ci_dbg("%s %d 0x%04x 0x%02x", + __func__, slot, address, value); + + if (0 != slot) + return -EINVAL; + + buf[0] = (address >> 8) & 0x0F; + buf[1] = address; + buf[2] = value; + + return tt3650_ci_msg_locked(ca, TT3650_CMD_CI_WR_ATTR, buf, 3, 3); +} + +static int tt3650_ci_read_cam_control(struct dvb_ca_en50221 *ca, + int slot, + u8 address) +{ + u8 buf[2]; + int ret; + + if (0 != slot) + return -EINVAL; + + buf[0] = address & 3; + + ret = tt3650_ci_msg_locked(ca, TT3650_CMD_CI_RD_CTRL, buf, 1, 2); + + ci_dbg("%s 0x%02x -> %d 0x%02x", + __func__, address, ret, buf[1]); + + if (ret < 0) + return ret; + + return buf[1]; +} + +static int tt3650_ci_write_cam_control(struct dvb_ca_en50221 *ca, + int slot, + u8 address, + u8 value) +{ + u8 buf[2]; + + ci_dbg("%s %d 0x%02x 0x%02x", + __func__, slot, address, value); + + if (0 != slot) + return -EINVAL; + + buf[0] = address; + buf[1] = value; + + return tt3650_ci_msg_locked(ca, TT3650_CMD_CI_WR_CTRL, buf, 2, 2); +} + +static int tt3650_ci_set_video_port(struct dvb_ca_en50221 *ca, + int slot, + int enable) +{ + u8 buf[1]; + int ret; + + ci_dbg("%s %d %d", __func__, slot, enable); + + if (0 != slot) + return -EINVAL; + + enable = !!enable; + buf[0] = enable; + + ret = tt3650_ci_msg_locked(ca, TT3650_CMD_CI_SET_VIDEO_PORT, buf, 1, 1); + if (ret < 0) + return ret; + + if (enable != buf[0]) { + err("CI not %sabled.", enable ? "en" : "dis"); + return -EIO; + } + + return 0; +} + +static int tt3650_ci_slot_shutdown(struct dvb_ca_en50221 *ca, int slot) +{ + return tt3650_ci_set_video_port(ca, slot, /* enable */ 0); +} + +static int tt3650_ci_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot) +{ + return tt3650_ci_set_video_port(ca, slot, /* enable */ 1); +} + +static int tt3650_ci_slot_reset(struct dvb_ca_en50221 *ca, int slot) +{ + struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data; + struct pctv452e_state *state = (struct pctv452e_state *)d->priv; + u8 buf[1]; + int ret; + + ci_dbg("%s %d", __func__, slot); + + if (0 != slot) + return -EINVAL; + + buf[0] = 0; + + mutex_lock(&state->ca_mutex); + + ret = tt3650_ci_msg(d, TT3650_CMD_CI_RESET, buf, 1, 1); + if (0 != ret) + goto failed; + + msleep(500); + + buf[0] = 1; + + ret = tt3650_ci_msg(d, TT3650_CMD_CI_RESET, buf, 1, 1); + if (0 != ret) + goto failed; + + msleep(500); + + buf[0] = 0; /* FTA */ + + ret = tt3650_ci_msg(d, TT3650_CMD_CI_SET_VIDEO_PORT, buf, 1, 1); + + failed: + mutex_unlock(&state->ca_mutex); + + return ret; +} + +static int tt3650_ci_poll_slot_status(struct dvb_ca_en50221 *ca, + int slot, + int open) +{ + u8 buf[1]; + int ret; + + if (0 != slot) + return -EINVAL; + + ret = tt3650_ci_msg_locked(ca, TT3650_CMD_CI_TEST, buf, 0, 1); + if (0 != ret) + return ret; + + if (1 == buf[0]) + return DVB_CA_EN50221_POLL_CAM_PRESENT | + DVB_CA_EN50221_POLL_CAM_READY; + + return 0; + +} + +static void tt3650_ci_uninit(struct dvb_usb_device *d) +{ + struct pctv452e_state *state; + + ci_dbg("%s", __func__); + + if (NULL == d) + return; + + state = (struct pctv452e_state *)d->priv; + if (NULL == state) + return; + + if (NULL == state->ca.data) + return; + + /* Error ignored. */ + tt3650_ci_set_video_port(&state->ca, /* slot */ 0, /* enable */ 0); + + dvb_ca_en50221_release(&state->ca); + + memset(&state->ca, 0, sizeof(state->ca)); +} + +static int tt3650_ci_init(struct dvb_usb_adapter *a) +{ + struct dvb_usb_device *d = a->dev; + struct pctv452e_state *state = (struct pctv452e_state *)d->priv; + int ret; + + ci_dbg("%s", __func__); + + mutex_init(&state->ca_mutex); + + state->ca.owner = THIS_MODULE; + state->ca.read_attribute_mem = tt3650_ci_read_attribute_mem; + state->ca.write_attribute_mem = tt3650_ci_write_attribute_mem; + state->ca.read_cam_control = tt3650_ci_read_cam_control; + state->ca.write_cam_control = tt3650_ci_write_cam_control; + state->ca.slot_reset = tt3650_ci_slot_reset; + state->ca.slot_shutdown = tt3650_ci_slot_shutdown; + state->ca.slot_ts_enable = tt3650_ci_slot_ts_enable; + state->ca.poll_slot_status = tt3650_ci_poll_slot_status; + state->ca.data = d; + + ret = dvb_ca_en50221_init(&a->dvb_adap, + &state->ca, + /* flags */ 0, + /* n_slots */ 1); + if (0 != ret) { + err("Cannot initialize CI: Error %d.", ret); + memset(&state->ca, 0, sizeof(state->ca)); + return ret; + } + + info("CI initialized."); + + return 0; +} + +#define CMD_BUFFER_SIZE 0x28 +static int pctv452e_i2c_msg(struct dvb_usb_device *d, u8 addr, + const u8 *snd_buf, u8 snd_len, + u8 *rcv_buf, u8 rcv_len) +{ + struct pctv452e_state *state = (struct pctv452e_state *)d->priv; + u8 buf[64]; + u8 id; + int ret; + + id = state->c++; + + ret = -EINVAL; + if (snd_len > 64 - 7 || rcv_len > 64 - 7) + goto failed; + + buf[0] = SYNC_BYTE_OUT; + buf[1] = id; + buf[2] = PCTV_CMD_I2C; + buf[3] = snd_len + 3; + buf[4] = addr << 1; + buf[5] = snd_len; + buf[6] = rcv_len; + + memcpy(buf + 7, snd_buf, snd_len); + + ret = dvb_usb_generic_rw(d, buf, 7 + snd_len, + buf, /* rcv_len */ 64, + /* delay_ms */ 0); + if (ret < 0) + goto failed; + + /* TT USB protocol error. */ + ret = -EIO; + if (SYNC_BYTE_IN != buf[0] || id != buf[1]) + goto failed; + + /* I2C device didn't respond as expected. */ + ret = -EREMOTEIO; + if (buf[5] < snd_len || buf[6] < rcv_len) + goto failed; + + memcpy(rcv_buf, buf + 7, rcv_len); + + return rcv_len; + +failed: + err("I2C error %d; %02X %02X %02X %02X %02X -> " + "%02X %02X %02X %02X %02X.", + ret, SYNC_BYTE_OUT, id, addr << 1, snd_len, rcv_len, + buf[0], buf[1], buf[4], buf[5], buf[6]); + + return ret; +} + +static int pctv452e_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msg, + int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adapter); + int i; + + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + for (i = 0; i < num; i++) { + u8 addr, snd_len, rcv_len, *snd_buf, *rcv_buf; + int ret; + + if (msg[i].flags & I2C_M_RD) { + addr = msg[i].addr; + snd_buf = NULL; + snd_len = 0; + rcv_buf = msg[i].buf; + rcv_len = msg[i].len; + } else { + addr = msg[i].addr; + snd_buf = msg[i].buf; + snd_len = msg[i].len; + rcv_buf = NULL; + rcv_len = 0; + } + + ret = pctv452e_i2c_msg(d, addr, snd_buf, snd_len, rcv_buf, + rcv_len); + if (ret < rcv_len) + break; + } + + mutex_unlock(&d->i2c_mutex); + return i; +} + +static u32 pctv452e_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static int pctv452e_power_ctrl(struct dvb_usb_device *d, int i) +{ + struct pctv452e_state *state = (struct pctv452e_state *)d->priv; + u8 b0[] = { 0xaa, 0, PCTV_CMD_RESET, 1, 0 }; + u8 rx[PCTV_ANSWER_LEN]; + int ret; + + info("%s: %d\n", __func__, i); + + if (!i) + return 0; + + if (state->initialized) + return 0; + + /* hmm where shoud this should go? */ + ret = usb_set_interface(d->udev, 0, ISOC_INTERFACE_ALTERNATIVE); + if (ret != 0) + info("%s: Warning set interface returned: %d\n", + __func__, ret); + + /* this is a one-time initialization, dont know where to put */ + b0[1] = state->c++; + /* reset board */ + ret = dvb_usb_generic_rw(d, b0, sizeof(b0), rx, PCTV_ANSWER_LEN, 0); + if (ret) + return ret; + + b0[1] = state->c++; + b0[4] = 1; + /* reset board (again?) */ + ret = dvb_usb_generic_rw(d, b0, sizeof(b0), rx, PCTV_ANSWER_LEN, 0); + if (ret) + return ret; + + state->initialized = 1; + + return 0; +} + +static int pctv452e_rc_query(struct dvb_usb_device *d) +{ + struct pctv452e_state *state = (struct pctv452e_state *)d->priv; + u8 b[CMD_BUFFER_SIZE]; + u8 rx[PCTV_ANSWER_LEN]; + int ret, i; + u8 id = state->c++; + + /* prepare command header */ + b[0] = SYNC_BYTE_OUT; + b[1] = id; + b[2] = PCTV_CMD_IR; + b[3] = 0; + + /* send ir request */ + ret = dvb_usb_generic_rw(d, b, 4, rx, PCTV_ANSWER_LEN, 0); + if (ret != 0) + return ret; + + if (debug > 3) { + info("%s: read: %2d: %02x %02x %02x: ", __func__, + ret, rx[0], rx[1], rx[2]); + for (i = 0; (i < rx[3]) && ((i+3) < PCTV_ANSWER_LEN); i++) + info(" %02x", rx[i+3]); + + info("\n"); + } + + if ((rx[3] == 9) && (rx[12] & 0x01)) { + /* got a "press" event */ + state->last_rc_key = (rx[7] << 8) | rx[6]; + if (debug > 2) + info("%s: cmd=0x%02x sys=0x%02x\n", + __func__, rx[6], rx[7]); + + rc_keydown(d->rc_dev, state->last_rc_key, 0); + } else if (state->last_rc_key) { + rc_keyup(d->rc_dev); + state->last_rc_key = 0; + } + + return 0; +} + +static int pctv452e_read_mac_address(struct dvb_usb_device *d, u8 mac[6]) +{ + const u8 mem_addr[] = { 0x1f, 0xcc }; + u8 encoded_mac[20]; + int ret; + + ret = -EAGAIN; + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + goto failed; + + ret = pctv452e_i2c_msg(d, I2C_ADDR_24C16, + mem_addr + 1, /* snd_len */ 1, + encoded_mac, /* rcv_len */ 20); + if (-EREMOTEIO == ret) + /* Caution! A 24C16 interprets 0xA2 0x1F 0xCC as a + byte write if /WC is low. */ + ret = pctv452e_i2c_msg(d, I2C_ADDR_24C64, + mem_addr, 2, + encoded_mac, 20); + + mutex_unlock(&d->i2c_mutex); + + if (20 != ret) + goto failed; + + ret = ttpci_eeprom_decode_mac(mac, encoded_mac); + if (0 != ret) + goto failed; + + return 0; + +failed: + memset(mac, 0, 6); + + return ret; +} + +static const struct stb0899_s1_reg pctv452e_init_dev[] = { + { STB0899_DISCNTRL1, 0x26 }, + { STB0899_DISCNTRL2, 0x80 }, + { STB0899_DISRX_ST0, 0x04 }, + { STB0899_DISRX_ST1, 0x20 }, + { STB0899_DISPARITY, 0x00 }, + { STB0899_DISFIFO, 0x00 }, + { STB0899_DISF22, 0x99 }, + { STB0899_DISF22RX, 0x85 }, /* 0xa8 */ + { STB0899_ACRPRESC, 0x11 }, + { STB0899_ACRDIV1, 0x0a }, + { STB0899_ACRDIV2, 0x05 }, + { STB0899_DACR1 , 0x00 }, + { STB0899_DACR2 , 0x00 }, + { STB0899_OUTCFG, 0x00 }, + { STB0899_MODECFG, 0x00 }, /* Inversion */ + { STB0899_IRQMSK_3, 0xf3 }, + { STB0899_IRQMSK_2, 0xfc }, + { STB0899_IRQMSK_1, 0xff }, + { STB0899_IRQMSK_0, 0xff }, + { STB0899_I2CCFG, 0x88 }, + { STB0899_I2CRPT, 0x58 }, + { STB0899_GPIO00CFG, 0x82 }, + { STB0899_GPIO01CFG, 0x82 }, /* LED: 0x02 green, 0x82 orange */ + { STB0899_GPIO02CFG, 0x82 }, + { STB0899_GPIO03CFG, 0x82 }, + { STB0899_GPIO04CFG, 0x82 }, + { STB0899_GPIO05CFG, 0x82 }, + { STB0899_GPIO06CFG, 0x82 }, + { STB0899_GPIO07CFG, 0x82 }, + { STB0899_GPIO08CFG, 0x82 }, + { STB0899_GPIO09CFG, 0x82 }, + { STB0899_GPIO10CFG, 0x82 }, + { STB0899_GPIO11CFG, 0x82 }, + { STB0899_GPIO12CFG, 0x82 }, + { STB0899_GPIO13CFG, 0x82 }, + { STB0899_GPIO14CFG, 0x82 }, + { STB0899_GPIO15CFG, 0x82 }, + { STB0899_GPIO16CFG, 0x82 }, + { STB0899_GPIO17CFG, 0x82 }, + { STB0899_GPIO18CFG, 0x82 }, + { STB0899_GPIO19CFG, 0x82 }, + { STB0899_GPIO20CFG, 0x82 }, + { STB0899_SDATCFG, 0xb8 }, + { STB0899_SCLTCFG, 0xba }, + { STB0899_AGCRFCFG, 0x1c }, /* 0x11 DVB-S; 0x1c DVB-S2 (1c, rjkm) */ + { STB0899_GPIO22, 0x82 }, + { STB0899_GPIO21, 0x91 }, + { STB0899_DIRCLKCFG, 0x82 }, + { STB0899_CLKOUT27CFG, 0x7e }, + { STB0899_STDBYCFG, 0x82 }, + { STB0899_CS0CFG, 0x82 }, + { STB0899_CS1CFG, 0x82 }, + { STB0899_DISEQCOCFG, 0x20 }, + { STB0899_NCOARSE, 0x15 }, /* 0x15 27Mhz, F/3 198MHz, F/6 108MHz */ + { STB0899_SYNTCTRL, 0x00 }, /* 0x00 CLKI, 0x02 XTALI */ + { STB0899_FILTCTRL, 0x00 }, + { STB0899_SYSCTRL, 0x00 }, + { STB0899_STOPCLK1, 0x20 }, /* orig: 0x00 budget-ci: 0x20 */ + { STB0899_STOPCLK2, 0x00 }, + { STB0899_INTBUFCTRL, 0x0a }, + { STB0899_AGC2I1, 0x00 }, + { STB0899_AGC2I2, 0x00 }, + { STB0899_AGCIQIN, 0x00 }, + { STB0899_TSTRES, 0x40 }, /* rjkm */ + { 0xffff, 0xff }, +}; + +static const struct stb0899_s1_reg pctv452e_init_s1_demod[] = { + { STB0899_DEMOD, 0x00 }, + { STB0899_RCOMPC, 0xc9 }, + { STB0899_AGC1CN, 0x01 }, + { STB0899_AGC1REF, 0x10 }, + { STB0899_RTC, 0x23 }, + { STB0899_TMGCFG, 0x4e }, + { STB0899_AGC2REF, 0x34 }, + { STB0899_TLSR, 0x84 }, + { STB0899_CFD, 0xf7 }, + { STB0899_ACLC, 0x87 }, + { STB0899_BCLC, 0x94 }, + { STB0899_EQON, 0x41 }, + { STB0899_LDT, 0xf1 }, + { STB0899_LDT2, 0xe3 }, + { STB0899_EQUALREF, 0xb4 }, + { STB0899_TMGRAMP, 0x10 }, + { STB0899_TMGTHD, 0x30 }, + { STB0899_IDCCOMP, 0xfd }, + { STB0899_QDCCOMP, 0xff }, + { STB0899_POWERI, 0x0c }, + { STB0899_POWERQ, 0x0f }, + { STB0899_RCOMP, 0x6c }, + { STB0899_AGCIQIN, 0x80 }, + { STB0899_AGC2I1, 0x06 }, + { STB0899_AGC2I2, 0x00 }, + { STB0899_TLIR, 0x30 }, + { STB0899_RTF, 0x7f }, + { STB0899_DSTATUS, 0x00 }, + { STB0899_LDI, 0xbc }, + { STB0899_CFRM, 0xea }, + { STB0899_CFRL, 0x31 }, + { STB0899_NIRM, 0x2b }, + { STB0899_NIRL, 0x80 }, + { STB0899_ISYMB, 0x1d }, + { STB0899_QSYMB, 0xa6 }, + { STB0899_SFRH, 0x2f }, + { STB0899_SFRM, 0x68 }, + { STB0899_SFRL, 0x40 }, + { STB0899_SFRUPH, 0x2f }, + { STB0899_SFRUPM, 0x68 }, + { STB0899_SFRUPL, 0x40 }, + { STB0899_EQUAI1, 0x02 }, + { STB0899_EQUAQ1, 0xff }, + { STB0899_EQUAI2, 0x04 }, + { STB0899_EQUAQ2, 0x05 }, + { STB0899_EQUAI3, 0x02 }, + { STB0899_EQUAQ3, 0xfd }, + { STB0899_EQUAI4, 0x03 }, + { STB0899_EQUAQ4, 0x07 }, + { STB0899_EQUAI5, 0x08 }, + { STB0899_EQUAQ5, 0xf5 }, + { STB0899_DSTATUS2, 0x00 }, + { STB0899_VSTATUS, 0x00 }, + { STB0899_VERROR, 0x86 }, + { STB0899_IQSWAP, 0x2a }, + { STB0899_ECNT1M, 0x00 }, + { STB0899_ECNT1L, 0x00 }, + { STB0899_ECNT2M, 0x00 }, + { STB0899_ECNT2L, 0x00 }, + { STB0899_ECNT3M, 0x0a }, + { STB0899_ECNT3L, 0xad }, + { STB0899_FECAUTO1, 0x06 }, + { STB0899_FECM, 0x01 }, + { STB0899_VTH12, 0xb0 }, + { STB0899_VTH23, 0x7a }, + { STB0899_VTH34, 0x58 }, + { STB0899_VTH56, 0x38 }, + { STB0899_VTH67, 0x34 }, + { STB0899_VTH78, 0x24 }, + { STB0899_PRVIT, 0xff }, + { STB0899_VITSYNC, 0x19 }, + { STB0899_RSULC, 0xb1 }, /* DVB = 0xb1, DSS = 0xa1 */ + { STB0899_TSULC, 0x42 }, + { STB0899_RSLLC, 0x41 }, + { STB0899_TSLPL, 0x12 }, + { STB0899_TSCFGH, 0x0c }, + { STB0899_TSCFGM, 0x00 }, + { STB0899_TSCFGL, 0x00 }, + { STB0899_TSOUT, 0x69 }, /* 0x0d for CAM */ + { STB0899_RSSYNCDEL, 0x00 }, + { STB0899_TSINHDELH, 0x02 }, + { STB0899_TSINHDELM, 0x00 }, + { STB0899_TSINHDELL, 0x00 }, + { STB0899_TSLLSTKM, 0x1b }, + { STB0899_TSLLSTKL, 0xb3 }, + { STB0899_TSULSTKM, 0x00 }, + { STB0899_TSULSTKL, 0x00 }, + { STB0899_PCKLENUL, 0xbc }, + { STB0899_PCKLENLL, 0xcc }, + { STB0899_RSPCKLEN, 0xbd }, + { STB0899_TSSTATUS, 0x90 }, + { STB0899_ERRCTRL1, 0xb6 }, + { STB0899_ERRCTRL2, 0x95 }, + { STB0899_ERRCTRL3, 0x8d }, + { STB0899_DMONMSK1, 0x27 }, + { STB0899_DMONMSK0, 0x03 }, + { STB0899_DEMAPVIT, 0x5c }, + { STB0899_PLPARM, 0x19 }, + { STB0899_PDELCTRL, 0x48 }, + { STB0899_PDELCTRL2, 0x00 }, + { STB0899_BBHCTRL1, 0x00 }, + { STB0899_BBHCTRL2, 0x00 }, + { STB0899_HYSTTHRESH, 0x77 }, + { STB0899_MATCSTM, 0x00 }, + { STB0899_MATCSTL, 0x00 }, + { STB0899_UPLCSTM, 0x00 }, + { STB0899_UPLCSTL, 0x00 }, + { STB0899_DFLCSTM, 0x00 }, + { STB0899_DFLCSTL, 0x00 }, + { STB0899_SYNCCST, 0x00 }, + { STB0899_SYNCDCSTM, 0x00 }, + { STB0899_SYNCDCSTL, 0x00 }, + { STB0899_ISI_ENTRY, 0x00 }, + { STB0899_ISI_BIT_EN, 0x00 }, + { STB0899_MATSTRM, 0xf0 }, + { STB0899_MATSTRL, 0x02 }, + { STB0899_UPLSTRM, 0x45 }, + { STB0899_UPLSTRL, 0x60 }, + { STB0899_DFLSTRM, 0xe3 }, + { STB0899_DFLSTRL, 0x00 }, + { STB0899_SYNCSTR, 0x47 }, + { STB0899_SYNCDSTRM, 0x05 }, + { STB0899_SYNCDSTRL, 0x18 }, + { STB0899_CFGPDELSTATUS1, 0x19 }, + { STB0899_CFGPDELSTATUS2, 0x2b }, + { STB0899_BBFERRORM, 0x00 }, + { STB0899_BBFERRORL, 0x01 }, + { STB0899_UPKTERRORM, 0x00 }, + { STB0899_UPKTERRORL, 0x00 }, + { 0xffff, 0xff }, +}; + +static struct stb0899_config stb0899_config = { + .init_dev = pctv452e_init_dev, + .init_s2_demod = stb0899_s2_init_2, + .init_s1_demod = pctv452e_init_s1_demod, + .init_s2_fec = stb0899_s2_init_4, + .init_tst = stb0899_s1_init_5, + + .demod_address = I2C_ADDR_STB0899, /* I2C Address */ + .block_sync_mode = STB0899_SYNC_FORCED, /* ? */ + + .xtal_freq = 27000000, /* Assume Hz ? */ + .inversion = IQ_SWAP_ON, /* ? */ + + .lo_clk = 76500000, + .hi_clk = 99000000, + + .ts_output_mode = 0, /* Use parallel mode */ + .clock_polarity = 0, + .data_clk_parity = 0, + .fec_mode = 0, + + .esno_ave = STB0899_DVBS2_ESNO_AVE, + .esno_quant = STB0899_DVBS2_ESNO_QUANT, + .avframes_coarse = STB0899_DVBS2_AVFRAMES_COARSE, + .avframes_fine = STB0899_DVBS2_AVFRAMES_FINE, + .miss_threshold = STB0899_DVBS2_MISS_THRESHOLD, + .uwp_threshold_acq = STB0899_DVBS2_UWP_THRESHOLD_ACQ, + .uwp_threshold_track = STB0899_DVBS2_UWP_THRESHOLD_TRACK, + .uwp_threshold_sof = STB0899_DVBS2_UWP_THRESHOLD_SOF, + .sof_search_timeout = STB0899_DVBS2_SOF_SEARCH_TIMEOUT, + + .btr_nco_bits = STB0899_DVBS2_BTR_NCO_BITS, + .btr_gain_shift_offset = STB0899_DVBS2_BTR_GAIN_SHIFT_OFFSET, + .crl_nco_bits = STB0899_DVBS2_CRL_NCO_BITS, + .ldpc_max_iter = STB0899_DVBS2_LDPC_MAX_ITER, + + .tuner_get_frequency = stb6100_get_frequency, + .tuner_set_frequency = stb6100_set_frequency, + .tuner_set_bandwidth = stb6100_set_bandwidth, + .tuner_get_bandwidth = stb6100_get_bandwidth, + .tuner_set_rfsiggain = NULL, + + /* helper for switching LED green/orange */ + .postproc = pctv45e_postproc +}; + +static struct stb6100_config stb6100_config = { + .tuner_address = I2C_ADDR_STB6100, + .refclock = 27000000 +}; + + +static struct i2c_algorithm pctv452e_i2c_algo = { + .master_xfer = pctv452e_i2c_xfer, + .functionality = pctv452e_i2c_func +}; + +static int pctv452e_frontend_attach(struct dvb_usb_adapter *a) +{ + struct usb_device_id *id; + + a->fe_adap[0].fe = dvb_attach(stb0899_attach, &stb0899_config, + &a->dev->i2c_adap); + if (!a->fe_adap[0].fe) + return -ENODEV; + if ((dvb_attach(lnbp22_attach, a->fe_adap[0].fe, + &a->dev->i2c_adap)) == 0) + err("Cannot attach lnbp22\n"); + + id = a->dev->desc->warm_ids[0]; + if (USB_VID_TECHNOTREND == id->idVendor + && USB_PID_TECHNOTREND_CONNECT_S2_3650_CI == id->idProduct) + /* Error ignored. */ + tt3650_ci_init(a); + + return 0; +} + +static int pctv452e_tuner_attach(struct dvb_usb_adapter *a) +{ + if (!a->fe_adap[0].fe) + return -ENODEV; + if (dvb_attach(stb6100_attach, a->fe_adap[0].fe, &stb6100_config, + &a->dev->i2c_adap) == 0) { + err("%s failed\n", __func__); + return -ENODEV; + } + + return 0; +} + +static struct usb_device_id pctv452e_usb_table[] = { + {USB_DEVICE(USB_VID_PINNACLE, USB_PID_PCTV_452E)}, + {USB_DEVICE(USB_VID_TECHNOTREND, USB_PID_TECHNOTREND_CONNECT_S2_3600)}, + {USB_DEVICE(USB_VID_TECHNOTREND, + USB_PID_TECHNOTREND_CONNECT_S2_3650_CI)}, + {} +}; +MODULE_DEVICE_TABLE(usb, pctv452e_usb_table); + +static struct dvb_usb_device_properties pctv452e_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, /* more ? */ + .usb_ctrl = DEVICE_SPECIFIC, + + .size_of_priv = sizeof(struct pctv452e_state), + + .power_ctrl = pctv452e_power_ctrl, + + .rc.core = { + .rc_codes = RC_MAP_DIB0700_RC5_TABLE, + .allowed_protos = RC_TYPE_UNKNOWN, + .rc_query = pctv452e_rc_query, + .rc_interval = 100, + }, + + .num_adapters = 1, + .adapter = {{ + .num_frontends = 1, + .fe = {{ + .frontend_attach = pctv452e_frontend_attach, + .tuner_attach = pctv452e_tuner_attach, + + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_ISOC, + .count = 4, + .endpoint = 0x02, + .u = { + .isoc = { + .framesperurb = 4, + .framesize = 940, + .interval = 1 + } + } + }, + } }, + } }, + + .i2c_algo = &pctv452e_i2c_algo, + + .generic_bulk_ctrl_endpoint = 1, /* allow generice rw function */ + + .num_device_descs = 1, + .devices = { + { .name = "PCTV HDTV USB", + .cold_ids = { NULL, NULL }, /* this is a warm only device */ + .warm_ids = { &pctv452e_usb_table[0], NULL } + }, + { 0 }, + } +}; + +static struct dvb_usb_device_properties tt_connect_s2_3600_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, /* more ? */ + .usb_ctrl = DEVICE_SPECIFIC, + + .size_of_priv = sizeof(struct pctv452e_state), + + .power_ctrl = pctv452e_power_ctrl, + .read_mac_address = pctv452e_read_mac_address, + + .rc.core = { + .rc_codes = RC_MAP_TT_1500, + .allowed_protos = RC_TYPE_UNKNOWN, + .rc_query = pctv452e_rc_query, + .rc_interval = 100, + }, + + .num_adapters = 1, + .adapter = {{ + .num_frontends = 1, + .fe = {{ + .frontend_attach = pctv452e_frontend_attach, + .tuner_attach = pctv452e_tuner_attach, + + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_ISOC, + .count = 7, + .endpoint = 0x02, + .u = { + .isoc = { + .framesperurb = 4, + .framesize = 940, + .interval = 1 + } + } + }, + + } }, + } }, + + .i2c_algo = &pctv452e_i2c_algo, + + .generic_bulk_ctrl_endpoint = 1, /* allow generic rw function*/ + + .num_device_descs = 2, + .devices = { + { .name = "Technotrend TT Connect S2-3600", + .cold_ids = { NULL, NULL }, /* this is a warm only device */ + .warm_ids = { &pctv452e_usb_table[1], NULL } + }, + { .name = "Technotrend TT Connect S2-3650-CI", + .cold_ids = { NULL, NULL }, + .warm_ids = { &pctv452e_usb_table[2], NULL } + }, + { 0 }, + } +}; + +static void pctv452e_usb_disconnect(struct usb_interface *intf) +{ + struct dvb_usb_device *d = usb_get_intfdata(intf); + + tt3650_ci_uninit(d); + dvb_usb_device_exit(intf); +} + +static int pctv452e_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + if (0 == dvb_usb_device_init(intf, &pctv452e_properties, + THIS_MODULE, NULL, adapter_nr) || + 0 == dvb_usb_device_init(intf, &tt_connect_s2_3600_properties, + THIS_MODULE, NULL, adapter_nr)) + return 0; + + return -ENODEV; +} + +static struct usb_driver pctv452e_usb_driver = { + .name = "pctv452e", + .probe = pctv452e_usb_probe, + .disconnect = pctv452e_usb_disconnect, + .id_table = pctv452e_usb_table, +}; + +static int __init pctv452e_usb_init(void) +{ + int ret = usb_register(&pctv452e_usb_driver); + if (ret) + err("%s: usb_register failed! Error %d", __FILE__, ret); + + return ret; +} + +static void __exit pctv452e_usb_exit(void) +{ + usb_deregister(&pctv452e_usb_driver); +} + +module_init(pctv452e_usb_init); +module_exit(pctv452e_usb_exit); + +MODULE_AUTHOR("Dominik Kuhlen <dkuhlen@gmx.net>"); +MODULE_AUTHOR("Andre Weidemann <Andre.Weidemann@web.de>"); +MODULE_AUTHOR("Michael H. Schimek <mschimek@gmx.at>"); +MODULE_DESCRIPTION("Pinnacle PCTV HDTV USB DVB / TT connect S2-3600 Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/dvb-usb/technisat-usb2.c b/drivers/media/dvb/dvb-usb/technisat-usb2.c index 473b95ed4d52..0998fe961950 100644 --- a/drivers/media/dvb/dvb-usb/technisat-usb2.c +++ b/drivers/media/dvb/dvb-usb/technisat-usb2.c @@ -292,7 +292,7 @@ static void technisat_usb2_green_led_control(struct work_struct *work) { struct technisat_usb2_state *state = container_of(work, struct technisat_usb2_state, green_led_work.work); - struct dvb_frontend *fe = state->dev->adapter[0].fe; + struct dvb_frontend *fe = state->dev->adapter[0].fe_adap[0].fe; if (state->power_state == 0) goto schedule; @@ -505,14 +505,14 @@ static int technisat_usb2_frontend_attach(struct dvb_usb_adapter *a) struct usb_device *udev = a->dev->udev; int ret; - a->fe = dvb_attach(stv090x_attach, &technisat_usb2_stv090x_config, + a->fe_adap[0].fe = dvb_attach(stv090x_attach, &technisat_usb2_stv090x_config, &a->dev->i2c_adap, STV090x_DEMODULATOR_0); - if (a->fe) { + if (a->fe_adap[0].fe) { struct stv6110x_devctl *ctl; ctl = dvb_attach(stv6110x_attach, - a->fe, + a->fe_adap[0].fe, &technisat_usb2_stv6110x_config, &a->dev->i2c_adap); @@ -532,8 +532,8 @@ static int technisat_usb2_frontend_attach(struct dvb_usb_adapter *a) /* call the init function once to initialize tuner's clock output divider and demod's master clock */ - if (a->fe->ops.init) - a->fe->ops.init(a->fe); + if (a->fe_adap[0].fe->ops.init) + a->fe_adap[0].fe->ops.init(a->fe_adap[0].fe); if (mutex_lock_interruptible(&a->dev->i2c_mutex) < 0) return -EAGAIN; @@ -548,20 +548,20 @@ static int technisat_usb2_frontend_attach(struct dvb_usb_adapter *a) if (ret != 0) err("could not set IF_CLK to external"); - a->fe->ops.set_voltage = technisat_usb2_set_voltage; + a->fe_adap[0].fe->ops.set_voltage = technisat_usb2_set_voltage; /* if everything was successful assign a nice name to the frontend */ - strlcpy(a->fe->ops.info.name, a->dev->desc->name, - sizeof(a->fe->ops.info.name)); + strlcpy(a->fe_adap[0].fe->ops.info.name, a->dev->desc->name, + sizeof(a->fe_adap[0].fe->ops.info.name)); } else { - dvb_frontend_detach(a->fe); - a->fe = NULL; + dvb_frontend_detach(a->fe_adap[0].fe); + a->fe_adap[0].fe = NULL; } } technisat_usb2_set_led_timer(a->dev, 1, 1); - return a->fe == NULL ? -ENODEV : 0; + return a->fe_adap[0].fe == NULL ? -ENODEV : 0; } /* Remote control */ @@ -697,6 +697,8 @@ static struct dvb_usb_device_properties technisat_usb2_devices = { .num_adapters = 1, .adapter = { { + .num_frontends = 1, + .fe = {{ .frontend_attach = technisat_usb2_frontend_attach, .stream = { @@ -711,7 +713,7 @@ static struct dvb_usb_device_properties technisat_usb2_devices = { } } }, - + }}, .size_of_priv = 0, }, }, diff --git a/drivers/media/dvb/dvb-usb/ttusb2.c b/drivers/media/dvb/dvb-usb/ttusb2.c index 0d4709ff9cbb..ea4eab8b3965 100644 --- a/drivers/media/dvb/dvb-usb/ttusb2.c +++ b/drivers/media/dvb/dvb-usb/ttusb2.c @@ -30,18 +30,43 @@ #include "tda826x.h" #include "tda10086.h" #include "tda1002x.h" +#include "tda10048.h" #include "tda827x.h" #include "lnbp21.h" +/* CA */ +#include "dvb_ca_en50221.h" /* debug */ static int dvb_usb_ttusb2_debug; #define deb_info(args...) dprintk(dvb_usb_ttusb2_debug,0x01,args) module_param_named(debug,dvb_usb_ttusb2_debug, int, 0644); MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able))." DVB_USB_DEBUG_STATUS); +static int dvb_usb_ttusb2_debug_ci; +module_param_named(debug_ci,dvb_usb_ttusb2_debug_ci, int, 0644); +MODULE_PARM_DESC(debug_ci, "set debugging ci." DVB_USB_DEBUG_STATUS); DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); +#define ci_dbg(format, arg...) \ +do { \ + if (dvb_usb_ttusb2_debug_ci) \ + printk(KERN_DEBUG DVB_USB_LOG_PREFIX \ + ": %s " format "\n" , __func__, ## arg); \ +} while (0) + +enum { + TT3650_CMD_CI_TEST = 0x40, + TT3650_CMD_CI_RD_CTRL, + TT3650_CMD_CI_WR_CTRL, + TT3650_CMD_CI_RD_ATTR, + TT3650_CMD_CI_WR_ATTR, + TT3650_CMD_CI_RESET, + TT3650_CMD_CI_SET_VIDEO_PORT +}; + struct ttusb2_state { + struct dvb_ca_en50221 ca; + struct mutex ca_mutex; u8 id; u16 last_rc_key; }; @@ -78,11 +103,260 @@ static int ttusb2_msg(struct dvb_usb_device *d, u8 cmd, return 0; } +/* ci */ +static int tt3650_ci_msg(struct dvb_usb_device *d, u8 cmd, u8 *data, unsigned int write_len, unsigned int read_len) +{ + int ret; + u8 rx[60];/* (64 -4) */ + ret = ttusb2_msg(d, cmd, data, write_len, rx, read_len); + if (!ret) + memcpy(data, rx, read_len); + return ret; +} + +static int tt3650_ci_msg_locked(struct dvb_ca_en50221 *ca, u8 cmd, u8 *data, unsigned int write_len, unsigned int read_len) +{ + struct dvb_usb_device *d = ca->data; + struct ttusb2_state *state = d->priv; + int ret; + + mutex_lock(&state->ca_mutex); + ret = tt3650_ci_msg(d, cmd, data, write_len, read_len); + mutex_unlock(&state->ca_mutex); + + return ret; +} + +static int tt3650_ci_read_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address) +{ + u8 buf[3]; + int ret = 0; + + if (slot) + return -EINVAL; + + buf[0] = (address >> 8) & 0x0F; + buf[1] = address; + + + ret = tt3650_ci_msg_locked(ca, TT3650_CMD_CI_RD_ATTR, buf, 2, 3); + + ci_dbg("%04x -> %d 0x%02x", address, ret, buf[2]); + + if (ret < 0) + return ret; + + return buf[2]; +} + +static int tt3650_ci_write_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address, u8 value) +{ + u8 buf[3]; + + ci_dbg("%d 0x%04x 0x%02x", slot, address, value); + + if (slot) + return -EINVAL; + + buf[0] = (address >> 8) & 0x0F; + buf[1] = address; + buf[2] = value; + + return tt3650_ci_msg_locked(ca, TT3650_CMD_CI_WR_ATTR, buf, 3, 3); +} + +static int tt3650_ci_read_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address) +{ + u8 buf[2]; + int ret; + + if (slot) + return -EINVAL; + + buf[0] = address & 3; + + ret = tt3650_ci_msg_locked(ca, TT3650_CMD_CI_RD_CTRL, buf, 1, 2); + + ci_dbg("0x%02x -> %d 0x%02x", address, ret, buf[1]); + + if (ret < 0) + return ret; + + return buf[1]; +} + +static int tt3650_ci_write_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address, u8 value) +{ + u8 buf[2]; + + ci_dbg("%d 0x%02x 0x%02x", slot, address, value); + + if (slot) + return -EINVAL; + + buf[0] = address; + buf[1] = value; + + return tt3650_ci_msg_locked(ca, TT3650_CMD_CI_WR_CTRL, buf, 2, 2); +} + +static int tt3650_ci_set_video_port(struct dvb_ca_en50221 *ca, int slot, int enable) +{ + u8 buf[1]; + int ret; + + ci_dbg("%d %d", slot, enable); + + if (slot) + return -EINVAL; + + buf[0] = enable; + + ret = tt3650_ci_msg_locked(ca, TT3650_CMD_CI_SET_VIDEO_PORT, buf, 1, 1); + if (ret < 0) + return ret; + + if (enable != buf[0]) { + err("CI not %sabled.", enable ? "en" : "dis"); + return -EIO; + } + + return 0; +} + +static int tt3650_ci_slot_shutdown(struct dvb_ca_en50221 *ca, int slot) +{ + return tt3650_ci_set_video_port(ca, slot, 0); +} + +static int tt3650_ci_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot) +{ + return tt3650_ci_set_video_port(ca, slot, 1); +} + +static int tt3650_ci_slot_reset(struct dvb_ca_en50221 *ca, int slot) +{ + struct dvb_usb_device *d = ca->data; + struct ttusb2_state *state = d->priv; + u8 buf[1]; + int ret; + + ci_dbg("%d", slot); + + if (slot) + return -EINVAL; + + buf[0] = 0; + + mutex_lock(&state->ca_mutex); + + ret = tt3650_ci_msg(d, TT3650_CMD_CI_RESET, buf, 1, 1); + if (ret) + goto failed; + + msleep(500); + + buf[0] = 1; + + ret = tt3650_ci_msg(d, TT3650_CMD_CI_RESET, buf, 1, 1); + if (ret) + goto failed; + + msleep(500); + + buf[0] = 0; /* FTA */ + + ret = tt3650_ci_msg(d, TT3650_CMD_CI_SET_VIDEO_PORT, buf, 1, 1); + + msleep(1100); + + failed: + mutex_unlock(&state->ca_mutex); + + return ret; +} + +static int tt3650_ci_poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open) +{ + u8 buf[1]; + int ret; + + if (slot) + return -EINVAL; + + ret = tt3650_ci_msg_locked(ca, TT3650_CMD_CI_TEST, buf, 0, 1); + if (ret) + return ret; + + if (1 == buf[0]) { + return DVB_CA_EN50221_POLL_CAM_PRESENT | + DVB_CA_EN50221_POLL_CAM_READY; + } + return 0; +} + +static void tt3650_ci_uninit(struct dvb_usb_device *d) +{ + struct ttusb2_state *state; + + ci_dbg(""); + + if (NULL == d) + return; + + state = d->priv; + if (NULL == state) + return; + + if (NULL == state->ca.data) + return; + + dvb_ca_en50221_release(&state->ca); + + memset(&state->ca, 0, sizeof(state->ca)); +} + +static int tt3650_ci_init(struct dvb_usb_adapter *a) +{ + struct dvb_usb_device *d = a->dev; + struct ttusb2_state *state = d->priv; + int ret; + + ci_dbg(""); + + mutex_init(&state->ca_mutex); + + state->ca.owner = THIS_MODULE; + state->ca.read_attribute_mem = tt3650_ci_read_attribute_mem; + state->ca.write_attribute_mem = tt3650_ci_write_attribute_mem; + state->ca.read_cam_control = tt3650_ci_read_cam_control; + state->ca.write_cam_control = tt3650_ci_write_cam_control; + state->ca.slot_reset = tt3650_ci_slot_reset; + state->ca.slot_shutdown = tt3650_ci_slot_shutdown; + state->ca.slot_ts_enable = tt3650_ci_slot_ts_enable; + state->ca.poll_slot_status = tt3650_ci_poll_slot_status; + state->ca.data = d; + + ret = dvb_ca_en50221_init(&a->dvb_adap, + &state->ca, + /* flags */ 0, + /* n_slots */ 1); + if (ret) { + err("Cannot initialize CI: Error %d.", ret); + memset(&state->ca, 0, sizeof(state->ca)); + return ret; + } + + info("CI initialized."); + + return 0; +} + static int ttusb2_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg msg[],int num) { struct dvb_usb_device *d = i2c_get_adapdata(adap); static u8 obuf[60], ibuf[60]; - int i,read; + int i, write_read, read; if (mutex_lock_interruptible(&d->i2c_mutex) < 0) return -EAGAIN; @@ -91,28 +365,35 @@ static int ttusb2_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg msg[],int num warn("more than 2 i2c messages at a time is not handled yet. TODO."); for (i = 0; i < num; i++) { - read = i+1 < num && (msg[i+1].flags & I2C_M_RD); + write_read = i+1 < num && (msg[i+1].flags & I2C_M_RD); + read = msg[i].flags & I2C_M_RD; - obuf[0] = (msg[i].addr << 1) | read; - obuf[1] = msg[i].len; + obuf[0] = (msg[i].addr << 1) | (write_read | read); + if (read) + obuf[1] = 0; + else + obuf[1] = msg[i].len; /* read request */ - if (read) + if (write_read) obuf[2] = msg[i+1].len; + else if (read) + obuf[2] = msg[i].len; else obuf[2] = 0; - memcpy(&obuf[3],msg[i].buf,msg[i].len); + memcpy(&obuf[3], msg[i].buf, msg[i].len); if (ttusb2_msg(d, CMD_I2C_XFER, obuf, msg[i].len+3, ibuf, obuf[2] + 3) < 0) { err("i2c transfer failed."); break; } - if (read) { - memcpy(msg[i+1].buf,&ibuf[3],msg[i+1].len); + if (write_read) { + memcpy(msg[i+1].buf, &ibuf[3], msg[i+1].len); i++; - } + } else if (read) + memcpy(msg[i].buf, &ibuf[3], msg[i].len); } mutex_unlock(&d->i2c_mutex); @@ -190,12 +471,31 @@ static struct tda10023_config tda10023_config = { .deltaf = 0xa511, }; +static struct tda10048_config tda10048_config = { + .demod_address = 0x10 >> 1, + .output_mode = TDA10048_PARALLEL_OUTPUT, + .inversion = TDA10048_INVERSION_ON, + .dtv6_if_freq_khz = TDA10048_IF_4000, + .dtv7_if_freq_khz = TDA10048_IF_4500, + .dtv8_if_freq_khz = TDA10048_IF_5000, + .clk_freq_khz = TDA10048_CLK_16000, + .no_firmware = 1, + .set_pll = true , + .pll_m = 5, + .pll_n = 3, + .pll_p = 0, +}; + +static struct tda827x_config tda827x_config = { + .config = 0, +}; + static int ttusb2_frontend_tda10086_attach(struct dvb_usb_adapter *adap) { if (usb_set_interface(adap->dev->udev,0,3) < 0) err("set interface to alts=3 failed"); - if ((adap->fe = dvb_attach(tda10086_attach, &tda10086_config, &adap->dev->i2c_adap)) == NULL) { + if ((adap->fe_adap[0].fe = dvb_attach(tda10086_attach, &tda10086_config, &adap->dev->i2c_adap)) == NULL) { deb_info("TDA10086 attach failed\n"); return -ENODEV; } @@ -203,20 +503,57 @@ static int ttusb2_frontend_tda10086_attach(struct dvb_usb_adapter *adap) return 0; } +static int ttusb2_ct3650_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +{ + struct dvb_usb_adapter *adap = fe->dvb->priv; + + return adap->fe_adap[0].fe->ops.i2c_gate_ctrl(adap->fe_adap[0].fe, enable); +} + static int ttusb2_frontend_tda10023_attach(struct dvb_usb_adapter *adap) { if (usb_set_interface(adap->dev->udev, 0, 3) < 0) err("set interface to alts=3 failed"); - if ((adap->fe = dvb_attach(tda10023_attach, &tda10023_config, &adap->dev->i2c_adap, 0x48)) == NULL) { - deb_info("TDA10023 attach failed\n"); - return -ENODEV; + + if (adap->fe_adap[0].fe == NULL) { + /* FE 0 DVB-C */ + adap->fe_adap[0].fe = dvb_attach(tda10023_attach, + &tda10023_config, &adap->dev->i2c_adap, 0x48); + + if (adap->fe_adap[0].fe == NULL) { + deb_info("TDA10023 attach failed\n"); + return -ENODEV; + } + tt3650_ci_init(adap); + } else { + adap->fe_adap[1].fe = dvb_attach(tda10048_attach, + &tda10048_config, &adap->dev->i2c_adap); + + if (adap->fe_adap[1].fe == NULL) { + deb_info("TDA10048 attach failed\n"); + return -ENODEV; + } + + /* tuner is behind TDA10023 I2C-gate */ + adap->fe_adap[1].fe->ops.i2c_gate_ctrl = ttusb2_ct3650_i2c_gate_ctrl; + } + return 0; } static int ttusb2_tuner_tda827x_attach(struct dvb_usb_adapter *adap) { - if (dvb_attach(tda827x_attach, adap->fe, 0x61, &adap->dev->i2c_adap, NULL) == NULL) { + struct dvb_frontend *fe; + + /* MFE: select correct FE to attach tuner since that's called twice */ + if (adap->fe_adap[1].fe == NULL) + fe = adap->fe_adap[0].fe; + else + fe = adap->fe_adap[1].fe; + + /* attach tuner */ + if (dvb_attach(tda827x_attach, fe, 0x61, &adap->dev->i2c_adap, &tda827x_config) == NULL) { printk(KERN_ERR "%s: No tda827x found!\n", __func__); return -ENODEV; } @@ -225,12 +562,12 @@ static int ttusb2_tuner_tda827x_attach(struct dvb_usb_adapter *adap) static int ttusb2_tuner_tda826x_attach(struct dvb_usb_adapter *adap) { - if (dvb_attach(tda826x_attach, adap->fe, 0x60, &adap->dev->i2c_adap, 0) == NULL) { + if (dvb_attach(tda826x_attach, adap->fe_adap[0].fe, 0x60, &adap->dev->i2c_adap, 0) == NULL) { deb_info("TDA8263 attach failed\n"); return -ENODEV; } - if (dvb_attach(lnbp21_attach, adap->fe, &adap->dev->i2c_adap, 0, 0) == NULL) { + if (dvb_attach(lnbp21_attach, adap->fe_adap[0].fe, &adap->dev->i2c_adap, 0, 0) == NULL) { deb_info("LNBP21 attach failed\n"); return -ENODEV; } @@ -242,6 +579,14 @@ static struct dvb_usb_device_properties ttusb2_properties; static struct dvb_usb_device_properties ttusb2_properties_s2400; static struct dvb_usb_device_properties ttusb2_properties_ct3650; +static void ttusb2_usb_disconnect(struct usb_interface *intf) +{ + struct dvb_usb_device *d = usb_get_intfdata(intf); + + tt3650_ci_uninit(d); + dvb_usb_device_exit(intf); +} + static int ttusb2_probe(struct usb_interface *intf, const struct usb_device_id *id) { @@ -277,6 +622,8 @@ static struct dvb_usb_device_properties ttusb2_properties = { .num_adapters = 1, .adapter = { { + .num_frontends = 1, + .fe = {{ .streaming_ctrl = NULL, // ttusb2_streaming_ctrl, .frontend_attach = ttusb2_frontend_tda10086_attach, @@ -295,6 +642,7 @@ static struct dvb_usb_device_properties ttusb2_properties = { } } } + }}, } }, @@ -329,6 +677,8 @@ static struct dvb_usb_device_properties ttusb2_properties_s2400 = { .num_adapters = 1, .adapter = { { + .num_frontends = 1, + .fe = {{ .streaming_ctrl = NULL, .frontend_attach = ttusb2_frontend_tda10086_attach, @@ -347,6 +697,7 @@ static struct dvb_usb_device_properties ttusb2_properties_s2400 = { } } } + }}, } }, @@ -383,6 +734,27 @@ static struct dvb_usb_device_properties ttusb2_properties_ct3650 = { .num_adapters = 1, .adapter = { { + .num_frontends = 2, + .fe = {{ + .streaming_ctrl = NULL, + + .frontend_attach = ttusb2_frontend_tda10023_attach, + .tuner_attach = ttusb2_tuner_tda827x_attach, + + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_ISOC, + .count = 5, + .endpoint = 0x02, + .u = { + .isoc = { + .framesperurb = 4, + .framesize = 940, + .interval = 1, + } + } + } + }, { .streaming_ctrl = NULL, .frontend_attach = ttusb2_frontend_tda10023_attach, @@ -401,6 +773,7 @@ static struct dvb_usb_device_properties ttusb2_properties_ct3650 = { } } } + }}, }, }, @@ -422,7 +795,7 @@ static struct dvb_usb_device_properties ttusb2_properties_ct3650 = { static struct usb_driver ttusb2_driver = { .name = "dvb_usb_ttusb2", .probe = ttusb2_probe, - .disconnect = dvb_usb_device_exit, + .disconnect = ttusb2_usb_disconnect, .id_table = ttusb2_table, }; diff --git a/drivers/media/dvb/dvb-usb/umt-010.c b/drivers/media/dvb/dvb-usb/umt-010.c index 118aab1a3e54..463673a5c2b2 100644 --- a/drivers/media/dvb/dvb-usb/umt-010.c +++ b/drivers/media/dvb/dvb-usb/umt-010.c @@ -60,14 +60,14 @@ static int umt_mt352_frontend_attach(struct dvb_usb_adapter *adap) umt_config.demod_init = umt_mt352_demod_init; umt_config.demod_address = 0xf; - adap->fe = dvb_attach(mt352_attach, &umt_config, &adap->dev->i2c_adap); + adap->fe_adap[0].fe = dvb_attach(mt352_attach, &umt_config, &adap->dev->i2c_adap); return 0; } static int umt_tuner_attach (struct dvb_usb_adapter *adap) { - dvb_attach(dvb_pll_attach, adap->fe, 0x61, NULL, DVB_PLL_TUA6034); + dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x61, NULL, DVB_PLL_TUA6034); return 0; } @@ -100,6 +100,8 @@ static struct dvb_usb_device_properties umt_properties = { .num_adapters = 1, .adapter = { { + .num_frontends = 1, + .fe = {{ .streaming_ctrl = dibusb2_0_streaming_ctrl, .frontend_attach = umt_mt352_frontend_attach, .tuner_attach = umt_tuner_attach, @@ -115,7 +117,7 @@ static struct dvb_usb_device_properties umt_properties = { } } }, - + }}, .size_of_priv = sizeof(struct dibusb_state), } }, diff --git a/drivers/media/dvb/dvb-usb/usb-urb.c b/drivers/media/dvb/dvb-usb/usb-urb.c index 86d68933b6b4..d62ee0f5a165 100644 --- a/drivers/media/dvb/dvb-usb/usb-urb.c +++ b/drivers/media/dvb/dvb-usb/usb-urb.c @@ -148,7 +148,7 @@ static int usb_bulk_urb_init(struct usb_data_stream *stream) if (!stream->urb_list[i]) { deb_mem("not enough memory for urb_alloc_urb!.\n"); for (j = 0; j < i; j++) - usb_free_urb(stream->urb_list[i]); + usb_free_urb(stream->urb_list[j]); return -ENOMEM; } usb_fill_bulk_urb( stream->urb_list[i], stream->udev, @@ -181,7 +181,7 @@ static int usb_isoc_urb_init(struct usb_data_stream *stream) if (!stream->urb_list[i]) { deb_mem("not enough memory for urb_alloc_urb!\n"); for (j = 0; j < i; j++) - usb_free_urb(stream->urb_list[i]); + usb_free_urb(stream->urb_list[j]); return -ENOMEM; } diff --git a/drivers/media/dvb/dvb-usb/vp702x.c b/drivers/media/dvb/dvb-usb/vp702x.c index 54355f84a98f..45e31f224814 100644 --- a/drivers/media/dvb/dvb-usb/vp702x.c +++ b/drivers/media/dvb/dvb-usb/vp702x.c @@ -320,7 +320,7 @@ static int vp702x_frontend_attach(struct dvb_usb_adapter *adap) vp702x_init_pid_filter(adap); - adap->fe = vp702x_fe_attach(adap->dev); + adap->fe_adap[0].fe = vp702x_fe_attach(adap->dev); vp702x_usb_out_op(adap->dev, SET_TUNER_POWER_REQ, 1, 7, NULL, 0); return 0; @@ -383,6 +383,8 @@ static struct dvb_usb_device_properties vp702x_properties = { .num_adapters = 1, .adapter = { { + .num_frontends = 1, + .fe = {{ .caps = DVB_USB_ADAP_RECEIVES_204_BYTE_TS, .streaming_ctrl = vp702x_streaming_ctrl, @@ -399,6 +401,7 @@ static struct dvb_usb_device_properties vp702x_properties = { } } }, + }}, .size_of_priv = sizeof(struct vp702x_adapter_state), } }, diff --git a/drivers/media/dvb/dvb-usb/vp7045.c b/drivers/media/dvb/dvb-usb/vp7045.c index 536c16c943bd..90873af5682e 100644 --- a/drivers/media/dvb/dvb-usb/vp7045.c +++ b/drivers/media/dvb/dvb-usb/vp7045.c @@ -214,7 +214,7 @@ static int vp7045_frontend_attach(struct dvb_usb_adapter *adap) /* Dump the EEPROM */ /* vp7045_read_eeprom(d,buf, 255, FX2_ID_ADDR); */ - adap->fe = vp7045_fe_attach(adap->dev); + adap->fe_adap[0].fe = vp7045_fe_attach(adap->dev); return 0; } @@ -245,6 +245,8 @@ static struct dvb_usb_device_properties vp7045_properties = { .num_adapters = 1, .adapter = { { + .num_frontends = 1, + .fe = {{ .frontend_attach = vp7045_frontend_attach, /* parameter for the MPEG2-data transfer */ .stream = { @@ -257,6 +259,7 @@ static struct dvb_usb_device_properties vp7045_properties = { } } }, + }}, } }, .power_ctrl = vp7045_power_ctrl, |