2 * linux/sound/oss/dmasound/dmasound_q40.c
6 * See linux/sound/oss/dmasound/dmasound_core.c for copyright and credits
9 * 28/01/2001 [0.1] Iain Sandoe
11 * - put in and populated the hardware_afmts field.
12 * [0.2] - put in SNDCTL_DSP_GETCAPS value.
13 * [0.3] - put in default hard/soft settings.
17 #include <linux/module.h>
18 #include <linux/init.h>
19 #include <linux/slab.h>
20 #include <linux/soundcard.h>
21 #include <linux/interrupt.h>
23 #include <asm/uaccess.h>
24 #include <asm/q40ints.h>
25 #include <asm/q40_master.h>
29 #define DMASOUND_Q40_REVISION 0
30 #define DMASOUND_Q40_EDITION 3
32 static int expand_bal; /* Balance factor for expanding (not volume!) */
33 static int expand_data; /* Data for expanding */
36 /*** Low level stuff *********************************************************/
39 static void *Q40Alloc(unsigned int size, gfp_t flags);
40 static void Q40Free(void *, unsigned int);
41 static int Q40IrqInit(void);
43 static void Q40IrqCleanUp(void);
45 static void Q40Silence(void);
46 static void Q40Init(void);
47 static int Q40SetFormat(int format);
48 static int Q40SetVolume(int volume);
49 static void Q40PlayNextFrame(int index);
50 static void Q40Play(void);
51 static irqreturn_t Q40StereoInterrupt(int irq, void *dummy);
52 static irqreturn_t Q40MonoInterrupt(int irq, void *dummy);
53 static void Q40Interrupt(void);
56 /*** Mid level stuff *********************************************************/
60 /* userCount, frameUsed, frameLeft == byte counts */
61 static ssize_t q40_ct_law(const u_char __user *userPtr, size_t userCount,
62 u_char frame[], ssize_t *frameUsed,
65 char *table = dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8: dmasound_alaw2dma8;
67 u_char *p = (u_char *) &frame[*frameUsed];
69 used = count = min_t(size_t, userCount, frameLeft);
70 if (copy_from_user(p,userPtr,count))
82 static ssize_t q40_ct_s8(const u_char __user *userPtr, size_t userCount,
83 u_char frame[], ssize_t *frameUsed,
87 u_char *p = (u_char *) &frame[*frameUsed];
89 used = count = min_t(size_t, userCount, frameLeft);
90 if (copy_from_user(p,userPtr,count))
101 static ssize_t q40_ct_u8(const u_char __user *userPtr, size_t userCount,
102 u_char frame[], ssize_t *frameUsed,
106 u_char *p = (u_char *) &frame[*frameUsed];
108 used = count = min_t(size_t, userCount, frameLeft);
109 if (copy_from_user(p,userPtr,count))
116 /* a bit too complicated to optimise right now ..*/
117 static ssize_t q40_ctx_law(const u_char __user *userPtr, size_t userCount,
118 u_char frame[], ssize_t *frameUsed,
121 unsigned char *table = (unsigned char *)
122 (dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8: dmasound_alaw2dma8);
123 unsigned int data = expand_data;
124 u_char *p = (u_char *) &frame[*frameUsed];
125 int bal = expand_bal;
126 int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
136 if (get_user(c, userPtr++))
149 *frameUsed += (ftotal - frameLeft);
155 static ssize_t q40_ctx_s8(const u_char __user *userPtr, size_t userCount,
156 u_char frame[], ssize_t *frameUsed,
159 u_char *p = (u_char *) &frame[*frameUsed];
160 unsigned int data = expand_data;
161 int bal = expand_bal;
162 int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
173 if (get_user(c, userPtr++))
186 *frameUsed += (ftotal - frameLeft);
192 static ssize_t q40_ctx_u8(const u_char __user *userPtr, size_t userCount,
193 u_char frame[], ssize_t *frameUsed,
196 u_char *p = (u_char *) &frame[*frameUsed];
197 unsigned int data = expand_data;
198 int bal = expand_bal;
199 int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
209 if (get_user(c, userPtr++))
221 *frameUsed += (ftotal - frameLeft) ;
226 /* compressing versions */
227 static ssize_t q40_ctc_law(const u_char __user *userPtr, size_t userCount,
228 u_char frame[], ssize_t *frameUsed,
231 unsigned char *table = (unsigned char *)
232 (dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8: dmasound_alaw2dma8);
233 unsigned int data = expand_data;
234 u_char *p = (u_char *) &frame[*frameUsed];
235 int bal = expand_bal;
236 int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
246 if (!(bal<(-hSpeed))) {
247 if (get_user(c, userPtr))
249 data = 0x80 + table[c];
262 *frameUsed += (ftotal - frameLeft);
268 static ssize_t q40_ctc_s8(const u_char __user *userPtr, size_t userCount,
269 u_char frame[], ssize_t *frameUsed,
272 u_char *p = (u_char *) &frame[*frameUsed];
273 unsigned int data = expand_data;
274 int bal = expand_bal;
275 int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
285 if (!(bal<(-hSpeed))) {
286 if (get_user(c, userPtr))
301 *frameUsed += (ftotal - frameLeft);
307 static ssize_t q40_ctc_u8(const u_char __user *userPtr, size_t userCount,
308 u_char frame[], ssize_t *frameUsed,
311 u_char *p = (u_char *) &frame[*frameUsed];
312 unsigned int data = expand_data;
313 int bal = expand_bal;
314 int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
324 if (!(bal<(-hSpeed))) {
325 if (get_user(c, userPtr))
340 *frameUsed += (ftotal - frameLeft) ;
346 static TRANS transQ40Normal = {
347 q40_ct_law, q40_ct_law, q40_ct_s8, q40_ct_u8, NULL, NULL, NULL, NULL
350 static TRANS transQ40Expanding = {
351 q40_ctx_law, q40_ctx_law, q40_ctx_s8, q40_ctx_u8, NULL, NULL, NULL, NULL
354 static TRANS transQ40Compressing = {
355 q40_ctc_law, q40_ctc_law, q40_ctc_s8, q40_ctc_u8, NULL, NULL, NULL, NULL
359 /*** Low level stuff *********************************************************/
361 static void *Q40Alloc(unsigned int size, gfp_t flags)
363 return kmalloc(size, flags); /* change to vmalloc */
366 static void Q40Free(void *ptr, unsigned int size)
371 static int __init Q40IrqInit(void)
373 /* Register interrupt handler. */
374 request_irq(Q40_IRQ_SAMPLE, Q40StereoInterrupt, 0,
375 "DMA sound", Q40Interrupt);
382 static void Q40IrqCleanUp(void)
384 master_outb(0,SAMPLE_ENABLE_REG);
385 free_irq(Q40_IRQ_SAMPLE, Q40Interrupt);
390 static void Q40Silence(void)
392 master_outb(0,SAMPLE_ENABLE_REG);
393 *DAC_LEFT=*DAC_RIGHT=127;
397 static unsigned int q40_sc;
399 static void Q40PlayNextFrame(int index)
405 /* used by Q40Play() if all doubts whether there really is something
406 * to be played are already wiped out.
408 start = write_sq.buffers[write_sq.front];
409 size = (write_sq.count == index ? write_sq.rear_size : write_sq.block_size);
414 write_sq.front = (write_sq.front+1) % write_sq.max_count;
417 speed=(dmasound.hard.speed==10000 ? 0 : 1);
419 master_outb( 0,SAMPLE_ENABLE_REG);
420 free_irq(Q40_IRQ_SAMPLE, Q40Interrupt);
421 if (dmasound.soft.stereo)
422 request_irq(Q40_IRQ_SAMPLE, Q40StereoInterrupt, 0,
423 "Q40 sound", Q40Interrupt);
425 request_irq(Q40_IRQ_SAMPLE, Q40MonoInterrupt, 0,
426 "Q40 sound", Q40Interrupt);
428 master_outb( speed, SAMPLE_RATE_REG);
429 master_outb( 1,SAMPLE_CLEAR_REG);
430 master_outb( 1,SAMPLE_ENABLE_REG);
433 static void Q40Play(void)
437 if (write_sq.active || write_sq.count<=0 ) {
438 /* There's already a frame loaded */
442 /* nothing in the queue */
443 if (write_sq.count <= 1 && write_sq.rear_size < write_sq.block_size && !write_sq.syncing) {
444 /* hmmm, the only existing frame is not
445 * yet filled and we're not syncing?
449 spin_lock_irqsave(&dmasound.lock, flags);
451 spin_unlock_irqrestore(&dmasound.lock, flags);
454 static irqreturn_t Q40StereoInterrupt(int irq, void *dummy)
456 spin_lock(&dmasound.lock);
459 *DAC_RIGHT=*q40_pp++;
461 master_outb(1,SAMPLE_CLEAR_REG);
462 }else Q40Interrupt();
463 spin_unlock(&dmasound.lock);
466 static irqreturn_t Q40MonoInterrupt(int irq, void *dummy)
468 spin_lock(&dmasound.lock);
471 *DAC_RIGHT=*q40_pp++;
473 master_outb(1,SAMPLE_CLEAR_REG);
474 }else Q40Interrupt();
475 spin_unlock(&dmasound.lock);
478 static void Q40Interrupt(void)
480 if (!write_sq.active) {
481 /* playing was interrupted and sq_reset() has already cleared
482 * the sq variables, so better don't do anything here.
484 WAKE_UP(write_sq.sync_queue);
485 master_outb(0,SAMPLE_ENABLE_REG); /* better safe */
487 } else write_sq.active=0;
492 { /* there was nothing to play, disable irq */
493 master_outb(0,SAMPLE_ENABLE_REG);
494 *DAC_LEFT=*DAC_RIGHT=127;
496 WAKE_UP(write_sq.action_queue);
499 master_outb(1,SAMPLE_CLEAR_REG);
503 static void Q40Init(void)
506 const int freq[] = {10000, 20000};
508 /* search a frequency that fits into the allowed error range */
511 for (i = 0; i < 2; i++)
512 if ((100 * abs(dmasound.soft.speed - freq[i]) / freq[i]) <= catchRadius)
515 dmasound.hard = dmasound.soft;
516 /*sound.hard.stereo=1;*/ /* no longer true */
517 dmasound.hard.size=8;
520 dmasound.soft.speed = freq[idx];
521 dmasound.trans_write = &transQ40Normal;
523 dmasound.trans_write = &transQ40Expanding;
527 if (dmasound.hard.speed > 20200) {
528 /* squeeze the sound, we do that */
529 dmasound.hard.speed = 20000;
530 dmasound.trans_write = &transQ40Compressing;
531 } else if (dmasound.hard.speed > 10000) {
532 dmasound.hard.speed = 20000;
534 dmasound.hard.speed = 10000;
536 expand_bal = -dmasound.soft.speed;
540 static int Q40SetFormat(int format)
542 /* Q40 sound supports only 8bit modes */
546 return(dmasound.soft.format);
556 dmasound.soft.format = format;
557 dmasound.soft.size = 8;
558 if (dmasound.minDev == SND_DEV_DSP) {
559 dmasound.dsp.format = format;
560 dmasound.dsp.size = 8;
567 static int Q40SetVolume(int volume)
573 /*** Machine definitions *****************************************************/
575 static SETTINGS def_hard = {
582 static SETTINGS def_soft = {
589 static MACHINE machQ40 = {
592 .owner = THIS_MODULE,
593 .dma_alloc = Q40Alloc,
595 .irqinit = Q40IrqInit,
597 .irqcleanup = Q40IrqCleanUp,
600 .silence = Q40Silence,
601 .setFormat = Q40SetFormat,
602 .setVolume = Q40SetVolume,
604 .min_dsp_speed = 10000,
605 .version = ((DMASOUND_Q40_REVISION<<8) | DMASOUND_Q40_EDITION),
606 .hardware_afmts = AFMT_U8, /* h'ware-supported formats *only* here */
607 .capabilities = DSP_CAP_BATCH /* As per SNDCTL_DSP_GETCAPS */
611 /*** Config & Setup **********************************************************/
614 int __init dmasound_q40_init(void)
617 dmasound.mach = machQ40;
618 dmasound.mach.default_hard = def_hard ;
619 dmasound.mach.default_soft = def_soft ;
620 return dmasound_init();
625 static void __exit dmasound_q40_cleanup(void)
630 module_init(dmasound_q40_init);
631 module_exit(dmasound_q40_cleanup);
633 MODULE_DESCRIPTION("Q40/Q60 sound driver");
634 MODULE_LICENSE("GPL");