18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  Driver for Quantek QT1010 silicon tuner
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Copyright (C) 2006 Antti Palosaari <crope@iki.fi>
68c2ecf20Sopenharmony_ci *                     Aapo Tahkola <aet@rasterburn.org>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci#include "qt1010.h"
98c2ecf20Sopenharmony_ci#include "qt1010_priv.h"
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci/* read single register */
128c2ecf20Sopenharmony_cistatic int qt1010_readreg(struct qt1010_priv *priv, u8 reg, u8 *val)
138c2ecf20Sopenharmony_ci{
148c2ecf20Sopenharmony_ci	struct i2c_msg msg[2] = {
158c2ecf20Sopenharmony_ci		{ .addr = priv->cfg->i2c_address,
168c2ecf20Sopenharmony_ci		  .flags = 0, .buf = &reg, .len = 1 },
178c2ecf20Sopenharmony_ci		{ .addr = priv->cfg->i2c_address,
188c2ecf20Sopenharmony_ci		  .flags = I2C_M_RD, .buf = val, .len = 1 },
198c2ecf20Sopenharmony_ci	};
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci	if (i2c_transfer(priv->i2c, msg, 2) != 2) {
228c2ecf20Sopenharmony_ci		dev_warn(&priv->i2c->dev, "%s: i2c rd failed reg=%02x\n",
238c2ecf20Sopenharmony_ci				KBUILD_MODNAME, reg);
248c2ecf20Sopenharmony_ci		return -EREMOTEIO;
258c2ecf20Sopenharmony_ci	}
268c2ecf20Sopenharmony_ci	return 0;
278c2ecf20Sopenharmony_ci}
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci/* write single register */
308c2ecf20Sopenharmony_cistatic int qt1010_writereg(struct qt1010_priv *priv, u8 reg, u8 val)
318c2ecf20Sopenharmony_ci{
328c2ecf20Sopenharmony_ci	u8 buf[2] = { reg, val };
338c2ecf20Sopenharmony_ci	struct i2c_msg msg = { .addr = priv->cfg->i2c_address,
348c2ecf20Sopenharmony_ci			       .flags = 0, .buf = buf, .len = 2 };
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	if (i2c_transfer(priv->i2c, &msg, 1) != 1) {
378c2ecf20Sopenharmony_ci		dev_warn(&priv->i2c->dev, "%s: i2c wr failed reg=%02x\n",
388c2ecf20Sopenharmony_ci				KBUILD_MODNAME, reg);
398c2ecf20Sopenharmony_ci		return -EREMOTEIO;
408c2ecf20Sopenharmony_ci	}
418c2ecf20Sopenharmony_ci	return 0;
428c2ecf20Sopenharmony_ci}
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cistatic int qt1010_set_params(struct dvb_frontend *fe)
458c2ecf20Sopenharmony_ci{
468c2ecf20Sopenharmony_ci	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
478c2ecf20Sopenharmony_ci	struct qt1010_priv *priv;
488c2ecf20Sopenharmony_ci	int err;
498c2ecf20Sopenharmony_ci	u32 freq, div, mod1, mod2;
508c2ecf20Sopenharmony_ci	u8 i, tmpval, reg05;
518c2ecf20Sopenharmony_ci	qt1010_i2c_oper_t rd[48] = {
528c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x01, 0x80 },
538c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x02, 0x3f },
548c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x05, 0xff }, /* 02 c write */
558c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x06, 0x44 },
568c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x07, 0xff }, /* 04 c write */
578c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x08, 0x08 },
588c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x09, 0xff }, /* 06 c write */
598c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x0a, 0xff }, /* 07 c write */
608c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x0b, 0xff }, /* 08 c write */
618c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x0c, 0xe1 },
628c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x1a, 0xff }, /* 10 c write */
638c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x1b, 0x00 },
648c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x1c, 0x89 },
658c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x11, 0xff }, /* 13 c write */
668c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x12, 0xff }, /* 14 c write */
678c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x22, 0xff }, /* 15 c write */
688c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x1e, 0x00 },
698c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x1e, 0xd0 },
708c2ecf20Sopenharmony_ci		{ QT1010_RD, 0x22, 0xff }, /* 16 c read */
718c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x1e, 0x00 },
728c2ecf20Sopenharmony_ci		{ QT1010_RD, 0x05, 0xff }, /* 20 c read */
738c2ecf20Sopenharmony_ci		{ QT1010_RD, 0x22, 0xff }, /* 21 c read */
748c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x23, 0xd0 },
758c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x1e, 0x00 },
768c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x1e, 0xe0 },
778c2ecf20Sopenharmony_ci		{ QT1010_RD, 0x23, 0xff }, /* 25 c read */
788c2ecf20Sopenharmony_ci		{ QT1010_RD, 0x23, 0xff }, /* 26 c read */
798c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x1e, 0x00 },
808c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x24, 0xd0 },
818c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x1e, 0x00 },
828c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x1e, 0xf0 },
838c2ecf20Sopenharmony_ci		{ QT1010_RD, 0x24, 0xff }, /* 31 c read */
848c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x1e, 0x00 },
858c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x14, 0x7f },
868c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x15, 0x7f },
878c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x05, 0xff }, /* 35 c write */
888c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x06, 0x00 },
898c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x15, 0x1f },
908c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x16, 0xff },
918c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x18, 0xff },
928c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x1f, 0xff }, /* 40 c write */
938c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x20, 0xff }, /* 41 c write */
948c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x21, 0x53 },
958c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x25, 0xff }, /* 43 c write */
968c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x26, 0x15 },
978c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x00, 0xff }, /* 45 c write */
988c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x02, 0x00 },
998c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x01, 0x00 }
1008c2ecf20Sopenharmony_ci	};
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci#define FREQ1 32000000 /* 32 MHz */
1038c2ecf20Sopenharmony_ci#define FREQ2  4000000 /* 4 MHz Quartz oscillator in the stick? */
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	priv = fe->tuner_priv;
1068c2ecf20Sopenharmony_ci	freq = c->frequency;
1078c2ecf20Sopenharmony_ci	div = (freq + QT1010_OFFSET) / QT1010_STEP;
1088c2ecf20Sopenharmony_ci	freq = (div * QT1010_STEP) - QT1010_OFFSET;
1098c2ecf20Sopenharmony_ci	mod1 = (freq + QT1010_OFFSET) % FREQ1;
1108c2ecf20Sopenharmony_ci	mod2 = (freq + QT1010_OFFSET) % FREQ2;
1118c2ecf20Sopenharmony_ci	priv->frequency = freq;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	if (fe->ops.i2c_gate_ctrl)
1148c2ecf20Sopenharmony_ci		fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	/* reg 05 base value */
1178c2ecf20Sopenharmony_ci	if      (freq < 290000000) reg05 = 0x14; /* 290 MHz */
1188c2ecf20Sopenharmony_ci	else if (freq < 610000000) reg05 = 0x34; /* 610 MHz */
1198c2ecf20Sopenharmony_ci	else if (freq < 802000000) reg05 = 0x54; /* 802 MHz */
1208c2ecf20Sopenharmony_ci	else                       reg05 = 0x74;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	/* 0x5 */
1238c2ecf20Sopenharmony_ci	rd[2].val = reg05;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	/* 07 - set frequency: 32 MHz scale */
1268c2ecf20Sopenharmony_ci	rd[4].val = (freq + QT1010_OFFSET) / FREQ1;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	/* 09 - changes every 8/24 MHz */
1298c2ecf20Sopenharmony_ci	if (mod1 < 8000000) rd[6].val = 0x1d;
1308c2ecf20Sopenharmony_ci	else                rd[6].val = 0x1c;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	/* 0a - set frequency: 4 MHz scale (max 28 MHz) */
1338c2ecf20Sopenharmony_ci	if      (mod1 < 1*FREQ2) rd[7].val = 0x09; /*  +0 MHz */
1348c2ecf20Sopenharmony_ci	else if (mod1 < 2*FREQ2) rd[7].val = 0x08; /*  +4 MHz */
1358c2ecf20Sopenharmony_ci	else if (mod1 < 3*FREQ2) rd[7].val = 0x0f; /*  +8 MHz */
1368c2ecf20Sopenharmony_ci	else if (mod1 < 4*FREQ2) rd[7].val = 0x0e; /* +12 MHz */
1378c2ecf20Sopenharmony_ci	else if (mod1 < 5*FREQ2) rd[7].val = 0x0d; /* +16 MHz */
1388c2ecf20Sopenharmony_ci	else if (mod1 < 6*FREQ2) rd[7].val = 0x0c; /* +20 MHz */
1398c2ecf20Sopenharmony_ci	else if (mod1 < 7*FREQ2) rd[7].val = 0x0b; /* +24 MHz */
1408c2ecf20Sopenharmony_ci	else                     rd[7].val = 0x0a; /* +28 MHz */
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	/* 0b - changes every 2/2 MHz */
1438c2ecf20Sopenharmony_ci	if (mod2 < 2000000) rd[8].val = 0x45;
1448c2ecf20Sopenharmony_ci	else                rd[8].val = 0x44;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	/* 1a - set frequency: 125 kHz scale (max 3875 kHz)*/
1478c2ecf20Sopenharmony_ci	tmpval = 0x78; /* byte, overflows intentionally */
1488c2ecf20Sopenharmony_ci	rd[10].val = tmpval-((mod2/QT1010_STEP)*0x08);
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	/* 11 */
1518c2ecf20Sopenharmony_ci	rd[13].val = 0xfd; /* TODO: correct value calculation */
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	/* 12 */
1548c2ecf20Sopenharmony_ci	rd[14].val = 0x91; /* TODO: correct value calculation */
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	/* 22 */
1578c2ecf20Sopenharmony_ci	if      (freq < 450000000) rd[15].val = 0xd0; /* 450 MHz */
1588c2ecf20Sopenharmony_ci	else if (freq < 482000000) rd[15].val = 0xd1; /* 482 MHz */
1598c2ecf20Sopenharmony_ci	else if (freq < 514000000) rd[15].val = 0xd4; /* 514 MHz */
1608c2ecf20Sopenharmony_ci	else if (freq < 546000000) rd[15].val = 0xd7; /* 546 MHz */
1618c2ecf20Sopenharmony_ci	else if (freq < 610000000) rd[15].val = 0xda; /* 610 MHz */
1628c2ecf20Sopenharmony_ci	else                       rd[15].val = 0xd0;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	/* 05 */
1658c2ecf20Sopenharmony_ci	rd[35].val = (reg05 & 0xf0);
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	/* 1f */
1688c2ecf20Sopenharmony_ci	if      (mod1 <  8000000) tmpval = 0x00;
1698c2ecf20Sopenharmony_ci	else if (mod1 < 12000000) tmpval = 0x01;
1708c2ecf20Sopenharmony_ci	else if (mod1 < 16000000) tmpval = 0x02;
1718c2ecf20Sopenharmony_ci	else if (mod1 < 24000000) tmpval = 0x03;
1728c2ecf20Sopenharmony_ci	else if (mod1 < 28000000) tmpval = 0x04;
1738c2ecf20Sopenharmony_ci	else                      tmpval = 0x05;
1748c2ecf20Sopenharmony_ci	rd[40].val = (priv->reg1f_init_val + 0x0e + tmpval);
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	/* 20 */
1778c2ecf20Sopenharmony_ci	if      (mod1 <  8000000) tmpval = 0x00;
1788c2ecf20Sopenharmony_ci	else if (mod1 < 12000000) tmpval = 0x01;
1798c2ecf20Sopenharmony_ci	else if (mod1 < 20000000) tmpval = 0x02;
1808c2ecf20Sopenharmony_ci	else if (mod1 < 24000000) tmpval = 0x03;
1818c2ecf20Sopenharmony_ci	else if (mod1 < 28000000) tmpval = 0x04;
1828c2ecf20Sopenharmony_ci	else                      tmpval = 0x05;
1838c2ecf20Sopenharmony_ci	rd[41].val = (priv->reg20_init_val + 0x0d + tmpval);
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	/* 25 */
1868c2ecf20Sopenharmony_ci	rd[43].val = priv->reg25_init_val;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	/* 00 */
1898c2ecf20Sopenharmony_ci	rd[45].val = 0x92; /* TODO: correct value calculation */
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	dev_dbg(&priv->i2c->dev,
1928c2ecf20Sopenharmony_ci			"%s: freq:%u 05:%02x 07:%02x 09:%02x 0a:%02x 0b:%02x " \
1938c2ecf20Sopenharmony_ci			"1a:%02x 11:%02x 12:%02x 22:%02x 05:%02x 1f:%02x " \
1948c2ecf20Sopenharmony_ci			"20:%02x 25:%02x 00:%02x\n", __func__, \
1958c2ecf20Sopenharmony_ci			freq, rd[2].val, rd[4].val, rd[6].val, rd[7].val, \
1968c2ecf20Sopenharmony_ci			rd[8].val, rd[10].val, rd[13].val, rd[14].val, \
1978c2ecf20Sopenharmony_ci			rd[15].val, rd[35].val, rd[40].val, rd[41].val, \
1988c2ecf20Sopenharmony_ci			rd[43].val, rd[45].val);
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(rd); i++) {
2018c2ecf20Sopenharmony_ci		if (rd[i].oper == QT1010_WR) {
2028c2ecf20Sopenharmony_ci			err = qt1010_writereg(priv, rd[i].reg, rd[i].val);
2038c2ecf20Sopenharmony_ci		} else { /* read is required to proper locking */
2048c2ecf20Sopenharmony_ci			err = qt1010_readreg(priv, rd[i].reg, &tmpval);
2058c2ecf20Sopenharmony_ci		}
2068c2ecf20Sopenharmony_ci		if (err) return err;
2078c2ecf20Sopenharmony_ci	}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	if (fe->ops.i2c_gate_ctrl)
2108c2ecf20Sopenharmony_ci		fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	return 0;
2138c2ecf20Sopenharmony_ci}
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_cistatic int qt1010_init_meas1(struct qt1010_priv *priv,
2168c2ecf20Sopenharmony_ci			     u8 oper, u8 reg, u8 reg_init_val, u8 *retval)
2178c2ecf20Sopenharmony_ci{
2188c2ecf20Sopenharmony_ci	u8 i, val1, val2;
2198c2ecf20Sopenharmony_ci	int err;
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	qt1010_i2c_oper_t i2c_data[] = {
2228c2ecf20Sopenharmony_ci		{ QT1010_WR, reg, reg_init_val },
2238c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x1e, 0x00 },
2248c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x1e, oper },
2258c2ecf20Sopenharmony_ci	};
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(i2c_data); i++) {
2288c2ecf20Sopenharmony_ci		err = qt1010_writereg(priv, i2c_data[i].reg,
2298c2ecf20Sopenharmony_ci				      i2c_data[i].val);
2308c2ecf20Sopenharmony_ci		if (err)
2318c2ecf20Sopenharmony_ci			return err;
2328c2ecf20Sopenharmony_ci	}
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	err = qt1010_readreg(priv, reg, &val2);
2358c2ecf20Sopenharmony_ci	if (err)
2368c2ecf20Sopenharmony_ci		return err;
2378c2ecf20Sopenharmony_ci	do {
2388c2ecf20Sopenharmony_ci		val1 = val2;
2398c2ecf20Sopenharmony_ci		err = qt1010_readreg(priv, reg, &val2);
2408c2ecf20Sopenharmony_ci		if (err)
2418c2ecf20Sopenharmony_ci			return err;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci		dev_dbg(&priv->i2c->dev, "%s: compare reg:%02x %02x %02x\n",
2448c2ecf20Sopenharmony_ci				__func__, reg, val1, val2);
2458c2ecf20Sopenharmony_ci	} while (val1 != val2);
2468c2ecf20Sopenharmony_ci	*retval = val1;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	return qt1010_writereg(priv, 0x1e, 0x00);
2498c2ecf20Sopenharmony_ci}
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_cistatic int qt1010_init_meas2(struct qt1010_priv *priv,
2528c2ecf20Sopenharmony_ci			    u8 reg_init_val, u8 *retval)
2538c2ecf20Sopenharmony_ci{
2548c2ecf20Sopenharmony_ci	u8 i, val = 0xff;
2558c2ecf20Sopenharmony_ci	int err;
2568c2ecf20Sopenharmony_ci	qt1010_i2c_oper_t i2c_data[] = {
2578c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x07, reg_init_val },
2588c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x22, 0xd0 },
2598c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x1e, 0x00 },
2608c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x1e, 0xd0 },
2618c2ecf20Sopenharmony_ci		{ QT1010_RD, 0x22, 0xff },
2628c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x1e, 0x00 },
2638c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x22, 0xff }
2648c2ecf20Sopenharmony_ci	};
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(i2c_data); i++) {
2678c2ecf20Sopenharmony_ci		if (i2c_data[i].oper == QT1010_WR) {
2688c2ecf20Sopenharmony_ci			err = qt1010_writereg(priv, i2c_data[i].reg,
2698c2ecf20Sopenharmony_ci					      i2c_data[i].val);
2708c2ecf20Sopenharmony_ci		} else {
2718c2ecf20Sopenharmony_ci			err = qt1010_readreg(priv, i2c_data[i].reg, &val);
2728c2ecf20Sopenharmony_ci		}
2738c2ecf20Sopenharmony_ci		if (err)
2748c2ecf20Sopenharmony_ci			return err;
2758c2ecf20Sopenharmony_ci	}
2768c2ecf20Sopenharmony_ci	*retval = val;
2778c2ecf20Sopenharmony_ci	return 0;
2788c2ecf20Sopenharmony_ci}
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_cistatic int qt1010_init(struct dvb_frontend *fe)
2818c2ecf20Sopenharmony_ci{
2828c2ecf20Sopenharmony_ci	struct qt1010_priv *priv = fe->tuner_priv;
2838c2ecf20Sopenharmony_ci	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
2848c2ecf20Sopenharmony_ci	int err = 0;
2858c2ecf20Sopenharmony_ci	u8 i, tmpval, *valptr = NULL;
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	static const qt1010_i2c_oper_t i2c_data[] = {
2888c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x01, 0x80 },
2898c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x0d, 0x84 },
2908c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x0e, 0xb7 },
2918c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x2a, 0x23 },
2928c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x2c, 0xdc },
2938c2ecf20Sopenharmony_ci		{ QT1010_M1, 0x25, 0x40 }, /* get reg 25 init value */
2948c2ecf20Sopenharmony_ci		{ QT1010_M1, 0x81, 0xff }, /* get reg 25 init value */
2958c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x2b, 0x70 },
2968c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x2a, 0x23 },
2978c2ecf20Sopenharmony_ci		{ QT1010_M1, 0x26, 0x08 },
2988c2ecf20Sopenharmony_ci		{ QT1010_M1, 0x82, 0xff },
2998c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x05, 0x14 },
3008c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x06, 0x44 },
3018c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x07, 0x28 },
3028c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x08, 0x0b },
3038c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x11, 0xfd },
3048c2ecf20Sopenharmony_ci		{ QT1010_M1, 0x22, 0x0d },
3058c2ecf20Sopenharmony_ci		{ QT1010_M1, 0xd0, 0xff },
3068c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x06, 0x40 },
3078c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x16, 0xf0 },
3088c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x02, 0x38 },
3098c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x03, 0x18 },
3108c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x20, 0xe0 },
3118c2ecf20Sopenharmony_ci		{ QT1010_M1, 0x1f, 0x20 }, /* get reg 1f init value */
3128c2ecf20Sopenharmony_ci		{ QT1010_M1, 0x84, 0xff }, /* get reg 1f init value */
3138c2ecf20Sopenharmony_ci		{ QT1010_RD, 0x20, 0x20 }, /* get reg 20 init value */
3148c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x03, 0x19 },
3158c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x02, 0x3f },
3168c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x21, 0x53 },
3178c2ecf20Sopenharmony_ci		{ QT1010_RD, 0x21, 0xff },
3188c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x11, 0xfd },
3198c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x05, 0x34 },
3208c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x06, 0x44 },
3218c2ecf20Sopenharmony_ci		{ QT1010_WR, 0x08, 0x08 }
3228c2ecf20Sopenharmony_ci	};
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	if (fe->ops.i2c_gate_ctrl)
3258c2ecf20Sopenharmony_ci		fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(i2c_data); i++) {
3288c2ecf20Sopenharmony_ci		switch (i2c_data[i].oper) {
3298c2ecf20Sopenharmony_ci		case QT1010_WR:
3308c2ecf20Sopenharmony_ci			err = qt1010_writereg(priv, i2c_data[i].reg,
3318c2ecf20Sopenharmony_ci					      i2c_data[i].val);
3328c2ecf20Sopenharmony_ci			break;
3338c2ecf20Sopenharmony_ci		case QT1010_RD:
3348c2ecf20Sopenharmony_ci			if (i2c_data[i].val == 0x20)
3358c2ecf20Sopenharmony_ci				valptr = &priv->reg20_init_val;
3368c2ecf20Sopenharmony_ci			else
3378c2ecf20Sopenharmony_ci				valptr = &tmpval;
3388c2ecf20Sopenharmony_ci			err = qt1010_readreg(priv, i2c_data[i].reg, valptr);
3398c2ecf20Sopenharmony_ci			break;
3408c2ecf20Sopenharmony_ci		case QT1010_M1:
3418c2ecf20Sopenharmony_ci			if (i2c_data[i].val == 0x25)
3428c2ecf20Sopenharmony_ci				valptr = &priv->reg25_init_val;
3438c2ecf20Sopenharmony_ci			else if (i2c_data[i].val == 0x1f)
3448c2ecf20Sopenharmony_ci				valptr = &priv->reg1f_init_val;
3458c2ecf20Sopenharmony_ci			else
3468c2ecf20Sopenharmony_ci				valptr = &tmpval;
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci			if (i >= ARRAY_SIZE(i2c_data) - 1)
3498c2ecf20Sopenharmony_ci				err = -EIO;
3508c2ecf20Sopenharmony_ci			else
3518c2ecf20Sopenharmony_ci				err = qt1010_init_meas1(priv, i2c_data[i + 1].reg,
3528c2ecf20Sopenharmony_ci							i2c_data[i].reg,
3538c2ecf20Sopenharmony_ci							i2c_data[i].val, valptr);
3548c2ecf20Sopenharmony_ci			i++;
3558c2ecf20Sopenharmony_ci			break;
3568c2ecf20Sopenharmony_ci		}
3578c2ecf20Sopenharmony_ci		if (err)
3588c2ecf20Sopenharmony_ci			return err;
3598c2ecf20Sopenharmony_ci	}
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	for (i = 0x31; i < 0x3a; i++) /* 0x31 - 0x39 */
3628c2ecf20Sopenharmony_ci		if ((err = qt1010_init_meas2(priv, i, &tmpval)))
3638c2ecf20Sopenharmony_ci			return err;
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	if (!c->frequency)
3668c2ecf20Sopenharmony_ci		c->frequency = 545000000; /* Sigmatek DVB-110 545000000 */
3678c2ecf20Sopenharmony_ci				      /* MSI Megasky 580 GL861 533000000 */
3688c2ecf20Sopenharmony_ci	return qt1010_set_params(fe);
3698c2ecf20Sopenharmony_ci}
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_cistatic void qt1010_release(struct dvb_frontend *fe)
3728c2ecf20Sopenharmony_ci{
3738c2ecf20Sopenharmony_ci	kfree(fe->tuner_priv);
3748c2ecf20Sopenharmony_ci	fe->tuner_priv = NULL;
3758c2ecf20Sopenharmony_ci}
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_cistatic int qt1010_get_frequency(struct dvb_frontend *fe, u32 *frequency)
3788c2ecf20Sopenharmony_ci{
3798c2ecf20Sopenharmony_ci	struct qt1010_priv *priv = fe->tuner_priv;
3808c2ecf20Sopenharmony_ci	*frequency = priv->frequency;
3818c2ecf20Sopenharmony_ci	return 0;
3828c2ecf20Sopenharmony_ci}
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_cistatic int qt1010_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
3858c2ecf20Sopenharmony_ci{
3868c2ecf20Sopenharmony_ci	*frequency = 36125000;
3878c2ecf20Sopenharmony_ci	return 0;
3888c2ecf20Sopenharmony_ci}
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_cistatic const struct dvb_tuner_ops qt1010_tuner_ops = {
3918c2ecf20Sopenharmony_ci	.info = {
3928c2ecf20Sopenharmony_ci		.name              = "Quantek QT1010",
3938c2ecf20Sopenharmony_ci		.frequency_min_hz  = QT1010_MIN_FREQ,
3948c2ecf20Sopenharmony_ci		.frequency_max_hz  = QT1010_MAX_FREQ,
3958c2ecf20Sopenharmony_ci		.frequency_step_hz = QT1010_STEP,
3968c2ecf20Sopenharmony_ci	},
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	.release       = qt1010_release,
3998c2ecf20Sopenharmony_ci	.init          = qt1010_init,
4008c2ecf20Sopenharmony_ci	/* TODO: implement sleep */
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	.set_params    = qt1010_set_params,
4038c2ecf20Sopenharmony_ci	.get_frequency = qt1010_get_frequency,
4048c2ecf20Sopenharmony_ci	.get_if_frequency = qt1010_get_if_frequency,
4058c2ecf20Sopenharmony_ci};
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_cistruct dvb_frontend * qt1010_attach(struct dvb_frontend *fe,
4088c2ecf20Sopenharmony_ci				    struct i2c_adapter *i2c,
4098c2ecf20Sopenharmony_ci				    struct qt1010_config *cfg)
4108c2ecf20Sopenharmony_ci{
4118c2ecf20Sopenharmony_ci	struct qt1010_priv *priv = NULL;
4128c2ecf20Sopenharmony_ci	u8 id;
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	priv = kzalloc(sizeof(struct qt1010_priv), GFP_KERNEL);
4158c2ecf20Sopenharmony_ci	if (priv == NULL)
4168c2ecf20Sopenharmony_ci		return NULL;
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	priv->cfg = cfg;
4198c2ecf20Sopenharmony_ci	priv->i2c = i2c;
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	if (fe->ops.i2c_gate_ctrl)
4228c2ecf20Sopenharmony_ci		fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	/* Try to detect tuner chip. Probably this is not correct register. */
4268c2ecf20Sopenharmony_ci	if (qt1010_readreg(priv, 0x29, &id) != 0 || (id != 0x39)) {
4278c2ecf20Sopenharmony_ci		kfree(priv);
4288c2ecf20Sopenharmony_ci		return NULL;
4298c2ecf20Sopenharmony_ci	}
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	if (fe->ops.i2c_gate_ctrl)
4328c2ecf20Sopenharmony_ci		fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	dev_info(&priv->i2c->dev,
4358c2ecf20Sopenharmony_ci			"%s: Quantek QT1010 successfully identified\n",
4368c2ecf20Sopenharmony_ci			KBUILD_MODNAME);
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	memcpy(&fe->ops.tuner_ops, &qt1010_tuner_ops,
4398c2ecf20Sopenharmony_ci	       sizeof(struct dvb_tuner_ops));
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	fe->tuner_priv = priv;
4428c2ecf20Sopenharmony_ci	return fe;
4438c2ecf20Sopenharmony_ci}
4448c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(qt1010_attach);
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Quantek QT1010 silicon tuner driver");
4478c2ecf20Sopenharmony_ciMODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
4488c2ecf20Sopenharmony_ciMODULE_AUTHOR("Aapo Tahkola <aet@rasterburn.org>");
4498c2ecf20Sopenharmony_ciMODULE_VERSION("0.1");
4508c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
451