162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci    tda18271-fe.c - driver for the Philips / NXP TDA18271 silicon tuner
462306a36Sopenharmony_ci
562306a36Sopenharmony_ci    Copyright (C) 2007, 2008 Michael Krufky <mkrufky@linuxtv.org>
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci*/
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include "tda18271-priv.h"
1062306a36Sopenharmony_ci#include "tda8290.h"
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/delay.h>
1362306a36Sopenharmony_ci#include <linux/videodev2.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ciint tda18271_debug;
1662306a36Sopenharmony_cimodule_param_named(debug, tda18271_debug, int, 0644);
1762306a36Sopenharmony_ciMODULE_PARM_DESC(debug, "set debug level (info=1, map=2, reg=4, adv=8, cal=16 (or-able))");
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cistatic int tda18271_cal_on_startup = -1;
2062306a36Sopenharmony_cimodule_param_named(cal, tda18271_cal_on_startup, int, 0644);
2162306a36Sopenharmony_ciMODULE_PARM_DESC(cal, "perform RF tracking filter calibration on startup");
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistatic DEFINE_MUTEX(tda18271_list_mutex);
2462306a36Sopenharmony_cistatic LIST_HEAD(hybrid_tuner_instance_list);
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci/*---------------------------------------------------------------------*/
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistatic int tda18271_toggle_output(struct dvb_frontend *fe, int standby)
2962306a36Sopenharmony_ci{
3062306a36Sopenharmony_ci	struct tda18271_priv *priv = fe->tuner_priv;
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	int ret = tda18271_set_standby_mode(fe, standby ? 1 : 0,
3362306a36Sopenharmony_ci			priv->output_opt & TDA18271_OUTPUT_LT_OFF ? 1 : 0,
3462306a36Sopenharmony_ci			priv->output_opt & TDA18271_OUTPUT_XT_OFF ? 1 : 0);
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	if (tda_fail(ret))
3762306a36Sopenharmony_ci		goto fail;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	tda_dbg("%s mode: xtal oscillator %s, slave tuner loop through %s\n",
4062306a36Sopenharmony_ci		standby ? "standby" : "active",
4162306a36Sopenharmony_ci		priv->output_opt & TDA18271_OUTPUT_XT_OFF ? "off" : "on",
4262306a36Sopenharmony_ci		priv->output_opt & TDA18271_OUTPUT_LT_OFF ? "off" : "on");
4362306a36Sopenharmony_cifail:
4462306a36Sopenharmony_ci	return ret;
4562306a36Sopenharmony_ci}
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci/*---------------------------------------------------------------------*/
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistatic inline int charge_pump_source(struct dvb_frontend *fe, int force)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	struct tda18271_priv *priv = fe->tuner_priv;
5262306a36Sopenharmony_ci	return tda18271_charge_pump_source(fe,
5362306a36Sopenharmony_ci					   (priv->role == TDA18271_SLAVE) ?
5462306a36Sopenharmony_ci					   TDA18271_CAL_PLL :
5562306a36Sopenharmony_ci					   TDA18271_MAIN_PLL, force);
5662306a36Sopenharmony_ci}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cistatic inline void tda18271_set_if_notch(struct dvb_frontend *fe)
5962306a36Sopenharmony_ci{
6062306a36Sopenharmony_ci	struct tda18271_priv *priv = fe->tuner_priv;
6162306a36Sopenharmony_ci	unsigned char *regs = priv->tda18271_regs;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	switch (priv->mode) {
6462306a36Sopenharmony_ci	case TDA18271_ANALOG:
6562306a36Sopenharmony_ci		regs[R_MPD]  &= ~0x80; /* IF notch = 0 */
6662306a36Sopenharmony_ci		break;
6762306a36Sopenharmony_ci	case TDA18271_DIGITAL:
6862306a36Sopenharmony_ci		regs[R_MPD]  |= 0x80; /* IF notch = 1 */
6962306a36Sopenharmony_ci		break;
7062306a36Sopenharmony_ci	}
7162306a36Sopenharmony_ci}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistatic int tda18271_channel_configuration(struct dvb_frontend *fe,
7462306a36Sopenharmony_ci					  struct tda18271_std_map_item *map,
7562306a36Sopenharmony_ci					  u32 freq, u32 bw)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	struct tda18271_priv *priv = fe->tuner_priv;
7862306a36Sopenharmony_ci	unsigned char *regs = priv->tda18271_regs;
7962306a36Sopenharmony_ci	int ret;
8062306a36Sopenharmony_ci	u32 N;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	/* update TV broadcast parameters */
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	/* set standard */
8562306a36Sopenharmony_ci	regs[R_EP3]  &= ~0x1f; /* clear std bits */
8662306a36Sopenharmony_ci	regs[R_EP3]  |= (map->agc_mode << 3) | map->std;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	if (priv->id == TDA18271HDC2) {
8962306a36Sopenharmony_ci		/* set rfagc to high speed mode */
9062306a36Sopenharmony_ci		regs[R_EP3] &= ~0x04;
9162306a36Sopenharmony_ci	}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	/* set cal mode to normal */
9462306a36Sopenharmony_ci	regs[R_EP4]  &= ~0x03;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	/* update IF output level */
9762306a36Sopenharmony_ci	regs[R_EP4]  &= ~0x1c; /* clear if level bits */
9862306a36Sopenharmony_ci	regs[R_EP4]  |= (map->if_lvl << 2);
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	/* update FM_RFn */
10162306a36Sopenharmony_ci	regs[R_EP4]  &= ~0x80;
10262306a36Sopenharmony_ci	regs[R_EP4]  |= map->fm_rfn << 7;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	/* update rf top / if top */
10562306a36Sopenharmony_ci	regs[R_EB22]  = 0x00;
10662306a36Sopenharmony_ci	regs[R_EB22] |= map->rfagc_top;
10762306a36Sopenharmony_ci	ret = tda18271_write_regs(fe, R_EB22, 1);
10862306a36Sopenharmony_ci	if (tda_fail(ret))
10962306a36Sopenharmony_ci		goto fail;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	/* --------------------------------------------------------------- */
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	/* disable Power Level Indicator */
11462306a36Sopenharmony_ci	regs[R_EP1]  |= 0x40;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	/* make sure thermometer is off */
11762306a36Sopenharmony_ci	regs[R_TM]   &= ~0x10;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	/* frequency dependent parameters */
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	tda18271_calc_ir_measure(fe, &freq);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	tda18271_calc_bp_filter(fe, &freq);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	tda18271_calc_rf_band(fe, &freq);
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	tda18271_calc_gain_taper(fe, &freq);
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	/* --------------------------------------------------------------- */
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	/* dual tuner and agc1 extra configuration */
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	switch (priv->role) {
13462306a36Sopenharmony_ci	case TDA18271_MASTER:
13562306a36Sopenharmony_ci		regs[R_EB1]  |= 0x04; /* main vco */
13662306a36Sopenharmony_ci		break;
13762306a36Sopenharmony_ci	case TDA18271_SLAVE:
13862306a36Sopenharmony_ci		regs[R_EB1]  &= ~0x04; /* cal vco */
13962306a36Sopenharmony_ci		break;
14062306a36Sopenharmony_ci	}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	/* agc1 always active */
14362306a36Sopenharmony_ci	regs[R_EB1]  &= ~0x02;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	/* agc1 has priority on agc2 */
14662306a36Sopenharmony_ci	regs[R_EB1]  &= ~0x01;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	ret = tda18271_write_regs(fe, R_EB1, 1);
14962306a36Sopenharmony_ci	if (tda_fail(ret))
15062306a36Sopenharmony_ci		goto fail;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	/* --------------------------------------------------------------- */
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	N = map->if_freq * 1000 + freq;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	switch (priv->role) {
15762306a36Sopenharmony_ci	case TDA18271_MASTER:
15862306a36Sopenharmony_ci		tda18271_calc_main_pll(fe, N);
15962306a36Sopenharmony_ci		tda18271_set_if_notch(fe);
16062306a36Sopenharmony_ci		tda18271_write_regs(fe, R_MPD, 4);
16162306a36Sopenharmony_ci		break;
16262306a36Sopenharmony_ci	case TDA18271_SLAVE:
16362306a36Sopenharmony_ci		tda18271_calc_cal_pll(fe, N);
16462306a36Sopenharmony_ci		tda18271_write_regs(fe, R_CPD, 4);
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci		regs[R_MPD] = regs[R_CPD] & 0x7f;
16762306a36Sopenharmony_ci		tda18271_set_if_notch(fe);
16862306a36Sopenharmony_ci		tda18271_write_regs(fe, R_MPD, 1);
16962306a36Sopenharmony_ci		break;
17062306a36Sopenharmony_ci	}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	ret = tda18271_write_regs(fe, R_TM, 7);
17362306a36Sopenharmony_ci	if (tda_fail(ret))
17462306a36Sopenharmony_ci		goto fail;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	/* force charge pump source */
17762306a36Sopenharmony_ci	charge_pump_source(fe, 1);
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	msleep(1);
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	/* return pll to normal operation */
18262306a36Sopenharmony_ci	charge_pump_source(fe, 0);
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	msleep(20);
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	if (priv->id == TDA18271HDC2) {
18762306a36Sopenharmony_ci		/* set rfagc to normal speed mode */
18862306a36Sopenharmony_ci		if (map->fm_rfn)
18962306a36Sopenharmony_ci			regs[R_EP3] &= ~0x04;
19062306a36Sopenharmony_ci		else
19162306a36Sopenharmony_ci			regs[R_EP3] |= 0x04;
19262306a36Sopenharmony_ci		ret = tda18271_write_regs(fe, R_EP3, 1);
19362306a36Sopenharmony_ci	}
19462306a36Sopenharmony_cifail:
19562306a36Sopenharmony_ci	return ret;
19662306a36Sopenharmony_ci}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_cistatic int tda18271_read_thermometer(struct dvb_frontend *fe)
19962306a36Sopenharmony_ci{
20062306a36Sopenharmony_ci	struct tda18271_priv *priv = fe->tuner_priv;
20162306a36Sopenharmony_ci	unsigned char *regs = priv->tda18271_regs;
20262306a36Sopenharmony_ci	int tm;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	/* switch thermometer on */
20562306a36Sopenharmony_ci	regs[R_TM]   |= 0x10;
20662306a36Sopenharmony_ci	tda18271_write_regs(fe, R_TM, 1);
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	/* read thermometer info */
20962306a36Sopenharmony_ci	tda18271_read_regs(fe);
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	if ((((regs[R_TM] & 0x0f) == 0x00) && ((regs[R_TM] & 0x20) == 0x20)) ||
21262306a36Sopenharmony_ci	    (((regs[R_TM] & 0x0f) == 0x08) && ((regs[R_TM] & 0x20) == 0x00))) {
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci		if ((regs[R_TM] & 0x20) == 0x20)
21562306a36Sopenharmony_ci			regs[R_TM] &= ~0x20;
21662306a36Sopenharmony_ci		else
21762306a36Sopenharmony_ci			regs[R_TM] |= 0x20;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci		tda18271_write_regs(fe, R_TM, 1);
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci		msleep(10); /* temperature sensing */
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci		/* read thermometer info */
22462306a36Sopenharmony_ci		tda18271_read_regs(fe);
22562306a36Sopenharmony_ci	}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	tm = tda18271_lookup_thermometer(fe);
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	/* switch thermometer off */
23062306a36Sopenharmony_ci	regs[R_TM]   &= ~0x10;
23162306a36Sopenharmony_ci	tda18271_write_regs(fe, R_TM, 1);
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	/* set CAL mode to normal */
23462306a36Sopenharmony_ci	regs[R_EP4]  &= ~0x03;
23562306a36Sopenharmony_ci	tda18271_write_regs(fe, R_EP4, 1);
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	return tm;
23862306a36Sopenharmony_ci}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci/* ------------------------------------------------------------------ */
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_cistatic int tda18271c2_rf_tracking_filters_correction(struct dvb_frontend *fe,
24362306a36Sopenharmony_ci						     u32 freq)
24462306a36Sopenharmony_ci{
24562306a36Sopenharmony_ci	struct tda18271_priv *priv = fe->tuner_priv;
24662306a36Sopenharmony_ci	struct tda18271_rf_tracking_filter_cal *map = priv->rf_cal_state;
24762306a36Sopenharmony_ci	unsigned char *regs = priv->tda18271_regs;
24862306a36Sopenharmony_ci	int i, ret;
24962306a36Sopenharmony_ci	u8 tm_current, dc_over_dt, rf_tab;
25062306a36Sopenharmony_ci	s32 rfcal_comp, approx;
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	/* power up */
25362306a36Sopenharmony_ci	ret = tda18271_set_standby_mode(fe, 0, 0, 0);
25462306a36Sopenharmony_ci	if (tda_fail(ret))
25562306a36Sopenharmony_ci		goto fail;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	/* read die current temperature */
25862306a36Sopenharmony_ci	tm_current = tda18271_read_thermometer(fe);
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	/* frequency dependent parameters */
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	tda18271_calc_rf_cal(fe, &freq);
26362306a36Sopenharmony_ci	rf_tab = regs[R_EB14];
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	i = tda18271_lookup_rf_band(fe, &freq, NULL);
26662306a36Sopenharmony_ci	if (tda_fail(i))
26762306a36Sopenharmony_ci		return i;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	if ((0 == map[i].rf3) || (freq / 1000 < map[i].rf2)) {
27062306a36Sopenharmony_ci		approx = map[i].rf_a1 * (s32)(freq / 1000 - map[i].rf1) +
27162306a36Sopenharmony_ci			map[i].rf_b1 + rf_tab;
27262306a36Sopenharmony_ci	} else {
27362306a36Sopenharmony_ci		approx = map[i].rf_a2 * (s32)(freq / 1000 - map[i].rf2) +
27462306a36Sopenharmony_ci			map[i].rf_b2 + rf_tab;
27562306a36Sopenharmony_ci	}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	if (approx < 0)
27862306a36Sopenharmony_ci		approx = 0;
27962306a36Sopenharmony_ci	if (approx > 255)
28062306a36Sopenharmony_ci		approx = 255;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	tda18271_lookup_map(fe, RF_CAL_DC_OVER_DT, &freq, &dc_over_dt);
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	/* calculate temperature compensation */
28562306a36Sopenharmony_ci	rfcal_comp = dc_over_dt * (s32)(tm_current - priv->tm_rfcal) / 1000;
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	regs[R_EB14] = (unsigned char)(approx + rfcal_comp);
28862306a36Sopenharmony_ci	ret = tda18271_write_regs(fe, R_EB14, 1);
28962306a36Sopenharmony_cifail:
29062306a36Sopenharmony_ci	return ret;
29162306a36Sopenharmony_ci}
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_cistatic int tda18271_por(struct dvb_frontend *fe)
29462306a36Sopenharmony_ci{
29562306a36Sopenharmony_ci	struct tda18271_priv *priv = fe->tuner_priv;
29662306a36Sopenharmony_ci	unsigned char *regs = priv->tda18271_regs;
29762306a36Sopenharmony_ci	int ret;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	/* power up detector 1 */
30062306a36Sopenharmony_ci	regs[R_EB12] &= ~0x20;
30162306a36Sopenharmony_ci	ret = tda18271_write_regs(fe, R_EB12, 1);
30262306a36Sopenharmony_ci	if (tda_fail(ret))
30362306a36Sopenharmony_ci		goto fail;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	regs[R_EB18] &= ~0x80; /* turn agc1 loop on */
30662306a36Sopenharmony_ci	regs[R_EB18] &= ~0x03; /* set agc1_gain to  6 dB */
30762306a36Sopenharmony_ci	ret = tda18271_write_regs(fe, R_EB18, 1);
30862306a36Sopenharmony_ci	if (tda_fail(ret))
30962306a36Sopenharmony_ci		goto fail;
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	regs[R_EB21] |= 0x03; /* set agc2_gain to -6 dB */
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	/* POR mode */
31462306a36Sopenharmony_ci	ret = tda18271_set_standby_mode(fe, 1, 0, 0);
31562306a36Sopenharmony_ci	if (tda_fail(ret))
31662306a36Sopenharmony_ci		goto fail;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	/* disable 1.5 MHz low pass filter */
31962306a36Sopenharmony_ci	regs[R_EB23] &= ~0x04; /* forcelp_fc2_en = 0 */
32062306a36Sopenharmony_ci	regs[R_EB23] &= ~0x02; /* XXX: lp_fc[2] = 0 */
32162306a36Sopenharmony_ci	ret = tda18271_write_regs(fe, R_EB21, 3);
32262306a36Sopenharmony_cifail:
32362306a36Sopenharmony_ci	return ret;
32462306a36Sopenharmony_ci}
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_cistatic int tda18271_calibrate_rf(struct dvb_frontend *fe, u32 freq)
32762306a36Sopenharmony_ci{
32862306a36Sopenharmony_ci	struct tda18271_priv *priv = fe->tuner_priv;
32962306a36Sopenharmony_ci	unsigned char *regs = priv->tda18271_regs;
33062306a36Sopenharmony_ci	u32 N;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	/* set CAL mode to normal */
33362306a36Sopenharmony_ci	regs[R_EP4]  &= ~0x03;
33462306a36Sopenharmony_ci	tda18271_write_regs(fe, R_EP4, 1);
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	/* switch off agc1 */
33762306a36Sopenharmony_ci	regs[R_EP3]  |= 0x40; /* sm_lt = 1 */
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	regs[R_EB18] |= 0x03; /* set agc1_gain to 15 dB */
34062306a36Sopenharmony_ci	tda18271_write_regs(fe, R_EB18, 1);
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	/* frequency dependent parameters */
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	tda18271_calc_bp_filter(fe, &freq);
34562306a36Sopenharmony_ci	tda18271_calc_gain_taper(fe, &freq);
34662306a36Sopenharmony_ci	tda18271_calc_rf_band(fe, &freq);
34762306a36Sopenharmony_ci	tda18271_calc_km(fe, &freq);
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	tda18271_write_regs(fe, R_EP1, 3);
35062306a36Sopenharmony_ci	tda18271_write_regs(fe, R_EB13, 1);
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	/* main pll charge pump source */
35362306a36Sopenharmony_ci	tda18271_charge_pump_source(fe, TDA18271_MAIN_PLL, 1);
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	/* cal pll charge pump source */
35662306a36Sopenharmony_ci	tda18271_charge_pump_source(fe, TDA18271_CAL_PLL, 1);
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	/* force dcdc converter to 0 V */
35962306a36Sopenharmony_ci	regs[R_EB14] = 0x00;
36062306a36Sopenharmony_ci	tda18271_write_regs(fe, R_EB14, 1);
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	/* disable plls lock */
36362306a36Sopenharmony_ci	regs[R_EB20] &= ~0x20;
36462306a36Sopenharmony_ci	tda18271_write_regs(fe, R_EB20, 1);
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	/* set CAL mode to RF tracking filter calibration */
36762306a36Sopenharmony_ci	regs[R_EP4]  |= 0x03;
36862306a36Sopenharmony_ci	tda18271_write_regs(fe, R_EP4, 2);
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	/* --------------------------------------------------------------- */
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	/* set the internal calibration signal */
37362306a36Sopenharmony_ci	N = freq;
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	tda18271_calc_cal_pll(fe, N);
37662306a36Sopenharmony_ci	tda18271_write_regs(fe, R_CPD, 4);
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	/* downconvert internal calibration */
37962306a36Sopenharmony_ci	N += 1000000;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	tda18271_calc_main_pll(fe, N);
38262306a36Sopenharmony_ci	tda18271_write_regs(fe, R_MPD, 4);
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	msleep(5);
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	tda18271_write_regs(fe, R_EP2, 1);
38762306a36Sopenharmony_ci	tda18271_write_regs(fe, R_EP1, 1);
38862306a36Sopenharmony_ci	tda18271_write_regs(fe, R_EP2, 1);
38962306a36Sopenharmony_ci	tda18271_write_regs(fe, R_EP1, 1);
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	/* --------------------------------------------------------------- */
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	/* normal operation for the main pll */
39462306a36Sopenharmony_ci	tda18271_charge_pump_source(fe, TDA18271_MAIN_PLL, 0);
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	/* normal operation for the cal pll  */
39762306a36Sopenharmony_ci	tda18271_charge_pump_source(fe, TDA18271_CAL_PLL, 0);
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	msleep(10); /* plls locking */
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	/* launch the rf tracking filters calibration */
40262306a36Sopenharmony_ci	regs[R_EB20]  |= 0x20;
40362306a36Sopenharmony_ci	tda18271_write_regs(fe, R_EB20, 1);
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	msleep(60); /* calibration */
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	/* --------------------------------------------------------------- */
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	/* set CAL mode to normal */
41062306a36Sopenharmony_ci	regs[R_EP4]  &= ~0x03;
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	/* switch on agc1 */
41362306a36Sopenharmony_ci	regs[R_EP3]  &= ~0x40; /* sm_lt = 0 */
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	regs[R_EB18] &= ~0x03; /* set agc1_gain to  6 dB */
41662306a36Sopenharmony_ci	tda18271_write_regs(fe, R_EB18, 1);
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	tda18271_write_regs(fe, R_EP3, 2);
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	/* synchronization */
42162306a36Sopenharmony_ci	tda18271_write_regs(fe, R_EP1, 1);
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	/* get calibration result */
42462306a36Sopenharmony_ci	tda18271_read_extended(fe);
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	return regs[R_EB14];
42762306a36Sopenharmony_ci}
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_cistatic int tda18271_powerscan(struct dvb_frontend *fe,
43062306a36Sopenharmony_ci			      u32 *freq_in, u32 *freq_out)
43162306a36Sopenharmony_ci{
43262306a36Sopenharmony_ci	struct tda18271_priv *priv = fe->tuner_priv;
43362306a36Sopenharmony_ci	unsigned char *regs = priv->tda18271_regs;
43462306a36Sopenharmony_ci	int sgn, bcal, count, wait, ret;
43562306a36Sopenharmony_ci	u8 cid_target;
43662306a36Sopenharmony_ci	u16 count_limit;
43762306a36Sopenharmony_ci	u32 freq;
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	freq = *freq_in;
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	tda18271_calc_rf_band(fe, &freq);
44262306a36Sopenharmony_ci	tda18271_calc_rf_cal(fe, &freq);
44362306a36Sopenharmony_ci	tda18271_calc_gain_taper(fe, &freq);
44462306a36Sopenharmony_ci	tda18271_lookup_cid_target(fe, &freq, &cid_target, &count_limit);
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	tda18271_write_regs(fe, R_EP2, 1);
44762306a36Sopenharmony_ci	tda18271_write_regs(fe, R_EB14, 1);
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	/* downconvert frequency */
45062306a36Sopenharmony_ci	freq += 1000000;
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	tda18271_calc_main_pll(fe, freq);
45362306a36Sopenharmony_ci	tda18271_write_regs(fe, R_MPD, 4);
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	msleep(5); /* pll locking */
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	/* detection mode */
45862306a36Sopenharmony_ci	regs[R_EP4]  &= ~0x03;
45962306a36Sopenharmony_ci	regs[R_EP4]  |= 0x01;
46062306a36Sopenharmony_ci	tda18271_write_regs(fe, R_EP4, 1);
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	/* launch power detection measurement */
46362306a36Sopenharmony_ci	tda18271_write_regs(fe, R_EP2, 1);
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	/* read power detection info, stored in EB10 */
46662306a36Sopenharmony_ci	ret = tda18271_read_extended(fe);
46762306a36Sopenharmony_ci	if (tda_fail(ret))
46862306a36Sopenharmony_ci		return ret;
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	/* algorithm initialization */
47162306a36Sopenharmony_ci	sgn = 1;
47262306a36Sopenharmony_ci	*freq_out = *freq_in;
47362306a36Sopenharmony_ci	bcal = 0;
47462306a36Sopenharmony_ci	count = 0;
47562306a36Sopenharmony_ci	wait = false;
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	while ((regs[R_EB10] & 0x3f) < cid_target) {
47862306a36Sopenharmony_ci		/* downconvert updated freq to 1 MHz */
47962306a36Sopenharmony_ci		freq = *freq_in + (sgn * count) + 1000000;
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci		tda18271_calc_main_pll(fe, freq);
48262306a36Sopenharmony_ci		tda18271_write_regs(fe, R_MPD, 4);
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci		if (wait) {
48562306a36Sopenharmony_ci			msleep(5); /* pll locking */
48662306a36Sopenharmony_ci			wait = false;
48762306a36Sopenharmony_ci		} else
48862306a36Sopenharmony_ci			udelay(100); /* pll locking */
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci		/* launch power detection measurement */
49162306a36Sopenharmony_ci		tda18271_write_regs(fe, R_EP2, 1);
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci		/* read power detection info, stored in EB10 */
49462306a36Sopenharmony_ci		ret = tda18271_read_extended(fe);
49562306a36Sopenharmony_ci		if (tda_fail(ret))
49662306a36Sopenharmony_ci			return ret;
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci		count += 200;
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci		if (count <= count_limit)
50162306a36Sopenharmony_ci			continue;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci		if (sgn <= 0)
50462306a36Sopenharmony_ci			break;
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci		sgn = -1 * sgn;
50762306a36Sopenharmony_ci		count = 200;
50862306a36Sopenharmony_ci		wait = true;
50962306a36Sopenharmony_ci	}
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	if ((regs[R_EB10] & 0x3f) >= cid_target) {
51262306a36Sopenharmony_ci		bcal = 1;
51362306a36Sopenharmony_ci		*freq_out = freq - 1000000;
51462306a36Sopenharmony_ci	} else
51562306a36Sopenharmony_ci		bcal = 0;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	tda_cal("bcal = %d, freq_in = %d, freq_out = %d (freq = %d)\n",
51862306a36Sopenharmony_ci		bcal, *freq_in, *freq_out, freq);
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	return bcal;
52162306a36Sopenharmony_ci}
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_cistatic int tda18271_powerscan_init(struct dvb_frontend *fe)
52462306a36Sopenharmony_ci{
52562306a36Sopenharmony_ci	struct tda18271_priv *priv = fe->tuner_priv;
52662306a36Sopenharmony_ci	unsigned char *regs = priv->tda18271_regs;
52762306a36Sopenharmony_ci	int ret;
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	/* set standard to digital */
53062306a36Sopenharmony_ci	regs[R_EP3]  &= ~0x1f; /* clear std bits */
53162306a36Sopenharmony_ci	regs[R_EP3]  |= 0x12;
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	/* set cal mode to normal */
53462306a36Sopenharmony_ci	regs[R_EP4]  &= ~0x03;
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	/* update IF output level */
53762306a36Sopenharmony_ci	regs[R_EP4]  &= ~0x1c; /* clear if level bits */
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	ret = tda18271_write_regs(fe, R_EP3, 2);
54062306a36Sopenharmony_ci	if (tda_fail(ret))
54162306a36Sopenharmony_ci		goto fail;
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	regs[R_EB18] &= ~0x03; /* set agc1_gain to   6 dB */
54462306a36Sopenharmony_ci	ret = tda18271_write_regs(fe, R_EB18, 1);
54562306a36Sopenharmony_ci	if (tda_fail(ret))
54662306a36Sopenharmony_ci		goto fail;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	regs[R_EB21] &= ~0x03; /* set agc2_gain to -15 dB */
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	/* 1.5 MHz low pass filter */
55162306a36Sopenharmony_ci	regs[R_EB23] |= 0x04; /* forcelp_fc2_en = 1 */
55262306a36Sopenharmony_ci	regs[R_EB23] |= 0x02; /* lp_fc[2] = 1 */
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	ret = tda18271_write_regs(fe, R_EB21, 3);
55562306a36Sopenharmony_cifail:
55662306a36Sopenharmony_ci	return ret;
55762306a36Sopenharmony_ci}
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_cistatic int tda18271_rf_tracking_filters_init(struct dvb_frontend *fe, u32 freq)
56062306a36Sopenharmony_ci{
56162306a36Sopenharmony_ci	struct tda18271_priv *priv = fe->tuner_priv;
56262306a36Sopenharmony_ci	struct tda18271_rf_tracking_filter_cal *map = priv->rf_cal_state;
56362306a36Sopenharmony_ci	unsigned char *regs = priv->tda18271_regs;
56462306a36Sopenharmony_ci	int bcal, rf, i;
56562306a36Sopenharmony_ci	s32 divisor, dividend;
56662306a36Sopenharmony_ci#define RF1 0
56762306a36Sopenharmony_ci#define RF2 1
56862306a36Sopenharmony_ci#define RF3 2
56962306a36Sopenharmony_ci	u32 rf_default[3];
57062306a36Sopenharmony_ci	u32 rf_freq[3];
57162306a36Sopenharmony_ci	s32 prog_cal[3];
57262306a36Sopenharmony_ci	s32 prog_tab[3];
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	i = tda18271_lookup_rf_band(fe, &freq, NULL);
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	if (tda_fail(i))
57762306a36Sopenharmony_ci		return i;
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	rf_default[RF1] = 1000 * map[i].rf1_def;
58062306a36Sopenharmony_ci	rf_default[RF2] = 1000 * map[i].rf2_def;
58162306a36Sopenharmony_ci	rf_default[RF3] = 1000 * map[i].rf3_def;
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	for (rf = RF1; rf <= RF3; rf++) {
58462306a36Sopenharmony_ci		if (0 == rf_default[rf])
58562306a36Sopenharmony_ci			return 0;
58662306a36Sopenharmony_ci		tda_cal("freq = %d, rf = %d\n", freq, rf);
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci		/* look for optimized calibration frequency */
58962306a36Sopenharmony_ci		bcal = tda18271_powerscan(fe, &rf_default[rf], &rf_freq[rf]);
59062306a36Sopenharmony_ci		if (tda_fail(bcal))
59162306a36Sopenharmony_ci			return bcal;
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci		tda18271_calc_rf_cal(fe, &rf_freq[rf]);
59462306a36Sopenharmony_ci		prog_tab[rf] = (s32)regs[R_EB14];
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci		if (1 == bcal)
59762306a36Sopenharmony_ci			prog_cal[rf] =
59862306a36Sopenharmony_ci				(s32)tda18271_calibrate_rf(fe, rf_freq[rf]);
59962306a36Sopenharmony_ci		else
60062306a36Sopenharmony_ci			prog_cal[rf] = prog_tab[rf];
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci		switch (rf) {
60362306a36Sopenharmony_ci		case RF1:
60462306a36Sopenharmony_ci			map[i].rf_a1 = 0;
60562306a36Sopenharmony_ci			map[i].rf_b1 = (prog_cal[RF1] - prog_tab[RF1]);
60662306a36Sopenharmony_ci			map[i].rf1   = rf_freq[RF1] / 1000;
60762306a36Sopenharmony_ci			break;
60862306a36Sopenharmony_ci		case RF2:
60962306a36Sopenharmony_ci			dividend = (prog_cal[RF2] - prog_tab[RF2] -
61062306a36Sopenharmony_ci				    prog_cal[RF1] + prog_tab[RF1]);
61162306a36Sopenharmony_ci			divisor = (s32)(rf_freq[RF2] - rf_freq[RF1]) / 1000;
61262306a36Sopenharmony_ci			map[i].rf_a1 = (dividend / divisor);
61362306a36Sopenharmony_ci			map[i].rf2   = rf_freq[RF2] / 1000;
61462306a36Sopenharmony_ci			break;
61562306a36Sopenharmony_ci		case RF3:
61662306a36Sopenharmony_ci			dividend = (prog_cal[RF3] - prog_tab[RF3] -
61762306a36Sopenharmony_ci				    prog_cal[RF2] + prog_tab[RF2]);
61862306a36Sopenharmony_ci			divisor = (s32)(rf_freq[RF3] - rf_freq[RF2]) / 1000;
61962306a36Sopenharmony_ci			map[i].rf_a2 = (dividend / divisor);
62062306a36Sopenharmony_ci			map[i].rf_b2 = (prog_cal[RF2] - prog_tab[RF2]);
62162306a36Sopenharmony_ci			map[i].rf3   = rf_freq[RF3] / 1000;
62262306a36Sopenharmony_ci			break;
62362306a36Sopenharmony_ci		default:
62462306a36Sopenharmony_ci			BUG();
62562306a36Sopenharmony_ci		}
62662306a36Sopenharmony_ci	}
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	return 0;
62962306a36Sopenharmony_ci}
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_cistatic int tda18271_calc_rf_filter_curve(struct dvb_frontend *fe)
63262306a36Sopenharmony_ci{
63362306a36Sopenharmony_ci	struct tda18271_priv *priv = fe->tuner_priv;
63462306a36Sopenharmony_ci	unsigned int i;
63562306a36Sopenharmony_ci	int ret;
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci	tda_info("performing RF tracking filter calibration\n");
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	/* wait for die temperature stabilization */
64062306a36Sopenharmony_ci	msleep(200);
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	ret = tda18271_powerscan_init(fe);
64362306a36Sopenharmony_ci	if (tda_fail(ret))
64462306a36Sopenharmony_ci		goto fail;
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci	/* rf band calibration */
64762306a36Sopenharmony_ci	for (i = 0; priv->rf_cal_state[i].rfmax != 0; i++) {
64862306a36Sopenharmony_ci		ret =
64962306a36Sopenharmony_ci		tda18271_rf_tracking_filters_init(fe, 1000 *
65062306a36Sopenharmony_ci						  priv->rf_cal_state[i].rfmax);
65162306a36Sopenharmony_ci		if (tda_fail(ret))
65262306a36Sopenharmony_ci			goto fail;
65362306a36Sopenharmony_ci	}
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	priv->tm_rfcal = tda18271_read_thermometer(fe);
65662306a36Sopenharmony_cifail:
65762306a36Sopenharmony_ci	return ret;
65862306a36Sopenharmony_ci}
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci/* ------------------------------------------------------------------ */
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_cistatic int tda18271c2_rf_cal_init(struct dvb_frontend *fe)
66362306a36Sopenharmony_ci{
66462306a36Sopenharmony_ci	struct tda18271_priv *priv = fe->tuner_priv;
66562306a36Sopenharmony_ci	unsigned char *regs = priv->tda18271_regs;
66662306a36Sopenharmony_ci	int ret;
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	/* test RF_CAL_OK to see if we need init */
66962306a36Sopenharmony_ci	if ((regs[R_EP1] & 0x10) == 0)
67062306a36Sopenharmony_ci		priv->cal_initialized = false;
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	if (priv->cal_initialized)
67362306a36Sopenharmony_ci		return 0;
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	ret = tda18271_calc_rf_filter_curve(fe);
67662306a36Sopenharmony_ci	if (tda_fail(ret))
67762306a36Sopenharmony_ci		goto fail;
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	ret = tda18271_por(fe);
68062306a36Sopenharmony_ci	if (tda_fail(ret))
68162306a36Sopenharmony_ci		goto fail;
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci	tda_info("RF tracking filter calibration complete\n");
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	priv->cal_initialized = true;
68662306a36Sopenharmony_ci	goto end;
68762306a36Sopenharmony_cifail:
68862306a36Sopenharmony_ci	tda_info("RF tracking filter calibration failed!\n");
68962306a36Sopenharmony_ciend:
69062306a36Sopenharmony_ci	return ret;
69162306a36Sopenharmony_ci}
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_cistatic int tda18271c1_rf_tracking_filter_calibration(struct dvb_frontend *fe,
69462306a36Sopenharmony_ci						     u32 freq, u32 bw)
69562306a36Sopenharmony_ci{
69662306a36Sopenharmony_ci	struct tda18271_priv *priv = fe->tuner_priv;
69762306a36Sopenharmony_ci	unsigned char *regs = priv->tda18271_regs;
69862306a36Sopenharmony_ci	int ret;
69962306a36Sopenharmony_ci	u32 N = 0;
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci	/* calculate bp filter */
70262306a36Sopenharmony_ci	tda18271_calc_bp_filter(fe, &freq);
70362306a36Sopenharmony_ci	tda18271_write_regs(fe, R_EP1, 1);
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci	regs[R_EB4]  &= 0x07;
70662306a36Sopenharmony_ci	regs[R_EB4]  |= 0x60;
70762306a36Sopenharmony_ci	tda18271_write_regs(fe, R_EB4, 1);
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	regs[R_EB7]   = 0x60;
71062306a36Sopenharmony_ci	tda18271_write_regs(fe, R_EB7, 1);
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	regs[R_EB14]  = 0x00;
71362306a36Sopenharmony_ci	tda18271_write_regs(fe, R_EB14, 1);
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	regs[R_EB20]  = 0xcc;
71662306a36Sopenharmony_ci	tda18271_write_regs(fe, R_EB20, 1);
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	/* set cal mode to RF tracking filter calibration */
71962306a36Sopenharmony_ci	regs[R_EP4]  |= 0x03;
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci	/* calculate cal pll */
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	switch (priv->mode) {
72462306a36Sopenharmony_ci	case TDA18271_ANALOG:
72562306a36Sopenharmony_ci		N = freq - 1250000;
72662306a36Sopenharmony_ci		break;
72762306a36Sopenharmony_ci	case TDA18271_DIGITAL:
72862306a36Sopenharmony_ci		N = freq + bw / 2;
72962306a36Sopenharmony_ci		break;
73062306a36Sopenharmony_ci	}
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	tda18271_calc_cal_pll(fe, N);
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci	/* calculate main pll */
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	switch (priv->mode) {
73762306a36Sopenharmony_ci	case TDA18271_ANALOG:
73862306a36Sopenharmony_ci		N = freq - 250000;
73962306a36Sopenharmony_ci		break;
74062306a36Sopenharmony_ci	case TDA18271_DIGITAL:
74162306a36Sopenharmony_ci		N = freq + bw / 2 + 1000000;
74262306a36Sopenharmony_ci		break;
74362306a36Sopenharmony_ci	}
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci	tda18271_calc_main_pll(fe, N);
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci	ret = tda18271_write_regs(fe, R_EP3, 11);
74862306a36Sopenharmony_ci	if (tda_fail(ret))
74962306a36Sopenharmony_ci		return ret;
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci	msleep(5); /* RF tracking filter calibration initialization */
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci	/* search for K,M,CO for RF calibration */
75462306a36Sopenharmony_ci	tda18271_calc_km(fe, &freq);
75562306a36Sopenharmony_ci	tda18271_write_regs(fe, R_EB13, 1);
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci	/* search for rf band */
75862306a36Sopenharmony_ci	tda18271_calc_rf_band(fe, &freq);
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci	/* search for gain taper */
76162306a36Sopenharmony_ci	tda18271_calc_gain_taper(fe, &freq);
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci	tda18271_write_regs(fe, R_EP2, 1);
76462306a36Sopenharmony_ci	tda18271_write_regs(fe, R_EP1, 1);
76562306a36Sopenharmony_ci	tda18271_write_regs(fe, R_EP2, 1);
76662306a36Sopenharmony_ci	tda18271_write_regs(fe, R_EP1, 1);
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	regs[R_EB4]  &= 0x07;
76962306a36Sopenharmony_ci	regs[R_EB4]  |= 0x40;
77062306a36Sopenharmony_ci	tda18271_write_regs(fe, R_EB4, 1);
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	regs[R_EB7]   = 0x40;
77362306a36Sopenharmony_ci	tda18271_write_regs(fe, R_EB7, 1);
77462306a36Sopenharmony_ci	msleep(10); /* pll locking */
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	regs[R_EB20]  = 0xec;
77762306a36Sopenharmony_ci	tda18271_write_regs(fe, R_EB20, 1);
77862306a36Sopenharmony_ci	msleep(60); /* RF tracking filter calibration completion */
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci	regs[R_EP4]  &= ~0x03; /* set cal mode to normal */
78162306a36Sopenharmony_ci	tda18271_write_regs(fe, R_EP4, 1);
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	tda18271_write_regs(fe, R_EP1, 1);
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	/* RF tracking filter correction for VHF_Low band */
78662306a36Sopenharmony_ci	if (0 == tda18271_calc_rf_cal(fe, &freq))
78762306a36Sopenharmony_ci		tda18271_write_regs(fe, R_EB14, 1);
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci	return 0;
79062306a36Sopenharmony_ci}
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci/* ------------------------------------------------------------------ */
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_cistatic int tda18271_ir_cal_init(struct dvb_frontend *fe)
79562306a36Sopenharmony_ci{
79662306a36Sopenharmony_ci	struct tda18271_priv *priv = fe->tuner_priv;
79762306a36Sopenharmony_ci	unsigned char *regs = priv->tda18271_regs;
79862306a36Sopenharmony_ci	int ret;
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci	ret = tda18271_read_regs(fe);
80162306a36Sopenharmony_ci	if (tda_fail(ret))
80262306a36Sopenharmony_ci		goto fail;
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	/* test IR_CAL_OK to see if we need init */
80562306a36Sopenharmony_ci	if ((regs[R_EP1] & 0x08) == 0)
80662306a36Sopenharmony_ci		ret = tda18271_init_regs(fe);
80762306a36Sopenharmony_cifail:
80862306a36Sopenharmony_ci	return ret;
80962306a36Sopenharmony_ci}
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_cistatic int tda18271_init(struct dvb_frontend *fe)
81262306a36Sopenharmony_ci{
81362306a36Sopenharmony_ci	struct tda18271_priv *priv = fe->tuner_priv;
81462306a36Sopenharmony_ci	int ret;
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci	mutex_lock(&priv->lock);
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci	/* full power up */
81962306a36Sopenharmony_ci	ret = tda18271_set_standby_mode(fe, 0, 0, 0);
82062306a36Sopenharmony_ci	if (tda_fail(ret))
82162306a36Sopenharmony_ci		goto fail;
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	/* initialization */
82462306a36Sopenharmony_ci	ret = tda18271_ir_cal_init(fe);
82562306a36Sopenharmony_ci	if (tda_fail(ret))
82662306a36Sopenharmony_ci		goto fail;
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci	if (priv->id == TDA18271HDC2)
82962306a36Sopenharmony_ci		tda18271c2_rf_cal_init(fe);
83062306a36Sopenharmony_cifail:
83162306a36Sopenharmony_ci	mutex_unlock(&priv->lock);
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	return ret;
83462306a36Sopenharmony_ci}
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_cistatic int tda18271_sleep(struct dvb_frontend *fe)
83762306a36Sopenharmony_ci{
83862306a36Sopenharmony_ci	struct tda18271_priv *priv = fe->tuner_priv;
83962306a36Sopenharmony_ci	int ret;
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci	mutex_lock(&priv->lock);
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci	/* enter standby mode, with required output features enabled */
84462306a36Sopenharmony_ci	ret = tda18271_toggle_output(fe, 1);
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	mutex_unlock(&priv->lock);
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci	return ret;
84962306a36Sopenharmony_ci}
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci/* ------------------------------------------------------------------ */
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_cistatic int tda18271_agc(struct dvb_frontend *fe)
85462306a36Sopenharmony_ci{
85562306a36Sopenharmony_ci	struct tda18271_priv *priv = fe->tuner_priv;
85662306a36Sopenharmony_ci	int ret = 0;
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci	switch (priv->config) {
85962306a36Sopenharmony_ci	case TDA8290_LNA_OFF:
86062306a36Sopenharmony_ci		/* no external agc configuration required */
86162306a36Sopenharmony_ci		if (tda18271_debug & DBG_ADV)
86262306a36Sopenharmony_ci			tda_dbg("no agc configuration provided\n");
86362306a36Sopenharmony_ci		break;
86462306a36Sopenharmony_ci	case TDA8290_LNA_ON_BRIDGE:
86562306a36Sopenharmony_ci		/* switch with GPIO of saa713x */
86662306a36Sopenharmony_ci		tda_dbg("invoking callback\n");
86762306a36Sopenharmony_ci		if (fe->callback)
86862306a36Sopenharmony_ci			ret = fe->callback(priv->i2c_props.adap->algo_data,
86962306a36Sopenharmony_ci					   DVB_FRONTEND_COMPONENT_TUNER,
87062306a36Sopenharmony_ci					   TDA18271_CALLBACK_CMD_AGC_ENABLE,
87162306a36Sopenharmony_ci					   priv->mode);
87262306a36Sopenharmony_ci		break;
87362306a36Sopenharmony_ci	case TDA8290_LNA_GP0_HIGH_ON:
87462306a36Sopenharmony_ci	case TDA8290_LNA_GP0_HIGH_OFF:
87562306a36Sopenharmony_ci	default:
87662306a36Sopenharmony_ci		/* n/a - currently not supported */
87762306a36Sopenharmony_ci		tda_err("unsupported configuration: %d\n", priv->config);
87862306a36Sopenharmony_ci		ret = -EINVAL;
87962306a36Sopenharmony_ci		break;
88062306a36Sopenharmony_ci	}
88162306a36Sopenharmony_ci	return ret;
88262306a36Sopenharmony_ci}
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_cistatic int tda18271_tune(struct dvb_frontend *fe,
88562306a36Sopenharmony_ci			 struct tda18271_std_map_item *map, u32 freq, u32 bw)
88662306a36Sopenharmony_ci{
88762306a36Sopenharmony_ci	struct tda18271_priv *priv = fe->tuner_priv;
88862306a36Sopenharmony_ci	int ret;
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci	tda_dbg("freq = %d, ifc = %d, bw = %d, agc_mode = %d, std = %d\n",
89162306a36Sopenharmony_ci		freq, map->if_freq, bw, map->agc_mode, map->std);
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci	ret = tda18271_agc(fe);
89462306a36Sopenharmony_ci	if (tda_fail(ret))
89562306a36Sopenharmony_ci		tda_warn("failed to configure agc\n");
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	ret = tda18271_init(fe);
89862306a36Sopenharmony_ci	if (tda_fail(ret))
89962306a36Sopenharmony_ci		goto fail;
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci	mutex_lock(&priv->lock);
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci	switch (priv->id) {
90462306a36Sopenharmony_ci	case TDA18271HDC1:
90562306a36Sopenharmony_ci		tda18271c1_rf_tracking_filter_calibration(fe, freq, bw);
90662306a36Sopenharmony_ci		break;
90762306a36Sopenharmony_ci	case TDA18271HDC2:
90862306a36Sopenharmony_ci		tda18271c2_rf_tracking_filters_correction(fe, freq);
90962306a36Sopenharmony_ci		break;
91062306a36Sopenharmony_ci	}
91162306a36Sopenharmony_ci	ret = tda18271_channel_configuration(fe, map, freq, bw);
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci	mutex_unlock(&priv->lock);
91462306a36Sopenharmony_cifail:
91562306a36Sopenharmony_ci	return ret;
91662306a36Sopenharmony_ci}
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci/* ------------------------------------------------------------------ */
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_cistatic int tda18271_set_params(struct dvb_frontend *fe)
92162306a36Sopenharmony_ci{
92262306a36Sopenharmony_ci	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
92362306a36Sopenharmony_ci	u32 delsys = c->delivery_system;
92462306a36Sopenharmony_ci	u32 bw = c->bandwidth_hz;
92562306a36Sopenharmony_ci	u32 freq = c->frequency;
92662306a36Sopenharmony_ci	struct tda18271_priv *priv = fe->tuner_priv;
92762306a36Sopenharmony_ci	struct tda18271_std_map *std_map = &priv->std;
92862306a36Sopenharmony_ci	struct tda18271_std_map_item *map;
92962306a36Sopenharmony_ci	int ret;
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci	priv->mode = TDA18271_DIGITAL;
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci	switch (delsys) {
93462306a36Sopenharmony_ci	case SYS_ATSC:
93562306a36Sopenharmony_ci		map = &std_map->atsc_6;
93662306a36Sopenharmony_ci		bw = 6000000;
93762306a36Sopenharmony_ci		break;
93862306a36Sopenharmony_ci	case SYS_ISDBT:
93962306a36Sopenharmony_ci	case SYS_DVBT:
94062306a36Sopenharmony_ci	case SYS_DVBT2:
94162306a36Sopenharmony_ci		if (bw <= 6000000) {
94262306a36Sopenharmony_ci			map = &std_map->dvbt_6;
94362306a36Sopenharmony_ci		} else if (bw <= 7000000) {
94462306a36Sopenharmony_ci			map = &std_map->dvbt_7;
94562306a36Sopenharmony_ci		} else {
94662306a36Sopenharmony_ci			map = &std_map->dvbt_8;
94762306a36Sopenharmony_ci		}
94862306a36Sopenharmony_ci		break;
94962306a36Sopenharmony_ci	case SYS_DVBC_ANNEX_B:
95062306a36Sopenharmony_ci		bw = 6000000;
95162306a36Sopenharmony_ci		fallthrough;
95262306a36Sopenharmony_ci	case SYS_DVBC_ANNEX_A:
95362306a36Sopenharmony_ci	case SYS_DVBC_ANNEX_C:
95462306a36Sopenharmony_ci		if (bw <= 6000000) {
95562306a36Sopenharmony_ci			map = &std_map->qam_6;
95662306a36Sopenharmony_ci		} else if (bw <= 7000000) {
95762306a36Sopenharmony_ci			map = &std_map->qam_7;
95862306a36Sopenharmony_ci		} else {
95962306a36Sopenharmony_ci			map = &std_map->qam_8;
96062306a36Sopenharmony_ci		}
96162306a36Sopenharmony_ci		break;
96262306a36Sopenharmony_ci	default:
96362306a36Sopenharmony_ci		tda_warn("modulation type not supported!\n");
96462306a36Sopenharmony_ci		return -EINVAL;
96562306a36Sopenharmony_ci	}
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci	/* When tuning digital, the analog demod must be tri-stated */
96862306a36Sopenharmony_ci	if (fe->ops.analog_ops.standby)
96962306a36Sopenharmony_ci		fe->ops.analog_ops.standby(fe);
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci	ret = tda18271_tune(fe, map, freq, bw);
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci	if (tda_fail(ret))
97462306a36Sopenharmony_ci		goto fail;
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_ci	priv->if_freq   = map->if_freq;
97762306a36Sopenharmony_ci	priv->frequency = freq;
97862306a36Sopenharmony_ci	priv->bandwidth = bw;
97962306a36Sopenharmony_cifail:
98062306a36Sopenharmony_ci	return ret;
98162306a36Sopenharmony_ci}
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_cistatic int tda18271_set_analog_params(struct dvb_frontend *fe,
98462306a36Sopenharmony_ci				      struct analog_parameters *params)
98562306a36Sopenharmony_ci{
98662306a36Sopenharmony_ci	struct tda18271_priv *priv = fe->tuner_priv;
98762306a36Sopenharmony_ci	struct tda18271_std_map *std_map = &priv->std;
98862306a36Sopenharmony_ci	struct tda18271_std_map_item *map;
98962306a36Sopenharmony_ci	char *mode;
99062306a36Sopenharmony_ci	int ret;
99162306a36Sopenharmony_ci	u32 freq = params->frequency * 125 *
99262306a36Sopenharmony_ci		((params->mode == V4L2_TUNER_RADIO) ? 1 : 1000) / 2;
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci	priv->mode = TDA18271_ANALOG;
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci	if (params->mode == V4L2_TUNER_RADIO) {
99762306a36Sopenharmony_ci		map = &std_map->fm_radio;
99862306a36Sopenharmony_ci		mode = "fm";
99962306a36Sopenharmony_ci	} else if (params->std & V4L2_STD_MN) {
100062306a36Sopenharmony_ci		map = &std_map->atv_mn;
100162306a36Sopenharmony_ci		mode = "MN";
100262306a36Sopenharmony_ci	} else if (params->std & V4L2_STD_B) {
100362306a36Sopenharmony_ci		map = &std_map->atv_b;
100462306a36Sopenharmony_ci		mode = "B";
100562306a36Sopenharmony_ci	} else if (params->std & V4L2_STD_GH) {
100662306a36Sopenharmony_ci		map = &std_map->atv_gh;
100762306a36Sopenharmony_ci		mode = "GH";
100862306a36Sopenharmony_ci	} else if (params->std & V4L2_STD_PAL_I) {
100962306a36Sopenharmony_ci		map = &std_map->atv_i;
101062306a36Sopenharmony_ci		mode = "I";
101162306a36Sopenharmony_ci	} else if (params->std & V4L2_STD_DK) {
101262306a36Sopenharmony_ci		map = &std_map->atv_dk;
101362306a36Sopenharmony_ci		mode = "DK";
101462306a36Sopenharmony_ci	} else if (params->std & V4L2_STD_SECAM_L) {
101562306a36Sopenharmony_ci		map = &std_map->atv_l;
101662306a36Sopenharmony_ci		mode = "L";
101762306a36Sopenharmony_ci	} else if (params->std & V4L2_STD_SECAM_LC) {
101862306a36Sopenharmony_ci		map = &std_map->atv_lc;
101962306a36Sopenharmony_ci		mode = "L'";
102062306a36Sopenharmony_ci	} else {
102162306a36Sopenharmony_ci		map = &std_map->atv_i;
102262306a36Sopenharmony_ci		mode = "xx";
102362306a36Sopenharmony_ci	}
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_ci	tda_dbg("setting tda18271 to system %s\n", mode);
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ci	ret = tda18271_tune(fe, map, freq, 0);
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci	if (tda_fail(ret))
103062306a36Sopenharmony_ci		goto fail;
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci	priv->if_freq   = map->if_freq;
103362306a36Sopenharmony_ci	priv->frequency = freq;
103462306a36Sopenharmony_ci	priv->bandwidth = 0;
103562306a36Sopenharmony_cifail:
103662306a36Sopenharmony_ci	return ret;
103762306a36Sopenharmony_ci}
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_cistatic void tda18271_release(struct dvb_frontend *fe)
104062306a36Sopenharmony_ci{
104162306a36Sopenharmony_ci	struct tda18271_priv *priv = fe->tuner_priv;
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_ci	mutex_lock(&tda18271_list_mutex);
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_ci	if (priv)
104662306a36Sopenharmony_ci		hybrid_tuner_release_state(priv);
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_ci	mutex_unlock(&tda18271_list_mutex);
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci	fe->tuner_priv = NULL;
105162306a36Sopenharmony_ci}
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_cistatic int tda18271_get_frequency(struct dvb_frontend *fe, u32 *frequency)
105462306a36Sopenharmony_ci{
105562306a36Sopenharmony_ci	struct tda18271_priv *priv = fe->tuner_priv;
105662306a36Sopenharmony_ci	*frequency = priv->frequency;
105762306a36Sopenharmony_ci	return 0;
105862306a36Sopenharmony_ci}
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_cistatic int tda18271_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
106162306a36Sopenharmony_ci{
106262306a36Sopenharmony_ci	struct tda18271_priv *priv = fe->tuner_priv;
106362306a36Sopenharmony_ci	*bandwidth = priv->bandwidth;
106462306a36Sopenharmony_ci	return 0;
106562306a36Sopenharmony_ci}
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_cistatic int tda18271_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
106862306a36Sopenharmony_ci{
106962306a36Sopenharmony_ci	struct tda18271_priv *priv = fe->tuner_priv;
107062306a36Sopenharmony_ci	*frequency = (u32)priv->if_freq * 1000;
107162306a36Sopenharmony_ci	return 0;
107262306a36Sopenharmony_ci}
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_ci/* ------------------------------------------------------------------ */
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci#define tda18271_update_std(std_cfg, name) do {				\
107762306a36Sopenharmony_ci	if (map->std_cfg.if_freq +					\
107862306a36Sopenharmony_ci		map->std_cfg.agc_mode + map->std_cfg.std +		\
107962306a36Sopenharmony_ci		map->std_cfg.if_lvl + map->std_cfg.rfagc_top > 0) {	\
108062306a36Sopenharmony_ci		tda_dbg("Using custom std config for %s\n", name);	\
108162306a36Sopenharmony_ci		memcpy(&std->std_cfg, &map->std_cfg,			\
108262306a36Sopenharmony_ci			sizeof(struct tda18271_std_map_item));		\
108362306a36Sopenharmony_ci	} } while (0)
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ci#define tda18271_dump_std_item(std_cfg, name) do {			\
108662306a36Sopenharmony_ci	tda_dbg("(%s) if_freq = %d, agc_mode = %d, std = %d, "		\
108762306a36Sopenharmony_ci		"if_lvl = %d, rfagc_top = 0x%02x\n",			\
108862306a36Sopenharmony_ci		name, std->std_cfg.if_freq,				\
108962306a36Sopenharmony_ci		std->std_cfg.agc_mode, std->std_cfg.std,		\
109062306a36Sopenharmony_ci		std->std_cfg.if_lvl, std->std_cfg.rfagc_top);		\
109162306a36Sopenharmony_ci	} while (0)
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_cistatic int tda18271_dump_std_map(struct dvb_frontend *fe)
109462306a36Sopenharmony_ci{
109562306a36Sopenharmony_ci	struct tda18271_priv *priv = fe->tuner_priv;
109662306a36Sopenharmony_ci	struct tda18271_std_map *std = &priv->std;
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci	tda_dbg("========== STANDARD MAP SETTINGS ==========\n");
109962306a36Sopenharmony_ci	tda18271_dump_std_item(fm_radio, "  fm  ");
110062306a36Sopenharmony_ci	tda18271_dump_std_item(atv_b,  "atv b ");
110162306a36Sopenharmony_ci	tda18271_dump_std_item(atv_dk, "atv dk");
110262306a36Sopenharmony_ci	tda18271_dump_std_item(atv_gh, "atv gh");
110362306a36Sopenharmony_ci	tda18271_dump_std_item(atv_i,  "atv i ");
110462306a36Sopenharmony_ci	tda18271_dump_std_item(atv_l,  "atv l ");
110562306a36Sopenharmony_ci	tda18271_dump_std_item(atv_lc, "atv l'");
110662306a36Sopenharmony_ci	tda18271_dump_std_item(atv_mn, "atv mn");
110762306a36Sopenharmony_ci	tda18271_dump_std_item(atsc_6, "atsc 6");
110862306a36Sopenharmony_ci	tda18271_dump_std_item(dvbt_6, "dvbt 6");
110962306a36Sopenharmony_ci	tda18271_dump_std_item(dvbt_7, "dvbt 7");
111062306a36Sopenharmony_ci	tda18271_dump_std_item(dvbt_8, "dvbt 8");
111162306a36Sopenharmony_ci	tda18271_dump_std_item(qam_6,  "qam 6 ");
111262306a36Sopenharmony_ci	tda18271_dump_std_item(qam_7,  "qam 7 ");
111362306a36Sopenharmony_ci	tda18271_dump_std_item(qam_8,  "qam 8 ");
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_ci	return 0;
111662306a36Sopenharmony_ci}
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_cistatic int tda18271_update_std_map(struct dvb_frontend *fe,
111962306a36Sopenharmony_ci				   struct tda18271_std_map *map)
112062306a36Sopenharmony_ci{
112162306a36Sopenharmony_ci	struct tda18271_priv *priv = fe->tuner_priv;
112262306a36Sopenharmony_ci	struct tda18271_std_map *std = &priv->std;
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ci	if (!map)
112562306a36Sopenharmony_ci		return -EINVAL;
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_ci	tda18271_update_std(fm_radio, "fm");
112862306a36Sopenharmony_ci	tda18271_update_std(atv_b,  "atv b");
112962306a36Sopenharmony_ci	tda18271_update_std(atv_dk, "atv dk");
113062306a36Sopenharmony_ci	tda18271_update_std(atv_gh, "atv gh");
113162306a36Sopenharmony_ci	tda18271_update_std(atv_i,  "atv i");
113262306a36Sopenharmony_ci	tda18271_update_std(atv_l,  "atv l");
113362306a36Sopenharmony_ci	tda18271_update_std(atv_lc, "atv l'");
113462306a36Sopenharmony_ci	tda18271_update_std(atv_mn, "atv mn");
113562306a36Sopenharmony_ci	tda18271_update_std(atsc_6, "atsc 6");
113662306a36Sopenharmony_ci	tda18271_update_std(dvbt_6, "dvbt 6");
113762306a36Sopenharmony_ci	tda18271_update_std(dvbt_7, "dvbt 7");
113862306a36Sopenharmony_ci	tda18271_update_std(dvbt_8, "dvbt 8");
113962306a36Sopenharmony_ci	tda18271_update_std(qam_6,  "qam 6");
114062306a36Sopenharmony_ci	tda18271_update_std(qam_7,  "qam 7");
114162306a36Sopenharmony_ci	tda18271_update_std(qam_8,  "qam 8");
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_ci	return 0;
114462306a36Sopenharmony_ci}
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_cistatic int tda18271_get_id(struct dvb_frontend *fe)
114762306a36Sopenharmony_ci{
114862306a36Sopenharmony_ci	struct tda18271_priv *priv = fe->tuner_priv;
114962306a36Sopenharmony_ci	unsigned char *regs = priv->tda18271_regs;
115062306a36Sopenharmony_ci	char *name;
115162306a36Sopenharmony_ci	int ret;
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_ci	mutex_lock(&priv->lock);
115462306a36Sopenharmony_ci	ret = tda18271_read_regs(fe);
115562306a36Sopenharmony_ci	mutex_unlock(&priv->lock);
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci	if (ret) {
115862306a36Sopenharmony_ci		tda_info("Error reading device ID @ %d-%04x, bailing out.\n",
115962306a36Sopenharmony_ci			 i2c_adapter_id(priv->i2c_props.adap),
116062306a36Sopenharmony_ci			 priv->i2c_props.addr);
116162306a36Sopenharmony_ci		return -EIO;
116262306a36Sopenharmony_ci	}
116362306a36Sopenharmony_ci
116462306a36Sopenharmony_ci	switch (regs[R_ID] & 0x7f) {
116562306a36Sopenharmony_ci	case 3:
116662306a36Sopenharmony_ci		name = "TDA18271HD/C1";
116762306a36Sopenharmony_ci		priv->id = TDA18271HDC1;
116862306a36Sopenharmony_ci		break;
116962306a36Sopenharmony_ci	case 4:
117062306a36Sopenharmony_ci		name = "TDA18271HD/C2";
117162306a36Sopenharmony_ci		priv->id = TDA18271HDC2;
117262306a36Sopenharmony_ci		break;
117362306a36Sopenharmony_ci	default:
117462306a36Sopenharmony_ci		tda_info("Unknown device (%i) detected @ %d-%04x, device not supported.\n",
117562306a36Sopenharmony_ci			 regs[R_ID], i2c_adapter_id(priv->i2c_props.adap),
117662306a36Sopenharmony_ci			 priv->i2c_props.addr);
117762306a36Sopenharmony_ci		return -EINVAL;
117862306a36Sopenharmony_ci	}
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_ci	tda_info("%s detected @ %d-%04x\n", name,
118162306a36Sopenharmony_ci		 i2c_adapter_id(priv->i2c_props.adap), priv->i2c_props.addr);
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_ci	return 0;
118462306a36Sopenharmony_ci}
118562306a36Sopenharmony_ci
118662306a36Sopenharmony_cistatic int tda18271_setup_configuration(struct dvb_frontend *fe,
118762306a36Sopenharmony_ci					struct tda18271_config *cfg)
118862306a36Sopenharmony_ci{
118962306a36Sopenharmony_ci	struct tda18271_priv *priv = fe->tuner_priv;
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_ci	priv->gate = (cfg) ? cfg->gate : TDA18271_GATE_AUTO;
119262306a36Sopenharmony_ci	priv->role = (cfg) ? cfg->role : TDA18271_MASTER;
119362306a36Sopenharmony_ci	priv->config = (cfg) ? cfg->config : 0;
119462306a36Sopenharmony_ci	priv->small_i2c = (cfg) ?
119562306a36Sopenharmony_ci		cfg->small_i2c : TDA18271_39_BYTE_CHUNK_INIT;
119662306a36Sopenharmony_ci	priv->output_opt = (cfg) ?
119762306a36Sopenharmony_ci		cfg->output_opt : TDA18271_OUTPUT_LT_XT_ON;
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_ci	return 0;
120062306a36Sopenharmony_ci}
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_cistatic inline int tda18271_need_cal_on_startup(struct tda18271_config *cfg)
120362306a36Sopenharmony_ci{
120462306a36Sopenharmony_ci	/* tda18271_cal_on_startup == -1 when cal module option is unset */
120562306a36Sopenharmony_ci	return ((tda18271_cal_on_startup == -1) ?
120662306a36Sopenharmony_ci		/* honor configuration setting */
120762306a36Sopenharmony_ci		((cfg) && (cfg->rf_cal_on_startup)) :
120862306a36Sopenharmony_ci		/* module option overrides configuration setting */
120962306a36Sopenharmony_ci		(tda18271_cal_on_startup)) ? 1 : 0;
121062306a36Sopenharmony_ci}
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_cistatic int tda18271_set_config(struct dvb_frontend *fe, void *priv_cfg)
121362306a36Sopenharmony_ci{
121462306a36Sopenharmony_ci	struct tda18271_config *cfg = (struct tda18271_config *) priv_cfg;
121562306a36Sopenharmony_ci
121662306a36Sopenharmony_ci	tda18271_setup_configuration(fe, cfg);
121762306a36Sopenharmony_ci
121862306a36Sopenharmony_ci	if (tda18271_need_cal_on_startup(cfg))
121962306a36Sopenharmony_ci		tda18271_init(fe);
122062306a36Sopenharmony_ci
122162306a36Sopenharmony_ci	/* override default std map with values in config struct */
122262306a36Sopenharmony_ci	if ((cfg) && (cfg->std_map))
122362306a36Sopenharmony_ci		tda18271_update_std_map(fe, cfg->std_map);
122462306a36Sopenharmony_ci
122562306a36Sopenharmony_ci	return 0;
122662306a36Sopenharmony_ci}
122762306a36Sopenharmony_ci
122862306a36Sopenharmony_cistatic const struct dvb_tuner_ops tda18271_tuner_ops = {
122962306a36Sopenharmony_ci	.info = {
123062306a36Sopenharmony_ci		.name = "NXP TDA18271HD",
123162306a36Sopenharmony_ci		.frequency_min_hz  =  45 * MHz,
123262306a36Sopenharmony_ci		.frequency_max_hz  = 864 * MHz,
123362306a36Sopenharmony_ci		.frequency_step_hz = 62500
123462306a36Sopenharmony_ci	},
123562306a36Sopenharmony_ci	.init              = tda18271_init,
123662306a36Sopenharmony_ci	.sleep             = tda18271_sleep,
123762306a36Sopenharmony_ci	.set_params        = tda18271_set_params,
123862306a36Sopenharmony_ci	.set_analog_params = tda18271_set_analog_params,
123962306a36Sopenharmony_ci	.release           = tda18271_release,
124062306a36Sopenharmony_ci	.set_config        = tda18271_set_config,
124162306a36Sopenharmony_ci	.get_frequency     = tda18271_get_frequency,
124262306a36Sopenharmony_ci	.get_bandwidth     = tda18271_get_bandwidth,
124362306a36Sopenharmony_ci	.get_if_frequency  = tda18271_get_if_frequency,
124462306a36Sopenharmony_ci};
124562306a36Sopenharmony_ci
124662306a36Sopenharmony_cistruct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr,
124762306a36Sopenharmony_ci				     struct i2c_adapter *i2c,
124862306a36Sopenharmony_ci				     struct tda18271_config *cfg)
124962306a36Sopenharmony_ci{
125062306a36Sopenharmony_ci	struct tda18271_priv *priv = NULL;
125162306a36Sopenharmony_ci	int instance, ret;
125262306a36Sopenharmony_ci
125362306a36Sopenharmony_ci	mutex_lock(&tda18271_list_mutex);
125462306a36Sopenharmony_ci
125562306a36Sopenharmony_ci	instance = hybrid_tuner_request_state(struct tda18271_priv, priv,
125662306a36Sopenharmony_ci					      hybrid_tuner_instance_list,
125762306a36Sopenharmony_ci					      i2c, addr, "tda18271");
125862306a36Sopenharmony_ci	switch (instance) {
125962306a36Sopenharmony_ci	case 0:
126062306a36Sopenharmony_ci		goto fail;
126162306a36Sopenharmony_ci	case 1:
126262306a36Sopenharmony_ci		/* new tuner instance */
126362306a36Sopenharmony_ci		fe->tuner_priv = priv;
126462306a36Sopenharmony_ci
126562306a36Sopenharmony_ci		tda18271_setup_configuration(fe, cfg);
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_ci		priv->cal_initialized = false;
126862306a36Sopenharmony_ci		mutex_init(&priv->lock);
126962306a36Sopenharmony_ci
127062306a36Sopenharmony_ci		ret = tda18271_get_id(fe);
127162306a36Sopenharmony_ci		if (tda_fail(ret))
127262306a36Sopenharmony_ci			goto fail;
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_ci		ret = tda18271_assign_map_layout(fe);
127562306a36Sopenharmony_ci		if (tda_fail(ret))
127662306a36Sopenharmony_ci			goto fail;
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_ci		/* if delay_cal is set, delay IR & RF calibration until init()
127962306a36Sopenharmony_ci		 * module option 'cal' overrides this delay */
128062306a36Sopenharmony_ci		if ((cfg->delay_cal) && (!tda18271_need_cal_on_startup(cfg)))
128162306a36Sopenharmony_ci			break;
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_ci		mutex_lock(&priv->lock);
128462306a36Sopenharmony_ci		tda18271_init_regs(fe);
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_ci		if ((tda18271_need_cal_on_startup(cfg)) &&
128762306a36Sopenharmony_ci		    (priv->id == TDA18271HDC2))
128862306a36Sopenharmony_ci			tda18271c2_rf_cal_init(fe);
128962306a36Sopenharmony_ci
129062306a36Sopenharmony_ci		/* enter standby mode, with required output features enabled */
129162306a36Sopenharmony_ci		ret = tda18271_toggle_output(fe, 1);
129262306a36Sopenharmony_ci		tda_fail(ret);
129362306a36Sopenharmony_ci
129462306a36Sopenharmony_ci		mutex_unlock(&priv->lock);
129562306a36Sopenharmony_ci		break;
129662306a36Sopenharmony_ci	default:
129762306a36Sopenharmony_ci		/* existing tuner instance */
129862306a36Sopenharmony_ci		fe->tuner_priv = priv;
129962306a36Sopenharmony_ci
130062306a36Sopenharmony_ci		/* allow dvb driver to override configuration settings */
130162306a36Sopenharmony_ci		if (cfg) {
130262306a36Sopenharmony_ci			if (cfg->gate != TDA18271_GATE_ANALOG)
130362306a36Sopenharmony_ci				priv->gate = cfg->gate;
130462306a36Sopenharmony_ci			if (cfg->role)
130562306a36Sopenharmony_ci				priv->role = cfg->role;
130662306a36Sopenharmony_ci			if (cfg->config)
130762306a36Sopenharmony_ci				priv->config = cfg->config;
130862306a36Sopenharmony_ci			if (cfg->small_i2c)
130962306a36Sopenharmony_ci				priv->small_i2c = cfg->small_i2c;
131062306a36Sopenharmony_ci			if (cfg->output_opt)
131162306a36Sopenharmony_ci				priv->output_opt = cfg->output_opt;
131262306a36Sopenharmony_ci			if (cfg->std_map)
131362306a36Sopenharmony_ci				tda18271_update_std_map(fe, cfg->std_map);
131462306a36Sopenharmony_ci		}
131562306a36Sopenharmony_ci		if (tda18271_need_cal_on_startup(cfg))
131662306a36Sopenharmony_ci			tda18271_init(fe);
131762306a36Sopenharmony_ci		break;
131862306a36Sopenharmony_ci	}
131962306a36Sopenharmony_ci
132062306a36Sopenharmony_ci	/* override default std map with values in config struct */
132162306a36Sopenharmony_ci	if ((cfg) && (cfg->std_map))
132262306a36Sopenharmony_ci		tda18271_update_std_map(fe, cfg->std_map);
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_ci	mutex_unlock(&tda18271_list_mutex);
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_ci	memcpy(&fe->ops.tuner_ops, &tda18271_tuner_ops,
132762306a36Sopenharmony_ci	       sizeof(struct dvb_tuner_ops));
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_ci	if (tda18271_debug & (DBG_MAP | DBG_ADV))
133062306a36Sopenharmony_ci		tda18271_dump_std_map(fe);
133162306a36Sopenharmony_ci
133262306a36Sopenharmony_ci	return fe;
133362306a36Sopenharmony_cifail:
133462306a36Sopenharmony_ci	mutex_unlock(&tda18271_list_mutex);
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_ci	tda18271_release(fe);
133762306a36Sopenharmony_ci	return NULL;
133862306a36Sopenharmony_ci}
133962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(tda18271_attach);
134062306a36Sopenharmony_ciMODULE_DESCRIPTION("NXP TDA18271HD analog / digital tuner driver");
134162306a36Sopenharmony_ciMODULE_AUTHOR("Michael Krufky <mkrufky@linuxtv.org>");
134262306a36Sopenharmony_ciMODULE_LICENSE("GPL");
134362306a36Sopenharmony_ciMODULE_VERSION("0.4");
1344