Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/sound/pci/cs5530.c
10814 views
1
/*
2
* cs5530.c - Initialisation code for Cyrix/NatSemi VSA1 softaudio
3
*
4
* (C) Copyright 2007 Ash Willis <[email protected]>
5
* (C) Copyright 2003 Red Hat Inc <[email protected]>
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 <linux/delay.h>
40
#include <linux/moduleparam.h>
41
#include <linux/pci.h>
42
#include <linux/slab.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 DEFINE_PCI_DEVICE_TABLE(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 = pci_ioremap_bar(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
err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
263
264
if (err < 0)
265
return err;
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
307
308