summaryrefslogtreecommitdiffstats
path: root/drivers/media
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2015-05-26 11:04:00 -0400
committerMauro Carvalho Chehab <mchehab@osg.samsung.com>2015-06-10 10:10:27 -0400
commit0f91c9d6bab956fe55c32f143b21cbcc3538f699 (patch)
tree6e5a01afdfbc7f4cf6ba6061510936860b02b0ca /drivers/media
parent76b91be3d360af85f4ad2a27a54268726bee7679 (diff)
[media] TS2020: Calculate tuner gain correctly
The TS2020 and TS2022 tuners take an input from the demodulator indicating the AGC setting on that component that is then used to influence the tuner's own gain. This should be taken into account when calculating the gain and signal strength. Further, the existing TS2020 driver miscalculates the signal strength as the result of its calculations can exceed the storage capacity of the 16-bit word used to return it to userspace. To this end: (1) Add a callback function (->get_agc_pwm()) in the ts2020_config struct that the tuner can call to get the AGC PWM value from the demodulator. (2) Modify the TS2020 driver to calculate the gain according to Montage's specification with the adjustment that we produce a negative value and scale it to 0.001dB units (which is what the DVBv5 API will require): (a) Callback to the demodulator to retrieve the AGC PWM value and then turn that into Vagc for incorporation in the calculations. If the callback is unset, assume a Vagc of 0. (b) Calculate the tuner gain from a combination of Vagc and the tuner's RF gain and baseband gain settings. (3) Turn this into a percentage signal strength as per Montage's specification for return to userspace with the DVBv3 API. (4) Provide a function in the M88DS3103 demodulator driver that can be used to get the AGC PWM value on behalf of the tuner. (5) The ts2020_config.get_agc_pwm function should be set by the code that stitches together the drivers for each card. For the DVBSky cards that use the M88DS3103 with the TS2020 or the TS2022, set the get_agc_pwm function to point to m88ds3103_get_agc_pwm. I have tested this with a DVBSky S952 card which has an M88DS3103 and a TS2022. Thanks to Montage for providing access to information about the workings of these parts. Signed-off-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')
-rw-r--r--drivers/media/dvb-frontends/m88ds3103.c16
-rw-r--r--drivers/media/dvb-frontends/m88ds3103.h2
-rw-r--r--drivers/media/dvb-frontends/ts2020.c138
-rw-r--r--drivers/media/dvb-frontends/ts2020.h5
-rw-r--r--drivers/media/pci/cx23885/cx23885-dvb.c3
-rw-r--r--drivers/media/usb/dvb-usb-v2/dvbsky.c2
6 files changed, 148 insertions, 18 deletions
diff --git a/drivers/media/dvb-frontends/m88ds3103.c b/drivers/media/dvb-frontends/m88ds3103.c
index 356f23343d33..e9b2d2b69b1d 100644
--- a/drivers/media/dvb-frontends/m88ds3103.c
+++ b/drivers/media/dvb-frontends/m88ds3103.c
@@ -52,6 +52,22 @@ err:
52 return ret; 52 return ret;
53} 53}
54 54
55/*
56 * Get the demodulator AGC PWM voltage setting supplied to the tuner.
57 */
58int m88ds3103_get_agc_pwm(struct dvb_frontend *fe, u8 *_agc_pwm)
59{
60 struct m88ds3103_dev *dev = fe->demodulator_priv;
61 unsigned tmp;
62 int ret;
63
64 ret = regmap_read(dev->regmap, 0x3f, &tmp);
65 if (ret == 0)
66 *_agc_pwm = tmp;
67 return ret;
68}
69EXPORT_SYMBOL(m88ds3103_get_agc_pwm);
70
55static int m88ds3103_read_status(struct dvb_frontend *fe, 71static int m88ds3103_read_status(struct dvb_frontend *fe,
56 enum fe_status *status) 72 enum fe_status *status)
57{ 73{
diff --git a/drivers/media/dvb-frontends/m88ds3103.h b/drivers/media/dvb-frontends/m88ds3103.h
index ff0390593049..04b355a005fb 100644
--- a/drivers/media/dvb-frontends/m88ds3103.h
+++ b/drivers/media/dvb-frontends/m88ds3103.h
@@ -176,6 +176,7 @@ extern struct dvb_frontend *m88ds3103_attach(
176 const struct m88ds3103_config *config, 176 const struct m88ds3103_config *config,
177 struct i2c_adapter *i2c, 177 struct i2c_adapter *i2c,
178 struct i2c_adapter **tuner_i2c); 178 struct i2c_adapter **tuner_i2c);
179extern int m88ds3103_get_agc_pwm(struct dvb_frontend *fe, u8 *_agc_pwm);
179#else 180#else
180static inline struct dvb_frontend *m88ds3103_attach( 181static inline struct dvb_frontend *m88ds3103_attach(
181 const struct m88ds3103_config *config, 182 const struct m88ds3103_config *config,
@@ -185,6 +186,7 @@ static inline struct dvb_frontend *m88ds3103_attach(
185 pr_warn("%s: driver disabled by Kconfig\n", __func__); 186 pr_warn("%s: driver disabled by Kconfig\n", __func__);
186 return NULL; 187 return NULL;
187} 188}
189#define m88ds3103_get_agc_pwm NULL
188#endif 190#endif
189 191
190#endif 192#endif
diff --git a/drivers/media/dvb-frontends/ts2020.c b/drivers/media/dvb-frontends/ts2020.c
index f674717fa921..277e1cff627b 100644
--- a/drivers/media/dvb-frontends/ts2020.c
+++ b/drivers/media/dvb-frontends/ts2020.c
@@ -32,6 +32,7 @@ struct ts2020_priv {
32 struct regmap_config regmap_config; 32 struct regmap_config regmap_config;
33 struct regmap *regmap; 33 struct regmap *regmap;
34 struct dvb_frontend *fe; 34 struct dvb_frontend *fe;
35 int (*get_agc_pwm)(struct dvb_frontend *fe, u8 *_agc_pwm);
35 /* i2c details */ 36 /* i2c details */
36 int i2c_address; 37 int i2c_address;
37 struct i2c_adapter *i2c; 38 struct i2c_adapter *i2c;
@@ -313,32 +314,132 @@ static int ts2020_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
313 return 0; 314 return 0;
314} 315}
315 316
316/* read TS2020 signal strength */ 317/*
317static int ts2020_read_signal_strength(struct dvb_frontend *fe, 318 * Get the tuner gain.
318 u16 *signal_strength) 319 * @fe: The front end for which we're determining the gain
320 * @v_agc: The voltage of the AGC from the demodulator (0-2600mV)
321 * @_gain: Where to store the gain (in 0.001dB units)
322 *
323 * Returns 0 or a negative error code.
324 */
325static int ts2020_read_tuner_gain(struct dvb_frontend *fe, unsigned v_agc,
326 __s64 *_gain)
319{ 327{
320 struct ts2020_priv *priv = fe->tuner_priv; 328 struct ts2020_priv *priv = fe->tuner_priv;
321 unsigned int utmp; 329 unsigned long gain1, gain2, gain3;
322 u16 sig_reading, sig_strength; 330 unsigned utmp;
323 u8 rfgain, bbgain; 331 int ret;
332
333 /* Read the RF gain */
334 ret = regmap_read(priv->regmap, 0x3d, &utmp);
335 if (ret < 0)
336 return ret;
337 gain1 = utmp & 0x1f;
338
339 /* Read the baseband gain */
340 ret = regmap_read(priv->regmap, 0x21, &utmp);
341 if (ret < 0)
342 return ret;
343 gain2 = utmp & 0x1f;
344
345 switch (priv->tuner) {
346 case TS2020_M88TS2020:
347 gain1 = clamp_t(long, gain1, 0, 15);
348 gain2 = clamp_t(long, gain2, 0, 13);
349 v_agc = clamp_t(long, v_agc, 400, 1100);
350
351 *_gain = -(gain1 * 2330 +
352 gain2 * 3500 +
353 v_agc * 24 / 10 * 10 +
354 10000);
355 /* gain in range -19600 to -116850 in units of 0.001dB */
356 break;
357
358 case TS2020_M88TS2022:
359 ret = regmap_read(priv->regmap, 0x66, &utmp);
360 if (ret < 0)
361 return ret;
362 gain3 = (utmp >> 3) & 0x07;
363
364 gain1 = clamp_t(long, gain1, 0, 15);
365 gain2 = clamp_t(long, gain2, 2, 16);
366 gain3 = clamp_t(long, gain3, 0, 6);
367 v_agc = clamp_t(long, v_agc, 600, 1600);
368
369 *_gain = -(gain1 * 2650 +
370 gain2 * 3380 +
371 gain3 * 2850 +
372 v_agc * 176 / 100 * 10 -
373 30000);
374 /* gain in range -47320 to -158950 in units of 0.001dB */
375 break;
376 }
377
378 return 0;
379}
380
381/*
382 * Get the AGC information from the demodulator and use that to calculate the
383 * tuner gain.
384 */
385static int ts2020_get_tuner_gain(struct dvb_frontend *fe, __s64 *_gain)
386{
387 struct ts2020_priv *priv = fe->tuner_priv;
388 int v_agc = 0, ret;
389 u8 agc_pwm;
324 390
325 regmap_read(priv->regmap, 0x3d, &utmp); 391 /* Read the AGC PWM rate from the demodulator */
326 rfgain = utmp & 0x1f; 392 if (priv->get_agc_pwm) {
327 regmap_read(priv->regmap, 0x21, &utmp); 393 ret = priv->get_agc_pwm(fe, &agc_pwm);
328 bbgain = utmp & 0x1f; 394 if (ret < 0)
395 return ret;
329 396
330 if (rfgain > 15) 397 switch (priv->tuner) {
331 rfgain = 15; 398 case TS2020_M88TS2020:
332 if (bbgain > 13) 399 v_agc = (int)agc_pwm * 20 - 1166;
333 bbgain = 13; 400 break;
401 case TS2020_M88TS2022:
402 v_agc = (int)agc_pwm * 16 - 670;
403 break;
404 }
334 405
335 sig_reading = rfgain * 2 + bbgain * 3; 406 if (v_agc < 0)
407 v_agc = 0;
408 }
336 409
337 sig_strength = 40 + (64 - sig_reading) * 50 / 64 ; 410 return ts2020_read_tuner_gain(fe, v_agc, _gain);
411}
338 412
339 /* cook the value to be suitable for szap-s2 human readable output */ 413/*
340 *signal_strength = sig_strength * 1000; 414 * Read TS2020 signal strength in v3 format.
415 */
416static int ts2020_read_signal_strength(struct dvb_frontend *fe,
417 u16 *signal_strength)
418{
419 unsigned strength;
420 __s64 gain;
421 int ret;
422
423 /* Determine the total gain of the tuner */
424 ret = ts2020_get_tuner_gain(fe, &gain);
425 if (ret < 0)
426 return ret;
427
428 /* Calculate the signal strength based on the total gain of the tuner */
429 if (gain < -85000)
430 /* 0%: no signal or weak signal */
431 strength = 0;
432 else if (gain < -65000)
433 /* 0% - 60%: weak signal */
434 strength = 0 + (85000 + gain) * 3 / 1000;
435 else if (gain < -45000)
436 /* 60% - 90%: normal signal */
437 strength = 60 + (65000 + gain) * 3 / 2000;
438 else
439 /* 90% - 99%: strong signal */
440 strength = 90 + (45000 + gain) / 5000;
341 441
442 *signal_strength = strength * 65535 / 100;
342 return 0; 443 return 0;
343} 444}
344 445
@@ -442,6 +543,7 @@ static int ts2020_probe(struct i2c_client *client,
442 dev->clk_out_div = pdata->clk_out_div; 543 dev->clk_out_div = pdata->clk_out_div;
443 dev->frequency_div = pdata->frequency_div; 544 dev->frequency_div = pdata->frequency_div;
444 dev->fe = fe; 545 dev->fe = fe;
546 dev->get_agc_pwm = pdata->get_agc_pwm;
445 fe->tuner_priv = dev; 547 fe->tuner_priv = dev;
446 dev->client = client; 548 dev->client = client;
447 549
diff --git a/drivers/media/dvb-frontends/ts2020.h b/drivers/media/dvb-frontends/ts2020.h
index f40bbcf9f6eb..37797244774d 100644
--- a/drivers/media/dvb-frontends/ts2020.h
+++ b/drivers/media/dvb-frontends/ts2020.h
@@ -57,6 +57,11 @@ struct ts2020_config {
57 * driver private, do not set value 57 * driver private, do not set value
58 */ 58 */
59 u8 attach_in_use:1; 59 u8 attach_in_use:1;
60
61 /* Operation to be called by the ts2020 driver to get the value of the
62 * AGC PWM tuner input as theoretically output by the demodulator.
63 */
64 int (*get_agc_pwm)(struct dvb_frontend *fe, u8 *_agc_pwm);
60}; 65};
61 66
62/* Do not add new ts2020_attach() users! Use I2C bindings instead. */ 67/* Do not add new ts2020_attach() users! Use I2C bindings instead. */
diff --git a/drivers/media/pci/cx23885/cx23885-dvb.c b/drivers/media/pci/cx23885/cx23885-dvb.c
index a77c2d3b50fb..6e8c24cdb2cd 100644
--- a/drivers/media/pci/cx23885/cx23885-dvb.c
+++ b/drivers/media/pci/cx23885/cx23885-dvb.c
@@ -1908,6 +1908,7 @@ static int dvb_register(struct cx23885_tsport *port)
1908 /* attach tuner */ 1908 /* attach tuner */
1909 memset(&ts2020_config, 0, sizeof(ts2020_config)); 1909 memset(&ts2020_config, 0, sizeof(ts2020_config));
1910 ts2020_config.fe = fe0->dvb.frontend; 1910 ts2020_config.fe = fe0->dvb.frontend;
1911 ts2020_config.get_agc_pwm = m88ds3103_get_agc_pwm;
1911 memset(&info, 0, sizeof(struct i2c_board_info)); 1912 memset(&info, 0, sizeof(struct i2c_board_info));
1912 strlcpy(info.type, "ts2020", I2C_NAME_SIZE); 1913 strlcpy(info.type, "ts2020", I2C_NAME_SIZE);
1913 info.addr = 0x60; 1914 info.addr = 0x60;
@@ -2039,6 +2040,7 @@ static int dvb_register(struct cx23885_tsport *port)
2039 /* attach tuner */ 2040 /* attach tuner */
2040 memset(&ts2020_config, 0, sizeof(ts2020_config)); 2041 memset(&ts2020_config, 0, sizeof(ts2020_config));
2041 ts2020_config.fe = fe0->dvb.frontend; 2042 ts2020_config.fe = fe0->dvb.frontend;
2043 ts2020_config.get_agc_pwm = m88ds3103_get_agc_pwm;
2042 memset(&info, 0, sizeof(struct i2c_board_info)); 2044 memset(&info, 0, sizeof(struct i2c_board_info));
2043 strlcpy(info.type, "ts2020", I2C_NAME_SIZE); 2045 strlcpy(info.type, "ts2020", I2C_NAME_SIZE);
2044 info.addr = 0x60; 2046 info.addr = 0x60;
@@ -2084,6 +2086,7 @@ static int dvb_register(struct cx23885_tsport *port)
2084 /* attach tuner */ 2086 /* attach tuner */
2085 memset(&ts2020_config, 0, sizeof(ts2020_config)); 2087 memset(&ts2020_config, 0, sizeof(ts2020_config));
2086 ts2020_config.fe = fe0->dvb.frontend; 2088 ts2020_config.fe = fe0->dvb.frontend;
2089 ts2020_config.get_agc_pwm = m88ds3103_get_agc_pwm;
2087 memset(&info, 0, sizeof(struct i2c_board_info)); 2090 memset(&info, 0, sizeof(struct i2c_board_info));
2088 strlcpy(info.type, "ts2020", I2C_NAME_SIZE); 2091 strlcpy(info.type, "ts2020", I2C_NAME_SIZE);
2089 info.addr = 0x60; 2092 info.addr = 0x60;
diff --git a/drivers/media/usb/dvb-usb-v2/dvbsky.c b/drivers/media/usb/dvb-usb-v2/dvbsky.c
index 5cc01bbdede9..0376c092bab8 100644
--- a/drivers/media/usb/dvb-usb-v2/dvbsky.c
+++ b/drivers/media/usb/dvb-usb-v2/dvbsky.c
@@ -332,6 +332,7 @@ static int dvbsky_s960_attach(struct dvb_usb_adapter *adap)
332 332
333 /* attach tuner */ 333 /* attach tuner */
334 ts2020_config.fe = adap->fe[0]; 334 ts2020_config.fe = adap->fe[0];
335 ts2020_config.get_agc_pwm = m88ds3103_get_agc_pwm;
335 strlcpy(info.type, "ts2020", I2C_NAME_SIZE); 336 strlcpy(info.type, "ts2020", I2C_NAME_SIZE);
336 info.addr = 0x60; 337 info.addr = 0x60;
337 info.platform_data = &ts2020_config; 338 info.platform_data = &ts2020_config;
@@ -454,6 +455,7 @@ static int dvbsky_s960c_attach(struct dvb_usb_adapter *adap)
454 455
455 /* attach tuner */ 456 /* attach tuner */
456 ts2020_config.fe = adap->fe[0]; 457 ts2020_config.fe = adap->fe[0];
458 ts2020_config.get_agc_pwm = m88ds3103_get_agc_pwm;
457 strlcpy(info.type, "ts2020", I2C_NAME_SIZE); 459 strlcpy(info.type, "ts2020", I2C_NAME_SIZE);
458 info.addr = 0x60; 460 info.addr = 0x60;
459 info.platform_data = &ts2020_config; 461 info.platform_data = &ts2020_config;