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