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