xref: /linux/sound/oss/dmasound/dmasound_q40.c (revision ca55b2fef3a9373fcfc30f82fd26bc7fccbda732)
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 <asm/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 
32 static int expand_bal;	/* Balance factor for expanding (not volume!) */
33 static int expand_data;	/* Data for expanding */
34 
35 
36 /*** Low level stuff *********************************************************/
37 
38 
39 static void *Q40Alloc(unsigned int size, gfp_t flags);
40 static void Q40Free(void *, unsigned int);
41 static int Q40IrqInit(void);
42 #ifdef MODULE
43 static void Q40IrqCleanUp(void);
44 #endif
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);
54 
55 
56 /*** Mid level stuff *********************************************************/
57 
58 
59 
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,
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 
82 static 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 
101 static 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 ..*/
117 static 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 
155 static 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 
192 static 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 */
227 static 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 
268 static 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 
307 static 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 
346 static TRANS transQ40Normal = {
347 	q40_ct_law, q40_ct_law, q40_ct_s8, q40_ct_u8, NULL, NULL, NULL, NULL
348 };
349 
350 static TRANS transQ40Expanding = {
351 	q40_ctx_law, q40_ctx_law, q40_ctx_s8, q40_ctx_u8, NULL, NULL, NULL, NULL
352 };
353 
354 static 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 
361 static void *Q40Alloc(unsigned int size, gfp_t flags)
362 {
363          return kmalloc(size, flags); /* change to vmalloc */
364 }
365 
366 static void Q40Free(void *ptr, unsigned int size)
367 {
368 	kfree(ptr);
369 }
370 
371 static 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
383 static void Q40IrqCleanUp(void)
384 {
385         master_outb(0,SAMPLE_ENABLE_REG);
386 	free_irq(Q40_IRQ_SAMPLE, Q40Interrupt);
387 }
388 #endif /* MODULE */
389 
390 
391 static void Q40Silence(void)
392 {
393         master_outb(0,SAMPLE_ENABLE_REG);
394 	*DAC_LEFT=*DAC_RIGHT=127;
395 }
396 
397 static char *q40_pp;
398 static unsigned int q40_sc;
399 
400 static 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 
437 static 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 
458 static 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 }
470 static 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 }
482 static 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 
507 static 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 
544 static 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 
571 static int Q40SetVolume(int volume)
572 {
573     return 0;
574 }
575 
576 
577 /*** Machine definitions *****************************************************/
578 
579 static SETTINGS def_hard = {
580 	.format	= AFMT_U8,
581 	.stereo	= 0,
582 	.size	= 8,
583 	.speed	= 10000
584 } ;
585 
586 static SETTINGS def_soft = {
587 	.format	= AFMT_U8,
588 	.stereo	= 0,
589 	.size	= 8,
590 	.speed	= 8000
591 } ;
592 
593 static 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 
618 static 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 
629 static void __exit dmasound_q40_cleanup(void)
630 {
631 	dmasound_deinit();
632 }
633 
634 module_init(dmasound_q40_init);
635 module_exit(dmasound_q40_cleanup);
636 
637 MODULE_DESCRIPTION("Q40/Q60 sound driver");
638 MODULE_LICENSE("GPL");
639