File Index
Files and directories
Sign in
FILE

fc0013.c

File preview with syntax highlighting.

linux-6.19.14/drivers/media/tuners/fc0013.c C
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 * Fitipower FC0013 tuner driver
  4 *
  5 * Copyright (C) 2012 Hans-Frieder Vogt <hfvogt@gmx.net>
  6 * partially based on driver code from Fitipower
  7 * Copyright (C) 2010 Fitipower Integrated Technology Inc
  8 */
  9
 10#include "fc0013.h"
 11#include "fc0013-priv.h"
 12
 13static int fc0013_writereg(struct fc0013_priv *priv, u8 reg, u8 val)
 14{
 15	u8 buf[2] = {reg, val};
 16	struct i2c_msg msg = {
 17		.addr = priv->addr, .flags = 0, .buf = buf, .len = 2
 18	};
 19
 20	if (i2c_transfer(priv->i2c, &msg, 1) != 1) {
 21		err("I2C write reg failed, reg: %02x, val: %02x", reg, val);
 22		return -EREMOTEIO;
 23	}
 24	return 0;
 25}
 26
 27static int fc0013_readreg(struct fc0013_priv *priv, u8 reg, u8 *val)
 28{
 29	struct i2c_msg msg[2] = {
 30		{ .addr = priv->addr, .flags = 0, .buf = &reg, .len = 1 },
 31		{ .addr = priv->addr, .flags = I2C_M_RD, .buf = val, .len = 1 },
 32	};
 33
 34	if (i2c_transfer(priv->i2c, msg, 2) != 2) {
 35		err("I2C read reg failed, reg: %02x", reg);
 36		return -EREMOTEIO;
 37	}
 38	return 0;
 39}
 40
 41static void fc0013_release(struct dvb_frontend *fe)
 42{
 43	kfree(fe->tuner_priv);
 44	fe->tuner_priv = NULL;
 45}
 46
 47static int fc0013_init(struct dvb_frontend *fe)
 48{
 49	struct fc0013_priv *priv = fe->tuner_priv;
 50	int i, ret = 0;
 51	unsigned char reg[] = {
 52		0x00,	/* reg. 0x00: dummy */
 53		0x09,	/* reg. 0x01 */
 54		0x16,	/* reg. 0x02 */
 55		0x00,	/* reg. 0x03 */
 56		0x00,	/* reg. 0x04 */
 57		0x17,	/* reg. 0x05 */
 58		0x02,	/* reg. 0x06 */
 59		0x0a,	/* reg. 0x07: CHECK */
 60		0xff,	/* reg. 0x08: AGC Clock divide by 256, AGC gain 1/256,
 61			   Loop Bw 1/8 */
 62		0x6f,	/* reg. 0x09: enable LoopThrough */
 63		0xb8,	/* reg. 0x0a: Disable LO Test Buffer */
 64		0x82,	/* reg. 0x0b: CHECK */
 65		0xfc,	/* reg. 0x0c: depending on AGC Up-Down mode, may need 0xf8 */
 66		0x01,	/* reg. 0x0d: AGC Not Forcing & LNA Forcing, may need 0x02 */
 67		0x00,	/* reg. 0x0e */
 68		0x00,	/* reg. 0x0f */
 69		0x00,	/* reg. 0x10 */
 70		0x00,	/* reg. 0x11 */
 71		0x00,	/* reg. 0x12 */
 72		0x00,	/* reg. 0x13 */
 73		0x50,	/* reg. 0x14: DVB-t High Gain, UHF.
 74			   Middle Gain: 0x48, Low Gain: 0x40 */
 75		0x01,	/* reg. 0x15 */
 76	};
 77
 78	switch (priv->xtal_freq) {
 79	case FC_XTAL_27_MHZ:
 80	case FC_XTAL_28_8_MHZ:
 81		reg[0x07] |= 0x20;
 82		break;
 83	case FC_XTAL_36_MHZ:
 84	default:
 85		break;
 86	}
 87
 88	if (priv->dual_master)
 89		reg[0x0c] |= 0x02;
 90
 91	if (fe->ops.i2c_gate_ctrl)
 92		fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
 93
 94	for (i = 1; i < sizeof(reg); i++) {
 95		ret = fc0013_writereg(priv, i, reg[i]);
 96		if (ret)
 97			break;
 98	}
 99
100	if (fe->ops.i2c_gate_ctrl)
101		fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
102
103	if (ret)
104		err("fc0013_writereg failed: %d", ret);
105
106	return ret;
107}
108
109static int fc0013_sleep(struct dvb_frontend *fe)
110{
111	/* nothing to do here */
112	return 0;
113}
114
115static int fc0013_set_vhf_track(struct fc0013_priv *priv, u32 freq)
116{
117	int ret;
118	u8 tmp;
119
120	ret = fc0013_readreg(priv, 0x1d, &tmp);
121	if (ret)
122		goto error_out;
123	tmp &= 0xe3;
124	if (freq <= 177500) {		/* VHF Track: 7 */
125		ret = fc0013_writereg(priv, 0x1d, tmp | 0x1c);
126	} else if (freq <= 184500) {	/* VHF Track: 6 */
127		ret = fc0013_writereg(priv, 0x1d, tmp | 0x18);
128	} else if (freq <= 191500) {	/* VHF Track: 5 */
129		ret = fc0013_writereg(priv, 0x1d, tmp | 0x14);
130	} else if (freq <= 198500) {	/* VHF Track: 4 */
131		ret = fc0013_writereg(priv, 0x1d, tmp | 0x10);
132	} else if (freq <= 205500) {	/* VHF Track: 3 */
133		ret = fc0013_writereg(priv, 0x1d, tmp | 0x0c);
134	} else if (freq <= 219500) {	/* VHF Track: 2 */
135		ret = fc0013_writereg(priv, 0x1d, tmp | 0x08);
136	} else if (freq < 300000) {	/* VHF Track: 1 */
137		ret = fc0013_writereg(priv, 0x1d, tmp | 0x04);
138	} else {			/* UHF and GPS */
139		ret = fc0013_writereg(priv, 0x1d, tmp | 0x1c);
140	}
141error_out:
142	return ret;
143}
144
145static int fc0013_set_params(struct dvb_frontend *fe)
146{
147	struct fc0013_priv *priv = fe->tuner_priv;
148	int i, ret = 0;
149	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
150	u32 freq = p->frequency / 1000;
151	u32 delsys = p->delivery_system;
152	unsigned char reg[7], am, pm, multi, tmp;
153	unsigned long f_vco;
154	unsigned short xtal_freq_khz_2, xin, xdiv;
155	bool vco_select = false;
156
157	if (fe->callback) {
158		ret = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER,
159			FC_FE_CALLBACK_VHF_ENABLE, (freq > 300000 ? 0 : 1));
160		if (ret)
161			goto exit;
162	}
163
164	switch (priv->xtal_freq) {
165	case FC_XTAL_27_MHZ:
166		xtal_freq_khz_2 = 27000 / 2;
167		break;
168	case FC_XTAL_36_MHZ:
169		xtal_freq_khz_2 = 36000 / 2;
170		break;
171	case FC_XTAL_28_8_MHZ:
172	default:
173		xtal_freq_khz_2 = 28800 / 2;
174		break;
175	}
176
177	if (fe->ops.i2c_gate_ctrl)
178		fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
179
180	/* set VHF track */
181	ret = fc0013_set_vhf_track(priv, freq);
182	if (ret)
183		goto exit;
184
185	if (freq < 300000) {
186		/* enable VHF filter */
187		ret = fc0013_readreg(priv, 0x07, &tmp);
188		if (ret)
189			goto exit;
190		ret = fc0013_writereg(priv, 0x07, tmp | 0x10);
191		if (ret)
192			goto exit;
193
194		/* disable UHF & disable GPS */
195		ret = fc0013_readreg(priv, 0x14, &tmp);
196		if (ret)
197			goto exit;
198		ret = fc0013_writereg(priv, 0x14, tmp & 0x1f);
199		if (ret)
200			goto exit;
201	} else if (freq <= 862000) {
202		/* disable VHF filter */
203		ret = fc0013_readreg(priv, 0x07, &tmp);
204		if (ret)
205			goto exit;
206		ret = fc0013_writereg(priv, 0x07, tmp & 0xef);
207		if (ret)
208			goto exit;
209
210		/* enable UHF & disable GPS */
211		ret = fc0013_readreg(priv, 0x14, &tmp);
212		if (ret)
213			goto exit;
214		ret = fc0013_writereg(priv, 0x14, (tmp & 0x1f) | 0x40);
215		if (ret)
216			goto exit;
217	} else {
218		/* disable VHF filter */
219		ret = fc0013_readreg(priv, 0x07, &tmp);
220		if (ret)
221			goto exit;
222		ret = fc0013_writereg(priv, 0x07, tmp & 0xef);
223		if (ret)
224			goto exit;
225
226		/* disable UHF & enable GPS */
227		ret = fc0013_readreg(priv, 0x14, &tmp);
228		if (ret)
229			goto exit;
230		ret = fc0013_writereg(priv, 0x14, (tmp & 0x1f) | 0x20);
231		if (ret)
232			goto exit;
233	}
234
235	/* select frequency divider and the frequency of VCO */
236	if (freq < 37084) {		/* freq * 96 < 3560000 */
237		multi = 96;
238		reg[5] = 0x82;
239		reg[6] = 0x00;
240	} else if (freq < 55625) {	/* freq * 64 < 3560000 */
241		multi = 64;
242		reg[5] = 0x02;
243		reg[6] = 0x02;
244	} else if (freq < 74167) {	/* freq * 48 < 3560000 */
245		multi = 48;
246		reg[5] = 0x42;
247		reg[6] = 0x00;
248	} else if (freq < 111250) {	/* freq * 32 < 3560000 */
249		multi = 32;
250		reg[5] = 0x82;
251		reg[6] = 0x02;
252	} else if (freq < 148334) {	/* freq * 24 < 3560000 */
253		multi = 24;
254		reg[5] = 0x22;
255		reg[6] = 0x00;
256	} else if (freq < 222500) {	/* freq * 16 < 3560000 */
257		multi = 16;
258		reg[5] = 0x42;
259		reg[6] = 0x02;
260	} else if (freq < 296667) {	/* freq * 12 < 3560000 */
261		multi = 12;
262		reg[5] = 0x12;
263		reg[6] = 0x00;
264	} else if (freq < 445000) {	/* freq * 8 < 3560000 */
265		multi = 8;
266		reg[5] = 0x22;
267		reg[6] = 0x02;
268	} else if (freq < 593334) {	/* freq * 6 < 3560000 */
269		multi = 6;
270		reg[5] = 0x0a;
271		reg[6] = 0x00;
272	} else if (freq < 950000) {	/* freq * 4 < 3800000 */
273		multi = 4;
274		reg[5] = 0x12;
275		reg[6] = 0x02;
276	} else {
277		multi = 2;
278		reg[5] = 0x0a;
279		reg[6] = 0x02;
280	}
281
282	f_vco = freq * multi;
283
284	if (f_vco >= 3060000) {
285		reg[6] |= 0x08;
286		vco_select = true;
287	}
288
289	if (freq >= 45000) {
290		/* From divided value (XDIV) determined the FA and FP value */
291		xdiv = (unsigned short)(f_vco / xtal_freq_khz_2);
292		if ((f_vco - xdiv * xtal_freq_khz_2) >= (xtal_freq_khz_2 / 2))
293			xdiv++;
294
295		pm = (unsigned char)(xdiv / 8);
296		am = (unsigned char)(xdiv - (8 * pm));
297
298		if (am < 2) {
299			reg[1] = am + 8;
300			reg[2] = pm - 1;
301		} else {
302			reg[1] = am;
303			reg[2] = pm;
304		}
305	} else {
306		/* fix for frequency less than 45 MHz */
307		reg[1] = 0x06;
308		reg[2] = 0x11;
309	}
310
311	/* fix clock out */
312	reg[6] |= 0x20;
313
314	/* From VCO frequency determines the XIN ( fractional part of Delta
315	   Sigma PLL) and divided value (XDIV) */
316	xin = (unsigned short)(f_vco - (f_vco / xtal_freq_khz_2) * xtal_freq_khz_2);
317	xin = (xin << 15) / xtal_freq_khz_2;
318	if (xin >= 16384)
319		xin += 32768;
320
321	reg[3] = xin >> 8;
322	reg[4] = xin & 0xff;
323
324	if (delsys == SYS_DVBT) {
325		reg[6] &= 0x3f; /* bits 6 and 7 describe the bandwidth */
326		switch (p->bandwidth_hz) {
327		case 6000000:
328			reg[6] |= 0x80;
329			break;
330		case 7000000:
331			reg[6] |= 0x40;
332			break;
333		case 8000000:
334		default:
335			break;
336		}
337	} else {
338		err("%s: modulation type not supported!", __func__);
339		return -EINVAL;
340	}
341
342	/* modified for Realtek demod */
343	reg[5] |= 0x07;
344
345	for (i = 1; i <= 6; i++) {
346		ret = fc0013_writereg(priv, i, reg[i]);
347		if (ret)
348			goto exit;
349	}
350
351	ret = fc0013_readreg(priv, 0x11, &tmp);
352	if (ret)
353		goto exit;
354	if (multi == 64)
355		ret = fc0013_writereg(priv, 0x11, tmp | 0x04);
356	else
357		ret = fc0013_writereg(priv, 0x11, tmp & 0xfb);
358	if (ret)
359		goto exit;
360
361	/* VCO Calibration */
362	ret = fc0013_writereg(priv, 0x0e, 0x80);
363	if (!ret)
364		ret = fc0013_writereg(priv, 0x0e, 0x00);
365
366	/* VCO Re-Calibration if needed */
367	if (!ret)
368		ret = fc0013_writereg(priv, 0x0e, 0x00);
369
370	if (!ret) {
371		msleep(10);
372		ret = fc0013_readreg(priv, 0x0e, &tmp);
373	}
374	if (ret)
375		goto exit;
376
377	/* vco selection */
378	tmp &= 0x3f;
379
380	if (vco_select) {
381		if (tmp > 0x3c) {
382			reg[6] &= ~0x08;
383			ret = fc0013_writereg(priv, 0x06, reg[6]);
384			if (!ret)
385				ret = fc0013_writereg(priv, 0x0e, 0x80);
386			if (!ret)
387				ret = fc0013_writereg(priv, 0x0e, 0x00);
388		}
389	} else {
390		if (tmp < 0x02) {
391			reg[6] |= 0x08;
392			ret = fc0013_writereg(priv, 0x06, reg[6]);
393			if (!ret)
394				ret = fc0013_writereg(priv, 0x0e, 0x80);
395			if (!ret)
396				ret = fc0013_writereg(priv, 0x0e, 0x00);
397		}
398	}
399
400	priv->frequency = p->frequency;
401	priv->bandwidth = p->bandwidth_hz;
402
403exit:
404	if (fe->ops.i2c_gate_ctrl)
405		fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
406	if (ret)
407		warn("%s: failed: %d", __func__, ret);
408	return ret;
409}
410
411static int fc0013_get_frequency(struct dvb_frontend *fe, u32 *frequency)
412{
413	struct fc0013_priv *priv = fe->tuner_priv;
414	*frequency = priv->frequency;
415	return 0;
416}
417
418static int fc0013_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
419{
420	/* always ? */
421	*frequency = 0;
422	return 0;
423}
424
425static int fc0013_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
426{
427	struct fc0013_priv *priv = fe->tuner_priv;
428	*bandwidth = priv->bandwidth;
429	return 0;
430}
431
432#define INPUT_ADC_LEVEL	-8
433
434static int fc0013_get_rf_strength(struct dvb_frontend *fe, u16 *strength)
435{
436	struct fc0013_priv *priv = fe->tuner_priv;
437	int ret;
438	unsigned char tmp;
439	int int_temp, lna_gain, int_lna, tot_agc_gain, power;
440	static const int fc0013_lna_gain_table[] = {
441		/* low gain */
442		-63, -58, -99, -73,
443		-63, -65, -54, -60,
444		/* middle gain */
445		 71,  70,  68,  67,
446		 65,  63,  61,  58,
447		/* high gain */
448		197, 191, 188, 186,
449		184, 182, 181, 179,
450	};
451
452	if (fe->ops.i2c_gate_ctrl)
453		fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
454
455	ret = fc0013_writereg(priv, 0x13, 0x00);
456	if (ret)
457		goto err;
458
459	ret = fc0013_readreg(priv, 0x13, &tmp);
460	if (ret)
461		goto err;
462	int_temp = tmp;
463
464	ret = fc0013_readreg(priv, 0x14, &tmp);
465	if (ret)
466		goto err;
467	lna_gain = tmp & 0x1f;
468
469	if (fe->ops.i2c_gate_ctrl)
470		fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
471
472	if (lna_gain < ARRAY_SIZE(fc0013_lna_gain_table)) {
473		int_lna = fc0013_lna_gain_table[lna_gain];
474		tot_agc_gain = (abs((int_temp >> 5) - 7) - 2 +
475				(int_temp & 0x1f)) * 2;
476		power = INPUT_ADC_LEVEL - tot_agc_gain - int_lna / 10;
477
478		if (power >= 45)
479			*strength = 255;	/* 100% */
480		else if (power < -95)
481			*strength = 0;
482		else
483			*strength = (power + 95) * 255 / 140;
484
485		*strength |= *strength << 8;
486	} else {
487		ret = -1;
488	}
489
490	goto exit;
491
492err:
493	if (fe->ops.i2c_gate_ctrl)
494		fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
495exit:
496	if (ret)
497		warn("%s: failed: %d", __func__, ret);
498	return ret;
499}
500
501static const struct dvb_tuner_ops fc0013_tuner_ops = {
502	.info = {
503		.name		  = "Fitipower FC0013",
504
505		.frequency_min_hz =   37 * MHz,	/* estimate */
506		.frequency_max_hz = 1680 * MHz,	/* CHECK */
507	},
508
509	.release	= fc0013_release,
510
511	.init		= fc0013_init,
512	.sleep		= fc0013_sleep,
513
514	.set_params	= fc0013_set_params,
515
516	.get_frequency	= fc0013_get_frequency,
517	.get_if_frequency = fc0013_get_if_frequency,
518	.get_bandwidth	= fc0013_get_bandwidth,
519
520	.get_rf_strength = fc0013_get_rf_strength,
521};
522
523struct dvb_frontend *fc0013_attach(struct dvb_frontend *fe,
524	struct i2c_adapter *i2c, u8 i2c_address, int dual_master,
525	enum fc001x_xtal_freq xtal_freq)
526{
527	struct fc0013_priv *priv = NULL;
528
529	priv = kzalloc(sizeof(struct fc0013_priv), GFP_KERNEL);
530	if (priv == NULL)
531		return NULL;
532
533	priv->i2c = i2c;
534	priv->dual_master = dual_master;
535	priv->addr = i2c_address;
536	priv->xtal_freq = xtal_freq;
537
538	info("Fitipower FC0013 successfully attached.");
539
540	fe->tuner_priv = priv;
541
542	memcpy(&fe->ops.tuner_ops, &fc0013_tuner_ops,
543		sizeof(struct dvb_tuner_ops));
544
545	return fe;
546}
547EXPORT_SYMBOL_GPL(fc0013_attach);
548
549MODULE_DESCRIPTION("Fitipower FC0013 silicon tuner driver");
550MODULE_AUTHOR("Hans-Frieder Vogt <hfvogt@gmx.net>");
551MODULE_LICENSE("GPL");
552MODULE_VERSION("0.2");
~/ Read-only file preview.