Loading...
1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * linux/sound/oss/dmasound/dmasound_q40.c
4 *
5 * Q40 DMA Sound Driver
6 *
7 * See linux/sound/oss/dmasound/dmasound_core.c for copyright and credits
8 * prior to 28/01/2001
9 *
10 * 28/01/2001 [0.1] Iain Sandoe
11 * - added versioning
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.
15 */
16
17
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>
23
24#include <linux/uaccess.h>
25#include <asm/q40ints.h>
26#include <asm/q40_master.h>
27
28#include "dmasound.h"
29
30#define DMASOUND_Q40_REVISION 0
31#define DMASOUND_Q40_EDITION 3
32
33static int expand_bal; /* Balance factor for expanding (not volume!) */
34static int expand_data; /* Data for expanding */
35
36
37/*** Low level stuff *********************************************************/
38
39
40static void *Q40Alloc(unsigned int size, gfp_t flags);
41static void Q40Free(void *, unsigned int);
42static int Q40IrqInit(void);
43#ifdef MODULE
44static void Q40IrqCleanUp(void);
45#endif
46static void Q40Silence(void);
47static void Q40Init(void);
48static int Q40SetFormat(int format);
49static int Q40SetVolume(int volume);
50static void Q40PlayNextFrame(int index);
51static void Q40Play(void);
52static irqreturn_t Q40StereoInterrupt(int irq, void *dummy);
53static irqreturn_t Q40MonoInterrupt(int irq, void *dummy);
54static void Q40Interrupt(void);
55
56
57/*** Mid level stuff *********************************************************/
58
59
60
61/* userCount, frameUsed, frameLeft == byte counts */
62static ssize_t q40_ct_law(const u_char __user *userPtr, size_t userCount,
63 u_char frame[], ssize_t *frameUsed,
64 ssize_t frameLeft)
65{
66 char *table = dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8: dmasound_alaw2dma8;
67 ssize_t count, used;
68 u_char *p = (u_char *) &frame[*frameUsed];
69
70 used = count = min_t(size_t, userCount, frameLeft);
71 if (copy_from_user(p,userPtr,count))
72 return -EFAULT;
73 while (count > 0) {
74 *p = table[*p]+128;
75 p++;
76 count--;
77 }
78 *frameUsed += used ;
79 return used;
80}
81
82
83static ssize_t q40_ct_s8(const u_char __user *userPtr, size_t userCount,
84 u_char frame[], ssize_t *frameUsed,
85 ssize_t frameLeft)
86{
87 ssize_t count, used;
88 u_char *p = (u_char *) &frame[*frameUsed];
89
90 used = count = min_t(size_t, userCount, frameLeft);
91 if (copy_from_user(p,userPtr,count))
92 return -EFAULT;
93 while (count > 0) {
94 *p = *p + 128;
95 p++;
96 count--;
97 }
98 *frameUsed += used;
99 return used;
100}
101
102static ssize_t q40_ct_u8(const u_char __user *userPtr, size_t userCount,
103 u_char frame[], ssize_t *frameUsed,
104 ssize_t frameLeft)
105{
106 ssize_t count, used;
107 u_char *p = (u_char *) &frame[*frameUsed];
108
109 used = count = min_t(size_t, userCount, frameLeft);
110 if (copy_from_user(p,userPtr,count))
111 return -EFAULT;
112 *frameUsed += used;
113 return used;
114}
115
116
117/* a bit too complicated to optimise right now ..*/
118static ssize_t q40_ctx_law(const u_char __user *userPtr, size_t userCount,
119 u_char frame[], ssize_t *frameUsed,
120 ssize_t frameLeft)
121{
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;
128 int utotal, ftotal;
129
130 ftotal = frameLeft;
131 utotal = userCount;
132 while (frameLeft) {
133 u_char c;
134 if (bal < 0) {
135 if (userCount == 0)
136 break;
137 if (get_user(c, userPtr++))
138 return -EFAULT;
139 data = table[c];
140 data += 0x80;
141 userCount--;
142 bal += hSpeed;
143 }
144 *p++ = data;
145 frameLeft--;
146 bal -= sSpeed;
147 }
148 expand_bal = bal;
149 expand_data = data;
150 *frameUsed += (ftotal - frameLeft);
151 utotal -= userCount;
152 return utotal;
153}
154
155
156static ssize_t q40_ctx_s8(const u_char __user *userPtr, size_t userCount,
157 u_char frame[], ssize_t *frameUsed,
158 ssize_t frameLeft)
159{
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;
164 int utotal, ftotal;
165
166
167 ftotal = frameLeft;
168 utotal = userCount;
169 while (frameLeft) {
170 u_char c;
171 if (bal < 0) {
172 if (userCount == 0)
173 break;
174 if (get_user(c, userPtr++))
175 return -EFAULT;
176 data = c ;
177 data += 0x80;
178 userCount--;
179 bal += hSpeed;
180 }
181 *p++ = data;
182 frameLeft--;
183 bal -= sSpeed;
184 }
185 expand_bal = bal;
186 expand_data = data;
187 *frameUsed += (ftotal - frameLeft);
188 utotal -= userCount;
189 return utotal;
190}
191
192
193static ssize_t q40_ctx_u8(const u_char __user *userPtr, size_t userCount,
194 u_char frame[], ssize_t *frameUsed,
195 ssize_t frameLeft)
196{
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;
201 int utotal, ftotal;
202
203 ftotal = frameLeft;
204 utotal = userCount;
205 while (frameLeft) {
206 u_char c;
207 if (bal < 0) {
208 if (userCount == 0)
209 break;
210 if (get_user(c, userPtr++))
211 return -EFAULT;
212 data = c ;
213 userCount--;
214 bal += hSpeed;
215 }
216 *p++ = data;
217 frameLeft--;
218 bal -= sSpeed;
219 }
220 expand_bal = bal;
221 expand_data = data;
222 *frameUsed += (ftotal - frameLeft) ;
223 utotal -= userCount;
224 return utotal;
225}
226
227/* compressing versions */
228static ssize_t q40_ctc_law(const u_char __user *userPtr, size_t userCount,
229 u_char frame[], ssize_t *frameUsed,
230 ssize_t frameLeft)
231{
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;
238 int utotal, ftotal;
239
240 ftotal = frameLeft;
241 utotal = userCount;
242 while (frameLeft) {
243 u_char c;
244 while(bal<0) {
245 if (userCount == 0)
246 goto lout;
247 if (!(bal<(-hSpeed))) {
248 if (get_user(c, userPtr))
249 return -EFAULT;
250 data = 0x80 + table[c];
251 }
252 userPtr++;
253 userCount--;
254 bal += hSpeed;
255 }
256 *p++ = data;
257 frameLeft--;
258 bal -= sSpeed;
259 }
260 lout:
261 expand_bal = bal;
262 expand_data = data;
263 *frameUsed += (ftotal - frameLeft);
264 utotal -= userCount;
265 return utotal;
266}
267
268
269static ssize_t q40_ctc_s8(const u_char __user *userPtr, size_t userCount,
270 u_char frame[], ssize_t *frameUsed,
271 ssize_t frameLeft)
272{
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;
277 int utotal, ftotal;
278
279 ftotal = frameLeft;
280 utotal = userCount;
281 while (frameLeft) {
282 u_char c;
283 while (bal < 0) {
284 if (userCount == 0)
285 goto lout;
286 if (!(bal<(-hSpeed))) {
287 if (get_user(c, userPtr))
288 return -EFAULT;
289 data = c + 0x80;
290 }
291 userPtr++;
292 userCount--;
293 bal += hSpeed;
294 }
295 *p++ = data;
296 frameLeft--;
297 bal -= sSpeed;
298 }
299 lout:
300 expand_bal = bal;
301 expand_data = data;
302 *frameUsed += (ftotal - frameLeft);
303 utotal -= userCount;
304 return utotal;
305}
306
307
308static ssize_t q40_ctc_u8(const u_char __user *userPtr, size_t userCount,
309 u_char frame[], ssize_t *frameUsed,
310 ssize_t frameLeft)
311{
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;
316 int utotal, ftotal;
317
318 ftotal = frameLeft;
319 utotal = userCount;
320 while (frameLeft) {
321 u_char c;
322 while (bal < 0) {
323 if (userCount == 0)
324 goto lout;
325 if (!(bal<(-hSpeed))) {
326 if (get_user(c, userPtr))
327 return -EFAULT;
328 data = c ;
329 }
330 userPtr++;
331 userCount--;
332 bal += hSpeed;
333 }
334 *p++ = data;
335 frameLeft--;
336 bal -= sSpeed;
337 }
338 lout:
339 expand_bal = bal;
340 expand_data = data;
341 *frameUsed += (ftotal - frameLeft) ;
342 utotal -= userCount;
343 return utotal;
344}
345
346
347static TRANS transQ40Normal = {
348 q40_ct_law, q40_ct_law, q40_ct_s8, q40_ct_u8, NULL, NULL, NULL, NULL
349};
350
351static TRANS transQ40Expanding = {
352 q40_ctx_law, q40_ctx_law, q40_ctx_s8, q40_ctx_u8, NULL, NULL, NULL, NULL
353};
354
355static TRANS transQ40Compressing = {
356 q40_ctc_law, q40_ctc_law, q40_ctc_s8, q40_ctc_u8, NULL, NULL, NULL, NULL
357};
358
359
360/*** Low level stuff *********************************************************/
361
362static void *Q40Alloc(unsigned int size, gfp_t flags)
363{
364 return kmalloc(size, flags); /* change to vmalloc */
365}
366
367static void Q40Free(void *ptr, unsigned int size)
368{
369 kfree(ptr);
370}
371
372static int __init Q40IrqInit(void)
373{
374 /* Register interrupt handler. */
375 if (request_irq(Q40_IRQ_SAMPLE, Q40StereoInterrupt, 0,
376 "DMA sound", Q40Interrupt))
377 return 0;
378
379 return(1);
380}
381
382
383#ifdef MODULE
384static void Q40IrqCleanUp(void)
385{
386 master_outb(0,SAMPLE_ENABLE_REG);
387 free_irq(Q40_IRQ_SAMPLE, Q40Interrupt);
388}
389#endif /* MODULE */
390
391
392static void Q40Silence(void)
393{
394 master_outb(0,SAMPLE_ENABLE_REG);
395 *DAC_LEFT=*DAC_RIGHT=127;
396}
397
398static char *q40_pp;
399static unsigned int q40_sc;
400
401static void Q40PlayNextFrame(int index)
402{
403 u_char *start;
404 u_long size;
405 u_char speed;
406 int error;
407
408 /* used by Q40Play() if all doubts whether there really is something
409 * to be played are already wiped out.
410 */
411 start = write_sq.buffers[write_sq.front];
412 size = (write_sq.count == index ? write_sq.rear_size : write_sq.block_size);
413
414 q40_pp=start;
415 q40_sc=size;
416
417 write_sq.front = (write_sq.front+1) % write_sq.max_count;
418 write_sq.active++;
419
420 speed=(dmasound.hard.speed==10000 ? 0 : 1);
421
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);
427 else
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");
432
433 master_outb( speed, SAMPLE_RATE_REG);
434 master_outb( 1,SAMPLE_CLEAR_REG);
435 master_outb( 1,SAMPLE_ENABLE_REG);
436}
437
438static void Q40Play(void)
439{
440 unsigned long flags;
441
442 if (write_sq.active || write_sq.count<=0 ) {
443 /* There's already a frame loaded */
444 return;
445 }
446
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?
451 */
452 return;
453 }
454 spin_lock_irqsave(&dmasound.lock, flags);
455 Q40PlayNextFrame(1);
456 spin_unlock_irqrestore(&dmasound.lock, flags);
457}
458
459static irqreturn_t Q40StereoInterrupt(int irq, void *dummy)
460{
461 spin_lock(&dmasound.lock);
462 if (q40_sc>1){
463 *DAC_LEFT=*q40_pp++;
464 *DAC_RIGHT=*q40_pp++;
465 q40_sc -=2;
466 master_outb(1,SAMPLE_CLEAR_REG);
467 }else Q40Interrupt();
468 spin_unlock(&dmasound.lock);
469 return IRQ_HANDLED;
470}
471static irqreturn_t Q40MonoInterrupt(int irq, void *dummy)
472{
473 spin_lock(&dmasound.lock);
474 if (q40_sc>0){
475 *DAC_LEFT=*q40_pp;
476 *DAC_RIGHT=*q40_pp++;
477 q40_sc --;
478 master_outb(1,SAMPLE_CLEAR_REG);
479 }else Q40Interrupt();
480 spin_unlock(&dmasound.lock);
481 return IRQ_HANDLED;
482}
483static void Q40Interrupt(void)
484{
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.
488 */
489 WAKE_UP(write_sq.sync_queue);
490 master_outb(0,SAMPLE_ENABLE_REG); /* better safe */
491 goto exit;
492 } else write_sq.active=0;
493 write_sq.count--;
494 Q40Play();
495
496 if (q40_sc<2)
497 { /* there was nothing to play, disable irq */
498 master_outb(0,SAMPLE_ENABLE_REG);
499 *DAC_LEFT=*DAC_RIGHT=127;
500 }
501 WAKE_UP(write_sq.action_queue);
502
503 exit:
504 master_outb(1,SAMPLE_CLEAR_REG);
505}
506
507
508static void Q40Init(void)
509{
510 int i, idx;
511 const int freq[] = {10000, 20000};
512
513 /* search a frequency that fits into the allowed error range */
514
515 idx = -1;
516 for (i = 0; i < 2; i++)
517 if ((100 * abs(dmasound.soft.speed - freq[i]) / freq[i]) <= catchRadius)
518 idx = i;
519
520 dmasound.hard = dmasound.soft;
521 /*sound.hard.stereo=1;*/ /* no longer true */
522 dmasound.hard.size=8;
523
524 if (idx > -1) {
525 dmasound.soft.speed = freq[idx];
526 dmasound.trans_write = &transQ40Normal;
527 } else
528 dmasound.trans_write = &transQ40Expanding;
529
530 Q40Silence();
531
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;
538 } else {
539 dmasound.hard.speed = 10000;
540 }
541 expand_bal = -dmasound.soft.speed;
542}
543
544
545static int Q40SetFormat(int format)
546{
547 /* Q40 sound supports only 8bit modes */
548
549 switch (format) {
550 case AFMT_QUERY:
551 return(dmasound.soft.format);
552 case AFMT_MU_LAW:
553 case AFMT_A_LAW:
554 case AFMT_S8:
555 case AFMT_U8:
556 break;
557 default:
558 format = AFMT_S8;
559 }
560
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;
566 }
567 Q40Init();
568
569 return(format);
570}
571
572static int Q40SetVolume(int volume)
573{
574 return 0;
575}
576
577
578/*** Machine definitions *****************************************************/
579
580static SETTINGS def_hard = {
581 .format = AFMT_U8,
582 .stereo = 0,
583 .size = 8,
584 .speed = 10000
585} ;
586
587static SETTINGS def_soft = {
588 .format = AFMT_U8,
589 .stereo = 0,
590 .size = 8,
591 .speed = 8000
592} ;
593
594static MACHINE machQ40 = {
595 .name = "Q40",
596 .name2 = "Q40",
597 .owner = THIS_MODULE,
598 .dma_alloc = Q40Alloc,
599 .dma_free = Q40Free,
600 .irqinit = Q40IrqInit,
601#ifdef MODULE
602 .irqcleanup = Q40IrqCleanUp,
603#endif /* MODULE */
604 .init = Q40Init,
605 .silence = Q40Silence,
606 .setFormat = Q40SetFormat,
607 .setVolume = Q40SetVolume,
608 .play = Q40Play,
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 */
613};
614
615
616/*** Config & Setup **********************************************************/
617
618
619static int __init dmasound_q40_init(void)
620{
621 if (MACH_IS_Q40) {
622 dmasound.mach = machQ40;
623 dmasound.mach.default_hard = def_hard ;
624 dmasound.mach.default_soft = def_soft ;
625 return dmasound_init();
626 } else
627 return -ENODEV;
628}
629
630static void __exit dmasound_q40_cleanup(void)
631{
632 dmasound_deinit();
633}
634
635module_init(dmasound_q40_init);
636module_exit(dmasound_q40_cleanup);
637
638MODULE_DESCRIPTION("Q40/Q60 sound driver");
639MODULE_LICENSE("GPL");
1/*
2 * linux/sound/oss/dmasound/dmasound_q40.c
3 *
4 * Q40 DMA Sound Driver
5 *
6 * See linux/sound/oss/dmasound/dmasound_core.c for copyright and credits
7 * prior to 28/01/2001
8 *
9 * 28/01/2001 [0.1] Iain Sandoe
10 * - added versioning
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.
14 */
15
16
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>
22
23#include <linux/uaccess.h>
24#include <asm/q40ints.h>
25#include <asm/q40_master.h>
26
27#include "dmasound.h"
28
29#define DMASOUND_Q40_REVISION 0
30#define DMASOUND_Q40_EDITION 3
31
32static int expand_bal; /* Balance factor for expanding (not volume!) */
33static int expand_data; /* Data for expanding */
34
35
36/*** Low level stuff *********************************************************/
37
38
39static void *Q40Alloc(unsigned int size, gfp_t flags);
40static void Q40Free(void *, unsigned int);
41static int Q40IrqInit(void);
42#ifdef MODULE
43static void Q40IrqCleanUp(void);
44#endif
45static void Q40Silence(void);
46static void Q40Init(void);
47static int Q40SetFormat(int format);
48static int Q40SetVolume(int volume);
49static void Q40PlayNextFrame(int index);
50static void Q40Play(void);
51static irqreturn_t Q40StereoInterrupt(int irq, void *dummy);
52static irqreturn_t Q40MonoInterrupt(int irq, void *dummy);
53static void Q40Interrupt(void);
54
55
56/*** Mid level stuff *********************************************************/
57
58
59
60/* userCount, frameUsed, frameLeft == byte counts */
61static ssize_t q40_ct_law(const u_char __user *userPtr, size_t userCount,
62 u_char frame[], ssize_t *frameUsed,
63 ssize_t frameLeft)
64{
65 char *table = dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8: dmasound_alaw2dma8;
66 ssize_t count, used;
67 u_char *p = (u_char *) &frame[*frameUsed];
68
69 used = count = min_t(size_t, userCount, frameLeft);
70 if (copy_from_user(p,userPtr,count))
71 return -EFAULT;
72 while (count > 0) {
73 *p = table[*p]+128;
74 p++;
75 count--;
76 }
77 *frameUsed += used ;
78 return used;
79}
80
81
82static ssize_t q40_ct_s8(const u_char __user *userPtr, size_t userCount,
83 u_char frame[], ssize_t *frameUsed,
84 ssize_t frameLeft)
85{
86 ssize_t count, used;
87 u_char *p = (u_char *) &frame[*frameUsed];
88
89 used = count = min_t(size_t, userCount, frameLeft);
90 if (copy_from_user(p,userPtr,count))
91 return -EFAULT;
92 while (count > 0) {
93 *p = *p + 128;
94 p++;
95 count--;
96 }
97 *frameUsed += used;
98 return used;
99}
100
101static ssize_t q40_ct_u8(const u_char __user *userPtr, size_t userCount,
102 u_char frame[], ssize_t *frameUsed,
103 ssize_t frameLeft)
104{
105 ssize_t count, used;
106 u_char *p = (u_char *) &frame[*frameUsed];
107
108 used = count = min_t(size_t, userCount, frameLeft);
109 if (copy_from_user(p,userPtr,count))
110 return -EFAULT;
111 *frameUsed += used;
112 return used;
113}
114
115
116/* a bit too complicated to optimise right now ..*/
117static ssize_t q40_ctx_law(const u_char __user *userPtr, size_t userCount,
118 u_char frame[], ssize_t *frameUsed,
119 ssize_t frameLeft)
120{
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;
127 int utotal, ftotal;
128
129 ftotal = frameLeft;
130 utotal = userCount;
131 while (frameLeft) {
132 u_char c;
133 if (bal < 0) {
134 if (userCount == 0)
135 break;
136 if (get_user(c, userPtr++))
137 return -EFAULT;
138 data = table[c];
139 data += 0x80;
140 userCount--;
141 bal += hSpeed;
142 }
143 *p++ = data;
144 frameLeft--;
145 bal -= sSpeed;
146 }
147 expand_bal = bal;
148 expand_data = data;
149 *frameUsed += (ftotal - frameLeft);
150 utotal -= userCount;
151 return utotal;
152}
153
154
155static ssize_t q40_ctx_s8(const u_char __user *userPtr, size_t userCount,
156 u_char frame[], ssize_t *frameUsed,
157 ssize_t frameLeft)
158{
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;
163 int utotal, ftotal;
164
165
166 ftotal = frameLeft;
167 utotal = userCount;
168 while (frameLeft) {
169 u_char c;
170 if (bal < 0) {
171 if (userCount == 0)
172 break;
173 if (get_user(c, userPtr++))
174 return -EFAULT;
175 data = c ;
176 data += 0x80;
177 userCount--;
178 bal += hSpeed;
179 }
180 *p++ = data;
181 frameLeft--;
182 bal -= sSpeed;
183 }
184 expand_bal = bal;
185 expand_data = data;
186 *frameUsed += (ftotal - frameLeft);
187 utotal -= userCount;
188 return utotal;
189}
190
191
192static ssize_t q40_ctx_u8(const u_char __user *userPtr, size_t userCount,
193 u_char frame[], ssize_t *frameUsed,
194 ssize_t frameLeft)
195{
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;
200 int utotal, ftotal;
201
202 ftotal = frameLeft;
203 utotal = userCount;
204 while (frameLeft) {
205 u_char c;
206 if (bal < 0) {
207 if (userCount == 0)
208 break;
209 if (get_user(c, userPtr++))
210 return -EFAULT;
211 data = c ;
212 userCount--;
213 bal += hSpeed;
214 }
215 *p++ = data;
216 frameLeft--;
217 bal -= sSpeed;
218 }
219 expand_bal = bal;
220 expand_data = data;
221 *frameUsed += (ftotal - frameLeft) ;
222 utotal -= userCount;
223 return utotal;
224}
225
226/* compressing versions */
227static ssize_t q40_ctc_law(const u_char __user *userPtr, size_t userCount,
228 u_char frame[], ssize_t *frameUsed,
229 ssize_t frameLeft)
230{
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;
237 int utotal, ftotal;
238
239 ftotal = frameLeft;
240 utotal = userCount;
241 while (frameLeft) {
242 u_char c;
243 while(bal<0) {
244 if (userCount == 0)
245 goto lout;
246 if (!(bal<(-hSpeed))) {
247 if (get_user(c, userPtr))
248 return -EFAULT;
249 data = 0x80 + table[c];
250 }
251 userPtr++;
252 userCount--;
253 bal += hSpeed;
254 }
255 *p++ = data;
256 frameLeft--;
257 bal -= sSpeed;
258 }
259 lout:
260 expand_bal = bal;
261 expand_data = data;
262 *frameUsed += (ftotal - frameLeft);
263 utotal -= userCount;
264 return utotal;
265}
266
267
268static ssize_t q40_ctc_s8(const u_char __user *userPtr, size_t userCount,
269 u_char frame[], ssize_t *frameUsed,
270 ssize_t frameLeft)
271{
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;
276 int utotal, ftotal;
277
278 ftotal = frameLeft;
279 utotal = userCount;
280 while (frameLeft) {
281 u_char c;
282 while (bal < 0) {
283 if (userCount == 0)
284 goto lout;
285 if (!(bal<(-hSpeed))) {
286 if (get_user(c, userPtr))
287 return -EFAULT;
288 data = c + 0x80;
289 }
290 userPtr++;
291 userCount--;
292 bal += hSpeed;
293 }
294 *p++ = data;
295 frameLeft--;
296 bal -= sSpeed;
297 }
298 lout:
299 expand_bal = bal;
300 expand_data = data;
301 *frameUsed += (ftotal - frameLeft);
302 utotal -= userCount;
303 return utotal;
304}
305
306
307static ssize_t q40_ctc_u8(const u_char __user *userPtr, size_t userCount,
308 u_char frame[], ssize_t *frameUsed,
309 ssize_t frameLeft)
310{
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;
315 int utotal, ftotal;
316
317 ftotal = frameLeft;
318 utotal = userCount;
319 while (frameLeft) {
320 u_char c;
321 while (bal < 0) {
322 if (userCount == 0)
323 goto lout;
324 if (!(bal<(-hSpeed))) {
325 if (get_user(c, userPtr))
326 return -EFAULT;
327 data = c ;
328 }
329 userPtr++;
330 userCount--;
331 bal += hSpeed;
332 }
333 *p++ = data;
334 frameLeft--;
335 bal -= sSpeed;
336 }
337 lout:
338 expand_bal = bal;
339 expand_data = data;
340 *frameUsed += (ftotal - frameLeft) ;
341 utotal -= userCount;
342 return utotal;
343}
344
345
346static TRANS transQ40Normal = {
347 q40_ct_law, q40_ct_law, q40_ct_s8, q40_ct_u8, NULL, NULL, NULL, NULL
348};
349
350static TRANS transQ40Expanding = {
351 q40_ctx_law, q40_ctx_law, q40_ctx_s8, q40_ctx_u8, NULL, NULL, NULL, NULL
352};
353
354static TRANS transQ40Compressing = {
355 q40_ctc_law, q40_ctc_law, q40_ctc_s8, q40_ctc_u8, NULL, NULL, NULL, NULL
356};
357
358
359/*** Low level stuff *********************************************************/
360
361static void *Q40Alloc(unsigned int size, gfp_t flags)
362{
363 return kmalloc(size, flags); /* change to vmalloc */
364}
365
366static void Q40Free(void *ptr, unsigned int size)
367{
368 kfree(ptr);
369}
370
371static int __init Q40IrqInit(void)
372{
373 /* Register interrupt handler. */
374 if (request_irq(Q40_IRQ_SAMPLE, Q40StereoInterrupt, 0,
375 "DMA sound", Q40Interrupt))
376 return 0;
377
378 return(1);
379}
380
381
382#ifdef MODULE
383static void Q40IrqCleanUp(void)
384{
385 master_outb(0,SAMPLE_ENABLE_REG);
386 free_irq(Q40_IRQ_SAMPLE, Q40Interrupt);
387}
388#endif /* MODULE */
389
390
391static void Q40Silence(void)
392{
393 master_outb(0,SAMPLE_ENABLE_REG);
394 *DAC_LEFT=*DAC_RIGHT=127;
395}
396
397static char *q40_pp;
398static unsigned int q40_sc;
399
400static void Q40PlayNextFrame(int index)
401{
402 u_char *start;
403 u_long size;
404 u_char speed;
405 int error;
406
407 /* used by Q40Play() if all doubts whether there really is something
408 * to be played are already wiped out.
409 */
410 start = write_sq.buffers[write_sq.front];
411 size = (write_sq.count == index ? write_sq.rear_size : write_sq.block_size);
412
413 q40_pp=start;
414 q40_sc=size;
415
416 write_sq.front = (write_sq.front+1) % write_sq.max_count;
417 write_sq.active++;
418
419 speed=(dmasound.hard.speed==10000 ? 0 : 1);
420
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);
426 else
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");
431
432 master_outb( speed, SAMPLE_RATE_REG);
433 master_outb( 1,SAMPLE_CLEAR_REG);
434 master_outb( 1,SAMPLE_ENABLE_REG);
435}
436
437static void Q40Play(void)
438{
439 unsigned long flags;
440
441 if (write_sq.active || write_sq.count<=0 ) {
442 /* There's already a frame loaded */
443 return;
444 }
445
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?
450 */
451 return;
452 }
453 spin_lock_irqsave(&dmasound.lock, flags);
454 Q40PlayNextFrame(1);
455 spin_unlock_irqrestore(&dmasound.lock, flags);
456}
457
458static irqreturn_t Q40StereoInterrupt(int irq, void *dummy)
459{
460 spin_lock(&dmasound.lock);
461 if (q40_sc>1){
462 *DAC_LEFT=*q40_pp++;
463 *DAC_RIGHT=*q40_pp++;
464 q40_sc -=2;
465 master_outb(1,SAMPLE_CLEAR_REG);
466 }else Q40Interrupt();
467 spin_unlock(&dmasound.lock);
468 return IRQ_HANDLED;
469}
470static irqreturn_t Q40MonoInterrupt(int irq, void *dummy)
471{
472 spin_lock(&dmasound.lock);
473 if (q40_sc>0){
474 *DAC_LEFT=*q40_pp;
475 *DAC_RIGHT=*q40_pp++;
476 q40_sc --;
477 master_outb(1,SAMPLE_CLEAR_REG);
478 }else Q40Interrupt();
479 spin_unlock(&dmasound.lock);
480 return IRQ_HANDLED;
481}
482static void Q40Interrupt(void)
483{
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.
487 */
488 WAKE_UP(write_sq.sync_queue);
489 master_outb(0,SAMPLE_ENABLE_REG); /* better safe */
490 goto exit;
491 } else write_sq.active=0;
492 write_sq.count--;
493 Q40Play();
494
495 if (q40_sc<2)
496 { /* there was nothing to play, disable irq */
497 master_outb(0,SAMPLE_ENABLE_REG);
498 *DAC_LEFT=*DAC_RIGHT=127;
499 }
500 WAKE_UP(write_sq.action_queue);
501
502 exit:
503 master_outb(1,SAMPLE_CLEAR_REG);
504}
505
506
507static void Q40Init(void)
508{
509 int i, idx;
510 const int freq[] = {10000, 20000};
511
512 /* search a frequency that fits into the allowed error range */
513
514 idx = -1;
515 for (i = 0; i < 2; i++)
516 if ((100 * abs(dmasound.soft.speed - freq[i]) / freq[i]) <= catchRadius)
517 idx = i;
518
519 dmasound.hard = dmasound.soft;
520 /*sound.hard.stereo=1;*/ /* no longer true */
521 dmasound.hard.size=8;
522
523 if (idx > -1) {
524 dmasound.soft.speed = freq[idx];
525 dmasound.trans_write = &transQ40Normal;
526 } else
527 dmasound.trans_write = &transQ40Expanding;
528
529 Q40Silence();
530
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;
537 } else {
538 dmasound.hard.speed = 10000;
539 }
540 expand_bal = -dmasound.soft.speed;
541}
542
543
544static int Q40SetFormat(int format)
545{
546 /* Q40 sound supports only 8bit modes */
547
548 switch (format) {
549 case AFMT_QUERY:
550 return(dmasound.soft.format);
551 case AFMT_MU_LAW:
552 case AFMT_A_LAW:
553 case AFMT_S8:
554 case AFMT_U8:
555 break;
556 default:
557 format = AFMT_S8;
558 }
559
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;
565 }
566 Q40Init();
567
568 return(format);
569}
570
571static int Q40SetVolume(int volume)
572{
573 return 0;
574}
575
576
577/*** Machine definitions *****************************************************/
578
579static SETTINGS def_hard = {
580 .format = AFMT_U8,
581 .stereo = 0,
582 .size = 8,
583 .speed = 10000
584} ;
585
586static SETTINGS def_soft = {
587 .format = AFMT_U8,
588 .stereo = 0,
589 .size = 8,
590 .speed = 8000
591} ;
592
593static MACHINE machQ40 = {
594 .name = "Q40",
595 .name2 = "Q40",
596 .owner = THIS_MODULE,
597 .dma_alloc = Q40Alloc,
598 .dma_free = Q40Free,
599 .irqinit = Q40IrqInit,
600#ifdef MODULE
601 .irqcleanup = Q40IrqCleanUp,
602#endif /* MODULE */
603 .init = Q40Init,
604 .silence = Q40Silence,
605 .setFormat = Q40SetFormat,
606 .setVolume = Q40SetVolume,
607 .play = Q40Play,
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 */
612};
613
614
615/*** Config & Setup **********************************************************/
616
617
618static int __init dmasound_q40_init(void)
619{
620 if (MACH_IS_Q40) {
621 dmasound.mach = machQ40;
622 dmasound.mach.default_hard = def_hard ;
623 dmasound.mach.default_soft = def_soft ;
624 return dmasound_init();
625 } else
626 return -ENODEV;
627}
628
629static void __exit dmasound_q40_cleanup(void)
630{
631 dmasound_deinit();
632}
633
634module_init(dmasound_q40_init);
635module_exit(dmasound_q40_cleanup);
636
637MODULE_DESCRIPTION("Q40/Q60 sound driver");
638MODULE_LICENSE("GPL");