aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/usb/dvb-usb-v2
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/usb/dvb-usb-v2')
-rw-r--r--drivers/media/usb/dvb-usb-v2/Kconfig3
-rw-r--r--drivers/media/usb/dvb-usb-v2/af9035.c1
-rw-r--r--drivers/media/usb/dvb-usb-v2/dvbsky.c438
-rw-r--r--drivers/media/usb/dvb-usb-v2/lmedm04.c22
-rw-r--r--drivers/media/usb/dvb-usb-v2/rtl28xxu.c231
-rw-r--r--drivers/media/usb/dvb-usb-v2/rtl28xxu.h7
6 files changed, 614 insertions, 88 deletions
diff --git a/drivers/media/usb/dvb-usb-v2/Kconfig b/drivers/media/usb/dvb-usb-v2/Kconfig
index 5b34323ad207..0982e734fab5 100644
--- a/drivers/media/usb/dvb-usb-v2/Kconfig
+++ b/drivers/media/usb/dvb-usb-v2/Kconfig
@@ -145,6 +145,9 @@ config DVB_USB_DVBSKY
tristate "DVBSky USB support"
depends on DVB_USB_V2
select DVB_M88DS3103 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_SI2168 if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_M88TS2022 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_SI2157 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_SP2 if MEDIA_SUBDRV_AUTOSELECT
help
Say Y here to support the USB receivers from DVBSky.
diff --git a/drivers/media/usb/dvb-usb-v2/af9035.c b/drivers/media/usb/dvb-usb-v2/af9035.c
index 1896ab218b11..80a29f5377ea 100644
--- a/drivers/media/usb/dvb-usb-v2/af9035.c
+++ b/drivers/media/usb/dvb-usb-v2/af9035.c
@@ -1171,6 +1171,7 @@ static int it930x_frontend_attach(struct dvb_usb_adapter *adap)
dev_dbg(&d->udev->dev, "adap->id=%d\n", adap->id);
+ memset(&si2168_config, 0, sizeof(si2168_config));
si2168_config.i2c_adapter = &adapter;
si2168_config.fe = &adap->fe[0];
si2168_config.ts_mode = SI2168_TS_SERIAL;
diff --git a/drivers/media/usb/dvb-usb-v2/dvbsky.c b/drivers/media/usb/dvb-usb-v2/dvbsky.c
index 34688c89df11..9b5add4499e3 100644
--- a/drivers/media/usb/dvb-usb-v2/dvbsky.c
+++ b/drivers/media/usb/dvb-usb-v2/dvbsky.c
@@ -21,10 +21,17 @@
#include "dvb_usb.h"
#include "m88ds3103.h"
#include "m88ts2022.h"
+#include "sp2.h"
+#include "si2168.h"
+#include "si2157.h"
#define DVBSKY_MSG_DELAY 0/*2000*/
#define DVBSKY_BUF_LEN 64
+static int dvb_usb_dvbsky_disable_rc;
+module_param_named(disable_rc, dvb_usb_dvbsky_disable_rc, int, 0644);
+MODULE_PARM_DESC(disable_rc, "Disable inbuilt IR receiver.");
+
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
struct dvbsky_state {
@@ -32,7 +39,9 @@ struct dvbsky_state {
u8 ibuf[DVBSKY_BUF_LEN];
u8 obuf[DVBSKY_BUF_LEN];
u8 last_lock;
+ struct i2c_client *i2c_client_demod;
struct i2c_client *i2c_client_tuner;
+ struct i2c_client *i2c_client_ci;
/* fe hook functions*/
int (*fe_set_voltage)(struct dvb_frontend *fe,
@@ -96,8 +105,7 @@ static int dvbsky_gpio_ctrl(struct dvb_usb_device *d, u8 gport, u8 value)
obuf[2] = value;
ret = dvbsky_usb_generic_rw(d, obuf, 3, ibuf, 1);
if (ret)
- dev_err(&d->udev->dev, "%s: %s() failed=%d\n",
- KBUILD_MODNAME, __func__, ret);
+ dev_err(&d->udev->dev, "failed=%d\n", ret);
return ret;
}
@@ -114,7 +122,7 @@ static int dvbsky_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
if (num > 2) {
dev_err(&d->udev->dev,
- "dvbsky_usb: too many i2c messages[%d] than 2.", num);
+ "too many i2c messages[%d], max 2.", num);
ret = -EOPNOTSUPP;
goto i2c_error;
}
@@ -122,7 +130,7 @@ static int dvbsky_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
if (num == 1) {
if (msg[0].len > 60) {
dev_err(&d->udev->dev,
- "dvbsky_usb: too many i2c bytes[%d] than 60.",
+ "too many i2c bytes[%d], max 60.",
msg[0].len);
ret = -EOPNOTSUPP;
goto i2c_error;
@@ -136,8 +144,7 @@ static int dvbsky_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
ret = dvbsky_usb_generic_rw(d, obuf, 4,
ibuf, msg[0].len + 1);
if (ret)
- dev_err(&d->udev->dev, "%s: %s() failed=%d\n",
- KBUILD_MODNAME, __func__, ret);
+ dev_err(&d->udev->dev, "failed=%d\n", ret);
if (!ret)
memcpy(msg[0].buf, &ibuf[1], msg[0].len);
} else {
@@ -149,13 +156,12 @@ static int dvbsky_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
ret = dvbsky_usb_generic_rw(d, obuf,
msg[0].len + 3, ibuf, 1);
if (ret)
- dev_err(&d->udev->dev, "%s: %s() failed=%d\n",
- KBUILD_MODNAME, __func__, ret);
+ dev_err(&d->udev->dev, "failed=%d\n", ret);
}
} else {
if ((msg[0].len > 60) || (msg[1].len > 60)) {
dev_err(&d->udev->dev,
- "dvbsky_usb: too many i2c bytes[w-%d][r-%d] than 60.",
+ "too many i2c bytes[w-%d][r-%d], max 60.",
msg[0].len, msg[1].len);
ret = -EOPNOTSUPP;
goto i2c_error;
@@ -169,8 +175,7 @@ static int dvbsky_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
ret = dvbsky_usb_generic_rw(d, obuf,
msg[0].len + 4, ibuf, msg[1].len + 1);
if (ret)
- dev_err(&d->udev->dev, "%s: %s() failed=%d\n",
- KBUILD_MODNAME, __func__, ret);
+ dev_err(&d->udev->dev, "failed=%d\n", ret);
if (!ret)
memcpy(msg[1].buf, &ibuf[1], msg[1].len);
@@ -201,8 +206,7 @@ static int dvbsky_rc_query(struct dvb_usb_device *d)
obuf[0] = 0x10;
ret = dvbsky_usb_generic_rw(d, obuf, 1, ibuf, 2);
if (ret)
- dev_err(&d->udev->dev, "%s: %s() failed=%d\n",
- KBUILD_MODNAME, __func__, ret);
+ dev_err(&d->udev->dev, "failed=%d\n", ret);
if (ret == 0)
code = (ibuf[0] << 8) | ibuf[1];
if (code != 0xffff) {
@@ -218,6 +222,11 @@ static int dvbsky_rc_query(struct dvb_usb_device *d)
static int dvbsky_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc)
{
+ if (dvb_usb_dvbsky_disable_rc) {
+ rc->map_name = NULL;
+ return 0;
+ }
+
rc->allowed_protos = RC_BIT_RC5;
rc->query = dvbsky_rc_query;
rc->interval = 300;
@@ -265,8 +274,6 @@ static int dvbsky_read_mac_addr(struct dvb_usb_adapter *adap, u8 mac[6])
if (i2c_transfer(&d->i2c_adap, msg, 2) == 2)
memcpy(mac, ibuf, 6);
- dev_info(&d->udev->dev, "dvbsky_usb MAC address=%pM\n", mac);
-
return 0;
}
@@ -362,6 +369,300 @@ fail_attach:
return ret;
}
+static int dvbsky_usb_ci_set_voltage(struct dvb_frontend *fe,
+ fe_sec_voltage_t voltage)
+{
+ struct dvb_usb_device *d = fe_to_d(fe);
+ struct dvbsky_state *state = d_to_priv(d);
+ u8 value;
+
+ if (voltage == SEC_VOLTAGE_OFF)
+ value = 0;
+ else
+ value = 1;
+ dvbsky_gpio_ctrl(d, 0x00, value);
+
+ return state->fe_set_voltage(fe, voltage);
+}
+
+static int dvbsky_ci_ctrl(void *priv, u8 read, int addr,
+ u8 data, int *mem)
+{
+ struct dvb_usb_device *d = priv;
+ int ret = 0;
+ u8 command[4], respond[2], command_size, respond_size;
+
+ command[1] = (u8)((addr >> 8) & 0xff); /*high part of address*/
+ command[2] = (u8)(addr & 0xff); /*low part of address*/
+ if (read) {
+ command[0] = 0x71;
+ command_size = 3;
+ respond_size = 2;
+ } else {
+ command[0] = 0x70;
+ command[3] = data;
+ command_size = 4;
+ respond_size = 1;
+ }
+ ret = dvbsky_usb_generic_rw(d, command, command_size,
+ respond, respond_size);
+ if (ret)
+ goto err;
+ if (read)
+ *mem = respond[1];
+ return ret;
+err:
+ dev_err(&d->udev->dev, "ci control failed=%d\n", ret);
+ return ret;
+}
+
+static const struct m88ds3103_config dvbsky_s960c_m88ds3103_config = {
+ .i2c_addr = 0x68,
+ .clock = 27000000,
+ .i2c_wr_max = 33,
+ .clock_out = 0,
+ .ts_mode = M88DS3103_TS_CI,
+ .ts_clk = 10000,
+ .ts_clk_pol = 1,
+ .agc = 0x99,
+ .lnb_hv_pol = 0,
+ .lnb_en_pol = 1,
+};
+
+static int dvbsky_s960c_attach(struct dvb_usb_adapter *adap)
+{
+ struct dvbsky_state *state = adap_to_priv(adap);
+ struct dvb_usb_device *d = adap_to_d(adap);
+ int ret = 0;
+ /* demod I2C adapter */
+ struct i2c_adapter *i2c_adapter;
+ struct i2c_client *client_tuner, *client_ci;
+ struct i2c_board_info info;
+ struct sp2_config sp2_config;
+ struct m88ts2022_config m88ts2022_config = {
+ .clock = 27000000,
+ };
+ memset(&info, 0, sizeof(struct i2c_board_info));
+
+ /* attach demod */
+ adap->fe[0] = dvb_attach(m88ds3103_attach,
+ &dvbsky_s960c_m88ds3103_config,
+ &d->i2c_adap,
+ &i2c_adapter);
+ if (!adap->fe[0]) {
+ dev_err(&d->udev->dev, "dvbsky_s960ci_attach fail.\n");
+ ret = -ENODEV;
+ goto fail_attach;
+ }
+
+ /* attach tuner */
+ m88ts2022_config.fe = adap->fe[0];
+ strlcpy(info.type, "m88ts2022", I2C_NAME_SIZE);
+ info.addr = 0x60;
+ info.platform_data = &m88ts2022_config;
+ request_module("m88ts2022");
+ client_tuner = i2c_new_device(i2c_adapter, &info);
+ if (client_tuner == NULL || client_tuner->dev.driver == NULL) {
+ ret = -ENODEV;
+ goto fail_tuner_device;
+ }
+
+ if (!try_module_get(client_tuner->dev.driver->owner)) {
+ ret = -ENODEV;
+ goto fail_tuner_module;
+ }
+
+ /* attach ci controller */
+ memset(&sp2_config, 0, sizeof(sp2_config));
+ sp2_config.dvb_adap = &adap->dvb_adap;
+ sp2_config.priv = d;
+ sp2_config.ci_control = dvbsky_ci_ctrl;
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ strlcpy(info.type, "sp2", I2C_NAME_SIZE);
+ info.addr = 0x40;
+ info.platform_data = &sp2_config;
+ request_module("sp2");
+ client_ci = i2c_new_device(&d->i2c_adap, &info);
+ if (client_ci == NULL || client_ci->dev.driver == NULL) {
+ ret = -ENODEV;
+ goto fail_ci_device;
+ }
+
+ if (!try_module_get(client_ci->dev.driver->owner)) {
+ ret = -ENODEV;
+ goto fail_ci_module;
+ }
+
+ /* delegate signal strength measurement to tuner */
+ adap->fe[0]->ops.read_signal_strength =
+ adap->fe[0]->ops.tuner_ops.get_rf_strength;
+
+ /* hook fe: need to resync the slave fifo when signal locks. */
+ state->fe_read_status = adap->fe[0]->ops.read_status;
+ adap->fe[0]->ops.read_status = dvbsky_usb_read_status;
+
+ /* hook fe: LNB off/on is control by Cypress usb chip. */
+ state->fe_set_voltage = adap->fe[0]->ops.set_voltage;
+ adap->fe[0]->ops.set_voltage = dvbsky_usb_ci_set_voltage;
+
+ state->i2c_client_tuner = client_tuner;
+ state->i2c_client_ci = client_ci;
+ return ret;
+fail_ci_module:
+ i2c_unregister_device(client_ci);
+fail_ci_device:
+ module_put(client_tuner->dev.driver->owner);
+fail_tuner_module:
+ i2c_unregister_device(client_tuner);
+fail_tuner_device:
+ dvb_frontend_detach(adap->fe[0]);
+fail_attach:
+ return ret;
+}
+
+static int dvbsky_t680c_attach(struct dvb_usb_adapter *adap)
+{
+ struct dvbsky_state *state = adap_to_priv(adap);
+ struct dvb_usb_device *d = adap_to_d(adap);
+ int ret = 0;
+ struct i2c_adapter *i2c_adapter;
+ struct i2c_client *client_demod, *client_tuner, *client_ci;
+ struct i2c_board_info info;
+ struct si2168_config si2168_config;
+ struct si2157_config si2157_config;
+ struct sp2_config sp2_config;
+
+ /* attach demod */
+ memset(&si2168_config, 0, sizeof(si2168_config));
+ si2168_config.i2c_adapter = &i2c_adapter;
+ si2168_config.fe = &adap->fe[0];
+ si2168_config.ts_mode = SI2168_TS_PARALLEL;
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ strlcpy(info.type, "si2168", I2C_NAME_SIZE);
+ info.addr = 0x64;
+ info.platform_data = &si2168_config;
+
+ request_module(info.type);
+ client_demod = i2c_new_device(&d->i2c_adap, &info);
+ if (client_demod == NULL ||
+ client_demod->dev.driver == NULL)
+ goto fail_demod_device;
+ if (!try_module_get(client_demod->dev.driver->owner))
+ goto fail_demod_module;
+
+ /* attach tuner */
+ memset(&si2157_config, 0, sizeof(si2157_config));
+ si2157_config.fe = adap->fe[0];
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ strlcpy(info.type, "si2157", I2C_NAME_SIZE);
+ info.addr = 0x60;
+ info.platform_data = &si2157_config;
+
+ request_module(info.type);
+ client_tuner = i2c_new_device(i2c_adapter, &info);
+ if (client_tuner == NULL ||
+ client_tuner->dev.driver == NULL)
+ goto fail_tuner_device;
+ if (!try_module_get(client_tuner->dev.driver->owner))
+ goto fail_tuner_module;
+
+ /* attach ci controller */
+ memset(&sp2_config, 0, sizeof(sp2_config));
+ sp2_config.dvb_adap = &adap->dvb_adap;
+ sp2_config.priv = d;
+ sp2_config.ci_control = dvbsky_ci_ctrl;
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ strlcpy(info.type, "sp2", I2C_NAME_SIZE);
+ info.addr = 0x40;
+ info.platform_data = &sp2_config;
+
+ request_module(info.type);
+ client_ci = i2c_new_device(&d->i2c_adap, &info);
+
+ if (client_ci == NULL || client_ci->dev.driver == NULL)
+ goto fail_ci_device;
+
+ if (!try_module_get(client_ci->dev.driver->owner))
+ goto fail_ci_module;
+
+ state->i2c_client_demod = client_demod;
+ state->i2c_client_tuner = client_tuner;
+ state->i2c_client_ci = client_ci;
+ return ret;
+fail_ci_module:
+ i2c_unregister_device(client_ci);
+fail_ci_device:
+ module_put(client_tuner->dev.driver->owner);
+fail_tuner_module:
+ i2c_unregister_device(client_tuner);
+fail_tuner_device:
+ module_put(client_demod->dev.driver->owner);
+fail_demod_module:
+ i2c_unregister_device(client_demod);
+fail_demod_device:
+ ret = -ENODEV;
+ return ret;
+}
+
+static int dvbsky_t330_attach(struct dvb_usb_adapter *adap)
+{
+ struct dvbsky_state *state = adap_to_priv(adap);
+ struct dvb_usb_device *d = adap_to_d(adap);
+ int ret = 0;
+ struct i2c_adapter *i2c_adapter;
+ struct i2c_client *client_demod, *client_tuner;
+ struct i2c_board_info info;
+ struct si2168_config si2168_config;
+ struct si2157_config si2157_config;
+
+ /* attach demod */
+ memset(&si2168_config, 0, sizeof(si2168_config));
+ si2168_config.i2c_adapter = &i2c_adapter;
+ si2168_config.fe = &adap->fe[0];
+ si2168_config.ts_mode = SI2168_TS_PARALLEL | 0x40;
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ strlcpy(info.type, "si2168", I2C_NAME_SIZE);
+ info.addr = 0x64;
+ info.platform_data = &si2168_config;
+
+ request_module(info.type);
+ client_demod = i2c_new_device(&d->i2c_adap, &info);
+ if (client_demod == NULL ||
+ client_demod->dev.driver == NULL)
+ goto fail_demod_device;
+ if (!try_module_get(client_demod->dev.driver->owner))
+ goto fail_demod_module;
+
+ /* attach tuner */
+ memset(&si2157_config, 0, sizeof(si2157_config));
+ si2157_config.fe = adap->fe[0];
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ strlcpy(info.type, "si2157", I2C_NAME_SIZE);
+ info.addr = 0x60;
+ info.platform_data = &si2157_config;
+
+ request_module(info.type);
+ client_tuner = i2c_new_device(i2c_adapter, &info);
+ if (client_tuner == NULL ||
+ client_tuner->dev.driver == NULL)
+ goto fail_tuner_device;
+ if (!try_module_get(client_tuner->dev.driver->owner))
+ goto fail_tuner_module;
+
+ state->i2c_client_demod = client_demod;
+ state->i2c_client_tuner = client_tuner;
+ return ret;
+fail_tuner_module:
+ i2c_unregister_device(client_tuner);
+fail_tuner_device:
+ module_put(client_demod->dev.driver->owner);
+fail_demod_module:
+ i2c_unregister_device(client_demod);
+fail_demod_device:
+ ret = -ENODEV;
+ return ret;
+}
+
static int dvbsky_identify_state(struct dvb_usb_device *d, const char **name)
{
dvbsky_gpio_ctrl(d, 0x04, 1);
@@ -404,6 +705,18 @@ static void dvbsky_exit(struct dvb_usb_device *d)
module_put(client->dev.driver->owner);
i2c_unregister_device(client);
}
+ client = state->i2c_client_demod;
+ /* remove I2C demod */
+ if (client) {
+ module_put(client->dev.driver->owner);
+ i2c_unregister_device(client);
+ }
+ client = state->i2c_client_ci;
+ /* remove I2C ci */
+ if (client) {
+ module_put(client->dev.driver->owner);
+ i2c_unregister_device(client);
+ }
}
/* DVB USB Driver stuff */
@@ -434,9 +747,104 @@ static struct dvb_usb_device_properties dvbsky_s960_props = {
}
};
+static struct dvb_usb_device_properties dvbsky_s960c_props = {
+ .driver_name = KBUILD_MODNAME,
+ .owner = THIS_MODULE,
+ .adapter_nr = adapter_nr,
+ .size_of_priv = sizeof(struct dvbsky_state),
+
+ .generic_bulk_ctrl_endpoint = 0x01,
+ .generic_bulk_ctrl_endpoint_response = 0x81,
+ .generic_bulk_ctrl_delay = DVBSKY_MSG_DELAY,
+
+ .i2c_algo = &dvbsky_i2c_algo,
+ .frontend_attach = dvbsky_s960c_attach,
+ .init = dvbsky_init,
+ .get_rc_config = dvbsky_get_rc_config,
+ .streaming_ctrl = dvbsky_streaming_ctrl,
+ .identify_state = dvbsky_identify_state,
+ .exit = dvbsky_exit,
+ .read_mac_address = dvbsky_read_mac_addr,
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .stream = DVB_USB_STREAM_BULK(0x82, 8, 4096),
+ }
+ }
+};
+
+static struct dvb_usb_device_properties dvbsky_t680c_props = {
+ .driver_name = KBUILD_MODNAME,
+ .owner = THIS_MODULE,
+ .adapter_nr = adapter_nr,
+ .size_of_priv = sizeof(struct dvbsky_state),
+
+ .generic_bulk_ctrl_endpoint = 0x01,
+ .generic_bulk_ctrl_endpoint_response = 0x81,
+ .generic_bulk_ctrl_delay = DVBSKY_MSG_DELAY,
+
+ .i2c_algo = &dvbsky_i2c_algo,
+ .frontend_attach = dvbsky_t680c_attach,
+ .init = dvbsky_init,
+ .get_rc_config = dvbsky_get_rc_config,
+ .streaming_ctrl = dvbsky_streaming_ctrl,
+ .identify_state = dvbsky_identify_state,
+ .exit = dvbsky_exit,
+ .read_mac_address = dvbsky_read_mac_addr,
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .stream = DVB_USB_STREAM_BULK(0x82, 8, 4096),
+ }
+ }
+};
+
+static struct dvb_usb_device_properties dvbsky_t330_props = {
+ .driver_name = KBUILD_MODNAME,
+ .owner = THIS_MODULE,
+ .adapter_nr = adapter_nr,
+ .size_of_priv = sizeof(struct dvbsky_state),
+
+ .generic_bulk_ctrl_endpoint = 0x01,
+ .generic_bulk_ctrl_endpoint_response = 0x81,
+ .generic_bulk_ctrl_delay = DVBSKY_MSG_DELAY,
+
+ .i2c_algo = &dvbsky_i2c_algo,
+ .frontend_attach = dvbsky_t330_attach,
+ .init = dvbsky_init,
+ .get_rc_config = dvbsky_get_rc_config,
+ .streaming_ctrl = dvbsky_streaming_ctrl,
+ .identify_state = dvbsky_identify_state,
+ .exit = dvbsky_exit,
+ .read_mac_address = dvbsky_read_mac_addr,
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .stream = DVB_USB_STREAM_BULK(0x82, 8, 4096),
+ }
+ }
+};
+
static const struct usb_device_id dvbsky_id_table[] = {
{ DVB_USB_DEVICE(0x0572, 0x6831,
&dvbsky_s960_props, "DVBSky S960/S860", RC_MAP_DVBSKY) },
+ { DVB_USB_DEVICE(0x0572, 0x960c,
+ &dvbsky_s960c_props, "DVBSky S960CI", RC_MAP_DVBSKY) },
+ { DVB_USB_DEVICE(0x0572, 0x680c,
+ &dvbsky_t680c_props, "DVBSky T680CI", RC_MAP_DVBSKY) },
+ { DVB_USB_DEVICE(0x0572, 0x0320,
+ &dvbsky_t330_props, "DVBSky T330", RC_MAP_DVBSKY) },
+ { DVB_USB_DEVICE(USB_VID_TECHNOTREND,
+ USB_PID_TECHNOTREND_TVSTICK_CT2_4400,
+ &dvbsky_t330_props, "TechnoTrend TVStick CT2-4400",
+ RC_MAP_TT_1500) },
+ { DVB_USB_DEVICE(USB_VID_TECHNOTREND,
+ USB_PID_TECHNOTREND_CONNECT_CT2_4650_CI,
+ &dvbsky_t680c_props, "TechnoTrend TT-connect CT2-4650 CI",
+ RC_MAP_TT_1500) },
{ }
};
MODULE_DEVICE_TABLE(usb, dvbsky_id_table);
diff --git a/drivers/media/usb/dvb-usb-v2/lmedm04.c b/drivers/media/usb/dvb-usb-v2/lmedm04.c
index 9f2c5459b73a..994de53a574b 100644
--- a/drivers/media/usb/dvb-usb-v2/lmedm04.c
+++ b/drivers/media/usb/dvb-usb-v2/lmedm04.c
@@ -817,20 +817,22 @@ static const char *lme_firmware_switch(struct dvb_usb_device *d, int cold)
case 0x1122:
switch (st->dvb_usb_lme2510_firmware) {
default:
- st->dvb_usb_lme2510_firmware = TUNER_S0194;
case TUNER_S0194:
fw_lme = fw_s0194;
ret = request_firmware(&fw, fw_lme, &udev->dev);
if (ret == 0) {
+ st->dvb_usb_lme2510_firmware = TUNER_S0194;
cold = 0;
break;
}
- st->dvb_usb_lme2510_firmware = TUNER_LG;
+ /* fall through */
case TUNER_LG:
fw_lme = fw_lg;
ret = request_firmware(&fw, fw_lme, &udev->dev);
- if (ret == 0)
+ if (ret == 0) {
+ st->dvb_usb_lme2510_firmware = TUNER_LG;
break;
+ }
st->dvb_usb_lme2510_firmware = TUNER_DEFAULT;
break;
}
@@ -838,26 +840,30 @@ static const char *lme_firmware_switch(struct dvb_usb_device *d, int cold)
case 0x1120:
switch (st->dvb_usb_lme2510_firmware) {
default:
- st->dvb_usb_lme2510_firmware = TUNER_S7395;
case TUNER_S7395:
fw_lme = fw_c_s7395;
ret = request_firmware(&fw, fw_lme, &udev->dev);
if (ret == 0) {
+ st->dvb_usb_lme2510_firmware = TUNER_S7395;
cold = 0;
break;
}
- st->dvb_usb_lme2510_firmware = TUNER_LG;
+ /* fall through */
case TUNER_LG:
fw_lme = fw_c_lg;
ret = request_firmware(&fw, fw_lme, &udev->dev);
- if (ret == 0)
+ if (ret == 0) {
+ st->dvb_usb_lme2510_firmware = TUNER_LG;
break;
- st->dvb_usb_lme2510_firmware = TUNER_S0194;
+ }
+ /* fall through */
case TUNER_S0194:
fw_lme = fw_c_s0194;
ret = request_firmware(&fw, fw_lme, &udev->dev);
- if (ret == 0)
+ if (ret == 0) {
+ st->dvb_usb_lme2510_firmware = TUNER_S0194;
break;
+ }
st->dvb_usb_lme2510_firmware = TUNER_DEFAULT;
cold = 0;
break;
diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c
index 27b1e0397e71..896a225ee011 100644
--- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c
+++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c
@@ -24,6 +24,9 @@
#include "rtl2830.h"
#include "rtl2832.h"
+#include "rtl2832_sdr.h"
+#include "mn88472.h"
+#include "mn88473.h"
#include "qt1010.h"
#include "mt2060.h"
@@ -35,25 +38,6 @@
#include "tua9001.h"
#include "r820t.h"
-/*
- * RTL2832_SDR module is in staging. That logic is added in order to avoid any
- * hard dependency to drivers/staging/ directory as we want compile mainline
- * driver even whole staging directory is missing.
- */
-#include <media/v4l2-subdev.h>
-
-#if IS_ENABLED(CONFIG_DVB_RTL2832_SDR)
-struct dvb_frontend *rtl2832_sdr_attach(struct dvb_frontend *fe,
- struct i2c_adapter *i2c, const struct rtl2832_config *cfg,
- struct v4l2_subdev *sd);
-#else
-static inline struct dvb_frontend *rtl2832_sdr_attach(struct dvb_frontend *fe,
- struct i2c_adapter *i2c, const struct rtl2832_config *cfg,
- struct v4l2_subdev *sd)
-{
- return NULL;
-}
-#endif
#ifdef CONFIG_MEDIA_ATTACH
#define dvb_attach_sdr(FUNCTION, ARGS...) ({ \
@@ -420,6 +404,8 @@ static int rtl2832u_read_config(struct dvb_usb_device *d)
struct rtl28xxu_req req_tda18272 = {0x00c0, CMD_I2C_RD, 2, buf};
struct rtl28xxu_req req_r820t = {0x0034, CMD_I2C_RD, 1, buf};
struct rtl28xxu_req req_r828d = {0x0074, CMD_I2C_RD, 1, buf};
+ struct rtl28xxu_req req_mn88472 = {0xff38, CMD_I2C_RD, 1, buf};
+ struct rtl28xxu_req req_mn88473 = {0xff38, CMD_I2C_RD, 1, buf};
dev_dbg(&d->udev->dev, "%s:\n", __func__);
@@ -449,7 +435,7 @@ static int rtl2832u_read_config(struct dvb_usb_device *d)
if (ret == 0 && buf[0] == 0xa1) {
priv->tuner = TUNER_RTL2832_FC0012;
priv->tuner_name = "FC0012";
- goto found;
+ goto tuner_found;
}
/* check FC0013 ID register; reg=00 val=a3 */
@@ -457,7 +443,7 @@ static int rtl2832u_read_config(struct dvb_usb_device *d)
if (ret == 0 && buf[0] == 0xa3) {
priv->tuner = TUNER_RTL2832_FC0013;
priv->tuner_name = "FC0013";
- goto found;
+ goto tuner_found;
}
/* check MT2266 ID register; reg=00 val=85 */
@@ -465,7 +451,7 @@ static int rtl2832u_read_config(struct dvb_usb_device *d)
if (ret == 0 && buf[0] == 0x85) {
priv->tuner = TUNER_RTL2832_MT2266;
priv->tuner_name = "MT2266";
- goto found;
+ goto tuner_found;
}
/* check FC2580 ID register; reg=01 val=56 */
@@ -473,7 +459,7 @@ static int rtl2832u_read_config(struct dvb_usb_device *d)
if (ret == 0 && buf[0] == 0x56) {
priv->tuner = TUNER_RTL2832_FC2580;
priv->tuner_name = "FC2580";
- goto found;
+ goto tuner_found;
}
/* check MT2063 ID register; reg=00 val=9e || 9c */
@@ -481,7 +467,7 @@ static int rtl2832u_read_config(struct dvb_usb_device *d)
if (ret == 0 && (buf[0] == 0x9e || buf[0] == 0x9c)) {
priv->tuner = TUNER_RTL2832_MT2063;
priv->tuner_name = "MT2063";
- goto found;
+ goto tuner_found;
}
/* check MAX3543 ID register; reg=00 val=38 */
@@ -489,7 +475,7 @@ static int rtl2832u_read_config(struct dvb_usb_device *d)
if (ret == 0 && buf[0] == 0x38) {
priv->tuner = TUNER_RTL2832_MAX3543;
priv->tuner_name = "MAX3543";
- goto found;
+ goto tuner_found;
}
/* check TUA9001 ID register; reg=7e val=2328 */
@@ -497,7 +483,7 @@ static int rtl2832u_read_config(struct dvb_usb_device *d)
if (ret == 0 && buf[0] == 0x23 && buf[1] == 0x28) {
priv->tuner = TUNER_RTL2832_TUA9001;
priv->tuner_name = "TUA9001";
- goto found;
+ goto tuner_found;
}
/* check MXL5007R ID register; reg=d9 val=14 */
@@ -505,7 +491,7 @@ static int rtl2832u_read_config(struct dvb_usb_device *d)
if (ret == 0 && buf[0] == 0x14) {
priv->tuner = TUNER_RTL2832_MXL5007T;
priv->tuner_name = "MXL5007T";
- goto found;
+ goto tuner_found;
}
/* check E4000 ID register; reg=02 val=40 */
@@ -513,7 +499,7 @@ static int rtl2832u_read_config(struct dvb_usb_device *d)
if (ret == 0 && buf[0] == 0x40) {
priv->tuner = TUNER_RTL2832_E4000;
priv->tuner_name = "E4000";
- goto found;
+ goto tuner_found;
}
/* check TDA18272 ID register; reg=00 val=c760 */
@@ -521,7 +507,7 @@ static int rtl2832u_read_config(struct dvb_usb_device *d)
if (ret == 0 && (buf[0] == 0xc7 || buf[1] == 0x60)) {
priv->tuner = TUNER_RTL2832_TDA18272;
priv->tuner_name = "TDA18272";
- goto found;
+ goto tuner_found;
}
/* check R820T ID register; reg=00 val=69 */
@@ -529,7 +515,7 @@ static int rtl2832u_read_config(struct dvb_usb_device *d)
if (ret == 0 && buf[0] == 0x69) {
priv->tuner = TUNER_RTL2832_R820T;
priv->tuner_name = "R820T";
- goto found;
+ goto tuner_found;
}
/* check R828D ID register; reg=00 val=69 */
@@ -537,13 +523,44 @@ static int rtl2832u_read_config(struct dvb_usb_device *d)
if (ret == 0 && buf[0] == 0x69) {
priv->tuner = TUNER_RTL2832_R828D;
priv->tuner_name = "R828D";
- goto found;
+ goto tuner_found;
}
-
-found:
+tuner_found:
dev_dbg(&d->udev->dev, "%s: tuner=%s\n", __func__, priv->tuner_name);
+ /* probe slave demod */
+ if (priv->tuner == TUNER_RTL2832_R828D) {
+ /* power on MN88472 demod on GPIO0 */
+ ret = rtl28xx_wr_reg_mask(d, SYS_GPIO_OUT_VAL, 0x01, 0x01);
+ if (ret)
+ goto err;
+
+ ret = rtl28xx_wr_reg_mask(d, SYS_GPIO_DIR, 0x00, 0x01);
+ if (ret)
+ goto err;
+
+ ret = rtl28xx_wr_reg_mask(d, SYS_GPIO_OUT_EN, 0x01, 0x01);
+ if (ret)
+ goto err;
+
+ /* check MN88472 answers */
+ ret = rtl28xxu_ctrl_msg(d, &req_mn88472);
+ if (ret == 0 && buf[0] == 0x02) {
+ dev_dbg(&d->udev->dev, "%s: MN88472 found\n", __func__);
+ priv->slave_demod = SLAVE_DEMOD_MN88472;
+ goto demod_found;
+ }
+
+ ret = rtl28xxu_ctrl_msg(d, &req_mn88473);
+ if (ret == 0 && buf[0] == 0x03) {
+ dev_dbg(&d->udev->dev, "%s: MN88473 found\n", __func__);
+ priv->slave_demod = SLAVE_DEMOD_MN88473;
+ goto demod_found;
+ }
+ }
+
+demod_found:
/* close demod I2C gate */
ret = rtl28xxu_ctrl_msg(d, &req_gate_close);
if (ret < 0)
@@ -818,7 +835,66 @@ static int rtl2832u_frontend_attach(struct dvb_usb_adapter *adap)
/* set fe callback */
adap->fe[0]->callback = rtl2832u_frontend_callback;
+ if (priv->slave_demod) {
+ struct i2c_board_info info = {};
+ struct i2c_client *client;
+
+ /*
+ * We continue on reduced mode, without DVB-T2/C, using master
+ * demod, when slave demod fails.
+ */
+ ret = 0;
+
+ /* attach slave demodulator */
+ if (priv->slave_demod == SLAVE_DEMOD_MN88472) {
+ struct mn88472_config mn88472_config = {};
+
+ mn88472_config.fe = &adap->fe[1];
+ mn88472_config.i2c_wr_max = 22,
+ strlcpy(info.type, "mn88472", I2C_NAME_SIZE);
+ info.addr = 0x18;
+ info.platform_data = &mn88472_config;
+ request_module(info.type);
+ client = i2c_new_device(priv->demod_i2c_adapter, &info);
+ if (client == NULL || client->dev.driver == NULL) {
+ priv->slave_demod = SLAVE_DEMOD_NONE;
+ goto err_slave_demod_failed;
+ }
+
+ if (!try_module_get(client->dev.driver->owner)) {
+ i2c_unregister_device(client);
+ priv->slave_demod = SLAVE_DEMOD_NONE;
+ goto err_slave_demod_failed;
+ }
+
+ priv->i2c_client_slave_demod = client;
+ } else {
+ struct mn88473_config mn88473_config = {};
+
+ mn88473_config.fe = &adap->fe[1];
+ mn88473_config.i2c_wr_max = 22,
+ strlcpy(info.type, "mn88473", I2C_NAME_SIZE);
+ info.addr = 0x18;
+ info.platform_data = &mn88473_config;
+ request_module(info.type);
+ client = i2c_new_device(priv->demod_i2c_adapter, &info);
+ if (client == NULL || client->dev.driver == NULL) {
+ priv->slave_demod = SLAVE_DEMOD_NONE;
+ goto err_slave_demod_failed;
+ }
+
+ if (!try_module_get(client->dev.driver->owner)) {
+ i2c_unregister_device(client);
+ priv->slave_demod = SLAVE_DEMOD_NONE;
+ goto err_slave_demod_failed;
+ }
+
+ priv->i2c_client_slave_demod = client;
+ }
+ }
+
return 0;
+err_slave_demod_failed:
err:
dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
return ret;
@@ -984,7 +1060,7 @@ static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap)
break;
}
- priv->client = client;
+ priv->i2c_client_tuner = client;
sd = i2c_get_clientdata(client);
i2c_set_adapdata(i2c_adap_internal, d);
@@ -1024,32 +1100,30 @@ static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap)
&rtl28xxu_rtl2832_r820t_config, NULL);
break;
case TUNER_RTL2832_R828D:
- /* power off mn88472 demod on GPIO0 */
- ret = rtl28xx_wr_reg_mask(d, SYS_GPIO_OUT_VAL, 0x00, 0x01);
- if (ret)
- goto err;
-
- ret = rtl28xx_wr_reg_mask(d, SYS_GPIO_DIR, 0x00, 0x01);
- if (ret)
- goto err;
-
- ret = rtl28xx_wr_reg_mask(d, SYS_GPIO_OUT_EN, 0x01, 0x01);
- if (ret)
- goto err;
-
- fe = dvb_attach(r820t_attach, adap->fe[0], &d->i2c_adap,
+ fe = dvb_attach(r820t_attach, adap->fe[0],
+ priv->demod_i2c_adapter,
&rtl2832u_r828d_config);
-
- /* Use tuner to get the signal strength */
adap->fe[0]->ops.read_signal_strength =
adap->fe[0]->ops.tuner_ops.get_rf_strength;
+
+ if (adap->fe[1]) {
+ fe = dvb_attach(r820t_attach, adap->fe[1],
+ priv->demod_i2c_adapter,
+ &rtl2832u_r828d_config);
+ adap->fe[1]->ops.read_signal_strength =
+ adap->fe[1]->ops.tuner_ops.get_rf_strength;
+ }
+
+ /* attach SDR */
+ dvb_attach_sdr(rtl2832_sdr_attach, adap->fe[0], &d->i2c_adap,
+ &rtl28xxu_rtl2832_r820t_config, NULL);
break;
default:
dev_err(&d->udev->dev, "%s: unknown tuner=%d\n", KBUILD_MODNAME,
priv->tuner);
}
- if (fe == NULL && priv->client == NULL) {
+ if (fe == NULL && priv->i2c_client_tuner == NULL) {
ret = -ENODEV;
goto err;
}
@@ -1097,11 +1171,19 @@ err:
static void rtl28xxu_exit(struct dvb_usb_device *d)
{
struct rtl28xxu_priv *priv = d->priv;
- struct i2c_client *client = priv->client;
+ struct i2c_client *client;
dev_dbg(&d->udev->dev, "%s:\n", __func__);
/* remove I2C tuner */
+ client = priv->i2c_client_tuner;
+ if (client) {
+ module_put(client->dev.driver->owner);
+ i2c_unregister_device(client);
+ }
+
+ /* remove I2C slave demod */
+ client = priv->i2c_client_slave_demod;
if (client) {
module_put(client->dev.driver->owner);
i2c_unregister_device(client);
@@ -1201,13 +1283,6 @@ static int rtl2832u_power_ctrl(struct dvb_usb_device *d, int onoff)
if (ret)
goto err;
- mdelay(5);
-
- /* enable ADC */
- ret = rtl28xx_wr_reg_mask(d, SYS_DEMOD_CTL, 0x48, 0x48);
- if (ret)
- goto err;
-
/* streaming EP: clear stall & reset */
ret = rtl28xx_wr_regs(d, USB_EPA_CTL, "\x00\x00", 2);
if (ret)
@@ -1222,11 +1297,6 @@ static int rtl2832u_power_ctrl(struct dvb_usb_device *d, int onoff)
if (ret)
goto err;
- /* disable ADC */
- ret = rtl28xx_wr_reg_mask(d, SYS_DEMOD_CTL, 0x00, 0x48);
- if (ret)
- goto err;
-
/* disable PLL */
ret = rtl28xx_wr_reg_mask(d, SYS_DEMOD_CTL, 0x00, 0x80);
if (ret)
@@ -1244,6 +1314,38 @@ err:
return ret;
}
+static int rtl2832u_frontend_ctrl(struct dvb_frontend *fe, int onoff)
+{
+ struct dvb_usb_device *d = fe_to_d(fe);
+ struct dvb_usb_adapter *adap = fe_to_adap(fe);
+ int ret;
+ u8 val;
+
+ dev_dbg(&d->udev->dev, "%s: fe=%d onoff=%d\n", __func__, fe->id, onoff);
+
+ /* control internal demod ADC */
+ if (fe->id == 0 && onoff)
+ val = 0x48; /* enable ADC */
+ else
+ val = 0x00; /* disable ADC */
+
+ ret = rtl28xx_wr_reg_mask(d, SYS_DEMOD_CTL, val, 0x48);
+ if (ret)
+ goto err;
+
+ /* bypass slave demod TS through master demod */
+ if (fe->id == 1 && onoff) {
+ ret = rtl2832_enable_external_ts_if(adap->fe[0]);
+ if (ret)
+ goto err;
+ }
+
+ return 0;
+err:
+ dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
#if IS_ENABLED(CONFIG_RC_CORE)
static int rtl2831u_rc_query(struct dvb_usb_device *d)
{
@@ -1467,6 +1569,7 @@ static const struct dvb_usb_device_properties rtl2832u_props = {
.size_of_priv = sizeof(struct rtl28xxu_priv),
.power_ctrl = rtl2832u_power_ctrl,
+ .frontend_ctrl = rtl2832u_frontend_ctrl,
.i2c_algo = &rtl28xxu_i2c_algo,
.read_config = rtl2832u_read_config,
.frontend_attach = rtl2832u_frontend_attach,
diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.h b/drivers/media/usb/dvb-usb-v2/rtl28xxu.h
index a26cab10f382..3e3ea9d64a38 100644
--- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.h
+++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.h
@@ -57,7 +57,12 @@ struct rtl28xxu_priv {
u8 page; /* integrated demod active register page */
struct i2c_adapter *demod_i2c_adapter;
bool rc_active;
- struct i2c_client *client;
+ struct i2c_client *i2c_client_tuner;
+ struct i2c_client *i2c_client_slave_demod;
+ #define SLAVE_DEMOD_NONE 0
+ #define SLAVE_DEMOD_MN88472 1
+ #define SLAVE_DEMOD_MN88473 2
+ unsigned int slave_demod:2;
};
enum rtl28xxu_chip_id {