source: trunk/minix/drivers/sb16/sb16_dsp.c@ 20

Last change on this file since 20 was 9, checked in by Mattia Monga, 14 years ago

Minix 3.1.2a

File size: 19.1 KB
RevLine 
[9]1/* This file contains the driver for a DSP (Digital Sound Processor) on
2 * a SoundBlaster 16 soundcard.
3 *
4 * The driver supports the following operations (using message format m2):
5 *
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 * ----------------------------------------------------------------
18 *
19 * The file contains one entry point:
20 *
21 * main: main entry when driver is brought up
22 *
23 * August 24 2005 Ported driver to user space (only audio playback) (Peter Boonstoppel)
24 * May 20 1995 Author: Michel R. Prevenier
25 */
26
27#include "sb16.h"
28
29
30_PROTOTYPE(void main, (void));
31FORWARD _PROTOTYPE( int dsp_open, (void) );
32FORWARD _PROTOTYPE( int dsp_close, (void) );
33FORWARD _PROTOTYPE( int dsp_ioctl, (message *m_ptr) );
34FORWARD _PROTOTYPE( void dsp_write, (message *m_ptr) );
35FORWARD _PROTOTYPE( void dsp_hardware_msg, (void) );
36FORWARD _PROTOTYPE( void dsp_status, (message *m_ptr) );
37
38FORWARD _PROTOTYPE( void reply, (int code, int replyee, int process, int status) );
39FORWARD _PROTOTYPE( void init_buffer, (void) );
40FORWARD _PROTOTYPE( int dsp_init, (void) );
41FORWARD _PROTOTYPE( int dsp_reset, (void) );
42FORWARD _PROTOTYPE( int dsp_command, (int value) );
43FORWARD _PROTOTYPE( int dsp_set_size, (unsigned int size) );
44FORWARD _PROTOTYPE( int dsp_set_speed, (unsigned int speed) );
45FORWARD _PROTOTYPE( int dsp_set_stereo, (unsigned int stereo) );
46FORWARD _PROTOTYPE( int dsp_set_bits, (unsigned int bits) );
47FORWARD _PROTOTYPE( int dsp_set_sign, (unsigned int sign) );
48FORWARD _PROTOTYPE( void dsp_dma_setup, (phys_bytes address, int count) );
49FORWARD _PROTOTYPE( void dsp_setup, (void) );
50
51PRIVATE int irq_hook_id; /* id of irq hook at the kernel */
52
53PRIVATE char DmaBuffer[DMA_SIZE + 64 * 1024];
54PRIVATE char* DmaPtr;
55PRIVATE phys_bytes DmaPhys;
56
57PRIVATE char Buffer[DSP_MAX_FRAGMENT_SIZE * DSP_NR_OF_BUFFERS];
58
59PRIVATE int DspVersion[2];
60PRIVATE unsigned int DspStereo = DEFAULT_STEREO;
61PRIVATE unsigned int DspSpeed = DEFAULT_SPEED;
62PRIVATE unsigned int DspBits = DEFAULT_BITS;
63PRIVATE unsigned int DspSign = DEFAULT_SIGN;
64PRIVATE unsigned int DspFragmentSize = DSP_MAX_FRAGMENT_SIZE;
65PRIVATE int DspAvail = 0;
66PRIVATE int DspBusy = 0;
67PRIVATE int DmaMode = 0;
68PRIVATE int DmaBusy = -1;
69PRIVATE int DmaFillNext = 0;
70PRIVATE int BufReadNext = -1;
71PRIVATE int BufFillNext = 0;
72
73PRIVATE int revivePending = 0;
74PRIVATE int reviveStatus;
75PRIVATE int reviveProcNr;
76
77#define dprint (void)
78
79
80/*===========================================================================*
81 * main
82 *===========================================================================*/
83PUBLIC void main()
84{
85 int r, caller, proc_nr, s;
86 message mess;
87
88 dprint("sb16_dsp.c: main()\n");
89
90 /* Get a DMA buffer. */
91 init_buffer();
92
93 while(TRUE) {
94 /* Wait for an incoming message */
95 receive(ANY, &mess);
96
97 caller = mess.m_source;
98 proc_nr = mess.IO_ENDPT;
99
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 case DEV_IOCTL: r = dsp_ioctl(&mess); break;
105
106 case DEV_READ: r = EINVAL; break; /* Not yet implemented */
107 case DEV_WRITE: dsp_write(&mess); continue; /* don't reply */
108
109 case DEV_STATUS: dsp_status(&mess); continue; /* don't reply */
110 case HARD_INT: dsp_hardware_msg(); continue; /* don't reply */
111 case SYS_SIG: continue; /* don't reply */
112 default: r = EINVAL;
113 }
114
115 /* Finally, prepare and send the reply message. */
116 reply(TASK_REPLY, caller, proc_nr, r);
117 }
118
119}
120
121
122/*===========================================================================*
123 * dsp_open
124 *===========================================================================*/
125PRIVATE int dsp_open()
126{
127 dprint("sb16_dsp.c: dsp_open()\n");
128
129 /* try to detect SoundBlaster card */
130 if(!DspAvail && dsp_init() != OK) return EIO;
131
132 /* Only one open at a time with soundcards */
133 if(DspBusy) return EBUSY;
134
135 /* Start with a clean DSP */
136 if(dsp_reset() != OK) return EIO;
137
138 /* Setup default values */
139 DspStereo = DEFAULT_STEREO;
140 DspSpeed = DEFAULT_SPEED;
141 DspBits = DEFAULT_BITS;
142 DspSign = DEFAULT_SIGN;
143 DspFragmentSize = DMA_SIZE / 2;
144
145 DspBusy = 1;
146
147 return OK;
148}
149
150
151/*===========================================================================*
152 * dsp_close
153 *===========================================================================*/
154PRIVATE int dsp_close()
155{
156 dprint("sb16_dsp.c: dsp_close()\n");
157
158 DspBusy = 0; /* soundcard available again */
159
160 return OK;
161}
162
163
164/*===========================================================================*
165 * dsp_ioctl
166 *===========================================================================*/
167PRIVATE int dsp_ioctl(m_ptr)
168message *m_ptr;
169{
170 int status;
171 phys_bytes user_phys;
172 unsigned int val;
173
174 dprint("sb16_dsp.c: dsp_ioctl()\n");
175
176 /* Cannot change parameters during play or recording */
177 if(DmaBusy >= 0) return EBUSY;
178
179 /* Get user data */
180 if(m_ptr->REQUEST != DSPIORESET) {
181 sys_vircopy(m_ptr->IO_ENDPT, D, (vir_bytes)m_ptr->ADDRESS, SELF, D, (vir_bytes)&val, sizeof(val));
182 }
183
184 dprint("dsp_ioctl: got ioctl %d, argument: %d\n", m_ptr->REQUEST, val);
185
186 switch(m_ptr->REQUEST) {
187 case DSPIORATE: status = dsp_set_speed(val); break;
188 case DSPIOSTEREO: status = dsp_set_stereo(val); break;
189 case DSPIOBITS: status = dsp_set_bits(val); break;
190 case DSPIOSIZE: status = dsp_set_size(val); break;
191 case DSPIOSIGN: status = dsp_set_sign(val); break;
192 case DSPIOMAX:
193 val = DSP_MAX_FRAGMENT_SIZE;
194 sys_vircopy(SELF, D, (vir_bytes)&val, m_ptr->IO_ENDPT, D, (vir_bytes)m_ptr->ADDRESS, sizeof(val));
195 status = OK;
196 break;
197 case DSPIORESET: status = dsp_reset(); break;
198 default: status = ENOTTY; break;
199 }
200
201 return status;
202}
203
204
205/*===========================================================================*
206 * dsp_write
207 *===========================================================================*/
208PRIVATE void dsp_write(m_ptr)
209message *m_ptr;
210{
211 int s;
212 message mess;
213
214 dprint("sb16_dsp.c: dsp_write()\n");
215
216 if(m_ptr->COUNT != DspFragmentSize) {
217 reply(TASK_REPLY, m_ptr->m_source, m_ptr->IO_ENDPT, EINVAL);
218 return;
219 }
220 if(m_ptr->m_type != DmaMode && DmaBusy >= 0) {
221 reply(TASK_REPLY, m_ptr->m_source, m_ptr->IO_ENDPT, EBUSY);
222 return;
223 }
224
225 reply(TASK_REPLY, m_ptr->m_source, m_ptr->IO_ENDPT, SUSPEND);
226
227 if(DmaBusy < 0) { /* Dma tranfer not yet started */
228
229 DmaMode = DEV_WRITE; /* Dma mode is writing */
230 sys_datacopy(m_ptr->IO_ENDPT, (vir_bytes)m_ptr->ADDRESS, SELF, (vir_bytes)DmaPtr, (phys_bytes)DspFragmentSize);
231 dsp_dma_setup(DmaPhys, DspFragmentSize * DMA_NR_OF_BUFFERS);
232 dsp_setup();
233 DmaBusy = 0; /* Dma is busy */
234 dprint(" filled dma[0]\n");
235 DmaFillNext = 1;
236
237 } else if(DmaBusy != DmaFillNext) { /* Dma transfer started, but Dma buffer not yet full */
238
239 sys_datacopy(m_ptr->IO_ENDPT, (vir_bytes)m_ptr->ADDRESS, SELF, (vir_bytes)DmaPtr + DmaFillNext * DspFragmentSize, (phys_bytes)DspFragmentSize);
240 dprint(" filled dma[%d]\n", DmaFillNext);
241 DmaFillNext = (DmaFillNext + 1) % DMA_NR_OF_BUFFERS;
242
243 } else if(BufReadNext < 0) { /* Dma buffer full, fill first element of second buffer */
244
245 sys_datacopy(m_ptr->IO_ENDPT, (vir_bytes)m_ptr->ADDRESS, SELF, (vir_bytes)Buffer, (phys_bytes)DspFragmentSize);
246 dprint(" filled buf[0]\n");
247 BufReadNext = 0;
248 BufFillNext = 1;
249
250 } else { /* Dma buffer is full, filling second buffer */
251
252 while(BufReadNext == BufFillNext) { /* Second buffer also full, wait for space to become available */
253 receive(HARDWARE, &mess);
254 dsp_hardware_msg();
255 }
256 sys_datacopy(m_ptr->IO_ENDPT, (vir_bytes)m_ptr->ADDRESS, SELF, (vir_bytes)Buffer + BufFillNext * DspFragmentSize, (phys_bytes)DspFragmentSize);
257 dprint(" filled buf[%d]\n", BufFillNext);
258 BufFillNext = (BufFillNext + 1) % DSP_NR_OF_BUFFERS;
259
260 }
261
262 revivePending = 1;
263 reviveStatus = DspFragmentSize;
264 reviveProcNr = m_ptr->IO_ENDPT;
265 notify(m_ptr->m_source);
266}
267
268
269/*===========================================================================*
270 * dsp_hardware_msg
271 *===========================================================================*/
272PRIVATE void dsp_hardware_msg()
273{
274 dprint("Interrupt: ");
275 if(DmaBusy >= 0) { /* Dma transfer was actually busy */
276 dprint("Finished playing dma[%d]; ", DmaBusy);
277 DmaBusy = (DmaBusy + 1) % DMA_NR_OF_BUFFERS;
278 if(DmaBusy == DmaFillNext) { /* Dma buffer empty, stop Dma transfer */
279
280 dsp_command((DspBits == 8 ? DSP_CMD_DMA8HALT : DSP_CMD_DMA16HALT));
281 dprint("No more work...!\n");
282 DmaBusy = -1;
283
284 } else if(BufReadNext >= 0) { /* Data in second buffer, copy one fragment to Dma buffer */
285
286 /* Acknowledge the interrupt on the DSP */
287 sb16_inb((DspBits == 8 ? DSP_DATA_AVL : DSP_DATA16_AVL));
288
289 memcpy(DmaPtr + DmaFillNext * DspFragmentSize, Buffer + BufReadNext * DspFragmentSize, DspFragmentSize);
290 dprint("copy buf[%d] -> dma[%d]; ", BufReadNext, DmaFillNext);
291 BufReadNext = (BufReadNext + 1) % DSP_NR_OF_BUFFERS;
292 DmaFillNext = (DmaFillNext + 1) % DMA_NR_OF_BUFFERS;
293 if(BufReadNext == BufFillNext) {
294 BufReadNext = -1;
295 }
296 dprint("Starting dma[%d]\n", DmaBusy);
297
298 return;
299
300 } else { /* Second buffer empty, still data in Dma buffer, continue playback */
301 dprint("Starting dma[%d]\n", DmaBusy);
302 }
303 }
304
305 /* Acknowledge the interrupt on the DSP */
306 sb16_inb((DspBits == 8 ? DSP_DATA_AVL : DSP_DATA16_AVL));
307}
308
309
310/*===========================================================================*
311 * dsp_status *
312 *===========================================================================*/
313PRIVATE void dsp_status(m_ptr)
314message *m_ptr; /* pointer to the newly arrived message */
315{
316 if(revivePending) {
317 m_ptr->m_type = DEV_REVIVE; /* build message */
318 m_ptr->REP_ENDPT = reviveProcNr;
319 m_ptr->REP_STATUS = reviveStatus;
320
321 revivePending = 0; /* unmark event */
322 } else {
323 m_ptr->m_type = DEV_NO_STATUS;
324 }
325
326 send(m_ptr->m_source, m_ptr); /* send the message */
327}
328
329
330/*===========================================================================*
331 * reply *
332 *===========================================================================*/
333PRIVATE void reply(code, replyee, process, status)
334int code;
335int replyee;
336int process;
337int status;
338{
339 message m;
340
341 m.m_type = code; /* TASK_REPLY or REVIVE */
342 m.REP_STATUS = status; /* result of device operation */
343 m.REP_ENDPT = process; /* which user made the request */
344
345 send(replyee, &m);
346}
347
348
349/*===========================================================================*
350 * init_buffer
351 *===========================================================================*/
352PRIVATE void init_buffer()
353{
354/* Select a buffer that can safely be used for dma transfers.
355 * Its absolute address is 'DmaPhys', the normal address is 'DmaPtr'.
356 */
357
358#if (CHIP == INTEL)
359 unsigned left;
360
361 DmaPtr = DmaBuffer;
362 sys_umap(SELF, D, (vir_bytes)DmaBuffer, (phys_bytes)sizeof(DmaBuffer), &DmaPhys);
363
364 if((left = dma_bytes_left(DmaPhys)) < DMA_SIZE) {
365 /* First half of buffer crosses a 64K boundary, can't DMA into that */
366 DmaPtr += left;
367 DmaPhys += left;
368 }
369#else /* CHIP != INTEL */
370 panic("SB16DSP","init_buffer() failed, CHIP != INTEL", 0);
371#endif /* CHIP == INTEL */
372}
373
374
375/*===========================================================================*
376 * dsp_init
377 *===========================================================================*/
378PRIVATE int dsp_init()
379{
380 int i, s;
381
382 if(dsp_reset () != OK) {
383 dprint("sb16: No SoundBlaster card detected\n");
384 return -1;
385 }
386
387 DspVersion[0] = DspVersion[1] = 0;
388 dsp_command(DSP_GET_VERSION); /* Get DSP version bytes */
389
390 for(i = 1000; i; i--) {
391 if(sb16_inb(DSP_DATA_AVL) & 0x80) {
392 if(DspVersion[0] == 0) {
393 DspVersion[0] = sb16_inb(DSP_READ);
394 } else {
395 DspVersion[1] = sb16_inb(DSP_READ);
396 break;
397 }
398 }
399 }
400
401 if(DspVersion[0] < 4) {
402 dprint("sb16: No SoundBlaster 16 compatible card detected\n");
403 return -1;
404 }
405
406 dprint("sb16: SoundBlaster DSP version %d.%d detected\n", DspVersion[0], DspVersion[1]);
407
408 /* set SB to use our IRQ and DMA channels */
409 mixer_set(MIXER_SET_IRQ, (1 << (SB_IRQ / 2 - 1)));
410 mixer_set(MIXER_SET_DMA, (1 << SB_DMA_8 | 1 << SB_DMA_16));
411
412 /* register interrupt vector and enable irq */
413 if ((s=sys_irqsetpolicy(SB_IRQ, IRQ_REENABLE, &irq_hook_id )) != OK)
414 panic("SB16DSP", "Couldn't set IRQ policy", s);
415 if ((s=sys_irqenable(&irq_hook_id)) != OK)
416 panic("SB16DSP", "Couldn't enable IRQ", s);
417
418 DspAvail = 1;
419 return OK;
420}
421
422
423/*===========================================================================*
424 * dsp_reset
425 *===========================================================================*/
426PRIVATE int dsp_reset()
427{
428 int i;
429
430 sb16_outb(DSP_RESET, 1);
431 for(i = 0; i < 1000; i++); /* wait a while */
432 sb16_outb(DSP_RESET, 0);
433
434 for(i = 0; i < 1000 && !(sb16_inb(DSP_DATA_AVL) & 0x80); i++);
435
436 if(sb16_inb(DSP_READ) != 0xAA) return EIO; /* No SoundBlaster */
437
438 DmaBusy = -1;
439
440 return OK;
441}
442
443
444/*===========================================================================*
445 * dsp_command
446 *===========================================================================*/
447PRIVATE int dsp_command(value)
448int value;
449{
450 int i, status;
451
452 for (i = 0; i < SB_TIMEOUT; i++) {
453 if((sb16_inb(DSP_STATUS) & 0x80) == 0) {
454 sb16_outb(DSP_COMMAND, value);
455 return OK;
456 }
457 }
458
459 dprint("sb16: SoundBlaster: DSP Command(%x) timeout\n", value);
460 return -1;
461}
462
463
464/*===========================================================================*
465 * dsp_set_size
466 *===========================================================================*/
467static int dsp_set_size(size)
468unsigned int size;
469{
470 dprint("dsp_set_size(): set fragment size to %u\n", size);
471
472 /* Sanity checks */
473 if(size < DSP_MIN_FRAGMENT_SIZE || size > DSP_MAX_FRAGMENT_SIZE || size % 2 != 0) {
474 return EINVAL;
475 }
476
477 DspFragmentSize = size;
478
479 return OK;
480}
481
482
483/*===========================================================================*
484 * dsp_set_speed
485 *===========================================================================*/
486static int dsp_set_speed(speed)
487unsigned int speed;
488{
489 dprint("sb16: setting speed to %u, stereo = %d\n", speed, DspStereo);
490
491 if(speed < DSP_MIN_SPEED || speed > DSP_MAX_SPEED) {
492 return EPERM;
493 }
494
495 /* Soundblaster 16 can be programmed with real sample rates
496 * instead of time constants
497 *
498 * Since you cannot sample and play at the same time
499 * we set in- and output rate to the same value
500 */
501
502 dsp_command(DSP_INPUT_RATE); /* set input rate */
503 dsp_command(speed >> 8); /* high byte of speed */
504 dsp_command(speed); /* low byte of speed */
505 dsp_command(DSP_OUTPUT_RATE); /* same for output rate */
506 dsp_command(speed >> 8);
507 dsp_command(speed);
508
509 DspSpeed = speed;
510
511 return OK;
512}
513
514
515/*===========================================================================*
516 * dsp_set_stereo
517 *===========================================================================*/
518static int dsp_set_stereo(stereo)
519unsigned int stereo;
520{
521 if(stereo) {
522 DspStereo = 1;
523 } else {
524 DspStereo = 0;
525 }
526
527 return OK;
528}
529
530
531/*===========================================================================*
532 * dsp_set_bits
533 *===========================================================================*/
534static int dsp_set_bits(bits)
535unsigned int bits;
536{
537 /* Sanity checks */
538 if(bits != 8 && bits != 16) {
539 return EINVAL;
540 }
541
542 DspBits = bits;
543
544 return OK;
545}
546
547
548/*===========================================================================*
549 * dsp_set_sign
550 *===========================================================================*/
551static int dsp_set_sign(sign)
552unsigned int sign;
553{
554 dprint("sb16: set sign to %u\n", sign);
555
556 DspSign = (sign > 0 ? 1 : 0);
557
558 return OK;
559}
560
561
562/*===========================================================================*
563 * dsp_dma_setup
564 *===========================================================================*/
565PRIVATE void dsp_dma_setup(address, count)
566phys_bytes address;
567int count;
568{
569 pvb_pair_t pvb[9];
570
571
572 dprint("Setting up %d bit DMA\n", DspBits);
573
574 if(DspBits == 8) { /* 8 bit sound */
575 count--;
576
577 pv_set(pvb[0], DMA8_MASK, SB_DMA_8 | 0x04); /* Disable DMA channel */
578 pv_set(pvb[1], DMA8_CLEAR, 0x00); /* Clear flip flop */
579
580 /* set DMA mode */
581 pv_set(pvb[2], DMA8_MODE, (DmaMode == DEV_WRITE ? DMA8_AUTO_PLAY : DMA8_AUTO_REC));
582
583 pv_set(pvb[3], DMA8_ADDR, address >> 0); /* Low_byte of address */
584 pv_set(pvb[4], DMA8_ADDR, address >> 8); /* High byte of address */
585 pv_set(pvb[5], DMA8_PAGE, address >> 16); /* 64K page number */
586 pv_set(pvb[6], DMA8_COUNT, count >> 0); /* Low byte of count */
587 pv_set(pvb[7], DMA8_COUNT, count >> 8); /* High byte of count */
588 pv_set(pvb[8], DMA8_MASK, SB_DMA_8); /* Enable DMA channel */
589
590 sys_voutb(pvb, 9);
591 } else { /* 16 bit sound */
592 count-= 2;
593
594 pv_set(pvb[0], DMA16_MASK, (SB_DMA_16 & 3) | 0x04); /* Disable DMA channel */
595
596 pv_set(pvb[1], DMA16_CLEAR, 0x00); /* Clear flip flop */
597
598 /* Set dma mode */
599 pv_set(pvb[2], DMA16_MODE, (DmaMode == DEV_WRITE ? DMA16_AUTO_PLAY : DMA16_AUTO_REC));
600
601 pv_set(pvb[3], DMA16_ADDR, (address >> 1) & 0xFF); /* Low_byte of address */
602 pv_set(pvb[4], DMA16_ADDR, (address >> 9) & 0xFF); /* High byte of address */
603 pv_set(pvb[5], DMA16_PAGE, (address >> 16) & 0xFE); /* 128K page number */
604 pv_set(pvb[6], DMA16_COUNT, count >> 1); /* Low byte of count */
605 pv_set(pvb[7], DMA16_COUNT, count >> 9); /* High byte of count */
606 pv_set(pvb[8], DMA16_MASK, SB_DMA_16 & 3); /* Enable DMA channel */
607
608 sys_voutb(pvb, 9);
609 }
610}
611
612
613/*===========================================================================*
614 * dsp_setup()
615 *===========================================================================*/
616PRIVATE void dsp_setup()
617{
618 /* Set current sample speed */
619 dsp_set_speed(DspSpeed);
620
621 /* Put the speaker on */
622 if(DmaMode == DEV_WRITE) {
623 dsp_command (DSP_CMD_SPKON); /* put speaker on */
624
625 /* Program DSP with dma mode */
626 dsp_command((DspBits == 8 ? DSP_CMD_8BITAUTO_OUT : DSP_CMD_16BITAUTO_OUT));
627 } else {
628 dsp_command (DSP_CMD_SPKOFF); /* put speaker off */
629
630 /* Program DSP with dma mode */
631 dsp_command((DspBits == 8 ? DSP_CMD_8BITAUTO_IN : DSP_CMD_16BITAUTO_IN));
632 }
633
634 /* Program DSP with transfer mode */
635 if (!DspSign) {
636 dsp_command((DspStereo == 1 ? DSP_MODE_STEREO_US : DSP_MODE_MONO_US));
637 } else {
638 dsp_command((DspStereo == 1 ? DSP_MODE_STEREO_S : DSP_MODE_MONO_S));
639 }
640
641 /* Give length of fragment to DSP */
642 if (DspBits == 8) { /* 8 bit transfer */
643 /* #bytes - 1 */
644 dsp_command((DspFragmentSize - 1) >> 0);
645 dsp_command((DspFragmentSize - 1) >> 8);
646 } else { /* 16 bit transfer */
647 /* #words - 1 */
648 dsp_command((DspFragmentSize - 1) >> 1);
649 dsp_command((DspFragmentSize - 1) >> 9);
650 }
651}
652
653
Note: See TracBrowser for help on using the repository browser.