[ALSA] oxygen: add PM support
Clemens Ladisch [Tue, 13 May 2008 07:24:39 +0000 (09:24 +0200)]
Add suspend/resume support.

Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
Signed-off-by: Jaroslav Kysela <perex@perex.cz>

sound/pci/oxygen/hifier.c
sound/pci/oxygen/oxygen.c
sound/pci/oxygen/oxygen.h
sound/pci/oxygen/oxygen_lib.c
sound/pci/oxygen/oxygen_pcm.c
sound/pci/oxygen/virtuoso.c

index 6e45a58..7442460 100644 (file)
@@ -146,6 +146,7 @@ static const struct oxygen_model model_hifier = {
        .init = hifier_init,
        .control_filter = hifier_control_filter,
        .cleanup = hifier_cleanup,
+       .resume = hifier_registers_init,
        .set_dac_params = set_ak4396_params,
        .set_adc_params = set_cs5340_params,
        .update_dac_volume = update_ak4396_volume,
@@ -186,6 +187,10 @@ static struct pci_driver hifier_driver = {
        .id_table = hifier_ids,
        .probe = hifier_probe,
        .remove = __devexit_p(oxygen_pci_remove),
+#ifdef CONFIG_PM
+       .suspend = oxygen_pci_suspend,
+       .resume = oxygen_pci_resume,
+#endif
 };
 
 static int __init alsa_card_hifier_init(void)
index 800ae30..7c8ae31 100644 (file)
@@ -192,6 +192,12 @@ static void generic_cleanup(struct oxygen *chip)
 {
 }
 
+static void generic_resume(struct oxygen *chip)
+{
+       ak4396_registers_init(chip);
+       wm8785_registers_init(chip);
+}
+
 static void set_ak4396_params(struct oxygen *chip,
                              struct snd_pcm_hw_params *params)
 {
@@ -278,6 +284,7 @@ static const struct oxygen_model model_generic = {
        .owner = THIS_MODULE,
        .init = generic_init,
        .cleanup = generic_cleanup,
+       .resume = generic_resume,
        .set_dac_params = set_ak4396_params,
        .set_adc_params = set_wm8785_params,
        .update_dac_volume = update_ak4396_volume,
@@ -305,6 +312,7 @@ static const struct oxygen_model model_meridian = {
        .owner = THIS_MODULE,
        .init = meridian_init,
        .cleanup = generic_cleanup,
+       .resume = ak4396_registers_init,
        .set_dac_params = set_ak4396_params,
        .set_adc_params = set_ak5385_params,
        .update_dac_volume = update_ak4396_volume,
@@ -353,6 +361,10 @@ static struct pci_driver oxygen_driver = {
        .id_table = oxygen_ids,
        .probe = generic_oxygen_probe,
        .remove = __devexit_p(oxygen_pci_remove),
+#ifdef CONFIG_PM
+       .suspend = oxygen_pci_suspend,
+       .resume = oxygen_pci_resume,
+#endif
 };
 
 static int __init alsa_card_oxygen_init(void)
index 29c9fa4..74a6448 100644 (file)
@@ -97,6 +97,8 @@ struct oxygen_model {
        int (*control_filter)(struct snd_kcontrol_new *template);
        int (*mixer_init)(struct oxygen *chip);
        void (*cleanup)(struct oxygen *chip);
+       void (*suspend)(struct oxygen *chip);
+       void (*resume)(struct oxygen *chip);
        void (*pcm_hardware_filter)(unsigned int channel,
                                    struct snd_pcm_hardware *hardware);
        void (*set_dac_params)(struct oxygen *chip,
@@ -125,6 +127,10 @@ struct oxygen_model {
 int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,
                     const struct oxygen_model *model);
 void oxygen_pci_remove(struct pci_dev *pci);
+#ifdef CONFIG_PM
+int oxygen_pci_suspend(struct pci_dev *pci, pm_message_t state);
+int oxygen_pci_resume(struct pci_dev *pci);
+#endif
 
 /* oxygen_mixer.c */
 
index 60bc159..22f3785 100644 (file)
@@ -314,6 +314,10 @@ static void oxygen_init(struct oxygen *chip)
                                    OXYGEN_SPDIF_LOCK_MASK |
                                    OXYGEN_SPDIF_RATE_MASK);
        oxygen_write32(chip, OXYGEN_SPDIF_OUTPUT_BITS, chip->spdif_bits);
+       oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS,
+                      OXYGEN_2WIRE_LENGTH_8 |
+                      OXYGEN_2WIRE_INTERRUPT_MASK |
+                      OXYGEN_2WIRE_SPEED_STANDARD);
        oxygen_clear_bits8(chip, OXYGEN_MPU401_CONTROL, OXYGEN_MPU401_LOOPBACK);
        oxygen_write8(chip, OXYGEN_GPI_INTERRUPT_MASK, 0);
        oxygen_write16(chip, OXYGEN_GPIO_INTERRUPT_MASK, 0);
@@ -534,3 +538,99 @@ void oxygen_pci_remove(struct pci_dev *pci)
        pci_set_drvdata(pci, NULL);
 }
 EXPORT_SYMBOL(oxygen_pci_remove);
+
+#ifdef CONFIG_PM
+int oxygen_pci_suspend(struct pci_dev *pci, pm_message_t state)
+{
+       struct snd_card *card = pci_get_drvdata(pci);
+       struct oxygen *chip = card->private_data;
+       unsigned int i, saved_interrupt_mask;
+
+       snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+
+       for (i = 0; i < PCM_COUNT; ++i)
+               if (chip->streams[i])
+                       snd_pcm_suspend(chip->streams[i]);
+
+       if (chip->model->suspend)
+               chip->model->suspend(chip);
+
+       spin_lock_irq(&chip->reg_lock);
+       saved_interrupt_mask = chip->interrupt_mask;
+       chip->interrupt_mask = 0;
+       oxygen_write16(chip, OXYGEN_DMA_STATUS, 0);
+       oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, 0);
+       spin_unlock_irq(&chip->reg_lock);
+
+       synchronize_irq(chip->irq);
+       flush_scheduled_work();
+       chip->interrupt_mask = saved_interrupt_mask;
+
+       pci_disable_device(pci);
+       pci_save_state(pci);
+       pci_set_power_state(pci, pci_choose_state(pci, state));
+       return 0;
+}
+EXPORT_SYMBOL(oxygen_pci_suspend);
+
+static const u32 registers_to_restore[OXYGEN_IO_SIZE / 32] = {
+       0xffffffff, 0x00ff077f, 0x00011d08, 0x007f00ff,
+       0x00300000, 0x00000fe4, 0x0ff7001f, 0x00000000
+};
+static const u32 ac97_registers_to_restore[2][0x40 / 32] = {
+       { 0x18284fa2, 0x03060000 },
+       { 0x00007fa6, 0x00200000 }
+};
+
+static inline int is_bit_set(const u32 *bitmap, unsigned int bit)
+{
+       return bitmap[bit / 32] & (1 << (bit & 31));
+}
+
+static void oxygen_restore_ac97(struct oxygen *chip, unsigned int codec)
+{
+       unsigned int i;
+
+       oxygen_write_ac97(chip, codec, AC97_RESET, 0);
+       msleep(1);
+       for (i = 1; i < 0x40; ++i)
+               if (is_bit_set(ac97_registers_to_restore[codec], i))
+                       oxygen_write_ac97(chip, codec, i * 2,
+                                         chip->saved_ac97_registers[codec][i]);
+}
+
+int oxygen_pci_resume(struct pci_dev *pci)
+{
+       struct snd_card *card = pci_get_drvdata(pci);
+       struct oxygen *chip = card->private_data;
+       unsigned int i;
+
+       pci_set_power_state(pci, PCI_D0);
+       pci_restore_state(pci);
+       if (pci_enable_device(pci) < 0) {
+               snd_printk(KERN_ERR "cannot reenable device");
+               snd_card_disconnect(card);
+               return -EIO;
+       }
+       pci_set_master(pci);
+
+       oxygen_write16(chip, OXYGEN_DMA_STATUS, 0);
+       oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, 0);
+       for (i = 0; i < OXYGEN_IO_SIZE; ++i)
+               if (is_bit_set(registers_to_restore, i))
+                       oxygen_write8(chip, i, chip->saved_registers._8[i]);
+       if (chip->has_ac97_0)
+               oxygen_restore_ac97(chip, 0);
+       if (chip->has_ac97_1)
+               oxygen_restore_ac97(chip, 1);
+
+       if (chip->model->resume)
+               chip->model->resume(chip);
+
+       oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, chip->interrupt_mask);
+
+       snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+       return 0;
+}
+EXPORT_SYMBOL(oxygen_pci_resume);
+#endif /* CONFIG_PM */
index b17c405..9680aa3 100644 (file)
@@ -517,6 +517,7 @@ static int oxygen_trigger(struct snd_pcm_substream *substream, int cmd)
        switch (cmd) {
        case SNDRV_PCM_TRIGGER_STOP:
        case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
                pausing = 0;
                break;
        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
index abd5313..9a2c16b 100644 (file)
@@ -359,6 +359,18 @@ static void xonar_dx_cleanup(struct oxygen *chip)
        oxygen_clear_bits8(chip, OXYGEN_FUNCTION, OXYGEN_FUNCTION_RESET_CODEC);
 }
 
