aboutsummaryrefslogtreecommitdiffstats
path: root/sound/pci
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
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')
-rw-r--r--sound/pci/Kconfig9
-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
6 files changed, 191 insertions, 8 deletions
diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig
index a2081803a827..d37346b12dc0 100644
--- a/sound/pci/Kconfig
+++ b/sound/pci/Kconfig
@@ -216,14 +216,19 @@ config SND_CS46XX_NEW_DSP
216 This works better than the old code, so say Y. 216 This works better than the old code, so say Y.
217 217
218config SND_CS5535AUDIO 218config SND_CS5535AUDIO
219 tristate "CS5535 Audio" 219 tristate "CS5535/CS5536 Audio"
220 depends on SND && X86 && !X86_64 220 depends on SND && X86 && !X86_64
221 select SND_PCM 221 select SND_PCM
222 select SND_AC97_CODEC 222 select SND_AC97_CODEC
223 help 223 help
224 Say Y here to include support for audio on CS5535 chips. It is 224 Say Y here to include support for audio on CS5535 chips. It is
225 referred to as NS CS5535 IO or AMD CS5535 IO companion in 225 referred to as NS CS5535 IO or AMD CS5535 IO companion in
226 various literature. 226 various literature. This driver also supports the CS5536 audio
227 device. However, for both chips, on certain boards, you may
228 need to use ac97_quirk=hp_only if your board has physically
229 mapped headphone out to master output. If that works for you,
230 send lspci -vvv output to the mailing list so that your board
231 can be identified in the quirks list.
227 232
228 To compile this driver as a module, choose M here: the module 233 To compile this driver as a module, choose M here: the module
229 will be called snd-cs5535audio. 234 will be called snd-cs5535audio.
diff --git a/sound/pci/cs5535audio/Makefile b/sound/pci/cs5535audio/Makefile
index 08d8ee6547d3..2911a8adc1f2 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 2c1213a35dcc..41f02f05dfdc 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 5e55a1a1ed65..4fd1f31a6cf9 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 60bb82b2ff47..f0a48693d687 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 000000000000..aad0e69db9c1
--- /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