diff --git a/arch/x86/configs/i386_greenridge_android_defconfig b/arch/x86/configs/i386_greenridge_android_defconfig diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 7a926c7..27896e5 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -395,6 +396,12 @@ struct azx { int capture_index_offset; int num_streams; +#ifdef CONFIG_SND_HDA_POWER_SAVE + unsigned char *running_stream_map; + /* count running streams for power down when all paused */ + unsigned int running_stream_count; +#endif + /* pci resources */ unsigned long addr; void __iomem *remap_addr; @@ -855,6 +862,9 @@ static unsigned int azx_get_response(struct hda_bus *bus, #ifdef CONFIG_SND_HDA_POWER_SAVE static void azx_power_notify(struct hda_bus *bus); +static int azx_update_running_streams(struct azx *chip, + int index, + unsigned char state); #endif /* reset codec link */ @@ -1701,6 +1716,10 @@ static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) struct snd_pcm_substream *s; int rstart = 0, start, nsync = 0, sbits = 0; int nwait, timeout; +#ifdef CONFIG_SND_HDA_POWER_SAVE + struct hda_codec *codec = apcm->codec; +#endif switch (cmd) { case SNDRV_PCM_TRIGGER_START: @@ -1727,6 +1747,13 @@ static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) snd_pcm_trigger_done(s, substream); } +#ifdef CONFIG_SND_HDA_POWER_SAVE + if (start && !codec->power_on) { + azx_init_chip(chip, 1); + snd_hda_power_up(codec); + } +#endif + spin_lock(&chip->reg_lock); if (nsync > 1) { /* first, set SYNC bits of corresponding streams */ @@ -1746,7 +1773,14 @@ static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) azx_stream_stop(chip, azx_dev); } azx_dev->running = start; +#ifdef CONFIG_SND_HDA_POWER_SAVE + azx_update_running_streams(chip, azx_dev->index, start); +#endif } spin_unlock(&chip->reg_lock); if (start) { if (nsync == 1) @@ -1789,6 +1823,23 @@ static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) azx_writel(chip, SYNC, azx_readl(chip, SYNC) & ~sbits); spin_unlock(&chip->reg_lock); } + +#ifdef CONFIG_SND_HDA_POWER_SAVE + if (!start && !chip->running_stream_count) + snd_hda_power_down(codec); +#endif + return 0; } @@ -2119,6 +2172,31 @@ static void azx_stop_chip(struct azx *chip) } #ifdef CONFIG_SND_HDA_POWER_SAVE +/** + * azx_update_running_streams - update state for running streams + * + * Returns 1 if there's a diff in state. + */ +static int azx_update_running_streams(struct azx *chip, + int index, + unsigned char state) +{ + unsigned char old_state = chip->running_stream_map[index]; + + if (state == old_state) + /* nothing changed */ + return 0; + + chip->running_stream_map[index] = state; + + if (state) + chip->running_stream_count++; + else + chip->running_stream_count--; + + return 1; +} + /* power-up/down the controller */ static void azx_power_notify(struct hda_bus *bus) { @@ -2126,16 +2204,41 @@ static void azx_power_notify(struct hda_bus *bus) struct hda_codec *c; int power_on = 0; + + /* Codecs can be powered down if all streams are paused/suspended and + * codec refcount is 1, since pcms are opened. + */ list_for_each_entry(c, &bus->codec_list, list) { + if (c->power_on) { + if (!chip->running_stream_count && (c->power_count <= 1)) { + printk("\tpowering down codec:\n"); + snd_hda_power_down(c); + } else { + power_on = 1; + break; + } + } - if (c->power_on) { - power_on = 1; - break; - } } if (power_on) azx_init_chip(chip, 1); else if (chip->running && power_save_controller && !bus->power_keep_link_on) azx_stop_chip(chip); } #endif /* CONFIG_SND_HDA_POWER_SAVE */ @@ -2269,6 +2372,9 @@ static int azx_free(struct azx *chip) snd_dma_free_pages(&chip->posbuf); pci_release_regions(chip->pci); pci_disable_device(chip->pci); +#ifdef CONFIG_SND_HDA_POWER_SAVE + kfree(chip->running_stream_map); +#endif kfree(chip->azx_dev); kfree(chip); @@ -2578,6 +2684,17 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci, goto errout; } +#ifdef CONFIG_SND_HDA_POWER_SAVE + chip->running_stream_map = kcalloc(chip->num_streams, + sizeof(*chip->running_stream_map), + GFP_KERNEL); + if (!chip->running_stream_map) { + snd_printk(KERN_ERR SFX "can't malloc running_stream_map\n"); + kfree(chip->azx_dev); + goto errout; + } +#endif + for (i = 0; i < chip->num_streams; i++) { /* allocate memory for the BDL for each stream */ err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,