V4L/DVB(7767): Move tuners to common/tuners
There were several issues in the past, caused by the hybrid tuner design, since now, the same tuner can be used by drivers/media/dvb and drivers/media/video. Kconfig items were rearranged, to split V4L/DVB core from their drivers. Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
This commit is contained in:
parent
5fe95e0b86
commit
7c91f0624a
49 changed files with 248 additions and 198 deletions
|
|
@ -5,6 +5,12 @@
|
|||
menu "Multimedia devices"
|
||||
depends on HAS_IOMEM
|
||||
|
||||
comment "Multimedia core support"
|
||||
|
||||
#
|
||||
# V4L core and enabled API's
|
||||
#
|
||||
|
||||
config VIDEO_DEV
|
||||
tristate "Video For Linux"
|
||||
---help---
|
||||
|
|
@ -58,136 +64,60 @@ config VIDEO_V4L1_COMPAT
|
|||
|
||||
If you are unsure as to whether this is required, answer Y.
|
||||
|
||||
config VIDEO_V4L2
|
||||
tristate
|
||||
depends on VIDEO_DEV && VIDEO_V4L2_COMMON
|
||||
default VIDEO_DEV && VIDEO_V4L2_COMMON
|
||||
#
|
||||
# DVB Core
|
||||
#
|
||||
|
||||
config VIDEO_V4L1
|
||||
config DVB_CORE
|
||||
tristate "DVB for Linux"
|
||||
depends on NET && INET
|
||||
select CRC32
|
||||
help
|
||||
Support Digital Video Broadcasting hardware. Enable this if you
|
||||
own a DVB adapter and want to use it or if you compile Linux for
|
||||
a digital SetTopBox.
|
||||
|
||||
DVB core utility functions for device handling, software fallbacks etc.
|
||||
Say Y when you have a DVB card and want to use it. Say Y if your want
|
||||
to build your drivers outside the kernel, but need the DVB core. All
|
||||
in-kernel drivers will select this automatically if needed.
|
||||
|
||||
API specs and user tools are available from <http://www.linuxtv.org/>.
|
||||
|
||||
Please report problems regarding this driver to the LinuxDVB
|
||||
mailing list.
|
||||
|
||||
If unsure say N.
|
||||
|
||||
config VIDEO_MEDIA
|
||||
tristate
|
||||
depends on VIDEO_DEV && VIDEO_V4L2_COMMON && VIDEO_ALLOW_V4L1
|
||||
default VIDEO_DEV && VIDEO_V4L2_COMMON && VIDEO_ALLOW_V4L1
|
||||
default DVB_CORE || VIDEO_DEV
|
||||
depends on DVB_CORE || VIDEO_DEV
|
||||
|
||||
comment "Multimedia drivers"
|
||||
|
||||
source "drivers/media/common/Kconfig"
|
||||
|
||||
#
|
||||
# Tuner drivers for DVB and V4L
|
||||
#
|
||||
|
||||
source "drivers/media/common/tuners/Kconfig"
|
||||
|
||||
#
|
||||
# Video/Radio/Hybrid adapters
|
||||
#
|
||||
|
||||
source "drivers/media/video/Kconfig"
|
||||
|
||||
source "drivers/media/radio/Kconfig"
|
||||
|
||||
#
|
||||
# DVB adapters
|
||||
#
|
||||
|
||||
source "drivers/media/dvb/Kconfig"
|
||||
|
||||
source "drivers/media/common/Kconfig"
|
||||
|
||||
config VIDEO_TUNER
|
||||
tristate
|
||||
depends on I2C
|
||||
select TUNER_XC2028 if !VIDEO_TUNER_CUSTOMIZE
|
||||
select TUNER_MT20XX if !VIDEO_TUNER_CUSTOMIZE
|
||||
select TUNER_TDA8290 if !VIDEO_TUNER_CUSTOMIZE
|
||||
select TUNER_TEA5761 if !VIDEO_TUNER_CUSTOMIZE
|
||||
select TUNER_TEA5767 if !VIDEO_TUNER_CUSTOMIZE
|
||||
select TUNER_SIMPLE if !VIDEO_TUNER_CUSTOMIZE
|
||||
select TUNER_TDA9887 if !VIDEO_TUNER_CUSTOMIZE
|
||||
|
||||
menuconfig VIDEO_TUNER_CUSTOMIZE
|
||||
bool "Customize analog tuner modules to build"
|
||||
depends on VIDEO_TUNER
|
||||
help
|
||||
This allows the user to deselect tuner drivers unnecessary
|
||||
for their hardware from the build. Use this option with care
|
||||
as deselecting tuner drivers which are in fact necessary will
|
||||
result in V4L devices which cannot be tuned due to lack of
|
||||
driver support
|
||||
|
||||
If unsure say N.
|
||||
|
||||
if VIDEO_TUNER_CUSTOMIZE
|
||||
|
||||
config TUNER_XC2028
|
||||
tristate "XCeive xc2028/xc3028 tuners"
|
||||
depends on I2C && FW_LOADER
|
||||
default m if VIDEO_TUNER_CUSTOMIZE
|
||||
help
|
||||
Say Y here to include support for the xc2028/xc3028 tuners.
|
||||
|
||||
config TUNER_MT20XX
|
||||
tristate "Microtune 2032 / 2050 tuners"
|
||||
depends on I2C
|
||||
default m if VIDEO_TUNER_CUSTOMIZE
|
||||
help
|
||||
Say Y here to include support for the MT2032 / MT2050 tuner.
|
||||
|
||||
config TUNER_TDA8290
|
||||
tristate "TDA 8290/8295 + 8275(a)/18271 tuner combo"
|
||||
depends on I2C
|
||||
select DVB_TDA827X
|
||||
select DVB_TDA18271
|
||||
default m if VIDEO_TUNER_CUSTOMIZE
|
||||
help
|
||||
Say Y here to include support for Philips TDA8290+8275(a) tuner.
|
||||
|
||||
config TUNER_TEA5761
|
||||
tristate "TEA 5761 radio tuner (EXPERIMENTAL)"
|
||||
depends on I2C && EXPERIMENTAL
|
||||
default m if VIDEO_TUNER_CUSTOMIZE
|
||||
help
|
||||
Say Y here to include support for the Philips TEA5761 radio tuner.
|
||||
|
||||
config TUNER_TEA5767
|
||||
tristate "TEA 5767 radio tuner"
|
||||
depends on I2C
|
||||
default m if VIDEO_TUNER_CUSTOMIZE
|
||||
help
|
||||
Say Y here to include support for the Philips TEA5767 radio tuner.
|
||||
|
||||
config TUNER_SIMPLE
|
||||
tristate "Simple tuner support"
|
||||
depends on I2C
|
||||
select TUNER_TDA9887
|
||||
default m if VIDEO_TUNER_CUSTOMIZE
|
||||
help
|
||||
Say Y here to include support for various simple tuners.
|
||||
|
||||
config TUNER_TDA9887
|
||||
tristate "TDA 9885/6/7 analog IF demodulator"
|
||||
depends on I2C
|
||||
default m if VIDEO_TUNER_CUSTOMIZE
|
||||
help
|
||||
Say Y here to include support for Philips TDA9885/6/7
|
||||
analog IF demodulator.
|
||||
|
||||
endif # VIDEO_TUNER_CUSTOMIZE
|
||||
|
||||
config VIDEOBUF_GEN
|
||||
tristate
|
||||
|
||||
config VIDEOBUF_DMA_SG
|
||||
depends on HAS_DMA
|
||||
select VIDEOBUF_GEN
|
||||
tristate
|
||||
|
||||
config VIDEOBUF_VMALLOC
|
||||
select VIDEOBUF_GEN
|
||||
tristate
|
||||
|
||||
config VIDEOBUF_DVB
|
||||
tristate
|
||||
select VIDEOBUF_GEN
|
||||
select VIDEOBUF_DMA_SG
|
||||
|
||||
config VIDEO_BTCX
|
||||
tristate
|
||||
|
||||
config VIDEO_IR_I2C
|
||||
tristate
|
||||
|
||||
config VIDEO_IR
|
||||
tristate
|
||||
depends on INPUT
|
||||
select VIDEO_IR_I2C if I2C
|
||||
|
||||
config VIDEO_TVEEPROM
|
||||
tristate
|
||||
depends on I2C
|
||||
|
||||
config DAB
|
||||
boolean "DAB adapters"
|
||||
---help---
|
||||
|
|
|
|||
|
|
@ -2,10 +2,10 @@
|
|||
# Makefile for the kernel multimedia device drivers.
|
||||
#
|
||||
|
||||
obj-y := common/
|
||||
obj-y += video/
|
||||
obj-$(CONFIG_VIDEO_MEDIA) += common/
|
||||
|
||||
# Since hybrid devices are here, should be compiled if DVB and/or V4L
|
||||
obj-$(CONFIG_VIDEO_MEDIA) += video/
|
||||
|
||||
obj-$(CONFIG_VIDEO_DEV) += radio/
|
||||
obj-$(CONFIG_DVB_CORE) += dvb/
|
||||
ifeq ($(CONFIG_DVB_CORE),)
|
||||
obj-$(CONFIG_VIDEO_TUNER) += dvb/frontends/
|
||||
endif
|
||||
|
|
|
|||
118
drivers/media/common/tuners/Kconfig
Normal file
118
drivers/media/common/tuners/Kconfig
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
config DVB_CORE_ATTACH
|
||||
bool "Load and attach frontend driver modules as needed"
|
||||
depends on DVB_CORE
|
||||
depends on MODULES
|
||||
help
|
||||
Remove the static dependency of DVB card drivers on all
|
||||
frontend modules for all possible card variants. Instead,
|
||||
allow the card drivers to only load the frontend modules
|
||||
they require. This saves several KBytes of memory.
|
||||
|
||||
Note: You will need module-init-tools v3.2 or later for this feature.
|
||||
|
||||
If unsure say Y.
|
||||
|
||||
config VIDEO_TUNER
|
||||
tristate
|
||||
default DVB_CORE || VIDEO_DEV
|
||||
depends on DVB_CORE || VIDEO_DEV
|
||||
select TUNER_XC2028 if !VIDEO_TUNER_CUSTOMIZE
|
||||
select DVB_TUNER_XC5000 if !VIDEO_TUNER_CUSTOMIZE
|
||||
select TUNER_MT20XX if !VIDEO_TUNER_CUSTOMIZE
|
||||
select TUNER_TDA8290 if !VIDEO_TUNER_CUSTOMIZE
|
||||
select TUNER_TEA5761 if !VIDEO_TUNER_CUSTOMIZE
|
||||
select TUNER_TEA5767 if !VIDEO_TUNER_CUSTOMIZE
|
||||
select TUNER_SIMPLE if !VIDEO_TUNER_CUSTOMIZE
|
||||
select TUNER_TDA9887 if !VIDEO_TUNER_CUSTOMIZE
|
||||
|
||||
menuconfig VIDEO_TUNER_CUSTOMIZE
|
||||
bool "Customize analog and hybrid tuner modules to build"
|
||||
depends on VIDEO_TUNER
|
||||
help
|
||||
This allows the user to deselect tuner drivers unnecessary
|
||||
for their hardware from the build. Use this option with care
|
||||
as deselecting tuner drivers which are in fact necessary will
|
||||
result in V4L/DVB devices which cannot be tuned due to lack of
|
||||
driver support
|
||||
|
||||
If unsure say N.
|
||||
|
||||
if VIDEO_TUNER_CUSTOMIZE
|
||||
|
||||
config TUNER_SIMPLE
|
||||
tristate "Simple tuner support"
|
||||
depends on I2C
|
||||
select TUNER_TDA9887
|
||||
default m if VIDEO_TUNER_CUSTOMIZE
|
||||
help
|
||||
Say Y here to include support for various simple tuners.
|
||||
|
||||
config TUNER_TDA8290
|
||||
tristate "TDA 8290/8295 + 8275(a)/18271 tuner combo"
|
||||
depends on I2C
|
||||
select DVB_TDA827X
|
||||
select DVB_TDA18271
|
||||
default m if VIDEO_TUNER_CUSTOMIZE
|
||||
help
|
||||
Say Y here to include support for Philips TDA8290+8275(a) tuner.
|
||||
|
||||
config DVB_TDA827X
|
||||
tristate "Philips TDA827X silicon tuner"
|
||||
depends on DVB_CORE && I2C
|
||||
default m if DVB_FE_CUSTOMISE
|
||||
help
|
||||
A DVB-T silicon tuner module. Say Y when you want to support this tuner.
|
||||
|
||||
config DVB_TDA18271
|
||||
tristate "NXP TDA18271 silicon tuner"
|
||||
depends on I2C
|
||||
default m if DVB_FE_CUSTOMISE
|
||||
help
|
||||
A silicon tuner module. Say Y when you want to support this tuner.
|
||||
|
||||
config TUNER_TDA9887
|
||||
tristate "TDA 9885/6/7 analog IF demodulator"
|
||||
depends on I2C
|
||||
default m if VIDEO_TUNER_CUSTOMIZE
|
||||
help
|
||||
Say Y here to include support for Philips TDA9885/6/7
|
||||
analog IF demodulator.
|
||||
|
||||
config TUNER_TEA5761
|
||||
tristate "TEA 5761 radio tuner (EXPERIMENTAL)"
|
||||
depends on I2C && EXPERIMENTAL
|
||||
default m if VIDEO_TUNER_CUSTOMIZE
|
||||
help
|
||||
Say Y here to include support for the Philips TEA5761 radio tuner.
|
||||
|
||||
config TUNER_TEA5767
|
||||
tristate "TEA 5767 radio tuner"
|
||||
depends on I2C
|
||||
default m if VIDEO_TUNER_CUSTOMIZE
|
||||
help
|
||||
Say Y here to include support for the Philips TEA5767 radio tuner.
|
||||
|
||||
config TUNER_MT20XX
|
||||
tristate "Microtune 2032 / 2050 tuners"
|
||||
depends on I2C
|
||||
default m if VIDEO_TUNER_CUSTOMIZE
|
||||
help
|
||||
Say Y here to include support for the MT2032 / MT2050 tuner.
|
||||
|
||||
config TUNER_XC2028
|
||||
tristate "XCeive xc2028/xc3028 tuners"
|
||||
depends on I2C && FW_LOADER
|
||||
default m if VIDEO_TUNER_CUSTOMIZE
|
||||
help
|
||||
Say Y here to include support for the xc2028/xc3028 tuners.
|
||||
|
||||
config DVB_TUNER_XC5000
|
||||
tristate "Xceive XC5000 silicon tuner"
|
||||
depends on I2C
|
||||
default m if DVB_FE_CUSTOMISE
|
||||
help
|
||||
A driver for the silicon tuner XC5000 from Xceive.
|
||||
This device is only used inside a SiP called togther with a
|
||||
demodulator for now.
|
||||
|
||||
endif # VIDEO_TUNER_CUSTOMIZE
|
||||
21
drivers/media/common/tuners/Makefile
Normal file
21
drivers/media/common/tuners/Makefile
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
#
|
||||
# Makefile for common V4L/DVB tuners
|
||||
#
|
||||
|
||||
tda18271-objs := tda18271-maps.o tda18271-common.o tda18271-fe.o
|
||||
|
||||
obj-$(CONFIG_TUNER_XC2028) += tuner-xc2028.o
|
||||
obj-$(CONFIG_TUNER_SIMPLE) += tuner-simple.o
|
||||
# tuner-types will be merged into tuner-simple, in the future
|
||||
obj-$(CONFIG_TUNER_SIMPLE) += tuner-types.o
|
||||
obj-$(CONFIG_TUNER_MT20XX) += mt20xx.o
|
||||
obj-$(CONFIG_TUNER_TDA8290) += tda8290.o
|
||||
obj-$(CONFIG_TUNER_TEA5767) += tea5767.o
|
||||
obj-$(CONFIG_TUNER_TEA5761) += tea5761.o
|
||||
obj-$(CONFIG_TUNER_TDA9887) += tda9887.o
|
||||
obj-$(CONFIG_DVB_TDA827X) += tda827x.o
|
||||
obj-$(CONFIG_DVB_TDA18271) += tda18271.o
|
||||
obj-$(CONFIG_DVB_TUNER_XC5000) += xc5000.o
|
||||
|
||||
EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
|
||||
EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
|
||||
670
drivers/media/common/tuners/mt20xx.c
Normal file
670
drivers/media/common/tuners/mt20xx.c
Normal file
|
|
@ -0,0 +1,670 @@
|
|||
/*
|
||||
* i2c tv tuner chip device driver
|
||||
* controls microtune tuners, mt2032 + mt2050 at the moment.
|
||||
*
|
||||
* This "mt20xx" module was split apart from the original "tuner" module.
|
||||
*/
|
||||
#include <linux/delay.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/videodev.h>
|
||||
#include "tuner-i2c.h"
|
||||
#include "mt20xx.h"
|
||||
|
||||
static int debug;
|
||||
module_param(debug, int, 0644);
|
||||
MODULE_PARM_DESC(debug, "enable verbose debug messages");
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
static unsigned int optimize_vco = 1;
|
||||
module_param(optimize_vco, int, 0644);
|
||||
|
||||
static unsigned int tv_antenna = 1;
|
||||
module_param(tv_antenna, int, 0644);
|
||||
|
||||
static unsigned int radio_antenna;
|
||||
module_param(radio_antenna, int, 0644);
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
#define MT2032 0x04
|
||||
#define MT2030 0x06
|
||||
#define MT2040 0x07
|
||||
#define MT2050 0x42
|
||||
|
||||
static char *microtune_part[] = {
|
||||
[ MT2030 ] = "MT2030",
|
||||
[ MT2032 ] = "MT2032",
|
||||
[ MT2040 ] = "MT2040",
|
||||
[ MT2050 ] = "MT2050",
|
||||
};
|
||||
|
||||
struct microtune_priv {
|
||||
struct tuner_i2c_props i2c_props;
|
||||
|
||||
unsigned int xogc;
|
||||
//unsigned int radio_if2;
|
||||
|
||||
u32 frequency;
|
||||
};
|
||||
|
||||
static int microtune_release(struct dvb_frontend *fe)
|
||||
{
|
||||
kfree(fe->tuner_priv);
|
||||
fe->tuner_priv = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int microtune_get_frequency(struct dvb_frontend *fe, u32 *frequency)
|
||||
{
|
||||
struct microtune_priv *priv = fe->tuner_priv;
|
||||
*frequency = priv->frequency;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// IsSpurInBand()?
|
||||
static int mt2032_spurcheck(struct dvb_frontend *fe,
|
||||
int f1, int f2, int spectrum_from,int spectrum_to)
|
||||
{
|
||||
struct microtune_priv *priv = fe->tuner_priv;
|
||||
int n1=1,n2,f;
|
||||
|
||||
f1=f1/1000; //scale to kHz to avoid 32bit overflows
|
||||
f2=f2/1000;
|
||||
spectrum_from/=1000;
|
||||
spectrum_to/=1000;
|
||||
|
||||
tuner_dbg("spurcheck f1=%d f2=%d from=%d to=%d\n",
|
||||
f1,f2,spectrum_from,spectrum_to);
|
||||
|
||||
do {
|
||||
n2=-n1;
|
||||
f=n1*(f1-f2);
|
||||
do {
|
||||
n2--;
|
||||
f=f-f2;
|
||||
tuner_dbg("spurtest n1=%d n2=%d ftest=%d\n",n1,n2,f);
|
||||
|
||||
if( (f>spectrum_from) && (f<spectrum_to))
|
||||
tuner_dbg("mt2032 spurcheck triggered: %d\n",n1);
|
||||
} while ( (f>(f2-spectrum_to)) || (n2>-5));
|
||||
n1++;
|
||||
} while (n1<5);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int mt2032_compute_freq(struct dvb_frontend *fe,
|
||||
unsigned int rfin,
|
||||
unsigned int if1, unsigned int if2,
|
||||
unsigned int spectrum_from,
|
||||
unsigned int spectrum_to,
|
||||
unsigned char *buf,
|
||||
int *ret_sel,
|
||||
unsigned int xogc) //all in Hz
|
||||
{
|
||||
struct microtune_priv *priv = fe->tuner_priv;
|
||||
unsigned int fref,lo1,lo1n,lo1a,s,sel,lo1freq, desired_lo1,
|
||||
desired_lo2,lo2,lo2n,lo2a,lo2num,lo2freq;
|
||||
|
||||
fref= 5250 *1000; //5.25MHz
|
||||
desired_lo1=rfin+if1;
|
||||
|
||||
lo1=(2*(desired_lo1/1000)+(fref/1000)) / (2*fref/1000);
|
||||
lo1n=lo1/8;
|
||||
lo1a=lo1-(lo1n*8);
|
||||
|
||||
s=rfin/1000/1000+1090;
|
||||
|
||||
if(optimize_vco) {
|
||||
if(s>1890) sel=0;
|
||||
else if(s>1720) sel=1;
|
||||
else if(s>1530) sel=2;
|
||||
else if(s>1370) sel=3;
|
||||
else sel=4; // >1090
|
||||
}
|
||||
else {
|
||||
if(s>1790) sel=0; // <1958
|
||||
else if(s>1617) sel=1;
|
||||
else if(s>1449) sel=2;
|
||||
else if(s>1291) sel=3;
|
||||
else sel=4; // >1090
|
||||
}
|
||||
*ret_sel=sel;
|
||||
|
||||
lo1freq=(lo1a+8*lo1n)*fref;
|
||||
|
||||
tuner_dbg("mt2032: rfin=%d lo1=%d lo1n=%d lo1a=%d sel=%d, lo1freq=%d\n",
|
||||
rfin,lo1,lo1n,lo1a,sel,lo1freq);
|
||||
|
||||
desired_lo2=lo1freq-rfin-if2;
|
||||
lo2=(desired_lo2)/fref;
|
||||
lo2n=lo2/8;
|
||||
lo2a=lo2-(lo2n*8);
|
||||
lo2num=((desired_lo2/1000)%(fref/1000))* 3780/(fref/1000); //scale to fit in 32bit arith
|
||||
lo2freq=(lo2a+8*lo2n)*fref + lo2num*(fref/1000)/3780*1000;
|
||||
|
||||
tuner_dbg("mt2032: rfin=%d lo2=%d lo2n=%d lo2a=%d num=%d lo2freq=%d\n",
|
||||
rfin,lo2,lo2n,lo2a,lo2num,lo2freq);
|
||||
|
||||
if(lo1a<0 || lo1a>7 || lo1n<17 ||lo1n>48 || lo2a<0 ||lo2a >7 ||lo2n<17 || lo2n>30) {
|
||||
tuner_info("mt2032: frequency parameters out of range: %d %d %d %d\n",
|
||||
lo1a, lo1n, lo2a,lo2n);
|
||||
return(-1);
|
||||
}
|
||||
|
||||
mt2032_spurcheck(fe, lo1freq, desired_lo2, spectrum_from, spectrum_to);
|
||||
// should recalculate lo1 (one step up/down)
|
||||
|
||||
// set up MT2032 register map for transfer over i2c
|
||||
buf[0]=lo1n-1;
|
||||
buf[1]=lo1a | (sel<<4);
|
||||
buf[2]=0x86; // LOGC
|
||||
buf[3]=0x0f; //reserved
|
||||
buf[4]=0x1f;
|
||||
buf[5]=(lo2n-1) | (lo2a<<5);
|
||||
if(rfin >400*1000*1000)
|
||||
buf[6]=0xe4;
|
||||
else
|
||||
buf[6]=0xf4; // set PKEN per rev 1.2
|
||||
buf[7]=8+xogc;
|
||||
buf[8]=0xc3; //reserved
|
||||
buf[9]=0x4e; //reserved
|
||||
buf[10]=0xec; //reserved
|
||||
buf[11]=(lo2num&0xff);
|
||||
buf[12]=(lo2num>>8) |0x80; // Lo2RST
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mt2032_check_lo_lock(struct dvb_frontend *fe)
|
||||
{
|
||||
struct microtune_priv *priv = fe->tuner_priv;
|
||||
int try,lock=0;
|
||||
unsigned char buf[2];
|
||||
|
||||
for(try=0;try<10;try++) {
|
||||
buf[0]=0x0e;
|
||||
tuner_i2c_xfer_send(&priv->i2c_props,buf,1);
|
||||
tuner_i2c_xfer_recv(&priv->i2c_props,buf,1);
|
||||
tuner_dbg("mt2032 Reg.E=0x%02x\n",buf[0]);
|
||||
lock=buf[0] &0x06;
|
||||
|
||||
if (lock==6)
|
||||
break;
|
||||
|
||||
tuner_dbg("mt2032: pll wait 1ms for lock (0x%2x)\n",buf[0]);
|
||||
udelay(1000);
|
||||
}
|
||||
return lock;
|
||||
}
|
||||
|
||||
static int mt2032_optimize_vco(struct dvb_frontend *fe,int sel,int lock)
|
||||
{
|
||||
struct microtune_priv *priv = fe->tuner_priv;
|
||||
unsigned char buf[2];
|
||||
int tad1;
|
||||
|
||||
buf[0]=0x0f;
|
||||
tuner_i2c_xfer_send(&priv->i2c_props,buf,1);
|
||||
tuner_i2c_xfer_recv(&priv->i2c_props,buf,1);
|
||||
tuner_dbg("mt2032 Reg.F=0x%02x\n",buf[0]);
|
||||
tad1=buf[0]&0x07;
|
||||
|
||||
if(tad1 ==0) return lock;
|
||||
if(tad1 ==1) return lock;
|
||||
|
||||
if(tad1==2) {
|
||||
if(sel==0)
|
||||
return lock;
|
||||
else sel--;
|
||||
}
|
||||
else {
|
||||
if(sel<4)
|
||||
sel++;
|
||||
else
|
||||
return lock;
|
||||
}
|
||||
|
||||
tuner_dbg("mt2032 optimize_vco: sel=%d\n",sel);
|
||||
|
||||
buf[0]=0x0f;
|
||||
buf[1]=sel;
|
||||
tuner_i2c_xfer_send(&priv->i2c_props,buf,2);
|
||||
lock=mt2032_check_lo_lock(fe);
|
||||
return lock;
|
||||
}
|
||||
|
||||
|
||||
static void mt2032_set_if_freq(struct dvb_frontend *fe, unsigned int rfin,
|
||||
unsigned int if1, unsigned int if2,
|
||||
unsigned int from, unsigned int to)
|
||||
{
|
||||
unsigned char buf[21];
|
||||
int lint_try,ret,sel,lock=0;
|
||||
struct microtune_priv *priv = fe->tuner_priv;
|
||||
|
||||
tuner_dbg("mt2032_set_if_freq rfin=%d if1=%d if2=%d from=%d to=%d\n",
|
||||
rfin,if1,if2,from,to);
|
||||
|
||||
buf[0]=0;
|
||||
ret=tuner_i2c_xfer_send(&priv->i2c_props,buf,1);
|
||||
tuner_i2c_xfer_recv(&priv->i2c_props,buf,21);
|
||||
|
||||
buf[0]=0;
|
||||
ret=mt2032_compute_freq(fe,rfin,if1,if2,from,to,&buf[1],&sel,priv->xogc);
|
||||
if (ret<0)
|
||||
return;
|
||||
|
||||
// send only the relevant registers per Rev. 1.2
|
||||
buf[0]=0;
|
||||
ret=tuner_i2c_xfer_send(&priv->i2c_props,buf,4);
|
||||
buf[5]=5;
|
||||
ret=tuner_i2c_xfer_send(&priv->i2c_props,buf+5,4);
|
||||
buf[11]=11;
|
||||
ret=tuner_i2c_xfer_send(&priv->i2c_props,buf+11,3);
|
||||
if(ret!=3)
|
||||
tuner_warn("i2c i/o error: rc == %d (should be 3)\n",ret);
|
||||
|
||||
// wait for PLLs to lock (per manual), retry LINT if not.
|
||||
for(lint_try=0; lint_try<2; lint_try++) {
|
||||
lock=mt2032_check_lo_lock(fe);
|
||||
|
||||
if(optimize_vco)
|
||||
lock=mt2032_optimize_vco(fe,sel,lock);
|
||||
if(lock==6) break;
|
||||
|
||||
tuner_dbg("mt2032: re-init PLLs by LINT\n");
|
||||
buf[0]=7;
|
||||
buf[1]=0x80 +8+priv->xogc; // set LINT to re-init PLLs
|
||||
tuner_i2c_xfer_send(&priv->i2c_props,buf,2);
|
||||
mdelay(10);
|
||||
buf[1]=8+priv->xogc;
|
||||
tuner_i2c_xfer_send(&priv->i2c_props,buf,2);
|
||||
}
|
||||
|
||||
if (lock!=6)
|
||||
tuner_warn("MT2032 Fatal Error: PLLs didn't lock.\n");
|
||||
|
||||
buf[0]=2;
|
||||
buf[1]=0x20; // LOGC for optimal phase noise
|
||||
ret=tuner_i2c_xfer_send(&priv->i2c_props,buf,2);
|
||||
if (ret!=2)
|
||||
tuner_warn("i2c i/o error: rc == %d (should be 2)\n",ret);
|
||||
}
|
||||
|
||||
|
||||
static int mt2032_set_tv_freq(struct dvb_frontend *fe,
|
||||
struct analog_parameters *params)
|
||||
{
|
||||
int if2,from,to;
|
||||
|
||||
// signal bandwidth and picture carrier
|
||||
if (params->std & V4L2_STD_525_60) {
|
||||
// NTSC
|
||||
from = 40750*1000;
|
||||
to = 46750*1000;
|
||||
if2 = 45750*1000;
|
||||
} else {
|
||||
// PAL
|
||||
from = 32900*1000;
|
||||
to = 39900*1000;
|
||||
if2 = 38900*1000;
|
||||
}
|
||||
|
||||
mt2032_set_if_freq(fe, params->frequency*62500,
|
||||
1090*1000*1000, if2, from, to);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mt2032_set_radio_freq(struct dvb_frontend *fe,
|
||||
struct analog_parameters *params)
|
||||
{
|
||||
struct microtune_priv *priv = fe->tuner_priv;
|
||||
int if2;
|
||||
|
||||
if (params->std & V4L2_STD_525_60) {
|
||||
tuner_dbg("pinnacle ntsc\n");
|
||||
if2 = 41300 * 1000;
|
||||
} else {
|
||||
tuner_dbg("pinnacle pal\n");
|
||||
if2 = 33300 * 1000;
|
||||
}
|
||||
|
||||
// per Manual for FM tuning: first if center freq. 1085 MHz
|
||||
mt2032_set_if_freq(fe, params->frequency * 125 / 2,
|
||||
1085*1000*1000,if2,if2,if2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mt2032_set_params(struct dvb_frontend *fe,
|
||||
struct analog_parameters *params)
|
||||
{
|
||||
struct microtune_priv *priv = fe->tuner_priv;
|
||||
int ret = -EINVAL;
|
||||
|
||||
switch (params->mode) {
|
||||
case V4L2_TUNER_RADIO:
|
||||
ret = mt2032_set_radio_freq(fe, params);
|
||||
priv->frequency = params->frequency * 125 / 2;
|
||||
break;
|
||||
case V4L2_TUNER_ANALOG_TV:
|
||||
case V4L2_TUNER_DIGITAL_TV:
|
||||
ret = mt2032_set_tv_freq(fe, params);
|
||||
priv->frequency = params->frequency * 62500;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct dvb_tuner_ops mt2032_tuner_ops = {
|
||||
.set_analog_params = mt2032_set_params,
|
||||
.release = microtune_release,
|
||||
.get_frequency = microtune_get_frequency,
|
||||
};
|
||||
|
||||
// Initialization as described in "MT203x Programming Procedures", Rev 1.2, Feb.2001
|
||||
static int mt2032_init(struct dvb_frontend *fe)
|
||||
{
|
||||
struct microtune_priv *priv = fe->tuner_priv;
|
||||
unsigned char buf[21];
|
||||
int ret,xogc,xok=0;
|
||||
|
||||
// Initialize Registers per spec.
|
||||
buf[1]=2; // Index to register 2
|
||||
buf[2]=0xff;
|
||||
buf[3]=0x0f;
|
||||
buf[4]=0x1f;
|
||||
ret=tuner_i2c_xfer_send(&priv->i2c_props,buf+1,4);
|
||||
|
||||
buf[5]=6; // Index register 6
|
||||
buf[6]=0xe4;
|
||||
buf[7]=0x8f;
|
||||
buf[8]=0xc3;
|
||||
buf[9]=0x4e;
|
||||
buf[10]=0xec;
|
||||
ret=tuner_i2c_xfer_send(&priv->i2c_props,buf+5,6);
|
||||
|
||||
buf[12]=13; // Index register 13
|
||||
buf[13]=0x32;
|
||||
ret=tuner_i2c_xfer_send(&priv->i2c_props,buf+12,2);
|
||||
|
||||
// Adjust XOGC (register 7), wait for XOK
|
||||
xogc=7;
|
||||
do {
|
||||
tuner_dbg("mt2032: xogc = 0x%02x\n",xogc&0x07);
|
||||
mdelay(10);
|
||||
buf[0]=0x0e;
|
||||
tuner_i2c_xfer_send(&priv->i2c_props,buf,1);
|
||||
tuner_i2c_xfer_recv(&priv->i2c_props,buf,1);
|
||||
xok=buf[0]&0x01;
|
||||
tuner_dbg("mt2032: xok = 0x%02x\n",xok);
|
||||
if (xok == 1) break;
|
||||
|
||||
xogc--;
|
||||
tuner_dbg("mt2032: xogc = 0x%02x\n",xogc&0x07);
|
||||
if (xogc == 3) {
|
||||
xogc=4; // min. 4 per spec
|
||||
break;
|
||||
}
|
||||
buf[0]=0x07;
|
||||
buf[1]=0x88 + xogc;
|
||||
ret=tuner_i2c_xfer_send(&priv->i2c_props,buf,2);
|
||||
if (ret!=2)
|
||||
tuner_warn("i2c i/o error: rc == %d (should be 2)\n",ret);
|
||||
} while (xok != 1 );
|
||||
priv->xogc=xogc;
|
||||
|
||||
memcpy(&fe->ops.tuner_ops, &mt2032_tuner_ops, sizeof(struct dvb_tuner_ops));
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
static void mt2050_set_antenna(struct dvb_frontend *fe, unsigned char antenna)
|
||||
{
|
||||
struct microtune_priv *priv = fe->tuner_priv;
|
||||
unsigned char buf[2];
|
||||
int ret;
|
||||
|
||||
buf[0] = 6;
|
||||
buf[1] = antenna ? 0x11 : 0x10;
|
||||
ret=tuner_i2c_xfer_send(&priv->i2c_props,buf,2);
|
||||
tuner_dbg("mt2050: enabled antenna connector %d\n", antenna);
|
||||
}
|
||||
|
||||
static void mt2050_set_if_freq(struct dvb_frontend *fe,unsigned int freq, unsigned int if2)
|
||||
{
|
||||
struct microtune_priv *priv = fe->tuner_priv;
|
||||
unsigned int if1=1218*1000*1000;
|
||||
unsigned int f_lo1,f_lo2,lo1,lo2,f_lo1_modulo,f_lo2_modulo,num1,num2,div1a,div1b,div2a,div2b;
|
||||
int ret;
|
||||
unsigned char buf[6];
|
||||
|
||||
tuner_dbg("mt2050_set_if_freq freq=%d if1=%d if2=%d\n",
|
||||
freq,if1,if2);
|
||||
|
||||
f_lo1=freq+if1;
|
||||
f_lo1=(f_lo1/1000000)*1000000;
|
||||
|
||||
f_lo2=f_lo1-freq-if2;
|
||||
f_lo2=(f_lo2/50000)*50000;
|
||||
|
||||
lo1=f_lo1/4000000;
|
||||
lo2=f_lo2/4000000;
|
||||
|
||||
f_lo1_modulo= f_lo1-(lo1*4000000);
|
||||
f_lo2_modulo= f_lo2-(lo2*4000000);
|
||||
|
||||
num1=4*f_lo1_modulo/4000000;
|
||||
num2=4096*(f_lo2_modulo/1000)/4000;
|
||||
|
||||
// todo spurchecks
|
||||
|
||||
div1a=(lo1/12)-1;
|
||||
div1b=lo1-(div1a+1)*12;
|
||||
|
||||
div2a=(lo2/8)-1;
|
||||
div2b=lo2-(div2a+1)*8;
|
||||
|
||||
if (debug > 1) {
|
||||
tuner_dbg("lo1 lo2 = %d %d\n", lo1, lo2);
|
||||
tuner_dbg("num1 num2 div1a div1b div2a div2b= %x %x %x %x %x %x\n",
|
||||
num1,num2,div1a,div1b,div2a,div2b);
|
||||
}
|
||||
|
||||
buf[0]=1;
|
||||
buf[1]= 4*div1b + num1;
|
||||
if(freq<275*1000*1000) buf[1] = buf[1]|0x80;
|
||||
|
||||
buf[2]=div1a;
|
||||
buf[3]=32*div2b + num2/256;
|
||||
buf[4]=num2-(num2/256)*256;
|
||||
buf[5]=div2a;
|
||||
if(num2!=0) buf[5]=buf[5]|0x40;
|
||||
|
||||
if (debug > 1) {
|
||||
int i;
|
||||
tuner_dbg("bufs is: ");
|
||||
for(i=0;i<6;i++)
|
||||
printk("%x ",buf[i]);
|
||||
printk("\n");
|
||||
}
|
||||
|
||||
ret=tuner_i2c_xfer_send(&priv->i2c_props,buf,6);
|
||||
if (ret!=6)
|
||||
tuner_warn("i2c i/o error: rc == %d (should be 6)\n",ret);
|
||||
}
|
||||
|
||||
static int mt2050_set_tv_freq(struct dvb_frontend *fe,
|
||||
struct analog_parameters *params)
|
||||
{
|
||||
unsigned int if2;
|
||||
|
||||
if (params->std & V4L2_STD_525_60) {
|
||||
// NTSC
|
||||
if2 = 45750*1000;
|
||||
} else {
|
||||
// PAL
|
||||
if2 = 38900*1000;
|
||||
}
|
||||
if (V4L2_TUNER_DIGITAL_TV == params->mode) {
|
||||
// DVB (pinnacle 300i)
|
||||
if2 = 36150*1000;
|
||||
}
|
||||
mt2050_set_if_freq(fe, params->frequency*62500, if2);
|
||||
mt2050_set_antenna(fe, tv_antenna);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mt2050_set_radio_freq(struct dvb_frontend *fe,
|
||||
struct analog_parameters *params)
|
||||
{
|
||||
struct microtune_priv *priv = fe->tuner_priv;
|
||||
int if2;
|
||||
|
||||
if (params->std & V4L2_STD_525_60) {
|
||||
tuner_dbg("pinnacle ntsc\n");
|
||||
if2 = 41300 * 1000;
|
||||
} else {
|
||||
tuner_dbg("pinnacle pal\n");
|
||||
if2 = 33300 * 1000;
|
||||
}
|
||||
|
||||
mt2050_set_if_freq(fe, params->frequency * 125 / 2, if2);
|
||||
mt2050_set_antenna(fe, radio_antenna);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mt2050_set_params(struct dvb_frontend *fe,
|
||||
struct analog_parameters *params)
|
||||
{
|
||||
struct microtune_priv *priv = fe->tuner_priv;
|
||||
int ret = -EINVAL;
|
||||
|
||||
switch (params->mode) {
|
||||
case V4L2_TUNER_RADIO:
|
||||
ret = mt2050_set_radio_freq(fe, params);
|
||||
priv->frequency = params->frequency * 125 / 2;
|
||||
break;
|
||||
case V4L2_TUNER_ANALOG_TV:
|
||||
case V4L2_TUNER_DIGITAL_TV:
|
||||
ret = mt2050_set_tv_freq(fe, params);
|
||||
priv->frequency = params->frequency * 62500;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct dvb_tuner_ops mt2050_tuner_ops = {
|
||||
.set_analog_params = mt2050_set_params,
|
||||
.release = microtune_release,
|
||||
.get_frequency = microtune_get_frequency,
|
||||
};
|
||||
|
||||
static int mt2050_init(struct dvb_frontend *fe)
|
||||
{
|
||||
struct microtune_priv *priv = fe->tuner_priv;
|
||||
unsigned char buf[2];
|
||||
int ret;
|
||||
|
||||
buf[0]=6;
|
||||
buf[1]=0x10;
|
||||
ret=tuner_i2c_xfer_send(&priv->i2c_props,buf,2); // power
|
||||
|
||||
buf[0]=0x0f;
|
||||
buf[1]=0x0f;
|
||||
ret=tuner_i2c_xfer_send(&priv->i2c_props,buf,2); // m1lo
|
||||
|
||||
buf[0]=0x0d;
|
||||
ret=tuner_i2c_xfer_send(&priv->i2c_props,buf,1);
|
||||
tuner_i2c_xfer_recv(&priv->i2c_props,buf,1);
|
||||
|
||||
tuner_dbg("mt2050: sro is %x\n",buf[0]);
|
||||
|
||||
memcpy(&fe->ops.tuner_ops, &mt2050_tuner_ops, sizeof(struct dvb_tuner_ops));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct dvb_frontend *microtune_attach(struct dvb_frontend *fe,
|
||||
struct i2c_adapter* i2c_adap,
|
||||
u8 i2c_addr)
|
||||
{
|
||||
struct microtune_priv *priv = NULL;
|
||||
char *name;
|
||||
unsigned char buf[21];
|
||||
int company_code;
|
||||
|
||||
priv = kzalloc(sizeof(struct microtune_priv), GFP_KERNEL);
|
||||
if (priv == NULL)
|
||||
return NULL;
|
||||
fe->tuner_priv = priv;
|
||||
|
||||
priv->i2c_props.addr = i2c_addr;
|
||||
priv->i2c_props.adap = i2c_adap;
|
||||
priv->i2c_props.name = "mt20xx";
|
||||
|
||||
//priv->radio_if2 = 10700 * 1000; /* 10.7MHz - FM radio */
|
||||
|
||||
memset(buf,0,sizeof(buf));
|
||||
|
||||
name = "unknown";
|
||||
|
||||
tuner_i2c_xfer_send(&priv->i2c_props,buf,1);
|
||||
tuner_i2c_xfer_recv(&priv->i2c_props,buf,21);
|
||||
if (debug) {
|
||||
int i;
|
||||
tuner_dbg("MT20xx hexdump:");
|
||||
for(i=0;i<21;i++) {
|
||||
printk(" %02x",buf[i]);
|
||||
if(((i+1)%8)==0) printk(" ");
|
||||
}
|
||||
printk("\n");
|
||||
}
|
||||
company_code = buf[0x11] << 8 | buf[0x12];
|
||||
tuner_info("microtune: companycode=%04x part=%02x rev=%02x\n",
|
||||
company_code,buf[0x13],buf[0x14]);
|
||||
|
||||
|
||||
if (buf[0x13] < ARRAY_SIZE(microtune_part) &&
|
||||
NULL != microtune_part[buf[0x13]])
|
||||
name = microtune_part[buf[0x13]];
|
||||
switch (buf[0x13]) {
|
||||
case MT2032:
|
||||
mt2032_init(fe);
|
||||
break;
|
||||
case MT2050:
|
||||
mt2050_init(fe);
|
||||
break;
|
||||
default:
|
||||
tuner_info("microtune %s found, not (yet?) supported, sorry :-/\n",
|
||||
name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
strlcpy(fe->ops.tuner_ops.info.name, name,
|
||||
sizeof(fe->ops.tuner_ops.info.name));
|
||||
tuner_info("microtune %s found, OK\n",name);
|
||||
return fe;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(microtune_attach);
|
||||
|
||||
MODULE_DESCRIPTION("Microtune tuner driver");
|
||||
MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
/*
|
||||
* Overrides for Emacs so that we follow Linus's tabbing style.
|
||||
* ---------------------------------------------------------------------------
|
||||
* Local variables:
|
||||
* c-basic-offset: 8
|
||||
* End:
|
||||
*/
|
||||
37
drivers/media/common/tuners/mt20xx.h
Normal file
37
drivers/media/common/tuners/mt20xx.h
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
#ifndef __MT20XX_H__
|
||||
#define __MT20XX_H__
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include "dvb_frontend.h"
|
||||
|
||||
#if defined(CONFIG_TUNER_MT20XX) || (defined(CONFIG_TUNER_MT20XX_MODULE) && defined(MODULE))
|
||||
extern struct dvb_frontend *microtune_attach(struct dvb_frontend *fe,
|
||||
struct i2c_adapter* i2c_adap,
|
||||
u8 i2c_addr);
|
||||
#else
|
||||
static inline struct dvb_frontend *microtune_attach(struct dvb_frontend *fe,
|
||||
struct i2c_adapter* i2c_adap,
|
||||
u8 i2c_addr)
|
||||
{
|
||||
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __MT20XX_H__ */
|
||||
666
drivers/media/common/tuners/tda18271-common.c
Normal file
666
drivers/media/common/tuners/tda18271-common.c
Normal file
|
|
@ -0,0 +1,666 @@
|
|||
/*
|
||||
tda18271-common.c - driver for the Philips / NXP TDA18271 silicon tuner
|
||||
|
||||
Copyright (C) 2007, 2008 Michael Krufky <mkrufky@linuxtv.org>
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
#include "tda18271-priv.h"
|
||||
|
||||
static int tda18271_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
|
||||
{
|
||||
struct tda18271_priv *priv = fe->tuner_priv;
|
||||
enum tda18271_i2c_gate gate;
|
||||
int ret = 0;
|
||||
|
||||
switch (priv->gate) {
|
||||
case TDA18271_GATE_DIGITAL:
|
||||
case TDA18271_GATE_ANALOG:
|
||||
gate = priv->gate;
|
||||
break;
|
||||
case TDA18271_GATE_AUTO:
|
||||
default:
|
||||
switch (priv->mode) {
|
||||
case TDA18271_DIGITAL:
|
||||
gate = TDA18271_GATE_DIGITAL;
|
||||
break;
|
||||
case TDA18271_ANALOG:
|
||||
default:
|
||||
gate = TDA18271_GATE_ANALOG;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch (gate) {
|
||||
case TDA18271_GATE_ANALOG:
|
||||
if (fe->ops.analog_ops.i2c_gate_ctrl)
|
||||
ret = fe->ops.analog_ops.i2c_gate_ctrl(fe, enable);
|
||||
break;
|
||||
case TDA18271_GATE_DIGITAL:
|
||||
if (fe->ops.i2c_gate_ctrl)
|
||||
ret = fe->ops.i2c_gate_ctrl(fe, enable);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
/*---------------------------------------------------------------------*/
|
||||
|
||||
static void tda18271_dump_regs(struct dvb_frontend *fe, int extended)
|
||||
{
|
||||
struct tda18271_priv *priv = fe->tuner_priv;
|
||||
unsigned char *regs = priv->tda18271_regs;
|
||||
|
||||
tda_reg("=== TDA18271 REG DUMP ===\n");
|
||||
tda_reg("ID_BYTE = 0x%02x\n", 0xff & regs[R_ID]);
|
||||
tda_reg("THERMO_BYTE = 0x%02x\n", 0xff & regs[R_TM]);
|
||||
tda_reg("POWER_LEVEL_BYTE = 0x%02x\n", 0xff & regs[R_PL]);
|
||||
tda_reg("EASY_PROG_BYTE_1 = 0x%02x\n", 0xff & regs[R_EP1]);
|
||||
tda_reg("EASY_PROG_BYTE_2 = 0x%02x\n", 0xff & regs[R_EP2]);
|
||||
tda_reg("EASY_PROG_BYTE_3 = 0x%02x\n", 0xff & regs[R_EP3]);
|
||||
tda_reg("EASY_PROG_BYTE_4 = 0x%02x\n", 0xff & regs[R_EP4]);
|
||||
tda_reg("EASY_PROG_BYTE_5 = 0x%02x\n", 0xff & regs[R_EP5]);
|
||||
tda_reg("CAL_POST_DIV_BYTE = 0x%02x\n", 0xff & regs[R_CPD]);
|
||||
tda_reg("CAL_DIV_BYTE_1 = 0x%02x\n", 0xff & regs[R_CD1]);
|
||||
tda_reg("CAL_DIV_BYTE_2 = 0x%02x\n", 0xff & regs[R_CD2]);
|
||||
tda_reg("CAL_DIV_BYTE_3 = 0x%02x\n", 0xff & regs[R_CD3]);
|
||||
tda_reg("MAIN_POST_DIV_BYTE = 0x%02x\n", 0xff & regs[R_MPD]);
|
||||
tda_reg("MAIN_DIV_BYTE_1 = 0x%02x\n", 0xff & regs[R_MD1]);
|
||||
tda_reg("MAIN_DIV_BYTE_2 = 0x%02x\n", 0xff & regs[R_MD2]);
|
||||
tda_reg("MAIN_DIV_BYTE_3 = 0x%02x\n", 0xff & regs[R_MD3]);
|
||||
|
||||
/* only dump extended regs if DBG_ADV is set */
|
||||
if (!(tda18271_debug & DBG_ADV))
|
||||
return;
|
||||
|
||||
/* W indicates write-only registers.
|
||||
* Register dump for write-only registers shows last value written. */
|
||||
|
||||
tda_reg("EXTENDED_BYTE_1 = 0x%02x\n", 0xff & regs[R_EB1]);
|
||||
tda_reg("EXTENDED_BYTE_2 = 0x%02x\n", 0xff & regs[R_EB2]);
|
||||
tda_reg("EXTENDED_BYTE_3 = 0x%02x\n", 0xff & regs[R_EB3]);
|
||||
tda_reg("EXTENDED_BYTE_4 = 0x%02x\n", 0xff & regs[R_EB4]);
|
||||
tda_reg("EXTENDED_BYTE_5 = 0x%02x\n", 0xff & regs[R_EB5]);
|
||||
tda_reg("EXTENDED_BYTE_6 = 0x%02x\n", 0xff & regs[R_EB6]);
|
||||
tda_reg("EXTENDED_BYTE_7 = 0x%02x\n", 0xff & regs[R_EB7]);
|
||||
tda_reg("EXTENDED_BYTE_8 = 0x%02x\n", 0xff & regs[R_EB8]);
|
||||
tda_reg("EXTENDED_BYTE_9 W = 0x%02x\n", 0xff & regs[R_EB9]);
|
||||
tda_reg("EXTENDED_BYTE_10 = 0x%02x\n", 0xff & regs[R_EB10]);
|
||||
tda_reg("EXTENDED_BYTE_11 = 0x%02x\n", 0xff & regs[R_EB11]);
|
||||
tda_reg("EXTENDED_BYTE_12 = 0x%02x\n", 0xff & regs[R_EB12]);
|
||||
tda_reg("EXTENDED_BYTE_13 = 0x%02x\n", 0xff & regs[R_EB13]);
|
||||
tda_reg("EXTENDED_BYTE_14 = 0x%02x\n", 0xff & regs[R_EB14]);
|
||||
tda_reg("EXTENDED_BYTE_15 = 0x%02x\n", 0xff & regs[R_EB15]);
|
||||
tda_reg("EXTENDED_BYTE_16 W = 0x%02x\n", 0xff & regs[R_EB16]);
|
||||
tda_reg("EXTENDED_BYTE_17 W = 0x%02x\n", 0xff & regs[R_EB17]);
|
||||
tda_reg("EXTENDED_BYTE_18 = 0x%02x\n", 0xff & regs[R_EB18]);
|
||||
tda_reg("EXTENDED_BYTE_19 W = 0x%02x\n", 0xff & regs[R_EB19]);
|
||||
tda_reg("EXTENDED_BYTE_20 W = 0x%02x\n", 0xff & regs[R_EB20]);
|
||||
tda_reg("EXTENDED_BYTE_21 = 0x%02x\n", 0xff & regs[R_EB21]);
|
||||
tda_reg("EXTENDED_BYTE_22 = 0x%02x\n", 0xff & regs[R_EB22]);
|
||||
tda_reg("EXTENDED_BYTE_23 = 0x%02x\n", 0xff & regs[R_EB23]);
|
||||
}
|
||||
|
||||
int tda18271_read_regs(struct dvb_frontend *fe)
|
||||
{
|
||||
struct tda18271_priv *priv = fe->tuner_priv;
|
||||
unsigned char *regs = priv->tda18271_regs;
|
||||
unsigned char buf = 0x00;
|
||||
int ret;
|
||||
struct i2c_msg msg[] = {
|
||||
{ .addr = priv->i2c_props.addr, .flags = 0,
|
||||
.buf = &buf, .len = 1 },
|
||||
{ .addr = priv->i2c_props.addr, .flags = I2C_M_RD,
|
||||
.buf = regs, .len = 16 }
|
||||
};
|
||||
|
||||
tda18271_i2c_gate_ctrl(fe, 1);
|
||||
|
||||
/* read all registers */
|
||||
ret = i2c_transfer(priv->i2c_props.adap, msg, 2);
|
||||
|
||||
tda18271_i2c_gate_ctrl(fe, 0);
|
||||
|
||||
if (ret != 2)
|
||||
tda_err("ERROR: i2c_transfer returned: %d\n", ret);
|
||||
|
||||
if (tda18271_debug & DBG_REG)
|
||||
tda18271_dump_regs(fe, 0);
|
||||
|
||||
return (ret == 2 ? 0 : ret);
|
||||
}
|
||||
|
||||
int tda18271_read_extended(struct dvb_frontend *fe)
|
||||
{
|
||||
struct tda18271_priv *priv = fe->tuner_priv;
|
||||
unsigned char *regs = priv->tda18271_regs;
|
||||
unsigned char regdump[TDA18271_NUM_REGS];
|
||||
unsigned char buf = 0x00;
|
||||
int ret, i;
|
||||
struct i2c_msg msg[] = {
|
||||
{ .addr = priv->i2c_props.addr, .flags = 0,
|
||||
.buf = &buf, .len = 1 },
|
||||
{ .addr = priv->i2c_props.addr, .flags = I2C_M_RD,
|
||||
.buf = regdump, .len = TDA18271_NUM_REGS }
|
||||
};
|
||||
|
||||
tda18271_i2c_gate_ctrl(fe, 1);
|
||||
|
||||
/* read all registers */
|
||||
ret = i2c_transfer(priv->i2c_props.adap, msg, 2);
|
||||
|
||||
tda18271_i2c_gate_ctrl(fe, 0);
|
||||
|
||||
if (ret != 2)
|
||||
tda_err("ERROR: i2c_transfer returned: %d\n", ret);
|
||||
|
||||
for (i = 0; i < TDA18271_NUM_REGS; i++) {
|
||||
/* don't update write-only registers */
|
||||
if ((i != R_EB9) &&
|
||||
(i != R_EB16) &&
|
||||
(i != R_EB17) &&
|
||||
(i != R_EB19) &&
|
||||
(i != R_EB20))
|
||||
regs[i] = regdump[i];
|
||||
}
|
||||
|
||||
if (tda18271_debug & DBG_REG)
|
||||
tda18271_dump_regs(fe, 1);
|
||||
|
||||
return (ret == 2 ? 0 : ret);
|
||||
}
|
||||
|
||||
int tda18271_write_regs(struct dvb_frontend *fe, int idx, int len)
|
||||
{
|
||||
struct tda18271_priv *priv = fe->tuner_priv;
|
||||
unsigned char *regs = priv->tda18271_regs;
|
||||
unsigned char buf[TDA18271_NUM_REGS + 1];
|
||||
struct i2c_msg msg = { .addr = priv->i2c_props.addr, .flags = 0,
|
||||
.buf = buf, .len = len + 1 };
|
||||
int i, ret;
|
||||
|
||||
BUG_ON((len == 0) || (idx + len > sizeof(buf)));
|
||||
|
||||
buf[0] = idx;
|
||||
for (i = 1; i <= len; i++)
|
||||
buf[i] = regs[idx - 1 + i];
|
||||
|
||||
tda18271_i2c_gate_ctrl(fe, 1);
|
||||
|
||||
/* write registers */
|
||||
ret = i2c_transfer(priv->i2c_props.adap, &msg, 1);
|
||||
|
||||
tda18271_i2c_gate_ctrl(fe, 0);
|
||||
|
||||
if (ret != 1)
|
||||
tda_err("ERROR: i2c_transfer returned: %d\n", ret);
|
||||
|
||||
return (ret == 1 ? 0 : ret);
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------*/
|
||||
|
||||
int tda18271_charge_pump_source(struct dvb_frontend *fe,
|
||||
enum tda18271_pll pll, int force)
|
||||
{
|
||||
struct tda18271_priv *priv = fe->tuner_priv;
|
||||
unsigned char *regs = priv->tda18271_regs;
|
||||
|
||||
int r_cp = (pll == TDA18271_CAL_PLL) ? R_EB7 : R_EB4;
|
||||
|
||||
regs[r_cp] &= ~0x20;
|
||||
regs[r_cp] |= ((force & 1) << 5);
|
||||
tda18271_write_regs(fe, r_cp, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tda18271_init_regs(struct dvb_frontend *fe)
|
||||
{
|
||||
struct tda18271_priv *priv = fe->tuner_priv;
|
||||
unsigned char *regs = priv->tda18271_regs;
|
||||
|
||||
tda_dbg("initializing registers for device @ %d-%04x\n",
|
||||
i2c_adapter_id(priv->i2c_props.adap),
|
||||
priv->i2c_props.addr);
|
||||
|
||||
/* initialize registers */
|
||||
switch (priv->id) {
|
||||
case TDA18271HDC1:
|
||||
regs[R_ID] = 0x83;
|
||||
break;
|
||||
case TDA18271HDC2:
|
||||
regs[R_ID] = 0x84;
|
||||
break;
|
||||
};
|
||||
|
||||
regs[R_TM] = 0x08;
|
||||
regs[R_PL] = 0x80;
|
||||
regs[R_EP1] = 0xc6;
|
||||
regs[R_EP2] = 0xdf;
|
||||
regs[R_EP3] = 0x16;
|
||||
regs[R_EP4] = 0x60;
|
||||
regs[R_EP5] = 0x80;
|
||||
regs[R_CPD] = 0x80;
|
||||
regs[R_CD1] = 0x00;
|
||||
regs[R_CD2] = 0x00;
|
||||
regs[R_CD3] = 0x00;
|
||||
regs[R_MPD] = 0x00;
|
||||
regs[R_MD1] = 0x00;
|
||||
regs[R_MD2] = 0x00;
|
||||
regs[R_MD3] = 0x00;
|
||||
|
||||
switch (priv->id) {
|
||||
case TDA18271HDC1:
|
||||
regs[R_EB1] = 0xff;
|
||||
break;
|
||||
case TDA18271HDC2:
|
||||
regs[R_EB1] = 0xfc;
|
||||
break;
|
||||
};
|
||||
|
||||
regs[R_EB2] = 0x01;
|
||||
regs[R_EB3] = 0x84;
|
||||
regs[R_EB4] = 0x41;
|
||||
regs[R_EB5] = 0x01;
|
||||
regs[R_EB6] = 0x84;
|
||||
regs[R_EB7] = 0x40;
|
||||
regs[R_EB8] = 0x07;
|
||||
regs[R_EB9] = 0x00;
|
||||
regs[R_EB10] = 0x00;
|
||||
regs[R_EB11] = 0x96;
|
||||
|
||||
switch (priv->id) {
|
||||
case TDA18271HDC1:
|
||||
regs[R_EB12] = 0x0f;
|
||||
break;
|
||||
case TDA18271HDC2:
|
||||
regs[R_EB12] = 0x33;
|
||||
break;
|
||||
};
|
||||
|
||||
regs[R_EB13] = 0xc1;
|
||||
regs[R_EB14] = 0x00;
|
||||
regs[R_EB15] = 0x8f;
|
||||
regs[R_EB16] = 0x00;
|
||||
regs[R_EB17] = 0x00;
|
||||
|
||||
switch (priv->id) {
|
||||
case TDA18271HDC1:
|
||||
regs[R_EB18] = 0x00;
|
||||
break;
|
||||
case TDA18271HDC2:
|
||||
regs[R_EB18] = 0x8c;
|
||||
break;
|
||||
};
|
||||
|
||||
regs[R_EB19] = 0x00;
|
||||
regs[R_EB20] = 0x20;
|
||||
|
||||
switch (priv->id) {
|
||||
case TDA18271HDC1:
|
||||
regs[R_EB21] = 0x33;
|
||||
break;
|
||||
case TDA18271HDC2:
|
||||
regs[R_EB21] = 0xb3;
|
||||
break;
|
||||
};
|
||||
|
||||
regs[R_EB22] = 0x48;
|
||||
regs[R_EB23] = 0xb0;
|
||||
|
||||
if (priv->small_i2c) {
|
||||
tda18271_write_regs(fe, 0x00, 0x10);
|
||||
tda18271_write_regs(fe, 0x10, 0x10);
|
||||
tda18271_write_regs(fe, 0x20, 0x07);
|
||||
} else
|
||||
tda18271_write_regs(fe, 0x00, TDA18271_NUM_REGS);
|
||||
|
||||
/* setup agc1 gain */
|
||||
regs[R_EB17] = 0x00;
|
||||
tda18271_write_regs(fe, R_EB17, 1);
|
||||
regs[R_EB17] = 0x03;
|
||||
tda18271_write_regs(fe, R_EB17, 1);
|
||||
regs[R_EB17] = 0x43;
|
||||
tda18271_write_regs(fe, R_EB17, 1);
|
||||
regs[R_EB17] = 0x4c;
|
||||
tda18271_write_regs(fe, R_EB17, 1);
|
||||
|
||||
/* setup agc2 gain */
|
||||
if ((priv->id) == TDA18271HDC1) {
|
||||
regs[R_EB20] = 0xa0;
|
||||
tda18271_write_regs(fe, R_EB20, 1);
|
||||
regs[R_EB20] = 0xa7;
|
||||
tda18271_write_regs(fe, R_EB20, 1);
|
||||
regs[R_EB20] = 0xe7;
|
||||
tda18271_write_regs(fe, R_EB20, 1);
|
||||
regs[R_EB20] = 0xec;
|
||||
tda18271_write_regs(fe, R_EB20, 1);
|
||||
}
|
||||
|
||||
/* image rejection calibration */
|
||||
|
||||
/* low-band */
|
||||
regs[R_EP3] = 0x1f;
|
||||
regs[R_EP4] = 0x66;
|
||||
regs[R_EP5] = 0x81;
|
||||
regs[R_CPD] = 0xcc;
|
||||
regs[R_CD1] = 0x6c;
|
||||
regs[R_CD2] = 0x00;
|
||||
regs[R_CD3] = 0x00;
|
||||
regs[R_MPD] = 0xcd;
|
||||
regs[R_MD1] = 0x77;
|
||||
regs[R_MD2] = 0x08;
|
||||
regs[R_MD3] = 0x00;
|
||||
|
||||
tda18271_write_regs(fe, R_EP3, 11);
|
||||
|
||||
if ((priv->id) == TDA18271HDC2) {
|
||||
/* main pll cp source on */
|
||||
tda18271_charge_pump_source(fe, TDA18271_MAIN_PLL, 1);
|
||||
msleep(1);
|
||||
|
||||
/* main pll cp source off */
|
||||
tda18271_charge_pump_source(fe, TDA18271_MAIN_PLL, 0);
|
||||
}
|
||||
|
||||
msleep(5); /* pll locking */
|
||||
|
||||
/* launch detector */
|
||||
tda18271_write_regs(fe, R_EP1, 1);
|
||||
msleep(5); /* wanted low measurement */
|
||||
|
||||
regs[R_EP5] = 0x85;
|
||||
regs[R_CPD] = 0xcb;
|
||||
regs[R_CD1] = 0x66;
|
||||
regs[R_CD2] = 0x70;
|
||||
|
||||
tda18271_write_regs(fe, R_EP3, 7);
|
||||
msleep(5); /* pll locking */
|
||||
|
||||
/* launch optimization algorithm */
|
||||
tda18271_write_regs(fe, R_EP2, 1);
|
||||
msleep(30); /* image low optimization completion */
|
||||
|
||||
/* mid-band */
|
||||
regs[R_EP5] = 0x82;
|
||||
regs[R_CPD] = 0xa8;
|
||||
regs[R_CD2] = 0x00;
|
||||
regs[R_MPD] = 0xa9;
|
||||
regs[R_MD1] = 0x73;
|
||||
regs[R_MD2] = 0x1a;
|
||||
|
||||
tda18271_write_regs(fe, R_EP3, 11);
|
||||
msleep(5); /* pll locking */
|
||||
|
||||
/* launch detector */
|
||||
tda18271_write_regs(fe, R_EP1, 1);
|
||||
msleep(5); /* wanted mid measurement */
|
||||
|
||||
regs[R_EP5] = 0x86;
|
||||
regs[R_CPD] = 0xa8;
|
||||
regs[R_CD1] = 0x66;
|
||||
regs[R_CD2] = 0xa0;
|
||||
|
||||
tda18271_write_regs(fe, R_EP3, 7);
|
||||
msleep(5); /* pll locking */
|
||||
|
||||
/* launch optimization algorithm */
|
||||
tda18271_write_regs(fe, R_EP2, 1);
|
||||
msleep(30); /* image mid optimization completion */
|
||||
|
||||
/* high-band */
|
||||
regs[R_EP5] = 0x83;
|
||||
regs[R_CPD] = 0x98;
|
||||
regs[R_CD1] = 0x65;
|
||||
regs[R_CD2] = 0x00;
|
||||
regs[R_MPD] = 0x99;
|
||||
regs[R_MD1] = 0x71;
|
||||
regs[R_MD2] = 0xcd;
|
||||
|
||||
tda18271_write_regs(fe, R_EP3, 11);
|
||||
msleep(5); /* pll locking */
|
||||
|
||||
/* launch detector */
|
||||
tda18271_write_regs(fe, R_EP1, 1);
|
||||
msleep(5); /* wanted high measurement */
|
||||
|
||||
regs[R_EP5] = 0x87;
|
||||
regs[R_CD1] = 0x65;
|
||||
regs[R_CD2] = 0x50;
|
||||
|
||||
tda18271_write_regs(fe, R_EP3, 7);
|
||||
msleep(5); /* pll locking */
|
||||
|
||||
/* launch optimization algorithm */
|
||||
tda18271_write_regs(fe, R_EP2, 1);
|
||||
msleep(30); /* image high optimization completion */
|
||||
|
||||
/* return to normal mode */
|
||||
regs[R_EP4] = 0x64;
|
||||
tda18271_write_regs(fe, R_EP4, 1);
|
||||
|
||||
/* synchronize */
|
||||
tda18271_write_regs(fe, R_EP1, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* Standby modes, EP3 [7:5]
|
||||
*
|
||||
* | SM || SM_LT || SM_XT || mode description
|
||||
* |=====\\=======\\=======\\===================================
|
||||
* | 0 || 0 || 0 || normal mode
|
||||
* |-----||-------||-------||-----------------------------------
|
||||
* | || || || standby mode w/ slave tuner output
|
||||
* | 1 || 0 || 0 || & loop thru & xtal oscillator on
|
||||
* |-----||-------||-------||-----------------------------------
|
||||
* | 1 || 1 || 0 || standby mode w/ xtal oscillator on
|
||||
* |-----||-------||-------||-----------------------------------
|
||||
* | 1 || 1 || 1 || power off
|
||||
*
|
||||
*/
|
||||
|
||||
int tda18271_set_standby_mode(struct dvb_frontend *fe,
|
||||
int sm, int sm_lt, int sm_xt)
|
||||
{
|
||||
struct tda18271_priv *priv = fe->tuner_priv;
|
||||
unsigned char *regs = priv->tda18271_regs;
|
||||
|
||||
tda_dbg("sm = %d, sm_lt = %d, sm_xt = %d\n", sm, sm_lt, sm_xt);
|
||||
|
||||
regs[R_EP3] &= ~0xe0; /* clear sm, sm_lt, sm_xt */
|
||||
regs[R_EP3] |= sm ? (1 << 7) : 0 |
|
||||
sm_lt ? (1 << 6) : 0 |
|
||||
sm_xt ? (1 << 5) : 0;
|
||||
|
||||
tda18271_write_regs(fe, R_EP3, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------*/
|
||||
|
||||
int tda18271_calc_main_pll(struct dvb_frontend *fe, u32 freq)
|
||||
{
|
||||
/* sets main post divider & divider bytes, but does not write them */
|
||||
struct tda18271_priv *priv = fe->tuner_priv;
|
||||
unsigned char *regs = priv->tda18271_regs;
|
||||
u8 d, pd;
|
||||
u32 div;
|
||||
|
||||
int ret = tda18271_lookup_pll_map(fe, MAIN_PLL, &freq, &pd, &d);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
|
||||
regs[R_MPD] = (0x77 & pd);
|
||||
|
||||
switch (priv->mode) {
|
||||
case TDA18271_ANALOG:
|
||||
regs[R_MPD] &= ~0x08;
|
||||
break;
|
||||
case TDA18271_DIGITAL:
|
||||
regs[R_MPD] |= 0x08;
|
||||
break;
|
||||
}
|
||||
|
||||
div = ((d * (freq / 1000)) << 7) / 125;
|
||||
|
||||
regs[R_MD1] = 0x7f & (div >> 16);
|
||||
regs[R_MD2] = 0xff & (div >> 8);
|
||||
regs[R_MD3] = 0xff & div;
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int tda18271_calc_cal_pll(struct dvb_frontend *fe, u32 freq)
|
||||
{
|
||||
/* sets cal post divider & divider bytes, but does not write them */
|
||||
struct tda18271_priv *priv = fe->tuner_priv;
|
||||
unsigned char *regs = priv->tda18271_regs;
|
||||
u8 d, pd;
|
||||
u32 div;
|
||||
|
||||
int ret = tda18271_lookup_pll_map(fe, CAL_PLL, &freq, &pd, &d);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
|
||||
regs[R_CPD] = pd;
|
||||
|
||||
div = ((d * (freq / 1000)) << 7) / 125;
|
||||
|
||||
regs[R_CD1] = 0x7f & (div >> 16);
|
||||
regs[R_CD2] = 0xff & (div >> 8);
|
||||
regs[R_CD3] = 0xff & div;
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------*/
|
||||
|
||||
int tda18271_calc_bp_filter(struct dvb_frontend *fe, u32 *freq)
|
||||
{
|
||||
/* sets bp filter bits, but does not write them */
|
||||
struct tda18271_priv *priv = fe->tuner_priv;
|
||||
unsigned char *regs = priv->tda18271_regs;
|
||||
u8 val;
|
||||
|
||||
int ret = tda18271_lookup_map(fe, BP_FILTER, freq, &val);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
|
||||
regs[R_EP1] &= ~0x07; /* clear bp filter bits */
|
||||
regs[R_EP1] |= (0x07 & val);
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int tda18271_calc_km(struct dvb_frontend *fe, u32 *freq)
|
||||
{
|
||||
/* sets K & M bits, but does not write them */
|
||||
struct tda18271_priv *priv = fe->tuner_priv;
|
||||
unsigned char *regs = priv->tda18271_regs;
|
||||
u8 val;
|
||||
|
||||
int ret = tda18271_lookup_map(fe, RF_CAL_KMCO, freq, &val);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
|
||||
regs[R_EB13] &= ~0x7c; /* clear k & m bits */
|
||||
regs[R_EB13] |= (0x7c & val);
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int tda18271_calc_rf_band(struct dvb_frontend *fe, u32 *freq)
|
||||
{
|
||||
/* sets rf band bits, but does not write them */
|
||||
struct tda18271_priv *priv = fe->tuner_priv;
|
||||
unsigned char *regs = priv->tda18271_regs;
|
||||
u8 val;
|
||||
|
||||
int ret = tda18271_lookup_map(fe, RF_BAND, freq, &val);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
|
||||
regs[R_EP2] &= ~0xe0; /* clear rf band bits */
|
||||
regs[R_EP2] |= (0xe0 & (val << 5));
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int tda18271_calc_gain_taper(struct dvb_frontend *fe, u32 *freq)
|
||||
{
|
||||
/* sets gain taper bits, but does not write them */
|
||||
struct tda18271_priv *priv = fe->tuner_priv;
|
||||
unsigned char *regs = priv->tda18271_regs;
|
||||
u8 val;
|
||||
|
||||
int ret = tda18271_lookup_map(fe, GAIN_TAPER, freq, &val);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
|
||||
regs[R_EP2] &= ~0x1f; /* clear gain taper bits */
|
||||
regs[R_EP2] |= (0x1f & val);
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int tda18271_calc_ir_measure(struct dvb_frontend *fe, u32 *freq)
|
||||
{
|
||||
/* sets IR Meas bits, but does not write them */
|
||||
struct tda18271_priv *priv = fe->tuner_priv;
|
||||
unsigned char *regs = priv->tda18271_regs;
|
||||
u8 val;
|
||||
|
||||
int ret = tda18271_lookup_map(fe, IR_MEASURE, freq, &val);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
|
||||
regs[R_EP5] &= ~0x07;
|
||||
regs[R_EP5] |= (0x07 & val);
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int tda18271_calc_rf_cal(struct dvb_frontend *fe, u32 *freq)
|
||||
{
|
||||
/* sets rf cal byte (RFC_Cprog), but does not write it */
|
||||
struct tda18271_priv *priv = fe->tuner_priv;
|
||||
unsigned char *regs = priv->tda18271_regs;
|
||||
u8 val;
|
||||
|
||||
tda18271_lookup_map(fe, RF_CAL, freq, &val);
|
||||
|
||||
regs[R_EB14] = val;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Overrides for Emacs so that we follow Linus's tabbing style.
|
||||
* ---------------------------------------------------------------------------
|
||||
* Local variables:
|
||||
* c-basic-offset: 8
|
||||
* End:
|
||||
*/
|
||||
1153
drivers/media/common/tuners/tda18271-fe.c
Normal file
1153
drivers/media/common/tuners/tda18271-fe.c
Normal file
File diff suppressed because it is too large
Load diff
1313
drivers/media/common/tuners/tda18271-maps.c
Normal file
1313
drivers/media/common/tuners/tda18271-maps.c
Normal file
File diff suppressed because it is too large
Load diff
220
drivers/media/common/tuners/tda18271-priv.h
Normal file
220
drivers/media/common/tuners/tda18271-priv.h
Normal file
|
|
@ -0,0 +1,220 @@
|
|||
/*
|
||||
tda18271-priv.h - private header for the NXP TDA18271 silicon tuner
|
||||
|
||||
Copyright (C) 2007, 2008 Michael Krufky <mkrufky@linuxtv.org>
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
#ifndef __TDA18271_PRIV_H__
|
||||
#define __TDA18271_PRIV_H__
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/mutex.h>
|
||||
#include "tuner-i2c.h"
|
||||
#include "tda18271.h"
|
||||
|
||||
#define R_ID 0x00 /* ID byte */
|
||||
#define R_TM 0x01 /* Thermo byte */
|
||||
#define R_PL 0x02 /* Power level byte */
|
||||
#define R_EP1 0x03 /* Easy Prog byte 1 */
|
||||
#define R_EP2 0x04 /* Easy Prog byte 2 */
|
||||
#define R_EP3 0x05 /* Easy Prog byte 3 */
|
||||
#define R_EP4 0x06 /* Easy Prog byte 4 */
|
||||
#define R_EP5 0x07 /* Easy Prog byte 5 */
|
||||
#define R_CPD 0x08 /* Cal Post-Divider byte */
|
||||
#define R_CD1 0x09 /* Cal Divider byte 1 */
|
||||
#define R_CD2 0x0a /* Cal Divider byte 2 */
|
||||
#define R_CD3 0x0b /* Cal Divider byte 3 */
|
||||
#define R_MPD 0x0c /* Main Post-Divider byte */
|
||||
#define R_MD1 0x0d /* Main Divider byte 1 */
|
||||
#define R_MD2 0x0e /* Main Divider byte 2 */
|
||||
#define R_MD3 0x0f /* Main Divider byte 3 */
|
||||
#define R_EB1 0x10 /* Extended byte 1 */
|
||||
#define R_EB2 0x11 /* Extended byte 2 */
|
||||
#define R_EB3 0x12 /* Extended byte 3 */
|
||||
#define R_EB4 0x13 /* Extended byte 4 */
|
||||
#define R_EB5 0x14 /* Extended byte 5 */
|
||||
#define R_EB6 0x15 /* Extended byte 6 */
|
||||
#define R_EB7 0x16 /* Extended byte 7 */
|
||||
#define R_EB8 0x17 /* Extended byte 8 */
|
||||
#define R_EB9 0x18 /* Extended byte 9 */
|
||||
#define R_EB10 0x19 /* Extended byte 10 */
|
||||
#define R_EB11 0x1a /* Extended byte 11 */
|
||||
#define R_EB12 0x1b /* Extended byte 12 */
|
||||
#define R_EB13 0x1c /* Extended byte 13 */
|
||||
#define R_EB14 0x1d /* Extended byte 14 */
|
||||
#define R_EB15 0x1e /* Extended byte 15 */
|
||||
#define R_EB16 0x1f /* Extended byte 16 */
|
||||
#define R_EB17 0x20 /* Extended byte 17 */
|
||||
#define R_EB18 0x21 /* Extended byte 18 */
|
||||
#define R_EB19 0x22 /* Extended byte 19 */
|
||||
#define R_EB20 0x23 /* Extended byte 20 */
|
||||
#define R_EB21 0x24 /* Extended byte 21 */
|
||||
#define R_EB22 0x25 /* Extended byte 22 */
|
||||
#define R_EB23 0x26 /* Extended byte 23 */
|
||||
|
||||
#define TDA18271_NUM_REGS 39
|
||||
|
||||
/*---------------------------------------------------------------------*/
|
||||
|
||||
struct tda18271_rf_tracking_filter_cal {
|
||||
u32 rfmax;
|
||||
u8 rfband;
|
||||
u32 rf1_def;
|
||||
u32 rf2_def;
|
||||
u32 rf3_def;
|
||||
u32 rf1;
|
||||
u32 rf2;
|
||||
u32 rf3;
|
||||
int rf_a1;
|
||||
int rf_b1;
|
||||
int rf_a2;
|
||||
int rf_b2;
|
||||
};
|
||||
|
||||
enum tda18271_pll {
|
||||
TDA18271_MAIN_PLL,
|
||||
TDA18271_CAL_PLL,
|
||||
};
|
||||
|
||||
enum tda18271_mode {
|
||||
TDA18271_ANALOG,
|
||||
TDA18271_DIGITAL,
|
||||
};
|
||||
|
||||
struct tda18271_map_layout;
|
||||
|
||||
enum tda18271_ver {
|
||||
TDA18271HDC1,
|
||||
TDA18271HDC2,
|
||||
};
|
||||
|
||||
struct tda18271_priv {
|
||||
unsigned char tda18271_regs[TDA18271_NUM_REGS];
|
||||
|
||||
struct list_head hybrid_tuner_instance_list;
|
||||
struct tuner_i2c_props i2c_props;
|
||||
|
||||
enum tda18271_mode mode;
|
||||
enum tda18271_role role;
|
||||
enum tda18271_i2c_gate gate;
|
||||
enum tda18271_ver id;
|
||||
|
||||
unsigned int tm_rfcal;
|
||||
unsigned int cal_initialized:1;
|
||||
unsigned int small_i2c:1;
|
||||
|
||||
struct tda18271_map_layout *maps;
|
||||
struct tda18271_std_map std;
|
||||
struct tda18271_rf_tracking_filter_cal rf_cal_state[8];
|
||||
|
||||
struct mutex lock;
|
||||
|
||||
u32 frequency;
|
||||
u32 bandwidth;
|
||||
};
|
||||
|
||||
/*---------------------------------------------------------------------*/
|
||||
|
||||
extern int tda18271_debug;
|
||||
|
||||
#define DBG_INFO 1
|
||||
#define DBG_MAP 2
|
||||
#define DBG_REG 4
|
||||
#define DBG_ADV 8
|
||||
#define DBG_CAL 16
|
||||
|
||||
#define tda_printk(kern, fmt, arg...) \
|
||||
printk(kern "%s: " fmt, __func__, ##arg)
|
||||
|
||||
#define dprintk(kern, lvl, fmt, arg...) do {\
|
||||
if (tda18271_debug & lvl) \
|
||||
tda_printk(kern, fmt, ##arg); } while (0)
|
||||
|
||||
#define tda_info(fmt, arg...) printk(KERN_INFO fmt, ##arg)
|
||||
#define tda_warn(fmt, arg...) tda_printk(KERN_WARNING, fmt, ##arg)
|
||||
#define tda_err(fmt, arg...) tda_printk(KERN_ERR, fmt, ##arg)
|
||||
#define tda_dbg(fmt, arg...) dprintk(KERN_DEBUG, DBG_INFO, fmt, ##arg)
|
||||
#define tda_map(fmt, arg...) dprintk(KERN_DEBUG, DBG_MAP, fmt, ##arg)
|
||||
#define tda_reg(fmt, arg...) dprintk(KERN_DEBUG, DBG_REG, fmt, ##arg)
|
||||
#define tda_cal(fmt, arg...) dprintk(KERN_DEBUG, DBG_CAL, fmt, ##arg)
|
||||
|
||||
/*---------------------------------------------------------------------*/
|
||||
|
||||
enum tda18271_map_type {
|
||||
/* tda18271_pll_map */
|
||||
MAIN_PLL,
|
||||
CAL_PLL,
|
||||
/* tda18271_map */
|
||||
RF_CAL,
|
||||
RF_CAL_KMCO,
|
||||
RF_CAL_DC_OVER_DT,
|
||||
BP_FILTER,
|
||||
RF_BAND,
|
||||
GAIN_TAPER,
|
||||
IR_MEASURE,
|
||||
};
|
||||
|
||||
extern int tda18271_lookup_pll_map(struct dvb_frontend *fe,
|
||||
enum tda18271_map_type map_type,
|
||||
u32 *freq, u8 *post_div, u8 *div);
|
||||
extern int tda18271_lookup_map(struct dvb_frontend *fe,
|
||||
enum tda18271_map_type map_type,
|
||||
u32 *freq, u8 *val);
|
||||
|
||||
extern int tda18271_lookup_thermometer(struct dvb_frontend *fe);
|
||||
|
||||
extern int tda18271_lookup_rf_band(struct dvb_frontend *fe,
|
||||
u32 *freq, u8 *rf_band);
|
||||
|
||||
extern int tda18271_lookup_cid_target(struct dvb_frontend *fe,
|
||||
u32 *freq, u8 *cid_target,
|
||||
u16 *count_limit);
|
||||
|
||||
extern int tda18271_assign_map_layout(struct dvb_frontend *fe);
|
||||
|
||||
/*---------------------------------------------------------------------*/
|
||||
|
||||
extern int tda18271_read_regs(struct dvb_frontend *fe);
|
||||
extern int tda18271_read_extended(struct dvb_frontend *fe);
|
||||
extern int tda18271_write_regs(struct dvb_frontend *fe, int idx, int len);
|
||||
extern int tda18271_init_regs(struct dvb_frontend *fe);
|
||||
|
||||
extern int tda18271_charge_pump_source(struct dvb_frontend *fe,
|
||||
enum tda18271_pll pll, int force);
|
||||
extern int tda18271_set_standby_mode(struct dvb_frontend *fe,
|
||||
int sm, int sm_lt, int sm_xt);
|
||||
|
||||
extern int tda18271_calc_main_pll(struct dvb_frontend *fe, u32 freq);
|
||||
extern int tda18271_calc_cal_pll(struct dvb_frontend *fe, u32 freq);
|
||||
|
||||
extern int tda18271_calc_bp_filter(struct dvb_frontend *fe, u32 *freq);
|
||||
extern int tda18271_calc_km(struct dvb_frontend *fe, u32 *freq);
|
||||
extern int tda18271_calc_rf_band(struct dvb_frontend *fe, u32 *freq);
|
||||
extern int tda18271_calc_gain_taper(struct dvb_frontend *fe, u32 *freq);
|
||||
extern int tda18271_calc_ir_measure(struct dvb_frontend *fe, u32 *freq);
|
||||
extern int tda18271_calc_rf_cal(struct dvb_frontend *fe, u32 *freq);
|
||||
|
||||
#endif /* __TDA18271_PRIV_H__ */
|
||||
|
||||
/*
|
||||
* Overrides for Emacs so that we follow Linus's tabbing style.
|
||||
* ---------------------------------------------------------------------------
|
||||
* Local variables:
|
||||
* c-basic-offset: 8
|
||||
* End:
|
||||
*/
|
||||
99
drivers/media/common/tuners/tda18271.h
Normal file
99
drivers/media/common/tuners/tda18271.h
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
tda18271.h - header for the Philips / NXP TDA18271 silicon tuner
|
||||
|
||||
Copyright (C) 2007, 2008 Michael Krufky <mkrufky@linuxtv.org>
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
#ifndef __TDA18271_H__
|
||||
#define __TDA18271_H__
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include "dvb_frontend.h"
|
||||
|
||||
struct tda18271_std_map_item {
|
||||
u16 if_freq;
|
||||
|
||||
/* EP3[4:3] */
|
||||
unsigned int agc_mode:2;
|
||||
/* EP3[2:0] */
|
||||
unsigned int std:3;
|
||||
/* EP4[7] */
|
||||
unsigned int fm_rfn:1;
|
||||
/* EP4[4:2] */
|
||||
unsigned int if_lvl:3;
|
||||
/* EB22[6:0] */
|
||||
unsigned int rfagc_top:7;
|
||||
};
|
||||
|
||||
struct tda18271_std_map {
|
||||
struct tda18271_std_map_item fm_radio;
|
||||
struct tda18271_std_map_item atv_b;
|
||||
struct tda18271_std_map_item atv_dk;
|
||||
struct tda18271_std_map_item atv_gh;
|
||||
struct tda18271_std_map_item atv_i;
|
||||
struct tda18271_std_map_item atv_l;
|
||||
struct tda18271_std_map_item atv_lc;
|
||||
struct tda18271_std_map_item atv_mn;
|
||||
struct tda18271_std_map_item atsc_6;
|
||||
struct tda18271_std_map_item dvbt_6;
|
||||
struct tda18271_std_map_item dvbt_7;
|
||||
struct tda18271_std_map_item dvbt_8;
|
||||
struct tda18271_std_map_item qam_6;
|
||||
struct tda18271_std_map_item qam_8;
|
||||
};
|
||||
|
||||
enum tda18271_role {
|
||||
TDA18271_MASTER = 0,
|
||||
TDA18271_SLAVE,
|
||||
};
|
||||
|
||||
enum tda18271_i2c_gate {
|
||||
TDA18271_GATE_AUTO = 0,
|
||||
TDA18271_GATE_ANALOG,
|
||||
TDA18271_GATE_DIGITAL,
|
||||
};
|
||||
|
||||
struct tda18271_config {
|
||||
/* override default if freq / std settings (optional) */
|
||||
struct tda18271_std_map *std_map;
|
||||
|
||||
/* master / slave tuner: master uses main pll, slave uses cal pll */
|
||||
enum tda18271_role role;
|
||||
|
||||
/* use i2c gate provided by analog or digital demod */
|
||||
enum tda18271_i2c_gate gate;
|
||||
|
||||
/* some i2c providers cant write all 39 registers at once */
|
||||
unsigned int small_i2c:1;
|
||||
};
|
||||
|
||||
#if defined(CONFIG_DVB_TDA18271) || (defined(CONFIG_DVB_TDA18271_MODULE) && defined(MODULE))
|
||||
extern struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr,
|
||||
struct i2c_adapter *i2c,
|
||||
struct tda18271_config *cfg);
|
||||
#else
|
||||
static inline struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe,
|
||||
u8 addr,
|
||||
struct i2c_adapter *i2c,
|
||||
struct tda18271_config *cfg)
|
||||
{
|
||||
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __TDA18271_H__ */
|
||||
852
drivers/media/common/tuners/tda827x.c
Normal file
852
drivers/media/common/tuners/tda827x.c
Normal file
|
|
@ -0,0 +1,852 @@
|
|||
/*
|
||||
*
|
||||
* (c) 2005 Hartmut Hackmann
|
||||
* (c) 2007 Michael Krufky
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <asm/types.h>
|
||||
#include <linux/dvb/frontend.h>
|
||||
#include <linux/videodev2.h>
|
||||
|
||||
#include "tda827x.h"
|
||||
|
||||
static int debug;
|
||||
module_param(debug, int, 0644);
|
||||
MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
|
||||
|
||||
#define dprintk(args...) \
|
||||
do { \
|
||||
if (debug) printk(KERN_DEBUG "tda827x: " args); \
|
||||
} while (0)
|
||||
|
||||
struct tda827x_priv {
|
||||
int i2c_addr;
|
||||
struct i2c_adapter *i2c_adap;
|
||||
struct tda827x_config *cfg;
|
||||
|
||||
unsigned int sgIF;
|
||||
unsigned char lpsel;
|
||||
|
||||
u32 frequency;
|
||||
u32 bandwidth;
|
||||
};
|
||||
|
||||
static void tda827x_set_std(struct dvb_frontend *fe,
|
||||
struct analog_parameters *params)
|
||||
{
|
||||
struct tda827x_priv *priv = fe->tuner_priv;
|
||||
char *mode;
|
||||
|
||||
priv->lpsel = 0;
|
||||
if (params->std & V4L2_STD_MN) {
|
||||
priv->sgIF = 92;
|
||||
priv->lpsel = 1;
|
||||
mode = "MN";
|
||||
} else if (params->std & V4L2_STD_B) {
|
||||
priv->sgIF = 108;
|
||||
mode = "B";
|
||||
} else if (params->std & V4L2_STD_GH) {
|
||||
priv->sgIF = 124;
|
||||
mode = "GH";
|
||||
} else if (params->std & V4L2_STD_PAL_I) {
|
||||
priv->sgIF = 124;
|
||||
mode = "I";
|
||||
} else if (params->std & V4L2_STD_DK) {
|
||||
priv->sgIF = 124;
|
||||
mode = "DK";
|
||||
} else if (params->std & V4L2_STD_SECAM_L) {
|
||||
priv->sgIF = 124;
|
||||
mode = "L";
|
||||
} else if (params->std & V4L2_STD_SECAM_LC) {
|
||||
priv->sgIF = 20;
|
||||
mode = "LC";
|
||||
} else {
|
||||
priv->sgIF = 124;
|
||||
mode = "xx";
|
||||
}
|
||||
|
||||
if (params->mode == V4L2_TUNER_RADIO)
|
||||
priv->sgIF = 88; /* if frequency is 5.5 MHz */
|
||||
|
||||
dprintk("setting tda827x to system %s\n", mode);
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
struct tda827x_data {
|
||||
u32 lomax;
|
||||
u8 spd;
|
||||
u8 bs;
|
||||
u8 bp;
|
||||
u8 cp;
|
||||
u8 gc3;
|
||||
u8 div1p5;
|
||||
};
|
||||
|
||||
static const struct tda827x_data tda827x_table[] = {
|
||||
{ .lomax = 62000000, .spd = 3, .bs = 2, .bp = 0, .cp = 0, .gc3 = 3, .div1p5 = 1},
|
||||
{ .lomax = 66000000, .spd = 3, .bs = 3, .bp = 0, .cp = 0, .gc3 = 3, .div1p5 = 1},
|
||||
{ .lomax = 76000000, .spd = 3, .bs = 1, .bp = 0, .cp = 0, .gc3 = 3, .div1p5 = 0},
|
||||
{ .lomax = 84000000, .spd = 3, .bs = 2, .bp = 0, .cp = 0, .gc3 = 3, .div1p5 = 0},
|
||||
{ .lomax = 93000000, .spd = 3, .bs = 2, .bp = 0, .cp = 0, .gc3 = 1, .div1p5 = 0},
|
||||
{ .lomax = 98000000, .spd = 3, .bs = 3, .bp = 0, .cp = 0, .gc3 = 1, .div1p5 = 0},
|
||||
{ .lomax = 109000000, .spd = 3, .bs = 3, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 0},
|
||||
{ .lomax = 123000000, .spd = 2, .bs = 2, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 1},
|
||||
{ .lomax = 133000000, .spd = 2, .bs = 3, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 1},
|
||||
{ .lomax = 151000000, .spd = 2, .bs = 1, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 0},
|
||||
{ .lomax = 154000000, .spd = 2, .bs = 2, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 0},
|
||||
{ .lomax = 181000000, .spd = 2, .bs = 2, .bp = 1, .cp = 0, .gc3 = 0, .div1p5 = 0},
|
||||
{ .lomax = 185000000, .spd = 2, .bs = 2, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 0},
|
||||
{ .lomax = 217000000, .spd = 2, .bs = 3, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 0},
|
||||
{ .lomax = 244000000, .spd = 1, .bs = 2, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 1},
|
||||
{ .lomax = 265000000, .spd = 1, .bs = 3, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 1},
|
||||
{ .lomax = 302000000, .spd = 1, .bs = 1, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 0},
|
||||
{ .lomax = 324000000, .spd = 1, .bs = 2, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 0},
|
||||
{ .lomax = 370000000, .spd = 1, .bs = 2, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 0},
|
||||
{ .lomax = 454000000, .spd = 1, .bs = 3, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 0},
|
||||
{ .lomax = 493000000, .spd = 0, .bs = 2, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 1},
|
||||
{ .lomax = 530000000, .spd = 0, .bs = 3, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 1},
|
||||
{ .lomax = 554000000, .spd = 0, .bs = 1, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 0},
|
||||
{ .lomax = 604000000, .spd = 0, .bs = 1, .bp = 4, .cp = 0, .gc3 = 0, .div1p5 = 0},
|
||||
{ .lomax = 696000000, .spd = 0, .bs = 2, .bp = 4, .cp = 0, .gc3 = 0, .div1p5 = 0},
|
||||
{ .lomax = 740000000, .spd = 0, .bs = 2, .bp = 4, .cp = 1, .gc3 = 0, .div1p5 = 0},
|
||||
{ .lomax = 820000000, .spd = 0, .bs = 3, .bp = 4, .cp = 0, .gc3 = 0, .div1p5 = 0},
|
||||
{ .lomax = 865000000, .spd = 0, .bs = 3, .bp = 4, .cp = 1, .gc3 = 0, .div1p5 = 0},
|
||||
{ .lomax = 0, .spd = 0, .bs = 0, .bp = 0, .cp = 0, .gc3 = 0, .div1p5 = 0}
|
||||
};
|
||||
|
||||
static int tda827xo_set_params(struct dvb_frontend *fe,
|
||||
struct dvb_frontend_parameters *params)
|
||||
{
|
||||
struct tda827x_priv *priv = fe->tuner_priv;
|
||||
u8 buf[14];
|
||||
|
||||
struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = 0,
|
||||
.buf = buf, .len = sizeof(buf) };
|
||||
int i, tuner_freq, if_freq;
|
||||
u32 N;
|
||||
|
||||
dprintk("%s:\n", __func__);
|
||||
switch (params->u.ofdm.bandwidth) {
|
||||
case BANDWIDTH_6_MHZ:
|
||||
if_freq = 4000000;
|
||||
break;
|
||||
case BANDWIDTH_7_MHZ:
|
||||
if_freq = 4500000;
|
||||
break;
|
||||
default: /* 8 MHz or Auto */
|
||||
if_freq = 5000000;
|
||||
break;
|
||||
}
|
||||
tuner_freq = params->frequency + if_freq;
|
||||
|
||||
i = 0;
|
||||
while (tda827x_table[i].lomax < tuner_freq) {
|
||||
if (tda827x_table[i + 1].lomax == 0)
|
||||
break;
|
||||
i++;
|
||||
}
|
||||
|
||||
N = ((tuner_freq + 125000) / 250000) << (tda827x_table[i].spd + 2);
|
||||
buf[0] = 0;
|
||||
buf[1] = (N>>8) | 0x40;
|
||||
buf[2] = N & 0xff;
|
||||
buf[3] = 0;
|
||||
buf[4] = 0x52;
|
||||
buf[5] = (tda827x_table[i].spd << 6) + (tda827x_table[i].div1p5 << 5) +
|
||||
(tda827x_table[i].bs << 3) +
|
||||
tda827x_table[i].bp;
|
||||
buf[6] = (tda827x_table[i].gc3 << 4) + 0x8f;
|
||||
buf[7] = 0xbf;
|
||||
buf[8] = 0x2a;
|
||||
buf[9] = 0x05;
|
||||
buf[10] = 0xff;
|
||||
buf[11] = 0x00;
|
||||
buf[12] = 0x00;
|
||||
buf[13] = 0x40;
|
||||
|
||||
msg.len = 14;
|
||||
if (fe->ops.i2c_gate_ctrl)
|
||||
fe->ops.i2c_gate_ctrl(fe, 1);
|
||||
if (i2c_transfer(priv->i2c_adap, &msg, 1) != 1) {
|
||||
printk("%s: could not write to tuner at addr: 0x%02x\n",
|
||||
__func__, priv->i2c_addr << 1);
|
||||
return -EIO;
|
||||
}
|
||||
msleep(500);
|
||||
/* correct CP value */
|
||||
buf[0] = 0x30;
|
||||
buf[1] = 0x50 + tda827x_table[i].cp;
|
||||
msg.len = 2;
|
||||
|
||||
if (fe->ops.i2c_gate_ctrl)
|
||||
fe->ops.i2c_gate_ctrl(fe, 1);
|
||||
i2c_transfer(priv->i2c_adap, &msg, 1);
|
||||
|
||||
priv->frequency = tuner_freq - if_freq; // FIXME
|
||||
priv->bandwidth = (fe->ops.info.type == FE_OFDM) ? params->u.ofdm.bandwidth : 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tda827xo_sleep(struct dvb_frontend *fe)
|
||||
{
|
||||
struct tda827x_priv *priv = fe->tuner_priv;
|
||||
static u8 buf[] = { 0x30, 0xd0 };
|
||||
struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = 0,
|
||||
.buf = buf, .len = sizeof(buf) };
|
||||
|
||||
dprintk("%s:\n", __func__);
|
||||
if (fe->ops.i2c_gate_ctrl)
|
||||
fe->ops.i2c_gate_ctrl(fe, 1);
|
||||
i2c_transfer(priv->i2c_adap, &msg, 1);
|
||||
|
||||
if (priv->cfg && priv->cfg->sleep)
|
||||
priv->cfg->sleep(fe);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
static int tda827xo_set_analog_params(struct dvb_frontend *fe,
|
||||
struct analog_parameters *params)
|
||||
{
|
||||
unsigned char tuner_reg[8];
|
||||
unsigned char reg2[2];
|
||||
u32 N;
|
||||
int i;
|
||||
struct tda827x_priv *priv = fe->tuner_priv;
|
||||
struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = 0 };
|
||||
unsigned int freq = params->frequency;
|
||||
|
||||
tda827x_set_std(fe, params);
|
||||
|
||||
if (params->mode == V4L2_TUNER_RADIO)
|
||||
freq = freq / 1000;
|
||||
|
||||
N = freq + priv->sgIF;
|
||||
|
||||
i = 0;
|
||||
while (tda827x_table[i].lomax < N * 62500) {
|
||||
if (tda827x_table[i + 1].lomax == 0)
|
||||
break;
|
||||
i++;
|
||||
}
|
||||
|
||||
N = N << tda827x_table[i].spd;
|
||||
|
||||
tuner_reg[0] = 0;
|
||||
tuner_reg[1] = (unsigned char)(N>>8);
|
||||
tuner_reg[2] = (unsigned char) N;
|
||||
tuner_reg[3] = 0x40;
|
||||
tuner_reg[4] = 0x52 + (priv->lpsel << 5);
|
||||
tuner_reg[5] = (tda827x_table[i].spd << 6) +
|
||||
(tda827x_table[i].div1p5 << 5) +
|
||||
(tda827x_table[i].bs << 3) + tda827x_table[i].bp;
|
||||
tuner_reg[6] = 0x8f + (tda827x_table[i].gc3 << 4);
|
||||
tuner_reg[7] = 0x8f;
|
||||
|
||||
msg.buf = tuner_reg;
|
||||
msg.len = 8;
|
||||
i2c_transfer(priv->i2c_adap, &msg, 1);
|
||||
|
||||
msg.buf = reg2;
|
||||
msg.len = 2;
|
||||
reg2[0] = 0x80;
|
||||
reg2[1] = 0;
|
||||
i2c_transfer(priv->i2c_adap, &msg, 1);
|
||||
|
||||
reg2[0] = 0x60;
|
||||
reg2[1] = 0xbf;
|
||||
i2c_transfer(priv->i2c_adap, &msg, 1);
|
||||
|
||||
reg2[0] = 0x30;
|
||||
reg2[1] = tuner_reg[4] + 0x80;
|
||||
i2c_transfer(priv->i2c_adap, &msg, 1);
|
||||
|
||||
msleep(1);
|
||||
reg2[0] = 0x30;
|
||||
reg2[1] = tuner_reg[4] + 4;
|
||||
i2c_transfer(priv->i2c_adap, &msg, 1);
|
||||
|
||||
msleep(1);
|
||||
reg2[0] = 0x30;
|
||||
reg2[1] = tuner_reg[4];
|
||||
i2c_transfer(priv->i2c_adap, &msg, 1);
|
||||
|
||||
msleep(550);
|
||||
reg2[0] = 0x30;
|
||||
reg2[1] = (tuner_reg[4] & 0xfc) + tda827x_table[i].cp;
|
||||
i2c_transfer(priv->i2c_adap, &msg, 1);
|
||||
|
||||
reg2[0] = 0x60;
|
||||
reg2[1] = 0x3f;
|
||||
i2c_transfer(priv->i2c_adap, &msg, 1);
|
||||
|
||||
reg2[0] = 0x80;
|
||||
reg2[1] = 0x08; /* Vsync en */
|
||||
i2c_transfer(priv->i2c_adap, &msg, 1);
|
||||
|
||||
priv->frequency = freq * 62500;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tda827xo_agcf(struct dvb_frontend *fe)
|
||||
{
|
||||
struct tda827x_priv *priv = fe->tuner_priv;
|
||||
unsigned char data[] = { 0x80, 0x0c };
|
||||
struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = 0,
|
||||
.buf = data, .len = 2};
|
||||
|
||||
i2c_transfer(priv->i2c_adap, &msg, 1);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
struct tda827xa_data {
|
||||
u32 lomax;
|
||||
u8 svco;
|
||||
u8 spd;
|
||||
u8 scr;
|
||||
u8 sbs;
|
||||
u8 gc3;
|
||||
};
|
||||
|
||||
static const struct tda827xa_data tda827xa_dvbt[] = {
|
||||
{ .lomax = 56875000, .svco = 3, .spd = 4, .scr = 0, .sbs = 0, .gc3 = 1},
|
||||
{ .lomax = 67250000, .svco = 0, .spd = 3, .scr = 0, .sbs = 0, .gc3 = 1},
|
||||
{ .lomax = 81250000, .svco = 1, .spd = 3, .scr = 0, .sbs = 0, .gc3 = 1},
|
||||
{ .lomax = 97500000, .svco = 2, .spd = 3, .scr = 0, .sbs = 0, .gc3 = 1},
|
||||
{ .lomax = 113750000, .svco = 3, .spd = 3, .scr = 0, .sbs = 1, .gc3 = 1},
|
||||
{ .lomax = 134500000, .svco = 0, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1},
|
||||
{ .lomax = 154000000, .svco = 1, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1},
|
||||
{ .lomax = 162500000, .svco = 1, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1},
|
||||
{ .lomax = 183000000, .svco = 2, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1},
|
||||
{ .lomax = 195000000, .svco = 2, .spd = 2, .scr = 0, .sbs = 2, .gc3 = 1},
|
||||
{ .lomax = 227500000, .svco = 3, .spd = 2, .scr = 0, .sbs = 2, .gc3 = 1},
|
||||
{ .lomax = 269000000, .svco = 0, .spd = 1, .scr = 0, .sbs = 2, .gc3 = 1},
|
||||
{ .lomax = 290000000, .svco = 1, .spd = 1, .scr = 0, .sbs = 2, .gc3 = 1},
|
||||
{ .lomax = 325000000, .svco = 1, .spd = 1, .scr = 0, .sbs = 3, .gc3 = 1},
|
||||
{ .lomax = 390000000, .svco = 2, .spd = 1, .scr = 0, .sbs = 3, .gc3 = 1},
|
||||
{ .lomax = 455000000, .svco = 3, .spd = 1, .scr = 0, .sbs = 3, .gc3 = 1},
|
||||
{ .lomax = 520000000, .svco = 0, .spd = 0, .scr = 0, .sbs = 3, .gc3 = 1},
|
||||
{ .lomax = 538000000, .svco = 0, .spd = 0, .scr = 1, .sbs = 3, .gc3 = 1},
|
||||
{ .lomax = 550000000, .svco = 1, .spd = 0, .scr = 0, .sbs = 3, .gc3 = 1},
|
||||
{ .lomax = 620000000, .svco = 1, .spd = 0, .scr = 0, .sbs = 4, .gc3 = 0},
|
||||
{ .lomax = 650000000, .svco = 1, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 0},
|
||||
{ .lomax = 700000000, .svco = 2, .spd = 0, .scr = 0, .sbs = 4, .gc3 = 0},
|
||||
{ .lomax = 780000000, .svco = 2, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 0},
|
||||
{ .lomax = 820000000, .svco = 3, .spd = 0, .scr = 0, .sbs = 4, .gc3 = 0},
|
||||
{ .lomax = 870000000, .svco = 3, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 0},
|
||||
{ .lomax = 911000000, .svco = 3, .spd = 0, .scr = 2, .sbs = 4, .gc3 = 0},
|
||||
{ .lomax = 0, .svco = 0, .spd = 0, .scr = 0, .sbs = 0, .gc3 = 0}
|
||||
};
|
||||
|
||||
static struct tda827xa_data tda827xa_analog[] = {
|
||||
{ .lomax = 56875000, .svco = 3, .spd = 4, .scr = 0, .sbs = 0, .gc3 = 3},
|
||||
{ .lomax = 67250000, .svco = 0, .spd = 3, .scr = 0, .sbs = 0, .gc3 = 3},
|
||||
{ .lomax = 81250000, .svco = 1, .spd = 3, .scr = 0, .sbs = 0, .gc3 = 3},
|
||||
{ .lomax = 97500000, .svco = 2, .spd = 3, .scr = 0, .sbs = 0, .gc3 = 3},
|
||||
{ .lomax = 113750000, .svco = 3, .spd = 3, .scr = 0, .sbs = 1, .gc3 = 1},
|
||||
{ .lomax = 134500000, .svco = 0, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1},
|
||||
{ .lomax = 154000000, .svco = 1, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1},
|
||||
{ .lomax = 162500000, .svco = 1, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1},
|
||||
{ .lomax = 183000000, .svco = 2, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1},
|
||||
{ .lomax = 195000000, .svco = 2, .spd = 2, .scr = 0, .sbs = 2, .gc3 = 1},
|
||||
{ .lomax = 227500000, .svco = 3, .spd = 2, .scr = 0, .sbs = 2, .gc3 = 3},
|
||||
{ .lomax = 269000000, .svco = 0, .spd = 1, .scr = 0, .sbs = 2, .gc3 = 3},
|
||||
{ .lomax = 325000000, .svco = 1, .spd = 1, .scr = 0, .sbs = 2, .gc3 = 1},
|
||||
{ .lomax = 390000000, .svco = 2, .spd = 1, .scr = 0, .sbs = 3, .gc3 = 3},
|
||||
{ .lomax = 455000000, .svco = 3, .spd = 1, .scr = 0, .sbs = 3, .gc3 = 3},
|
||||
{ .lomax = 520000000, .svco = 0, .spd = 0, .scr = 0, .sbs = 3, .gc3 = 1},
|
||||
{ .lomax = 538000000, .svco = 0, .spd = 0, .scr = 1, .sbs = 3, .gc3 = 1},
|
||||
{ .lomax = 554000000, .svco = 1, .spd = 0, .scr = 0, .sbs = 3, .gc3 = 1},
|
||||
{ .lomax = 620000000, .svco = 1, .spd = 0, .scr = 0, .sbs = 4, .gc3 = 0},
|
||||
{ .lomax = 650000000, .svco = 1, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 0},
|
||||
{ .lomax = 700000000, .svco = 2, .spd = 0, .scr = 0, .sbs = 4, .gc3 = 0},
|
||||
{ .lomax = 780000000, .svco = 2, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 0},
|
||||
{ .lomax = 820000000, .svco = 3, .spd = 0, .scr = 0, .sbs = 4, .gc3 = 0},
|
||||
{ .lomax = 870000000, .svco = 3, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 0},
|
||||
{ .lomax = 911000000, .svco = 3, .spd = 0, .scr = 2, .sbs = 4, .gc3 = 0},
|
||||
{ .lomax = 0, .svco = 0, .spd = 0, .scr = 0, .sbs = 0, .gc3 = 0}
|
||||
};
|
||||
|
||||
static int tda827xa_sleep(struct dvb_frontend *fe)
|
||||
{
|
||||
struct tda827x_priv *priv = fe->tuner_priv;
|
||||
static u8 buf[] = { 0x30, 0x90 };
|
||||
struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = 0,
|
||||
.buf = buf, .len = sizeof(buf) };
|
||||
|
||||
dprintk("%s:\n", __func__);
|
||||
if (fe->ops.i2c_gate_ctrl)
|
||||
fe->ops.i2c_gate_ctrl(fe, 1);
|
||||
|
||||
i2c_transfer(priv->i2c_adap, &msg, 1);
|
||||
|
||||
if (fe->ops.i2c_gate_ctrl)
|
||||
fe->ops.i2c_gate_ctrl(fe, 0);
|
||||
|
||||
if (priv->cfg && priv->cfg->sleep)
|
||||
priv->cfg->sleep(fe);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tda827xa_lna_gain(struct dvb_frontend *fe, int high,
|
||||
struct analog_parameters *params)
|
||||
{
|
||||
struct tda827x_priv *priv = fe->tuner_priv;
|
||||
unsigned char buf[] = {0x22, 0x01};
|
||||
int arg;
|
||||
int gp_func;
|
||||
struct i2c_msg msg = { .addr = priv->cfg->switch_addr, .flags = 0,
|
||||
.buf = buf, .len = sizeof(buf) };
|
||||
|
||||
if (NULL == priv->cfg) {
|
||||
dprintk("tda827x_config not defined, cannot set LNA gain!\n");
|
||||
return;
|
||||
}
|
||||
if (priv->cfg->config) {
|
||||
if (high)
|
||||
dprintk("setting LNA to high gain\n");
|
||||
else
|
||||
dprintk("setting LNA to low gain\n");
|
||||
}
|
||||
switch (priv->cfg->config) {
|
||||
case 0: /* no LNA */
|
||||
break;
|
||||
case 1: /* switch is GPIO 0 of tda8290 */
|
||||
case 2:
|
||||
if (params == NULL) {
|
||||
gp_func = 0;
|
||||
arg = 0;
|
||||
} else {
|
||||
/* turn Vsync on */
|
||||
gp_func = 1;
|
||||
if (params->std & V4L2_STD_MN)
|
||||
arg = 1;
|
||||
else
|
||||
arg = 0;
|
||||
}
|
||||
if (priv->cfg->tuner_callback)
|
||||
priv->cfg->tuner_callback(priv->i2c_adap->algo_data,
|
||||
gp_func, arg);
|
||||
buf[1] = high ? 0 : 1;
|
||||
if (priv->cfg->config == 2)
|
||||
buf[1] = high ? 1 : 0;
|
||||
i2c_transfer(priv->i2c_adap, &msg, 1);
|
||||
break;
|
||||
case 3: /* switch with GPIO of saa713x */
|
||||
if (priv->cfg->tuner_callback)
|
||||
priv->cfg->tuner_callback(priv->i2c_adap->algo_data, 0, high);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int tda827xa_set_params(struct dvb_frontend *fe,
|
||||
struct dvb_frontend_parameters *params)
|
||||
{
|
||||
struct tda827x_priv *priv = fe->tuner_priv;
|
||||
u8 buf[11];
|
||||
|
||||
struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = 0,
|
||||
.buf = buf, .len = sizeof(buf) };
|
||||
|
||||
int i, tuner_freq, if_freq;
|
||||
u32 N;
|
||||
|
||||
dprintk("%s:\n", __func__);
|
||||
|
||||
tda827xa_lna_gain(fe, 1, NULL);
|
||||
msleep(20);
|
||||
|
||||
switch (params->u.ofdm.bandwidth) {
|
||||
case BANDWIDTH_6_MHZ:
|
||||
if_freq = 4000000;
|
||||
break;
|
||||
case BANDWIDTH_7_MHZ:
|
||||
if_freq = 4500000;
|
||||
break;
|
||||
default: /* 8 MHz or Auto */
|
||||
if_freq = 5000000;
|
||||
break;
|
||||
}
|
||||
tuner_freq = params->frequency + if_freq;
|
||||
|
||||
i = 0;
|
||||
while (tda827xa_dvbt[i].lomax < tuner_freq) {
|
||||
if(tda827xa_dvbt[i + 1].lomax == 0)
|
||||
break;
|
||||
i++;
|
||||
}
|
||||
|
||||
N = ((tuner_freq + 31250) / 62500) << tda827xa_dvbt[i].spd;
|
||||
buf[0] = 0; // subaddress
|
||||
buf[1] = N >> 8;
|
||||
buf[2] = N & 0xff;
|
||||
buf[3] = 0;
|
||||
buf[4] = 0x16;
|
||||
buf[5] = (tda827xa_dvbt[i].spd << 5) + (tda827xa_dvbt[i].svco << 3) +
|
||||
tda827xa_dvbt[i].sbs;
|
||||
buf[6] = 0x4b + (tda827xa_dvbt[i].gc3 << 4);
|
||||
buf[7] = 0x1c;
|
||||
buf[8] = 0x06;
|
||||
buf[9] = 0x24;
|
||||
buf[10] = 0x00;
|
||||
msg.len = 11;
|
||||
if (fe->ops.i2c_gate_ctrl)
|
||||
fe->ops.i2c_gate_ctrl(fe, 1);
|
||||
if (i2c_transfer(priv->i2c_adap, &msg, 1) != 1) {
|
||||
printk("%s: could not write to tuner at addr: 0x%02x\n",
|
||||
__func__, priv->i2c_addr << 1);
|
||||
return -EIO;
|
||||
}
|
||||
buf[0] = 0x90;
|
||||
buf[1] = 0xff;
|
||||
buf[2] = 0x60;
|
||||
buf[3] = 0x00;
|
||||
buf[4] = 0x59; // lpsel, for 6MHz + 2
|
||||
msg.len = 5;
|
||||
if (fe->ops.i2c_gate_ctrl)
|
||||
fe->ops.i2c_gate_ctrl(fe, 1);
|
||||
i2c_transfer(priv->i2c_adap, &msg, 1);
|
||||
|
||||
buf[0] = 0xa0;
|
||||
buf[1] = 0x40;
|
||||
msg.len = 2;
|
||||
if (fe->ops.i2c_gate_ctrl)
|
||||
fe->ops.i2c_gate_ctrl(fe, 1);
|
||||
i2c_transfer(priv->i2c_adap, &msg, 1);
|
||||
|
||||
msleep(11);
|
||||
msg.flags = I2C_M_RD;
|
||||
if (fe->ops.i2c_gate_ctrl)
|
||||
fe->ops.i2c_gate_ctrl(fe, 1);
|
||||
i2c_transfer(priv->i2c_adap, &msg, 1);
|
||||
msg.flags = 0;
|
||||
|
||||
buf[1] >>= 4;
|
||||
dprintk("tda8275a AGC2 gain is: %d\n", buf[1]);
|
||||
if ((buf[1]) < 2) {
|
||||
tda827xa_lna_gain(fe, 0, NULL);
|
||||
buf[0] = 0x60;
|
||||
buf[1] = 0x0c;
|
||||
if (fe->ops.i2c_gate_ctrl)
|
||||
fe->ops.i2c_gate_ctrl(fe, 1);
|
||||
i2c_transfer(priv->i2c_adap, &msg, 1);
|
||||
}
|
||||
|
||||
buf[0] = 0xc0;
|
||||
buf[1] = 0x99; // lpsel, for 6MHz + 2
|
||||
if (fe->ops.i2c_gate_ctrl)
|
||||
fe->ops.i2c_gate_ctrl(fe, 1);
|
||||
i2c_transfer(priv->i2c_adap, &msg, 1);
|
||||
|
||||
buf[0] = 0x60;
|
||||
buf[1] = 0x3c;
|
||||
if (fe->ops.i2c_gate_ctrl)
|
||||
fe->ops.i2c_gate_ctrl(fe, 1);
|
||||
i2c_transfer(priv->i2c_adap, &msg, 1);
|
||||
|
||||
/* correct CP value */
|
||||
buf[0] = 0x30;
|
||||
buf[1] = 0x10 + tda827xa_dvbt[i].scr;
|
||||
if (fe->ops.i2c_gate_ctrl)
|
||||
fe->ops.i2c_gate_ctrl(fe, 1);
|
||||
i2c_transfer(priv->i2c_adap, &msg, 1);
|
||||
|
||||
msleep(163);
|
||||
buf[0] = 0xc0;
|
||||
buf[1] = 0x39; // lpsel, for 6MHz + 2
|
||||
if (fe->ops.i2c_gate_ctrl)
|
||||
fe->ops.i2c_gate_ctrl(fe, 1);
|
||||
i2c_transfer(priv->i2c_adap, &msg, 1);
|
||||
|
||||
msleep(3);
|
||||
/* freeze AGC1 */
|
||||
buf[0] = 0x50;
|
||||
buf[1] = 0x4f + (tda827xa_dvbt[i].gc3 << 4);
|
||||
if (fe->ops.i2c_gate_ctrl)
|
||||
fe->ops.i2c_gate_ctrl(fe, 1);
|
||||
i2c_transfer(priv->i2c_adap, &msg, 1);
|
||||
|
||||
priv->frequency = tuner_freq - if_freq; // FIXME
|
||||
priv->bandwidth = (fe->ops.info.type == FE_OFDM) ? params->u.ofdm.bandwidth : 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int tda827xa_set_analog_params(struct dvb_frontend *fe,
|
||||
struct analog_parameters *params)
|
||||
{
|
||||
unsigned char tuner_reg[11];
|
||||
u32 N;
|
||||
int i;
|
||||
struct tda827x_priv *priv = fe->tuner_priv;
|
||||
struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = 0,
|
||||
.buf = tuner_reg, .len = sizeof(tuner_reg) };
|
||||
unsigned int freq = params->frequency;
|
||||
|
||||
tda827x_set_std(fe, params);
|
||||
|
||||
tda827xa_lna_gain(fe, 1, params);
|
||||
msleep(10);
|
||||
|
||||
if (params->mode == V4L2_TUNER_RADIO)
|
||||
freq = freq / 1000;
|
||||
|
||||
N = freq + priv->sgIF;
|
||||
|
||||
i = 0;
|
||||
while (tda827xa_analog[i].lomax < N * 62500) {
|
||||
if (tda827xa_analog[i + 1].lomax == 0)
|
||||
break;
|
||||
i++;
|
||||
}
|
||||
|
||||
N = N << tda827xa_analog[i].spd;
|
||||
|
||||
tuner_reg[0] = 0;
|
||||
tuner_reg[1] = (unsigned char)(N>>8);
|
||||
tuner_reg[2] = (unsigned char) N;
|
||||
tuner_reg[3] = 0;
|
||||
tuner_reg[4] = 0x16;
|
||||
tuner_reg[5] = (tda827xa_analog[i].spd << 5) +
|
||||
(tda827xa_analog[i].svco << 3) +
|
||||
tda827xa_analog[i].sbs;
|
||||
tuner_reg[6] = 0x8b + (tda827xa_analog[i].gc3 << 4);
|
||||
tuner_reg[7] = 0x1c;
|
||||
tuner_reg[8] = 4;
|
||||
tuner_reg[9] = 0x20;
|
||||
tuner_reg[10] = 0x00;
|
||||
msg.len = 11;
|
||||
i2c_transfer(priv->i2c_adap, &msg, 1);
|
||||
|
||||
tuner_reg[0] = 0x90;
|
||||
tuner_reg[1] = 0xff;
|
||||
tuner_reg[2] = 0xe0;
|
||||
tuner_reg[3] = 0;
|
||||
tuner_reg[4] = 0x99 + (priv->lpsel << 1);
|
||||
msg.len = 5;
|
||||
i2c_transfer(priv->i2c_adap, &msg, 1);
|
||||
|
||||
tuner_reg[0] = 0xa0;
|
||||
tuner_reg[1] = 0xc0;
|
||||
msg.len = 2;
|
||||
i2c_transfer(priv->i2c_adap, &msg, 1);
|
||||
|
||||
tuner_reg[0] = 0x30;
|
||||
tuner_reg[1] = 0x10 + tda827xa_analog[i].scr;
|
||||
i2c_transfer(priv->i2c_adap, &msg, 1);
|
||||
|
||||
msg.flags = I2C_M_RD;
|
||||
i2c_transfer(priv->i2c_adap, &msg, 1);
|
||||
msg.flags = 0;
|
||||
tuner_reg[1] >>= 4;
|
||||
dprintk("AGC2 gain is: %d\n", tuner_reg[1]);
|
||||
if (tuner_reg[1] < 1)
|
||||
tda827xa_lna_gain(fe, 0, params);
|
||||
|
||||
msleep(100);
|
||||
tuner_reg[0] = 0x60;
|
||||
tuner_reg[1] = 0x3c;
|
||||
i2c_transfer(priv->i2c_adap, &msg, 1);
|
||||
|
||||
msleep(163);
|
||||
tuner_reg[0] = 0x50;
|
||||
tuner_reg[1] = 0x8f + (tda827xa_analog[i].gc3 << 4);
|
||||
i2c_transfer(priv->i2c_adap, &msg, 1);
|
||||
|
||||
tuner_reg[0] = 0x80;
|
||||
tuner_reg[1] = 0x28;
|
||||
i2c_transfer(priv->i2c_adap, &msg, 1);
|
||||
|
||||
tuner_reg[0] = 0xb0;
|
||||
tuner_reg[1] = 0x01;
|
||||
i2c_transfer(priv->i2c_adap, &msg, 1);
|
||||
|
||||
tuner_reg[0] = 0xc0;
|
||||
tuner_reg[1] = 0x19 + (priv->lpsel << 1);
|
||||
i2c_transfer(priv->i2c_adap, &msg, 1);
|
||||
|
||||
priv->frequency = freq * 62500;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tda827xa_agcf(struct dvb_frontend *fe)
|
||||
{
|
||||
struct tda827x_priv *priv = fe->tuner_priv;
|
||||
unsigned char data[] = {0x80, 0x2c};
|
||||
struct i2c_msg msg = {.addr = priv->i2c_addr, .flags = 0,
|
||||
.buf = data, .len = 2};
|
||||
i2c_transfer(priv->i2c_adap, &msg, 1);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
static int tda827x_release(struct dvb_frontend *fe)
|
||||
{
|
||||
kfree(fe->tuner_priv);
|
||||
fe->tuner_priv = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tda827x_get_frequency(struct dvb_frontend *fe, u32 *frequency)
|
||||
{
|
||||
struct tda827x_priv *priv = fe->tuner_priv;
|
||||
*frequency = priv->frequency;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tda827x_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
|
||||
{
|
||||
struct tda827x_priv *priv = fe->tuner_priv;
|
||||
*bandwidth = priv->bandwidth;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tda827x_init(struct dvb_frontend *fe)
|
||||
{
|
||||
struct tda827x_priv *priv = fe->tuner_priv;
|
||||
dprintk("%s:\n", __func__);
|
||||
if (priv->cfg && priv->cfg->init)
|
||||
priv->cfg->init(fe);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tda827x_probe_version(struct dvb_frontend *fe);
|
||||
|
||||
static int tda827x_initial_init(struct dvb_frontend *fe)
|
||||
{
|
||||
int ret;
|
||||
ret = tda827x_probe_version(fe);
|
||||
if (ret)
|
||||
return ret;
|
||||
return fe->ops.tuner_ops.init(fe);
|
||||
}
|
||||
|
||||
static int tda827x_initial_sleep(struct dvb_frontend *fe)
|
||||
{
|
||||
int ret;
|
||||
ret = tda827x_probe_version(fe);
|
||||
if (ret)
|
||||
return ret;
|
||||
return fe->ops.tuner_ops.sleep(fe);
|
||||
}
|
||||
|
||||
static struct dvb_tuner_ops tda827xo_tuner_ops = {
|
||||
.info = {
|
||||
.name = "Philips TDA827X",
|
||||
.frequency_min = 55000000,
|
||||
.frequency_max = 860000000,
|
||||
.frequency_step = 250000
|
||||
},
|
||||
.release = tda827x_release,
|
||||
.init = tda827x_initial_init,
|
||||
.sleep = tda827x_initial_sleep,
|
||||
.set_params = tda827xo_set_params,
|
||||
.set_analog_params = tda827xo_set_analog_params,
|
||||
.get_frequency = tda827x_get_frequency,
|
||||
.get_bandwidth = tda827x_get_bandwidth,
|
||||
};
|
||||
|
||||
static struct dvb_tuner_ops tda827xa_tuner_ops = {
|
||||
.info = {
|
||||
.name = "Philips TDA827XA",
|
||||
.frequency_min = 44000000,
|
||||
.frequency_max = 906000000,
|
||||
.frequency_step = 62500
|
||||
},
|
||||
.release = tda827x_release,
|
||||
.init = tda827x_init,
|
||||
.sleep = tda827xa_sleep,
|
||||
.set_params = tda827xa_set_params,
|
||||
.set_analog_params = tda827xa_set_analog_params,
|
||||
.get_frequency = tda827x_get_frequency,
|
||||
.get_bandwidth = tda827x_get_bandwidth,
|
||||
};
|
||||
|
||||
static int tda827x_probe_version(struct dvb_frontend *fe)
|
||||
{ u8 data;
|
||||
struct tda827x_priv *priv = fe->tuner_priv;
|
||||
struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = I2C_M_RD,
|
||||
.buf = &data, .len = 1 };
|
||||
if (fe->ops.i2c_gate_ctrl)
|
||||
fe->ops.i2c_gate_ctrl(fe, 1);
|
||||
if (i2c_transfer(priv->i2c_adap, &msg, 1) != 1) {
|
||||
printk("%s: could not read from tuner at addr: 0x%02x\n",
|
||||
__func__, msg.addr << 1);
|
||||
return -EIO;
|
||||
}
|
||||
if ((data & 0x3c) == 0) {
|
||||
dprintk("tda827x tuner found\n");
|
||||
fe->ops.tuner_ops.init = tda827x_init;
|
||||
fe->ops.tuner_ops.sleep = tda827xo_sleep;
|
||||
if (priv->cfg)
|
||||
priv->cfg->agcf = tda827xo_agcf;
|
||||
} else {
|
||||
dprintk("tda827xa tuner found\n");
|
||||
memcpy(&fe->ops.tuner_ops, &tda827xa_tuner_ops, sizeof(struct dvb_tuner_ops));
|
||||
if (priv->cfg)
|
||||
priv->cfg->agcf = tda827xa_agcf;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct dvb_frontend *tda827x_attach(struct dvb_frontend *fe, int addr,
|
||||
struct i2c_adapter *i2c,
|
||||
struct tda827x_config *cfg)
|
||||
{
|
||||
struct tda827x_priv *priv = NULL;
|
||||
|
||||
dprintk("%s:\n", __func__);
|
||||
priv = kzalloc(sizeof(struct tda827x_priv), GFP_KERNEL);
|
||||
if (priv == NULL)
|
||||
return NULL;
|
||||
|
||||
priv->i2c_addr = addr;
|
||||
priv->i2c_adap = i2c;
|
||||
priv->cfg = cfg;
|
||||
memcpy(&fe->ops.tuner_ops, &tda827xo_tuner_ops, sizeof(struct dvb_tuner_ops));
|
||||
fe->tuner_priv = priv;
|
||||
|
||||
dprintk("type set to %s\n", fe->ops.tuner_ops.info.name);
|
||||
|
||||
return fe;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tda827x_attach);
|
||||
|
||||
MODULE_DESCRIPTION("DVB TDA827x driver");
|
||||
MODULE_AUTHOR("Hartmut Hackmann <hartmut.hackmann@t-online.de>");
|
||||
MODULE_AUTHOR("Michael Krufky <mkrufky@linuxtv.org>");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
/*
|
||||
* Overrides for Emacs so that we follow Linus's tabbing style.
|
||||
* ---------------------------------------------------------------------------
|
||||
* Local variables:
|
||||
* c-basic-offset: 8
|
||||
* End:
|
||||
*/
|
||||
69
drivers/media/common/tuners/tda827x.h
Normal file
69
drivers/media/common/tuners/tda827x.h
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
DVB Driver for Philips tda827x / tda827xa Silicon tuners
|
||||
|
||||
(c) 2005 Hartmut Hackmann
|
||||
(c) 2007 Michael Krufky
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef __DVB_TDA827X_H__
|
||||
#define __DVB_TDA827X_H__
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include "dvb_frontend.h"
|
||||
|
||||
struct tda827x_config
|
||||
{
|
||||
/* saa7134 - provided callbacks */
|
||||
int (*init) (struct dvb_frontend *fe);
|
||||
int (*sleep) (struct dvb_frontend *fe);
|
||||
|
||||
/* interface to tda829x driver */
|
||||
unsigned int config;
|
||||
int switch_addr;
|
||||
int (*tuner_callback) (void *dev, int command, int arg);
|
||||
|
||||
void (*agcf)(struct dvb_frontend *fe);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Attach a tda827x tuner to the supplied frontend structure.
|
||||
*
|
||||
* @param fe Frontend to attach to.
|
||||
* @param addr i2c address of the tuner.
|
||||
* @param i2c i2c adapter to use.
|
||||
* @param cfg optional callback function pointers.
|
||||
* @return FE pointer on success, NULL on failure.
|
||||
*/
|
||||
#if defined(CONFIG_DVB_TDA827X) || (defined(CONFIG_DVB_TDA827X_MODULE) && defined(MODULE))
|
||||
extern struct dvb_frontend* tda827x_attach(struct dvb_frontend *fe, int addr,
|
||||
struct i2c_adapter *i2c,
|
||||
struct tda827x_config *cfg);
|
||||
#else
|
||||
static inline struct dvb_frontend* tda827x_attach(struct dvb_frontend *fe,
|
||||
int addr,
|
||||
struct i2c_adapter *i2c,
|
||||
struct tda827x_config *cfg)
|
||||
{
|
||||
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
#endif // CONFIG_DVB_TDA827X
|
||||
|
||||
#endif // __DVB_TDA827X_H__
|
||||
804
drivers/media/common/tuners/tda8290.c
Normal file
804
drivers/media/common/tuners/tda8290.c
Normal file
|
|
@ -0,0 +1,804 @@
|
|||
/*
|
||||
|
||||
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.
|
||||
|
||||
This "tda8290" module was split apart from the original "tuner" module.
|
||||
*/
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/videodev.h>
|
||||
#include "tuner-i2c.h"
|
||||
#include "tda8290.h"
|
||||
#include "tda827x.h"
|
||||
#include "tda18271.h"
|
||||
|
||||
static int debug;
|
||||
module_param(debug, int, 0644);
|
||||
MODULE_PARM_DESC(debug, "enable verbose debug messages");
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
struct tda8290_priv {
|
||||
struct tuner_i2c_props i2c_props;
|
||||
|
||||
unsigned char tda8290_easy_mode;
|
||||
|
||||
unsigned char tda827x_addr;
|
||||
|
||||
unsigned char ver;
|
||||
#define TDA8290 1
|
||||
#define TDA8295 2
|
||||
#define TDA8275 4
|
||||
#define TDA8275A 8
|
||||
#define TDA18271 16
|
||||
|
||||
struct tda827x_config cfg;
|
||||
};
|
||||
|
||||
/*---------------------------------------------------------------------*/
|
||||
|
||||
static int tda8290_i2c_bridge(struct dvb_frontend *fe, int close)
|
||||
{
|
||||
struct tda8290_priv *priv = fe->analog_demod_priv;
|
||||
|
||||
unsigned char enable[2] = { 0x21, 0xC0 };
|
||||
unsigned char disable[2] = { 0x21, 0x00 };
|
||||
unsigned char *msg;
|
||||
|
||||
if (close) {
|
||||
msg = enable;
|
||||
tuner_i2c_xfer_send(&priv->i2c_props, msg, 2);
|
||||
/* let the bridge stabilize */
|
||||
msleep(20);
|
||||
} else {
|
||||
msg = disable;
|
||||
tuner_i2c_xfer_send(&priv->i2c_props, msg, 2);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tda8295_i2c_bridge(struct dvb_frontend *fe, int close)
|
||||
{
|
||||
struct tda8290_priv *priv = fe->analog_demod_priv;
|
||||
|
||||
unsigned char enable[2] = { 0x45, 0xc1 };
|
||||
unsigned char disable[2] = { 0x46, 0x00 };
|
||||
unsigned char buf[3] = { 0x45, 0x01, 0x00 };
|
||||
unsigned char *msg;
|
||||
|
||||
if (close) {
|
||||
msg = enable;
|
||||
tuner_i2c_xfer_send(&priv->i2c_props, msg, 2);
|
||||
/* let the bridge stabilize */
|
||||
msleep(20);
|
||||
} else {
|
||||
msg = disable;
|
||||
tuner_i2c_xfer_send(&priv->i2c_props, msg, 1);
|
||||
tuner_i2c_xfer_recv(&priv->i2c_props, &msg[1], 1);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------*/
|
||||
|
||||
static void set_audio(struct dvb_frontend *fe,
|
||||
struct analog_parameters *params)
|
||||
{
|
||||
struct tda8290_priv *priv = fe->analog_demod_priv;
|
||||
char* mode;
|
||||
|
||||
if (params->std & V4L2_STD_MN) {
|
||||
priv->tda8290_easy_mode = 0x01;
|
||||
mode = "MN";
|
||||
} else if (params->std & V4L2_STD_B) {
|
||||
priv->tda8290_easy_mode = 0x02;
|
||||
mode = "B";
|
||||
} else if (params->std & V4L2_STD_GH) {
|
||||
priv->tda8290_easy_mode = 0x04;
|
||||
mode = "GH";
|
||||
} else if (params->std & V4L2_STD_PAL_I) {
|
||||
priv->tda8290_easy_mode = 0x08;
|
||||
mode = "I";
|
||||
} else if (params->std & V4L2_STD_DK) {
|
||||
priv->tda8290_easy_mode = 0x10;
|
||||
mode = "DK";
|
||||
} else if (params->std & V4L2_STD_SECAM_L) {
|
||||
priv->tda8290_easy_mode = 0x20;
|
||||
mode = "L";
|
||||
} else if (params->std & V4L2_STD_SECAM_LC) {
|
||||
priv->tda8290_easy_mode = 0x40;
|
||||
mode = "LC";
|
||||
} else {
|
||||
priv->tda8290_easy_mode = 0x10;
|
||||
mode = "xx";
|
||||
}
|
||||
|
||||
tuner_dbg("setting tda829x to system %s\n", mode);
|
||||
}
|
||||
|
||||
static void tda8290_set_params(struct dvb_frontend *fe,
|
||||
struct analog_parameters *params)
|
||||
{
|
||||
struct tda8290_priv *priv = fe->analog_demod_priv;
|
||||
|
||||
unsigned char soft_reset[] = { 0x00, 0x00 };
|
||||
unsigned char easy_mode[] = { 0x01, priv->tda8290_easy_mode };
|
||||
unsigned char expert_mode[] = { 0x01, 0x80 };
|
||||
unsigned char agc_out_on[] = { 0x02, 0x00 };
|
||||
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,
|
||||
pll_stat;
|
||||
int i;
|
||||
|
||||
set_audio(fe, params);
|
||||
|
||||
if (priv->cfg.config)
|
||||
tuner_dbg("tda827xa config is 0x%02x\n", priv->cfg.config);
|
||||
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);
|
||||
msleep(1);
|
||||
|
||||
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);
|
||||
|
||||
tda8290_i2c_bridge(fe, 1);
|
||||
|
||||
if (fe->ops.tuner_ops.set_analog_params)
|
||||
fe->ops.tuner_ops.set_analog_params(fe, params);
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
tuner_i2c_xfer_send(&priv->i2c_props, &addr_pll_stat, 1);
|
||||
tuner_i2c_xfer_recv(&priv->i2c_props, &pll_stat, 1);
|
||||
if (pll_stat & 0x80) {
|
||||
tuner_i2c_xfer_send(&priv->i2c_props, &addr_adc_sat, 1);
|
||||
tuner_i2c_xfer_recv(&priv->i2c_props, &adc_sat, 1);
|
||||
tuner_i2c_xfer_send(&priv->i2c_props, &addr_agc_stat, 1);
|
||||
tuner_i2c_xfer_recv(&priv->i2c_props, &agc_stat, 1);
|
||||
tuner_dbg("tda8290 is locked, AGC: %d\n", agc_stat);
|
||||
break;
|
||||
} else {
|
||||
tuner_dbg("tda8290 not locked, no signal?\n");
|
||||
msleep(100);
|
||||
}
|
||||
}
|
||||
/* adjust headroom resp. gain */
|
||||
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);
|
||||
tuner_i2c_xfer_send(&priv->i2c_props, gainset_2, 2);
|
||||
msleep(100);
|
||||
tuner_i2c_xfer_send(&priv->i2c_props, &addr_agc_stat, 1);
|
||||
tuner_i2c_xfer_recv(&priv->i2c_props, &agc_stat, 1);
|
||||
tuner_i2c_xfer_send(&priv->i2c_props, &addr_pll_stat, 1);
|
||||
tuner_i2c_xfer_recv(&priv->i2c_props, &pll_stat, 1);
|
||||
if ((agc_stat > 115) || !(pll_stat & 0x80)) {
|
||||
tuner_dbg("adjust gain, step 2. Agc: %d, lock: %d\n",
|
||||
agc_stat, pll_stat & 0x80);
|
||||
if (priv->cfg.agcf)
|
||||
priv->cfg.agcf(fe);
|
||||
msleep(100);
|
||||
tuner_i2c_xfer_send(&priv->i2c_props, &addr_agc_stat, 1);
|
||||
tuner_i2c_xfer_recv(&priv->i2c_props, &agc_stat, 1);
|
||||
tuner_i2c_xfer_send(&priv->i2c_props, &addr_pll_stat, 1);
|
||||
tuner_i2c_xfer_recv(&priv->i2c_props, &pll_stat, 1);
|
||||
if((agc_stat > 115) || !(pll_stat & 0x80)) {
|
||||
tuner_dbg("adjust gain, step 3. Agc: %d\n", agc_stat);
|
||||
tuner_i2c_xfer_send(&priv->i2c_props, adc_head_12, 2);
|
||||
tuner_i2c_xfer_send(&priv->i2c_props, pll_bw_low, 2);
|
||||
msleep(100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* l/ l' deadlock? */
|
||||
if(priv->tda8290_easy_mode & 0x60) {
|
||||
tuner_i2c_xfer_send(&priv->i2c_props, &addr_adc_sat, 1);
|
||||
tuner_i2c_xfer_recv(&priv->i2c_props, &adc_sat, 1);
|
||||
tuner_i2c_xfer_send(&priv->i2c_props, &addr_pll_stat, 1);
|
||||
tuner_i2c_xfer_recv(&priv->i2c_props, &pll_stat, 1);
|
||||
if ((adc_sat > 20) || !(pll_stat & 0x80)) {
|
||||
tuner_dbg("trying to resolve SECAM L deadlock\n");
|
||||
tuner_i2c_xfer_send(&priv->i2c_props, agc_rst_on, 2);
|
||||
msleep(40);
|
||||
tuner_i2c_xfer_send(&priv->i2c_props, agc_rst_off, 2);
|
||||
}
|
||||
}
|
||||
|
||||
tda8290_i2c_bridge(fe, 0);
|
||||
tuner_i2c_xfer_send(&priv->i2c_props, if_agc_set, 2);
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------*/
|
||||
|
||||
static void tda8295_power(struct dvb_frontend *fe, int enable)
|
||||
{
|
||||
struct tda8290_priv *priv = fe->analog_demod_priv;
|
||||
unsigned char buf[] = { 0x30, 0x00 }; /* clb_stdbt */
|
||||
|
||||
tuner_i2c_xfer_send(&priv->i2c_props, &buf[0], 1);
|
||||
tuner_i2c_xfer_recv(&priv->i2c_props, &buf[1], 1);
|
||||
|
||||
if (enable)
|
||||
buf[1] = 0x01;
|
||||
else
|
||||
buf[1] = 0x03;
|
||||
|
||||
tuner_i2c_xfer_send(&priv->i2c_props, buf, 2);
|
||||
}
|
||||
|
||||
static void tda8295_set_easy_mode(struct dvb_frontend *fe, int enable)
|
||||
{
|
||||
struct tda8290_priv *priv = fe->analog_demod_priv;
|
||||
unsigned char buf[] = { 0x01, 0x00 };
|
||||
|
||||
tuner_i2c_xfer_send(&priv->i2c_props, &buf[0], 1);
|
||||
tuner_i2c_xfer_recv(&priv->i2c_props, &buf[1], 1);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static void tda8295_set_video_std(struct dvb_frontend *fe)
|
||||
{
|
||||
struct tda8290_priv *priv = fe->analog_demod_priv;
|
||||
unsigned char buf[] = { 0x00, priv->tda8290_easy_mode };
|
||||
|
||||
tuner_i2c_xfer_send(&priv->i2c_props, buf, 2);
|
||||
|
||||
tda8295_set_easy_mode(fe, 1);
|
||||
msleep(20);
|
||||
tda8295_set_easy_mode(fe, 0);
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------*/
|
||||
|
||||
static void tda8295_agc1_out(struct dvb_frontend *fe, int enable)
|
||||
{
|
||||
struct tda8290_priv *priv = fe->analog_demod_priv;
|
||||
unsigned char buf[] = { 0x02, 0x00 }; /* DIV_FUNC */
|
||||
|
||||
tuner_i2c_xfer_send(&priv->i2c_props, &buf[0], 1);
|
||||
tuner_i2c_xfer_recv(&priv->i2c_props, &buf[1], 1);
|
||||
|
||||
if (enable)
|
||||
buf[1] &= ~0x40;
|
||||
else
|
||||
buf[1] |= 0x40;
|
||||
|
||||
tuner_i2c_xfer_send(&priv->i2c_props, buf, 2);
|
||||
}
|
||||
|
||||
static void tda8295_agc2_out(struct dvb_frontend *fe, int enable)
|
||||
{
|
||||
struct tda8290_priv *priv = fe->analog_demod_priv;
|
||||
unsigned char set_gpio_cf[] = { 0x44, 0x00 };
|
||||
unsigned char set_gpio_val[] = { 0x46, 0x00 };
|
||||
|
||||
tuner_i2c_xfer_send(&priv->i2c_props, &set_gpio_cf[0], 1);
|
||||
tuner_i2c_xfer_recv(&priv->i2c_props, &set_gpio_cf[1], 1);
|
||||
tuner_i2c_xfer_send(&priv->i2c_props, &set_gpio_val[0], 1);
|
||||
tuner_i2c_xfer_recv(&priv->i2c_props, &set_gpio_val[1], 1);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static int tda8295_has_signal(struct dvb_frontend *fe)
|
||||
{
|
||||
struct tda8290_priv *priv = fe->analog_demod_priv;
|
||||
|
||||
unsigned char hvpll_stat = 0x26;
|
||||
unsigned char ret;
|
||||
|
||||
tuner_i2c_xfer_send(&priv->i2c_props, &hvpll_stat, 1);
|
||||
tuner_i2c_xfer_recv(&priv->i2c_props, &ret, 1);
|
||||
return (ret & 0x01) ? 65535 : 0;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------*/
|
||||
|
||||
static void tda8295_set_params(struct dvb_frontend *fe,
|
||||
struct analog_parameters *params)
|
||||
{
|
||||
struct tda8290_priv *priv = fe->analog_demod_priv;
|
||||
|
||||
unsigned char blanking_mode[] = { 0x1d, 0x00 };
|
||||
|
||||
set_audio(fe, params);
|
||||
|
||||
tuner_dbg("%s: freq = %d\n", __func__, params->frequency);
|
||||
|
||||
tda8295_power(fe, 1);
|
||||
tda8295_agc1_out(fe, 1);
|
||||
|
||||
tuner_i2c_xfer_send(&priv->i2c_props, &blanking_mode[0], 1);
|
||||
tuner_i2c_xfer_recv(&priv->i2c_props, &blanking_mode[1], 1);
|
||||
|
||||
tda8295_set_video_std(fe);
|
||||
|
||||
blanking_mode[1] = 0x03;
|
||||
tuner_i2c_xfer_send(&priv->i2c_props, blanking_mode, 2);
|
||||
msleep(20);
|
||||
|
||||
tda8295_i2c_bridge(fe, 1);
|
||||
|
||||
if (fe->ops.tuner_ops.set_analog_params)
|
||||
fe->ops.tuner_ops.set_analog_params(fe, params);
|
||||
|
||||
if (priv->cfg.agcf)
|
||||
priv->cfg.agcf(fe);
|
||||
|
||||
if (tda8295_has_signal(fe))
|
||||
tuner_dbg("tda8295 is locked\n");
|
||||
else
|
||||
tuner_dbg("tda8295 not locked, no signal?\n");
|
||||
|
||||
tda8295_i2c_bridge(fe, 0);
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------*/
|
||||
|
||||
static int tda8290_has_signal(struct dvb_frontend *fe)
|
||||
{
|
||||
struct tda8290_priv *priv = fe->analog_demod_priv;
|
||||
|
||||
unsigned char i2c_get_afc[1] = { 0x1B };
|
||||
unsigned char afc = 0;
|
||||
|
||||
tuner_i2c_xfer_send(&priv->i2c_props, i2c_get_afc, ARRAY_SIZE(i2c_get_afc));
|
||||
tuner_i2c_xfer_recv(&priv->i2c_props, &afc, 1);
|
||||
return (afc & 0x80)? 65535:0;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------*/
|
||||
|
||||
static void tda8290_standby(struct dvb_frontend *fe)
|
||||
{
|
||||
struct tda8290_priv *priv = fe->analog_demod_priv;
|
||||
|
||||
unsigned char cb1[] = { 0x30, 0xD0 };
|
||||
unsigned char tda8290_standby[] = { 0x00, 0x02 };
|
||||
unsigned char tda8290_agc_tri[] = { 0x02, 0x20 };
|
||||
struct i2c_msg msg = {.addr = priv->tda827x_addr, .flags=0, .buf=cb1, .len = 2};
|
||||
|
||||
tda8290_i2c_bridge(fe, 1);
|
||||
if (priv->ver & TDA8275A)
|
||||
cb1[1] = 0x90;
|
||||
i2c_transfer(priv->i2c_props.adap, &msg, 1);
|
||||
tda8290_i2c_bridge(fe, 0);
|
||||
tuner_i2c_xfer_send(&priv->i2c_props, tda8290_agc_tri, 2);
|
||||
tuner_i2c_xfer_send(&priv->i2c_props, tda8290_standby, 2);
|
||||
}
|
||||
|
||||
static void tda8295_standby(struct dvb_frontend *fe)
|
||||
{
|
||||
tda8295_agc1_out(fe, 0); /* Put AGC in tri-state */
|
||||
|
||||
tda8295_power(fe, 0);
|
||||
}
|
||||
|
||||
static void tda8290_init_if(struct dvb_frontend *fe)
|
||||
{
|
||||
struct tda8290_priv *priv = fe->analog_demod_priv;
|
||||
|
||||
unsigned char set_VS[] = { 0x30, 0x6F };
|
||||
unsigned char set_GP00_CF[] = { 0x20, 0x01 };
|
||||
unsigned char set_GP01_CF[] = { 0x20, 0x0B };
|
||||
|
||||
if ((priv->cfg.config == 1) || (priv->cfg.config == 2))
|
||||
tuner_i2c_xfer_send(&priv->i2c_props, set_GP00_CF, 2);
|
||||
else
|
||||
tuner_i2c_xfer_send(&priv->i2c_props, set_GP01_CF, 2);
|
||||
tuner_i2c_xfer_send(&priv->i2c_props, set_VS, 2);
|
||||
}
|
||||
|
||||
static void tda8295_init_if(struct dvb_frontend *fe)
|
||||
{
|
||||
struct tda8290_priv *priv = fe->analog_demod_priv;
|
||||
|
||||
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 };
|
||||
|
||||
tda8295_power(fe, 1);
|
||||
|
||||
tda8295_set_easy_mode(fe, 0);
|
||||
tda8295_set_video_std(fe);
|
||||
|
||||
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);
|
||||
|
||||
tda8295_agc1_out(fe, 0);
|
||||
tda8295_agc2_out(fe, 0);
|
||||
}
|
||||
|
||||
static void tda8290_init_tuner(struct dvb_frontend *fe)
|
||||
{
|
||||
struct tda8290_priv *priv = fe->analog_demod_priv;
|
||||
unsigned char tda8275_init[] = { 0x00, 0x00, 0x00, 0x40, 0xdC, 0x04, 0xAf,
|
||||
0x3F, 0x2A, 0x04, 0xFF, 0x00, 0x00, 0x40 };
|
||||
unsigned char tda8275a_init[] = { 0x00, 0x00, 0x00, 0x00, 0xdC, 0x05, 0x8b,
|
||||
0x0c, 0x04, 0x20, 0xFF, 0x00, 0x00, 0x4b };
|
||||
struct i2c_msg msg = {.addr = priv->tda827x_addr, .flags=0,
|
||||
.buf=tda8275_init, .len = 14};
|
||||
if (priv->ver & TDA8275A)
|
||||
msg.buf = tda8275a_init;
|
||||
|
||||
tda8290_i2c_bridge(fe, 1);
|
||||
i2c_transfer(priv->i2c_props.adap, &msg, 1);
|
||||
tda8290_i2c_bridge(fe, 0);
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------*/
|
||||
|
||||
static void tda829x_release(struct dvb_frontend *fe)
|
||||
{
|
||||
struct tda8290_priv *priv = fe->analog_demod_priv;
|
||||
|
||||
/* only try to release the tuner if we've
|
||||
* attached it from within this module */
|
||||
if (priv->ver & (TDA18271 | TDA8275 | TDA8275A))
|
||||
if (fe->ops.tuner_ops.release)
|
||||
fe->ops.tuner_ops.release(fe);
|
||||
|
||||
kfree(fe->analog_demod_priv);
|
||||
fe->analog_demod_priv = NULL;
|
||||
}
|
||||
|
||||
static struct tda18271_config tda829x_tda18271_config = {
|
||||
.gate = TDA18271_GATE_ANALOG,
|
||||
};
|
||||
|
||||
static int tda829x_find_tuner(struct dvb_frontend *fe)
|
||||
{
|
||||
struct tda8290_priv *priv = fe->analog_demod_priv;
|
||||
struct analog_demod_ops *analog_ops = &fe->ops.analog_ops;
|
||||
int i, ret, tuners_found;
|
||||
u32 tuner_addrs;
|
||||
u8 data;
|
||||
struct i2c_msg msg = { .flags = I2C_M_RD, .buf = &data, .len = 1 };
|
||||
|
||||
if (NULL == analog_ops->i2c_gate_ctrl)
|
||||
return -EINVAL;
|
||||
|
||||
analog_ops->i2c_gate_ctrl(fe, 1);
|
||||
|
||||
/* probe for tuner chip */
|
||||
tuners_found = 0;
|
||||
tuner_addrs = 0;
|
||||
for (i = 0x60; i <= 0x63; i++) {
|
||||
msg.addr = i;
|
||||
ret = i2c_transfer(priv->i2c_props.adap, &msg, 1);
|
||||
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
|
||||
*/
|
||||
|
||||
analog_ops->i2c_gate_ctrl(fe, 0);
|
||||
|
||||
if (tuners_found > 1)
|
||||
for (i = 0; i < tuners_found; i++) {
|
||||
msg.addr = tuner_addrs & 0xff;
|
||||
ret = i2c_transfer(priv->i2c_props.adap, &msg, 1);
|
||||
if (ret == 1)
|
||||
tuner_addrs = tuner_addrs >> 8;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if (tuner_addrs == 0) {
|
||||
tuner_addrs = 0x60;
|
||||
tuner_info("could not clearly identify tuner address, "
|
||||
"defaulting to %x\n", tuner_addrs);
|
||||
} else {
|
||||
tuner_addrs = tuner_addrs & 0xff;
|
||||
tuner_info("setting tuner address to %x\n", tuner_addrs);
|
||||
}
|
||||
priv->tda827x_addr = tuner_addrs;
|
||||
msg.addr = tuner_addrs;
|
||||
|
||||
analog_ops->i2c_gate_ctrl(fe, 1);
|
||||
ret = i2c_transfer(priv->i2c_props.adap, &msg, 1);
|
||||
|
||||
if (ret != 1) {
|
||||
tuner_warn("tuner access failed!\n");
|
||||
return -EREMOTEIO;
|
||||
}
|
||||
|
||||
if ((data == 0x83) || (data == 0x84)) {
|
||||
priv->ver |= TDA18271;
|
||||
tda18271_attach(fe, priv->tda827x_addr,
|
||||
priv->i2c_props.adap,
|
||||
&tda829x_tda18271_config);
|
||||
} else {
|
||||
if ((data & 0x3c) == 0)
|
||||
priv->ver |= TDA8275;
|
||||
else
|
||||
priv->ver |= TDA8275A;
|
||||
|
||||
tda827x_attach(fe, priv->tda827x_addr, priv->i2c_props.adap, &priv->cfg);
|
||||
priv->cfg.switch_addr = priv->i2c_props.addr;
|
||||
}
|
||||
if (fe->ops.tuner_ops.init)
|
||||
fe->ops.tuner_ops.init(fe);
|
||||
|
||||
if (fe->ops.tuner_ops.sleep)
|
||||
fe->ops.tuner_ops.sleep(fe);
|
||||
|
||||
analog_ops->i2c_gate_ctrl(fe, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tda8290_probe(struct tuner_i2c_props *i2c_props)
|
||||
{
|
||||
#define TDA8290_ID 0x89
|
||||
unsigned char tda8290_id[] = { 0x1f, 0x00 };
|
||||
|
||||
/* detect tda8290 */
|
||||
tuner_i2c_xfer_send(i2c_props, &tda8290_id[0], 1);
|
||||
tuner_i2c_xfer_recv(i2c_props, &tda8290_id[1], 1);
|
||||
|
||||
if (tda8290_id[1] == TDA8290_ID) {
|
||||
if (debug)
|
||||
printk(KERN_DEBUG "%s: tda8290 detected @ %d-%04x\n",
|
||||
__func__, i2c_adapter_id(i2c_props->adap),
|
||||
i2c_props->addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static int tda8295_probe(struct tuner_i2c_props *i2c_props)
|
||||
{
|
||||
#define TDA8295_ID 0x8a
|
||||
unsigned char tda8295_id[] = { 0x2f, 0x00 };
|
||||
|
||||
/* detect tda8295 */
|
||||
tuner_i2c_xfer_send(i2c_props, &tda8295_id[0], 1);
|
||||
tuner_i2c_xfer_recv(i2c_props, &tda8295_id[1], 1);
|
||||
|
||||
if (tda8295_id[1] == TDA8295_ID) {
|
||||
if (debug)
|
||||
printk(KERN_DEBUG "%s: tda8295 detected @ %d-%04x\n",
|
||||
__func__, i2c_adapter_id(i2c_props->adap),
|
||||
i2c_props->addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static struct analog_demod_ops tda8290_ops = {
|
||||
.set_params = tda8290_set_params,
|
||||
.has_signal = tda8290_has_signal,
|
||||
.standby = tda8290_standby,
|
||||
.release = tda829x_release,
|
||||
.i2c_gate_ctrl = tda8290_i2c_bridge,
|
||||
};
|
||||
|
||||
static struct analog_demod_ops tda8295_ops = {
|
||||
.set_params = tda8295_set_params,
|
||||
.has_signal = tda8295_has_signal,
|
||||
.standby = tda8295_standby,
|
||||
.release = tda829x_release,
|
||||
.i2c_gate_ctrl = tda8295_i2c_bridge,
|
||||
};
|
||||
|
||||
struct dvb_frontend *tda829x_attach(struct dvb_frontend *fe,
|
||||
struct i2c_adapter *i2c_adap, u8 i2c_addr,
|
||||
struct tda829x_config *cfg)
|
||||
{
|
||||
struct tda8290_priv *priv = NULL;
|
||||
char *name;
|
||||
|
||||
priv = kzalloc(sizeof(struct tda8290_priv), GFP_KERNEL);
|
||||
if (priv == NULL)
|
||||
return NULL;
|
||||
fe->analog_demod_priv = priv;
|
||||
|
||||
priv->i2c_props.addr = i2c_addr;
|
||||
priv->i2c_props.adap = i2c_adap;
|
||||
priv->i2c_props.name = "tda829x";
|
||||
if (cfg) {
|
||||
priv->cfg.config = cfg->lna_cfg;
|
||||
priv->cfg.tuner_callback = cfg->tuner_callback;
|
||||
}
|
||||
|
||||
if (tda8290_probe(&priv->i2c_props) == 0) {
|
||||
priv->ver = TDA8290;
|
||||
memcpy(&fe->ops.analog_ops, &tda8290_ops,
|
||||
sizeof(struct analog_demod_ops));
|
||||
}
|
||||
|
||||
if (tda8295_probe(&priv->i2c_props) == 0) {
|
||||
priv->ver = TDA8295;
|
||||
memcpy(&fe->ops.analog_ops, &tda8295_ops,
|
||||
sizeof(struct analog_demod_ops));
|
||||
}
|
||||
|
||||
if ((!(cfg) || (TDA829X_PROBE_TUNER == cfg->probe_tuner)) &&
|
||||
(tda829x_find_tuner(fe) < 0))
|
||||
goto fail;
|
||||
|
||||
switch (priv->ver) {
|
||||
case TDA8290:
|
||||
name = "tda8290";
|
||||
break;
|
||||
case TDA8295:
|
||||
name = "tda8295";
|
||||
break;
|
||||
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);
|
||||
|
||||
fe->ops.analog_ops.info.name = name;
|
||||
|
||||
if (priv->ver & TDA8290) {
|
||||
tda8290_init_tuner(fe);
|
||||
tda8290_init_if(fe);
|
||||
} else if (priv->ver & TDA8295)
|
||||
tda8295_init_if(fe);
|
||||
|
||||
return fe;
|
||||
|
||||
fail:
|
||||
tda829x_release(fe);
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tda829x_attach);
|
||||
|
||||
int tda829x_probe(struct i2c_adapter *i2c_adap, u8 i2c_addr)
|
||||
{
|
||||
struct tuner_i2c_props i2c_props = {
|
||||
.adap = i2c_adap,
|
||||
.addr = i2c_addr,
|
||||
};
|
||||
|
||||
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 };
|
||||
unsigned char addr_dto_lsb = 0x07;
|
||||
unsigned char data;
|
||||
#define PROBE_BUFFER_SIZE 8
|
||||
unsigned char buf[PROBE_BUFFER_SIZE];
|
||||
int i;
|
||||
|
||||
/* rule out tda9887, which would return the same byte repeatedly */
|
||||
tuner_i2c_xfer_send(&i2c_props, soft_reset, 1);
|
||||
tuner_i2c_xfer_recv(&i2c_props, buf, PROBE_BUFFER_SIZE);
|
||||
for (i = 1; i < PROBE_BUFFER_SIZE; i++) {
|
||||
if (buf[i] != buf[0])
|
||||
break;
|
||||
}
|
||||
|
||||
/* all bytes are equal, not a tda829x - probably a tda9887 */
|
||||
if (i == PROBE_BUFFER_SIZE)
|
||||
return -ENODEV;
|
||||
|
||||
if ((tda8290_probe(&i2c_props) == 0) ||
|
||||
(tda8295_probe(&i2c_props) == 0))
|
||||
return 0;
|
||||
|
||||
/* fall back to old probing method */
|
||||
tuner_i2c_xfer_send(&i2c_props, easy_mode_b, 2);
|
||||
tuner_i2c_xfer_send(&i2c_props, soft_reset, 2);
|
||||
tuner_i2c_xfer_send(&i2c_props, &addr_dto_lsb, 1);
|
||||
tuner_i2c_xfer_recv(&i2c_props, &data, 1);
|
||||
if (data == 0) {
|
||||
tuner_i2c_xfer_send(&i2c_props, easy_mode_g, 2);
|
||||
tuner_i2c_xfer_send(&i2c_props, soft_reset, 2);
|
||||
tuner_i2c_xfer_send(&i2c_props, &addr_dto_lsb, 1);
|
||||
tuner_i2c_xfer_recv(&i2c_props, &data, 1);
|
||||
if (data == 0x7b) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
tuner_i2c_xfer_send(&i2c_props, restore_9886, 3);
|
||||
return -ENODEV;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tda829x_probe);
|
||||
|
||||
MODULE_DESCRIPTION("Philips/NXP TDA8290/TDA8295 analog IF demodulator driver");
|
||||
MODULE_AUTHOR("Gerd Knorr, Hartmut Hackmann, Michael Krufky");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
/*
|
||||
* Overrides for Emacs so that we follow Linus's tabbing style.
|
||||
* ---------------------------------------------------------------------------
|
||||
* Local variables:
|
||||
* c-basic-offset: 8
|
||||
* End:
|
||||
*/
|
||||
57
drivers/media/common/tuners/tda8290.h
Normal file
57
drivers/media/common/tuners/tda8290.h
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
#ifndef __TDA8290_H__
|
||||
#define __TDA8290_H__
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include "dvb_frontend.h"
|
||||
|
||||
struct tda829x_config {
|
||||
unsigned int lna_cfg;
|
||||
int (*tuner_callback) (void *dev, int command, int arg);
|
||||
|
||||
unsigned int probe_tuner:1;
|
||||
#define TDA829X_PROBE_TUNER 0
|
||||
#define TDA829X_DONT_PROBE 1
|
||||
};
|
||||
|
||||
#if defined(CONFIG_TUNER_TDA8290) || (defined(CONFIG_TUNER_TDA8290_MODULE) && defined(MODULE))
|
||||
extern int tda829x_probe(struct i2c_adapter *i2c_adap, u8 i2c_addr);
|
||||
|
||||
extern struct dvb_frontend *tda829x_attach(struct dvb_frontend *fe,
|
||||
struct i2c_adapter *i2c_adap,
|
||||
u8 i2c_addr,
|
||||
struct tda829x_config *cfg);
|
||||
#else
|
||||
static inline int tda829x_probe(struct i2c_adapter *i2c_adap, u8 i2c_addr)
|
||||
{
|
||||
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static inline struct dvb_frontend *tda829x_attach(struct dvb_frontend *fe,
|
||||
struct i2c_adapter *i2c_adap,
|
||||
u8 i2c_addr,
|
||||
struct tda829x_config *cfg)
|
||||
{
|
||||
printk(KERN_INFO "%s: not probed - driver disabled by Kconfig\n",
|
||||
__func__);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __TDA8290_H__ */
|
||||
717
drivers/media/common/tuners/tda9887.c
Normal file
717
drivers/media/common/tuners/tda9887.c
Normal file
|
|
@ -0,0 +1,717 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/videodev.h>
|
||||
#include <media/v4l2-common.h>
|
||||
#include <media/tuner.h>
|
||||
#include "tuner-i2c.h"
|
||||
#include "tda9887.h"
|
||||
|
||||
|
||||
/* Chips:
|
||||
TDA9885 (PAL, NTSC)
|
||||
TDA9886 (PAL, SECAM, NTSC)
|
||||
TDA9887 (PAL, SECAM, NTSC, FM Radio)
|
||||
|
||||
Used as part of several tuners
|
||||
*/
|
||||
|
||||
static int debug;
|
||||
module_param(debug, int, 0644);
|
||||
MODULE_PARM_DESC(debug, "enable verbose debug messages");
|
||||
|
||||
static DEFINE_MUTEX(tda9887_list_mutex);
|
||||
static LIST_HEAD(hybrid_tuner_instance_list);
|
||||
|
||||
struct tda9887_priv {
|
||||
struct tuner_i2c_props i2c_props;
|
||||
struct list_head hybrid_tuner_instance_list;
|
||||
|
||||
unsigned char data[4];
|
||||
unsigned int config;
|
||||
unsigned int mode;
|
||||
unsigned int audmode;
|
||||
v4l2_std_id std;
|
||||
};
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
#define UNSET (-1U)
|
||||
|
||||
struct tvnorm {
|
||||
v4l2_std_id std;
|
||||
char *name;
|
||||
unsigned char b;
|
||||
unsigned char c;
|
||||
unsigned char e;
|
||||
};
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
//
|
||||
// TDA defines
|
||||
//
|
||||
|
||||
//// first reg (b)
|
||||
#define cVideoTrapBypassOFF 0x00 // bit b0
|
||||
#define cVideoTrapBypassON 0x01 // bit b0
|
||||
|
||||
#define cAutoMuteFmInactive 0x00 // bit b1
|
||||
#define cAutoMuteFmActive 0x02 // bit b1
|
||||
|
||||
#define cIntercarrier 0x00 // bit b2
|
||||
#define cQSS 0x04 // bit b2
|
||||
|
||||
#define cPositiveAmTV 0x00 // bit b3:4
|
||||
#define cFmRadio 0x08 // bit b3:4
|
||||
#define cNegativeFmTV 0x10 // bit b3:4
|
||||
|
||||
|
||||
#define cForcedMuteAudioON 0x20 // bit b5
|
||||
#define cForcedMuteAudioOFF 0x00 // bit b5
|
||||
|
||||
#define cOutputPort1Active 0x00 // bit b6
|
||||
#define cOutputPort1Inactive 0x40 // bit b6
|
||||
|
||||
#define cOutputPort2Active 0x00 // bit b7
|
||||
#define cOutputPort2Inactive 0x80 // bit b7
|
||||
|
||||
|
||||
//// second reg (c)
|
||||
#define cDeemphasisOFF 0x00 // bit c5
|
||||
#define cDeemphasisON 0x20 // bit c5
|
||||
|
||||
#define cDeemphasis75 0x00 // bit c6
|
||||
#define cDeemphasis50 0x40 // bit c6
|
||||
|
||||
#define cAudioGain0 0x00 // bit c7
|
||||
#define cAudioGain6 0x80 // bit c7
|
||||
|
||||
#define cTopMask 0x1f // bit c0:4
|
||||
#define cTopDefault 0x10 // bit c0:4
|
||||
|
||||
//// third reg (e)
|
||||
#define cAudioIF_4_5 0x00 // bit e0:1
|
||||
#define cAudioIF_5_5 0x01 // bit e0:1
|
||||
#define cAudioIF_6_0 0x02 // bit e0:1
|
||||
#define cAudioIF_6_5 0x03 // bit e0:1
|
||||
|
||||
|
||||
#define cVideoIFMask 0x1c // bit e2:4
|
||||
/* Video IF selection in TV Mode (bit B3=0) */
|
||||
#define cVideoIF_58_75 0x00 // bit e2:4
|
||||
#define cVideoIF_45_75 0x04 // bit e2:4
|
||||
#define cVideoIF_38_90 0x08 // bit e2:4
|
||||
#define cVideoIF_38_00 0x0C // bit e2:4
|
||||
#define cVideoIF_33_90 0x10 // bit e2:4
|
||||
#define cVideoIF_33_40 0x14 // bit e2:4
|
||||
#define cRadioIF_45_75 0x18 // bit e2:4
|
||||
#define cRadioIF_38_90 0x1C // bit e2:4
|
||||
|
||||
/* IF1 selection in Radio Mode (bit B3=1) */
|
||||
#define cRadioIF_33_30 0x00 // bit e2,4 (also 0x10,0x14)
|
||||
#define cRadioIF_41_30 0x04 // bit e2,4
|
||||
|
||||
/* Output of AFC pin in radio mode when bit E7=1 */
|
||||
#define cRadioAGC_SIF 0x00 // bit e3
|
||||
#define cRadioAGC_FM 0x08 // bit e3
|
||||
|
||||
#define cTunerGainNormal 0x00 // bit e5
|
||||
#define cTunerGainLow 0x20 // bit e5
|
||||
|
||||
#define cGating_18 0x00 // bit e6
|
||||
#define cGating_36 0x40 // bit e6
|
||||
|
||||
#define cAgcOutON 0x80 // bit e7
|
||||
#define cAgcOutOFF 0x00 // bit e7
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
static struct tvnorm tvnorms[] = {
|
||||
{
|
||||
.std = V4L2_STD_PAL_BG | V4L2_STD_PAL_H | V4L2_STD_PAL_N,
|
||||
.name = "PAL-BGHN",
|
||||
.b = ( cNegativeFmTV |
|
||||
cQSS ),
|
||||
.c = ( cDeemphasisON |
|
||||
cDeemphasis50 |
|
||||
cTopDefault),
|
||||
.e = ( cGating_36 |
|
||||
cAudioIF_5_5 |
|
||||
cVideoIF_38_90 ),
|
||||
},{
|
||||
.std = V4L2_STD_PAL_I,
|
||||
.name = "PAL-I",
|
||||
.b = ( cNegativeFmTV |
|
||||
cQSS ),
|
||||
.c = ( cDeemphasisON |
|
||||
cDeemphasis50 |
|
||||
cTopDefault),
|
||||
.e = ( cGating_36 |
|
||||
cAudioIF_6_0 |
|
||||
cVideoIF_38_90 ),
|
||||
},{
|
||||
.std = V4L2_STD_PAL_DK,
|
||||
.name = "PAL-DK",
|
||||
.b = ( cNegativeFmTV |
|
||||
cQSS ),
|
||||
.c = ( cDeemphasisON |
|
||||
cDeemphasis50 |
|
||||
cTopDefault),
|
||||
.e = ( cGating_36 |
|
||||
cAudioIF_6_5 |
|
||||
cVideoIF_38_90 ),
|
||||
},{
|
||||
.std = V4L2_STD_PAL_M | V4L2_STD_PAL_Nc,
|
||||
.name = "PAL-M/Nc",
|
||||
.b = ( cNegativeFmTV |
|
||||
cQSS ),
|
||||
.c = ( cDeemphasisON |
|
||||
cDeemphasis75 |
|
||||
cTopDefault),
|
||||
.e = ( cGating_36 |
|
||||
cAudioIF_4_5 |
|
||||
cVideoIF_45_75 ),
|
||||
},{
|
||||
.std = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H,
|
||||
.name = "SECAM-BGH",
|
||||
.b = ( cPositiveAmTV |
|
||||
cQSS ),
|
||||
.c = ( cTopDefault),
|
||||
.e = ( cGating_36 |
|
||||
cAudioIF_5_5 |
|
||||
cVideoIF_38_90 ),
|
||||
},{
|
||||
.std = V4L2_STD_SECAM_L,
|
||||
.name = "SECAM-L",
|
||||
.b = ( cPositiveAmTV |
|
||||
cQSS ),
|
||||
.c = ( cTopDefault),
|
||||
.e = ( cGating_36 |
|
||||
cAudioIF_6_5 |
|
||||
cVideoIF_38_90 ),
|
||||
},{
|
||||
.std = V4L2_STD_SECAM_LC,
|
||||
.name = "SECAM-L'",
|
||||
.b = ( cOutputPort2Inactive |
|
||||
cPositiveAmTV |
|
||||
cQSS ),
|
||||
.c = ( cTopDefault),
|
||||
.e = ( cGating_36 |
|
||||
cAudioIF_6_5 |
|
||||
cVideoIF_33_90 ),
|
||||
},{
|
||||
.std = V4L2_STD_SECAM_DK,
|
||||
.name = "SECAM-DK",
|
||||
.b = ( cNegativeFmTV |
|
||||
cQSS ),
|
||||
.c = ( cDeemphasisON |
|
||||
cDeemphasis50 |
|
||||
cTopDefault),
|
||||
.e = ( cGating_36 |
|
||||
cAudioIF_6_5 |
|
||||
cVideoIF_38_90 ),
|
||||
},{
|
||||
.std = V4L2_STD_NTSC_M | V4L2_STD_NTSC_M_KR,
|
||||
.name = "NTSC-M",
|
||||
.b = ( cNegativeFmTV |
|
||||
cQSS ),
|
||||
.c = ( cDeemphasisON |
|
||||
cDeemphasis75 |
|
||||
cTopDefault),
|
||||
.e = ( cGating_36 |
|
||||
cAudioIF_4_5 |
|
||||
cVideoIF_45_75 ),
|
||||
},{
|
||||
.std = V4L2_STD_NTSC_M_JP,
|
||||
.name = "NTSC-M-JP",
|
||||
.b = ( cNegativeFmTV |
|
||||
cQSS ),
|
||||
.c = ( cDeemphasisON |
|
||||
cDeemphasis50 |
|
||||
cTopDefault),
|
||||
.e = ( cGating_36 |
|
||||
cAudioIF_4_5 |
|
||||
cVideoIF_58_75 ),
|
||||
}
|
||||
};
|
||||
|
||||
static struct tvnorm radio_stereo = {
|
||||
.name = "Radio Stereo",
|
||||
.b = ( cFmRadio |
|
||||
cQSS ),
|
||||
.c = ( cDeemphasisOFF |
|
||||
cAudioGain6 |
|
||||
cTopDefault),
|
||||
.e = ( cTunerGainLow |
|
||||
cAudioIF_5_5 |
|
||||
cRadioIF_38_90 ),
|
||||
};
|
||||
|
||||
static struct tvnorm radio_mono = {
|
||||
.name = "Radio Mono",
|
||||
.b = ( cFmRadio |
|
||||
cQSS ),
|
||||
.c = ( cDeemphasisON |
|
||||
cDeemphasis75 |
|
||||
cTopDefault),
|
||||
.e = ( cTunerGainLow |
|
||||
cAudioIF_5_5 |
|
||||
cRadioIF_38_90 ),
|
||||
};
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
static void dump_read_message(struct dvb_frontend *fe, unsigned char *buf)
|
||||
{
|
||||
struct tda9887_priv *priv = fe->analog_demod_priv;
|
||||
|
||||
static char *afc[16] = {
|
||||
"- 12.5 kHz",
|
||||
"- 37.5 kHz",
|
||||
"- 62.5 kHz",
|
||||
"- 87.5 kHz",
|
||||
"-112.5 kHz",
|
||||
"-137.5 kHz",
|
||||
"-162.5 kHz",
|
||||
"-187.5 kHz [min]",
|
||||
"+187.5 kHz [max]",
|
||||
"+162.5 kHz",
|
||||
"+137.5 kHz",
|
||||
"+112.5 kHz",
|
||||
"+ 87.5 kHz",
|
||||
"+ 62.5 kHz",
|
||||
"+ 37.5 kHz",
|
||||
"+ 12.5 kHz",
|
||||
};
|
||||
tuner_info("read: 0x%2x\n", buf[0]);
|
||||
tuner_info(" after power on : %s\n", (buf[0] & 0x01) ? "yes" : "no");
|
||||
tuner_info(" afc : %s\n", afc[(buf[0] >> 1) & 0x0f]);
|
||||
tuner_info(" fmif level : %s\n", (buf[0] & 0x20) ? "high" : "low");
|
||||
tuner_info(" afc window : %s\n", (buf[0] & 0x40) ? "in" : "out");
|
||||
tuner_info(" vfi level : %s\n", (buf[0] & 0x80) ? "high" : "low");
|
||||
}
|
||||
|
||||
static void dump_write_message(struct dvb_frontend *fe, unsigned char *buf)
|
||||
{
|
||||
struct tda9887_priv *priv = fe->analog_demod_priv;
|
||||
|
||||
static char *sound[4] = {
|
||||
"AM/TV",
|
||||
"FM/radio",
|
||||
"FM/TV",
|
||||
"FM/radio"
|
||||
};
|
||||
static char *adjust[32] = {
|
||||
"-16", "-15", "-14", "-13", "-12", "-11", "-10", "-9",
|
||||
"-8", "-7", "-6", "-5", "-4", "-3", "-2", "-1",
|
||||
"0", "+1", "+2", "+3", "+4", "+5", "+6", "+7",
|
||||
"+8", "+9", "+10", "+11", "+12", "+13", "+14", "+15"
|
||||
};
|
||||
static char *deemph[4] = {
|
||||
"no", "no", "75", "50"
|
||||
};
|
||||
static char *carrier[4] = {
|
||||
"4.5 MHz",
|
||||
"5.5 MHz",
|
||||
"6.0 MHz",
|
||||
"6.5 MHz / AM"
|
||||
};
|
||||
static char *vif[8] = {
|
||||
"58.75 MHz",
|
||||
"45.75 MHz",
|
||||
"38.9 MHz",
|
||||
"38.0 MHz",
|
||||
"33.9 MHz",
|
||||
"33.4 MHz",
|
||||
"45.75 MHz + pin13",
|
||||
"38.9 MHz + pin13",
|
||||
};
|
||||
static char *rif[4] = {
|
||||
"44 MHz",
|
||||
"52 MHz",
|
||||
"52 MHz",
|
||||
"44 MHz",
|
||||
};
|
||||
|
||||
tuner_info("write: byte B 0x%02x\n", buf[1]);
|
||||
tuner_info(" B0 video mode : %s\n",
|
||||
(buf[1] & 0x01) ? "video trap" : "sound trap");
|
||||
tuner_info(" B1 auto mute fm : %s\n",
|
||||
(buf[1] & 0x02) ? "yes" : "no");
|
||||
tuner_info(" B2 carrier mode : %s\n",
|
||||
(buf[1] & 0x04) ? "QSS" : "Intercarrier");
|
||||
tuner_info(" B3-4 tv sound/radio : %s\n",
|
||||
sound[(buf[1] & 0x18) >> 3]);
|
||||
tuner_info(" B5 force mute audio: %s\n",
|
||||
(buf[1] & 0x20) ? "yes" : "no");
|
||||
tuner_info(" B6 output port 1 : %s\n",
|
||||
(buf[1] & 0x40) ? "high (inactive)" : "low (active)");
|
||||
tuner_info(" B7 output port 2 : %s\n",
|
||||
(buf[1] & 0x80) ? "high (inactive)" : "low (active)");
|
||||
|
||||
tuner_info("write: byte C 0x%02x\n", buf[2]);
|
||||
tuner_info(" C0-4 top adjustment : %s dB\n",
|
||||
adjust[buf[2] & 0x1f]);
|
||||
tuner_info(" C5-6 de-emphasis : %s\n",
|
||||
deemph[(buf[2] & 0x60) >> 5]);
|
||||
tuner_info(" C7 audio gain : %s\n",
|
||||
(buf[2] & 0x80) ? "-6" : "0");
|
||||
|
||||
tuner_info("write: byte E 0x%02x\n", buf[3]);
|
||||
tuner_info(" E0-1 sound carrier : %s\n",
|
||||
carrier[(buf[3] & 0x03)]);
|
||||
tuner_info(" E6 l pll gating : %s\n",
|
||||
(buf[3] & 0x40) ? "36" : "13");
|
||||
|
||||
if (buf[1] & 0x08) {
|
||||
/* radio */
|
||||
tuner_info(" E2-4 video if : %s\n",
|
||||
rif[(buf[3] & 0x0c) >> 2]);
|
||||
tuner_info(" E7 vif agc output : %s\n",
|
||||
(buf[3] & 0x80)
|
||||
? ((buf[3] & 0x10) ? "fm-agc radio" :
|
||||
"sif-agc radio")
|
||||
: "fm radio carrier afc");
|
||||
} else {
|
||||
/* video */
|
||||
tuner_info(" E2-4 video if : %s\n",
|
||||
vif[(buf[3] & 0x1c) >> 2]);
|
||||
tuner_info(" E5 tuner gain : %s\n",
|
||||
(buf[3] & 0x80)
|
||||
? ((buf[3] & 0x20) ? "external" : "normal")
|
||||
: ((buf[3] & 0x20) ? "minimum" : "normal"));
|
||||
tuner_info(" E7 vif agc output : %s\n",
|
||||
(buf[3] & 0x80) ? ((buf[3] & 0x20)
|
||||
? "pin3 port, pin22 vif agc out"
|
||||
: "pin22 port, pin3 vif acg ext in")
|
||||
: "pin3+pin22 port");
|
||||
}
|
||||
tuner_info("--\n");
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
static int tda9887_set_tvnorm(struct dvb_frontend *fe)
|
||||
{
|
||||
struct tda9887_priv *priv = fe->analog_demod_priv;
|
||||
struct tvnorm *norm = NULL;
|
||||
char *buf = priv->data;
|
||||
int i;
|
||||
|
||||
if (priv->mode == V4L2_TUNER_RADIO) {
|
||||
if (priv->audmode == V4L2_TUNER_MODE_MONO)
|
||||
norm = &radio_mono;
|
||||
else
|
||||
norm = &radio_stereo;
|
||||
} else {
|
||||
for (i = 0; i < ARRAY_SIZE(tvnorms); i++) {
|
||||
if (tvnorms[i].std & priv->std) {
|
||||
norm = tvnorms+i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (NULL == norm) {
|
||||
tuner_dbg("Unsupported tvnorm entry - audio muted\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
tuner_dbg("configure for: %s\n", norm->name);
|
||||
buf[1] = norm->b;
|
||||
buf[2] = norm->c;
|
||||
buf[3] = norm->e;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int port1 = UNSET;
|
||||
static unsigned int port2 = UNSET;
|
||||
static unsigned int qss = UNSET;
|
||||
static unsigned int adjust = UNSET;
|
||||
|
||||
module_param(port1, int, 0644);
|
||||
module_param(port2, int, 0644);
|
||||
module_param(qss, int, 0644);
|
||||
module_param(adjust, int, 0644);
|
||||
|
||||
static int tda9887_set_insmod(struct dvb_frontend *fe)
|
||||
{
|
||||
struct tda9887_priv *priv = fe->analog_demod_priv;
|
||||
char *buf = priv->data;
|
||||
|
||||
if (UNSET != port1) {
|
||||
if (port1)
|
||||
buf[1] |= cOutputPort1Inactive;
|
||||
else
|
||||
buf[1] &= ~cOutputPort1Inactive;
|
||||
}
|
||||
if (UNSET != port2) {
|
||||
if (port2)
|
||||
buf[1] |= cOutputPort2Inactive;
|
||||
else
|
||||
buf[1] &= ~cOutputPort2Inactive;
|
||||
}
|
||||
|
||||
if (UNSET != qss) {
|
||||
if (qss)
|
||||
buf[1] |= cQSS;
|
||||
else
|
||||
buf[1] &= ~cQSS;
|
||||
}
|
||||
|
||||
if (adjust >= 0x00 && adjust < 0x20) {
|
||||
buf[2] &= ~cTopMask;
|
||||
buf[2] |= adjust;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tda9887_do_config(struct dvb_frontend *fe)
|
||||
{
|
||||
struct tda9887_priv *priv = fe->analog_demod_priv;
|
||||
char *buf = priv->data;
|
||||
|
||||
if (priv->config & TDA9887_PORT1_ACTIVE)
|
||||
buf[1] &= ~cOutputPort1Inactive;
|
||||
if (priv->config & TDA9887_PORT1_INACTIVE)
|
||||
buf[1] |= cOutputPort1Inactive;
|
||||
if (priv->config & TDA9887_PORT2_ACTIVE)
|
||||
buf[1] &= ~cOutputPort2Inactive;
|
||||
if (priv->config & TDA9887_PORT2_INACTIVE)
|
||||
buf[1] |= cOutputPort2Inactive;
|
||||
|
||||
if (priv->config & TDA9887_QSS)
|
||||
buf[1] |= cQSS;
|
||||
if (priv->config & TDA9887_INTERCARRIER)
|
||||
buf[1] &= ~cQSS;
|
||||
|
||||
if (priv->config & TDA9887_AUTOMUTE)
|
||||
buf[1] |= cAutoMuteFmActive;
|
||||
if (priv->config & TDA9887_DEEMPHASIS_MASK) {
|
||||
buf[2] &= ~0x60;
|
||||
switch (priv->config & TDA9887_DEEMPHASIS_MASK) {
|
||||
case TDA9887_DEEMPHASIS_NONE:
|
||||
buf[2] |= cDeemphasisOFF;
|
||||
break;
|
||||
case TDA9887_DEEMPHASIS_50:
|
||||
buf[2] |= cDeemphasisON | cDeemphasis50;
|
||||
break;
|
||||
case TDA9887_DEEMPHASIS_75:
|
||||
buf[2] |= cDeemphasisON | cDeemphasis75;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (priv->config & TDA9887_TOP_SET) {
|
||||
buf[2] &= ~cTopMask;
|
||||
buf[2] |= (priv->config >> 8) & cTopMask;
|
||||
}
|
||||
if ((priv->config & TDA9887_INTERCARRIER_NTSC) &&
|
||||
(priv->std & V4L2_STD_NTSC))
|
||||
buf[1] &= ~cQSS;
|
||||
if (priv->config & TDA9887_GATING_18)
|
||||
buf[3] &= ~cGating_36;
|
||||
|
||||
if (priv->mode == V4L2_TUNER_RADIO) {
|
||||
if (priv->config & TDA9887_RIF_41_3) {
|
||||
buf[3] &= ~cVideoIFMask;
|
||||
buf[3] |= cRadioIF_41_30;
|
||||
}
|
||||
if (priv->config & TDA9887_GAIN_NORMAL)
|
||||
buf[3] &= ~cTunerGainLow;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
static int tda9887_status(struct dvb_frontend *fe)
|
||||
{
|
||||
struct tda9887_priv *priv = fe->analog_demod_priv;
|
||||
unsigned char buf[1];
|
||||
int rc;
|
||||
|
||||
memset(buf,0,sizeof(buf));
|
||||
if (1 != (rc = tuner_i2c_xfer_recv(&priv->i2c_props,buf,1)))
|
||||
tuner_info("i2c i/o error: rc == %d (should be 1)\n", rc);
|
||||
dump_read_message(fe, buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tda9887_configure(struct dvb_frontend *fe)
|
||||
{
|
||||
struct tda9887_priv *priv = fe->analog_demod_priv;
|
||||
int rc;
|
||||
|
||||
memset(priv->data,0,sizeof(priv->data));
|
||||
tda9887_set_tvnorm(fe);
|
||||
|
||||
/* A note on the port settings:
|
||||
These settings tend to depend on the specifics of the board.
|
||||
By default they are set to inactive (bit value 1) by this driver,
|
||||
overwriting any changes made by the tvnorm. This means that it
|
||||
is the responsibility of the module using the tda9887 to set
|
||||
these values in case of changes in the tvnorm.
|
||||
In many cases port 2 should be made active (0) when selecting
|
||||
SECAM-L, and port 2 should remain inactive (1) for SECAM-L'.
|
||||
|
||||
For the other standards the tda9887 application note says that
|
||||
the ports should be set to active (0), but, again, that may
|
||||
differ depending on the precise hardware configuration.
|
||||
*/
|
||||
priv->data[1] |= cOutputPort1Inactive;
|
||||
priv->data[1] |= cOutputPort2Inactive;
|
||||
|
||||
tda9887_do_config(fe);
|
||||
tda9887_set_insmod(fe);
|
||||
|
||||
if (priv->mode == T_STANDBY)
|
||||
priv->data[1] |= cForcedMuteAudioON;
|
||||
|
||||
tuner_dbg("writing: b=0x%02x c=0x%02x e=0x%02x\n",
|
||||
priv->data[1], priv->data[2], priv->data[3]);
|
||||
if (debug > 1)
|
||||
dump_write_message(fe, priv->data);
|
||||
|
||||
if (4 != (rc = tuner_i2c_xfer_send(&priv->i2c_props,priv->data,4)))
|
||||
tuner_info("i2c i/o error: rc == %d (should be 4)\n", rc);
|
||||
|
||||
if (debug > 2) {
|
||||
msleep_interruptible(1000);
|
||||
tda9887_status(fe);
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
static void tda9887_tuner_status(struct dvb_frontend *fe)
|
||||
{
|
||||
struct tda9887_priv *priv = fe->analog_demod_priv;
|
||||
tuner_info("Data bytes: b=0x%02x c=0x%02x e=0x%02x\n",
|
||||
priv->data[1], priv->data[2], priv->data[3]);
|
||||
}
|
||||
|
||||
static int tda9887_get_afc(struct dvb_frontend *fe)
|
||||
{
|
||||
struct tda9887_priv *priv = fe->analog_demod_priv;
|
||||
static int AFC_BITS_2_kHz[] = {
|
||||
-12500, -37500, -62500, -97500,
|
||||
-112500, -137500, -162500, -187500,
|
||||
187500, 162500, 137500, 112500,
|
||||
97500 , 62500, 37500 , 12500
|
||||
};
|
||||
int afc=0;
|
||||
__u8 reg = 0;
|
||||
|
||||
if (1 == tuner_i2c_xfer_recv(&priv->i2c_props,®,1))
|
||||
afc = AFC_BITS_2_kHz[(reg>>1)&0x0f];
|
||||
|
||||
return afc;
|
||||
}
|
||||
|
||||
static void tda9887_standby(struct dvb_frontend *fe)
|
||||
{
|
||||
struct tda9887_priv *priv = fe->analog_demod_priv;
|
||||
|
||||
priv->mode = T_STANDBY;
|
||||
|
||||
tda9887_configure(fe);
|
||||
}
|
||||
|
||||
static void tda9887_set_params(struct dvb_frontend *fe,
|
||||
struct analog_parameters *params)
|
||||
{
|
||||
struct tda9887_priv *priv = fe->analog_demod_priv;
|
||||
|
||||
priv->mode = params->mode;
|
||||
priv->audmode = params->audmode;
|
||||
priv->std = params->std;
|
||||
tda9887_configure(fe);
|
||||
}
|
||||
|
||||
static int tda9887_set_config(struct dvb_frontend *fe, void *priv_cfg)
|
||||
{
|
||||
struct tda9887_priv *priv = fe->analog_demod_priv;
|
||||
|
||||
priv->config = *(unsigned int *)priv_cfg;
|
||||
tda9887_configure(fe);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tda9887_release(struct dvb_frontend *fe)
|
||||
{
|
||||
struct tda9887_priv *priv = fe->analog_demod_priv;
|
||||
|
||||
mutex_lock(&tda9887_list_mutex);
|
||||
|
||||
if (priv)
|
||||
hybrid_tuner_release_state(priv);
|
||||
|
||||
mutex_unlock(&tda9887_list_mutex);
|
||||
|
||||
fe->analog_demod_priv = NULL;
|
||||
}
|
||||
|
||||
static struct analog_demod_ops tda9887_ops = {
|
||||
.info = {
|
||||
.name = "tda9887",
|
||||
},
|
||||
.set_params = tda9887_set_params,
|
||||
.standby = tda9887_standby,
|
||||
.tuner_status = tda9887_tuner_status,
|
||||
.get_afc = tda9887_get_afc,
|
||||
.release = tda9887_release,
|
||||
.set_config = tda9887_set_config,
|
||||
};
|
||||
|
||||
struct dvb_frontend *tda9887_attach(struct dvb_frontend *fe,
|
||||
struct i2c_adapter *i2c_adap,
|
||||
u8 i2c_addr)
|
||||
{
|
||||
struct tda9887_priv *priv = NULL;
|
||||
int instance;
|
||||
|
||||
mutex_lock(&tda9887_list_mutex);
|
||||
|
||||
instance = hybrid_tuner_request_state(struct tda9887_priv, priv,
|
||||
hybrid_tuner_instance_list,
|
||||
i2c_adap, i2c_addr, "tda9887");
|
||||
switch (instance) {
|
||||
case 0:
|
||||
mutex_unlock(&tda9887_list_mutex);
|
||||
return NULL;
|
||||
break;
|
||||
case 1:
|
||||
fe->analog_demod_priv = priv;
|
||||
priv->mode = T_STANDBY;
|
||||
tuner_info("tda988[5/6/7] found\n");
|
||||
break;
|
||||
default:
|
||||
fe->analog_demod_priv = priv;
|
||||
break;
|
||||
}
|
||||
|
||||
mutex_unlock(&tda9887_list_mutex);
|
||||
|
||||
memcpy(&fe->ops.analog_ops, &tda9887_ops,
|
||||
sizeof(struct analog_demod_ops));
|
||||
|
||||
return fe;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tda9887_attach);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
/*
|
||||
* Overrides for Emacs so that we follow Linus's tabbing style.
|
||||
* ---------------------------------------------------------------------------
|
||||
* Local variables:
|
||||
* c-basic-offset: 8
|
||||
* End:
|
||||
*/
|
||||
38
drivers/media/common/tuners/tda9887.h
Normal file
38
drivers/media/common/tuners/tda9887.h
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
#ifndef __TDA9887_H__
|
||||
#define __TDA9887_H__
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include "dvb_frontend.h"
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
#if defined(CONFIG_TUNER_TDA9887) || (defined(CONFIG_TUNER_TDA9887_MODULE) && defined(MODULE))
|
||||
extern struct dvb_frontend *tda9887_attach(struct dvb_frontend *fe,
|
||||
struct i2c_adapter *i2c_adap,
|
||||
u8 i2c_addr);
|
||||
#else
|
||||
static inline struct dvb_frontend *tda9887_attach(struct dvb_frontend *fe,
|
||||
struct i2c_adapter *i2c_adap,
|
||||
u8 i2c_addr)
|
||||
{
|
||||
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __TDA9887_H__ */
|
||||
324
drivers/media/common/tuners/tea5761.c
Normal file
324
drivers/media/common/tuners/tea5761.c
Normal file
|
|
@ -0,0 +1,324 @@
|
|||
/*
|
||||
* For Philips TEA5761 FM Chip
|
||||
* I2C address is allways 0x20 (0x10 at 7-bit mode).
|
||||
*
|
||||
* Copyright (c) 2005-2007 Mauro Carvalho Chehab (mchehab@infradead.org)
|
||||
* This code is placed under the terms of the GNUv2 General Public License
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/videodev.h>
|
||||
#include <media/tuner.h>
|
||||
#include "tuner-i2c.h"
|
||||
#include "tea5761.h"
|
||||
|
||||
static int debug;
|
||||
module_param(debug, int, 0644);
|
||||
MODULE_PARM_DESC(debug, "enable verbose debug messages");
|
||||
|
||||
struct tea5761_priv {
|
||||
struct tuner_i2c_props i2c_props;
|
||||
|
||||
u32 frequency;
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/***************************
|
||||
* TEA5761HN I2C registers *
|
||||
***************************/
|
||||
|
||||
/* INTREG - Read: bytes 0 and 1 / Write: byte 0 */
|
||||
|
||||
/* first byte for reading */
|
||||
#define TEA5761_INTREG_IFFLAG 0x10
|
||||
#define TEA5761_INTREG_LEVFLAG 0x8
|
||||
#define TEA5761_INTREG_FRRFLAG 0x2
|
||||
#define TEA5761_INTREG_BLFLAG 0x1
|
||||
|
||||
/* second byte for reading / byte for writing */
|
||||
#define TEA5761_INTREG_IFMSK 0x10
|
||||
#define TEA5761_INTREG_LEVMSK 0x8
|
||||
#define TEA5761_INTREG_FRMSK 0x2
|
||||
#define TEA5761_INTREG_BLMSK 0x1
|
||||
|
||||
/* FRQSET - Read: bytes 2 and 3 / Write: byte 1 and 2 */
|
||||
|
||||
/* First byte */
|
||||
#define TEA5761_FRQSET_SEARCH_UP 0x80 /* 1=Station search from botton to up */
|
||||
#define TEA5761_FRQSET_SEARCH_MODE 0x40 /* 1=Search mode */
|
||||
|
||||
/* Bits 0-5 for divider MSB */
|
||||
|
||||
/* Second byte */
|
||||
/* Bits 0-7 for divider LSB */
|
||||
|
||||
/* TNCTRL - Read: bytes 4 and 5 / Write: Bytes 3 and 4 */
|
||||
|
||||
/* first byte */
|
||||
|
||||
#define TEA5761_TNCTRL_PUPD_0 0x40 /* Power UP/Power Down MSB */
|
||||
#define TEA5761_TNCTRL_BLIM 0X20 /* 1= Japan Frequencies, 0= European frequencies */
|
||||
#define TEA5761_TNCTRL_SWPM 0x10 /* 1= software port is FRRFLAG */
|
||||
#define TEA5761_TNCTRL_IFCTC 0x08 /* 1= IF count time 15.02 ms, 0= IF count time 2.02 ms */
|
||||
#define TEA5761_TNCTRL_AFM 0x04
|
||||
#define TEA5761_TNCTRL_SMUTE 0x02 /* 1= Soft mute */
|
||||
#define TEA5761_TNCTRL_SNC 0x01
|
||||
|
||||
/* second byte */
|
||||
|
||||
#define TEA5761_TNCTRL_MU 0x80 /* 1=Hard mute */
|
||||
#define TEA5761_TNCTRL_SSL_1 0x40
|
||||
#define TEA5761_TNCTRL_SSL_0 0x20
|
||||
#define TEA5761_TNCTRL_HLSI 0x10
|
||||
#define TEA5761_TNCTRL_MST 0x08 /* 1 = mono */
|
||||
#define TEA5761_TNCTRL_SWP 0x04
|
||||
#define TEA5761_TNCTRL_DTC 0x02 /* 1 = deemphasis 50 us, 0 = deemphasis 75 us */
|
||||
#define TEA5761_TNCTRL_AHLSI 0x01
|
||||
|
||||
/* FRQCHECK - Read: bytes 6 and 7 */
|
||||
/* First byte */
|
||||
|
||||
/* Bits 0-5 for divider MSB */
|
||||
|
||||
/* Second byte */
|
||||
/* Bits 0-7 for divider LSB */
|
||||
|
||||
/* TUNCHECK - Read: bytes 8 and 9 */
|
||||
|
||||
/* First byte */
|
||||
#define TEA5761_TUNCHECK_IF_MASK 0x7e /* IF count */
|
||||
#define TEA5761_TUNCHECK_TUNTO 0x01
|
||||
|
||||
/* Second byte */
|
||||
#define TEA5761_TUNCHECK_LEV_MASK 0xf0 /* Level Count */
|
||||
#define TEA5761_TUNCHECK_LD 0x08
|
||||
#define TEA5761_TUNCHECK_STEREO 0x04
|
||||
|
||||
/* TESTREG - Read: bytes 10 and 11 / Write: bytes 5 and 6 */
|
||||
|
||||
/* All zero = no test mode */
|
||||
|
||||
/* MANID - Read: bytes 12 and 13 */
|
||||
|
||||
/* First byte - should be 0x10 */
|
||||
#define TEA5767_MANID_VERSION_MASK 0xf0 /* Version = 1 */
|
||||
#define TEA5767_MANID_ID_MSB_MASK 0x0f /* Manufacurer ID - should be 0 */
|
||||
|
||||
/* Second byte - Should be 0x2b */
|
||||
|
||||
#define TEA5767_MANID_ID_LSB_MASK 0xfe /* Manufacturer ID - should be 0x15 */
|
||||
#define TEA5767_MANID_IDAV 0x01 /* 1 = Chip has ID, 0 = Chip has no ID */
|
||||
|
||||
/* Chip ID - Read: bytes 14 and 15 */
|
||||
|
||||
/* First byte - should be 0x57 */
|
||||
|
||||
/* Second byte - should be 0x61 */
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
#define FREQ_OFFSET 0 /* for TEA5767, it is 700 to give the right freq */
|
||||
static void tea5761_status_dump(unsigned char *buffer)
|
||||
{
|
||||
unsigned int div, frq;
|
||||
|
||||
div = ((buffer[2] & 0x3f) << 8) | buffer[3];
|
||||
|
||||
frq = 1000 * (div * 32768 / 1000 + FREQ_OFFSET + 225) / 4; /* Freq in KHz */
|
||||
|
||||
printk(KERN_INFO "tea5761: Frequency %d.%03d KHz (divider = 0x%04x)\n",
|
||||
frq / 1000, frq % 1000, div);
|
||||
}
|
||||
|
||||
/* Freq should be specifyed at 62.5 Hz */
|
||||
static int set_radio_freq(struct dvb_frontend *fe,
|
||||
struct analog_parameters *params)
|
||||
{
|
||||
struct tea5761_priv *priv = fe->tuner_priv;
|
||||
unsigned int frq = params->frequency;
|
||||
unsigned char buffer[7] = {0, 0, 0, 0, 0, 0, 0 };
|
||||
unsigned div;
|
||||
int rc;
|
||||
|
||||
tuner_dbg("radio freq counter %d\n", frq);
|
||||
|
||||
if (params->mode == T_STANDBY) {
|
||||
tuner_dbg("TEA5761 set to standby mode\n");
|
||||
buffer[5] |= TEA5761_TNCTRL_MU;
|
||||
} else {
|
||||
buffer[4] |= TEA5761_TNCTRL_PUPD_0;
|
||||
}
|
||||
|
||||
|
||||
if (params->audmode == V4L2_TUNER_MODE_MONO) {
|
||||
tuner_dbg("TEA5761 set to mono\n");
|
||||
buffer[5] |= TEA5761_TNCTRL_MST;
|
||||
} else {
|
||||
tuner_dbg("TEA5761 set to stereo\n");
|
||||
}
|
||||
|
||||
div = (1000 * (frq * 4 / 16 + 700 + 225) ) >> 15;
|
||||
buffer[1] = (div >> 8) & 0x3f;
|
||||
buffer[2] = div & 0xff;
|
||||
|
||||
if (debug)
|
||||
tea5761_status_dump(buffer);
|
||||
|
||||
if (7 != (rc = tuner_i2c_xfer_send(&priv->i2c_props, buffer, 7)))
|
||||
tuner_warn("i2c i/o error: rc == %d (should be 5)\n", rc);
|
||||
|
||||
priv->frequency = frq * 125 / 2;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tea5761_read_status(struct dvb_frontend *fe, char *buffer)
|
||||
{
|
||||
struct tea5761_priv *priv = fe->tuner_priv;
|
||||
int rc;
|
||||
|
||||
memset(buffer, 0, 16);
|
||||
if (16 != (rc = tuner_i2c_xfer_recv(&priv->i2c_props, buffer, 16))) {
|
||||
tuner_warn("i2c i/o error: rc == %d (should be 16)\n", rc);
|
||||
return -EREMOTEIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int tea5761_signal(struct dvb_frontend *fe, const char *buffer)
|
||||
{
|
||||
struct tea5761_priv *priv = fe->tuner_priv;
|
||||
|
||||
int signal = ((buffer[9] & TEA5761_TUNCHECK_LEV_MASK) << (13 - 4));
|
||||
|
||||
tuner_dbg("Signal strength: %d\n", signal);
|
||||
|
||||
return signal;
|
||||
}
|
||||
|
||||
static inline int tea5761_stereo(struct dvb_frontend *fe, const char *buffer)
|
||||
{
|
||||
struct tea5761_priv *priv = fe->tuner_priv;
|
||||
|
||||
int stereo = buffer[9] & TEA5761_TUNCHECK_STEREO;
|
||||
|
||||
tuner_dbg("Radio ST GET = %02x\n", stereo);
|
||||
|
||||
return (stereo ? V4L2_TUNER_SUB_STEREO : 0);
|
||||
}
|
||||
|
||||
static int tea5761_get_status(struct dvb_frontend *fe, u32 *status)
|
||||
{
|
||||
unsigned char buffer[16];
|
||||
|
||||
*status = 0;
|
||||
|
||||
if (0 == tea5761_read_status(fe, buffer)) {
|
||||
if (tea5761_signal(fe, buffer))
|
||||
*status = TUNER_STATUS_LOCKED;
|
||||
if (tea5761_stereo(fe, buffer))
|
||||
*status |= TUNER_STATUS_STEREO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tea5761_get_rf_strength(struct dvb_frontend *fe, u16 *strength)
|
||||
{
|
||||
unsigned char buffer[16];
|
||||
|
||||
*strength = 0;
|
||||
|
||||
if (0 == tea5761_read_status(fe, buffer))
|
||||
*strength = tea5761_signal(fe, buffer);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tea5761_autodetection(struct i2c_adapter* i2c_adap, u8 i2c_addr)
|
||||
{
|
||||
unsigned char buffer[16];
|
||||
int rc;
|
||||
struct tuner_i2c_props i2c = { .adap = i2c_adap, .addr = i2c_addr };
|
||||
|
||||
if (16 != (rc = tuner_i2c_xfer_recv(&i2c, buffer, 16))) {
|
||||
printk(KERN_WARNING "it is not a TEA5761. Received %i chars.\n", rc);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((buffer[13] != 0x2b) || (buffer[14] != 0x57) || (buffer[15] != 0x061)) {
|
||||
printk(KERN_WARNING "Manufacturer ID= 0x%02x, Chip ID = %02x%02x."
|
||||
" It is not a TEA5761\n",
|
||||
buffer[13], buffer[14], buffer[15]);
|
||||
return -EINVAL;
|
||||
}
|
||||
printk(KERN_WARNING "tea5761: TEA%02x%02x detected. "
|
||||
"Manufacturer ID= 0x%02x\n",
|
||||
buffer[14], buffer[15], buffer[13]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tea5761_release(struct dvb_frontend *fe)
|
||||
{
|
||||
kfree(fe->tuner_priv);
|
||||
fe->tuner_priv = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tea5761_get_frequency(struct dvb_frontend *fe, u32 *frequency)
|
||||
{
|
||||
struct tea5761_priv *priv = fe->tuner_priv;
|
||||
*frequency = priv->frequency;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct dvb_tuner_ops tea5761_tuner_ops = {
|
||||
.info = {
|
||||
.name = "tea5761", // Philips TEA5761HN FM Radio
|
||||
},
|
||||
.set_analog_params = set_radio_freq,
|
||||
.release = tea5761_release,
|
||||
.get_frequency = tea5761_get_frequency,
|
||||
.get_status = tea5761_get_status,
|
||||
.get_rf_strength = tea5761_get_rf_strength,
|
||||
};
|
||||
|
||||
struct dvb_frontend *tea5761_attach(struct dvb_frontend *fe,
|
||||
struct i2c_adapter* i2c_adap,
|
||||
u8 i2c_addr)
|
||||
{
|
||||
struct tea5761_priv *priv = NULL;
|
||||
|
||||
if (tea5761_autodetection(i2c_adap, i2c_addr) == EINVAL)
|
||||
return NULL;
|
||||
|
||||
priv = kzalloc(sizeof(struct tea5761_priv), GFP_KERNEL);
|
||||
if (priv == NULL)
|
||||
return NULL;
|
||||
fe->tuner_priv = priv;
|
||||
|
||||
priv->i2c_props.addr = i2c_addr;
|
||||
priv->i2c_props.adap = i2c_adap;
|
||||
priv->i2c_props.name = "tea5761";
|
||||
|
||||
memcpy(&fe->ops.tuner_ops, &tea5761_tuner_ops,
|
||||
sizeof(struct dvb_tuner_ops));
|
||||
|
||||
tuner_info("type set to %s\n", "Philips TEA5761HN FM Radio");
|
||||
|
||||
return fe;
|
||||
}
|
||||
|
||||
|
||||
EXPORT_SYMBOL_GPL(tea5761_attach);
|
||||
EXPORT_SYMBOL_GPL(tea5761_autodetection);
|
||||
|
||||
MODULE_DESCRIPTION("Philips TEA5761 FM tuner driver");
|
||||
MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
|
||||
MODULE_LICENSE("GPL");
|
||||
47
drivers/media/common/tuners/tea5761.h
Normal file
47
drivers/media/common/tuners/tea5761.h
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
#ifndef __TEA5761_H__
|
||||
#define __TEA5761_H__
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include "dvb_frontend.h"
|
||||
|
||||
#if defined(CONFIG_TUNER_TEA5761) || (defined(CONFIG_TUNER_TEA5761_MODULE) && defined(MODULE))
|
||||
extern int tea5761_autodetection(struct i2c_adapter* i2c_adap, u8 i2c_addr);
|
||||
|
||||
extern struct dvb_frontend *tea5761_attach(struct dvb_frontend *fe,
|
||||
struct i2c_adapter* i2c_adap,
|
||||
u8 i2c_addr);
|
||||
#else
|
||||
static inline int tea5761_autodetection(struct i2c_adapter* i2c_adap,
|
||||
u8 i2c_addr)
|
||||
{
|
||||
printk(KERN_INFO "%s: not probed - driver disabled by Kconfig\n",
|
||||
__func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static inline struct dvb_frontend *tea5761_attach(struct dvb_frontend *fe,
|
||||
struct i2c_adapter* i2c_adap,
|
||||
u8 i2c_addr)
|
||||
{
|
||||
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __TEA5761_H__ */
|
||||
474
drivers/media/common/tuners/tea5767.c
Normal file
474
drivers/media/common/tuners/tea5767.c
Normal file
|
|
@ -0,0 +1,474 @@
|
|||
/*
|
||||
* For Philips TEA5767 FM Chip used on some TV Cards like Prolink Pixelview
|
||||
* I2C address is allways 0xC0.
|
||||
*
|
||||
*
|
||||
* Copyright (c) 2005 Mauro Carvalho Chehab (mchehab@infradead.org)
|
||||
* This code is placed under the terms of the GNU General Public License
|
||||
*
|
||||
* tea5767 autodetection thanks to Torsten Seeboth and Atsushi Nakagawa
|
||||
* from their contributions on DScaler.
|
||||
*/
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/videodev.h>
|
||||
#include "tuner-i2c.h"
|
||||
#include "tea5767.h"
|
||||
|
||||
static int debug;
|
||||
module_param(debug, int, 0644);
|
||||
MODULE_PARM_DESC(debug, "enable verbose debug messages");
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
struct tea5767_priv {
|
||||
struct tuner_i2c_props i2c_props;
|
||||
u32 frequency;
|
||||
struct tea5767_ctrl ctrl;
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/******************************
|
||||
* Write mode register values *
|
||||
******************************/
|
||||
|
||||
/* First register */
|
||||
#define TEA5767_MUTE 0x80 /* Mutes output */
|
||||
#define TEA5767_SEARCH 0x40 /* Activates station search */
|
||||
/* Bits 0-5 for divider MSB */
|
||||
|
||||
/* Second register */
|
||||
/* Bits 0-7 for divider LSB */
|
||||
|
||||
/* Third register */
|
||||
|
||||
/* Station search from botton to up */
|
||||
#define TEA5767_SEARCH_UP 0x80
|
||||
|
||||
/* Searches with ADC output = 10 */
|
||||
#define TEA5767_SRCH_HIGH_LVL 0x60
|
||||
|
||||
/* Searches with ADC output = 10 */
|
||||
#define TEA5767_SRCH_MID_LVL 0x40
|
||||
|
||||
/* Searches with ADC output = 5 */
|
||||
#define TEA5767_SRCH_LOW_LVL 0x20
|
||||
|
||||
/* if on, div=4*(Frf+Fif)/Fref otherwise, div=4*(Frf-Fif)/Freq) */
|
||||
#define TEA5767_HIGH_LO_INJECT 0x10
|
||||
|
||||
/* Disable stereo */
|
||||
#define TEA5767_MONO 0x08
|
||||
|
||||
/* Disable right channel and turns to mono */
|
||||
#define TEA5767_MUTE_RIGHT 0x04
|
||||
|
||||
/* Disable left channel and turns to mono */
|
||||
#define TEA5767_MUTE_LEFT 0x02
|
||||
|
||||
#define TEA5767_PORT1_HIGH 0x01
|
||||
|
||||
/* Fourth register */
|
||||
#define TEA5767_PORT2_HIGH 0x80
|
||||
/* Chips stops working. Only I2C bus remains on */
|
||||
#define TEA5767_STDBY 0x40
|
||||
|
||||
/* Japan freq (76-108 MHz. If disabled, 87.5-108 MHz */
|
||||
#define TEA5767_JAPAN_BAND 0x20
|
||||
|
||||
/* Unselected means 32.768 KHz freq as reference. Otherwise Xtal at 13 MHz */
|
||||
#define TEA5767_XTAL_32768 0x10
|
||||
|
||||
/* Cuts weak signals */
|
||||
#define TEA5767_SOFT_MUTE 0x08
|
||||
|
||||
/* Activates high cut control */
|
||||
#define TEA5767_HIGH_CUT_CTRL 0x04
|
||||
|
||||
/* Activates stereo noise control */
|
||||
#define TEA5767_ST_NOISE_CTL 0x02
|
||||
|
||||
/* If activate PORT 1 indicates SEARCH or else it is used as PORT1 */
|
||||
#define TEA5767_SRCH_IND 0x01
|
||||
|
||||
/* Fifth register */
|
||||
|
||||
/* By activating, it will use Xtal at 13 MHz as reference for divider */
|
||||
#define TEA5767_PLLREF_ENABLE 0x80
|
||||
|
||||
/* By activating, deemphasis=50, or else, deemphasis of 50us */
|
||||
#define TEA5767_DEEMPH_75 0X40
|
||||
|
||||
/*****************************
|
||||
* Read mode register values *
|
||||
*****************************/
|
||||
|
||||
/* First register */
|
||||
#define TEA5767_READY_FLAG_MASK 0x80
|
||||
#define TEA5767_BAND_LIMIT_MASK 0X40
|
||||
/* Bits 0-5 for divider MSB after search or preset */
|
||||
|
||||
/* Second register */
|
||||
/* Bits 0-7 for divider LSB after search or preset */
|
||||
|
||||
/* Third register */
|
||||
#define TEA5767_STEREO_MASK 0x80
|
||||
#define TEA5767_IF_CNTR_MASK 0x7f
|
||||
|
||||
/* Fourth register */
|
||||
#define TEA5767_ADC_LEVEL_MASK 0xf0
|
||||
|
||||
/* should be 0 */
|
||||
#define TEA5767_CHIP_ID_MASK 0x0f
|
||||
|
||||
/* Fifth register */
|
||||
/* Reserved for future extensions */
|
||||
#define TEA5767_RESERVED_MASK 0xff
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
static void tea5767_status_dump(struct tea5767_priv *priv,
|
||||
unsigned char *buffer)
|
||||
{
|
||||
unsigned int div, frq;
|
||||
|
||||
if (TEA5767_READY_FLAG_MASK & buffer[0])
|
||||
tuner_info("Ready Flag ON\n");
|
||||
else
|
||||
tuner_info("Ready Flag OFF\n");
|
||||
|
||||
if (TEA5767_BAND_LIMIT_MASK & buffer[0])
|
||||
tuner_info("Tuner at band limit\n");
|
||||
else
|
||||
tuner_info("Tuner not at band limit\n");
|
||||
|
||||
div = ((buffer[0] & 0x3f) << 8) | buffer[1];
|
||||
|
||||
switch (priv->ctrl.xtal_freq) {
|
||||
case TEA5767_HIGH_LO_13MHz:
|
||||
frq = (div * 50000 - 700000 - 225000) / 4; /* Freq in KHz */
|
||||
break;
|
||||
case TEA5767_LOW_LO_13MHz:
|
||||
frq = (div * 50000 + 700000 + 225000) / 4; /* Freq in KHz */
|
||||
break;
|
||||
case TEA5767_LOW_LO_32768:
|
||||
frq = (div * 32768 + 700000 + 225000) / 4; /* Freq in KHz */
|
||||
break;
|
||||
case TEA5767_HIGH_LO_32768:
|
||||
default:
|
||||
frq = (div * 32768 - 700000 - 225000) / 4; /* Freq in KHz */
|
||||
break;
|
||||
}
|
||||
buffer[0] = (div >> 8) & 0x3f;
|
||||
buffer[1] = div & 0xff;
|
||||
|
||||
tuner_info("Frequency %d.%03d KHz (divider = 0x%04x)\n",
|
||||
frq / 1000, frq % 1000, div);
|
||||
|
||||
if (TEA5767_STEREO_MASK & buffer[2])
|
||||
tuner_info("Stereo\n");
|
||||
else
|
||||
tuner_info("Mono\n");
|
||||
|
||||
tuner_info("IF Counter = %d\n", buffer[2] & TEA5767_IF_CNTR_MASK);
|
||||
|
||||
tuner_info("ADC Level = %d\n",
|
||||
(buffer[3] & TEA5767_ADC_LEVEL_MASK) >> 4);
|
||||
|
||||
tuner_info("Chip ID = %d\n", (buffer[3] & TEA5767_CHIP_ID_MASK));
|
||||
|
||||
tuner_info("Reserved = 0x%02x\n",
|
||||
(buffer[4] & TEA5767_RESERVED_MASK));
|
||||
}
|
||||
|
||||
/* Freq should be specifyed at 62.5 Hz */
|
||||
static int set_radio_freq(struct dvb_frontend *fe,
|
||||
struct analog_parameters *params)
|
||||
{
|
||||
struct tea5767_priv *priv = fe->tuner_priv;
|
||||
unsigned int frq = params->frequency;
|
||||
unsigned char buffer[5];
|
||||
unsigned div;
|
||||
int rc;
|
||||
|
||||
tuner_dbg("radio freq = %d.%03d MHz\n", frq/16000,(frq/16)%1000);
|
||||
|
||||
buffer[2] = 0;
|
||||
|
||||
if (priv->ctrl.port1)
|
||||
buffer[2] |= TEA5767_PORT1_HIGH;
|
||||
|
||||
if (params->audmode == V4L2_TUNER_MODE_MONO) {
|
||||
tuner_dbg("TEA5767 set to mono\n");
|
||||
buffer[2] |= TEA5767_MONO;
|
||||
} else {
|
||||
tuner_dbg("TEA5767 set to stereo\n");
|
||||
}
|
||||
|
||||
|
||||
buffer[3] = 0;
|
||||
|
||||
if (priv->ctrl.port2)
|
||||
buffer[3] |= TEA5767_PORT2_HIGH;
|
||||
|
||||
if (priv->ctrl.high_cut)
|
||||
buffer[3] |= TEA5767_HIGH_CUT_CTRL;
|
||||
|
||||
if (priv->ctrl.st_noise)
|
||||
buffer[3] |= TEA5767_ST_NOISE_CTL;
|
||||
|
||||
if (priv->ctrl.soft_mute)
|
||||
buffer[3] |= TEA5767_SOFT_MUTE;
|
||||
|
||||
if (priv->ctrl.japan_band)
|
||||
buffer[3] |= TEA5767_JAPAN_BAND;
|
||||
|
||||
buffer[4] = 0;
|
||||
|
||||
if (priv->ctrl.deemph_75)
|
||||
buffer[4] |= TEA5767_DEEMPH_75;
|
||||
|
||||
if (priv->ctrl.pllref)
|
||||
buffer[4] |= TEA5767_PLLREF_ENABLE;
|
||||
|
||||
|
||||
/* Rounds freq to next decimal value - for 62.5 KHz step */
|
||||
/* frq = 20*(frq/16)+radio_frq[frq%16]; */
|
||||
|
||||
switch (priv->ctrl.xtal_freq) {
|
||||
case TEA5767_HIGH_LO_13MHz:
|
||||
tuner_dbg("radio HIGH LO inject xtal @ 13 MHz\n");
|
||||
buffer[2] |= TEA5767_HIGH_LO_INJECT;
|
||||
div = (frq * (4000 / 16) + 700000 + 225000 + 25000) / 50000;
|
||||
break;
|
||||
case TEA5767_LOW_LO_13MHz:
|
||||
tuner_dbg("radio LOW LO inject xtal @ 13 MHz\n");
|
||||
|
||||
div = (frq * (4000 / 16) - 700000 - 225000 + 25000) / 50000;
|
||||
break;
|
||||
case TEA5767_LOW_LO_32768:
|
||||
tuner_dbg("radio LOW LO inject xtal @ 32,768 MHz\n");
|
||||
buffer[3] |= TEA5767_XTAL_32768;
|
||||
/* const 700=4000*175 Khz - to adjust freq to right value */
|
||||
div = ((frq * (4000 / 16) - 700000 - 225000) + 16384) >> 15;
|
||||
break;
|
||||
case TEA5767_HIGH_LO_32768:
|
||||
default:
|
||||
tuner_dbg("radio HIGH LO inject xtal @ 32,768 MHz\n");
|
||||
|
||||
buffer[2] |= TEA5767_HIGH_LO_INJECT;
|
||||
buffer[3] |= TEA5767_XTAL_32768;
|
||||
div = ((frq * (4000 / 16) + 700000 + 225000) + 16384) >> 15;
|
||||
break;
|
||||
}
|
||||
buffer[0] = (div >> 8) & 0x3f;
|
||||
buffer[1] = div & 0xff;
|
||||
|
||||
if (5 != (rc = tuner_i2c_xfer_send(&priv->i2c_props, buffer, 5)))
|
||||
tuner_warn("i2c i/o error: rc == %d (should be 5)\n", rc);
|
||||
|
||||
if (debug) {
|
||||
if (5 != (rc = tuner_i2c_xfer_recv(&priv->i2c_props, buffer, 5)))
|
||||
tuner_warn("i2c i/o error: rc == %d (should be 5)\n", rc);
|
||||
else
|
||||
tea5767_status_dump(priv, buffer);
|
||||
}
|
||||
|
||||
priv->frequency = frq * 125 / 2;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tea5767_read_status(struct dvb_frontend *fe, char *buffer)
|
||||
{
|
||||
struct tea5767_priv *priv = fe->tuner_priv;
|
||||
int rc;
|
||||
|
||||
memset(buffer, 0, 5);
|
||||
if (5 != (rc = tuner_i2c_xfer_recv(&priv->i2c_props, buffer, 5))) {
|
||||
tuner_warn("i2c i/o error: rc == %d (should be 5)\n", rc);
|
||||
return -EREMOTEIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int tea5767_signal(struct dvb_frontend *fe, const char *buffer)
|
||||
{
|
||||
struct tea5767_priv *priv = fe->tuner_priv;
|
||||
|
||||
int signal = ((buffer[3] & TEA5767_ADC_LEVEL_MASK) << 8);
|
||||
|
||||
tuner_dbg("Signal strength: %d\n", signal);
|
||||
|
||||
return signal;
|
||||
}
|
||||
|
||||
static inline int tea5767_stereo(struct dvb_frontend *fe, const char *buffer)
|
||||
{
|
||||
struct tea5767_priv *priv = fe->tuner_priv;
|
||||
|
||||
int stereo = buffer[2] & TEA5767_STEREO_MASK;
|
||||
|
||||
tuner_dbg("Radio ST GET = %02x\n", stereo);
|
||||
|
||||
return (stereo ? V4L2_TUNER_SUB_STEREO : 0);
|
||||
}
|
||||
|
||||
static int tea5767_get_status(struct dvb_frontend *fe, u32 *status)
|
||||
{
|
||||
unsigned char buffer[5];
|
||||
|
||||
*status = 0;
|
||||
|
||||
if (0 == tea5767_read_status(fe, buffer)) {
|
||||
if (tea5767_signal(fe, buffer))
|
||||
*status = TUNER_STATUS_LOCKED;
|
||||
if (tea5767_stereo(fe, buffer))
|
||||
*status |= TUNER_STATUS_STEREO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tea5767_get_rf_strength(struct dvb_frontend *fe, u16 *strength)
|
||||
{
|
||||
unsigned char buffer[5];
|
||||
|
||||
*strength = 0;
|
||||
|
||||
if (0 == tea5767_read_status(fe, buffer))
|
||||
*strength = tea5767_signal(fe, buffer);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tea5767_standby(struct dvb_frontend *fe)
|
||||
{
|
||||
unsigned char buffer[5];
|
||||
struct tea5767_priv *priv = fe->tuner_priv;
|
||||
unsigned div, rc;
|
||||
|
||||
div = (87500 * 4 + 700 + 225 + 25) / 50; /* Set frequency to 87.5 MHz */
|
||||
buffer[0] = (div >> 8) & 0x3f;
|
||||
buffer[1] = div & 0xff;
|
||||
buffer[2] = TEA5767_PORT1_HIGH;
|
||||
buffer[3] = TEA5767_PORT2_HIGH | TEA5767_HIGH_CUT_CTRL |
|
||||
TEA5767_ST_NOISE_CTL | TEA5767_JAPAN_BAND | TEA5767_STDBY;
|
||||
buffer[4] = 0;
|
||||
|
||||
if (5 != (rc = tuner_i2c_xfer_send(&priv->i2c_props, buffer, 5)))
|
||||
tuner_warn("i2c i/o error: rc == %d (should be 5)\n", rc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tea5767_autodetection(struct i2c_adapter* i2c_adap, u8 i2c_addr)
|
||||
{
|
||||
struct tuner_i2c_props i2c = { .adap = i2c_adap, .addr = i2c_addr };
|
||||
unsigned char buffer[7] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
|
||||
int rc;
|
||||
|
||||
if ((rc = tuner_i2c_xfer_recv(&i2c, buffer, 7))< 5) {
|
||||
printk(KERN_WARNING "It is not a TEA5767. Received %i bytes.\n", rc);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
/* If all bytes are the same then it's a TV tuner and not a tea5767 */
|
||||
if (buffer[0] == buffer[1] && buffer[0] == buffer[2] &&
|
||||
buffer[0] == buffer[3] && buffer[0] == buffer[4]) {
|
||||
printk(KERN_WARNING "All bytes are equal. It is not a TEA5767\n");
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
/* Status bytes:
|
||||
* Byte 4: bit 3:1 : CI (Chip Identification) == 0
|
||||
* bit 0 : internally set to 0
|
||||
* Byte 5: bit 7:0 : == 0
|
||||
*/
|
||||
if (((buffer[3] & 0x0f) != 0x00) || (buffer[4] != 0x00)) {
|
||||
printk(KERN_WARNING "Chip ID is not zero. It is not a TEA5767\n");
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tea5767_release(struct dvb_frontend *fe)
|
||||
{
|
||||
kfree(fe->tuner_priv);
|
||||
fe->tuner_priv = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tea5767_get_frequency(struct dvb_frontend *fe, u32 *frequency)
|
||||
{
|
||||
struct tea5767_priv *priv = fe->tuner_priv;
|
||||
*frequency = priv->frequency;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tea5767_set_config (struct dvb_frontend *fe, void *priv_cfg)
|
||||
{
|
||||
struct tea5767_priv *priv = fe->tuner_priv;
|
||||
|
||||
memcpy(&priv->ctrl, priv_cfg, sizeof(priv->ctrl));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct dvb_tuner_ops tea5767_tuner_ops = {
|
||||
.info = {
|
||||
.name = "tea5767", // Philips TEA5767HN FM Radio
|
||||
},
|
||||
|
||||
.set_analog_params = set_radio_freq,
|
||||
.set_config = tea5767_set_config,
|
||||
.sleep = tea5767_standby,
|
||||
.release = tea5767_release,
|
||||
.get_frequency = tea5767_get_frequency,
|
||||
.get_status = tea5767_get_status,
|
||||
.get_rf_strength = tea5767_get_rf_strength,
|
||||
};
|
||||
|
||||
struct dvb_frontend *tea5767_attach(struct dvb_frontend *fe,
|
||||
struct i2c_adapter* i2c_adap,
|
||||
u8 i2c_addr)
|
||||
{
|
||||
struct tea5767_priv *priv = NULL;
|
||||
|
||||
priv = kzalloc(sizeof(struct tea5767_priv), GFP_KERNEL);
|
||||
if (priv == NULL)
|
||||
return NULL;
|
||||
fe->tuner_priv = priv;
|
||||
|
||||
priv->i2c_props.addr = i2c_addr;
|
||||
priv->i2c_props.adap = i2c_adap;
|
||||
priv->i2c_props.name = "tea5767";
|
||||
|
||||
priv->ctrl.xtal_freq = TEA5767_HIGH_LO_32768;
|
||||
priv->ctrl.port1 = 1;
|
||||
priv->ctrl.port2 = 1;
|
||||
priv->ctrl.high_cut = 1;
|
||||
priv->ctrl.st_noise = 1;
|
||||
priv->ctrl.japan_band = 1;
|
||||
|
||||
memcpy(&fe->ops.tuner_ops, &tea5767_tuner_ops,
|
||||
sizeof(struct dvb_tuner_ops));
|
||||
|
||||
tuner_info("type set to %s\n", "Philips TEA5767HN FM Radio");
|
||||
|
||||
return fe;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(tea5767_attach);
|
||||
EXPORT_SYMBOL_GPL(tea5767_autodetection);
|
||||
|
||||
MODULE_DESCRIPTION("Philips TEA5767 FM tuner driver");
|
||||
MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
|
||||
MODULE_LICENSE("GPL");
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue