diff options
-rw-r--r-- | drivers/media/dvb/frontends/au8522.c | 133 | ||||
-rw-r--r-- | drivers/media/dvb/frontends/au8522.h | 17 | ||||
-rw-r--r-- | drivers/media/video/au0828/au0828-dvb.c | 39 |
3 files changed, 188 insertions, 1 deletions
diff --git a/drivers/media/dvb/frontends/au8522.c b/drivers/media/dvb/frontends/au8522.c index 0b82cc2a1e16..eabf9a68e7ec 100644 --- a/drivers/media/dvb/frontends/au8522.c +++ b/drivers/media/dvb/frontends/au8522.c | |||
@@ -40,6 +40,8 @@ struct au8522_state { | |||
40 | u32 current_frequency; | 40 | u32 current_frequency; |
41 | fe_modulation_t current_modulation; | 41 | fe_modulation_t current_modulation; |
42 | 42 | ||
43 | u32 fe_status; | ||
44 | unsigned int led_state; | ||
43 | }; | 45 | }; |
44 | 46 | ||
45 | static int debug; | 47 | static int debug; |
@@ -538,11 +540,98 @@ static int au8522_init(struct dvb_frontend *fe) | |||
538 | return 0; | 540 | return 0; |
539 | } | 541 | } |
540 | 542 | ||
543 | static int au8522_led_gpio_enable(struct au8522_state *state, int onoff) | ||
544 | { | ||
545 | struct au8522_led_config *led_config = state->config->led_cfg; | ||
546 | u8 val; | ||
547 | |||
548 | /* bail out if we cant control an LED */ | ||
549 | if (!led_config || !led_config->gpio_output || | ||
550 | !led_config->gpio_output_enable || !led_config->gpio_output_disable) | ||
551 | return 0; | ||
552 | |||
553 | val = au8522_readreg(state, 0x4000 | | ||
554 | (led_config->gpio_output & ~0xc000)); | ||
555 | if (onoff) { | ||
556 | /* enable GPIO output */ | ||
557 | val &= ~((led_config->gpio_output_enable >> 8) & 0xff); | ||
558 | val |= (led_config->gpio_output_enable & 0xff); | ||
559 | } else { | ||
560 | /* disable GPIO output */ | ||
561 | val &= ~((led_config->gpio_output_disable >> 8) & 0xff); | ||
562 | val |= (led_config->gpio_output_disable & 0xff); | ||
563 | } | ||
564 | return au8522_writereg(state, 0x8000 | | ||
565 | (led_config->gpio_output & ~0xc000), val); | ||
566 | } | ||
567 | |||
568 | /* led = 0 | off | ||
569 | * led = 1 | signal ok | ||
570 | * led = 2 | signal strong | ||
571 | * led < 0 | only light led if leds are currently off | ||
572 | */ | ||
573 | static int au8522_led_ctrl(struct au8522_state *state, int led) | ||
574 | { | ||
575 | struct au8522_led_config *led_config = state->config->led_cfg; | ||
576 | int i, ret = 0; | ||
577 | |||
578 | /* bail out if we cant control an LED */ | ||
579 | if (!led_config || !led_config->gpio_leds || | ||
580 | !led_config->num_led_states || !led_config->led_states) | ||
581 | return 0; | ||
582 | |||
583 | if (led < 0) { | ||
584 | /* if LED is already lit, then leave it as-is */ | ||
585 | if (state->led_state) | ||
586 | return 0; | ||
587 | else | ||
588 | led *= -1; | ||
589 | } | ||
590 | |||
591 | /* toggle LED if changing state */ | ||
592 | if (state->led_state != led) { | ||
593 | u8 val; | ||
594 | |||
595 | dprintk("%s: %d\n", __func__, led); | ||
596 | |||
597 | au8522_led_gpio_enable(state, 1); | ||
598 | |||
599 | val = au8522_readreg(state, 0x4000 | | ||
600 | (led_config->gpio_leds & ~0xc000)); | ||
601 | |||
602 | /* start with all leds off */ | ||
603 | for (i = 0; i < led_config->num_led_states; i++) | ||
604 | val &= ~led_config->led_states[i]; | ||
605 | |||
606 | /* set selected LED state */ | ||
607 | if (led < led_config->num_led_states) | ||
608 | val |= led_config->led_states[led]; | ||
609 | else if (led_config->num_led_states) | ||
610 | val |= | ||
611 | led_config->led_states[led_config->num_led_states - 1]; | ||
612 | |||
613 | ret = au8522_writereg(state, 0x8000 | | ||
614 | (led_config->gpio_leds & ~0xc000), val); | ||
615 | if (ret < 0) | ||
616 | return ret; | ||
617 | |||
618 | state->led_state = led; | ||
619 | |||
620 | if (led == 0) | ||
621 | au8522_led_gpio_enable(state, 0); | ||
622 | } | ||
623 | |||
624 | return 0; | ||
625 | } | ||
626 | |||
541 | static int au8522_sleep(struct dvb_frontend *fe) | 627 | static int au8522_sleep(struct dvb_frontend *fe) |
542 | { | 628 | { |
543 | struct au8522_state *state = fe->demodulator_priv; | 629 | struct au8522_state *state = fe->demodulator_priv; |
544 | dprintk("%s()\n", __func__); | 630 | dprintk("%s()\n", __func__); |
545 | 631 | ||
632 | /* turn off led */ | ||
633 | au8522_led_ctrl(state, 0); | ||
634 | |||
546 | state->current_frequency = 0; | 635 | state->current_frequency = 0; |
547 | 636 | ||
548 | return 0; | 637 | return 0; |
@@ -592,12 +681,53 @@ static int au8522_read_status(struct dvb_frontend *fe, fe_status_t *status) | |||
592 | *status |= FE_HAS_CARRIER | FE_HAS_SIGNAL; | 681 | *status |= FE_HAS_CARRIER | FE_HAS_SIGNAL; |
593 | break; | 682 | break; |
594 | } | 683 | } |
684 | state->fe_status = *status; | ||
685 | |||
686 | if (*status & FE_HAS_LOCK) | ||
687 | /* turn on LED, if it isn't on already */ | ||
688 | au8522_led_ctrl(state, -1); | ||
689 | else | ||
690 | /* turn off LED */ | ||
691 | au8522_led_ctrl(state, 0); | ||
595 | 692 | ||
596 | dprintk("%s() status 0x%08x\n", __func__, *status); | 693 | dprintk("%s() status 0x%08x\n", __func__, *status); |
597 | 694 | ||
598 | return 0; | 695 | return 0; |
599 | } | 696 | } |
600 | 697 | ||
698 | static int au8522_led_status(struct au8522_state *state, const u16 *snr) | ||
699 | { | ||
700 | struct au8522_led_config *led_config = state->config->led_cfg; | ||
701 | int led; | ||
702 | u16 strong; | ||
703 | |||
704 | /* bail out if we cant control an LED */ | ||
705 | if (!led_config) | ||
706 | return 0; | ||
707 | |||
708 | if (0 == (state->fe_status & FE_HAS_LOCK)) | ||
709 | return au8522_led_ctrl(state, 0); | ||
710 | else if (state->current_modulation == QAM_256) | ||
711 | strong = led_config->qam256_strong; | ||
712 | else if (state->current_modulation == QAM_64) | ||
713 | strong = led_config->qam64_strong; | ||
714 | else /* (state->current_modulation == VSB_8) */ | ||
715 | strong = led_config->vsb8_strong; | ||
716 | |||
717 | if (*snr >= strong) | ||
718 | led = 2; | ||
719 | else | ||
720 | led = 1; | ||
721 | |||
722 | if ((state->led_state) && | ||
723 | (((strong < *snr) ? (*snr - strong) : (strong - *snr)) <= 10)) | ||
724 | /* snr didn't change enough to bother | ||
725 | * changing the color of the led */ | ||
726 | return 0; | ||
727 | |||
728 | return au8522_led_ctrl(state, led); | ||
729 | } | ||
730 | |||
601 | static int au8522_read_snr(struct dvb_frontend *fe, u16 *snr) | 731 | static int au8522_read_snr(struct dvb_frontend *fe, u16 *snr) |
602 | { | 732 | { |
603 | struct au8522_state *state = fe->demodulator_priv; | 733 | struct au8522_state *state = fe->demodulator_priv; |
@@ -621,6 +751,9 @@ static int au8522_read_snr(struct dvb_frontend *fe, u16 *snr) | |||
621 | au8522_readreg(state, 0x4311), | 751 | au8522_readreg(state, 0x4311), |
622 | snr); | 752 | snr); |
623 | 753 | ||
754 | if (state->config->led_cfg) | ||
755 | au8522_led_status(state, snr); | ||
756 | |||
624 | return ret; | 757 | return ret; |
625 | } | 758 | } |
626 | 759 | ||
diff --git a/drivers/media/dvb/frontends/au8522.h b/drivers/media/dvb/frontends/au8522.h index 595915ade8c3..7b94f554a093 100644 --- a/drivers/media/dvb/frontends/au8522.h +++ b/drivers/media/dvb/frontends/au8522.h | |||
@@ -30,6 +30,21 @@ enum au8522_if_freq { | |||
30 | AU8522_IF_3_25MHZ, | 30 | AU8522_IF_3_25MHZ, |
31 | }; | 31 | }; |
32 | 32 | ||
33 | struct au8522_led_config { | ||
34 | u16 vsb8_strong; | ||
35 | u16 qam64_strong; | ||
36 | u16 qam256_strong; | ||
37 | |||
38 | u16 gpio_output; | ||
39 | /* unset hi bits, set low bits */ | ||
40 | u16 gpio_output_enable; | ||
41 | u16 gpio_output_disable; | ||
42 | |||
43 | u16 gpio_leds; | ||
44 | u8 *led_states; | ||
45 | unsigned int num_led_states; | ||
46 | }; | ||
47 | |||
33 | struct au8522_config { | 48 | struct au8522_config { |
34 | /* the demodulator's i2c address */ | 49 | /* the demodulator's i2c address */ |
35 | u8 demod_address; | 50 | u8 demod_address; |
@@ -39,6 +54,8 @@ struct au8522_config { | |||
39 | #define AU8522_DEMODLOCKING 1 | 54 | #define AU8522_DEMODLOCKING 1 |
40 | u8 status_mode; | 55 | u8 status_mode; |
41 | 56 | ||
57 | struct au8522_led_config *led_cfg; | ||
58 | |||
42 | enum au8522_if_freq vsb_if; | 59 | enum au8522_if_freq vsb_if; |
43 | enum au8522_if_freq qam_if; | 60 | enum au8522_if_freq qam_if; |
44 | }; | 61 | }; |
diff --git a/drivers/media/video/au0828/au0828-dvb.c b/drivers/media/video/au0828/au0828-dvb.c index a52abce16e1a..f0fcdb4769d7 100644 --- a/drivers/media/video/au0828/au0828-dvb.c +++ b/drivers/media/video/au0828/au0828-dvb.c | |||
@@ -36,11 +36,39 @@ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); | |||
36 | #define _AU0828_BULKPIPE 0x83 | 36 | #define _AU0828_BULKPIPE 0x83 |
37 | #define _BULKPIPESIZE 0xe522 | 37 | #define _BULKPIPESIZE 0xe522 |
38 | 38 | ||
39 | static u8 hauppauge_hvr950q_led_states[] = { | ||
40 | 0x00, /* off */ | ||
41 | 0x02, /* yellow */ | ||
42 | 0x04, /* green */ | ||
43 | }; | ||
44 | |||
45 | static struct au8522_led_config hauppauge_hvr950q_led_cfg = { | ||
46 | .gpio_output = 0x00e0, | ||
47 | .gpio_output_enable = 0x6006, | ||
48 | .gpio_output_disable = 0x0660, | ||
49 | |||
50 | .gpio_leds = 0x00e2, | ||
51 | .led_states = hauppauge_hvr950q_led_states, | ||
52 | .num_led_states = sizeof(hauppauge_hvr950q_led_states), | ||
53 | |||
54 | .vsb8_strong = 20 /* dB */ * 10, | ||
55 | .qam64_strong = 25 /* dB */ * 10, | ||
56 | .qam256_strong = 32 /* dB */ * 10, | ||
57 | }; | ||
58 | |||
39 | static struct au8522_config hauppauge_hvr950q_config = { | 59 | static struct au8522_config hauppauge_hvr950q_config = { |
40 | .demod_address = 0x8e >> 1, | 60 | .demod_address = 0x8e >> 1, |
41 | .status_mode = AU8522_DEMODLOCKING, | 61 | .status_mode = AU8522_DEMODLOCKING, |
42 | .qam_if = AU8522_IF_6MHZ, | 62 | .qam_if = AU8522_IF_6MHZ, |
43 | .vsb_if = AU8522_IF_6MHZ, | 63 | .vsb_if = AU8522_IF_6MHZ, |
64 | .led_cfg = &hauppauge_hvr950q_led_cfg, | ||
65 | }; | ||
66 | |||
67 | static struct au8522_config fusionhdtv7usb_config = { | ||
68 | .demod_address = 0x8e >> 1, | ||
69 | .status_mode = AU8522_DEMODLOCKING, | ||
70 | .qam_if = AU8522_IF_6MHZ, | ||
71 | .vsb_if = AU8522_IF_6MHZ, | ||
44 | }; | 72 | }; |
45 | 73 | ||
46 | static struct au8522_config hauppauge_woodbury_config = { | 74 | static struct au8522_config hauppauge_woodbury_config = { |
@@ -352,7 +380,6 @@ int au0828_dvb_register(struct au0828_dev *dev) | |||
352 | switch (dev->board) { | 380 | switch (dev->board) { |
353 | case AU0828_BOARD_HAUPPAUGE_HVR850: | 381 | case AU0828_BOARD_HAUPPAUGE_HVR850: |
354 | case AU0828_BOARD_HAUPPAUGE_HVR950Q: | 382 | case AU0828_BOARD_HAUPPAUGE_HVR950Q: |
355 | case AU0828_BOARD_DVICO_FUSIONHDTV7: | ||
356 | dvb->frontend = dvb_attach(au8522_attach, | 383 | dvb->frontend = dvb_attach(au8522_attach, |
357 | &hauppauge_hvr950q_config, | 384 | &hauppauge_hvr950q_config, |
358 | &dev->i2c_adap); | 385 | &dev->i2c_adap); |
@@ -378,6 +405,16 @@ int au0828_dvb_register(struct au0828_dev *dev) | |||
378 | 0x60, &dev->i2c_adap, | 405 | 0x60, &dev->i2c_adap, |
379 | &hauppauge_woodbury_tunerconfig); | 406 | &hauppauge_woodbury_tunerconfig); |
380 | break; | 407 | break; |
408 | case AU0828_BOARD_DVICO_FUSIONHDTV7: | ||
409 | dvb->frontend = dvb_attach(au8522_attach, | ||
410 | &fusionhdtv7usb_config, | ||
411 | &dev->i2c_adap); | ||
412 | if (dvb->frontend != NULL) { | ||
413 | dvb_attach(xc5000_attach, dvb->frontend, | ||
414 | &dev->i2c_adap, | ||
415 | &hauppauge_hvr950q_tunerconfig); | ||
416 | } | ||
417 | break; | ||
381 | default: | 418 | default: |
382 | printk(KERN_WARNING "The frontend of your DVB/ATSC card " | 419 | printk(KERN_WARNING "The frontend of your DVB/ATSC card " |
383 | "isn't supported yet\n"); | 420 | "isn't supported yet\n"); |