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 if (request_irq(Q40_IRQ_SAMPLE
, Q40StereoInterrupt
, 0,
375 "DMA sound", Q40Interrupt
))
383 static void Q40IrqCleanUp(void)
385 master_outb(0,SAMPLE_ENABLE_REG
);
386 free_irq(Q40_IRQ_SAMPLE
, Q40Interrupt
);
391 static void Q40Silence(void)
393 master_outb(0,SAMPLE_ENABLE_REG
);
394 *DAC_LEFT
=*DAC_RIGHT
=127;
398 static unsigned int q40_sc
;
400 static void Q40PlayNextFrame(int index
)
407 /* used by Q40Play() if all doubts whether there really is something
408 * to be played are already wiped out.
410 start
= write_sq
.buffers
[write_sq
.front
];
411 size
= (write_sq
.count
== index
? write_sq
.rear_size
: write_sq
.block_size
);
416 write_sq
.front
= (write_sq
.front
+1) % write_sq
.max_count
;
419 speed
=(dmasound
.hard
.speed
==10000 ? 0 : 1);
421 master_outb( 0,SAMPLE_ENABLE_REG
);
422 free_irq(Q40_IRQ_SAMPLE
, Q40Interrupt
);
423 if (dmasound
.soft
.stereo
)
424 error
= request_irq(Q40_IRQ_SAMPLE
, Q40StereoInterrupt
, 0,
425 "Q40 sound", Q40Interrupt
);
427 error
= request_irq(Q40_IRQ_SAMPLE
, Q40MonoInterrupt
, 0,
428 "Q40 sound", Q40Interrupt
);
429 if (error
&& printk_ratelimit())
430 pr_err("Couldn't register sound interrupt\n");
432 master_outb( speed
, SAMPLE_RATE_REG
);
433 master_outb( 1,SAMPLE_CLEAR_REG
);
434 master_outb( 1,SAMPLE_ENABLE_REG
);
437 static void Q40Play(void)
441 if (write_sq
.active
|| write_sq
.count
<=0 ) {
442 /* There's already a frame loaded */
446 /* nothing in the queue */
447 if (write_sq
.count
<= 1 && write_sq
.rear_size
< write_sq
.block_size
&& !write_sq
.syncing
) {
448 /* hmmm, the only existing frame is not
449 * yet filled and we're not syncing?
453 spin_lock_irqsave(&dmasound
.lock
, flags
);
455 spin_unlock_irqrestore(&dmasound
.lock
, flags
);
458 static irqreturn_t
Q40StereoInterrupt(int irq
, void *dummy
)
460 spin_lock(&dmasound
.lock
);
463 *DAC_RIGHT
=*q40_pp
++;
465 master_outb(1,SAMPLE_CLEAR_REG
);
466 }else Q40Interrupt();
467 spin_unlock(&dmasound
.lock
);
470 static irqreturn_t
Q40MonoInterrupt(int irq
, void *dummy
)
472 spin_lock(&dmasound
.lock
);
475 *DAC_RIGHT
=*q40_pp
++;
477 master_outb(1,SAMPLE_CLEAR_REG
);
478 }else Q40Interrupt();
479 spin_unlock(&dmasound
.lock
);
482 static void Q40Interrupt(void)
484 if (!write_sq
.active
) {
485 /* playing was interrupted and sq_reset() has already cleared
486 * the sq variables, so better don't do anything here.
488 WAKE_UP(write_sq
.sync_queue
);
489 master_outb(0,SAMPLE_ENABLE_REG
); /* better safe */
491 } else write_sq
.active
=0;
496 { /* there was nothing to play, disable irq */
497 master_outb(0,SAMPLE_ENABLE_REG
);
498 *DAC_LEFT
=*DAC_RIGHT
=127;
500 WAKE_UP(write_sq
.action_queue
);
503 master_outb(1,SAMPLE_CLEAR_REG
);
507 static void Q40Init(void)
510 const int freq
[] = {10000, 20000};
512 /* search a frequency that fits into the allowed error range */
515 for (i
= 0; i
< 2; i
++)
516 if ((100 * abs(dmasound
.soft
.speed
- freq
[i
]) / freq
[i
]) <= catchRadius
)
519 dmasound
.hard
= dmasound
.soft
;
520 /*sound.hard.stereo=1;*/ /* no longer true */
521 dmasound
.hard
.size
=8;
524 dmasound
.soft
.speed
= freq
[idx
];
525 dmasound
.trans_write
= &transQ40Normal
;
527 dmasound
.trans_write
= &transQ40Expanding
;
531 if (dmasound
.hard
.speed
> 20200) {
532 /* squeeze the sound, we do that */
533 dmasound
.hard
.speed
= 20000;
534 dmasound
.trans_write
= &transQ40Compressing
;
535 } else if (dmasound
.hard
.speed
> 10000) {
536 dmasound
.hard
.speed
= 20000;
538 dmasound
.hard
.speed
= 10000;
540 expand_bal
= -dmasound
.soft
.speed
;
544 static int Q40SetFormat(int format
)
546 /* Q40 sound supports only 8bit modes */
550 return(dmasound
.soft
.format
);
560 dmasound
.soft
.format
= format
;
561 dmasound
.soft
.size
= 8;
562 if (dmasound
.minDev
== SND_DEV_DSP
) {
563 dmasound
.dsp
.format
= format
;
564 dmasound
.dsp
.size
= 8;
571 static int Q40SetVolume(int volume
)
577 /*** Machine definitions *****************************************************/
579 static SETTINGS def_hard
= {
586 static SETTINGS def_soft
= {
593 static MACHINE machQ40
= {
596 .owner
= THIS_MODULE
,
597 .dma_alloc
= Q40Alloc
,
599 .irqinit
= Q40IrqInit
,
601 .irqcleanup
= Q40IrqCleanUp
,
604 .silence
= Q40Silence
,
605 .setFormat
= Q40SetFormat
,
606 .setVolume
= Q40SetVolume
,
608 .min_dsp_speed
= 10000,
609 .version
= ((DMASOUND_Q40_REVISION
<<8) | DMASOUND_Q40_EDITION
),
610 .hardware_afmts
= AFMT_U8
, /* h'ware-supported formats *only* here */
611 .capabilities
= DSP_CAP_BATCH
/* As per SNDCTL_DSP_GETCAPS */
615 /*** Config & Setup **********************************************************/
618 static int __init
dmasound_q40_init(void)
621 dmasound
.mach
= machQ40
;
622 dmasound
.mach
.default_hard
= def_hard
;
623 dmasound
.mach
.default_soft
= def_soft
;
624 return dmasound_init();
629 static void __exit
dmasound_q40_cleanup(void)
634 module_init(dmasound_q40_init
);
635 module_exit(dmasound_q40_cleanup
);
637 MODULE_DESCRIPTION("Q40/Q60 sound driver");
638 MODULE_LICENSE("GPL");