2019-05-29 22:18:00 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
2010-09-28 07:52:43 +08:00
|
|
|
/*
|
|
|
|
* Fujitu mb86a20s ISDB-T/ISDB-Tsb Module driver
|
|
|
|
*
|
2014-02-07 18:03:07 +08:00
|
|
|
* Copyright (C) 2010-2013 Mauro Carvalho Chehab
|
2010-09-28 07:52:43 +08:00
|
|
|
* Copyright (C) 2009-2010 Douglas Landgraf <dougsland@redhat.com>
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <asm/div64.h>
|
|
|
|
|
2017-12-29 02:03:51 +08:00
|
|
|
#include <media/dvb_frontend.h>
|
2010-09-28 07:52:43 +08:00
|
|
|
#include "mb86a20s.h"
|
|
|
|
|
2013-04-08 07:50:34 +08:00
|
|
|
#define NUM_LAYERS 3
|
|
|
|
|
2013-03-04 18:10:06 +08:00
|
|
|
enum mb86a20s_bandwidth {
|
|
|
|
MB86A20S_13SEG = 0,
|
|
|
|
MB86A20S_13SEG_PARTIAL = 1,
|
|
|
|
MB86A20S_1SEG = 2,
|
|
|
|
MB86A20S_3SEG = 3,
|
|
|
|
};
|
|
|
|
|
2014-08-21 05:08:17 +08:00
|
|
|
static u8 mb86a20s_subchannel[] = {
|
2013-03-04 18:10:06 +08:00
|
|
|
0xb0, 0xc0, 0xd0, 0xe0,
|
|
|
|
0xf0, 0x00, 0x10, 0x20,
|
|
|
|
};
|
|
|
|
|
2010-09-28 07:52:43 +08:00
|
|
|
struct mb86a20s_state {
|
|
|
|
struct i2c_adapter *i2c;
|
|
|
|
const struct mb86a20s_config *config;
|
2013-01-22 23:28:31 +08:00
|
|
|
u32 last_frequency;
|
2010-09-28 07:52:43 +08:00
|
|
|
|
|
|
|
struct dvb_frontend frontend;
|
[media] mb86a20s: Be sure that device is initialized before starting DVB
Due to a hard to track bug between tda829x/tda18271/saa7134, tda829x
wants to go to analog mode during DVB initialization, causing some
I2C errors.
The analog failure doesn't cause any harm, as the device were already
properly initialized in analog mode. However, the failure at the digital
mode causes the frontend mb86a20s to not initialize. Fortunately, at
least on my tests, it was possible to detect that the device is a
mb86a20s before the failure.
What happens is that tda8290 is a very bad boy: during DVB setup, it
keeps insisting to call tda18271 analog_set_params, that calls
tune_agc code. The tune_agc code calls saa7134 driver, changing the
value of GPIO 27, switching from digital to analog mode and disabling
the access to mb86a20s, as, on Kworld SBTVD, the same GPIO used
to switch the hardware AGC mode seems to be used to enable the I2C
switch that allows access to the frontend (mb86a20s).
So, a call to analog_set_params ultimately disables the access to
the frontend, and causes a failure at the init frontend logic.
This patch is a workaround for this issue: it simply checks if the
frontend init had any failure. If so, it will init the frontend when
some DTV application will try to set DVB mode.
Even being a hack for Kworld SBTVD to work, and assumning that we could
teach tda8290 to be a good boy, this is actually an improvement at the
frontend driver, as it will be more reliable to initialization failures.
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
2011-01-14 22:10:05 +08:00
|
|
|
|
2013-03-01 03:45:39 +08:00
|
|
|
u32 if_freq;
|
2013-03-04 18:10:06 +08:00
|
|
|
enum mb86a20s_bandwidth bw;
|
|
|
|
bool inversion;
|
|
|
|
u32 subchannel;
|
2013-03-01 03:45:39 +08:00
|
|
|
|
2013-04-08 07:50:34 +08:00
|
|
|
u32 estimated_rate[NUM_LAYERS];
|
2013-03-02 21:15:30 +08:00
|
|
|
unsigned long get_strength_time;
|
2013-01-15 07:34:55 +08:00
|
|
|
|
[media] mb86a20s: Be sure that device is initialized before starting DVB
Due to a hard to track bug between tda829x/tda18271/saa7134, tda829x
wants to go to analog mode during DVB initialization, causing some
I2C errors.
The analog failure doesn't cause any harm, as the device were already
properly initialized in analog mode. However, the failure at the digital
mode causes the frontend mb86a20s to not initialize. Fortunately, at
least on my tests, it was possible to detect that the device is a
mb86a20s before the failure.
What happens is that tda8290 is a very bad boy: during DVB setup, it
keeps insisting to call tda18271 analog_set_params, that calls
tune_agc code. The tune_agc code calls saa7134 driver, changing the
value of GPIO 27, switching from digital to analog mode and disabling
the access to mb86a20s, as, on Kworld SBTVD, the same GPIO used
to switch the hardware AGC mode seems to be used to enable the I2C
switch that allows access to the frontend (mb86a20s).
So, a call to analog_set_params ultimately disables the access to
the frontend, and causes a failure at the init frontend logic.
This patch is a workaround for this issue: it simply checks if the
frontend init had any failure. If so, it will init the frontend when
some DTV application will try to set DVB mode.
Even being a hack for Kworld SBTVD to work, and assumning that we could
teach tda8290 to be a good boy, this is actually an improvement at the
frontend driver, as it will be more reliable to initialization failures.
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
2011-01-14 22:10:05 +08:00
|
|
|
bool need_init;
|
2010-09-28 07:52:43 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
struct regdata {
|
|
|
|
u8 reg;
|
|
|
|
u8 data;
|
|
|
|
};
|
|
|
|
|
2013-01-15 07:34:55 +08:00
|
|
|
#define BER_SAMPLING_RATE 1 /* Seconds */
|
|
|
|
|
2010-09-28 07:52:43 +08:00
|
|
|
/*
|
|
|
|
* Initialization sequence: Use whatevere default values that PV SBTVD
|
|
|
|
* does on its initialisation, obtained via USB snoop
|
|
|
|
*/
|
2013-03-01 03:45:39 +08:00
|
|
|
static struct regdata mb86a20s_init1[] = {
|
2010-09-28 07:52:43 +08:00
|
|
|
{ 0x70, 0x0f },
|
|
|
|
{ 0x70, 0xff },
|
|
|
|
{ 0x08, 0x01 },
|
2013-03-02 02:20:25 +08:00
|
|
|
{ 0x50, 0xd1 }, { 0x51, 0x20 },
|
2013-03-01 03:45:39 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
static struct regdata mb86a20s_init2[] = {
|
2016-09-04 21:43:53 +08:00
|
|
|
{ 0x50, 0xd1 }, { 0x51, 0x22 },
|
|
|
|
{ 0x39, 0x01 },
|
|
|
|
{ 0x71, 0x00 },
|
2010-09-28 07:52:43 +08:00
|
|
|
{ 0x3b, 0x21 },
|
2016-09-04 21:43:53 +08:00
|
|
|
{ 0x3c, 0x3a },
|
2010-09-28 07:52:43 +08:00
|
|
|
{ 0x01, 0x0d },
|
2016-09-04 21:43:53 +08:00
|
|
|
{ 0x04, 0x08 }, { 0x05, 0x05 },
|
2012-01-11 20:56:30 +08:00
|
|
|
{ 0x04, 0x0e }, { 0x05, 0x00 },
|
2016-09-04 21:43:53 +08:00
|
|
|
{ 0x04, 0x0f }, { 0x05, 0x14 },
|
|
|
|
{ 0x04, 0x0b }, { 0x05, 0x8c },
|
2012-01-11 20:56:30 +08:00
|
|
|
{ 0x04, 0x00 }, { 0x05, 0x00 },
|
2016-09-04 21:43:53 +08:00
|
|
|
{ 0x04, 0x01 }, { 0x05, 0x07 },
|
|
|
|
{ 0x04, 0x02 }, { 0x05, 0x0f },
|
|
|
|
{ 0x04, 0x03 }, { 0x05, 0xa0 },
|
2012-01-11 20:56:30 +08:00
|
|
|
{ 0x04, 0x09 }, { 0x05, 0x00 },
|
|
|
|
{ 0x04, 0x0a }, { 0x05, 0xff },
|
2016-09-04 21:43:53 +08:00
|
|
|
{ 0x04, 0x27 }, { 0x05, 0x64 },
|
2012-01-11 20:56:30 +08:00
|
|
|
{ 0x04, 0x28 }, { 0x05, 0x00 },
|
2016-09-04 21:43:53 +08:00
|
|
|
{ 0x04, 0x1e }, { 0x05, 0xff },
|
|
|
|
{ 0x04, 0x29 }, { 0x05, 0x0a },
|
|
|
|
{ 0x04, 0x32 }, { 0x05, 0x0a },
|
2012-01-11 20:56:30 +08:00
|
|
|
{ 0x04, 0x14 }, { 0x05, 0x02 },
|
|
|
|
{ 0x04, 0x04 }, { 0x05, 0x00 },
|
|
|
|
{ 0x04, 0x05 }, { 0x05, 0x22 },
|
|
|
|
{ 0x04, 0x06 }, { 0x05, 0x0e },
|
|
|
|
{ 0x04, 0x07 }, { 0x05, 0xd8 },
|
|
|
|
{ 0x04, 0x12 }, { 0x05, 0x00 },
|
|
|
|
{ 0x04, 0x13 }, { 0x05, 0xff },
|
2013-01-22 23:28:31 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* On this demod, when the bit count reaches the count below,
|
|
|
|
* it collects the bit error count. The bit counters are initialized
|
|
|
|
* to 65535 here. This warrants that all of them will be quickly
|
|
|
|
* calculated when device gets locked. As TMCC is parsed, the values
|
2013-01-15 07:34:55 +08:00
|
|
|
* will be adjusted later in the driver's code.
|
2013-01-22 23:28:31 +08:00
|
|
|
*/
|
|
|
|
{ 0x52, 0x01 }, /* Turn on BER before Viterbi */
|
|
|
|
{ 0x50, 0xa7 }, { 0x51, 0x00 },
|
2012-01-11 20:56:30 +08:00
|
|
|
{ 0x50, 0xa8 }, { 0x51, 0xff },
|
|
|
|
{ 0x50, 0xa9 }, { 0x51, 0xff },
|
2013-01-22 23:28:31 +08:00
|
|
|
{ 0x50, 0xaa }, { 0x51, 0x00 },
|
2012-01-11 20:56:30 +08:00
|
|
|
{ 0x50, 0xab }, { 0x51, 0xff },
|
|
|
|
{ 0x50, 0xac }, { 0x51, 0xff },
|
2013-01-22 23:28:31 +08:00
|
|
|
{ 0x50, 0xad }, { 0x51, 0x00 },
|
2012-01-11 20:56:30 +08:00
|
|
|
{ 0x50, 0xae }, { 0x51, 0xff },
|
|
|
|
{ 0x50, 0xaf }, { 0x51, 0xff },
|
2013-01-22 23:28:31 +08:00
|
|
|
|
2013-01-24 21:25:16 +08:00
|
|
|
/*
|
|
|
|
* On this demod, post BER counts blocks. When the count reaches the
|
|
|
|
* value below, it collects the block error count. The block counters
|
|
|
|
* are initialized to 127 here. This warrants that all of them will be
|
|
|
|
* quickly calculated when device gets locked. As TMCC is parsed, the
|
|
|
|
* values will be adjusted later in the driver's code.
|
|
|
|
*/
|
|
|
|
{ 0x5e, 0x07 }, /* Turn on BER after Viterbi */
|
|
|
|
{ 0x50, 0xdc }, { 0x51, 0x00 },
|
|
|
|
{ 0x50, 0xdd }, { 0x51, 0x7f },
|
|
|
|
{ 0x50, 0xde }, { 0x51, 0x00 },
|
|
|
|
{ 0x50, 0xdf }, { 0x51, 0x7f },
|
|
|
|
{ 0x50, 0xe0 }, { 0x51, 0x00 },
|
|
|
|
{ 0x50, 0xe1 }, { 0x51, 0x7f },
|
2013-01-18 07:10:47 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* On this demod, when the block count reaches the count below,
|
|
|
|
* it collects the block error count. The block counters are initialized
|
|
|
|
* to 127 here. This warrants that all of them will be quickly
|
|
|
|
* calculated when device gets locked. As TMCC is parsed, the values
|
|
|
|
* will be adjusted later in the driver's code.
|
|
|
|
*/
|
|
|
|
{ 0x50, 0xb0 }, { 0x51, 0x07 }, /* Enable PER */
|
|
|
|
{ 0x50, 0xb2 }, { 0x51, 0x00 },
|
|
|
|
{ 0x50, 0xb3 }, { 0x51, 0x7f },
|
|
|
|
{ 0x50, 0xb4 }, { 0x51, 0x00 },
|
|
|
|
{ 0x50, 0xb5 }, { 0x51, 0x7f },
|
|
|
|
{ 0x50, 0xb6 }, { 0x51, 0x00 },
|
|
|
|
{ 0x50, 0xb7 }, { 0x51, 0x7f },
|
2013-01-17 02:12:05 +08:00
|
|
|
|
|
|
|
{ 0x50, 0x50 }, { 0x51, 0x02 }, /* MER manual mode */
|
2013-01-22 23:28:31 +08:00
|
|
|
{ 0x50, 0x51 }, { 0x51, 0x04 }, /* MER symbol 4 */
|
|
|
|
{ 0x45, 0x04 }, /* CN symbol 4 */
|
2013-01-17 02:12:05 +08:00
|
|
|
{ 0x48, 0x04 }, /* CN manual mode */
|
2016-09-04 21:43:53 +08:00
|
|
|
{ 0x50, 0xd5 }, { 0x51, 0x01 },
|
2012-01-11 20:56:30 +08:00
|
|
|
{ 0x50, 0xd6 }, { 0x51, 0x1f },
|
|
|
|
{ 0x50, 0xd2 }, { 0x51, 0x03 },
|
2016-09-04 21:43:53 +08:00
|
|
|
{ 0x50, 0xd7 }, { 0x51, 0x3f },
|
2010-09-28 07:52:43 +08:00
|
|
|
{ 0x1c, 0x01 },
|
2016-09-04 21:43:53 +08:00
|
|
|
{ 0x28, 0x06 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x03 },
|
|
|
|
{ 0x28, 0x07 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x0d },
|
|
|
|
{ 0x28, 0x08 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x02 },
|
|
|
|
{ 0x28, 0x09 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x01 },
|
|
|
|
{ 0x28, 0x0a }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x21 },
|
|
|
|
{ 0x28, 0x0b }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x29 },
|
|
|
|
{ 0x28, 0x0c }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x16 },
|
|
|
|
{ 0x28, 0x0d }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x31 },
|
|
|
|
{ 0x28, 0x0e }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x0e },
|
|
|
|
{ 0x28, 0x0f }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x4e },
|
|
|
|
{ 0x28, 0x10 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x46 },
|
|
|
|
{ 0x28, 0x11 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x0f },
|
|
|
|
{ 0x28, 0x12 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x56 },
|
|
|
|
{ 0x28, 0x13 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x35 },
|
|
|
|
{ 0x28, 0x14 }, { 0x29, 0x00 }, { 0x2a, 0x01 }, { 0x2b, 0xbe },
|
|
|
|
{ 0x28, 0x15 }, { 0x29, 0x00 }, { 0x2a, 0x01 }, { 0x2b, 0x84 },
|
|
|
|
{ 0x28, 0x16 }, { 0x29, 0x00 }, { 0x2a, 0x03 }, { 0x2b, 0xee },
|
|
|
|
{ 0x28, 0x17 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x98 },
|
|
|
|
{ 0x28, 0x18 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x9f },
|
|
|
|
{ 0x28, 0x19 }, { 0x29, 0x00 }, { 0x2a, 0x07 }, { 0x2b, 0xb2 },
|
|
|
|
{ 0x28, 0x1a }, { 0x29, 0x00 }, { 0x2a, 0x06 }, { 0x2b, 0xc2 },
|
|
|
|
{ 0x28, 0x1b }, { 0x29, 0x00 }, { 0x2a, 0x07 }, { 0x2b, 0x4a },
|
|
|
|
{ 0x28, 0x1c }, { 0x29, 0x00 }, { 0x2a, 0x01 }, { 0x2b, 0xbc },
|
|
|
|
{ 0x28, 0x1d }, { 0x29, 0x00 }, { 0x2a, 0x04 }, { 0x2b, 0xba },
|
|
|
|
{ 0x28, 0x1e }, { 0x29, 0x00 }, { 0x2a, 0x06 }, { 0x2b, 0x14 },
|
2012-01-11 20:56:30 +08:00
|
|
|
{ 0x50, 0x1e }, { 0x51, 0x5d },
|
|
|
|
{ 0x50, 0x22 }, { 0x51, 0x00 },
|
|
|
|
{ 0x50, 0x23 }, { 0x51, 0xc8 },
|
|
|
|
{ 0x50, 0x24 }, { 0x51, 0x00 },
|
|
|
|
{ 0x50, 0x25 }, { 0x51, 0xf0 },
|
|
|
|
{ 0x50, 0x26 }, { 0x51, 0x00 },
|
|
|
|
{ 0x50, 0x27 }, { 0x51, 0xc3 },
|
|
|
|
{ 0x50, 0x39 }, { 0x51, 0x02 },
|
2016-09-04 21:43:53 +08:00
|
|
|
{ 0x50, 0xd5 }, { 0x51, 0x01 },
|
2010-09-28 07:52:43 +08:00
|
|
|
{ 0xd0, 0x00 },
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct regdata mb86a20s_reset_reception[] = {
|
|
|
|
{ 0x70, 0xf0 },
|
|
|
|
{ 0x70, 0xff },
|
|
|
|
{ 0x08, 0x01 },
|
|
|
|
{ 0x08, 0x00 },
|
|
|
|
};
|
|
|
|
|
2013-01-24 21:25:16 +08:00
|
|
|
static struct regdata mb86a20s_per_ber_reset[] = {
|
|
|
|
{ 0x53, 0x00 }, /* pre BER Counter reset */
|
2013-01-22 23:28:31 +08:00
|
|
|
{ 0x53, 0x07 },
|
|
|
|
|
2013-01-24 21:25:16 +08:00
|
|
|
{ 0x5f, 0x00 }, /* post BER Counter reset */
|
|
|
|
{ 0x5f, 0x07 },
|
|
|
|
|
2013-01-22 23:28:31 +08:00
|
|
|
{ 0x50, 0xb1 }, /* PER Counter reset */
|
|
|
|
{ 0x51, 0x07 },
|
|
|
|
{ 0x51, 0x00 },
|
|
|
|
};
|
|
|
|
|
2013-01-22 18:53:11 +08:00
|
|
|
/*
|
|
|
|
* I2C read/write functions and macros
|
|
|
|
*/
|
|
|
|
|
2010-09-28 07:52:43 +08:00
|
|
|
static int mb86a20s_i2c_writereg(struct mb86a20s_state *state,
|
2013-01-22 23:28:31 +08:00
|
|
|
u8 i2c_addr, u8 reg, u8 data)
|
2010-09-28 07:52:43 +08:00
|
|
|
{
|
|
|
|
u8 buf[] = { reg, data };
|
|
|
|
struct i2c_msg msg = {
|
|
|
|
.addr = i2c_addr, .flags = 0, .buf = buf, .len = 2
|
|
|
|
};
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
rc = i2c_transfer(state->i2c, &msg, 1);
|
|
|
|
if (rc != 1) {
|
2013-01-22 19:13:08 +08:00
|
|
|
dev_err(&state->i2c->dev,
|
|
|
|
"%s: writereg error (rc == %i, reg == 0x%02x, data == 0x%02x)\n",
|
|
|
|
__func__, rc, reg, data);
|
2010-09-28 07:52:43 +08:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mb86a20s_i2c_writeregdata(struct mb86a20s_state *state,
|
|
|
|
u8 i2c_addr, struct regdata *rd, int size)
|
|
|
|
{
|
|
|
|
int i, rc;
|
|
|
|
|
|
|
|
for (i = 0; i < size; i++) {
|
|
|
|
rc = mb86a20s_i2c_writereg(state, i2c_addr, rd[i].reg,
|
|
|
|
rd[i].data);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mb86a20s_i2c_readreg(struct mb86a20s_state *state,
|
|
|
|
u8 i2c_addr, u8 reg)
|
|
|
|
{
|
|
|
|
u8 val;
|
|
|
|
int rc;
|
|
|
|
struct i2c_msg msg[] = {
|
|
|
|
{ .addr = i2c_addr, .flags = 0, .buf = ®, .len = 1 },
|
|
|
|
{ .addr = i2c_addr, .flags = I2C_M_RD, .buf = &val, .len = 1 }
|
|
|
|
};
|
|
|
|
|
|
|
|
rc = i2c_transfer(state->i2c, msg, 2);
|
|
|
|
|
|
|
|
if (rc != 2) {
|
2013-01-22 19:13:08 +08:00
|
|
|
dev_err(&state->i2c->dev, "%s: reg=0x%x (error=%d)\n",
|
|
|
|
__func__, reg, rc);
|
|
|
|
return (rc < 0) ? rc : -EIO;
|
2010-09-28 07:52:43 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define mb86a20s_readreg(state, reg) \
|
|
|
|
mb86a20s_i2c_readreg(state, state->config->demod_address, reg)
|
|
|
|
#define mb86a20s_writereg(state, reg, val) \
|
|
|
|
mb86a20s_i2c_writereg(state, state->config->demod_address, reg, val)
|
|
|
|
#define mb86a20s_writeregdata(state, regdata) \
|
|
|
|
mb86a20s_i2c_writeregdata(state, state->config->demod_address, \
|
|
|
|
regdata, ARRAY_SIZE(regdata))
|
|
|
|
|
2013-01-22 23:28:31 +08:00
|
|
|
/*
|
|
|
|
* Ancillary internal routines (likely compiled inlined)
|
|
|
|
*
|
|
|
|
* The functions below assume that gateway lock has already obtained
|
|
|
|
*/
|
|
|
|
|
2015-06-08 01:53:52 +08:00
|
|
|
static int mb86a20s_read_status(struct dvb_frontend *fe, enum fe_status *status)
|
2010-09-28 07:52:43 +08:00
|
|
|
{
|
|
|
|
struct mb86a20s_state *state = fe->demodulator_priv;
|
2013-01-22 18:53:11 +08:00
|
|
|
int val;
|
2010-09-28 07:52:43 +08:00
|
|
|
|
2013-01-22 18:53:11 +08:00
|
|
|
*status = 0;
|
2010-09-28 07:52:43 +08:00
|
|
|
|
2016-07-15 23:59:15 +08:00
|
|
|
val = mb86a20s_readreg(state, 0x0a);
|
2013-01-22 18:53:11 +08:00
|
|
|
if (val < 0)
|
|
|
|
return val;
|
2010-10-03 16:27:59 +08:00
|
|
|
|
2016-07-15 23:59:15 +08:00
|
|
|
val &= 0xf;
|
2013-01-22 18:53:11 +08:00
|
|
|
if (val >= 2)
|
|
|
|
*status |= FE_HAS_SIGNAL;
|
2010-09-28 07:52:43 +08:00
|
|
|
|
2013-01-22 18:53:11 +08:00
|
|
|
if (val >= 4)
|
|
|
|
*status |= FE_HAS_CARRIER;
|
2010-10-04 03:48:49 +08:00
|
|
|
|
2013-01-22 18:53:11 +08:00
|
|
|
if (val >= 5)
|
|
|
|
*status |= FE_HAS_VITERBI;
|
2010-10-04 03:48:49 +08:00
|
|
|
|
2013-01-22 18:53:11 +08:00
|
|
|
if (val >= 7)
|
|
|
|
*status |= FE_HAS_SYNC;
|
2010-10-03 16:27:59 +08:00
|
|
|
|
2016-09-04 21:16:18 +08:00
|
|
|
/*
|
|
|
|
* Actually, on state S8, it starts receiving TS, but the TS
|
|
|
|
* output is only on normal state after the transition to S9.
|
|
|
|
*/
|
|
|
|
if (val >= 9)
|
2013-01-22 18:53:11 +08:00
|
|
|
*status |= FE_HAS_LOCK;
|
|
|
|
|
2013-01-22 19:13:08 +08:00
|
|
|
dev_dbg(&state->i2c->dev, "%s: Status = 0x%02x (state = %d)\n",
|
|
|
|
__func__, *status, val);
|
2013-01-22 18:53:11 +08:00
|
|
|
|
2013-03-02 20:06:17 +08:00
|
|
|
return val;
|
2010-09-28 07:52:43 +08:00
|
|
|
}
|
|
|
|
|
2013-01-22 23:28:31 +08:00
|
|
|
static int mb86a20s_read_signal_strength(struct dvb_frontend *fe)
|
2010-09-28 07:52:43 +08:00
|
|
|
{
|
|
|
|
struct mb86a20s_state *state = fe->demodulator_priv;
|
2013-03-02 21:15:30 +08:00
|
|
|
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
|
2013-01-22 23:28:31 +08:00
|
|
|
int rc;
|
2010-09-28 07:52:43 +08:00
|
|
|
unsigned rf_max, rf_min, rf;
|
2010-10-03 16:27:59 +08:00
|
|
|
|
2013-03-02 21:15:30 +08:00
|
|
|
if (state->get_strength_time &&
|
|
|
|
(!time_after(jiffies, state->get_strength_time)))
|
|
|
|
return c->strength.stat[0].uvalue;
|
|
|
|
|
|
|
|
/* Reset its value if an error happen */
|
|
|
|
c->strength.stat[0].uvalue = 0;
|
|
|
|
|
2010-09-28 07:52:43 +08:00
|
|
|
/* Does a binary search to get RF strength */
|
|
|
|
rf_max = 0xfff;
|
|
|
|
rf_min = 0;
|
|
|
|
do {
|
|
|
|
rf = (rf_max + rf_min) / 2;
|
2013-01-22 23:28:31 +08:00
|
|
|
rc = mb86a20s_writereg(state, 0x04, 0x1f);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
rc = mb86a20s_writereg(state, 0x05, rf >> 8);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
rc = mb86a20s_writereg(state, 0x04, 0x20);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
2013-03-02 03:15:16 +08:00
|
|
|
rc = mb86a20s_writereg(state, 0x05, rf);
|
2013-01-22 23:28:31 +08:00
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
2010-09-28 07:52:43 +08:00
|
|
|
|
2013-01-22 23:28:31 +08:00
|
|
|
rc = mb86a20s_readreg(state, 0x02);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
if (rc & 0x08)
|
2010-09-28 07:52:43 +08:00
|
|
|
rf_min = (rf_max + rf_min) / 2;
|
|
|
|
else
|
|
|
|
rf_max = (rf_max + rf_min) / 2;
|
|
|
|
if (rf_max - rf_min < 4) {
|
2013-01-22 23:28:31 +08:00
|
|
|
rf = (rf_max + rf_min) / 2;
|
|
|
|
|
|
|
|
/* Rescale it from 2^12 (4096) to 2^16 */
|
2013-03-02 21:15:30 +08:00
|
|
|
rf = rf << (16 - 12);
|
|
|
|
if (rf)
|
|
|
|
rf |= (1 << 12) - 1;
|
|
|
|
|
2013-01-22 19:13:08 +08:00
|
|
|
dev_dbg(&state->i2c->dev,
|
|
|
|
"%s: signal strength = %d (%d < RF=%d < %d)\n",
|
|
|
|
__func__, rf, rf_min, rf >> 4, rf_max);
|
2013-03-02 21:15:30 +08:00
|
|
|
c->strength.stat[0].uvalue = rf;
|
|
|
|
state->get_strength_time = jiffies +
|
|
|
|
msecs_to_jiffies(1000);
|
|
|
|
return 0;
|
2010-09-28 07:52:43 +08:00
|
|
|
}
|
|
|
|
} while (1);
|
|
|
|
}
|
|
|
|
|
2012-01-10 04:16:36 +08:00
|
|
|
static int mb86a20s_get_modulation(struct mb86a20s_state *state,
|
|
|
|
unsigned layer)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
static unsigned char reg[] = {
|
|
|
|
[0] = 0x86, /* Layer A */
|
|
|
|
[1] = 0x8a, /* Layer B */
|
|
|
|
[2] = 0x8e, /* Layer C */
|
|
|
|
};
|
|
|
|
|
2012-01-13 13:28:34 +08:00
|
|
|
if (layer >= ARRAY_SIZE(reg))
|
2012-01-10 04:16:36 +08:00
|
|
|
return -EINVAL;
|
|
|
|
rc = mb86a20s_writereg(state, 0x6d, reg[layer]);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
rc = mb86a20s_readreg(state, 0x6e);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
2013-01-14 23:31:13 +08:00
|
|
|
switch ((rc >> 4) & 0x07) {
|
2012-01-10 04:16:36 +08:00
|
|
|
case 0:
|
|
|
|
return DQPSK;
|
|
|
|
case 1:
|
|
|
|
return QPSK;
|
|
|
|
case 2:
|
|
|
|
return QAM_16;
|
|
|
|
case 3:
|
|
|
|
return QAM_64;
|
|
|
|
default:
|
|
|
|
return QAM_AUTO;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mb86a20s_get_fec(struct mb86a20s_state *state,
|
|
|
|
unsigned layer)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
static unsigned char reg[] = {
|
|
|
|
[0] = 0x87, /* Layer A */
|
|
|
|
[1] = 0x8b, /* Layer B */
|
|
|
|
[2] = 0x8f, /* Layer C */
|
|
|
|
};
|
|
|
|
|
2012-01-13 13:28:34 +08:00
|
|
|
if (layer >= ARRAY_SIZE(reg))
|
2012-01-10 04:16:36 +08:00
|
|
|
return -EINVAL;
|
|
|
|
rc = mb86a20s_writereg(state, 0x6d, reg[layer]);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
rc = mb86a20s_readreg(state, 0x6e);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
2013-01-14 23:31:13 +08:00
|
|
|
switch ((rc >> 4) & 0x07) {
|
2012-01-10 04:16:36 +08:00
|
|
|
case 0:
|
|
|
|
return FEC_1_2;
|
|
|
|
case 1:
|
|
|
|
return FEC_2_3;
|
|
|
|
case 2:
|
|
|
|
return FEC_3_4;
|
|
|
|
case 3:
|
|
|
|
return FEC_5_6;
|
|
|
|
case 4:
|
|
|
|
return FEC_7_8;
|
|
|
|
default:
|
|
|
|
return FEC_AUTO;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mb86a20s_get_interleaving(struct mb86a20s_state *state,
|
|
|
|
unsigned layer)
|
|
|
|
{
|
|
|
|
int rc;
|
2014-07-12 08:37:47 +08:00
|
|
|
int interleaving[] = {
|
|
|
|
0, 1, 2, 4, 8
|
|
|
|
};
|
2012-01-10 04:16:36 +08:00
|
|
|
|
|
|
|
static unsigned char reg[] = {
|
|
|
|
[0] = 0x88, /* Layer A */
|
|
|
|
[1] = 0x8c, /* Layer B */
|
|
|
|
[2] = 0x90, /* Layer C */
|
|
|
|
};
|
|
|
|
|
2012-01-13 13:28:34 +08:00
|
|
|
if (layer >= ARRAY_SIZE(reg))
|
2012-01-10 04:16:36 +08:00
|
|
|
return -EINVAL;
|
|
|
|
rc = mb86a20s_writereg(state, 0x6d, reg[layer]);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
rc = mb86a20s_readreg(state, 0x6e);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
2013-01-14 23:31:13 +08:00
|
|
|
|
2014-07-12 08:37:47 +08:00
|
|
|
return interleaving[(rc >> 4) & 0x07];
|
2012-01-10 04:16:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int mb86a20s_get_segment_count(struct mb86a20s_state *state,
|
|
|
|
unsigned layer)
|
|
|
|
{
|
|
|
|
int rc, count;
|
|
|
|
static unsigned char reg[] = {
|
|
|
|
[0] = 0x89, /* Layer A */
|
|
|
|
[1] = 0x8d, /* Layer B */
|
|
|
|
[2] = 0x91, /* Layer C */
|
|
|
|
};
|
|
|
|
|
2013-01-22 19:13:08 +08:00
|
|
|
dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
|
|
|
|
|
2012-01-13 13:28:34 +08:00
|
|
|
if (layer >= ARRAY_SIZE(reg))
|
2012-01-10 04:16:36 +08:00
|
|
|
return -EINVAL;
|
2013-01-22 19:13:08 +08:00
|
|
|
|
2012-01-10 04:16:36 +08:00
|
|
|
rc = mb86a20s_writereg(state, 0x6d, reg[layer]);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
rc = mb86a20s_readreg(state, 0x6e);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
count = (rc >> 4) & 0x0f;
|
|
|
|
|
2013-01-22 19:13:08 +08:00
|
|
|
dev_dbg(&state->i2c->dev, "%s: segments: %d.\n", __func__, count);
|
|
|
|
|
2012-01-10 04:16:36 +08:00
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
2013-01-14 20:26:09 +08:00
|
|
|
static void mb86a20s_reset_frontend_cache(struct dvb_frontend *fe)
|
|
|
|
{
|
2013-01-22 19:13:08 +08:00
|
|
|
struct mb86a20s_state *state = fe->demodulator_priv;
|
2013-01-14 20:26:09 +08:00
|
|
|
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
|
|
|
|
|
2013-01-22 19:13:08 +08:00
|
|
|
dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
|
|
|
|
|
2013-01-14 20:26:09 +08:00
|
|
|
/* Fixed parameters */
|
|
|
|
c->delivery_system = SYS_ISDBT;
|
|
|
|
c->bandwidth_hz = 6000000;
|
|
|
|
|
|
|
|
/* Initialize values that will be later autodetected */
|
|
|
|
c->isdbt_layer_enabled = 0;
|
|
|
|
c->transmission_mode = TRANSMISSION_MODE_AUTO;
|
|
|
|
c->guard_interval = GUARD_INTERVAL_AUTO;
|
|
|
|
c->isdbt_sb_mode = 0;
|
|
|
|
c->isdbt_sb_segment_count = 0;
|
|
|
|
}
|
|
|
|
|
2013-01-15 07:34:55 +08:00
|
|
|
/*
|
|
|
|
* Estimates the bit rate using the per-segment bit rate given by
|
|
|
|
* ABNT/NBR 15601 spec (table 4).
|
|
|
|
*/
|
|
|
|
static u32 isdbt_rate[3][5][4] = {
|
|
|
|
{ /* DQPSK/QPSK */
|
|
|
|
{ 280850, 312060, 330420, 340430 }, /* 1/2 */
|
|
|
|
{ 374470, 416080, 440560, 453910 }, /* 2/3 */
|
|
|
|
{ 421280, 468090, 495630, 510650 }, /* 3/4 */
|
|
|
|
{ 468090, 520100, 550700, 567390 }, /* 5/6 */
|
|
|
|
{ 491500, 546110, 578230, 595760 }, /* 7/8 */
|
|
|
|
}, { /* QAM16 */
|
|
|
|
{ 561710, 624130, 660840, 680870 }, /* 1/2 */
|
|
|
|
{ 748950, 832170, 881120, 907820 }, /* 2/3 */
|
|
|
|
{ 842570, 936190, 991260, 1021300 }, /* 3/4 */
|
|
|
|
{ 936190, 1040210, 1101400, 1134780 }, /* 5/6 */
|
|
|
|
{ 983000, 1092220, 1156470, 1191520 }, /* 7/8 */
|
|
|
|
}, { /* QAM64 */
|
|
|
|
{ 842570, 936190, 991260, 1021300 }, /* 1/2 */
|
|
|
|
{ 1123430, 1248260, 1321680, 1361740 }, /* 2/3 */
|
|
|
|
{ 1263860, 1404290, 1486900, 1531950 }, /* 3/4 */
|
|
|
|
{ 1404290, 1560320, 1652110, 1702170 }, /* 5/6 */
|
|
|
|
{ 1474500, 1638340, 1734710, 1787280 }, /* 7/8 */
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
static void mb86a20s_layer_bitrate(struct dvb_frontend *fe, u32 layer,
|
2013-04-08 08:07:47 +08:00
|
|
|
u32 modulation, u32 forward_error_correction,
|
2014-07-12 08:37:48 +08:00
|
|
|
u32 guard_interval,
|
2013-01-15 07:34:55 +08:00
|
|
|
u32 segment)
|
|
|
|
{
|
|
|
|
struct mb86a20s_state *state = fe->demodulator_priv;
|
|
|
|
u32 rate;
|
2013-04-08 08:07:47 +08:00
|
|
|
int mod, fec, guard;
|
2013-01-15 07:34:55 +08:00
|
|
|
|
|
|
|
/*
|
2014-07-12 08:37:48 +08:00
|
|
|
* If modulation/fec/guard is not detected, the default is
|
2013-01-15 07:34:55 +08:00
|
|
|
* to consider the lowest bit rate, to avoid taking too long time
|
|
|
|
* to get BER.
|
|
|
|
*/
|
|
|
|
switch (modulation) {
|
|
|
|
case DQPSK:
|
|
|
|
case QPSK:
|
|
|
|
default:
|
2013-04-08 08:07:47 +08:00
|
|
|
mod = 0;
|
2013-01-15 07:34:55 +08:00
|
|
|
break;
|
|
|
|
case QAM_16:
|
2013-04-08 08:07:47 +08:00
|
|
|
mod = 1;
|
2013-01-15 07:34:55 +08:00
|
|
|
break;
|
|
|
|
case QAM_64:
|
2013-04-08 08:07:47 +08:00
|
|
|
mod = 2;
|
2013-01-15 07:34:55 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2013-04-08 08:07:47 +08:00
|
|
|
switch (forward_error_correction) {
|
2013-01-15 07:34:55 +08:00
|
|
|
default:
|
|
|
|
case FEC_1_2:
|
|
|
|
case FEC_AUTO:
|
2013-04-08 08:07:47 +08:00
|
|
|
fec = 0;
|
2013-01-15 07:34:55 +08:00
|
|
|
break;
|
|
|
|
case FEC_2_3:
|
2013-04-08 08:07:47 +08:00
|
|
|
fec = 1;
|
2013-01-15 07:34:55 +08:00
|
|
|
break;
|
|
|
|
case FEC_3_4:
|
2013-04-08 08:07:47 +08:00
|
|
|
fec = 2;
|
2013-01-15 07:34:55 +08:00
|
|
|
break;
|
|
|
|
case FEC_5_6:
|
2013-04-08 08:07:47 +08:00
|
|
|
fec = 3;
|
2013-01-15 07:34:55 +08:00
|
|
|
break;
|
|
|
|
case FEC_7_8:
|
2013-04-08 08:07:47 +08:00
|
|
|
fec = 4;
|
2013-01-15 07:34:55 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2014-07-12 08:37:48 +08:00
|
|
|
switch (guard_interval) {
|
2013-01-15 07:34:55 +08:00
|
|
|
default:
|
|
|
|
case GUARD_INTERVAL_1_4:
|
2013-04-08 08:07:47 +08:00
|
|
|
guard = 0;
|
2013-01-15 07:34:55 +08:00
|
|
|
break;
|
|
|
|
case GUARD_INTERVAL_1_8:
|
2013-04-08 08:07:47 +08:00
|
|
|
guard = 1;
|
2013-01-15 07:34:55 +08:00
|
|
|
break;
|
|
|
|
case GUARD_INTERVAL_1_16:
|
2013-04-08 08:07:47 +08:00
|
|
|
guard = 2;
|
2013-01-15 07:34:55 +08:00
|
|
|
break;
|
|
|
|
case GUARD_INTERVAL_1_32:
|
2013-04-08 08:07:47 +08:00
|
|
|
guard = 3;
|
2013-01-15 07:34:55 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Samples BER at BER_SAMPLING_RATE seconds */
|
2013-04-08 08:07:47 +08:00
|
|
|
rate = isdbt_rate[mod][fec][guard] * segment * BER_SAMPLING_RATE;
|
2013-01-15 07:34:55 +08:00
|
|
|
|
|
|
|
/* Avoids sampling too quickly or to overflow the register */
|
|
|
|
if (rate < 256)
|
|
|
|
rate = 256;
|
|
|
|
else if (rate > (1 << 24) - 1)
|
|
|
|
rate = (1 << 24) - 1;
|
|
|
|
|
|
|
|
dev_dbg(&state->i2c->dev,
|
|
|
|
"%s: layer %c bitrate: %d kbps; counter = %d (0x%06x)\n",
|
2013-04-08 08:07:47 +08:00
|
|
|
__func__, 'A' + layer,
|
|
|
|
segment * isdbt_rate[mod][fec][guard]/1000,
|
2013-01-15 07:34:55 +08:00
|
|
|
rate, rate);
|
|
|
|
|
2013-04-08 08:06:12 +08:00
|
|
|
state->estimated_rate[layer] = rate;
|
2013-01-15 07:34:55 +08:00
|
|
|
}
|
|
|
|
|
[media] dvb: don't require a parameter for get_frontend
Just like set_frontend, use the dvb cache properties for get_frontend.
This is more consistent, as both functions are now symetric. Also,
at the places get_frontend is called, it makes sense to update the
cache.
Most of this patch were generated by this small perl script:
while (<>) { $file .= $_; }
if ($file =~ m/\.get_frontend\s*=\s*([\d\w_]+)/) {
my $get = $1;
$file =~ s/($get)(\s*\([^\,\)]+)\,\s*struct\s+dtv_frontend_properties\s*\*\s*([_\d\w]+)\)\s*\{/\1\2)\n{\n\tstruct dtv_frontend_properties *\3 = &fe->dtv_property_cache;/g;
}
print $file;
Of course, the changes at dvb_frontend.[ch] were made by hand,
as well as the changes on a few other places, where get_frontend()
is called internally inside the driver.
On some places, get_frontend() were just a void function. Those
occurrences were removed, as the DVB core handles such cases.
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
2011-12-30 22:30:21 +08:00
|
|
|
static int mb86a20s_get_frontend(struct dvb_frontend *fe)
|
2010-09-28 07:52:43 +08:00
|
|
|
{
|
2012-01-10 04:16:36 +08:00
|
|
|
struct mb86a20s_state *state = fe->demodulator_priv;
|
2013-01-14 20:26:09 +08:00
|
|
|
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
|
2013-04-08 07:52:16 +08:00
|
|
|
int layer, rc;
|
2010-09-28 07:52:43 +08:00
|
|
|
|
2013-01-22 19:13:08 +08:00
|
|
|
dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
|
|
|
|
|
2013-01-14 20:26:09 +08:00
|
|
|
/* Reset frontend cache to default values */
|
|
|
|
mb86a20s_reset_frontend_cache(fe);
|
2012-01-10 04:16:36 +08:00
|
|
|
|
|
|
|
/* Check for partial reception */
|
|
|
|
rc = mb86a20s_writereg(state, 0x6d, 0x85);
|
2013-01-14 20:26:09 +08:00
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
rc = mb86a20s_readreg(state, 0x6e);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
c->isdbt_partial_reception = (rc & 0x10) ? 1 : 0;
|
2012-01-10 04:16:36 +08:00
|
|
|
|
|
|
|
/* Get per-layer data */
|
2013-01-14 20:26:09 +08:00
|
|
|
|
2013-04-08 07:52:16 +08:00
|
|
|
for (layer = 0; layer < NUM_LAYERS; layer++) {
|
2013-01-22 19:13:08 +08:00
|
|
|
dev_dbg(&state->i2c->dev, "%s: getting data for layer %c.\n",
|
2013-04-08 07:52:16 +08:00
|
|
|
__func__, 'A' + layer);
|
2013-01-22 19:13:08 +08:00
|
|
|
|
2013-04-08 07:52:16 +08:00
|
|
|
rc = mb86a20s_get_segment_count(state, layer);
|
2013-01-14 20:26:09 +08:00
|
|
|
if (rc < 0)
|
2013-01-22 19:13:08 +08:00
|
|
|
goto noperlayer_error;
|
2013-01-15 07:34:55 +08:00
|
|
|
if (rc >= 0 && rc < 14) {
|
2013-04-08 07:52:16 +08:00
|
|
|
c->layer[layer].segment_count = rc;
|
2013-01-15 07:34:55 +08:00
|
|
|
} else {
|
2013-04-08 07:52:16 +08:00
|
|
|
c->layer[layer].segment_count = 0;
|
|
|
|
state->estimated_rate[layer] = 0;
|
2012-01-10 04:16:36 +08:00
|
|
|
continue;
|
2013-01-14 20:26:09 +08:00
|
|
|
}
|
2013-04-08 07:52:16 +08:00
|
|
|
c->isdbt_layer_enabled |= 1 << layer;
|
|
|
|
rc = mb86a20s_get_modulation(state, layer);
|
2013-01-14 20:26:09 +08:00
|
|
|
if (rc < 0)
|
2013-01-22 19:13:08 +08:00
|
|
|
goto noperlayer_error;
|
|
|
|
dev_dbg(&state->i2c->dev, "%s: modulation %d.\n",
|
|
|
|
__func__, rc);
|
2013-04-08 07:52:16 +08:00
|
|
|
c->layer[layer].modulation = rc;
|
|
|
|
rc = mb86a20s_get_fec(state, layer);
|
2013-01-14 20:26:09 +08:00
|
|
|
if (rc < 0)
|
2013-01-22 19:13:08 +08:00
|
|
|
goto noperlayer_error;
|
|
|
|
dev_dbg(&state->i2c->dev, "%s: FEC %d.\n",
|
|
|
|
__func__, rc);
|
2013-04-08 07:52:16 +08:00
|
|
|
c->layer[layer].fec = rc;
|
|
|
|
rc = mb86a20s_get_interleaving(state, layer);
|
2013-01-14 20:26:09 +08:00
|
|
|
if (rc < 0)
|
2013-01-22 19:13:08 +08:00
|
|
|
goto noperlayer_error;
|
|
|
|
dev_dbg(&state->i2c->dev, "%s: interleaving %d.\n",
|
|
|
|
__func__, rc);
|
2013-04-08 07:52:16 +08:00
|
|
|
c->layer[layer].interleaving = rc;
|
|
|
|
mb86a20s_layer_bitrate(fe, layer, c->layer[layer].modulation,
|
|
|
|
c->layer[layer].fec,
|
2014-07-12 08:37:48 +08:00
|
|
|
c->guard_interval,
|
2013-04-08 07:52:16 +08:00
|
|
|
c->layer[layer].segment_count);
|
2012-01-10 04:16:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
rc = mb86a20s_writereg(state, 0x6d, 0x84);
|
2013-01-14 20:26:09 +08:00
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
if ((rc & 0x60) == 0x20) {
|
|
|
|
c->isdbt_sb_mode = 1;
|
2012-01-10 04:16:36 +08:00
|
|
|
/* At least, one segment should exist */
|
2013-01-14 20:26:09 +08:00
|
|
|
if (!c->isdbt_sb_segment_count)
|
|
|
|
c->isdbt_sb_segment_count = 1;
|
|
|
|
}
|
2012-01-10 04:16:36 +08:00
|
|
|
|
|
|
|
/* Get transmission mode and guard interval */
|
|
|
|
rc = mb86a20s_readreg(state, 0x07);
|
2013-01-14 20:26:09 +08:00
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
2014-07-22 01:55:05 +08:00
|
|
|
c->transmission_mode = TRANSMISSION_MODE_AUTO;
|
2013-01-14 20:26:09 +08:00
|
|
|
if ((rc & 0x60) == 0x20) {
|
2014-07-22 01:55:05 +08:00
|
|
|
/* Only modes 2 and 3 are supported */
|
|
|
|
switch ((rc >> 2) & 0x03) {
|
2013-01-14 20:26:09 +08:00
|
|
|
case 1:
|
|
|
|
c->transmission_mode = TRANSMISSION_MODE_4K;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
c->transmission_mode = TRANSMISSION_MODE_8K;
|
|
|
|
break;
|
2012-01-10 04:16:36 +08:00
|
|
|
}
|
2013-01-14 20:26:09 +08:00
|
|
|
}
|
2014-07-22 01:55:05 +08:00
|
|
|
c->guard_interval = GUARD_INTERVAL_AUTO;
|
2013-01-14 20:26:09 +08:00
|
|
|
if (!(rc & 0x10)) {
|
2014-07-22 01:55:05 +08:00
|
|
|
/* Guard interval 1/32 is not supported */
|
2013-01-14 20:26:09 +08:00
|
|
|
switch (rc & 0x3) {
|
|
|
|
case 0:
|
|
|
|
c->guard_interval = GUARD_INTERVAL_1_4;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
c->guard_interval = GUARD_INTERVAL_1_8;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
c->guard_interval = GUARD_INTERVAL_1_16;
|
|
|
|
break;
|
2012-01-10 04:16:36 +08:00
|
|
|
}
|
|
|
|
}
|
2013-01-22 23:28:31 +08:00
|
|
|
return 0;
|
2012-01-10 04:16:36 +08:00
|
|
|
|
2013-01-22 19:13:08 +08:00
|
|
|
noperlayer_error:
|
2010-09-28 07:52:43 +08:00
|
|
|
|
2013-01-22 23:28:31 +08:00
|
|
|
/* per-layer info is incomplete; discard all per-layer */
|
|
|
|
c->isdbt_layer_enabled = 0;
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mb86a20s_reset_counters(struct dvb_frontend *fe)
|
|
|
|
{
|
|
|
|
struct mb86a20s_state *state = fe->demodulator_priv;
|
|
|
|
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
|
|
|
|
int rc, val;
|
|
|
|
|
|
|
|
dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
|
|
|
|
|
|
|
|
/* Reset the counters, if the channel changed */
|
|
|
|
if (state->last_frequency != c->frequency) {
|
|
|
|
memset(&c->cnr, 0, sizeof(c->cnr));
|
|
|
|
memset(&c->pre_bit_error, 0, sizeof(c->pre_bit_error));
|
|
|
|
memset(&c->pre_bit_count, 0, sizeof(c->pre_bit_count));
|
2013-01-24 21:25:16 +08:00
|
|
|
memset(&c->post_bit_error, 0, sizeof(c->post_bit_error));
|
|
|
|
memset(&c->post_bit_count, 0, sizeof(c->post_bit_count));
|
2013-01-22 23:28:31 +08:00
|
|
|
memset(&c->block_error, 0, sizeof(c->block_error));
|
|
|
|
memset(&c->block_count, 0, sizeof(c->block_count));
|
|
|
|
|
|
|
|
state->last_frequency = c->frequency;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Clear status for most stats */
|
|
|
|
|
2013-01-24 21:25:16 +08:00
|
|
|
/* BER/PER counter reset */
|
|
|
|
rc = mb86a20s_writeregdata(state, mb86a20s_per_ber_reset);
|
2013-01-22 23:28:31 +08:00
|
|
|
if (rc < 0)
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
/* CNR counter reset */
|
|
|
|
rc = mb86a20s_readreg(state, 0x45);
|
|
|
|
if (rc < 0)
|
|
|
|
goto err;
|
|
|
|
val = rc;
|
|
|
|
rc = mb86a20s_writereg(state, 0x45, val | 0x10);
|
|
|
|
if (rc < 0)
|
|
|
|
goto err;
|
|
|
|
rc = mb86a20s_writereg(state, 0x45, val & 0x6f);
|
|
|
|
if (rc < 0)
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
/* MER counter reset */
|
|
|
|
rc = mb86a20s_writereg(state, 0x50, 0x50);
|
|
|
|
if (rc < 0)
|
|
|
|
goto err;
|
|
|
|
rc = mb86a20s_readreg(state, 0x51);
|
|
|
|
if (rc < 0)
|
|
|
|
goto err;
|
|
|
|
val = rc;
|
|
|
|
rc = mb86a20s_writereg(state, 0x51, val | 0x01);
|
|
|
|
if (rc < 0)
|
|
|
|
goto err;
|
|
|
|
rc = mb86a20s_writereg(state, 0x51, val & 0x06);
|
|
|
|
if (rc < 0)
|
|
|
|
goto err;
|
|
|
|
|
2013-01-22 23:30:07 +08:00
|
|
|
goto ok;
|
2013-01-22 23:28:31 +08:00
|
|
|
err:
|
2013-01-22 23:30:07 +08:00
|
|
|
dev_err(&state->i2c->dev,
|
|
|
|
"%s: Can't reset FE statistics (error %d).\n",
|
|
|
|
__func__, rc);
|
|
|
|
ok:
|
2013-01-14 20:26:09 +08:00
|
|
|
return rc;
|
2013-01-22 23:28:31 +08:00
|
|
|
}
|
|
|
|
|
2013-01-24 22:48:44 +08:00
|
|
|
static int mb86a20s_get_pre_ber(struct dvb_frontend *fe,
|
|
|
|
unsigned layer,
|
|
|
|
u32 *error, u32 *count)
|
2013-01-22 23:30:07 +08:00
|
|
|
{
|
|
|
|
struct mb86a20s_state *state = fe->demodulator_priv;
|
2013-01-24 22:48:44 +08:00
|
|
|
int rc, val;
|
2013-01-22 23:30:07 +08:00
|
|
|
|
|
|
|
dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
|
|
|
|
|
2013-04-08 07:50:34 +08:00
|
|
|
if (layer >= NUM_LAYERS)
|
2013-01-22 23:30:07 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/* Check if the BER measures are already available */
|
|
|
|
rc = mb86a20s_readreg(state, 0x54);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
/* Check if data is available for that layer */
|
|
|
|
if (!(rc & (1 << layer))) {
|
|
|
|
dev_dbg(&state->i2c->dev,
|
2013-01-24 22:48:44 +08:00
|
|
|
"%s: preBER for layer %c is not available yet.\n",
|
2013-01-22 23:30:07 +08:00
|
|
|
__func__, 'A' + layer);
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Read Bit Error Count */
|
|
|
|
rc = mb86a20s_readreg(state, 0x55 + layer * 3);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
*error = rc << 16;
|
|
|
|
rc = mb86a20s_readreg(state, 0x56 + layer * 3);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
*error |= rc << 8;
|
|
|
|
rc = mb86a20s_readreg(state, 0x57 + layer * 3);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
*error |= rc;
|
|
|
|
|
|
|
|
dev_dbg(&state->i2c->dev,
|
|
|
|
"%s: bit error before Viterbi for layer %c: %d.\n",
|
|
|
|
__func__, 'A' + layer, *error);
|
|
|
|
|
|
|
|
/* Read Bit Count */
|
|
|
|
rc = mb86a20s_writereg(state, 0x50, 0xa7 + layer * 3);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
rc = mb86a20s_readreg(state, 0x51);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
*count = rc << 16;
|
|
|
|
rc = mb86a20s_writereg(state, 0x50, 0xa8 + layer * 3);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
rc = mb86a20s_readreg(state, 0x51);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
*count |= rc << 8;
|
|
|
|
rc = mb86a20s_writereg(state, 0x50, 0xa9 + layer * 3);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
rc = mb86a20s_readreg(state, 0x51);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
*count |= rc;
|
|
|
|
|
|
|
|
dev_dbg(&state->i2c->dev,
|
|
|
|
"%s: bit count before Viterbi for layer %c: %d.\n",
|
|
|
|
__func__, 'A' + layer, *count);
|
|
|
|
|
|
|
|
|
2013-01-15 07:34:55 +08:00
|
|
|
/*
|
|
|
|
* As we get TMCC data from the frontend, we can better estimate the
|
|
|
|
* BER bit counters, in order to do the BER measure during a longer
|
|
|
|
* time. Use those data, if available, to update the bit count
|
|
|
|
* measure.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (state->estimated_rate[layer]
|
|
|
|
&& state->estimated_rate[layer] != *count) {
|
|
|
|
dev_dbg(&state->i2c->dev,
|
2013-01-24 22:48:44 +08:00
|
|
|
"%s: updating layer %c preBER counter to %d.\n",
|
2013-01-15 07:34:55 +08:00
|
|
|
__func__, 'A' + layer, state->estimated_rate[layer]);
|
2013-01-24 22:48:44 +08:00
|
|
|
|
|
|
|
/* Turn off BER before Viterbi */
|
|
|
|
rc = mb86a20s_writereg(state, 0x52, 0x00);
|
|
|
|
|
|
|
|
/* Update counter for this layer */
|
2013-01-15 07:34:55 +08:00
|
|
|
rc = mb86a20s_writereg(state, 0x50, 0xa7 + layer * 3);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
rc = mb86a20s_writereg(state, 0x51,
|
|
|
|
state->estimated_rate[layer] >> 16);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
rc = mb86a20s_writereg(state, 0x50, 0xa8 + layer * 3);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
rc = mb86a20s_writereg(state, 0x51,
|
|
|
|
state->estimated_rate[layer] >> 8);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
rc = mb86a20s_writereg(state, 0x50, 0xa9 + layer * 3);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
rc = mb86a20s_writereg(state, 0x51,
|
|
|
|
state->estimated_rate[layer]);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
2013-01-24 22:48:44 +08:00
|
|
|
|
|
|
|
/* Turn on BER before Viterbi */
|
|
|
|
rc = mb86a20s_writereg(state, 0x52, 0x01);
|
|
|
|
|
|
|
|
/* Reset all preBER counters */
|
|
|
|
rc = mb86a20s_writereg(state, 0x53, 0x00);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
rc = mb86a20s_writereg(state, 0x53, 0x07);
|
|
|
|
} else {
|
|
|
|
/* Reset counter to collect new data */
|
|
|
|
rc = mb86a20s_readreg(state, 0x53);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
val = rc;
|
|
|
|
rc = mb86a20s_writereg(state, 0x53, val & ~(1 << layer));
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
rc = mb86a20s_writereg(state, 0x53, val | (1 << layer));
|
2013-01-15 07:34:55 +08:00
|
|
|
}
|
|
|
|
|
2013-01-24 21:25:16 +08:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mb86a20s_get_post_ber(struct dvb_frontend *fe,
|
|
|
|
unsigned layer,
|
|
|
|
u32 *error, u32 *count)
|
|
|
|
{
|
|
|
|
struct mb86a20s_state *state = fe->demodulator_priv;
|
|
|
|
u32 counter, collect_rate;
|
|
|
|
int rc, val;
|
|
|
|
|
|
|
|
dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
|
|
|
|
|
2013-04-08 07:50:34 +08:00
|
|
|
if (layer >= NUM_LAYERS)
|
2013-01-24 21:25:16 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/* Check if the BER measures are already available */
|
|
|
|
rc = mb86a20s_readreg(state, 0x60);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
/* Check if data is available for that layer */
|
|
|
|
if (!(rc & (1 << layer))) {
|
|
|
|
dev_dbg(&state->i2c->dev,
|
|
|
|
"%s: post BER for layer %c is not available yet.\n",
|
|
|
|
__func__, 'A' + layer);
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
2013-01-15 07:34:55 +08:00
|
|
|
|
2013-01-24 21:25:16 +08:00
|
|
|
/* Read Bit Error Count */
|
|
|
|
rc = mb86a20s_readreg(state, 0x64 + layer * 3);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
*error = rc << 16;
|
|
|
|
rc = mb86a20s_readreg(state, 0x65 + layer * 3);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
*error |= rc << 8;
|
|
|
|
rc = mb86a20s_readreg(state, 0x66 + layer * 3);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
*error |= rc;
|
|
|
|
|
|
|
|
dev_dbg(&state->i2c->dev,
|
|
|
|
"%s: post bit error for layer %c: %d.\n",
|
|
|
|
__func__, 'A' + layer, *error);
|
|
|
|
|
|
|
|
/* Read Bit Count */
|
|
|
|
rc = mb86a20s_writereg(state, 0x50, 0xdc + layer * 2);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
rc = mb86a20s_readreg(state, 0x51);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
counter = rc << 8;
|
|
|
|
rc = mb86a20s_writereg(state, 0x50, 0xdd + layer * 2);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
rc = mb86a20s_readreg(state, 0x51);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
counter |= rc;
|
|
|
|
*count = counter * 204 * 8;
|
|
|
|
|
|
|
|
dev_dbg(&state->i2c->dev,
|
|
|
|
"%s: post bit count for layer %c: %d.\n",
|
|
|
|
__func__, 'A' + layer, *count);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* As we get TMCC data from the frontend, we can better estimate the
|
|
|
|
* BER bit counters, in order to do the BER measure during a longer
|
|
|
|
* time. Use those data, if available, to update the bit count
|
|
|
|
* measure.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (!state->estimated_rate[layer])
|
|
|
|
goto reset_measurement;
|
|
|
|
|
|
|
|
collect_rate = state->estimated_rate[layer] / 204 / 8;
|
|
|
|
if (collect_rate < 32)
|
|
|
|
collect_rate = 32;
|
|
|
|
if (collect_rate > 65535)
|
|
|
|
collect_rate = 65535;
|
|
|
|
if (collect_rate != counter) {
|
|
|
|
dev_dbg(&state->i2c->dev,
|
|
|
|
"%s: updating postBER counter on layer %c to %d.\n",
|
|
|
|
__func__, 'A' + layer, collect_rate);
|
|
|
|
|
|
|
|
/* Turn off BER after Viterbi */
|
|
|
|
rc = mb86a20s_writereg(state, 0x5e, 0x00);
|
|
|
|
|
|
|
|
/* Update counter for this layer */
|
|
|
|
rc = mb86a20s_writereg(state, 0x50, 0xdc + layer * 2);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
rc = mb86a20s_writereg(state, 0x51, collect_rate >> 8);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
rc = mb86a20s_writereg(state, 0x50, 0xdd + layer * 2);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
rc = mb86a20s_writereg(state, 0x51, collect_rate & 0xff);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
/* Turn on BER after Viterbi */
|
|
|
|
rc = mb86a20s_writereg(state, 0x5e, 0x07);
|
|
|
|
|
|
|
|
/* Reset all preBER counters */
|
|
|
|
rc = mb86a20s_writereg(state, 0x5f, 0x00);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
rc = mb86a20s_writereg(state, 0x5f, 0x07);
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
reset_measurement:
|
2013-01-22 23:30:07 +08:00
|
|
|
/* Reset counter to collect new data */
|
2013-01-24 22:48:44 +08:00
|
|
|
rc = mb86a20s_readreg(state, 0x5f);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
val = rc;
|
|
|
|
rc = mb86a20s_writereg(state, 0x5f, val & ~(1 << layer));
|
2013-01-22 23:30:07 +08:00
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
2013-01-24 21:25:16 +08:00
|
|
|
rc = mb86a20s_writereg(state, 0x5f, val | (1 << layer));
|
2013-01-22 23:30:07 +08:00
|
|
|
|
2013-01-24 22:48:44 +08:00
|
|
|
return rc;
|
2013-01-22 23:30:07 +08:00
|
|
|
}
|
|
|
|
|
2013-01-18 07:10:47 +08:00
|
|
|
static int mb86a20s_get_blk_error(struct dvb_frontend *fe,
|
|
|
|
unsigned layer,
|
|
|
|
u32 *error, u32 *count)
|
|
|
|
{
|
|
|
|
struct mb86a20s_state *state = fe->demodulator_priv;
|
2013-01-24 22:51:23 +08:00
|
|
|
int rc, val;
|
2013-01-18 07:10:47 +08:00
|
|
|
u32 collect_rate;
|
|
|
|
dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
|
|
|
|
|
2013-04-08 07:50:34 +08:00
|
|
|
if (layer >= NUM_LAYERS)
|
2013-01-18 07:10:47 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/* Check if the PER measures are already available */
|
|
|
|
rc = mb86a20s_writereg(state, 0x50, 0xb8);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
rc = mb86a20s_readreg(state, 0x51);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
/* Check if data is available for that layer */
|
|
|
|
|
|
|
|
if (!(rc & (1 << layer))) {
|
|
|
|
dev_dbg(&state->i2c->dev,
|
|
|
|
"%s: block counts for layer %c aren't available yet.\n",
|
|
|
|
__func__, 'A' + layer);
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Read Packet error Count */
|
|
|
|
rc = mb86a20s_writereg(state, 0x50, 0xb9 + layer * 2);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
rc = mb86a20s_readreg(state, 0x51);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
*error = rc << 8;
|
|
|
|
rc = mb86a20s_writereg(state, 0x50, 0xba + layer * 2);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
rc = mb86a20s_readreg(state, 0x51);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
*error |= rc;
|
2013-03-02 20:05:42 +08:00
|
|
|
dev_dbg(&state->i2c->dev, "%s: block error for layer %c: %d.\n",
|
2013-01-18 07:10:47 +08:00
|
|
|
__func__, 'A' + layer, *error);
|
|
|
|
|
|
|
|
/* Read Bit Count */
|
|
|
|
rc = mb86a20s_writereg(state, 0x50, 0xb2 + layer * 2);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
rc = mb86a20s_readreg(state, 0x51);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
*count = rc << 8;
|
|
|
|
rc = mb86a20s_writereg(state, 0x50, 0xb3 + layer * 2);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
rc = mb86a20s_readreg(state, 0x51);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
*count |= rc;
|
|
|
|
|
|
|
|
dev_dbg(&state->i2c->dev,
|
|
|
|
"%s: block count for layer %c: %d.\n",
|
|
|
|
__func__, 'A' + layer, *count);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* As we get TMCC data from the frontend, we can better estimate the
|
|
|
|
* BER bit counters, in order to do the BER measure during a longer
|
|
|
|
* time. Use those data, if available, to update the bit count
|
|
|
|
* measure.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (!state->estimated_rate[layer])
|
|
|
|
goto reset_measurement;
|
|
|
|
|
|
|
|
collect_rate = state->estimated_rate[layer] / 204 / 8;
|
|
|
|
if (collect_rate < 32)
|
|
|
|
collect_rate = 32;
|
|
|
|
if (collect_rate > 65535)
|
|
|
|
collect_rate = 65535;
|
|
|
|
|
|
|
|
if (collect_rate != *count) {
|
|
|
|
dev_dbg(&state->i2c->dev,
|
|
|
|
"%s: updating PER counter on layer %c to %d.\n",
|
|
|
|
__func__, 'A' + layer, collect_rate);
|
2013-01-24 22:51:23 +08:00
|
|
|
|
|
|
|
/* Stop PER measurement */
|
|
|
|
rc = mb86a20s_writereg(state, 0x50, 0xb0);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
rc = mb86a20s_writereg(state, 0x51, 0x00);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
/* Update this layer's counter */
|
2013-01-18 07:10:47 +08:00
|
|
|
rc = mb86a20s_writereg(state, 0x50, 0xb2 + layer * 2);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
rc = mb86a20s_writereg(state, 0x51, collect_rate >> 8);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
rc = mb86a20s_writereg(state, 0x50, 0xb3 + layer * 2);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
rc = mb86a20s_writereg(state, 0x51, collect_rate & 0xff);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
2013-01-24 22:51:23 +08:00
|
|
|
|
|
|
|
/* start PER measurement */
|
|
|
|
rc = mb86a20s_writereg(state, 0x50, 0xb0);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
rc = mb86a20s_writereg(state, 0x51, 0x07);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
/* Reset all counters to collect new data */
|
|
|
|
rc = mb86a20s_writereg(state, 0x50, 0xb1);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
rc = mb86a20s_writereg(state, 0x51, 0x07);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
rc = mb86a20s_writereg(state, 0x51, 0x00);
|
|
|
|
|
|
|
|
return rc;
|
2013-01-18 07:10:47 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
reset_measurement:
|
|
|
|
/* Reset counter to collect new data */
|
|
|
|
rc = mb86a20s_writereg(state, 0x50, 0xb1);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
2013-01-24 22:51:23 +08:00
|
|
|
rc = mb86a20s_readreg(state, 0x51);
|
2013-01-18 07:10:47 +08:00
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
2013-01-24 22:51:23 +08:00
|
|
|
val = rc;
|
|
|
|
rc = mb86a20s_writereg(state, 0x51, val | (1 << layer));
|
2013-01-18 07:10:47 +08:00
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
2013-01-24 22:51:23 +08:00
|
|
|
rc = mb86a20s_writereg(state, 0x51, val & ~(1 << layer));
|
2013-01-18 07:10:47 +08:00
|
|
|
|
2013-01-24 22:51:23 +08:00
|
|
|
return rc;
|
2013-01-18 07:10:47 +08:00
|
|
|
}
|
|
|
|
|
2013-01-17 02:12:05 +08:00
|
|
|
struct linear_segments {
|
|
|
|
unsigned x, y;
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* All tables below return a dB/1000 measurement
|
|
|
|
*/
|
|
|
|
|
2014-08-21 05:08:17 +08:00
|
|
|
static const struct linear_segments cnr_to_db_table[] = {
|
2013-01-17 02:12:05 +08:00
|
|
|
{ 19648, 0},
|
|
|
|
{ 18187, 1000},
|
|
|
|
{ 16534, 2000},
|
|
|
|
{ 14823, 3000},
|
|
|
|
{ 13161, 4000},
|
|
|
|
{ 11622, 5000},
|
|
|
|
{ 10279, 6000},
|
|
|
|
{ 9089, 7000},
|
|
|
|
{ 8042, 8000},
|
|
|
|
{ 7137, 9000},
|
|
|
|
{ 6342, 10000},
|
|
|
|
{ 5641, 11000},
|
|
|
|
{ 5030, 12000},
|
|
|
|
{ 4474, 13000},
|
|
|
|
{ 3988, 14000},
|
|
|
|
{ 3556, 15000},
|
|
|
|
{ 3180, 16000},
|
|
|
|
{ 2841, 17000},
|
|
|
|
{ 2541, 18000},
|
|
|
|
{ 2276, 19000},
|
|
|
|
{ 2038, 20000},
|
|
|
|
{ 1800, 21000},
|
|
|
|
{ 1625, 22000},
|
|
|
|
{ 1462, 23000},
|
|
|
|
{ 1324, 24000},
|
|
|
|
{ 1175, 25000},
|
|
|
|
{ 1063, 26000},
|
|
|
|
{ 980, 27000},
|
|
|
|
{ 907, 28000},
|
|
|
|
{ 840, 29000},
|
|
|
|
{ 788, 30000},
|
|
|
|
};
|
|
|
|
|
2014-08-21 05:08:17 +08:00
|
|
|
static const struct linear_segments cnr_64qam_table[] = {
|
2013-01-17 02:12:05 +08:00
|
|
|
{ 3922688, 0},
|
|
|
|
{ 3920384, 1000},
|
|
|
|
{ 3902720, 2000},
|
|
|
|
{ 3894784, 3000},
|
|
|
|
{ 3882496, 4000},
|
|
|
|
{ 3872768, 5000},
|
|
|
|
{ 3858944, 6000},
|
|
|
|
{ 3851520, 7000},
|
|
|
|
{ 3838976, 8000},
|
|
|
|
{ 3829248, 9000},
|
|
|
|
{ 3818240, 10000},
|
|
|
|
{ 3806976, 11000},
|
|
|
|
{ 3791872, 12000},
|
|
|
|
{ 3767040, 13000},
|
|
|
|
{ 3720960, 14000},
|
|
|
|
{ 3637504, 15000},
|
|
|
|
{ 3498496, 16000},
|
|
|
|
{ 3296000, 17000},
|
|
|
|
{ 3031040, 18000},
|
|
|
|
{ 2715392, 19000},
|
|
|
|
{ 2362624, 20000},
|
|
|
|
{ 1963264, 21000},
|
|
|
|
{ 1649664, 22000},
|
|
|
|
{ 1366784, 23000},
|
|
|
|
{ 1120768, 24000},
|
|
|
|
{ 890880, 25000},
|
|
|
|
{ 723456, 26000},
|
|
|
|
{ 612096, 27000},
|
|
|
|
{ 518912, 28000},
|
|
|
|
{ 448256, 29000},
|
|
|
|
{ 388864, 30000},
|
|
|
|
};
|
|
|
|
|
2014-08-21 05:08:17 +08:00
|
|
|
static const struct linear_segments cnr_16qam_table[] = {
|
2013-01-17 02:12:05 +08:00
|
|
|
{ 5314816, 0},
|
|
|
|
{ 5219072, 1000},
|
|
|
|
{ 5118720, 2000},
|
|
|
|
{ 4998912, 3000},
|
|
|
|
{ 4875520, 4000},
|
|
|
|
{ 4736000, 5000},
|
|
|
|
{ 4604160, 6000},
|
|
|
|
{ 4458752, 7000},
|
|
|
|
{ 4300288, 8000},
|
|
|
|
{ 4092928, 9000},
|
|
|
|
{ 3836160, 10000},
|
|
|
|
{ 3521024, 11000},
|
|
|
|
{ 3155968, 12000},
|
|
|
|
{ 2756864, 13000},
|
|
|
|
{ 2347008, 14000},
|
|
|
|
{ 1955072, 15000},
|
|
|
|
{ 1593600, 16000},
|
|
|
|
{ 1297920, 17000},
|
|
|
|
{ 1043968, 18000},
|
|
|
|
{ 839680, 19000},
|
|
|
|
{ 672256, 20000},
|
|
|
|
{ 523008, 21000},
|
|
|
|
{ 424704, 22000},
|
|
|
|
{ 345088, 23000},
|
|
|
|
{ 280064, 24000},
|
|
|
|
{ 221440, 25000},
|
|
|
|
{ 179712, 26000},
|
|
|
|
{ 151040, 27000},
|
|
|
|
{ 128512, 28000},
|
|
|
|
{ 110080, 29000},
|
|
|
|
{ 95744, 30000},
|
|
|
|
};
|
|
|
|
|
2014-08-21 05:08:17 +08:00
|
|
|
static const struct linear_segments cnr_qpsk_table[] = {
|
2013-01-17 02:12:05 +08:00
|
|
|
{ 2834176, 0},
|
|
|
|
{ 2683648, 1000},
|
|
|
|
{ 2536960, 2000},
|
|
|
|
{ 2391808, 3000},
|
|
|
|
{ 2133248, 4000},
|
|
|
|
{ 1906176, 5000},
|
|
|
|
{ 1666560, 6000},
|
|
|
|
{ 1422080, 7000},
|
|
|
|
{ 1189632, 8000},
|
|
|
|
{ 976384, 9000},
|
|
|
|
{ 790272, 10000},
|
|
|
|
{ 633344, 11000},
|
|
|
|
{ 505600, 12000},
|
|
|
|
{ 402944, 13000},
|
|
|
|
{ 320768, 14000},
|
|
|
|
{ 255488, 15000},
|
|
|
|
{ 204032, 16000},
|
|
|
|
{ 163072, 17000},
|
|
|
|
{ 130304, 18000},
|
|
|
|
{ 105216, 19000},
|
|
|
|
{ 83456, 20000},
|
|
|
|
{ 65024, 21000},
|
|
|
|
{ 52480, 22000},
|
|
|
|
{ 42752, 23000},
|
|
|
|
{ 34560, 24000},
|
|
|
|
{ 27136, 25000},
|
|
|
|
{ 22016, 26000},
|
|
|
|
{ 18432, 27000},
|
|
|
|
{ 15616, 28000},
|
|
|
|
{ 13312, 29000},
|
|
|
|
{ 11520, 30000},
|
|
|
|
};
|
|
|
|
|
2014-08-21 05:08:17 +08:00
|
|
|
static u32 interpolate_value(u32 value, const struct linear_segments *segments,
|
2013-01-17 02:12:05 +08:00
|
|
|
unsigned len)
|
|
|
|
{
|
|
|
|
u64 tmp64;
|
|
|
|
u32 dx, dy;
|
|
|
|
int i, ret;
|
|
|
|
|
|
|
|
if (value >= segments[0].x)
|
|
|
|
return segments[0].y;
|
|
|
|
if (value < segments[len-1].x)
|
|
|
|
return segments[len-1].y;
|
|
|
|
|
|
|
|
for (i = 1; i < len - 1; i++) {
|
|
|
|
/* If value is identical, no need to interpolate */
|
|
|
|
if (value == segments[i].x)
|
|
|
|
return segments[i].y;
|
|
|
|
if (value > segments[i].x)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Linear interpolation between the two (x,y) points */
|
|
|
|
dy = segments[i].y - segments[i - 1].y;
|
|
|
|
dx = segments[i - 1].x - segments[i].x;
|
|
|
|
tmp64 = value - segments[i].x;
|
|
|
|
tmp64 *= dy;
|
|
|
|
do_div(tmp64, dx);
|
|
|
|
ret = segments[i].y - tmp64;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mb86a20s_get_main_CNR(struct dvb_frontend *fe)
|
|
|
|
{
|
|
|
|
struct mb86a20s_state *state = fe->demodulator_priv;
|
|
|
|
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
|
|
|
|
u32 cnr_linear, cnr;
|
|
|
|
int rc, val;
|
|
|
|
|
|
|
|
/* Check if CNR is available */
|
|
|
|
rc = mb86a20s_readreg(state, 0x45);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
if (!(rc & 0x40)) {
|
2013-03-02 20:05:42 +08:00
|
|
|
dev_dbg(&state->i2c->dev, "%s: CNR is not available yet.\n",
|
2013-01-17 02:12:05 +08:00
|
|
|
__func__);
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
val = rc;
|
|
|
|
|
|
|
|
rc = mb86a20s_readreg(state, 0x46);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
cnr_linear = rc << 8;
|
|
|
|
|
|
|
|
rc = mb86a20s_readreg(state, 0x46);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
cnr_linear |= rc;
|
|
|
|
|
|
|
|
cnr = interpolate_value(cnr_linear,
|
|
|
|
cnr_to_db_table, ARRAY_SIZE(cnr_to_db_table));
|
|
|
|
|
|
|
|
c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
|
|
|
|
c->cnr.stat[0].svalue = cnr;
|
|
|
|
|
|
|
|
dev_dbg(&state->i2c->dev, "%s: CNR is %d.%03d dB (%d)\n",
|
|
|
|
__func__, cnr / 1000, cnr % 1000, cnr_linear);
|
|
|
|
|
|
|
|
/* CNR counter reset */
|
|
|
|
rc = mb86a20s_writereg(state, 0x45, val | 0x10);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
rc = mb86a20s_writereg(state, 0x45, val & 0x6f);
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2013-01-18 07:10:47 +08:00
|
|
|
static int mb86a20s_get_blk_error_layer_CNR(struct dvb_frontend *fe)
|
2013-01-17 02:12:05 +08:00
|
|
|
{
|
|
|
|
struct mb86a20s_state *state = fe->demodulator_priv;
|
|
|
|
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
|
|
|
|
u32 mer, cnr;
|
2013-04-08 07:52:16 +08:00
|
|
|
int rc, val, layer;
|
2014-08-21 05:08:17 +08:00
|
|
|
const struct linear_segments *segs;
|
2013-01-17 02:12:05 +08:00
|
|
|
unsigned segs_len;
|
|
|
|
|
|
|
|
dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
|
|
|
|
|
|
|
|
/* Check if the measures are already available */
|
|
|
|
rc = mb86a20s_writereg(state, 0x50, 0x5b);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
rc = mb86a20s_readreg(state, 0x51);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
/* Check if data is available */
|
|
|
|
if (!(rc & 0x01)) {
|
2013-03-02 20:05:42 +08:00
|
|
|
dev_dbg(&state->i2c->dev,
|
2013-01-17 02:12:05 +08:00
|
|
|
"%s: MER measures aren't available yet.\n", __func__);
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Read all layers */
|
2013-04-08 07:52:16 +08:00
|
|
|
for (layer = 0; layer < NUM_LAYERS; layer++) {
|
|
|
|
if (!(c->isdbt_layer_enabled & (1 << layer))) {
|
|
|
|
c->cnr.stat[1 + layer].scale = FE_SCALE_NOT_AVAILABLE;
|
2013-01-17 02:12:05 +08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2013-04-08 07:52:16 +08:00
|
|
|
rc = mb86a20s_writereg(state, 0x50, 0x52 + layer * 3);
|
2013-01-17 02:12:05 +08:00
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
rc = mb86a20s_readreg(state, 0x51);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
mer = rc << 16;
|
2013-04-08 07:52:16 +08:00
|
|
|
rc = mb86a20s_writereg(state, 0x50, 0x53 + layer * 3);
|
2013-01-17 02:12:05 +08:00
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
rc = mb86a20s_readreg(state, 0x51);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
mer |= rc << 8;
|
2013-04-08 07:52:16 +08:00
|
|
|
rc = mb86a20s_writereg(state, 0x50, 0x54 + layer * 3);
|
2013-01-17 02:12:05 +08:00
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
rc = mb86a20s_readreg(state, 0x51);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
mer |= rc;
|
|
|
|
|
2013-04-08 07:52:16 +08:00
|
|
|
switch (c->layer[layer].modulation) {
|
2013-01-17 02:12:05 +08:00
|
|
|
case DQPSK:
|
|
|
|
case QPSK:
|
|
|
|
segs = cnr_qpsk_table;
|
|
|
|
segs_len = ARRAY_SIZE(cnr_qpsk_table);
|
|
|
|
break;
|
|
|
|
case QAM_16:
|
|
|
|
segs = cnr_16qam_table;
|
|
|
|
segs_len = ARRAY_SIZE(cnr_16qam_table);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
case QAM_64:
|
|
|
|
segs = cnr_64qam_table;
|
|
|
|
segs_len = ARRAY_SIZE(cnr_64qam_table);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
cnr = interpolate_value(mer, segs, segs_len);
|
|
|
|
|
2013-04-08 07:52:16 +08:00
|
|
|
c->cnr.stat[1 + layer].scale = FE_SCALE_DECIBEL;
|
|
|
|
c->cnr.stat[1 + layer].svalue = cnr;
|
2013-01-17 02:12:05 +08:00
|
|
|
|
|
|
|
dev_dbg(&state->i2c->dev,
|
|
|
|
"%s: CNR for layer %c is %d.%03d dB (MER = %d).\n",
|
2013-04-08 07:52:16 +08:00
|
|
|
__func__, 'A' + layer, cnr / 1000, cnr % 1000, mer);
|
2013-01-17 02:12:05 +08:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Start a new MER measurement */
|
|
|
|
/* MER counter reset */
|
|
|
|
rc = mb86a20s_writereg(state, 0x50, 0x50);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
rc = mb86a20s_readreg(state, 0x51);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
val = rc;
|
|
|
|
|
|
|
|
rc = mb86a20s_writereg(state, 0x51, val | 0x01);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
rc = mb86a20s_writereg(state, 0x51, val & 0x06);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-01-22 23:28:31 +08:00
|
|
|
static void mb86a20s_stats_not_ready(struct dvb_frontend *fe)
|
|
|
|
{
|
|
|
|
struct mb86a20s_state *state = fe->demodulator_priv;
|
|
|
|
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
|
2013-04-08 07:52:16 +08:00
|
|
|
int layer;
|
2013-01-22 23:28:31 +08:00
|
|
|
|
|
|
|
dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
|
2013-01-14 20:26:09 +08:00
|
|
|
|
2013-01-22 23:28:31 +08:00
|
|
|
/* Fill the length of each status counter */
|
|
|
|
|
|
|
|
/* Only global stats */
|
|
|
|
c->strength.len = 1;
|
|
|
|
|
|
|
|
/* Per-layer stats - 3 layers + global */
|
2013-04-08 07:50:34 +08:00
|
|
|
c->cnr.len = NUM_LAYERS + 1;
|
|
|
|
c->pre_bit_error.len = NUM_LAYERS + 1;
|
|
|
|
c->pre_bit_count.len = NUM_LAYERS + 1;
|
|
|
|
c->post_bit_error.len = NUM_LAYERS + 1;
|
|
|
|
c->post_bit_count.len = NUM_LAYERS + 1;
|
|
|
|
c->block_error.len = NUM_LAYERS + 1;
|
|
|
|
c->block_count.len = NUM_LAYERS + 1;
|
2013-01-22 23:28:31 +08:00
|
|
|
|
|
|
|
/* Signal is always available */
|
|
|
|
c->strength.stat[0].scale = FE_SCALE_RELATIVE;
|
|
|
|
c->strength.stat[0].uvalue = 0;
|
|
|
|
|
|
|
|
/* Put all of them at FE_SCALE_NOT_AVAILABLE */
|
2013-04-08 07:52:16 +08:00
|
|
|
for (layer = 0; layer < NUM_LAYERS + 1; layer++) {
|
|
|
|
c->cnr.stat[layer].scale = FE_SCALE_NOT_AVAILABLE;
|
|
|
|
c->pre_bit_error.stat[layer].scale = FE_SCALE_NOT_AVAILABLE;
|
|
|
|
c->pre_bit_count.stat[layer].scale = FE_SCALE_NOT_AVAILABLE;
|
|
|
|
c->post_bit_error.stat[layer].scale = FE_SCALE_NOT_AVAILABLE;
|
|
|
|
c->post_bit_count.stat[layer].scale = FE_SCALE_NOT_AVAILABLE;
|
|
|
|
c->block_error.stat[layer].scale = FE_SCALE_NOT_AVAILABLE;
|
|
|
|
c->block_count.stat[layer].scale = FE_SCALE_NOT_AVAILABLE;
|
2013-01-22 23:28:31 +08:00
|
|
|
}
|
2010-09-28 07:52:43 +08:00
|
|
|
}
|
|
|
|
|
2013-03-02 20:06:17 +08:00
|
|
|
static int mb86a20s_get_stats(struct dvb_frontend *fe, int status_nr)
|
2013-01-22 23:30:07 +08:00
|
|
|
{
|
|
|
|
struct mb86a20s_state *state = fe->demodulator_priv;
|
|
|
|
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
|
2013-04-08 07:52:16 +08:00
|
|
|
int rc = 0, layer;
|
2013-01-22 23:30:07 +08:00
|
|
|
u32 bit_error = 0, bit_count = 0;
|
|
|
|
u32 t_pre_bit_error = 0, t_pre_bit_count = 0;
|
2013-01-24 21:25:16 +08:00
|
|
|
u32 t_post_bit_error = 0, t_post_bit_count = 0;
|
2013-01-18 07:10:47 +08:00
|
|
|
u32 block_error = 0, block_count = 0;
|
|
|
|
u32 t_block_error = 0, t_block_count = 0;
|
2013-01-24 21:25:16 +08:00
|
|
|
int active_layers = 0, pre_ber_layers = 0, post_ber_layers = 0;
|
|
|
|
int per_layers = 0;
|
2013-01-22 23:30:07 +08:00
|
|
|
|
2013-01-17 02:12:05 +08:00
|
|
|
dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
|
|
|
|
|
|
|
|
mb86a20s_get_main_CNR(fe);
|
|
|
|
|
2013-01-22 23:30:07 +08:00
|
|
|
/* Get per-layer stats */
|
2013-01-18 07:10:47 +08:00
|
|
|
mb86a20s_get_blk_error_layer_CNR(fe);
|
2013-01-17 02:12:05 +08:00
|
|
|
|
2013-03-02 20:06:17 +08:00
|
|
|
/*
|
|
|
|
* At state 7, only CNR is available
|
|
|
|
* For BER measures, state=9 is required
|
|
|
|
* FIXME: we may get MER measures with state=8
|
|
|
|
*/
|
|
|
|
if (status_nr < 9)
|
|
|
|
return 0;
|
|
|
|
|
2013-04-08 07:52:16 +08:00
|
|
|
for (layer = 0; layer < NUM_LAYERS; layer++) {
|
|
|
|
if (c->isdbt_layer_enabled & (1 << layer)) {
|
2013-01-22 23:30:07 +08:00
|
|
|
/* Layer is active and has rc segments */
|
|
|
|
active_layers++;
|
|
|
|
|
|
|
|
/* Handle BER before vterbi */
|
2013-04-08 07:52:16 +08:00
|
|
|
rc = mb86a20s_get_pre_ber(fe, layer,
|
2013-01-24 22:48:44 +08:00
|
|
|
&bit_error, &bit_count);
|
2013-01-22 23:30:07 +08:00
|
|
|
if (rc >= 0) {
|
2013-04-08 07:52:16 +08:00
|
|
|
c->pre_bit_error.stat[1 + layer].scale = FE_SCALE_COUNTER;
|
|
|
|
c->pre_bit_error.stat[1 + layer].uvalue += bit_error;
|
|
|
|
c->pre_bit_count.stat[1 + layer].scale = FE_SCALE_COUNTER;
|
|
|
|
c->pre_bit_count.stat[1 + layer].uvalue += bit_count;
|
2013-01-22 23:30:07 +08:00
|
|
|
} else if (rc != -EBUSY) {
|
|
|
|
/*
|
|
|
|
* If an I/O error happened,
|
|
|
|
* measures are now unavailable
|
|
|
|
*/
|
2013-04-08 07:52:16 +08:00
|
|
|
c->pre_bit_error.stat[1 + layer].scale = FE_SCALE_NOT_AVAILABLE;
|
|
|
|
c->pre_bit_count.stat[1 + layer].scale = FE_SCALE_NOT_AVAILABLE;
|
2013-01-22 23:30:07 +08:00
|
|
|
dev_err(&state->i2c->dev,
|
|
|
|
"%s: Can't get BER for layer %c (error %d).\n",
|
2013-04-08 07:52:16 +08:00
|
|
|
__func__, 'A' + layer, rc);
|
2013-01-22 23:30:07 +08:00
|
|
|
}
|
2013-04-08 07:52:16 +08:00
|
|
|
if (c->block_error.stat[1 + layer].scale != FE_SCALE_NOT_AVAILABLE)
|
2013-01-24 21:25:16 +08:00
|
|
|
pre_ber_layers++;
|
|
|
|
|
|
|
|
/* Handle BER post vterbi */
|
2013-04-08 07:52:16 +08:00
|
|
|
rc = mb86a20s_get_post_ber(fe, layer,
|
2013-01-24 21:25:16 +08:00
|
|
|
&bit_error, &bit_count);
|
|
|
|
if (rc >= 0) {
|
2013-04-08 07:52:16 +08:00
|
|
|
c->post_bit_error.stat[1 + layer].scale = FE_SCALE_COUNTER;
|
|
|
|
c->post_bit_error.stat[1 + layer].uvalue += bit_error;
|
|
|
|
c->post_bit_count.stat[1 + layer].scale = FE_SCALE_COUNTER;
|
|
|
|
c->post_bit_count.stat[1 + layer].uvalue += bit_count;
|
2013-01-24 21:25:16 +08:00
|
|
|
} else if (rc != -EBUSY) {
|
|
|
|
/*
|
|
|
|
* If an I/O error happened,
|
|
|
|
* measures are now unavailable
|
|
|
|
*/
|
2013-04-08 07:52:16 +08:00
|
|
|
c->post_bit_error.stat[1 + layer].scale = FE_SCALE_NOT_AVAILABLE;
|
|
|
|
c->post_bit_count.stat[1 + layer].scale = FE_SCALE_NOT_AVAILABLE;
|
2013-01-24 21:25:16 +08:00
|
|
|
dev_err(&state->i2c->dev,
|
|
|
|
"%s: Can't get BER for layer %c (error %d).\n",
|
2013-04-08 07:52:16 +08:00
|
|
|
__func__, 'A' + layer, rc);
|
2013-01-24 21:25:16 +08:00
|
|
|
}
|
2013-04-08 07:52:16 +08:00
|
|
|
if (c->block_error.stat[1 + layer].scale != FE_SCALE_NOT_AVAILABLE)
|
2013-01-24 21:25:16 +08:00
|
|
|
post_ber_layers++;
|
2013-01-22 23:30:07 +08:00
|
|
|
|
2013-01-18 07:10:47 +08:00
|
|
|
/* Handle Block errors for PER/UCB reports */
|
2013-04-08 07:52:16 +08:00
|
|
|
rc = mb86a20s_get_blk_error(fe, layer,
|
2013-01-18 07:10:47 +08:00
|
|
|
&block_error,
|
|
|
|
&block_count);
|
|
|
|
if (rc >= 0) {
|
2013-04-08 07:52:16 +08:00
|
|
|
c->block_error.stat[1 + layer].scale = FE_SCALE_COUNTER;
|
|
|
|
c->block_error.stat[1 + layer].uvalue += block_error;
|
|
|
|
c->block_count.stat[1 + layer].scale = FE_SCALE_COUNTER;
|
|
|
|
c->block_count.stat[1 + layer].uvalue += block_count;
|
2013-01-18 07:10:47 +08:00
|
|
|
} else if (rc != -EBUSY) {
|
|
|
|
/*
|
|
|
|
* If an I/O error happened,
|
|
|
|
* measures are now unavailable
|
|
|
|
*/
|
2013-04-08 07:52:16 +08:00
|
|
|
c->block_error.stat[1 + layer].scale = FE_SCALE_NOT_AVAILABLE;
|
|
|
|
c->block_count.stat[1 + layer].scale = FE_SCALE_NOT_AVAILABLE;
|
2013-01-18 07:10:47 +08:00
|
|
|
dev_err(&state->i2c->dev,
|
|
|
|
"%s: Can't get PER for layer %c (error %d).\n",
|
2013-04-08 07:52:16 +08:00
|
|
|
__func__, 'A' + layer, rc);
|
2013-01-18 07:10:47 +08:00
|
|
|
|
|
|
|
}
|
2013-04-08 07:52:16 +08:00
|
|
|
if (c->block_error.stat[1 + layer].scale != FE_SCALE_NOT_AVAILABLE)
|
2013-01-18 07:10:47 +08:00
|
|
|
per_layers++;
|
|
|
|
|
2013-01-24 21:25:16 +08:00
|
|
|
/* Update total preBER */
|
2013-04-08 07:52:16 +08:00
|
|
|
t_pre_bit_error += c->pre_bit_error.stat[1 + layer].uvalue;
|
|
|
|
t_pre_bit_count += c->pre_bit_count.stat[1 + layer].uvalue;
|
2013-01-18 07:10:47 +08:00
|
|
|
|
2013-01-24 21:25:16 +08:00
|
|
|
/* Update total postBER */
|
2013-04-08 07:52:16 +08:00
|
|
|
t_post_bit_error += c->post_bit_error.stat[1 + layer].uvalue;
|
|
|
|
t_post_bit_count += c->post_bit_count.stat[1 + layer].uvalue;
|
2013-01-24 21:25:16 +08:00
|
|
|
|
2013-01-18 07:10:47 +08:00
|
|
|
/* Update total PER */
|
2013-04-08 07:52:16 +08:00
|
|
|
t_block_error += c->block_error.stat[1 + layer].uvalue;
|
|
|
|
t_block_count += c->block_count.stat[1 + layer].uvalue;
|
2013-01-22 23:30:07 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Start showing global count if at least one error count is
|
|
|
|
* available.
|
|
|
|
*/
|
2013-01-24 21:25:16 +08:00
|
|
|
if (pre_ber_layers) {
|
2013-01-22 23:30:07 +08:00
|
|
|
/*
|
|
|
|
* At least one per-layer BER measure was read. We can now
|
|
|
|
* calculate the total BER
|
|
|
|
*
|
|
|
|
* Total Bit Error/Count is calculated as the sum of the
|
|
|
|
* bit errors on all active layers.
|
|
|
|
*/
|
|
|
|
c->pre_bit_error.stat[0].scale = FE_SCALE_COUNTER;
|
|
|
|
c->pre_bit_error.stat[0].uvalue = t_pre_bit_error;
|
|
|
|
c->pre_bit_count.stat[0].scale = FE_SCALE_COUNTER;
|
|
|
|
c->pre_bit_count.stat[0].uvalue = t_pre_bit_count;
|
2013-01-25 00:16:17 +08:00
|
|
|
} else {
|
|
|
|
c->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
|
|
|
|
c->pre_bit_count.stat[0].scale = FE_SCALE_COUNTER;
|
2013-01-22 23:30:07 +08:00
|
|
|
}
|
|
|
|
|
2013-01-24 21:25:16 +08:00
|
|
|
/*
|
|
|
|
* Start showing global count if at least one error count is
|
|
|
|
* available.
|
|
|
|
*/
|
|
|
|
if (post_ber_layers) {
|
|
|
|
/*
|
|
|
|
* At least one per-layer BER measure was read. We can now
|
|
|
|
* calculate the total BER
|
|
|
|
*
|
|
|
|
* Total Bit Error/Count is calculated as the sum of the
|
|
|
|
* bit errors on all active layers.
|
|
|
|
*/
|
|
|
|
c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
|
|
|
|
c->post_bit_error.stat[0].uvalue = t_post_bit_error;
|
|
|
|
c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
|
|
|
|
c->post_bit_count.stat[0].uvalue = t_post_bit_count;
|
2013-01-25 00:16:17 +08:00
|
|
|
} else {
|
|
|
|
c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
|
|
|
|
c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
|
2013-01-24 21:25:16 +08:00
|
|
|
}
|
|
|
|
|
2013-01-18 07:10:47 +08:00
|
|
|
if (per_layers) {
|
|
|
|
/*
|
|
|
|
* At least one per-layer UCB measure was read. We can now
|
|
|
|
* calculate the total UCB
|
|
|
|
*
|
|
|
|
* Total block Error/Count is calculated as the sum of the
|
|
|
|
* block errors on all active layers.
|
|
|
|
*/
|
|
|
|
c->block_error.stat[0].scale = FE_SCALE_COUNTER;
|
|
|
|
c->block_error.stat[0].uvalue = t_block_error;
|
|
|
|
c->block_count.stat[0].scale = FE_SCALE_COUNTER;
|
|
|
|
c->block_count.stat[0].uvalue = t_block_count;
|
2013-01-25 00:16:17 +08:00
|
|
|
} else {
|
|
|
|
c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
|
|
|
|
c->block_count.stat[0].scale = FE_SCALE_COUNTER;
|
2013-01-18 07:10:47 +08:00
|
|
|
}
|
|
|
|
|
2013-01-22 23:30:07 +08:00
|
|
|
return rc;
|
|
|
|
}
|
2013-01-22 23:28:31 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The functions below are called via DVB callbacks, so they need to
|
|
|
|
* properly use the I2C gate control
|
|
|
|
*/
|
|
|
|
|
2013-01-22 18:53:11 +08:00
|
|
|
static int mb86a20s_initfe(struct dvb_frontend *fe)
|
|
|
|
{
|
|
|
|
struct mb86a20s_state *state = fe->demodulator_priv;
|
2013-03-01 03:45:39 +08:00
|
|
|
u64 pll;
|
2013-03-04 19:15:49 +08:00
|
|
|
u32 fclk;
|
2013-01-22 18:53:11 +08:00
|
|
|
int rc;
|
2013-03-04 18:10:06 +08:00
|
|
|
u8 regD5 = 1, reg71, reg09 = 0x3a;
|
2013-01-22 18:53:11 +08:00
|
|
|
|
2013-01-22 19:13:08 +08:00
|
|
|
dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
|
2013-01-22 18:53:11 +08:00
|
|
|
|
|
|
|
if (fe->ops.i2c_gate_ctrl)
|
|
|
|
fe->ops.i2c_gate_ctrl(fe, 0);
|
|
|
|
|
|
|
|
/* Initialize the frontend */
|
2013-03-01 03:45:39 +08:00
|
|
|
rc = mb86a20s_writeregdata(state, mb86a20s_init1);
|
2013-01-22 18:53:11 +08:00
|
|
|
if (rc < 0)
|
|
|
|
goto err;
|
|
|
|
|
2013-03-04 18:10:06 +08:00
|
|
|
if (!state->inversion)
|
|
|
|
reg09 |= 0x04;
|
|
|
|
rc = mb86a20s_writereg(state, 0x09, reg09);
|
|
|
|
if (rc < 0)
|
|
|
|
goto err;
|
|
|
|
if (!state->bw)
|
|
|
|
reg71 = 1;
|
|
|
|
else
|
|
|
|
reg71 = 0;
|
|
|
|
rc = mb86a20s_writereg(state, 0x39, reg71);
|
|
|
|
if (rc < 0)
|
|
|
|
goto err;
|
|
|
|
rc = mb86a20s_writereg(state, 0x71, state->bw);
|
|
|
|
if (rc < 0)
|
|
|
|
goto err;
|
|
|
|
if (state->subchannel) {
|
|
|
|
rc = mb86a20s_writereg(state, 0x44, state->subchannel);
|
|
|
|
if (rc < 0)
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2013-03-04 19:15:49 +08:00
|
|
|
fclk = state->config->fclk;
|
|
|
|
if (!fclk)
|
|
|
|
fclk = 32571428;
|
|
|
|
|
2013-03-01 03:45:39 +08:00
|
|
|
/* Adjust IF frequency to match tuner */
|
|
|
|
if (fe->ops.tuner_ops.get_if_frequency)
|
|
|
|
fe->ops.tuner_ops.get_if_frequency(fe, &state->if_freq);
|
|
|
|
|
|
|
|
if (!state->if_freq)
|
|
|
|
state->if_freq = 3300000;
|
|
|
|
|
2013-03-04 19:15:49 +08:00
|
|
|
pll = (((u64)1) << 34) * state->if_freq;
|
|
|
|
do_div(pll, 63 * fclk);
|
|
|
|
pll = (1 << 25) - pll;
|
|
|
|
rc = mb86a20s_writereg(state, 0x28, 0x2a);
|
|
|
|
if (rc < 0)
|
|
|
|
goto err;
|
|
|
|
rc = mb86a20s_writereg(state, 0x29, (pll >> 16) & 0xff);
|
|
|
|
if (rc < 0)
|
|
|
|
goto err;
|
|
|
|
rc = mb86a20s_writereg(state, 0x2a, (pll >> 8) & 0xff);
|
|
|
|
if (rc < 0)
|
|
|
|
goto err;
|
|
|
|
rc = mb86a20s_writereg(state, 0x2b, pll & 0xff);
|
|
|
|
if (rc < 0)
|
|
|
|
goto err;
|
|
|
|
dev_dbg(&state->i2c->dev, "%s: fclk=%d, IF=%d, clock reg=0x%06llx\n",
|
|
|
|
__func__, fclk, state->if_freq, (long long)pll);
|
|
|
|
|
2013-03-01 03:45:39 +08:00
|
|
|
/* pll = freq[Hz] * 2^24/10^6 / 16.285714286 */
|
|
|
|
pll = state->if_freq * 1677721600L;
|
|
|
|
do_div(pll, 1628571429L);
|
|
|
|
rc = mb86a20s_writereg(state, 0x28, 0x20);
|
|
|
|
if (rc < 0)
|
|
|
|
goto err;
|
|
|
|
rc = mb86a20s_writereg(state, 0x29, (pll >> 16) & 0xff);
|
|
|
|
if (rc < 0)
|
|
|
|
goto err;
|
|
|
|
rc = mb86a20s_writereg(state, 0x2a, (pll >> 8) & 0xff);
|
|
|
|
if (rc < 0)
|
|
|
|
goto err;
|
|
|
|
rc = mb86a20s_writereg(state, 0x2b, pll & 0xff);
|
|
|
|
if (rc < 0)
|
|
|
|
goto err;
|
2013-03-04 19:15:49 +08:00
|
|
|
dev_dbg(&state->i2c->dev, "%s: IF=%d, IF reg=0x%06llx\n",
|
2013-03-01 03:45:39 +08:00
|
|
|
__func__, state->if_freq, (long long)pll);
|
|
|
|
|
2013-08-09 19:53:26 +08:00
|
|
|
if (!state->config->is_serial)
|
2013-01-22 18:53:11 +08:00
|
|
|
regD5 &= ~1;
|
|
|
|
|
2013-08-09 19:53:26 +08:00
|
|
|
rc = mb86a20s_writereg(state, 0x50, 0xd5);
|
|
|
|
if (rc < 0)
|
|
|
|
goto err;
|
|
|
|
rc = mb86a20s_writereg(state, 0x51, regD5);
|
|
|
|
if (rc < 0)
|
|
|
|
goto err;
|
2013-01-22 18:53:11 +08:00
|
|
|
|
2013-03-01 03:45:39 +08:00
|
|
|
rc = mb86a20s_writeregdata(state, mb86a20s_init2);
|
|
|
|
if (rc < 0)
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
|
2013-01-22 18:53:11 +08:00
|
|
|
err:
|
|
|
|
if (fe->ops.i2c_gate_ctrl)
|
|
|
|
fe->ops.i2c_gate_ctrl(fe, 1);
|
|
|
|
|
|
|
|
if (rc < 0) {
|
|
|
|
state->need_init = true;
|
2013-01-22 19:13:08 +08:00
|
|
|
dev_info(&state->i2c->dev,
|
|
|
|
"mb86a20s: Init failed. Will try again later\n");
|
2013-01-22 18:53:11 +08:00
|
|
|
} else {
|
|
|
|
state->need_init = false;
|
2013-01-22 19:13:08 +08:00
|
|
|
dev_dbg(&state->i2c->dev, "Initialization succeeded.\n");
|
2013-01-22 18:53:11 +08:00
|
|
|
}
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mb86a20s_set_frontend(struct dvb_frontend *fe)
|
|
|
|
{
|
|
|
|
struct mb86a20s_state *state = fe->demodulator_priv;
|
|
|
|
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
|
2013-03-04 18:10:06 +08:00
|
|
|
int rc, if_freq;
|
2013-01-22 19:13:08 +08:00
|
|
|
dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
|
2013-01-22 18:53:11 +08:00
|
|
|
|
2013-03-04 18:10:06 +08:00
|
|
|
if (!c->isdbt_layer_enabled)
|
|
|
|
c->isdbt_layer_enabled = 7;
|
|
|
|
|
|
|
|
if (c->isdbt_layer_enabled == 1)
|
|
|
|
state->bw = MB86A20S_1SEG;
|
|
|
|
else if (c->isdbt_partial_reception)
|
|
|
|
state->bw = MB86A20S_13SEG_PARTIAL;
|
|
|
|
else
|
|
|
|
state->bw = MB86A20S_13SEG;
|
|
|
|
|
|
|
|
if (c->inversion == INVERSION_ON)
|
|
|
|
state->inversion = true;
|
|
|
|
else
|
|
|
|
state->inversion = false;
|
|
|
|
|
|
|
|
if (!c->isdbt_sb_mode) {
|
|
|
|
state->subchannel = 0;
|
|
|
|
} else {
|
2013-04-08 07:51:32 +08:00
|
|
|
if (c->isdbt_sb_subchannel >= ARRAY_SIZE(mb86a20s_subchannel))
|
2013-03-04 18:10:06 +08:00
|
|
|
c->isdbt_sb_subchannel = 0;
|
|
|
|
|
|
|
|
state->subchannel = mb86a20s_subchannel[c->isdbt_sb_subchannel];
|
|
|
|
}
|
|
|
|
|
2013-01-22 18:53:11 +08:00
|
|
|
/*
|
|
|
|
* Gate should already be opened, but it doesn't hurt to
|
|
|
|
* double-check
|
|
|
|
*/
|
|
|
|
if (fe->ops.i2c_gate_ctrl)
|
|
|
|
fe->ops.i2c_gate_ctrl(fe, 1);
|
|
|
|
fe->ops.tuner_ops.set_params(fe);
|
|
|
|
|
2013-03-03 00:45:31 +08:00
|
|
|
if (fe->ops.tuner_ops.get_if_frequency)
|
2013-03-01 03:45:39 +08:00
|
|
|
fe->ops.tuner_ops.get_if_frequency(fe, &if_freq);
|
|
|
|
|
2013-01-22 18:53:11 +08:00
|
|
|
/*
|
|
|
|
* Make it more reliable: if, for some reason, the initial
|
|
|
|
* device initialization doesn't happen, initialize it when
|
|
|
|
* a SBTVD parameters are adjusted.
|
|
|
|
*
|
|
|
|
* Unfortunately, due to a hard to track bug at tda829x/tda18271,
|
|
|
|
* the agc callback logic is not called during DVB attach time,
|
|
|
|
* causing mb86a20s to not be initialized with Kworld SBTVD.
|
|
|
|
* So, this hack is needed, in order to make Kworld SBTVD to work.
|
2013-03-01 03:45:39 +08:00
|
|
|
*
|
|
|
|
* It is also needed to change the IF after the initial init.
|
2013-03-03 00:45:31 +08:00
|
|
|
*
|
|
|
|
* HACK: Always init the frontend when set_frontend is called:
|
|
|
|
* it was noticed that, on some devices, it fails to lock on a
|
|
|
|
* different channel. So, it is better to reset everything, even
|
|
|
|
* wasting some time, than to loose channel lock.
|
2013-01-22 18:53:11 +08:00
|
|
|
*/
|
2013-03-03 00:45:31 +08:00
|
|
|
mb86a20s_initfe(fe);
|
2013-01-22 18:53:11 +08:00
|
|
|
|
|
|
|
if (fe->ops.i2c_gate_ctrl)
|
|
|
|
fe->ops.i2c_gate_ctrl(fe, 0);
|
2013-01-15 07:34:55 +08:00
|
|
|
|
2013-01-22 18:53:11 +08:00
|
|
|
rc = mb86a20s_writeregdata(state, mb86a20s_reset_reception);
|
2013-01-22 23:28:31 +08:00
|
|
|
mb86a20s_reset_counters(fe);
|
2013-03-03 02:02:23 +08:00
|
|
|
mb86a20s_stats_not_ready(fe);
|
2013-01-15 07:34:55 +08:00
|
|
|
|
2013-01-22 18:53:11 +08:00
|
|
|
if (fe->ops.i2c_gate_ctrl)
|
|
|
|
fe->ops.i2c_gate_ctrl(fe, 1);
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2013-01-22 23:28:31 +08:00
|
|
|
static int mb86a20s_read_status_and_stats(struct dvb_frontend *fe,
|
2015-06-08 01:53:52 +08:00
|
|
|
enum fe_status *status)
|
2013-01-22 18:49:39 +08:00
|
|
|
{
|
2013-01-22 23:28:31 +08:00
|
|
|
struct mb86a20s_state *state = fe->demodulator_priv;
|
2013-03-02 20:06:17 +08:00
|
|
|
int rc, status_nr;
|
2013-01-22 18:49:39 +08:00
|
|
|
|
2013-01-22 23:28:31 +08:00
|
|
|
dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
|
2013-01-22 18:49:39 +08:00
|
|
|
|
|
|
|
if (fe->ops.i2c_gate_ctrl)
|
|
|
|
fe->ops.i2c_gate_ctrl(fe, 0);
|
|
|
|
|
2013-01-22 23:28:31 +08:00
|
|
|
/* Get lock */
|
2013-03-02 20:06:17 +08:00
|
|
|
status_nr = mb86a20s_read_status(fe, status);
|
|
|
|
if (status_nr < 7) {
|
2013-01-22 23:28:31 +08:00
|
|
|
mb86a20s_stats_not_ready(fe);
|
|
|
|
mb86a20s_reset_frontend_cache(fe);
|
|
|
|
}
|
2013-03-02 20:06:17 +08:00
|
|
|
if (status_nr < 0) {
|
2013-01-22 23:30:07 +08:00
|
|
|
dev_err(&state->i2c->dev,
|
|
|
|
"%s: Can't read frontend lock status\n", __func__);
|
2016-09-11 00:49:01 +08:00
|
|
|
rc = status_nr;
|
2013-01-22 23:28:31 +08:00
|
|
|
goto error;
|
2013-01-22 23:30:07 +08:00
|
|
|
}
|
2013-01-22 23:28:31 +08:00
|
|
|
|
|
|
|
/* Get signal strength */
|
|
|
|
rc = mb86a20s_read_signal_strength(fe);
|
|
|
|
if (rc < 0) {
|
2013-01-22 23:30:07 +08:00
|
|
|
dev_err(&state->i2c->dev,
|
|
|
|
"%s: Can't reset VBER registers.\n", __func__);
|
2013-01-22 23:28:31 +08:00
|
|
|
mb86a20s_stats_not_ready(fe);
|
|
|
|
mb86a20s_reset_frontend_cache(fe);
|
2013-01-22 23:30:07 +08:00
|
|
|
|
|
|
|
rc = 0; /* Status is OK */
|
2013-01-22 23:28:31 +08:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2013-03-02 20:06:17 +08:00
|
|
|
if (status_nr >= 7) {
|
2013-01-22 23:28:31 +08:00
|
|
|
/* Get TMCC info*/
|
|
|
|
rc = mb86a20s_get_frontend(fe);
|
2013-01-22 23:30:07 +08:00
|
|
|
if (rc < 0) {
|
|
|
|
dev_err(&state->i2c->dev,
|
|
|
|
"%s: Can't get FE TMCC data.\n", __func__);
|
|
|
|
rc = 0; /* Status is OK */
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get statistics */
|
2013-03-02 20:06:17 +08:00
|
|
|
rc = mb86a20s_get_stats(fe, status_nr);
|
2013-01-22 23:30:07 +08:00
|
|
|
if (rc < 0 && rc != -EBUSY) {
|
|
|
|
dev_err(&state->i2c->dev,
|
|
|
|
"%s: Can't get FE statistics.\n", __func__);
|
|
|
|
rc = 0;
|
2013-01-22 23:28:31 +08:00
|
|
|
goto error;
|
2013-01-22 23:30:07 +08:00
|
|
|
}
|
|
|
|
rc = 0; /* Don't return EBUSY to userspace */
|
2013-01-22 23:28:31 +08:00
|
|
|
}
|
2013-01-22 23:30:07 +08:00
|
|
|
goto ok;
|
2013-01-22 23:28:31 +08:00
|
|
|
|
2013-01-22 23:30:07 +08:00
|
|
|
error:
|
2013-01-22 23:28:31 +08:00
|
|
|
mb86a20s_stats_not_ready(fe);
|
2013-01-22 18:49:39 +08:00
|
|
|
|
2013-01-22 23:30:07 +08:00
|
|
|
ok:
|
2013-01-22 18:49:39 +08:00
|
|
|
if (fe->ops.i2c_gate_ctrl)
|
|
|
|
fe->ops.i2c_gate_ctrl(fe, 1);
|
2013-01-22 23:30:07 +08:00
|
|
|
|
2013-01-22 23:28:31 +08:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mb86a20s_read_signal_strength_from_cache(struct dvb_frontend *fe,
|
|
|
|
u16 *strength)
|
|
|
|
{
|
|
|
|
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
|
|
|
|
|
|
|
|
|
|
|
|
*strength = c->strength.stat[0].uvalue;
|
2013-01-22 18:49:39 +08:00
|
|
|
|
2013-01-22 23:28:31 +08:00
|
|
|
return 0;
|
2013-01-22 18:49:39 +08:00
|
|
|
}
|
|
|
|
|
2010-09-28 07:52:43 +08:00
|
|
|
static int mb86a20s_tune(struct dvb_frontend *fe,
|
2011-12-27 04:48:33 +08:00
|
|
|
bool re_tune,
|
2010-09-28 07:52:43 +08:00
|
|
|
unsigned int mode_flags,
|
|
|
|
unsigned int *delay,
|
2015-06-08 01:53:52 +08:00
|
|
|
enum fe_status *status)
|
2010-09-28 07:52:43 +08:00
|
|
|
{
|
2013-01-22 19:13:08 +08:00
|
|
|
struct mb86a20s_state *state = fe->demodulator_priv;
|
2010-09-28 07:52:43 +08:00
|
|
|
int rc = 0;
|
|
|
|
|
2013-01-22 19:13:08 +08:00
|
|
|
dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
|
2010-09-28 07:52:43 +08:00
|
|
|
|
2011-12-27 04:48:33 +08:00
|
|
|
if (re_tune)
|
2011-12-26 23:11:51 +08:00
|
|
|
rc = mb86a20s_set_frontend(fe);
|
2010-09-28 07:52:43 +08:00
|
|
|
|
|
|
|
if (!(mode_flags & FE_TUNE_MODE_ONESHOT))
|
2013-01-22 23:28:31 +08:00
|
|
|
mb86a20s_read_status_and_stats(fe, status);
|
2010-09-28 07:52:43 +08:00
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void mb86a20s_release(struct dvb_frontend *fe)
|
|
|
|
{
|
|
|
|
struct mb86a20s_state *state = fe->demodulator_priv;
|
|
|
|
|
2013-01-22 19:13:08 +08:00
|
|
|
dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
|
2010-09-28 07:52:43 +08:00
|
|
|
|
|
|
|
kfree(state);
|
|
|
|
}
|
|
|
|
|
2018-04-24 21:19:18 +08:00
|
|
|
static enum dvbfe_algo mb86a20s_get_frontend_algo(struct dvb_frontend *fe)
|
2016-09-04 21:16:18 +08:00
|
|
|
{
|
2018-01-04 19:47:28 +08:00
|
|
|
return DVBFE_ALGO_HW;
|
2016-09-04 21:16:18 +08:00
|
|
|
}
|
|
|
|
|
2016-08-10 05:32:21 +08:00
|
|
|
static const struct dvb_frontend_ops mb86a20s_ops;
|
2010-09-28 07:52:43 +08:00
|
|
|
|
|
|
|
struct dvb_frontend *mb86a20s_attach(const struct mb86a20s_config *config,
|
|
|
|
struct i2c_adapter *i2c)
|
|
|
|
{
|
2013-01-22 19:13:08 +08:00
|
|
|
struct mb86a20s_state *state;
|
2010-09-28 07:52:43 +08:00
|
|
|
u8 rev;
|
|
|
|
|
2013-01-23 23:22:22 +08:00
|
|
|
dev_dbg(&i2c->dev, "%s called.\n", __func__);
|
|
|
|
|
2010-09-28 07:52:43 +08:00
|
|
|
/* allocate memory for the internal state */
|
2017-08-28 17:55:16 +08:00
|
|
|
state = kzalloc(sizeof(*state), GFP_KERNEL);
|
2017-08-28 17:46:57 +08:00
|
|
|
if (!state)
|
2017-08-30 14:44:29 +08:00
|
|
|
return NULL;
|
2010-09-28 07:52:43 +08:00
|
|
|
|
|
|
|
/* setup the state */
|
|
|
|
state->config = config;
|
|
|
|
state->i2c = i2c;
|
|
|
|
|
|
|
|
/* create dvb_frontend */
|
|
|
|
memcpy(&state->frontend.ops, &mb86a20s_ops,
|
|
|
|
sizeof(struct dvb_frontend_ops));
|
|
|
|
state->frontend.demodulator_priv = state;
|
|
|
|
|
|
|
|
/* Check if it is a mb86a20s frontend */
|
|
|
|
rev = mb86a20s_readreg(state, 0);
|
2017-08-30 14:44:29 +08:00
|
|
|
if (rev != 0x13) {
|
|
|
|
kfree(state);
|
2013-01-23 23:22:22 +08:00
|
|
|
dev_dbg(&i2c->dev,
|
2013-01-22 19:13:08 +08:00
|
|
|
"Frontend revision %d is unknown - aborting.\n",
|
2010-09-28 07:52:43 +08:00
|
|
|
rev);
|
2017-08-30 14:44:29 +08:00
|
|
|
return NULL;
|
2010-09-28 07:52:43 +08:00
|
|
|
}
|
|
|
|
|
2017-08-30 14:44:29 +08:00
|
|
|
dev_info(&i2c->dev, "Detected a Fujitsu mb86a20s frontend\n");
|
2010-09-28 07:52:43 +08:00
|
|
|
return &state->frontend;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(mb86a20s_attach);
|
|
|
|
|
2016-08-10 05:32:21 +08:00
|
|
|
static const struct dvb_frontend_ops mb86a20s_ops = {
|
2011-12-26 23:11:51 +08:00
|
|
|
.delsys = { SYS_ISDBT },
|
2010-09-28 07:52:43 +08:00
|
|
|
/* Use dib8000 values per default */
|
|
|
|
.info = {
|
|
|
|
.name = "Fujitsu mb86A20s",
|
2013-03-04 18:10:06 +08:00
|
|
|
.caps = FE_CAN_RECOVER |
|
2010-09-28 07:52:43 +08:00
|
|
|
FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
|
|
|
|
FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
|
|
|
|
FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 |
|
|
|
|
FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_QAM_AUTO |
|
|
|
|
FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_HIERARCHY_AUTO,
|
|
|
|
/* Actually, those values depend on the used tuner */
|
2018-07-06 06:59:36 +08:00
|
|
|
.frequency_min_hz = 45 * MHz,
|
|
|
|
.frequency_max_hz = 864 * MHz,
|
|
|
|
.frequency_stepsize_hz = 62500,
|
2010-09-28 07:52:43 +08:00
|
|
|
},
|
|
|
|
|
|
|
|
.release = mb86a20s_release,
|
|
|
|
|
|
|
|
.init = mb86a20s_initfe,
|
2011-12-26 23:11:51 +08:00
|
|
|
.set_frontend = mb86a20s_set_frontend,
|
2013-01-22 23:28:31 +08:00
|
|
|
.read_status = mb86a20s_read_status_and_stats,
|
|
|
|
.read_signal_strength = mb86a20s_read_signal_strength_from_cache,
|
2010-09-28 07:52:43 +08:00
|
|
|
.tune = mb86a20s_tune,
|
2016-09-04 21:16:18 +08:00
|
|
|
.get_frontend_algo = mb86a20s_get_frontend_algo,
|
2010-09-28 07:52:43 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
MODULE_DESCRIPTION("DVB Frontend module for Fujitsu mb86A20s hardware");
|
2014-02-07 18:03:07 +08:00
|
|
|
MODULE_AUTHOR("Mauro Carvalho Chehab");
|
2010-09-28 07:52:43 +08:00
|
|
|
MODULE_LICENSE("GPL");
|