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");