1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
4*4882a593Smuzhiyun * Uros Bizjak <uros@kss-loka.si>
5*4882a593Smuzhiyun *
6*4882a593Smuzhiyun * Lowlevel routines for control of Sound Blaster cards
7*4882a593Smuzhiyun */
8*4882a593Smuzhiyun
9*4882a593Smuzhiyun #include <linux/delay.h>
10*4882a593Smuzhiyun #include <linux/init.h>
11*4882a593Smuzhiyun #include <linux/interrupt.h>
12*4882a593Smuzhiyun #include <linux/slab.h>
13*4882a593Smuzhiyun #include <linux/ioport.h>
14*4882a593Smuzhiyun #include <linux/module.h>
15*4882a593Smuzhiyun #include <linux/io.h>
16*4882a593Smuzhiyun #include <sound/core.h>
17*4882a593Smuzhiyun #include <sound/sb.h>
18*4882a593Smuzhiyun #include <sound/initval.h>
19*4882a593Smuzhiyun
20*4882a593Smuzhiyun #include <asm/dma.h>
21*4882a593Smuzhiyun
22*4882a593Smuzhiyun MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
23*4882a593Smuzhiyun MODULE_DESCRIPTION("ALSA lowlevel driver for Sound Blaster cards");
24*4882a593Smuzhiyun MODULE_LICENSE("GPL");
25*4882a593Smuzhiyun
26*4882a593Smuzhiyun #define BUSY_LOOPS 100000
27*4882a593Smuzhiyun
28*4882a593Smuzhiyun #undef IO_DEBUG
29*4882a593Smuzhiyun
snd_sbdsp_command(struct snd_sb * chip,unsigned char val)30*4882a593Smuzhiyun int snd_sbdsp_command(struct snd_sb *chip, unsigned char val)
31*4882a593Smuzhiyun {
32*4882a593Smuzhiyun int i;
33*4882a593Smuzhiyun #ifdef IO_DEBUG
34*4882a593Smuzhiyun snd_printk(KERN_DEBUG "command 0x%x\n", val);
35*4882a593Smuzhiyun #endif
36*4882a593Smuzhiyun for (i = BUSY_LOOPS; i; i--)
37*4882a593Smuzhiyun if ((inb(SBP(chip, STATUS)) & 0x80) == 0) {
38*4882a593Smuzhiyun outb(val, SBP(chip, COMMAND));
39*4882a593Smuzhiyun return 1;
40*4882a593Smuzhiyun }
41*4882a593Smuzhiyun snd_printd("%s [0x%lx]: timeout (0x%x)\n", __func__, chip->port, val);
42*4882a593Smuzhiyun return 0;
43*4882a593Smuzhiyun }
44*4882a593Smuzhiyun
snd_sbdsp_get_byte(struct snd_sb * chip)45*4882a593Smuzhiyun int snd_sbdsp_get_byte(struct snd_sb *chip)
46*4882a593Smuzhiyun {
47*4882a593Smuzhiyun int val;
48*4882a593Smuzhiyun int i;
49*4882a593Smuzhiyun for (i = BUSY_LOOPS; i; i--) {
50*4882a593Smuzhiyun if (inb(SBP(chip, DATA_AVAIL)) & 0x80) {
51*4882a593Smuzhiyun val = inb(SBP(chip, READ));
52*4882a593Smuzhiyun #ifdef IO_DEBUG
53*4882a593Smuzhiyun snd_printk(KERN_DEBUG "get_byte 0x%x\n", val);
54*4882a593Smuzhiyun #endif
55*4882a593Smuzhiyun return val;
56*4882a593Smuzhiyun }
57*4882a593Smuzhiyun }
58*4882a593Smuzhiyun snd_printd("%s [0x%lx]: timeout\n", __func__, chip->port);
59*4882a593Smuzhiyun return -ENODEV;
60*4882a593Smuzhiyun }
61*4882a593Smuzhiyun
snd_sbdsp_reset(struct snd_sb * chip)62*4882a593Smuzhiyun int snd_sbdsp_reset(struct snd_sb *chip)
63*4882a593Smuzhiyun {
64*4882a593Smuzhiyun int i;
65*4882a593Smuzhiyun
66*4882a593Smuzhiyun outb(1, SBP(chip, RESET));
67*4882a593Smuzhiyun udelay(10);
68*4882a593Smuzhiyun outb(0, SBP(chip, RESET));
69*4882a593Smuzhiyun udelay(30);
70*4882a593Smuzhiyun for (i = BUSY_LOOPS; i; i--)
71*4882a593Smuzhiyun if (inb(SBP(chip, DATA_AVAIL)) & 0x80) {
72*4882a593Smuzhiyun if (inb(SBP(chip, READ)) == 0xaa)
73*4882a593Smuzhiyun return 0;
74*4882a593Smuzhiyun else
75*4882a593Smuzhiyun break;
76*4882a593Smuzhiyun }
77*4882a593Smuzhiyun snd_printdd("%s [0x%lx] failed...\n", __func__, chip->port);
78*4882a593Smuzhiyun return -ENODEV;
79*4882a593Smuzhiyun }
80*4882a593Smuzhiyun
snd_sbdsp_version(struct snd_sb * chip)81*4882a593Smuzhiyun static int snd_sbdsp_version(struct snd_sb * chip)
82*4882a593Smuzhiyun {
83*4882a593Smuzhiyun unsigned int result;
84*4882a593Smuzhiyun
85*4882a593Smuzhiyun snd_sbdsp_command(chip, SB_DSP_GET_VERSION);
86*4882a593Smuzhiyun result = (short) snd_sbdsp_get_byte(chip) << 8;
87*4882a593Smuzhiyun result |= (short) snd_sbdsp_get_byte(chip);
88*4882a593Smuzhiyun return result;
89*4882a593Smuzhiyun }
90*4882a593Smuzhiyun
snd_sbdsp_probe(struct snd_sb * chip)91*4882a593Smuzhiyun static int snd_sbdsp_probe(struct snd_sb * chip)
92*4882a593Smuzhiyun {
93*4882a593Smuzhiyun int version;
94*4882a593Smuzhiyun int major, minor;
95*4882a593Smuzhiyun char *str;
96*4882a593Smuzhiyun unsigned long flags;
97*4882a593Smuzhiyun
98*4882a593Smuzhiyun /*
99*4882a593Smuzhiyun * initialization sequence
100*4882a593Smuzhiyun */
101*4882a593Smuzhiyun
102*4882a593Smuzhiyun spin_lock_irqsave(&chip->reg_lock, flags);
103*4882a593Smuzhiyun if (snd_sbdsp_reset(chip) < 0) {
104*4882a593Smuzhiyun spin_unlock_irqrestore(&chip->reg_lock, flags);
105*4882a593Smuzhiyun return -ENODEV;
106*4882a593Smuzhiyun }
107*4882a593Smuzhiyun version = snd_sbdsp_version(chip);
108*4882a593Smuzhiyun if (version < 0) {
109*4882a593Smuzhiyun spin_unlock_irqrestore(&chip->reg_lock, flags);
110*4882a593Smuzhiyun return -ENODEV;
111*4882a593Smuzhiyun }
112*4882a593Smuzhiyun spin_unlock_irqrestore(&chip->reg_lock, flags);
113*4882a593Smuzhiyun major = version >> 8;
114*4882a593Smuzhiyun minor = version & 0xff;
115*4882a593Smuzhiyun snd_printdd("SB [0x%lx]: DSP chip found, version = %i.%i\n",
116*4882a593Smuzhiyun chip->port, major, minor);
117*4882a593Smuzhiyun
118*4882a593Smuzhiyun switch (chip->hardware) {
119*4882a593Smuzhiyun case SB_HW_AUTO:
120*4882a593Smuzhiyun switch (major) {
121*4882a593Smuzhiyun case 1:
122*4882a593Smuzhiyun chip->hardware = SB_HW_10;
123*4882a593Smuzhiyun str = "1.0";
124*4882a593Smuzhiyun break;
125*4882a593Smuzhiyun case 2:
126*4882a593Smuzhiyun if (minor) {
127*4882a593Smuzhiyun chip->hardware = SB_HW_201;
128*4882a593Smuzhiyun str = "2.01+";
129*4882a593Smuzhiyun } else {
130*4882a593Smuzhiyun chip->hardware = SB_HW_20;
131*4882a593Smuzhiyun str = "2.0";
132*4882a593Smuzhiyun }
133*4882a593Smuzhiyun break;
134*4882a593Smuzhiyun case 3:
135*4882a593Smuzhiyun chip->hardware = SB_HW_PRO;
136*4882a593Smuzhiyun str = "Pro";
137*4882a593Smuzhiyun break;
138*4882a593Smuzhiyun case 4:
139*4882a593Smuzhiyun chip->hardware = SB_HW_16;
140*4882a593Smuzhiyun str = "16";
141*4882a593Smuzhiyun break;
142*4882a593Smuzhiyun default:
143*4882a593Smuzhiyun snd_printk(KERN_INFO "SB [0x%lx]: unknown DSP chip version %i.%i\n",
144*4882a593Smuzhiyun chip->port, major, minor);
145*4882a593Smuzhiyun return -ENODEV;
146*4882a593Smuzhiyun }
147*4882a593Smuzhiyun break;
148*4882a593Smuzhiyun case SB_HW_ALS100:
149*4882a593Smuzhiyun str = "16 (ALS-100)";
150*4882a593Smuzhiyun break;
151*4882a593Smuzhiyun case SB_HW_ALS4000:
152*4882a593Smuzhiyun str = "16 (ALS-4000)";
153*4882a593Smuzhiyun break;
154*4882a593Smuzhiyun case SB_HW_DT019X:
155*4882a593Smuzhiyun str = "(DT019X/ALS007)";
156*4882a593Smuzhiyun break;
157*4882a593Smuzhiyun case SB_HW_CS5530:
158*4882a593Smuzhiyun str = "16 (CS5530)";
159*4882a593Smuzhiyun break;
160*4882a593Smuzhiyun case SB_HW_JAZZ16:
161*4882a593Smuzhiyun str = "Pro (Jazz16)";
162*4882a593Smuzhiyun break;
163*4882a593Smuzhiyun default:
164*4882a593Smuzhiyun return -ENODEV;
165*4882a593Smuzhiyun }
166*4882a593Smuzhiyun sprintf(chip->name, "Sound Blaster %s", str);
167*4882a593Smuzhiyun chip->version = (major << 8) | minor;
168*4882a593Smuzhiyun return 0;
169*4882a593Smuzhiyun }
170*4882a593Smuzhiyun
snd_sbdsp_free(struct snd_sb * chip)171*4882a593Smuzhiyun static int snd_sbdsp_free(struct snd_sb *chip)
172*4882a593Smuzhiyun {
173*4882a593Smuzhiyun release_and_free_resource(chip->res_port);
174*4882a593Smuzhiyun if (chip->irq >= 0)
175*4882a593Smuzhiyun free_irq(chip->irq, (void *) chip);
176*4882a593Smuzhiyun #ifdef CONFIG_ISA
177*4882a593Smuzhiyun if (chip->dma8 >= 0) {
178*4882a593Smuzhiyun disable_dma(chip->dma8);
179*4882a593Smuzhiyun free_dma(chip->dma8);
180*4882a593Smuzhiyun }
181*4882a593Smuzhiyun if (chip->dma16 >= 0 && chip->dma16 != chip->dma8) {
182*4882a593Smuzhiyun disable_dma(chip->dma16);
183*4882a593Smuzhiyun free_dma(chip->dma16);
184*4882a593Smuzhiyun }
185*4882a593Smuzhiyun #endif
186*4882a593Smuzhiyun kfree(chip);
187*4882a593Smuzhiyun return 0;
188*4882a593Smuzhiyun }
189*4882a593Smuzhiyun
snd_sbdsp_dev_free(struct snd_device * device)190*4882a593Smuzhiyun static int snd_sbdsp_dev_free(struct snd_device *device)
191*4882a593Smuzhiyun {
192*4882a593Smuzhiyun struct snd_sb *chip = device->device_data;
193*4882a593Smuzhiyun return snd_sbdsp_free(chip);
194*4882a593Smuzhiyun }
195*4882a593Smuzhiyun
snd_sbdsp_create(struct snd_card * card,unsigned long port,int irq,irq_handler_t irq_handler,int dma8,int dma16,unsigned short hardware,struct snd_sb ** r_chip)196*4882a593Smuzhiyun int snd_sbdsp_create(struct snd_card *card,
197*4882a593Smuzhiyun unsigned long port,
198*4882a593Smuzhiyun int irq,
199*4882a593Smuzhiyun irq_handler_t irq_handler,
200*4882a593Smuzhiyun int dma8,
201*4882a593Smuzhiyun int dma16,
202*4882a593Smuzhiyun unsigned short hardware,
203*4882a593Smuzhiyun struct snd_sb **r_chip)
204*4882a593Smuzhiyun {
205*4882a593Smuzhiyun struct snd_sb *chip;
206*4882a593Smuzhiyun int err;
207*4882a593Smuzhiyun static const struct snd_device_ops ops = {
208*4882a593Smuzhiyun .dev_free = snd_sbdsp_dev_free,
209*4882a593Smuzhiyun };
210*4882a593Smuzhiyun
211*4882a593Smuzhiyun if (snd_BUG_ON(!r_chip))
212*4882a593Smuzhiyun return -EINVAL;
213*4882a593Smuzhiyun *r_chip = NULL;
214*4882a593Smuzhiyun chip = kzalloc(sizeof(*chip), GFP_KERNEL);
215*4882a593Smuzhiyun if (chip == NULL)
216*4882a593Smuzhiyun return -ENOMEM;
217*4882a593Smuzhiyun spin_lock_init(&chip->reg_lock);
218*4882a593Smuzhiyun spin_lock_init(&chip->open_lock);
219*4882a593Smuzhiyun spin_lock_init(&chip->midi_input_lock);
220*4882a593Smuzhiyun spin_lock_init(&chip->mixer_lock);
221*4882a593Smuzhiyun chip->irq = -1;
222*4882a593Smuzhiyun chip->dma8 = -1;
223*4882a593Smuzhiyun chip->dma16 = -1;
224*4882a593Smuzhiyun chip->port = port;
225*4882a593Smuzhiyun
226*4882a593Smuzhiyun if (request_irq(irq, irq_handler,
227*4882a593Smuzhiyun (hardware == SB_HW_ALS4000 ||
228*4882a593Smuzhiyun hardware == SB_HW_CS5530) ?
229*4882a593Smuzhiyun IRQF_SHARED : 0,
230*4882a593Smuzhiyun "SoundBlaster", (void *) chip)) {
231*4882a593Smuzhiyun snd_printk(KERN_ERR "sb: can't grab irq %d\n", irq);
232*4882a593Smuzhiyun snd_sbdsp_free(chip);
233*4882a593Smuzhiyun return -EBUSY;
234*4882a593Smuzhiyun }
235*4882a593Smuzhiyun chip->irq = irq;
236*4882a593Smuzhiyun card->sync_irq = chip->irq;
237*4882a593Smuzhiyun
238*4882a593Smuzhiyun if (hardware == SB_HW_ALS4000)
239*4882a593Smuzhiyun goto __skip_allocation;
240*4882a593Smuzhiyun
241*4882a593Smuzhiyun if ((chip->res_port = request_region(port, 16, "SoundBlaster")) == NULL) {
242*4882a593Smuzhiyun snd_printk(KERN_ERR "sb: can't grab port 0x%lx\n", port);
243*4882a593Smuzhiyun snd_sbdsp_free(chip);
244*4882a593Smuzhiyun return -EBUSY;
245*4882a593Smuzhiyun }
246*4882a593Smuzhiyun
247*4882a593Smuzhiyun #ifdef CONFIG_ISA
248*4882a593Smuzhiyun if (dma8 >= 0 && request_dma(dma8, "SoundBlaster - 8bit")) {
249*4882a593Smuzhiyun snd_printk(KERN_ERR "sb: can't grab DMA8 %d\n", dma8);
250*4882a593Smuzhiyun snd_sbdsp_free(chip);
251*4882a593Smuzhiyun return -EBUSY;
252*4882a593Smuzhiyun }
253*4882a593Smuzhiyun chip->dma8 = dma8;
254*4882a593Smuzhiyun if (dma16 >= 0) {
255*4882a593Smuzhiyun if (hardware != SB_HW_ALS100 && (dma16 < 5 || dma16 > 7)) {
256*4882a593Smuzhiyun /* no duplex */
257*4882a593Smuzhiyun dma16 = -1;
258*4882a593Smuzhiyun } else if (request_dma(dma16, "SoundBlaster - 16bit")) {
259*4882a593Smuzhiyun snd_printk(KERN_ERR "sb: can't grab DMA16 %d\n", dma16);
260*4882a593Smuzhiyun snd_sbdsp_free(chip);
261*4882a593Smuzhiyun return -EBUSY;
262*4882a593Smuzhiyun }
263*4882a593Smuzhiyun }
264*4882a593Smuzhiyun chip->dma16 = dma16;
265*4882a593Smuzhiyun #endif
266*4882a593Smuzhiyun
267*4882a593Smuzhiyun __skip_allocation:
268*4882a593Smuzhiyun chip->card = card;
269*4882a593Smuzhiyun chip->hardware = hardware;
270*4882a593Smuzhiyun if ((err = snd_sbdsp_probe(chip)) < 0) {
271*4882a593Smuzhiyun snd_sbdsp_free(chip);
272*4882a593Smuzhiyun return err;
273*4882a593Smuzhiyun }
274*4882a593Smuzhiyun if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
275*4882a593Smuzhiyun snd_sbdsp_free(chip);
276*4882a593Smuzhiyun return err;
277*4882a593Smuzhiyun }
278*4882a593Smuzhiyun *r_chip = chip;
279*4882a593Smuzhiyun return 0;
280*4882a593Smuzhiyun }
281*4882a593Smuzhiyun
282*4882a593Smuzhiyun EXPORT_SYMBOL(snd_sbdsp_command);
283*4882a593Smuzhiyun EXPORT_SYMBOL(snd_sbdsp_get_byte);
284*4882a593Smuzhiyun EXPORT_SYMBOL(snd_sbdsp_reset);
285*4882a593Smuzhiyun EXPORT_SYMBOL(snd_sbdsp_create);
286*4882a593Smuzhiyun /* sb_mixer.c */
287*4882a593Smuzhiyun EXPORT_SYMBOL(snd_sbmixer_write);
288*4882a593Smuzhiyun EXPORT_SYMBOL(snd_sbmixer_read);
289*4882a593Smuzhiyun EXPORT_SYMBOL(snd_sbmixer_new);
290*4882a593Smuzhiyun EXPORT_SYMBOL(snd_sbmixer_add_ctl);
291*4882a593Smuzhiyun #ifdef CONFIG_PM
292*4882a593Smuzhiyun EXPORT_SYMBOL(snd_sbmixer_suspend);
293*4882a593Smuzhiyun EXPORT_SYMBOL(snd_sbmixer_resume);
294*4882a593Smuzhiyun #endif
295