12 #define BUFSIZE 16384
\r
14 #define REG_MIXPORT (base_port + 0x4)
\r
15 #define REG_MIXDATA (base_port + 0x5)
\r
16 #define REG_RESET (base_port + 0x6)
\r
17 #define REG_RDATA (base_port + 0xa)
\r
18 #define REG_WDATA (base_port + 0xc)
\r
19 #define REG_WSTAT (base_port + 0xc)
\r
20 #define REG_RSTAT (base_port + 0xe)
\r
21 #define REG_INTACK (base_port + 0xe)
\r
22 #define REG_INT16ACK (base_port + 0xf)
\r
24 #define WSTAT_BUSY 0x80
\r
25 #define RSTAT_RDY 0x80
\r
27 #define DSP_RATE 0x40
\r
28 #define DSP4_OUT_RATE 0x41
\r
29 #define DSP4_IN_RATE 0x42
\r
30 #define DSP_BLOCKSZ 0x48
\r
31 #define DSP_GET_VER 0xe1
\r
33 /* start DMA playback/recording. combine with fifo/auto/input flags */
\r
34 #define DSP4_START_DMA8 0xc0
\r
35 #define DSP4_START_DMA16 0xb0
\r
36 #define DSP4_FIFO 0x02
\r
37 #define DSP4_AUTO 0x04
\r
38 #define DSP4_INPUT 0x08
\r
40 /* transfer mode commands */
\r
41 #define DSP4_MODE_SIGNED 0x10
\r
42 #define DSP4_MODE_STEREO 0x20
\r
44 /* immediately pause/continue */
\r
45 #define DSP_PAUSE_DMA8 0xd0
\r
46 #define DSP_ENABLE_OUTPUT 0xd1
\r
47 #define DSP_DISABLE_OUTPUT 0xd3
\r
48 #define DSP_CONT_DMA8 0xd4
\r
49 #define DSP_PAUSE_DMA16 0xd5
\r
50 #define DSP_CONT_DMA16 0xd6
\r
52 /* end the playback at the end of the current buffer */
\r
53 #define DSP_END_DMA16 0xd9
\r
54 #define DSP_END_DMA8 0xda
\r
56 /* mixer registers */
\r
57 #define MIX_MASTER 0x02
\r
58 #define MIX_VOICE 0x0a
\r
59 #define MIX_SBPRO_VOICE 0x04
\r
60 #define MIX_SBPRO_MASTER 0x22
\r
61 #define MIX_SB16_MASTER_L 0x30
\r
62 #define MIX_SB16_MASTER_R 0x31
\r
63 #define MIX_SB16_VOICE_L 0x32
\r
64 #define MIX_SB16_VOICE_R 0x33
\r
66 #define MIX_SBPRO_STEREO 0x0e
\r
67 #define MIX_SB16_IRQ_SEL 0x80
\r
68 #define MIX_SB16_DMA_SEL 0x81
\r
69 #define MIX_SB16_INTSTAT 0x82
\r
71 #define INTSTAT_DMA8 0x01
\r
72 #define INTSTAT_DMA16 0x02
\r
73 #define INTSTAT_MPU401 0x04
\r
75 #define VER_MAJOR(x) ((x) >> 8)
\r
76 #define VER_MINOR(x) ((x) & 0xff)
\r
78 /* delay for about 1us */
\r
79 #define iodelay() outp(0x80, 0)
\r
82 static int reset_dsp(void);
\r
83 static void *sb_buffer(int *size);
\r
84 static void start(int rate, int bits, int nchan);
\r
85 static void pause(void);
\r
86 static void cont(void);
\r
87 static void stop(void);
\r
88 static int isplaying(void);
\r
89 static void setvolume(int ctl, int vol);
\r
90 static int getvolume(int ctl);
\r
92 static void set_sbpro_stereo(void);
\r
93 static int start_dsp4(int rate, int bits, unsigned int mode, int num_samples);
\r
94 static int start_dsp(int rate, int nchan, int num_samples);
\r
95 static void INTERRUPT intr_handler();
\r
96 static void write_dsp(unsigned char val);
\r
97 static unsigned char read_dsp(void);
\r
98 static void write_mix(int reg, unsigned char val);
\r
99 static unsigned char read_mix(int reg);
\r
100 static int get_dsp_version(void);
\r
101 static int dsp4_detect_irq(void);
\r
102 static int dsp4_detect_dma(void);
\r
103 static void print_dsp_info(void);
\r
104 static const char *sbname(int ver);
\r
107 static const struct audrv sbdrv = {
\r
118 static int base_port;
\r
119 static int irq, dma_chan, dma16_chan, dsp_ver;
\r
121 static void *buffer[2];
\r
122 static volatile int wrbuf;
\r
124 static volatile int playing;
\r
125 static int cur_bits, auto_init, high_speed;
\r
127 static void (INTERRUPT *prev_intr_handler)();
\r
130 int sb_detect(struct audrv *drv)
\r
135 if((env = getenv("BLASTER"))) {
\r
137 if(sscanf(env, "A%x I%d D%d H%d", &base_port, &irq, &dma_chan, &dma16_chan) >= 3) {
\r
138 if(reset_dsp() == 0) {
\r
139 dsp_ver = get_dsp_version();
\r
145 printf("sb_detect: malformed BLASTER environment variable. Fallback to probing.\n");
\r
149 for(i=0; i<6; i++) {
\r
150 base_port = 0x200 + ((i + 1) << 4);
\r
151 if(reset_dsp() == 0) {
\r
152 dsp_ver = get_dsp_version();
\r
154 if(dsp_ver >= 0x400) {
\r
155 if(dsp4_detect_irq() == -1) {
\r
156 printf("sb_detect: failed to configure IRQ\n");
\r
159 if(dsp4_detect_dma() == -1) {
\r
160 printf("sb_detect: failed to configure DMA\n");
\r
165 /* XXX for old sound blasters, hard-code to IRQ 5 DMA 1 for now */
\r
170 printf("sb_detect: old sound blaster dsp. assuming: irq 5, dma 1\n");
\r
182 static int reset_dsp(void)
\r
186 outp(REG_RESET, 1);
\r
187 for(i=0; i<3; i++) iodelay();
\r
188 outp(REG_RESET, 0);
\r
190 for(i=0; i<128; i++) {
\r
191 if(inp(REG_RSTAT) & RSTAT_RDY) {
\r
192 if(inp(REG_RDATA) == 0xaa) {
\r
201 static void start(int rate, int bits, int nchan)
\r
203 uint16_t seg, pmsel;
\r
204 uint32_t addr, next64k;
\r
205 int size, mode, num_samples, dsp_num_samples;
\r
208 /* allocate a buffer in low memory that doesn't cross 64k boundaries */
\r
209 if(!(seg = dpmi_alloc(BUFSIZE * 2 / 16, &pmsel))) {
\r
210 fprintf(stderr, "SB start: failed to allocate DMA buffer\n");
\r
214 addr = (uint32_t)seg << 4;
\r
215 next64k = (addr + 0x10000) & 0xffff0000;
\r
216 if(next64k - addr < BUFSIZE) {
\r
220 buffer[0] = (void*)addr;
\r
221 buffer[1] = (void*)(addr + BUFSIZE / 2);
\r
227 if(!(size = audio_callback(buffer[wrbuf], BUFSIZE))) {
\r
230 addr = (uint32_t)buffer[wrbuf];
\r
231 num_samples = bits == 8 ? size : size / 2;
\r
233 if(size < BUFSIZE) {
\r
234 auto_init = 0; /* single transfer mode */
\r
235 dsp_num_samples = num_samples;
\r
237 /* Auto-init playback mode:
\r
238 * fill the whole buffer with data, program the DMA for BUFSIZE transfer,
\r
239 * and program the DSP for BUFSIZE/2 transfer. We'll get an interrupt in the
\r
240 * middle, while the DSP uses the upper half, and we'll refill the bottom half.
\r
241 * Then continue ping-ponging the two halves of the buffer until we run out of
\r
245 dsp_num_samples = num_samples / 2;
\r
249 if(!prev_intr_handler) {
\r
250 prev_intr_handler = _dos_getvect(IRQ_TO_INTR(irq));
\r
252 _dos_setvect(IRQ_TO_INTR(irq), intr_handler);
\r
259 write_dsp(DSP_ENABLE_OUTPUT);
\r
260 dma_out(cur_bits == 8 ? dma_chan : dma16_chan, addr, BUFSIZE, DMA_SINGLE | auto_init ? DMA_AUTO : 0);
\r
262 if(dsp_ver >= 0x400) {
\r
263 int mode = bits == 8 ? 0 : DSP4_MODE_SIGNED;
\r
265 mode |= DSP4_MODE_STEREO;
\r
267 start_dsp4(rate, cur_bits, mode, dsp_num_samples);
\r
269 if(nchan > 1 && dsp_ver >= 0x300) {
\r
270 set_sbpro_stereo();
\r
272 start_dsp(rate, cur_bits, dsp_num_samples);
\r
277 static void pause(void)
\r
279 write_dsp(cur_bits == 8 ? DSP_PAUSE_DMA8 : DSP_PAUSE_DMA16);
\r
282 static void cont(void)
\r
284 write_dsp(cur_bits == 8 ? DSP_CONT_DMA8 : DSP_CONT_DMA16);
\r
287 static void stop(void)
\r
289 write_dsp(DSP_DISABLE_OUTPUT);
\r
292 /* TODO: don't _enable, restore state */
\r
294 _dos_setvect(IRQ_TO_INTR(irq), prev_intr_handler);
\r
297 write_dsp(cur_bits == 8 ? DSP_END_DMA8 : DSP_END_DMA16);
\r
301 static int isplaying(void)
\r
306 static void setvolume(int ctl, int vol)
\r
310 if(VER_MAJOR(dsp_ver) >= 4) {
\r
311 /* DSP 4.x - SB16 */
\r
313 if(ctl == AUDIO_PCM) {
\r
314 write_mix(MIX_SB16_VOICE_L, val);
\r
315 write_mix(MIX_SB16_VOICE_R, val);
\r
317 write_mix(MIX_SB16_MASTER_L, val);
\r
318 write_mix(MIX_SB16_MASTER_R, val);
\r
321 } else if(VER_MAJOR(dsp_ver) >= 3) {
\r
322 /* DSP 3.x - SBPro */
\r
326 if(ctl == AUDIO_PCM) {
\r
327 write_mix(MIX_SBPRO_VOICE, val);
\r
329 write_mix(MIX_SBPRO_MASTER, val);
\r
333 /* DSP 2.x - SB 2.0 */
\r
334 if(ctl == AUDIO_PCM) {
\r
335 val = (vol >> 5) & 6;
\r
336 write_mix(MIX_VOICE, val);
\r
338 val = (vol >> 4) & 0xe;
\r
339 write_mix(MIX_MASTER, val);
\r
344 static int getvolume(int ctl)
\r
349 if(dsp_ver >= 0x400) {
\r
350 /* DSP 4.x - SB16 */
\r
352 if(ctl == AUDIO_PCM) {
\r
353 lreg = MIX_SB16_VOICE_L;
\r
354 rreg = MIX_SB16_VOICE_R;
\r
355 } else { /* MASTER or DEFAULT */
\r
356 lreg = MIX_SB16_MASTER_L;
\r
357 rreg = MIX_SB16_MASTER_R;
\r
360 val = read_mix(lreg);
\r
361 left = (val & 0xf8) | (val >> 5);
\r
362 val = read_mix(rreg);
\r
363 right = (val & 0xf8) | (val >> 5);
\r
365 } else if(dsp_ver >= 0x300) {
\r
366 /* DSP 3.x - SBPro */
\r
367 val = read_mix(ctl == AUDIO_PCM ? MIX_SBPRO_VOICE : MIX_SBPRO_MASTER);
\r
369 /* left is top 3 bits, duplicate twice(-ish) */
\r
370 left = (val & 0xe0) | ((val >> 3) & 0x1c) | (val >> 6);
\r
371 /* right is top 3 bits of the lower nibble */
\r
372 right = (val << 4) | ((val << 1) & 0x1c) | ((val >> 2) & 3);
\r
377 /* DSP 2.x - SB 2.0 */
\r
378 if(ctl == AUDIO_PCM) {
\r
379 /* voice is in bits 1 and 2 */
\r
380 val = read_mix(MIX_VOICE);
\r
381 volume = (val << 5) | ((val << 3) & 0x30) | ((val << 1) & 0xc) | ((val >> 1) & 3);
\r
383 /* master is in the 3 top bits of the lower nibble */
\r
384 val = read_mix(MIX_MASTER);
\r
385 volume = (val << 4) | ((val << 1) & 0x1c) | ((val >> 2) & 3);
\r
390 return (left + right) >> 1;
\r
393 static void set_sbpro_stereo(void)
\r
397 static int start_dsp4(int rate, int bits, unsigned int mode, int num_samples)
\r
399 unsigned char cmd = bits == 8 ? DSP4_START_DMA8 : DSP4_START_DMA16;
\r
401 cmd |= DSP4_AUTO | DSP4_FIFO;
\r
404 /* set output rate */
\r
405 write_dsp(DSP4_OUT_RATE);
\r
406 write_dsp(rate >> 8);
\r
407 write_dsp(rate & 0xff);
\r
409 /* program the DSP to start the DMA transfer */
\r
413 write_dsp(num_samples & 0xff);
\r
414 write_dsp((num_samples >> 8) & 0xff);
\r
419 static int start_dsp(int rate, int nchan, int num_samples)
\r
421 /* set time constant */
\r
422 int tcon = 256 - 1000000 / rate;
\r
423 write_dsp(DSP_RATE);
\r
428 if(dsp_ver < 0x300) return -1; /* need a sb pro for stereo */
\r
430 set_sbpro_stereo();
\r
431 high_speed = rate >= 11025;
\r
434 high_speed = rate >= 23000;
\r
437 /* program the DSP to start the DMA transfer */
\r
438 return -1; /* TODO */
\r
442 static void INTERRUPT intr_handler()
\r
445 void *bptr = buffer[wrbuf];
\r
446 uint32_t addr = (uint32_t)bptr;
\r
450 /* ask for more data */
\r
451 if(!(size = audio_callback(bptr, BUFSIZE / 2))) {
\r
455 /* acknowledge the interrupt */
\r
456 if(cur_bits == 8) {
\r
459 unsigned char istat = read_mix(MIX_SB16_INTSTAT);
\r
460 if(istat & INTSTAT_DMA16) {
\r
466 outp(PIC2_CMD, OCW2_EOI);
\r
468 outp(PIC1_CMD, OCW2_EOI);
\r
471 static void write_dsp(unsigned char val)
\r
473 while(inp(REG_WSTAT) & WSTAT_BUSY);
\r
474 outp(REG_WDATA, val);
\r
477 static unsigned char read_dsp(void)
\r
479 while((inp(REG_RSTAT) & RSTAT_RDY) == 0);
\r
480 return inp(REG_RDATA);
\r
483 static void write_mix(int reg, unsigned char val)
\r
485 outp(REG_MIXPORT, reg);
\r
486 outp(REG_MIXDATA, val);
\r
489 static unsigned char read_mix(int reg)
\r
491 outp(REG_MIXPORT, reg);
\r
492 return inp(REG_MIXDATA);
\r
495 static int get_dsp_version(void)
\r
499 write_dsp(DSP_GET_VER);
\r
500 major = read_dsp();
\r
501 minor = read_dsp();
\r
503 return (major << 8) | minor;
\r
506 static int dsp4_detect_irq(void)
\r
509 static int irqtab[] = {2, 5, 7, 10};
\r
512 irqsel = read_mix(MIX_SB16_IRQ_SEL);
\r
513 for(i=0; i<4; i++) {
\r
514 if(irqsel & (1 << i)) {
\r
520 /* try to force IRQ 5 */
\r
521 write_mix(MIX_SB16_IRQ_SEL, 2); /* bit1 selects irq 5 */
\r
523 /* re-read to verify */
\r
524 irqsel = read_mix(MIX_SB16_IRQ_SEL);
\r
534 static int dsp4_detect_dma(void)
\r
536 int i, dmasel, tmp;
\r
537 static int dmatab[] = {0, 1, -1, 3, -1, 5, 6, 7};
\r
541 dmasel = read_mix(MIX_SB16_DMA_SEL);
\r
542 for(i=0; i<4; i++) {
\r
543 if(dmasel & (1 << i)) {
\r
544 dma_chan = dmatab[i];
\r
548 for(i=5; i<8; i++) {
\r
549 if(dmasel & (1 << i)) {
\r
550 dma16_chan = dmatab[i];
\r
554 if(dma_chan == -1) {
\r
555 /* try to force DMA 1 */
\r
558 if(dma16_chan == -1) {
\r
559 /* try to force 16bit DMA 5 */
\r
563 if(dma_chan == -1 || dma16_chan == -1) {
\r
564 write_mix(MIX_SB16_DMA_SEL, dmasel);
\r
566 /* re-read to verify */
\r
567 tmp = read_mix(MIX_SB16_DMA_SEL);
\r
568 if(tmp != dmasel) {
\r
578 static void print_dsp_info(void)
\r
580 printf("sb_detect: %s (DSP v%d.%02d) port %xh, irq %d", sbname(dsp_ver),
\r
581 VER_MAJOR(dsp_ver), VER_MINOR(dsp_ver), base_port, irq);
\r
582 if(VER_MAJOR(dsp_ver) >= 4) {
\r
583 printf(" dma %d/%d\n", dma_chan, dma16_chan);
\r
585 printf(" dma %d\n", dma_chan);
\r
589 #define V(maj, min) (((maj) << 8) | (min))
\r
591 static const char *sbname(int ver)
\r
593 int major = VER_MAJOR(ver);
\r
594 int minor = VER_MINOR(ver);
\r
599 return "Sound Blaster 1.5";
\r
601 return "Sound Blaster 1.0";
\r
604 if(minor == 1 || minor == 2) {
\r
605 return "Sound Blaster 2.0";
\r
612 return "Sound Blaster Pro";
\r
615 return "Sound Blaster Pro 2";
\r
617 return "Gallant SC-6000";
\r
627 return "Sound Blaster 16";
\r
629 return "Sound Blaster 16 SCSI-2";
\r
631 return "Sound Blaster AWE 32";
\r
633 return "Sound Blaster ViBRA16C";
\r
635 return "Sound Blaster AWE 64";
\r
642 return "Unknown Sound Blaster";
\r