1 // SPDX-License-Identifier: GPL-2.0-only
3 * linux/sound/oss/dmasound/dmasound_q40.c
7 * See linux/sound/oss/dmasound/dmasound_core.c for copyright and credits
10 * 28/01/2001 [0.1] Iain Sandoe
12 * - put in and populated the hardware_afmts field.
13 * [0.2] - put in SNDCTL_DSP_GETCAPS value.
14 * [0.3] - put in default hard/soft settings.
18 #include <linux/module.h>
19 #include <linux/init.h>
20 #include <linux/slab.h>
21 #include <linux/soundcard.h>
22 #include <linux/interrupt.h>
24 #include <linux/uaccess.h>
25 #include <asm/q40ints.h>
26 #include <asm/q40_master.h>
30 #define DMASOUND_Q40_REVISION 0
31 #define DMASOUND_Q40_EDITION 3
33 static int expand_bal; /* Balance factor for expanding (not volume!) */
34 static int expand_data; /* Data for expanding */
37 /*** Low level stuff *********************************************************/
40 static void *Q40Alloc(unsigned int size, gfp_t flags);
41 static void Q40Free(void *, unsigned int);
42 static int Q40IrqInit(void);
44 static void Q40IrqCleanUp(void);
46 static void Q40Silence(void);
47 static void Q40Init(void);
48 static int Q40SetFormat(int format);
49 static int Q40SetVolume(int volume);
50 static void Q40PlayNextFrame(int index);
51 static void Q40Play(void);
52 static irqreturn_t Q40StereoInterrupt(int irq, void *dummy);
53 static irqreturn_t Q40MonoInterrupt(int irq, void *dummy);
54 static void Q40Interrupt(void);
57 /*** Mid level stuff *********************************************************/
61 /* userCount, frameUsed, frameLeft == byte counts */
62 static ssize_t q40_ct_law(const u_char __user *userPtr, size_t userCount,
63 u_char frame[], ssize_t *frameUsed,
66 char *table = dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8: dmasound_alaw2dma8;
68 u_char *p = (u_char *) &frame[*frameUsed];
70 used = count = min_t(size_t, userCount, frameLeft);
71 if (copy_from_user(p,userPtr,count))
83 static ssize_t q40_ct_s8(const u_char __user *userPtr, size_t userCount,
84 u_char frame[], ssize_t *frameUsed,
88 u_char *p = (u_char *) &frame[*frameUsed];
90 used = count = min_t(size_t, userCount, frameLeft);
91 if (copy_from_user(p,userPtr,count))
102 static ssize_t q40_ct_u8(const u_char __user *userPtr, size_t userCount,
103 u_char frame[], ssize_t *frameUsed,
107 u_char *p = (u_char *) &frame[*frameUsed];
109 used = count = min_t(size_t, userCount, frameLeft);
110 if (copy_from_user(p,userPtr,count))
117 /* a bit too complicated to optimise right now ..*/
118 static ssize_t q40_ctx_law(const u_char __user *userPtr, size_t userCount,
119 u_char frame[], ssize_t *frameUsed,
122 unsigned char *table = (unsigned char *)
123 (dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8: dmasound_alaw2dma8);
124 unsigned int data = expand_data;
125 u_char *p = (u_char *) &frame[*frameUsed];
126 int bal = expand_bal;
127 int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
137 if (get_user(c, userPtr++))
150 *frameUsed += (ftotal - frameLeft);
156 static ssize_t q40_ctx_s8(const u_char __user *userPtr, size_t userCount,
157 u_char frame[], ssize_t *frameUsed,
160 u_char *p = (u_char *) &frame[*frameUsed];
161 unsigned int data = expand_data;
162 int bal = expand_bal;
163 int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
174 if (get_user(c, userPtr++))
187 *frameUsed += (ftotal - frameLeft);
193 static ssize_t q40_ctx_u8(const u_char __user *userPtr, size_t userCount,
194 u_char frame[], ssize_t *frameUsed,
197 u_char *p = (u_char *) &frame[*frameUsed];
198 unsigned int data = expand_data;
199 int bal = expand_bal;
200 int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
210 if (get_user(c, userPtr++))
222 *frameUsed += (ftotal - frameLeft) ;
227 /* compressing versions */
228 static ssize_t q40_ctc_law(const u_char __user *userPtr, size_t userCount,
229 u_char frame[], ssize_t *frameUsed,
232 unsigned char *table = (unsigned char *)
233 (dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8: dmasound_alaw2dma8);
234 unsigned int data = expand_data;
235 u_char *p = (u_char *) &frame[*frameUsed];
236 int bal = expand_bal;
237 int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
247 if (!(bal<(-hSpeed))) {
248 if (get_user(c, userPtr))
250 data = 0x80 + table[c];
263 *frameUsed += (ftotal - frameLeft);
269 static ssize_t q40_ctc_s8(const u_char __user *userPtr, size_t userCount,
270 u_char frame[], ssize_t *frameUsed,
273 u_char *p = (u_char *) &frame[*frameUsed];
274 unsigned int data = expand_data;
275 int bal = expand_bal;
276 int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
286 if (!(bal<(-hSpeed))) {
287 if (get_user(c, userPtr))
302 *frameUsed += (ftotal - frameLeft);
308 static ssize_t q40_ctc_u8(const u_char __user *userPtr, size_t userCount,
309 u_char frame[], ssize_t *frameUsed,
312 u_char *p = (u_char *) &frame[*frameUsed];
313 unsigned int data = expand_data;
314 int bal = expand_bal;
315 int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
325 if (!(bal<(-hSpeed))) {
326 if (get_user(c, userPtr))
341 *frameUsed += (ftotal - frameLeft) ;
347 static TRANS transQ40Normal = {
348 q40_ct_law, q40_ct_law, q40_ct_s8, q40_ct_u8, NULL, NULL, NULL, NULL
351 static TRANS transQ40Expanding = {
352 q40_ctx_law, q40_ctx_law, q40_ctx_s8, q40_ctx_u8, NULL, NULL, NULL, NULL
355 static TRANS transQ40Compressing = {
356 q40_ctc_law, q40_ctc_law, q40_ctc_s8, q40_ctc_u8, NULL, NULL, NULL, NULL
360 /*** Low level stuff *********************************************************/
362 static void *Q40Alloc(unsigned int size, gfp_t flags)
364 return kmalloc(size, flags); /* change to vmalloc */
367 static void Q40Free(void *ptr, unsigned int size)
372 static int __init Q40IrqInit(void)
374 /* Register interrupt handler. */
375 if (request_irq(Q40_IRQ_SAMPLE, Q40StereoInterrupt, 0,
376 "DMA sound", Q40Interrupt))
384 static void Q40IrqCleanUp(void)
386 master_outb(0,SAMPLE_ENABLE_REG);
387 free_irq(Q40_IRQ_SAMPLE, Q40Interrupt);
392 static void Q40Silence(void)
394 master_outb(0,SAMPLE_ENABLE_REG);
395 *DAC_LEFT=*DAC_RIGHT=127;
399 static unsigned int q40_sc;
401 static void Q40PlayNextFrame(int index)
408 /* used by Q40Play() if all doubts whether there really is something
409 * to be played are already wiped out.
411 start = write_sq.buffers[write_sq.front];
412 size = (write_sq.count == index ? write_sq.rear_size : write_sq.block_size);
417 write_sq.front = (write_sq.front+1) % write_sq.max_count;
420 speed=(dmasound.hard.speed==10000 ? 0 : 1);
422 master_outb( 0,SAMPLE_ENABLE_REG);
423 free_irq(Q40_IRQ_SAMPLE, Q40Interrupt);
424 if (dmasound.soft.stereo)
425 error = request_irq(Q40_IRQ_SAMPLE, Q40StereoInterrupt, 0,
426 "Q40 sound", Q40Interrupt);
428 error = request_irq(Q40_IRQ_SAMPLE, Q40MonoInterrupt, 0,
429 "Q40 sound", Q40Interrupt);
430 if (error && printk_ratelimit())
431 pr_err("Couldn't register sound interrupt\n");
433 master_outb( speed, SAMPLE_RATE_REG);
434 master_outb( 1,SAMPLE_CLEAR_REG);
435 master_outb( 1,SAMPLE_ENABLE_REG);
438 static void Q40Play(void)
442 if (write_sq.active || write_sq.count<=0 ) {
443 /* There's already a frame loaded */
447 /* nothing in the queue */
448 if (write_sq.count <= 1 && write_sq.rear_size < write_sq.block_size && !write_sq.syncing) {
449 /* hmmm, the only existing frame is not
450 * yet filled and we're not syncing?
454 spin_lock_irqsave(&dmasound.lock, flags);
456 spin_unlock_irqrestore(&dmasound.lock, flags);
459 static irqreturn_t Q40StereoInterrupt(int irq, void *dummy)
461 spin_lock(&dmasound.lock);
464 *DAC_RIGHT=*q40_pp++;
466 master_outb(1,SAMPLE_CLEAR_REG);
467 }else Q40Interrupt();
468 spin_unlock(&dmasound.lock);
471 static irqreturn_t Q40MonoInterrupt(int irq, void *dummy)
473 spin_lock(&dmasound.lock);
476 *DAC_RIGHT=*q40_pp++;
478 master_outb(1,SAMPLE_CLEAR_REG);
479 }else Q40Interrupt();
480 spin_unlock(&dmasound.lock);
483 static void Q40Interrupt(void)
485 if (!write_sq.active) {
486 /* playing was interrupted and sq_reset() has already cleared
487 * the sq variables, so better don't do anything here.
489 WAKE_UP(write_sq.sync_queue);
490 master_outb(0,SAMPLE_ENABLE_REG); /* better safe */
492 } else write_sq.active=0;
497 { /* there was nothing to play, disable irq */
498 master_outb(0,SAMPLE_ENABLE_REG);
499 *DAC_LEFT=*DAC_RIGHT=127;
501 WAKE_UP(write_sq.action_queue);
504 master_outb(1,SAMPLE_CLEAR_REG);
508 static void Q40Init(void)
511 const int freq[] = {10000, 20000};
513 /* search a frequency that fits into the allowed error range */
516 for (i = 0; i < 2; i++)
517 if ((100 * abs(dmasound.soft.speed - freq[i]) / freq[i]) <= catchRadius)
520 dmasound.hard = dmasound.soft;
521 /*sound.hard.stereo=1;*/ /* no longer true */
522 dmasound.hard.size=8;
525 dmasound.soft.speed = freq[idx];
526 dmasound.trans_write = &transQ40Normal;
528 dmasound.trans_write = &transQ40Expanding;
532 if (dmasound.hard.speed > 20200) {
533 /* squeeze the sound, we do that */
534 dmasound.hard.speed = 20000;
535 dmasound.trans_write = &transQ40Compressing;
536 } else if (dmasound.hard.speed > 10000) {
537 dmasound.hard.speed = 20000;
539 dmasound.hard.speed = 10000;
541 expand_bal = -dmasound.soft.speed;
545 static int Q40SetFormat(int format)
547 /* Q40 sound supports only 8bit modes */
551 return(dmasound.soft.format);
561 dmasound.soft.format = format;
562 dmasound.soft.size = 8;
563 if (dmasound.minDev == SND_DEV_DSP) {
564 dmasound.dsp.format = format;
565 dmasound.dsp.size = 8;
572 static int Q40SetVolume(int volume)
578 /*** Machine definitions *****************************************************/
580 static SETTINGS def_hard = {
587 static SETTINGS def_soft = {
594 static MACHINE machQ40 = {
597 .owner = THIS_MODULE,
598 .dma_alloc = Q40Alloc,
600 .irqinit = Q40IrqInit,
602 .irqcleanup = Q40IrqCleanUp,
605 .silence = Q40Silence,
606 .setFormat = Q40SetFormat,
607 .setVolume = Q40SetVolume,
609 .min_dsp_speed = 10000,
610 .version = ((DMASOUND_Q40_REVISION<<8) | DMASOUND_Q40_EDITION),
611 .hardware_afmts = AFMT_U8, /* h'ware-supported formats *only* here */
612 .capabilities = DSP_CAP_BATCH /* As per SNDCTL_DSP_GETCAPS */
616 /*** Config & Setup **********************************************************/
619 static int __init dmasound_q40_init(void)
622 dmasound.mach = machQ40;
623 dmasound.mach.default_hard = def_hard ;
624 dmasound.mach.default_soft = def_soft ;
625 return dmasound_init();
630 static void __exit dmasound_q40_cleanup(void)
635 module_init(dmasound_q40_init);
636 module_exit(dmasound_q40_cleanup);
638 MODULE_DESCRIPTION("Q40/Q60 sound driver");
639 MODULE_LICENSE("GPL");