PageRenderTime 37ms CodeModel.GetById 19ms app.highlight 15ms RepoModel.GetById 0ms app.codeStats 0ms

/drivers/media/common/tuners/tda18218.c

https://bitbucket.org/ndreys/linux-sunxi
C | 334 lines | 245 code | 56 blank | 33 comment | 42 complexity | 377b2ac4e2aef737bbe49e47dbde8190 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.0, AGPL-1.0
  1/*
  2 * NXP TDA18218HN silicon tuner driver
  3 *
  4 * Copyright (C) 2010 Antti Palosaari <crope@iki.fi>
  5 *
  6 *    This program is free software; you can redistribute it and/or modify
  7 *    it under the terms of the GNU General Public License as published by
  8 *    the Free Software Foundation; either version 2 of the License, or
  9 *    (at your option) any later version.
 10 *
 11 *    This program is distributed in the hope that it will be useful,
 12 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 13 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 14 *    GNU General Public License for more details.
 15 *
 16 *    You should have received a copy of the GNU General Public License
 17 *    along with this program; if not, write to the Free Software
 18 *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 19 */
 20
 21#include "tda18218.h"
 22#include "tda18218_priv.h"
 23
 24static int debug;
 25module_param(debug, int, 0644);
 26MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
 27
 28/* write multiple registers */
 29static int tda18218_wr_regs(struct tda18218_priv *priv, u8 reg, u8 *val, u8 len)
 30{
 31	int ret = 0;
 32	u8 buf[1+len], quotient, remainder, i, msg_len, msg_len_max;
 33	struct i2c_msg msg[1] = {
 34		{
 35			.addr = priv->cfg->i2c_address,
 36			.flags = 0,
 37			.buf = buf,
 38		}
 39	};
 40
 41	msg_len_max = priv->cfg->i2c_wr_max - 1;
 42	quotient = len / msg_len_max;
 43	remainder = len % msg_len_max;
 44	msg_len = msg_len_max;
 45	for (i = 0; (i <= quotient && remainder); i++) {
 46		if (i == quotient)  /* set len of the last msg */
 47			msg_len = remainder;
 48
 49		msg[0].len = msg_len + 1;
 50		buf[0] = reg + i * msg_len_max;
 51		memcpy(&buf[1], &val[i * msg_len_max], msg_len);
 52
 53		ret = i2c_transfer(priv->i2c, msg, 1);
 54		if (ret != 1)
 55			break;
 56	}
 57
 58	if (ret == 1) {
 59		ret = 0;
 60	} else {
 61		warn("i2c wr failed ret:%d reg:%02x len:%d", ret, reg, len);
 62		ret = -EREMOTEIO;
 63	}
 64
 65	return ret;
 66}
 67
 68/* read multiple registers */
 69static int tda18218_rd_regs(struct tda18218_priv *priv, u8 reg, u8 *val, u8 len)
 70{
 71	int ret;
 72	u8 buf[reg+len]; /* we must start read always from reg 0x00 */
 73	struct i2c_msg msg[2] = {
 74		{
 75			.addr = priv->cfg->i2c_address,
 76			.flags = 0,
 77			.len = 1,
 78			.buf = "\x00",
 79		}, {
 80			.addr = priv->cfg->i2c_address,
 81			.flags = I2C_M_RD,
 82			.len = sizeof(buf),
 83			.buf = buf,
 84		}
 85	};
 86
 87	ret = i2c_transfer(priv->i2c, msg, 2);
 88	if (ret == 2) {
 89		memcpy(val, &buf[reg], len);
 90		ret = 0;
 91	} else {
 92		warn("i2c rd failed ret:%d reg:%02x len:%d", ret, reg, len);
 93		ret = -EREMOTEIO;
 94	}
 95
 96	return ret;
 97}
 98
 99/* write single register */
