xref: /OK3568_Linux_fs/kernel/sound/oss/dmasound/dmasound_q40.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  *  linux/sound/oss/dmasound/dmasound_q40.c
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  *  Q40 DMA Sound Driver
6*4882a593Smuzhiyun  *
7*4882a593Smuzhiyun  *  See linux/sound/oss/dmasound/dmasound_core.c for copyright and credits
8*4882a593Smuzhiyun  *  prior to 28/01/2001
9*4882a593Smuzhiyun  *
10*4882a593Smuzhiyun  *  28/01/2001 [0.1] Iain Sandoe
11*4882a593Smuzhiyun  *		     - added versioning
12*4882a593Smuzhiyun  *		     - put in and populated the hardware_afmts field.
13*4882a593Smuzhiyun  *             [0.2] - put in SNDCTL_DSP_GETCAPS value.
14*4882a593Smuzhiyun  *	       [0.3] - put in default hard/soft settings.
15*4882a593Smuzhiyun  */
16*4882a593Smuzhiyun 
17*4882a593Smuzhiyun 
18*4882a593Smuzhiyun #include <linux/module.h>
19*4882a593Smuzhiyun #include <linux/init.h>
20*4882a593Smuzhiyun #include <linux/slab.h>
21*4882a593Smuzhiyun #include <linux/soundcard.h>
22*4882a593Smuzhiyun #include <linux/interrupt.h>
23*4882a593Smuzhiyun 
24*4882a593Smuzhiyun #include <linux/uaccess.h>
25*4882a593Smuzhiyun #include <asm/q40ints.h>
26*4882a593Smuzhiyun #include <asm/q40_master.h>
27*4882a593Smuzhiyun 
28*4882a593Smuzhiyun #include "dmasound.h"
29*4882a593Smuzhiyun 
30*4882a593Smuzhiyun #define DMASOUND_Q40_REVISION 0
31*4882a593Smuzhiyun #define DMASOUND_Q40_EDITION 3
32*4882a593Smuzhiyun 
33*4882a593Smuzhiyun static int expand_bal;	/* Balance factor for expanding (not volume!) */
34*4882a593Smuzhiyun static int expand_data;	/* Data for expanding */
35*4882a593Smuzhiyun 
36*4882a593Smuzhiyun 
37*4882a593Smuzhiyun /*** Low level stuff *********************************************************/
38*4882a593Smuzhiyun 
39*4882a593Smuzhiyun 
40*4882a593Smuzhiyun static void *Q40Alloc(unsigned int size, gfp_t flags);
41*4882a593Smuzhiyun static void Q40Free(void *, unsigned int);
42*4882a593Smuzhiyun static int Q40IrqInit(void);
43*4882a593Smuzhiyun #ifdef MODULE
44*4882a593Smuzhiyun static void Q40IrqCleanUp(void);
45*4882a593Smuzhiyun #endif
46*4882a593Smuzhiyun static void Q40Silence(void);
47*4882a593Smuzhiyun static void Q40Init(void);
48*4882a593Smuzhiyun static int Q40SetFormat(int format);
49*4882a593Smuzhiyun static int Q40SetVolume(int volume);
50*4882a593Smuzhiyun static void Q40PlayNextFrame(int index);
51*4882a593Smuzhiyun static void Q40Play(void);
52*4882a593Smuzhiyun static irqreturn_t Q40StereoInterrupt(int irq, void *dummy);
53*4882a593Smuzhiyun static irqreturn_t Q40MonoInterrupt(int irq, void *dummy);
54*4882a593Smuzhiyun static void Q40Interrupt(void);
55*4882a593Smuzhiyun 
56*4882a593Smuzhiyun 
57*4882a593Smuzhiyun /*** Mid level stuff *********************************************************/
58*4882a593Smuzhiyun 
59*4882a593Smuzhiyun 
60*4882a593Smuzhiyun 
61*4882a593Smuzhiyun /* userCount, frameUsed, frameLeft == byte counts */
q40_ct_law(const u_char __user * userPtr,size_t userCount,u_char frame[],ssize_t * frameUsed,ssize_t frameLeft)62*4882a593Smuzhiyun static ssize_t q40_ct_law(const u_char __user *userPtr, size_t userCount,
63*4882a593Smuzhiyun 			   u_char frame[], ssize_t *frameUsed,
64*4882a593Smuzhiyun 			   ssize_t frameLeft)
65*4882a593Smuzhiyun {
66*4882a593Smuzhiyun 	char *table = dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8: dmasound_alaw2dma8;
67*4882a593Smuzhiyun 	ssize_t count, used;
68*4882a593Smuzhiyun 	u_char *p = (u_char *) &frame[*frameUsed];
69*4882a593Smuzhiyun 
70*4882a593Smuzhiyun 	used = count = min_t(size_t, userCount, frameLeft);
71*4882a593Smuzhiyun 	if (copy_from_user(p,userPtr,count))
72*4882a593Smuzhiyun 	  return -EFAULT;
73*4882a593Smuzhiyun 	while (count > 0) {
74*4882a593Smuzhiyun 		*p = table[*p]+128;
75*4882a593Smuzhiyun 		p++;
76*4882a593Smuzhiyun 		count--;
77*4882a593Smuzhiyun 	}
78*4882a593Smuzhiyun 	*frameUsed += used ;
79*4882a593Smuzhiyun 	return used;
80*4882a593Smuzhiyun }
81*4882a593Smuzhiyun 
82*4882a593Smuzhiyun 
q40_ct_s8(const u_char __user * userPtr,size_t userCount,u_char frame[],ssize_t * frameUsed,ssize_t frameLeft)83*4882a593Smuzhiyun static ssize_t q40_ct_s8(const u_char __user *userPtr, size_t userCount,
84*4882a593Smuzhiyun 			  u_char frame[], ssize_t *frameUsed,
85*4882a593Smuzhiyun 			  ssize_t frameLeft)
86*4882a593Smuzhiyun {
87*4882a593Smuzhiyun 	ssize_t count, used;
88*4882a593Smuzhiyun 	u_char *p = (u_char *) &frame[*frameUsed];
89*4882a593Smuzhiyun 
90*4882a593Smuzhiyun 	used = count = min_t(size_t, userCount, frameLeft);
91*4882a593Smuzhiyun 	if (copy_from_user(p,userPtr,count))
92*4882a593Smuzhiyun 	  return -EFAULT;
93*4882a593Smuzhiyun 	while (count > 0) {
94*4882a593Smuzhiyun 		*p = *p + 128;
95*4882a593Smuzhiyun 		p++;
96*4882a593Smuzhiyun 		count--;
97*4882a593Smuzhiyun 	}
98*4882a593Smuzhiyun 	*frameUsed += used;
99*4882a593Smuzhiyun 	return used;
100*4882a593Smuzhiyun }
101*4882a593Smuzhiyun 
q40_ct_u8(const u_char __user * userPtr,size_t userCount,u_char frame[],ssize_t * frameUsed,ssize_t frameLeft)102*4882a593Smuzhiyun static ssize_t q40_ct_u8(const u_char __user *userPtr, size_t userCount,
103*4882a593Smuzhiyun 			  u_char frame[], ssize_t *frameUsed,
104*4882a593Smuzhiyun 			  ssize_t frameLeft)
105*4882a593Smuzhiyun {
106*4882a593Smuzhiyun 	ssize_t count, used;
107*4882a593Smuzhiyun 	u_char *p = (u_char *) &frame[*frameUsed];
108*4882a593Smuzhiyun 
109*4882a593Smuzhiyun 	used = count = min_t(size_t, userCount, frameLeft);
110*4882a593Smuzhiyun 	if (copy_from_user(p,userPtr,count))
111*4882a593Smuzhiyun 	  return -EFAULT;
112*4882a593Smuzhiyun 	*frameUsed += used;
113*4882a593Smuzhiyun 	return used;
114*4882a593Smuzhiyun }
115*4882a593Smuzhiyun 
116*4882a593Smuzhiyun 
117*4882a593Smuzhiyun /* a bit too complicated to optimise right now ..*/
q40_ctx_law(const u_char __user * userPtr,size_t userCount,u_char frame[],ssize_t * frameUsed,ssize_t frameLeft)118*4882a593Smuzhiyun static ssize_t q40_ctx_law(const u_char __user *userPtr, size_t userCount,
119*4882a593Smuzhiyun 			    u_char frame[], ssize_t *frameUsed,
120*4882a593Smuzhiyun 			    ssize_t frameLeft)
121*4882a593Smuzhiyun {
122*4882a593Smuzhiyun 	unsigned char *table = (unsigned char *)
123*4882a593Smuzhiyun 		(dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8: dmasound_alaw2dma8);
124*4882a593Smuzhiyun 	unsigned int data = expand_data;
125*4882a593Smuzhiyun 	u_char *p = (u_char *) &frame[*frameUsed];
126*4882a593Smuzhiyun 	int bal = expand_bal;
127*4882a593Smuzhiyun 	int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
128*4882a593Smuzhiyun 	int utotal, ftotal;
129*4882a593Smuzhiyun 
130*4882a593Smuzhiyun 	ftotal = frameLeft;
131*4882a593Smuzhiyun 	utotal = userCount;
132*4882a593Smuzhiyun 	while (frameLeft) {
133*4882a593Smuzhiyun 		u_char c;
134*4882a593Smuzhiyun 		if (bal < 0) {
135*4882a593Smuzhiyun 			if (userCount == 0)
136*4882a593Smuzhiyun 				break;
137*4882a593Smuzhiyun 			if (get_user(c, userPtr++))
138*4882a593Smuzhiyun 				return -EFAULT;
139*4882a593Smuzhiyun 			data = table[c];
140*4882a593Smuzhiyun 			data += 0x80;
141*4882a593Smuzhiyun 			userCount--;
142*4882a593Smuzhiyun 			bal += hSpeed;
143*4882a593Smuzhiyun 		}
144*4882a593Smuzhiyun 		*p++ = data;
145*4882a593Smuzhiyun 		frameLeft--;
146*4882a593Smuzhiyun 		bal -= sSpeed;
147*4882a593Smuzhiyun 	}
148*4882a593Smuzhiyun 	expand_bal = bal;
149*4882a593Smuzhiyun 	expand_data = data;
150*4882a593Smuzhiyun 	*frameUsed += (ftotal - frameLeft);
151*4882a593Smuzhiyun 	utotal -= userCount;
152*4882a593Smuzhiyun 	return utotal;
153*4882a593Smuzhiyun }
154*4882a593Smuzhiyun 
155*4882a593Smuzhiyun 
q40_ctx_s8(const u_char __user * userPtr,size_t userCount,u_char frame[],ssize_t * frameUsed,ssize_t frameLeft)156*4882a593Smuzhiyun static ssize_t q40_ctx_s8(const u_char __user *userPtr, size_t userCount,
157*4882a593Smuzhiyun 			   u_char frame[], ssize_t *frameUsed,
158*4882a593Smuzhiyun 			   ssize_t frameLeft)
159*4882a593Smuzhiyun {
160*4882a593Smuzhiyun 	u_char *p = (u_char *) &frame[*frameUsed];
161*4882a593Smuzhiyun 	unsigned int data = expand_data;
162*4882a593Smuzhiyun 	int bal = expand_bal;
163*4882a593Smuzhiyun 	int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
164*4882a593Smuzhiyun 	int utotal, ftotal;
165*4882a593Smuzhiyun 
166*4882a593Smuzhiyun 
167*4882a593Smuzhiyun 	ftotal = frameLeft;
168*4882a593Smuzhiyun 	utotal = userCount;
169*4882a593Smuzhiyun 	while (frameLeft) {
170*4882a593Smuzhiyun 		u_char c;
171*4882a593Smuzhiyun 		if (bal < 0) {
172*4882a593Smuzhiyun 			if (userCount == 0)
173*4882a593Smuzhiyun 				break;
174*4882a593Smuzhiyun 			if (get_user(c, userPtr++))
175*4882a593Smuzhiyun 				return -EFAULT;
176*4882a593Smuzhiyun 			data = c ;
177*4882a593Smuzhiyun 			data += 0x80;
178*4882a593Smuzhiyun 			userCount--;
179*4882a593Smuzhiyun 			bal += hSpeed;
180*4882a593Smuzhiyun 		}
181*4882a593Smuzhiyun 		*p++ = data;
182*4882a593Smuzhiyun 		frameLeft--;
183*4882a593Smuzhiyun 		bal -= sSpeed;
184*4882a593Smuzhiyun 	}
185*4882a593Smuzhiyun 	expand_bal = bal;
186*4882a593Smuzhiyun 	expand_data = data;
187*4882a593Smuzhiyun 	*frameUsed += (ftotal - frameLeft);
188*4882a593Smuzhiyun 	utotal -= userCount;
189*4882a593Smuzhiyun 	return utotal;
190*4882a593Smuzhiyun }
191*4882a593Smuzhiyun 
192*4882a593Smuzhiyun 
q40_ctx_u8(const u_char __user * userPtr,size_t userCount,u_char frame[],ssize_t * frameUsed,ssize_t frameLeft)193*4882a593Smuzhiyun static ssize_t q40_ctx_u8(const u_char __user *userPtr, size_t userCount,
194*4882a593Smuzhiyun 			   u_char frame[], ssize_t *frameUsed,
195*4882a593Smuzhiyun 			   ssize_t frameLeft)
196*4882a593Smuzhiyun {
197*4882a593Smuzhiyun 	u_char *p = (u_char *) &frame[*frameUsed];
198*4882a593Smuzhiyun 	unsigned int data = expand_data;
199*4882a593Smuzhiyun 	int bal = expand_bal;
200*4882a593Smuzhiyun 	int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
201*4882a593Smuzhiyun 	int utotal, ftotal;
202*4882a593Smuzhiyun 
203*4882a593Smuzhiyun 	ftotal = frameLeft;
204*4882a593Smuzhiyun 	utotal = userCount;
205*4882a593Smuzhiyun 	while (frameLeft) {
206*4882a593Smuzhiyun 		u_char c;
207*4882a593Smuzhiyun 		if (bal < 0) {
208*4882a593Smuzhiyun 			if (userCount == 0)
209*4882a593Smuzhiyun 				break;
210*4882a593Smuzhiyun 			if (get_user(c, userPtr++))
211*4882a593Smuzhiyun 				return -EFAULT;
212*4882a593Smuzhiyun 			data = c ;
213*4882a593Smuzhiyun 			userCount--;
214*4882a593Smuzhiyun 			bal += hSpeed;
215*4882a593Smuzhiyun 		}
216*4882a593Smuzhiyun 		*p++ = data;
217*4882a593Smuzhiyun 		frameLeft--;
218*4882a593Smuzhiyun 		bal -= sSpeed;
219*4882a593Smuzhiyun 	}
220*4882a593Smuzhiyun 	expand_bal = bal;
221*4882a593Smuzhiyun 	expand_data = data;
222*4882a593Smuzhiyun 	*frameUsed += (ftotal - frameLeft) ;
223*4882a593Smuzhiyun 	utotal -= userCount;
224*4882a593Smuzhiyun 	return utotal;
225*4882a593Smuzhiyun }
226*4882a593Smuzhiyun 
227*4882a593Smuzhiyun /* compressing versions */
q40_ctc_law(const u_char __user * userPtr,size_t userCount,u_char frame[],ssize_t * frameUsed,ssize_t frameLeft)228*4882a593Smuzhiyun static ssize_t q40_ctc_law(const u_char __user *userPtr, size_t userCount,
229*4882a593Smuzhiyun 			    u_char frame[], ssize_t *frameUsed,
230*4882a593Smuzhiyun 			    ssize_t frameLeft)
231*4882a593Smuzhiyun {
232*4882a593Smuzhiyun 	unsigned char *table = (unsigned char *)
233*4882a593Smuzhiyun 		(dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8: dmasound_alaw2dma8);
234*4882a593Smuzhiyun 	unsigned int data = expand_data;
235*4882a593Smuzhiyun 	u_char *p = (u_char *) &frame[*frameUsed];
236*4882a593Smuzhiyun 	int bal = expand_bal;
237*4882a593Smuzhiyun 	int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
238*4882a593Smuzhiyun 	int utotal, ftotal;
239*4882a593Smuzhiyun 
240*4882a593Smuzhiyun 	ftotal = frameLeft;
241*4882a593Smuzhiyun 	utotal = userCount;
242*4882a593Smuzhiyun 	while (frameLeft) {
243*4882a593Smuzhiyun 		u_char c;
244*4882a593Smuzhiyun 		while(bal<0) {
245*4882a593Smuzhiyun 			if (userCount == 0)
246*4882a593Smuzhiyun 				goto lout;
247*4882a593Smuzhiyun 			if (!(bal<(-hSpeed))) {
248*4882a593Smuzhiyun 				if (get_user(c, userPtr))
249*4882a593Smuzhiyun 					return -EFAULT;
250*4882a593Smuzhiyun 				data = 0x80 + table[c];
251*4882a593Smuzhiyun 			}
252*4882a593Smuzhiyun 			userPtr++;
253*4882a593Smuzhiyun 			userCount--;
254*4882a593Smuzhiyun 			bal += hSpeed;
255*4882a593Smuzhiyun 		}
256*4882a593Smuzhiyun 		*p++ = data;
257*4882a593Smuzhiyun 		frameLeft--;
258*4882a593Smuzhiyun 		bal -= sSpeed;
259*4882a593Smuzhiyun 	}
260*4882a593Smuzhiyun  lout:
261*4882a593Smuzhiyun 	expand_bal = bal;
262*4882a593Smuzhiyun 	expand_data = data;
263*4882a593Smuzhiyun 	*frameUsed += (ftotal - frameLeft);
264*4882a593Smuzhiyun 	utotal -= userCount;
265*4882a593Smuzhiyun 	return utotal;
266*4882a593Smuzhiyun }
267*4882a593Smuzhiyun 
268*4882a593Smuzhiyun 
q40_ctc_s8(const u_char __user * userPtr,size_t userCount,u_char frame[],ssize_t * frameUsed,ssize_t frameLeft)269*4882a593Smuzhiyun static ssize_t q40_ctc_s8(const u_char __user *userPtr, size_t userCount,
270*4882a593Smuzhiyun 			   u_char frame[], ssize_t *frameUsed,
271*4882a593Smuzhiyun 			   ssize_t frameLeft)
272*4882a593Smuzhiyun {
273*4882a593Smuzhiyun 	u_char *p = (u_char *) &frame[*frameUsed];
274*4882a593Smuzhiyun 	unsigned int data = expand_data;
275*4882a593Smuzhiyun 	int bal = expand_bal;
276*4882a593Smuzhiyun 	int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
277*4882a593Smuzhiyun 	int utotal, ftotal;
278*4882a593Smuzhiyun 
279*4882a593Smuzhiyun 	ftotal = frameLeft;
280*4882a593Smuzhiyun 	utotal = userCount;
281*4882a593Smuzhiyun 	while (frameLeft) {
282*4882a593Smuzhiyun 		u_char c;
283*4882a593Smuzhiyun 		while (bal < 0) {
284*4882a593Smuzhiyun 			if (userCount == 0)
285*4882a593Smuzhiyun 				goto lout;
286*4882a593Smuzhiyun 			if (!(bal<(-hSpeed))) {
287*4882a593Smuzhiyun 				if (get_user(c, userPtr))
288*4882a593Smuzhiyun 					return -EFAULT;
289*4882a593Smuzhiyun 				data = c + 0x80;
290*4882a593Smuzhiyun 			}
291*4882a593Smuzhiyun 			userPtr++;
292*4882a593Smuzhiyun 			userCount--;
293*4882a593Smuzhiyun 			bal += hSpeed;
294*4882a593Smuzhiyun 		}
295*4882a593Smuzhiyun 		*p++ = data;
296*4882a593Smuzhiyun 		frameLeft--;
297*4882a593Smuzhiyun 		bal -= sSpeed;
298*4882a593Smuzhiyun 	}
299*4882a593Smuzhiyun  lout:
300*4882a593Smuzhiyun 	expand_bal = bal;
301*4882a593Smuzhiyun 	expand_data = data;
302*4882a593Smuzhiyun 	*frameUsed += (ftotal - frameLeft);
303*4882a593Smuzhiyun 	utotal -= userCount;
304*4882a593Smuzhiyun 	return utotal;
305*4882a593Smuzhiyun }
306*4882a593Smuzhiyun 
307*4882a593Smuzhiyun 
q40_ctc_u8(const u_char __user * userPtr,size_t userCount,u_char frame[],ssize_t * frameUsed,ssize_t frameLeft)308*4882a593Smuzhiyun static ssize_t q40_ctc_u8(const u_char __user *userPtr, size_t userCount,
309*4882a593Smuzhiyun 			   u_char frame[], ssize_t *frameUsed,
310*4882a593Smuzhiyun 			   ssize_t frameLeft)
311*4882a593Smuzhiyun {
312*4882a593Smuzhiyun 	u_char *p = (u_char *) &frame[*frameUsed];
313*4882a593Smuzhiyun 	unsigned int data = expand_data;
314*4882a593Smuzhiyun 	int bal = expand_bal;
315*4882a593Smuzhiyun 	int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
316*4882a593Smuzhiyun 	int utotal, ftotal;
317*4882a593Smuzhiyun 
318*4882a593Smuzhiyun 	ftotal = frameLeft;
319*4882a593Smuzhiyun 	utotal = userCount;
320*4882a593Smuzhiyun 	while (frameLeft) {
321*4882a593Smuzhiyun 		u_char c;
322*4882a593Smuzhiyun 		while (bal < 0) {
323*4882a593Smuzhiyun 			if (userCount == 0)
324*4882a593Smuzhiyun 				goto lout;
325*4882a593Smuzhiyun 			if (!(bal<(-hSpeed))) {
326*4882a593Smuzhiyun 				if (get_user(c, userPtr))
327*4882a593Smuzhiyun 					return -EFAULT;
328*4882a593Smuzhiyun 				data = c ;
329*4882a593Smuzhiyun 			}
330*4882a593Smuzhiyun 			userPtr++;
331*4882a593Smuzhiyun 			userCount--;
332*4882a593Smuzhiyun 			bal += hSpeed;
333*4882a593Smuzhiyun 		}
334*4882a593Smuzhiyun 		*p++ = data;
335*4882a593Smuzhiyun 		frameLeft--;
336*4882a593Smuzhiyun 		bal -= sSpeed;
337*4882a593Smuzhiyun 	}
338*4882a593Smuzhiyun  lout:
339*4882a593Smuzhiyun 	expand_bal = bal;
340*4882a593Smuzhiyun 	expand_data = data;
341*4882a593Smuzhiyun 	*frameUsed += (ftotal - frameLeft) ;
342*4882a593Smuzhiyun 	utotal -= userCount;
343*4882a593Smuzhiyun 	return utotal;
344*4882a593Smuzhiyun }
345*4882a593Smuzhiyun 
346*4882a593Smuzhiyun 
347*4882a593Smuzhiyun static TRANS transQ40Normal = {
348*4882a593Smuzhiyun 	q40_ct_law, q40_ct_law, q40_ct_s8, q40_ct_u8, NULL, NULL, NULL, NULL
349*4882a593Smuzhiyun };
350*4882a593Smuzhiyun 
351*4882a593Smuzhiyun static TRANS transQ40Expanding = {
352*4882a593Smuzhiyun 	q40_ctx_law, q40_ctx_law, q40_ctx_s8, q40_ctx_u8, NULL, NULL, NULL, NULL
353*4882a593Smuzhiyun };
354*4882a593Smuzhiyun 
355*4882a593Smuzhiyun static TRANS transQ40Compressing = {
356*4882a593Smuzhiyun 	q40_ctc_law, q40_ctc_law, q40_ctc_s8, q40_ctc_u8, NULL, NULL, NULL, NULL
357*4882a593Smuzhiyun };
358*4882a593Smuzhiyun 
359*4882a593Smuzhiyun 
360*4882a593Smuzhiyun /*** Low level stuff *********************************************************/
361*4882a593Smuzhiyun 
Q40Alloc(unsigned int size,gfp_t flags)362*4882a593Smuzhiyun static void *Q40Alloc(unsigned int size, gfp_t flags)
363*4882a593Smuzhiyun {
364*4882a593Smuzhiyun          return kmalloc(size, flags); /* change to vmalloc */
365*4882a593Smuzhiyun }
366*4882a593Smuzhiyun 
Q40Free(void * ptr,unsigned int size)367*4882a593Smuzhiyun static void Q40Free(void *ptr, unsigned int size)
368*4882a593Smuzhiyun {
369*4882a593Smuzhiyun 	kfree(ptr);
370*4882a593Smuzhiyun }
371*4882a593Smuzhiyun 
Q40IrqInit(void)372*4882a593Smuzhiyun static int __init Q40IrqInit(void)
373*4882a593Smuzhiyun {
374*4882a593Smuzhiyun 	/* Register interrupt handler. */
375*4882a593Smuzhiyun 	if (request_irq(Q40_IRQ_SAMPLE, Q40StereoInterrupt, 0,
376*4882a593Smuzhiyun 		    "DMA sound", Q40Interrupt))
377*4882a593Smuzhiyun 		return 0;
378*4882a593Smuzhiyun 
379*4882a593Smuzhiyun 	return(1);
380*4882a593Smuzhiyun }
381*4882a593Smuzhiyun 
382*4882a593Smuzhiyun 
383*4882a593Smuzhiyun #ifdef MODULE
Q40IrqCleanUp(void)384*4882a593Smuzhiyun static void Q40IrqCleanUp(void)
385*4882a593Smuzhiyun {
386*4882a593Smuzhiyun         master_outb(0,SAMPLE_ENABLE_REG);
387*4882a593Smuzhiyun 	free_irq(Q40_IRQ_SAMPLE, Q40Interrupt);
388*4882a593Smuzhiyun }
389*4882a593Smuzhiyun #endif /* MODULE */
390*4882a593Smuzhiyun 
391*4882a593Smuzhiyun 
Q40Silence(void)392*4882a593Smuzhiyun static void Q40Silence(void)
393*4882a593Smuzhiyun {
394*4882a593Smuzhiyun         master_outb(0,SAMPLE_ENABLE_REG);
395*4882a593Smuzhiyun 	*DAC_LEFT=*DAC_RIGHT=127;
396*4882a593Smuzhiyun }
397*4882a593Smuzhiyun 
398*4882a593Smuzhiyun static char *q40_pp;
399*4882a593Smuzhiyun static unsigned int q40_sc;
400*4882a593Smuzhiyun 
Q40PlayNextFrame(int index)401*4882a593Smuzhiyun static void Q40PlayNextFrame(int index)
402*4882a593Smuzhiyun {
403*4882a593Smuzhiyun 	u_char *start;
404*4882a593Smuzhiyun 	u_long size;
405*4882a593Smuzhiyun 	u_char speed;
406*4882a593Smuzhiyun 	int error;
407*4882a593Smuzhiyun 
408*4882a593Smuzhiyun 	/* used by Q40Play() if all doubts whether there really is something
409*4882a593Smuzhiyun 	 * to be played are already wiped out.
410*4882a593Smuzhiyun 	 */
411*4882a593Smuzhiyun 	start = write_sq.buffers[write_sq.front];
412*4882a593Smuzhiyun 	size = (write_sq.count == index ? write_sq.rear_size : write_sq.block_size);
413*4882a593Smuzhiyun 
414*4882a593Smuzhiyun 	q40_pp=start;
415*4882a593Smuzhiyun 	q40_sc=size;
416*4882a593Smuzhiyun 
417*4882a593Smuzhiyun 	write_sq.front = (write_sq.front+1) % write_sq.max_count;
418*4882a593Smuzhiyun 	write_sq.active++;
419*4882a593Smuzhiyun 
420*4882a593Smuzhiyun 	speed=(dmasound.hard.speed==10000 ? 0 : 1);
421*4882a593Smuzhiyun 
422*4882a593Smuzhiyun 	master_outb( 0,SAMPLE_ENABLE_REG);
423*4882a593Smuzhiyun 	free_irq(Q40_IRQ_SAMPLE, Q40Interrupt);
424*4882a593Smuzhiyun 	if (dmasound.soft.stereo)
425*4882a593Smuzhiyun 		error = request_irq(Q40_IRQ_SAMPLE, Q40StereoInterrupt, 0,
426*4882a593Smuzhiyun 				    "Q40 sound", Q40Interrupt);
427*4882a593Smuzhiyun 	  else
428*4882a593Smuzhiyun 		error = request_irq(Q40_IRQ_SAMPLE, Q40MonoInterrupt, 0,
429*4882a593Smuzhiyun 				    "Q40 sound", Q40Interrupt);
430*4882a593Smuzhiyun 	if (error && printk_ratelimit())
431*4882a593Smuzhiyun 		pr_err("Couldn't register sound interrupt\n");
432*4882a593Smuzhiyun 
433*4882a593Smuzhiyun 	master_outb( speed, SAMPLE_RATE_REG);
434*4882a593Smuzhiyun 	master_outb( 1,SAMPLE_CLEAR_REG);
435*4882a593Smuzhiyun 	master_outb( 1,SAMPLE_ENABLE_REG);
436*4882a593Smuzhiyun }
437*4882a593Smuzhiyun 
Q40Play(void)438*4882a593Smuzhiyun static void Q40Play(void)
439*4882a593Smuzhiyun {
440*4882a593Smuzhiyun         unsigned long flags;
441*4882a593Smuzhiyun 
442*4882a593Smuzhiyun 	if (write_sq.active || write_sq.count<=0 ) {
443*4882a593Smuzhiyun 		/* There's already a frame loaded */
444*4882a593Smuzhiyun 		return;
445*4882a593Smuzhiyun 	}
446*4882a593Smuzhiyun 
447*4882a593Smuzhiyun 	/* nothing in the queue */
448*4882a593Smuzhiyun 	if (write_sq.count <= 1 && write_sq.rear_size < write_sq.block_size && !write_sq.syncing) {
449*4882a593Smuzhiyun 	         /* hmmm, the only existing frame is not
450*4882a593Smuzhiyun 		  * yet filled and we're not syncing?
451*4882a593Smuzhiyun 		  */
452*4882a593Smuzhiyun 	         return;
453*4882a593Smuzhiyun 	}
454*4882a593Smuzhiyun 	spin_lock_irqsave(&dmasound.lock, flags);
455*4882a593Smuzhiyun 	Q40PlayNextFrame(1);
456*4882a593Smuzhiyun 	spin_unlock_irqrestore(&dmasound.lock, flags);
457*4882a593Smuzhiyun }
458*4882a593Smuzhiyun 
Q40StereoInterrupt(int irq,void * dummy)459*4882a593Smuzhiyun static irqreturn_t Q40StereoInterrupt(int irq, void *dummy)
460*4882a593Smuzhiyun {
461*4882a593Smuzhiyun 	spin_lock(&dmasound.lock);
462*4882a593Smuzhiyun         if (q40_sc>1){
463*4882a593Smuzhiyun             *DAC_LEFT=*q40_pp++;
464*4882a593Smuzhiyun 	    *DAC_RIGHT=*q40_pp++;
465*4882a593Smuzhiyun 	    q40_sc -=2;
466*4882a593Smuzhiyun 	    master_outb(1,SAMPLE_CLEAR_REG);
467*4882a593Smuzhiyun 	}else Q40Interrupt();
468*4882a593Smuzhiyun 	spin_unlock(&dmasound.lock);
469*4882a593Smuzhiyun 	return IRQ_HANDLED;
470*4882a593Smuzhiyun }
Q40MonoInterrupt(int irq,void * dummy)471*4882a593Smuzhiyun static irqreturn_t Q40MonoInterrupt(int irq, void *dummy)
472*4882a593Smuzhiyun {
473*4882a593Smuzhiyun 	spin_lock(&dmasound.lock);
474*4882a593Smuzhiyun         if (q40_sc>0){
475*4882a593Smuzhiyun             *DAC_LEFT=*q40_pp;
476*4882a593Smuzhiyun 	    *DAC_RIGHT=*q40_pp++;
477*4882a593Smuzhiyun 	    q40_sc --;
478*4882a593Smuzhiyun 	    master_outb(1,SAMPLE_CLEAR_REG);
479*4882a593Smuzhiyun 	}else Q40Interrupt();
480*4882a593Smuzhiyun 	spin_unlock(&dmasound.lock);
481*4882a593Smuzhiyun 	return IRQ_HANDLED;
482*4882a593Smuzhiyun }
Q40Interrupt(void)483*4882a593Smuzhiyun static void Q40Interrupt(void)
484*4882a593Smuzhiyun {
485*4882a593Smuzhiyun 	if (!write_sq.active) {
486*4882a593Smuzhiyun 	          /* playing was interrupted and sq_reset() has already cleared
487*4882a593Smuzhiyun 		   * the sq variables, so better don't do anything here.
488*4882a593Smuzhiyun 		   */
489*4882a593Smuzhiyun 	           WAKE_UP(write_sq.sync_queue);
490*4882a593Smuzhiyun 		   master_outb(0,SAMPLE_ENABLE_REG); /* better safe */
491*4882a593Smuzhiyun 		   goto exit;
492*4882a593Smuzhiyun 	} else write_sq.active=0;
493*4882a593Smuzhiyun 	write_sq.count--;
494*4882a593Smuzhiyun 	Q40Play();
495*4882a593Smuzhiyun 
496*4882a593Smuzhiyun 	if (q40_sc<2)
497*4882a593Smuzhiyun 	      { /* there was nothing to play, disable irq */
498*4882a593Smuzhiyun 		master_outb(0,SAMPLE_ENABLE_REG);
499*4882a593Smuzhiyun 		*DAC_LEFT=*DAC_RIGHT=127;
500*4882a593Smuzhiyun 	      }
501*4882a593Smuzhiyun 	WAKE_UP(write_sq.action_queue);
502*4882a593Smuzhiyun 
503*4882a593Smuzhiyun  exit:
504*4882a593Smuzhiyun 	master_outb(1,SAMPLE_CLEAR_REG);
505*4882a593Smuzhiyun }
506*4882a593Smuzhiyun 
507*4882a593Smuzhiyun 
Q40Init(void)508*4882a593Smuzhiyun static void Q40Init(void)
509*4882a593Smuzhiyun {
510*4882a593Smuzhiyun 	int i, idx;
511*4882a593Smuzhiyun 	const int freq[] = {10000, 20000};
512*4882a593Smuzhiyun 
513*4882a593Smuzhiyun 	/* search a frequency that fits into the allowed error range */
514*4882a593Smuzhiyun 
515*4882a593Smuzhiyun 	idx = -1;
516*4882a593Smuzhiyun 	for (i = 0; i < 2; i++)
517*4882a593Smuzhiyun 		if ((100 * abs(dmasound.soft.speed - freq[i]) / freq[i]) <= catchRadius)
518*4882a593Smuzhiyun 			idx = i;
519*4882a593Smuzhiyun 
520*4882a593Smuzhiyun 	dmasound.hard = dmasound.soft;
521*4882a593Smuzhiyun 	/*sound.hard.stereo=1;*/ /* no longer true */
522*4882a593Smuzhiyun 	dmasound.hard.size=8;
523*4882a593Smuzhiyun 
524*4882a593Smuzhiyun 	if (idx > -1) {
525*4882a593Smuzhiyun 		dmasound.soft.speed = freq[idx];
526*4882a593Smuzhiyun 		dmasound.trans_write = &transQ40Normal;
527*4882a593Smuzhiyun 	} else
528*4882a593Smuzhiyun 		dmasound.trans_write = &transQ40Expanding;
529*4882a593Smuzhiyun 
530*4882a593Smuzhiyun 	Q40Silence();
531*4882a593Smuzhiyun 
532*4882a593Smuzhiyun 	if (dmasound.hard.speed > 20200) {
533*4882a593Smuzhiyun 		/* squeeze the sound, we do that */
534*4882a593Smuzhiyun 		dmasound.hard.speed = 20000;
535*4882a593Smuzhiyun 		dmasound.trans_write = &transQ40Compressing;
536*4882a593Smuzhiyun 	} else if (dmasound.hard.speed > 10000) {
537*4882a593Smuzhiyun 		dmasound.hard.speed = 20000;
538*4882a593Smuzhiyun 	} else {
539*4882a593Smuzhiyun 		dmasound.hard.speed = 10000;
540*4882a593Smuzhiyun 	}
541*4882a593Smuzhiyun 	expand_bal = -dmasound.soft.speed;
542*4882a593Smuzhiyun }
543*4882a593Smuzhiyun 
544*4882a593Smuzhiyun 
Q40SetFormat(int format)545*4882a593Smuzhiyun static int Q40SetFormat(int format)
546*4882a593Smuzhiyun {
547*4882a593Smuzhiyun 	/* Q40 sound supports only 8bit modes */
548*4882a593Smuzhiyun 
549*4882a593Smuzhiyun 	switch (format) {
550*4882a593Smuzhiyun 	case AFMT_QUERY:
551*4882a593Smuzhiyun 		return(dmasound.soft.format);
552*4882a593Smuzhiyun 	case AFMT_MU_LAW:
553*4882a593Smuzhiyun 	case AFMT_A_LAW:
554*4882a593Smuzhiyun 	case AFMT_S8:
555*4882a593Smuzhiyun 	case AFMT_U8:
556*4882a593Smuzhiyun 		break;
557*4882a593Smuzhiyun 	default:
558*4882a593Smuzhiyun 		format = AFMT_S8;
559*4882a593Smuzhiyun 	}
560*4882a593Smuzhiyun 
561*4882a593Smuzhiyun 	dmasound.soft.format = format;
562*4882a593Smuzhiyun 	dmasound.soft.size = 8;
563*4882a593Smuzhiyun 	if (dmasound.minDev == SND_DEV_DSP) {
564*4882a593Smuzhiyun 		dmasound.dsp.format = format;
565*4882a593Smuzhiyun 		dmasound.dsp.size = 8;
566*4882a593Smuzhiyun 	}
567*4882a593Smuzhiyun 	Q40Init();
568*4882a593Smuzhiyun 
569*4882a593Smuzhiyun 	return(format);
570*4882a593Smuzhiyun }
571*4882a593Smuzhiyun 
Q40SetVolume(int volume)572*4882a593Smuzhiyun static int Q40SetVolume(int volume)
573*4882a593Smuzhiyun {
574*4882a593Smuzhiyun     return 0;
575*4882a593Smuzhiyun }
576*4882a593Smuzhiyun 
577*4882a593Smuzhiyun 
578*4882a593Smuzhiyun /*** Machine definitions *****************************************************/
579*4882a593Smuzhiyun 
580*4882a593Smuzhiyun static SETTINGS def_hard = {
581*4882a593Smuzhiyun 	.format	= AFMT_U8,
582*4882a593Smuzhiyun 	.stereo	= 0,
583*4882a593Smuzhiyun 	.size	= 8,
584*4882a593Smuzhiyun 	.speed	= 10000
585*4882a593Smuzhiyun } ;
586*4882a593Smuzhiyun 
587*4882a593Smuzhiyun static SETTINGS def_soft = {
588*4882a593Smuzhiyun 	.format	= AFMT_U8,
589*4882a593Smuzhiyun 	.stereo	= 0,
590*4882a593Smuzhiyun 	.size	= 8,
591*4882a593Smuzhiyun 	.speed	= 8000
592*4882a593Smuzhiyun } ;
593*4882a593Smuzhiyun 
594*4882a593Smuzhiyun static MACHINE machQ40 = {
595*4882a593Smuzhiyun 	.name		= "Q40",
596*4882a593Smuzhiyun 	.name2		= "Q40",
597*4882a593Smuzhiyun 	.owner		= THIS_MODULE,
598*4882a593Smuzhiyun 	.dma_alloc	= Q40Alloc,
599*4882a593Smuzhiyun 	.dma_free	= Q40Free,
600*4882a593Smuzhiyun 	.irqinit	= Q40IrqInit,
601*4882a593Smuzhiyun #ifdef MODULE
602*4882a593Smuzhiyun 	.irqcleanup	= Q40IrqCleanUp,
603*4882a593Smuzhiyun #endif /* MODULE */
604*4882a593Smuzhiyun 	.init		= Q40Init,
605*4882a593Smuzhiyun 	.silence	= Q40Silence,
606*4882a593Smuzhiyun 	.setFormat	= Q40SetFormat,
607*4882a593Smuzhiyun 	.setVolume	= Q40SetVolume,
608*4882a593Smuzhiyun 	.play		= Q40Play,
609*4882a593Smuzhiyun  	.min_dsp_speed	= 10000,
610*4882a593Smuzhiyun 	.version	= ((DMASOUND_Q40_REVISION<<8) | DMASOUND_Q40_EDITION),
611*4882a593Smuzhiyun 	.hardware_afmts	= AFMT_U8, /* h'ware-supported formats *only* here */
612*4882a593Smuzhiyun 	.capabilities	= DSP_CAP_BATCH  /* As per SNDCTL_DSP_GETCAPS */
613*4882a593Smuzhiyun };
614*4882a593Smuzhiyun 
615*4882a593Smuzhiyun 
616*4882a593Smuzhiyun /*** Config & Setup **********************************************************/
617*4882a593Smuzhiyun 
618*4882a593Smuzhiyun 
dmasound_q40_init(void)619*4882a593Smuzhiyun static int __init dmasound_q40_init(void)
620*4882a593Smuzhiyun {
621*4882a593Smuzhiyun 	if (MACH_IS_Q40) {
622*4882a593Smuzhiyun 	    dmasound.mach = machQ40;
623*4882a593Smuzhiyun 	    dmasound.mach.default_hard = def_hard ;
624*4882a593Smuzhiyun 	    dmasound.mach.default_soft = def_soft ;
625*4882a593Smuzhiyun 	    return dmasound_init();
626*4882a593Smuzhiyun 	} else
627*4882a593Smuzhiyun 	    return -ENODEV;
628*4882a593Smuzhiyun }
629*4882a593Smuzhiyun 
dmasound_q40_cleanup(void)630*4882a593Smuzhiyun static void __exit dmasound_q40_cleanup(void)
631*4882a593Smuzhiyun {
632*4882a593Smuzhiyun 	dmasound_deinit();
633*4882a593Smuzhiyun }
634*4882a593Smuzhiyun 
635*4882a593Smuzhiyun module_init(dmasound_q40_init);
636*4882a593Smuzhiyun module_exit(dmasound_q40_cleanup);
637*4882a593Smuzhiyun 
638*4882a593Smuzhiyun MODULE_DESCRIPTION("Q40/Q60 sound driver");
639*4882a593Smuzhiyun MODULE_LICENSE("GPL");
640