diff options
Diffstat (limited to 'sound/isa/msnd/msnd.c')
-rw-r--r-- | sound/isa/msnd/msnd.c | 705 |
1 files changed, 705 insertions, 0 deletions
diff --git a/sound/isa/msnd/msnd.c b/sound/isa/msnd/msnd.c new file mode 100644 index 000000000000..906454413ed2 --- /dev/null +++ b/sound/isa/msnd/msnd.c | |||
@@ -0,0 +1,705 @@ | |||
1 | /********************************************************************* | ||
2 | * | ||
3 | * 2002/06/30 Karsten Wiese: | ||
4 | * removed kernel-version dependencies. | ||
5 | * ripped from linux kernel 2.4.18 (OSS Implementation) by me. | ||
6 | * In the OSS Version, this file is compiled to a separate MODULE, | ||
7 | * that is used by the pinnacle and the classic driver. | ||
8 | * since there is no classic driver for alsa yet (i dont have a classic | ||
9 | * & writing one blindfold is difficult) this file's object is statically | ||
10 | * linked into the pinnacle-driver-module for now. look for the string | ||
11 | * "uncomment this to make this a module again" | ||
12 | * to do guess what. | ||
13 | * | ||
14 | * the following is a copy of the 2.4.18 OSS FREE file-heading comment: | ||
15 | * | ||
16 | * msnd.c - Driver Base | ||
17 | * | ||
18 | * Turtle Beach MultiSound Sound Card Driver for Linux | ||
19 | * | ||
20 | * Copyright (C) 1998 Andrew Veliath | ||
21 | * | ||
22 | * This program is free software; you can redistribute it and/or modify | ||
23 | * it under the terms of the GNU General Public License as published by | ||
24 | * the Free Software Foundation; either version 2 of the License, or | ||
25 | * (at your option) any later version. | ||
26 | * | ||
27 | * This program is distributed in the hope that it will be useful, | ||
28 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
29 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
30 | * GNU General Public License for more details. | ||
31 | * | ||
32 | * You should have received a copy of the GNU General Public License | ||
33 | * along with this program; if not, write to the Free Software | ||
34 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
35 | * | ||
36 | ********************************************************************/ | ||
37 | |||
38 | #include <linux/kernel.h> | ||
39 | #include <linux/types.h> | ||
40 | #include <linux/interrupt.h> | ||
41 | #include <linux/io.h> | ||
42 | #include <linux/fs.h> | ||
43 | #include <linux/delay.h> | ||
44 | |||
45 | #include <sound/core.h> | ||
46 | #include <sound/initval.h> | ||
47 | #include <sound/pcm.h> | ||
48 | #include <sound/pcm_params.h> | ||
49 | |||
50 | #include "msnd.h" | ||
51 | |||
52 | #define LOGNAME "msnd" | ||
53 | |||
54 | |||
55 | void snd_msnd_init_queue(void *base, int start, int size) | ||
56 | { | ||
57 | writew(PCTODSP_BASED(start), base + JQS_wStart); | ||
58 | writew(PCTODSP_OFFSET(size) - 1, base + JQS_wSize); | ||
59 | writew(0, base + JQS_wHead); | ||
60 | writew(0, base + JQS_wTail); | ||
61 | } | ||
62 | EXPORT_SYMBOL(snd_msnd_init_queue); | ||
63 | |||
64 | static int snd_msnd_wait_TXDE(struct snd_msnd *dev) | ||
65 | { | ||
66 | unsigned int io = dev->io; | ||
67 | int timeout = 1000; | ||
68 | |||
69 | while (timeout-- > 0) | ||
70 | if (inb(io + HP_ISR) & HPISR_TXDE) | ||
71 | return 0; | ||
72 | |||
73 | return -EIO; | ||
74 | } | ||
75 | |||
76 | static int snd_msnd_wait_HC0(struct snd_msnd *dev) | ||
77 | { | ||
78 | unsigned int io = dev->io; | ||
79 | int timeout = 1000; | ||
80 | |||
81 | while (timeout-- > 0) | ||
82 | if (!(inb(io + HP_CVR) & HPCVR_HC)) | ||
83 | return 0; | ||
84 | |||
85 | return -EIO; | ||
86 | } | ||
87 | |||
88 | int snd_msnd_send_dsp_cmd(struct snd_msnd *dev, u8 cmd) | ||
89 | { | ||
90 | unsigned long flags; | ||
91 | |||
92 | spin_lock_irqsave(&dev->lock, flags); | ||
93 | if (snd_msnd_wait_HC0(dev) == 0) { | ||
94 | outb(cmd, dev->io + HP_CVR); | ||
95 | spin_unlock_irqrestore(&dev->lock, flags); | ||
96 | return 0; | ||
97 | } | ||
98 | spin_unlock_irqrestore(&dev->lock, flags); | ||
99 | |||
100 | snd_printd(KERN_ERR LOGNAME ": Send DSP command timeout\n"); | ||
101 | |||
102 | return -EIO; | ||
103 | } | ||
104 | EXPORT_SYMBOL(snd_msnd_send_dsp_cmd); | ||
105 | |||
106 | int snd_msnd_send_word(struct snd_msnd *dev, unsigned char high, | ||
107 | unsigned char mid, unsigned char low) | ||
108 | { | ||
109 | unsigned int io = dev->io; | ||
110 | |||
111 | if (snd_msnd_wait_TXDE(dev) == 0) { | ||
112 | outb(high, io + HP_TXH); | ||
113 | outb(mid, io + HP_TXM); | ||
114 | outb(low, io + HP_TXL); | ||
115 | return 0; | ||
116 | } | ||
117 | |||
118 | snd_printd(KERN_ERR LOGNAME ": Send host word timeout\n"); | ||
119 | |||
120 | return -EIO; | ||
121 | } | ||
122 | EXPORT_SYMBOL(snd_msnd_send_word); | ||
123 | |||
124 | int snd_msnd_upload_host(struct snd_msnd *dev, const u8 *bin, int len) | ||
125 | { | ||
126 | int i; | ||
127 | |||
128 | if (len % 3 != 0) { | ||
129 | snd_printk(KERN_ERR LOGNAME | ||
130 | ": Upload host data not multiple of 3!\n"); | ||
131 | return -EINVAL; | ||
132 | } | ||
133 | |||
134 | for (i = 0; i < len; i += 3) | ||
135 | if (snd_msnd_send_word(dev, bin[i], bin[i + 1], bin[i + 2])) | ||
136 | return -EIO; | ||
137 | |||
138 | inb(dev->io + HP_RXL); | ||
139 | inb(dev->io + HP_CVR); | ||
140 | |||
141 | return 0; | ||
142 | } | ||
143 | EXPORT_SYMBOL(snd_msnd_upload_host); | ||
144 | |||
145 | int snd_msnd_enable_irq(struct snd_msnd *dev) | ||
146 | { | ||
147 | unsigned long flags; | ||
148 | |||
149 | if (dev->irq_ref++) | ||
150 | return 0; | ||
151 | |||
152 | snd_printdd(LOGNAME ": Enabling IRQ\n"); | ||
153 | |||
154 | spin_lock_irqsave(&dev->lock, flags); | ||
155 | if (snd_msnd_wait_TXDE(dev) == 0) { | ||
156 | outb(inb(dev->io + HP_ICR) | HPICR_TREQ, dev->io + HP_ICR); | ||
157 | if (dev->type == msndClassic) | ||
158 | outb(dev->irqid, dev->io + HP_IRQM); | ||
159 | |||
160 | outb(inb(dev->io + HP_ICR) & ~HPICR_TREQ, dev->io + HP_ICR); | ||
161 | outb(inb(dev->io + HP_ICR) | HPICR_RREQ, dev->io + HP_ICR); | ||
162 | enable_irq(dev->irq); | ||
163 | snd_msnd_init_queue(dev->DSPQ, dev->dspq_data_buff, | ||
164 | dev->dspq_buff_size); | ||
165 | spin_unlock_irqrestore(&dev->lock, flags); | ||
166 | return 0; | ||
167 | } | ||
168 | spin_unlock_irqrestore(&dev->lock, flags); | ||
169 | |||
170 | snd_printd(KERN_ERR LOGNAME ": Enable IRQ failed\n"); | ||
171 | |||
172 | return -EIO; | ||
173 | } | ||
174 | EXPORT_SYMBOL(snd_msnd_enable_irq); | ||
175 | |||
176 | int snd_msnd_disable_irq(struct snd_msnd *dev) | ||
177 | { | ||
178 | unsigned long flags; | ||
179 | |||
180 | if (--dev->irq_ref > 0) | ||
181 | return 0; | ||
182 | |||
183 | if (dev->irq_ref < 0) | ||
184 | snd_printd(KERN_WARNING LOGNAME ": IRQ ref count is %d\n", | ||
185 | dev->irq_ref); | ||
186 | |||
187 | snd_printdd(LOGNAME ": Disabling IRQ\n"); | ||
188 | |||
189 | spin_lock_irqsave(&dev->lock, flags); | ||
190 | if (snd_msnd_wait_TXDE(dev) == 0) { | ||
191 | outb(inb(dev->io + HP_ICR) & ~HPICR_RREQ, dev->io + HP_ICR); | ||
192 | if (dev->type == msndClassic) | ||
193 | outb(HPIRQ_NONE, dev->io + HP_IRQM); | ||
194 | disable_irq(dev->irq); | ||
195 | spin_unlock_irqrestore(&dev->lock, flags); | ||
196 | return 0; | ||
197 | } | ||
198 | spin_unlock_irqrestore(&dev->lock, flags); | ||
199 | |||
200 | snd_printd(KERN_ERR LOGNAME ": Disable IRQ failed\n"); | ||
201 | |||
202 | return -EIO; | ||
203 | } | ||
204 | EXPORT_SYMBOL(snd_msnd_disable_irq); | ||
205 | |||
206 | static inline long get_play_delay_jiffies(struct snd_msnd *chip, long size) | ||
207 | { | ||
208 | long tmp = (size * HZ * chip->play_sample_size) / 8; | ||
209 | return tmp / (chip->play_sample_rate * chip->play_channels); | ||
210 | } | ||
211 | |||
212 | static void snd_msnd_dsp_write_flush(struct snd_msnd *chip) | ||
213 | { | ||
214 | if (!(chip->mode & FMODE_WRITE) || !test_bit(F_WRITING, &chip->flags)) | ||
215 | return; | ||
216 | set_bit(F_WRITEFLUSH, &chip->flags); | ||
217 | /* interruptible_sleep_on_timeout( | ||
218 | &chip->writeflush, | ||
219 | get_play_delay_jiffies(&chip, chip->DAPF.len));*/ | ||
220 | clear_bit(F_WRITEFLUSH, &chip->flags); | ||
221 | if (!signal_pending(current)) | ||
222 | schedule_timeout_interruptible( | ||
223 | get_play_delay_jiffies(chip, chip->play_period_bytes)); | ||
224 | clear_bit(F_WRITING, &chip->flags); | ||
225 | } | ||
226 | |||
227 | void snd_msnd_dsp_halt(struct snd_msnd *chip, struct file *file) | ||
228 | { | ||
229 | if ((file ? file->f_mode : chip->mode) & FMODE_READ) { | ||
230 | clear_bit(F_READING, &chip->flags); | ||
231 | snd_msnd_send_dsp_cmd(chip, HDEX_RECORD_STOP); | ||
232 | snd_msnd_disable_irq(chip); | ||
233 | if (file) { | ||
234 | snd_printd(KERN_INFO LOGNAME | ||
235 | ": Stopping read for %p\n", file); | ||
236 | chip->mode &= ~FMODE_READ; | ||
237 | } | ||
238 | clear_bit(F_AUDIO_READ_INUSE, &chip->flags); | ||
239 | } | ||
240 | if ((file ? file->f_mode : chip->mode) & FMODE_WRITE) { | ||
241 | if (test_bit(F_WRITING, &chip->flags)) { | ||
242 | snd_msnd_dsp_write_flush(chip); | ||
243 | snd_msnd_send_dsp_cmd(chip, HDEX_PLAY_STOP); | ||
244 | } | ||
245 | snd_msnd_disable_irq(chip); | ||
246 | if (file) { | ||
247 | snd_printd(KERN_INFO | ||
248 | LOGNAME ": Stopping write for %p\n", file); | ||
249 | chip->mode &= ~FMODE_WRITE; | ||
250 | } | ||
251 | clear_bit(F_AUDIO_WRITE_INUSE, &chip->flags); | ||
252 | } | ||
253 | } | ||
254 | EXPORT_SYMBOL(snd_msnd_dsp_halt); | ||
255 | |||
256 | |||
257 | int snd_msnd_DARQ(struct snd_msnd *chip, int bank) | ||
258 | { | ||
259 | int /*size, n,*/ timeout = 3; | ||
260 | u16 wTmp; | ||
261 | /* void *DAQD; */ | ||
262 | |||
263 | /* Increment the tail and check for queue wrap */ | ||
264 | wTmp = readw(chip->DARQ + JQS_wTail) + PCTODSP_OFFSET(DAQDS__size); | ||
265 | if (wTmp > readw(chip->DARQ + JQS_wSize)) | ||
266 | wTmp = 0; | ||
267 | while (wTmp == readw(chip->DARQ + JQS_wHead) && timeout--) | ||
268 | udelay(1); | ||
269 | |||
270 | if (chip->capturePeriods == 2) { | ||
271 | void *pDAQ = chip->mappedbase + DARQ_DATA_BUFF + | ||
272 | bank * DAQDS__size + DAQDS_wStart; | ||
273 | unsigned short offset = 0x3000 + chip->capturePeriodBytes; | ||
274 | |||
275 | if (readw(pDAQ) != PCTODSP_BASED(0x3000)) | ||
276 | offset = 0x3000; | ||
277 | writew(PCTODSP_BASED(offset), pDAQ); | ||
278 | } | ||
279 | |||
280 | writew(wTmp, chip->DARQ + JQS_wTail); | ||
281 | |||
282 | #if 0 | ||
283 | /* Get our digital audio queue struct */ | ||
284 | DAQD = bank * DAQDS__size + chip->mappedbase + DARQ_DATA_BUFF; | ||
285 | |||
286 | /* Get length of data */ | ||
287 | size = readw(DAQD + DAQDS_wSize); | ||
288 | |||
289 | /* Read data from the head (unprotected bank 1 access okay | ||
290 | since this is only called inside an interrupt) */ | ||
291 | outb(HPBLKSEL_1, chip->io + HP_BLKS); | ||
292 | n = msnd_fifo_write(&chip->DARF, | ||
293 | (char *)(chip->base + bank * DAR_BUFF_SIZE), | ||
294 | size, 0); | ||
295 | if (n <= 0) { | ||
296 | outb(HPBLKSEL_0, chip->io + HP_BLKS); | ||
297 | return n; | ||
298 | } | ||
299 | outb(HPBLKSEL_0, chip->io + HP_BLKS); | ||
300 | #endif | ||
301 | |||
302 | return 1; | ||
303 | } | ||
304 | EXPORT_SYMBOL(snd_msnd_DARQ); | ||
305 | |||
306 | int snd_msnd_DAPQ(struct snd_msnd *chip, int start) | ||
307 | { | ||
308 | u16 DAPQ_tail; | ||
309 | int protect = start, nbanks = 0; | ||
310 | void *DAQD; | ||
311 | static int play_banks_submitted; | ||
312 | /* unsigned long flags; | ||
313 | spin_lock_irqsave(&chip->lock, flags); not necessary */ | ||
314 | |||
315 | DAPQ_tail = readw(chip->DAPQ + JQS_wTail); | ||
316 | while (DAPQ_tail != readw(chip->DAPQ + JQS_wHead) || start) { | ||
317 | int bank_num = DAPQ_tail / PCTODSP_OFFSET(DAQDS__size); | ||
318 | |||
319 | if (start) { | ||
320 | start = 0; | ||
321 | play_banks_submitted = 0; | ||
322 | } | ||
323 | |||
324 | /* Get our digital audio queue struct */ | ||
325 | DAQD = bank_num * DAQDS__size + chip->mappedbase + | ||
326 | DAPQ_DATA_BUFF; | ||
327 | |||
328 | /* Write size of this bank */ | ||
329 | writew(chip->play_period_bytes, DAQD + DAQDS_wSize); | ||
330 | if (play_banks_submitted < 3) | ||
331 | ++play_banks_submitted; | ||
332 | else if (chip->playPeriods == 2) { | ||
333 | unsigned short offset = chip->play_period_bytes; | ||
334 | |||
335 | if (readw(DAQD + DAQDS_wStart) != PCTODSP_BASED(0x0)) | ||
336 | offset = 0; | ||
337 | |||
338 | writew(PCTODSP_BASED(offset), DAQD + DAQDS_wStart); | ||
339 | } | ||
340 | ++nbanks; | ||
341 | |||
342 | /* Then advance the tail */ | ||
343 | /* | ||
344 | if (protect) | ||
345 | snd_printd(KERN_INFO "B %X %lX\n", | ||
346 | bank_num, xtime.tv_usec); | ||
347 | */ | ||
348 | |||
349 | DAPQ_tail = (++bank_num % 3) * PCTODSP_OFFSET(DAQDS__size); | ||
350 | writew(DAPQ_tail, chip->DAPQ + JQS_wTail); | ||
351 | /* Tell the DSP to play the bank */ | ||
352 | snd_msnd_send_dsp_cmd(chip, HDEX_PLAY_START); | ||
353 | if (protect) | ||
354 | if (2 == bank_num) | ||
355 | break; | ||
356 | } | ||
357 | /* | ||
358 | if (protect) | ||
359 | snd_printd(KERN_INFO "%lX\n", xtime.tv_usec); | ||
360 | */ | ||
361 | /* spin_unlock_irqrestore(&chip->lock, flags); not necessary */ | ||
362 | return nbanks; | ||
363 | } | ||
364 | EXPORT_SYMBOL(snd_msnd_DAPQ); | ||
365 | |||
366 | static void snd_msnd_play_reset_queue(struct snd_msnd *chip, | ||
367 | unsigned int pcm_periods, | ||
368 | unsigned int pcm_count) | ||
369 | { | ||
370 | int n; | ||
371 | void *pDAQ = chip->mappedbase + DAPQ_DATA_BUFF; | ||
372 | |||
373 | chip->last_playbank = -1; | ||
374 | chip->playLimit = pcm_count * (pcm_periods - 1); | ||
375 | chip->playPeriods = pcm_periods; | ||
376 | writew(PCTODSP_OFFSET(0 * DAQDS__size), chip->DAPQ + JQS_wHead); | ||
377 | writew(PCTODSP_OFFSET(0 * DAQDS__size), chip->DAPQ + JQS_wTail); | ||
378 | |||
379 | chip->play_period_bytes = pcm_count; | ||
380 | |||
381 | for (n = 0; n < pcm_periods; ++n, pDAQ += DAQDS__size) { | ||
382 | writew(PCTODSP_BASED((u32)(pcm_count * n)), | ||
383 | pDAQ + DAQDS_wStart); | ||
384 | writew(0, pDAQ + DAQDS_wSize); | ||
385 | writew(1, pDAQ + DAQDS_wFormat); | ||
386 | writew(chip->play_sample_size, pDAQ + DAQDS_wSampleSize); | ||
387 | writew(chip->play_channels, pDAQ + DAQDS_wChannels); | ||
388 | writew(chip->play_sample_rate, pDAQ + DAQDS_wSampleRate); | ||
389 | writew(HIMT_PLAY_DONE * 0x100 + n, pDAQ + DAQDS_wIntMsg); | ||
390 | writew(n, pDAQ + DAQDS_wFlags); | ||
391 | } | ||
392 | } | ||
393 | |||
394 | static void snd_msnd_capture_reset_queue(struct snd_msnd *chip, | ||
395 | unsigned int pcm_periods, | ||
396 | unsigned int pcm_count) | ||
397 | { | ||
398 | int n; | ||
399 | void *pDAQ; | ||
400 | /* unsigned long flags; */ | ||
401 | |||
402 | /* snd_msnd_init_queue(chip->DARQ, DARQ_DATA_BUFF, DARQ_BUFF_SIZE); */ | ||
403 | |||
404 | chip->last_recbank = 2; | ||
405 | chip->captureLimit = pcm_count * (pcm_periods - 1); | ||
406 | chip->capturePeriods = pcm_periods; | ||
407 | writew(PCTODSP_OFFSET(0 * DAQDS__size), chip->DARQ + JQS_wHead); | ||
408 | writew(PCTODSP_OFFSET(chip->last_recbank * DAQDS__size), | ||
409 | chip->DARQ + JQS_wTail); | ||
410 | |||
411 | #if 0 /* Critical section: bank 1 access. this is how the OSS driver does it:*/ | ||
412 | spin_lock_irqsave(&chip->lock, flags); | ||
413 | outb(HPBLKSEL_1, chip->io + HP_BLKS); | ||
414 | memset_io(chip->mappedbase, 0, DAR_BUFF_SIZE * 3); | ||
415 | outb(HPBLKSEL_0, chip->io + HP_BLKS); | ||
416 | spin_unlock_irqrestore(&chip->lock, flags); | ||
417 | #endif | ||
418 | |||
419 | chip->capturePeriodBytes = pcm_count; | ||
420 | snd_printdd("snd_msnd_capture_reset_queue() %i\n", pcm_count); | ||
421 | |||
422 | pDAQ = chip->mappedbase + DARQ_DATA_BUFF; | ||
423 | |||
424 | for (n = 0; n < pcm_periods; ++n, pDAQ += DAQDS__size) { | ||
425 | u32 tmp = pcm_count * n; | ||
426 | |||
427 | writew(PCTODSP_BASED(tmp + 0x3000), pDAQ + DAQDS_wStart); | ||
428 | writew(pcm_count, pDAQ + DAQDS_wSize); | ||
429 | writew(1, pDAQ + DAQDS_wFormat); | ||
430 | writew(chip->capture_sample_size, pDAQ + DAQDS_wSampleSize); | ||
431 | writew(chip->capture_channels, pDAQ + DAQDS_wChannels); | ||
432 | writew(chip->capture_sample_rate, pDAQ + DAQDS_wSampleRate); | ||
433 | writew(HIMT_RECORD_DONE * 0x100 + n, pDAQ + DAQDS_wIntMsg); | ||
434 | writew(n, pDAQ + DAQDS_wFlags); | ||
435 | } | ||
436 | } | ||
437 | |||
438 | static struct snd_pcm_hardware snd_msnd_playback = { | ||
439 | .info = SNDRV_PCM_INFO_MMAP | | ||
440 | SNDRV_PCM_INFO_INTERLEAVED | | ||
441 | SNDRV_PCM_INFO_MMAP_VALID, | ||
442 | .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, | ||
443 | .rates = SNDRV_PCM_RATE_8000_48000, | ||
444 | .rate_min = 8000, | ||
445 | .rate_max = 48000, | ||
446 | .channels_min = 1, | ||
447 | .channels_max = 2, | ||
448 | .buffer_bytes_max = 0x3000, | ||
449 | .period_bytes_min = 0x40, | ||
450 | .period_bytes_max = 0x1800, | ||
451 | .periods_min = 2, | ||
452 | .periods_max = 3, | ||
453 | .fifo_size = 0, | ||
454 | }; | ||
455 | |||
456 | static struct snd_pcm_hardware snd_msnd_capture = { | ||
457 | .info = SNDRV_PCM_INFO_MMAP | | ||
458 | SNDRV_PCM_INFO_INTERLEAVED | | ||
459 | SNDRV_PCM_INFO_MMAP_VALID, | ||
460 | .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, | ||
461 | .rates = SNDRV_PCM_RATE_8000_48000, | ||
462 | .rate_min = 8000, | ||
463 | .rate_max = 48000, | ||
464 | .channels_min = 1, | ||
465 | .channels_max = 2, | ||
466 | .buffer_bytes_max = 0x3000, | ||
467 | .period_bytes_min = 0x40, | ||
468 | .period_bytes_max = 0x1800, | ||
469 | .periods_min = 2, | ||
470 | .periods_max = 3, | ||
471 | .fifo_size = 0, | ||
472 | }; | ||
473 | |||
474 | |||
475 | static int snd_msnd_playback_open(struct snd_pcm_substream *substream) | ||
476 | { | ||
477 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
478 | struct snd_msnd *chip = snd_pcm_substream_chip(substream); | ||
479 | |||
480 | set_bit(F_AUDIO_WRITE_INUSE, &chip->flags); | ||
481 | clear_bit(F_WRITING, &chip->flags); | ||
482 | snd_msnd_enable_irq(chip); | ||
483 | |||
484 | runtime->dma_area = chip->mappedbase; | ||
485 | runtime->dma_bytes = 0x3000; | ||
486 | |||
487 | chip->playback_substream = substream; | ||
488 | runtime->hw = snd_msnd_playback; | ||
489 | return 0; | ||
490 | } | ||
491 | |||
492 | static int snd_msnd_playback_close(struct snd_pcm_substream *substream) | ||
493 | { | ||
494 | struct snd_msnd *chip = snd_pcm_substream_chip(substream); | ||
495 | |||
496 | snd_msnd_disable_irq(chip); | ||
497 | clear_bit(F_AUDIO_WRITE_INUSE, &chip->flags); | ||
498 | return 0; | ||
499 | } | ||
500 | |||
501 | |||
502 | static int snd_msnd_playback_hw_params(struct snd_pcm_substream *substream, | ||
503 | struct snd_pcm_hw_params *params) | ||
504 | { | ||
505 | int i; | ||
506 | struct snd_msnd *chip = snd_pcm_substream_chip(substream); | ||
507 | void *pDAQ = chip->mappedbase + DAPQ_DATA_BUFF; | ||
508 | |||
509 | chip->play_sample_size = snd_pcm_format_width(params_format(params)); | ||
510 | chip->play_channels = params_channels(params); | ||
511 | chip->play_sample_rate = params_rate(params); | ||
512 | |||
513 | for (i = 0; i < 3; ++i, pDAQ += DAQDS__size) { | ||
514 | writew(chip->play_sample_size, pDAQ + DAQDS_wSampleSize); | ||
515 | writew(chip->play_channels, pDAQ + DAQDS_wChannels); | ||
516 | writew(chip->play_sample_rate, pDAQ + DAQDS_wSampleRate); | ||
517 | } | ||
518 | /* dont do this here: | ||
519 | * snd_msnd_calibrate_adc(chip->play_sample_rate); | ||
520 | */ | ||
521 | |||
522 | return 0; | ||
523 | } | ||
524 | |||
525 | static int snd_msnd_playback_prepare(struct snd_pcm_substream *substream) | ||
526 | { | ||
527 | struct snd_msnd *chip = snd_pcm_substream_chip(substream); | ||
528 | unsigned int pcm_size = snd_pcm_lib_buffer_bytes(substream); | ||
529 | unsigned int pcm_count = snd_pcm_lib_period_bytes(substream); | ||
530 | unsigned int pcm_periods = pcm_size / pcm_count; | ||
531 | |||
532 | snd_msnd_play_reset_queue(chip, pcm_periods, pcm_count); | ||
533 | chip->playDMAPos = 0; | ||
534 | return 0; | ||
535 | } | ||
536 | |||
537 | static int snd_msnd_playback_trigger(struct snd_pcm_substream *substream, | ||
538 | int cmd) | ||
539 | { | ||
540 | struct snd_msnd *chip = snd_pcm_substream_chip(substream); | ||
541 | int result = 0; | ||
542 | |||
543 | if (cmd == SNDRV_PCM_TRIGGER_START) { | ||
544 | snd_printdd("snd_msnd_playback_trigger(START)\n"); | ||
545 | chip->banksPlayed = 0; | ||
546 | set_bit(F_WRITING, &chip->flags); | ||
547 | snd_msnd_DAPQ(chip, 1); | ||
548 | } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { | ||
549 | snd_printdd("snd_msnd_playback_trigger(STop)\n"); | ||
550 | /* interrupt diagnostic, comment this out later */ | ||
551 | clear_bit(F_WRITING, &chip->flags); | ||
552 | snd_msnd_send_dsp_cmd(chip, HDEX_PLAY_STOP); | ||
553 | } else { | ||
554 | snd_printd(KERN_ERR "snd_msnd_playback_trigger(?????)\n"); | ||
555 | result = -EINVAL; | ||
556 | } | ||
557 | |||
558 | snd_printdd("snd_msnd_playback_trigger() ENDE\n"); | ||
559 | return result; | ||
560 | } | ||
561 | |||
562 | static snd_pcm_uframes_t | ||
563 | snd_msnd_playback_pointer(struct snd_pcm_substream *substream) | ||
564 | { | ||
565 | struct snd_msnd *chip = snd_pcm_substream_chip(substream); | ||
566 | |||
567 | return bytes_to_frames(substream->runtime, chip->playDMAPos); | ||
568 | } | ||
569 | |||
570 | |||
571 | static struct snd_pcm_ops snd_msnd_playback_ops = { | ||
572 | .open = snd_msnd_playback_open, | ||
573 | .close = snd_msnd_playback_close, | ||
574 | .ioctl = snd_pcm_lib_ioctl, | ||
575 | .hw_params = snd_msnd_playback_hw_params, | ||
576 | .prepare = snd_msnd_playback_prepare, | ||
577 | .trigger = snd_msnd_playback_trigger, | ||
578 | .pointer = snd_msnd_playback_pointer, | ||
579 | }; | ||
580 | |||
581 | static int snd_msnd_capture_open(struct snd_pcm_substream *substream) | ||
582 | { | ||
583 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
584 | struct snd_msnd *chip = snd_pcm_substream_chip(substream); | ||
585 | |||
586 | set_bit(F_AUDIO_READ_INUSE, &chip->flags); | ||
587 | snd_msnd_enable_irq(chip); | ||
588 | runtime->dma_area = chip->mappedbase + 0x3000; | ||
589 | runtime->dma_bytes = 0x3000; | ||
590 | memset(runtime->dma_area, 0, runtime->dma_bytes); | ||
591 | chip->capture_substream = substream; | ||
592 | runtime->hw = snd_msnd_capture; | ||
593 | return 0; | ||
594 | } | ||
595 | |||
596 | static int snd_msnd_capture_close(struct snd_pcm_substream *substream) | ||
597 | { | ||
598 | struct snd_msnd *chip = snd_pcm_substream_chip(substream); | ||
599 | |||
600 | snd_msnd_disable_irq(chip); | ||
601 | clear_bit(F_AUDIO_READ_INUSE, &chip->flags); | ||
602 | return 0; | ||
603 | } | ||
604 | |||
605 | static int snd_msnd_capture_prepare(struct snd_pcm_substream *substream) | ||
606 | { | ||
607 | struct snd_msnd *chip = snd_pcm_substream_chip(substream); | ||
608 | unsigned int pcm_size = snd_pcm_lib_buffer_bytes(substream); | ||
609 | unsigned int pcm_count = snd_pcm_lib_period_bytes(substream); | ||
610 | unsigned int pcm_periods = pcm_size / pcm_count; | ||
611 | |||
612 | snd_msnd_capture_reset_queue(chip, pcm_periods, pcm_count); | ||
613 | chip->captureDMAPos = 0; | ||
614 | return 0; | ||
615 | } | ||
616 | |||
617 | static int snd_msnd_capture_trigger(struct snd_pcm_substream *substream, | ||
618 | int cmd) | ||
619 | { | ||
620 | struct snd_msnd *chip = snd_pcm_substream_chip(substream); | ||
621 | |||
622 | if (cmd == SNDRV_PCM_TRIGGER_START) { | ||
623 | chip->last_recbank = -1; | ||
624 | set_bit(F_READING, &chip->flags); | ||
625 | if (snd_msnd_send_dsp_cmd(chip, HDEX_RECORD_START) == 0) | ||
626 | return 0; | ||
627 | |||
628 | clear_bit(F_READING, &chip->flags); | ||
629 | } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { | ||
630 | clear_bit(F_READING, &chip->flags); | ||
631 | snd_msnd_send_dsp_cmd(chip, HDEX_RECORD_STOP); | ||
632 | return 0; | ||
633 | } | ||
634 | return -EINVAL; | ||
635 | } | ||
636 | |||
637 | |||
638 | static snd_pcm_uframes_t | ||
639 | snd_msnd_capture_pointer(struct snd_pcm_substream *substream) | ||
640 | { | ||
641 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
642 | struct snd_msnd *chip = snd_pcm_substream_chip(substream); | ||
643 | |||
644 | return bytes_to_frames(runtime, chip->captureDMAPos); | ||
645 | } | ||
646 | |||
647 | |||
648 | static int snd_msnd_capture_hw_params(struct snd_pcm_substream *substream, | ||
649 | struct snd_pcm_hw_params *params) | ||
650 | { | ||
651 | int i; | ||
652 | struct snd_msnd *chip = snd_pcm_substream_chip(substream); | ||
653 | void *pDAQ = chip->mappedbase + DARQ_DATA_BUFF; | ||
654 | |||
655 | chip->capture_sample_size = snd_pcm_format_width(params_format(params)); | ||
656 | chip->capture_channels = params_channels(params); | ||
657 | chip->capture_sample_rate = params_rate(params); | ||
658 | |||
659 | for (i = 0; i < 3; ++i, pDAQ += DAQDS__size) { | ||
660 | writew(chip->capture_sample_size, pDAQ + DAQDS_wSampleSize); | ||
661 | writew(chip->capture_channels, pDAQ + DAQDS_wChannels); | ||
662 | writew(chip->capture_sample_rate, pDAQ + DAQDS_wSampleRate); | ||
663 | } | ||
664 | return 0; | ||
665 | } | ||
666 | |||
667 | |||
668 | static struct snd_pcm_ops snd_msnd_capture_ops = { | ||
669 | .open = snd_msnd_capture_open, | ||
670 | .close = snd_msnd_capture_close, | ||
671 | .ioctl = snd_pcm_lib_ioctl, | ||
672 | .hw_params = snd_msnd_capture_hw_params, | ||
673 | .prepare = snd_msnd_capture_prepare, | ||
674 | .trigger = snd_msnd_capture_trigger, | ||
675 | .pointer = snd_msnd_capture_pointer, | ||
676 | }; | ||
677 | |||
678 | |||
679 | int snd_msnd_pcm(struct snd_card *card, int device, | ||
680 | struct snd_pcm **rpcm) | ||
681 | { | ||
682 | struct snd_msnd *chip = card->private_data; | ||
683 | struct snd_pcm *pcm; | ||
684 | int err; | ||
685 | |||
686 | err = snd_pcm_new(card, "MSNDPINNACLE", device, 1, 1, &pcm); | ||
687 | if (err < 0) | ||
688 | return err; | ||
689 | |||
690 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_msnd_playback_ops); | ||
691 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_msnd_capture_ops); | ||
692 | |||
693 | pcm->private_data = chip; | ||
694 | strcpy(pcm->name, "Hurricane"); | ||
695 | |||
696 | |||
697 | if (rpcm) | ||
698 | *rpcm = pcm; | ||
699 | return 0; | ||
700 | } | ||
701 | EXPORT_SYMBOL(snd_msnd_pcm); | ||
702 | |||
703 | MODULE_DESCRIPTION("Common routines for Turtle Beach Multisound drivers"); | ||
704 | MODULE_LICENSE("GPL"); | ||
705 | |||