diff options
author | Steven Toth <stoth@hauppauge.com> | 2007-10-24 20:05:51 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@infradead.org> | 2007-11-04 18:41:19 -0500 |
commit | dd7d5013cdad2efe7ddbb3f77728cfe0ce295e5b (patch) | |
tree | 6fc4fe932e68f8066f3c6ff757658607d990aaed /drivers/media/dvb/frontends/s5h1409.c | |
parent | bb8d56a4d8cad90825db0c12b55d66fde91dfa44 (diff) |
V4L/DVB (6402): s5h1409: Fix broken QAM support
This patch enables QAM Annex-B support (US digital cable)
for the s5h1409 VSB/QAM demodulator.
Tested successfully with the mt2131 tuner,
present on the following supported boards:
Hauppauge WinTV-HVR-1250
Hauppauge WinTV-HVR-1800
Hauppauge WinTV-HVR-1800lp
This patch is also known to work with an upcoming XC5000 tuner driver.
Signed-off-by: Steven Toth <stoth@hauppauge.com>
Signed-off-by: Michael Krufky <mkrufky@linuxtv.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
Diffstat (limited to 'drivers/media/dvb/frontends/s5h1409.c')
-rw-r--r-- | drivers/media/dvb/frontends/s5h1409.c | 96 |
1 files changed, 86 insertions, 10 deletions
diff --git a/drivers/media/dvb/frontends/s5h1409.c b/drivers/media/dvb/frontends/s5h1409.c index 30e8a705fad4..8dee7ec9456a 100644 --- a/drivers/media/dvb/frontends/s5h1409.c +++ b/drivers/media/dvb/frontends/s5h1409.c | |||
@@ -42,6 +42,9 @@ struct s5h1409_state { | |||
42 | fe_modulation_t current_modulation; | 42 | fe_modulation_t current_modulation; |
43 | 43 | ||
44 | u32 current_frequency; | 44 | u32 current_frequency; |
45 | |||
46 | u32 is_qam_locked; | ||
47 | u32 qam_state; | ||
45 | }; | 48 | }; |
46 | 49 | ||
47 | static int debug = 0; | 50 | static int debug = 0; |
@@ -94,6 +97,7 @@ static struct init_tab { | |||
94 | { 0xac, 0x1003, }, | 97 | { 0xac, 0x1003, }, |
95 | { 0xad, 0x103f, }, | 98 | { 0xad, 0x103f, }, |
96 | { 0xe2, 0x0100, }, | 99 | { 0xe2, 0x0100, }, |
100 | { 0xe3, 0x0000, }, | ||
97 | { 0x28, 0x1010, }, | 101 | { 0x28, 0x1010, }, |
98 | { 0xb1, 0x000e, }, | 102 | { 0xb1, 0x000e, }, |
99 | }; | 103 | }; |
@@ -335,6 +339,8 @@ static int s5h1409_softreset(struct dvb_frontend* fe) | |||
335 | 339 | ||
336 | s5h1409_writereg(state, 0xf5, 0); | 340 | s5h1409_writereg(state, 0xf5, 0); |
337 | s5h1409_writereg(state, 0xf5, 1); | 341 | s5h1409_writereg(state, 0xf5, 1); |
342 | state->is_qam_locked = 0; | ||
343 | state->qam_state = 0; | ||
338 | return 0; | 344 | return 0; |
339 | } | 345 | } |
340 | 346 | ||
@@ -349,6 +355,11 @@ static int s5h1409_set_if_freq(struct dvb_frontend* fe, int KHz) | |||
349 | s5h1409_writereg(state, 0x87, 0x01be); | 355 | s5h1409_writereg(state, 0x87, 0x01be); |
350 | s5h1409_writereg(state, 0x88, 0x0436); | 356 | s5h1409_writereg(state, 0x88, 0x0436); |
351 | s5h1409_writereg(state, 0x89, 0x054d); | 357 | s5h1409_writereg(state, 0x89, 0x054d); |
358 | } else | ||
359 | if (KHz == 4000) { | ||
360 | s5h1409_writereg(state, 0x87, 0x014b); | ||
361 | s5h1409_writereg(state, 0x88, 0x0cb5); | ||
362 | s5h1409_writereg(state, 0x89, 0x03e2); | ||
352 | } else { | 363 | } else { |
353 | printk("%s() Invalid arg = %d KHz\n", __FUNCTION__, KHz); | 364 | printk("%s() Invalid arg = %d KHz\n", __FUNCTION__, KHz); |
354 | ret = -1; | 365 | ret = -1; |
@@ -361,7 +372,7 @@ static int s5h1409_set_spectralinversion(struct dvb_frontend* fe, int inverted) | |||
361 | { | 372 | { |
362 | struct s5h1409_state* state = fe->demodulator_priv; | 373 | struct s5h1409_state* state = fe->demodulator_priv; |
363 | 374 | ||
364 | dprintk("%s()\n", __FUNCTION__); | 375 | dprintk("%s(%d)\n", __FUNCTION__, inverted); |
365 | 376 | ||
366 | if(inverted == 1) | 377 | if(inverted == 1) |
367 | return s5h1409_writereg(state, 0x1b, 0x1101); /* Inverted */ | 378 | return s5h1409_writereg(state, 0x1b, 0x1101); /* Inverted */ |
@@ -382,14 +393,10 @@ static int s5h1409_enable_modulation(struct dvb_frontend* fe, | |||
382 | s5h1409_writereg(state, 0xf4, 0); | 393 | s5h1409_writereg(state, 0xf4, 0); |
383 | break; | 394 | break; |
384 | case QAM_64: | 395 | case QAM_64: |
385 | dprintk("%s() QAM_64\n", __FUNCTION__); | ||
386 | s5h1409_writereg(state, 0xf4, 1); | ||
387 | s5h1409_writereg(state, 0x85, 0x100); | ||
388 | break; | ||
389 | case QAM_256: | 396 | case QAM_256: |
390 | dprintk("%s() QAM_256\n", __FUNCTION__); | 397 | dprintk("%s() QAM_AUTO (64/256)\n", __FUNCTION__); |
391 | s5h1409_writereg(state, 0xf4, 1); | 398 | s5h1409_writereg(state, 0xf4, 1); |
392 | s5h1409_writereg(state, 0x85, 0x101); | 399 | s5h1409_writereg(state, 0x85, 0x110); |
393 | break; | 400 | break; |
394 | default: | 401 | default: |
395 | dprintk("%s() Invalid modulation\n", __FUNCTION__); | 402 | dprintk("%s() Invalid modulation\n", __FUNCTION__); |
@@ -423,7 +430,7 @@ static int s5h1409_set_gpio(struct dvb_frontend* fe, int enable) | |||
423 | if (enable) | 430 | if (enable) |
424 | return s5h1409_writereg(state, 0xe3, 0x1100); | 431 | return s5h1409_writereg(state, 0xe3, 0x1100); |
425 | else | 432 | else |
426 | return s5h1409_writereg(state, 0xe3, 0); | 433 | return s5h1409_writereg(state, 0xe3, 0x1000); |
427 | } | 434 | } |
428 | 435 | ||
429 | static int s5h1409_sleep(struct dvb_frontend* fe, int enable) | 436 | static int s5h1409_sleep(struct dvb_frontend* fe, int enable) |
@@ -444,6 +451,66 @@ static int s5h1409_register_reset(struct dvb_frontend* fe) | |||
444 | return s5h1409_writereg(state, 0xfa, 0); | 451 | return s5h1409_writereg(state, 0xfa, 0); |
445 | } | 452 | } |
446 | 453 | ||
454 | static void s5h1409_set_qam_amhum_mode(struct dvb_frontend *fe) | ||
455 | { | ||
456 | struct s5h1409_state *state = fe->demodulator_priv; | ||
457 | u16 reg; | ||
458 | |||
459 | if (state->is_qam_locked) | ||
460 | return; | ||
461 | |||
462 | /* QAM EQ lock check */ | ||
463 | reg = s5h1409_readreg(state, 0xf0); | ||
464 | |||
465 | if ((reg >> 13) & 0x1) { | ||
466 | |||
467 | state->is_qam_locked = 1; | ||
468 | reg &= 0xff; | ||
469 | |||
470 | s5h1409_writereg(state, 0x96, 0x00c); | ||
471 | if ((reg < 0x38) || (reg > 0x68) ) { | ||
472 | s5h1409_writereg(state, 0x93, 0x3332); | ||
473 | s5h1409_writereg(state, 0x9e, 0x2c37); | ||
474 | } else { | ||
475 | s5h1409_writereg(state, 0x93, 0x3130); | ||
476 | s5h1409_writereg(state, 0x9e, 0x2836); | ||
477 | } | ||
478 | |||
479 | } else { | ||
480 | s5h1409_writereg(state, 0x96, 0x0008); | ||
481 | s5h1409_writereg(state, 0x93, 0x3332); | ||
482 | s5h1409_writereg(state, 0x9e, 0x2c37); | ||
483 | } | ||
484 | } | ||
485 | |||
486 | static void s5h1409_set_qam_interleave_mode(struct dvb_frontend *fe) | ||
487 | { | ||
488 | struct s5h1409_state *state = fe->demodulator_priv; | ||
489 | u16 reg, reg1, reg2; | ||
490 | |||
491 | reg = s5h1409_readreg(state, 0xf1); | ||
492 | |||
493 | /* Master lock */ | ||
494 | if ((reg >> 15) & 0x1) { | ||
495 | if (state->qam_state != 2) { | ||
496 | state->qam_state = 2; | ||
497 | reg1 = s5h1409_readreg(state, 0xb2); | ||
498 | reg2 = s5h1409_readreg(state, 0xad); | ||
499 | |||
500 | s5h1409_writereg(state, 0x96, 0x20); | ||
501 | s5h1409_writereg(state, 0xad, | ||
502 | ( ((reg1 & 0xf000) >> 4) | (reg2 & 0xf0ff)) ); | ||
503 | s5h1409_writereg(state, 0xab, 0x1100); | ||
504 | } | ||
505 | } else { | ||
506 | if (state->qam_state != 1) { | ||
507 | state->qam_state = 1; | ||
508 | s5h1409_writereg(state, 0x96, 0x08); | ||
509 | s5h1409_writereg(state, 0xab, 0x1101); | ||
510 | } | ||
511 | } | ||
512 | } | ||
513 | |||
447 | /* Talk to the demod, set the FEC, GUARD, QAM settings etc */ | 514 | /* Talk to the demod, set the FEC, GUARD, QAM settings etc */ |
448 | static int s5h1409_set_frontend (struct dvb_frontend* fe, | 515 | static int s5h1409_set_frontend (struct dvb_frontend* fe, |
449 | struct dvb_frontend_parameters *p) | 516 | struct dvb_frontend_parameters *p) |
@@ -458,12 +525,21 @@ static int s5h1409_set_frontend (struct dvb_frontend* fe, | |||
458 | 525 | ||
459 | s5h1409_enable_modulation(fe, p->u.vsb.modulation); | 526 | s5h1409_enable_modulation(fe, p->u.vsb.modulation); |
460 | 527 | ||
528 | /* Allow the demod to settle */ | ||
529 | msleep(100); | ||
530 | |||
461 | if (fe->ops.tuner_ops.set_params) { | 531 | if (fe->ops.tuner_ops.set_params) { |
462 | if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); | 532 | if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); |
463 | fe->ops.tuner_ops.set_params(fe, p); | 533 | fe->ops.tuner_ops.set_params(fe, p); |
464 | if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); | 534 | if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); |
465 | } | 535 | } |
466 | 536 | ||
537 | /* Optimize the demod for QAM */ | ||
538 | if (p->u.vsb.modulation != VSB_8) { | ||
539 | s5h1409_set_qam_amhum_mode(fe); | ||
540 | s5h1409_set_qam_interleave_mode(fe); | ||
541 | } | ||
542 | |||
467 | return 0; | 543 | return 0; |
468 | } | 544 | } |
469 | 545 | ||
@@ -495,8 +571,8 @@ static int s5h1409_init (struct dvb_frontend* fe) | |||
495 | s5h1409_set_gpio(fe, state->config->gpio); | 571 | s5h1409_set_gpio(fe, state->config->gpio); |
496 | s5h1409_softreset(fe); | 572 | s5h1409_softreset(fe); |
497 | 573 | ||
498 | /* Note: Leaving the I2C gate open here. */ | 574 | /* Note: Leaving the I2C gate closed. */ |
499 | s5h1409_i2c_gate_ctrl(fe, 1); | 575 | s5h1409_i2c_gate_ctrl(fe, 0); |
500 | 576 | ||
501 | return 0; | 577 | return 0; |
502 | } | 578 | } |