xref: /openbmc/linux/drivers/media/tuners/tua9001.c (revision ccae7af2bf07dfef69cc2eb6ebc9e1ff15addfbd)
1*ccae7af2SMauro Carvalho Chehab /*
2*ccae7af2SMauro Carvalho Chehab  * Infineon TUA 9001 silicon tuner driver
3*ccae7af2SMauro Carvalho Chehab  *
4*ccae7af2SMauro Carvalho Chehab  * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
5*ccae7af2SMauro Carvalho Chehab  *
6*ccae7af2SMauro Carvalho Chehab  *    This program is free software; you can redistribute it and/or modify
7*ccae7af2SMauro Carvalho Chehab  *    it under the terms of the GNU General Public License as published by
8*ccae7af2SMauro Carvalho Chehab  *    the Free Software Foundation; either version 2 of the License, or
9*ccae7af2SMauro Carvalho Chehab  *    (at your option) any later version.
10*ccae7af2SMauro Carvalho Chehab  *
11*ccae7af2SMauro Carvalho Chehab  *    This program is distributed in the hope that it will be useful,
12*ccae7af2SMauro Carvalho Chehab  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
13*ccae7af2SMauro Carvalho Chehab  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14*ccae7af2SMauro Carvalho Chehab  *    GNU General Public License for more details.
15*ccae7af2SMauro Carvalho Chehab  *
16*ccae7af2SMauro Carvalho Chehab  *    You should have received a copy of the GNU General Public License along
17*ccae7af2SMauro Carvalho Chehab  *    with this program; if not, write to the Free Software Foundation, Inc.,
18*ccae7af2SMauro Carvalho Chehab  *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19*ccae7af2SMauro Carvalho Chehab  */
20*ccae7af2SMauro Carvalho Chehab 
21*ccae7af2SMauro Carvalho Chehab #include "tua9001.h"
22*ccae7af2SMauro Carvalho Chehab #include "tua9001_priv.h"
23*ccae7af2SMauro Carvalho Chehab 
24*ccae7af2SMauro Carvalho Chehab /* write register */
25*ccae7af2SMauro Carvalho Chehab static int tua9001_wr_reg(struct tua9001_priv *priv, u8 reg, u16 val)
26*ccae7af2SMauro Carvalho Chehab {
27*ccae7af2SMauro Carvalho Chehab 	int ret;
28*ccae7af2SMauro Carvalho Chehab 	u8 buf[3] = { reg, (val >> 8) & 0xff, (val >> 0) & 0xff };
29*ccae7af2SMauro Carvalho Chehab 	struct i2c_msg msg[1] = {
30*ccae7af2SMauro Carvalho Chehab 		{
31*ccae7af2SMauro Carvalho Chehab 			.addr = priv->cfg->i2c_addr,
32*ccae7af2SMauro Carvalho Chehab 			.flags = 0,
33*ccae7af2SMauro Carvalho Chehab 			.len = sizeof(buf),
34*ccae7af2SMauro Carvalho Chehab 			.buf = buf,
35*ccae7af2SMauro Carvalho Chehab 		}
36*ccae7af2SMauro Carvalho Chehab 	};
37*ccae7af2SMauro Carvalho Chehab 
38*ccae7af2SMauro Carvalho Chehab 	ret = i2c_transfer(priv->i2c, msg, 1);
39*ccae7af2SMauro Carvalho Chehab 	if (ret == 1) {
40*ccae7af2SMauro Carvalho Chehab 		ret = 0;
41*ccae7af2SMauro Carvalho Chehab 	} else {
42*ccae7af2SMauro Carvalho Chehab 		printk(KERN_WARNING "%s: I2C wr failed=%d reg=%02x\n",
43*ccae7af2SMauro Carvalho Chehab 				__func__, ret, reg);
44*ccae7af2SMauro Carvalho Chehab 		ret = -EREMOTEIO;
45*ccae7af2SMauro Carvalho Chehab 	}
46*ccae7af2SMauro Carvalho Chehab 
47*ccae7af2SMauro Carvalho Chehab 	return ret;
48*ccae7af2SMauro Carvalho Chehab }
49*ccae7af2SMauro Carvalho Chehab 
50*ccae7af2SMauro Carvalho Chehab static int tua9001_release(struct dvb_frontend *fe)
51*ccae7af2SMauro Carvalho Chehab {
52*ccae7af2SMauro Carvalho Chehab 	kfree(fe->tuner_priv);
53*ccae7af2SMauro Carvalho Chehab 	fe->tuner_priv = NULL;
54*ccae7af2SMauro Carvalho Chehab 
55*ccae7af2SMauro Carvalho Chehab 	return 0;
56*ccae7af2SMauro Carvalho Chehab }
57*ccae7af2SMauro Carvalho Chehab 
58*ccae7af2SMauro Carvalho Chehab static int tua9001_init(struct dvb_frontend *fe)
59*ccae7af2SMauro Carvalho Chehab {
60*ccae7af2SMauro Carvalho Chehab 	struct tua9001_priv *priv = fe->tuner_priv;
61*ccae7af2SMauro Carvalho Chehab 	int ret = 0;
62*ccae7af2SMauro Carvalho Chehab 	u8 i;
63*ccae7af2SMauro Carvalho Chehab 	struct reg_val data[] = {
64*ccae7af2SMauro Carvalho Chehab 		{ 0x1e, 0x6512 },
65*ccae7af2SMauro Carvalho Chehab 		{ 0x25, 0xb888 },
66*ccae7af2SMauro Carvalho Chehab 		{ 0x39, 0x5460 },
67*ccae7af2SMauro Carvalho Chehab 		{ 0x3b, 0x00c0 },
68*ccae7af2SMauro Carvalho Chehab 		{ 0x3a, 0xf000 },
69*ccae7af2SMauro Carvalho Chehab 		{ 0x08, 0x0000 },
70*ccae7af2SMauro Carvalho Chehab 		{ 0x32, 0x0030 },
71*ccae7af2SMauro Carvalho Chehab 		{ 0x41, 0x703a },
72*ccae7af2SMauro Carvalho Chehab 		{ 0x40, 0x1c78 },
73*ccae7af2SMauro Carvalho Chehab 		{ 0x2c, 0x1c00 },
74*ccae7af2SMauro Carvalho Chehab 		{ 0x36, 0xc013 },
75*ccae7af2SMauro Carvalho Chehab 		{ 0x37, 0x6f18 },
76*ccae7af2SMauro Carvalho Chehab 		{ 0x27, 0x0008 },
77*ccae7af2SMauro Carvalho Chehab 		{ 0x2a, 0x0001 },
78*ccae7af2SMauro Carvalho Chehab 		{ 0x34, 0x0a40 },
79*ccae7af2SMauro Carvalho Chehab 	};
80*ccae7af2SMauro Carvalho Chehab 
81*ccae7af2SMauro Carvalho Chehab 	if (fe->ops.i2c_gate_ctrl)
82*ccae7af2SMauro Carvalho Chehab 		fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c-gate */
83*ccae7af2SMauro Carvalho Chehab 
84*ccae7af2SMauro Carvalho Chehab 	for (i = 0; i < ARRAY_SIZE(data); i++) {
85*ccae7af2SMauro Carvalho Chehab 		ret = tua9001_wr_reg(priv, data[i].reg, data[i].val);
86*ccae7af2SMauro Carvalho Chehab 		if (ret)
87*ccae7af2SMauro Carvalho Chehab 			break;
88*ccae7af2SMauro Carvalho Chehab 	}
89*ccae7af2SMauro Carvalho Chehab 
90*ccae7af2SMauro Carvalho Chehab 	if (fe->ops.i2c_gate_ctrl)
91*ccae7af2SMauro Carvalho Chehab 		fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c-gate */
92*ccae7af2SMauro Carvalho Chehab 
93*ccae7af2SMauro Carvalho Chehab 	if (ret < 0)
94*ccae7af2SMauro Carvalho Chehab 		pr_debug("%s: failed=%d\n", __func__, ret);
95*ccae7af2SMauro Carvalho Chehab 
96*ccae7af2SMauro Carvalho Chehab 	return ret;
97*ccae7af2SMauro Carvalho Chehab }
98*ccae7af2SMauro Carvalho Chehab 
99*ccae7af2SMauro Carvalho Chehab static int tua9001_set_params(struct dvb_frontend *fe)
100*ccae7af2SMauro Carvalho Chehab {
101*ccae7af2SMauro Carvalho Chehab 	struct tua9001_priv *priv = fe->tuner_priv;
102*ccae7af2SMauro Carvalho Chehab 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
103*ccae7af2SMauro Carvalho Chehab 	int ret, i;
104*ccae7af2SMauro Carvalho Chehab 	u16 val;
105*ccae7af2SMauro Carvalho Chehab 	u32 frequency;
106*ccae7af2SMauro Carvalho Chehab 	struct reg_val data[2];
107*ccae7af2SMauro Carvalho Chehab 
108*ccae7af2SMauro Carvalho Chehab 	pr_debug("%s: delivery_system=%d frequency=%d bandwidth_hz=%d\n",
109*ccae7af2SMauro Carvalho Chehab 			__func__, c->delivery_system, c->frequency,
110*ccae7af2SMauro Carvalho Chehab 			c->bandwidth_hz);
111*ccae7af2SMauro Carvalho Chehab 
112*ccae7af2SMauro Carvalho Chehab 	switch (c->delivery_system) {
113*ccae7af2SMauro Carvalho Chehab 	case SYS_DVBT:
114*ccae7af2SMauro Carvalho Chehab 		switch (c->bandwidth_hz) {
115*ccae7af2SMauro Carvalho Chehab 		case 8000000:
116*ccae7af2SMauro Carvalho Chehab 			val  = 0x0000;
117*ccae7af2SMauro Carvalho Chehab 			break;
118*ccae7af2SMauro Carvalho Chehab 		case 7000000:
119*ccae7af2SMauro Carvalho Chehab 			val  = 0x1000;
120*ccae7af2SMauro Carvalho Chehab 			break;
121*ccae7af2SMauro Carvalho Chehab 		case 6000000:
122*ccae7af2SMauro Carvalho Chehab 			val  = 0x2000;
123*ccae7af2SMauro Carvalho Chehab 			break;
124*ccae7af2SMauro Carvalho Chehab 		case 5000000:
125*ccae7af2SMauro Carvalho Chehab 			val  = 0x3000;
126*ccae7af2SMauro Carvalho Chehab 			break;
127*ccae7af2SMauro Carvalho Chehab 		default:
128*ccae7af2SMauro Carvalho Chehab 			ret = -EINVAL;
129*ccae7af2SMauro Carvalho Chehab 			goto err;
130*ccae7af2SMauro Carvalho Chehab 		}
131*ccae7af2SMauro Carvalho Chehab 		break;
132*ccae7af2SMauro Carvalho Chehab 	default:
133*ccae7af2SMauro Carvalho Chehab 		ret = -EINVAL;
134*ccae7af2SMauro Carvalho Chehab 		goto err;
135*ccae7af2SMauro Carvalho Chehab 	}
136*ccae7af2SMauro Carvalho Chehab 
137*ccae7af2SMauro Carvalho Chehab 	data[0].reg = 0x04;
138*ccae7af2SMauro Carvalho Chehab 	data[0].val = val;
139*ccae7af2SMauro Carvalho Chehab 
140*ccae7af2SMauro Carvalho Chehab 	frequency = (c->frequency - 150000000);
141*ccae7af2SMauro Carvalho Chehab 	frequency /= 100;
142*ccae7af2SMauro Carvalho Chehab 	frequency *= 48;
143*ccae7af2SMauro Carvalho Chehab 	frequency /= 10000;
144*ccae7af2SMauro Carvalho Chehab 
145*ccae7af2SMauro Carvalho Chehab 	data[1].reg = 0x1f;
146*ccae7af2SMauro Carvalho Chehab 	data[1].val = frequency;
147*ccae7af2SMauro Carvalho Chehab 
148*ccae7af2SMauro Carvalho Chehab 	if (fe->ops.i2c_gate_ctrl)
149*ccae7af2SMauro Carvalho Chehab 		fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c-gate */
150*ccae7af2SMauro Carvalho Chehab 
151*ccae7af2SMauro Carvalho Chehab 	for (i = 0; i < ARRAY_SIZE(data); i++) {
152*ccae7af2SMauro Carvalho Chehab 		ret = tua9001_wr_reg(priv, data[i].reg, data[i].val);
153*ccae7af2SMauro Carvalho Chehab 		if (ret < 0)
154*ccae7af2SMauro Carvalho Chehab 			break;
155*ccae7af2SMauro Carvalho Chehab 	}
156*ccae7af2SMauro Carvalho Chehab 
157*ccae7af2SMauro Carvalho Chehab 	if (fe->ops.i2c_gate_ctrl)
158*ccae7af2SMauro Carvalho Chehab 		fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c-gate */
159*ccae7af2SMauro Carvalho Chehab 
160*ccae7af2SMauro Carvalho Chehab err:
161*ccae7af2SMauro Carvalho Chehab 	if (ret < 0)
162*ccae7af2SMauro Carvalho Chehab 		pr_debug("%s: failed=%d\n", __func__, ret);
163*ccae7af2SMauro Carvalho Chehab 
164*ccae7af2SMauro Carvalho Chehab 	return ret;
165*ccae7af2SMauro Carvalho Chehab }
166*ccae7af2SMauro Carvalho Chehab 
167*ccae7af2SMauro Carvalho Chehab static int tua9001_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
168*ccae7af2SMauro Carvalho Chehab {
169*ccae7af2SMauro Carvalho Chehab 	*frequency = 0; /* Zero-IF */
170*ccae7af2SMauro Carvalho Chehab 
171*ccae7af2SMauro Carvalho Chehab 	return 0;
172*ccae7af2SMauro Carvalho Chehab }
173*ccae7af2SMauro Carvalho Chehab 
174*ccae7af2SMauro Carvalho Chehab static const struct dvb_tuner_ops tua9001_tuner_ops = {
175*ccae7af2SMauro Carvalho Chehab 	.info = {
176*ccae7af2SMauro Carvalho Chehab 		.name           = "Infineon TUA 9001",
177*ccae7af2SMauro Carvalho Chehab 
178*ccae7af2SMauro Carvalho Chehab 		.frequency_min  = 170000000,
179*ccae7af2SMauro Carvalho Chehab 		.frequency_max  = 862000000,
180*ccae7af2SMauro Carvalho Chehab 		.frequency_step = 0,
181*ccae7af2SMauro Carvalho Chehab 	},
182*ccae7af2SMauro Carvalho Chehab 
183*ccae7af2SMauro Carvalho Chehab 	.release = tua9001_release,
184*ccae7af2SMauro Carvalho Chehab 
185*ccae7af2SMauro Carvalho Chehab 	.init = tua9001_init,
186*ccae7af2SMauro Carvalho Chehab 	.set_params = tua9001_set_params,
187*ccae7af2SMauro Carvalho Chehab 
188*ccae7af2SMauro Carvalho Chehab 	.get_if_frequency = tua9001_get_if_frequency,
189*ccae7af2SMauro Carvalho Chehab };
190*ccae7af2SMauro Carvalho Chehab 
191*ccae7af2SMauro Carvalho Chehab struct dvb_frontend *tua9001_attach(struct dvb_frontend *fe,
192*ccae7af2SMauro Carvalho Chehab 		struct i2c_adapter *i2c, struct tua9001_config *cfg)
193*ccae7af2SMauro Carvalho Chehab {
194*ccae7af2SMauro Carvalho Chehab 	struct tua9001_priv *priv = NULL;
195*ccae7af2SMauro Carvalho Chehab 
196*ccae7af2SMauro Carvalho Chehab 	priv = kzalloc(sizeof(struct tua9001_priv), GFP_KERNEL);
197*ccae7af2SMauro Carvalho Chehab 	if (priv == NULL)
198*ccae7af2SMauro Carvalho Chehab 		return NULL;
199*ccae7af2SMauro Carvalho Chehab 
200*ccae7af2SMauro Carvalho Chehab 	priv->cfg = cfg;
201*ccae7af2SMauro Carvalho Chehab 	priv->i2c = i2c;
202*ccae7af2SMauro Carvalho Chehab 
203*ccae7af2SMauro Carvalho Chehab 	printk(KERN_INFO "Infineon TUA 9001 successfully attached.");
204*ccae7af2SMauro Carvalho Chehab 
205*ccae7af2SMauro Carvalho Chehab 	memcpy(&fe->ops.tuner_ops, &tua9001_tuner_ops,
206*ccae7af2SMauro Carvalho Chehab 			sizeof(struct dvb_tuner_ops));
207*ccae7af2SMauro Carvalho Chehab 
208*ccae7af2SMauro Carvalho Chehab 	fe->tuner_priv = priv;
209*ccae7af2SMauro Carvalho Chehab 	return fe;
210*ccae7af2SMauro Carvalho Chehab }
211*ccae7af2SMauro Carvalho Chehab EXPORT_SYMBOL(tua9001_attach);
212*ccae7af2SMauro Carvalho Chehab 
213*ccae7af2SMauro Carvalho Chehab MODULE_DESCRIPTION("Infineon TUA 9001 silicon tuner driver");
214*ccae7af2SMauro Carvalho Chehab MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
215*ccae7af2SMauro Carvalho Chehab MODULE_LICENSE("GPL");
216