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 <minix/endpoint.h>
31 _PROTOTYPE(void main
, (void));
32 FORWARD
_PROTOTYPE( int dsp_open
, (void) );
33 FORWARD
_PROTOTYPE( int dsp_close
, (void) );
34 FORWARD
_PROTOTYPE( int dsp_ioctl
, (message
*m_ptr
) );
35 FORWARD
_PROTOTYPE( void dsp_write
, (message
*m_ptr
) );
36 FORWARD
_PROTOTYPE( void dsp_hardware_msg
, (void) );
37 FORWARD
_PROTOTYPE( void dsp_status
, (message
*m_ptr
) );
39 FORWARD
_PROTOTYPE( void reply
, (int code
, int replyee
, int process
, int status
) );
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];
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
;
79 /* SEF functions and variables. */
80 FORWARD
_PROTOTYPE( void sef_local_startup
, (void) );
81 FORWARD
_PROTOTYPE( int sef_cb_init_fresh
, (int type
, sef_init_info_t
*info
) );
82 EXTERN
_PROTOTYPE( void sef_cb_lu_prepare
, (int state
) );
83 EXTERN
_PROTOTYPE( int sef_cb_lu_state_isvalid
, (int state
) );
84 EXTERN
_PROTOTYPE( void sef_cb_lu_state_dump
, (int state
) );
85 PUBLIC
int is_processing
= FALSE
;
86 PUBLIC
int is_status_msg_expected
= FALSE
;
88 /*===========================================================================*
90 *===========================================================================*/
93 int r
, caller
, proc_nr
;
96 /* SEF local startup. */
100 /* Wait for an incoming message */
101 sef_receive(ANY
, &mess
);
103 caller
= mess
.m_source
;
104 proc_nr
= mess
.IO_ENDPT
;
106 if (is_notify(mess
.m_type
)) {
107 switch (_ENDPOINT_P(mess
.m_source
)) {
110 continue; /* don't reply */
112 continue; /* don't reply */
117 /* dont with this message */
121 /* Now carry out the work. */
122 switch(mess
.m_type
) {
123 case DEV_OPEN
: r
= dsp_open(); break;
124 case DEV_CLOSE
: r
= dsp_close(); break;
126 case DEV_IOCTL
: r
= dsp_ioctl(&mess
); break;
130 case DEV_READ
: r
= EINVAL
; break; /* Not yet implemented */
131 case DEV_WRITE
: dsp_write(&mess
); continue; /* don't reply */
134 case DEV_STATUS
: dsp_status(&mess
); continue; /* don't reply */
139 /* Finally, prepare and send the reply message. */
140 reply(TASK_REPLY
, caller
, proc_nr
, r
);
145 /*===========================================================================*
146 * sef_local_startup *
147 *===========================================================================*/
148 PRIVATE
void sef_local_startup()
150 /* Register init callbacks. */
151 sef_setcb_init_fresh(sef_cb_init_fresh
);
152 sef_setcb_init_lu(sef_cb_init_fresh
);
153 sef_setcb_init_restart(sef_cb_init_fresh
);
155 /* Register live update callbacks. */
156 sef_setcb_lu_prepare(sef_cb_lu_prepare
);
157 sef_setcb_lu_state_isvalid(sef_cb_lu_state_isvalid
);
158 sef_setcb_lu_state_dump(sef_cb_lu_state_dump
);
160 /* Let SEF perform startup. */
164 /*===========================================================================*
165 * sef_cb_init_fresh *
166 *===========================================================================*/
167 PRIVATE
int sef_cb_init_fresh(int type
, sef_init_info_t
*info
)
169 /* Initialize the rtl8169 driver. */
172 /* Select a buffer that can safely be used for dma transfers.
173 * Its absolute address is 'DmaPhys', the normal address is 'DmaPtr'.
177 sys_umap(SELF
, D
, (vir_bytes
)DmaBuffer
, (phys_bytes
)sizeof(DmaBuffer
), &DmaPhys
);
179 if((left
= dma_bytes_left(DmaPhys
)) < DMA_SIZE
) {
180 /* First half of buffer crosses a 64K boundary, can't DMA into that */
184 #else /* CHIP != INTEL */
185 panic("SB16DSP","initialization failed, CHIP != INTEL", 0);
186 #endif /* CHIP == INTEL */
191 /*===========================================================================*
193 *===========================================================================*/
194 PRIVATE
int dsp_open()
196 dprint("sb16_dsp.c: dsp_open()\n");
198 /* try to detect SoundBlaster card */
199 if(!DspAvail
&& dsp_init() != OK
) return EIO
;
201 /* Only one open at a time with soundcards */
202 if(DspBusy
) return EBUSY
;
204 /* Start with a clean DSP */
205 if(dsp_reset() != OK
) return EIO
;
207 /* Setup default values */
208 DspStereo
= DEFAULT_STEREO
;
209 DspSpeed
= DEFAULT_SPEED
;
210 DspBits
= DEFAULT_BITS
;
211 DspSign
= DEFAULT_SIGN
;
212 DspFragmentSize
= DMA_SIZE
/ 2;
220 /*===========================================================================*
222 *===========================================================================*/
223 PRIVATE
int dsp_close()
225 dprint("sb16_dsp.c: dsp_close()\n");
227 DspBusy
= 0; /* soundcard available again */
233 /*===========================================================================*
235 *===========================================================================*/
236 PRIVATE
int dsp_ioctl(m_ptr
)
240 phys_bytes user_phys
;
243 dprint("sb16_dsp.c: dsp_ioctl()\n");
245 /* Cannot change parameters during play or recording */
246 if(DmaBusy
>= 0) return EBUSY
;
249 if(m_ptr
->REQUEST
!= DSPIORESET
) {
250 sys_vircopy(m_ptr
->IO_ENDPT
, D
, (vir_bytes
)m_ptr
->ADDRESS
, SELF
, D
, (vir_bytes
)&val
, sizeof(val
));
253 dprint("dsp_ioctl: got ioctl %d, argument: %d\n", m_ptr
->REQUEST
, val
);
255 switch(m_ptr
->REQUEST
) {
256 case DSPIORATE
: status
= dsp_set_speed(val
); break;
257 case DSPIOSTEREO
: status
= dsp_set_stereo(val
); break;
258 case DSPIOBITS
: status
= dsp_set_bits(val
); break;
259 case DSPIOSIZE
: status
= dsp_set_size(val
); break;
260 case DSPIOSIGN
: status
= dsp_set_sign(val
); break;
262 val
= DSP_MAX_FRAGMENT_SIZE
;
263 sys_vircopy(SELF
, D
, (vir_bytes
)&val
, m_ptr
->IO_ENDPT
, D
, (vir_bytes
)m_ptr
->ADDRESS
, sizeof(val
));
266 case DSPIORESET
: status
= dsp_reset(); break;
267 default: status
= ENOTTY
; break;
274 /*===========================================================================*
276 *===========================================================================*/
277 PRIVATE
void dsp_write(m_ptr
)
283 dprint("sb16_dsp.c: dsp_write()\n");
285 if(m_ptr
->COUNT
!= DspFragmentSize
) {
286 reply(TASK_REPLY
, m_ptr
->m_source
, m_ptr
->IO_ENDPT
, EINVAL
);
289 if(m_ptr
->m_type
!= DmaMode
&& DmaBusy
>= 0) {
290 reply(TASK_REPLY
, m_ptr
->m_source
, m_ptr
->IO_ENDPT
, EBUSY
);
294 reply(TASK_REPLY
, m_ptr
->m_source
, m_ptr
->IO_ENDPT
, SUSPEND
);
295 is_processing
= TRUE
;
297 if(DmaBusy
< 0) { /* Dma tranfer not yet started */
299 DmaMode
= DEV_WRITE_S
; /* Dma mode is writing */
300 sys_datacopy(m_ptr
->IO_ENDPT
, (vir_bytes
)m_ptr
->ADDRESS
, SELF
, (vir_bytes
)DmaPtr
, (phys_bytes
)DspFragmentSize
);
301 dsp_dma_setup(DmaPhys
, DspFragmentSize
* DMA_NR_OF_BUFFERS
);
303 DmaBusy
= 0; /* Dma is busy */
304 dprint(" filled dma[0]\n");
307 } else if(DmaBusy
!= DmaFillNext
) { /* Dma transfer started, but Dma buffer not yet full */
309 sys_datacopy(m_ptr
->IO_ENDPT
, (vir_bytes
)m_ptr
->ADDRESS
, SELF
, (vir_bytes
)DmaPtr
+ DmaFillNext
* DspFragmentSize
, (phys_bytes
)DspFragmentSize
);
310 dprint(" filled dma[%d]\n", DmaFillNext
);
311 DmaFillNext
= (DmaFillNext
+ 1) % DMA_NR_OF_BUFFERS
;
313 } else if(BufReadNext
< 0) { /* Dma buffer full, fill first element of second buffer */
315 sys_datacopy(m_ptr
->IO_ENDPT
, (vir_bytes
)m_ptr
->ADDRESS
, SELF
, (vir_bytes
)Buffer
, (phys_bytes
)DspFragmentSize
);
316 dprint(" filled buf[0]\n");
320 } else { /* Dma buffer is full, filling second buffer */
322 while(BufReadNext
== BufFillNext
) { /* Second buffer also full, wait for space to become available */
323 sef_receive(HARDWARE
, &mess
);
326 sys_datacopy(m_ptr
->IO_ENDPT
, (vir_bytes
)m_ptr
->ADDRESS
, SELF
, (vir_bytes
)Buffer
+ BufFillNext
* DspFragmentSize
, (phys_bytes
)DspFragmentSize
);
327 dprint(" filled buf[%d]\n", BufFillNext
);
328 BufFillNext
= (BufFillNext
+ 1) % DSP_NR_OF_BUFFERS
;
332 is_status_msg_expected
= TRUE
;
334 reviveStatus
= DspFragmentSize
;
335 reviveProcNr
= m_ptr
->IO_ENDPT
;
336 notify(m_ptr
->m_source
);
340 /*===========================================================================*
342 *===========================================================================*/
343 PRIVATE
void dsp_hardware_msg()
345 dprint("Interrupt: ");
346 if(DmaBusy
>= 0) { /* Dma transfer was actually busy */
347 dprint("Finished playing dma[%d]; ", DmaBusy
);
348 DmaBusy
= (DmaBusy
+ 1) % DMA_NR_OF_BUFFERS
;
349 if(DmaBusy
== DmaFillNext
) { /* Dma buffer empty, stop Dma transfer */
351 dsp_command((DspBits
== 8 ? DSP_CMD_DMA8HALT
: DSP_CMD_DMA16HALT
));
352 dprint("No more work...!\n");
355 } else if(BufReadNext
>= 0) { /* Data in second buffer, copy one fragment to Dma buffer */
357 /* Acknowledge the interrupt on the DSP */
358 sb16_inb((DspBits
== 8 ? DSP_DATA_AVL
: DSP_DATA16_AVL
));
360 memcpy(DmaPtr
+ DmaFillNext
* DspFragmentSize
, Buffer
+ BufReadNext
* DspFragmentSize
, DspFragmentSize
);
361 dprint("copy buf[%d] -> dma[%d]; ", BufReadNext
, DmaFillNext
);
362 BufReadNext
= (BufReadNext
+ 1) % DSP_NR_OF_BUFFERS
;
363 DmaFillNext
= (DmaFillNext
+ 1) % DMA_NR_OF_BUFFERS
;
364 if(BufReadNext
== BufFillNext
) {
367 dprint("Starting dma[%d]\n", DmaBusy
);
371 } else { /* Second buffer empty, still data in Dma buffer, continue playback */
372 dprint("Starting dma[%d]\n", DmaBusy
);
376 /* Acknowledge the interrupt on the DSP */
377 sb16_inb((DspBits
== 8 ? DSP_DATA_AVL
: DSP_DATA16_AVL
));
381 /*===========================================================================*
383 *===========================================================================*/
384 PRIVATE
void dsp_status(m_ptr
)
385 message
*m_ptr
; /* pointer to the newly arrived message */
388 m_ptr
->m_type
= DEV_REVIVE
; /* build message */
389 m_ptr
->REP_ENDPT
= reviveProcNr
;
390 m_ptr
->REP_STATUS
= reviveStatus
;
392 revivePending
= 0; /* unmark event */
393 is_processing
= FALSE
;
395 m_ptr
->m_type
= DEV_NO_STATUS
;
397 is_status_msg_expected
= FALSE
;
400 send(m_ptr
->m_source
, m_ptr
); /* send the message */
404 /*===========================================================================*
406 *===========================================================================*/
407 PRIVATE
void reply(code
, replyee
, process
, status
)
415 m
.m_type
= code
; /* TASK_REPLY or REVIVE */
416 m
.REP_STATUS
= status
; /* result of device operation */
417 m
.REP_ENDPT
= process
; /* which user made the request */
422 /*===========================================================================*
424 *===========================================================================*/
425 PRIVATE
int dsp_init()
429 if(dsp_reset () != OK
) {
430 dprint("sb16: No SoundBlaster card detected\n");
434 DspVersion
[0] = DspVersion
[1] = 0;
435 dsp_command(DSP_GET_VERSION
); /* Get DSP version bytes */
437 for(i
= 1000; i
; i
--) {
438 if(sb16_inb(DSP_DATA_AVL
) & 0x80) {
439 if(DspVersion
[0] == 0) {
440 DspVersion
[0] = sb16_inb(DSP_READ
);
442 DspVersion
[1] = sb16_inb(DSP_READ
);
448 if(DspVersion
[0] < 4) {
449 dprint("sb16: No SoundBlaster 16 compatible card detected\n");
453 dprint("sb16: SoundBlaster DSP version %d.%d detected\n", DspVersion
[0], DspVersion
[1]);
455 /* set SB to use our IRQ and DMA channels */
456 mixer_set(MIXER_SET_IRQ
, (1 << (SB_IRQ
/ 2 - 1)));
457 mixer_set(MIXER_SET_DMA
, (1 << SB_DMA_8
| 1 << SB_DMA_16
));
459 /* register interrupt vector and enable irq */
460 if ((s
=sys_irqsetpolicy(SB_IRQ
, IRQ_REENABLE
, &irq_hook_id
)) != OK
)
461 panic("SB16DSP", "Couldn't set IRQ policy", s
);
462 if ((s
=sys_irqenable(&irq_hook_id
)) != OK
)
463 panic("SB16DSP", "Couldn't enable IRQ", s
);
470 /*===========================================================================*
472 *===========================================================================*/
473 PRIVATE
int dsp_reset()
477 sb16_outb(DSP_RESET
, 1);
478 for(i
= 0; i
< 1000; i
++); /* wait a while */
479 sb16_outb(DSP_RESET
, 0);
481 for(i
= 0; i
< 1000 && !(sb16_inb(DSP_DATA_AVL
) & 0x80); i
++);
483 if(sb16_inb(DSP_READ
) != 0xAA) return EIO
; /* No SoundBlaster */
491 /*===========================================================================*
493 *===========================================================================*/
494 PRIVATE
int dsp_command(value
)
499 for (i
= 0; i
< SB_TIMEOUT
; i
++) {
500 if((sb16_inb(DSP_STATUS
) & 0x80) == 0) {
501 sb16_outb(DSP_COMMAND
, value
);
506 dprint("sb16: SoundBlaster: DSP Command(%x) timeout\n", value
);
511 /*===========================================================================*
513 *===========================================================================*/
514 static int dsp_set_size(size
)
517 dprint("dsp_set_size(): set fragment size to %u\n", size
);
520 if(size
< DSP_MIN_FRAGMENT_SIZE
|| size
> DSP_MAX_FRAGMENT_SIZE
|| size
% 2 != 0) {
524 DspFragmentSize
= size
;
530 /*===========================================================================*
532 *===========================================================================*/
533 static int dsp_set_speed(speed
)
536 dprint("sb16: setting speed to %u, stereo = %d\n", speed
, DspStereo
);
538 if(speed
< DSP_MIN_SPEED
|| speed
> DSP_MAX_SPEED
) {
542 /* Soundblaster 16 can be programmed with real sample rates
543 * instead of time constants
545 * Since you cannot sample and play at the same time
546 * we set in- and output rate to the same value
549 dsp_command(DSP_INPUT_RATE
); /* set input rate */
550 dsp_command(speed
>> 8); /* high byte of speed */
551 dsp_command(speed
); /* low byte of speed */
552 dsp_command(DSP_OUTPUT_RATE
); /* same for output rate */
553 dsp_command(speed
>> 8);
562 /*===========================================================================*
564 *===========================================================================*/
565 static int dsp_set_stereo(stereo
)
578 /*===========================================================================*
580 *===========================================================================*/
581 static int dsp_set_bits(bits
)
585 if(bits
!= 8 && bits
!= 16) {
595 /*===========================================================================*
597 *===========================================================================*/
598 static int dsp_set_sign(sign
)
601 dprint("sb16: set sign to %u\n", sign
);
603 DspSign
= (sign
> 0 ? 1 : 0);
609 /*===========================================================================*
611 *===========================================================================*/
612 PRIVATE
void dsp_dma_setup(address
, count
)
619 dprint("Setting up %d bit DMA\n", DspBits
);
621 if(DspBits
== 8) { /* 8 bit sound */
624 pv_set(pvb
[0], DMA8_MASK
, SB_DMA_8
| 0x04); /* Disable DMA channel */
625 pv_set(pvb
[1], DMA8_CLEAR
, 0x00); /* Clear flip flop */
628 pv_set(pvb
[2], DMA8_MODE
, (DmaMode
== DEV_WRITE_S
? DMA8_AUTO_PLAY
: DMA8_AUTO_REC
));
630 pv_set(pvb
[3], DMA8_ADDR
, (address
>> 0) & 0xff); /* Low_byte of address */
631 pv_set(pvb
[4], DMA8_ADDR
, (address
>> 8) & 0xff); /* High byte of address */
632 pv_set(pvb
[5], DMA8_PAGE
, (address
>> 16) & 0xff); /* 64K page number */
633 pv_set(pvb
[6], DMA8_COUNT
, (count
>> 0) & 0xff); /* Low byte of count */
634 pv_set(pvb
[7], DMA8_COUNT
, (count
>> 8) & 0xff); /* High byte of count */
635 pv_set(pvb
[8], DMA8_MASK
, SB_DMA_8
); /* Enable DMA channel */
638 } else { /* 16 bit sound */
641 pv_set(pvb
[0], DMA16_MASK
, (SB_DMA_16
& 3) | 0x04); /* Disable DMA channel */
643 pv_set(pvb
[1], DMA16_CLEAR
, 0x00); /* Clear flip flop */
646 pv_set(pvb
[2], DMA16_MODE
, (DmaMode
== DEV_WRITE_S
? DMA16_AUTO_PLAY
: DMA16_AUTO_REC
));
648 pv_set(pvb
[3], DMA16_ADDR
, (address
>> 1) & 0xFF); /* Low_byte of address */
649 pv_set(pvb
[4], DMA16_ADDR
, (address
>> 9) & 0xFF); /* High byte of address */
650 pv_set(pvb
[5], DMA16_PAGE
, (address
>> 16) & 0xFE); /* 128K page number */
651 pv_set(pvb
[6], DMA16_COUNT
, (count
>> 1) & 0xff); /* Low byte of count */
652 pv_set(pvb
[7], DMA16_COUNT
, (count
>> 9) & 0xff); /* High byte of count */
653 pv_set(pvb
[8], DMA16_MASK
, SB_DMA_16
& 3); /* Enable DMA channel */
660 /*===========================================================================*
662 *===========================================================================*/
663 PRIVATE
void dsp_setup()
665 /* Set current sample speed */
666 dsp_set_speed(DspSpeed
);
668 /* Put the speaker on */
669 if(DmaMode
== DEV_WRITE_S
) {
670 dsp_command (DSP_CMD_SPKON
); /* put speaker on */
672 /* Program DSP with dma mode */
673 dsp_command((DspBits
== 8 ? DSP_CMD_8BITAUTO_OUT
: DSP_CMD_16BITAUTO_OUT
));
675 dsp_command (DSP_CMD_SPKOFF
); /* put speaker off */
677 /* Program DSP with dma mode */
678 dsp_command((DspBits
== 8 ? DSP_CMD_8BITAUTO_IN
: DSP_CMD_16BITAUTO_IN
));
681 /* Program DSP with transfer mode */
683 dsp_command((DspStereo
== 1 ? DSP_MODE_STEREO_US
: DSP_MODE_MONO_US
));
685 dsp_command((DspStereo
== 1 ? DSP_MODE_STEREO_S
: DSP_MODE_MONO_S
));
688 /* Give length of fragment to DSP */
689 if (DspBits
== 8) { /* 8 bit transfer */
691 dsp_command((DspFragmentSize
- 1) >> 0);
692 dsp_command((DspFragmentSize
- 1) >> 8);
693 } else { /* 16 bit transfer */
695 dsp_command((DspFragmentSize
- 1) >> 1);
696 dsp_command((DspFragmentSize
- 1) >> 9);