initial import
[dosrtxon] / libs / mikmod / drivers / dos / dossb.c
1 /*      MikMod sound library
2         (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for
3         complete list.
4
5         This library is free software; you can redistribute it and/or modify
6         it under the terms of the GNU Library General Public License as
7         published by the Free Software Foundation; either version 2 of
8         the License, or (at your option) any later version.
9
10         This program is distributed in the hope that it will be useful,
11         but WITHOUT ANY WARRANTY; without even the implied warranty of
12         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13         GNU Library General Public License for more details.
14
15         You should have received a copy of the GNU Library General Public
16         License along with this library; if not, write to the Free Software
17         Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
18         02111-1307, USA.
19 */
20
21 /*==============================================================================
22
23   Sound Blaster I/O routines, common for SB8, SBPro and SB16
24   Written by Andrew Zabolotny <bit@eltech.ru>
25
26 ==============================================================================*/
27
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
31
32 #ifdef DRV_SB
33
34 #include <stdlib.h>
35 #include <dpmi.h>
36 #include <go32.h>
37 #include <dos.h>
38 #include <sys/nearptr.h>
39 #include <sys/farptr.h>
40 #include <string.h>
41
42 #include "dossb.h"
43
44 /********************************************* Private variables/routines *****/
45
46 __sb_state sb;
47
48 /* Wait for SoundBlaster for some time */
49 #if !defined(__GNUC__) || (__GNUC__ < 3) || (__GNUC__ == 3 && __GNUC_MINOR__ == 0)
50 # define _func_noinline volatile /* match original code */
51 # define _func_noclone
52 #else
53 /* avoid warnings from newer gcc:
54  * "function definition has qualified void return type" and
55  * function return types not compatible due to 'volatile' */
56 # define _func_noinline __attribute__((__noinline__))
57 # if (__GNUC__ < 4) || (__GNUC__ == 4 && __GNUC_MINOR__ < 5)
58 #  define _func_noclone
59 # else
60 #  define _func_noclone __attribute__((__noclone__))
61 # endif
62 #endif
63 _func_noinline
64 _func_noclone
65  void __sb_wait()
66 {
67         inportb(SB_DSP_RESET);
68         inportb(SB_DSP_RESET);
69         inportb(SB_DSP_RESET);
70         inportb(SB_DSP_RESET);
71         inportb(SB_DSP_RESET);
72         inportb(SB_DSP_RESET);
73 }
74
75 static void sb_irq()
76 {
77         /* Make sure its not a spurious IRQ */
78         if (!irq_check(sb.irq_handle))
79                 return;
80
81         sb.irqcount++;
82
83         /* Acknowledge DMA transfer is complete */
84         if (sb.mode & SBMODE_16BITS)
85                 __sb_dsp_ack_dma16();
86         else
87                 __sb_dsp_ack_dma8();
88
89         /* SoundBlaster 1.x cannot do autoinit ... */
90         if (sb.dspver < SBVER_20)
91                 __sb_dspreg_outwlh(SBDSP_DMA_PCM8, (sb.dma_buff->size >> 1) - 1);
92
93         /* Send EOI */
94         irq_ack(sb.irq_handle);
95
96         enable();
97         if (sb.timer_callback)
98                 sb.timer_callback();
99 }
100
101 static void sb_irq_end()
102 {
103 }
104
105 static boolean __sb_reset()
106 {
107         /* Disable the output */
108         sb_output(FALSE);
109
110         /* Clear pending ints if any */
111         __sb_dsp_ack_dma8();
112         __sb_dsp_ack_dma16();
113
114         /* Reset the DSP */
115         outportb(SB_DSP_RESET, SBM_DSP_RESET);
116         __sb_wait();
117         __sb_wait();
118         outportb(SB_DSP_RESET, 0);
119
120         /* Now wait for AA coming from datain port */
121         if (__sb_dsp_in() != 0xaa)
122                 return FALSE;
123
124         /* Finally, get the DSP version */
125         if ((sb.dspver = __sb_dsp_version()) == 0xffff)
126                 return FALSE;
127         /* Check again */
128         if (sb.dspver != __sb_dsp_version())
129                 return FALSE;
130
131         return TRUE;
132 }
133
134 /***************************************************** SB detection stuff *****/
135
136 static int __sb_irq_irqdetect(int irqno)
137 {
138         __sb_dsp_ack_dma8();
139         return 1;
140 }
141
142 static void __sb_irq_dmadetect()
143 {
144         /* Make sure its not a spurious IRQ */
145         if (!irq_check(sb.irq_handle))
146                 return;
147
148         sb.irqcount++;
149
150         /* Acknowledge DMA transfer is complete */
151         if (sb.mode & SBMODE_16BITS)
152                 __sb_dsp_ack_dma16();
153         else
154                 __sb_dsp_ack_dma8();
155
156         /* Send EOI */
157         irq_ack(sb.irq_handle);
158 }
159
160 static boolean __sb_detect()
161 {
162         /* First find the port number */
163         if (!sb.port) {
164                 int i;
165                 for (i = 5; i >= 0; i--) {
166                         sb.port = 0x210 + i * 0x10;
167                         if (__sb_reset())
168                                 break;
169                 }
170                 if (i < 0) {
171                         sb.port = 0;
172                         return FALSE;
173                 }
174         }
175
176         /* Now detect the IRQ and DMA numbers */
177         if (!sb.irq) {
178                 unsigned int irqmask, sbirqmask, sbirqcount;
179                 unsigned long timer;
180
181                 /* IRQ can be one of 2,3,5,7,10 */
182                 irq_detect_start(0x04ac, __sb_irq_irqdetect);
183
184                 /* Prepare timeout counter */
185                 _farsetsel(_dos_ds);
186                 timer = _farnspeekl(0x46c);
187
188                 sbirqmask = 0;
189                 sbirqcount = 10;                /* Emit 10 SB irqs */
190
191                 /* Tell SoundBlaster to emit IRQ for 8-bit transfers */
192                 __sb_dsp_out(SBDSP_GEN_IRQ8);
193                 __sb_wait();
194                 for (;;) {
195                         irq_detect_get(0, &irqmask);
196                         if (irqmask) {
197                                 sbirqmask |= irqmask;
198                                 if (!--sbirqcount)
199                                         break;
200                                 __sb_dsp_out(SBDSP_GEN_IRQ8);
201                         }
202                         if (_farnspeekl(0x46c) - timer >= 9)    /* Wait ~1/2 secs */
203                                 break;
204                 }
205                 if (sbirqmask)
206                         for (sb.irq = 15; sb.irq > 0; sb.irq--)
207                                 if (irq_detect_get(sb.irq, &irqmask) == 10)
208                                         break;
209
210                 irq_detect_end();
211                 if (!sb.irq)
212                         return FALSE;
213         }
214
215         /* Detect the 8-bit and 16-bit DMAs */
216         if (!sb.dma8 || ((sb.dspver >= SBVER_16) && !sb.dma16)) {
217                 static int __dma8[] = { 0, 1, 3 };
218                 static int __dma16[] = { 5, 6, 7 };
219                 int *dma;
220
221                 sb_output(FALSE);
222                 /* Temporary hook SB IRQ */
223                 sb.irq_handle = irq_hook(sb.irq, __sb_irq_dmadetect, 200);
224                 irq_enable(sb.irq_handle);
225                 if (sb.irq > 7)
226                         _irq_enable(2);
227
228                 /* Start a short DMA transfer and check if IRQ happened */
229                 for (;;) {
230                         int i;
231                         unsigned int timer, oldcount;
232
233                         if (!sb.dma8)
234                                 dma = &sb.dma8;
235                         else if ((sb.dspver >= SBVER_16) && !sb.dma16)
236                                 dma = &sb.dma16;
237                         else
238                                 break;
239
240                         for (i = 0; i < 3; i++) {
241                                 boolean success = 1;
242
243                                 *dma = (dma == &sb.dma8) ? __dma8[i] : __dma16[i];
244                                 oldcount = sb.irqcount;
245
246                                 dma_disable(*dma);
247                                 dma_set_mode(*dma, DMA_MODE_WRITE);
248                                 dma_clear_ff(*dma);
249                                 dma_set_count(*dma, 2);
250                                 dma_enable(*dma);
251
252                                 __sb_dspreg_out(SBDSP_SET_TIMING, 206); /* 20KHz */
253                                 if (dma == &sb.dma8) {
254                                         sb.mode = 0;
255                                         __sb_dspreg_outwlh(SBDSP_DMA_PCM8, 1);
256                                 } else {
257                                         sb.mode = SBMODE_16BITS;
258                                         __sb_dspreg_out(SBDSP_DMA_GENERIC16, 0);
259                                         __sb_dsp_out(0);
260                                         __sb_dsp_out(1);
261                                 }
262
263                                 _farsetsel(_dos_ds);
264                                 timer = _farnspeekl(0x46c);
265
266                                 while (oldcount == sb.irqcount)
267                                         if (_farnspeekl(0x46c) - timer >= 2) {
268                                                 success = 0;
269                                                 break;
270                                         }
271                                 dma_disable(*dma);
272                                 if (success)
273                                         break;
274                                 *dma = 0;
275                         }
276                         if (!*dma)
277                                 break;
278                 }
279
280                 irq_unhook(sb.irq_handle);
281                 sb.irq_handle = NULL;
282                 if (!sb.dma8 || ((sb.dspver >= SBVER_16) && !sb.dma16))
283                         return FALSE;
284         }
285         return TRUE;
286 }
287
288 /*************************************************** High-level interface *****/
289
290 /* Detect whenever SoundBlaster is present and fill "sb" structure */
291 boolean sb_detect()
292 {
293         char *env;
294
295         /* Try to find the port and DMA from environment */
296         env = getenv("BLASTER");
297
298         while (env && *env) {
299                 /* Skip whitespace */
300                 while ((*env == ' ') || (*env == '\t'))
301                         env++;
302                 if (!*env)
303                         break;
304
305                 switch (*env++) {
306                   case 'A':
307                   case 'a':
308                         if (!sb.port)
309                                 sb.port = strtol(env, &env, 16);
310                         break;
311                   case 'E':
312                   case 'e':
313                         if (!sb.aweport)
314                                 sb.aweport = strtol(env, &env, 16);
315                         break;
316                   case 'I':
317                   case 'i':
318                         if (!sb.irq)
319                                 sb.irq = strtol(env, &env, 10);
320                         break;
321                   case 'D':
322                   case 'd':
323                         if (!sb.dma8)
324                                 sb.dma8 = strtol(env, &env, 10);
325                         break;
326                   case 'H':
327                   case 'h':
328                         if (!sb.dma16)
329                                 sb.dma16 = strtol(env, &env, 10);
330                         break;
331                   default:
332                         /* Skip other values (H == MIDI, T == model, any other?) */
333                         while (*env && (*env != ' ') && (*env != '\t'))
334                                 env++;
335                         break;
336                 }
337         }
338
339         /* Try to detect missing sound card parameters */
340         __sb_detect();
341
342         if (!sb.port || !sb.irq || !sb.dma8)
343                 return FALSE;
344
345         if (!__sb_reset())
346                 return FALSE;
347
348         if ((sb.dspver >= SBVER_16) && !sb.dma16)
349                 return FALSE;
350
351         if (sb.dspver >= SBVER_PRO)
352                 sb.caps |= SBMODE_STEREO;
353         if (sb.dspver >= SBVER_16 && sb.dma16)
354                 sb.caps |= SBMODE_16BITS;
355         if (sb.dspver < SBVER_20)
356                 sb.maxfreq_mono = 22222;
357         else
358                 sb.maxfreq_mono = 45454;
359         if (sb.dspver <= SBVER_16)
360                 sb.maxfreq_stereo = 22727;
361         else
362                 sb.maxfreq_stereo = 45454;
363
364         sb.ok = 1;
365         return TRUE;
366 }
367
368 /* Reset SoundBlaster */
369 void sb_reset()
370 {
371         sb_stop_dma();
372         __sb_reset();
373 }
374
375 /* Start working with SoundBlaster */
376 boolean sb_open()
377 {
378         __dpmi_meminfo struct_info;
379
380         if (!sb.ok)
381                 if (!sb_detect())
382                         return FALSE;
383
384         if (sb.open)
385                 return FALSE;
386
387         /* Now lock the sb structure in memory */
388         struct_info.address = __djgpp_base_address + (unsigned long)&sb;
389         struct_info.size = sizeof(sb);
390         if (__dpmi_lock_linear_region(&struct_info))
391                 return FALSE;
392
393         /* Hook the SB IRQ */
394         sb.irq_handle = irq_hook(sb.irq, sb_irq, (long)sb_irq_end - (long)sb_irq);
395         if (!sb.irq_handle) {
396                 __dpmi_unlock_linear_region(&struct_info);
397                 return FALSE;
398         }
399
400         /* Enable the interrupt */
401         irq_enable(sb.irq_handle);
402         if (sb.irq > 7)
403                 _irq_enable(2);
404
405         sb.open++;
406
407         return TRUE;
408 }
409
410 /* Finish working with SoundBlaster */
411 boolean sb_close()
412 {
413         __dpmi_meminfo struct_info;
414         if (!sb.open)
415                 return FALSE;
416
417         sb.open--;
418
419         /* Stop/free DMA buffer */
420         sb_stop_dma();
421
422         /* Unhook IRQ */
423         irq_unhook(sb.irq_handle);
424         sb.irq_handle = NULL;
425
426         /* Unlock the sb structure */
427         struct_info.address = __djgpp_base_address + (unsigned long)&sb;
428         struct_info.size = sizeof(sb);
429         __dpmi_unlock_linear_region(&struct_info);
430
431         return TRUE;
432 }
433
434 /* Enable/disable stereo DSP mode */
435 /* Enable/disable speaker output */
436 void sb_output(boolean enable)
437 {
438         __sb_dsp_out(enable ? SBDSP_SPEAKER_ENA : SBDSP_SPEAKER_DIS);
439 }
440
441 /* Start playing from DMA buffer */
442 boolean sb_start_dma(unsigned char mode, unsigned int freq)
443 {
444         int dmachannel = (mode & SBMODE_16BITS) ? sb.dma16 : sb.dma8;
445         int dmabuffsize;
446         unsigned int tc = 0;            /* timing constant (<=sbpro only) */
447
448         /* Stop DMA transfer if it is enabled */
449         sb_stop_dma();
450
451         /* Sanity check */
452         if ((mode & SBMODE_MASK & sb.caps) != (mode & SBMODE_MASK))
453                 return FALSE;
454
455         /* Check this SB can perform at requested frequency */
456         if (((mode & SBMODE_STEREO) && (freq > sb.maxfreq_stereo))
457                 || (!(mode & SBMODE_STEREO) && (freq > sb.maxfreq_mono)))
458                 return FALSE;
459
460         /* Check the timing constant here to avoid failing later */
461         if (sb.dspver < SBVER_16) {
462                 /* SBpro cannot do signed transfer */
463                 if (mode & SBMODE_SIGNED)
464                         return FALSE;
465
466                 /* Old SBs have a different way on setting DMA timing constant */
467                 tc = freq;
468                 if (mode & SBMODE_STEREO)
469                         tc *= 2;
470                 tc = 1000000 / tc;
471                 if (tc > 255)
472                         return FALSE;
473         }
474
475         sb.mode = mode;
476
477         /* Get a DMA buffer enough for a 1/4sec interval... 4K <= dmasize <= 32K */
478         dmabuffsize = freq;
479         if (mode & SBMODE_STEREO)
480                 dmabuffsize *= 2;
481         if (mode & SBMODE_16BITS)
482                 dmabuffsize *= 2;
483         dmabuffsize >>= 2;
484         if (dmabuffsize < 4096)
485                 dmabuffsize = 4096;
486         if (dmabuffsize > 32768)
487                 dmabuffsize = 32768;
488         dmabuffsize = (dmabuffsize + 255) & 0xffffff00;
489
490         sb.dma_buff = dma_allocate(dmachannel, dmabuffsize);
491         if (!sb.dma_buff)
492                 return FALSE;
493
494         /* Fill DMA buffer with silence */
495         dmabuffsize = sb.dma_buff->size;
496         if (mode & SBMODE_SIGNED)
497                 memset(sb.dma_buff->linear, 0, dmabuffsize);
498         else
499                 memset(sb.dma_buff->linear, 0x80, dmabuffsize);
500
501         /* Prime DMA for transfer */
502         dma_start(sb.dma_buff, dmabuffsize, DMA_MODE_WRITE | DMA_MODE_AUTOINIT);
503
504         /* Tell SoundBlaster to start transfer */
505         if (sb.dspver >= SBVER_16) {    /* SB16 */
506                 __sb_dspreg_outwhl(SBDSP_SET_RATE, freq);
507
508                 /* Start DMA->DAC transfer */
509                 __sb_dspreg_out(SBM_GENDAC_AUTOINIT | SBM_GENDAC_FIFO |
510                                                 ((mode & SBMODE_16BITS) ? SBDSP_DMA_GENERIC16 :
511                                                  SBDSP_DMA_GENERIC8),
512                                                 ((mode & SBMODE_SIGNED) ? SBM_GENDAC_SIGNED : 0) |
513                                                 ((mode & SBMODE_STEREO) ? SBM_GENDAC_STEREO : 0));
514
515                 /* Write the length of transfer */
516                 dmabuffsize = (dmabuffsize >> 2) - 1;
517                 __sb_dsp_out(dmabuffsize);
518                 __sb_dsp_out(dmabuffsize >> 8);
519         } else {
520                 __sb_dspreg_out(SBDSP_SET_TIMING, 256 - tc);
521                 dmabuffsize = (dmabuffsize >> 1) - 1;
522                 if (sb.dspver >= SBVER_20) {    /* SB 2.0/Pro */
523                         /* Set stereo mode */
524                         __sb_stereo((mode & SBMODE_STEREO) ? TRUE : FALSE);
525                         __sb_dspreg_outwlh(SBDSP_SET_DMA_BLOCK, dmabuffsize);
526                         if (sb.dspver >= SBVER_PRO)
527                                 __sb_dsp_out(SBDSP_HS_DMA_DAC8_AUTO);
528                         else
529                                 __sb_dsp_out(SBDSP_DMA_PCM8_AUTO);
530                 } else {                                /* Original SB */
531                         /* Start DMA->DAC transfer */
532                         __sb_dspreg_outwlh(SBDSP_DMA_PCM8, dmabuffsize);
533                 }
534         }
535
536         return TRUE;
537 }
538
539 /* Stop playing from DMA buffer */
540 void sb_stop_dma()
541 {
542         if (!sb.dma_buff)
543                 return;
544
545         if (sb.mode & SBMODE_16BITS)
546                 __sb_dsp_out(SBDSP_DMA_HALT16);
547         else
548                 __sb_dsp_out(SBDSP_DMA_HALT8);
549
550         dma_disable(sb.dma_buff->channel);
551         dma_free(sb.dma_buff);
552         sb.dma_buff = NULL;
553 }
554
555 /* Query current position/total size of the DMA buffer */
556 void sb_query_dma(unsigned int *dma_size, unsigned int *dma_pos)
557 {
558         unsigned int dma_left;
559         *dma_size = sb.dma_buff->size;
560         /* It can happen we try to read DMA count when HI/LO bytes will be
561            inconsistent */
562         for (;;) {
563                 unsigned int dma_left_test;
564                 dma_clear_ff(sb.dma_buff->channel);
565                 dma_left_test = dma_get_count(sb.dma_buff->channel);
566                 dma_left = dma_get_count(sb.dma_buff->channel);
567                 if ((dma_left >= dma_left_test) && (dma_left - dma_left_test < 10))
568                         break;
569         }
570         *dma_pos = *dma_size - dma_left;
571 }
572
573 #endif /* DRV_SB */
574
575 /* ex:set ts=4: */