1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Power management for audio on multifunction CS5535 companion device
4*4882a593Smuzhiyun * Copyright (C) Jaya Kumar
5*4882a593Smuzhiyun */
6*4882a593Smuzhiyun
7*4882a593Smuzhiyun #include <linux/init.h>
8*4882a593Smuzhiyun #include <linux/pci.h>
9*4882a593Smuzhiyun #include <linux/delay.h>
10*4882a593Smuzhiyun #include <sound/core.h>
11*4882a593Smuzhiyun #include <sound/control.h>
12*4882a593Smuzhiyun #include <sound/initval.h>
13*4882a593Smuzhiyun #include <sound/asoundef.h>
14*4882a593Smuzhiyun #include <sound/pcm.h>
15*4882a593Smuzhiyun #include <sound/ac97_codec.h>
16*4882a593Smuzhiyun #include "cs5535audio.h"
17*4882a593Smuzhiyun
snd_cs5535audio_stop_hardware(struct cs5535audio * cs5535au)18*4882a593Smuzhiyun static void snd_cs5535audio_stop_hardware(struct cs5535audio *cs5535au)
19*4882a593Smuzhiyun {
20*4882a593Smuzhiyun /*
21*4882a593Smuzhiyun we depend on snd_ac97_suspend to tell the
22*4882a593Smuzhiyun AC97 codec to shutdown. the amd spec suggests
23*4882a593Smuzhiyun that the LNK_SHUTDOWN be done at the same time
24*4882a593Smuzhiyun that the codec power-down is issued. instead,
25*4882a593Smuzhiyun we do it just after rather than at the same
26*4882a593Smuzhiyun time. excluding codec specific build_ops->suspend
27*4882a593Smuzhiyun ac97 powerdown hits:
28*4882a593Smuzhiyun 0x8000 EAPD
29*4882a593Smuzhiyun 0x4000 Headphone amplifier
30*4882a593Smuzhiyun 0x0300 ADC & DAC
31*4882a593Smuzhiyun 0x0400 Analog Mixer powerdown (Vref on)
32*4882a593Smuzhiyun I am not sure if this is the best that we can do.
33*4882a593Smuzhiyun The remainder to be investigated are:
34*4882a593Smuzhiyun - analog mixer (vref off) 0x0800
35*4882a593Smuzhiyun - AC-link powerdown 0x1000
36*4882a593Smuzhiyun - codec internal clock 0x2000
37*4882a593Smuzhiyun */
38*4882a593Smuzhiyun
39*4882a593Smuzhiyun /* set LNK_SHUTDOWN to shutdown AC link */
40*4882a593Smuzhiyun cs_writel(cs5535au, ACC_CODEC_CNTL, ACC_CODEC_CNTL_LNK_SHUTDOWN);
41*4882a593Smuzhiyun
42*4882a593Smuzhiyun }
43*4882a593Smuzhiyun
snd_cs5535audio_suspend(struct device * dev)44*4882a593Smuzhiyun static int __maybe_unused snd_cs5535audio_suspend(struct device *dev)
45*4882a593Smuzhiyun {
46*4882a593Smuzhiyun struct snd_card *card = dev_get_drvdata(dev);
47*4882a593Smuzhiyun struct cs5535audio *cs5535au = card->private_data;
48*4882a593Smuzhiyun int i;
49*4882a593Smuzhiyun
50*4882a593Smuzhiyun snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
51*4882a593Smuzhiyun snd_ac97_suspend(cs5535au->ac97);
52*4882a593Smuzhiyun for (i = 0; i < NUM_CS5535AUDIO_DMAS; i++) {
53*4882a593Smuzhiyun struct cs5535audio_dma *dma = &cs5535au->dmas[i];
54*4882a593Smuzhiyun if (dma && dma->substream)
55*4882a593Smuzhiyun dma->saved_prd = dma->ops->read_prd(cs5535au);
56*4882a593Smuzhiyun }
57*4882a593Smuzhiyun /* save important regs, then disable aclink in hw */
58*4882a593Smuzhiyun snd_cs5535audio_stop_hardware(cs5535au);
59*4882a593Smuzhiyun return 0;
60*4882a593Smuzhiyun }
61*4882a593Smuzhiyun
snd_cs5535audio_resume(struct device * dev)62*4882a593Smuzhiyun static int __maybe_unused snd_cs5535audio_resume(struct device *dev)
63*4882a593Smuzhiyun {
64*4882a593Smuzhiyun struct snd_card *card = dev_get_drvdata(dev);
65*4882a593Smuzhiyun struct cs5535audio *cs5535au = card->private_data;
66*4882a593Smuzhiyun u32 tmp;
67*4882a593Smuzhiyun int timeout;
68*4882a593Smuzhiyun int i;
69*4882a593Smuzhiyun
70*4882a593Smuzhiyun /* set LNK_WRM_RST to reset AC link */
71*4882a593Smuzhiyun cs_writel(cs5535au, ACC_CODEC_CNTL, ACC_CODEC_CNTL_LNK_WRM_RST);
72*4882a593Smuzhiyun
73*4882a593Smuzhiyun timeout = 50;
74*4882a593Smuzhiyun do {
75*4882a593Smuzhiyun tmp = cs_readl(cs5535au, ACC_CODEC_STATUS);
76*4882a593Smuzhiyun if (tmp & PRM_RDY_STS)
77*4882a593Smuzhiyun break;
78*4882a593Smuzhiyun udelay(1);
79*4882a593Smuzhiyun } while (--timeout);
80*4882a593Smuzhiyun
81*4882a593Smuzhiyun if (!timeout)
82*4882a593Smuzhiyun dev_err(cs5535au->card->dev, "Failure getting AC Link ready\n");
83*4882a593Smuzhiyun
84*4882a593Smuzhiyun /* set up rate regs, dma. actual initiation is done in trig */
85*4882a593Smuzhiyun for (i = 0; i < NUM_CS5535AUDIO_DMAS; i++) {
86*4882a593Smuzhiyun struct cs5535audio_dma *dma = &cs5535au->dmas[i];
87*4882a593Smuzhiyun if (dma && dma->substream) {
88*4882a593Smuzhiyun dma->substream->ops->prepare(dma->substream);
89*4882a593Smuzhiyun dma->ops->setup_prd(cs5535au, dma->saved_prd);
90*4882a593Smuzhiyun }
91*4882a593Smuzhiyun }
92*4882a593Smuzhiyun
93*4882a593Smuzhiyun /* we depend on ac97 to perform the codec power up */
94*4882a593Smuzhiyun snd_ac97_resume(cs5535au->ac97);
95*4882a593Smuzhiyun snd_power_change_state(card, SNDRV_CTL_POWER_D0);
96*4882a593Smuzhiyun
97*4882a593Smuzhiyun return 0;
98*4882a593Smuzhiyun }
99*4882a593Smuzhiyun
100*4882a593Smuzhiyun SIMPLE_DEV_PM_OPS(snd_cs5535audio_pm, snd_cs5535audio_suspend, snd_cs5535audio_resume);
101