/*
tda18271-fe.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 <linux/delay.h>
#include <linux/videodev2.h>
#include "tda18271-priv.h"
int tda18271_debug;
module_param_named(debug, tda18271_debug, int, 0644);
MODULE_PARM_DESC(debug, "set debug level "
"(info=1, map=2, reg=4, adv=8, cal=16 (or-able))");
static int tda18271_cal_on_startup;
module_param_named(cal, tda18271_cal_on_startup, int, 0644);
MODULE_PARM_DESC(cal, "perform RF tracking filter calibration on startup");
static DEFINE_MUTEX(tda18271_list_mutex);
static LIST_HEAD(hybrid_tuner_instance_list);
/*---------------------------------------------------------------------*/
static inline int charge_pump_source(struct dvb_frontend *fe, int force)
{
struct tda18271_priv *priv = fe->tuner_priv;
return tda18271_charge_pump_source(fe,
(priv->role == TDA18271_SLAVE) ?
TDA18271_CAL_PLL :
TDA18271_MAIN_PLL, force);
}
static int tda18271_channel_configuration(struct dvb_frontend *fe,
struct tda18271_std_map_item *map,
u32 freq, u32 bw)
{
struct tda18271_priv *priv = fe->tuner_priv;
unsigned char *regs = priv->tda18271_regs;
u32 N;
/* update TV broadcast parameters */
/* set standard */
regs[R_EP3] &= ~0x1f; /* clear std bits */
regs[R_EP3] |= (map->agc_mode << 3) | map->std;
/* set rfagc to high speed mode */
regs[R_EP3] &= ~0x04;
/* set cal mode to normal */
regs[R_EP4] &= ~0x03;
/* update IF output level & IF notch frequency */
regs[R_EP4] &= ~0x1c; /* clear if level bits */
regs[R_EP4] |= (map->if_lvl << 2);
switch (priv->mode) {
case TDA18271_ANALOG:
regs[R_MPD] &= ~0x80; /* IF notch = 0 */
break;
case TDA18271_DIGITAL:
regs[R_MPD] |= 0x80; /* IF notch = 1 */
break;
}
/* update FM_RFn */
regs[R_EP4] &= ~0x80;
regs[R_EP4] |= map->fm_rfn << 7;
/* update rf top / if top */
regs[R_EB22] = 0x00;
regs[R_EB22] |= map->rfagc_top;
tda18271_write_regs(fe, R_EB22, 1);
/* --------------------------------------------------------------- */
/* disable Power Level Indicator */
regs[R_EP1] |= 0x40;
/* frequency dependent parameters */
tda18271_calc_ir_measure(fe, &freq);
tda18271_calc_bp_filter(fe, &freq);
tda18271_calc_rf_band(fe, &freq);
tda18271_calc_gain_taper(fe, &freq);
/* --------------------------------------------------------------- */
/* dual tuner and agc1 extra configuration */
switch (priv->role) {
case TDA18271_MASTER:
regs[R_EB1] |= 0x04; /* main vco */
break;
case TDA18271_SLAVE:
regs[R_EB1] &= ~0x04; /* cal vco */
break;
}
/* agc1 always active */
regs[R_EB1] &= ~0x02;
/* agc1 has priority on agc2 */
regs[R_EB1] &= ~0x01;
tda18271_write_regs(fe, R_EB1, 1);
/*