|
@@ -1,5 +1,5 @@
|
|
|
/*
|
|
|
- * Montage M88DS3103 demodulator driver
|
|
|
+ * Montage M88DS3103/M88RS6000 demodulator driver
|
|
|
*
|
|
|
* Copyright (C) 2013 Antti Palosaari <crope@iki.fi>
|
|
|
*
|
|
@@ -162,7 +162,7 @@ static int m88ds3103_wr_reg_val_tab(struct m88ds3103_priv *priv,
|
|
|
|
|
|
dev_dbg(&priv->i2c->dev, "%s: tab_len=%d\n", __func__, tab_len);
|
|
|
|
|
|
- if (tab_len > 83) {
|
|
|
+ if (tab_len > 86) {
|
|
|
ret = -EINVAL;
|
|
|
goto err;
|
|
|
}
|
|
@@ -246,7 +246,7 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
|
|
|
int ret, len;
|
|
|
const struct m88ds3103_reg_val *init;
|
|
|
u8 u8tmp, u8tmp1, u8tmp2;
|
|
|
- u8 buf[2];
|
|
|
+ u8 buf[3];
|
|
|
u16 u16tmp, divide_ratio;
|
|
|
u32 tuner_frequency, target_mclk;
|
|
|
s32 s32tmp;
|
|
@@ -262,6 +262,22 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
|
|
|
goto err;
|
|
|
}
|
|
|
|
|
|
+ /* reset */
|
|
|
+ ret = m88ds3103_wr_reg(priv, 0x07, 0x80);
|
|
|
+ if (ret)
|
|
|
+ goto err;
|
|
|
+
|
|
|
+ ret = m88ds3103_wr_reg(priv, 0x07, 0x00);
|
|
|
+ if (ret)
|
|
|
+ goto err;
|
|
|
+
|
|
|
+ /* Disable demod clock path */
|
|
|
+ if (priv->chip_id == M88RS6000_CHIP_ID) {
|
|
|
+ ret = m88ds3103_wr_reg(priv, 0x06, 0xe0);
|
|
|
+ if (ret)
|
|
|
+ goto err;
|
|
|
+ }
|
|
|
+
|
|
|
/* program tuner */
|
|
|
if (fe->ops.tuner_ops.set_params) {
|
|
|
ret = fe->ops.tuner_ops.set_params(fe);
|
|
@@ -282,14 +298,76 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
|
|
|
tuner_frequency = c->frequency;
|
|
|
}
|
|
|
|
|
|
- /* reset */
|
|
|
- ret = m88ds3103_wr_reg(priv, 0x07, 0x80);
|
|
|
- if (ret)
|
|
|
- goto err;
|
|
|
+ /* select M88RS6000 demod main mclk and ts mclk from tuner die. */
|
|
|
+ if (priv->chip_id == M88RS6000_CHIP_ID) {
|
|
|
+ if (c->symbol_rate > 45010000)
|
|
|
+ priv->mclk_khz = 110250;
|
|
|
+ else
|
|
|
+ priv->mclk_khz = 96000;
|
|
|
|
|
|
- ret = m88ds3103_wr_reg(priv, 0x07, 0x00);
|
|
|
- if (ret)
|
|
|
- goto err;
|
|
|
+ if (c->delivery_system == SYS_DVBS)
|
|
|
+ target_mclk = 96000;
|
|
|
+ else
|
|
|
+ target_mclk = 144000;
|
|
|
+
|
|
|
+ /* Enable demod clock path */
|
|
|
+ ret = m88ds3103_wr_reg(priv, 0x06, 0x00);
|
|
|
+ if (ret)
|
|
|
+ goto err;
|
|
|
+ usleep_range(10000, 20000);
|
|
|
+ } else {
|
|
|
+ /* set M88DS3103 mclk and ts mclk. */
|
|
|
+ priv->mclk_khz = 96000;
|
|
|
+
|
|
|
+ if (c->delivery_system == SYS_DVBS)
|
|
|
+ target_mclk = 96000;
|
|
|
+ else {
|
|
|
+ switch (priv->cfg->ts_mode) {
|
|
|
+ case M88DS3103_TS_SERIAL:
|
|
|
+ case M88DS3103_TS_SERIAL_D7:
|
|
|
+ if (c->symbol_rate < 18000000)
|
|
|
+ target_mclk = 96000;
|
|
|
+ else
|
|
|
+ target_mclk = 144000;
|
|
|
+ break;
|
|
|
+ case M88DS3103_TS_PARALLEL:
|
|
|
+ case M88DS3103_TS_CI:
|
|
|
+ if (c->symbol_rate < 18000000)
|
|
|
+ target_mclk = 96000;
|
|
|
+ else if (c->symbol_rate < 28000000)
|
|
|
+ target_mclk = 144000;
|
|
|
+ else
|
|
|
+ target_mclk = 192000;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ dev_dbg(&priv->i2c->dev, "%s: invalid ts_mode\n",
|
|
|
+ __func__);
|
|
|
+ ret = -EINVAL;
|
|
|
+ goto err;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ switch (target_mclk) {
|
|
|
+ case 96000:
|
|
|
+ u8tmp1 = 0x02; /* 0b10 */
|
|
|
+ u8tmp2 = 0x01; /* 0b01 */
|
|
|
+ break;
|
|
|
+ case 144000:
|
|
|
+ u8tmp1 = 0x00; /* 0b00 */
|
|
|
+ u8tmp2 = 0x01; /* 0b01 */
|
|
|
+ break;
|
|
|
+ case 192000:
|
|
|
+ u8tmp1 = 0x03; /* 0b11 */
|
|
|
+ u8tmp2 = 0x00; /* 0b00 */
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ ret = m88ds3103_wr_reg_mask(priv, 0x22, u8tmp1 << 6, 0xc0);
|
|
|
+ if (ret)
|
|
|
+ goto err;
|
|
|
+ ret = m88ds3103_wr_reg_mask(priv, 0x24, u8tmp2 << 6, 0xc0);
|
|
|
+ if (ret)
|
|
|
+ goto err;
|
|
|
+ }
|
|
|
|
|
|
ret = m88ds3103_wr_reg(priv, 0xb2, 0x01);
|
|
|
if (ret)
|
|
@@ -301,36 +379,21 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
|
|
|
|
|
|
switch (c->delivery_system) {
|
|
|
case SYS_DVBS:
|
|
|
- len = ARRAY_SIZE(m88ds3103_dvbs_init_reg_vals);
|
|
|
- init = m88ds3103_dvbs_init_reg_vals;
|
|
|
- target_mclk = 96000;
|
|
|
+ if (priv->chip_id == M88RS6000_CHIP_ID) {
|
|
|
+ len = ARRAY_SIZE(m88rs6000_dvbs_init_reg_vals);
|
|
|
+ init = m88rs6000_dvbs_init_reg_vals;
|
|
|
+ } else {
|
|
|
+ len = ARRAY_SIZE(m88ds3103_dvbs_init_reg_vals);
|
|
|
+ init = m88ds3103_dvbs_init_reg_vals;
|
|
|
+ }
|
|
|
break;
|
|
|
case SYS_DVBS2:
|
|
|
- len = ARRAY_SIZE(m88ds3103_dvbs2_init_reg_vals);
|
|
|
- init = m88ds3103_dvbs2_init_reg_vals;
|
|
|
-
|
|
|
- switch (priv->cfg->ts_mode) {
|
|
|
- case M88DS3103_TS_SERIAL:
|
|
|
- case M88DS3103_TS_SERIAL_D7:
|
|
|
- if (c->symbol_rate < 18000000)
|
|
|
- target_mclk = 96000;
|
|
|
- else
|
|
|
- target_mclk = 144000;
|
|
|
- break;
|
|
|
- case M88DS3103_TS_PARALLEL:
|
|
|
- case M88DS3103_TS_CI:
|
|
|
- if (c->symbol_rate < 18000000)
|
|
|
- target_mclk = 96000;
|
|
|
- else if (c->symbol_rate < 28000000)
|
|
|
- target_mclk = 144000;
|
|
|
- else
|
|
|
- target_mclk = 192000;
|
|
|
- break;
|
|
|
- default:
|
|
|
- dev_dbg(&priv->i2c->dev, "%s: invalid ts_mode\n",
|
|
|
- __func__);
|
|
|
- ret = -EINVAL;
|
|
|
- goto err;
|
|
|
+ if (priv->chip_id == M88RS6000_CHIP_ID) {
|
|
|
+ len = ARRAY_SIZE(m88rs6000_dvbs2_init_reg_vals);
|
|
|
+ init = m88rs6000_dvbs2_init_reg_vals;
|
|
|
+ } else {
|
|
|
+ len = ARRAY_SIZE(m88ds3103_dvbs2_init_reg_vals);
|
|
|
+ init = m88ds3103_dvbs2_init_reg_vals;
|
|
|
}
|
|
|
break;
|
|
|
default:
|
|
@@ -347,6 +410,30 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
|
|
|
goto err;
|
|
|
}
|
|
|
|
|
|
+ if (priv->chip_id == M88RS6000_CHIP_ID) {
|
|
|
+ if ((c->delivery_system == SYS_DVBS2)
|
|
|
+ && ((c->symbol_rate / 1000) <= 5000)) {
|
|
|
+ ret = m88ds3103_wr_reg(priv, 0xc0, 0x04);
|
|
|
+ if (ret)
|
|
|
+ goto err;
|
|
|
+ buf[0] = 0x09;
|
|
|
+ buf[1] = 0x22;
|
|
|
+ buf[2] = 0x88;
|
|
|
+ ret = m88ds3103_wr_regs(priv, 0x8a, buf, 3);
|
|
|
+ if (ret)
|
|
|
+ goto err;
|
|
|
+ }
|
|
|
+ ret = m88ds3103_wr_reg_mask(priv, 0x9d, 0x08, 0x08);
|
|
|
+ if (ret)
|
|
|
+ goto err;
|
|
|
+ ret = m88ds3103_wr_reg(priv, 0xf1, 0x01);
|
|
|
+ if (ret)
|
|
|
+ goto err;
|
|
|
+ ret = m88ds3103_wr_reg_mask(priv, 0x30, 0x80, 0x80);
|
|
|
+ if (ret)
|
|
|
+ goto err;
|
|
|
+ }
|
|
|
+
|
|
|
u8tmp1 = 0; /* silence compiler warning */
|
|
|
switch (priv->cfg->ts_mode) {
|
|
|
case M88DS3103_TS_SERIAL:
|
|
@@ -420,29 +507,6 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
|
|
|
if (ret)
|
|
|
goto err;
|
|
|
|
|
|
- switch (target_mclk) {
|
|
|
- case 96000:
|
|
|
- u8tmp1 = 0x02; /* 0b10 */
|
|
|
- u8tmp2 = 0x01; /* 0b01 */
|
|
|
- break;
|
|
|
- case 144000:
|
|
|
- u8tmp1 = 0x00; /* 0b00 */
|
|
|
- u8tmp2 = 0x01; /* 0b01 */
|
|
|
- break;
|
|
|
- case 192000:
|
|
|
- u8tmp1 = 0x03; /* 0b11 */
|
|
|
- u8tmp2 = 0x00; /* 0b00 */
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- ret = m88ds3103_wr_reg_mask(priv, 0x22, u8tmp1 << 6, 0xc0);
|
|
|
- if (ret)
|
|
|
- goto err;
|
|
|
-
|
|
|
- ret = m88ds3103_wr_reg_mask(priv, 0x24, u8tmp2 << 6, 0xc0);
|
|
|
- if (ret)
|
|
|
- goto err;
|
|
|
-
|
|
|
if (c->symbol_rate <= 3000000)
|
|
|
u8tmp = 0x20;
|
|
|
else if (c->symbol_rate <= 10000000)
|
|
@@ -466,7 +530,7 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
|
|
|
if (ret)
|
|
|
goto err;
|
|
|
|
|
|
- u16tmp = DIV_ROUND_CLOSEST((c->symbol_rate / 1000) << 15, M88DS3103_MCLK_KHZ / 2);
|
|
|
+ u16tmp = DIV_ROUND_CLOSEST((c->symbol_rate / 1000) << 15, priv->mclk_khz / 2);
|
|
|
buf[0] = (u16tmp >> 0) & 0xff;
|
|
|
buf[1] = (u16tmp >> 8) & 0xff;
|
|
|
ret = m88ds3103_wr_regs(priv, 0x61, buf, 2);
|
|
@@ -489,7 +553,7 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
|
|
|
(tuner_frequency - c->frequency));
|
|
|
|
|
|
s32tmp = 0x10000 * (tuner_frequency - c->frequency);
|
|
|
- s32tmp = DIV_ROUND_CLOSEST(s32tmp, M88DS3103_MCLK_KHZ);
|
|
|
+ s32tmp = DIV_ROUND_CLOSEST(s32tmp, priv->mclk_khz);
|
|
|
if (s32tmp < 0)
|
|
|
s32tmp += 0x10000;
|
|
|
|
|
@@ -520,7 +584,7 @@ static int m88ds3103_init(struct dvb_frontend *fe)
|
|
|
struct m88ds3103_priv *priv = fe->demodulator_priv;
|
|
|
int ret, len, remaining;
|
|
|
const struct firmware *fw = NULL;
|
|
|
- u8 *fw_file = M88DS3103_FIRMWARE;
|
|
|
+ u8 *fw_file;
|
|
|
u8 u8tmp;
|
|
|
|
|
|
dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
|
|
@@ -541,15 +605,6 @@ static int m88ds3103_init(struct dvb_frontend *fe)
|
|
|
if (ret)
|
|
|
goto err;
|
|
|
|
|
|
- /* reset */
|
|
|
- ret = m88ds3103_wr_reg(priv, 0x07, 0x60);
|
|
|
- if (ret)
|
|
|
- goto err;
|
|
|
-
|
|
|
- ret = m88ds3103_wr_reg(priv, 0x07, 0x00);
|
|
|
- if (ret)
|
|
|
- goto err;
|
|
|
-
|
|
|
/* firmware status */
|
|
|
ret = m88ds3103_rd_reg(priv, 0xb9, &u8tmp);
|
|
|
if (ret)
|
|
@@ -560,10 +615,23 @@ static int m88ds3103_init(struct dvb_frontend *fe)
|
|
|
if (u8tmp)
|
|
|
goto skip_fw_download;
|
|
|
|
|
|
+ /* global reset, global diseqc reset, golbal fec reset */
|
|
|
+ ret = m88ds3103_wr_reg(priv, 0x07, 0xe0);
|
|
|
+ if (ret)
|
|
|
+ goto err;
|
|
|
+
|
|
|
+ ret = m88ds3103_wr_reg(priv, 0x07, 0x00);
|
|
|
+ if (ret)
|
|
|
+ goto err;
|
|
|
+
|
|
|
/* cold state - try to download firmware */
|
|
|
dev_info(&priv->i2c->dev, "%s: found a '%s' in cold state\n",
|
|
|
KBUILD_MODNAME, m88ds3103_ops.info.name);
|
|
|
|
|
|
+ if (priv->chip_id == M88RS6000_CHIP_ID)
|
|
|
+ fw_file = M88RS6000_FIRMWARE;
|
|
|
+ else
|
|
|
+ fw_file = M88DS3103_FIRMWARE;
|
|
|
/* request the firmware, this will block and timeout */
|
|
|
ret = request_firmware(&fw, fw_file, priv->i2c->dev.parent);
|
|
|
if (ret) {
|
|
@@ -635,13 +703,18 @@ static int m88ds3103_sleep(struct dvb_frontend *fe)
|
|
|
{
|
|
|
struct m88ds3103_priv *priv = fe->demodulator_priv;
|
|
|
int ret;
|
|
|
+ u8 u8tmp;
|
|
|
|
|
|
dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
|
|
|
|
|
|
priv->delivery_system = SYS_UNDEFINED;
|
|
|
|
|
|
/* TS Hi-Z */
|
|
|
- ret = m88ds3103_wr_reg_mask(priv, 0x27, 0x00, 0x01);
|
|
|
+ if (priv->chip_id == M88RS6000_CHIP_ID)
|
|
|
+ u8tmp = 0x29;
|
|
|
+ else
|
|
|
+ u8tmp = 0x27;
|
|
|
+ ret = m88ds3103_wr_reg_mask(priv, u8tmp, 0x00, 0x01);
|
|
|
if (ret)
|
|
|
goto err;
|
|
|
|
|
@@ -830,7 +903,7 @@ static int m88ds3103_get_frontend(struct dvb_frontend *fe)
|
|
|
goto err;
|
|
|
|
|
|
c->symbol_rate = 1ull * ((buf[1] << 8) | (buf[0] << 0)) *
|
|
|
- M88DS3103_MCLK_KHZ * 1000 / 0x10000;
|
|
|
+ priv->mclk_khz * 1000 / 0x10000;
|
|
|
|
|
|
return 0;
|
|
|
err:
|
|
@@ -1310,18 +1383,22 @@ struct dvb_frontend *m88ds3103_attach(const struct m88ds3103_config *cfg,
|
|
|
priv->i2c = i2c;
|
|
|
mutex_init(&priv->i2c_mutex);
|
|
|
|
|
|
- ret = m88ds3103_rd_reg(priv, 0x01, &chip_id);
|
|
|
+ /* 0x00: chip id[6:0], 0x01: chip ver[7:0], 0x02: chip ver[15:8] */
|
|
|
+ ret = m88ds3103_rd_reg(priv, 0x00, &chip_id);
|
|
|
if (ret)
|
|
|
goto err;
|
|
|
|
|
|
- dev_dbg(&priv->i2c->dev, "%s: chip_id=%02x\n", __func__, chip_id);
|
|
|
+ chip_id >>= 1;
|
|
|
+ dev_info(&priv->i2c->dev, "%s: chip_id=%02x\n", __func__, chip_id);
|
|
|
|
|
|
switch (chip_id) {
|
|
|
- case 0xd0:
|
|
|
+ case M88RS6000_CHIP_ID:
|
|
|
+ case M88DS3103_CHIP_ID:
|
|
|
break;
|
|
|
default:
|
|
|
goto err;
|
|
|
}
|
|
|
+ priv->chip_id = chip_id;
|
|
|
|
|
|
switch (priv->cfg->clock_out) {
|
|
|
case M88DS3103_CLOCK_OUT_DISABLED:
|
|
@@ -1337,6 +1414,11 @@ struct dvb_frontend *m88ds3103_attach(const struct m88ds3103_config *cfg,
|
|
|
goto err;
|
|
|
}
|
|
|
|
|
|
+ /* 0x29 register is defined differently for m88rs6000. */
|
|
|
+ /* set internal tuner address to 0x21 */
|
|
|
+ if (chip_id == M88RS6000_CHIP_ID)
|
|
|
+ u8tmp = 0x00;
|
|
|
+
|
|
|
ret = m88ds3103_wr_reg(priv, 0x29, u8tmp);
|
|
|
if (ret)
|
|
|
goto err;
|
|
@@ -1364,6 +1446,9 @@ struct dvb_frontend *m88ds3103_attach(const struct m88ds3103_config *cfg,
|
|
|
|
|
|
/* create dvb_frontend */
|
|
|
memcpy(&priv->fe.ops, &m88ds3103_ops, sizeof(struct dvb_frontend_ops));
|
|
|
+ if (priv->chip_id == M88RS6000_CHIP_ID)
|
|
|
+ strncpy(priv->fe.ops.info.name,
|
|
|
+ "Montage M88RS6000", sizeof(priv->fe.ops.info.name));
|
|
|
priv->fe.demodulator_priv = priv;
|
|
|
|
|
|
return &priv->fe;
|
|
@@ -1423,3 +1508,4 @@ MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
|
|
|
MODULE_DESCRIPTION("Montage M88DS3103 DVB-S/S2 demodulator driver");
|
|
|
MODULE_LICENSE("GPL");
|
|
|
MODULE_FIRMWARE(M88DS3103_FIRMWARE);
|
|
|
+MODULE_FIRMWARE(M88RS6000_FIRMWARE);
|