ALSA: hda: Disable platform clocks when not required
Sumit Bhattacharya [Mon, 28 Nov 2011 08:11:25 +0000 (13:11 +0530)]
Add support for disabling of platform driver clocks when HDA is idle
to reduce HDA power consumption. Take care of enabling of HDA clocks
before accessing any hardware register.

Bug 904530

Change-Id: I51be7990e900cef3dac376d9635c48cd1ffbfa99
Signed-off-by: Sumit Bhattacharya <sumitb@nvidia.com>
Reviewed-on: http://git-master/r/66886
Reviewed-by: Scott Peterson <speterson@nvidia.com>

sound/pci/hda/hda_intel.c

index 7b7fc90..cc32d89 100644 (file)
@@ -441,6 +441,7 @@ struct azx {
        /* platform driver clocks */
        struct clk **platform_clks;
        int platform_clk_count;
+       int platform_clk_enable;
 #endif
 
        /* locks */
@@ -1276,14 +1277,21 @@ static void azx_platform_enable_clocks(struct azx *chip)
 
        for (i = 0; i < chip->platform_clk_count; i++)
                clk_enable(chip->platform_clks[i]);
+
+       chip->platform_clk_enable++;
 }
 
 static void azx_platform_disable_clocks(struct azx *chip)
 {
        int i;
 
+       if (!chip->platform_clk_enable)
+               return;
+
        for (i = 0; i < chip->platform_clk_count; i++)
                clk_disable(chip->platform_clks[i]);
+
+       chip->platform_clk_enable--;
 }
 #endif /* CONFIG_SND_HDA_PLATFORM_DRIVER */
 
@@ -2374,11 +2382,19 @@ static void azx_power_notify(struct hda_bus *bus)
                        break;
                }
        }
-       if (power_on)
+       if (power_on) {
+#ifdef CONFIG_SND_HDA_PLATFORM_DRIVER
+               azx_platform_enable_clocks(chip);
+#endif
                azx_init_chip(chip, 1);
+       }
        else if (chip->running && power_save_controller &&
-                !bus->power_keep_link_on)
+                !bus->power_keep_link_on) {
                azx_stop_chip(chip);
+#ifdef CONFIG_SND_HDA_PLATFORM_DRIVER
+               azx_platform_disable_clocks(chip);
+#endif
+       }
 }
 #endif /* CONFIG_SND_HDA_POWER_SAVE */
 
@@ -2403,6 +2419,12 @@ static int azx_suspend(struct azx *chip, pm_message_t state)
        struct snd_card *card = chip->card;
        int i;
 
+#if defined(CONFIG_SND_HDA_PLATFORM_DRIVER) && \
+       defined(CONFIG_SND_HDA_POWER_SAVE)
+       if (!chip->platform_clk_enable)
+               azx_platform_enable_clocks(chip);
+#endif
+
        snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
        azx_clear_irq_pending(chip);
        for (i = 0; i < HDA_MAX_PCMS; i++)
@@ -2472,6 +2494,13 @@ static int azx_resume(struct azx *chip)
 
        snd_hda_resume(chip->bus);
        snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+
+#if defined(CONFIG_SND_HDA_PLATFORM_DRIVER) && \
+       defined(CONFIG_SND_HDA_POWER_SAVE)
+       if (chip->pdev)
+               azx_platform_disable_clocks(chip);
+#endif
+
        return 0;
 }
 
@@ -2518,8 +2547,22 @@ static int azx_resume_platform(struct platform_device *pdev)
 static int azx_halt(struct notifier_block *nb, unsigned long event, void *buf)
 {
        struct azx *chip = container_of(nb, struct azx, reboot_notifier);
+
+#if defined(CONFIG_SND_HDA_PLATFORM_DRIVER) && \
+       defined(CONFIG_SND_HDA_POWER_SAVE)
+       if (chip->pdev)
+               azx_platform_enable_clocks(chip);
+#endif
+
        snd_hda_bus_reboot_notify(chip->bus);
        azx_stop_chip(chip);
+
+#if defined(CONFIG_SND_HDA_PLATFORM_DRIVER) && \
+       defined(CONFIG_SND_HDA_POWER_SAVE)
+       if (chip->pdev)
+               azx_platform_disable_clocks(chip);
+#endif
+
        return NOTIFY_OK;
 }