1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /********************************************************************* |
3 | * |
4 | * Linux multisound pinnacle/fiji driver for ALSA. |
5 | * |
6 | * 2002/06/30 Karsten Wiese: |
7 | * for now this is only used to build a pinnacle / fiji driver. |
8 | * the OSS parent of this code is designed to also support |
9 | * the multisound classic via the file msnd_classic.c. |
10 | * to make it easier for some brave heart to implemt classic |
11 | * support in alsa, i left all the MSND_CLASSIC tokens in this file. |
12 | * but for now this untested & undone. |
13 | * |
14 | * ripped from linux kernel 2.4.18 by Karsten Wiese. |
15 | * |
16 | * the following is a copy of the 2.4.18 OSS FREE file-heading comment: |
17 | * |
18 | * Turtle Beach MultiSound Sound Card Driver for Linux |
19 | * msnd_pinnacle.c / msnd_classic.c |
20 | * |
21 | * -- If MSND_CLASSIC is defined: |
22 | * |
23 | * -> driver for Turtle Beach Classic/Monterey/Tahiti |
24 | * |
25 | * -- Else |
26 | * |
27 | * -> driver for Turtle Beach Pinnacle/Fiji |
28 | * |
29 | * 12-3-2000 Modified IO port validation Steve Sycamore |
30 | * |
31 | * Copyright (C) 1998 Andrew Veliath |
32 | * |
33 | ********************************************************************/ |
34 | |
35 | #include <linux/kernel.h> |
36 | #include <linux/module.h> |
37 | #include <linux/interrupt.h> |
38 | #include <linux/types.h> |
39 | #include <linux/delay.h> |
40 | #include <linux/ioport.h> |
41 | #include <linux/firmware.h> |
42 | #include <linux/isa.h> |
43 | #include <linux/isapnp.h> |
44 | #include <linux/irq.h> |
45 | #include <linux/io.h> |
46 | |
47 | #include <sound/core.h> |
48 | #include <sound/initval.h> |
49 | #include <sound/asound.h> |
50 | #include <sound/pcm.h> |
51 | #include <sound/mpu401.h> |
52 | |
53 | #ifdef MSND_CLASSIC |
54 | # ifndef __alpha__ |
55 | # define SLOWIO |
56 | # endif |
57 | #endif |
58 | #include "msnd.h" |
59 | #ifdef MSND_CLASSIC |
60 | # include "msnd_classic.h" |
61 | # define LOGNAME "msnd_classic" |
62 | # define DEV_NAME "msnd-classic" |
63 | #else |
64 | # include "msnd_pinnacle.h" |
65 | # define LOGNAME "snd_msnd_pinnacle" |
66 | # define DEV_NAME "msnd-pinnacle" |
67 | #endif |
68 | |
69 | static void set_default_audio_parameters(struct snd_msnd *chip) |
70 | { |
71 | chip->play_sample_size = snd_pcm_format_width(DEFSAMPLESIZE); |
72 | chip->play_sample_rate = DEFSAMPLERATE; |
73 | chip->play_channels = DEFCHANNELS; |
74 | chip->capture_sample_size = snd_pcm_format_width(DEFSAMPLESIZE); |
75 | chip->capture_sample_rate = DEFSAMPLERATE; |
76 | chip->capture_channels = DEFCHANNELS; |
77 | } |
78 | |
79 | static void snd_msnd_eval_dsp_msg(struct snd_msnd *chip, u16 wMessage) |
80 | { |
81 | switch (HIBYTE(wMessage)) { |
82 | case HIMT_PLAY_DONE: { |
83 | if (chip->banksPlayed < 3) |
84 | snd_printdd("%08X: HIMT_PLAY_DONE: %i\n" , |
85 | (unsigned)jiffies, LOBYTE(wMessage)); |
86 | |
87 | if (chip->last_playbank == LOBYTE(wMessage)) { |
88 | snd_printdd("chip.last_playbank == LOBYTE(wMessage)\n" ); |
89 | break; |
90 | } |
91 | chip->banksPlayed++; |
92 | |
93 | if (test_bit(F_WRITING, &chip->flags)) |
94 | snd_msnd_DAPQ(chip, start: 0); |
95 | |
96 | chip->last_playbank = LOBYTE(wMessage); |
97 | chip->playDMAPos += chip->play_period_bytes; |
98 | if (chip->playDMAPos > chip->playLimit) |
99 | chip->playDMAPos = 0; |
100 | snd_pcm_period_elapsed(substream: chip->playback_substream); |
101 | |
102 | break; |
103 | } |
104 | case HIMT_RECORD_DONE: |
105 | if (chip->last_recbank == LOBYTE(wMessage)) |
106 | break; |
107 | chip->last_recbank = LOBYTE(wMessage); |
108 | chip->captureDMAPos += chip->capturePeriodBytes; |
109 | if (chip->captureDMAPos > (chip->captureLimit)) |
110 | chip->captureDMAPos = 0; |
111 | |
112 | if (test_bit(F_READING, &chip->flags)) |
113 | snd_msnd_DARQ(chip, start: chip->last_recbank); |
114 | |
115 | snd_pcm_period_elapsed(substream: chip->capture_substream); |
116 | break; |
117 | |
118 | case HIMT_DSP: |
119 | switch (LOBYTE(wMessage)) { |
120 | #ifndef MSND_CLASSIC |
121 | case HIDSP_PLAY_UNDER: |
122 | #endif |
123 | case HIDSP_INT_PLAY_UNDER: |
124 | snd_printd(KERN_WARNING LOGNAME ": Play underflow %i\n" , |
125 | chip->banksPlayed); |
126 | if (chip->banksPlayed > 2) |
127 | clear_bit(F_WRITING, addr: &chip->flags); |
128 | break; |
129 | |
130 | case HIDSP_INT_RECORD_OVER: |
131 | snd_printd(KERN_WARNING LOGNAME ": Record overflow\n" ); |
132 | clear_bit(F_READING, addr: &chip->flags); |
133 | break; |
134 | |
135 | default: |
136 | snd_printd(KERN_WARNING LOGNAME |
137 | ": DSP message %d 0x%02x\n" , |
138 | LOBYTE(wMessage), LOBYTE(wMessage)); |
139 | break; |
140 | } |
141 | break; |
142 | |
143 | case HIMT_MIDI_IN_UCHAR: |
144 | if (chip->msndmidi_mpu) |
145 | snd_msndmidi_input_read(mpu: chip->msndmidi_mpu); |
146 | break; |
147 | |
148 | default: |
149 | snd_printd(KERN_WARNING LOGNAME ": HIMT message %d 0x%02x\n" , |
150 | HIBYTE(wMessage), HIBYTE(wMessage)); |
151 | break; |
152 | } |
153 | } |
154 | |
155 | static irqreturn_t snd_msnd_interrupt(int irq, void *dev_id) |
156 | { |
157 | struct snd_msnd *chip = dev_id; |
158 | void __iomem *pwDSPQData = chip->mappedbase + DSPQ_DATA_BUFF; |
159 | u16 head, tail, size; |
160 | |
161 | /* Send ack to DSP */ |
162 | /* inb(chip->io + HP_RXL); */ |
163 | |
164 | /* Evaluate queued DSP messages */ |
165 | head = readw(addr: chip->DSPQ + JQS_wHead); |
166 | tail = readw(addr: chip->DSPQ + JQS_wTail); |
167 | size = readw(addr: chip->DSPQ + JQS_wSize); |
168 | if (head > size || tail > size) |
169 | goto out; |
170 | while (head != tail) { |
171 | snd_msnd_eval_dsp_msg(chip, readw(addr: pwDSPQData + 2 * head)); |
172 | if (++head > size) |
173 | head = 0; |
174 | writew(val: head, addr: chip->DSPQ + JQS_wHead); |
175 | } |
176 | out: |
177 | /* Send ack to DSP */ |
178 | inb(port: chip->io + HP_RXL); |
179 | return IRQ_HANDLED; |
180 | } |
181 | |
182 | |
183 | static int snd_msnd_reset_dsp(long io, unsigned char *info) |
184 | { |
185 | int timeout = 100; |
186 | |
187 | outb(HPDSPRESET_ON, port: io + HP_DSPR); |
188 | msleep(msecs: 1); |
189 | #ifndef MSND_CLASSIC |
190 | if (info) |
191 | *info = inb(io + HP_INFO); |
192 | #endif |
193 | outb(HPDSPRESET_OFF, port: io + HP_DSPR); |
194 | msleep(msecs: 1); |
195 | while (timeout-- > 0) { |
196 | if (inb(port: io + HP_CVR) == HP_CVR_DEF) |
197 | return 0; |
198 | msleep(msecs: 1); |
199 | } |
200 | snd_printk(KERN_ERR LOGNAME ": Cannot reset DSP\n" ); |
201 | |
202 | return -EIO; |
203 | } |
204 | |
205 | static int snd_msnd_probe(struct snd_card *card) |
206 | { |
207 | struct snd_msnd *chip = card->private_data; |
208 | unsigned char info; |
209 | #ifndef MSND_CLASSIC |
210 | char *xv, *rev = NULL; |
211 | char *pin = "TB Pinnacle" , *fiji = "TB Fiji" ; |
212 | char *pinfiji = "TB Pinnacle/Fiji" ; |
213 | #endif |
214 | |
215 | if (!request_region(chip->io, DSP_NUMIO, "probing" )) { |
216 | snd_printk(KERN_ERR LOGNAME ": I/O port conflict\n" ); |
217 | return -ENODEV; |
218 | } |
219 | |
220 | if (snd_msnd_reset_dsp(io: chip->io, info: &info) < 0) { |
221 | release_region(chip->io, DSP_NUMIO); |
222 | return -ENODEV; |
223 | } |
224 | |
225 | #ifdef MSND_CLASSIC |
226 | strcpy(p: card->shortname, q: "Classic/Tahiti/Monterey" ); |
227 | strcpy(p: card->longname, q: "Turtle Beach Multisound" ); |
228 | printk(KERN_INFO LOGNAME ": %s, " |
229 | "I/O 0x%lx-0x%lx, IRQ %d, memory mapped to 0x%lX-0x%lX\n" , |
230 | card->shortname, |
231 | chip->io, chip->io + DSP_NUMIO - 1, |
232 | chip->irq, |
233 | chip->base, chip->base + 0x7fff); |
234 | #else |
235 | switch (info >> 4) { |
236 | case 0xf: |
237 | xv = "<= 1.15" ; |
238 | break; |
239 | case 0x1: |
240 | xv = "1.18/1.2" ; |
241 | break; |
242 | case 0x2: |
243 | xv = "1.3" ; |
244 | break; |
245 | case 0x3: |
246 | xv = "1.4" ; |
247 | break; |
248 | default: |
249 | xv = "unknown" ; |
250 | break; |
251 | } |
252 | |
253 | switch (info & 0x7) { |
254 | case 0x0: |
255 | rev = "I" ; |
256 | strcpy(card->shortname, pin); |
257 | break; |
258 | case 0x1: |
259 | rev = "F" ; |
260 | strcpy(card->shortname, pin); |
261 | break; |
262 | case 0x2: |
263 | rev = "G" ; |
264 | strcpy(card->shortname, pin); |
265 | break; |
266 | case 0x3: |
267 | rev = "H" ; |
268 | strcpy(card->shortname, pin); |
269 | break; |
270 | case 0x4: |
271 | rev = "E" ; |
272 | strcpy(card->shortname, fiji); |
273 | break; |
274 | case 0x5: |
275 | rev = "C" ; |
276 | strcpy(card->shortname, fiji); |
277 | break; |
278 | case 0x6: |
279 | rev = "D" ; |
280 | strcpy(card->shortname, fiji); |
281 | break; |
282 | case 0x7: |
283 | rev = "A-B (Fiji) or A-E (Pinnacle)" ; |
284 | strcpy(card->shortname, pinfiji); |
285 | break; |
286 | } |
287 | strcpy(card->longname, "Turtle Beach Multisound Pinnacle" ); |
288 | printk(KERN_INFO LOGNAME ": %s revision %s, Xilinx version %s, " |
289 | "I/O 0x%lx-0x%lx, IRQ %d, memory mapped to 0x%lX-0x%lX\n" , |
290 | card->shortname, |
291 | rev, xv, |
292 | chip->io, chip->io + DSP_NUMIO - 1, |
293 | chip->irq, |
294 | chip->base, chip->base + 0x7fff); |
295 | #endif |
296 | |
297 | release_region(chip->io, DSP_NUMIO); |
298 | return 0; |
299 | } |
300 | |
301 | static int snd_msnd_init_sma(struct snd_msnd *chip) |
302 | { |
303 | static int initted; |
304 | u16 mastVolLeft, mastVolRight; |
305 | unsigned long flags; |
306 | |
307 | #ifdef MSND_CLASSIC |
308 | outb(value: chip->memid, port: chip->io + HP_MEMM); |
309 | #endif |
310 | outb(HPBLKSEL_0, port: chip->io + HP_BLKS); |
311 | /* Motorola 56k shared memory base */ |
312 | chip->SMA = chip->mappedbase + SMA_STRUCT_START; |
313 | |
314 | if (initted) { |
315 | mastVolLeft = readw(addr: chip->SMA + SMA_wCurrMastVolLeft); |
316 | mastVolRight = readw(addr: chip->SMA + SMA_wCurrMastVolRight); |
317 | } else |
318 | mastVolLeft = mastVolRight = 0; |
319 | memset_io(chip->mappedbase, 0, 0x8000); |
320 | |
321 | /* Critical section: bank 1 access */ |
322 | spin_lock_irqsave(&chip->lock, flags); |
323 | outb(HPBLKSEL_1, port: chip->io + HP_BLKS); |
324 | memset_io(chip->mappedbase, 0, 0x8000); |
325 | outb(HPBLKSEL_0, port: chip->io + HP_BLKS); |
326 | spin_unlock_irqrestore(lock: &chip->lock, flags); |
327 | |
328 | /* Digital audio play queue */ |
329 | chip->DAPQ = chip->mappedbase + DAPQ_OFFSET; |
330 | snd_msnd_init_queue(base: chip->DAPQ, DAPQ_DATA_BUFF, DAPQ_BUFF_SIZE); |
331 | |
332 | /* Digital audio record queue */ |
333 | chip->DARQ = chip->mappedbase + DARQ_OFFSET; |
334 | snd_msnd_init_queue(base: chip->DARQ, DARQ_DATA_BUFF, DARQ_BUFF_SIZE); |
335 | |
336 | /* MIDI out queue */ |
337 | chip->MODQ = chip->mappedbase + MODQ_OFFSET; |
338 | snd_msnd_init_queue(base: chip->MODQ, MODQ_DATA_BUFF, MODQ_BUFF_SIZE); |
339 | |
340 | /* MIDI in queue */ |
341 | chip->MIDQ = chip->mappedbase + MIDQ_OFFSET; |
342 | snd_msnd_init_queue(base: chip->MIDQ, MIDQ_DATA_BUFF, MIDQ_BUFF_SIZE); |
343 | |
344 | /* DSP -> host message queue */ |
345 | chip->DSPQ = chip->mappedbase + DSPQ_OFFSET; |
346 | snd_msnd_init_queue(base: chip->DSPQ, DSPQ_DATA_BUFF, DSPQ_BUFF_SIZE); |
347 | |
348 | /* Setup some DSP values */ |
349 | #ifndef MSND_CLASSIC |
350 | writew(1, chip->SMA + SMA_wCurrPlayFormat); |
351 | writew(chip->play_sample_size, chip->SMA + SMA_wCurrPlaySampleSize); |
352 | writew(chip->play_channels, chip->SMA + SMA_wCurrPlayChannels); |
353 | writew(chip->play_sample_rate, chip->SMA + SMA_wCurrPlaySampleRate); |
354 | #endif |
355 | writew(val: chip->play_sample_rate, addr: chip->SMA + SMA_wCalFreqAtoD); |
356 | writew(val: mastVolLeft, addr: chip->SMA + SMA_wCurrMastVolLeft); |
357 | writew(val: mastVolRight, addr: chip->SMA + SMA_wCurrMastVolRight); |
358 | #ifndef MSND_CLASSIC |
359 | writel(0x00010000, chip->SMA + SMA_dwCurrPlayPitch); |
360 | writel(0x00000001, chip->SMA + SMA_dwCurrPlayRate); |
361 | #endif |
362 | writew(val: 0x303, addr: chip->SMA + SMA_wCurrInputTagBits); |
363 | |
364 | initted = 1; |
365 | |
366 | return 0; |
367 | } |
368 | |
369 | |
370 | static int upload_dsp_code(struct snd_card *card) |
371 | { |
372 | struct snd_msnd *chip = card->private_data; |
373 | const struct firmware *init_fw = NULL, *perm_fw = NULL; |
374 | int err; |
375 | |
376 | outb(HPBLKSEL_0, port: chip->io + HP_BLKS); |
377 | |
378 | err = request_firmware(fw: &init_fw, INITCODEFILE, device: card->dev); |
379 | if (err < 0) { |
380 | printk(KERN_ERR LOGNAME ": Error loading " INITCODEFILE); |
381 | goto cleanup1; |
382 | } |
383 | err = request_firmware(fw: &perm_fw, PERMCODEFILE, device: card->dev); |
384 | if (err < 0) { |
385 | printk(KERN_ERR LOGNAME ": Error loading " PERMCODEFILE); |
386 | goto cleanup; |
387 | } |
388 | |
389 | memcpy_toio(chip->mappedbase, perm_fw->data, perm_fw->size); |
390 | if (snd_msnd_upload_host(chip, bin: init_fw->data, len: init_fw->size) < 0) { |
391 | printk(KERN_WARNING LOGNAME ": Error uploading to DSP\n" ); |
392 | err = -ENODEV; |
393 | goto cleanup; |
394 | } |
395 | printk(KERN_INFO LOGNAME ": DSP firmware uploaded\n" ); |
396 | err = 0; |
397 | |
398 | cleanup: |
399 | release_firmware(fw: perm_fw); |
400 | cleanup1: |
401 | release_firmware(fw: init_fw); |
402 | return err; |
403 | } |
404 | |
405 | #ifdef MSND_CLASSIC |
406 | static void reset_proteus(struct snd_msnd *chip) |
407 | { |
408 | outb(HPPRORESET_ON, port: chip->io + HP_PROR); |
409 | msleep(TIME_PRO_RESET); |
410 | outb(HPPRORESET_OFF, port: chip->io + HP_PROR); |
411 | msleep(TIME_PRO_RESET_DONE); |
412 | } |
413 | #endif |
414 | |
415 | static int snd_msnd_initialize(struct snd_card *card) |
416 | { |
417 | struct snd_msnd *chip = card->private_data; |
418 | int err, timeout; |
419 | |
420 | #ifdef MSND_CLASSIC |
421 | outb(HPWAITSTATE_0, port: chip->io + HP_WAIT); |
422 | outb(HPBITMODE_16, port: chip->io + HP_BITM); |
423 | |
424 | reset_proteus(chip); |
425 | #endif |
426 | err = snd_msnd_init_sma(chip); |
427 | if (err < 0) { |
428 | printk(KERN_WARNING LOGNAME ": Cannot initialize SMA\n" ); |
429 | return err; |
430 | } |
431 | |
432 | err = snd_msnd_reset_dsp(io: chip->io, NULL); |
433 | if (err < 0) |
434 | return err; |
435 | |
436 | err = upload_dsp_code(card); |
437 | if (err < 0) { |
438 | printk(KERN_WARNING LOGNAME ": Cannot upload DSP code\n" ); |
439 | return err; |
440 | } |
441 | |
442 | timeout = 200; |
443 | |
444 | while (readw(addr: chip->mappedbase)) { |
445 | msleep(msecs: 1); |
446 | if (!timeout--) { |
447 | snd_printd(KERN_ERR LOGNAME ": DSP reset timeout\n" ); |
448 | return -EIO; |
449 | } |
450 | } |
451 | |
452 | snd_msndmix_setup(chip); |
453 | return 0; |
454 | } |
455 | |
456 | static int snd_msnd_dsp_full_reset(struct snd_card *card) |
457 | { |
458 | struct snd_msnd *chip = card->private_data; |
459 | int rv; |
460 | |
461 | if (test_bit(F_RESETTING, &chip->flags) || ++chip->nresets > 10) |
462 | return 0; |
463 | |
464 | set_bit(F_RESETTING, addr: &chip->flags); |
465 | snd_msnd_dsp_halt(chip, NULL); /* Unconditionally halt */ |
466 | |
467 | rv = snd_msnd_initialize(card); |
468 | if (rv) |
469 | printk(KERN_WARNING LOGNAME ": DSP reset failed\n" ); |
470 | snd_msndmix_force_recsrc(chip, recsrc: 0); |
471 | clear_bit(F_RESETTING, addr: &chip->flags); |
472 | return rv; |
473 | } |
474 | |
475 | |
476 | static int snd_msnd_send_dsp_cmd_chk(struct snd_msnd *chip, u8 cmd) |
477 | { |
478 | if (snd_msnd_send_dsp_cmd(chip, cmd) == 0) |
479 | return 0; |
480 | snd_msnd_dsp_full_reset(card: chip->card); |
481 | return snd_msnd_send_dsp_cmd(chip, cmd); |
482 | } |
483 | |
484 | static int snd_msnd_calibrate_adc(struct snd_msnd *chip, u16 srate) |
485 | { |
486 | snd_printdd("snd_msnd_calibrate_adc(%i)\n" , srate); |
487 | writew(val: srate, addr: chip->SMA + SMA_wCalFreqAtoD); |
488 | if (chip->calibrate_signal == 0) |
489 | writew(readw(addr: chip->SMA + SMA_wCurrHostStatusFlags) |
490 | | 0x0001, addr: chip->SMA + SMA_wCurrHostStatusFlags); |
491 | else |
492 | writew(readw(addr: chip->SMA + SMA_wCurrHostStatusFlags) |
493 | & ~0x0001, addr: chip->SMA + SMA_wCurrHostStatusFlags); |
494 | if (snd_msnd_send_word(chip, high: 0, mid: 0, HDEXAR_CAL_A_TO_D) == 0 && |
495 | snd_msnd_send_dsp_cmd_chk(chip, HDEX_AUX_REQ) == 0) { |
496 | schedule_timeout_interruptible(timeout: msecs_to_jiffies(m: 333)); |
497 | return 0; |
498 | } |
499 | printk(KERN_WARNING LOGNAME ": ADC calibration failed\n" ); |
500 | return -EIO; |
501 | } |
502 | |
503 | /* |
504 | * ALSA callback function, called when attempting to open the MIDI device. |
505 | */ |
506 | static int snd_msnd_mpu401_open(struct snd_mpu401 *mpu) |
507 | { |
508 | snd_msnd_enable_irq(chip: mpu->private_data); |
509 | snd_msnd_send_dsp_cmd(chip: mpu->private_data, HDEX_MIDI_IN_START); |
510 | return 0; |
511 | } |
512 | |
513 | static void snd_msnd_mpu401_close(struct snd_mpu401 *mpu) |
514 | { |
515 | snd_msnd_send_dsp_cmd(chip: mpu->private_data, HDEX_MIDI_IN_STOP); |
516 | snd_msnd_disable_irq(chip: mpu->private_data); |
517 | } |
518 | |
519 | static long mpu_io[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; |
520 | static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; |
521 | |
522 | static int snd_msnd_attach(struct snd_card *card) |
523 | { |
524 | struct snd_msnd *chip = card->private_data; |
525 | int err; |
526 | |
527 | err = devm_request_irq(dev: card->dev, irq: chip->irq, handler: snd_msnd_interrupt, irqflags: 0, |
528 | devname: card->shortname, dev_id: chip); |
529 | if (err < 0) { |
530 | printk(KERN_ERR LOGNAME ": Couldn't grab IRQ %d\n" , chip->irq); |
531 | return err; |
532 | } |
533 | card->sync_irq = chip->irq; |
534 | if (!devm_request_region(card->dev, chip->io, DSP_NUMIO, |
535 | card->shortname)) |
536 | return -EBUSY; |
537 | |
538 | if (!devm_request_mem_region(card->dev, chip->base, BUFFSIZE, |
539 | card->shortname)) { |
540 | printk(KERN_ERR LOGNAME |
541 | ": unable to grab memory region 0x%lx-0x%lx\n" , |
542 | chip->base, chip->base + BUFFSIZE - 1); |
543 | return -EBUSY; |
544 | } |
545 | chip->mappedbase = devm_ioremap(dev: card->dev, offset: chip->base, size: 0x8000); |
546 | if (!chip->mappedbase) { |
547 | printk(KERN_ERR LOGNAME |
548 | ": unable to map memory region 0x%lx-0x%lx\n" , |
549 | chip->base, chip->base + BUFFSIZE - 1); |
550 | return -EIO; |
551 | } |
552 | |
553 | err = snd_msnd_dsp_full_reset(card); |
554 | if (err < 0) |
555 | return err; |
556 | |
557 | err = snd_msnd_pcm(card, device: 0); |
558 | if (err < 0) { |
559 | printk(KERN_ERR LOGNAME ": error creating new PCM device\n" ); |
560 | return err; |
561 | } |
562 | |
563 | err = snd_msndmix_new(card); |
564 | if (err < 0) { |
565 | printk(KERN_ERR LOGNAME ": error creating new Mixer device\n" ); |
566 | return err; |
567 | } |
568 | |
569 | |
570 | if (mpu_io[0] != SNDRV_AUTO_PORT) { |
571 | struct snd_mpu401 *mpu; |
572 | |
573 | err = snd_mpu401_uart_new(card, device: 0, MPU401_HW_MPU401, |
574 | port: mpu_io[0], |
575 | MPU401_MODE_INPUT | |
576 | MPU401_MODE_OUTPUT, |
577 | irq: mpu_irq[0], |
578 | rrawmidi: &chip->rmidi); |
579 | if (err < 0) { |
580 | printk(KERN_ERR LOGNAME |
581 | ": error creating new Midi device\n" ); |
582 | return err; |
583 | } |
584 | mpu = chip->rmidi->private_data; |
585 | |
586 | mpu->open_input = snd_msnd_mpu401_open; |
587 | mpu->close_input = snd_msnd_mpu401_close; |
588 | mpu->private_data = chip; |
589 | } |
590 | |
591 | disable_irq(irq: chip->irq); |
592 | snd_msnd_calibrate_adc(chip, srate: chip->play_sample_rate); |
593 | snd_msndmix_force_recsrc(chip, recsrc: 0); |
594 | |
595 | err = snd_card_register(card); |
596 | if (err < 0) |
597 | return err; |
598 | |
599 | return 0; |
600 | } |
601 | |
602 | |
603 | #ifndef MSND_CLASSIC |
604 | |
605 | /* Pinnacle/Fiji Logical Device Configuration */ |
606 | |
607 | static int snd_msnd_write_cfg(int cfg, int reg, int value) |
608 | { |
609 | outb(reg, cfg); |
610 | outb(value, cfg + 1); |
611 | if (value != inb(cfg + 1)) { |
612 | printk(KERN_ERR LOGNAME ": snd_msnd_write_cfg: I/O error\n" ); |
613 | return -EIO; |
614 | } |
615 | return 0; |
616 | } |
617 | |
618 | static int snd_msnd_write_cfg_io0(int cfg, int num, u16 io) |
619 | { |
620 | if (snd_msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) |
621 | return -EIO; |
622 | if (snd_msnd_write_cfg(cfg, IREG_IO0_BASEHI, HIBYTE(io))) |
623 | return -EIO; |
624 | if (snd_msnd_write_cfg(cfg, IREG_IO0_BASELO, LOBYTE(io))) |
625 | return -EIO; |
626 | return 0; |
627 | } |
628 | |
629 | static int snd_msnd_write_cfg_io1(int cfg, int num, u16 io) |
630 | { |
631 | if (snd_msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) |
632 | return -EIO; |
633 | if (snd_msnd_write_cfg(cfg, IREG_IO1_BASEHI, HIBYTE(io))) |
634 | return -EIO; |
635 | if (snd_msnd_write_cfg(cfg, IREG_IO1_BASELO, LOBYTE(io))) |
636 | return -EIO; |
637 | return 0; |
638 | } |
639 | |
640 | static int snd_msnd_write_cfg_irq(int cfg, int num, u16 irq) |
641 | { |
642 | if (snd_msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) |
643 | return -EIO; |
644 | if (snd_msnd_write_cfg(cfg, IREG_IRQ_NUMBER, LOBYTE(irq))) |
645 | return -EIO; |
646 | if (snd_msnd_write_cfg(cfg, IREG_IRQ_TYPE, IRQTYPE_EDGE)) |
647 | return -EIO; |
648 | return 0; |
649 | } |
650 | |
651 | static int snd_msnd_write_cfg_mem(int cfg, int num, int mem) |
652 | { |
653 | u16 wmem; |
654 | |
655 | mem >>= 8; |
656 | wmem = (u16)(mem & 0xfff); |
657 | if (snd_msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) |
658 | return -EIO; |
659 | if (snd_msnd_write_cfg(cfg, IREG_MEMBASEHI, HIBYTE(wmem))) |
660 | return -EIO; |
661 | if (snd_msnd_write_cfg(cfg, IREG_MEMBASELO, LOBYTE(wmem))) |
662 | return -EIO; |
663 | if (wmem && snd_msnd_write_cfg(cfg, IREG_MEMCONTROL, |
664 | MEMTYPE_HIADDR | MEMTYPE_16BIT)) |
665 | return -EIO; |
666 | return 0; |
667 | } |
668 | |
669 | static int snd_msnd_activate_logical(int cfg, int num) |
670 | { |
671 | if (snd_msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) |
672 | return -EIO; |
673 | if (snd_msnd_write_cfg(cfg, IREG_ACTIVATE, LD_ACTIVATE)) |
674 | return -EIO; |
675 | return 0; |
676 | } |
677 | |
678 | static int snd_msnd_write_cfg_logical(int cfg, int num, u16 io0, |
679 | u16 io1, u16 irq, int mem) |
680 | { |
681 | if (snd_msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) |
682 | return -EIO; |
683 | if (snd_msnd_write_cfg_io0(cfg, num, io0)) |
684 | return -EIO; |
685 | if (snd_msnd_write_cfg_io1(cfg, num, io1)) |
686 | return -EIO; |
687 | if (snd_msnd_write_cfg_irq(cfg, num, irq)) |
688 | return -EIO; |
689 | if (snd_msnd_write_cfg_mem(cfg, num, mem)) |
690 | return -EIO; |
691 | if (snd_msnd_activate_logical(cfg, num)) |
692 | return -EIO; |
693 | return 0; |
694 | } |
695 | |
696 | static int snd_msnd_pinnacle_cfg_reset(int cfg) |
697 | { |
698 | int i; |
699 | |
700 | /* Reset devices if told to */ |
701 | printk(KERN_INFO LOGNAME ": Resetting all devices\n" ); |
702 | for (i = 0; i < 4; ++i) |
703 | if (snd_msnd_write_cfg_logical(cfg, i, 0, 0, 0, 0)) |
704 | return -EIO; |
705 | |
706 | return 0; |
707 | } |
708 | #endif |
709 | |
710 | static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ |
711 | static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ |
712 | |
713 | module_param_array(index, int, NULL, 0444); |
714 | MODULE_PARM_DESC(index, "Index value for msnd_pinnacle soundcard." ); |
715 | module_param_array(id, charp, NULL, 0444); |
716 | MODULE_PARM_DESC(id, "ID string for msnd_pinnacle soundcard." ); |
717 | |
718 | static long io[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; |
719 | static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; |
720 | static long mem[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; |
721 | |
722 | #ifndef MSND_CLASSIC |
723 | static long cfg[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; |
724 | |
725 | /* Extra Peripheral Configuration (Default: Disable) */ |
726 | static long ide_io0[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; |
727 | static long ide_io1[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; |
728 | static int ide_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; |
729 | |
730 | static long joystick_io[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; |
731 | /* If we have the digital daugherboard... */ |
732 | static int digital[SNDRV_CARDS]; |
733 | |
734 | /* Extra Peripheral Configuration */ |
735 | static int reset[SNDRV_CARDS]; |
736 | #endif |
737 | |
738 | static int write_ndelay[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = 1 }; |
739 | |
740 | static int calibrate_signal; |
741 | |
742 | #ifdef CONFIG_PNP |
743 | static bool isapnp[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; |
744 | module_param_array(isapnp, bool, NULL, 0444); |
745 | MODULE_PARM_DESC(isapnp, "ISA PnP detection for specified soundcard." ); |
746 | #define has_isapnp(x) isapnp[x] |
747 | #else |
748 | #define has_isapnp(x) 0 |
749 | #endif |
750 | |
751 | MODULE_AUTHOR("Karsten Wiese <annabellesgarden@yahoo.de>" ); |
752 | MODULE_DESCRIPTION("Turtle Beach " LONGNAME " Linux Driver" ); |
753 | MODULE_LICENSE("GPL" ); |
754 | MODULE_FIRMWARE(INITCODEFILE); |
755 | MODULE_FIRMWARE(PERMCODEFILE); |
756 | |
757 | module_param_hw_array(io, long, ioport, NULL, 0444); |
758 | MODULE_PARM_DESC(io, "IO port #" ); |
759 | module_param_hw_array(irq, int, irq, NULL, 0444); |
760 | module_param_hw_array(mem, long, iomem, NULL, 0444); |
761 | module_param_array(write_ndelay, int, NULL, 0444); |
762 | module_param(calibrate_signal, int, 0444); |
763 | #ifndef MSND_CLASSIC |
764 | module_param_array(digital, int, NULL, 0444); |
765 | module_param_hw_array(cfg, long, ioport, NULL, 0444); |
766 | module_param_array(reset, int, NULL, 0444); |
767 | module_param_hw_array(mpu_io, long, ioport, NULL, 0444); |
768 | module_param_hw_array(mpu_irq, int, irq, NULL, 0444); |
769 | module_param_hw_array(ide_io0, long, ioport, NULL, 0444); |
770 | module_param_hw_array(ide_io1, long, ioport, NULL, 0444); |
771 | module_param_hw_array(ide_irq, int, irq, NULL, 0444); |
772 | module_param_hw_array(joystick_io, long, ioport, NULL, 0444); |
773 | #endif |
774 | |
775 | |
776 | static int snd_msnd_isa_match(struct device *pdev, unsigned int i) |
777 | { |
778 | if (io[i] == SNDRV_AUTO_PORT) |
779 | return 0; |
780 | |
781 | if (irq[i] == SNDRV_AUTO_PORT || mem[i] == SNDRV_AUTO_PORT) { |
782 | printk(KERN_WARNING LOGNAME ": io, irq and mem must be set\n" ); |
783 | return 0; |
784 | } |
785 | |
786 | #ifdef MSND_CLASSIC |
787 | if (!(io[i] == 0x290 || |
788 | io[i] == 0x260 || |
789 | io[i] == 0x250 || |
790 | io[i] == 0x240 || |
791 | io[i] == 0x230 || |
792 | io[i] == 0x220 || |
793 | io[i] == 0x210 || |
794 | io[i] == 0x3e0)) { |
795 | printk(KERN_ERR LOGNAME ": \"io\" - DSP I/O base must be set " |
796 | " to 0x210, 0x220, 0x230, 0x240, 0x250, 0x260, 0x290, " |
797 | "or 0x3E0\n" ); |
798 | return 0; |
799 | } |
800 | #else |
801 | if (io[i] < 0x100 || io[i] > 0x3e0 || (io[i] % 0x10) != 0) { |
802 | printk(KERN_ERR LOGNAME |
803 | ": \"io\" - DSP I/O base must within the range 0x100 " |
804 | "to 0x3E0 and must be evenly divisible by 0x10\n" ); |
805 | return 0; |
806 | } |
807 | #endif /* MSND_CLASSIC */ |
808 | |
809 | if (!(irq[i] == 5 || |
810 | irq[i] == 7 || |
811 | irq[i] == 9 || |
812 | irq[i] == 10 || |
813 | irq[i] == 11 || |
814 | irq[i] == 12)) { |
815 | printk(KERN_ERR LOGNAME |
816 | ": \"irq\" - must be set to 5, 7, 9, 10, 11 or 12\n" ); |
817 | return 0; |
818 | } |
819 | |
820 | if (!(mem[i] == 0xb0000 || |
821 | mem[i] == 0xc8000 || |
822 | mem[i] == 0xd0000 || |
823 | mem[i] == 0xd8000 || |
824 | mem[i] == 0xe0000 || |
825 | mem[i] == 0xe8000)) { |
826 | printk(KERN_ERR LOGNAME ": \"mem\" - must be set to " |
827 | "0xb0000, 0xc8000, 0xd0000, 0xd8000, 0xe0000 or " |
828 | "0xe8000\n" ); |
829 | return 0; |
830 | } |
831 | |
832 | #ifndef MSND_CLASSIC |
833 | if (cfg[i] == SNDRV_AUTO_PORT) { |
834 | printk(KERN_INFO LOGNAME ": Assuming PnP mode\n" ); |
835 | } else if (cfg[i] != 0x250 && cfg[i] != 0x260 && cfg[i] != 0x270) { |
836 | printk(KERN_INFO LOGNAME |
837 | ": Config port must be 0x250, 0x260 or 0x270 " |
838 | "(or unspecified for PnP mode)\n" ); |
839 | return 0; |
840 | } |
841 | #endif /* MSND_CLASSIC */ |
842 | |
843 | return 1; |
844 | } |
845 | |
846 | static int snd_msnd_isa_probe(struct device *pdev, unsigned int idx) |
847 | { |
848 | int err; |
849 | struct snd_card *card; |
850 | struct snd_msnd *chip; |
851 | |
852 | if (has_isapnp(idx) |
853 | #ifndef MSND_CLASSIC |
854 | || cfg[idx] == SNDRV_AUTO_PORT |
855 | #endif |
856 | ) { |
857 | printk(KERN_INFO LOGNAME ": Assuming PnP mode\n" ); |
858 | return -ENODEV; |
859 | } |
860 | |
861 | err = snd_devm_card_new(parent: pdev, idx: index[idx], xid: id[idx], THIS_MODULE, |
862 | extra_size: sizeof(struct snd_msnd), card_ret: &card); |
863 | if (err < 0) |
864 | return err; |
865 | |
866 | chip = card->private_data; |
867 | chip->card = card; |
868 | |
869 | #ifdef MSND_CLASSIC |
870 | switch (irq[idx]) { |
871 | case 5: |
872 | chip->irqid = HPIRQ_5; break; |
873 | case 7: |
874 | chip->irqid = HPIRQ_7; break; |
875 | case 9: |
876 | chip->irqid = HPIRQ_9; break; |
877 | case 10: |
878 | chip->irqid = HPIRQ_10; break; |
879 | case 11: |
880 | chip->irqid = HPIRQ_11; break; |
881 | case 12: |
882 | chip->irqid = HPIRQ_12; break; |
883 | } |
884 | |
885 | switch (mem[idx]) { |
886 | case 0xb0000: |
887 | chip->memid = HPMEM_B000; break; |
888 | case 0xc8000: |
889 | chip->memid = HPMEM_C800; break; |
890 | case 0xd0000: |
891 | chip->memid = HPMEM_D000; break; |
892 | case 0xd8000: |
893 | chip->memid = HPMEM_D800; break; |
894 | case 0xe0000: |
895 | chip->memid = HPMEM_E000; break; |
896 | case 0xe8000: |
897 | chip->memid = HPMEM_E800; break; |
898 | } |
899 | #else |
900 | printk(KERN_INFO LOGNAME ": Non-PnP mode: configuring at port 0x%lx\n" , |
901 | cfg[idx]); |
902 | |
903 | if (!devm_request_region(card->dev, cfg[idx], 2, |
904 | "Pinnacle/Fiji Config" )) { |
905 | printk(KERN_ERR LOGNAME ": Config port 0x%lx conflict\n" , |
906 | cfg[idx]); |
907 | return -EIO; |
908 | } |
909 | if (reset[idx]) |
910 | if (snd_msnd_pinnacle_cfg_reset(cfg[idx])) |
911 | return -EIO; |
912 | |
913 | /* DSP */ |
914 | err = snd_msnd_write_cfg_logical(cfg[idx], 0, |
915 | io[idx], 0, |
916 | irq[idx], mem[idx]); |
917 | |
918 | if (err) |
919 | return err; |
920 | |
921 | /* The following are Pinnacle specific */ |
922 | |
923 | /* MPU */ |
924 | if (mpu_io[idx] != SNDRV_AUTO_PORT |
925 | && mpu_irq[idx] != SNDRV_AUTO_IRQ) { |
926 | printk(KERN_INFO LOGNAME |
927 | ": Configuring MPU to I/O 0x%lx IRQ %d\n" , |
928 | mpu_io[idx], mpu_irq[idx]); |
929 | err = snd_msnd_write_cfg_logical(cfg[idx], 1, |
930 | mpu_io[idx], 0, |
931 | mpu_irq[idx], 0); |
932 | |
933 | if (err) |
934 | return err; |
935 | } |
936 | |
937 | /* IDE */ |
938 | if (ide_io0[idx] != SNDRV_AUTO_PORT |
939 | && ide_io1[idx] != SNDRV_AUTO_PORT |
940 | && ide_irq[idx] != SNDRV_AUTO_IRQ) { |
941 | printk(KERN_INFO LOGNAME |
942 | ": Configuring IDE to I/O 0x%lx, 0x%lx IRQ %d\n" , |
943 | ide_io0[idx], ide_io1[idx], ide_irq[idx]); |
944 | err = snd_msnd_write_cfg_logical(cfg[idx], 2, |
945 | ide_io0[idx], ide_io1[idx], |
946 | ide_irq[idx], 0); |
947 | |
948 | if (err) |
949 | return err; |
950 | } |
951 | |
952 | /* Joystick */ |
953 | if (joystick_io[idx] != SNDRV_AUTO_PORT) { |
954 | printk(KERN_INFO LOGNAME |
955 | ": Configuring joystick to I/O 0x%lx\n" , |
956 | joystick_io[idx]); |
957 | err = snd_msnd_write_cfg_logical(cfg[idx], 3, |
958 | joystick_io[idx], 0, |
959 | 0, 0); |
960 | |
961 | if (err) |
962 | return err; |
963 | } |
964 | |
965 | #endif /* MSND_CLASSIC */ |
966 | |
967 | set_default_audio_parameters(chip); |
968 | #ifdef MSND_CLASSIC |
969 | chip->type = msndClassic; |
970 | #else |
971 | chip->type = msndPinnacle; |
972 | #endif |
973 | chip->io = io[idx]; |
974 | chip->irq = irq[idx]; |
975 | chip->base = mem[idx]; |
976 | |
977 | chip->calibrate_signal = calibrate_signal ? 1 : 0; |
978 | chip->recsrc = 0; |
979 | chip->dspq_data_buff = DSPQ_DATA_BUFF; |
980 | chip->dspq_buff_size = DSPQ_BUFF_SIZE; |
981 | if (write_ndelay[idx]) |
982 | clear_bit(F_DISABLE_WRITE_NDELAY, addr: &chip->flags); |
983 | else |
984 | set_bit(F_DISABLE_WRITE_NDELAY, addr: &chip->flags); |
985 | #ifndef MSND_CLASSIC |
986 | if (digital[idx]) |
987 | set_bit(F_HAVEDIGITAL, &chip->flags); |
988 | #endif |
989 | spin_lock_init(&chip->lock); |
990 | err = snd_msnd_probe(card); |
991 | if (err < 0) { |
992 | printk(KERN_ERR LOGNAME ": Probe failed\n" ); |
993 | return err; |
994 | } |
995 | |
996 | err = snd_msnd_attach(card); |
997 | if (err < 0) { |
998 | printk(KERN_ERR LOGNAME ": Attach failed\n" ); |
999 | return err; |
1000 | } |
1001 | dev_set_drvdata(dev: pdev, data: card); |
1002 | |
1003 | return 0; |
1004 | } |
1005 | |
1006 | static struct isa_driver snd_msnd_driver = { |
1007 | .match = snd_msnd_isa_match, |
1008 | .probe = snd_msnd_isa_probe, |
1009 | /* FIXME: suspend, resume */ |
1010 | .driver = { |
1011 | .name = DEV_NAME |
1012 | }, |
1013 | }; |
1014 | |
1015 | #ifdef CONFIG_PNP |
1016 | static int snd_msnd_pnp_detect(struct pnp_card_link *pcard, |
1017 | const struct pnp_card_device_id *pid) |
1018 | { |
1019 | static int idx; |
1020 | struct pnp_dev *pnp_dev; |
1021 | struct pnp_dev *mpu_dev; |
1022 | struct snd_card *card; |
1023 | struct snd_msnd *chip; |
1024 | int ret; |
1025 | |
1026 | for ( ; idx < SNDRV_CARDS; idx++) { |
1027 | if (has_isapnp(idx)) |
1028 | break; |
1029 | } |
1030 | if (idx >= SNDRV_CARDS) |
1031 | return -ENODEV; |
1032 | |
1033 | /* |
1034 | * Check that we still have room for another sound card ... |
1035 | */ |
1036 | pnp_dev = pnp_request_card_device(clink: pcard, id: pid->devs[0].id, NULL); |
1037 | if (!pnp_dev) |
1038 | return -ENODEV; |
1039 | |
1040 | mpu_dev = pnp_request_card_device(clink: pcard, id: pid->devs[1].id, NULL); |
1041 | if (!mpu_dev) |
1042 | return -ENODEV; |
1043 | |
1044 | if (!pnp_is_active(dev: pnp_dev) && pnp_activate_dev(dev: pnp_dev) < 0) { |
1045 | printk(KERN_INFO "msnd_pinnacle: device is inactive\n" ); |
1046 | return -EBUSY; |
1047 | } |
1048 | |
1049 | if (!pnp_is_active(dev: mpu_dev) && pnp_activate_dev(dev: mpu_dev) < 0) { |
1050 | printk(KERN_INFO "msnd_pinnacle: MPU device is inactive\n" ); |
1051 | return -EBUSY; |
1052 | } |
1053 | |
1054 | /* |
1055 | * Create a new ALSA sound card entry, in anticipation |
1056 | * of detecting our hardware ... |
1057 | */ |
1058 | ret = snd_devm_card_new(parent: &pcard->card->dev, |
1059 | idx: index[idx], xid: id[idx], THIS_MODULE, |
1060 | extra_size: sizeof(struct snd_msnd), card_ret: &card); |
1061 | if (ret < 0) |
1062 | return ret; |
1063 | |
1064 | chip = card->private_data; |
1065 | chip->card = card; |
1066 | |
1067 | /* |
1068 | * Read the correct parameters off the ISA PnP bus ... |
1069 | */ |
1070 | io[idx] = pnp_port_start(dev: pnp_dev, bar: 0); |
1071 | irq[idx] = pnp_irq(dev: pnp_dev, bar: 0); |
1072 | mem[idx] = pnp_mem_start(dev: pnp_dev, bar: 0); |
1073 | mpu_io[idx] = pnp_port_start(dev: mpu_dev, bar: 0); |
1074 | mpu_irq[idx] = pnp_irq(dev: mpu_dev, bar: 0); |
1075 | |
1076 | set_default_audio_parameters(chip); |
1077 | #ifdef MSND_CLASSIC |
1078 | chip->type = msndClassic; |
1079 | #else |
1080 | chip->type = msndPinnacle; |
1081 | #endif |
1082 | chip->io = io[idx]; |
1083 | chip->irq = irq[idx]; |
1084 | chip->base = mem[idx]; |
1085 | |
1086 | chip->calibrate_signal = calibrate_signal ? 1 : 0; |
1087 | chip->recsrc = 0; |
1088 | chip->dspq_data_buff = DSPQ_DATA_BUFF; |
1089 | chip->dspq_buff_size = DSPQ_BUFF_SIZE; |
1090 | if (write_ndelay[idx]) |
1091 | clear_bit(F_DISABLE_WRITE_NDELAY, addr: &chip->flags); |
1092 | else |
1093 | set_bit(F_DISABLE_WRITE_NDELAY, addr: &chip->flags); |
1094 | #ifndef MSND_CLASSIC |
1095 | if (digital[idx]) |
1096 | set_bit(F_HAVEDIGITAL, &chip->flags); |
1097 | #endif |
1098 | spin_lock_init(&chip->lock); |
1099 | ret = snd_msnd_probe(card); |
1100 | if (ret < 0) { |
1101 | printk(KERN_ERR LOGNAME ": Probe failed\n" ); |
1102 | return ret; |
1103 | } |
1104 | |
1105 | ret = snd_msnd_attach(card); |
1106 | if (ret < 0) { |
1107 | printk(KERN_ERR LOGNAME ": Attach failed\n" ); |
1108 | return ret; |
1109 | } |
1110 | |
1111 | pnp_set_card_drvdata(pcard, data: card); |
1112 | ++idx; |
1113 | return 0; |
1114 | } |
1115 | |
1116 | static int isa_registered; |
1117 | static int pnp_registered; |
1118 | |
1119 | static const struct pnp_card_device_id msnd_pnpids[] = { |
1120 | /* Pinnacle PnP */ |
1121 | { .id = "BVJ0440" , .devs = { { "TBS0000" }, { "TBS0001" } } }, |
1122 | { .id = "" } /* end */ |
1123 | }; |
1124 | |
1125 | MODULE_DEVICE_TABLE(pnp_card, msnd_pnpids); |
1126 | |
1127 | static struct pnp_card_driver msnd_pnpc_driver = { |
1128 | .flags = PNP_DRIVER_RES_DO_NOT_CHANGE, |
1129 | .name = "msnd_pinnacle" , |
1130 | .id_table = msnd_pnpids, |
1131 | .probe = snd_msnd_pnp_detect, |
1132 | }; |
1133 | #endif /* CONFIG_PNP */ |
1134 | |
1135 | static int __init snd_msnd_init(void) |
1136 | { |
1137 | int err; |
1138 | |
1139 | err = isa_register_driver(&snd_msnd_driver, SNDRV_CARDS); |
1140 | #ifdef CONFIG_PNP |
1141 | if (!err) |
1142 | isa_registered = 1; |
1143 | |
1144 | err = pnp_register_card_driver(drv: &msnd_pnpc_driver); |
1145 | if (!err) |
1146 | pnp_registered = 1; |
1147 | |
1148 | if (isa_registered) |
1149 | err = 0; |
1150 | #endif |
1151 | return err; |
1152 | } |
1153 | |
1154 | static void __exit snd_msnd_exit(void) |
1155 | { |
1156 | #ifdef CONFIG_PNP |
1157 | if (pnp_registered) |
1158 | pnp_unregister_card_driver(drv: &msnd_pnpc_driver); |
1159 | if (isa_registered) |
1160 | #endif |
1161 | isa_unregister_driver(&snd_msnd_driver); |
1162 | } |
1163 | |
1164 | module_init(snd_msnd_init); |
1165 | module_exit(snd_msnd_exit); |
1166 | |
1167 | |