diff options
author | Takashi Iwai <tiwai@suse.de> | 2007-05-24 12:46:54 -0400 |
---|---|---|
committer | Jaroslav Kysela <perex@suse.cz> | 2007-07-20 05:11:19 -0400 |
commit | 621887aee9c7b4b613c12b82b83df7e56877f303 (patch) | |
tree | fa26d2a5a584db9646ec33df6eb3965f2704bc7a /sound | |
parent | 0ba7962b9f06c02dd1af93002e8d757805d16758 (diff) |
[ALSA] Add support for Cyrix/NatSemi Geode CS5530 (VSA1)
Add support for Cyrix/NatSemi Geode SC5530 (VSA1).
The driver is snd-cs5530.
Signed-off-by Ash Willis <ashwillis@programmer.net>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Jaroslav Kysela <perex@suse.cz>
Diffstat (limited to 'sound')
-rw-r--r-- | sound/isa/sb/sb16_main.c | 10 | ||||
-rw-r--r-- | sound/isa/sb/sb_common.c | 5 | ||||
-rw-r--r-- | sound/isa/sb/sb_mixer.c | 3 | ||||
-rw-r--r-- | sound/pci/Kconfig | 10 | ||||
-rw-r--r-- | sound/pci/Makefile | 2 | ||||
-rw-r--r-- | sound/pci/cs5530.c | 306 |
6 files changed, 335 insertions, 1 deletions
diff --git a/sound/isa/sb/sb16_main.c b/sound/isa/sb/sb16_main.c index 383911b9e74d..5d4d3aafe2d5 100644 --- a/sound/isa/sb/sb16_main.c +++ b/sound/isa/sb/sb16_main.c | |||
@@ -563,6 +563,11 @@ static int snd_sb16_playback_open(struct snd_pcm_substream *substream) | |||
563 | __open_ok: | 563 | __open_ok: |
564 | if (chip->hardware == SB_HW_ALS100) | 564 | if (chip->hardware == SB_HW_ALS100) |
565 | runtime->hw.rate_max = 48000; | 565 | runtime->hw.rate_max = 48000; |
566 | if (chip->hardware == SB_HW_CS5530) { | ||
567 | runtime->hw.buffer_bytes_max = 32 * 1024; | ||
568 | runtime->hw.periods_min = 2; | ||
569 | runtime->hw.rate_min = 44100; | ||
570 | } | ||
566 | if (chip->mode & SB_RATE_LOCK) | 571 | if (chip->mode & SB_RATE_LOCK) |
567 | runtime->hw.rate_min = runtime->hw.rate_max = chip->locked_rate; | 572 | runtime->hw.rate_min = runtime->hw.rate_max = chip->locked_rate; |
568 | chip->playback_substream = substream; | 573 | chip->playback_substream = substream; |
@@ -633,6 +638,11 @@ static int snd_sb16_capture_open(struct snd_pcm_substream *substream) | |||
633 | __open_ok: | 638 | __open_ok: |
634 | if (chip->hardware == SB_HW_ALS100) | 639 | if (chip->hardware == SB_HW_ALS100) |
635 | runtime->hw.rate_max = 48000; | 640 | runtime->hw.rate_max = 48000; |
641 | if (chip->hardware == SB_HW_CS5530) { | ||
642 | runtime->hw.buffer_bytes_max = 32 * 1024; | ||
643 | runtime->hw.periods_min = 2; | ||
644 | runtime->hw.rate_min = 44100; | ||
645 | } | ||
636 | if (chip->mode & SB_RATE_LOCK) | 646 | if (chip->mode & SB_RATE_LOCK) |
637 | runtime->hw.rate_min = runtime->hw.rate_max = chip->locked_rate; | 647 | runtime->hw.rate_min = runtime->hw.rate_max = chip->locked_rate; |
638 | chip->capture_substream = substream; | 648 | chip->capture_substream = substream; |
diff --git a/sound/isa/sb/sb_common.c b/sound/isa/sb/sb_common.c index 3094f3852167..efa9d5c2558a 100644 --- a/sound/isa/sb/sb_common.c +++ b/sound/isa/sb/sb_common.c | |||
@@ -128,7 +128,7 @@ static int snd_sbdsp_probe(struct snd_sb * chip) | |||
128 | minor = version & 0xff; | 128 | minor = version & 0xff; |
129 | snd_printdd("SB [0x%lx]: DSP chip found, version = %i.%i\n", | 129 | snd_printdd("SB [0x%lx]: DSP chip found, version = %i.%i\n", |
130 | chip->port, major, minor); | 130 | chip->port, major, minor); |
131 | 131 | ||
132 | switch (chip->hardware) { | 132 | switch (chip->hardware) { |
133 | case SB_HW_AUTO: | 133 | case SB_HW_AUTO: |
134 | switch (major) { | 134 | switch (major) { |
@@ -168,6 +168,9 @@ static int snd_sbdsp_probe(struct snd_sb * chip) | |||
168 | case SB_HW_DT019X: | 168 | case SB_HW_DT019X: |
169 | str = "(DT019X/ALS007)"; | 169 | str = "(DT019X/ALS007)"; |
170 | break; | 170 | break; |
171 | case SB_HW_CS5530: | ||
172 | str = "16 (CS5530)"; | ||
173 | break; | ||
171 | default: | 174 | default: |
172 | return -ENODEV; | 175 | return -ENODEV; |
173 | } | 176 | } |
diff --git a/sound/isa/sb/sb_mixer.c b/sound/isa/sb/sb_mixer.c index 490b1ca5cf58..3d4befcff28e 100644 --- a/sound/isa/sb/sb_mixer.c +++ b/sound/isa/sb/sb_mixer.c | |||
@@ -821,6 +821,7 @@ int snd_sbmixer_new(struct snd_sb *chip) | |||
821 | break; | 821 | break; |
822 | case SB_HW_16: | 822 | case SB_HW_16: |
823 | case SB_HW_ALS100: | 823 | case SB_HW_ALS100: |
824 | case SB_HW_CS5530: | ||
824 | if ((err = snd_sbmixer_init(chip, | 825 | if ((err = snd_sbmixer_init(chip, |
825 | snd_sb16_controls, | 826 | snd_sb16_controls, |
826 | ARRAY_SIZE(snd_sb16_controls), | 827 | ARRAY_SIZE(snd_sb16_controls), |
@@ -950,6 +951,7 @@ void snd_sbmixer_suspend(struct snd_sb *chip) | |||
950 | break; | 951 | break; |
951 | case SB_HW_16: | 952 | case SB_HW_16: |
952 | case SB_HW_ALS100: | 953 | case SB_HW_ALS100: |
954 | case SB_HW_CS5530: | ||
953 | save_mixer(chip, sb16_saved_regs, ARRAY_SIZE(sb16_saved_regs)); | 955 | save_mixer(chip, sb16_saved_regs, ARRAY_SIZE(sb16_saved_regs)); |
954 | break; | 956 | break; |
955 | case SB_HW_ALS4000: | 957 | case SB_HW_ALS4000: |
@@ -975,6 +977,7 @@ void snd_sbmixer_resume(struct snd_sb *chip) | |||
975 | break; | 977 | break; |
976 | case SB_HW_16: | 978 | case SB_HW_16: |
977 | case SB_HW_ALS100: | 979 | case SB_HW_ALS100: |
980 | case SB_HW_CS5530: | ||
978 | restore_mixer(chip, sb16_saved_regs, ARRAY_SIZE(sb16_saved_regs)); | 981 | restore_mixer(chip, sb16_saved_regs, ARRAY_SIZE(sb16_saved_regs)); |
979 | break; | 982 | break; |
980 | case SB_HW_ALS4000: | 983 | case SB_HW_ALS4000: |
diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig index 61e35ecc57b8..fa17786e1587 100644 --- a/sound/pci/Kconfig +++ b/sound/pci/Kconfig | |||
@@ -215,6 +215,16 @@ config SND_CS46XX_NEW_DSP | |||
215 | 215 | ||
216 | This works better than the old code, so say Y. | 216 | This works better than the old code, so say Y. |
217 | 217 | ||
218 | config SND_CS5530 | ||
219 | tristate "CS5530 Audio" | ||
220 | depends on SND && ISA_DMA_API | ||
221 | select SND_SB16 | ||
222 | help | ||
223 | Say Y here to include support for audio on Cyrix/NatSemi CS5530 chips. | ||
224 | |||
225 | To compile this driver as a module, choose M here: the module | ||
226 | will be called snd-cs5530. | ||
227 | |||
218 | config SND_CS5535AUDIO | 228 | config SND_CS5535AUDIO |
219 | tristate "CS5535/CS5536 Audio" | 229 | tristate "CS5535/CS5536 Audio" |
220 | depends on SND && X86 && !X86_64 | 230 | depends on SND && X86 && !X86_64 |
diff --git a/sound/pci/Makefile b/sound/pci/Makefile index e06736da9ef1..cd76e0293d06 100644 --- a/sound/pci/Makefile +++ b/sound/pci/Makefile | |||
@@ -12,6 +12,7 @@ snd-azt3328-objs := azt3328.o | |||
12 | snd-bt87x-objs := bt87x.o | 12 | snd-bt87x-objs := bt87x.o |
13 | snd-cmipci-objs := cmipci.o | 13 | snd-cmipci-objs := cmipci.o |
14 | snd-cs4281-objs := cs4281.o | 14 | snd-cs4281-objs := cs4281.o |
15 | snd-cs5530-objs := cs5530.o | ||
15 | snd-ens1370-objs := ens1370.o | 16 | snd-ens1370-objs := ens1370.o |
16 | snd-ens1371-objs := ens1371.o | 17 | snd-ens1371-objs := ens1371.o |
17 | snd-es1938-objs := es1938.o | 18 | snd-es1938-objs := es1938.o |
@@ -36,6 +37,7 @@ obj-$(CONFIG_SND_AZT3328) += snd-azt3328.o | |||
36 | obj-$(CONFIG_SND_BT87X) += snd-bt87x.o | 37 | obj-$(CONFIG_SND_BT87X) += snd-bt87x.o |
37 | obj-$(CONFIG_SND_CMIPCI) += snd-cmipci.o | 38 | obj-$(CONFIG_SND_CMIPCI) += snd-cmipci.o |
38 | obj-$(CONFIG_SND_CS4281) += snd-cs4281.o | 39 | obj-$(CONFIG_SND_CS4281) += snd-cs4281.o |
40 | obj-$(CONFIG_SND_CS5530) += snd-cs5530.o | ||
39 | obj-$(CONFIG_SND_ENS1370) += snd-ens1370.o | 41 | obj-$(CONFIG_SND_ENS1370) += snd-ens1370.o |
40 | obj-$(CONFIG_SND_ENS1371) += snd-ens1371.o | 42 | obj-$(CONFIG_SND_ENS1371) += snd-ens1371.o |
41 | obj-$(CONFIG_SND_ES1938) += snd-es1938.o | 43 | obj-$(CONFIG_SND_ES1938) += snd-es1938.o |
diff --git a/sound/pci/cs5530.c b/sound/pci/cs5530.c new file mode 100644 index 000000000000..240a0a462209 --- /dev/null +++ b/sound/pci/cs5530.c | |||
@@ -0,0 +1,306 @@ | |||
1 | /* | ||
2 | * cs5530.c - Initialisation code for Cyrix/NatSemi VSA1 softaudio | ||
3 | * | ||
4 | * (C) Copyright 2007 Ash Willis <ashwillis@programmer.net> | ||
5 | * (C) Copyright 2003 Red Hat Inc <alan@redhat.com> | ||
6 | * | ||
7 | * This driver was ported (shamelessly ripped ;) from oss/kahlua.c but I did | ||
8 | * mess with it a bit. The chip seems to have to have trouble with full duplex | ||
9 | * mode. If we're recording in 8bit 8000kHz, say, and we then attempt to | ||
10 | * simultaneously play back audio at 16bit 44100kHz, the device actually plays | ||
11 | * back in the same format in which it is capturing. By forcing the chip to | ||
12 | * always play/capture in 16/44100, we can let alsa-lib convert the samples and | ||
13 | * that way we can hack up some full duplex audio. | ||
14 | * | ||
15 | * XpressAudio(tm) is used on the Cyrix MediaGX (now NatSemi Geode) systems. | ||
16 | * The older version (VSA1) provides fairly good soundblaster emulation | ||
17 | * although there are a couple of bugs: large DMA buffers break record, | ||
18 | * and the MPU event handling seems suspect. VSA2 allows the native driver | ||
19 | * to control the AC97 audio engine directly and requires a different driver. | ||
20 | * | ||
21 | * Thanks to National Semiconductor for providing the needed information | ||
22 | * on the XpressAudio(tm) internals. | ||
23 | * | ||
24 | * This program is free software; you can redistribute it and/or modify it | ||
25 | * under the terms of the GNU General Public License as published by the | ||
26 | * Free Software Foundation; either version 2, or (at your option) any | ||
27 | * later version. | ||
28 | * | ||
29 | * This program is distributed in the hope that it will be useful, but | ||
30 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
31 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
32 | * General Public License for more details. | ||
33 | * | ||
34 | * TO DO: | ||
35 | * Investigate whether we can portably support Cognac (5520) in the | ||
36 | * same manner. | ||
37 | */ | ||
38 | |||
39 | #include <sound/driver.h> | ||
40 | #include <linux/delay.h> | ||
41 | #include <linux/moduleparam.h> | ||
42 | #include <linux/pci.h> | ||
43 | #include <sound/core.h> | ||
44 | #include <sound/sb.h> | ||
45 | #include <sound/initval.h> | ||
46 | |||
47 | MODULE_AUTHOR("Ash Willis"); | ||
48 | MODULE_DESCRIPTION("CS5530 Audio"); | ||
49 | MODULE_LICENSE("GPL"); | ||
50 | |||
51 | static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; | ||
52 | static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; | ||
53 | static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; | ||
54 | |||
55 | struct snd_cs5530 { | ||
56 | struct snd_card *card; | ||
57 | struct pci_dev *pci; | ||
58 | struct snd_sb *sb; | ||
59 | unsigned long pci_base; | ||
60 | }; | ||
61 | |||
62 | static struct pci_device_id snd_cs5530_ids[] = { | ||
63 | {PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5530_AUDIO, PCI_ANY_ID, | ||
64 | PCI_ANY_ID, 0, 0}, | ||
65 | {0,} | ||
66 | }; | ||
67 | |||
68 | MODULE_DEVICE_TABLE(pci, snd_cs5530_ids); | ||
69 | |||
70 | static int snd_cs5530_free(struct snd_cs5530 *chip) | ||
71 | { | ||
72 | pci_release_regions(chip->pci); | ||
73 | pci_disable_device(chip->pci); | ||
74 | kfree(chip); | ||
75 | return 0; | ||
76 | } | ||
77 | |||
78 | static int snd_cs5530_dev_free(struct snd_device *device) | ||
79 | { | ||
80 | struct snd_cs5530 *chip = device->device_data; | ||
81 | return snd_cs5530_free(chip); | ||
82 | } | ||
83 | |||
84 | static void __devexit snd_cs5530_remove(struct pci_dev *pci) | ||
85 | { | ||
86 | snd_card_free(pci_get_drvdata(pci)); | ||
87 | pci_set_drvdata(pci, NULL); | ||
88 | } | ||
89 | |||
90 | static u8 __devinit snd_cs5530_mixer_read(unsigned long io, u8 reg) | ||
91 | { | ||
92 | outb(reg, io + 4); | ||
93 | udelay(20); | ||
94 | reg = inb(io + 5); | ||
95 | udelay(20); | ||
96 | return reg; | ||
97 | } | ||
98 | |||
99 | static int __devinit snd_cs5530_create(struct snd_card *card, | ||
100 | struct pci_dev *pci, | ||
101 | struct snd_cs5530 **rchip) | ||
102 | { | ||
103 | struct snd_cs5530 *chip; | ||
104 | unsigned long sb_base; | ||
105 | u8 irq, dma8, dma16 = 0; | ||
106 | u16 map; | ||
107 | void __iomem *mem; | ||
108 | int err; | ||
109 | |||
110 | static struct snd_device_ops ops = { | ||
111 | .dev_free = snd_cs5530_dev_free, | ||
112 | }; | ||
113 | *rchip = NULL; | ||
114 | |||
115 | err = pci_enable_device(pci); | ||
116 | if (err < 0) | ||
117 | return err; | ||
118 | |||
119 | chip = kzalloc(sizeof(*chip), GFP_KERNEL); | ||
120 | if (chip == NULL) { | ||
121 | pci_disable_device(pci); | ||
122 | return -ENOMEM; | ||
123 | } | ||
124 | |||
125 | chip->card = card; | ||
126 | chip->pci = pci; | ||
127 | |||
128 | err = pci_request_regions(pci, "CS5530"); | ||
129 | if (err < 0) { | ||
130 | kfree(chip); | ||
131 | pci_disable_device(pci); | ||
132 | return err; | ||
133 | } | ||
134 | chip->pci_base = pci_resource_start(pci, 0); | ||
135 | |||
136 | mem = ioremap_nocache(chip->pci_base, pci_resource_len(pci, 0)); | ||
137 | if (mem == NULL) { | ||
138 | kfree(chip); | ||
139 | pci_disable_device(pci); | ||
140 | return -EBUSY; | ||
141 | } | ||
142 | |||
143 | map = readw(mem + 0x18); | ||
144 | iounmap(mem); | ||
145 | |||
146 | /* Map bits | ||
147 | 0:1 * 0x20 + 0x200 = sb base | ||
148 | 2 sb enable | ||
149 | 3 adlib enable | ||
150 | 5 MPU enable 0x330 | ||
151 | 6 MPU enable 0x300 | ||
152 | |||
153 | The other bits may be used internally so must be masked */ | ||
154 | |||
155 | sb_base = 0x220 + 0x20 * (map & 3); | ||
156 | |||
157 | if (map & (1<<2)) | ||
158 | printk(KERN_INFO "CS5530: XpressAudio at 0x%lx\n", sb_base); | ||
159 | else { | ||
160 | printk(KERN_ERR "Could not find XpressAudio!\n"); | ||
161 | snd_cs5530_free(chip); | ||
162 | return -ENODEV; | ||
163 | } | ||
164 | |||
165 | if (map & (1<<5)) | ||
166 | printk(KERN_INFO "CS5530: MPU at 0x300\n"); | ||
167 | else if (map & (1<<6)) | ||
168 | printk(KERN_INFO "CS5530: MPU at 0x330\n"); | ||
169 | |||
170 | irq = snd_cs5530_mixer_read(sb_base, 0x80) & 0x0F; | ||
171 | dma8 = snd_cs5530_mixer_read(sb_base, 0x81); | ||
172 | |||
173 | if (dma8 & 0x20) | ||
174 | dma16 = 5; | ||
175 | else if (dma8 & 0x40) | ||
176 | dma16 = 6; | ||
177 | else if (dma8 & 0x80) | ||
178 | dma16 = 7; | ||
179 | else { | ||
180 | printk(KERN_ERR "CS5530: No 16bit DMA enabled\n"); | ||
181 | snd_cs5530_free(chip); | ||
182 | return -ENODEV; | ||
183 | } | ||
184 | |||
185 | if (dma8 & 0x01) | ||
186 | dma8 = 0; | ||
187 | else if (dma8 & 02) | ||
188 | dma8 = 1; | ||
189 | else if (dma8 & 0x08) | ||
190 | dma8 = 3; | ||
191 | else { | ||
192 | printk(KERN_ERR "CS5530: No 8bit DMA enabled\n"); | ||
193 | snd_cs5530_free(chip); | ||
194 | return -ENODEV; | ||
195 | } | ||
196 | |||
197 | if (irq & 1) | ||
198 | irq = 9; | ||
199 | else if (irq & 2) | ||
200 | irq = 5; | ||
201 | else if (irq & 4) | ||
202 | irq = 7; | ||
203 | else if (irq & 8) | ||
204 | irq = 10; | ||
205 | else { | ||
206 | printk(KERN_ERR "CS5530: SoundBlaster IRQ not set\n"); | ||
207 | snd_cs5530_free(chip); | ||
208 | return -ENODEV; | ||
209 | } | ||
210 | |||
211 | printk(KERN_INFO "CS5530: IRQ: %d DMA8: %d DMA16: %d\n", irq, dma8, | ||
212 | dma16); | ||
213 | |||
214 | err = snd_sbdsp_create(card, sb_base, irq, snd_sb16dsp_interrupt, dma8, | ||
215 | dma16, SB_HW_CS5530, &chip->sb); | ||
216 | if (err < 0) { | ||
217 | printk(KERN_ERR "CS5530: Could not create SoundBlaster\n"); | ||
218 | snd_cs5530_free(chip); | ||
219 | return err; | ||
220 | } | ||
221 | |||
222 | err = snd_sb16dsp_pcm(chip->sb, 0, &chip->sb->pcm); | ||
223 | if (err < 0) { | ||
224 | printk(KERN_ERR "CS5530: Could not create PCM\n"); | ||
225 | snd_cs5530_free(chip); | ||
226 | return err; | ||
227 | } | ||
228 | |||
229 | err = snd_sbmixer_new(chip->sb); | ||
230 | if (err < 0) { | ||
231 | printk(KERN_ERR "CS5530: Could not create Mixer\n"); | ||
232 | snd_cs5530_free(chip); | ||
233 | return err; | ||
234 | } | ||
235 | |||
236 | err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops); | ||
237 | if (err < 0) { | ||
238 | snd_cs5530_free(chip); | ||
239 | return err; | ||
240 | } | ||
241 | |||
242 | snd_card_set_dev(card, &pci->dev); | ||
243 | *rchip = chip; | ||
244 | return 0; | ||
245 | } | ||
246 | |||
247 | static int __devinit snd_cs5530_probe(struct pci_dev *pci, | ||
248 | const struct pci_device_id *pci_id) | ||
249 | { | ||
250 | static int dev; | ||
251 | struct snd_card *card; | ||
252 | struct snd_cs5530 *chip = NULL; | ||
253 | int err; | ||
254 | |||
255 | if (dev >= SNDRV_CARDS) | ||
256 | return -ENODEV; | ||
257 | if (!enable[dev]) { | ||
258 | dev++; | ||
259 | return -ENOENT; | ||
260 | } | ||
261 | |||
262 | card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); | ||
263 | |||
264 | if (card == NULL) | ||
265 | return -ENOMEM; | ||
266 | |||
267 | err = snd_cs5530_create(card, pci, &chip); | ||
268 | if (err < 0) { | ||
269 | snd_card_free(card); | ||
270 | return err; | ||
271 | } | ||
272 | |||
273 | strcpy(card->driver, "CS5530"); | ||
274 | strcpy(card->shortname, "CS5530 Audio"); | ||
275 | sprintf(card->longname, "%s at 0x%lx", card->shortname, chip->pci_base); | ||
276 | |||
277 | err = snd_card_register(card); | ||
278 | if (err < 0) { | ||
279 | snd_card_free(card); | ||
280 | return err; | ||
281 | } | ||
282 | pci_set_drvdata(pci, card); | ||
283 | dev++; | ||
284 | return 0; | ||
285 | } | ||
286 | |||
287 | static struct pci_driver driver = { | ||
288 | .name = "CS5530_Audio", | ||
289 | .id_table = snd_cs5530_ids, | ||
290 | .probe = snd_cs5530_probe, | ||
291 | .remove = __devexit_p(snd_cs5530_remove), | ||
292 | }; | ||
293 | |||
294 | static int __init alsa_card_cs5530_init(void) | ||
295 | { | ||
296 | return pci_register_driver(&driver); | ||
297 | } | ||
298 | |||
299 | static void __exit alsa_card_cs5530_exit(void) | ||
300 | { | ||
301 | pci_unregister_driver(&driver); | ||
302 | } | ||
303 | |||
304 | module_init(alsa_card_cs5530_init) | ||
305 | module_exit(alsa_card_cs5530_exit) | ||
306 | |||