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
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];
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
;
80 /*===========================================================================*
82 *===========================================================================*/
85 int r
, caller
, proc_nr
, s
;
88 dprint("sb16_dsp.c: main()\n");
90 /* Get a DMA buffer. */
94 /* Wait for an incoming message */
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;
105 case DEV_IOCTL
: r
= dsp_ioctl(&mess
); break;
109 case DEV_READ
: r
= EINVAL
; break; /* Not yet implemented */
110 case DEV_WRITE
: dsp_write(&mess
); continue; /* don't reply */
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 */
119 /* Finally, prepare and send the reply message. */
120 reply(TASK_REPLY
, caller
, proc_nr
, r
);
126 /*===========================================================================*
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;
155 /*===========================================================================*
157 *===========================================================================*/
158 PRIVATE
int dsp_close()
160 dprint("sb16_dsp.c: dsp_close()\n");
162 DspBusy
= 0; /* soundcard available again */
168 /*===========================================================================*
170 *===========================================================================*/
171 PRIVATE
int dsp_ioctl(m_ptr
)
175 phys_bytes user_phys
;
178 dprint("sb16_dsp.c: dsp_ioctl()\n");
180 /* Cannot change parameters during play or recording */
181 if(DmaBusy
>= 0) return EBUSY
;
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;
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
));
201 case DSPIORESET
: status
= dsp_reset(); break;
202 default: status
= ENOTTY
; break;
209 /*===========================================================================*
211 *===========================================================================*/
212 PRIVATE
void dsp_write(m_ptr
)
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
);
224 if(m_ptr
->m_type
!= DmaMode
&& DmaBusy
>= 0) {
225 reply(TASK_REPLY
, m_ptr
->m_source
, m_ptr
->IO_ENDPT
, EBUSY
);
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
);
237 DmaBusy
= 0; /* Dma is busy */
238 dprint(" filled dma[0]\n");
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");
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
);
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
;
267 reviveStatus
= DspFragmentSize
;
268 reviveProcNr
= m_ptr
->IO_ENDPT
;
269 notify(m_ptr
->m_source
);
273 /*===========================================================================*
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");
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
) {
300 dprint("Starting dma[%d]\n", DmaBusy
);
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 /*===========================================================================*
316 *===========================================================================*/
317 PRIVATE
void dsp_status(m_ptr
)
318 message
*m_ptr
; /* pointer to the newly arrived message */
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 */
327 m_ptr
->m_type
= DEV_NO_STATUS
;
330 send(m_ptr
->m_source
, m_ptr
); /* send the message */
334 /*===========================================================================*
336 *===========================================================================*/
337 PRIVATE
void reply(code
, replyee
, process
, status
)
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 */
353 /*===========================================================================*
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'.
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 */
373 #else /* CHIP != INTEL */
374 panic("SB16DSP","init_buffer() failed, CHIP != INTEL", 0);
375 #endif /* CHIP == INTEL */
379 /*===========================================================================*
381 *===========================================================================*/
382 PRIVATE
int dsp_init()
386 if(dsp_reset () != OK
) {
387 dprint("sb16: No SoundBlaster card detected\n");
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
);
399 DspVersion
[1] = sb16_inb(DSP_READ
);
405 if(DspVersion
[0] < 4) {
406 dprint("sb16: No SoundBlaster 16 compatible card detected\n");
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
);
427 /*===========================================================================*
429 *===========================================================================*/
430 PRIVATE
int dsp_reset()
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 */
448 /*===========================================================================*
450 *===========================================================================*/
451 PRIVATE
int dsp_command(value
)
456 for (i
= 0; i
< SB_TIMEOUT
; i
++) {
457 if((sb16_inb(DSP_STATUS
) & 0x80) == 0) {
458 sb16_outb(DSP_COMMAND
, value
);
463 dprint("sb16: SoundBlaster: DSP Command(%x) timeout\n", value
);
468 /*===========================================================================*
470 *===========================================================================*/
471 static int dsp_set_size(size
)
474 dprint("dsp_set_size(): set fragment size to %u\n", size
);
477 if(size
< DSP_MIN_FRAGMENT_SIZE
|| size
> DSP_MAX_FRAGMENT_SIZE
|| size
% 2 != 0) {
481 DspFragmentSize
= size
;
487 /*===========================================================================*
489 *===========================================================================*/
490 static int dsp_set_speed(speed
)
493 dprint("sb16: setting speed to %u, stereo = %d\n", speed
, DspStereo
);
495 if(speed
< DSP_MIN_SPEED
|| speed
> DSP_MAX_SPEED
) {
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);
519 /*===========================================================================*
521 *===========================================================================*/
522 static int dsp_set_stereo(stereo
)
535 /*===========================================================================*
537 *===========================================================================*/
538 static int dsp_set_bits(bits
)
542 if(bits
!= 8 && bits
!= 16) {
552 /*===========================================================================*
554 *===========================================================================*/
555 static int dsp_set_sign(sign
)
558 dprint("sb16: set sign to %u\n", sign
);
560 DspSign
= (sign
> 0 ? 1 : 0);
566 /*===========================================================================*
568 *===========================================================================*/
569 PRIVATE
void dsp_dma_setup(address
, count
)
576 dprint("Setting up %d bit DMA\n", DspBits
);
578 if(DspBits
== 8) { /* 8 bit sound */
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 */
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 */
595 } else { /* 16 bit sound */
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 */
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 */
617 /*===========================================================================*
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
));
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 */
640 dsp_command((DspStereo
== 1 ? DSP_MODE_STEREO_US
: DSP_MODE_MONO_US
));
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 */
648 dsp_command((DspFragmentSize
- 1) >> 0);
649 dsp_command((DspFragmentSize
- 1) >> 8);
650 } else { /* 16 bit transfer */
652 dsp_command((DspFragmentSize
- 1) >> 1);
653 dsp_command((DspFragmentSize
- 1) >> 9);