2010-09-28 06:52:43 +07:00
|
|
|
/*
|
|
|
|
* Fujitu mb86a20s ISDB-T/ISDB-Tsb Module driver
|
|
|
|
*
|
2013-01-14 19:26:09 +07:00
|
|
|
* Copyright (C) 2010-2013 Mauro Carvalho Chehab <mchehab@redhat.com>
|
2010-09-28 06:52:43 +07:00
|
|
|
* Copyright (C) 2009-2010 Douglas Landgraf <dougsland@redhat.com>
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License as
|
|
|
|
* published by the Free Software Foundation version 2.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* General Public License for more details.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <asm/div64.h>
|
|
|
|
|
|
|
|
#include "dvb_frontend.h"
|
|
|
|
#include "mb86a20s.h"
|
|
|
|
|
|
|
|
static int debug = 1;
|
|
|
|
module_param(debug, int, 0644);
|
|
|
|
MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)");
|
|
|
|
|
|
|
|
struct mb86a20s_state {
|
|
|
|
struct i2c_adapter *i2c;
|
|
|
|
const struct mb86a20s_config *config;
|
2013-01-22 22:28:31 +07:00
|
|
|
u32 last_frequency;
|
2010-09-28 06:52:43 +07: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 21:10:05 +07:00
|
|
|
|
2013-01-15 06:34:55 +07:00
|
|
|
u32 estimated_rate[3];
|
|
|
|
|
[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 21:10:05 +07:00
|
|
|
bool need_init;
|
2010-09-28 06:52:43 +07:00
|
|
|
};
|
|
|
|
|
|
|
|
struct regdata {
|
|
|
|
u8 reg;
|
|
|
|
u8 data;
|
|
|
|
};
|
|
|
|
|
2013-01-15 06:34:55 +07:00
|
|
|
#define BER_SAMPLING_RATE 1 /* Seconds */
|
|
|
|
|
2010-09-28 06:52:43 +07:00
|
|
|
/*
|
|
|
|
* Initialization sequence: Use whatevere default values that PV SBTVD
|
|
|
|
* does on its initialisation, obtained via USB snoop
|
|
|
|
*/
|
|
|
|
static struct regdata mb86a20s_init[] = {
|
|
|
|
{ 0x70, 0x0f },
|
|
|
|
{ 0x70, 0xff },
|
|
|
|
{ 0x08, 0x01 },
|
|
|
|
{ 0x09, 0x3e },
|
2012-01-11 19:56:30 +07:00
|
|
|
{ 0x50, 0xd1 }, { 0x51, 0x22 },
|
2010-09-28 06:52:43 +07:00
|
|
|
{ 0x39, 0x01 },
|
|
|
|
{ 0x71, 0x00 },
|
2012-01-11 19:56:30 +07:00
|
|
|
{ 0x28, 0x2a }, { 0x29, 0x00 }, { 0x2a, 0xff }, { 0x2b, 0x80 },
|
|
|
|
{ 0x28, 0x20 }, { 0x29, 0x33 }, { 0x2a, 0xdf }, { 0x2b, 0xa9 },
|
mb86a20s: Add a few more register settings at the init seq
Some time ago, Manoel sent us a patch adding more stuff
to the init sequence. However, his patch were also doing
non-related stuff, by changing the init logic without
any good reason. So, it was asked for him to submit a
patch with just the data that has changed, in order to
allow us to better analyze it.
As he didn't what it was requested, I finally found some
time to dig into his init sequence and add it here.
Basically, new stuff is added there. There are a few changes:
1) The removal of the extra (duplicated) logic that puts
the chip into the serial mode;
2) Some Viterbi VBER measurement init data was changed from
0x00 to 0xff for layer A, to match what was done for
layers B and C.
None of those caused any regressions and both make sense
on my eyes.
The other parameters additions actually increased the
tuning quality for some channels. Yet, some channels that
were previously discovered with scan disappered, while
others appeared instead. This were tested in Brasilia,
with an external antena.
At the overall, it is now a little better. So, better to
add these, and then try to figure out a configuration that
would get even better scanning results.
Reported-by: Manoel Pinheiro <pinusdtv@hotmail.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
2012-01-11 20:00:28 +07:00
|
|
|
{ 0x28, 0x22 }, { 0x29, 0x00 }, { 0x2a, 0x1f }, { 0x2b, 0xf0 },
|
2010-09-28 06:52:43 +07:00
|
|
|
{ 0x3b, 0x21 },
|
|
|
|
{ 0x3c, 0x3a },
|
|
|
|
{ 0x01, 0x0d },
|
2012-01-11 19:56:30 +07:00
|
|
|
{ 0x04, 0x08 }, { 0x05, 0x05 },
|
|
|
|
{ 0x04, 0x0e }, { 0x05, 0x00 },
|
|
|
|
{ 0x04, 0x0f }, { 0x05, 0x14 },
|
|
|
|
{ 0x04, 0x0b }, { 0x05, 0x8c },
|
|
|
|
{ 0x04, 0x00 }, { 0x05, 0x00 },
|
|
|
|
{ 0x04, 0x01 }, { 0x05, 0x07 },
|
|
|
|
{ 0x04, 0x02 }, { 0x05, 0x0f },
|
|
|
|
{ 0x04, 0x03 }, { 0x05, 0xa0 },
|
|
|
|
{ 0x04, 0x09 }, { 0x05, 0x00 },
|
|
|
|
{ 0x04, 0x0a }, { 0x05, 0xff },
|
|
|
|
{ 0x04, 0x27 }, { 0x05, 0x64 },
|
|
|
|
{ 0x04, 0x28 }, { 0x05, 0x00 },
|
|
|
|
{ 0x04, 0x1e }, { 0x05, 0xff },
|
|
|
|
{ 0x04, 0x29 }, { 0x05, 0x0a },
|
|
|
|
{ 0x04, 0x32 }, { 0x05, 0x0a },
|
|
|
|
{ 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 },
|
mb86a20s: Add a few more register settings at the init seq
Some time ago, Manoel sent us a patch adding more stuff
to the init sequence. However, his patch were also doing
non-related stuff, by changing the init logic without
any good reason. So, it was asked for him to submit a
patch with just the data that has changed, in order to
allow us to better analyze it.
As he didn't what it was requested, I finally found some
time to dig into his init sequence and add it here.
Basically, new stuff is added there. There are a few changes:
1) The removal of the extra (duplicated) logic that puts
the chip into the serial mode;
2) Some Viterbi VBER measurement init data was changed from
0x00 to 0xff for layer A, to match what was done for
layers B and C.
None of those caused any regressions and both make sense
on my eyes.
The other parameters additions actually increased the
tuning quality for some channels. Yet, some channels that
were previously discovered with scan disappered, while
others appeared instead. This were tested in Brasilia,
with an external antena.
At the overall, it is now a little better. So, better to
add these, and then try to figure out a configuration that
would get even better scanning results.
Reported-by: Manoel Pinheiro <pinusdtv@hotmail.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
2012-01-11 20:00:28 +07:00
|
|
|
{ 0x04, 0x15 }, { 0x05, 0x4e },
|
|
|
|
{ 0x04, 0x16 }, { 0x05, 0x20 },
|
2013-01-22 22:28:31 +07: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 06:34:55 +07:00
|
|
|
* will be adjusted later in the driver's code.
|
2013-01-22 22:28:31 +07:00
|
|
|
*/
|
|
|
|
{ 0x52, 0x01 }, /* Turn on BER before Viterbi */
|
|
|
|
{ 0x50, 0xa7 }, { 0x51, 0x00 },
|
2012-01-11 19:56:30 +07:00
|
|
|
{ 0x50, 0xa8 }, { 0x51, 0xff },
|
|
|
|
{ 0x50, 0xa9 }, { 0x51, 0xff },
|
2013-01-22 22:28:31 +07:00
|
|
|
{ 0x50, 0xaa }, { 0x51, 0x00 },
|
2012-01-11 19:56:30 +07:00
|
|
|
{ 0x50, 0xab }, { 0x51, 0xff },
|
|
|
|
{ 0x50, 0xac }, { 0x51, 0xff },
|
2013-01-22 22:28:31 +07:00
|
|
|
{ 0x50, 0xad }, { 0x51, 0x00 },
|
2012-01-11 19:56:30 +07:00
|
|
|
{ 0x50, 0xae }, { 0x51, 0xff },
|
|
|
|
{ 0x50, 0xaf }, { 0x51, 0xff },
|
2013-01-22 22:28:31 +07:00
|
|
|
|
|
|
|
{ 0x5e, 0x00 }, /* Turn off BER after Viterbi */
|
2012-01-11 19:56:30 +07:00
|
|
|
{ 0x50, 0xdc }, { 0x51, 0x01 },
|
|
|
|
{ 0x50, 0xdd }, { 0x51, 0xf4 },
|
|
|
|
{ 0x50, 0xde }, { 0x51, 0x01 },
|
|
|
|
{ 0x50, 0xdf }, { 0x51, 0xf4 },
|
|
|
|
{ 0x50, 0xe0 }, { 0x51, 0x01 },
|
|
|
|
{ 0x50, 0xe1 }, { 0x51, 0xf4 },
|
|
|
|
{ 0x50, 0xb0 }, { 0x51, 0x07 },
|
|
|
|
{ 0x50, 0xb2 }, { 0x51, 0xff },
|
|
|
|
{ 0x50, 0xb3 }, { 0x51, 0xff },
|
|
|
|
{ 0x50, 0xb4 }, { 0x51, 0xff },
|
|
|
|
{ 0x50, 0xb5 }, { 0x51, 0xff },
|
|
|
|
{ 0x50, 0xb6 }, { 0x51, 0xff },
|
|
|
|
{ 0x50, 0xb7 }, { 0x51, 0xff },
|
|
|
|
{ 0x50, 0x50 }, { 0x51, 0x02 },
|
2013-01-22 22:28:31 +07:00
|
|
|
{ 0x50, 0x51 }, { 0x51, 0x04 }, /* MER symbol 4 */
|
|
|
|
{ 0x45, 0x04 }, /* CN symbol 4 */
|
2010-09-28 06:52:43 +07:00
|
|
|
{ 0x48, 0x04 },
|
2012-01-11 19:56:30 +07:00
|
|
|
{ 0x50, 0xd5 }, { 0x51, 0x01 }, /* Serial */
|
|
|
|
{ 0x50, 0xd6 }, { 0x51, 0x1f },
|
|
|
|
{ 0x50, 0xd2 }, { 0x51, 0x03 },
|
|
|
|
{ 0x50, 0xd7 }, { 0x51, 0x3f },
|
mb86a20s: Add a few more register settings at the init seq
Some time ago, Manoel sent us a patch adding more stuff
to the init sequence. However, his patch were also doing
non-related stuff, by changing the init logic without
any good reason. So, it was asked for him to submit a
patch with just the data that has changed, in order to
allow us to better analyze it.
As he didn't what it was requested, I finally found some
time to dig into his init sequence and add it here.
Basically, new stuff is added there. There are a few changes:
1) The removal of the extra (duplicated) logic that puts
the chip into the serial mode;
2) Some Viterbi VBER measurement init data was changed from
0x00 to 0xff for layer A, to match what was done for
layers B and C.
None of those caused any regressions and both make sense
on my eyes.
The other parameters additions actually increased the
tuning quality for some channels. Yet, some channels that
were previously discovered with scan disappered, while
others appeared instead. This were tested in Brasilia,
with an external antena.
At the overall, it is now a little better. So, better to
add these, and then try to figure out a configuration that
would get even better scanning results.
Reported-by: Manoel Pinheiro <pinusdtv@hotmail.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
2012-01-11 20:00:28 +07:00
|
|
|
{ 0x28, 0x74 }, { 0x29, 0x00 }, { 0x28, 0x74 }, { 0x29, 0x40 },
|
|
|
|
{ 0x28, 0x46 }, { 0x29, 0x2c }, { 0x28, 0x46 }, { 0x29, 0x0c },
|
2013-01-15 00:12:10 +07:00
|
|
|
|
|
|
|
{ 0x04, 0x40 }, { 0x05, 0x00 },
|
mb86a20s: Add a few more register settings at the init seq
Some time ago, Manoel sent us a patch adding more stuff
to the init sequence. However, his patch were also doing
non-related stuff, by changing the init logic without
any good reason. So, it was asked for him to submit a
patch with just the data that has changed, in order to
allow us to better analyze it.
As he didn't what it was requested, I finally found some
time to dig into his init sequence and add it here.
Basically, new stuff is added there. There are a few changes:
1) The removal of the extra (duplicated) logic that puts
the chip into the serial mode;
2) Some Viterbi VBER measurement init data was changed from
0x00 to 0xff for layer A, to match what was done for
layers B and C.
None of those caused any regressions and both make sense
on my eyes.
The other parameters additions actually increased the
tuning quality for some channels. Yet, some channels that
were previously discovered with scan disappered, while
others appeared instead. This were tested in Brasilia,
with an external antena.
At the overall, it is now a little better. So, better to
add these, and then try to figure out a configuration that
would get even better scanning results.
Reported-by: Manoel Pinheiro <pinusdtv@hotmail.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
2012-01-11 20:00:28 +07:00
|
|
|
{ 0x28, 0x00 }, { 0x29, 0x10 },
|
|
|
|
{ 0x28, 0x05 }, { 0x29, 0x02 },
|
2010-09-28 06:52:43 +07:00
|
|
|
{ 0x1c, 0x01 },
|
2012-01-11 19:56:30 +07: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 },
|
|
|
|
{ 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 },
|
mb86a20s: Add a few more register settings at the init seq
Some time ago, Manoel sent us a patch adding more stuff
to the init sequence. However, his patch were also doing
non-related stuff, by changing the init logic without
any good reason. So, it was asked for him to submit a
patch with just the data that has changed, in order to
allow us to better analyze it.
As he didn't what it was requested, I finally found some
time to dig into his init sequence and add it here.
Basically, new stuff is added there. There are a few changes:
1) The removal of the extra (duplicated) logic that puts
the chip into the serial mode;
2) Some Viterbi VBER measurement init data was changed from
0x00 to 0xff for layer A, to match what was done for
layers B and C.
None of those caused any regressions and both make sense
on my eyes.
The other parameters additions actually increased the
tuning quality for some channels. Yet, some channels that
were previously discovered with scan disappered, while
others appeared instead. This were tested in Brasilia,
with an external antena.
At the overall, it is now a little better. So, better to
add these, and then try to figure out a configuration that
would get even better scanning results.
Reported-by: Manoel Pinheiro <pinusdtv@hotmail.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
2012-01-11 20:00:28 +07:00
|
|
|
{ 0x28, 0x6a }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x00 },
|
2010-09-28 06:52:43 +07:00
|
|
|
{ 0xd0, 0x00 },
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct regdata mb86a20s_reset_reception[] = {
|
|
|
|
{ 0x70, 0xf0 },
|
|
|
|
{ 0x70, 0xff },
|
|
|
|
{ 0x08, 0x01 },
|
|
|
|
{ 0x08, 0x00 },
|
|
|
|
};
|
|
|
|
|
2013-01-22 22:28:31 +07:00
|
|
|
static struct regdata mb86a20s_vber_reset[] = {
|
|
|
|
{ 0x53, 0x00 }, /* VBER Counter reset */
|
|
|
|
{ 0x53, 0x07 },
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct regdata mb86a20s_per_reset[] = {
|
|
|
|
{ 0x50, 0xb1 }, /* PER Counter reset */
|
|
|
|
{ 0x51, 0x07 },
|
|
|
|
{ 0x51, 0x00 },
|
|
|
|
};
|
|
|
|
|
2013-01-22 17:53:11 +07:00
|
|
|
/*
|
|
|
|
* I2C read/write functions and macros
|
|
|
|
*/
|
|
|
|
|
2010-09-28 06:52:43 +07:00
|
|
|
static int mb86a20s_i2c_writereg(struct mb86a20s_state *state,
|
2013-01-22 22:28:31 +07:00
|
|
|
u8 i2c_addr, u8 reg, u8 data)
|
2010-09-28 06:52:43 +07: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 18:13:08 +07: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 06:52:43 +07: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 18:13:08 +07:00
|
|
|
dev_err(&state->i2c->dev, "%s: reg=0x%x (error=%d)\n",
|
|
|
|
__func__, reg, rc);
|
|
|
|
return (rc < 0) ? rc : -EIO;
|
2010-09-28 06:52:43 +07: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 22:28:31 +07:00
|
|
|
/*
|
|
|
|
* Ancillary internal routines (likely compiled inlined)
|
|
|
|
*
|
|
|
|
* The functions below assume that gateway lock has already obtained
|
|
|
|
*/
|
|
|
|
|
2013-01-22 17:53:11 +07:00
|
|
|
static int mb86a20s_read_status(struct dvb_frontend *fe, fe_status_t *status)
|
2010-09-28 06:52:43 +07:00
|
|
|
{
|
|
|
|
struct mb86a20s_state *state = fe->demodulator_priv;
|
2013-01-22 17:53:11 +07:00
|
|
|
int val;
|
2010-09-28 06:52:43 +07:00
|
|
|
|
2013-01-22 17:53:11 +07:00
|
|
|
*status = 0;
|
2010-09-28 06:52:43 +07:00
|
|
|
|
2013-01-22 17:53:11 +07:00
|
|
|
val = mb86a20s_readreg(state, 0x0a) & 0xf;
|
|
|
|
if (val < 0)
|
|
|
|
return val;
|
2010-10-03 15:27:59 +07:00
|
|
|
|
2013-01-22 17:53:11 +07:00
|
|
|
if (val >= 2)
|
|
|
|
*status |= FE_HAS_SIGNAL;
|
2010-09-28 06:52:43 +07:00
|
|
|
|
2013-01-22 17:53:11 +07:00
|
|
|
if (val >= 4)
|
|
|
|
*status |= FE_HAS_CARRIER;
|
2010-10-04 02:48:49 +07:00
|
|
|
|
2013-01-22 17:53:11 +07:00
|
|
|
if (val >= 5)
|
|
|
|
*status |= FE_HAS_VITERBI;
|
2010-10-04 02:48:49 +07:00
|
|
|
|
2013-01-22 17:53:11 +07:00
|
|
|
if (val >= 7)
|
|
|
|
*status |= FE_HAS_SYNC;
|
2010-10-03 15:27:59 +07:00
|
|
|
|
2013-01-22 17:53:11 +07:00
|
|
|
if (val >= 8) /* Maybe 9? */
|
|
|
|
*status |= FE_HAS_LOCK;
|
|
|
|
|
2013-01-22 18:13:08 +07:00
|
|
|
dev_dbg(&state->i2c->dev, "%s: Status = 0x%02x (state = %d)\n",
|
|
|
|
__func__, *status, val);
|
2013-01-22 17:53:11 +07:00
|
|
|
|
|
|
|
return 0;
|
2010-09-28 06:52:43 +07:00
|
|
|
}
|
|
|
|
|
2013-01-22 22:28:31 +07:00
|
|
|
static int mb86a20s_read_signal_strength(struct dvb_frontend *fe)
|
2010-09-28 06:52:43 +07:00
|
|
|
{
|
|
|
|
struct mb86a20s_state *state = fe->demodulator_priv;
|
2013-01-22 22:28:31 +07:00
|
|
|
int rc;
|
2010-09-28 06:52:43 +07:00
|
|
|
unsigned rf_max, rf_min, rf;
|
2010-10-03 15:27:59 +07:00
|
|
|
|
2010-09-28 06:52:43 +07: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 22:28:31 +07: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;
|
|
|
|
rc = mb86a20s_writereg(state, 0x04, rf);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
2010-09-28 06:52:43 +07:00
|
|
|
|
2013-01-22 22:28:31 +07:00
|
|
|
rc = mb86a20s_readreg(state, 0x02);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
if (rc & 0x08)
|
2010-09-28 06:52:43 +07: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 22:28:31 +07:00
|
|
|
rf = (rf_max + rf_min) / 2;
|
|
|
|
|
|
|
|
/* Rescale it from 2^12 (4096) to 2^16 */
|
|
|
|
rf <<= (16 - 12);
|
2013-01-22 18:13:08 +07:00
|
|
|
dev_dbg(&state->i2c->dev,
|
|
|
|
"%s: signal strength = %d (%d < RF=%d < %d)\n",
|
|
|
|
__func__, rf, rf_min, rf >> 4, rf_max);
|
2013-01-22 22:28:31 +07:00
|
|
|
return rf;
|
2010-09-28 06:52:43 +07:00
|
|
|
}
|
|
|
|
} while (1);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-01-10 03:16:36 +07: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 12:28:34 +07:00
|
|
|
if (layer >= ARRAY_SIZE(reg))
|
2012-01-10 03:16:36 +07: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 22:31:13 +07:00
|
|
|
switch ((rc >> 4) & 0x07) {
|
2012-01-10 03:16:36 +07: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 12:28:34 +07:00
|
|
|
if (layer >= ARRAY_SIZE(reg))
|
2012-01-10 03:16:36 +07: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 22:31:13 +07:00
|
|
|
switch ((rc >> 4) & 0x07) {
|
2012-01-10 03:16:36 +07: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;
|
|
|
|
|
|
|
|
static unsigned char reg[] = {
|
|
|
|
[0] = 0x88, /* Layer A */
|
|
|
|
[1] = 0x8c, /* Layer B */
|
|
|
|
[2] = 0x90, /* Layer C */
|
|
|
|
};
|
|
|
|
|
2012-01-13 12:28:34 +07:00
|
|
|
if (layer >= ARRAY_SIZE(reg))
|
2012-01-10 03:16:36 +07: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 22:31:13 +07:00
|
|
|
|
|
|
|
switch ((rc >> 4) & 0x07) {
|
|
|
|
case 1:
|
|
|
|
return GUARD_INTERVAL_1_4;
|
|
|
|
case 2:
|
|
|
|
return GUARD_INTERVAL_1_8;
|
|
|
|
case 3:
|
|
|
|
return GUARD_INTERVAL_1_16;
|
|
|
|
case 4:
|
|
|
|
return GUARD_INTERVAL_1_32;
|
|
|
|
|
|
|
|
default:
|
|
|
|
case 0:
|
|
|
|
return GUARD_INTERVAL_AUTO;
|
|
|
|
}
|
2012-01-10 03:16:36 +07: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 18:13:08 +07:00
|
|
|
dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
|
|
|
|
|
2012-01-13 12:28:34 +07:00
|
|
|
if (layer >= ARRAY_SIZE(reg))
|
2012-01-10 03:16:36 +07:00
|
|
|
return -EINVAL;
|
2013-01-22 18:13:08 +07:00
|
|
|
|
2012-01-10 03:16:36 +07: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 18:13:08 +07:00
|
|
|
dev_dbg(&state->i2c->dev, "%s: segments: %d.\n", __func__, count);
|
|
|
|
|
2012-01-10 03:16:36 +07:00
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
2013-01-14 19:26:09 +07:00
|
|
|
static void mb86a20s_reset_frontend_cache(struct dvb_frontend *fe)
|
|
|
|
{
|
2013-01-22 18:13:08 +07:00
|
|
|
struct mb86a20s_state *state = fe->demodulator_priv;
|
2013-01-14 19:26:09 +07:00
|
|
|
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
|
|
|
|
|
2013-01-22 18:13:08 +07:00
|
|
|
dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
|
|
|
|
|
2013-01-14 19:26:09 +07: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 06:34:55 +07: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,
|
|
|
|
u32 modulation, u32 fec, u32 interleaving,
|
|
|
|
u32 segment)
|
|
|
|
{
|
|
|
|
struct mb86a20s_state *state = fe->demodulator_priv;
|
|
|
|
u32 rate;
|
|
|
|
int m, f, i;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If modulation/fec/interleaving is not detected, the default is
|
|
|
|
* to consider the lowest bit rate, to avoid taking too long time
|
|
|
|
* to get BER.
|
|
|
|
*/
|
|
|
|
switch (modulation) {
|
|
|
|
case DQPSK:
|
|
|
|
case QPSK:
|
|
|
|
default:
|
|
|
|
m = 0;
|
|
|
|
break;
|
|
|
|
case QAM_16:
|
|
|
|
m = 1;
|
|
|
|
break;
|
|
|
|
case QAM_64:
|
|
|
|
m = 2;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (fec) {
|
|
|
|
default:
|
|
|
|
case FEC_1_2:
|
|
|
|
case FEC_AUTO:
|
|
|
|
f = 0;
|
|
|
|
break;
|
|
|
|
case FEC_2_3:
|
|
|
|
f = 1;
|
|
|
|
break;
|
|
|
|
case FEC_3_4:
|
|
|
|
f = 2;
|
|
|
|
break;
|
|
|
|
case FEC_5_6:
|
|
|
|
f = 3;
|
|
|
|
break;
|
|
|
|
case FEC_7_8:
|
|
|
|
f = 4;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (interleaving) {
|
|
|
|
default:
|
|
|
|
case GUARD_INTERVAL_1_4:
|
|
|
|
i = 0;
|
|
|
|
break;
|
|
|
|
case GUARD_INTERVAL_1_8:
|
|
|
|
i = 1;
|
|
|
|
break;
|
|
|
|
case GUARD_INTERVAL_1_16:
|
|
|
|
i = 2;
|
|
|
|
break;
|
|
|
|
case GUARD_INTERVAL_1_32:
|
|
|
|
i = 3;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Samples BER at BER_SAMPLING_RATE seconds */
|
|
|
|
rate = isdbt_rate[m][f][i] * segment * BER_SAMPLING_RATE;
|
|
|
|
|
|
|
|
/* 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",
|
|
|
|
__func__, 'A' + layer, segment * isdbt_rate[m][f][i]/1000,
|
|
|
|
rate, rate);
|
|
|
|
|
|
|
|
state->estimated_rate[i] = rate;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
[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 21:30:21 +07:00
|
|
|
static int mb86a20s_get_frontend(struct dvb_frontend *fe)
|
2010-09-28 06:52:43 +07:00
|
|
|
{
|
2012-01-10 03:16:36 +07:00
|
|
|
struct mb86a20s_state *state = fe->demodulator_priv;
|
2013-01-14 19:26:09 +07:00
|
|
|
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
|
2012-01-10 03:16:36 +07:00
|
|
|
int i, rc;
|
2010-09-28 06:52:43 +07:00
|
|
|
|
2013-01-22 18:13:08 +07:00
|
|
|
dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
|
|
|
|
|
2013-01-14 19:26:09 +07:00
|
|
|
/* Reset frontend cache to default values */
|
|
|
|
mb86a20s_reset_frontend_cache(fe);
|
2012-01-10 03:16:36 +07:00
|
|
|
|
|
|
|
/* Check for partial reception */
|
|
|
|
rc = mb86a20s_writereg(state, 0x6d, 0x85);
|
2013-01-14 19:26:09 +07: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 03:16:36 +07:00
|
|
|
|
|
|
|
/* Get per-layer data */
|
2013-01-14 19:26:09 +07:00
|
|
|
|
2012-01-10 03:16:36 +07:00
|
|
|
for (i = 0; i < 3; i++) {
|
2013-01-22 18:13:08 +07:00
|
|
|
dev_dbg(&state->i2c->dev, "%s: getting data for layer %c.\n",
|
|
|
|
__func__, 'A' + i);
|
|
|
|
|
2012-01-10 03:16:36 +07:00
|
|
|
rc = mb86a20s_get_segment_count(state, i);
|
2013-01-14 19:26:09 +07:00
|
|
|
if (rc < 0)
|
2013-01-22 18:13:08 +07:00
|
|
|
goto noperlayer_error;
|
2013-01-15 06:34:55 +07:00
|
|
|
if (rc >= 0 && rc < 14) {
|
2013-01-14 19:26:09 +07:00
|
|
|
c->layer[i].segment_count = rc;
|
2013-01-15 06:34:55 +07:00
|
|
|
} else {
|
2013-01-14 19:26:09 +07:00
|
|
|
c->layer[i].segment_count = 0;
|
2013-01-15 06:34:55 +07:00
|
|
|
state->estimated_rate[i] = 0;
|
2012-01-10 03:16:36 +07:00
|
|
|
continue;
|
2013-01-14 19:26:09 +07:00
|
|
|
}
|
|
|
|
c->isdbt_layer_enabled |= 1 << i;
|
2012-01-10 03:16:36 +07:00
|
|
|
rc = mb86a20s_get_modulation(state, i);
|
2013-01-14 19:26:09 +07:00
|
|
|
if (rc < 0)
|
2013-01-22 18:13:08 +07:00
|
|
|
goto noperlayer_error;
|
|
|
|
dev_dbg(&state->i2c->dev, "%s: modulation %d.\n",
|
|
|
|
__func__, rc);
|
2013-01-14 19:26:09 +07:00
|
|
|
c->layer[i].modulation = rc;
|
2012-01-10 03:16:36 +07:00
|
|
|
rc = mb86a20s_get_fec(state, i);
|
2013-01-14 19:26:09 +07:00
|
|
|
if (rc < 0)
|
2013-01-22 18:13:08 +07:00
|
|
|
goto noperlayer_error;
|
|
|
|
dev_dbg(&state->i2c->dev, "%s: FEC %d.\n",
|
|
|
|
__func__, rc);
|
2013-01-14 19:26:09 +07:00
|
|
|
c->layer[i].fec = rc;
|
2012-01-10 03:16:36 +07:00
|
|
|
rc = mb86a20s_get_interleaving(state, i);
|
2013-01-14 19:26:09 +07:00
|
|
|
if (rc < 0)
|
2013-01-22 18:13:08 +07:00
|
|
|
goto noperlayer_error;
|
|
|
|
dev_dbg(&state->i2c->dev, "%s: interleaving %d.\n",
|
|
|
|
__func__, rc);
|
2013-01-14 19:26:09 +07:00
|
|
|
c->layer[i].interleaving = rc;
|
2013-01-15 06:34:55 +07:00
|
|
|
mb86a20s_layer_bitrate(fe, i, c->layer[i].modulation,
|
|
|
|
c->layer[i].fec,
|
|
|
|
c->layer[i].interleaving,
|
|
|
|
c->layer[i].segment_count);
|
2012-01-10 03:16:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
rc = mb86a20s_writereg(state, 0x6d, 0x84);
|
2013-01-14 19:26:09 +07:00
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
if ((rc & 0x60) == 0x20) {
|
|
|
|
c->isdbt_sb_mode = 1;
|
2012-01-10 03:16:36 +07:00
|
|
|
/* At least, one segment should exist */
|
2013-01-14 19:26:09 +07:00
|
|
|
if (!c->isdbt_sb_segment_count)
|
|
|
|
c->isdbt_sb_segment_count = 1;
|
|
|
|
}
|
2012-01-10 03:16:36 +07:00
|
|
|
|
|
|
|
/* Get transmission mode and guard interval */
|
|
|
|
rc = mb86a20s_readreg(state, 0x07);
|
2013-01-14 19:26:09 +07:00
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
if ((rc & 0x60) == 0x20) {
|
|
|
|
switch (rc & 0x0c >> 2) {
|
|
|
|
case 0:
|
|
|
|
c->transmission_mode = TRANSMISSION_MODE_2K;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
c->transmission_mode = TRANSMISSION_MODE_4K;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
c->transmission_mode = TRANSMISSION_MODE_8K;
|
|
|
|
break;
|
2012-01-10 03:16:36 +07:00
|
|
|
}
|
2013-01-14 19:26:09 +07:00
|
|
|
}
|
|
|
|
if (!(rc & 0x10)) {
|
|
|
|
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 03:16:36 +07:00
|
|
|
}
|
|
|
|
}
|
2013-01-22 22:28:31 +07:00
|
|
|
return 0;
|
2012-01-10 03:16:36 +07:00
|
|
|
|
2013-01-22 18:13:08 +07:00
|
|
|
noperlayer_error:
|
2010-09-28 06:52:43 +07:00
|
|
|
|
2013-01-22 22:28:31 +07: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->strength, 0, sizeof(c->strength));
|
|
|
|
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));
|
|
|
|
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 */
|
|
|
|
|
|
|
|
/* BER counter reset */
|
|
|
|
rc = mb86a20s_writeregdata(state, mb86a20s_vber_reset);
|
|
|
|
if (rc < 0)
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
/* MER, PER counter reset */
|
|
|
|
rc = mb86a20s_writeregdata(state, mb86a20s_per_reset);
|
|
|
|
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 22:30:07 +07:00
|
|
|
goto ok;
|
2013-01-22 22:28:31 +07:00
|
|
|
err:
|
2013-01-22 22:30:07 +07:00
|
|
|
dev_err(&state->i2c->dev,
|
|
|
|
"%s: Can't reset FE statistics (error %d).\n",
|
|
|
|
__func__, rc);
|
|
|
|
ok:
|
2013-01-14 19:26:09 +07:00
|
|
|
return rc;
|
2013-01-22 22:28:31 +07:00
|
|
|
}
|
|
|
|
|
2013-01-22 22:30:07 +07:00
|
|
|
static int mb86a20s_get_ber_before_vterbi(struct dvb_frontend *fe,
|
|
|
|
unsigned layer,
|
|
|
|
u32 *error, u32 *count)
|
|
|
|
{
|
|
|
|
struct mb86a20s_state *state = fe->demodulator_priv;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
|
|
|
|
|
|
|
|
if (layer >= 3)
|
|
|
|
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,
|
|
|
|
"%s: BER for layer %c is not available yet.\n",
|
|
|
|
__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 06:34:55 +07: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,
|
|
|
|
"%s: updating layer %c counter to %d.\n",
|
|
|
|
__func__, 'A' + layer, state->estimated_rate[layer]);
|
|
|
|
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-22 22:30:07 +07:00
|
|
|
/* Reset counter to collect new data */
|
|
|
|
rc = mb86a20s_writereg(state, 0x53, 0x07 & ~(1 << layer));
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
rc = mb86a20s_writereg(state, 0x53, 0x07);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-01-22 22:28:31 +07: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;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
|
2013-01-14 19:26:09 +07:00
|
|
|
|
2013-01-22 22:28:31 +07:00
|
|
|
/* Fill the length of each status counter */
|
|
|
|
|
|
|
|
/* Only global stats */
|
|
|
|
c->strength.len = 1;
|
|
|
|
|
|
|
|
/* Per-layer stats - 3 layers + global */
|
|
|
|
c->cnr.len = 4;
|
|
|
|
c->pre_bit_error.len = 4;
|
|
|
|
c->pre_bit_count.len = 4;
|
|
|
|
c->block_error.len = 4;
|
|
|
|
c->block_count.len = 4;
|
|
|
|
|
|
|
|
/* 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 */
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
|
|
c->cnr.stat[i].scale = FE_SCALE_NOT_AVAILABLE;
|
|
|
|
c->pre_bit_error.stat[i].scale = FE_SCALE_NOT_AVAILABLE;
|
|
|
|
c->pre_bit_count.stat[i].scale = FE_SCALE_NOT_AVAILABLE;
|
|
|
|
c->block_error.stat[i].scale = FE_SCALE_NOT_AVAILABLE;
|
|
|
|
c->block_count.stat[i].scale = FE_SCALE_NOT_AVAILABLE;
|
|
|
|
}
|
2010-09-28 06:52:43 +07:00
|
|
|
}
|
|
|
|
|
2013-01-22 22:30:07 +07:00
|
|
|
static int mb86a20s_get_stats(struct dvb_frontend *fe)
|
|
|
|
{
|
|
|
|
struct mb86a20s_state *state = fe->demodulator_priv;
|
|
|
|
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
|
|
|
|
int rc = 0, i;
|
|
|
|
u32 bit_error = 0, bit_count = 0;
|
|
|
|
u32 t_pre_bit_error = 0, t_pre_bit_count = 0;
|
|
|
|
int active_layers = 0, ber_layers = 0;
|
|
|
|
|
|
|
|
/* Get per-layer stats */
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
|
|
if (c->isdbt_layer_enabled & (1 << i)) {
|
|
|
|
/* Layer is active and has rc segments */
|
|
|
|
active_layers++;
|
|
|
|
|
|
|
|
/* Read per-layer BER */
|
|
|
|
/* Handle BER before vterbi */
|
|
|
|
rc = mb86a20s_get_ber_before_vterbi(fe, i,
|
|
|
|
&bit_error,
|
|
|
|
&bit_count);
|
|
|
|
if (rc >= 0) {
|
|
|
|
c->pre_bit_error.stat[1 + i].scale = FE_SCALE_COUNTER;
|
|
|
|
c->pre_bit_error.stat[1 + i].uvalue += bit_error;
|
|
|
|
c->pre_bit_count.stat[1 + i].scale = FE_SCALE_COUNTER;
|
|
|
|
c->pre_bit_count.stat[1 + i].uvalue += bit_count;
|
|
|
|
} else if (rc != -EBUSY) {
|
|
|
|
/*
|
|
|
|
* If an I/O error happened,
|
|
|
|
* measures are now unavailable
|
|
|
|
*/
|
|
|
|
c->pre_bit_error.stat[1 + i].scale = FE_SCALE_NOT_AVAILABLE;
|
|
|
|
c->pre_bit_count.stat[1 + i].scale = FE_SCALE_NOT_AVAILABLE;
|
|
|
|
dev_err(&state->i2c->dev,
|
|
|
|
"%s: Can't get BER for layer %c (error %d).\n",
|
|
|
|
__func__, 'A' + i, rc);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (c->block_error.stat[1 + i].scale != FE_SCALE_NOT_AVAILABLE)
|
|
|
|
ber_layers++;
|
|
|
|
|
|
|
|
/* Update total BER */
|
|
|
|
t_pre_bit_error += c->pre_bit_error.stat[1 + i].uvalue;
|
|
|
|
t_pre_bit_count += c->pre_bit_count.stat[1 + i].uvalue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Start showing global count if at least one error count is
|
|
|
|
* available.
|
|
|
|
*/
|
|
|
|
if (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->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;
|
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
2013-01-22 22:28:31 +07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The functions below are called via DVB callbacks, so they need to
|
|
|
|
* properly use the I2C gate control
|
|
|
|
*/
|
|
|
|
|
2013-01-22 17:53:11 +07:00
|
|
|
static int mb86a20s_initfe(struct dvb_frontend *fe)
|
|
|
|
{
|
|
|
|
struct mb86a20s_state *state = fe->demodulator_priv;
|
|
|
|
int rc;
|
|
|
|
u8 regD5 = 1;
|
|
|
|
|
2013-01-22 18:13:08 +07:00
|
|
|
dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
|
2013-01-22 17:53:11 +07:00
|
|
|
|
|
|
|
if (fe->ops.i2c_gate_ctrl)
|
|
|
|
fe->ops.i2c_gate_ctrl(fe, 0);
|
|
|
|
|
|
|
|
/* Initialize the frontend */
|
|
|
|
rc = mb86a20s_writeregdata(state, mb86a20s_init);
|
|
|
|
if (rc < 0)
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
if (!state->config->is_serial) {
|
|
|
|
regD5 &= ~1;
|
|
|
|
|
|
|
|
rc = mb86a20s_writereg(state, 0x50, 0xd5);
|
|
|
|
if (rc < 0)
|
|
|
|
goto err;
|
|
|
|
rc = mb86a20s_writereg(state, 0x51, regD5);
|
|
|
|
if (rc < 0)
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
err:
|
|
|
|
if (fe->ops.i2c_gate_ctrl)
|
|
|
|
fe->ops.i2c_gate_ctrl(fe, 1);
|
|
|
|
|
|
|
|
if (rc < 0) {
|
|
|
|
state->need_init = true;
|
2013-01-22 18:13:08 +07:00
|
|
|
dev_info(&state->i2c->dev,
|
|
|
|
"mb86a20s: Init failed. Will try again later\n");
|
2013-01-22 17:53:11 +07:00
|
|
|
} else {
|
|
|
|
state->need_init = false;
|
2013-01-22 18:13:08 +07:00
|
|
|
dev_dbg(&state->i2c->dev, "Initialization succeeded.\n");
|
2013-01-22 17:53:11 +07:00
|
|
|
}
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mb86a20s_set_frontend(struct dvb_frontend *fe)
|
|
|
|
{
|
|
|
|
struct mb86a20s_state *state = fe->demodulator_priv;
|
|
|
|
int rc;
|
|
|
|
#if 0
|
|
|
|
/*
|
|
|
|
* FIXME: Properly implement the set frontend properties
|
|
|
|
*/
|
|
|
|
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
|
|
|
|
#endif
|
2013-01-22 18:13:08 +07:00
|
|
|
dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
|
2013-01-22 17:53:11 +07: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);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
if (state->need_init)
|
|
|
|
mb86a20s_initfe(fe);
|
|
|
|
|
|
|
|
if (fe->ops.i2c_gate_ctrl)
|
|
|
|
fe->ops.i2c_gate_ctrl(fe, 0);
|
2013-01-15 06:34:55 +07:00
|
|
|
|
2013-01-22 17:53:11 +07:00
|
|
|
rc = mb86a20s_writeregdata(state, mb86a20s_reset_reception);
|
2013-01-22 22:28:31 +07:00
|
|
|
mb86a20s_reset_counters(fe);
|
2013-01-15 06:34:55 +07:00
|
|
|
|
2013-01-22 17:53:11 +07:00
|
|
|
if (fe->ops.i2c_gate_ctrl)
|
|
|
|
fe->ops.i2c_gate_ctrl(fe, 1);
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2013-01-22 22:28:31 +07:00
|
|
|
static int mb86a20s_read_status_and_stats(struct dvb_frontend *fe,
|
|
|
|
fe_status_t *status)
|
2013-01-22 17:49:39 +07:00
|
|
|
{
|
2013-01-22 22:28:31 +07:00
|
|
|
struct mb86a20s_state *state = fe->demodulator_priv;
|
|
|
|
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
|
|
|
|
int rc;
|
2013-01-22 17:49:39 +07:00
|
|
|
|
2013-01-22 22:28:31 +07:00
|
|
|
dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
|
2013-01-22 17:49:39 +07:00
|
|
|
|
|
|
|
if (fe->ops.i2c_gate_ctrl)
|
|
|
|
fe->ops.i2c_gate_ctrl(fe, 0);
|
|
|
|
|
2013-01-22 22:28:31 +07:00
|
|
|
/* Get lock */
|
|
|
|
rc = mb86a20s_read_status(fe, status);
|
|
|
|
if (!(*status & FE_HAS_LOCK)) {
|
|
|
|
mb86a20s_stats_not_ready(fe);
|
|
|
|
mb86a20s_reset_frontend_cache(fe);
|
|
|
|
}
|
2013-01-22 22:30:07 +07:00
|
|
|
if (rc < 0) {
|
|
|
|
dev_err(&state->i2c->dev,
|
|
|
|
"%s: Can't read frontend lock status\n", __func__);
|
2013-01-22 22:28:31 +07:00
|
|
|
goto error;
|
2013-01-22 22:30:07 +07:00
|
|
|
}
|
2013-01-22 22:28:31 +07:00
|
|
|
|
|
|
|
/* Get signal strength */
|
|
|
|
rc = mb86a20s_read_signal_strength(fe);
|
|
|
|
if (rc < 0) {
|
2013-01-22 22:30:07 +07:00
|
|
|
dev_err(&state->i2c->dev,
|
|
|
|
"%s: Can't reset VBER registers.\n", __func__);
|
2013-01-22 22:28:31 +07:00
|
|
|
mb86a20s_stats_not_ready(fe);
|
|
|
|
mb86a20s_reset_frontend_cache(fe);
|
2013-01-22 22:30:07 +07:00
|
|
|
|
|
|
|
rc = 0; /* Status is OK */
|
2013-01-22 22:28:31 +07:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
/* Fill signal strength */
|
|
|
|
c->strength.stat[0].uvalue = rc;
|
|
|
|
|
|
|
|
if (*status & FE_HAS_LOCK) {
|
|
|
|
/* Get TMCC info*/
|
|
|
|
rc = mb86a20s_get_frontend(fe);
|
2013-01-22 22:30:07 +07: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 */
|
|
|
|
rc = mb86a20s_get_stats(fe);
|
|
|
|
if (rc < 0 && rc != -EBUSY) {
|
|
|
|
dev_err(&state->i2c->dev,
|
|
|
|
"%s: Can't get FE statistics.\n", __func__);
|
|
|
|
rc = 0;
|
2013-01-22 22:28:31 +07:00
|
|
|
goto error;
|
2013-01-22 22:30:07 +07:00
|
|
|
}
|
|
|
|
rc = 0; /* Don't return EBUSY to userspace */
|
2013-01-22 22:28:31 +07:00
|
|
|
}
|
2013-01-22 22:30:07 +07:00
|
|
|
goto ok;
|
2013-01-22 22:28:31 +07:00
|
|
|
|
2013-01-22 22:30:07 +07:00
|
|
|
error:
|
2013-01-22 22:28:31 +07:00
|
|
|
mb86a20s_stats_not_ready(fe);
|
2013-01-22 17:49:39 +07:00
|
|
|
|
2013-01-22 22:30:07 +07:00
|
|
|
ok:
|
2013-01-22 17:49:39 +07:00
|
|
|
if (fe->ops.i2c_gate_ctrl)
|
|
|
|
fe->ops.i2c_gate_ctrl(fe, 1);
|
2013-01-22 22:30:07 +07:00
|
|
|
|
2013-01-22 22:28:31 +07: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 17:49:39 +07:00
|
|
|
|
2013-01-22 22:28:31 +07:00
|
|
|
return 0;
|
2013-01-22 17:49:39 +07:00
|
|
|
}
|
|
|
|
|
2013-01-22 22:28:31 +07:00
|
|
|
static int mb86a20s_get_frontend_dummy(struct dvb_frontend *fe)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* get_frontend is now handled together with other stats
|
|
|
|
* retrival, when read_status() is called, as some statistics
|
|
|
|
* will depend on the layers detection.
|
|
|
|
*/
|
|
|
|
return 0;
|
|
|
|
};
|
|
|
|
|
2010-09-28 06:52:43 +07:00
|
|
|
static int mb86a20s_tune(struct dvb_frontend *fe,
|
2011-12-27 03:48:33 +07:00
|
|
|
bool re_tune,
|
2010-09-28 06:52:43 +07:00
|
|
|
unsigned int mode_flags,
|
|
|
|
unsigned int *delay,
|
|
|
|
fe_status_t *status)
|
|
|
|
{
|
2013-01-22 18:13:08 +07:00
|
|
|
struct mb86a20s_state *state = fe->demodulator_priv;
|
2010-09-28 06:52:43 +07:00
|
|
|
int rc = 0;
|
|
|
|
|
2013-01-22 18:13:08 +07:00
|
|
|
dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
|
2010-09-28 06:52:43 +07:00
|
|
|
|
2011-12-27 03:48:33 +07:00
|
|
|
if (re_tune)
|
2011-12-26 22:11:51 +07:00
|
|
|
rc = mb86a20s_set_frontend(fe);
|
2010-09-28 06:52:43 +07:00
|
|
|
|
|
|
|
if (!(mode_flags & FE_TUNE_MODE_ONESHOT))
|
2013-01-22 22:28:31 +07:00
|
|
|
mb86a20s_read_status_and_stats(fe, status);
|
2010-09-28 06:52:43 +07:00
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void mb86a20s_release(struct dvb_frontend *fe)
|
|
|
|
{
|
|
|
|
struct mb86a20s_state *state = fe->demodulator_priv;
|
|
|
|
|
2013-01-22 18:13:08 +07:00
|
|
|
dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
|
2010-09-28 06:52:43 +07:00
|
|
|
|
|
|
|
kfree(state);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct dvb_frontend_ops mb86a20s_ops;
|
|
|
|
|
|
|
|
struct dvb_frontend *mb86a20s_attach(const struct mb86a20s_config *config,
|
|
|
|
struct i2c_adapter *i2c)
|
|
|
|
{
|
2013-01-22 18:13:08 +07:00
|
|
|
struct mb86a20s_state *state;
|
2010-09-28 06:52:43 +07:00
|
|
|
u8 rev;
|
|
|
|
|
2013-01-23 22:22:22 +07:00
|
|
|
dev_dbg(&i2c->dev, "%s called.\n", __func__);
|
|
|
|
|
2010-09-28 06:52:43 +07:00
|
|
|
/* allocate memory for the internal state */
|
2013-01-22 18:13:08 +07:00
|
|
|
state = kzalloc(sizeof(struct mb86a20s_state), GFP_KERNEL);
|
2010-09-28 06:52:43 +07:00
|
|
|
if (state == NULL) {
|
2013-01-23 22:22:22 +07:00
|
|
|
dev_err(&i2c->dev,
|
2013-01-22 18:13:08 +07:00
|
|
|
"%s: unable to allocate memory for state\n", __func__);
|
2010-09-28 06:52:43 +07:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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);
|
|
|
|
|
|
|
|
if (rev == 0x13) {
|
2013-01-23 22:22:22 +07:00
|
|
|
dev_info(&i2c->dev,
|
2013-01-22 18:13:08 +07:00
|
|
|
"Detected a Fujitsu mb86a20s frontend\n");
|
2010-09-28 06:52:43 +07:00
|
|
|
} else {
|
2013-01-23 22:22:22 +07:00
|
|
|
dev_dbg(&i2c->dev,
|
2013-01-22 18:13:08 +07:00
|
|
|
"Frontend revision %d is unknown - aborting.\n",
|
2010-09-28 06:52:43 +07:00
|
|
|
rev);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
return &state->frontend;
|
|
|
|
|
|
|
|
error:
|
|
|
|
kfree(state);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(mb86a20s_attach);
|
|
|
|
|
|
|
|
static struct dvb_frontend_ops mb86a20s_ops = {
|
2011-12-26 22:11:51 +07:00
|
|
|
.delsys = { SYS_ISDBT },
|
2010-09-28 06:52:43 +07:00
|
|
|
/* Use dib8000 values per default */
|
|
|
|
.info = {
|
|
|
|
.name = "Fujitsu mb86A20s",
|
|
|
|
.caps = FE_CAN_INVERSION_AUTO | FE_CAN_RECOVER |
|
|
|
|
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 */
|
|
|
|
.frequency_min = 45000000,
|
|
|
|
.frequency_max = 864000000,
|
|
|
|
.frequency_stepsize = 62500,
|
|
|
|
},
|
|
|
|
|
|
|
|
.release = mb86a20s_release,
|
|
|
|
|
|
|
|
.init = mb86a20s_initfe,
|
2011-12-26 22:11:51 +07:00
|
|
|
.set_frontend = mb86a20s_set_frontend,
|
2013-01-22 22:28:31 +07:00
|
|
|
.get_frontend = mb86a20s_get_frontend_dummy,
|
|
|
|
.read_status = mb86a20s_read_status_and_stats,
|
|
|
|
.read_signal_strength = mb86a20s_read_signal_strength_from_cache,
|
2010-09-28 06:52:43 +07:00
|
|
|
.tune = mb86a20s_tune,
|
|
|
|
};
|
|
|
|
|
|
|
|
MODULE_DESCRIPTION("DVB Frontend module for Fujitsu mb86A20s hardware");
|
|
|
|
MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
|
|
|
|
MODULE_LICENSE("GPL");
|