blob: 1d572940e243637994b302f16a56bdb5b7caa7c6 [file] [log] [blame]
Steven Toth265a6512008-04-18 21:34:00 -03001/*
2 Auvitek AU8522 QAM/8VSB demodulator driver
3
Steven Toth6d897612008-09-03 17:12:12 -03004 Copyright (C) 2008 Steven Toth <stoth@linuxtv.org>
Steven Toth265a6512008-04-18 21:34:00 -03005
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
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19
20*/
21
22#include <linux/kernel.h>
23#include <linux/init.h>
24#include <linux/module.h>
25#include <linux/string.h>
Steven Toth265a6512008-04-18 21:34:00 -030026#include <linux/delay.h>
27#include "dvb_frontend.h"
Steven Toth265a6512008-04-18 21:34:00 -030028#include "au8522.h"
Devin Heitmueller0c44bf32009-03-11 02:59:56 -030029#include "au8522_priv.h"
Steven Toth265a6512008-04-18 21:34:00 -030030
Mauro Carvalho Chehab18d73c52008-04-18 22:12:52 -030031static int debug;
32
Devin Heitmueller209fdf62009-03-11 03:00:36 -030033/* Despite the name "hybrid_tuner", the framework works just as well for
34 hybrid demodulators as well... */
35static LIST_HEAD(hybrid_tuner_instance_list);
Devin Heitmueller4ff5ed42009-03-11 03:00:45 -030036static DEFINE_MUTEX(au8522_list_mutex);
Devin Heitmueller209fdf62009-03-11 03:00:36 -030037
Devin Heitmueller62899a22009-03-15 18:48:52 -030038#define dprintk(arg...)\
39 do { if (debug)\
40 printk(arg);\
Mauro Carvalho Chehab18d73c52008-04-18 22:12:52 -030041 } while (0)
Steven Toth265a6512008-04-18 21:34:00 -030042
43/* 16 bit registers, 8 bit values */
Devin Heitmueller0c44bf32009-03-11 02:59:56 -030044int au8522_writereg(struct au8522_state *state, u16 reg, u8 data)
Steven Toth265a6512008-04-18 21:34:00 -030045{
46 int ret;
Devin Heitmueller62899a22009-03-15 18:48:52 -030047 u8 buf[] = { (reg >> 8) | 0x80, reg & 0xff, data };
Steven Toth265a6512008-04-18 21:34:00 -030048
49 struct i2c_msg msg = { .addr = state->config->demod_address,
50 .flags = 0, .buf = buf, .len = 3 };
51
52 ret = i2c_transfer(state->i2c, &msg, 1);
53
54 if (ret != 1)
55 printk("%s: writereg error (reg == 0x%02x, val == 0x%04x, "
Michael Krufkyce1719a2008-04-02 18:59:48 -030056 "ret == %i)\n", __func__, reg, data, ret);
Steven Toth265a6512008-04-18 21:34:00 -030057
58 return (ret != 1) ? -1 : 0;
59}
60
Devin Heitmueller0c44bf32009-03-11 02:59:56 -030061u8 au8522_readreg(struct au8522_state *state, u16 reg)
Steven Toth265a6512008-04-18 21:34:00 -030062{
63 int ret;
Devin Heitmueller62899a22009-03-15 18:48:52 -030064 u8 b0[] = { (reg >> 8) | 0x40, reg & 0xff };
65 u8 b1[] = { 0 };
Steven Toth265a6512008-04-18 21:34:00 -030066
Devin Heitmueller62899a22009-03-15 18:48:52 -030067 struct i2c_msg msg[] = {
Steven Toth265a6512008-04-18 21:34:00 -030068 { .addr = state->config->demod_address, .flags = 0,
69 .buf = b0, .len = 2 },
70 { .addr = state->config->demod_address, .flags = I2C_M_RD,
71 .buf = b1, .len = 1 } };
72
73 ret = i2c_transfer(state->i2c, msg, 2);
74
75 if (ret != 2)
Michael Krufkyce1719a2008-04-02 18:59:48 -030076 printk(KERN_ERR "%s: readreg error (ret == %i)\n",
77 __func__, ret);
Steven Toth265a6512008-04-18 21:34:00 -030078 return b1[0];
79}
80
Michael Krufkye059b0f2008-04-02 18:59:48 -030081static int au8522_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
Steven Toth265a6512008-04-18 21:34:00 -030082{
Michael Krufkye059b0f2008-04-02 18:59:48 -030083 struct au8522_state *state = fe->demodulator_priv;
Steven Toth265a6512008-04-18 21:34:00 -030084
Michael Krufkyce1719a2008-04-02 18:59:48 -030085 dprintk("%s(%d)\n", __func__, enable);
Steven Toth265a6512008-04-18 21:34:00 -030086
Devin Heitmueller7f2c9832010-02-17 22:47:55 -030087 if (state->operational_mode == AU8522_ANALOG_MODE) {
88 /* We're being asked to manage the gate even though we're
89 not in digital mode. This can occur if we get switched
90 over to analog mode before the dvb_frontend kernel thread
91 has completely shutdown */
92 return 0;
93 }
94
Steven Toth265a6512008-04-18 21:34:00 -030095 if (enable)
96 return au8522_writereg(state, 0x106, 1);
97 else
98 return au8522_writereg(state, 0x106, 0);
99}
100
Michael Krufky0daa5de2008-04-10 04:24:56 -0300101struct mse2snr_tab {
Steven Tothf01699b2008-04-10 02:01:48 -0300102 u16 val;
103 u16 data;
Michael Krufky0daa5de2008-04-10 04:24:56 -0300104};
105
106/* VSB SNR lookup table */
107static struct mse2snr_tab vsb_mse2snr_tab[] = {
Steven Tothf01699b2008-04-10 02:01:48 -0300108 { 0, 270 },
109 { 2, 250 },
110 { 3, 240 },
111 { 5, 230 },
112 { 7, 220 },
113 { 9, 210 },
114 { 12, 200 },
115 { 13, 195 },
116 { 15, 190 },
117 { 17, 185 },
118 { 19, 180 },
119 { 21, 175 },
120 { 24, 170 },
121 { 27, 165 },
122 { 31, 160 },
123 { 32, 158 },
124 { 33, 156 },
125 { 36, 152 },
126 { 37, 150 },
127 { 39, 148 },
128 { 40, 146 },
129 { 41, 144 },
130 { 43, 142 },
131 { 44, 140 },
132 { 48, 135 },
133 { 50, 130 },
134 { 43, 142 },
135 { 53, 125 },
136 { 56, 120 },
137 { 256, 115 },
138};
139
140/* QAM64 SNR lookup table */
Michael Krufky0daa5de2008-04-10 04:24:56 -0300141static struct mse2snr_tab qam64_mse2snr_tab[] = {
Steven Tothf01699b2008-04-10 02:01:48 -0300142 { 15, 0 },
143 { 16, 290 },
144 { 17, 288 },
145 { 18, 286 },
146 { 19, 284 },
147 { 20, 282 },
148 { 21, 281 },
149 { 22, 279 },
150 { 23, 277 },
151 { 24, 275 },
152 { 25, 273 },
153 { 26, 271 },
154 { 27, 269 },
155 { 28, 268 },
156 { 29, 266 },
157 { 30, 264 },
158 { 31, 262 },
159 { 32, 260 },
160 { 33, 259 },
161 { 34, 258 },
162 { 35, 256 },
163 { 36, 255 },
164 { 37, 254 },
165 { 38, 252 },
166 { 39, 251 },
167 { 40, 250 },
168 { 41, 249 },
169 { 42, 248 },
170 { 43, 246 },
171 { 44, 245 },
172 { 45, 244 },
173 { 46, 242 },
174 { 47, 241 },
175 { 48, 240 },
176 { 50, 239 },
177 { 51, 238 },
178 { 53, 237 },
179 { 54, 236 },
180 { 56, 235 },
181 { 57, 234 },
182 { 59, 233 },
183 { 60, 232 },
184 { 62, 231 },
185 { 63, 230 },
186 { 65, 229 },
187 { 67, 228 },
188 { 68, 227 },
189 { 70, 226 },
190 { 71, 225 },
191 { 73, 224 },
192 { 74, 223 },
193 { 76, 222 },
194 { 78, 221 },
195 { 80, 220 },
196 { 82, 219 },
197 { 85, 218 },
198 { 88, 217 },
199 { 90, 216 },
200 { 92, 215 },
201 { 93, 214 },
202 { 94, 212 },
203 { 95, 211 },
204 { 97, 210 },
205 { 99, 209 },
206 { 101, 208 },
207 { 102, 207 },
208 { 104, 206 },
209 { 107, 205 },
210 { 111, 204 },
211 { 114, 203 },
212 { 118, 202 },
213 { 122, 201 },
214 { 125, 200 },
215 { 128, 199 },
216 { 130, 198 },
217 { 132, 197 },
218 { 256, 190 },
219};
220
221/* QAM256 SNR lookup table */
Michael Krufky0daa5de2008-04-10 04:24:56 -0300222static struct mse2snr_tab qam256_mse2snr_tab[] = {
Steven Tothf01699b2008-04-10 02:01:48 -0300223 { 16, 0 },
224 { 17, 400 },
225 { 18, 398 },
226 { 19, 396 },
227 { 20, 394 },
228 { 21, 392 },
229 { 22, 390 },
230 { 23, 388 },
231 { 24, 386 },
232 { 25, 384 },
233 { 26, 382 },
234 { 27, 380 },
235 { 28, 379 },
236 { 29, 378 },
237 { 30, 377 },
238 { 31, 376 },
239 { 32, 375 },
240 { 33, 374 },
241 { 34, 373 },
242 { 35, 372 },
243 { 36, 371 },
244 { 37, 370 },
245 { 38, 362 },
246 { 39, 354 },
247 { 40, 346 },
248 { 41, 338 },
249 { 42, 330 },
250 { 43, 328 },
251 { 44, 326 },
252 { 45, 324 },
253 { 46, 322 },
254 { 47, 320 },
255 { 48, 319 },
256 { 49, 318 },
257 { 50, 317 },
258 { 51, 316 },
259 { 52, 315 },
260 { 53, 314 },
261 { 54, 313 },
262 { 55, 312 },
263 { 56, 311 },
264 { 57, 310 },
265 { 58, 308 },
266 { 59, 306 },
267 { 60, 304 },
268 { 61, 302 },
269 { 62, 300 },
270 { 63, 298 },
271 { 65, 295 },
272 { 68, 294 },
273 { 70, 293 },
274 { 73, 292 },
275 { 76, 291 },
276 { 78, 290 },
277 { 79, 289 },
278 { 81, 288 },
279 { 82, 287 },
280 { 83, 286 },
281 { 84, 285 },
282 { 85, 284 },
283 { 86, 283 },
284 { 88, 282 },
285 { 89, 281 },
286 { 256, 280 },
287};
288
Mauro Carvalho Chehab18d73c52008-04-18 22:12:52 -0300289static int au8522_mse2snr_lookup(struct mse2snr_tab *tab, int sz, int mse,
290 u16 *snr)
Steven Tothf01699b2008-04-10 02:01:48 -0300291{
292 int i, ret = -EINVAL;
293 dprintk("%s()\n", __func__);
294
Michael Krufky0daa5de2008-04-10 04:24:56 -0300295 for (i = 0; i < sz; i++) {
296 if (mse < tab[i].val) {
297 *snr = tab[i].data;
Steven Tothf01699b2008-04-10 02:01:48 -0300298 ret = 0;
299 break;
300 }
301 }
302 dprintk("%s() snr=%d\n", __func__, *snr);
303 return ret;
304}
305
Michael Krufky2e7acd72008-09-03 16:46:35 -0300306static int au8522_set_if(struct dvb_frontend *fe, enum au8522_if_freq if_freq)
307{
308 struct au8522_state *state = fe->demodulator_priv;
Michael Krufkydf76de02008-09-03 16:46:40 -0300309 u8 r0b5, r0b6, r0b7;
310 char *ifmhz;
Michael Krufky2e7acd72008-09-03 16:46:35 -0300311
312 switch (if_freq) {
313 case AU8522_IF_3_25MHZ:
Michael Krufkydf76de02008-09-03 16:46:40 -0300314 ifmhz = "3.25";
315 r0b5 = 0x00;
316 r0b6 = 0x3d;
317 r0b7 = 0xa0;
Michael Krufky2e7acd72008-09-03 16:46:35 -0300318 break;
319 case AU8522_IF_4MHZ:
Michael Krufkydf76de02008-09-03 16:46:40 -0300320 ifmhz = "4.00";
321 r0b5 = 0x00;
322 r0b6 = 0x4b;
323 r0b7 = 0xd9;
Michael Krufky2e7acd72008-09-03 16:46:35 -0300324 break;
325 case AU8522_IF_6MHZ:
Michael Krufkydf76de02008-09-03 16:46:40 -0300326 ifmhz = "6.00";
327 r0b5 = 0xfb;
328 r0b6 = 0x8e;
329 r0b7 = 0x39;
Michael Krufky2e7acd72008-09-03 16:46:35 -0300330 break;
331 default:
332 dprintk("%s() IF Frequency not supported\n", __func__);
333 return -EINVAL;
334 }
Michael Krufkydf76de02008-09-03 16:46:40 -0300335 dprintk("%s() %s MHz\n", __func__, ifmhz);
336 au8522_writereg(state, 0x80b5, r0b5);
337 au8522_writereg(state, 0x80b6, r0b6);
338 au8522_writereg(state, 0x80b7, r0b7);
339
Michael Krufky2e7acd72008-09-03 16:46:35 -0300340 return 0;
341}
342
Steven Tothf01699b2008-04-10 02:01:48 -0300343/* VSB Modulation table */
344static struct {
345 u16 reg;
346 u16 data;
347} VSB_mod_tab[] = {
348 { 0x8090, 0x84 },
349 { 0x4092, 0x11 },
350 { 0x2005, 0x00 },
351 { 0x8091, 0x80 },
352 { 0x80a3, 0x0c },
353 { 0x80a4, 0xe8 },
354 { 0x8081, 0xc4 },
355 { 0x80a5, 0x40 },
356 { 0x80a7, 0x40 },
357 { 0x80a6, 0x67 },
358 { 0x8262, 0x20 },
359 { 0x821c, 0x30 },
360 { 0x80d8, 0x1a },
361 { 0x8227, 0xa0 },
362 { 0x8121, 0xff },
363 { 0x80a8, 0xf0 },
364 { 0x80a9, 0x05 },
365 { 0x80aa, 0x77 },
366 { 0x80ab, 0xf0 },
367 { 0x80ac, 0x05 },
368 { 0x80ad, 0x77 },
369 { 0x80ae, 0x41 },
370 { 0x80af, 0x66 },
371 { 0x821b, 0xcc },
372 { 0x821d, 0x80 },
Steven Tothf01699b2008-04-10 02:01:48 -0300373 { 0x80a4, 0xe8 },
374 { 0x8231, 0x13 },
375};
376
Frank Dischner040d4cb2009-06-14 23:05:20 -0300377/* QAM64 Modulation table */
Steven Tothf01699b2008-04-10 02:01:48 -0300378static struct {
379 u16 reg;
380 u16 data;
Frank Dischner040d4cb2009-06-14 23:05:20 -0300381} QAM64_mod_tab[] = {
382 { 0x00a3, 0x09 },
383 { 0x00a4, 0x00 },
384 { 0x0081, 0xc4 },
385 { 0x00a5, 0x40 },
386 { 0x00aa, 0x77 },
387 { 0x00ad, 0x77 },
388 { 0x00a6, 0x67 },
389 { 0x0262, 0x20 },
390 { 0x021c, 0x30 },
391 { 0x00b8, 0x3e },
392 { 0x00b9, 0xf0 },
393 { 0x00ba, 0x01 },
394 { 0x00bb, 0x18 },
395 { 0x00bc, 0x50 },
396 { 0x00bd, 0x00 },
397 { 0x00be, 0xea },
398 { 0x00bf, 0xef },
399 { 0x00c0, 0xfc },
400 { 0x00c1, 0xbd },
401 { 0x00c2, 0x1f },
402 { 0x00c3, 0xfc },
403 { 0x00c4, 0xdd },
404 { 0x00c5, 0xaf },
405 { 0x00c6, 0x00 },
406 { 0x00c7, 0x38 },
407 { 0x00c8, 0x30 },
408 { 0x00c9, 0x05 },
409 { 0x00ca, 0x4a },
410 { 0x00cb, 0xd0 },
411 { 0x00cc, 0x01 },
412 { 0x00cd, 0xd9 },
413 { 0x00ce, 0x6f },
414 { 0x00cf, 0xf9 },
415 { 0x00d0, 0x70 },
416 { 0x00d1, 0xdf },
417 { 0x00d2, 0xf7 },
418 { 0x00d3, 0xc2 },
419 { 0x00d4, 0xdf },
420 { 0x00d5, 0x02 },
421 { 0x00d6, 0x9a },
422 { 0x00d7, 0xd0 },
423 { 0x0250, 0x0d },
424 { 0x0251, 0xcd },
425 { 0x0252, 0xe0 },
426 { 0x0253, 0x05 },
427 { 0x0254, 0xa7 },
428 { 0x0255, 0xff },
429 { 0x0256, 0xed },
430 { 0x0257, 0x5b },
431 { 0x0258, 0xae },
432 { 0x0259, 0xe6 },
433 { 0x025a, 0x3d },
434 { 0x025b, 0x0f },
435 { 0x025c, 0x0d },
436 { 0x025d, 0xea },
437 { 0x025e, 0xf2 },
438 { 0x025f, 0x51 },
439 { 0x0260, 0xf5 },
440 { 0x0261, 0x06 },
441 { 0x021a, 0x00 },
442 { 0x0546, 0x40 },
443 { 0x0210, 0xc7 },
444 { 0x0211, 0xaa },
445 { 0x0212, 0xab },
446 { 0x0213, 0x02 },
447 { 0x0502, 0x00 },
448 { 0x0121, 0x04 },
449 { 0x0122, 0x04 },
450 { 0x052e, 0x10 },
451 { 0x00a4, 0xca },
452 { 0x00a7, 0x40 },
453 { 0x0526, 0x01 },
454};
455
456/* QAM256 Modulation table */
457static struct {
458 u16 reg;
459 u16 data;
460} QAM256_mod_tab[] = {
Steven Tothf01699b2008-04-10 02:01:48 -0300461 { 0x80a3, 0x09 },
462 { 0x80a4, 0x00 },
463 { 0x8081, 0xc4 },
464 { 0x80a5, 0x40 },
Steven Tothf01699b2008-04-10 02:01:48 -0300465 { 0x80aa, 0x77 },
466 { 0x80ad, 0x77 },
467 { 0x80a6, 0x67 },
468 { 0x8262, 0x20 },
469 { 0x821c, 0x30 },
470 { 0x80b8, 0x3e },
471 { 0x80b9, 0xf0 },
472 { 0x80ba, 0x01 },
473 { 0x80bb, 0x18 },
474 { 0x80bc, 0x50 },
475 { 0x80bd, 0x00 },
476 { 0x80be, 0xea },
477 { 0x80bf, 0xef },
478 { 0x80c0, 0xfc },
479 { 0x80c1, 0xbd },
480 { 0x80c2, 0x1f },
481 { 0x80c3, 0xfc },
482 { 0x80c4, 0xdd },
483 { 0x80c5, 0xaf },
484 { 0x80c6, 0x00 },
485 { 0x80c7, 0x38 },
486 { 0x80c8, 0x30 },
487 { 0x80c9, 0x05 },
488 { 0x80ca, 0x4a },
489 { 0x80cb, 0xd0 },
490 { 0x80cc, 0x01 },
491 { 0x80cd, 0xd9 },
492 { 0x80ce, 0x6f },
493 { 0x80cf, 0xf9 },
494 { 0x80d0, 0x70 },
495 { 0x80d1, 0xdf },
496 { 0x80d2, 0xf7 },
497 { 0x80d3, 0xc2 },
498 { 0x80d4, 0xdf },
499 { 0x80d5, 0x02 },
500 { 0x80d6, 0x9a },
501 { 0x80d7, 0xd0 },
502 { 0x8250, 0x0d },
503 { 0x8251, 0xcd },
504 { 0x8252, 0xe0 },
505 { 0x8253, 0x05 },
506 { 0x8254, 0xa7 },
507 { 0x8255, 0xff },
508 { 0x8256, 0xed },
509 { 0x8257, 0x5b },
510 { 0x8258, 0xae },
511 { 0x8259, 0xe6 },
512 { 0x825a, 0x3d },
513 { 0x825b, 0x0f },
514 { 0x825c, 0x0d },
515 { 0x825d, 0xea },
516 { 0x825e, 0xf2 },
517 { 0x825f, 0x51 },
518 { 0x8260, 0xf5 },
519 { 0x8261, 0x06 },
520 { 0x821a, 0x00 },
521 { 0x8546, 0x40 },
522 { 0x8210, 0x26 },
523 { 0x8211, 0xf6 },
524 { 0x8212, 0x84 },
525 { 0x8213, 0x02 },
526 { 0x8502, 0x01 },
527 { 0x8121, 0x04 },
528 { 0x8122, 0x04 },
529 { 0x852e, 0x10 },
530 { 0x80a4, 0xca },
531 { 0x80a7, 0x40 },
532 { 0x8526, 0x01 },
533};
534
Michael Krufkye059b0f2008-04-02 18:59:48 -0300535static int au8522_enable_modulation(struct dvb_frontend *fe,
536 fe_modulation_t m)
Steven Toth265a6512008-04-18 21:34:00 -0300537{
Michael Krufkye059b0f2008-04-02 18:59:48 -0300538 struct au8522_state *state = fe->demodulator_priv;
Steven Tothf01699b2008-04-10 02:01:48 -0300539 int i;
Steven Toth265a6512008-04-18 21:34:00 -0300540
Michael Krufkyce1719a2008-04-02 18:59:48 -0300541 dprintk("%s(0x%08x)\n", __func__, m);
Steven Toth265a6512008-04-18 21:34:00 -0300542
Mauro Carvalho Chehab18d73c52008-04-18 22:12:52 -0300543 switch (m) {
Steven Toth265a6512008-04-18 21:34:00 -0300544 case VSB_8:
Michael Krufkyce1719a2008-04-02 18:59:48 -0300545 dprintk("%s() VSB_8\n", __func__);
Steven Tothf01699b2008-04-10 02:01:48 -0300546 for (i = 0; i < ARRAY_SIZE(VSB_mod_tab); i++)
547 au8522_writereg(state,
548 VSB_mod_tab[i].reg,
549 VSB_mod_tab[i].data);
Michael Krufky2e7acd72008-09-03 16:46:35 -0300550 au8522_set_if(fe, state->config->vsb_if);
Steven Toth265a6512008-04-18 21:34:00 -0300551 break;
552 case QAM_64:
Frank Dischner040d4cb2009-06-14 23:05:20 -0300553 dprintk("%s() QAM 64\n", __func__);
554 for (i = 0; i < ARRAY_SIZE(QAM64_mod_tab); i++)
Steven Tothf01699b2008-04-10 02:01:48 -0300555 au8522_writereg(state,
Frank Dischner040d4cb2009-06-14 23:05:20 -0300556 QAM64_mod_tab[i].reg,
557 QAM64_mod_tab[i].data);
558 au8522_set_if(fe, state->config->qam_if);
559 break;
560 case QAM_256:
561 dprintk("%s() QAM 256\n", __func__);
562 for (i = 0; i < ARRAY_SIZE(QAM256_mod_tab); i++)
563 au8522_writereg(state,
564 QAM256_mod_tab[i].reg,
565 QAM256_mod_tab[i].data);
Michael Krufky2e7acd72008-09-03 16:46:35 -0300566 au8522_set_if(fe, state->config->qam_if);
Steven Toth265a6512008-04-18 21:34:00 -0300567 break;
568 default:
Michael Krufkyce1719a2008-04-02 18:59:48 -0300569 dprintk("%s() Invalid modulation\n", __func__);
Steven Toth265a6512008-04-18 21:34:00 -0300570 return -EINVAL;
571 }
572
573 state->current_modulation = m;
574
575 return 0;
576}
577
578/* Talk to the demod, set the FEC, GUARD, QAM settings etc */
Michael Krufkye059b0f2008-04-02 18:59:48 -0300579static int au8522_set_frontend(struct dvb_frontend *fe,
580 struct dvb_frontend_parameters *p)
Steven Toth265a6512008-04-18 21:34:00 -0300581{
Michael Krufkye059b0f2008-04-02 18:59:48 -0300582 struct au8522_state *state = fe->demodulator_priv;
Michael Krufky74d50722008-06-13 20:33:23 -0300583 int ret = -EINVAL;
Steven Toth265a6512008-04-18 21:34:00 -0300584
Michael Krufkyce1719a2008-04-02 18:59:48 -0300585 dprintk("%s(frequency=%d)\n", __func__, p->frequency);
Steven Toth265a6512008-04-18 21:34:00 -0300586
Michael Krufky74d50722008-06-13 20:33:23 -0300587 if ((state->current_frequency == p->frequency) &&
588 (state->current_modulation == p->u.vsb.modulation))
589 return 0;
Steven Toth265a6512008-04-18 21:34:00 -0300590
591 au8522_enable_modulation(fe, p->u.vsb.modulation);
592
593 /* Allow the demod to settle */
594 msleep(100);
595
596 if (fe->ops.tuner_ops.set_params) {
Mauro Carvalho Chehab18d73c52008-04-18 22:12:52 -0300597 if (fe->ops.i2c_gate_ctrl)
598 fe->ops.i2c_gate_ctrl(fe, 1);
Michael Krufky74d50722008-06-13 20:33:23 -0300599 ret = fe->ops.tuner_ops.set_params(fe, p);
Mauro Carvalho Chehab18d73c52008-04-18 22:12:52 -0300600 if (fe->ops.i2c_gate_ctrl)
601 fe->ops.i2c_gate_ctrl(fe, 0);
Steven Toth265a6512008-04-18 21:34:00 -0300602 }
603
Michael Krufky74d50722008-06-13 20:33:23 -0300604 if (ret < 0)
605 return ret;
606
607 state->current_frequency = p->frequency;
608
Steven Toth265a6512008-04-18 21:34:00 -0300609 return 0;
610}
611
612/* Reset the demod hardware and reset all of the configuration registers
613 to a default state. */
Devin Heitmueller0c44bf32009-03-11 02:59:56 -0300614int au8522_init(struct dvb_frontend *fe)
Steven Toth265a6512008-04-18 21:34:00 -0300615{
Michael Krufkye059b0f2008-04-02 18:59:48 -0300616 struct au8522_state *state = fe->demodulator_priv;
Michael Krufkyce1719a2008-04-02 18:59:48 -0300617 dprintk("%s()\n", __func__);
Steven Toth265a6512008-04-18 21:34:00 -0300618
Devin Heitmueller7f2c9832010-02-17 22:47:55 -0300619 state->operational_mode = AU8522_DIGITAL_MODE;
620
Devin Heitmuellerb628a2a2010-02-17 22:51:46 -0300621 /* Clear out any state associated with the digital side of the
622 chip, so that when it gets powered back up it won't think
623 that it is already tuned */
624 state->current_frequency = 0;
625
Steven Toth265a6512008-04-18 21:34:00 -0300626 au8522_writereg(state, 0xa4, 1 << 5);
627
628 au8522_i2c_gate_ctrl(fe, 1);
629
630 return 0;
631}
632
Michael Krufkyadeeac32008-04-05 20:55:14 -0300633static int au8522_led_gpio_enable(struct au8522_state *state, int onoff)
634{
635 struct au8522_led_config *led_config = state->config->led_cfg;
636 u8 val;
637
Lucas De Marchi25985ed2011-03-30 22:57:33 -0300638 /* bail out if we can't control an LED */
Michael Krufkyadeeac32008-04-05 20:55:14 -0300639 if (!led_config || !led_config->gpio_output ||
640 !led_config->gpio_output_enable || !led_config->gpio_output_disable)
641 return 0;
642
643 val = au8522_readreg(state, 0x4000 |
644 (led_config->gpio_output & ~0xc000));
645 if (onoff) {
646 /* enable GPIO output */
647 val &= ~((led_config->gpio_output_enable >> 8) & 0xff);
648 val |= (led_config->gpio_output_enable & 0xff);
649 } else {
650 /* disable GPIO output */
651 val &= ~((led_config->gpio_output_disable >> 8) & 0xff);
652 val |= (led_config->gpio_output_disable & 0xff);
653 }
654 return au8522_writereg(state, 0x8000 |
655 (led_config->gpio_output & ~0xc000), val);
656}
657
658/* led = 0 | off
659 * led = 1 | signal ok
660 * led = 2 | signal strong
661 * led < 0 | only light led if leds are currently off
662 */
663static int au8522_led_ctrl(struct au8522_state *state, int led)
664{
665 struct au8522_led_config *led_config = state->config->led_cfg;
666 int i, ret = 0;
667
Lucas De Marchi25985ed2011-03-30 22:57:33 -0300668 /* bail out if we can't control an LED */
Michael Krufkyadeeac32008-04-05 20:55:14 -0300669 if (!led_config || !led_config->gpio_leds ||
670 !led_config->num_led_states || !led_config->led_states)
671 return 0;
672
673 if (led < 0) {
674 /* if LED is already lit, then leave it as-is */
675 if (state->led_state)
676 return 0;
677 else
678 led *= -1;
679 }
680
681 /* toggle LED if changing state */
682 if (state->led_state != led) {
683 u8 val;
684
685 dprintk("%s: %d\n", __func__, led);
686
687 au8522_led_gpio_enable(state, 1);
688
689 val = au8522_readreg(state, 0x4000 |
690 (led_config->gpio_leds & ~0xc000));
691
692 /* start with all leds off */
693 for (i = 0; i < led_config->num_led_states; i++)
694 val &= ~led_config->led_states[i];
695
696 /* set selected LED state */
697 if (led < led_config->num_led_states)
698 val |= led_config->led_states[led];
699 else if (led_config->num_led_states)
700 val |=
701 led_config->led_states[led_config->num_led_states - 1];
702
703 ret = au8522_writereg(state, 0x8000 |
704 (led_config->gpio_leds & ~0xc000), val);
705 if (ret < 0)
706 return ret;
707
708 state->led_state = led;
709
710 if (led == 0)
711 au8522_led_gpio_enable(state, 0);
712 }
713
714 return 0;
715}
716
Devin Heitmueller0c44bf32009-03-11 02:59:56 -0300717int au8522_sleep(struct dvb_frontend *fe)
Michael Krufky74d50722008-06-13 20:33:23 -0300718{
719 struct au8522_state *state = fe->demodulator_priv;
720 dprintk("%s()\n", __func__);
721
Devin Heitmueller7f2c9832010-02-17 22:47:55 -0300722 /* Only power down if the digital side is currently using the chip */
723 if (state->operational_mode == AU8522_ANALOG_MODE) {
724 /* We're not in one of the expected power modes, which means
725 that the DVB thread is probably telling us to go to sleep
726 even though the analog frontend has already started using
727 the chip. So ignore the request */
728 return 0;
729 }
730
Michael Krufkyadeeac32008-04-05 20:55:14 -0300731 /* turn off led */
732 au8522_led_ctrl(state, 0);
733
Devin Heitmueller7bf63ed2009-03-11 03:00:34 -0300734 /* Power down the chip */
735 au8522_writereg(state, 0xa4, 1 << 5);
736
Michael Krufky74d50722008-06-13 20:33:23 -0300737 state->current_frequency = 0;
738
739 return 0;
740}
741
Michael Krufkye059b0f2008-04-02 18:59:48 -0300742static int au8522_read_status(struct dvb_frontend *fe, fe_status_t *status)
Steven Toth265a6512008-04-18 21:34:00 -0300743{
Michael Krufkye059b0f2008-04-02 18:59:48 -0300744 struct au8522_state *state = fe->demodulator_priv;
Steven Toth265a6512008-04-18 21:34:00 -0300745 u8 reg;
746 u32 tuner_status = 0;
747
748 *status = 0;
749
750 if (state->current_modulation == VSB_8) {
Michael Krufkyce1719a2008-04-02 18:59:48 -0300751 dprintk("%s() Checking VSB_8\n", __func__);
Steven Toth265a6512008-04-18 21:34:00 -0300752 reg = au8522_readreg(state, 0x4088);
Steven Toth836c2852008-06-21 19:32:41 -0300753 if ((reg & 0x03) == 0x03)
754 *status |= FE_HAS_LOCK | FE_HAS_SYNC | FE_HAS_VITERBI;
Steven Toth265a6512008-04-18 21:34:00 -0300755 } else {
Michael Krufkyce1719a2008-04-02 18:59:48 -0300756 dprintk("%s() Checking QAM\n", __func__);
Steven Toth265a6512008-04-18 21:34:00 -0300757 reg = au8522_readreg(state, 0x4541);
Michael Krufkye059b0f2008-04-02 18:59:48 -0300758 if (reg & 0x80)
Steven Toth265a6512008-04-18 21:34:00 -0300759 *status |= FE_HAS_VITERBI;
Michael Krufkye059b0f2008-04-02 18:59:48 -0300760 if (reg & 0x20)
Steven Toth265a6512008-04-18 21:34:00 -0300761 *status |= FE_HAS_LOCK | FE_HAS_SYNC;
762 }
763
Mauro Carvalho Chehab18d73c52008-04-18 22:12:52 -0300764 switch (state->config->status_mode) {
Steven Toth265a6512008-04-18 21:34:00 -0300765 case AU8522_DEMODLOCKING:
Michael Krufkyce1719a2008-04-02 18:59:48 -0300766 dprintk("%s() DEMODLOCKING\n", __func__);
Steven Toth265a6512008-04-18 21:34:00 -0300767 if (*status & FE_HAS_VITERBI)
768 *status |= FE_HAS_CARRIER | FE_HAS_SIGNAL;
769 break;
770 case AU8522_TUNERLOCKING:
771 /* Get the tuner status */
Michael Krufkyce1719a2008-04-02 18:59:48 -0300772 dprintk("%s() TUNERLOCKING\n", __func__);
Steven Toth265a6512008-04-18 21:34:00 -0300773 if (fe->ops.tuner_ops.get_status) {
774 if (fe->ops.i2c_gate_ctrl)
775 fe->ops.i2c_gate_ctrl(fe, 1);
776
777 fe->ops.tuner_ops.get_status(fe, &tuner_status);
778
779 if (fe->ops.i2c_gate_ctrl)
780 fe->ops.i2c_gate_ctrl(fe, 0);
781 }
782 if (tuner_status)
783 *status |= FE_HAS_CARRIER | FE_HAS_SIGNAL;
784 break;
785 }
Michael Krufkyadeeac32008-04-05 20:55:14 -0300786 state->fe_status = *status;
787
788 if (*status & FE_HAS_LOCK)
789 /* turn on LED, if it isn't on already */
790 au8522_led_ctrl(state, -1);
791 else
792 /* turn off LED */
793 au8522_led_ctrl(state, 0);
Steven Toth265a6512008-04-18 21:34:00 -0300794
Michael Krufkyce1719a2008-04-02 18:59:48 -0300795 dprintk("%s() status 0x%08x\n", __func__, *status);
Steven Toth265a6512008-04-18 21:34:00 -0300796
797 return 0;
798}
799
Michael Krufkyadeeac32008-04-05 20:55:14 -0300800static int au8522_led_status(struct au8522_state *state, const u16 *snr)
801{
802 struct au8522_led_config *led_config = state->config->led_cfg;
803 int led;
804 u16 strong;
805
Lucas De Marchi25985ed2011-03-30 22:57:33 -0300806 /* bail out if we can't control an LED */
Michael Krufkyadeeac32008-04-05 20:55:14 -0300807 if (!led_config)
808 return 0;
809
810 if (0 == (state->fe_status & FE_HAS_LOCK))
811 return au8522_led_ctrl(state, 0);
812 else if (state->current_modulation == QAM_256)
813 strong = led_config->qam256_strong;
814 else if (state->current_modulation == QAM_64)
815 strong = led_config->qam64_strong;
816 else /* (state->current_modulation == VSB_8) */
817 strong = led_config->vsb8_strong;
818
819 if (*snr >= strong)
820 led = 2;
821 else
822 led = 1;
823
824 if ((state->led_state) &&
825 (((strong < *snr) ? (*snr - strong) : (strong - *snr)) <= 10))
826 /* snr didn't change enough to bother
827 * changing the color of the led */
828 return 0;
829
830 return au8522_led_ctrl(state, led);
831}
832
Michael Krufkye059b0f2008-04-02 18:59:48 -0300833static int au8522_read_snr(struct dvb_frontend *fe, u16 *snr)
Steven Toth265a6512008-04-18 21:34:00 -0300834{
Steven Tothf01699b2008-04-10 02:01:48 -0300835 struct au8522_state *state = fe->demodulator_priv;
836 int ret = -EINVAL;
837
Michael Krufkyce1719a2008-04-02 18:59:48 -0300838 dprintk("%s()\n", __func__);
Steven Toth265a6512008-04-18 21:34:00 -0300839
Steven Tothf01699b2008-04-10 02:01:48 -0300840 if (state->current_modulation == QAM_256)
Michael Krufky0daa5de2008-04-10 04:24:56 -0300841 ret = au8522_mse2snr_lookup(qam256_mse2snr_tab,
842 ARRAY_SIZE(qam256_mse2snr_tab),
843 au8522_readreg(state, 0x4522),
844 snr);
Steven Tothf01699b2008-04-10 02:01:48 -0300845 else if (state->current_modulation == QAM_64)
Michael Krufky0daa5de2008-04-10 04:24:56 -0300846 ret = au8522_mse2snr_lookup(qam64_mse2snr_tab,
847 ARRAY_SIZE(qam64_mse2snr_tab),
848 au8522_readreg(state, 0x4522),
849 snr);
Steven Tothf01699b2008-04-10 02:01:48 -0300850 else /* VSB_8 */
Michael Krufky0daa5de2008-04-10 04:24:56 -0300851 ret = au8522_mse2snr_lookup(vsb_mse2snr_tab,
852 ARRAY_SIZE(vsb_mse2snr_tab),
853 au8522_readreg(state, 0x4311),
854 snr);
Steven Toth265a6512008-04-18 21:34:00 -0300855
Michael Krufkyadeeac32008-04-05 20:55:14 -0300856 if (state->config->led_cfg)
857 au8522_led_status(state, snr);
858
Steven Tothf01699b2008-04-10 02:01:48 -0300859 return ret;
Steven Toth265a6512008-04-18 21:34:00 -0300860}
861
Michael Krufkye059b0f2008-04-02 18:59:48 -0300862static int au8522_read_signal_strength(struct dvb_frontend *fe,
863 u16 *signal_strength)
Steven Toth265a6512008-04-18 21:34:00 -0300864{
865 return au8522_read_snr(fe, signal_strength);
866}
867
Michael Krufkye059b0f2008-04-02 18:59:48 -0300868static int au8522_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
Steven Toth265a6512008-04-18 21:34:00 -0300869{
Michael Krufkye059b0f2008-04-02 18:59:48 -0300870 struct au8522_state *state = fe->demodulator_priv;
Steven Toth265a6512008-04-18 21:34:00 -0300871
Michael Krufky8973dc42008-04-05 23:08:08 -0300872 if (state->current_modulation == VSB_8)
873 *ucblocks = au8522_readreg(state, 0x4087);
874 else
875 *ucblocks = au8522_readreg(state, 0x4543);
Steven Toth265a6512008-04-18 21:34:00 -0300876
877 return 0;
878}
879
Michael Krufkye059b0f2008-04-02 18:59:48 -0300880static int au8522_read_ber(struct dvb_frontend *fe, u32 *ber)
Steven Toth265a6512008-04-18 21:34:00 -0300881{
882 return au8522_read_ucblocks(fe, ber);
883}
884
Michael Krufkye059b0f2008-04-02 18:59:48 -0300885static int au8522_get_frontend(struct dvb_frontend *fe,
Steven Toth265a6512008-04-18 21:34:00 -0300886 struct dvb_frontend_parameters *p)
887{
Michael Krufkye059b0f2008-04-02 18:59:48 -0300888 struct au8522_state *state = fe->demodulator_priv;
Steven Toth265a6512008-04-18 21:34:00 -0300889
890 p->frequency = state->current_frequency;
891 p->u.vsb.modulation = state->current_modulation;
892
893 return 0;
894}
895
Michael Krufkye059b0f2008-04-02 18:59:48 -0300896static int au8522_get_tune_settings(struct dvb_frontend *fe,
897 struct dvb_frontend_tune_settings *tune)
Steven Toth265a6512008-04-18 21:34:00 -0300898{
899 tune->min_delay_ms = 1000;
900 return 0;
901}
902
Devin Heitmueller209fdf62009-03-11 03:00:36 -0300903static struct dvb_frontend_ops au8522_ops;
904
905int au8522_get_state(struct au8522_state **state, struct i2c_adapter *i2c,
906 u8 client_address)
907{
Devin Heitmueller4ff5ed42009-03-11 03:00:45 -0300908 int ret;
909
910 mutex_lock(&au8522_list_mutex);
911 ret = hybrid_tuner_request_state(struct au8522_state, (*state),
912 hybrid_tuner_instance_list,
913 i2c, client_address, "au8522");
914 mutex_unlock(&au8522_list_mutex);
915
916 return ret;
Devin Heitmueller209fdf62009-03-11 03:00:36 -0300917}
918
919void au8522_release_state(struct au8522_state *state)
920{
Devin Heitmueller4ff5ed42009-03-11 03:00:45 -0300921 mutex_lock(&au8522_list_mutex);
Devin Heitmueller209fdf62009-03-11 03:00:36 -0300922 if (state != NULL)
923 hybrid_tuner_release_state(state);
Devin Heitmueller4ff5ed42009-03-11 03:00:45 -0300924 mutex_unlock(&au8522_list_mutex);
Devin Heitmueller209fdf62009-03-11 03:00:36 -0300925}
926
927
Michael Krufkye059b0f2008-04-02 18:59:48 -0300928static void au8522_release(struct dvb_frontend *fe)
Steven Toth265a6512008-04-18 21:34:00 -0300929{
Michael Krufkye059b0f2008-04-02 18:59:48 -0300930 struct au8522_state *state = fe->demodulator_priv;
Devin Heitmueller209fdf62009-03-11 03:00:36 -0300931 au8522_release_state(state);
Steven Toth265a6512008-04-18 21:34:00 -0300932}
933
Michael Krufkye059b0f2008-04-02 18:59:48 -0300934struct dvb_frontend *au8522_attach(const struct au8522_config *config,
935 struct i2c_adapter *i2c)
Steven Toth265a6512008-04-18 21:34:00 -0300936{
Michael Krufkye059b0f2008-04-02 18:59:48 -0300937 struct au8522_state *state = NULL;
Devin Heitmueller209fdf62009-03-11 03:00:36 -0300938 int instance;
Steven Toth265a6512008-04-18 21:34:00 -0300939
940 /* allocate memory for the internal state */
Devin Heitmueller209fdf62009-03-11 03:00:36 -0300941 instance = au8522_get_state(&state, i2c, config->demod_address);
942 switch (instance) {
943 case 0:
944 dprintk("%s state allocation failed\n", __func__);
945 break;
946 case 1:
947 /* new demod instance */
948 dprintk("%s using new instance\n", __func__);
949 break;
950 default:
951 /* existing demod instance */
952 dprintk("%s using existing instance\n", __func__);
953 break;
954 }
Steven Toth265a6512008-04-18 21:34:00 -0300955
956 /* setup the state */
957 state->config = config;
958 state->i2c = i2c;
Devin Heitmueller7f2c9832010-02-17 22:47:55 -0300959 state->operational_mode = AU8522_DIGITAL_MODE;
960
Steven Toth265a6512008-04-18 21:34:00 -0300961 /* create dvb_frontend */
962 memcpy(&state->frontend.ops, &au8522_ops,
963 sizeof(struct dvb_frontend_ops));
964 state->frontend.demodulator_priv = state;
965
966 if (au8522_init(&state->frontend) != 0) {
967 printk(KERN_ERR "%s: Failed to initialize correctly\n",
Michael Krufkyce1719a2008-04-02 18:59:48 -0300968 __func__);
Steven Toth265a6512008-04-18 21:34:00 -0300969 goto error;
970 }
971
972 /* Note: Leaving the I2C gate open here. */
973 au8522_i2c_gate_ctrl(&state->frontend, 1);
974
975 return &state->frontend;
976
977error:
Devin Heitmueller209fdf62009-03-11 03:00:36 -0300978 au8522_release_state(state);
Steven Toth265a6512008-04-18 21:34:00 -0300979 return NULL;
980}
Mauro Carvalho Chehab18d73c52008-04-18 22:12:52 -0300981EXPORT_SYMBOL(au8522_attach);
Steven Toth265a6512008-04-18 21:34:00 -0300982
983static struct dvb_frontend_ops au8522_ops = {
984
985 .info = {
986 .name = "Auvitek AU8522 QAM/8VSB Frontend",
987 .type = FE_ATSC,
988 .frequency_min = 54000000,
989 .frequency_max = 858000000,
990 .frequency_stepsize = 62500,
991 .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB
992 },
993
994 .init = au8522_init,
Michael Krufky74d50722008-06-13 20:33:23 -0300995 .sleep = au8522_sleep,
Steven Toth265a6512008-04-18 21:34:00 -0300996 .i2c_gate_ctrl = au8522_i2c_gate_ctrl,
997 .set_frontend = au8522_set_frontend,
998 .get_frontend = au8522_get_frontend,
999 .get_tune_settings = au8522_get_tune_settings,
1000 .read_status = au8522_read_status,
1001 .read_ber = au8522_read_ber,
1002 .read_signal_strength = au8522_read_signal_strength,
1003 .read_snr = au8522_read_snr,
1004 .read_ucblocks = au8522_read_ucblocks,
1005 .release = au8522_release,
1006};
1007
1008module_param(debug, int, 0644);
1009MODULE_PARM_DESC(debug, "Enable verbose debug messages");
1010
1011MODULE_DESCRIPTION("Auvitek AU8522 QAM-B/ATSC Demodulator driver");
1012MODULE_AUTHOR("Steven Toth");
1013MODULE_LICENSE("GPL");