blob: 507ef4bb047ac1c4a9606df1f672f37fe63f8a04 [file] [log] [blame]
Antti Palosaaric0adca72011-07-08 23:34:09 -03001/*
2 * Realtek RTL2830 DVB-T demodulator 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
22/*
23 * Driver implements own I2C-adapter for tuner I2C access. That's since chip
24 * have unusual I2C-gate control which closes gate automatically after each
25 * I2C transfer. Using own I2C adapter we can workaround that.
26 */
27
28#include "rtl2830_priv.h"
29
30int rtl2830_debug;
31module_param_named(debug, rtl2830_debug, int, 0644);
32MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
33
Antti Palosaari0485a702011-08-04 20:27:19 -030034/* write multiple hardware registers */
35static int rtl2830_wr(struct rtl2830_priv *priv, u8 reg, u8 *val, int len)
Antti Palosaaric0adca72011-07-08 23:34:09 -030036{
37 int ret;
Antti Palosaari0485a702011-08-04 20:27:19 -030038 u8 buf[1+len];
Antti Palosaaric0adca72011-07-08 23:34:09 -030039 struct i2c_msg msg[1] = {
40 {
41 .addr = priv->cfg.i2c_addr,
42 .flags = 0,
Antti Palosaari0485a702011-08-04 20:27:19 -030043 .len = 1+len,
Antti Palosaaric0adca72011-07-08 23:34:09 -030044 .buf = buf,
45 }
46 };
47
Antti Palosaari0485a702011-08-04 20:27:19 -030048 buf[0] = reg;
49 memcpy(&buf[1], val, len);
Antti Palosaaric0adca72011-07-08 23:34:09 -030050
51 ret = i2c_transfer(priv->i2c, msg, 1);
52 if (ret == 1) {
53 ret = 0;
54 } else {
Antti Palosaari0485a702011-08-04 20:27:19 -030055 warn("i2c wr failed=%d reg=%02x len=%d", ret, reg, len);
Antti Palosaaric0adca72011-07-08 23:34:09 -030056 ret = -EREMOTEIO;
57 }
58 return ret;
59}
60
Antti Palosaari0485a702011-08-04 20:27:19 -030061/* read multiple hardware registers */
62static int rtl2830_rd(struct rtl2830_priv *priv, u8 reg, u8 *val, int len)
Antti Palosaaric0adca72011-07-08 23:34:09 -030063{
64 int ret;
Antti Palosaaric0adca72011-07-08 23:34:09 -030065 struct i2c_msg msg[2] = {
66 {
67 .addr = priv->cfg.i2c_addr,
68 .flags = 0,
Antti Palosaari0485a702011-08-04 20:27:19 -030069 .len = 1,
70 .buf = &reg,
Antti Palosaaric0adca72011-07-08 23:34:09 -030071 }, {
72 .addr = priv->cfg.i2c_addr,
73 .flags = I2C_M_RD,
74 .len = len,
75 .buf = val,
76 }
77 };
78
Antti Palosaaric0adca72011-07-08 23:34:09 -030079 ret = i2c_transfer(priv->i2c, msg, 2);
80 if (ret == 2) {
81 ret = 0;
82 } else {
Antti Palosaari0485a702011-08-04 20:27:19 -030083 warn("i2c rd failed=%d reg=%02x len=%d", ret, reg, len);
Antti Palosaaric0adca72011-07-08 23:34:09 -030084 ret = -EREMOTEIO;
85 }
86 return ret;
87}
88
Antti Palosaari0485a702011-08-04 20:27:19 -030089/* write multiple registers */
90static int rtl2830_wr_regs(struct rtl2830_priv *priv, u16 reg, u8 *val, int len)
91{
92 int ret;
93 u8 reg2 = (reg >> 0) & 0xff;
94 u8 page = (reg >> 8) & 0xff;
95
96 /* switch bank if needed */
97 if (page != priv->page) {
98 ret = rtl2830_wr(priv, 0x00, &page, 1);
99 if (ret)
100 return ret;
101
102 priv->page = page;
103 }
104
105 return rtl2830_wr(priv, reg2, val, len);
106}
107
108/* read multiple registers */
109static int rtl2830_rd_regs(struct rtl2830_priv *priv, u16 reg, u8 *val, int len)
110{
111 int ret;
112 u8 reg2 = (reg >> 0) & 0xff;
113 u8 page = (reg >> 8) & 0xff;
114
115 /* switch bank if needed */
116 if (page != priv->page) {
117 ret = rtl2830_wr(priv, 0x00, &page, 1);
118 if (ret)
119 return ret;
120
121 priv->page = page;
122 }
123
124 return rtl2830_rd(priv, reg2, val, len);
125}
126
Antti Palosaaric0adca72011-07-08 23:34:09 -0300127#if 0 /* currently not used */
128/* write single register */
129static int rtl2830_wr_reg(struct rtl2830_priv *priv, u16 reg, u8 val)
130{
131 return rtl2830_wr_regs(priv, reg, &val, 1);
132}
133#endif
134
135/* read single register */
136static int rtl2830_rd_reg(struct rtl2830_priv *priv, u16 reg, u8 *val)
137{
138 return rtl2830_rd_regs(priv, reg, val, 1);
139}
140
141/* write single register with mask */
142int rtl2830_wr_reg_mask(struct rtl2830_priv *priv, u16 reg, u8 val, u8 mask)
143{
144 int ret;
145 u8 tmp;
146
147 /* no need for read if whole reg is written */
148 if (mask != 0xff) {
149 ret = rtl2830_rd_regs(priv, reg, &tmp, 1);
150 if (ret)
151 return ret;
152
153 val &= mask;
154 tmp &= ~mask;
155 val |= tmp;
156 }
157
158 return rtl2830_wr_regs(priv, reg, &val, 1);
159}
160
161/* read single register with mask */
162int rtl2830_rd_reg_mask(struct rtl2830_priv *priv, u16 reg, u8 *val, u8 mask)
163{
164 int ret, i;
165 u8 tmp;
166
167 ret = rtl2830_rd_regs(priv, reg, &tmp, 1);
168 if (ret)
169 return ret;
170
171 tmp &= mask;
172
173 /* find position of the first bit */
174 for (i = 0; i < 8; i++) {
175 if ((mask >> i) & 0x01)
176 break;
177 }
178 *val = tmp >> i;
179
180 return 0;
181}
182
183static int rtl2830_init(struct dvb_frontend *fe)
184{
185 struct rtl2830_priv *priv = fe->demodulator_priv;
186 int ret, i;
187 u64 num;
188 u8 buf[3], tmp;
189 u32 if_ctl;
190 struct rtl2830_reg_val_mask tab[] = {
191 { 0x00d, 0x01, 0x03 },
192 { 0x00d, 0x10, 0x10 },
193 { 0x104, 0x00, 0x1e },
194 { 0x105, 0x80, 0x80 },
195 { 0x110, 0x02, 0x03 },
196 { 0x110, 0x08, 0x0c },
197 { 0x17b, 0x00, 0x40 },
198 { 0x17d, 0x05, 0x0f },
199 { 0x17d, 0x50, 0xf0 },
200 { 0x18c, 0x08, 0x0f },
201 { 0x18d, 0x00, 0xc0 },
202 { 0x188, 0x05, 0x0f },
203 { 0x189, 0x00, 0xfc },
204 { 0x2d5, 0x02, 0x02 },
205 { 0x2f1, 0x02, 0x06 },
206 { 0x2f1, 0x20, 0xf8 },
207 { 0x16d, 0x00, 0x01 },
208 { 0x1a6, 0x00, 0x80 },
209 { 0x106, priv->cfg.vtop, 0x3f },
210 { 0x107, priv->cfg.krf, 0x3f },
211 { 0x112, 0x28, 0xff },
212 { 0x103, priv->cfg.agc_targ_val, 0xff },
213 { 0x00a, 0x02, 0x07 },
214 { 0x140, 0x0c, 0x3c },
215 { 0x140, 0x40, 0xc0 },
216 { 0x15b, 0x05, 0x07 },
217 { 0x15b, 0x28, 0x38 },
218 { 0x15c, 0x05, 0x07 },
219 { 0x15c, 0x28, 0x38 },
220 { 0x115, priv->cfg.spec_inv, 0x01 },
221 { 0x16f, 0x01, 0x07 },
222 { 0x170, 0x18, 0x38 },
223 { 0x172, 0x0f, 0x0f },
224 { 0x173, 0x08, 0x38 },
225 { 0x175, 0x01, 0x07 },
226 { 0x176, 0x00, 0xc0 },
227 };
228
229 for (i = 0; i < ARRAY_SIZE(tab); i++) {
230 ret = rtl2830_wr_reg_mask(priv, tab[i].reg, tab[i].val,
231 tab[i].mask);
232 if (ret)
233 goto err;
234 }
235
236 ret = rtl2830_wr_regs(priv, 0x18f, "\x28\x00", 2);
237 if (ret)
238 goto err;
239
240 ret = rtl2830_wr_regs(priv, 0x195,
241 "\x04\x06\x0a\x12\x0a\x12\x1e\x28", 8);
242 if (ret)
243 goto err;
244
245 num = priv->cfg.if_dvbt % priv->cfg.xtal;
246 num *= 0x400000;
Gianluca Gennari479e3492012-03-04 18:19:41 -0300247 num = div_u64(num, priv->cfg.xtal);
Antti Palosaaric0adca72011-07-08 23:34:09 -0300248 num = -num;
249 if_ctl = num & 0x3fffff;
250 dbg("%s: if_ctl=%08x", __func__, if_ctl);
251
252 ret = rtl2830_rd_reg_mask(priv, 0x119, &tmp, 0xc0); /* b[7:6] */
253 if (ret)
254 goto err;
255
256 buf[0] = tmp << 6;
257 buf[0] = (if_ctl >> 16) & 0x3f;
258 buf[1] = (if_ctl >> 8) & 0xff;
259 buf[2] = (if_ctl >> 0) & 0xff;
260
261 ret = rtl2830_wr_regs(priv, 0x119, buf, 3);
262 if (ret)
263 goto err;
264
265 /* TODO: spec init */
266
267 /* soft reset */
268 ret = rtl2830_wr_reg_mask(priv, 0x101, 0x04, 0x04);
269 if (ret)
270 goto err;
271
272 ret = rtl2830_wr_reg_mask(priv, 0x101, 0x00, 0x04);
273 if (ret)
274 goto err;
275
Antti Palosaaria8567cf2012-01-21 22:40:58 -0300276 priv->sleeping = false;
277
Antti Palosaaric0adca72011-07-08 23:34:09 -0300278 return ret;
279err:
280 dbg("%s: failed=%d", __func__, ret);
281 return ret;
282}
283
Antti Palosaaria8567cf2012-01-21 22:40:58 -0300284static int rtl2830_sleep(struct dvb_frontend *fe)
285{
286 struct rtl2830_priv *priv = fe->demodulator_priv;
287 priv->sleeping = true;
288 return 0;
289}
290
Antti Palosaaric0adca72011-07-08 23:34:09 -0300291int rtl2830_get_tune_settings(struct dvb_frontend *fe,
292 struct dvb_frontend_tune_settings *s)
293{
294 s->min_delay_ms = 500;
295 s->step_size = fe->ops.info.frequency_stepsize * 2;
296 s->max_drift = (fe->ops.info.frequency_stepsize * 2) + 1;
297
298 return 0;
299}
300
301static int rtl2830_set_frontend(struct dvb_frontend *fe)
302{
303 struct rtl2830_priv *priv = fe->demodulator_priv;
304 struct dtv_frontend_properties *c = &fe->dtv_property_cache;
305 int ret, i;
306 static u8 bw_params1[3][34] = {
307 {
308 0x1f, 0xf0, 0x1f, 0xf0, 0x1f, 0xfa, 0x00, 0x17, 0x00, 0x41,
309 0x00, 0x64, 0x00, 0x67, 0x00, 0x38, 0x1f, 0xde, 0x1f, 0x7a,
310 0x1f, 0x47, 0x1f, 0x7c, 0x00, 0x30, 0x01, 0x4b, 0x02, 0x82,
311 0x03, 0x73, 0x03, 0xcf, /* 6 MHz */
312 }, {
313 0x1f, 0xfa, 0x1f, 0xda, 0x1f, 0xc1, 0x1f, 0xb3, 0x1f, 0xca,
314 0x00, 0x07, 0x00, 0x4d, 0x00, 0x6d, 0x00, 0x40, 0x1f, 0xca,
315 0x1f, 0x4d, 0x1f, 0x2a, 0x1f, 0xb2, 0x00, 0xec, 0x02, 0x7e,
316 0x03, 0xd0, 0x04, 0x53, /* 7 MHz */
317 }, {
318 0x00, 0x10, 0x00, 0x0e, 0x1f, 0xf7, 0x1f, 0xc9, 0x1f, 0xa0,
319 0x1f, 0xa6, 0x1f, 0xec, 0x00, 0x4e, 0x00, 0x7d, 0x00, 0x3a,
320 0x1f, 0x98, 0x1f, 0x10, 0x1f, 0x40, 0x00, 0x75, 0x02, 0x5f,
321 0x04, 0x24, 0x04, 0xdb, /* 8 MHz */
322 },
323 };
324 static u8 bw_params2[3][6] = {
325 {0xc3, 0x0c, 0x44, 0x33, 0x33, 0x30,}, /* 6 MHz */
326 {0xb8, 0xe3, 0x93, 0x99, 0x99, 0x98,}, /* 7 MHz */
327 {0xae, 0xba, 0xf3, 0x26, 0x66, 0x64,}, /* 8 MHz */
328 };
329
330
331 dbg("%s: frequency=%d bandwidth_hz=%d inversion=%d", __func__,
332 c->frequency, c->bandwidth_hz, c->inversion);
333
334 /* program tuner */
335 if (fe->ops.tuner_ops.set_params)
336 fe->ops.tuner_ops.set_params(fe);
337
338 switch (c->bandwidth_hz) {
339 case 6000000:
340 i = 0;
341 break;
342 case 7000000:
343 i = 1;
344 break;
345 case 8000000:
346 i = 2;
347 break;
348 default:
349 dbg("invalid bandwidth");
350 return -EINVAL;
351 }
352
353 ret = rtl2830_wr_reg_mask(priv, 0x008, i << 1, 0x06);
354 if (ret)
355 goto err;
356
357 /* 1/2 split I2C write */
358 ret = rtl2830_wr_regs(priv, 0x11c, &bw_params1[i][0], 17);
359 if (ret)
360 goto err;
361
362 /* 2/2 split I2C write */
363 ret = rtl2830_wr_regs(priv, 0x12d, &bw_params1[i][17], 17);
364 if (ret)
365 goto err;
366
367 ret = rtl2830_wr_regs(priv, 0x19d, bw_params2[i], 6);
368 if (ret)
369 goto err;
370
371 return ret;
372err:
373 dbg("%s: failed=%d", __func__, ret);
374 return ret;
375}
376
Antti Palosaari631a2b62012-05-18 15:58:57 -0300377static int rtl2830_get_frontend(struct dvb_frontend *fe)
378{
379 struct rtl2830_priv *priv = fe->demodulator_priv;
380 struct dtv_frontend_properties *c = &fe->dtv_property_cache;
381 int ret;
382 u8 buf[3];
383
384 ret = rtl2830_rd_regs(priv, 0x33c, buf, 2);
385 if (ret)
386 goto err;
387
388 ret = rtl2830_rd_reg(priv, 0x351, &buf[2]);
389 if (ret)
390 goto err;
391
392 dbg("%s: TPS=%02x %02x %02x", __func__, buf[0], buf[1], buf[2]);
393
394 switch ((buf[0] >> 2) & 3) {
395 case 0:
396 c->modulation = QPSK;
397 break;
398 case 1:
399 c->modulation = QAM_16;
400 break;
401 case 2:
402 c->modulation = QAM_64;
403 break;
404 }
405
406 switch ((buf[2] >> 2) & 1) {
407 case 0:
408 c->transmission_mode = TRANSMISSION_MODE_2K;
409 break;
410 case 1:
411 c->transmission_mode = TRANSMISSION_MODE_8K;
412 }
413
414 switch ((buf[2] >> 0) & 3) {
415 case 0:
416 c->guard_interval = GUARD_INTERVAL_1_32;
417 break;
418 case 1:
419 c->guard_interval = GUARD_INTERVAL_1_16;
420 break;
421 case 2:
422 c->guard_interval = GUARD_INTERVAL_1_8;
423 break;
424 case 3:
425 c->guard_interval = GUARD_INTERVAL_1_4;
426 break;
427 }
428
429 switch ((buf[0] >> 4) & 7) {
430 case 0:
431 c->hierarchy = HIERARCHY_NONE;
432 break;
433 case 1:
434 c->hierarchy = HIERARCHY_1;
435 break;
436 case 2:
437 c->hierarchy = HIERARCHY_2;
438 break;
439 case 3:
440 c->hierarchy = HIERARCHY_4;
441 break;
442 }
443
444 switch ((buf[1] >> 3) & 7) {
445 case 0:
446 c->code_rate_HP = FEC_1_2;
447 break;
448 case 1:
449 c->code_rate_HP = FEC_2_3;
450 break;
451 case 2:
452 c->code_rate_HP = FEC_3_4;
453 break;
454 case 3:
455 c->code_rate_HP = FEC_5_6;
456 break;
457 case 4:
458 c->code_rate_HP = FEC_7_8;
459 break;
460 }
461
462 switch ((buf[1] >> 0) & 7) {
463 case 0:
464 c->code_rate_LP = FEC_1_2;
465 break;
466 case 1:
467 c->code_rate_LP = FEC_2_3;
468 break;
469 case 2:
470 c->code_rate_LP = FEC_3_4;
471 break;
472 case 3:
473 c->code_rate_LP = FEC_5_6;
474 break;
475 case 4:
476 c->code_rate_LP = FEC_7_8;
477 break;
478 }
479
480 return 0;
481err:
482 dbg("%s: failed=%d", __func__, ret);
483 return ret;
484}
485
Antti Palosaaric0adca72011-07-08 23:34:09 -0300486static int rtl2830_read_status(struct dvb_frontend *fe, fe_status_t *status)
487{
488 struct rtl2830_priv *priv = fe->demodulator_priv;
489 int ret;
490 u8 tmp;
491 *status = 0;
492
Antti Palosaaria8567cf2012-01-21 22:40:58 -0300493 if (priv->sleeping)
494 return 0;
495
Antti Palosaaric0adca72011-07-08 23:34:09 -0300496 ret = rtl2830_rd_reg_mask(priv, 0x351, &tmp, 0x78); /* [6:3] */
497 if (ret)
498 goto err;
499
500 if (tmp == 11) {
501 *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
502 FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
503 } else if (tmp == 10) {
504 *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
505 FE_HAS_VITERBI;
506 }
507
508 return ret;
509err:
510 dbg("%s: failed=%d", __func__, ret);
511 return ret;
512}
513
514static int rtl2830_read_snr(struct dvb_frontend *fe, u16 *snr)
515{
Antti Palosaarieba672a2012-05-15 18:32:33 -0300516 struct rtl2830_priv *priv = fe->demodulator_priv;
517 int ret, hierarchy, constellation;
518 u8 buf[2], tmp;
519 u16 tmp16;
520#define CONSTELLATION_NUM 3
521#define HIERARCHY_NUM 4
522 static const u32 snr_constant[CONSTELLATION_NUM][HIERARCHY_NUM] = {
523 { 70705899, 70705899, 70705899, 70705899 },
524 { 82433173, 82433173, 87483115, 94445660 },
525 { 92888734, 92888734, 95487525, 99770748 },
526 };
527
528 /* reports SNR in resolution of 0.1 dB */
529
530 ret = rtl2830_rd_reg(priv, 0x33c, &tmp);
531 if (ret)
532 goto err;
533
534 constellation = (tmp >> 2) & 0x03; /* [3:2] */
535 if (constellation > CONSTELLATION_NUM - 1)
536 goto err;
537
538 hierarchy = (tmp >> 4) & 0x07; /* [6:4] */
539 if (hierarchy > HIERARCHY_NUM - 1)
540 goto err;
541
542 ret = rtl2830_rd_regs(priv, 0x40c, buf, 2);
543 if (ret)
544 goto err;
545
546 tmp16 = buf[0] << 8 | buf[1];
547
548 if (tmp16)
549 *snr = (snr_constant[constellation][hierarchy] -
550 intlog10(tmp16)) / ((1 << 24) / 100);
551 else
552 *snr = 0;
553
Antti Palosaaric0adca72011-07-08 23:34:09 -0300554 return 0;
Antti Palosaarieba672a2012-05-15 18:32:33 -0300555err:
556 dbg("%s: failed=%d", __func__, ret);
557 return ret;
Antti Palosaaric0adca72011-07-08 23:34:09 -0300558}
559
560static int rtl2830_read_ber(struct dvb_frontend *fe, u32 *ber)
561{
Antti Palosaari525ffc12012-05-18 12:23:42 -0300562 struct rtl2830_priv *priv = fe->demodulator_priv;
563 int ret;
564 u8 buf[2];
565
566 ret = rtl2830_rd_regs(priv, 0x34e, buf, 2);
567 if (ret)
568 goto err;
569
570 *ber = buf[0] << 8 | buf[1];
571
Antti Palosaaric0adca72011-07-08 23:34:09 -0300572 return 0;
Antti Palosaari525ffc12012-05-18 12:23:42 -0300573err:
574 dbg("%s: failed=%d", __func__, ret);
575 return ret;
Antti Palosaaric0adca72011-07-08 23:34:09 -0300576}
577
578static int rtl2830_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
579{
580 *ucblocks = 0;
581 return 0;
582}
583
584static int rtl2830_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
585{
Antti Palosaari78e75072012-05-18 15:17:51 -0300586 struct rtl2830_priv *priv = fe->demodulator_priv;
587 int ret;
588 u8 buf[2];
589 u16 if_agc_raw, if_agc;
590
591 ret = rtl2830_rd_regs(priv, 0x359, buf, 2);
592 if (ret)
593 goto err;
594
595 if_agc_raw = (buf[0] << 8 | buf[1]) & 0x3fff;
596
597 if (if_agc_raw & (1 << 9))
598 if_agc = -(~(if_agc_raw - 1) & 0x1ff);
599 else
600 if_agc = if_agc_raw;
601
602 *strength = (u8) (55 - if_agc / 182);
603 *strength |= *strength << 8;
604
Antti Palosaaric0adca72011-07-08 23:34:09 -0300605 return 0;
Antti Palosaari78e75072012-05-18 15:17:51 -0300606err:
607 dbg("%s: failed=%d", __func__, ret);
608 return ret;
Antti Palosaaric0adca72011-07-08 23:34:09 -0300609}
610
611static struct dvb_frontend_ops rtl2830_ops;
612
613static u32 rtl2830_tuner_i2c_func(struct i2c_adapter *adapter)
614{
615 return I2C_FUNC_I2C;
616}
617
618static int rtl2830_tuner_i2c_xfer(struct i2c_adapter *i2c_adap,
619 struct i2c_msg msg[], int num)
620{
621 struct rtl2830_priv *priv = i2c_get_adapdata(i2c_adap);
622 int ret;
623
624 /* open i2c-gate */
625 ret = rtl2830_wr_reg_mask(priv, 0x101, 0x08, 0x08);
626 if (ret)
627 goto err;
628
629 ret = i2c_transfer(priv->i2c, msg, num);
630 if (ret < 0)
631 warn("tuner i2c failed=%d", ret);
632
633 return ret;
634err:
635 dbg("%s: failed=%d", __func__, ret);
636 return ret;
637}
638
639static struct i2c_algorithm rtl2830_tuner_i2c_algo = {
640 .master_xfer = rtl2830_tuner_i2c_xfer,
641 .functionality = rtl2830_tuner_i2c_func,
642};
643
644struct i2c_adapter *rtl2830_get_tuner_i2c_adapter(struct dvb_frontend *fe)
645{
646 struct rtl2830_priv *priv = fe->demodulator_priv;
647 return &priv->tuner_i2c_adapter;
648}
649EXPORT_SYMBOL(rtl2830_get_tuner_i2c_adapter);
650
651static void rtl2830_release(struct dvb_frontend *fe)
652{
653 struct rtl2830_priv *priv = fe->demodulator_priv;
654
655 i2c_del_adapter(&priv->tuner_i2c_adapter);
656 kfree(priv);
657}
658
659struct dvb_frontend *rtl2830_attach(const struct rtl2830_config *cfg,
660 struct i2c_adapter *i2c)
661{
662 struct rtl2830_priv *priv = NULL;
663 int ret = 0;
664 u8 tmp;
665
666 /* allocate memory for the internal state */
667 priv = kzalloc(sizeof(struct rtl2830_priv), GFP_KERNEL);
668 if (priv == NULL)
669 goto err;
670
671 /* setup the priv */
672 priv->i2c = i2c;
673 memcpy(&priv->cfg, cfg, sizeof(struct rtl2830_config));
674
675 /* check if the demod is there */
676 ret = rtl2830_rd_reg(priv, 0x000, &tmp);
677 if (ret)
678 goto err;
679
680 /* create dvb_frontend */
681 memcpy(&priv->fe.ops, &rtl2830_ops, sizeof(struct dvb_frontend_ops));
682 priv->fe.demodulator_priv = priv;
683
684 /* create tuner i2c adapter */
685 strlcpy(priv->tuner_i2c_adapter.name, "RTL2830 tuner I2C adapter",
686 sizeof(priv->tuner_i2c_adapter.name));
687 priv->tuner_i2c_adapter.algo = &rtl2830_tuner_i2c_algo;
688 priv->tuner_i2c_adapter.algo_data = NULL;
689 i2c_set_adapdata(&priv->tuner_i2c_adapter, priv);
690 if (i2c_add_adapter(&priv->tuner_i2c_adapter) < 0) {
691 err("tuner I2C bus could not be initialized");
692 goto err;
693 }
694
Antti Palosaaria8567cf2012-01-21 22:40:58 -0300695 priv->sleeping = true;
696
Antti Palosaaric0adca72011-07-08 23:34:09 -0300697 return &priv->fe;
698err:
699 dbg("%s: failed=%d", __func__, ret);
700 kfree(priv);
701 return NULL;
702}
703EXPORT_SYMBOL(rtl2830_attach);
704
705static struct dvb_frontend_ops rtl2830_ops = {
706 .delsys = { SYS_DVBT },
707 .info = {
708 .name = "Realtek RTL2830 (DVB-T)",
709 .caps = FE_CAN_FEC_1_2 |
710 FE_CAN_FEC_2_3 |
711 FE_CAN_FEC_3_4 |
712 FE_CAN_FEC_5_6 |
713 FE_CAN_FEC_7_8 |
714 FE_CAN_FEC_AUTO |
715 FE_CAN_QPSK |
716 FE_CAN_QAM_16 |
717 FE_CAN_QAM_64 |
718 FE_CAN_QAM_AUTO |
719 FE_CAN_TRANSMISSION_MODE_AUTO |
720 FE_CAN_GUARD_INTERVAL_AUTO |
721 FE_CAN_HIERARCHY_AUTO |
722 FE_CAN_RECOVER |
723 FE_CAN_MUTE_TS
724 },
725
726 .release = rtl2830_release,
727
728 .init = rtl2830_init,
Antti Palosaaria8567cf2012-01-21 22:40:58 -0300729 .sleep = rtl2830_sleep,
Antti Palosaaric0adca72011-07-08 23:34:09 -0300730
731 .get_tune_settings = rtl2830_get_tune_settings,
732
733 .set_frontend = rtl2830_set_frontend,
Antti Palosaari631a2b62012-05-18 15:58:57 -0300734 .get_frontend = rtl2830_get_frontend,
Antti Palosaaric0adca72011-07-08 23:34:09 -0300735
736 .read_status = rtl2830_read_status,
737 .read_snr = rtl2830_read_snr,
738 .read_ber = rtl2830_read_ber,
739 .read_ucblocks = rtl2830_read_ucblocks,
740 .read_signal_strength = rtl2830_read_signal_strength,
741};
742
743MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
744MODULE_DESCRIPTION("Realtek RTL2830 DVB-T demodulator driver");
745MODULE_LICENSE("GPL");