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
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");
639