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