1 /*         ______   ___    ___
2  *        /\  _  \ /\_ \  /\_ \
3  *        \ \ \L\ \\//\ \ \//\ \      __     __   _ __   ___
4  *         \ \  __ \ \ \ \  \ \ \   /'__`\ /'_ `\/\`'__\/ __`\
5  *          \ \ \/\ \ \_\ \_ \_\ \_/\  __//\ \L\ \ \ \//\ \L\ \
6  *           \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
7  *            \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
8  *                                           /\____/
9  *                                           \_/__/
10  *
11  *      Ensoniq Soundscape driver.
12  *
13  *      By Andreas Kluge.
14  *
15  *      Based on code by Andrew P. Weir.
16  *
17  *      See readme.txt for copyright information.
18  */
19 
20 
21 #include <stdio.h>
22 #include <string.h>
23 
24 #include "allegro.h"
25 #include "allegro/internal/aintern.h"
26 #include "allegro/platform/aintdos.h"
27 
28 #ifndef ALLEGRO_DOS
29    #error something is wrong with the makefile
30 #endif
31 
32 
33 
34 #define ODIE            0       /* ODIE gate array */
35 #define OPUS            1       /* OPUS gate array */
36 #define MMIC            2       /* MiMIC gate array */
37 
38 static int soundscape_hw_ver = -1;      /* as reported by detection */
39 
40 static char *ensoniq_gate_array[] = { "ODIE", "OPUS", "MiMIC" };
41 
42 
43 #define GA_HOSTCTL_OFF  2       /* host port ctrl/stat reg */
44 #define GA_ADDR_OFF     4       /* indirect address reg */
45 #define GA_DATA_OFF     5       /* indirect data reg */
46 #define GA_CODEC_OFF    8       /* for some boards CoDec is fixed from base */
47 
48 #define GA_DMAB_REG     3       /* DMA chan B assign reg */
49 #define GA_INTCFG_REG   4       /* interrupt configuration reg */
50 #define GA_DMACFG_REG   5       /* DMA configuration reg */
51 #define GA_CDCFG_REG    6       /* CD-ROM/CoDec config reg */
52 #define GA_HMCTL_REG    9       /* host master control reg */
53 
54 #define CD_ADDR_OFF     0       /* indirect address reg */
55 #define CD_DATA_OFF     1       /* indirect data reg */
56 #define CD_STATUS_OFF   2       /* status register */
57 
58 
59 #define OUT_TO_ADDR(n)  outportb(soundscape_waveport + CD_ADDR_OFF, n)
60 
61 #define CODEC_MODE_CHANGE_ON()   OUT_TO_ADDR(0x40)
62 #define CODEC_MODE_CHANGE_OFF()  OUT_TO_ADDR(0x00)
63 
64 
65 #define CD_ADCL_REG     0       /* left DAC input control reg */
66 #define CD_ADCR_REG     1       /* right DAC input control reg */
67 #define CD_CDAUXL_REG   2       /* left DAC output control reg */
68 #define CD_CDAUXR_REG   3       /* right DAC output control reg */
69 #define CD_DACL_REG     6       /* left DAC output control reg */
70 #define CD_DACR_REG     7       /* right DAC output control reg */
71 #define CD_FORMAT_REG   8       /* clock and data format reg */
72 #define CD_CONFIG_REG   9       /* interface config register */
73 #define CD_PINCTL_REG   10      /* external pin control reg */
74 #define CD_UCOUNT_REG   14      /* upper count reg */
75 #define CD_LCOUNT_REG   15      /* lower count reg */
76 #define CD_XFORMAT_REG  28      /* extended format reg - 1845 record */
77 #define CD_XUCOUNT_REG  30      /* extended upper count reg - 1845 record */
78 #define CD_XLCOUNT_REG  31      /* extended lower count reg - 1845 record */
79 
80 
81 #define ENABLE_CODEC_IRQ()    cd_write(CD_PINCTL_REG, cd_read(CD_PINCTL_REG) | 0x02)
82 #define DISABLE_CODEC_IRQ()   cd_write(CD_PINCTL_REG, cd_read(CD_PINCTL_REG) & 0xFD);
83 
84 
85 static int soundscape_enabled = FALSE;
86 static int soundscape_mem_allocated = FALSE;
87 static int soundscape_dma_count = 0;
88 static int soundscape_dma;
89 static int soundscape_freq;
90 static int soundscape_baseport;              /* gate Array/MPU-401 port */
91 static int soundscape_waveport;              /* the AD-1848 base port */
92 static int soundscape_midiirq;               /* the MPU-401 IRQ */
93 static int soundscape_waveirq;               /* the PCM IRQ */
94 
95 static int soundscape_detect(int input);
96 static int soundscape_init(int input, int voices);
97 static void soundscape_exit(int input);
98 static int soundscape_set_mixer_volume(int volume);
99 static int soundscape_buffer_size(void);
100 
101 static int soundscape_int = -1;              /* interrupt vector */
102 static int soundscape_dma_size = -1;         /* size of dma transfer */
103 
104 static volatile int soundscape_semaphore = FALSE;
105 
106 static int soundscape_sel;                   /* selector for the DMA buffer */
107 static unsigned long soundscape_buf[2];      /* pointers to the two buffers */
108 static int soundscape_bufnum = 0;            /* the one currently in use */
109 
110 static void soundscape_lock_mem(void);
111 
112 static char soundscape_desc[256] = EMPTY_STRING;
113 
114 static int cd_cfg_save;             /* gate array register save area */
115 static int dma_cfg_save;            /* gate array register save area */
116 static int int_cfg_save;            /* gate array register save area */
117 
118 static int dac_save_l;              /* DAC left volume save */
119 static int dac_save_r;              /* DAC right volume save */
120 static int cdx_save_l;              /* CD/Aux left volume save */
121 static int cdx_save_r;              /* CD/Aux right volume save */
122 static int adc_save_l;              /* ADC left volume save */
123 static int adc_save_r;              /* ADC right volume save */
124 
125 static int ss_irqs[4] = { 9, 5, 7, 10 };
126 static int rs_irqs[4] = { 9, 7, 5, 15 };
127 
128 static int *soundscape_irqset;      /* pointer to one of the IRQ sets */
129 
130 static int soundscape_detected = FALSE;
131 
132 
133 typedef struct                      /* DMA controller registers ... */
134 {
135    unsigned char addr;              /* address register, lower/upper */
136    unsigned char count;             /* address register, lower/upper */
137    unsigned char status;            /* status register */
138    unsigned char mask;              /* single channel mask register */
139    unsigned char mode;              /* mode register */
140    unsigned char clrff;             /* clear flip-flop register */
141    unsigned char page;              /* fixed page register */
142 } DMAC_REGS;
143 
144 
145 static DMAC_REGS dmac_regs[4] =      /* the DMAC regs for chans 0-3 */
146 {
147    { 0x00, 0x01, 0x08, 0x0A, 0x0B, 0x0C, 0x87 },
148    { 0x02, 0x03, 0x08, 0x0A, 0x0B, 0x0C, 0x83 },
149    { 0x04, 0x05, 0x08, 0x0A, 0x0B, 0x0C, 0x81 },
150    { 0x06, 0x07, 0x08, 0x0A, 0x0B, 0x0C, 0x82 }
151 };
152 
153 
154 static DMAC_REGS *dmac_reg_p;       /* a pointer to a DMAC reg struct */
155 
156 
157 
158 DIGI_DRIVER digi_soundscape =
159 {
160    DIGI_SOUNDSCAPE,
161    empty_string,
162    empty_string,
163    "Soundscape",
164    0, 0, MIXER_MAX_SFX, MIXER_DEF_SFX,
165    soundscape_detect,
166    soundscape_init,
167    soundscape_exit,
168    soundscape_set_mixer_volume,
169    NULL,
170    NULL,
171    NULL,
172    soundscape_buffer_size,
173    _mixer_init_voice,
174    _mixer_release_voice,
175    _mixer_start_voice,
176    _mixer_stop_voice,
177    _mixer_loop_voice,
178    _mixer_get_position,
179    _mixer_set_position,
180    _mixer_get_volume,
181    _mixer_set_volume,
182    _mixer_ramp_volume,
183    _mixer_stop_volume_ramp,
184    _mixer_get_frequency,
185    _mixer_set_frequency,
186    _mixer_sweep_frequency,
187    _mixer_stop_frequency_sweep,
188    _mixer_get_pan,
189    _mixer_set_pan,
190    _mixer_sweep_pan,
191    _mixer_stop_pan_sweep,
192    _mixer_set_echo,
193    _mixer_set_tremolo,
194    _mixer_set_vibrato,
195    0, 0,
196    NULL,
197    NULL,
198    NULL,
199    NULL,
200    NULL,
201    NULL
202 };
203 
204 
205 
206 /* soundscape_buffer_size:
207  *  Returns the current DMA buffer size, for use by the audiostream code.
208  */
soundscape_buffer_size(void)209 static int soundscape_buffer_size(void)
210 {
211    return soundscape_dma_size/4; /* convert bytes to stereo 16 bit samples */
212 }
213 
214 
215 
216 /* cd_write:
217  *  This function is used to write the indirect addressed registers in the
218  *  Ad-1848 or compatible CoDec. It will preserve the special function bits
219  *  in the upper-nibble of the indirect address register.
220  */
cd_write(int rnum,int value)221 static void cd_write(int rnum, int value)
222 {
223    OUT_TO_ADDR((inportb(soundscape_waveport + CD_ADDR_OFF) & 0xF0) | rnum);
224    outportb(soundscape_waveport + CD_DATA_OFF, value);
225 }
226 
227 
228 
229 /* cd_read:
230  *  This function is used to read the indirect addressed registers in the
231  *  AD-1848 or compatible CoDec. It will preserve the special function bits
232  *  in the upper-nibble of the indirect address register.
233  */
cd_read(int rnum)234 static int cd_read(int rnum)
235 {
236    OUT_TO_ADDR((inportb(soundscape_waveport + CD_ADDR_OFF) & 0xF0) | rnum);
237    return inportb(soundscape_waveport + CD_DATA_OFF);
238 }
239 
240 
241 
242 /* ga_write:
243  *  This function is used to write the indirect addressed registers in the
244  *  Ensoniq Soundscape gate array.
245  */
ga_write(int rnum,int value)246 static void ga_write(int rnum, int value)
247 {
248    outportb(soundscape_baseport + GA_ADDR_OFF, rnum);
249    outportb(soundscape_baseport + GA_DATA_OFF, value);
250 }
251 
252 
253 
254 /* ga_read:
255  *  This function is used to read the indirect addressed registers in the
256  *  Ensoniq Soundscape gate array.
257  */
ga_read(int rnum)258 static int ga_read(int rnum)
259 {
260    outportb(soundscape_baseport + GA_ADDR_OFF, rnum);
261    return inportb(soundscape_baseport + GA_DATA_OFF);
262 }
263 
264 
265 
266 /* set_dac_vol:
267  *  This function sets the left and right DAC output level in the CoDec.
268  */
set_dac_vol(int lvol,int rvol)269 static void set_dac_vol(int lvol, int rvol)
270 {
271    cd_write(CD_DACL_REG, ~(lvol >> 2) & 0x3F);
272    cd_write(CD_DACR_REG, ~(rvol >> 2) & 0x3F);
273 }
274 
275 
276 
277 /* resume_codec:
278  *  This function will resume the CoDec auto-restart DMA process.
279  */
resume_codec(int direction)280 static void resume_codec(int direction)
281 {
282    cd_write(CD_CONFIG_REG, direction ? 0x02 : 0x01);
283 }
284 
285 
286 
287 /* set_format:
288  *  This function sets the CoDec audio data format for record or playback.
289  */
set_format(int srate,int stereo,int size16bit,int direction)290 static int set_format(int srate, int stereo, int size16bit, int direction)
291 {
292    int format = 0;
293    int i;
294 
295    /* first, find the sample rate ... */
296    switch (srate) {
297 
298       case 5512:  format = 0x01; soundscape_dma_size = 512;  break;
299       case 6615:  format = 0x0F; soundscape_dma_size = 512;  break;
300       case 8000:  format = 0x00; soundscape_dma_size = 512;  break;
301       case 9600:  format = 0x0E; soundscape_dma_size = 512;  break;
302       case 11025: format = 0x03; soundscape_dma_size = 512;  break; /* 11363 */
303       case 16000: format = 0x02; soundscape_dma_size = 512;  break; /* 17046 */
304       case 18900: format = 0x05; soundscape_dma_size = 1024; break;
305       case 22050: format = 0x07; soundscape_dma_size = 1024; break; /* 22729 */
306       case 27428: format = 0x04; soundscape_dma_size = 1024; break;
307       case 32000: format = 0x06; soundscape_dma_size = 2048; break;
308       case 33075: format = 0x0D; soundscape_dma_size = 2048; break;
309       case 37800: format = 0x09; soundscape_dma_size = 2048; break;
310       case 44100: format = 0x0B; soundscape_dma_size = 2048; break; /* 44194 */
311       case 48000: format = 0x0C; soundscape_dma_size = 2048; break;
312 
313       default:
314 	 return FALSE;
315    }
316 
317    /* set other format bits ... */
318    if (stereo)
319       format |= 0x10;
320 
321    if (size16bit) {
322       format |= 0x40;
323       soundscape_dma_size *= 2;
324    }
325 
326    soundscape_freq = srate;
327 
328    CODEC_MODE_CHANGE_ON();
329 
330    /* and write the format register */
331    cd_write(CD_FORMAT_REG, format);
332 
333    /* if not using ODIE and recording, setup extended format register */
334    if ((soundscape_hw_ver) != ODIE && (direction))
335       cd_write(CD_XFORMAT_REG, format & 0x70);
336 
337    /* delay for internal re-sync */
338    for (i=0; i<200000; i++)
339       inportb(soundscape_baseport + GA_ADDR_OFF);
340 
341    CODEC_MODE_CHANGE_OFF();
342 
343    return TRUE;
344 }
345 
346 
347 
348 /* stop_codec:
349  *  This function will stop the CoDec auto-restart DMA process.
350  */
stop_codec(void)351 static void stop_codec(void)
352 {
353    int i;
354 
355    cd_write(CD_CONFIG_REG, cd_read(CD_CONFIG_REG) & 0xFC);
356 
357    /* let the CoDec receive its last DACK(s). The DMAC must not be */
358    /* masked while the CoDec has DRQs pending. */
359    for (i=0; i<64; i++) {
360       if (!(inportb(dmac_reg_p->status) & (0x10 << soundscape_dma)))
361 	 break;
362    }
363 }
364 
365 
366 
367 /* get_ini_config_entry:
368  *  This function parses a file (SNDSCAPE.INI) for a left-hand string and,
369  *  if found, writes its associated right-hand value to a destination buffer.
370  *  This function is case-insensitive.
371  */
get_ini_config_entry(char * entry,char * dest,unsigned int dest_size,FILE * fp)372 static int get_ini_config_entry(char *entry, char *dest, unsigned int dest_size, FILE *fp)
373 {
374    char str[83];
375    char tokstr[33];
376    char *p;
377 
378    /* make a local copy of the entry, upper-case it */
379    _al_sane_strncpy(tokstr, entry, sizeof(tokstr));
380    strupr(tokstr);
381 
382    /* rewind the file and try to find it... */
383    rewind(fp);
384 
385    for (;;) {
386       /* get the next string from the file */
387       fgets(str, 83, fp);
388       if (feof(fp)) {
389 	 fclose(fp);
390 	 return -1;
391       }
392 
393       /* properly terminate the string */
394       for (p=str; *p; p++) {
395 	 if (uisspace(*p)) {
396 	    *p = 0;
397 	    break;
398 	 }
399       }
400 
401       /* see if it's an 'equate' string; if so, zero the '=' */
402       p = strchr(str, '=');
403       if (!p)
404 	 continue;
405 
406       *p = 0;
407 
408       /* upper-case the current string and test it */
409       strupr(str);
410 
411       if (strcmp(str, tokstr))
412 	 continue;
413 
414       /* it's our string - copy the right-hand value to buffer */
415       _al_sane_strncpy(dest, str+strlen(str)+1, dest_size);
416       break;
417    }
418 
419    return 0;
420 }
421 
422 
423 
424 /* get_init_config:
425  *  This function gets all parameters from a file (SNDSCAPE.INI)
426  */
get_init_config(void)427 static int get_init_config(void)
428 {
429    FILE *fp = NULL;
430    char str[78];
431    char *ep;
432 
433    /* get the environment var and build the filename, then open it */
434    if (!(ep = getenv("SNDSCAPE")))
435       return FALSE;
436 
437    _al_sane_strncpy(str, ep, sizeof(str));
438 
439    if (str[strlen(str)-1] == '\\')
440       str[strlen(str)-1] = 0;
441 
442    strncat(str, "\\SNDSCAPE.INI", sizeof(str)-1);
443 
444    if (!(fp = fopen(str, "r")))
445       return FALSE;
446 
447    /* read all of the necessary config info ... */
448    if (get_ini_config_entry("Product", str, sizeof(str), fp)) {
449       fclose(fp);
450       return FALSE;
451    }
452 
453    /* if an old product name is read, set the IRQs accordingly */
454    strupr(str);
455    if (strstr(str, "SOUNDFX") || strstr(str, "MEDIA_FX"))
456       soundscape_irqset = rs_irqs;
457    else
458       soundscape_irqset = ss_irqs;
459 
460    if (get_ini_config_entry("Port", str, sizeof(str), fp)) {
461       fclose(fp);
462       return FALSE;
463    }
464 
465    soundscape_baseport = strtol(str, NULL, 16);
466 
467    if (get_ini_config_entry("WavePort", str, sizeof(str), fp)) {
468       fclose(fp);
469       return FALSE;
470    }
471 
472    soundscape_waveport = strtol(str, NULL, 16);
473 
474    if (get_ini_config_entry("IRQ", str, sizeof(str), fp)) {
475       fclose(fp);
476       return FALSE;
477    }
478 
479    soundscape_midiirq = strtol(str, NULL, 10);
480 
481    if (soundscape_midiirq == 2)
482       soundscape_midiirq = 9;
483 
484    if (get_ini_config_entry("SBIRQ", str, sizeof(str), fp)) {
485       fclose(fp);
486       return FALSE;
487    }
488 
489    soundscape_waveirq = strtol(str, NULL, 10);
490 
491    if (soundscape_waveirq == 2)
492       soundscape_waveirq = 9;
493 
494    if (get_ini_config_entry("DMA", str, sizeof(str), fp)) {
495       fclose(fp);
496       return FALSE;
497    }
498 
499    soundscape_dma = strtol(str, NULL, 10);
500 
501    fclose(fp);
502    return TRUE;
503 }
504 
505 
506 
507 /* detect_soundscape:
508  *  This function is used to detect the presence of a Soundscape card in a
509  *  system. It will read the hardware config info from the SNDSCAPE.INI file,
510  *  the path to which is indicated by the SNDSCAPE environment variable. This
511  *  config info will be stored in global variable space for the other driver
512  *  functions to reference. Once the config settings have been determined, a
513  *  hardware test will be performed to see if the Soundscape card is actually
514  *  present. If this function is not explicitly called by the application, it
515  *  it will be called by the OpenSoundscape function.
516  */
detect_soundscape(void)517 static int detect_soundscape(void)
518 {
519    int tmp;
520 
521    if (!get_init_config())
522       return FALSE;
523 
524    /* see if Soundscape is there by reading HW ... */
525    if ((inportb(soundscape_baseport + GA_HOSTCTL_OFF) & 0x78) != 0x00)
526       return FALSE;
527 
528    if ((inportb(soundscape_baseport + GA_ADDR_OFF) & 0xF0) == 0xF0)
529       return FALSE;
530 
531    outportb(soundscape_baseport + GA_ADDR_OFF, 0xF5);
532    tmp = inportb(soundscape_baseport + GA_ADDR_OFF);
533 
534    if ((tmp & 0xF0) == 0xF0)
535       return FALSE;
536 
537    if ((tmp & 0x0F) != 0x05)
538       return FALSE;
539 
540    /* formulate the chip ID */
541    if ((tmp & 0x80) != 0x00)
542       soundscape_hw_ver = MMIC;
543    else if ((tmp & 0x70) != 0x00)
544       soundscape_hw_ver = OPUS;
545    else
546       soundscape_hw_ver = ODIE;
547 
548    /* now do a quick check to make sure the CoDec is there too */
549    if ((inportb(soundscape_waveport) & 0x80) != 0x00)
550       return FALSE;
551 
552    soundscape_detected = TRUE;
553    return TRUE;
554 }
555 
556 
557 
558 /* soundscape_set_mixer_volume:
559  *  Sets the Soundscape mixer volume for playing digital samples.
560  */
soundscape_set_mixer_volume(int volume)561 static int soundscape_set_mixer_volume(int volume)
562 {
563    if (volume >= 0)
564       set_dac_vol(volume, volume);
565 
566    return 0;
567 }
568 
569 
570 
571 /* soundscape_interrupt:
572  *  The Soundscape end-of-buffer interrupt handler.
573  */
soundscape_interrupt(void)574 static int soundscape_interrupt(void)
575 {
576    /* if the CoDec is interrupting ... */
577    if (inportb(soundscape_waveport + CD_STATUS_OFF) & 0x01) {
578 
579       /* clear the AD-1848 interrupt */
580       outportb(soundscape_waveport + CD_STATUS_OFF, 0x00);
581 
582       soundscape_dma_count++;
583       if (soundscape_dma_count > 16) {
584 	 soundscape_bufnum = (_dma_todo(soundscape_dma) > (unsigned)soundscape_dma_size) ? 1 : 0;
585 	 soundscape_dma_count = 0;
586       }
587 
588       if (!(soundscape_semaphore)) {
589 	 soundscape_semaphore = TRUE;
590 
591 	 /* mix some more samples */
592 	 ENABLE();
593 	 _mix_some_samples(soundscape_buf[soundscape_bufnum], _dos_ds, TRUE);
594 	 DISABLE();
595 
596 	 soundscape_semaphore = FALSE;
597       }
598 
599       soundscape_bufnum = 1 - soundscape_bufnum;
600    }
601 
602    /* acknowledge interrupt */
603    _eoi(soundscape_waveirq);
604    return 0;
605 }
606 
607 END_OF_STATIC_FUNCTION(soundscape_interrupt);
608 
609 
610 
611 /* soundscape_detect:
612  *  This function is used to detect the presence of a Soundscape card.
613  */
soundscape_detect(int input)614 static int soundscape_detect(int input)
615 {
616    char tmp[64];
617 
618    /* input isn't supported yet */
619    if (input)
620       return FALSE;
621 
622    if (!detect_soundscape()) {
623       ustrzcpy(allegro_error, ALLEGRO_ERROR_SIZE, get_config_text("Soundscape not found"));
624       return FALSE;
625    }
626 
627    /* figure out the hardware interrupt number */
628    soundscape_int = _map_irq(soundscape_waveirq);
629 
630    /* get desired frequency from _sound variable */
631    soundscape_freq = _sound_freq;
632    if (soundscape_freq == -1)
633       soundscape_freq = 48000;
634 
635    /* adjust SB-frequencies for this CoDec */
636    if (soundscape_freq < 12000)              /* 11906 -> 11025 */
637       soundscape_freq = 11025;
638    else if (soundscape_freq < 18000)         /* 16129 -> 16000 */
639       soundscape_freq = 16000;
640    else if (soundscape_freq < 24000)         /* 22727 -> 22050 */
641       soundscape_freq = 22050;
642    else                                      /* 45454 -> 48000 */
643       soundscape_freq = 48000;
644 
645    /* set up the card description */
646    uszprintf(soundscape_desc, sizeof(soundscape_desc),
647              get_config_text("Soundscape %s (%d hz) on port %X, using IRQ %d, DMA channel %d and waveport %X"),
648              uconvert_ascii(ensoniq_gate_array[soundscape_hw_ver], tmp),
649 	     soundscape_freq, soundscape_baseport, soundscape_waveirq, soundscape_dma, soundscape_waveport);
650 
651    digi_soundscape.desc = soundscape_desc;
652 
653    return TRUE;
654 }
655 
656 
657 
658 /* soundscape_init:
659  *  This function opens the Soundscape driver. It will setup the Soundscape
660  *  hardware for Native PCM mode.
661  */
soundscape_init(int input,int voices)662 static int soundscape_init(int input, int voices)
663 {
664    int windx, mindx, tmp;
665 
666    /* see if we need to detect first */
667    if (!soundscape_detected)
668       if (!soundscape_detect(0))
669 	 return -1;
670 
671    /* set DMA controller register set pointer based on channel */
672    dmac_reg_p = &dmac_regs[soundscape_dma];
673 
674    /* in case the CoDec is running, stop it */
675    stop_codec();
676 
677    /* clear possible CoDec and SoundBlaster emulation interrupts */
678    outportb(soundscape_waveport + CD_STATUS_OFF, 0x00);
679    inportb(0x22E);
680 
681    /* input isn't supported yet, don't init twice without exit */
682    if ((input) || (soundscape_enabled))
683       return -1;
684 
685    /* set format, always stereo, 16 bit */
686    set_format(soundscape_freq, 1, 1, 0);
687 
688    if (_dma_allocate_mem(soundscape_dma_size * 2, &soundscape_sel, &soundscape_buf[0]) != 0)
689       return -1;
690 
691    soundscape_mem_allocated = TRUE;
692 
693    soundscape_buf[1] = soundscape_buf[0] + soundscape_dma_size;
694 
695    soundscape_lock_mem();
696 
697    digi_soundscape.voices = voices;
698 
699    if (_mixer_init(soundscape_dma_size/2, soundscape_freq, TRUE, TRUE, &digi_soundscape.voices) != 0) {
700       soundscape_exit(0);
701       return -1;
702    }
703 
704    _mix_some_samples(soundscape_buf[0], _dos_ds, TRUE);
705    _mix_some_samples(soundscape_buf[1], _dos_ds, TRUE);
706    soundscape_bufnum = 0;
707 
708    _enable_irq(soundscape_waveirq);
709    _install_irq(soundscape_int, soundscape_interrupt);
710 
711    if (soundscape_hw_ver != MMIC) {
712       /* derive the MIDI and Wave IRQ indices (0-3) for reg writes */
713       for (mindx=0; mindx<4; mindx++)
714 	 if (soundscape_midiirq == *(soundscape_irqset + mindx))
715 	    break;
716 
717       for (windx=0; windx<4; windx++)
718 	 if (soundscape_waveirq == *(soundscape_irqset + windx))
719 	    break;
720 
721       /* setup the CoDec DMA polarity */
722       ga_write(GA_DMACFG_REG, 0x50);
723 
724       /* give the CoDec control of the DMA and Wave IRQ resources */
725       cd_cfg_save = ga_read(GA_CDCFG_REG);
726       ga_write(GA_CDCFG_REG, 0x89 | (soundscape_dma << 4) | (windx << 1));
727 
728       /* pull the Sound Blaster emulation off of those resources */
729       dma_cfg_save = ga_read(GA_DMAB_REG);
730       ga_write(GA_DMAB_REG, 0x20);
731       int_cfg_save = ga_read(GA_INTCFG_REG);
732       ga_write(GA_INTCFG_REG, 0xF0 | (mindx << 2) | mindx);
733    }
734 
735    /* save all volumes that we might use */
736    dac_save_l = cd_read(CD_DACL_REG);
737    dac_save_r = cd_read(CD_DACR_REG);
738    cdx_save_l = cd_read(CD_CDAUXL_REG);
739    cdx_save_r = cd_read(CD_CDAUXL_REG);
740    adc_save_l = cd_read(CD_ADCL_REG);
741    adc_save_r = cd_read(CD_ADCR_REG);
742 
743    set_dac_vol(127, 127);
744 
745    /* select the mic/line input to the record mux */
746    /* if not ODIE, set the mic gain bit too */
747    cd_write(CD_ADCL_REG, (cd_read(CD_ADCL_REG) & 0x3F) | (soundscape_hw_ver == ODIE ? 0x80 : 0xA0));
748    cd_write(CD_ADCR_REG, (cd_read(CD_ADCR_REG) & 0x3F) | (soundscape_hw_ver == ODIE ? 0x80 : 0xA0));
749 
750    /* put the CoDec into mode change state */
751    CODEC_MODE_CHANGE_ON();
752 
753    /* setup CoDec mode - single DMA chan, AutoCal on */
754    cd_write(CD_CONFIG_REG, 0x0c);
755 
756    ENABLE_CODEC_IRQ();
757 
758    _dma_start(soundscape_dma, soundscape_buf[0], soundscape_dma_size * 2, TRUE, FALSE) ;
759 
760    /* write the CoDec interrupt count - sample frames per half-buffer. */
761    tmp = soundscape_dma_size / 4 - 1;
762    cd_write(CD_LCOUNT_REG, tmp);
763    cd_write(CD_UCOUNT_REG, tmp >> 8);
764 
765    CODEC_MODE_CHANGE_OFF();
766 
767    /* start the CoDec for output */
768    resume_codec(0);
769    rest(100);
770 
771    soundscape_enabled = TRUE;
772 
773    return 0;
774 }
775 
776 
777 
778 /* soundscape_exit:
779  *  Soundscape driver cleanup routine, removes ints, stops dma,
780  *  frees buffers, etc.
781  */
soundscape_exit(int input)782 static void soundscape_exit(int input)
783 {
784    if (soundscape_enabled) {
785 
786       /* in case the CoDec is running, stop it */
787       stop_codec();
788 
789       /* stop dma transfer */
790       _dma_stop(soundscape_dma);
791 
792       /* disable the CoDec interrupt pin */
793       DISABLE_CODEC_IRQ();
794 
795       /* restore all volumes ... */
796       cd_write(CD_DACL_REG, dac_save_l);
797       cd_write(CD_DACR_REG, dac_save_r);
798       cd_write(CD_CDAUXL_REG, cdx_save_l);
799       cd_write(CD_CDAUXL_REG, cdx_save_r);
800       cd_write(CD_ADCL_REG, adc_save_l);
801       cd_write(CD_ADCR_REG, adc_save_r);
802 
803       /* if necessary, restore gate array resource registers */
804       if (soundscape_hw_ver != MMIC) {
805 	 ga_write(GA_INTCFG_REG, int_cfg_save);
806 	 ga_write(GA_DMAB_REG, dma_cfg_save);
807 	 ga_write(GA_CDCFG_REG, cd_cfg_save);
808       }
809 
810       _remove_irq(soundscape_int);              /* restore interrupts */
811       _restore_irq(soundscape_waveirq);         /* reset PIC channels */
812    }
813 
814    if (soundscape_mem_allocated)
815       __dpmi_free_dos_memory(soundscape_sel);   /* free DMA memory buffer */
816 
817    _mixer_exit();
818    soundscape_hw_ver = -1;
819    soundscape_detected = FALSE;
820    soundscape_detected = FALSE;
821    soundscape_enabled = FALSE;
822 }
823 
824 
825 
826 /* soundscape_lock_mem:
827  *  Locks all the memory touched by parts of the Soundscape code that are
828  *  executed in an interrupt context.
829  */
soundscape_lock_mem(void)830 static void soundscape_lock_mem(void)
831 {
832    LOCK_VARIABLE(digi_soundscape);
833    LOCK_VARIABLE(soundscape_freq);
834    LOCK_VARIABLE(soundscape_baseport);
835    LOCK_VARIABLE(soundscape_waveport);
836    LOCK_VARIABLE(soundscape_dma);
837    LOCK_VARIABLE(soundscape_midiirq);
838    LOCK_VARIABLE(soundscape_waveirq);
839    LOCK_VARIABLE(soundscape_int);
840    LOCK_VARIABLE(soundscape_hw_ver);
841    LOCK_VARIABLE(soundscape_dma_size);
842    LOCK_VARIABLE(soundscape_sel);
843    LOCK_VARIABLE(soundscape_buf);
844    LOCK_VARIABLE(soundscape_bufnum);
845    LOCK_VARIABLE(soundscape_dma_count);
846    LOCK_VARIABLE(soundscape_semaphore);
847    LOCK_FUNCTION(soundscape_interrupt);
848 
849    _dma_lock_mem();
850 }
851 
852