2005-04-17 06:20:36 +08:00
|
|
|
/*
|
2005-11-09 13:37:48 +08:00
|
|
|
|
|
|
|
i2c tv tuner chip device driver
|
|
|
|
controls the philips tda8290+75 tuner chip combo.
|
|
|
|
|
|
|
|
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; either version 2 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
2007-08-28 08:22:20 +08:00
|
|
|
|
|
|
|
This "tda8290" module was split apart from the original "tuner" module.
|
2005-11-09 13:37:48 +08:00
|
|
|
*/
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/i2c.h>
|
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h
percpu.h is included by sched.h and module.h and thus ends up being
included when building most .c files. percpu.h includes slab.h which
in turn includes gfp.h making everything defined by the two files
universally available and complicating inclusion dependencies.
percpu.h -> slab.h dependency is about to be removed. Prepare for
this change by updating users of gfp and slab facilities include those
headers directly instead of assuming availability. As this conversion
needs to touch large number of source files, the following script is
used as the basis of conversion.
http://userweb.kernel.org/~tj/misc/slabh-sweep.py
The script does the followings.
* Scan files for gfp and slab usages and update includes such that
only the necessary includes are there. ie. if only gfp is used,
gfp.h, if slab is used, slab.h.
* When the script inserts a new include, it looks at the include
blocks and try to put the new include such that its order conforms
to its surrounding. It's put in the include block which contains
core kernel includes, in the same order that the rest are ordered -
alphabetical, Christmas tree, rev-Xmas-tree or at the end if there
doesn't seem to be any matching order.
* If the script can't find a place to put a new include (mostly
because the file doesn't have fitting include block), it prints out
an error message indicating which .h file needs to be added to the
file.
The conversion was done in the following steps.
1. The initial automatic conversion of all .c files updated slightly
over 4000 files, deleting around 700 includes and adding ~480 gfp.h
and ~3000 slab.h inclusions. The script emitted errors for ~400
files.
2. Each error was manually checked. Some didn't need the inclusion,
some needed manual addition while adding it to implementation .h or
embedding .c file was more appropriate for others. This step added
inclusions to around 150 files.
3. The script was run again and the output was compared to the edits
from #2 to make sure no file was left behind.
4. Several build tests were done and a couple of problems were fixed.
e.g. lib/decompress_*.c used malloc/free() wrappers around slab
APIs requiring slab.h to be added manually.
5. The script was run on all .h files but without automatically
editing them as sprinkling gfp.h and slab.h inclusions around .h
files could easily lead to inclusion dependency hell. Most gfp.h
inclusion directives were ignored as stuff from gfp.h was usually
wildly available and often used in preprocessor macros. Each
slab.h inclusion directive was examined and added manually as
necessary.
6. percpu.h was updated not to include slab.h.
7. Build test were done on the following configurations and failures
were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my
distributed build env didn't work with gcov compiles) and a few
more options had to be turned off depending on archs to make things
build (like ipr on powerpc/64 which failed due to missing writeq).
* x86 and x86_64 UP and SMP allmodconfig and a custom test config.
* powerpc and powerpc64 SMP allmodconfig
* sparc and sparc64 SMP allmodconfig
* ia64 SMP allmodconfig
* s390 SMP allmodconfig
* alpha SMP allmodconfig
* um on x86_64 SMP allmodconfig
8. percpu.h modifications were reverted so that it could be applied as
a separate patch and serve as bisection point.
Given the fact that I had only a couple of failures from tests on step
6, I'm fairly confident about the coverage of this conversion patch.
If there is a breakage, it's likely to be something in one of the arch
headers which should be easily discoverable easily on most builds of
the specific arch.
Signed-off-by: Tejun Heo <tj@kernel.org>
Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 16:04:11 +08:00
|
|
|
#include <linux/slab.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/delay.h>
|
2009-02-20 04:31:17 +08:00
|
|
|
#include <linux/videodev2.h>
|
2007-12-09 13:26:48 +08:00
|
|
|
#include "tuner-i2c.h"
|
2007-08-28 08:22:20 +08:00
|
|
|
#include "tda8290.h"
|
2007-08-26 06:08:45 +08:00
|
|
|
#include "tda827x.h"
|
2007-10-22 20:56:38 +08:00
|
|
|
#include "tda18271.h"
|
2007-08-28 08:22:20 +08:00
|
|
|
|
2007-12-09 13:26:48 +08:00
|
|
|
static int debug;
|
|
|
|
module_param(debug, int, 0644);
|
2007-08-28 08:22:20 +08:00
|
|
|
MODULE_PARM_DESC(debug, "enable verbose debug messages");
|
|
|
|
|
2008-12-08 21:36:57 +08:00
|
|
|
static int deemphasis_50;
|
2009-09-28 05:19:58 +08:00
|
|
|
module_param(deemphasis_50, int, 0644);
|
2008-12-08 21:36:57 +08:00
|
|
|
MODULE_PARM_DESC(deemphasis_50, "0 - 75us deemphasis; 1 - 50us deemphasis");
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
|
2007-05-30 09:54:06 +08:00
|
|
|
struct tda8290_priv {
|
2007-08-21 12:24:42 +08:00
|
|
|
struct tuner_i2c_props i2c_props;
|
|
|
|
|
2007-05-30 09:54:06 +08:00
|
|
|
unsigned char tda8290_easy_mode;
|
2007-08-26 06:08:45 +08:00
|
|
|
|
2007-05-30 09:54:06 +08:00
|
|
|
unsigned char tda827x_addr;
|
2007-10-27 13:00:57 +08:00
|
|
|
|
|
|
|
unsigned char ver;
|
|
|
|
#define TDA8290 1
|
|
|
|
#define TDA8295 2
|
|
|
|
#define TDA8275 4
|
|
|
|
#define TDA8275A 8
|
|
|
|
#define TDA18271 16
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-08-26 06:08:45 +08:00
|
|
|
struct tda827x_config cfg;
|
2013-02-04 09:48:56 +08:00
|
|
|
struct tda18271_std_map *tda18271_std_map;
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
|
|
|
|
2005-11-09 13:37:48 +08:00
|
|
|
/*---------------------------------------------------------------------*/
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-10-24 20:30:17 +08:00
|
|
|
static int tda8290_i2c_bridge(struct dvb_frontend *fe, int close)
|
2005-11-09 13:37:48 +08:00
|
|
|
{
|
2007-10-22 06:39:50 +08:00
|
|
|
struct tda8290_priv *priv = fe->analog_demod_priv;
|
2007-08-21 12:24:42 +08:00
|
|
|
|
2005-11-09 13:37:48 +08:00
|
|
|
unsigned char enable[2] = { 0x21, 0xC0 };
|
2006-02-07 16:49:09 +08:00
|
|
|
unsigned char disable[2] = { 0x21, 0x00 };
|
2005-11-09 13:37:48 +08:00
|
|
|
unsigned char *msg;
|
2007-10-24 20:30:17 +08:00
|
|
|
|
|
|
|
if (close) {
|
2005-11-09 13:37:48 +08:00
|
|
|
msg = enable;
|
2007-08-21 12:24:42 +08:00
|
|
|
tuner_i2c_xfer_send(&priv->i2c_props, msg, 2);
|
2005-11-09 13:37:48 +08:00
|
|
|
/* let the bridge stabilize */
|
|
|
|
msleep(20);
|
|
|
|
} else {
|
|
|
|
msg = disable;
|
2007-08-21 12:24:42 +08:00
|
|
|
tuner_i2c_xfer_send(&priv->i2c_props, msg, 2);
|
2005-11-09 13:37:48 +08:00
|
|
|
}
|
2007-10-24 20:30:17 +08:00
|
|
|
|
|
|
|
return 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2007-10-24 20:30:17 +08:00
|
|
|
static int tda8295_i2c_bridge(struct dvb_frontend *fe, int close)
|
2007-10-22 20:56:38 +08:00
|
|
|
{
|
2007-10-22 06:39:50 +08:00
|
|
|
struct tda8290_priv *priv = fe->analog_demod_priv;
|
2007-10-22 20:56:38 +08:00
|
|
|
|
|
|
|
unsigned char enable[2] = { 0x45, 0xc1 };
|
|
|
|
unsigned char disable[2] = { 0x46, 0x00 };
|
|
|
|
unsigned char buf[3] = { 0x45, 0x01, 0x00 };
|
|
|
|
unsigned char *msg;
|
2007-10-24 20:30:17 +08:00
|
|
|
|
2007-10-22 20:56:38 +08:00
|
|
|
if (close) {
|
|
|
|
msg = enable;
|
|
|
|
tuner_i2c_xfer_send(&priv->i2c_props, msg, 2);
|
|
|
|
/* let the bridge stabilize */
|
|
|
|
msleep(20);
|
|
|
|
} else {
|
|
|
|
msg = disable;
|
2011-01-13 22:58:36 +08:00
|
|
|
tuner_i2c_xfer_send_recv(&priv->i2c_props, msg, 1, &msg[1], 1);
|
2007-10-22 20:56:38 +08:00
|
|
|
|
|
|
|
buf[2] = msg[1];
|
|
|
|
buf[2] &= ~0x04;
|
|
|
|
tuner_i2c_xfer_send(&priv->i2c_props, buf, 3);
|
|
|
|
msleep(5);
|
|
|
|
|
|
|
|
msg[1] |= 0x04;
|
|
|
|
tuner_i2c_xfer_send(&priv->i2c_props, msg, 2);
|
|
|
|
}
|
2007-10-24 20:30:17 +08:00
|
|
|
|
|
|
|
return 0;
|
2007-10-22 20:56:38 +08:00
|
|
|
}
|
|
|
|
|
2005-11-09 13:37:48 +08:00
|
|
|
/*---------------------------------------------------------------------*/
|
|
|
|
|
2007-12-09 04:06:30 +08:00
|
|
|
static void set_audio(struct dvb_frontend *fe,
|
|
|
|
struct analog_parameters *params)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2007-10-22 06:39:50 +08:00
|
|
|
struct tda8290_priv *priv = fe->analog_demod_priv;
|
2007-08-28 08:22:20 +08:00
|
|
|
char* mode;
|
|
|
|
|
2007-12-09 04:06:30 +08:00
|
|
|
if (params->std & V4L2_STD_MN) {
|
2007-08-28 08:22:20 +08:00
|
|
|
priv->tda8290_easy_mode = 0x01;
|
|
|
|
mode = "MN";
|
2007-12-09 04:06:30 +08:00
|
|
|
} else if (params->std & V4L2_STD_B) {
|
2007-08-28 08:22:20 +08:00
|
|
|
priv->tda8290_easy_mode = 0x02;
|
|
|
|
mode = "B";
|
2007-12-09 04:06:30 +08:00
|
|
|
} else if (params->std & V4L2_STD_GH) {
|
2007-08-28 08:22:20 +08:00
|
|
|
priv->tda8290_easy_mode = 0x04;
|
|
|
|
mode = "GH";
|
2007-12-09 04:06:30 +08:00
|
|
|
} else if (params->std & V4L2_STD_PAL_I) {
|
2007-08-28 08:22:20 +08:00
|
|
|
priv->tda8290_easy_mode = 0x08;
|
|
|
|
mode = "I";
|
2007-12-09 04:06:30 +08:00
|
|
|
} else if (params->std & V4L2_STD_DK) {
|
2007-08-28 08:22:20 +08:00
|
|
|
priv->tda8290_easy_mode = 0x10;
|
|
|
|
mode = "DK";
|
2007-12-09 04:06:30 +08:00
|
|
|
} else if (params->std & V4L2_STD_SECAM_L) {
|
2007-08-28 08:22:20 +08:00
|
|
|
priv->tda8290_easy_mode = 0x20;
|
|
|
|
mode = "L";
|
2007-12-09 04:06:30 +08:00
|
|
|
} else if (params->std & V4L2_STD_SECAM_LC) {
|
2007-08-28 08:22:20 +08:00
|
|
|
priv->tda8290_easy_mode = 0x40;
|
|
|
|
mode = "LC";
|
|
|
|
} else {
|
|
|
|
priv->tda8290_easy_mode = 0x10;
|
|
|
|
mode = "xx";
|
|
|
|
}
|
|
|
|
|
2008-12-08 21:36:57 +08:00
|
|
|
if (params->mode == V4L2_TUNER_RADIO) {
|
2010-01-11 05:13:33 +08:00
|
|
|
/* Set TDA8295 to FM radio; Start TDA8290 with MN values */
|
|
|
|
priv->tda8290_easy_mode = (priv->ver & TDA8295) ? 0x80 : 0x01;
|
2008-12-08 21:36:57 +08:00
|
|
|
tuner_dbg("setting to radio FM\n");
|
|
|
|
} else {
|
|
|
|
tuner_dbg("setting tda829x to system %s\n", mode);
|
|
|
|
}
|
2007-08-28 08:22:20 +08:00
|
|
|
}
|
|
|
|
|
2009-01-05 12:23:50 +08:00
|
|
|
static struct {
|
2008-12-08 21:36:57 +08:00
|
|
|
unsigned char seq[2];
|
|
|
|
} fm_mode[] = {
|
|
|
|
{ { 0x01, 0x81} }, /* Put device into expert mode */
|
|
|
|
{ { 0x03, 0x48} }, /* Disable NOTCH and VIDEO filters */
|
|
|
|
{ { 0x04, 0x04} }, /* Disable color carrier filter (SSIF) */
|
|
|
|
{ { 0x05, 0x04} }, /* ADC headroom */
|
|
|
|
{ { 0x06, 0x10} }, /* group delay flat */
|
|
|
|
|
|
|
|
{ { 0x07, 0x00} }, /* use the same radio DTO values as a tda8295 */
|
|
|
|
{ { 0x08, 0x00} },
|
|
|
|
{ { 0x09, 0x80} },
|
|
|
|
{ { 0x0a, 0xda} },
|
|
|
|
{ { 0x0b, 0x4b} },
|
|
|
|
{ { 0x0c, 0x68} },
|
|
|
|
|
|
|
|
{ { 0x0d, 0x00} }, /* PLL off, no video carrier detect */
|
|
|
|
{ { 0x14, 0x00} }, /* disable auto mute if no video */
|
|
|
|
};
|
|
|
|
|
2007-12-09 04:06:30 +08:00
|
|
|
static void tda8290_set_params(struct dvb_frontend *fe,
|
|
|
|
struct analog_parameters *params)
|
2007-08-28 08:22:20 +08:00
|
|
|
{
|
2007-10-22 06:39:50 +08:00
|
|
|
struct tda8290_priv *priv = fe->analog_demod_priv;
|
|
|
|
|
2005-11-09 13:37:48 +08:00
|
|
|
unsigned char soft_reset[] = { 0x00, 0x00 };
|
2007-05-30 09:54:06 +08:00
|
|
|
unsigned char easy_mode[] = { 0x01, priv->tda8290_easy_mode };
|
2005-11-09 13:37:48 +08:00
|
|
|
unsigned char expert_mode[] = { 0x01, 0x80 };
|
2006-02-07 16:49:09 +08:00
|
|
|
unsigned char agc_out_on[] = { 0x02, 0x00 };
|
2005-11-09 13:37:48 +08:00
|
|
|
unsigned char gainset_off[] = { 0x28, 0x14 };
|
|
|
|
unsigned char if_agc_spd[] = { 0x0f, 0x88 };
|
|
|
|
unsigned char adc_head_6[] = { 0x05, 0x04 };
|
|
|
|
unsigned char adc_head_9[] = { 0x05, 0x02 };
|
|
|
|
unsigned char adc_head_12[] = { 0x05, 0x01 };
|
|
|
|
unsigned char pll_bw_nom[] = { 0x0d, 0x47 };
|
|
|
|
unsigned char pll_bw_low[] = { 0x0d, 0x27 };
|
|
|
|
unsigned char gainset_2[] = { 0x28, 0x64 };
|
|
|
|
unsigned char agc_rst_on[] = { 0x0e, 0x0b };
|
|
|
|
unsigned char agc_rst_off[] = { 0x0e, 0x09 };
|
|
|
|
unsigned char if_agc_set[] = { 0x0f, 0x81 };
|
|
|
|
unsigned char addr_adc_sat = 0x1a;
|
|
|
|
unsigned char addr_agc_stat = 0x1d;
|
|
|
|
unsigned char addr_pll_stat = 0x1b;
|
|
|
|
unsigned char adc_sat, agc_stat,
|
2005-11-09 13:37:50 +08:00
|
|
|
pll_stat;
|
2007-04-27 23:31:12 +08:00
|
|
|
int i;
|
2005-11-09 13:37:48 +08:00
|
|
|
|
2007-12-09 04:06:30 +08:00
|
|
|
set_audio(fe, params);
|
2007-08-28 08:22:20 +08:00
|
|
|
|
2007-12-09 13:26:48 +08:00
|
|
|
if (priv->cfg.config)
|
2008-04-23 01:46:08 +08:00
|
|
|
tuner_dbg("tda827xa config is 0x%02x\n", priv->cfg.config);
|
2007-08-21 12:24:42 +08:00
|
|
|
tuner_i2c_xfer_send(&priv->i2c_props, easy_mode, 2);
|
|
|
|
tuner_i2c_xfer_send(&priv->i2c_props, agc_out_on, 2);
|
|
|
|
tuner_i2c_xfer_send(&priv->i2c_props, soft_reset, 2);
|
2005-11-09 13:37:48 +08:00
|
|
|
msleep(1);
|
|
|
|
|
2008-12-08 21:36:57 +08:00
|
|
|
if (params->mode == V4L2_TUNER_RADIO) {
|
|
|
|
unsigned char deemphasis[] = { 0x13, 1 };
|
|
|
|
|
|
|
|
/* FIXME: allow using a different deemphasis */
|
|
|
|
|
|
|
|
if (deemphasis_50)
|
|
|
|
deemphasis[1] = 2;
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(fm_mode); i++)
|
|
|
|
tuner_i2c_xfer_send(&priv->i2c_props, fm_mode[i].seq, 2);
|
|
|
|
|
|
|
|
tuner_i2c_xfer_send(&priv->i2c_props, deemphasis, 2);
|
|
|
|
} else {
|
|
|
|
expert_mode[1] = priv->tda8290_easy_mode + 0x80;
|
|
|
|
tuner_i2c_xfer_send(&priv->i2c_props, expert_mode, 2);
|
|
|
|
tuner_i2c_xfer_send(&priv->i2c_props, gainset_off, 2);
|
|
|
|
tuner_i2c_xfer_send(&priv->i2c_props, if_agc_spd, 2);
|
|
|
|
if (priv->tda8290_easy_mode & 0x60)
|
|
|
|
tuner_i2c_xfer_send(&priv->i2c_props, adc_head_9, 2);
|
|
|
|
else
|
|
|
|
tuner_i2c_xfer_send(&priv->i2c_props, adc_head_6, 2);
|
|
|
|
tuner_i2c_xfer_send(&priv->i2c_props, pll_bw_nom, 2);
|
|
|
|
}
|
2005-11-09 13:37:48 +08:00
|
|
|
|
[media] saa7134: Kworld SBTVD: make both analog and digital to work
There are some weird bugs at tda8290/tda18271 initialization, as it
insits do do analog initialization during DVB frontend attach:
DVB: registering new adapter (saa7133[0])
DVB: registering adapter 0 frontend 0 (Fujitsu mb86A20s)...
mb86a20s: mb86a20s_initfe
tda18271_write_regs: [2-0060|M] ERROR: idx = 0x5, len = 1, i2c_transfer returned: -5
tda18271_init: [2-0060|M] error -5 on line 830
tda18271_tune: [2-0060|M] error -5 on line 908
tda18271_write_regs
tda18271_write_regs: [2-0060|M] ERROR: idx = 0x5, len = 1, i2c_transfer returned: -5
tda18271c2_rf_tracking_filters_correction: [2-0060|M] error -5 on line 265
tda18271_write_regs
tda18271_write_regs: [2-0060|M] ERROR: idx = 0x25, len = 1, i2c_transfer returned: -5
tda18271_channel_configuration: [2-0060|M] error -5 on line 119
tda18271_set_analog_params: [2-0060|M] error -5 on line 1045
tda18271_set_analog_params: [2-0060|M] error -5 on line 1045
tda829x 2-004b: tda8295 not locked, no signal?
tda829x 2-004b: tda8295_i2c_bridge: disable i2c gate
tda829x 2-004b: tda8295 not locked, no signal?
tda829x 2-004b: tda8295_i2c_bridge: disable i2c gate
mb86a20s_i2c_writereg: writereg error (rc == -5, reg == 0x29, data == 0x33)
mb86a20s: Init failed. Will try again later
The problem is that mb86a20s is only visible if the analog part is disabled.
However, due to a trick at mb86a20s, it will later initialize properly:
mb86a20s: mb86a20s_initfe: Initialization succeded.
This is hacky and ugly. However, I coldn't find any easy way to fix it.
A proper fix would be to have a resource locking schema, used by both
V4L and DVB parts that would block access to analog registers while
digital registers are in use, but this will probably put tda829x into
a dead lock.
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
2011-01-14 23:03:03 +08:00
|
|
|
|
2013-02-04 09:47:38 +08:00
|
|
|
if (fe->ops.analog_ops.i2c_gate_ctrl)
|
|
|
|
fe->ops.analog_ops.i2c_gate_ctrl(fe, 1);
|
2007-08-26 06:08:45 +08:00
|
|
|
|
2007-10-22 06:39:50 +08:00
|
|
|
if (fe->ops.tuner_ops.set_analog_params)
|
2007-12-09 04:06:30 +08:00
|
|
|
fe->ops.tuner_ops.set_analog_params(fe, params);
|
2007-08-26 06:08:45 +08:00
|
|
|
|
2007-04-27 23:31:12 +08:00
|
|
|
for (i = 0; i < 3; i++) {
|
2011-01-13 22:58:36 +08:00
|
|
|
tuner_i2c_xfer_send_recv(&priv->i2c_props,
|
|
|
|
&addr_pll_stat, 1, &pll_stat, 1);
|
2007-04-27 23:31:12 +08:00
|
|
|
if (pll_stat & 0x80) {
|
2011-01-13 22:58:36 +08:00
|
|
|
tuner_i2c_xfer_send_recv(&priv->i2c_props,
|
|
|
|
&addr_adc_sat, 1,
|
|
|
|
&adc_sat, 1);
|
|
|
|
tuner_i2c_xfer_send_recv(&priv->i2c_props,
|
|
|
|
&addr_agc_stat, 1,
|
|
|
|
&agc_stat, 1);
|
2007-04-27 23:31:12 +08:00
|
|
|
tuner_dbg("tda8290 is locked, AGC: %d\n", agc_stat);
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
tuner_dbg("tda8290 not locked, no signal?\n");
|
|
|
|
msleep(100);
|
|
|
|
}
|
|
|
|
}
|
2005-11-09 13:37:48 +08:00
|
|
|
/* adjust headroom resp. gain */
|
2005-11-09 13:38:38 +08:00
|
|
|
if ((agc_stat > 115) || (!(pll_stat & 0x80) && (adc_sat < 20))) {
|
|
|
|
tuner_dbg("adjust gain, step 1. Agc: %d, ADC stat: %d, lock: %d\n",
|
|
|
|
agc_stat, adc_sat, pll_stat & 0x80);
|
2007-08-21 12:24:42 +08:00
|
|
|
tuner_i2c_xfer_send(&priv->i2c_props, gainset_2, 2);
|
2005-11-09 13:37:48 +08:00
|
|
|
msleep(100);
|
2011-01-13 22:58:36 +08:00
|
|
|
tuner_i2c_xfer_send_recv(&priv->i2c_props,
|
|
|
|
&addr_agc_stat, 1, &agc_stat, 1);
|
|
|
|
tuner_i2c_xfer_send_recv(&priv->i2c_props,
|
|
|
|
&addr_pll_stat, 1, &pll_stat, 1);
|
2005-11-09 13:37:48 +08:00
|
|
|
if ((agc_stat > 115) || !(pll_stat & 0x80)) {
|
2005-11-09 13:38:38 +08:00
|
|
|
tuner_dbg("adjust gain, step 2. Agc: %d, lock: %d\n",
|
|
|
|
agc_stat, pll_stat & 0x80);
|
2007-08-26 06:08:45 +08:00
|
|
|
if (priv->cfg.agcf)
|
2007-10-22 06:39:50 +08:00
|
|
|
priv->cfg.agcf(fe);
|
2005-11-09 13:37:48 +08:00
|
|
|
msleep(100);
|
2011-01-13 22:58:36 +08:00
|
|
|
tuner_i2c_xfer_send_recv(&priv->i2c_props,
|
|
|
|
&addr_agc_stat, 1,
|
|
|
|
&agc_stat, 1);
|
|
|
|
tuner_i2c_xfer_send_recv(&priv->i2c_props,
|
|
|
|
&addr_pll_stat, 1,
|
|
|
|
&pll_stat, 1);
|
2005-11-09 13:37:48 +08:00
|
|
|
if((agc_stat > 115) || !(pll_stat & 0x80)) {
|
|
|
|
tuner_dbg("adjust gain, step 3. Agc: %d\n", agc_stat);
|
2007-08-21 12:24:42 +08:00
|
|
|
tuner_i2c_xfer_send(&priv->i2c_props, adc_head_12, 2);
|
|
|
|
tuner_i2c_xfer_send(&priv->i2c_props, pll_bw_low, 2);
|
2005-11-09 13:37:48 +08:00
|
|
|
msleep(100);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2005-11-09 13:37:48 +08:00
|
|
|
/* l/ l' deadlock? */
|
2007-05-30 09:54:06 +08:00
|
|
|
if(priv->tda8290_easy_mode & 0x60) {
|
2011-01-13 22:58:36 +08:00
|
|
|
tuner_i2c_xfer_send_recv(&priv->i2c_props,
|
|
|
|
&addr_adc_sat, 1,
|
|
|
|
&adc_sat, 1);
|
|
|
|
tuner_i2c_xfer_send_recv(&priv->i2c_props,
|
|
|
|
&addr_pll_stat, 1,
|
|
|
|
&pll_stat, 1);
|
2005-11-09 13:37:48 +08:00
|
|
|
if ((adc_sat > 20) || !(pll_stat & 0x80)) {
|
2005-11-09 13:38:38 +08:00
|
|
|
tuner_dbg("trying to resolve SECAM L deadlock\n");
|
2007-08-21 12:24:42 +08:00
|
|
|
tuner_i2c_xfer_send(&priv->i2c_props, agc_rst_on, 2);
|
2005-11-09 13:37:48 +08:00
|
|
|
msleep(40);
|
2007-08-21 12:24:42 +08:00
|
|
|
tuner_i2c_xfer_send(&priv->i2c_props, agc_rst_off, 2);
|
2005-11-09 13:37:48 +08:00
|
|
|
}
|
|
|
|
}
|
2005-06-29 11:45:21 +08:00
|
|
|
|
2013-02-04 09:47:38 +08:00
|
|
|
if (fe->ops.analog_ops.i2c_gate_ctrl)
|
|
|
|
fe->ops.analog_ops.i2c_gate_ctrl(fe, 0);
|
2007-08-21 12:24:42 +08:00
|
|
|
tuner_i2c_xfer_send(&priv->i2c_props, if_agc_set, 2);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2005-11-09 13:37:48 +08:00
|
|
|
/*---------------------------------------------------------------------*/
|
|
|
|
|
2007-10-22 06:39:50 +08:00
|
|
|
static void tda8295_power(struct dvb_frontend *fe, int enable)
|
2007-10-22 20:56:38 +08:00
|
|
|
{
|
2007-10-22 06:39:50 +08:00
|
|
|
struct tda8290_priv *priv = fe->analog_demod_priv;
|
2007-10-22 20:56:38 +08:00
|
|
|
unsigned char buf[] = { 0x30, 0x00 }; /* clb_stdbt */
|
|
|
|
|
2011-01-13 22:58:36 +08:00
|
|
|
tuner_i2c_xfer_send_recv(&priv->i2c_props, &buf[0], 1, &buf[1], 1);
|
2007-10-22 20:56:38 +08:00
|
|
|
|
|
|
|
if (enable)
|
|
|
|
buf[1] = 0x01;
|
|
|
|
else
|
|
|
|
buf[1] = 0x03;
|
|
|
|
|
|
|
|
tuner_i2c_xfer_send(&priv->i2c_props, buf, 2);
|
|
|
|
}
|
|
|
|
|
2007-10-22 06:39:50 +08:00
|
|
|
static void tda8295_set_easy_mode(struct dvb_frontend *fe, int enable)
|
2007-10-22 20:56:38 +08:00
|
|
|
{
|
2007-10-22 06:39:50 +08:00
|
|
|
struct tda8290_priv *priv = fe->analog_demod_priv;
|
2007-10-22 20:56:38 +08:00
|
|
|
unsigned char buf[] = { 0x01, 0x00 };
|
|
|
|
|
2011-01-13 22:58:36 +08:00
|
|
|
tuner_i2c_xfer_send_recv(&priv->i2c_props, &buf[0], 1, &buf[1], 1);
|
2007-10-22 20:56:38 +08:00
|
|
|
|
|
|
|
if (enable)
|
|
|
|
buf[1] = 0x01; /* rising edge sets regs 0x02 - 0x23 */
|
|
|
|
else
|
|
|
|
buf[1] = 0x00; /* reset active bit */
|
|
|
|
|
|
|
|
tuner_i2c_xfer_send(&priv->i2c_props, buf, 2);
|
|
|
|
}
|
|
|
|
|
2007-10-22 06:39:50 +08:00
|
|
|
static void tda8295_set_video_std(struct dvb_frontend *fe)
|
2007-10-22 20:56:38 +08:00
|
|
|
{
|
2007-10-22 06:39:50 +08:00
|
|
|
struct tda8290_priv *priv = fe->analog_demod_priv;
|
2007-10-22 20:56:38 +08:00
|
|
|
unsigned char buf[] = { 0x00, priv->tda8290_easy_mode };
|
|
|
|
|
|
|
|
tuner_i2c_xfer_send(&priv->i2c_props, buf, 2);
|
|
|
|
|
2007-10-22 06:39:50 +08:00
|
|
|
tda8295_set_easy_mode(fe, 1);
|
2007-10-22 20:56:38 +08:00
|
|
|
msleep(20);
|
2007-10-22 06:39:50 +08:00
|
|
|
tda8295_set_easy_mode(fe, 0);
|
2007-10-22 20:56:38 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*---------------------------------------------------------------------*/
|
|
|
|
|
2007-10-22 06:39:50 +08:00
|
|
|
static void tda8295_agc1_out(struct dvb_frontend *fe, int enable)
|
2007-10-22 20:56:38 +08:00
|
|
|
{
|
2007-10-22 06:39:50 +08:00
|
|
|
struct tda8290_priv *priv = fe->analog_demod_priv;
|
2007-10-22 20:56:38 +08:00
|
|
|
unsigned char buf[] = { 0x02, 0x00 }; /* DIV_FUNC */
|
|
|
|
|
2011-01-13 22:58:36 +08:00
|
|
|
tuner_i2c_xfer_send_recv(&priv->i2c_props, &buf[0], 1, &buf[1], 1);
|
2007-10-22 20:56:38 +08:00
|
|
|
|
|
|
|
if (enable)
|
|
|
|
buf[1] &= ~0x40;
|
|
|
|
else
|
|
|
|
buf[1] |= 0x40;
|
|
|
|
|
|
|
|
tuner_i2c_xfer_send(&priv->i2c_props, buf, 2);
|
|
|
|
}
|
|
|
|
|
2007-10-22 06:39:50 +08:00
|
|
|
static void tda8295_agc2_out(struct dvb_frontend *fe, int enable)
|
2007-10-22 20:56:38 +08:00
|
|
|
{
|
2007-10-22 06:39:50 +08:00
|
|
|
struct tda8290_priv *priv = fe->analog_demod_priv;
|
2007-10-22 20:56:38 +08:00
|
|
|
unsigned char set_gpio_cf[] = { 0x44, 0x00 };
|
|
|
|
unsigned char set_gpio_val[] = { 0x46, 0x00 };
|
|
|
|
|
2011-01-13 22:58:36 +08:00
|
|
|
tuner_i2c_xfer_send_recv(&priv->i2c_props,
|
|
|
|
&set_gpio_cf[0], 1, &set_gpio_cf[1], 1);
|
|
|
|
tuner_i2c_xfer_send_recv(&priv->i2c_props,
|
|
|
|
&set_gpio_val[0], 1, &set_gpio_val[1], 1);
|
2007-10-22 20:56:38 +08:00
|
|
|
|
|
|
|
set_gpio_cf[1] &= 0xf0; /* clear GPIO_0 bits 3-0 */
|
|
|
|
|
|
|
|
if (enable) {
|
|
|
|
set_gpio_cf[1] |= 0x01; /* config GPIO_0 as Open Drain Out */
|
|
|
|
set_gpio_val[1] &= 0xfe; /* set GPIO_0 pin low */
|
|
|
|
}
|
|
|
|
tuner_i2c_xfer_send(&priv->i2c_props, set_gpio_cf, 2);
|
|
|
|
tuner_i2c_xfer_send(&priv->i2c_props, set_gpio_val, 2);
|
|
|
|
}
|
|
|
|
|
2013-04-06 15:41:29 +08:00
|
|
|
static int tda8295_has_signal(struct dvb_frontend *fe, u16 *signal)
|
2007-10-22 20:56:38 +08:00
|
|
|
{
|
2007-10-22 06:39:50 +08:00
|
|
|
struct tda8290_priv *priv = fe->analog_demod_priv;
|
2007-10-22 20:56:38 +08:00
|
|
|
|
|
|
|
unsigned char hvpll_stat = 0x26;
|
|
|
|
unsigned char ret;
|
|
|
|
|
2011-01-13 22:58:36 +08:00
|
|
|
tuner_i2c_xfer_send_recv(&priv->i2c_props, &hvpll_stat, 1, &ret, 1);
|
2013-04-06 15:41:29 +08:00
|
|
|
*signal = (ret & 0x01) ? 65535 : 0;
|
|
|
|
return 0;
|
2007-10-22 20:56:38 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*---------------------------------------------------------------------*/
|
|
|
|
|
2007-12-09 04:06:30 +08:00
|
|
|
static void tda8295_set_params(struct dvb_frontend *fe,
|
|
|
|
struct analog_parameters *params)
|
2007-10-22 20:56:38 +08:00
|
|
|
{
|
2007-10-22 06:39:50 +08:00
|
|
|
struct tda8290_priv *priv = fe->analog_demod_priv;
|
2013-04-06 15:41:29 +08:00
|
|
|
u16 signal = 0;
|
2007-10-22 20:56:38 +08:00
|
|
|
unsigned char blanking_mode[] = { 0x1d, 0x00 };
|
|
|
|
|
2007-12-09 04:06:30 +08:00
|
|
|
set_audio(fe, params);
|
2007-10-22 20:56:38 +08:00
|
|
|
|
2008-04-09 10:20:00 +08:00
|
|
|
tuner_dbg("%s: freq = %d\n", __func__, params->frequency);
|
2007-10-22 20:56:38 +08:00
|
|
|
|
2007-10-22 06:39:50 +08:00
|
|
|
tda8295_power(fe, 1);
|
|
|
|
tda8295_agc1_out(fe, 1);
|
2007-10-22 20:56:38 +08:00
|
|
|
|
2011-01-13 22:58:36 +08:00
|
|
|
tuner_i2c_xfer_send_recv(&priv->i2c_props,
|
|
|
|
&blanking_mode[0], 1, &blanking_mode[1], 1);
|
2007-10-22 20:56:38 +08:00
|
|
|
|
2007-10-22 06:39:50 +08:00
|
|
|
tda8295_set_video_std(fe);
|
2007-10-22 20:56:38 +08:00
|
|
|
|
|
|
|
blanking_mode[1] = 0x03;
|
|
|
|
tuner_i2c_xfer_send(&priv->i2c_props, blanking_mode, 2);
|
|
|
|
msleep(20);
|
|
|
|
|
2013-02-04 09:47:38 +08:00
|
|
|
if (fe->ops.analog_ops.i2c_gate_ctrl)
|
|
|
|
fe->ops.analog_ops.i2c_gate_ctrl(fe, 1);
|
2007-10-22 20:56:38 +08:00
|
|
|
|
2007-10-22 06:39:50 +08:00
|
|
|
if (fe->ops.tuner_ops.set_analog_params)
|
2007-12-09 04:06:30 +08:00
|
|
|
fe->ops.tuner_ops.set_analog_params(fe, params);
|
2007-10-22 20:56:38 +08:00
|
|
|
|
|
|
|
if (priv->cfg.agcf)
|
2007-10-22 06:39:50 +08:00
|
|
|
priv->cfg.agcf(fe);
|
2007-10-22 20:56:38 +08:00
|
|
|
|
2013-04-06 15:41:29 +08:00
|
|
|
tda8295_has_signal(fe, &signal);
|
|
|
|
if (signal)
|
2007-10-22 20:56:38 +08:00
|
|
|
tuner_dbg("tda8295 is locked\n");
|
|
|
|
else
|
|
|
|
tuner_dbg("tda8295 not locked, no signal?\n");
|
|
|
|
|
2013-02-04 09:47:38 +08:00
|
|
|
if (fe->ops.analog_ops.i2c_gate_ctrl)
|
|
|
|
fe->ops.analog_ops.i2c_gate_ctrl(fe, 0);
|
2007-10-22 20:56:38 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*---------------------------------------------------------------------*/
|
|
|
|
|
2013-04-06 15:41:29 +08:00
|
|
|
static int tda8290_has_signal(struct dvb_frontend *fe, u16 *signal)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2007-10-22 06:39:50 +08:00
|
|
|
struct tda8290_priv *priv = fe->analog_demod_priv;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-08-28 08:22:20 +08:00
|
|
|
unsigned char i2c_get_afc[1] = { 0x1B };
|
|
|
|
unsigned char afc = 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2011-01-13 22:58:36 +08:00
|
|
|
tuner_i2c_xfer_send_recv(&priv->i2c_props,
|
|
|
|
i2c_get_afc, ARRAY_SIZE(i2c_get_afc), &afc, 1);
|
2013-04-06 15:41:29 +08:00
|
|
|
*signal = (afc & 0x80) ? 65535 : 0;
|
|
|
|
return 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2005-11-09 13:37:48 +08:00
|
|
|
/*---------------------------------------------------------------------*/
|
|
|
|
|
2007-10-22 06:39:50 +08:00
|
|
|
static void tda8290_standby(struct dvb_frontend *fe)
|
2005-09-10 04:03:37 +08:00
|
|
|
{
|
2007-10-22 06:39:50 +08:00
|
|
|
struct tda8290_priv *priv = fe->analog_demod_priv;
|
|
|
|
|
2005-11-09 13:37:48 +08:00
|
|
|
unsigned char cb1[] = { 0x30, 0xD0 };
|
|
|
|
unsigned char tda8290_standby[] = { 0x00, 0x02 };
|
2006-02-07 16:49:09 +08:00
|
|
|
unsigned char tda8290_agc_tri[] = { 0x02, 0x20 };
|
2007-05-30 09:54:06 +08:00
|
|
|
struct i2c_msg msg = {.addr = priv->tda827x_addr, .flags=0, .buf=cb1, .len = 2};
|
2005-11-09 13:37:48 +08:00
|
|
|
|
2013-02-04 09:47:38 +08:00
|
|
|
if (fe->ops.analog_ops.i2c_gate_ctrl)
|
|
|
|
fe->ops.analog_ops.i2c_gate_ctrl(fe, 1);
|
2007-10-27 13:00:57 +08:00
|
|
|
if (priv->ver & TDA8275A)
|
2005-11-09 13:37:48 +08:00
|
|
|
cb1[1] = 0x90;
|
2007-08-21 12:24:42 +08:00
|
|
|
i2c_transfer(priv->i2c_props.adap, &msg, 1);
|
2013-02-04 09:47:38 +08:00
|
|
|
if (fe->ops.analog_ops.i2c_gate_ctrl)
|
|
|
|
fe->ops.analog_ops.i2c_gate_ctrl(fe, 0);
|
2007-08-21 12:24:42 +08:00
|
|
|
tuner_i2c_xfer_send(&priv->i2c_props, tda8290_agc_tri, 2);
|
|
|
|
tuner_i2c_xfer_send(&priv->i2c_props, tda8290_standby, 2);
|
2005-09-10 04:03:37 +08:00
|
|
|
}
|
|
|
|
|
2007-10-22 06:39:50 +08:00
|
|
|
static void tda8295_standby(struct dvb_frontend *fe)
|
2007-10-22 20:56:38 +08:00
|
|
|
{
|
2007-10-22 06:39:50 +08:00
|
|
|
tda8295_agc1_out(fe, 0); /* Put AGC in tri-state */
|
2007-10-22 20:56:38 +08:00
|
|
|
|
2007-10-22 06:39:50 +08:00
|
|
|
tda8295_power(fe, 0);
|
2007-10-22 20:56:38 +08:00
|
|
|
}
|
|
|
|
|
2007-10-22 06:39:50 +08:00
|
|
|
static void tda8290_init_if(struct dvb_frontend *fe)
|
2005-11-09 13:37:48 +08:00
|
|
|
{
|
2007-10-22 06:39:50 +08:00
|
|
|
struct tda8290_priv *priv = fe->analog_demod_priv;
|
2007-08-21 12:24:42 +08:00
|
|
|
|
2005-11-09 13:37:48 +08:00
|
|
|
unsigned char set_VS[] = { 0x30, 0x6F };
|
2007-04-27 23:31:12 +08:00
|
|
|
unsigned char set_GP00_CF[] = { 0x20, 0x01 };
|
2005-11-09 13:37:48 +08:00
|
|
|
unsigned char set_GP01_CF[] = { 0x20, 0x0B };
|
|
|
|
|
2013-04-07 01:29:29 +08:00
|
|
|
if ((priv->cfg.config == TDA8290_LNA_GP0_HIGH_ON) ||
|
|
|
|
(priv->cfg.config == TDA8290_LNA_GP0_HIGH_OFF))
|
2007-08-21 12:24:42 +08:00
|
|
|
tuner_i2c_xfer_send(&priv->i2c_props, set_GP00_CF, 2);
|
2007-04-27 23:31:12 +08:00
|
|
|
else
|
2007-08-21 12:24:42 +08:00
|
|
|
tuner_i2c_xfer_send(&priv->i2c_props, set_GP01_CF, 2);
|
|
|
|
tuner_i2c_xfer_send(&priv->i2c_props, set_VS, 2);
|
2005-11-09 13:37:48 +08:00
|
|
|
}
|
|
|
|
|
2007-10-22 06:39:50 +08:00
|
|
|
static void tda8295_init_if(struct dvb_frontend *fe)
|
2007-10-22 20:56:38 +08:00
|
|
|
{
|
2007-10-22 06:39:50 +08:00
|
|
|
struct tda8290_priv *priv = fe->analog_demod_priv;
|
2007-10-22 20:56:38 +08:00
|
|
|
|
|
|
|
static unsigned char set_adc_ctl[] = { 0x33, 0x14 };
|
|
|
|
static unsigned char set_adc_ctl2[] = { 0x34, 0x00 };
|
|
|
|
static unsigned char set_pll_reg6[] = { 0x3e, 0x63 };
|
|
|
|
static unsigned char set_pll_reg0[] = { 0x38, 0x23 };
|
|
|
|
static unsigned char set_pll_reg7[] = { 0x3f, 0x01 };
|
|
|
|
static unsigned char set_pll_reg10[] = { 0x42, 0x61 };
|
|
|
|
static unsigned char set_gpio_reg0[] = { 0x44, 0x0b };
|
|
|
|
|
2007-10-22 06:39:50 +08:00
|
|
|
tda8295_power(fe, 1);
|
2007-10-22 20:56:38 +08:00
|
|
|
|
2007-10-22 06:39:50 +08:00
|
|
|
tda8295_set_easy_mode(fe, 0);
|
|
|
|
tda8295_set_video_std(fe);
|
2007-10-22 20:56:38 +08:00
|
|
|
|
|
|
|
tuner_i2c_xfer_send(&priv->i2c_props, set_adc_ctl, 2);
|
|
|
|
tuner_i2c_xfer_send(&priv->i2c_props, set_adc_ctl2, 2);
|
|
|
|
tuner_i2c_xfer_send(&priv->i2c_props, set_pll_reg6, 2);
|
|
|
|
tuner_i2c_xfer_send(&priv->i2c_props, set_pll_reg0, 2);
|
|
|
|
tuner_i2c_xfer_send(&priv->i2c_props, set_pll_reg7, 2);
|
|
|
|
tuner_i2c_xfer_send(&priv->i2c_props, set_pll_reg10, 2);
|
|
|
|
tuner_i2c_xfer_send(&priv->i2c_props, set_gpio_reg0, 2);
|
|
|
|
|
2007-10-22 06:39:50 +08:00
|
|
|
tda8295_agc1_out(fe, 0);
|
|
|
|
tda8295_agc2_out(fe, 0);
|
2007-10-22 20:56:38 +08:00
|
|
|
}
|
|
|
|
|
2007-10-22 06:39:50 +08:00
|
|
|
static void tda8290_init_tuner(struct dvb_frontend *fe)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2007-10-22 06:39:50 +08:00
|
|
|
struct tda8290_priv *priv = fe->analog_demod_priv;
|
2005-11-09 13:37:48 +08:00
|
|
|
unsigned char tda8275_init[] = { 0x00, 0x00, 0x00, 0x40, 0xdC, 0x04, 0xAf,
|
2005-11-09 13:37:50 +08:00
|
|
|
0x3F, 0x2A, 0x04, 0xFF, 0x00, 0x00, 0x40 };
|
2005-11-09 13:37:48 +08:00
|
|
|
unsigned char tda8275a_init[] = { 0x00, 0x00, 0x00, 0x00, 0xdC, 0x05, 0x8b,
|
2005-11-09 13:37:50 +08:00
|
|
|
0x0c, 0x04, 0x20, 0xFF, 0x00, 0x00, 0x4b };
|
2007-05-30 09:54:06 +08:00
|
|
|
struct i2c_msg msg = {.addr = priv->tda827x_addr, .flags=0,
|
2005-11-09 13:37:51 +08:00
|
|
|
.buf=tda8275_init, .len = 14};
|
2007-10-27 13:00:57 +08:00
|
|
|
if (priv->ver & TDA8275A)
|
2005-11-09 13:37:48 +08:00
|
|
|
msg.buf = tda8275a_init;
|
|
|
|
|
2013-02-04 09:47:38 +08:00
|
|
|
if (fe->ops.analog_ops.i2c_gate_ctrl)
|
|
|
|
fe->ops.analog_ops.i2c_gate_ctrl(fe, 1);
|
2007-08-21 12:24:42 +08:00
|
|
|
i2c_transfer(priv->i2c_props.adap, &msg, 1);
|
2013-02-04 09:47:38 +08:00
|
|
|
if (fe->ops.analog_ops.i2c_gate_ctrl)
|
|
|
|
fe->ops.analog_ops.i2c_gate_ctrl(fe, 0);
|
2005-11-09 13:37:48 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*---------------------------------------------------------------------*/
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-10-22 06:39:50 +08:00
|
|
|
static void tda829x_release(struct dvb_frontend *fe)
|
2007-06-05 02:20:11 +08:00
|
|
|
{
|
2008-01-07 02:52:56 +08:00
|
|
|
struct tda8290_priv *priv = fe->analog_demod_priv;
|
|
|
|
|
2008-01-09 21:44:27 +08:00
|
|
|
/* only try to release the tuner if we've
|
|
|
|
* attached it from within this module */
|
|
|
|
if (priv->ver & (TDA18271 | TDA8275 | TDA8275A))
|
2008-01-07 02:52:56 +08:00
|
|
|
if (fe->ops.tuner_ops.release)
|
|
|
|
fe->ops.tuner_ops.release(fe);
|
2007-06-05 02:20:11 +08:00
|
|
|
|
2007-10-22 06:39:50 +08:00
|
|
|
kfree(fe->analog_demod_priv);
|
|
|
|
fe->analog_demod_priv = NULL;
|
2007-08-28 08:22:20 +08:00
|
|
|
}
|
|
|
|
|
2008-01-02 14:01:54 +08:00
|
|
|
static struct tda18271_config tda829x_tda18271_config = {
|
|
|
|
.gate = TDA18271_GATE_ANALOG,
|
|
|
|
};
|
|
|
|
|
2007-10-27 13:00:57 +08:00
|
|
|
static int tda829x_find_tuner(struct dvb_frontend *fe)
|
2005-11-09 13:37:48 +08:00
|
|
|
{
|
2007-10-27 13:00:57 +08:00
|
|
|
struct tda8290_priv *priv = fe->analog_demod_priv;
|
2005-11-09 13:37:48 +08:00
|
|
|
int i, ret, tuners_found;
|
|
|
|
u32 tuner_addrs;
|
2007-10-27 13:00:57 +08:00
|
|
|
u8 data;
|
|
|
|
struct i2c_msg msg = { .flags = I2C_M_RD, .buf = &data, .len = 1 };
|
2005-11-09 13:37:48 +08:00
|
|
|
|
2013-02-04 09:47:38 +08:00
|
|
|
if (fe->ops.analog_ops.i2c_gate_ctrl)
|
|
|
|
fe->ops.analog_ops.i2c_gate_ctrl(fe, 1);
|
2007-08-21 12:24:42 +08:00
|
|
|
|
2005-11-09 13:37:48 +08:00
|
|
|
/* probe for tuner chip */
|
|
|
|
tuners_found = 0;
|
|
|
|
tuner_addrs = 0;
|
2007-10-27 13:00:57 +08:00
|
|
|
for (i = 0x60; i <= 0x63; i++) {
|
2005-11-09 13:37:48 +08:00
|
|
|
msg.addr = i;
|
2007-08-21 12:24:42 +08:00
|
|
|
ret = i2c_transfer(priv->i2c_props.adap, &msg, 1);
|
2005-11-09 13:37:48 +08:00
|
|
|
if (ret == 1) {
|
|
|
|
tuners_found++;
|
|
|
|
tuner_addrs = (tuner_addrs << 8) + i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* if there is more than one tuner, we expect the right one is
|
|
|
|
behind the bridge and we choose the highest address that doesn't
|
|
|
|
give a response now
|
|
|
|
*/
|
2007-10-27 13:00:57 +08:00
|
|
|
|
2013-02-04 09:47:38 +08:00
|
|
|
if (fe->ops.analog_ops.i2c_gate_ctrl)
|
|
|
|
fe->ops.analog_ops.i2c_gate_ctrl(fe, 0);
|
2007-10-27 13:00:57 +08:00
|
|
|
|
|
|
|
if (tuners_found > 1)
|
2005-11-09 13:37:48 +08:00
|
|
|
for (i = 0; i < tuners_found; i++) {
|
|
|
|
msg.addr = tuner_addrs & 0xff;
|
2007-08-21 12:24:42 +08:00
|
|
|
ret = i2c_transfer(priv->i2c_props.adap, &msg, 1);
|
2007-10-27 13:00:57 +08:00
|
|
|
if (ret == 1)
|
2005-11-09 13:37:48 +08:00
|
|
|
tuner_addrs = tuner_addrs >> 8;
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
}
|
2007-10-27 13:00:57 +08:00
|
|
|
|
2005-11-09 13:37:48 +08:00
|
|
|
if (tuner_addrs == 0) {
|
2007-10-27 13:00:57 +08:00
|
|
|
tuner_addrs = 0x60;
|
|
|
|
tuner_info("could not clearly identify tuner address, "
|
|
|
|
"defaulting to %x\n", tuner_addrs);
|
2005-11-09 13:37:48 +08:00
|
|
|
} else {
|
|
|
|
tuner_addrs = tuner_addrs & 0xff;
|
2007-08-28 08:22:20 +08:00
|
|
|
tuner_info("setting tuner address to %x\n", tuner_addrs);
|
2005-11-09 13:37:48 +08:00
|
|
|
}
|
2007-05-30 09:54:06 +08:00
|
|
|
priv->tda827x_addr = tuner_addrs;
|
2005-11-09 13:37:48 +08:00
|
|
|
msg.addr = tuner_addrs;
|
|
|
|
|
2013-02-04 09:47:38 +08:00
|
|
|
if (fe->ops.analog_ops.i2c_gate_ctrl)
|
|
|
|
fe->ops.analog_ops.i2c_gate_ctrl(fe, 1);
|
2007-08-21 12:24:42 +08:00
|
|
|
ret = i2c_transfer(priv->i2c_props.adap, &msg, 1);
|
2007-08-28 08:22:20 +08:00
|
|
|
|
2007-10-27 13:00:57 +08:00
|
|
|
if (ret != 1) {
|
|
|
|
tuner_warn("tuner access failed!\n");
|
2013-02-04 09:47:38 +08:00
|
|
|
if (fe->ops.analog_ops.i2c_gate_ctrl)
|
|
|
|
fe->ops.analog_ops.i2c_gate_ctrl(fe, 0);
|
2007-10-27 13:00:57 +08:00
|
|
|
return -EREMOTEIO;
|
2005-11-09 13:37:48 +08:00
|
|
|
}
|
2007-08-26 06:08:45 +08:00
|
|
|
|
2008-01-02 09:52:09 +08:00
|
|
|
if ((data == 0x83) || (data == 0x84)) {
|
2007-10-27 13:00:57 +08:00
|
|
|
priv->ver |= TDA18271;
|
2009-03-05 06:42:06 +08:00
|
|
|
tda829x_tda18271_config.config = priv->cfg.config;
|
2013-02-04 09:48:56 +08:00
|
|
|
tda829x_tda18271_config.std_map = priv->tda18271_std_map;
|
2008-04-29 14:54:19 +08:00
|
|
|
dvb_attach(tda18271_attach, fe, priv->tda827x_addr,
|
|
|
|
priv->i2c_props.adap, &tda829x_tda18271_config);
|
2007-10-27 13:00:57 +08:00
|
|
|
} else {
|
|
|
|
if ((data & 0x3c) == 0)
|
|
|
|
priv->ver |= TDA8275;
|
|
|
|
else
|
|
|
|
priv->ver |= TDA8275A;
|
2007-06-07 03:15:15 +08:00
|
|
|
|
2008-04-29 14:54:19 +08:00
|
|
|
dvb_attach(tda827x_attach, fe, priv->tda827x_addr,
|
|
|
|
priv->i2c_props.adap, &priv->cfg);
|
2008-04-23 01:46:08 +08:00
|
|
|
priv->cfg.switch_addr = priv->i2c_props.addr;
|
2007-10-27 13:00:57 +08:00
|
|
|
}
|
2007-12-09 03:25:41 +08:00
|
|
|
if (fe->ops.tuner_ops.init)
|
|
|
|
fe->ops.tuner_ops.init(fe);
|
2007-11-24 02:08:11 +08:00
|
|
|
|
2007-12-09 03:25:41 +08:00
|
|
|
if (fe->ops.tuner_ops.sleep)
|
|
|
|
fe->ops.tuner_ops.sleep(fe);
|
2007-11-24 02:08:11 +08:00
|
|
|
|
2013-02-04 09:47:38 +08:00
|
|
|
if (fe->ops.analog_ops.i2c_gate_ctrl)
|
|
|
|
fe->ops.analog_ops.i2c_gate_ctrl(fe, 0);
|
2007-08-26 06:08:45 +08:00
|
|
|
|
|
|
|
return 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2007-10-22 20:56:38 +08:00
|
|
|
|
2007-11-04 09:14:54 +08:00
|
|
|
static int tda8290_probe(struct tuner_i2c_props *i2c_props)
|
|
|
|
{
|
|
|
|
#define TDA8290_ID 0x89
|
2011-01-13 22:58:36 +08:00
|
|
|
u8 reg = 0x1f, id;
|
|
|
|
struct i2c_msg msg_read[] = {
|
2011-03-01 23:38:48 +08:00
|
|
|
{ .addr = i2c_props->addr, .flags = 0, .len = 1, .buf = ® },
|
|
|
|
{ .addr = i2c_props->addr, .flags = I2C_M_RD, .len = 1, .buf = &id },
|
2011-01-13 22:58:36 +08:00
|
|
|
};
|
2007-11-04 09:14:54 +08:00
|
|
|
|
|
|
|
/* detect tda8290 */
|
2011-01-13 22:58:36 +08:00
|
|
|
if (i2c_transfer(i2c_props->adap, msg_read, 2) != 2) {
|
2011-03-01 23:38:48 +08:00
|
|
|
printk(KERN_WARNING "%s: couldn't read register 0x%02x\n",
|
2011-01-13 22:58:36 +08:00
|
|
|
__func__, reg);
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
2007-11-04 09:14:54 +08:00
|
|
|
|
2011-01-13 22:58:36 +08:00
|
|
|
if (id == TDA8290_ID) {
|
2007-12-09 13:26:48 +08:00
|
|
|
if (debug)
|
2007-11-04 09:14:54 +08:00
|
|
|
printk(KERN_DEBUG "%s: tda8290 detected @ %d-%04x\n",
|
2008-04-09 10:20:00 +08:00
|
|
|
__func__, i2c_adapter_id(i2c_props->adap),
|
2007-11-04 09:14:54 +08:00
|
|
|
i2c_props->addr);
|
|
|
|
return 0;
|
|
|
|
}
|
2007-11-04 21:51:28 +08:00
|
|
|
return -ENODEV;
|
2007-11-04 09:14:54 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int tda8295_probe(struct tuner_i2c_props *i2c_props)
|
|
|
|
{
|
|
|
|
#define TDA8295_ID 0x8a
|
2009-12-31 15:32:29 +08:00
|
|
|
#define TDA8295C2_ID 0x8b
|
2011-01-13 22:58:36 +08:00
|
|
|
u8 reg = 0x2f, id;
|
|
|
|
struct i2c_msg msg_read[] = {
|
2011-03-01 23:38:48 +08:00
|
|
|
{ .addr = i2c_props->addr, .flags = 0, .len = 1, .buf = ® },
|
|
|
|
{ .addr = i2c_props->addr, .flags = I2C_M_RD, .len = 1, .buf = &id },
|
2011-01-13 22:58:36 +08:00
|
|
|
};
|
2007-11-04 09:14:54 +08:00
|
|
|
|
2011-03-01 23:38:48 +08:00
|
|
|
/* detect tda8295 */
|
2011-01-13 22:58:36 +08:00
|
|
|
if (i2c_transfer(i2c_props->adap, msg_read, 2) != 2) {
|
2011-03-01 23:38:48 +08:00
|
|
|
printk(KERN_WARNING "%s: couldn't read register 0x%02x\n",
|
2011-01-13 22:58:36 +08:00
|
|
|
__func__, reg);
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
2007-11-04 09:14:54 +08:00
|
|
|
|
2011-01-13 22:58:36 +08:00
|
|
|
if ((id & 0xfe) == TDA8295_ID) {
|
2007-12-09 13:26:48 +08:00
|
|
|
if (debug)
|
2009-12-31 15:32:29 +08:00
|
|
|
printk(KERN_DEBUG "%s: %s detected @ %d-%04x\n",
|
2011-01-13 22:58:36 +08:00
|
|
|
__func__, (id == TDA8295_ID) ?
|
2009-12-31 15:32:29 +08:00
|
|
|
"tda8295c1" : "tda8295c2",
|
|
|
|
i2c_adapter_id(i2c_props->adap),
|
2007-11-04 09:14:54 +08:00
|
|
|
i2c_props->addr);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-11-04 21:51:28 +08:00
|
|
|
return -ENODEV;
|
2007-11-04 09:14:54 +08:00
|
|
|
}
|
|
|
|
|
2007-12-21 22:18:32 +08:00
|
|
|
static struct analog_demod_ops tda8290_ops = {
|
2007-12-09 04:06:30 +08:00
|
|
|
.set_params = tda8290_set_params,
|
2007-10-27 13:00:57 +08:00
|
|
|
.has_signal = tda8290_has_signal,
|
|
|
|
.standby = tda8290_standby,
|
|
|
|
.release = tda829x_release,
|
|
|
|
.i2c_gate_ctrl = tda8290_i2c_bridge,
|
|
|
|
};
|
|
|
|
|
2007-12-21 22:18:32 +08:00
|
|
|
static struct analog_demod_ops tda8295_ops = {
|
2007-12-09 04:06:30 +08:00
|
|
|
.set_params = tda8295_set_params,
|
2007-10-27 13:00:57 +08:00
|
|
|
.has_signal = tda8295_has_signal,
|
|
|
|
.standby = tda8295_standby,
|
|
|
|
.release = tda829x_release,
|
|
|
|
.i2c_gate_ctrl = tda8295_i2c_bridge,
|
|
|
|
};
|
|
|
|
|
2007-12-09 13:26:48 +08:00
|
|
|
struct dvb_frontend *tda829x_attach(struct dvb_frontend *fe,
|
|
|
|
struct i2c_adapter *i2c_adap, u8 i2c_addr,
|
|
|
|
struct tda829x_config *cfg)
|
2007-10-22 20:56:38 +08:00
|
|
|
{
|
|
|
|
struct tda8290_priv *priv = NULL;
|
2007-12-09 13:26:48 +08:00
|
|
|
char *name;
|
2007-10-27 13:00:57 +08:00
|
|
|
|
2007-10-22 20:56:38 +08:00
|
|
|
priv = kzalloc(sizeof(struct tda8290_priv), GFP_KERNEL);
|
|
|
|
if (priv == NULL)
|
2007-12-09 13:26:48 +08:00
|
|
|
return NULL;
|
2007-10-27 13:00:57 +08:00
|
|
|
fe->analog_demod_priv = priv;
|
2007-10-22 20:56:38 +08:00
|
|
|
|
2007-12-09 13:26:48 +08:00
|
|
|
priv->i2c_props.addr = i2c_addr;
|
|
|
|
priv->i2c_props.adap = i2c_adap;
|
2008-04-23 01:41:53 +08:00
|
|
|
priv->i2c_props.name = "tda829x";
|
2013-02-04 09:48:56 +08:00
|
|
|
if (cfg) {
|
|
|
|
priv->cfg.config = cfg->lna_cfg;
|
|
|
|
priv->tda18271_std_map = cfg->tda18271_std_map;
|
|
|
|
}
|
2007-10-22 20:56:38 +08:00
|
|
|
|
2007-11-04 09:14:54 +08:00
|
|
|
if (tda8290_probe(&priv->i2c_props) == 0) {
|
2007-10-27 13:00:57 +08:00
|
|
|
priv->ver = TDA8290;
|
2007-12-21 22:18:32 +08:00
|
|
|
memcpy(&fe->ops.analog_ops, &tda8290_ops,
|
|
|
|
sizeof(struct analog_demod_ops));
|
2007-10-22 20:56:38 +08:00
|
|
|
}
|
2007-10-27 13:00:57 +08:00
|
|
|
|
2007-11-04 09:14:54 +08:00
|
|
|
if (tda8295_probe(&priv->i2c_props) == 0) {
|
2007-10-27 13:00:57 +08:00
|
|
|
priv->ver = TDA8295;
|
2007-12-21 22:18:32 +08:00
|
|
|
memcpy(&fe->ops.analog_ops, &tda8295_ops,
|
|
|
|
sizeof(struct analog_demod_ops));
|
2007-10-22 20:56:38 +08:00
|
|
|
}
|
|
|
|
|
2013-02-04 09:47:38 +08:00
|
|
|
if (cfg && cfg->no_i2c_gate)
|
|
|
|
fe->ops.analog_ops.i2c_gate_ctrl = NULL;
|
|
|
|
|
2011-01-14 01:01:39 +08:00
|
|
|
if (!(cfg) || (TDA829X_PROBE_TUNER == cfg->probe_tuner)) {
|
|
|
|
tda8295_power(fe, 1);
|
|
|
|
if (tda829x_find_tuner(fe) < 0)
|
|
|
|
goto fail;
|
2011-01-13 23:02:00 +08:00
|
|
|
}
|
2007-10-22 20:56:38 +08:00
|
|
|
|
2007-12-09 13:26:48 +08:00
|
|
|
switch (priv->ver) {
|
2007-12-24 15:36:14 +08:00
|
|
|
case TDA8290:
|
|
|
|
name = "tda8290";
|
|
|
|
break;
|
|
|
|
case TDA8295:
|
|
|
|
name = "tda8295";
|
|
|
|
break;
|
2007-12-09 13:26:48 +08:00
|
|
|
case TDA8290 | TDA8275:
|
|
|
|
name = "tda8290+75";
|
|
|
|
break;
|
|
|
|
case TDA8295 | TDA8275:
|
|
|
|
name = "tda8295+75";
|
|
|
|
break;
|
|
|
|
case TDA8290 | TDA8275A:
|
|
|
|
name = "tda8290+75a";
|
|
|
|
break;
|
|
|
|
case TDA8295 | TDA8275A:
|
|
|
|
name = "tda8295+75a";
|
|
|
|
break;
|
|
|
|
case TDA8290 | TDA18271:
|
|
|
|
name = "tda8290+18271";
|
|
|
|
break;
|
|
|
|
case TDA8295 | TDA18271:
|
|
|
|
name = "tda8295+18271";
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
tuner_info("type set to %s\n", name);
|
|
|
|
|
2008-01-21 21:55:37 +08:00
|
|
|
fe->ops.analog_ops.info.name = name;
|
|
|
|
|
2007-10-27 13:00:57 +08:00
|
|
|
if (priv->ver & TDA8290) {
|
2009-01-06 05:25:04 +08:00
|
|
|
if (priv->ver & (TDA8275 | TDA8275A))
|
|
|
|
tda8290_init_tuner(fe);
|
2007-10-27 13:00:57 +08:00
|
|
|
tda8290_init_if(fe);
|
|
|
|
} else if (priv->ver & TDA8295)
|
|
|
|
tda8295_init_if(fe);
|
2007-10-22 20:56:38 +08:00
|
|
|
|
2007-12-09 13:26:48 +08:00
|
|
|
return fe;
|
2007-12-09 16:16:10 +08:00
|
|
|
|
|
|
|
fail:
|
2011-01-14 01:01:39 +08:00
|
|
|
memset(&fe->ops.analog_ops, 0, sizeof(struct analog_demod_ops));
|
|
|
|
|
2007-12-09 16:16:10 +08:00
|
|
|
tda829x_release(fe);
|
2007-12-09 13:26:48 +08:00
|
|
|
return NULL;
|
2007-10-22 20:56:38 +08:00
|
|
|
}
|
2007-10-27 13:00:57 +08:00
|
|
|
EXPORT_SYMBOL_GPL(tda829x_attach);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-12-09 13:26:48 +08:00
|
|
|
int tda829x_probe(struct i2c_adapter *i2c_adap, u8 i2c_addr)
|
2005-11-09 13:38:00 +08:00
|
|
|
{
|
2007-08-28 08:22:20 +08:00
|
|
|
struct tuner_i2c_props i2c_props = {
|
2007-12-09 13:26:48 +08:00
|
|
|
.adap = i2c_adap,
|
|
|
|
.addr = i2c_addr,
|
2007-08-28 08:22:20 +08:00
|
|
|
};
|
2007-08-21 12:24:42 +08:00
|
|
|
|
2006-02-27 11:09:11 +08:00
|
|
|
unsigned char soft_reset[] = { 0x00, 0x00 };
|
|
|
|
unsigned char easy_mode_b[] = { 0x01, 0x02 };
|
|
|
|
unsigned char easy_mode_g[] = { 0x01, 0x04 };
|
|
|
|
unsigned char restore_9886[] = { 0x00, 0xd6, 0x30 };
|
2005-11-09 13:38:00 +08:00
|
|
|
unsigned char addr_dto_lsb = 0x07;
|
|
|
|
unsigned char data;
|
2007-11-04 22:03:22 +08:00
|
|
|
#define PROBE_BUFFER_SIZE 8
|
|
|
|
unsigned char buf[PROBE_BUFFER_SIZE];
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* rule out tda9887, which would return the same byte repeatedly */
|
2011-01-13 22:58:36 +08:00
|
|
|
tuner_i2c_xfer_send_recv(&i2c_props,
|
|
|
|
soft_reset, 1, buf, PROBE_BUFFER_SIZE);
|
2007-11-04 22:03:22 +08:00
|
|
|
for (i = 1; i < PROBE_BUFFER_SIZE; i++) {
|
2007-11-05 20:54:42 +08:00
|
|
|
if (buf[i] != buf[0])
|
|
|
|
break;
|
2007-11-04 22:03:22 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* all bytes are equal, not a tda829x - probably a tda9887 */
|
|
|
|
if (i == PROBE_BUFFER_SIZE)
|
|
|
|
return -ENODEV;
|
2005-11-09 13:38:00 +08:00
|
|
|
|
2007-11-04 09:14:54 +08:00
|
|
|
if ((tda8290_probe(&i2c_props) == 0) ||
|
|
|
|
(tda8295_probe(&i2c_props) == 0))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* fall back to old probing method */
|
2007-08-28 08:22:20 +08:00
|
|
|
tuner_i2c_xfer_send(&i2c_props, easy_mode_b, 2);
|
|
|
|
tuner_i2c_xfer_send(&i2c_props, soft_reset, 2);
|
2011-01-13 22:58:36 +08:00
|
|
|
tuner_i2c_xfer_send_recv(&i2c_props, &addr_dto_lsb, 1, &data, 1);
|
2005-11-09 13:38:00 +08:00
|
|
|
if (data == 0) {
|
2007-08-28 08:22:20 +08:00
|
|
|
tuner_i2c_xfer_send(&i2c_props, easy_mode_g, 2);
|
|
|
|
tuner_i2c_xfer_send(&i2c_props, soft_reset, 2);
|
2011-01-13 22:58:36 +08:00
|
|
|
tuner_i2c_xfer_send_recv(&i2c_props,
|
|
|
|
&addr_dto_lsb, 1, &data, 1);
|
2005-11-09 13:38:00 +08:00
|
|
|
if (data == 0x7b) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
2007-08-28 08:22:20 +08:00
|
|
|
tuner_i2c_xfer_send(&i2c_props, restore_9886, 3);
|
2007-11-04 21:51:28 +08:00
|
|
|
return -ENODEV;
|
2005-11-09 13:38:00 +08:00
|
|
|
}
|
2007-11-04 09:14:54 +08:00
|
|
|
EXPORT_SYMBOL_GPL(tda829x_probe);
|
2007-08-28 08:22:20 +08:00
|
|
|
|
2007-10-27 13:00:57 +08:00
|
|
|
MODULE_DESCRIPTION("Philips/NXP TDA8290/TDA8295 analog IF demodulator driver");
|
2007-10-22 20:56:38 +08:00
|
|
|
MODULE_AUTHOR("Gerd Knorr, Hartmut Hackmann, Michael Krufky");
|
2007-08-28 08:22:20 +08:00
|
|
|
MODULE_LICENSE("GPL");
|