Merge branch 'for-linus' of git://git390.marist.edu/pub/scm/linux-2.6
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / drivers / media / common / tuners / tda18212.c
CommitLineData
26eb7045
AP
1/*
2 * NXP TDA18212HN silicon tuner driver
3 *
4 * Copyright (C) 2011 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 along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21#include "tda18212_priv.h"
22
23static int debug;
24module_param(debug, int, 0644);
25MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
26
27/* write multiple registers */
28static int tda18212_wr_regs(struct tda18212_priv *priv, u8 reg, u8 *val,
29 int len)
30{
31 int ret;
32 u8 buf[len+1];
33 struct i2c_msg msg[1] = {
34 {
35 .addr = priv->cfg->i2c_address,
36 .flags = 0,
37 .len = sizeof(buf),
38 .buf = buf,
39 }
40 };
41
42 buf[0] = reg;
43 memcpy(&buf[1], val, len);
44
45 ret = i2c_transfer(priv->i2c, msg, 1);
46 if (ret == 1) {
47 ret = 0;
48 } else {
49 warn("i2c wr failed ret:%d reg:%02x len:%d", ret, reg, len);
50 ret = -EREMOTEIO;
51 }
52 return ret;
53}
54
55/* read multiple registers */
56static int tda18212_rd_regs(struct tda18212_priv *priv, u8 reg, u8 *val,
57 int len)
58{
59 int ret;
60 u8 buf[len];
61 struct i2c_msg msg[2] = {
62 {
63 .addr = priv->cfg->i2c_address,
64 .flags = 0,
65 .len = 1,
66 .buf = &reg,
67 }, {
68 .addr = priv->cfg->i2c_address,
69 .flags = I2C_M_RD,
70 .len = sizeof(buf),
71 .buf = buf,
72 }
73 };
74
75 ret = i2c_transfer(priv->i2c, msg, 2);
76 if (ret == 2) {
77 memcpy(val, buf, len);
78 ret = 0;
79 } else {
80 warn("i2c rd failed ret:%d reg:%02x len:%d", ret, reg, len);
81 ret = -EREMOTEIO;
82 }
83
84 return ret;
85}
86
87/* write single register */
88static int tda18212_wr_reg(struct tda18212_priv *priv, u8 reg, u8 val)
89{
90 return tda18212_wr_regs(priv, reg, &val, 1);
91}
92
93/* read single register */
94static int tda18212_rd_reg(struct tda18212_priv *priv, u8 reg, u8 *val)
95{
96 return tda18212_rd_regs(priv, reg, val, 1);
97}
98
99#if 0 /* keep, useful when developing driver */
100static void tda18212_dump_regs(struct tda18212_priv *priv)
101{
102 int i;
103 u8 buf[256];
104
105 #define TDA18212_RD_LEN 32
106 for (i = 0; i < sizeof(buf); i += TDA18212_RD_LEN)
107 tda18212_rd_regs(priv, i, &buf[i], TDA18212_RD_LEN);
108
109 print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 32, 1, buf,
110 sizeof(buf), true);
111
112 return;
113}
114#endif
115
116static int tda18212_set_params(struct dvb_frontend *fe,
117 struct dvb_frontend_parameters *p)
118{
119 struct tda18212_priv *priv = fe->tuner_priv;
120 struct dtv_frontend_properties *c = &fe->dtv_property_cache;
121 int ret, i;
122 u32 if_khz;
123 u8 buf[9];
124 static const u8 bw_params[][3] = {
125 /* 0f 13 23 */
126 { 0xb3, 0x20, 0x03 }, /* DVB-T 6 MHz */
127 { 0xb3, 0x31, 0x01 }, /* DVB-T 7 MHz */
128 { 0xb3, 0x22, 0x01 }, /* DVB-T 8 MHz */
129 { 0x92, 0x53, 0x03 }, /* DVB-C */
130 };
131
132 dbg("%s: delsys=%d RF=%d BW=%d", __func__,
133 c->delivery_system, c->frequency, c->bandwidth_hz);
134
135 if (fe->ops.i2c_gate_ctrl)
136 fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
137
138 switch (c->delivery_system) {
139 case SYS_DVBT:
140 switch (c->bandwidth_hz) {
141 case 6000000:
142 if_khz = priv->cfg->if_dvbt_6;
143 i = 0;
144 break;
145 case 7000000:
146 if_khz = priv->cfg->if_dvbt_7;
147 i = 1;
148 break;
149 case 8000000:
150 if_khz = priv->cfg->if_dvbt_8;
151 i = 2;
152 break;
153 default:
154 ret = -EINVAL;
155 goto error;
156 }
157 break;
158 case SYS_DVBC_ANNEX_AC:
159 if_khz = priv->cfg->if_dvbc;
160 i = 3;
161 break;
162 default:
163 ret = -EINVAL;
164 goto error;
165 }
166
167 ret = tda18212_wr_reg(priv, 0x23, bw_params[i][2]);
168 if (ret)
169 goto error;
170
171 ret = tda18212_wr_reg(priv, 0x06, 0x00);
172 if (ret)
173 goto error;
174
175 ret = tda18212_wr_reg(priv, 0x0f, bw_params[i][0]);
176 if (ret)
177 goto error;
178
179 buf[0] = 0x02;
180 buf[1] = bw_params[i][1];
181 buf[2] = 0x03; /* default value */
182 buf[3] = if_khz / 50;
183 buf[4] = ((c->frequency / 1000) >> 16) & 0xff;
184 buf[5] = ((c->frequency / 1000) >> 8) & 0xff;
185 buf[6] = ((c->frequency / 1000) >> 0) & 0xff;
186 buf[7] = 0xc1;
187 buf[8] = 0x01;
188 ret = tda18212_wr_regs(priv, 0x12, buf, sizeof(buf));
189 if (ret)
190 goto error;
191
192exit:
193 if (fe->ops.i2c_gate_ctrl)
194 fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
195
196 return ret;
197
198error:
199 dbg("%s: failed:%d", __func__, ret);
200 goto exit;
201}
202
203static int tda18212_release(struct dvb_frontend *fe)
204{
205 kfree(fe->tuner_priv);
206 fe->tuner_priv = NULL;
207 return 0;
208}
209
210static const struct dvb_tuner_ops tda18212_tuner_ops = {
211 .info = {
212 .name = "NXP TDA18212",
213
214 .frequency_min = 48000000,
215 .frequency_max = 864000000,
216 .frequency_step = 1000,
217 },
218
219 .release = tda18212_release,
220
221 .set_params = tda18212_set_params,
222};
223
224struct dvb_frontend *tda18212_attach(struct dvb_frontend *fe,
225 struct i2c_adapter *i2c, struct tda18212_config *cfg)
226{
227 struct tda18212_priv *priv = NULL;
228 int ret;
229 u8 val;
230
231 priv = kzalloc(sizeof(struct tda18212_priv), GFP_KERNEL);
232 if (priv == NULL)
233 return NULL;
234
235 priv->cfg = cfg;
236 priv->i2c = i2c;
237 fe->tuner_priv = priv;
238
239 if (fe->ops.i2c_gate_ctrl)
240 fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
241
242 /* check if the tuner is there */
243 ret = tda18212_rd_reg(priv, 0x00, &val);
244
245 if (fe->ops.i2c_gate_ctrl)
246 fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
247
248 dbg("%s: ret:%d chip ID:%02x", __func__, ret, val);
249 if (ret || val != 0xc7) {
250 kfree(priv);
251 return NULL;
252 }
253
254 info("NXP TDA18212HN successfully identified.");
255
256 memcpy(&fe->ops.tuner_ops, &tda18212_tuner_ops,
257 sizeof(struct dvb_tuner_ops));
258
259 return fe;
260}
261EXPORT_SYMBOL(tda18212_attach);
262
263MODULE_DESCRIPTION("NXP TDA18212HN silicon tuner driver");
264MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
265MODULE_LICENSE("GPL");