Path: blob/master/drivers/media/common/tuners/tda18271-fe.c
15112 views
/*1tda18271-fe.c - driver for the Philips / NXP TDA18271 silicon tuner23Copyright (C) 2007, 2008 Michael Krufky <[email protected]>45This program is free software; you can redistribute it and/or modify6it under the terms of the GNU General Public License as published by7the Free Software Foundation; either version 2 of the License, or8(at your option) any later version.910This program is distributed in the hope that it will be useful,11but WITHOUT ANY WARRANTY; without even the implied warranty of12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the13GNU General Public License for more details.1415You should have received a copy of the GNU General Public License16along with this program; if not, write to the Free Software17Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.18*/1920#include <linux/delay.h>21#include <linux/videodev2.h>22#include "tda18271-priv.h"2324int tda18271_debug;25module_param_named(debug, tda18271_debug, int, 0644);26MODULE_PARM_DESC(debug, "set debug level "27"(info=1, map=2, reg=4, adv=8, cal=16 (or-able))");2829static int tda18271_cal_on_startup = -1;30module_param_named(cal, tda18271_cal_on_startup, int, 0644);31MODULE_PARM_DESC(cal, "perform RF tracking filter calibration on startup");3233static DEFINE_MUTEX(tda18271_list_mutex);34static LIST_HEAD(hybrid_tuner_instance_list);3536/*---------------------------------------------------------------------*/3738static int tda18271_toggle_output(struct dvb_frontend *fe, int standby)39{40struct tda18271_priv *priv = fe->tuner_priv;4142int ret = tda18271_set_standby_mode(fe, standby ? 1 : 0,43priv->output_opt & TDA18271_OUTPUT_LT_OFF ? 1 : 0,44priv->output_opt & TDA18271_OUTPUT_XT_OFF ? 1 : 0);4546if (tda_fail(ret))47goto fail;4849tda_dbg("%s mode: xtal oscillator %s, slave tuner loop thru %s\n",50standby ? "standby" : "active",51priv->output_opt & TDA18271_OUTPUT_XT_OFF ? "off" : "on",52priv->output_opt & TDA18271_OUTPUT_LT_OFF ? "off" : "on");53fail:54return ret;55}5657/*---------------------------------------------------------------------*/5859static inline int charge_pump_source(struct dvb_frontend *fe, int force)60{61struct tda18271_priv *priv = fe->tuner_priv;62return tda18271_charge_pump_source(fe,63(priv->role == TDA18271_SLAVE) ?64TDA18271_CAL_PLL :65TDA18271_MAIN_PLL, force);66}6768static inline void tda18271_set_if_notch(struct dvb_frontend *fe)69{70struct tda18271_priv *priv = fe->tuner_priv;71unsigned char *regs = priv->tda18271_regs;7273switch (priv->mode) {74case TDA18271_ANALOG:75regs[R_MPD] &= ~0x80; /* IF notch = 0 */76break;77case TDA18271_DIGITAL:78regs[R_MPD] |= 0x80; /* IF notch = 1 */79break;80}81}8283static int tda18271_channel_configuration(struct dvb_frontend *fe,84struct tda18271_std_map_item *map,85u32 freq, u32 bw)86{87struct tda18271_priv *priv = fe->tuner_priv;88unsigned char *regs = priv->tda18271_regs;89int ret;90u32 N;9192/* update TV broadcast parameters */9394/* set standard */95regs[R_EP3] &= ~0x1f; /* clear std bits */96regs[R_EP3] |= (map->agc_mode << 3) | map->std;9798if (priv->id == TDA18271HDC2) {99/* set rfagc to high speed mode */100regs[R_EP3] &= ~0x04;101}102103/* set cal mode to normal */104regs[R_EP4] &= ~0x03;105106/* update IF output level */107regs[R_EP4] &= ~0x1c; /* clear if level bits */108regs[R_EP4] |= (map->if_lvl << 2);109110/* update FM_RFn */111regs[R_EP4] &= ~0x80;112regs[R_EP4] |= map->fm_rfn << 7;113114/* update rf top / if top */115regs[R_EB22] = 0x00;116regs[R_EB22] |= map->rfagc_top;117ret = tda18271_write_regs(fe, R_EB22, 1);118if (tda_fail(ret))119goto fail;120121/* --------------------------------------------------------------- */122123/* disable Power Level Indicator */124regs[R_EP1] |= 0x40;125126/* make sure thermometer is off */127regs[R_TM] &= ~0x10;128129/* frequency dependent parameters */130131tda18271_calc_ir_measure(fe, &freq);132133tda18271_calc_bp_filter(fe, &freq);134135tda18271_calc_rf_band(fe, &freq);136137tda18271_calc_gain_taper(fe, &freq);138139/* --------------------------------------------------------------- */140141/* dual tuner and agc1 extra configuration */142143switch (priv->role) {144case TDA18271_MASTER:145regs[R_EB1] |= 0x04; /* main vco */146break;147case TDA18271_SLAVE:148regs[R_EB1] &= ~0x04; /* cal vco */149break;150}151152/* agc1 always active */153regs[R_EB1] &= ~0x02;154155/* agc1 has priority on agc2 */156regs[R_EB1] &= ~0x01;157158ret = tda18271_write_regs(fe, R_EB1, 1);159if (tda_fail(ret))160goto fail;161162/* --------------------------------------------------------------- */163164N = map->if_freq * 1000 + freq;165166switch (priv->role) {167case TDA18271_MASTER:168tda18271_calc_main_pll(fe, N);169tda18271_set_if_notch(fe);170tda18271_write_regs(fe, R_MPD, 4);171break;172case TDA18271_SLAVE:173tda18271_calc_cal_pll(fe, N);174tda18271_write_regs(fe, R_CPD, 4);175176regs[R_MPD] = regs[R_CPD] & 0x7f;177tda18271_set_if_notch(fe);178tda18271_write_regs(fe, R_MPD, 1);179break;180}181182ret = tda18271_write_regs(fe, R_TM, 7);183if (tda_fail(ret))184goto fail;185186/* force charge pump source */187charge_pump_source(fe, 1);188189msleep(1);190191/* return pll to normal operation */192charge_pump_source(fe, 0);193194msleep(20);195196if (priv->id == TDA18271HDC2) {197/* set rfagc to normal speed mode */198if (map->fm_rfn)199regs[R_EP3] &= ~0x04;200else201regs[R_EP3] |= 0x04;202ret = tda18271_write_regs(fe, R_EP3, 1);203}204fail:205return ret;206}207208static int tda18271_read_thermometer(struct dvb_frontend *fe)209{210struct tda18271_priv *priv = fe->tuner_priv;211unsigned char *regs = priv->tda18271_regs;212int tm;213214/* switch thermometer on */215regs[R_TM] |= 0x10;216tda18271_write_regs(fe, R_TM, 1);217218/* read thermometer info */219tda18271_read_regs(fe);220221if ((((regs[R_TM] & 0x0f) == 0x00) && ((regs[R_TM] & 0x20) == 0x20)) ||222(((regs[R_TM] & 0x0f) == 0x08) && ((regs[R_TM] & 0x20) == 0x00))) {223224if ((regs[R_TM] & 0x20) == 0x20)225regs[R_TM] &= ~0x20;226else227regs[R_TM] |= 0x20;228229tda18271_write_regs(fe, R_TM, 1);230231msleep(10); /* temperature sensing */232233/* read thermometer info */234tda18271_read_regs(fe);235}236237tm = tda18271_lookup_thermometer(fe);238239/* switch thermometer off */240regs[R_TM] &= ~0x10;241tda18271_write_regs(fe, R_TM, 1);242243/* set CAL mode to normal */244regs[R_EP4] &= ~0x03;245tda18271_write_regs(fe, R_EP4, 1);246247return tm;248}249250/* ------------------------------------------------------------------ */251252static int tda18271c2_rf_tracking_filters_correction(struct dvb_frontend *fe,253u32 freq)254{255struct tda18271_priv *priv = fe->tuner_priv;256struct tda18271_rf_tracking_filter_cal *map = priv->rf_cal_state;257unsigned char *regs = priv->tda18271_regs;258int i, ret;259u8 tm_current, dc_over_dt, rf_tab;260s32 rfcal_comp, approx;261262/* power up */263ret = tda18271_set_standby_mode(fe, 0, 0, 0);264if (tda_fail(ret))265goto fail;266267/* read die current temperature */268tm_current = tda18271_read_thermometer(fe);269270/* frequency dependent parameters */271272tda18271_calc_rf_cal(fe, &freq);273rf_tab = regs[R_EB14];274275i = tda18271_lookup_rf_band(fe, &freq, NULL);276if (tda_fail(i))277return i;278279if ((0 == map[i].rf3) || (freq / 1000 < map[i].rf2)) {280approx = map[i].rf_a1 * (s32)(freq / 1000 - map[i].rf1) +281map[i].rf_b1 + rf_tab;282} else {283approx = map[i].rf_a2 * (s32)(freq / 1000 - map[i].rf2) +284map[i].rf_b2 + rf_tab;285}286287if (approx < 0)288approx = 0;289if (approx > 255)290approx = 255;291292tda18271_lookup_map(fe, RF_CAL_DC_OVER_DT, &freq, &dc_over_dt);293294/* calculate temperature compensation */295rfcal_comp = dc_over_dt * (s32)(tm_current - priv->tm_rfcal) / 1000;296297regs[R_EB14] = (unsigned char)(approx + rfcal_comp);298ret = tda18271_write_regs(fe, R_EB14, 1);299fail:300return ret;301}302303static int tda18271_por(struct dvb_frontend *fe)304{305struct tda18271_priv *priv = fe->tuner_priv;306unsigned char *regs = priv->tda18271_regs;307int ret;308309/* power up detector 1 */310regs[R_EB12] &= ~0x20;311ret = tda18271_write_regs(fe, R_EB12, 1);312if (tda_fail(ret))313goto fail;314315regs[R_EB18] &= ~0x80; /* turn agc1 loop on */316regs[R_EB18] &= ~0x03; /* set agc1_gain to 6 dB */317ret = tda18271_write_regs(fe, R_EB18, 1);318if (tda_fail(ret))319goto fail;320321regs[R_EB21] |= 0x03; /* set agc2_gain to -6 dB */322323/* POR mode */324ret = tda18271_set_standby_mode(fe, 1, 0, 0);325if (tda_fail(ret))326goto fail;327328/* disable 1.5 MHz low pass filter */329regs[R_EB23] &= ~0x04; /* forcelp_fc2_en = 0 */330regs[R_EB23] &= ~0x02; /* XXX: lp_fc[2] = 0 */331ret = tda18271_write_regs(fe, R_EB21, 3);332fail:333return ret;334}335336static int tda18271_calibrate_rf(struct dvb_frontend *fe, u32 freq)337{338struct tda18271_priv *priv = fe->tuner_priv;339unsigned char *regs = priv->tda18271_regs;340u32 N;341342/* set CAL mode to normal */343regs[R_EP4] &= ~0x03;344tda18271_write_regs(fe, R_EP4, 1);345346/* switch off agc1 */347regs[R_EP3] |= 0x40; /* sm_lt = 1 */348349regs[R_EB18] |= 0x03; /* set agc1_gain to 15 dB */350tda18271_write_regs(fe, R_EB18, 1);351352/* frequency dependent parameters */353354tda18271_calc_bp_filter(fe, &freq);355tda18271_calc_gain_taper(fe, &freq);356tda18271_calc_rf_band(fe, &freq);357tda18271_calc_km(fe, &freq);358359tda18271_write_regs(fe, R_EP1, 3);360tda18271_write_regs(fe, R_EB13, 1);361362/* main pll charge pump source */363tda18271_charge_pump_source(fe, TDA18271_MAIN_PLL, 1);364365/* cal pll charge pump source */366tda18271_charge_pump_source(fe, TDA18271_CAL_PLL, 1);367368/* force dcdc converter to 0 V */369regs[R_EB14] = 0x00;370tda18271_write_regs(fe, R_EB14, 1);371372/* disable plls lock */373regs[R_EB20] &= ~0x20;374tda18271_write_regs(fe, R_EB20, 1);375376/* set CAL mode to RF tracking filter calibration */377regs[R_EP4] |= 0x03;378tda18271_write_regs(fe, R_EP4, 2);379380/* --------------------------------------------------------------- */381382/* set the internal calibration signal */383N = freq;384385tda18271_calc_cal_pll(fe, N);386tda18271_write_regs(fe, R_CPD, 4);387388/* downconvert internal calibration */389N += 1000000;390391tda18271_calc_main_pll(fe, N);392tda18271_write_regs(fe, R_MPD, 4);393394msleep(5);395396tda18271_write_regs(fe, R_EP2, 1);397tda18271_write_regs(fe, R_EP1, 1);398tda18271_write_regs(fe, R_EP2, 1);399tda18271_write_regs(fe, R_EP1, 1);400401/* --------------------------------------------------------------- */402403/* normal operation for the main pll */404tda18271_charge_pump_source(fe, TDA18271_MAIN_PLL, 0);405406/* normal operation for the cal pll */407tda18271_charge_pump_source(fe, TDA18271_CAL_PLL, 0);408409msleep(10); /* plls locking */410411/* launch the rf tracking filters calibration */412regs[R_EB20] |= 0x20;413tda18271_write_regs(fe, R_EB20, 1);414415msleep(60); /* calibration */416417/* --------------------------------------------------------------- */418419/* set CAL mode to normal */420regs[R_EP4] &= ~0x03;421422/* switch on agc1 */423regs[R_EP3] &= ~0x40; /* sm_lt = 0 */424425regs[R_EB18] &= ~0x03; /* set agc1_gain to 6 dB */426tda18271_write_regs(fe, R_EB18, 1);427428tda18271_write_regs(fe, R_EP3, 2);429430/* synchronization */431tda18271_write_regs(fe, R_EP1, 1);432433/* get calibration result */434tda18271_read_extended(fe);435436return regs[R_EB14];437}438439static int tda18271_powerscan(struct dvb_frontend *fe,440u32 *freq_in, u32 *freq_out)441{442struct tda18271_priv *priv = fe->tuner_priv;443unsigned char *regs = priv->tda18271_regs;444int sgn, bcal, count, wait, ret;445u8 cid_target;446u16 count_limit;447u32 freq;448449freq = *freq_in;450451tda18271_calc_rf_band(fe, &freq);452tda18271_calc_rf_cal(fe, &freq);453tda18271_calc_gain_taper(fe, &freq);454tda18271_lookup_cid_target(fe, &freq, &cid_target, &count_limit);455456tda18271_write_regs(fe, R_EP2, 1);457tda18271_write_regs(fe, R_EB14, 1);458459/* downconvert frequency */460freq += 1000000;461462tda18271_calc_main_pll(fe, freq);463tda18271_write_regs(fe, R_MPD, 4);464465msleep(5); /* pll locking */466467/* detection mode */468regs[R_EP4] &= ~0x03;469regs[R_EP4] |= 0x01;470tda18271_write_regs(fe, R_EP4, 1);471472/* launch power detection measurement */473tda18271_write_regs(fe, R_EP2, 1);474475/* read power detection info, stored in EB10 */476ret = tda18271_read_extended(fe);477if (tda_fail(ret))478return ret;479480/* algorithm initialization */481sgn = 1;482*freq_out = *freq_in;483bcal = 0;484count = 0;485wait = false;486487while ((regs[R_EB10] & 0x3f) < cid_target) {488/* downconvert updated freq to 1 MHz */489freq = *freq_in + (sgn * count) + 1000000;490491tda18271_calc_main_pll(fe, freq);492tda18271_write_regs(fe, R_MPD, 4);493494if (wait) {495msleep(5); /* pll locking */496wait = false;497} else498udelay(100); /* pll locking */499500/* launch power detection measurement */501tda18271_write_regs(fe, R_EP2, 1);502503/* read power detection info, stored in EB10 */504ret = tda18271_read_extended(fe);505if (tda_fail(ret))506return ret;507508count += 200;509510if (count <= count_limit)511continue;512513if (sgn <= 0)514break;515516sgn = -1 * sgn;517count = 200;518wait = true;519}520521if ((regs[R_EB10] & 0x3f) >= cid_target) {522bcal = 1;523*freq_out = freq - 1000000;524} else525bcal = 0;526527tda_cal("bcal = %d, freq_in = %d, freq_out = %d (freq = %d)\n",528bcal, *freq_in, *freq_out, freq);529530return bcal;531}532533static int tda18271_powerscan_init(struct dvb_frontend *fe)534{535struct tda18271_priv *priv = fe->tuner_priv;536unsigned char *regs = priv->tda18271_regs;537int ret;538539/* set standard to digital */540regs[R_EP3] &= ~0x1f; /* clear std bits */541regs[R_EP3] |= 0x12;542543/* set cal mode to normal */544regs[R_EP4] &= ~0x03;545546/* update IF output level */547regs[R_EP4] &= ~0x1c; /* clear if level bits */548549ret = tda18271_write_regs(fe, R_EP3, 2);550if (tda_fail(ret))551goto fail;552553regs[R_EB18] &= ~0x03; /* set agc1_gain to 6 dB */554ret = tda18271_write_regs(fe, R_EB18, 1);555if (tda_fail(ret))556goto fail;557558regs[R_EB21] &= ~0x03; /* set agc2_gain to -15 dB */559560/* 1.5 MHz low pass filter */561regs[R_EB23] |= 0x04; /* forcelp_fc2_en = 1 */562regs[R_EB23] |= 0x02; /* lp_fc[2] = 1 */563564ret = tda18271_write_regs(fe, R_EB21, 3);565fail:566return ret;567}568569static int tda18271_rf_tracking_filters_init(struct dvb_frontend *fe, u32 freq)570{571struct tda18271_priv *priv = fe->tuner_priv;572struct tda18271_rf_tracking_filter_cal *map = priv->rf_cal_state;573unsigned char *regs = priv->tda18271_regs;574int bcal, rf, i;575s32 divisor, dividend;576#define RF1 0577#define RF2 1578#define RF3 2579u32 rf_default[3];580u32 rf_freq[3];581s32 prog_cal[3];582s32 prog_tab[3];583584i = tda18271_lookup_rf_band(fe, &freq, NULL);585586if (tda_fail(i))587return i;588589rf_default[RF1] = 1000 * map[i].rf1_def;590rf_default[RF2] = 1000 * map[i].rf2_def;591rf_default[RF3] = 1000 * map[i].rf3_def;592593for (rf = RF1; rf <= RF3; rf++) {594if (0 == rf_default[rf])595return 0;596tda_cal("freq = %d, rf = %d\n", freq, rf);597598/* look for optimized calibration frequency */599bcal = tda18271_powerscan(fe, &rf_default[rf], &rf_freq[rf]);600if (tda_fail(bcal))601return bcal;602603tda18271_calc_rf_cal(fe, &rf_freq[rf]);604prog_tab[rf] = (s32)regs[R_EB14];605606if (1 == bcal)607prog_cal[rf] =608(s32)tda18271_calibrate_rf(fe, rf_freq[rf]);609else610prog_cal[rf] = prog_tab[rf];611612switch (rf) {613case RF1:614map[i].rf_a1 = 0;615map[i].rf_b1 = (prog_cal[RF1] - prog_tab[RF1]);616map[i].rf1 = rf_freq[RF1] / 1000;617break;618case RF2:619dividend = (prog_cal[RF2] - prog_tab[RF2] -620prog_cal[RF1] + prog_tab[RF1]);621divisor = (s32)(rf_freq[RF2] - rf_freq[RF1]) / 1000;622map[i].rf_a1 = (dividend / divisor);623map[i].rf2 = rf_freq[RF2] / 1000;624break;625case RF3:626dividend = (prog_cal[RF3] - prog_tab[RF3] -627prog_cal[RF2] + prog_tab[RF2]);628divisor = (s32)(rf_freq[RF3] - rf_freq[RF2]) / 1000;629map[i].rf_a2 = (dividend / divisor);630map[i].rf_b2 = (prog_cal[RF2] - prog_tab[RF2]);631map[i].rf3 = rf_freq[RF3] / 1000;632break;633default:634BUG();635}636}637638return 0;639}640641static int tda18271_calc_rf_filter_curve(struct dvb_frontend *fe)642{643struct tda18271_priv *priv = fe->tuner_priv;644unsigned int i;645int ret;646647tda_info("tda18271: performing RF tracking filter calibration\n");648649/* wait for die temperature stabilization */650msleep(200);651652ret = tda18271_powerscan_init(fe);653if (tda_fail(ret))654goto fail;655656/* rf band calibration */657for (i = 0; priv->rf_cal_state[i].rfmax != 0; i++) {658ret =659tda18271_rf_tracking_filters_init(fe, 1000 *660priv->rf_cal_state[i].rfmax);661if (tda_fail(ret))662goto fail;663}664665priv->tm_rfcal = tda18271_read_thermometer(fe);666fail:667return ret;668}669670/* ------------------------------------------------------------------ */671672static int tda18271c2_rf_cal_init(struct dvb_frontend *fe)673{674struct tda18271_priv *priv = fe->tuner_priv;675unsigned char *regs = priv->tda18271_regs;676int ret;677678/* test RF_CAL_OK to see if we need init */679if ((regs[R_EP1] & 0x10) == 0)680priv->cal_initialized = false;681682if (priv->cal_initialized)683return 0;684685ret = tda18271_calc_rf_filter_curve(fe);686if (tda_fail(ret))687goto fail;688689ret = tda18271_por(fe);690if (tda_fail(ret))691goto fail;692693tda_info("tda18271: RF tracking filter calibration complete\n");694695priv->cal_initialized = true;696goto end;697fail:698tda_info("tda18271: RF tracking filter calibration failed!\n");699end:700return ret;701}702703static int tda18271c1_rf_tracking_filter_calibration(struct dvb_frontend *fe,704u32 freq, u32 bw)705{706struct tda18271_priv *priv = fe->tuner_priv;707unsigned char *regs = priv->tda18271_regs;708int ret;709u32 N = 0;710711/* calculate bp filter */712tda18271_calc_bp_filter(fe, &freq);713tda18271_write_regs(fe, R_EP1, 1);714715regs[R_EB4] &= 0x07;716regs[R_EB4] |= 0x60;717tda18271_write_regs(fe, R_EB4, 1);718719regs[R_EB7] = 0x60;720tda18271_write_regs(fe, R_EB7, 1);721722regs[R_EB14] = 0x00;723tda18271_write_regs(fe, R_EB14, 1);724725regs[R_EB20] = 0xcc;726tda18271_write_regs(fe, R_EB20, 1);727728/* set cal mode to RF tracking filter calibration */729regs[R_EP4] |= 0x03;730731/* calculate cal pll */732733switch (priv->mode) {734case TDA18271_ANALOG:735N = freq - 1250000;736break;737case TDA18271_DIGITAL:738N = freq + bw / 2;739break;740}741742tda18271_calc_cal_pll(fe, N);743744/* calculate main pll */745746switch (priv->mode) {747case TDA18271_ANALOG:748N = freq - 250000;749break;750case TDA18271_DIGITAL:751N = freq + bw / 2 + 1000000;752break;753}754755tda18271_calc_main_pll(fe, N);756757ret = tda18271_write_regs(fe, R_EP3, 11);758if (tda_fail(ret))759return ret;760761msleep(5); /* RF tracking filter calibration initialization */762763/* search for K,M,CO for RF calibration */764tda18271_calc_km(fe, &freq);765tda18271_write_regs(fe, R_EB13, 1);766767/* search for rf band */768tda18271_calc_rf_band(fe, &freq);769770/* search for gain taper */771tda18271_calc_gain_taper(fe, &freq);772773tda18271_write_regs(fe, R_EP2, 1);774tda18271_write_regs(fe, R_EP1, 1);775tda18271_write_regs(fe, R_EP2, 1);776tda18271_write_regs(fe, R_EP1, 1);777778regs[R_EB4] &= 0x07;779regs[R_EB4] |= 0x40;780tda18271_write_regs(fe, R_EB4, 1);781782regs[R_EB7] = 0x40;783tda18271_write_regs(fe, R_EB7, 1);784msleep(10); /* pll locking */785786regs[R_EB20] = 0xec;787tda18271_write_regs(fe, R_EB20, 1);788msleep(60); /* RF tracking filter calibration completion */789790regs[R_EP4] &= ~0x03; /* set cal mode to normal */791tda18271_write_regs(fe, R_EP4, 1);792793tda18271_write_regs(fe, R_EP1, 1);794795/* RF tracking filter correction for VHF_Low band */796if (0 == tda18271_calc_rf_cal(fe, &freq))797tda18271_write_regs(fe, R_EB14, 1);798799return 0;800}801802/* ------------------------------------------------------------------ */803804static int tda18271_ir_cal_init(struct dvb_frontend *fe)805{806struct tda18271_priv *priv = fe->tuner_priv;807unsigned char *regs = priv->tda18271_regs;808int ret;809810ret = tda18271_read_regs(fe);811if (tda_fail(ret))812goto fail;813814/* test IR_CAL_OK to see if we need init */815if ((regs[R_EP1] & 0x08) == 0)816ret = tda18271_init_regs(fe);817fail:818return ret;819}820821static int tda18271_init(struct dvb_frontend *fe)822{823struct tda18271_priv *priv = fe->tuner_priv;824int ret;825826mutex_lock(&priv->lock);827828/* full power up */829ret = tda18271_set_standby_mode(fe, 0, 0, 0);830if (tda_fail(ret))831goto fail;832833/* initialization */834ret = tda18271_ir_cal_init(fe);835if (tda_fail(ret))836goto fail;837838if (priv->id == TDA18271HDC2)839tda18271c2_rf_cal_init(fe);840fail:841mutex_unlock(&priv->lock);842843return ret;844}845846static int tda18271_sleep(struct dvb_frontend *fe)847{848struct tda18271_priv *priv = fe->tuner_priv;849int ret;850851mutex_lock(&priv->lock);852853/* enter standby mode, with required output features enabled */854ret = tda18271_toggle_output(fe, 1);855856mutex_unlock(&priv->lock);857858return ret;859}860861/* ------------------------------------------------------------------ */862863static int tda18271_agc(struct dvb_frontend *fe)864{865struct tda18271_priv *priv = fe->tuner_priv;866int ret = 0;867868switch (priv->config) {869case 0:870/* no external agc configuration required */871if (tda18271_debug & DBG_ADV)872tda_dbg("no agc configuration provided\n");873break;874case 3:875/* switch with GPIO of saa713x */876tda_dbg("invoking callback\n");877if (fe->callback)878ret = fe->callback(priv->i2c_props.adap->algo_data,879DVB_FRONTEND_COMPONENT_TUNER,880TDA18271_CALLBACK_CMD_AGC_ENABLE,881priv->mode);882break;883case 1:884case 2:885default:886/* n/a - currently not supported */887tda_err("unsupported configuration: %d\n", priv->config);888ret = -EINVAL;889break;890}891return ret;892}893894static int tda18271_tune(struct dvb_frontend *fe,895struct tda18271_std_map_item *map, u32 freq, u32 bw)896{897struct tda18271_priv *priv = fe->tuner_priv;898int ret;899900tda_dbg("freq = %d, ifc = %d, bw = %d, agc_mode = %d, std = %d\n",901freq, map->if_freq, bw, map->agc_mode, map->std);902903ret = tda18271_agc(fe);904if (tda_fail(ret))905tda_warn("failed to configure agc\n");906907ret = tda18271_init(fe);908if (tda_fail(ret))909goto fail;910911mutex_lock(&priv->lock);912913switch (priv->id) {914case TDA18271HDC1:915tda18271c1_rf_tracking_filter_calibration(fe, freq, bw);916break;917case TDA18271HDC2:918tda18271c2_rf_tracking_filters_correction(fe, freq);919break;920}921ret = tda18271_channel_configuration(fe, map, freq, bw);922923mutex_unlock(&priv->lock);924fail:925return ret;926}927928/* ------------------------------------------------------------------ */929930static int tda18271_set_params(struct dvb_frontend *fe,931struct dvb_frontend_parameters *params)932{933struct tda18271_priv *priv = fe->tuner_priv;934struct tda18271_std_map *std_map = &priv->std;935struct tda18271_std_map_item *map;936int ret;937u32 bw, freq = params->frequency;938939priv->mode = TDA18271_DIGITAL;940941if (fe->ops.info.type == FE_ATSC) {942switch (params->u.vsb.modulation) {943case VSB_8:944case VSB_16:945map = &std_map->atsc_6;946break;947case QAM_64:948case QAM_256:949map = &std_map->qam_6;950break;951default:952tda_warn("modulation not set!\n");953return -EINVAL;954}955#if 0956/* userspace request is already center adjusted */957freq += 1750000; /* Adjust to center (+1.75MHZ) */958#endif959bw = 6000000;960} else if (fe->ops.info.type == FE_OFDM) {961switch (params->u.ofdm.bandwidth) {962case BANDWIDTH_6_MHZ:963bw = 6000000;964map = &std_map->dvbt_6;965break;966case BANDWIDTH_7_MHZ:967bw = 7000000;968map = &std_map->dvbt_7;969break;970case BANDWIDTH_8_MHZ:971bw = 8000000;972map = &std_map->dvbt_8;973break;974default:975tda_warn("bandwidth not set!\n");976return -EINVAL;977}978} else if (fe->ops.info.type == FE_QAM) {979/* DVB-C */980map = &std_map->qam_8;981bw = 8000000;982} else {983tda_warn("modulation type not supported!\n");984return -EINVAL;985}986987/* When tuning digital, the analog demod must be tri-stated */988if (fe->ops.analog_ops.standby)989fe->ops.analog_ops.standby(fe);990991ret = tda18271_tune(fe, map, freq, bw);992993if (tda_fail(ret))994goto fail;995996priv->frequency = freq;997priv->bandwidth = (fe->ops.info.type == FE_OFDM) ?998params->u.ofdm.bandwidth : 0;999fail:1000return ret;1001}10021003static int tda18271_set_analog_params(struct dvb_frontend *fe,1004struct analog_parameters *params)1005{1006struct tda18271_priv *priv = fe->tuner_priv;1007struct tda18271_std_map *std_map = &priv->std;1008struct tda18271_std_map_item *map;1009char *mode;1010int ret;1011u32 freq = params->frequency * 125 *1012((params->mode == V4L2_TUNER_RADIO) ? 1 : 1000) / 2;10131014priv->mode = TDA18271_ANALOG;10151016if (params->mode == V4L2_TUNER_RADIO) {1017map = &std_map->fm_radio;1018mode = "fm";1019} else if (params->std & V4L2_STD_MN) {1020map = &std_map->atv_mn;1021mode = "MN";1022} else if (params->std & V4L2_STD_B) {1023map = &std_map->atv_b;1024mode = "B";1025} else if (params->std & V4L2_STD_GH) {1026map = &std_map->atv_gh;1027mode = "GH";1028} else if (params->std & V4L2_STD_PAL_I) {1029map = &std_map->atv_i;1030mode = "I";1031} else if (params->std & V4L2_STD_DK) {1032map = &std_map->atv_dk;1033mode = "DK";1034} else if (params->std & V4L2_STD_SECAM_L) {1035map = &std_map->atv_l;1036mode = "L";1037} else if (params->std & V4L2_STD_SECAM_LC) {1038map = &std_map->atv_lc;1039mode = "L'";1040} else {1041map = &std_map->atv_i;1042mode = "xx";1043}10441045tda_dbg("setting tda18271 to system %s\n", mode);10461047ret = tda18271_tune(fe, map, freq, 0);10481049if (tda_fail(ret))1050goto fail;10511052priv->frequency = freq;1053priv->bandwidth = 0;1054fail:1055return ret;1056}10571058static int tda18271_release(struct dvb_frontend *fe)1059{1060struct tda18271_priv *priv = fe->tuner_priv;10611062mutex_lock(&tda18271_list_mutex);10631064if (priv)1065hybrid_tuner_release_state(priv);10661067mutex_unlock(&tda18271_list_mutex);10681069fe->tuner_priv = NULL;10701071return 0;1072}10731074static int tda18271_get_frequency(struct dvb_frontend *fe, u32 *frequency)1075{1076struct tda18271_priv *priv = fe->tuner_priv;1077*frequency = priv->frequency;1078return 0;1079}10801081static int tda18271_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)1082{1083struct tda18271_priv *priv = fe->tuner_priv;1084*bandwidth = priv->bandwidth;1085return 0;1086}10871088/* ------------------------------------------------------------------ */10891090#define tda18271_update_std(std_cfg, name) do { \1091if (map->std_cfg.if_freq + \1092map->std_cfg.agc_mode + map->std_cfg.std + \1093map->std_cfg.if_lvl + map->std_cfg.rfagc_top > 0) { \1094tda_dbg("Using custom std config for %s\n", name); \1095memcpy(&std->std_cfg, &map->std_cfg, \1096sizeof(struct tda18271_std_map_item)); \1097} } while (0)10981099#define tda18271_dump_std_item(std_cfg, name) do { \1100tda_dbg("(%s) if_freq = %d, agc_mode = %d, std = %d, " \1101"if_lvl = %d, rfagc_top = 0x%02x\n", \1102name, std->std_cfg.if_freq, \1103std->std_cfg.agc_mode, std->std_cfg.std, \1104std->std_cfg.if_lvl, std->std_cfg.rfagc_top); \1105} while (0)11061107static int tda18271_dump_std_map(struct dvb_frontend *fe)1108{1109struct tda18271_priv *priv = fe->tuner_priv;1110struct tda18271_std_map *std = &priv->std;11111112tda_dbg("========== STANDARD MAP SETTINGS ==========\n");1113tda18271_dump_std_item(fm_radio, " fm ");1114tda18271_dump_std_item(atv_b, "atv b ");1115tda18271_dump_std_item(atv_dk, "atv dk");1116tda18271_dump_std_item(atv_gh, "atv gh");1117tda18271_dump_std_item(atv_i, "atv i ");1118tda18271_dump_std_item(atv_l, "atv l ");1119tda18271_dump_std_item(atv_lc, "atv l'");1120tda18271_dump_std_item(atv_mn, "atv mn");1121tda18271_dump_std_item(atsc_6, "atsc 6");1122tda18271_dump_std_item(dvbt_6, "dvbt 6");1123tda18271_dump_std_item(dvbt_7, "dvbt 7");1124tda18271_dump_std_item(dvbt_8, "dvbt 8");1125tda18271_dump_std_item(qam_6, "qam 6 ");1126tda18271_dump_std_item(qam_8, "qam 8 ");11271128return 0;1129}11301131static int tda18271_update_std_map(struct dvb_frontend *fe,1132struct tda18271_std_map *map)1133{1134struct tda18271_priv *priv = fe->tuner_priv;1135struct tda18271_std_map *std = &priv->std;11361137if (!map)1138return -EINVAL;11391140tda18271_update_std(fm_radio, "fm");1141tda18271_update_std(atv_b, "atv b");1142tda18271_update_std(atv_dk, "atv dk");1143tda18271_update_std(atv_gh, "atv gh");1144tda18271_update_std(atv_i, "atv i");1145tda18271_update_std(atv_l, "atv l");1146tda18271_update_std(atv_lc, "atv l'");1147tda18271_update_std(atv_mn, "atv mn");1148tda18271_update_std(atsc_6, "atsc 6");1149tda18271_update_std(dvbt_6, "dvbt 6");1150tda18271_update_std(dvbt_7, "dvbt 7");1151tda18271_update_std(dvbt_8, "dvbt 8");1152tda18271_update_std(qam_6, "qam 6");1153tda18271_update_std(qam_8, "qam 8");11541155return 0;1156}11571158static int tda18271_get_id(struct dvb_frontend *fe)1159{1160struct tda18271_priv *priv = fe->tuner_priv;1161unsigned char *regs = priv->tda18271_regs;1162char *name;11631164mutex_lock(&priv->lock);1165tda18271_read_regs(fe);1166mutex_unlock(&priv->lock);11671168switch (regs[R_ID] & 0x7f) {1169case 3:1170name = "TDA18271HD/C1";1171priv->id = TDA18271HDC1;1172break;1173case 4:1174name = "TDA18271HD/C2";1175priv->id = TDA18271HDC2;1176break;1177default:1178tda_info("Unknown device (%i) detected @ %d-%04x, device not supported.\n",1179regs[R_ID], i2c_adapter_id(priv->i2c_props.adap),1180priv->i2c_props.addr);1181return -EINVAL;1182}11831184tda_info("%s detected @ %d-%04x\n", name,1185i2c_adapter_id(priv->i2c_props.adap), priv->i2c_props.addr);11861187return 0;1188}11891190static int tda18271_setup_configuration(struct dvb_frontend *fe,1191struct tda18271_config *cfg)1192{1193struct tda18271_priv *priv = fe->tuner_priv;11941195priv->gate = (cfg) ? cfg->gate : TDA18271_GATE_AUTO;1196priv->role = (cfg) ? cfg->role : TDA18271_MASTER;1197priv->config = (cfg) ? cfg->config : 0;1198priv->small_i2c = (cfg) ?1199cfg->small_i2c : TDA18271_39_BYTE_CHUNK_INIT;1200priv->output_opt = (cfg) ?1201cfg->output_opt : TDA18271_OUTPUT_LT_XT_ON;12021203return 0;1204}12051206static inline int tda18271_need_cal_on_startup(struct tda18271_config *cfg)1207{1208/* tda18271_cal_on_startup == -1 when cal module option is unset */1209return ((tda18271_cal_on_startup == -1) ?1210/* honor configuration setting */1211((cfg) && (cfg->rf_cal_on_startup)) :1212/* module option overrides configuration setting */1213(tda18271_cal_on_startup)) ? 1 : 0;1214}12151216static int tda18271_set_config(struct dvb_frontend *fe, void *priv_cfg)1217{1218struct tda18271_config *cfg = (struct tda18271_config *) priv_cfg;12191220tda18271_setup_configuration(fe, cfg);12211222if (tda18271_need_cal_on_startup(cfg))1223tda18271_init(fe);12241225/* override default std map with values in config struct */1226if ((cfg) && (cfg->std_map))1227tda18271_update_std_map(fe, cfg->std_map);12281229return 0;1230}12311232static struct dvb_tuner_ops tda18271_tuner_ops = {1233.info = {1234.name = "NXP TDA18271HD",1235.frequency_min = 45000000,1236.frequency_max = 864000000,1237.frequency_step = 625001238},1239.init = tda18271_init,1240.sleep = tda18271_sleep,1241.set_params = tda18271_set_params,1242.set_analog_params = tda18271_set_analog_params,1243.release = tda18271_release,1244.set_config = tda18271_set_config,1245.get_frequency = tda18271_get_frequency,1246.get_bandwidth = tda18271_get_bandwidth,1247};12481249struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr,1250struct i2c_adapter *i2c,1251struct tda18271_config *cfg)1252{1253struct tda18271_priv *priv = NULL;1254int instance, ret;12551256mutex_lock(&tda18271_list_mutex);12571258instance = hybrid_tuner_request_state(struct tda18271_priv, priv,1259hybrid_tuner_instance_list,1260i2c, addr, "tda18271");1261switch (instance) {1262case 0:1263goto fail;1264case 1:1265/* new tuner instance */1266fe->tuner_priv = priv;12671268tda18271_setup_configuration(fe, cfg);12691270priv->cal_initialized = false;1271mutex_init(&priv->lock);12721273ret = tda18271_get_id(fe);1274if (tda_fail(ret))1275goto fail;12761277ret = tda18271_assign_map_layout(fe);1278if (tda_fail(ret))1279goto fail;12801281mutex_lock(&priv->lock);1282tda18271_init_regs(fe);12831284if ((tda18271_need_cal_on_startup(cfg)) &&1285(priv->id == TDA18271HDC2))1286tda18271c2_rf_cal_init(fe);12871288mutex_unlock(&priv->lock);1289break;1290default:1291/* existing tuner instance */1292fe->tuner_priv = priv;12931294/* allow dvb driver to override configuration settings */1295if (cfg) {1296if (cfg->gate != TDA18271_GATE_ANALOG)1297priv->gate = cfg->gate;1298if (cfg->role)1299priv->role = cfg->role;1300if (cfg->config)1301priv->config = cfg->config;1302if (cfg->small_i2c)1303priv->small_i2c = cfg->small_i2c;1304if (cfg->output_opt)1305priv->output_opt = cfg->output_opt;1306if (cfg->std_map)1307tda18271_update_std_map(fe, cfg->std_map);1308}1309if (tda18271_need_cal_on_startup(cfg))1310tda18271_init(fe);1311break;1312}13131314/* override default std map with values in config struct */1315if ((cfg) && (cfg->std_map))1316tda18271_update_std_map(fe, cfg->std_map);13171318mutex_unlock(&tda18271_list_mutex);13191320memcpy(&fe->ops.tuner_ops, &tda18271_tuner_ops,1321sizeof(struct dvb_tuner_ops));13221323if (tda18271_debug & (DBG_MAP | DBG_ADV))1324tda18271_dump_std_map(fe);13251326return fe;1327fail:1328mutex_unlock(&tda18271_list_mutex);13291330tda18271_release(fe);1331return NULL;1332}1333EXPORT_SYMBOL_GPL(tda18271_attach);1334MODULE_DESCRIPTION("NXP TDA18271HD analog / digital tuner driver");1335MODULE_AUTHOR("Michael Krufky <[email protected]>");1336MODULE_LICENSE("GPL");1337MODULE_VERSION("0.4");13381339/*1340* Overrides for Emacs so that we follow Linus's tabbing style.1341* ---------------------------------------------------------------------------1342* Local variables:1343* c-basic-offset: 81344* End:1345*/134613471348