blob: f0c9c42867de2e8cad570229f8592a6b445f1b47 [file] [log] [blame]
Antti Palosaarid9cb41a2012-09-08 22:07:24 -03001/*
2 * FCI FC2580 silicon tuner driver
3 *
4 * Copyright (C) 2012 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 "fc2580_priv.h"
22
Mauro Carvalho Chehabf1baab82013-11-02 06:07:42 -030023/* Max transfer size done by I2C transfer functions */
24#define MAX_XFER_SIZE 64
25
Antti Palosaarid9cb41a2012-09-08 22:07:24 -030026/*
27 * TODO:
28 * I2C write and read works only for one single register. Multiple registers
29 * could not be accessed using normal register address auto-increment.
30 * There could be (very likely) register to change that behavior....
31 *
32 * Due to that limitation functions:
33 * fc2580_wr_regs()
34 * fc2580_rd_regs()
35 * could not be used for accessing more than one register at once.
36 *
37 * TODO:
38 * Currently it blind writes bunch of static registers from the
39 * fc2580_freq_regs_lut[] when fc2580_set_params() is called. Add some
40 * logic to reduce unneeded register writes.
Antti Palosaarid9cb41a2012-09-08 22:07:24 -030041 */
42
43/* write multiple registers */
44static int fc2580_wr_regs(struct fc2580_priv *priv, u8 reg, u8 *val, int len)
45{
46 int ret;
Mauro Carvalho Chehabf1baab82013-11-02 06:07:42 -030047 u8 buf[MAX_XFER_SIZE];
Antti Palosaarid9cb41a2012-09-08 22:07:24 -030048 struct i2c_msg msg[1] = {
49 {
50 .addr = priv->cfg->i2c_addr,
51 .flags = 0,
Mauro Carvalho Chehabf1baab82013-11-02 06:07:42 -030052 .len = 1 + len,
Antti Palosaarid9cb41a2012-09-08 22:07:24 -030053 .buf = buf,
54 }
55 };
56
Mauro Carvalho Chehabf1baab82013-11-02 06:07:42 -030057 if (1 + len > sizeof(buf)) {
58 dev_warn(&priv->i2c->dev,
59 "%s: i2c wr reg=%04x: len=%d is too big!\n",
60 KBUILD_MODNAME, reg, len);
61 return -EINVAL;
62 }
63
Antti Palosaarid9cb41a2012-09-08 22:07:24 -030064 buf[0] = reg;
65 memcpy(&buf[1], val, len);
66
67 ret = i2c_transfer(priv->i2c, msg, 1);
68 if (ret == 1) {
69 ret = 0;
70 } else {
71 dev_warn(&priv->i2c->dev, "%s: i2c wr failed=%d reg=%02x " \
72 "len=%d\n", KBUILD_MODNAME, ret, reg, len);
73 ret = -EREMOTEIO;
74 }
75 return ret;
76}
77
78/* read multiple registers */
79static int fc2580_rd_regs(struct fc2580_priv *priv, u8 reg, u8 *val, int len)
80{
81 int ret;
Mauro Carvalho Chehabf1baab82013-11-02 06:07:42 -030082 u8 buf[MAX_XFER_SIZE];
Antti Palosaarid9cb41a2012-09-08 22:07:24 -030083 struct i2c_msg msg[2] = {
84 {
85 .addr = priv->cfg->i2c_addr,
86 .flags = 0,
87 .len = 1,
88 .buf = &reg,
89 }, {
90 .addr = priv->cfg->i2c_addr,
91 .flags = I2C_M_RD,
Mauro Carvalho Chehabf1baab82013-11-02 06:07:42 -030092 .len = len,
Antti Palosaarid9cb41a2012-09-08 22:07:24 -030093 .buf = buf,
94 }
95 };
96
Mauro Carvalho Chehabf1baab82013-11-02 06:07:42 -030097 if (len > sizeof(buf)) {
98 dev_warn(&priv->i2c->dev,
99 "%s: i2c rd reg=%04x: len=%d is too big!\n",
100 KBUILD_MODNAME, reg, len);
101 return -EINVAL;
102 }
103
Antti Palosaarid9cb41a2012-09-08 22:07:24 -0300104 ret = i2c_transfer(priv->i2c, msg, 2);
105 if (ret == 2) {
106 memcpy(val, buf, len);
107 ret = 0;
108 } else {
109 dev_warn(&priv->i2c->dev, "%s: i2c rd failed=%d reg=%02x " \
110 "len=%d\n", KBUILD_MODNAME, ret, reg, len);
111 ret = -EREMOTEIO;
112 }
113
114 return ret;
115}
116
117/* write single register */
118static int fc2580_wr_reg(struct fc2580_priv *priv, u8 reg, u8 val)
119{
120 return fc2580_wr_regs(priv, reg, &val, 1);
121}
122
123/* read single register */
124static int fc2580_rd_reg(struct fc2580_priv *priv, u8 reg, u8 *val)
125{
126 return fc2580_rd_regs(priv, reg, val, 1);
127}
128
Antti Palosaarif2ea86d2012-11-06 16:48:41 -0300129/* write single register conditionally only when value differs from 0xff
130 * XXX: This is special routine meant only for writing fc2580_freq_regs_lut[]
131 * values. Do not use for the other purposes. */
132static int fc2580_wr_reg_ff(struct fc2580_priv *priv, u8 reg, u8 val)
133{
134 if (val == 0xff)
135 return 0;
136 else
137 return fc2580_wr_regs(priv, reg, &val, 1);
138}
139
Antti Palosaarid9cb41a2012-09-08 22:07:24 -0300140static int fc2580_set_params(struct dvb_frontend *fe)
141{
142 struct fc2580_priv *priv = fe->tuner_priv;
143 struct dtv_frontend_properties *c = &fe->dtv_property_cache;
Gianluca Gennari6e6dc882012-09-24 08:51:01 -0300144 int ret = 0, i;
Antti Palosaarid9cb41a2012-09-08 22:07:24 -0300145 unsigned int r_val, n_val, k_val, k_val_reg, f_ref;
146 u8 tmp_val, r18_val;
147 u64 f_vco;
148
149 /*
150 * Fractional-N synthesizer/PLL.
151 * Most likely all those PLL calculations are not correct. I am not
152 * sure, but it looks like it is divider based Fractional-N synthesizer.
153 * There is divider for reference clock too?
154 * Anyhow, synthesizer calculation results seems to be quite correct.
155 */
156
157 dev_dbg(&priv->i2c->dev, "%s: delivery_system=%d frequency=%d " \
158 "bandwidth_hz=%d\n", __func__,
159 c->delivery_system, c->frequency, c->bandwidth_hz);
160
161 if (fe->ops.i2c_gate_ctrl)
162 fe->ops.i2c_gate_ctrl(fe, 1);
163
164 /* PLL */
165 for (i = 0; i < ARRAY_SIZE(fc2580_pll_lut); i++) {
166 if (c->frequency <= fc2580_pll_lut[i].freq)
167 break;
168 }
169
170 if (i == ARRAY_SIZE(fc2580_pll_lut))
171 goto err;
172
173 f_vco = c->frequency;
174 f_vco *= fc2580_pll_lut[i].div;
175
Gianluca Gennarie221a1b2012-09-24 07:37:16 -0300176 if (f_vco >= 2600000000UL)
Antti Palosaarid9cb41a2012-09-08 22:07:24 -0300177 tmp_val = 0x0e | fc2580_pll_lut[i].band;
178 else
179 tmp_val = 0x06 | fc2580_pll_lut[i].band;
180
181 ret = fc2580_wr_reg(priv, 0x02, tmp_val);
182 if (ret < 0)
183 goto err;
184
185 if (f_vco >= 2UL * 76 * priv->cfg->clock) {
186 r_val = 1;
187 r18_val = 0x00;
188 } else if (f_vco >= 1UL * 76 * priv->cfg->clock) {
189 r_val = 2;
190 r18_val = 0x10;
191 } else {
192 r_val = 4;
193 r18_val = 0x20;
194 }
195
196 f_ref = 2UL * priv->cfg->clock / r_val;
Gianluca Gennari9dc72162012-09-24 07:37:18 -0300197 n_val = div_u64_rem(f_vco, f_ref, &k_val);
Antti Palosaari8845cc62014-04-10 21:18:16 -0300198 k_val_reg = div_u64(1ULL * k_val * (1 << 20), f_ref);
Antti Palosaarid9cb41a2012-09-08 22:07:24 -0300199
200 ret = fc2580_wr_reg(priv, 0x18, r18_val | ((k_val_reg >> 16) & 0xff));
201 if (ret < 0)
202 goto err;
203
204 ret = fc2580_wr_reg(priv, 0x1a, (k_val_reg >> 8) & 0xff);
205 if (ret < 0)
206 goto err;
207
208 ret = fc2580_wr_reg(priv, 0x1b, (k_val_reg >> 0) & 0xff);
209 if (ret < 0)
210 goto err;
211
212 ret = fc2580_wr_reg(priv, 0x1c, n_val);
213 if (ret < 0)
214 goto err;
215
216 if (priv->cfg->clock >= 28000000) {
217 ret = fc2580_wr_reg(priv, 0x4b, 0x22);
218 if (ret < 0)
219 goto err;
220 }
221
222 if (fc2580_pll_lut[i].band == 0x00) {
223 if (c->frequency <= 794000000)
224 tmp_val = 0x9f;
225 else
226 tmp_val = 0x8f;
227
228 ret = fc2580_wr_reg(priv, 0x2d, tmp_val);
229 if (ret < 0)
230 goto err;
231 }
232
233 /* registers */
234 for (i = 0; i < ARRAY_SIZE(fc2580_freq_regs_lut); i++) {
235 if (c->frequency <= fc2580_freq_regs_lut[i].freq)
236 break;
237 }
238
239 if (i == ARRAY_SIZE(fc2580_freq_regs_lut))
240 goto err;
241
Antti Palosaarif2ea86d2012-11-06 16:48:41 -0300242 ret = fc2580_wr_reg_ff(priv, 0x25, fc2580_freq_regs_lut[i].r25_val);
Antti Palosaarid9cb41a2012-09-08 22:07:24 -0300243 if (ret < 0)
244 goto err;
245
Antti Palosaarif2ea86d2012-11-06 16:48:41 -0300246 ret = fc2580_wr_reg_ff(priv, 0x27, fc2580_freq_regs_lut[i].r27_val);
Antti Palosaarid9cb41a2012-09-08 22:07:24 -0300247 if (ret < 0)
248 goto err;
249
Antti Palosaarif2ea86d2012-11-06 16:48:41 -0300250 ret = fc2580_wr_reg_ff(priv, 0x28, fc2580_freq_regs_lut[i].r28_val);
Antti Palosaarid9cb41a2012-09-08 22:07:24 -0300251 if (ret < 0)
252 goto err;
253
Antti Palosaarif2ea86d2012-11-06 16:48:41 -0300254 ret = fc2580_wr_reg_ff(priv, 0x29, fc2580_freq_regs_lut[i].r29_val);
Antti Palosaarid9cb41a2012-09-08 22:07:24 -0300255 if (ret < 0)
256 goto err;
257
Antti Palosaarif2ea86d2012-11-06 16:48:41 -0300258 ret = fc2580_wr_reg_ff(priv, 0x2b, fc2580_freq_regs_lut[i].r2b_val);
Antti Palosaarid9cb41a2012-09-08 22:07:24 -0300259 if (ret < 0)
260 goto err;
261
Antti Palosaarif2ea86d2012-11-06 16:48:41 -0300262 ret = fc2580_wr_reg_ff(priv, 0x2c, fc2580_freq_regs_lut[i].r2c_val);
Antti Palosaarid9cb41a2012-09-08 22:07:24 -0300263 if (ret < 0)
264 goto err;
265
Antti Palosaarif2ea86d2012-11-06 16:48:41 -0300266 ret = fc2580_wr_reg_ff(priv, 0x2d, fc2580_freq_regs_lut[i].r2d_val);
Antti Palosaarid9cb41a2012-09-08 22:07:24 -0300267 if (ret < 0)
268 goto err;
269
Antti Palosaarif2ea86d2012-11-06 16:48:41 -0300270 ret = fc2580_wr_reg_ff(priv, 0x30, fc2580_freq_regs_lut[i].r30_val);
Antti Palosaarid9cb41a2012-09-08 22:07:24 -0300271 if (ret < 0)
272 goto err;
273
Antti Palosaarif2ea86d2012-11-06 16:48:41 -0300274 ret = fc2580_wr_reg_ff(priv, 0x44, fc2580_freq_regs_lut[i].r44_val);
Antti Palosaarid9cb41a2012-09-08 22:07:24 -0300275 if (ret < 0)
276 goto err;
277
Antti Palosaarif2ea86d2012-11-06 16:48:41 -0300278 ret = fc2580_wr_reg_ff(priv, 0x50, fc2580_freq_regs_lut[i].r50_val);
Antti Palosaarid9cb41a2012-09-08 22:07:24 -0300279 if (ret < 0)
280 goto err;
281
Antti Palosaarif2ea86d2012-11-06 16:48:41 -0300282 ret = fc2580_wr_reg_ff(priv, 0x53, fc2580_freq_regs_lut[i].r53_val);
Antti Palosaarid9cb41a2012-09-08 22:07:24 -0300283 if (ret < 0)
284 goto err;
285
Antti Palosaarif2ea86d2012-11-06 16:48:41 -0300286 ret = fc2580_wr_reg_ff(priv, 0x5f, fc2580_freq_regs_lut[i].r5f_val);
Antti Palosaarid9cb41a2012-09-08 22:07:24 -0300287 if (ret < 0)
288 goto err;
289
Antti Palosaarif2ea86d2012-11-06 16:48:41 -0300290 ret = fc2580_wr_reg_ff(priv, 0x61, fc2580_freq_regs_lut[i].r61_val);
Antti Palosaarid9cb41a2012-09-08 22:07:24 -0300291 if (ret < 0)
292 goto err;
293
Antti Palosaarif2ea86d2012-11-06 16:48:41 -0300294 ret = fc2580_wr_reg_ff(priv, 0x62, fc2580_freq_regs_lut[i].r62_val);
Antti Palosaarid9cb41a2012-09-08 22:07:24 -0300295 if (ret < 0)
296 goto err;
297
Antti Palosaarif2ea86d2012-11-06 16:48:41 -0300298 ret = fc2580_wr_reg_ff(priv, 0x63, fc2580_freq_regs_lut[i].r63_val);
Antti Palosaarid9cb41a2012-09-08 22:07:24 -0300299 if (ret < 0)
300 goto err;
301
Antti Palosaarif2ea86d2012-11-06 16:48:41 -0300302 ret = fc2580_wr_reg_ff(priv, 0x67, fc2580_freq_regs_lut[i].r67_val);
Antti Palosaarid9cb41a2012-09-08 22:07:24 -0300303 if (ret < 0)
304 goto err;
305
Antti Palosaarif2ea86d2012-11-06 16:48:41 -0300306 ret = fc2580_wr_reg_ff(priv, 0x68, fc2580_freq_regs_lut[i].r68_val);
Antti Palosaarid9cb41a2012-09-08 22:07:24 -0300307 if (ret < 0)
308 goto err;
309
Antti Palosaarif2ea86d2012-11-06 16:48:41 -0300310 ret = fc2580_wr_reg_ff(priv, 0x69, fc2580_freq_regs_lut[i].r69_val);
Antti Palosaarid9cb41a2012-09-08 22:07:24 -0300311 if (ret < 0)
312 goto err;
313
Antti Palosaarif2ea86d2012-11-06 16:48:41 -0300314 ret = fc2580_wr_reg_ff(priv, 0x6a, fc2580_freq_regs_lut[i].r6a_val);
Antti Palosaarid9cb41a2012-09-08 22:07:24 -0300315 if (ret < 0)
316 goto err;
317
Antti Palosaarif2ea86d2012-11-06 16:48:41 -0300318 ret = fc2580_wr_reg_ff(priv, 0x6b, fc2580_freq_regs_lut[i].r6b_val);
Antti Palosaarid9cb41a2012-09-08 22:07:24 -0300319 if (ret < 0)
320 goto err;
321
Antti Palosaarif2ea86d2012-11-06 16:48:41 -0300322 ret = fc2580_wr_reg_ff(priv, 0x6c, fc2580_freq_regs_lut[i].r6c_val);
Antti Palosaarid9cb41a2012-09-08 22:07:24 -0300323 if (ret < 0)
324 goto err;
325
Antti Palosaarif2ea86d2012-11-06 16:48:41 -0300326 ret = fc2580_wr_reg_ff(priv, 0x6d, fc2580_freq_regs_lut[i].r6d_val);
Antti Palosaarid9cb41a2012-09-08 22:07:24 -0300327 if (ret < 0)
328 goto err;
329
Antti Palosaarif2ea86d2012-11-06 16:48:41 -0300330 ret = fc2580_wr_reg_ff(priv, 0x6e, fc2580_freq_regs_lut[i].r6e_val);
Antti Palosaarid9cb41a2012-09-08 22:07:24 -0300331 if (ret < 0)
332 goto err;
333
Antti Palosaarif2ea86d2012-11-06 16:48:41 -0300334 ret = fc2580_wr_reg_ff(priv, 0x6f, fc2580_freq_regs_lut[i].r6f_val);
Antti Palosaarid9cb41a2012-09-08 22:07:24 -0300335 if (ret < 0)
336 goto err;
337
338 /* IF filters */
339 for (i = 0; i < ARRAY_SIZE(fc2580_if_filter_lut); i++) {
340 if (c->bandwidth_hz <= fc2580_if_filter_lut[i].freq)
341 break;
342 }
343
344 if (i == ARRAY_SIZE(fc2580_if_filter_lut))
345 goto err;
346
347 ret = fc2580_wr_reg(priv, 0x36, fc2580_if_filter_lut[i].r36_val);
348 if (ret < 0)
349 goto err;
350
Antti Palosaari8845cc62014-04-10 21:18:16 -0300351 ret = fc2580_wr_reg(priv, 0x37, div_u64(1ULL * priv->cfg->clock *
352 fc2580_if_filter_lut[i].mul, 1000000000));
Antti Palosaarid9cb41a2012-09-08 22:07:24 -0300353 if (ret < 0)
354 goto err;
355
356 ret = fc2580_wr_reg(priv, 0x39, fc2580_if_filter_lut[i].r39_val);
357 if (ret < 0)
358 goto err;
359
360 /* calibration? */
361 ret = fc2580_wr_reg(priv, 0x2e, 0x09);
362 if (ret < 0)
363 goto err;
364
365 for (i = 0; i < 5; i++) {
366 ret = fc2580_rd_reg(priv, 0x2f, &tmp_val);
367 if (ret < 0)
368 goto err;
369
370 /* done when [7:6] are set */
371 if ((tmp_val & 0xc0) == 0xc0)
372 break;
373
374 ret = fc2580_wr_reg(priv, 0x2e, 0x01);
375 if (ret < 0)
376 goto err;
377
378 ret = fc2580_wr_reg(priv, 0x2e, 0x09);
379 if (ret < 0)
380 goto err;
381
382 usleep_range(5000, 25000);
383 }
384
385 dev_dbg(&priv->i2c->dev, "%s: loop=%i\n", __func__, i);
386
387 ret = fc2580_wr_reg(priv, 0x2e, 0x01);
388 if (ret < 0)
389 goto err;
390
391 if (fe->ops.i2c_gate_ctrl)
392 fe->ops.i2c_gate_ctrl(fe, 0);
393
394 return 0;
395err:
396 if (fe->ops.i2c_gate_ctrl)
397 fe->ops.i2c_gate_ctrl(fe, 0);
398
399 dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
400 return ret;
401}
402
403static int fc2580_init(struct dvb_frontend *fe)
404{
405 struct fc2580_priv *priv = fe->tuner_priv;
406 int ret, i;
407
408 dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
409
410 if (fe->ops.i2c_gate_ctrl)
411 fe->ops.i2c_gate_ctrl(fe, 1);
412
413 for (i = 0; i < ARRAY_SIZE(fc2580_init_reg_vals); i++) {
414 ret = fc2580_wr_reg(priv, fc2580_init_reg_vals[i].reg,
415 fc2580_init_reg_vals[i].val);
416 if (ret < 0)
417 goto err;
418 }
419
420 if (fe->ops.i2c_gate_ctrl)
421 fe->ops.i2c_gate_ctrl(fe, 0);
422
423 return 0;
424err:
425 if (fe->ops.i2c_gate_ctrl)
426 fe->ops.i2c_gate_ctrl(fe, 0);
427
428 dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
429 return ret;
430}
431
432static int fc2580_sleep(struct dvb_frontend *fe)
433{
434 struct fc2580_priv *priv = fe->tuner_priv;
435 int ret;
436
437 dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
438
439 if (fe->ops.i2c_gate_ctrl)
440 fe->ops.i2c_gate_ctrl(fe, 1);
441
442 ret = fc2580_wr_reg(priv, 0x02, 0x0a);
443 if (ret < 0)
444 goto err;
445
446 if (fe->ops.i2c_gate_ctrl)
447 fe->ops.i2c_gate_ctrl(fe, 0);
448
449 return 0;
450err:
451 if (fe->ops.i2c_gate_ctrl)
452 fe->ops.i2c_gate_ctrl(fe, 0);
453
454 dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
455 return ret;
456}
457
458static int fc2580_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
459{
460 struct fc2580_priv *priv = fe->tuner_priv;
461
462 dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
463
464 *frequency = 0; /* Zero-IF */
465
466 return 0;
467}
468
469static int fc2580_release(struct dvb_frontend *fe)
470{
471 struct fc2580_priv *priv = fe->tuner_priv;
472
473 dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
474
475 kfree(fe->tuner_priv);
476
477 return 0;
478}
479
480static const struct dvb_tuner_ops fc2580_tuner_ops = {
481 .info = {
482 .name = "FCI FC2580",
483 .frequency_min = 174000000,
484 .frequency_max = 862000000,
485 },
486
487 .release = fc2580_release,
488
489 .init = fc2580_init,
490 .sleep = fc2580_sleep,
491 .set_params = fc2580_set_params,
492
493 .get_if_frequency = fc2580_get_if_frequency,
494};
495
496struct dvb_frontend *fc2580_attach(struct dvb_frontend *fe,
497 struct i2c_adapter *i2c, const struct fc2580_config *cfg)
498{
499 struct fc2580_priv *priv;
500 int ret;
501 u8 chip_id;
502
503 if (fe->ops.i2c_gate_ctrl)
504 fe->ops.i2c_gate_ctrl(fe, 1);
505
506 priv = kzalloc(sizeof(struct fc2580_priv), GFP_KERNEL);
507 if (!priv) {
508 ret = -ENOMEM;
509 dev_err(&i2c->dev, "%s: kzalloc() failed\n", KBUILD_MODNAME);
510 goto err;
511 }
512
513 priv->cfg = cfg;
514 priv->i2c = i2c;
Antti Palosaarid9cb41a2012-09-08 22:07:24 -0300515
516 /* check if the tuner is there */
517 ret = fc2580_rd_reg(priv, 0x01, &chip_id);
518 if (ret < 0)
519 goto err;
520
521 dev_dbg(&priv->i2c->dev, "%s: chip_id=%02x\n", __func__, chip_id);
522
Antti Palosaari132f56f2012-09-21 20:28:43 -0300523 switch (chip_id) {
524 case 0x56:
525 case 0x5a:
526 break;
527 default:
Antti Palosaarid9cb41a2012-09-08 22:07:24 -0300528 goto err;
Oliver Schinagld67ceb32012-09-20 14:57:17 -0300529 }
Antti Palosaarid9cb41a2012-09-08 22:07:24 -0300530
531 dev_info(&priv->i2c->dev,
532 "%s: FCI FC2580 successfully identified\n",
533 KBUILD_MODNAME);
534
Antti Palosaarief40c002012-09-21 23:36:27 -0300535 fe->tuner_priv = priv;
536 memcpy(&fe->ops.tuner_ops, &fc2580_tuner_ops,
537 sizeof(struct dvb_tuner_ops));
538
Antti Palosaarid9cb41a2012-09-08 22:07:24 -0300539 if (fe->ops.i2c_gate_ctrl)
540 fe->ops.i2c_gate_ctrl(fe, 0);
541
542 return fe;
543err:
544 if (fe->ops.i2c_gate_ctrl)
545 fe->ops.i2c_gate_ctrl(fe, 0);
546
547 dev_dbg(&i2c->dev, "%s: failed=%d\n", __func__, ret);
548 kfree(priv);
549 return NULL;
550}
551EXPORT_SYMBOL(fc2580_attach);
552
553MODULE_DESCRIPTION("FCI FC2580 silicon tuner driver");
554MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
555MODULE_LICENSE("GPL");