diff options
| author | Antti Palosaari <crope@iki.fi> | 2015-03-23 13:14:40 -0400 |
|---|---|---|
| committer | Mauro Carvalho Chehab <mchehab@osg.samsung.com> | 2015-04-02 21:24:03 -0400 |
| commit | abd9025b95619c02f83583c9659298bc65dcdf50 (patch) | |
| tree | fda949142bd0c14253c6928169237e65bdf8a631 /drivers/media/dvb-frontends | |
| parent | 27254c36346b8c3990f4951c39afc495c7c2c2ad (diff) | |
[media] ts2020: add support for TS2022
TS2022 is slightly newer and different version of same tuner, which
could be supported with rather small changes. Tuner type is
auto-detected.
Tested-by: David Howells <dhowells@redhat.com>
Signed-off-by: Antti Palosaari <crope@iki.fi>
Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
Diffstat (limited to 'drivers/media/dvb-frontends')
| -rw-r--r-- | drivers/media/dvb-frontends/ts2020.c | 120 | ||||
| -rw-r--r-- | drivers/media/dvb-frontends/ts2020.h | 25 |
2 files changed, 129 insertions, 16 deletions
diff --git a/drivers/media/dvb-frontends/ts2020.c b/drivers/media/dvb-frontends/ts2020.c index 9aba044dabed..24c4712b77fe 100644 --- a/drivers/media/dvb-frontends/ts2020.c +++ b/drivers/media/dvb-frontends/ts2020.c | |||
| @@ -29,9 +29,19 @@ struct ts2020_priv { | |||
| 29 | /* i2c details */ | 29 | /* i2c details */ |
| 30 | int i2c_address; | 30 | int i2c_address; |
| 31 | struct i2c_adapter *i2c; | 31 | struct i2c_adapter *i2c; |
| 32 | u8 clk_out_div; | 32 | u8 clk_out:2; |
| 33 | u8 clk_out_div:5; | ||
| 33 | u32 frequency; | 34 | u32 frequency; |
| 34 | u32 frequency_div; | 35 | u32 frequency_div; |
| 36 | #define TS2020_M88TS2020 0 | ||
| 37 | #define TS2020_M88TS2022 1 | ||
| 38 | u8 tuner; | ||
| 39 | u8 loop_through:1; | ||
| 40 | }; | ||
| 41 | |||
| 42 | struct ts2020_reg_val { | ||
| 43 | u8 reg; | ||
| 44 | u8 val; | ||
| 35 | }; | 45 | }; |
| 36 | 46 | ||
| 37 | static int ts2020_release(struct dvb_frontend *fe) | 47 | static int ts2020_release(struct dvb_frontend *fe) |
| @@ -121,6 +131,9 @@ static int ts2020_sleep(struct dvb_frontend *fe) | |||
| 121 | .len = 2 | 131 | .len = 2 |
| 122 | }; | 132 | }; |
| 123 | 133 | ||
| 134 | if (priv->tuner == TS2020_M88TS2022) | ||
| 135 | buf[0] = 0x00; | ||
| 136 | |||
| 124 | if (fe->ops.i2c_gate_ctrl) | 137 | if (fe->ops.i2c_gate_ctrl) |
| 125 | fe->ops.i2c_gate_ctrl(fe, 1); | 138 | fe->ops.i2c_gate_ctrl(fe, 1); |
| 126 | 139 | ||
| @@ -137,15 +150,64 @@ static int ts2020_sleep(struct dvb_frontend *fe) | |||
| 137 | static int ts2020_init(struct dvb_frontend *fe) | 150 | static int ts2020_init(struct dvb_frontend *fe) |
| 138 | { | 151 | { |
| 139 | struct ts2020_priv *priv = fe->tuner_priv; | 152 | struct ts2020_priv *priv = fe->tuner_priv; |
| 153 | int i; | ||
| 154 | u8 u8tmp; | ||
| 155 | |||
| 156 | if (priv->tuner == TS2020_M88TS2020) { | ||
| 157 | ts2020_writereg(fe, 0x42, 0x73); | ||
| 158 | ts2020_writereg(fe, 0x05, priv->clk_out_div); | ||
| 159 | ts2020_writereg(fe, 0x20, 0x27); | ||
| 160 | ts2020_writereg(fe, 0x07, 0x02); | ||
| 161 | ts2020_writereg(fe, 0x11, 0xff); | ||
| 162 | ts2020_writereg(fe, 0x60, 0xf9); | ||
| 163 | ts2020_writereg(fe, 0x08, 0x01); | ||
| 164 | ts2020_writereg(fe, 0x00, 0x41); | ||
| 165 | } else { | ||
| 166 | static const struct ts2020_reg_val reg_vals[] = { | ||
| 167 | {0x7d, 0x9d}, | ||
| 168 | {0x7c, 0x9a}, | ||
| 169 | {0x7a, 0x76}, | ||
| 170 | {0x3b, 0x01}, | ||
| 171 | {0x63, 0x88}, | ||
| 172 | {0x61, 0x85}, | ||
| 173 | {0x22, 0x30}, | ||
| 174 | {0x30, 0x40}, | ||
| 175 | {0x20, 0x23}, | ||
| 176 | {0x24, 0x02}, | ||
| 177 | {0x12, 0xa0}, | ||
| 178 | }; | ||
| 179 | |||
| 180 | ts2020_writereg(fe, 0x00, 0x01); | ||
| 181 | ts2020_writereg(fe, 0x00, 0x03); | ||
| 182 | |||
| 183 | switch (priv->clk_out) { | ||
| 184 | case TS2020_CLK_OUT_DISABLED: | ||
| 185 | u8tmp = 0x60; | ||
| 186 | break; | ||
| 187 | case TS2020_CLK_OUT_ENABLED: | ||
| 188 | u8tmp = 0x70; | ||
| 189 | ts2020_writereg(fe, 0x05, priv->clk_out_div); | ||
| 190 | break; | ||
| 191 | case TS2020_CLK_OUT_ENABLED_XTALOUT: | ||
| 192 | u8tmp = 0x6c; | ||
| 193 | break; | ||
| 194 | default: | ||
| 195 | u8tmp = 0x60; | ||
| 196 | break; | ||
| 197 | } | ||
| 198 | |||
| 199 | ts2020_writereg(fe, 0x42, u8tmp); | ||
| 200 | |||
| 201 | if (priv->loop_through) | ||
| 202 | u8tmp = 0xec; | ||
| 203 | else | ||
| 204 | u8tmp = 0x6c; | ||
| 140 | 205 | ||
| 141 | ts2020_writereg(fe, 0x42, 0x73); | 206 | ts2020_writereg(fe, 0x62, u8tmp); |
| 142 | ts2020_writereg(fe, 0x05, priv->clk_out_div); | 207 | |
| 143 | ts2020_writereg(fe, 0x20, 0x27); | 208 | for (i = 0; i < ARRAY_SIZE(reg_vals); i++) |
| 144 | ts2020_writereg(fe, 0x07, 0x02); | 209 | ts2020_writereg(fe, reg_vals[i].reg, reg_vals[i].val); |
| 145 | ts2020_writereg(fe, 0x11, 0xff); | 210 | } |
| 146 | ts2020_writereg(fe, 0x60, 0xf9); | ||
| 147 | ts2020_writereg(fe, 0x08, 0x01); | ||
| 148 | ts2020_writereg(fe, 0x00, 0x41); | ||
| 149 | 211 | ||
| 150 | return 0; | 212 | return 0; |
| 151 | } | 213 | } |
| @@ -203,7 +265,14 @@ static int ts2020_set_params(struct dvb_frontend *fe) | |||
| 203 | ndiv = ndiv + ndiv % 2; | 265 | ndiv = ndiv + ndiv % 2; |
| 204 | ndiv = ndiv - 1024; | 266 | ndiv = ndiv - 1024; |
| 205 | 267 | ||
| 206 | ret = ts2020_writereg(fe, 0x10, 0x80 | lo); | 268 | if (priv->tuner == TS2020_M88TS2020) { |
| 269 | lpf_coeff = 2766; | ||
| 270 | ret = ts2020_writereg(fe, 0x10, 0x80 | lo); | ||
| 271 | } else { | ||
| 272 | lpf_coeff = 3200; | ||
| 273 | ret = ts2020_writereg(fe, 0x10, 0x0b); | ||
| 274 | ret |= ts2020_writereg(fe, 0x11, 0x40); | ||
| 275 | } | ||
| 207 | 276 | ||
| 208 | /* Set frequency divider */ | 277 | /* Set frequency divider */ |
| 209 | ret |= ts2020_writereg(fe, 0x01, (ndiv >> 8) & 0xf); | 278 | ret |= ts2020_writereg(fe, 0x01, (ndiv >> 8) & 0xf); |
| @@ -220,7 +289,8 @@ static int ts2020_set_params(struct dvb_frontend *fe) | |||
| 220 | ret |= ts2020_tuner_gate_ctrl(fe, 0x08); | 289 | ret |= ts2020_tuner_gate_ctrl(fe, 0x08); |
| 221 | 290 | ||
| 222 | /* Tuner RF */ | 291 | /* Tuner RF */ |
| 223 | ret |= ts2020_set_tuner_rf(fe); | 292 | if (priv->tuner == TS2020_M88TS2020) |
| 293 | ret |= ts2020_set_tuner_rf(fe); | ||
| 224 | 294 | ||
| 225 | gdiv28 = (TS2020_XTAL_FREQ / 1000 * 1694 + 500) / 1000; | 295 | gdiv28 = (TS2020_XTAL_FREQ / 1000 * 1694 + 500) / 1000; |
| 226 | ret |= ts2020_writereg(fe, 0x04, gdiv28 & 0xff); | 296 | ret |= ts2020_writereg(fe, 0x04, gdiv28 & 0xff); |
| @@ -228,6 +298,15 @@ static int ts2020_set_params(struct dvb_frontend *fe) | |||
| 228 | if (ret < 0) | 298 | if (ret < 0) |
| 229 | return -ENODEV; | 299 | return -ENODEV; |
| 230 | 300 | ||
| 301 | if (priv->tuner == TS2020_M88TS2022) { | ||
| 302 | ret = ts2020_writereg(fe, 0x25, 0x00); | ||
| 303 | ret |= ts2020_writereg(fe, 0x27, 0x70); | ||
| 304 | ret |= ts2020_writereg(fe, 0x41, 0x09); | ||
| 305 | ret |= ts2020_writereg(fe, 0x08, 0x0b); | ||
| 306 | if (ret < 0) | ||
| 307 | return -ENODEV; | ||
| 308 | } | ||
| 309 | |||
| 231 | value = ts2020_readreg(fe, 0x26); | 310 | value = ts2020_readreg(fe, 0x26); |
| 232 | 311 | ||
| 233 | f3db = (symbol_rate * 135) / 200 + 2000; | 312 | f3db = (symbol_rate * 135) / 200 + 2000; |
| @@ -243,8 +322,6 @@ static int ts2020_set_params(struct dvb_frontend *fe) | |||
| 243 | if (mlpf_max > 63) | 322 | if (mlpf_max > 63) |
| 244 | mlpf_max = 63; | 323 | mlpf_max = 63; |
| 245 | 324 | ||
| 246 | lpf_coeff = 2766; | ||
| 247 | |||
| 248 | nlpf = (f3db * gdiv28 * 2 / lpf_coeff / | 325 | nlpf = (f3db * gdiv28 * 2 / lpf_coeff / |
| 249 | (TS2020_XTAL_FREQ / 1000) + 1) / 2; | 326 | (TS2020_XTAL_FREQ / 1000) + 1) / 2; |
| 250 | if (nlpf > 23) | 327 | if (nlpf > 23) |
| @@ -285,6 +362,13 @@ static int ts2020_get_frequency(struct dvb_frontend *fe, u32 *frequency) | |||
| 285 | { | 362 | { |
| 286 | struct ts2020_priv *priv = fe->tuner_priv; | 363 | struct ts2020_priv *priv = fe->tuner_priv; |
| 287 | *frequency = priv->frequency; | 364 | *frequency = priv->frequency; |
| 365 | |||
| 366 | return 0; | ||
| 367 | } | ||
| 368 | |||
| 369 | static int ts2020_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) | ||
| 370 | { | ||
| 371 | *frequency = 0; /* Zero-IF */ | ||
| 288 | return 0; | 372 | return 0; |
| 289 | } | 373 | } |
| 290 | 374 | ||
| @@ -324,6 +408,7 @@ static struct dvb_tuner_ops ts2020_tuner_ops = { | |||
| 324 | .sleep = ts2020_sleep, | 408 | .sleep = ts2020_sleep, |
| 325 | .set_params = ts2020_set_params, | 409 | .set_params = ts2020_set_params, |
| 326 | .get_frequency = ts2020_get_frequency, | 410 | .get_frequency = ts2020_get_frequency, |
| 411 | .get_if_frequency = ts2020_get_if_frequency, | ||
| 327 | .get_rf_strength = ts2020_read_signal_strength, | 412 | .get_rf_strength = ts2020_read_signal_strength, |
| 328 | }; | 413 | }; |
| 329 | 414 | ||
| @@ -340,6 +425,7 @@ struct dvb_frontend *ts2020_attach(struct dvb_frontend *fe, | |||
| 340 | 425 | ||
| 341 | priv->i2c_address = config->tuner_address; | 426 | priv->i2c_address = config->tuner_address; |
| 342 | priv->i2c = i2c; | 427 | priv->i2c = i2c; |
| 428 | priv->clk_out = config->clk_out; | ||
| 343 | priv->clk_out_div = config->clk_out_div; | 429 | priv->clk_out_div = config->clk_out_div; |
| 344 | priv->frequency_div = config->frequency_div; | 430 | priv->frequency_div = config->frequency_div; |
| 345 | fe->tuner_priv = priv; | 431 | fe->tuner_priv = priv; |
| @@ -358,9 +444,13 @@ struct dvb_frontend *ts2020_attach(struct dvb_frontend *fe, | |||
| 358 | 444 | ||
| 359 | /* Check the tuner version */ | 445 | /* Check the tuner version */ |
| 360 | buf = ts2020_readreg(fe, 0x00); | 446 | buf = ts2020_readreg(fe, 0x00); |
| 361 | if ((buf == 0x01) || (buf == 0x41) || (buf == 0x81)) | 447 | if ((buf == 0x01) || (buf == 0x41) || (buf == 0x81)) { |
| 362 | printk(KERN_INFO "%s: Find tuner TS2020!\n", __func__); | 448 | printk(KERN_INFO "%s: Find tuner TS2020!\n", __func__); |
| 363 | else { | 449 | priv->tuner = TS2020_M88TS2020; |
| 450 | } else if ((buf == 0x83) || (buf == 0xc3)) { | ||
| 451 | printk(KERN_INFO "%s: Find tuner TS2022!\n", __func__); | ||
| 452 | priv->tuner = TS2020_M88TS2022; | ||
| 453 | } else { | ||
| 364 | printk(KERN_ERR "%s: Read tuner reg[0] = %d\n", __func__, buf); | 454 | printk(KERN_ERR "%s: Read tuner reg[0] = %d\n", __func__, buf); |
| 365 | kfree(priv); | 455 | kfree(priv); |
| 366 | return NULL; | 456 | return NULL; |
diff --git a/drivers/media/dvb-frontends/ts2020.h b/drivers/media/dvb-frontends/ts2020.h index b2fe6bb3a38b..8a08dccc217f 100644 --- a/drivers/media/dvb-frontends/ts2020.h +++ b/drivers/media/dvb-frontends/ts2020.h | |||
| @@ -27,8 +27,31 @@ | |||
| 27 | 27 | ||
| 28 | struct ts2020_config { | 28 | struct ts2020_config { |
| 29 | u8 tuner_address; | 29 | u8 tuner_address; |
| 30 | u8 clk_out_div; | ||
| 31 | u32 frequency_div; | 30 | u32 frequency_div; |
| 31 | |||
| 32 | /* | ||
| 33 | * RF loop-through | ||
| 34 | */ | ||
| 35 | u8 loop_through:1; | ||
| 36 | |||
| 37 | /* | ||
| 38 | * clock output | ||
| 39 | */ | ||
| 40 | #define TS2020_CLK_OUT_DISABLED 0 | ||
| 41 | #define TS2020_CLK_OUT_ENABLED 1 | ||
| 42 | #define TS2020_CLK_OUT_ENABLED_XTALOUT 2 | ||
| 43 | u8 clk_out:2; | ||
| 44 | |||
| 45 | /* | ||
| 46 | * clock output divider | ||
| 47 | * 1 - 31 | ||
| 48 | */ | ||
| 49 | u8 clk_out_div:5; | ||
| 50 | |||
| 51 | /* | ||
| 52 | * pointer to DVB frontend | ||
| 53 | */ | ||
| 54 | struct dvb_frontend *fe; | ||
| 32 | }; | 55 | }; |
| 33 | 56 | ||
| 34 | #if IS_ENABLED(CONFIG_DVB_TS2020) | 57 | #if IS_ENABLED(CONFIG_DVB_TS2020) |
