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