Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Power management for audio on multifunction CS5535 companion device
0004  * Copyright (C) Jaya Kumar
0005  */
0006 
0007 #include <linux/init.h>
0008 #include <linux/pci.h>
0009 #include <linux/delay.h>
0010 #include <sound/core.h>
0011 #include <sound/control.h>
0012 #include <sound/initval.h>
0013 #include <sound/asoundef.h>
0014 #include <sound/pcm.h>
0015 #include <sound/ac97_codec.h>
0016 #include "cs5535audio.h"
0017 
0018 static void snd_cs5535audio_stop_hardware(struct cs5535audio *cs5535au)
0019 {
0020     /* 
0021     we depend on snd_ac97_suspend to tell the
0022     AC97 codec to shutdown. the amd spec suggests
0023     that the LNK_SHUTDOWN be done at the same time
0024     that the codec power-down is issued. instead,
0025     we do it just after rather than at the same 
0026     time. excluding codec specific build_ops->suspend
0027     ac97 powerdown hits:
0028     0x8000 EAPD 
0029     0x4000 Headphone amplifier 
0030     0x0300 ADC & DAC 
0031     0x0400 Analog Mixer powerdown (Vref on) 
0032     I am not sure if this is the best that we can do.
0033     The remainder to be investigated are:
0034     - analog mixer (vref off) 0x0800
0035     - AC-link powerdown 0x1000
0036     - codec internal clock 0x2000
0037     */
0038 
0039     /* set LNK_SHUTDOWN to shutdown AC link */
0040     cs_writel(cs5535au, ACC_CODEC_CNTL, ACC_CODEC_CNTL_LNK_SHUTDOWN);
0041 
0042 }
0043 
0044 static int __maybe_unused snd_cs5535audio_suspend(struct device *dev)
0045 {
0046     struct snd_card *card = dev_get_drvdata(dev);
0047     struct cs5535audio *cs5535au = card->private_data;
0048     int i;
0049 
0050     snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
0051     snd_ac97_suspend(cs5535au->ac97);
0052     for (i = 0; i < NUM_CS5535AUDIO_DMAS; i++) {
0053         struct cs5535audio_dma *dma = &cs5535au->dmas[i];
0054         if (dma && dma->substream)
0055             dma->saved_prd = dma->ops->read_prd(cs5535au);
0056     }
0057     /* save important regs, then disable aclink in hw */
0058     snd_cs5535audio_stop_hardware(cs5535au);
0059     return 0;
0060 }
0061 
0062 static int __maybe_unused snd_cs5535audio_resume(struct device *dev)
0063 {
0064     struct snd_card *card = dev_get_drvdata(dev);
0065     struct cs5535audio *cs5535au = card->private_data;
0066     u32 tmp;
0067     int timeout;
0068     int i;
0069 
0070     /* set LNK_WRM_RST to reset AC link */
0071     cs_writel(cs5535au, ACC_CODEC_CNTL, ACC_CODEC_CNTL_LNK_WRM_RST);
0072 
0073     timeout = 50;
0074     do {
0075         tmp = cs_readl(cs5535au, ACC_CODEC_STATUS);
0076         if (tmp & PRM_RDY_STS)
0077             break;
0078         udelay(1);
0079     } while (--timeout);
0080 
0081     if (!timeout)
0082         dev_err(cs5535au->card->dev, "Failure getting AC Link ready\n");
0083 
0084     /* set up rate regs, dma. actual initiation is done in trig */
0085     for (i = 0; i < NUM_CS5535AUDIO_DMAS; i++) {
0086         struct cs5535audio_dma *dma = &cs5535au->dmas[i];
0087         if (dma && dma->substream) {
0088             dma->substream->ops->prepare(dma->substream);
0089             dma->ops->setup_prd(cs5535au, dma->saved_prd);
0090         }
0091     }
0092 
0093     /* we depend on ac97 to perform the codec power up */
0094     snd_ac97_resume(cs5535au->ac97);
0095     snd_power_change_state(card, SNDRV_CTL_POWER_D0);
0096 
0097     return 0;
0098 }
0099 
0100 SIMPLE_DEV_PM_OPS(snd_cs5535audio_pm, snd_cs5535audio_suspend, snd_cs5535audio_resume);