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