xref: /linux/drivers/media/dvb-frontends/tda826x.c (revision 14c4bf3c6f7d76d2b2b50cc82f6830d6948f6faa)
19a0bf528SMauro Carvalho Chehab   /*
29a0bf528SMauro Carvalho Chehab      Driver for Philips tda8262/tda8263 DVBS Silicon tuners
39a0bf528SMauro Carvalho Chehab 
49a0bf528SMauro Carvalho Chehab      (c) 2006 Andrew de Quincey
59a0bf528SMauro Carvalho Chehab 
69a0bf528SMauro Carvalho Chehab      This program is free software; you can redistribute it and/or modify
79a0bf528SMauro Carvalho Chehab      it under the terms of the GNU General Public License as published by
89a0bf528SMauro Carvalho Chehab      the Free Software Foundation; either version 2 of the License, or
99a0bf528SMauro Carvalho Chehab      (at your option) any later version.
109a0bf528SMauro Carvalho Chehab 
119a0bf528SMauro Carvalho Chehab      This program is distributed in the hope that it will be useful,
129a0bf528SMauro Carvalho Chehab      but WITHOUT ANY WARRANTY; without even the implied warranty of
139a0bf528SMauro Carvalho Chehab      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
149a0bf528SMauro Carvalho Chehab 
159a0bf528SMauro Carvalho Chehab      GNU General Public License for more details.
169a0bf528SMauro Carvalho Chehab 
179a0bf528SMauro Carvalho Chehab      You should have received a copy of the GNU General Public License
189a0bf528SMauro Carvalho Chehab      along with this program; if not, write to the Free Software
199a0bf528SMauro Carvalho Chehab      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
209a0bf528SMauro Carvalho Chehab 
219a0bf528SMauro Carvalho Chehab   */
229a0bf528SMauro Carvalho Chehab 
239a0bf528SMauro Carvalho Chehab #include <linux/slab.h>
249a0bf528SMauro Carvalho Chehab #include <linux/module.h>
259a0bf528SMauro Carvalho Chehab #include <linux/dvb/frontend.h>
269a0bf528SMauro Carvalho Chehab #include <asm/types.h>
279a0bf528SMauro Carvalho Chehab 
289a0bf528SMauro Carvalho Chehab #include "tda826x.h"
299a0bf528SMauro Carvalho Chehab 
309a0bf528SMauro Carvalho Chehab static int debug;
319a0bf528SMauro Carvalho Chehab #define dprintk(args...) \
329a0bf528SMauro Carvalho Chehab 	do { \
339a0bf528SMauro Carvalho Chehab 		if (debug) printk(KERN_DEBUG "tda826x: " args); \
349a0bf528SMauro Carvalho Chehab 	} while (0)
359a0bf528SMauro Carvalho Chehab 
369a0bf528SMauro Carvalho Chehab struct tda826x_priv {
379a0bf528SMauro Carvalho Chehab 	/* i2c details */
389a0bf528SMauro Carvalho Chehab 	int i2c_address;
399a0bf528SMauro Carvalho Chehab 	struct i2c_adapter *i2c;
409a0bf528SMauro Carvalho Chehab 	u8 has_loopthrough:1;
419a0bf528SMauro Carvalho Chehab 	u32 frequency;
429a0bf528SMauro Carvalho Chehab };
439a0bf528SMauro Carvalho Chehab 
449a0bf528SMauro Carvalho Chehab static int tda826x_release(struct dvb_frontend *fe)
459a0bf528SMauro Carvalho Chehab {
469a0bf528SMauro Carvalho Chehab 	kfree(fe->tuner_priv);
479a0bf528SMauro Carvalho Chehab 	fe->tuner_priv = NULL;
489a0bf528SMauro Carvalho Chehab 	return 0;
499a0bf528SMauro Carvalho Chehab }
509a0bf528SMauro Carvalho Chehab 
519a0bf528SMauro Carvalho Chehab static int tda826x_sleep(struct dvb_frontend *fe)
529a0bf528SMauro Carvalho Chehab {
539a0bf528SMauro Carvalho Chehab 	struct tda826x_priv *priv = fe->tuner_priv;
549a0bf528SMauro Carvalho Chehab 	int ret;
559a0bf528SMauro Carvalho Chehab 	u8 buf [] = { 0x00, 0x8d };
569a0bf528SMauro Carvalho Chehab 	struct i2c_msg msg = { .addr = priv->i2c_address, .flags = 0, .buf = buf, .len = 2 };
579a0bf528SMauro Carvalho Chehab 
589a0bf528SMauro Carvalho Chehab 	dprintk("%s:\n", __func__);
599a0bf528SMauro Carvalho Chehab 
609a0bf528SMauro Carvalho Chehab 	if (!priv->has_loopthrough)
619a0bf528SMauro Carvalho Chehab 		buf[1] = 0xad;
629a0bf528SMauro Carvalho Chehab 
639a0bf528SMauro Carvalho Chehab 	if (fe->ops.i2c_gate_ctrl)
649a0bf528SMauro Carvalho Chehab 		fe->ops.i2c_gate_ctrl(fe, 1);
659a0bf528SMauro Carvalho Chehab 	if ((ret = i2c_transfer (priv->i2c, &msg, 1)) != 1) {
669a0bf528SMauro Carvalho Chehab 		dprintk("%s: i2c error\n", __func__);
679a0bf528SMauro Carvalho Chehab 	}
689a0bf528SMauro Carvalho Chehab 	if (fe->ops.i2c_gate_ctrl)
699a0bf528SMauro Carvalho Chehab 		fe->ops.i2c_gate_ctrl(fe, 0);
709a0bf528SMauro Carvalho Chehab 
719a0bf528SMauro Carvalho Chehab 	return (ret == 1) ? 0 : ret;
729a0bf528SMauro Carvalho Chehab }
739a0bf528SMauro Carvalho Chehab 
749a0bf528SMauro Carvalho Chehab static int tda826x_set_params(struct dvb_frontend *fe)
759a0bf528SMauro Carvalho Chehab {
769a0bf528SMauro Carvalho Chehab 	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
779a0bf528SMauro Carvalho Chehab 	struct tda826x_priv *priv = fe->tuner_priv;
789a0bf528SMauro Carvalho Chehab 	int ret;
799a0bf528SMauro Carvalho Chehab 	u32 div;
809a0bf528SMauro Carvalho Chehab 	u32 ksyms;
819a0bf528SMauro Carvalho Chehab 	u32 bandwidth;
829a0bf528SMauro Carvalho Chehab 	u8 buf [11];
839a0bf528SMauro Carvalho Chehab 	struct i2c_msg msg = { .addr = priv->i2c_address, .flags = 0, .buf = buf, .len = 11 };
849a0bf528SMauro Carvalho Chehab 
859a0bf528SMauro Carvalho Chehab 	dprintk("%s:\n", __func__);
869a0bf528SMauro Carvalho Chehab 
879a0bf528SMauro Carvalho Chehab 	div = (p->frequency + (1000-1)) / 1000;
889a0bf528SMauro Carvalho Chehab 
899a0bf528SMauro Carvalho Chehab 	/* BW = ((1 + RO) * SR/2 + 5) * 1.3      [SR in MSPS, BW in MHz] */
909a0bf528SMauro Carvalho Chehab 	/* with R0 = 0.35 and some transformations: */
919a0bf528SMauro Carvalho Chehab 	ksyms = p->symbol_rate / 1000;
929a0bf528SMauro Carvalho Chehab 	bandwidth = (878 * ksyms + 6500000) / 1000000 + 1;
939a0bf528SMauro Carvalho Chehab 	if (bandwidth < 5)
949a0bf528SMauro Carvalho Chehab 		bandwidth = 5;
959a0bf528SMauro Carvalho Chehab 	else if (bandwidth > 36)
969a0bf528SMauro Carvalho Chehab 		bandwidth = 36;
979a0bf528SMauro Carvalho Chehab 
989a0bf528SMauro Carvalho Chehab 	buf[0] = 0x00; // subaddress
999a0bf528SMauro Carvalho Chehab 	buf[1] = 0x09; // powerdown RSSI + the magic value 1
1009a0bf528SMauro Carvalho Chehab 	if (!priv->has_loopthrough)
1019a0bf528SMauro Carvalho Chehab 		buf[1] |= 0x20; // power down loopthrough if not needed
1029a0bf528SMauro Carvalho Chehab 	buf[2] = (1<<5) | 0x0b; // 1Mhz + 0.45 VCO
1039a0bf528SMauro Carvalho Chehab 	buf[3] = div >> 7;
1049a0bf528SMauro Carvalho Chehab 	buf[4] = div << 1;
1059a0bf528SMauro Carvalho Chehab 	buf[5] = ((bandwidth - 5) << 3) | 7; /* baseband cut-off */
1069a0bf528SMauro Carvalho Chehab 	buf[6] = 0xfe; // baseband gain 9 db + no RF attenuation
1079a0bf528SMauro Carvalho Chehab 	buf[7] = 0x83; // charge pumps at high, tests off
1089a0bf528SMauro Carvalho Chehab 	buf[8] = 0x80; // recommended value 4 for AMPVCO + disable ports.
1099a0bf528SMauro Carvalho Chehab 	buf[9] = 0x1a; // normal caltime + recommended values for SELTH + SELVTL
1109a0bf528SMauro Carvalho Chehab 	buf[10] = 0xd4; // recommended value 13 for BBIAS + unknown bit set on
1119a0bf528SMauro Carvalho Chehab 
1129a0bf528SMauro Carvalho Chehab 	if (fe->ops.i2c_gate_ctrl)
1139a0bf528SMauro Carvalho Chehab 		fe->ops.i2c_gate_ctrl(fe, 1);
1149a0bf528SMauro Carvalho Chehab 	if ((ret = i2c_transfer (priv->i2c, &msg, 1)) != 1) {
1159a0bf528SMauro Carvalho Chehab 		dprintk("%s: i2c error\n", __func__);
1169a0bf528SMauro Carvalho Chehab 	}
1179a0bf528SMauro Carvalho Chehab 	if (fe->ops.i2c_gate_ctrl)
1189a0bf528SMauro Carvalho Chehab 		fe->ops.i2c_gate_ctrl(fe, 0);
1199a0bf528SMauro Carvalho Chehab 
1209a0bf528SMauro Carvalho Chehab 	priv->frequency = div * 1000;
1219a0bf528SMauro Carvalho Chehab 
1229a0bf528SMauro Carvalho Chehab 	return (ret == 1) ? 0 : ret;
1239a0bf528SMauro Carvalho Chehab }
1249a0bf528SMauro Carvalho Chehab 
1259a0bf528SMauro Carvalho Chehab static int tda826x_get_frequency(struct dvb_frontend *fe, u32 *frequency)
1269a0bf528SMauro Carvalho Chehab {
1279a0bf528SMauro Carvalho Chehab 	struct tda826x_priv *priv = fe->tuner_priv;
1289a0bf528SMauro Carvalho Chehab 	*frequency = priv->frequency;
1299a0bf528SMauro Carvalho Chehab 	return 0;
1309a0bf528SMauro Carvalho Chehab }
1319a0bf528SMauro Carvalho Chehab 
132*14c4bf3cSJulia Lawall static const struct dvb_tuner_ops tda826x_tuner_ops = {
1339a0bf528SMauro Carvalho Chehab 	.info = {
1349a0bf528SMauro Carvalho Chehab 		.name = "Philips TDA826X",
1359a0bf528SMauro Carvalho Chehab 		.frequency_min = 950000,
1369a0bf528SMauro Carvalho Chehab 		.frequency_max = 2175000
1379a0bf528SMauro Carvalho Chehab 	},
1389a0bf528SMauro Carvalho Chehab 	.release = tda826x_release,
1399a0bf528SMauro Carvalho Chehab 	.sleep = tda826x_sleep,
1409a0bf528SMauro Carvalho Chehab 	.set_params = tda826x_set_params,
1419a0bf528SMauro Carvalho Chehab 	.get_frequency = tda826x_get_frequency,
1429a0bf528SMauro Carvalho Chehab };
1439a0bf528SMauro Carvalho Chehab 
1449a0bf528SMauro Carvalho Chehab struct dvb_frontend *tda826x_attach(struct dvb_frontend *fe, int addr, struct i2c_adapter *i2c, int has_loopthrough)
1459a0bf528SMauro Carvalho Chehab {
1469a0bf528SMauro Carvalho Chehab 	struct tda826x_priv *priv = NULL;
1479a0bf528SMauro Carvalho Chehab 	u8 b1 [] = { 0, 0 };
1489a0bf528SMauro Carvalho Chehab 	struct i2c_msg msg[2] = {
1499a0bf528SMauro Carvalho Chehab 		{ .addr = addr, .flags = 0,        .buf = NULL, .len = 0 },
1509a0bf528SMauro Carvalho Chehab 		{ .addr = addr, .flags = I2C_M_RD, .buf = b1, .len = 2 }
1519a0bf528SMauro Carvalho Chehab 	};
1529a0bf528SMauro Carvalho Chehab 	int ret;
1539a0bf528SMauro Carvalho Chehab 
1549a0bf528SMauro Carvalho Chehab 	dprintk("%s:\n", __func__);
1559a0bf528SMauro Carvalho Chehab 
1569a0bf528SMauro Carvalho Chehab 	if (fe->ops.i2c_gate_ctrl)
1579a0bf528SMauro Carvalho Chehab 		fe->ops.i2c_gate_ctrl(fe, 1);
1589a0bf528SMauro Carvalho Chehab 	ret = i2c_transfer (i2c, msg, 2);
1599a0bf528SMauro Carvalho Chehab 	if (fe->ops.i2c_gate_ctrl)
1609a0bf528SMauro Carvalho Chehab 		fe->ops.i2c_gate_ctrl(fe, 0);
1619a0bf528SMauro Carvalho Chehab 
1629a0bf528SMauro Carvalho Chehab 	if (ret != 2)
1639a0bf528SMauro Carvalho Chehab 		return NULL;
1649a0bf528SMauro Carvalho Chehab 	if (!(b1[1] & 0x80))
1659a0bf528SMauro Carvalho Chehab 		return NULL;
1669a0bf528SMauro Carvalho Chehab 
1679a0bf528SMauro Carvalho Chehab 	priv = kzalloc(sizeof(struct tda826x_priv), GFP_KERNEL);
1689a0bf528SMauro Carvalho Chehab 	if (priv == NULL)
1699a0bf528SMauro Carvalho Chehab 		return NULL;
1709a0bf528SMauro Carvalho Chehab 
1719a0bf528SMauro Carvalho Chehab 	priv->i2c_address = addr;
1729a0bf528SMauro Carvalho Chehab 	priv->i2c = i2c;
1739a0bf528SMauro Carvalho Chehab 	priv->has_loopthrough = has_loopthrough;
1749a0bf528SMauro Carvalho Chehab 
1759a0bf528SMauro Carvalho Chehab 	memcpy(&fe->ops.tuner_ops, &tda826x_tuner_ops, sizeof(struct dvb_tuner_ops));
1769a0bf528SMauro Carvalho Chehab 
1779a0bf528SMauro Carvalho Chehab 	fe->tuner_priv = priv;
1789a0bf528SMauro Carvalho Chehab 
1799a0bf528SMauro Carvalho Chehab 	return fe;
1809a0bf528SMauro Carvalho Chehab }
1819a0bf528SMauro Carvalho Chehab EXPORT_SYMBOL(tda826x_attach);
1829a0bf528SMauro Carvalho Chehab 
1839a0bf528SMauro Carvalho Chehab module_param(debug, int, 0644);
1849a0bf528SMauro Carvalho Chehab MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
1859a0bf528SMauro Carvalho Chehab 
1869a0bf528SMauro Carvalho Chehab MODULE_DESCRIPTION("DVB TDA826x driver");
1879a0bf528SMauro Carvalho Chehab MODULE_AUTHOR("Andrew de Quincey");
1889a0bf528SMauro Carvalho Chehab MODULE_LICENSE("GPL");
189