1 /*
2 * linux/drivers/sound/dmasound/dmasound_q40.c
3 *
4 * Q40 DMA Sound Driver
5 *
6 * See linux/drivers/sound/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
22 #include <asm/uaccess.h>
23 #include <asm/q40ints.h>
24 #include <asm/q40_master.h>
25
26 #include "dmasound.h"
27
28 #define DMASOUND_Q40_REVISION 0
29 #define DMASOUND_Q40_EDITION 3
30
31 static int expand_bal; /* Balance factor for expanding (not volume!) */
32 static int expand_data; /* Data for expanding */
33
34
35 /*** Low level stuff *********************************************************/
36
37
38 static void Q40Open(void);
39 static void Q40Release(void);
40 static void *Q40Alloc(unsigned int size, int flags);
41 static void Q40Free(void *, unsigned int);
42 static int Q40IrqInit(void);
43 #ifdef MODULE
44 static void Q40IrqCleanUp(void);
45 #endif
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 void Q40StereoInterrupt(int irq, void *dummy, struct pt_regs *fp);
53 static void Q40MonoInterrupt(int irq, void *dummy, struct pt_regs *fp);
54 static void Q40Interrupt(void);
55
56
57 /*** Mid level stuff *********************************************************/
58
59
60
61 /* userCount, frameUsed, frameLeft == byte counts */
q40_ct_law(const u_char * userPtr,size_t userCount,u_char frame[],ssize_t * frameUsed,ssize_t frameLeft)62 static ssize_t q40_ct_law(const u_char *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
q40_ct_s8(const u_char * userPtr,size_t userCount,u_char frame[],ssize_t * frameUsed,ssize_t frameLeft)83 static ssize_t q40_ct_s8(const u_char *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
q40_ct_u8(const u_char * userPtr,size_t userCount,u_char frame[],ssize_t * frameUsed,ssize_t frameLeft)102 static ssize_t q40_ct_u8(const u_char *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 ..*/
q40_ctx_law(const u_char * userPtr,size_t userCount,u_char frame[],ssize_t * frameUsed,ssize_t frameLeft)118 static ssize_t q40_ctx_law(const u_char *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
q40_ctx_s8(const u_char * userPtr,size_t userCount,u_char frame[],ssize_t * frameUsed,ssize_t frameLeft)156 static ssize_t q40_ctx_s8(const u_char *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
q40_ctx_u8(const u_char * userPtr,size_t userCount,u_char frame[],ssize_t * frameUsed,ssize_t frameLeft)193 static ssize_t q40_ctx_u8(const u_char *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 */
q40_ctc_law(const u_char * userPtr,size_t userCount,u_char frame[],ssize_t * frameUsed,ssize_t frameLeft)228 static ssize_t q40_ctc_law(const u_char *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
q40_ctc_s8(const u_char * userPtr,size_t userCount,u_char frame[],ssize_t * frameUsed,ssize_t frameLeft)269 static ssize_t q40_ctc_s8(const u_char *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
q40_ctc_u8(const u_char * userPtr,size_t userCount,u_char frame[],ssize_t * frameUsed,ssize_t frameLeft)308 static ssize_t q40_ctc_u8(const u_char *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
347 static TRANS transQ40Normal = {
348 q40_ct_law, q40_ct_law, q40_ct_s8, q40_ct_u8, NULL, NULL, NULL, NULL
349 };
350
351 static TRANS transQ40Expanding = {
352 q40_ctx_law, q40_ctx_law, q40_ctx_s8, q40_ctx_u8, NULL, NULL, NULL, NULL
353 };
354
355 static 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
362
Q40Open(void)363 static void Q40Open(void)
364 {
365 MOD_INC_USE_COUNT;
366 }
367
Q40Release(void)368 static void Q40Release(void)
369 {
370 MOD_DEC_USE_COUNT;
371 }
372
373
Q40Alloc(unsigned int size,int flags)374 static void *Q40Alloc(unsigned int size, int flags)
375 {
376 return kmalloc(size, flags); /* change to vmalloc */
377 }
378
Q40Free(void * ptr,unsigned int size)379 static void Q40Free(void *ptr, unsigned int size)
380 {
381 kfree(ptr);
382 }
383
Q40IrqInit(void)384 static int __init Q40IrqInit(void)
385 {
386 /* Register interrupt handler. */
387 request_irq(Q40_IRQ_SAMPLE, Q40StereoInterrupt, 0,
388 "DMA sound", Q40Interrupt);
389
390 return(1);
391 }
392
393
394 #ifdef MODULE
Q40IrqCleanUp(void)395 static void Q40IrqCleanUp(void)
396 {
397 master_outb(0,SAMPLE_ENABLE_REG);
398 free_irq(Q40_IRQ_SAMPLE, Q40Interrupt);
399 }
400 #endif /* MODULE */
401
402
Q40Silence(void)403 static void Q40Silence(void)
404 {
405 master_outb(0,SAMPLE_ENABLE_REG);
406 *DAC_LEFT=*DAC_RIGHT=127;
407 }
408
409 static char *q40_pp=NULL;
410 static unsigned int q40_sc=0;
411
Q40PlayNextFrame(int index)412 static void Q40PlayNextFrame(int index)
413 {
414 u_char *start;
415 u_long size;
416 u_char speed;
417
418 /* used by Q40Play() if all doubts whether there really is something
419 * to be played are already wiped out.
420 */
421 start = write_sq.buffers[write_sq.front];
422 size = (write_sq.count == index ? write_sq.rear_size : write_sq.block_size);
423
424 q40_pp=start;
425 q40_sc=size;
426
427 write_sq.front = (write_sq.front+1) % write_sq.max_count;
428 write_sq.active++;
429
430 speed=(dmasound.hard.speed==10000 ? 0 : 1);
431
432 master_outb( 0,SAMPLE_ENABLE_REG);
433 free_irq(Q40_IRQ_SAMPLE, Q40Interrupt);
434 if (dmasound.soft.stereo)
435 request_irq(Q40_IRQ_SAMPLE, Q40StereoInterrupt, 0,
436 "Q40 sound", Q40Interrupt);
437 else
438 request_irq(Q40_IRQ_SAMPLE, Q40MonoInterrupt, 0,
439 "Q40 sound", Q40Interrupt);
440
441 master_outb( speed, SAMPLE_RATE_REG);
442 master_outb( 1,SAMPLE_CLEAR_REG);
443 master_outb( 1,SAMPLE_ENABLE_REG);
444 }
445
Q40Play(void)446 static void Q40Play(void)
447 {
448 unsigned long flags;
449
450 if (write_sq.active || write_sq.count<=0 ) {
451 /* There's already a frame loaded */
452 return;
453 }
454
455 /* nothing in the queue */
456 if (write_sq.count <= 1 && write_sq.rear_size < write_sq.block_size && !write_sq.syncing) {
457 /* hmmm, the only existing frame is not
458 * yet filled and we're not syncing?
459 */
460 return;
461 }
462 save_flags(flags); cli();
463 Q40PlayNextFrame(1);
464 restore_flags(flags);
465 }
466
Q40StereoInterrupt(int irq,void * dummy,struct pt_regs * fp)467 static void Q40StereoInterrupt(int irq, void *dummy, struct pt_regs *fp)
468 {
469 if (q40_sc>1){
470 *DAC_LEFT=*q40_pp++;
471 *DAC_RIGHT=*q40_pp++;
472 q40_sc -=2;
473 master_outb(1,SAMPLE_CLEAR_REG);
474 }else Q40Interrupt();
475 }
Q40MonoInterrupt(int irq,void * dummy,struct pt_regs * fp)476 static void Q40MonoInterrupt(int irq, void *dummy, struct pt_regs *fp)
477 {
478 if (q40_sc>0){
479 *DAC_LEFT=*q40_pp;
480 *DAC_RIGHT=*q40_pp++;
481 q40_sc --;
482 master_outb(1,SAMPLE_CLEAR_REG);
483 }else Q40Interrupt();
484 }
Q40Interrupt(void)485 static void Q40Interrupt(void)
486 {
487 if (!write_sq.active) {
488 /* playing was interrupted and sq_reset() has already cleared
489 * the sq variables, so better don't do anything here.
490 */
491 WAKE_UP(write_sq.sync_queue);
492 master_outb(0,SAMPLE_ENABLE_REG); /* better safe */
493 goto exit;
494 } else write_sq.active=0;
495 write_sq.count--;
496 Q40Play();
497
498 if (q40_sc<2)
499 { /* there was nothing to play, disable irq */
500 master_outb(0,SAMPLE_ENABLE_REG);
501 *DAC_LEFT=*DAC_RIGHT=127;
502 }
503 WAKE_UP(write_sq.action_queue);
504
505 exit:
506 master_outb(1,SAMPLE_CLEAR_REG);
507 }
508
509
Q40Init(void)510 static void Q40Init(void)
511 {
512 int i, idx;
513 const int freq[] = {10000, 20000};
514
515 /* search a frequency that fits into the allowed error range */
516
517 idx = -1;
518 for (i = 0; i < 2; i++)
519 if ((100 * abs(dmasound.soft.speed - freq[i]) / freq[i]) <= catchRadius)
520 idx = i;
521
522 dmasound.hard = dmasound.soft;
523 /*sound.hard.stereo=1;*/ /* no longer true */
524 dmasound.hard.size=8;
525
526 if (idx > -1) {
527 dmasound.soft.speed = freq[idx];
528 dmasound.trans_write = &transQ40Normal;
529 } else
530 dmasound.trans_write = &transQ40Expanding;
531
532 Q40Silence();
533
534 if (dmasound.hard.speed > 20200) {
535 /* squeeze the sound, we do that */
536 dmasound.hard.speed = 20000;
537 dmasound.trans_write = &transQ40Compressing;
538 } else if (dmasound.hard.speed > 10000) {
539 dmasound.hard.speed = 20000;
540 } else {
541 dmasound.hard.speed = 10000;
542 }
543 expand_bal = -dmasound.soft.speed;
544 }
545
546
Q40SetFormat(int format)547 static int Q40SetFormat(int format)
548 {
549 /* Q40 sound supports only 8bit modes */
550
551 switch (format) {
552 case AFMT_QUERY:
553 return(dmasound.soft.format);
554 case AFMT_MU_LAW:
555 case AFMT_A_LAW:
556 case AFMT_S8:
557 case AFMT_U8:
558 break;
559 default:
560 format = AFMT_S8;
561 }
562
563 dmasound.soft.format = format;
564 dmasound.soft.size = 8;
565 if (dmasound.minDev == SND_DEV_DSP) {
566 dmasound.dsp.format = format;
567 dmasound.dsp.size = 8;
568 }
569 Q40Init();
570
571 return(format);
572 }
573
Q40SetVolume(int volume)574 static int Q40SetVolume(int volume)
575 {
576 return 0;
577 }
578
579
580 /*** Machine definitions *****************************************************/
581
582 static SETTINGS def_hard = {
583 format: AFMT_U8,
584 stereo: 0,
585 size: 8,
586 speed: 10000
587 } ;
588
589 static SETTINGS def_soft = {
590 format: AFMT_U8,
591 stereo: 0,
592 size: 8,
593 speed: 8000
594 } ;
595
596 static MACHINE machQ40 = {
597 name: "Q40",
598 name2: "Q40",
599 open: Q40Open,
600 release: Q40Release,
601 dma_alloc: Q40Alloc,
602 dma_free: Q40Free,
603 irqinit: Q40IrqInit,
604 #ifdef MODULE
605 irqcleanup: Q40IrqCleanUp,
606 #endif /* MODULE */
607 init: Q40Init,
608 silence: Q40Silence,
609 setFormat: Q40SetFormat,
610 setVolume: Q40SetVolume,
611 play: Q40Play,
612 min_dsp_speed: 10000,
613 version: ((DMASOUND_Q40_REVISION<<8) | DMASOUND_Q40_EDITION),
614 hardware_afmts: AFMT_U8, /* h'ware-supported formats *only* here */
615 capabilities: DSP_CAP_BATCH /* As per SNDCTL_DSP_GETCAPS */
616 };
617
618
619 /*** Config & Setup **********************************************************/
620
621
dmasound_q40_init(void)622 int __init dmasound_q40_init(void)
623 {
624 if (MACH_IS_Q40) {
625 dmasound.mach = machQ40;
626 dmasound.mach.default_hard = def_hard ;
627 dmasound.mach.default_soft = def_soft ;
628 return dmasound_init();
629 } else
630 return -ENODEV;
631 }
632
dmasound_q40_cleanup(void)633 static void __exit dmasound_q40_cleanup(void)
634 {
635 dmasound_deinit();
636 }
637
638 module_init(dmasound_q40_init);
639 module_exit(dmasound_q40_cleanup);
640
641 MODULE_DESCRIPTION("Q40/Q60 sound driver");
642 MODULE_LICENSE("GPL");
643