explicitly mask byte i/o values to bytes.
[minix3.git] / drivers / sb16 / sb16_dsp.c
blobb05be18c70ee5d0ac8ccc17b2208c6b70136f947
1 /* This file contains the driver for a DSP (Digital Sound Processor) on
2 * a SoundBlaster 16 soundcard.
4 * The driver supports the following operations (using message format m2):
6 * m_type DEVICE IO_ENDPT COUNT POSITION ADRRESS
7 * ----------------------------------------------------------------
8 * | DEV_OPEN | device | proc nr | | | |
9 * |------------+---------+---------+---------+---------+---------|
10 * | DEV_CLOSE | device | proc nr | | | |
11 * |------------+---------+---------+---------+---------+---------|
12 * | DEV_READ | device | proc nr | bytes | | buf ptr |
13 * |------------+---------+---------+---------+---------+---------|
14 * | DEV_WRITE | device | proc nr | bytes | | buf ptr |
15 * |------------+---------+---------+---------+---------+---------|
16 * | DEV_IOCTL | device | proc nr |func code| | buf ptr |
17 * ----------------------------------------------------------------
19 * The file contains one entry point:
21 * main: main entry when driver is brought up
23 * August 24 2005 Ported driver to user space (only audio playback) (Peter Boonstoppel)
24 * May 20 1995 Author: Michel R. Prevenier
27 #include "sb16.h"
30 _PROTOTYPE(void main, (void));
31 FORWARD _PROTOTYPE( int dsp_open, (void) );
32 FORWARD _PROTOTYPE( int dsp_close, (void) );
33 FORWARD _PROTOTYPE( int dsp_ioctl, (message *m_ptr) );
34 FORWARD _PROTOTYPE( void dsp_write, (message *m_ptr) );
35 FORWARD _PROTOTYPE( void dsp_hardware_msg, (void) );
36 FORWARD _PROTOTYPE( void dsp_status, (message *m_ptr) );
38 FORWARD _PROTOTYPE( void reply, (int code, int replyee, int process, int status) );
39 FORWARD _PROTOTYPE( void init_buffer, (void) );
40 FORWARD _PROTOTYPE( int dsp_init, (void) );
41 FORWARD _PROTOTYPE( int dsp_reset, (void) );
42 FORWARD _PROTOTYPE( int dsp_command, (int value) );
43 FORWARD _PROTOTYPE( int dsp_set_size, (unsigned int size) );
44 FORWARD _PROTOTYPE( int dsp_set_speed, (unsigned int speed) );
45 FORWARD _PROTOTYPE( int dsp_set_stereo, (unsigned int stereo) );
46 FORWARD _PROTOTYPE( int dsp_set_bits, (unsigned int bits) );
47 FORWARD _PROTOTYPE( int dsp_set_sign, (unsigned int sign) );
48 FORWARD _PROTOTYPE( void dsp_dma_setup, (phys_bytes address, int count) );
49 FORWARD _PROTOTYPE( void dsp_setup, (void) );
51 PRIVATE int irq_hook_id; /* id of irq hook at the kernel */
53 PRIVATE char DmaBuffer[DMA_SIZE + 64 * 1024];
54 PRIVATE char* DmaPtr;
55 PRIVATE phys_bytes DmaPhys;
57 PRIVATE char Buffer[DSP_MAX_FRAGMENT_SIZE * DSP_NR_OF_BUFFERS];
59 PRIVATE int DspVersion[2];
60 PRIVATE unsigned int DspStereo = DEFAULT_STEREO;
61 PRIVATE unsigned int DspSpeed = DEFAULT_SPEED;
62 PRIVATE unsigned int DspBits = DEFAULT_BITS;
63 PRIVATE unsigned int DspSign = DEFAULT_SIGN;
64 PRIVATE unsigned int DspFragmentSize = DSP_MAX_FRAGMENT_SIZE;
65 PRIVATE int DspAvail = 0;
66 PRIVATE int DspBusy = 0;
67 PRIVATE int DmaMode = 0;
68 PRIVATE int DmaBusy = -1;
69 PRIVATE int DmaFillNext = 0;
70 PRIVATE int BufReadNext = -1;
71 PRIVATE int BufFillNext = 0;
73 PRIVATE int revivePending = 0;
74 PRIVATE int reviveStatus;
75 PRIVATE int reviveProcNr;
77 #define dprint (void)
80 /*===========================================================================*
81 * main
82 *===========================================================================*/
83 PUBLIC void main()
85 int r, caller, proc_nr, s;
86 message mess;
88 dprint("sb16_dsp.c: main()\n");
90 /* Get a DMA buffer. */
91 init_buffer();
93 while(TRUE) {
94 /* Wait for an incoming message */
95 receive(ANY, &mess);
97 caller = mess.m_source;
98 proc_nr = mess.IO_ENDPT;
100 /* Now carry out the work. */
101 switch(mess.m_type) {
102 case DEV_OPEN: r = dsp_open(); break;
103 case DEV_CLOSE: r = dsp_close(); break;
104 #ifdef DEV_IOCTL
105 case DEV_IOCTL: r = dsp_ioctl(&mess); break;
106 #endif
107 #ifdef DEV_READ
109 case DEV_READ: r = EINVAL; break; /* Not yet implemented */
110 case DEV_WRITE: dsp_write(&mess); continue; /* don't reply */
111 #endif
113 case DEV_STATUS: dsp_status(&mess); continue; /* don't reply */
114 case HARD_INT: dsp_hardware_msg(); continue; /* don't reply */
115 case SYS_SIG: continue; /* don't reply */
116 default: r = EINVAL;
119 /* Finally, prepare and send the reply message. */
120 reply(TASK_REPLY, caller, proc_nr, r);
126 /*===========================================================================*
127 * dsp_open
128 *===========================================================================*/
129 PRIVATE int dsp_open()
131 dprint("sb16_dsp.c: dsp_open()\n");
133 /* try to detect SoundBlaster card */
134 if(!DspAvail && dsp_init() != OK) return EIO;
136 /* Only one open at a time with soundcards */
137 if(DspBusy) return EBUSY;
139 /* Start with a clean DSP */
140 if(dsp_reset() != OK) return EIO;
142 /* Setup default values */
143 DspStereo = DEFAULT_STEREO;
144 DspSpeed = DEFAULT_SPEED;
145 DspBits = DEFAULT_BITS;
146 DspSign = DEFAULT_SIGN;
147 DspFragmentSize = DMA_SIZE / 2;
149 DspBusy = 1;
151 return OK;
155 /*===========================================================================*
156 * dsp_close
157 *===========================================================================*/
158 PRIVATE int dsp_close()
160 dprint("sb16_dsp.c: dsp_close()\n");
162 DspBusy = 0; /* soundcard available again */
164 return OK;
168 /*===========================================================================*
169 * dsp_ioctl
170 *===========================================================================*/
171 PRIVATE int dsp_ioctl(m_ptr)
172 message *m_ptr;
174 int status;
175 phys_bytes user_phys;
176 unsigned int val;
178 dprint("sb16_dsp.c: dsp_ioctl()\n");
180 /* Cannot change parameters during play or recording */
181 if(DmaBusy >= 0) return EBUSY;
183 /* Get user data */
184 if(m_ptr->REQUEST != DSPIORESET) {
185 sys_vircopy(m_ptr->IO_ENDPT, D, (vir_bytes)m_ptr->ADDRESS, SELF, D, (vir_bytes)&val, sizeof(val));
188 dprint("dsp_ioctl: got ioctl %d, argument: %d\n", m_ptr->REQUEST, val);
190 switch(m_ptr->REQUEST) {
191 case DSPIORATE: status = dsp_set_speed(val); break;
192 case DSPIOSTEREO: status = dsp_set_stereo(val); break;
193 case DSPIOBITS: status = dsp_set_bits(val); break;
194 case DSPIOSIZE: status = dsp_set_size(val); break;
195 case DSPIOSIGN: status = dsp_set_sign(val); break;
196 case DSPIOMAX:
197 val = DSP_MAX_FRAGMENT_SIZE;
198 sys_vircopy(SELF, D, (vir_bytes)&val, m_ptr->IO_ENDPT, D, (vir_bytes)m_ptr->ADDRESS, sizeof(val));
199 status = OK;
200 break;
201 case DSPIORESET: status = dsp_reset(); break;
202 default: status = ENOTTY; break;
205 return status;
209 /*===========================================================================*
210 * dsp_write
211 *===========================================================================*/
212 PRIVATE void dsp_write(m_ptr)
213 message *m_ptr;
215 int s;
216 message mess;
218 dprint("sb16_dsp.c: dsp_write()\n");
220 if(m_ptr->COUNT != DspFragmentSize) {
221 reply(TASK_REPLY, m_ptr->m_source, m_ptr->IO_ENDPT, EINVAL);
222 return;
224 if(m_ptr->m_type != DmaMode && DmaBusy >= 0) {
225 reply(TASK_REPLY, m_ptr->m_source, m_ptr->IO_ENDPT, EBUSY);
226 return;
229 reply(TASK_REPLY, m_ptr->m_source, m_ptr->IO_ENDPT, SUSPEND);
231 if(DmaBusy < 0) { /* Dma tranfer not yet started */
233 DmaMode = DEV_WRITE_S; /* Dma mode is writing */
234 sys_datacopy(m_ptr->IO_ENDPT, (vir_bytes)m_ptr->ADDRESS, SELF, (vir_bytes)DmaPtr, (phys_bytes)DspFragmentSize);
235 dsp_dma_setup(DmaPhys, DspFragmentSize * DMA_NR_OF_BUFFERS);
236 dsp_setup();
237 DmaBusy = 0; /* Dma is busy */
238 dprint(" filled dma[0]\n");
239 DmaFillNext = 1;
241 } else if(DmaBusy != DmaFillNext) { /* Dma transfer started, but Dma buffer not yet full */
243 sys_datacopy(m_ptr->IO_ENDPT, (vir_bytes)m_ptr->ADDRESS, SELF, (vir_bytes)DmaPtr + DmaFillNext * DspFragmentSize, (phys_bytes)DspFragmentSize);
244 dprint(" filled dma[%d]\n", DmaFillNext);
245 DmaFillNext = (DmaFillNext + 1) % DMA_NR_OF_BUFFERS;
247 } else if(BufReadNext < 0) { /* Dma buffer full, fill first element of second buffer */
249 sys_datacopy(m_ptr->IO_ENDPT, (vir_bytes)m_ptr->ADDRESS, SELF, (vir_bytes)Buffer, (phys_bytes)DspFragmentSize);
250 dprint(" filled buf[0]\n");
251 BufReadNext = 0;
252 BufFillNext = 1;
254 } else { /* Dma buffer is full, filling second buffer */
256 while(BufReadNext == BufFillNext) { /* Second buffer also full, wait for space to become available */
257 receive(HARDWARE, &mess);
258 dsp_hardware_msg();
260 sys_datacopy(m_ptr->IO_ENDPT, (vir_bytes)m_ptr->ADDRESS, SELF, (vir_bytes)Buffer + BufFillNext * DspFragmentSize, (phys_bytes)DspFragmentSize);
261 dprint(" filled buf[%d]\n", BufFillNext);
262 BufFillNext = (BufFillNext + 1) % DSP_NR_OF_BUFFERS;
266 revivePending = 1;
267 reviveStatus = DspFragmentSize;
268 reviveProcNr = m_ptr->IO_ENDPT;
269 notify(m_ptr->m_source);
273 /*===========================================================================*
274 * dsp_hardware_msg
275 *===========================================================================*/
276 PRIVATE void dsp_hardware_msg()
278 dprint("Interrupt: ");
279 if(DmaBusy >= 0) { /* Dma transfer was actually busy */
280 dprint("Finished playing dma[%d]; ", DmaBusy);
281 DmaBusy = (DmaBusy + 1) % DMA_NR_OF_BUFFERS;
282 if(DmaBusy == DmaFillNext) { /* Dma buffer empty, stop Dma transfer */
284 dsp_command((DspBits == 8 ? DSP_CMD_DMA8HALT : DSP_CMD_DMA16HALT));
285 dprint("No more work...!\n");
286 DmaBusy = -1;
288 } else if(BufReadNext >= 0) { /* Data in second buffer, copy one fragment to Dma buffer */
290 /* Acknowledge the interrupt on the DSP */
291 sb16_inb((DspBits == 8 ? DSP_DATA_AVL : DSP_DATA16_AVL));
293 memcpy(DmaPtr + DmaFillNext * DspFragmentSize, Buffer + BufReadNext * DspFragmentSize, DspFragmentSize);
294 dprint("copy buf[%d] -> dma[%d]; ", BufReadNext, DmaFillNext);
295 BufReadNext = (BufReadNext + 1) % DSP_NR_OF_BUFFERS;
296 DmaFillNext = (DmaFillNext + 1) % DMA_NR_OF_BUFFERS;
297 if(BufReadNext == BufFillNext) {
298 BufReadNext = -1;
300 dprint("Starting dma[%d]\n", DmaBusy);
302 return;
304 } else { /* Second buffer empty, still data in Dma buffer, continue playback */
305 dprint("Starting dma[%d]\n", DmaBusy);
309 /* Acknowledge the interrupt on the DSP */
310 sb16_inb((DspBits == 8 ? DSP_DATA_AVL : DSP_DATA16_AVL));
314 /*===========================================================================*
315 * dsp_status *
316 *===========================================================================*/
317 PRIVATE void dsp_status(m_ptr)
318 message *m_ptr; /* pointer to the newly arrived message */
320 if(revivePending) {
321 m_ptr->m_type = DEV_REVIVE; /* build message */
322 m_ptr->REP_ENDPT = reviveProcNr;
323 m_ptr->REP_STATUS = reviveStatus;
325 revivePending = 0; /* unmark event */
326 } else {
327 m_ptr->m_type = DEV_NO_STATUS;
330 send(m_ptr->m_source, m_ptr); /* send the message */
334 /*===========================================================================*
335 * reply *
336 *===========================================================================*/
337 PRIVATE void reply(code, replyee, process, status)
338 int code;
339 int replyee;
340 int process;
341 int status;
343 message m;
345 m.m_type = code; /* TASK_REPLY or REVIVE */
346 m.REP_STATUS = status; /* result of device operation */
347 m.REP_ENDPT = process; /* which user made the request */
349 send(replyee, &m);
353 /*===========================================================================*
354 * init_buffer
355 *===========================================================================*/
356 PRIVATE void init_buffer()
358 /* Select a buffer that can safely be used for dma transfers.
359 * Its absolute address is 'DmaPhys', the normal address is 'DmaPtr'.
362 #if (CHIP == INTEL)
363 unsigned left;
365 DmaPtr = DmaBuffer;
366 sys_umap(SELF, D, (vir_bytes)DmaBuffer, (phys_bytes)sizeof(DmaBuffer), &DmaPhys);
368 if((left = dma_bytes_left(DmaPhys)) < DMA_SIZE) {
369 /* First half of buffer crosses a 64K boundary, can't DMA into that */
370 DmaPtr += left;
371 DmaPhys += left;
373 #else /* CHIP != INTEL */
374 panic("SB16DSP","init_buffer() failed, CHIP != INTEL", 0);
375 #endif /* CHIP == INTEL */
379 /*===========================================================================*
380 * dsp_init
381 *===========================================================================*/
382 PRIVATE int dsp_init()
384 int i, s;
386 if(dsp_reset () != OK) {
387 dprint("sb16: No SoundBlaster card detected\n");
388 return -1;
391 DspVersion[0] = DspVersion[1] = 0;
392 dsp_command(DSP_GET_VERSION); /* Get DSP version bytes */
394 for(i = 1000; i; i--) {
395 if(sb16_inb(DSP_DATA_AVL) & 0x80) {
396 if(DspVersion[0] == 0) {
397 DspVersion[0] = sb16_inb(DSP_READ);
398 } else {
399 DspVersion[1] = sb16_inb(DSP_READ);
400 break;
405 if(DspVersion[0] < 4) {
406 dprint("sb16: No SoundBlaster 16 compatible card detected\n");
407 return -1;
410 dprint("sb16: SoundBlaster DSP version %d.%d detected\n", DspVersion[0], DspVersion[1]);
412 /* set SB to use our IRQ and DMA channels */
413 mixer_set(MIXER_SET_IRQ, (1 << (SB_IRQ / 2 - 1)));
414 mixer_set(MIXER_SET_DMA, (1 << SB_DMA_8 | 1 << SB_DMA_16));
416 /* register interrupt vector and enable irq */
417 if ((s=sys_irqsetpolicy(SB_IRQ, IRQ_REENABLE, &irq_hook_id )) != OK)
418 panic("SB16DSP", "Couldn't set IRQ policy", s);
419 if ((s=sys_irqenable(&irq_hook_id)) != OK)
420 panic("SB16DSP", "Couldn't enable IRQ", s);
422 DspAvail = 1;
423 return OK;
427 /*===========================================================================*
428 * dsp_reset
429 *===========================================================================*/
430 PRIVATE int dsp_reset()
432 int i;
434 sb16_outb(DSP_RESET, 1);
435 for(i = 0; i < 1000; i++); /* wait a while */
436 sb16_outb(DSP_RESET, 0);
438 for(i = 0; i < 1000 && !(sb16_inb(DSP_DATA_AVL) & 0x80); i++);
440 if(sb16_inb(DSP_READ) != 0xAA) return EIO; /* No SoundBlaster */
442 DmaBusy = -1;
444 return OK;
448 /*===========================================================================*
449 * dsp_command
450 *===========================================================================*/
451 PRIVATE int dsp_command(value)
452 int value;
454 int i, status;
456 for (i = 0; i < SB_TIMEOUT; i++) {
457 if((sb16_inb(DSP_STATUS) & 0x80) == 0) {
458 sb16_outb(DSP_COMMAND, value);
459 return OK;
463 dprint("sb16: SoundBlaster: DSP Command(%x) timeout\n", value);
464 return -1;
468 /*===========================================================================*
469 * dsp_set_size
470 *===========================================================================*/
471 static int dsp_set_size(size)
472 unsigned int size;
474 dprint("dsp_set_size(): set fragment size to %u\n", size);
476 /* Sanity checks */
477 if(size < DSP_MIN_FRAGMENT_SIZE || size > DSP_MAX_FRAGMENT_SIZE || size % 2 != 0) {
478 return EINVAL;
481 DspFragmentSize = size;
483 return OK;
487 /*===========================================================================*
488 * dsp_set_speed
489 *===========================================================================*/
490 static int dsp_set_speed(speed)
491 unsigned int speed;
493 dprint("sb16: setting speed to %u, stereo = %d\n", speed, DspStereo);
495 if(speed < DSP_MIN_SPEED || speed > DSP_MAX_SPEED) {
496 return EPERM;
499 /* Soundblaster 16 can be programmed with real sample rates
500 * instead of time constants
502 * Since you cannot sample and play at the same time
503 * we set in- and output rate to the same value
506 dsp_command(DSP_INPUT_RATE); /* set input rate */
507 dsp_command(speed >> 8); /* high byte of speed */
508 dsp_command(speed); /* low byte of speed */
509 dsp_command(DSP_OUTPUT_RATE); /* same for output rate */
510 dsp_command(speed >> 8);
511 dsp_command(speed);
513 DspSpeed = speed;
515 return OK;
519 /*===========================================================================*
520 * dsp_set_stereo
521 *===========================================================================*/
522 static int dsp_set_stereo(stereo)
523 unsigned int stereo;
525 if(stereo) {
526 DspStereo = 1;
527 } else {
528 DspStereo = 0;
531 return OK;
535 /*===========================================================================*
536 * dsp_set_bits
537 *===========================================================================*/
538 static int dsp_set_bits(bits)
539 unsigned int bits;
541 /* Sanity checks */
542 if(bits != 8 && bits != 16) {
543 return EINVAL;
546 DspBits = bits;
548 return OK;
552 /*===========================================================================*
553 * dsp_set_sign
554 *===========================================================================*/
555 static int dsp_set_sign(sign)
556 unsigned int sign;
558 dprint("sb16: set sign to %u\n", sign);
560 DspSign = (sign > 0 ? 1 : 0);
562 return OK;
566 /*===========================================================================*
567 * dsp_dma_setup
568 *===========================================================================*/
569 PRIVATE void dsp_dma_setup(address, count)
570 phys_bytes address;
571 int count;
573 pvb_pair_t pvb[9];
576 dprint("Setting up %d bit DMA\n", DspBits);
578 if(DspBits == 8) { /* 8 bit sound */
579 count--;
581 pv_set(pvb[0], DMA8_MASK, SB_DMA_8 | 0x04); /* Disable DMA channel */
582 pv_set(pvb[1], DMA8_CLEAR, 0x00); /* Clear flip flop */
584 /* set DMA mode */
585 pv_set(pvb[2], DMA8_MODE, (DmaMode == DEV_WRITE_S ? DMA8_AUTO_PLAY : DMA8_AUTO_REC));
587 pv_set(pvb[3], DMA8_ADDR, (address >> 0) & 0xff); /* Low_byte of address */
588 pv_set(pvb[4], DMA8_ADDR, (address >> 8) & 0xff); /* High byte of address */
589 pv_set(pvb[5], DMA8_PAGE, (address >> 16) & 0xff); /* 64K page number */
590 pv_set(pvb[6], DMA8_COUNT, (count >> 0) & 0xff); /* Low byte of count */
591 pv_set(pvb[7], DMA8_COUNT, (count >> 8) & 0xff); /* High byte of count */
592 pv_set(pvb[8], DMA8_MASK, SB_DMA_8); /* Enable DMA channel */
594 sys_voutb(pvb, 9);
595 } else { /* 16 bit sound */
596 count-= 2;
598 pv_set(pvb[0], DMA16_MASK, (SB_DMA_16 & 3) | 0x04); /* Disable DMA channel */
600 pv_set(pvb[1], DMA16_CLEAR, 0x00); /* Clear flip flop */
602 /* Set dma mode */
603 pv_set(pvb[2], DMA16_MODE, (DmaMode == DEV_WRITE_S ? DMA16_AUTO_PLAY : DMA16_AUTO_REC));
605 pv_set(pvb[3], DMA16_ADDR, (address >> 1) & 0xFF); /* Low_byte of address */
606 pv_set(pvb[4], DMA16_ADDR, (address >> 9) & 0xFF); /* High byte of address */
607 pv_set(pvb[5], DMA16_PAGE, (address >> 16) & 0xFE); /* 128K page number */
608 pv_set(pvb[6], DMA16_COUNT, (count >> 1) & 0xff); /* Low byte of count */
609 pv_set(pvb[7], DMA16_COUNT, (count >> 9) & 0xff); /* High byte of count */
610 pv_set(pvb[8], DMA16_MASK, SB_DMA_16 & 3); /* Enable DMA channel */
612 sys_voutb(pvb, 9);
617 /*===========================================================================*
618 * dsp_setup()
619 *===========================================================================*/
620 PRIVATE void dsp_setup()
622 /* Set current sample speed */
623 dsp_set_speed(DspSpeed);
625 /* Put the speaker on */
626 if(DmaMode == DEV_WRITE_S) {
627 dsp_command (DSP_CMD_SPKON); /* put speaker on */
629 /* Program DSP with dma mode */
630 dsp_command((DspBits == 8 ? DSP_CMD_8BITAUTO_OUT : DSP_CMD_16BITAUTO_OUT));
631 } else {
632 dsp_command (DSP_CMD_SPKOFF); /* put speaker off */
634 /* Program DSP with dma mode */
635 dsp_command((DspBits == 8 ? DSP_CMD_8BITAUTO_IN : DSP_CMD_16BITAUTO_IN));
638 /* Program DSP with transfer mode */
639 if (!DspSign) {
640 dsp_command((DspStereo == 1 ? DSP_MODE_STEREO_US : DSP_MODE_MONO_US));
641 } else {
642 dsp_command((DspStereo == 1 ? DSP_MODE_STEREO_S : DSP_MODE_MONO_S));
645 /* Give length of fragment to DSP */
646 if (DspBits == 8) { /* 8 bit transfer */
647 /* #bytes - 1 */
648 dsp_command((DspFragmentSize - 1) >> 0);
649 dsp_command((DspFragmentSize - 1) >> 8);
650 } else { /* 16 bit transfer */
651 /* #words - 1 */
652 dsp_command((DspFragmentSize - 1) >> 1);
653 dsp_command((DspFragmentSize - 1) >> 9);