100static int tda18218_wr_reg(struct tda18218_priv *priv, u8 reg, u8 val)
101{
102	return tda18218_wr_regs(priv, reg, &val, 1);
103}
104
105/* read single register */
106
107static int tda18218_rd_reg(struct tda18218_priv *priv, u8 reg, u8 *val)
108{
109	return tda18218_rd_regs(priv, reg, val, 1);
110}
111
112static int tda18218_set_params(struct dvb_frontend *fe,
113	struct dvb_frontend_parameters *params)
114{
115	struct tda18218_priv *priv = fe->tuner_priv;
116	int ret;
117	u8 buf[3], i, BP_Filter, LP_Fc;
118	u32 LO_Frac;
119	/* TODO: find out correct AGC algorithm */
120	u8 agc[][2] = {
121		{ R20_AGC11, 0x60 },
122		{ R23_AGC21, 0x02 },
123		{ R20_AGC11, 0xa0 },
124		{ R23_AGC21, 0x09 },
125		{ R20_AGC11, 0xe0 },
126		{ R23_AGC21, 0x0c },
127		{ R20_AGC11, 0x40 },
128		{ R23_AGC21, 0x01 },
129		{ R20_AGC11, 0x80 },
130		{ R23_AGC21, 0x08 },
131		{ R20_AGC11, 0xc0 },
132		{ R23_AGC21, 0x0b },
133		{ R24_AGC22, 0x1c },
134		{ R24_AGC22, 0x0c },
135	};
136
137	if (fe->ops.i2c_gate_ctrl)
138		fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
139
140	/* low-pass filter cut-off frequency */
141	switch (params->u.ofdm.bandwidth) {
142	case BANDWIDTH_6_MHZ:
143		LP_Fc = 0;
144		LO_Frac = params->frequency + 4000000;
145		break;
146	case BANDWIDTH_7_MHZ:
147		LP_Fc = 1;
148		LO_Frac = params->frequency + 3500000;
149		break;
150	case BANDWIDTH_8_MHZ:
151	default:
152		LP_Fc = 2;
153		LO_Frac = params->frequency + 4000000;
154		break;
155	}
156
157	/* band-pass filter */
158	if (LO_Frac < 188000000)
159		BP_Filter = 3;
160	else if (LO_Frac < 253000000)
161		BP_Filter = 4;
162	else if (LO_Frac < 343000000)
163		BP_Filter = 5;
164	else
165		BP_Filter = 6;
166
167	buf[0] = (priv->regs[R1A_IF1] & ~7) | BP_Filter; /* BP_Filter */
168	buf[1] = (priv->regs[R1B_IF2] & ~3) | LP_Fc; /* LP_Fc */
169	buf[2] = priv->regs[R1C_AGC2B];
170	ret = tda18218_wr_regs(priv, R1A_IF1, buf, 3);
171	if (ret)
172		goto error;
173
174	buf[0] = (LO_Frac / 1000) >> 12; /* LO_Frac_0 */
175	buf[1] = (LO_Frac / 1000) >> 4; /* LO_Frac_1 */
176	buf[2] = (LO_Frac / 1000) << 4 |
177		(priv->regs[R0C_MD5] & 0x0f); /* LO_Frac_2 */
178	ret = tda18218_wr_regs(priv, R0A_MD3, buf, 3);
179	if (ret)
180		goto error;
181
182	buf[0] = priv->regs[R0F_MD8] | (1 << 6); /* Freq_prog_Start */
183	ret = tda18218_wr_regs(priv, R0F_MD8, buf, 1);
184	if (ret)
185		goto error;
186
187	buf[0] = priv->regs[R0F_MD8] & ~(1 << 6); /* Freq_prog_Start */
188	ret = tda18218_wr_regs(priv, R0F_MD8, buf, 1);
189	if (ret)
190		goto error;
191
192	/* trigger AGC */
193	for (i = 0; i < ARRAY_SIZE(agc); i++) {
194		ret = tda18218_wr_reg(priv, agc[i][0], agc[i][1]);
195		if (ret)
196			goto error;
197	}
198
199error:
200	if (fe->ops.i2c_gate_ctrl)
201		fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
202
203	if (ret)
204		dbg("%s: failed ret:%d", __func__, ret);
205
206	return ret;
207}
208
209static int tda18218_sleep(struct dvb_frontend *fe)
210{
211	struct tda18218_priv *priv = fe->tuner_priv;
212	int ret;
213
214	if (fe->ops.i2c_gate_ctrl)
215		fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
216
217	/* standby */
218	ret = tda18218_wr_reg(priv, R17_PD1, priv->regs[R17_PD1] | (1 << 0));
219
220	if (fe->ops.i2c_gate_ctrl)
221		fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
222
223	if (ret)
224		dbg("%s: failed ret:%d", __func__, ret);
225
226	return ret;
227}
228
229static int tda18218_init(struct dvb_frontend *fe)
230{
231	struct tda18218_priv *priv = fe->tuner_priv;
232	int ret;
233
234	/* TODO: calibrations */
235
236	if (fe->ops.i2c_gate_ctrl)
237		fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
238
239	ret = tda18218_wr_regs(priv, R00_ID, priv->regs, TDA18218_NUM_REGS);
240
241	if (fe->ops.i2c_gate_ctrl)
242		fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
243
244	if (ret)
245		dbg("%s: failed ret:%d", __func__, ret);
246
247	return ret;
248}
249
250static int tda18218_release(struct dvb_frontend *fe)
251{
252	kfree(fe->tuner_priv);
253	fe->tuner_priv = NULL;
254	return 0;
255}
256
257static const struct dvb_tuner_ops tda18218_tuner_ops = {
258	.info = {
259		.name           = "NXP TDA18218",
260
261		.frequency_min  = 174000000,
262		.frequency_max  = 864000000,
263		.frequency_step =      1000,
264	},
265
266	.release       = tda18218_release,
267	.init          = tda18218_init,
268	.sleep         = tda18218_sleep,
269
270	.set_params    = tda18218_set_params,
271};
272
273struct dvb_frontend *tda18218_attach(struct dvb_frontend *fe,
274	struct i2c_adapter *i2c, struct tda18218_config *cfg)
275{
276	struct tda18218_priv *priv = NULL;
277	u8 val;
278	int ret;
279	/* chip default registers values */
280	static u8 def_regs[] = {
281		0xc0, 0x88, 0x00, 0x8e, 0x03, 0x00, 0x00, 0xd0, 0x00, 0x40,
282		0x00, 0x00, 0x07, 0xff, 0x84, 0x09, 0x00, 0x13, 0x00, 0x00,
283		0x01, 0x84, 0x09, 0xf0, 0x19, 0x0a, 0x8e, 0x69, 0x98, 0x01,
284		0x00, 0x58, 0x10, 0x40, 0x8c, 0x00, 0x0c, 0x48, 0x85, 0xc9,
285		0xa7, 0x00, 0x00, 0x00, 0x30, 0x81, 0x80, 0x00, 0x39, 0x00,
286		0x8a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xf6
287	};
288
289	priv = kzalloc(sizeof(struct tda18218_priv), GFP_KERNEL);
290	if (priv == NULL)
291		return NULL;
292
293	priv->cfg = cfg;
294	priv->i2c = i2c;
295	fe->tuner_priv = priv;
296
297	if (fe->ops.i2c_gate_ctrl)
298		fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
299
300	/* check if the tuner is there */
301	ret = tda18218_rd_reg(priv, R00_ID, &val);
302	dbg("%s: ret:%d chip ID:%02x", __func__, ret, val);
303	if (ret || val != def_regs[R00_ID]) {
304		kfree(priv);
305		return NULL;
306	}
307
308	info("NXP TDA18218HN successfully identified.");
309
310	memcpy(&fe->ops.tuner_ops, &tda18218_tuner_ops,
311		sizeof(struct dvb_tuner_ops));
312	memcpy(priv->regs, def_regs, sizeof(def_regs));
313
314	/* loop-through enabled chip default register values */
315	if (priv->cfg->loop_through) {
316		priv->regs[R17_PD1] = 0xb0;
317		priv->regs[R18_PD2] = 0x59;
318	}
319
320	/* standby */
321	ret = tda18218_wr_reg(priv, R17_PD1, priv->regs[R17_PD1] | (1 << 0));
322	if (ret)
323		dbg("%s: failed ret:%d", __func__, ret);
324
325	if (fe->ops.i2c_gate_ctrl)
326		fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
327
328	return fe;
329}
330EXPORT_SYMBOL(tda18218_attach);
331
332MODULE_DESCRIPTION("NXP TDA18218HN silicon tuner driver");
333MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
334MODULE_LICENSE("GPL");