Linux Audio

Check our new training course

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