[ALSA] virtuoso: monitor external power on D2X
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>
diff --git a/sound/pci/oxygen/oxygen.h b/sound/pci/oxygen/oxygen.h
index e71c534..ad50fb8 100644
--- a/sound/pci/oxygen/oxygen.h
+++ b/sound/pci/oxygen/oxygen.h
@@ -66,6 +66,7 @@
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 @@
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;
diff --git a/sound/pci/oxygen/oxygen_lib.c b/sound/pci/oxygen/oxygen_lib.c
index 540e56b..6eb36dd 100644
--- a/sound/pci/oxygen/oxygen_lib.c
+++ b/sound/pci/oxygen/oxygen_lib.c
@@ -85,7 +85,7 @@
}
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_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 @@
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);
diff --git a/sound/pci/oxygen/virtuoso.c b/sound/pci/oxygen/virtuoso.c
index 2e1a699..40e92f5 100644
--- a/sound/pci/oxygen/virtuoso.c
+++ b/sound/pci/oxygen/virtuoso.c
@@ -136,6 +136,11 @@
/* 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 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 @@
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 @@
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 @@
.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 |