aboutsummaryrefslogtreecommitdiffstats
path: root/sound/pci/cs5535audio
diff options
context:
space:
mode:
authorJaya Kumar <jayakumar.alsa@gmail.com>2006-04-28 08:34:49 -0400
committerJaroslav Kysela <perex@suse.cz>2006-06-22 15:32:49 -0400
commit9ac25594e68a4b61516e7c1140d8c0f7ef449e20 (patch)
tree628040fb065b882191a6ff5ed7aba42ef04b99d6 /sound/pci/cs5535audio
parent5e1b1518a53fc62d9f39a13819c849336c6d8dd4 (diff)
[ALSA] PM support for cs5535audio
Appended is my patch adding PM support to the cs5535audio driver. I also added the ac97 quirk but it's not yet confirmed which boards need to be in the quirk list. The patch also includes some Kconfig and misc cleanup. Signed-off-by: Jaya Kumar <jayakumar.alsa@gmail.com> Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci/cs5535audio')
-rw-r--r--sound/pci/cs5535audio/Makefile4
-rw-r--r--sound/pci/cs5535audio/cs5535audio.c31
-rw-r--r--sound/pci/cs5535audio/cs5535audio.h8
-rw-r--r--sound/pci/cs5535audio/cs5535audio_pcm.c24
-rw-r--r--sound/pci/cs5535audio/cs5535audio_pm.c123
5 files changed, 184 insertions, 6 deletions
diff --git a/sound/pci/cs5535audio/Makefile b/sound/pci/cs5535audio/Makefile
index 08d8ee6547d..2911a8adc1f 100644
--- a/sound/pci/cs5535audio/Makefile
+++ b/sound/pci/cs5535audio/Makefile
@@ -4,5 +4,9 @@
4 4
5snd-cs5535audio-objs := cs5535audio.o cs5535audio_pcm.o 5snd-cs5535audio-objs := cs5535audio.o cs5535audio_pcm.o
6 6
7ifdef CONFIG_PM
8snd-cs5535audio-objs += cs5535audio_pm.o
9endif
10
7# Toplevel Module Dependency 11# Toplevel Module Dependency
8obj-$(CONFIG_SND_CS5535AUDIO) += snd-cs5535audio.o 12obj-$(CONFIG_SND_CS5535AUDIO) += snd-cs5535audio.o
diff --git a/sound/pci/cs5535audio/cs5535audio.c b/sound/pci/cs5535audio/cs5535audio.c
index 2c1213a35dc..41f02f05dfd 100644
--- a/sound/pci/cs5535audio/cs5535audio.c
+++ b/sound/pci/cs5535audio/cs5535audio.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Driver for audio on multifunction CS5535 companion device 2 * Driver for audio on multifunction CS5535/6 companion device
3 * Copyright (C) Jaya Kumar 3 * Copyright (C) Jaya Kumar
4 * 4 *
5 * Based on Jaroslav Kysela and Takashi Iwai's examples. 5 * Based on Jaroslav Kysela and Takashi Iwai's examples.
@@ -40,16 +40,29 @@
40 40
41#define DRIVER_NAME "cs5535audio" 41#define DRIVER_NAME "cs5535audio"
42 42
43static char *ac97_quirk;
44module_param(ac97_quirk, charp, 0444);
45MODULE_PARM_DESC(ac97_quirk, "AC'97 board specific workarounds.");
46
47static struct ac97_quirk ac97_quirks[] __devinitdata = {
48#if 0 /* Not yet confirmed if all 5536 boards are HP only */
49 {
50 .subvendor = PCI_VENDOR_ID_AMD,
51 .subdevice = PCI_DEVICE_ID_AMD_CS5536_AUDIO,
52 .name = "AMD RDK",
53 .type = AC97_TUNE_HP_ONLY
54 },
55#endif
56 {}
57};
43 58
44static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; 59static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
45static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; 60static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
46static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; 61static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
47 62
48static struct pci_device_id snd_cs5535audio_ids[] __devinitdata = { 63static struct pci_device_id snd_cs5535audio_ids[] __devinitdata = {
49 { PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_CS5535_AUDIO, 64 { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_CS5535_AUDIO) },
50 PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, 65 { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_AUDIO) },
51 { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_AUDIO,
52 PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },
53 {} 66 {}
54}; 67};
55 68
@@ -148,6 +161,8 @@ static int snd_cs5535audio_mixer(struct cs5535audio *cs5535au)
148 return err; 161 return err;
149 } 162 }
150 163
164 snd_ac97_tune_hardware(cs5535au->ac97, ac97_quirks, ac97_quirk);
165
151 return 0; 166 return 0;
152} 167}
153 168
@@ -347,6 +362,8 @@ static int __devinit snd_cs5535audio_probe(struct pci_dev *pci,
347 if ((err = snd_cs5535audio_create(card, pci, &cs5535au)) < 0) 362 if ((err = snd_cs5535audio_create(card, pci, &cs5535au)) < 0)
348 goto probefail_out; 363 goto probefail_out;
349 364
365 card->private_data = cs5535au;
366
350 if ((err = snd_cs5535audio_mixer(cs5535au)) < 0) 367 if ((err = snd_cs5535audio_mixer(cs5535au)) < 0)
351 goto probefail_out; 368 goto probefail_out;
352 369
@@ -383,6 +400,10 @@ static struct pci_driver driver = {
383 .id_table = snd_cs5535audio_ids, 400 .id_table = snd_cs5535audio_ids,
384 .probe = snd_cs5535audio_probe, 401 .probe = snd_cs5535audio_probe,
385 .remove = __devexit_p(snd_cs5535audio_remove), 402 .remove = __devexit_p(snd_cs5535audio_remove),
403#ifdef CONFIG_PM
404 .suspend = snd_cs5535audio_suspend,
405 .resume = snd_cs5535audio_resume,
406#endif
386}; 407};
387 408
388static int __init alsa_card_cs5535audio_init(void) 409static int __init alsa_card_cs5535audio_init(void)
diff --git a/sound/pci/cs5535audio/cs5535audio.h b/sound/pci/cs5535audio/cs5535audio.h
index 5e55a1a1ed6..4fd1f31a6cf 100644
--- a/sound/pci/cs5535audio/cs5535audio.h
+++ b/sound/pci/cs5535audio/cs5535audio.h
@@ -74,6 +74,8 @@
74#define PRM_RDY_STS 0x00800000 74#define PRM_RDY_STS 0x00800000
75#define ACC_CODEC_CNTL_WR_CMD (~0x80000000) 75#define ACC_CODEC_CNTL_WR_CMD (~0x80000000)
76#define ACC_CODEC_CNTL_RD_CMD 0x80000000 76#define ACC_CODEC_CNTL_RD_CMD 0x80000000
77#define ACC_CODEC_CNTL_LNK_SHUTDOWN 0x00040000
78#define ACC_CODEC_CNTL_LNK_WRM_RST 0x00020000
77#define PRD_JMP 0x2000 79#define PRD_JMP 0x2000
78#define PRD_EOP 0x4000 80#define PRD_EOP 0x4000
79#define PRD_EOT 0x8000 81#define PRD_EOT 0x8000
@@ -88,6 +90,7 @@ struct cs5535audio_dma_ops {
88 void (*disable_dma)(struct cs5535audio *cs5535au); 90 void (*disable_dma)(struct cs5535audio *cs5535au);
89 void (*pause_dma)(struct cs5535audio *cs5535au); 91 void (*pause_dma)(struct cs5535audio *cs5535au);
90 void (*setup_prd)(struct cs5535audio *cs5535au, u32 prd_addr); 92 void (*setup_prd)(struct cs5535audio *cs5535au, u32 prd_addr);
93 u32 (*read_prd)(struct cs5535audio *cs5535au);
91 u32 (*read_dma_pntr)(struct cs5535audio *cs5535au); 94 u32 (*read_dma_pntr)(struct cs5535audio *cs5535au);
92}; 95};
93 96
@@ -103,11 +106,14 @@ struct cs5535audio_dma {
103 struct snd_pcm_substream *substream; 106 struct snd_pcm_substream *substream;
104 unsigned int buf_addr, buf_bytes; 107 unsigned int buf_addr, buf_bytes;
105 unsigned int period_bytes, periods; 108 unsigned int period_bytes, periods;
109 int suspended;
110 u32 saved_prd;
106}; 111};
107 112
108struct cs5535audio { 113struct cs5535audio {
109 struct snd_card *card; 114 struct snd_card *card;
110 struct snd_ac97 *ac97; 115 struct snd_ac97 *ac97;
116 struct snd_pcm *pcm;
111 int irq; 117 int irq;
112 struct pci_dev *pci; 118 struct pci_dev *pci;
113 unsigned long port; 119 unsigned long port;
@@ -117,6 +123,8 @@ struct cs5535audio {
117 struct cs5535audio_dma dmas[NUM_CS5535AUDIO_DMAS]; 123 struct cs5535audio_dma dmas[NUM_CS5535AUDIO_DMAS];
118}; 124};
119 125
126int snd_cs5535audio_suspend(struct pci_dev *pci, pm_message_t state);
127int snd_cs5535audio_resume(struct pci_dev *pci);
120int __devinit snd_cs5535audio_pcm(struct cs5535audio *cs5535audio); 128int __devinit snd_cs5535audio_pcm(struct cs5535audio *cs5535audio);
121 129
122#endif /* __SOUND_CS5535AUDIO_H */ 130#endif /* __SOUND_CS5535AUDIO_H */
diff --git a/sound/pci/cs5535audio/cs5535audio_pcm.c b/sound/pci/cs5535audio/cs5535audio_pcm.c
index 60bb82b2ff4..f0a48693d68 100644
--- a/sound/pci/cs5535audio/cs5535audio_pcm.c
+++ b/sound/pci/cs5535audio/cs5535audio_pcm.c
@@ -43,7 +43,8 @@ static struct snd_pcm_hardware snd_cs5535audio_playback =
43 SNDRV_PCM_INFO_BLOCK_TRANSFER | 43 SNDRV_PCM_INFO_BLOCK_TRANSFER |
44 SNDRV_PCM_INFO_MMAP_VALID | 44 SNDRV_PCM_INFO_MMAP_VALID |
45 SNDRV_PCM_INFO_PAUSE | 45 SNDRV_PCM_INFO_PAUSE |
46 SNDRV_PCM_INFO_SYNC_START 46 SNDRV_PCM_INFO_SYNC_START |
47 SNDRV_PCM_INFO_RESUME
47 ), 48 ),
48 .formats = ( 49 .formats = (
49 SNDRV_PCM_FMTBIT_S16_LE 50 SNDRV_PCM_FMTBIT_S16_LE
@@ -193,6 +194,11 @@ static void cs5535audio_playback_setup_prd(struct cs5535audio *cs5535au,
193 cs_writel(cs5535au, ACC_BM0_PRD, prd_addr); 194 cs_writel(cs5535au, ACC_BM0_PRD, prd_addr);
194} 195}
195 196
197static u32 cs5535audio_playback_read_prd(struct cs5535audio *cs5535au)
198{
199 return cs_readl(cs5535au, ACC_BM0_PRD);
200}
201
196static u32 cs5535audio_playback_read_dma_pntr(struct cs5535audio *cs5535au) 202static u32 cs5535audio_playback_read_dma_pntr(struct cs5535audio *cs5535au)
197{ 203{
198 return cs_readl(cs5535au, ACC_BM0_PNTR); 204 return cs_readl(cs5535au, ACC_BM0_PNTR);
@@ -219,6 +225,11 @@ static void cs5535audio_capture_setup_prd(struct cs5535audio *cs5535au,
219 cs_writel(cs5535au, ACC_BM1_PRD, prd_addr); 225 cs_writel(cs5535au, ACC_BM1_PRD, prd_addr);
220} 226}
221 227
228static u32 cs5535audio_capture_read_prd(struct cs5535audio *cs5535au)
229{
230 return cs_readl(cs5535au, ACC_BM1_PRD);
231}
232
222static u32 cs5535audio_capture_read_dma_pntr(struct cs5535audio *cs5535au) 233static u32 cs5535audio_capture_read_dma_pntr(struct cs5535audio *cs5535au)
223{ 234{
224 return cs_readl(cs5535au, ACC_BM1_PNTR); 235 return cs_readl(cs5535au, ACC_BM1_PNTR);
@@ -285,9 +296,17 @@ static int snd_cs5535audio_trigger(struct snd_pcm_substream *substream, int cmd)
285 case SNDRV_PCM_TRIGGER_START: 296 case SNDRV_PCM_TRIGGER_START:
286 dma->ops->enable_dma(cs5535au); 297 dma->ops->enable_dma(cs5535au);
287 break; 298 break;
299 case SNDRV_PCM_TRIGGER_RESUME:
300 dma->ops->enable_dma(cs5535au);
301 dma->suspended = 0;
302 break;
288 case SNDRV_PCM_TRIGGER_STOP: 303 case SNDRV_PCM_TRIGGER_STOP:
289 dma->ops->disable_dma(cs5535au); 304 dma->ops->disable_dma(cs5535au);
290 break; 305 break;
306 case SNDRV_PCM_TRIGGER_SUSPEND:
307 dma->ops->disable_dma(cs5535au);
308 dma->suspended = 1;
309 break;
291 default: 310 default:
292 snd_printk(KERN_ERR "unhandled trigger\n"); 311 snd_printk(KERN_ERR "unhandled trigger\n");
293 err = -EINVAL; 312 err = -EINVAL;
@@ -375,6 +394,7 @@ static struct cs5535audio_dma_ops snd_cs5535audio_playback_dma_ops = {
375 .enable_dma = cs5535audio_playback_enable_dma, 394 .enable_dma = cs5535audio_playback_enable_dma,
376 .disable_dma = cs5535audio_playback_disable_dma, 395 .disable_dma = cs5535audio_playback_disable_dma,
377 .setup_prd = cs5535audio_playback_setup_prd, 396 .setup_prd = cs5535audio_playback_setup_prd,
397 .read_prd = cs5535audio_playback_read_prd,
378 .pause_dma = cs5535audio_playback_pause_dma, 398 .pause_dma = cs5535audio_playback_pause_dma,
379 .read_dma_pntr = cs5535audio_playback_read_dma_pntr, 399 .read_dma_pntr = cs5535audio_playback_read_dma_pntr,
380}; 400};
@@ -384,6 +404,7 @@ static struct cs5535audio_dma_ops snd_cs5535audio_capture_dma_ops = {
384 .enable_dma = cs5535audio_capture_enable_dma, 404 .enable_dma = cs5535audio_capture_enable_dma,
385 .disable_dma = cs5535audio_capture_disable_dma, 405 .disable_dma = cs5535audio_capture_disable_dma,
386 .setup_prd = cs5535audio_capture_setup_prd, 406 .setup_prd = cs5535audio_capture_setup_prd,
407 .read_prd = cs5535audio_capture_read_prd,
387 .pause_dma = cs5535audio_capture_pause_dma, 408 .pause_dma = cs5535audio_capture_pause_dma,
388 .read_dma_pntr = cs5535audio_capture_read_dma_pntr, 409 .read_dma_pntr = cs5535audio_capture_read_dma_pntr,
389}; 410};
@@ -413,6 +434,7 @@ int __devinit snd_cs5535audio_pcm(struct cs5535audio *cs5535au)
413 snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, 434 snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
414 snd_dma_pci_data(cs5535au->pci), 435 snd_dma_pci_data(cs5535au->pci),
415 64*1024, 128*1024); 436 64*1024, 128*1024);
437 cs5535au->pcm = pcm;
416 438
417 return 0; 439 return 0;
418} 440}
diff --git a/sound/pci/cs5535audio/cs5535audio_pm.c b/sound/pci/cs5535audio/cs5535audio_pm.c
new file mode 100644
index 00000000000..aad0e69db9c
--- /dev/null
+++ b/sound/pci/cs5535audio/cs5535audio_pm.c
@@ -0,0 +1,123 @@
1/*
2 * Power management for audio on multifunction CS5535 companion device
3 * Copyright (C) Jaya Kumar
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 *
19 */
20
21#include <linux/init.h>
22#include <linux/slab.h>
23#include <linux/pci.h>
24#include <linux/delay.h>
25#include <sound/driver.h>
26#include <sound/core.h>
27#include <sound/control.h>
28#include <sound/initval.h>
29#include <sound/asoundef.h>
30#include <sound/pcm.h>
31#include <sound/ac97_codec.h>
32#include "cs5535audio.h"
33
34static void snd_cs5535audio_stop_hardware(struct cs5535audio *cs5535au)
35{
36 /*
37 we depend on snd_ac97_suspend to tell the
38 AC97 codec to shutdown. the amd spec suggests
39 that the LNK_SHUTDOWN be done at the same time
40 that the codec power-down is issued. instead,
41 we do it just after rather than at the same
42 time. excluding codec specific build_ops->suspend
43 ac97 powerdown hits:
44 0x8000 EAPD
45 0x4000 Headphone amplifier
46 0x0300 ADC & DAC
47 0x0400 Analog Mixer powerdown (Vref on)
48 I am not sure if this is the best that we can do.
49 The remainder to be investigated are:
50 - analog mixer (vref off) 0x0800
51 - AC-link powerdown 0x1000
52 - codec internal clock 0x2000
53 */
54
55 /* set LNK_SHUTDOWN to shutdown AC link */
56 cs_writel(cs5535au, ACC_CODEC_CNTL, ACC_CODEC_CNTL_LNK_SHUTDOWN);
57
58}
59
60int snd_cs5535audio_suspend(struct pci_dev *pci, pm_message_t state)
61{
62 struct snd_card *card = pci_get_drvdata(pci);
63 struct cs5535audio *cs5535au = card->private_data;
64 int i;
65
66 snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
67 for (i = 0; i < NUM_CS5535AUDIO_DMAS; i++) {
68 struct cs5535audio_dma *dma = &cs5535au->dmas[i];
69 if (dma && dma->substream && !dma->suspended)
70 dma->saved_prd = dma->ops->read_prd(cs5535au);
71 }
72 snd_pcm_suspend_all(cs5535au->pcm);
73 snd_ac97_suspend(cs5535au->ac97);
74 /* save important regs, then disable aclink in hw */
75 snd_cs5535audio_stop_hardware(cs5535au);
76 pci_disable_device(pci);
77 pci_save_state(pci);
78
79 return 0;
80}
81
82int snd_cs5535audio_resume(struct pci_dev *pci)
83{
84 struct snd_card *card = pci_get_drvdata(pci);
85 struct cs5535audio *cs5535au = card->private_data;
86 u32 tmp;
87 int timeout;
88 int i;
89
90 pci_restore_state(pci);
91 pci_enable_device(pci);
92 pci_set_master(pci);
93
94 /* set LNK_WRM_RST to reset AC link */
95 cs_writel(cs5535au, ACC_CODEC_CNTL, ACC_CODEC_CNTL_LNK_WRM_RST);
96
97 timeout = 50;
98 do {
99 tmp = cs_readl(cs5535au, ACC_CODEC_STATUS);
100 if (tmp & PRM_RDY_STS)
101 break;
102 udelay(1);
103 } while (--timeout);
104
105 if (!timeout)
106 snd_printk(KERN_ERR "Failure getting AC Link ready\n");
107
108 /* we depend on ac97 to perform the codec power up */
109 snd_ac97_resume(cs5535au->ac97);
110 /* set up rate regs, dma. actual initiation is done in trig */
111 for (i = 0; i < NUM_CS5535AUDIO_DMAS; i++) {
112 struct cs5535audio_dma *dma = &cs5535au->dmas[i];
113 if (dma && dma->substream && dma->suspended) {
114 dma->substream->ops->prepare(dma->substream);
115 dma->ops->setup_prd(cs5535au, dma->saved_prd);
116 }
117 }
118
119 snd_power_change_state(card, SNDRV_CTL_POWER_D0);
120
121 return 0;
122}
123