[ALSA] virtuoso: monitor external power on D2X
authorClemens Ladisch <clemens@ladisch.de>
Mon, 28 Jan 2008 07:36:55 +0000 (08:36 +0100)
committerJaroslav Kysela <perex@perex.cz>
Thu, 31 Jan 2008 16:30:18 +0000 (17:30 +0100)
On the Xonar D2X, monitor the GPIO pin that indicates whether external
power is present.

Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
sound/pci/oxygen/oxygen.h
sound/pci/oxygen/oxygen_lib.c
sound/pci/oxygen/virtuoso.c

index e71c534989951da5998217d82fecd26f2a860074..ad50fb8b206b5a356c082682db51e8e13be25640 100644 (file)
@@ -66,6 +66,7 @@ struct oxygen {
        struct snd_pcm_substream *streams[PCM_COUNT];
        struct snd_kcontrol *controls[CONTROL_COUNT];
        struct work_struct spdif_input_bits_work;
+       struct work_struct gpio_work;
        wait_queue_head_t ac97_waitqueue;
 };
 
@@ -88,6 +89,7 @@ struct oxygen_model {
        void (*update_dac_mute)(struct oxygen *chip);
        void (*ac97_switch_hook)(struct oxygen *chip, unsigned int codec,
                                 unsigned int reg, int mute);
+       void (*gpio_changed)(struct oxygen *chip);
        size_t model_data_size;
        u8 dac_channels;
        u8 used_channels;
index 540e56b757944c408dff5903da49bc25084228d2..6eb36dd11476f7c9e5b8715dfba93273faa3b650 100644 (file)
@@ -85,7 +85,7 @@ static irqreturn_t oxygen_interrupt(int dummy, void *dev_id)
        }
 
        if (status & OXYGEN_INT_GPIO)
-               ;
+               schedule_work(&chip->gpio_work);
 
        if ((status & OXYGEN_INT_MIDI) && chip->midi)
                snd_mpu401_uart_interrupt(0, chip->midi->private_data);
@@ -157,6 +157,14 @@ static void oxygen_spdif_input_bits_changed(struct work_struct *work)
        }
 }
 
+static void oxygen_gpio_changed(struct work_struct *work)
+{
+       struct oxygen *chip = container_of(work, struct oxygen, gpio_work);
+
+       if (chip->model->gpio_changed)
+               chip->model->gpio_changed(chip);
+}
+
 #ifdef CONFIG_PROC_FS
 static void oxygen_proc_read(struct snd_info_entry *entry,
                             struct snd_info_buffer *buffer)
@@ -413,6 +421,7 @@ int __devinit oxygen_pci_probe(struct pci_dev *pci, int index, char *id,
        mutex_init(&chip->mutex);
        INIT_WORK(&chip->spdif_input_bits_work,
                  oxygen_spdif_input_bits_changed);
+       INIT_WORK(&chip->gpio_work, oxygen_gpio_changed);
        init_waitqueue_head(&chip->ac97_waitqueue);
 
        err = pci_enable_device(pci);
index 2e1a6996fa864b5b281b841a8da3687e40af8995..40e92f5cd69c760111073cfe8e8a9c60e1f00cce 100644 (file)
@@ -136,6 +136,11 @@ MODULE_DEVICE_TABLE(pci, xonar_ids);
 /* register 23 */
 #define PCM1796_ID_MASK                0x1f
 
+struct xonar_data {
+       u8 is_d2x;
+       u8 has_power;
+};
+
 static void pcm1796_write(struct oxygen *chip, unsigned int codec,
                          u8 reg, u8 value)
 {
@@ -153,8 +158,11 @@ static void pcm1796_write(struct oxygen *chip, unsigned int codec,
 
 static void xonar_init(struct oxygen *chip)
 {
+       struct xonar_data *data = chip->model_data;
        unsigned int i;
 
+       data->is_d2x = chip->pci->subsystem_device == 0x82b7;
+
        for (i = 0; i < 4; ++i) {
                pcm1796_write(chip, i, 18, PCM1796_FMT_24_LJUST | PCM1796_ATLD);
                pcm1796_write(chip, i, 19, PCM1796_FLT_SHARP | PCM1796_ATS_1);
@@ -169,6 +177,15 @@ static void xonar_init(struct oxygen *chip)
        oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
                              GPIO_CS5381_M_SINGLE,
                              GPIO_CS5381_M_MASK | GPIO_ALT);
+       if (data->is_d2x) {
+               oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL,
+                                   GPIO_EXT_POWER);
+               oxygen_set_bits16(chip, OXYGEN_GPIO_INTERRUPT_MASK,
+                                 GPIO_EXT_POWER);
+               chip->interrupt_mask |= OXYGEN_INT_GPIO;
+               data->has_power = !!(oxygen_read16(chip, OXYGEN_GPIO_DATA)
+                                    & GPIO_EXT_POWER);
+       }
        oxygen_ac97_set_bits(chip, 0, CM9780_JACK, CM9780_FMIC2MIC);
        oxygen_ac97_clear_bits(chip, 0, CM9780_GPIO_STATUS, GPIO_LINE_MUTE);
        msleep(300);
@@ -234,6 +251,27 @@ static void set_cs5381_params(struct oxygen *chip,
                              value, GPIO_CS5381_M_MASK);
 }
 
+static void xonar_gpio_changed(struct oxygen *chip)
+{
+       struct xonar_data *data = chip->model_data;
+       u8 has_power;
+
+       if (!data->is_d2x)
+               return;
+       has_power = !!(oxygen_read16(chip, OXYGEN_GPIO_DATA)
+                      & GPIO_EXT_POWER);
+       if (has_power != data->has_power) {
+               data->has_power = has_power;
+               if (has_power) {
+                       snd_printk(KERN_NOTICE "power restored\n");
+               } else {
+                       snd_printk(KERN_CRIT
+                                  "Hey! Don't unplug the power cable!\n");
+                       /* TODO: stop PCMs */
+               }
+       }
+}
+
 static void mute_ac97_ctl(struct oxygen *chip, unsigned int control)
 {
        unsigned int index = chip->controls[control]->private_value & 0xff;
@@ -360,6 +398,8 @@ static const struct oxygen_model model_xonar = {
        .update_dac_volume = update_pcm1796_volume,
        .update_dac_mute = update_pcm1796_mute,
        .ac97_switch_hook = xonar_ac97_switch_hook,
+       .gpio_changed = xonar_gpio_changed,
+       .model_data_size = sizeof(struct xonar_data),
        .dac_channels = 8,
        .used_channels = OXYGEN_CHANNEL_B |
                         OXYGEN_CHANNEL_C |