+static void xonar_d2_resume(struct oxygen *chip)
+{
+       pcm1796_init(chip);
+       xonar_enable_output(chip);
+}
+
+static void xonar_dx_resume(struct oxygen *chip)
+{
+       cs43xx_init(chip);
+       xonar_enable_output(chip);
+}
+
 static void set_pcm1796_params(struct oxygen *chip,
                               struct snd_pcm_hw_params *params)
 {
@@ -551,6 +563,8 @@ static const struct oxygen_model xonar_models[] = {
                .control_filter = xonar_d2_control_filter,
                .mixer_init = xonar_mixer_init,
                .cleanup = xonar_cleanup,
+               .suspend = xonar_cleanup,
+               .resume = xonar_d2_resume,
                .set_dac_params = set_pcm1796_params,
                .set_adc_params = set_cs53x1_params,
                .update_dac_volume = update_pcm1796_volume,
@@ -579,6 +593,8 @@ static const struct oxygen_model xonar_models[] = {
                .control_filter = xonar_d2_control_filter,
                .mixer_init = xonar_mixer_init,
                .cleanup = xonar_cleanup,
+               .suspend = xonar_cleanup,
+               .resume = xonar_d2_resume,
                .set_dac_params = set_pcm1796_params,
                .set_adc_params = set_cs53x1_params,
                .update_dac_volume = update_pcm1796_volume,
@@ -608,6 +624,8 @@ static const struct oxygen_model xonar_models[] = {
                .control_filter = xonar_dx_control_filter,
                .mixer_init = xonar_dx_mixer_init,
                .cleanup = xonar_dx_cleanup,
+               .suspend = xonar_dx_cleanup,
+               .resume = xonar_dx_resume,
                .set_dac_params = set_cs43xx_params,
                .set_adc_params = set_cs53x1_params,
                .update_dac_volume = update_cs43xx_volume,
@@ -652,6 +670,10 @@ static struct pci_driver xonar_driver = {
        .id_table = xonar_ids,
        .probe = xonar_probe,
        .remove = __devexit_p(oxygen_pci_remove),
+#ifdef CONFIG_PM
+       .suspend = oxygen_pci_suspend,
+       .resume = oxygen_pci_resume,
+#endif
 };
 
 static int __init alsa_card_xonar_init(void)