V4L/DVB (9149): hvr950q: led feedback based on snr
authorMichael Krufky <mkrufky@linuxtv.org>
Sat, 5 Apr 2008 23:55:14 +0000 (20:55 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Mon, 13 Oct 2008 09:15:57 +0000 (07:15 -0200)
Signed-off-by: Michael Krufky <mkrufky@linuxtv.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
drivers/media/dvb/frontends/au8522.c
drivers/media/dvb/frontends/au8522.h
drivers/media/video/au0828/au0828-dvb.c

index 0b82cc2..eabf9a6 100644 (file)
@@ -40,6 +40,8 @@ struct au8522_state {
        u32 current_frequency;
        fe_modulation_t current_modulation;
 
+       u32 fe_status;
+       unsigned int led_state;
 };
 
 static int debug;
@@ -538,11 +540,98 @@ static int au8522_init(struct dvb_frontend *fe)
        return 0;
 }
 
+static int au8522_led_gpio_enable(struct au8522_state *state, int onoff)
+{
+       struct au8522_led_config *led_config = state->config->led_cfg;
+       u8 val;
+
+       /* bail out if we cant control an LED */
+       if (!led_config || !led_config->gpio_output ||
+           !led_config->gpio_output_enable || !led_config->gpio_output_disable)
+               return 0;
+
+       val = au8522_readreg(state, 0x4000 |
+                            (led_config->gpio_output & ~0xc000));
+       if (onoff) {
+               /* enable GPIO output */
+               val &= ~((led_config->gpio_output_enable >> 8) & 0xff);
+               val |=  (led_config->gpio_output_enable & 0xff);
+       } else {
+               /* disable GPIO output */
+               val &= ~((led_config->gpio_output_disable >> 8) & 0xff);
+               val |=  (led_config->gpio_output_disable & 0xff);
+       }
+       return au8522_writereg(state, 0x8000 |
+                              (led_config->gpio_output & ~0xc000), val);
+}
+
+/* led = 0 | off
+ * led = 1 | signal ok
+ * led = 2 | signal strong
+ * led < 0 | only light led if leds are currently off
+ */
+static int au8522_led_ctrl(struct au8522_state *state, int led)
+{
+       struct au8522_led_config *led_config = state->config->led_cfg;
+       int i, ret = 0;
+
+       /* bail out if we cant control an LED */
+       if (!led_config || !led_config->gpio_leds ||
+           !led_config->num_led_states || !led_config->led_states)
+               return 0;
+
+       if (led < 0) {
+               /* if LED is already lit, then leave it as-is */
+               if (state->led_state)
+                       return 0;
+               else
+                       led *= -1;
+       }
+
+       /* toggle LED if changing state */
+       if (state->led_state != led) {
+               u8 val;
+
+               dprintk("%s: %d\n", __func__, led);
+
+               au8522_led_gpio_enable(state, 1);
+
+               val = au8522_readreg(state, 0x4000 |
+                                    (led_config->gpio_leds & ~0xc000));
+
+               /* start with all leds off */
+               for (i = 0; i < led_config->num_led_states; i++)
+                       val &= ~led_config->led_states[i];
+
+               /* set selected LED state */
+               if (led < led_config->num_led_states)
+                       val |= led_config->led_states[led];
+               else if (led_config->num_led_states)
+                       val |=
+                       led_config->led_states[led_config->num_led_states - 1];
+
+               ret = au8522_writereg(state, 0x8000 |
+                                     (led_config->gpio_leds & ~0xc000), val);
+               if (ret < 0)
+                       return ret;
+
+               state->led_state = led;
+
+               if (led == 0)
+                       au8522_led_gpio_enable(state, 0);
+       }
+
+       return 0;
+}
+
 static int au8522_sleep(struct dvb_frontend *fe)
 {
        struct au8522_state *state = fe->demodulator_priv;
        dprintk("%s()\n", __func__);
 
+       /* turn off led */
+       au8522_led_ctrl(state, 0);
+
        state->current_frequency = 0;
 
        return 0;
@@ -592,12 +681,53 @@ static int au8522_read_status(struct dvb_frontend *fe, fe_status_t *status)
                        *status |= FE_HAS_CARRIER | FE_HAS_SIGNAL;
                break;
        }
+       state->fe_status = *status;
+
+       if (*status & FE_HAS_LOCK)
+               /* turn on LED, if it isn't on already */
+               au8522_led_ctrl(state, -1);
+       else
+               /* turn off LED */
+               au8522_led_ctrl(state, 0);
 
        dprintk("%s() status 0x%08x\n", __func__, *status);
 
        return 0;
 }
 
+static int au8522_led_status(struct au8522_state *state, const u16 *snr)
+{
+       struct au8522_led_config *led_config = state->config->led_cfg;
+       int led;
+       u16 strong;
+
+       /* bail out if we cant control an LED */
+       if (!led_config)
+               return 0;
+
+       if (0 == (state->fe_status & FE_HAS_LOCK))
+               return au8522_led_ctrl(state, 0);
+       else if (state->current_modulation == QAM_256)
+               strong = led_config->qam256_strong;
+       else if (state->current_modulation == QAM_64)
+               strong = led_config->qam64_strong;
+       else /* (state->current_modulation == VSB_8) */
+               strong = led_config->vsb8_strong;
+
+       if (*snr >= strong)
+               led = 2;
+       else
+               led = 1;
+
+       if ((state->led_state) &&
+           (((strong < *snr) ? (*snr - strong) : (strong - *snr)) <= 10))
+               /* snr didn't change enough to bother
+                * changing the color of the led */
+               return 0;
+
+       return au8522_led_ctrl(state, led);
+}
+
 static int au8522_read_snr(struct dvb_frontend *fe, u16 *snr)
 {
        struct au8522_state *state = fe->demodulator_priv;
@@ -621,6 +751,9 @@ static int au8522_read_snr(struct dvb_frontend *fe, u16 *snr)
                                            au8522_readreg(state, 0x4311),
                                            snr);
 
+       if (state->config->led_cfg)
+               au8522_led_status(state, snr);
+
        return ret;
 }
 
index 595915a..7b94f55 100644 (file)
@@ -30,6 +30,21 @@ enum au8522_if_freq {
        AU8522_IF_3_25MHZ,
 };
 
+struct au8522_led_config {
+       u16 vsb8_strong;
+       u16 qam64_strong;
+       u16 qam256_strong;
+
+       u16 gpio_output;
+       /* unset hi bits, set low bits */
+       u16 gpio_output_enable;
+       u16 gpio_output_disable;
+
+       u16 gpio_leds;
+       u8 *led_states;
+       unsigned int num_led_states;
+};
+
 struct au8522_config {
        /* the demodulator's i2c address */
        u8 demod_address;
@@ -39,6 +54,8 @@ struct au8522_config {
 #define AU8522_DEMODLOCKING 1
        u8 status_mode;
 
+       struct au8522_led_config *led_cfg;
+
        enum au8522_if_freq vsb_if;
        enum au8522_if_freq qam_if;
 };
index a52abce..f0fcdb4 100644 (file)
@@ -36,11 +36,39 @@ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
 #define _AU0828_BULKPIPE 0x83
 #define _BULKPIPESIZE 0xe522
 
+static u8 hauppauge_hvr950q_led_states[] = {
+       0x00, /* off */
+       0x02, /* yellow */
+       0x04, /* green */
+};
+
+static struct au8522_led_config hauppauge_hvr950q_led_cfg = {
+       .gpio_output = 0x00e0,
+       .gpio_output_enable  = 0x6006,
+       .gpio_output_disable = 0x0660,
+
+       .gpio_leds = 0x00e2,
+       .led_states  = hauppauge_hvr950q_led_states,
+       .num_led_states = sizeof(hauppauge_hvr950q_led_states),
+
+       .vsb8_strong   = 20 /* dB */ * 10,
+       .qam64_strong  = 25 /* dB */ * 10,
+       .qam256_strong = 32 /* dB */ * 10,
+};
+
 static struct au8522_config hauppauge_hvr950q_config = {
        .demod_address = 0x8e >> 1,
        .status_mode   = AU8522_DEMODLOCKING,
        .qam_if        = AU8522_IF_6MHZ,
        .vsb_if        = AU8522_IF_6MHZ,
+       .led_cfg       = &hauppauge_hvr950q_led_cfg,
+};
+
+static struct au8522_config fusionhdtv7usb_config = {
+       .demod_address = 0x8e >> 1,
+       .status_mode   = AU8522_DEMODLOCKING,
+       .qam_if        = AU8522_IF_6MHZ,
+       .vsb_if        = AU8522_IF_6MHZ,
 };
 
 static struct au8522_config hauppauge_woodbury_config = {
@@ -352,7 +380,6 @@ int au0828_dvb_register(struct au0828_dev *dev)
        switch (dev->board) {
        case AU0828_BOARD_HAUPPAUGE_HVR850:
        case AU0828_BOARD_HAUPPAUGE_HVR950Q:
-       case AU0828_BOARD_DVICO_FUSIONHDTV7:
                dvb->frontend = dvb_attach(au8522_attach,
                                &hauppauge_hvr950q_config,
                                &dev->i2c_adap);
@@ -378,6 +405,16 @@ int au0828_dvb_register(struct au0828_dev *dev)
                                   0x60, &dev->i2c_adap,
                                   &hauppauge_woodbury_tunerconfig);
                break;
+       case AU0828_BOARD_DVICO_FUSIONHDTV7:
+               dvb->frontend = dvb_attach(au8522_attach,
+                               &fusionhdtv7usb_config,
+                               &dev->i2c_adap);
+               if (dvb->frontend != NULL) {
+                       dvb_attach(xc5000_attach, dvb->frontend,
+                               &dev->i2c_adap,
+                               &hauppauge_hvr950q_tunerconfig);
+               }
+               break;
        default:
                printk(KERN_WARNING "The frontend of your DVB/ATSC card "
                       "isn't supported yet\n");