Commit 699dcffc authored by Olli Salonen's avatar Olli Salonen Committed by Mauro Carvalho Chehab

[media] rtl2832: add support for GoTView MasterHD 3 USB tuner

GoTView MasterHD 3 is a DVB-T2/C USB 2.0 tuner.

It's based on the following components:
- USB bridge: RTL2832P (contains also DVB-T demodulator)
- Demodulator: Si2168-A30
- Tuner: Si2148-A20

The demodulator and the tuner will need firmwares. The Si2148 uses Si2158
firmware. Antti has the firmwares available for download:
http://palosaari.fi/linux/v4l-dvb/firmware/

Do note that for DVB-T either of the demodulators can be used. DVB-C and
DVB-T2 are only supported by the Si2168 demodulator. The driver will
register 2 frontends for the same adapter. Frontend 0 will be the RTL2832
demodulator and frontend 1 will be the Si2168 demodulator. The same
tuner is used for both.

As a consequence of the above, it's recommended to use application that
does implement proper DVBv5 support.

For some reason, the old I2C write method sporadically fails. Thus the
need for an option to only use the new I2C write method supported by the
RTL2832.
Signed-off-by: default avatarOlli Salonen <olli.salonen@iki.fi>
Signed-off-by: default avatarAntti Palosaari <crope@iki.fi>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@osg.samsung.com>
parent d744a0cb
...@@ -376,6 +376,10 @@ static int rtl2832_init(struct dvb_frontend *fe) ...@@ -376,6 +376,10 @@ static int rtl2832_init(struct dvb_frontend *fe)
len = ARRAY_SIZE(rtl2832_tuner_init_r820t); len = ARRAY_SIZE(rtl2832_tuner_init_r820t);
init = rtl2832_tuner_init_r820t; init = rtl2832_tuner_init_r820t;
break; break;
case RTL2832_TUNER_SI2157:
len = ARRAY_SIZE(rtl2832_tuner_init_si2157);
init = rtl2832_tuner_init_si2157;
break;
default: default:
ret = -EINVAL; ret = -EINVAL;
goto err; goto err;
......
...@@ -47,6 +47,7 @@ struct rtl2832_platform_data { ...@@ -47,6 +47,7 @@ struct rtl2832_platform_data {
#define RTL2832_TUNER_FC0013 0x29 #define RTL2832_TUNER_FC0013 0x29
#define RTL2832_TUNER_R820T 0x2a #define RTL2832_TUNER_R820T 0x2a
#define RTL2832_TUNER_R828D 0x2b #define RTL2832_TUNER_R828D 0x2b
#define RTL2832_TUNER_SI2157 0x2c
u8 tuner; u8 tuner;
struct dvb_frontend* (*get_dvb_frontend)(struct i2c_client *); struct dvb_frontend* (*get_dvb_frontend)(struct i2c_client *);
......
...@@ -377,4 +377,29 @@ static const struct rtl2832_reg_value rtl2832_tuner_init_r820t[] = { ...@@ -377,4 +377,29 @@ static const struct rtl2832_reg_value rtl2832_tuner_init_r820t[] = {
{DVBT_SPEC_INV, 0x1}, {DVBT_SPEC_INV, 0x1},
}; };
static const struct rtl2832_reg_value rtl2832_tuner_init_si2157[] = {
{DVBT_DAGC_TRG_VAL, 0x39},
{DVBT_AGC_TARG_VAL_0, 0x0},
{DVBT_AGC_TARG_VAL_8_1, 0x40},
{DVBT_AAGC_LOOP_GAIN, 0x16},
{DVBT_LOOP_GAIN2_3_0, 0x8},
{DVBT_LOOP_GAIN2_4, 0x1},
{DVBT_LOOP_GAIN3, 0x18},
{DVBT_VTOP1, 0x35},
{DVBT_VTOP2, 0x21},
{DVBT_VTOP3, 0x21},
{DVBT_KRF1, 0x0},
{DVBT_KRF2, 0x40},
{DVBT_KRF3, 0x10},
{DVBT_KRF4, 0x10},
{DVBT_IF_AGC_MIN, 0x80},
{DVBT_IF_AGC_MAX, 0x7f},
{DVBT_RF_AGC_MIN, 0x80},
{DVBT_RF_AGC_MAX, 0x7f},
{DVBT_POLAR_RF_AGC, 0x0},
{DVBT_POLAR_IF_AGC, 0x0},
{DVBT_AD7_SETTING, 0xe9f4},
{DVBT_SPEC_INV, 0x0},
};
#endif /* RTL2832_PRIV_H */ #endif /* RTL2832_PRIV_H */
...@@ -217,7 +217,7 @@ static int rtl28xxu_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], ...@@ -217,7 +217,7 @@ static int rtl28xxu_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
req.data = &msg[0].buf[1]; req.data = &msg[0].buf[1];
ret = rtl28xxu_ctrl_msg(d, &req); ret = rtl28xxu_ctrl_msg(d, &req);
} }
} else if (msg[0].len < 23) { } else if ((msg[0].len < 23) && (!dev->new_i2c_write)) {
/* method 2 - old I2C */ /* method 2 - old I2C */
req.value = (msg[0].buf[0] << 8) | (msg[0].addr << 1); req.value = (msg[0].buf[0] << 8) | (msg[0].addr << 1);
req.index = CMD_I2C_WR; req.index = CMD_I2C_WR;
...@@ -363,6 +363,8 @@ static int rtl2832u_read_config(struct dvb_usb_device *d) ...@@ -363,6 +363,8 @@ static int rtl2832u_read_config(struct dvb_usb_device *d)
struct rtl28xxu_req req_r828d = {0x0074, 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_mn88472 = {0xff38, CMD_I2C_RD, 1, buf};
struct rtl28xxu_req req_mn88473 = {0xff38, CMD_I2C_RD, 1, buf}; struct rtl28xxu_req req_mn88473 = {0xff38, CMD_I2C_RD, 1, buf};
struct rtl28xxu_req req_si2157 = {0x00c0, CMD_I2C_RD, 1, buf};
struct rtl28xxu_req req_si2168 = {0x00c8, CMD_I2C_RD, 1, buf};
dev_dbg(&d->intf->dev, "\n"); dev_dbg(&d->intf->dev, "\n");
...@@ -483,6 +485,35 @@ static int rtl2832u_read_config(struct dvb_usb_device *d) ...@@ -483,6 +485,35 @@ static int rtl2832u_read_config(struct dvb_usb_device *d)
goto tuner_found; goto tuner_found;
} }
/* GPIO0 and GPIO5 to reset Si2157/Si2168 tuner and demod */
ret = rtl28xxu_wr_reg_mask(d, SYS_GPIO_OUT_VAL, 0x00, 0x21);
if (ret)
goto err;
ret = rtl28xxu_wr_reg_mask(d, SYS_GPIO_OUT_EN, 0x00, 0x21);
if (ret)
goto err;
msleep(50);
ret = rtl28xxu_wr_reg_mask(d, SYS_GPIO_OUT_VAL, 0x21, 0x21);
if (ret)
goto err;
ret = rtl28xxu_wr_reg_mask(d, SYS_GPIO_OUT_EN, 0x21, 0x21);
if (ret)
goto err;
msleep(50);
/* check Si2157 ID register; reg=c0 val=80 */
ret = rtl28xxu_ctrl_msg(d, &req_si2157);
if (ret == 0 && ((buf[0] & 0x80) == 0x80)) {
dev->tuner = TUNER_RTL2832_SI2157;
dev->tuner_name = "SI2157";
goto tuner_found;
}
tuner_found: tuner_found:
dev_dbg(&d->intf->dev, "tuner=%s\n", dev->tuner_name); dev_dbg(&d->intf->dev, "tuner=%s\n", dev->tuner_name);
...@@ -516,6 +547,15 @@ static int rtl2832u_read_config(struct dvb_usb_device *d) ...@@ -516,6 +547,15 @@ static int rtl2832u_read_config(struct dvb_usb_device *d)
goto demod_found; goto demod_found;
} }
} }
if (dev->tuner == TUNER_RTL2832_SI2157) {
/* check Si2168 ID register; reg=c8 val=80 */
ret = rtl28xxu_ctrl_msg(d, &req_si2168);
if (ret == 0 && ((buf[0] & 0x80) == 0x80)) {
dev_dbg(&d->intf->dev, "Si2168 found\n");
dev->slave_demod = SLAVE_DEMOD_SI2168;
goto demod_found;
}
}
demod_found: demod_found:
/* close demod I2C gate */ /* close demod I2C gate */
...@@ -674,6 +714,11 @@ static const struct rtl2832_platform_data rtl2832_r820t_platform_data = { ...@@ -674,6 +714,11 @@ static const struct rtl2832_platform_data rtl2832_r820t_platform_data = {
.tuner = TUNER_RTL2832_R820T, .tuner = TUNER_RTL2832_R820T,
}; };
static const struct rtl2832_platform_data rtl2832_si2157_platform_data = {
.clk = 28800000,
.tuner = TUNER_RTL2832_SI2157,
};
static int rtl2832u_fc0012_tuner_callback(struct dvb_usb_device *d, static int rtl2832u_fc0012_tuner_callback(struct dvb_usb_device *d,
int cmd, int arg) int cmd, int arg)
{ {
...@@ -823,6 +868,9 @@ static int rtl2832u_frontend_attach(struct dvb_usb_adapter *adap) ...@@ -823,6 +868,9 @@ static int rtl2832u_frontend_attach(struct dvb_usb_adapter *adap)
case TUNER_RTL2832_R828D: case TUNER_RTL2832_R828D:
*pdata = rtl2832_r820t_platform_data; *pdata = rtl2832_r820t_platform_data;
break; break;
case TUNER_RTL2832_SI2157:
*pdata = rtl2832_si2157_platform_data;
break;
default: default:
dev_err(&d->intf->dev, "unknown tuner %s\n", dev->tuner_name); dev_err(&d->intf->dev, "unknown tuner %s\n", dev->tuner_name);
ret = -ENODEV; ret = -ENODEV;
...@@ -890,7 +938,7 @@ static int rtl2832u_frontend_attach(struct dvb_usb_adapter *adap) ...@@ -890,7 +938,7 @@ static int rtl2832u_frontend_attach(struct dvb_usb_adapter *adap)
} }
dev->i2c_client_slave_demod = client; dev->i2c_client_slave_demod = client;
} else { } else if (dev->slave_demod == SLAVE_DEMOD_MN88473) {
struct mn88473_config mn88473_config = {}; struct mn88473_config mn88473_config = {};
mn88473_config.fe = &adap->fe[1]; mn88473_config.fe = &adap->fe[1];
...@@ -912,9 +960,37 @@ static int rtl2832u_frontend_attach(struct dvb_usb_adapter *adap) ...@@ -912,9 +960,37 @@ static int rtl2832u_frontend_attach(struct dvb_usb_adapter *adap)
} }
dev->i2c_client_slave_demod = client; dev->i2c_client_slave_demod = client;
} else {
struct si2168_config si2168_config = {};
struct i2c_adapter *adapter;
si2168_config.i2c_adapter = &adapter;
si2168_config.fe = &adap->fe[1];
si2168_config.ts_mode = SI2168_TS_SERIAL;
si2168_config.ts_clock_inv = false;
si2168_config.ts_clock_gapped = true;
strlcpy(info.type, "si2168", I2C_NAME_SIZE);
info.addr = 0x64;
info.platform_data = &si2168_config;
request_module(info.type);
client = i2c_new_device(&d->i2c_adap, &info);
if (client == NULL || client->dev.driver == NULL) {
dev->slave_demod = SLAVE_DEMOD_NONE;
goto err_slave_demod_failed;
} }
if (!try_module_get(client->dev.driver->owner)) {
i2c_unregister_device(client);
dev->slave_demod = SLAVE_DEMOD_NONE;
goto err_slave_demod_failed;
} }
dev->i2c_client_slave_demod = client;
/* for Si2168 devices use only new I2C write method */
dev->new_i2c_write = true;
}
}
return 0; return 0;
err_slave_demod_failed: err_slave_demod_failed:
err: err:
...@@ -1154,6 +1230,39 @@ static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap) ...@@ -1154,6 +1230,39 @@ static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap)
adap->fe[1]->ops.tuner_ops.get_rf_strength; adap->fe[1]->ops.tuner_ops.get_rf_strength;
} }
break; break;
case TUNER_RTL2832_SI2157: {
struct si2157_config si2157_config = {
.fe = adap->fe[0],
.if_port = 0,
.inversion = false,
};
strlcpy(info.type, "si2157", I2C_NAME_SIZE);
info.addr = 0x60;
info.platform_data = &si2157_config;
request_module(info.type);
client = i2c_new_device(&d->i2c_adap, &info);
if (client == NULL || client->dev.driver == NULL)
break;
if (!try_module_get(client->dev.driver->owner)) {
i2c_unregister_device(client);
break;
}
dev->i2c_client_tuner = client;
subdev = i2c_get_clientdata(client);
/* copy tuner ops for 2nd FE as tuner is shared */
if (adap->fe[1]) {
adap->fe[1]->tuner_priv =
adap->fe[0]->tuner_priv;
memcpy(&adap->fe[1]->ops.tuner_ops,
&adap->fe[0]->ops.tuner_ops,
sizeof(struct dvb_tuner_ops));
}
}
break;
default: default:
dev_err(&d->intf->dev, "unknown tuner %d\n", dev->tuner); dev_err(&d->intf->dev, "unknown tuner %d\n", dev->tuner);
} }
...@@ -1770,6 +1879,8 @@ static const struct usb_device_id rtl28xxu_id_table[] = { ...@@ -1770,6 +1879,8 @@ static const struct usb_device_id rtl28xxu_id_table[] = {
/* RTL2832P devices: */ /* RTL2832P devices: */
{ DVB_USB_DEVICE(USB_VID_HANFTEK, 0x0131, { DVB_USB_DEVICE(USB_VID_HANFTEK, 0x0131,
&rtl28xxu_props, "Astrometa DVB-T2", NULL) }, &rtl28xxu_props, "Astrometa DVB-T2", NULL) },
{ DVB_USB_DEVICE(0x5654, 0xca42,
&rtl28xxu_props, "GoTView MasterHD 3", NULL) },
{ } { }
}; };
MODULE_DEVICE_TABLE(usb, rtl28xxu_id_table); MODULE_DEVICE_TABLE(usb, rtl28xxu_id_table);
......
...@@ -41,6 +41,8 @@ ...@@ -41,6 +41,8 @@
#include "fc2580.h" #include "fc2580.h"
#include "tua9001.h" #include "tua9001.h"
#include "r820t.h" #include "r820t.h"
#include "si2168.h"
#include "si2157.h"
/* /*
* USB commands * USB commands
...@@ -76,6 +78,7 @@ struct rtl28xxu_dev { ...@@ -76,6 +78,7 @@ struct rtl28xxu_dev {
u8 page; /* integrated demod active register page */ u8 page; /* integrated demod active register page */
struct i2c_adapter *demod_i2c_adapter; struct i2c_adapter *demod_i2c_adapter;
bool rc_active; bool rc_active;
bool new_i2c_write;
struct i2c_client *i2c_client_demod; struct i2c_client *i2c_client_demod;
struct i2c_client *i2c_client_tuner; struct i2c_client *i2c_client_tuner;
struct i2c_client *i2c_client_slave_demod; struct i2c_client *i2c_client_slave_demod;
...@@ -83,6 +86,7 @@ struct rtl28xxu_dev { ...@@ -83,6 +86,7 @@ struct rtl28xxu_dev {
#define SLAVE_DEMOD_NONE 0 #define SLAVE_DEMOD_NONE 0
#define SLAVE_DEMOD_MN88472 1 #define SLAVE_DEMOD_MN88472 1
#define SLAVE_DEMOD_MN88473 2 #define SLAVE_DEMOD_MN88473 2
#define SLAVE_DEMOD_SI2168 3
unsigned int slave_demod:2; unsigned int slave_demod:2;
union { union {
struct rtl2830_platform_data rtl2830_platform_data; struct rtl2830_platform_data rtl2830_platform_data;
...@@ -116,6 +120,7 @@ enum rtl28xxu_tuner { ...@@ -116,6 +120,7 @@ enum rtl28xxu_tuner {
TUNER_RTL2832_FC0013, TUNER_RTL2832_FC0013,
TUNER_RTL2832_R820T, TUNER_RTL2832_R820T,
TUNER_RTL2832_R828D, TUNER_RTL2832_R828D,
TUNER_RTL2832_SI2157,
}; };
struct rtl28xxu_req { struct rtl28xxu_req {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment