xref: /OK3568_Linux_fs/kernel/sound/oss/dmasound/dmasound_paula.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  *  linux/sound/oss/dmasound/dmasound_paula.c
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  *  Amiga `Paula' 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 constraint on state buffer usage.
15*4882a593Smuzhiyun  *	       [0.4] - put in default hard/soft settings
16*4882a593Smuzhiyun */
17*4882a593Smuzhiyun 
18*4882a593Smuzhiyun 
19*4882a593Smuzhiyun #include <linux/module.h>
20*4882a593Smuzhiyun #include <linux/mm.h>
21*4882a593Smuzhiyun #include <linux/init.h>
22*4882a593Smuzhiyun #include <linux/ioport.h>
23*4882a593Smuzhiyun #include <linux/soundcard.h>
24*4882a593Smuzhiyun #include <linux/interrupt.h>
25*4882a593Smuzhiyun #include <linux/platform_device.h>
26*4882a593Smuzhiyun 
27*4882a593Smuzhiyun #include <linux/uaccess.h>
28*4882a593Smuzhiyun #include <asm/setup.h>
29*4882a593Smuzhiyun #include <asm/amigahw.h>
30*4882a593Smuzhiyun #include <asm/amigaints.h>
31*4882a593Smuzhiyun #include <asm/machdep.h>
32*4882a593Smuzhiyun 
33*4882a593Smuzhiyun #include "dmasound.h"
34*4882a593Smuzhiyun 
35*4882a593Smuzhiyun #define DMASOUND_PAULA_REVISION 0
36*4882a593Smuzhiyun #define DMASOUND_PAULA_EDITION 4
37*4882a593Smuzhiyun 
38*4882a593Smuzhiyun #define custom amiga_custom
39*4882a593Smuzhiyun    /*
40*4882a593Smuzhiyun     *	The minimum period for audio depends on htotal (for OCS/ECS/AGA)
41*4882a593Smuzhiyun     *	(Imported from arch/m68k/amiga/amisound.c)
42*4882a593Smuzhiyun     */
43*4882a593Smuzhiyun 
44*4882a593Smuzhiyun extern volatile u_short amiga_audio_min_period;
45*4882a593Smuzhiyun 
46*4882a593Smuzhiyun 
47*4882a593Smuzhiyun    /*
48*4882a593Smuzhiyun     *	amiga_mksound() should be able to restore the period after beeping
49*4882a593Smuzhiyun     *	(Imported from arch/m68k/amiga/amisound.c)
50*4882a593Smuzhiyun     */
51*4882a593Smuzhiyun 
52*4882a593Smuzhiyun extern u_short amiga_audio_period;
53*4882a593Smuzhiyun 
54*4882a593Smuzhiyun 
55*4882a593Smuzhiyun    /*
56*4882a593Smuzhiyun     *	Audio DMA masks
57*4882a593Smuzhiyun     */
58*4882a593Smuzhiyun 
59*4882a593Smuzhiyun #define AMI_AUDIO_OFF	(DMAF_AUD0 | DMAF_AUD1 | DMAF_AUD2 | DMAF_AUD3)
60*4882a593Smuzhiyun #define AMI_AUDIO_8	(DMAF_SETCLR | DMAF_MASTER | DMAF_AUD0 | DMAF_AUD1)
61*4882a593Smuzhiyun #define AMI_AUDIO_14	(AMI_AUDIO_8 | DMAF_AUD2 | DMAF_AUD3)
62*4882a593Smuzhiyun 
63*4882a593Smuzhiyun 
64*4882a593Smuzhiyun     /*
65*4882a593Smuzhiyun      *  Helper pointers for 16(14)-bit sound
66*4882a593Smuzhiyun      */
67*4882a593Smuzhiyun 
68*4882a593Smuzhiyun static int write_sq_block_size_half, write_sq_block_size_quarter;
69*4882a593Smuzhiyun 
70*4882a593Smuzhiyun 
71*4882a593Smuzhiyun /*** Low level stuff *********************************************************/
72*4882a593Smuzhiyun 
73*4882a593Smuzhiyun 
74*4882a593Smuzhiyun static void *AmiAlloc(unsigned int size, gfp_t flags);
75*4882a593Smuzhiyun static void AmiFree(void *obj, unsigned int size);
76*4882a593Smuzhiyun static int AmiIrqInit(void);
77*4882a593Smuzhiyun #ifdef MODULE
78*4882a593Smuzhiyun static void AmiIrqCleanUp(void);
79*4882a593Smuzhiyun #endif
80*4882a593Smuzhiyun static void AmiSilence(void);
81*4882a593Smuzhiyun static void AmiInit(void);
82*4882a593Smuzhiyun static int AmiSetFormat(int format);
83*4882a593Smuzhiyun static int AmiSetVolume(int volume);
84*4882a593Smuzhiyun static int AmiSetTreble(int treble);
85*4882a593Smuzhiyun static void AmiPlayNextFrame(int index);
86*4882a593Smuzhiyun static void AmiPlay(void);
87*4882a593Smuzhiyun static irqreturn_t AmiInterrupt(int irq, void *dummy);
88*4882a593Smuzhiyun 
89*4882a593Smuzhiyun #ifdef CONFIG_HEARTBEAT
90*4882a593Smuzhiyun 
91*4882a593Smuzhiyun     /*
92*4882a593Smuzhiyun      *  Heartbeat interferes with sound since the 7 kHz low-pass filter and the
93*4882a593Smuzhiyun      *  power LED are controlled by the same line.
94*4882a593Smuzhiyun      */
95*4882a593Smuzhiyun 
96*4882a593Smuzhiyun static void (*saved_heartbeat)(int) = NULL;
97*4882a593Smuzhiyun 
disable_heartbeat(void)98*4882a593Smuzhiyun static inline void disable_heartbeat(void)
99*4882a593Smuzhiyun {
100*4882a593Smuzhiyun 	if (mach_heartbeat) {
101*4882a593Smuzhiyun 	    saved_heartbeat = mach_heartbeat;
102*4882a593Smuzhiyun 	    mach_heartbeat = NULL;
103*4882a593Smuzhiyun 	}
104*4882a593Smuzhiyun 	AmiSetTreble(dmasound.treble);
105*4882a593Smuzhiyun }
106*4882a593Smuzhiyun 
enable_heartbeat(void)107*4882a593Smuzhiyun static inline void enable_heartbeat(void)
108*4882a593Smuzhiyun {
109*4882a593Smuzhiyun 	if (saved_heartbeat)
110*4882a593Smuzhiyun 	    mach_heartbeat = saved_heartbeat;
111*4882a593Smuzhiyun }
112*4882a593Smuzhiyun #else /* !CONFIG_HEARTBEAT */
113*4882a593Smuzhiyun #define disable_heartbeat()	do { } while (0)
114*4882a593Smuzhiyun #define enable_heartbeat()	do { } while (0)
115*4882a593Smuzhiyun #endif /* !CONFIG_HEARTBEAT */
116*4882a593Smuzhiyun 
117*4882a593Smuzhiyun 
118*4882a593Smuzhiyun /*** Mid level stuff *********************************************************/
119*4882a593Smuzhiyun 
120*4882a593Smuzhiyun static void AmiMixerInit(void);
121*4882a593Smuzhiyun static int AmiMixerIoctl(u_int cmd, u_long arg);
122*4882a593Smuzhiyun static int AmiWriteSqSetup(void);
123*4882a593Smuzhiyun static int AmiStateInfo(char *buffer, size_t space);
124*4882a593Smuzhiyun 
125*4882a593Smuzhiyun 
126*4882a593Smuzhiyun /*** Translations ************************************************************/
127*4882a593Smuzhiyun 
128*4882a593Smuzhiyun /* ++TeSche: radically changed for new expanding purposes...
129*4882a593Smuzhiyun  *
130*4882a593Smuzhiyun  * These two routines now deal with copying/expanding/translating the samples
131*4882a593Smuzhiyun  * from user space into our buffer at the right frequency. They take care about
132*4882a593Smuzhiyun  * how much data there's actually to read, how much buffer space there is and
133*4882a593Smuzhiyun  * to convert samples into the right frequency/encoding. They will only work on
134*4882a593Smuzhiyun  * complete samples so it may happen they leave some bytes in the input stream
135*4882a593Smuzhiyun  * if the user didn't write a multiple of the current sample size. They both
136*4882a593Smuzhiyun  * return the number of bytes they've used from both streams so you may detect
137*4882a593Smuzhiyun  * such a situation. Luckily all programs should be able to cope with that.
138*4882a593Smuzhiyun  *
139*4882a593Smuzhiyun  * I think I've optimized anything as far as one can do in plain C, all
140*4882a593Smuzhiyun  * variables should fit in registers and the loops are really short. There's
141*4882a593Smuzhiyun  * one loop for every possible situation. Writing a more generalized and thus
142*4882a593Smuzhiyun  * parameterized loop would only produce slower code. Feel free to optimize
143*4882a593Smuzhiyun  * this in assembler if you like. :)
144*4882a593Smuzhiyun  *
145*4882a593Smuzhiyun  * I think these routines belong here because they're not yet really hardware
146*4882a593Smuzhiyun  * independent, especially the fact that the Falcon can play 16bit samples
147*4882a593Smuzhiyun  * only in stereo is hardcoded in both of them!
148*4882a593Smuzhiyun  *
149*4882a593Smuzhiyun  * ++geert: split in even more functions (one per format)
150*4882a593Smuzhiyun  */
151*4882a593Smuzhiyun 
152*4882a593Smuzhiyun 
153*4882a593Smuzhiyun     /*
154*4882a593Smuzhiyun      *  Native format
155*4882a593Smuzhiyun      */
156*4882a593Smuzhiyun 
ami_ct_s8(const u_char __user * userPtr,size_t userCount,u_char frame[],ssize_t * frameUsed,ssize_t frameLeft)157*4882a593Smuzhiyun static ssize_t ami_ct_s8(const u_char __user *userPtr, size_t userCount,
158*4882a593Smuzhiyun 			 u_char frame[], ssize_t *frameUsed, ssize_t frameLeft)
159*4882a593Smuzhiyun {
160*4882a593Smuzhiyun 	ssize_t count, used;
161*4882a593Smuzhiyun 
162*4882a593Smuzhiyun 	if (!dmasound.soft.stereo) {
163*4882a593Smuzhiyun 		void *p = &frame[*frameUsed];
164*4882a593Smuzhiyun 		count = min_t(unsigned long, userCount, frameLeft) & ~1;
165*4882a593Smuzhiyun 		used = count;
166*4882a593Smuzhiyun 		if (copy_from_user(p, userPtr, count))
167*4882a593Smuzhiyun 			return -EFAULT;
168*4882a593Smuzhiyun 	} else {
169*4882a593Smuzhiyun 		u_char *left = &frame[*frameUsed>>1];
170*4882a593Smuzhiyun 		u_char *right = left+write_sq_block_size_half;
171*4882a593Smuzhiyun 		count = min_t(unsigned long, userCount, frameLeft)>>1 & ~1;
172*4882a593Smuzhiyun 		used = count*2;
173*4882a593Smuzhiyun 		while (count > 0) {
174*4882a593Smuzhiyun 			if (get_user(*left++, userPtr++)
175*4882a593Smuzhiyun 			    || get_user(*right++, userPtr++))
176*4882a593Smuzhiyun 				return -EFAULT;
177*4882a593Smuzhiyun 			count--;
178*4882a593Smuzhiyun 		}
179*4882a593Smuzhiyun 	}
180*4882a593Smuzhiyun 	*frameUsed += used;
181*4882a593Smuzhiyun 	return used;
182*4882a593Smuzhiyun }
183*4882a593Smuzhiyun 
184*4882a593Smuzhiyun 
185*4882a593Smuzhiyun     /*
186*4882a593Smuzhiyun      *  Copy and convert 8 bit data
187*4882a593Smuzhiyun      */
188*4882a593Smuzhiyun 
189*4882a593Smuzhiyun #define GENERATE_AMI_CT8(funcname, convsample)				\
190*4882a593Smuzhiyun static ssize_t funcname(const u_char __user *userPtr, size_t userCount,	\
191*4882a593Smuzhiyun 			u_char frame[], ssize_t *frameUsed,		\
192*4882a593Smuzhiyun 			ssize_t frameLeft)				\
193*4882a593Smuzhiyun {									\
194*4882a593Smuzhiyun 	ssize_t count, used;						\
195*4882a593Smuzhiyun 									\
196*4882a593Smuzhiyun 	if (!dmasound.soft.stereo) {					\
197*4882a593Smuzhiyun 		u_char *p = &frame[*frameUsed];				\
198*4882a593Smuzhiyun 		count = min_t(size_t, userCount, frameLeft) & ~1;	\
199*4882a593Smuzhiyun 		used = count;						\
200*4882a593Smuzhiyun 		while (count > 0) {					\
201*4882a593Smuzhiyun 			u_char data;					\
202*4882a593Smuzhiyun 			if (get_user(data, userPtr++))			\
203*4882a593Smuzhiyun 				return -EFAULT;				\
204*4882a593Smuzhiyun 			*p++ = convsample(data);			\
205*4882a593Smuzhiyun 			count--;					\
206*4882a593Smuzhiyun 		}							\
207*4882a593Smuzhiyun 	} else {							\
208*4882a593Smuzhiyun 		u_char *left = &frame[*frameUsed>>1];			\
209*4882a593Smuzhiyun 		u_char *right = left+write_sq_block_size_half;		\
210*4882a593Smuzhiyun 		count = min_t(size_t, userCount, frameLeft)>>1 & ~1;	\
211*4882a593Smuzhiyun 		used = count*2;						\
212*4882a593Smuzhiyun 		while (count > 0) {					\
213*4882a593Smuzhiyun 			u_char data;					\
214*4882a593Smuzhiyun 			if (get_user(data, userPtr++))			\
215*4882a593Smuzhiyun 				return -EFAULT;				\
216*4882a593Smuzhiyun 			*left++ = convsample(data);			\
217*4882a593Smuzhiyun 			if (get_user(data, userPtr++))			\
218*4882a593Smuzhiyun 				return -EFAULT;				\
219*4882a593Smuzhiyun 			*right++ = convsample(data);			\
220*4882a593Smuzhiyun 			count--;					\
221*4882a593Smuzhiyun 		}							\
222*4882a593Smuzhiyun 	}								\
223*4882a593Smuzhiyun 	*frameUsed += used;						\
224*4882a593Smuzhiyun 	return used;							\
225*4882a593Smuzhiyun }
226*4882a593Smuzhiyun 
227*4882a593Smuzhiyun #define AMI_CT_ULAW(x)	(dmasound_ulaw2dma8[(x)])
228*4882a593Smuzhiyun #define AMI_CT_ALAW(x)	(dmasound_alaw2dma8[(x)])
229*4882a593Smuzhiyun #define AMI_CT_U8(x)	((x) ^ 0x80)
230*4882a593Smuzhiyun 
231*4882a593Smuzhiyun GENERATE_AMI_CT8(ami_ct_ulaw, AMI_CT_ULAW)
232*4882a593Smuzhiyun GENERATE_AMI_CT8(ami_ct_alaw, AMI_CT_ALAW)
233*4882a593Smuzhiyun GENERATE_AMI_CT8(ami_ct_u8, AMI_CT_U8)
234*4882a593Smuzhiyun 
235*4882a593Smuzhiyun 
236*4882a593Smuzhiyun     /*
237*4882a593Smuzhiyun      *  Copy and convert 16 bit data
238*4882a593Smuzhiyun      */
239*4882a593Smuzhiyun 
240*4882a593Smuzhiyun #define GENERATE_AMI_CT_16(funcname, convsample)			\
241*4882a593Smuzhiyun static ssize_t funcname(const u_char __user *userPtr, size_t userCount,	\
242*4882a593Smuzhiyun 			u_char frame[], ssize_t *frameUsed,		\
243*4882a593Smuzhiyun 			ssize_t frameLeft)				\
244*4882a593Smuzhiyun {									\
245*4882a593Smuzhiyun 	const u_short __user *ptr = (const u_short __user *)userPtr;	\
246*4882a593Smuzhiyun 	ssize_t count, used;						\
247*4882a593Smuzhiyun 	u_short data;							\
248*4882a593Smuzhiyun 									\
249*4882a593Smuzhiyun 	if (!dmasound.soft.stereo) {					\
250*4882a593Smuzhiyun 		u_char *high = &frame[*frameUsed>>1];			\
251*4882a593Smuzhiyun 		u_char *low = high+write_sq_block_size_half;		\
252*4882a593Smuzhiyun 		count = min_t(size_t, userCount, frameLeft)>>1 & ~1;	\
253*4882a593Smuzhiyun 		used = count*2;						\
254*4882a593Smuzhiyun 		while (count > 0) {					\
255*4882a593Smuzhiyun 			if (get_user(data, ptr++))			\
256*4882a593Smuzhiyun 				return -EFAULT;				\
257*4882a593Smuzhiyun 			data = convsample(data);			\
258*4882a593Smuzhiyun 			*high++ = data>>8;				\
259*4882a593Smuzhiyun 			*low++ = (data>>2) & 0x3f;			\
260*4882a593Smuzhiyun 			count--;					\
261*4882a593Smuzhiyun 		}							\
262*4882a593Smuzhiyun 	} else {							\
263*4882a593Smuzhiyun 		u_char *lefth = &frame[*frameUsed>>2];			\
264*4882a593Smuzhiyun 		u_char *leftl = lefth+write_sq_block_size_quarter;	\
265*4882a593Smuzhiyun 		u_char *righth = lefth+write_sq_block_size_half;	\
266*4882a593Smuzhiyun 		u_char *rightl = righth+write_sq_block_size_quarter;	\
267*4882a593Smuzhiyun 		count = min_t(size_t, userCount, frameLeft)>>2 & ~1;	\
268*4882a593Smuzhiyun 		used = count*4;						\
269*4882a593Smuzhiyun 		while (count > 0) {					\
270*4882a593Smuzhiyun 			if (get_user(data, ptr++))			\
271*4882a593Smuzhiyun 				return -EFAULT;				\
272*4882a593Smuzhiyun 			data = convsample(data);			\
273*4882a593Smuzhiyun 			*lefth++ = data>>8;				\
274*4882a593Smuzhiyun 			*leftl++ = (data>>2) & 0x3f;			\
275*4882a593Smuzhiyun 			if (get_user(data, ptr++))			\
276*4882a593Smuzhiyun 				return -EFAULT;				\
277*4882a593Smuzhiyun 			data = convsample(data);			\
278*4882a593Smuzhiyun 			*righth++ = data>>8;				\
279*4882a593Smuzhiyun 			*rightl++ = (data>>2) & 0x3f;			\
280*4882a593Smuzhiyun 			count--;					\
281*4882a593Smuzhiyun 		}							\
282*4882a593Smuzhiyun 	}								\
283*4882a593Smuzhiyun 	*frameUsed += used;						\
284*4882a593Smuzhiyun 	return used;							\
285*4882a593Smuzhiyun }
286*4882a593Smuzhiyun 
287*4882a593Smuzhiyun #define AMI_CT_S16BE(x)	(x)
288*4882a593Smuzhiyun #define AMI_CT_U16BE(x)	((x) ^ 0x8000)
289*4882a593Smuzhiyun #define AMI_CT_S16LE(x)	(le2be16((x)))
290*4882a593Smuzhiyun #define AMI_CT_U16LE(x)	(le2be16((x)) ^ 0x8000)
291*4882a593Smuzhiyun 
292*4882a593Smuzhiyun GENERATE_AMI_CT_16(ami_ct_s16be, AMI_CT_S16BE)
293*4882a593Smuzhiyun GENERATE_AMI_CT_16(ami_ct_u16be, AMI_CT_U16BE)
294*4882a593Smuzhiyun GENERATE_AMI_CT_16(ami_ct_s16le, AMI_CT_S16LE)
295*4882a593Smuzhiyun GENERATE_AMI_CT_16(ami_ct_u16le, AMI_CT_U16LE)
296*4882a593Smuzhiyun 
297*4882a593Smuzhiyun 
298*4882a593Smuzhiyun static TRANS transAmiga = {
299*4882a593Smuzhiyun 	.ct_ulaw	= ami_ct_ulaw,
300*4882a593Smuzhiyun 	.ct_alaw	= ami_ct_alaw,
301*4882a593Smuzhiyun 	.ct_s8		= ami_ct_s8,
302*4882a593Smuzhiyun 	.ct_u8		= ami_ct_u8,
303*4882a593Smuzhiyun 	.ct_s16be	= ami_ct_s16be,
304*4882a593Smuzhiyun 	.ct_u16be	= ami_ct_u16be,
305*4882a593Smuzhiyun 	.ct_s16le	= ami_ct_s16le,
306*4882a593Smuzhiyun 	.ct_u16le	= ami_ct_u16le,
307*4882a593Smuzhiyun };
308*4882a593Smuzhiyun 
309*4882a593Smuzhiyun /*** Low level stuff *********************************************************/
310*4882a593Smuzhiyun 
StopDMA(void)311*4882a593Smuzhiyun static inline void StopDMA(void)
312*4882a593Smuzhiyun {
313*4882a593Smuzhiyun 	custom.aud[0].audvol = custom.aud[1].audvol = 0;
314*4882a593Smuzhiyun 	custom.aud[2].audvol = custom.aud[3].audvol = 0;
315*4882a593Smuzhiyun 	custom.dmacon = AMI_AUDIO_OFF;
316*4882a593Smuzhiyun 	enable_heartbeat();
317*4882a593Smuzhiyun }
318*4882a593Smuzhiyun 
AmiAlloc(unsigned int size,gfp_t flags)319*4882a593Smuzhiyun static void *AmiAlloc(unsigned int size, gfp_t flags)
320*4882a593Smuzhiyun {
321*4882a593Smuzhiyun 	return amiga_chip_alloc((long)size, "dmasound [Paula]");
322*4882a593Smuzhiyun }
323*4882a593Smuzhiyun 
AmiFree(void * obj,unsigned int size)324*4882a593Smuzhiyun static void AmiFree(void *obj, unsigned int size)
325*4882a593Smuzhiyun {
326*4882a593Smuzhiyun 	amiga_chip_free (obj);
327*4882a593Smuzhiyun }
328*4882a593Smuzhiyun 
AmiIrqInit(void)329*4882a593Smuzhiyun static int __init AmiIrqInit(void)
330*4882a593Smuzhiyun {
331*4882a593Smuzhiyun 	/* turn off DMA for audio channels */
332*4882a593Smuzhiyun 	StopDMA();
333*4882a593Smuzhiyun 
334*4882a593Smuzhiyun 	/* Register interrupt handler. */
335*4882a593Smuzhiyun 	if (request_irq(IRQ_AMIGA_AUD0, AmiInterrupt, 0, "DMA sound",
336*4882a593Smuzhiyun 			AmiInterrupt))
337*4882a593Smuzhiyun 		return 0;
338*4882a593Smuzhiyun 	return 1;
339*4882a593Smuzhiyun }
340*4882a593Smuzhiyun 
341*4882a593Smuzhiyun #ifdef MODULE
AmiIrqCleanUp(void)342*4882a593Smuzhiyun static void AmiIrqCleanUp(void)
343*4882a593Smuzhiyun {
344*4882a593Smuzhiyun 	/* turn off DMA for audio channels */
345*4882a593Smuzhiyun 	StopDMA();
346*4882a593Smuzhiyun 	/* release the interrupt */
347*4882a593Smuzhiyun 	free_irq(IRQ_AMIGA_AUD0, AmiInterrupt);
348*4882a593Smuzhiyun }
349*4882a593Smuzhiyun #endif /* MODULE */
350*4882a593Smuzhiyun 
AmiSilence(void)351*4882a593Smuzhiyun static void AmiSilence(void)
352*4882a593Smuzhiyun {
353*4882a593Smuzhiyun 	/* turn off DMA for audio channels */
354*4882a593Smuzhiyun 	StopDMA();
355*4882a593Smuzhiyun }
356*4882a593Smuzhiyun 
357*4882a593Smuzhiyun 
AmiInit(void)358*4882a593Smuzhiyun static void AmiInit(void)
359*4882a593Smuzhiyun {
360*4882a593Smuzhiyun 	int period, i;
361*4882a593Smuzhiyun 
362*4882a593Smuzhiyun 	AmiSilence();
363*4882a593Smuzhiyun 
364*4882a593Smuzhiyun 	if (dmasound.soft.speed)
365*4882a593Smuzhiyun 		period = amiga_colorclock/dmasound.soft.speed-1;
366*4882a593Smuzhiyun 	else
367*4882a593Smuzhiyun 		period = amiga_audio_min_period;
368*4882a593Smuzhiyun 	dmasound.hard = dmasound.soft;
369*4882a593Smuzhiyun 	dmasound.trans_write = &transAmiga;
370*4882a593Smuzhiyun 
371*4882a593Smuzhiyun 	if (period < amiga_audio_min_period) {
372*4882a593Smuzhiyun 		/* we would need to squeeze the sound, but we won't do that */
373*4882a593Smuzhiyun 		period = amiga_audio_min_period;
374*4882a593Smuzhiyun 	} else if (period > 65535) {
375*4882a593Smuzhiyun 		period = 65535;
376*4882a593Smuzhiyun 	}
377*4882a593Smuzhiyun 	dmasound.hard.speed = amiga_colorclock/(period+1);
378*4882a593Smuzhiyun 
379*4882a593Smuzhiyun 	for (i = 0; i < 4; i++)
380*4882a593Smuzhiyun 		custom.aud[i].audper = period;
381*4882a593Smuzhiyun 	amiga_audio_period = period;
382*4882a593Smuzhiyun }
383*4882a593Smuzhiyun 
384*4882a593Smuzhiyun 
AmiSetFormat(int format)385*4882a593Smuzhiyun static int AmiSetFormat(int format)
386*4882a593Smuzhiyun {
387*4882a593Smuzhiyun 	int size;
388*4882a593Smuzhiyun 
389*4882a593Smuzhiyun 	/* Amiga sound DMA supports 8bit and 16bit (pseudo 14 bit) modes */
390*4882a593Smuzhiyun 
391*4882a593Smuzhiyun 	switch (format) {
392*4882a593Smuzhiyun 	case AFMT_QUERY:
393*4882a593Smuzhiyun 		return dmasound.soft.format;
394*4882a593Smuzhiyun 	case AFMT_MU_LAW:
395*4882a593Smuzhiyun 	case AFMT_A_LAW:
396*4882a593Smuzhiyun 	case AFMT_U8:
397*4882a593Smuzhiyun 	case AFMT_S8:
398*4882a593Smuzhiyun 		size = 8;
399*4882a593Smuzhiyun 		break;
400*4882a593Smuzhiyun 	case AFMT_S16_BE:
401*4882a593Smuzhiyun 	case AFMT_U16_BE:
402*4882a593Smuzhiyun 	case AFMT_S16_LE:
403*4882a593Smuzhiyun 	case AFMT_U16_LE:
404*4882a593Smuzhiyun 		size = 16;
405*4882a593Smuzhiyun 		break;
406*4882a593Smuzhiyun 	default: /* :-) */
407*4882a593Smuzhiyun 		size = 8;
408*4882a593Smuzhiyun 		format = AFMT_S8;
409*4882a593Smuzhiyun 	}
410*4882a593Smuzhiyun 
411*4882a593Smuzhiyun 	dmasound.soft.format = format;
412*4882a593Smuzhiyun 	dmasound.soft.size = size;
413*4882a593Smuzhiyun 	if (dmasound.minDev == SND_DEV_DSP) {
414*4882a593Smuzhiyun 		dmasound.dsp.format = format;
415*4882a593Smuzhiyun 		dmasound.dsp.size = dmasound.soft.size;
416*4882a593Smuzhiyun 	}
417*4882a593Smuzhiyun 	AmiInit();
418*4882a593Smuzhiyun 
419*4882a593Smuzhiyun 	return format;
420*4882a593Smuzhiyun }
421*4882a593Smuzhiyun 
422*4882a593Smuzhiyun 
423*4882a593Smuzhiyun #define VOLUME_VOXWARE_TO_AMI(v) \
424*4882a593Smuzhiyun 	(((v) < 0) ? 0 : ((v) > 100) ? 64 : ((v) * 64)/100)
425*4882a593Smuzhiyun #define VOLUME_AMI_TO_VOXWARE(v) ((v)*100/64)
426*4882a593Smuzhiyun 
AmiSetVolume(int volume)427*4882a593Smuzhiyun static int AmiSetVolume(int volume)
428*4882a593Smuzhiyun {
429*4882a593Smuzhiyun 	dmasound.volume_left = VOLUME_VOXWARE_TO_AMI(volume & 0xff);
430*4882a593Smuzhiyun 	custom.aud[0].audvol = dmasound.volume_left;
431*4882a593Smuzhiyun 	dmasound.volume_right = VOLUME_VOXWARE_TO_AMI((volume & 0xff00) >> 8);
432*4882a593Smuzhiyun 	custom.aud[1].audvol = dmasound.volume_right;
433*4882a593Smuzhiyun 	if (dmasound.hard.size == 16) {
434*4882a593Smuzhiyun 		if (dmasound.volume_left == 64 && dmasound.volume_right == 64) {
435*4882a593Smuzhiyun 			custom.aud[2].audvol = 1;
436*4882a593Smuzhiyun 			custom.aud[3].audvol = 1;
437*4882a593Smuzhiyun 		} else {
438*4882a593Smuzhiyun 			custom.aud[2].audvol = 0;
439*4882a593Smuzhiyun 			custom.aud[3].audvol = 0;
440*4882a593Smuzhiyun 		}
441*4882a593Smuzhiyun 	}
442*4882a593Smuzhiyun 	return VOLUME_AMI_TO_VOXWARE(dmasound.volume_left) |
443*4882a593Smuzhiyun 	       (VOLUME_AMI_TO_VOXWARE(dmasound.volume_right) << 8);
444*4882a593Smuzhiyun }
445*4882a593Smuzhiyun 
AmiSetTreble(int treble)446*4882a593Smuzhiyun static int AmiSetTreble(int treble)
447*4882a593Smuzhiyun {
448*4882a593Smuzhiyun 	dmasound.treble = treble;
449*4882a593Smuzhiyun 	if (treble < 50)
450*4882a593Smuzhiyun 		ciaa.pra &= ~0x02;
451*4882a593Smuzhiyun 	else
452*4882a593Smuzhiyun 		ciaa.pra |= 0x02;
453*4882a593Smuzhiyun 	return treble;
454*4882a593Smuzhiyun }
455*4882a593Smuzhiyun 
456*4882a593Smuzhiyun 
457*4882a593Smuzhiyun #define AMI_PLAY_LOADED		1
458*4882a593Smuzhiyun #define AMI_PLAY_PLAYING	2
459*4882a593Smuzhiyun #define AMI_PLAY_MASK		3
460*4882a593Smuzhiyun 
461*4882a593Smuzhiyun 
AmiPlayNextFrame(int index)462*4882a593Smuzhiyun static void AmiPlayNextFrame(int index)
463*4882a593Smuzhiyun {
464*4882a593Smuzhiyun 	u_char *start, *ch0, *ch1, *ch2, *ch3;
465*4882a593Smuzhiyun 	u_long size;
466*4882a593Smuzhiyun 
467*4882a593Smuzhiyun 	/* used by AmiPlay() if all doubts whether there really is something
468*4882a593Smuzhiyun 	 * to be played are already wiped out.
469*4882a593Smuzhiyun 	 */
470*4882a593Smuzhiyun 	start = write_sq.buffers[write_sq.front];
471*4882a593Smuzhiyun 	size = (write_sq.count == index ? write_sq.rear_size
472*4882a593Smuzhiyun 					: write_sq.block_size)>>1;
473*4882a593Smuzhiyun 
474*4882a593Smuzhiyun 	if (dmasound.hard.stereo) {
475*4882a593Smuzhiyun 		ch0 = start;
476*4882a593Smuzhiyun 		ch1 = start+write_sq_block_size_half;
477*4882a593Smuzhiyun 		size >>= 1;
478*4882a593Smuzhiyun 	} else {
479*4882a593Smuzhiyun 		ch0 = start;
480*4882a593Smuzhiyun 		ch1 = start;
481*4882a593Smuzhiyun 	}
482*4882a593Smuzhiyun 
483*4882a593Smuzhiyun 	disable_heartbeat();
484*4882a593Smuzhiyun 	custom.aud[0].audvol = dmasound.volume_left;
485*4882a593Smuzhiyun 	custom.aud[1].audvol = dmasound.volume_right;
486*4882a593Smuzhiyun 	if (dmasound.hard.size == 8) {
487*4882a593Smuzhiyun 		custom.aud[0].audlc = (u_short *)ZTWO_PADDR(ch0);
488*4882a593Smuzhiyun 		custom.aud[0].audlen = size;
489*4882a593Smuzhiyun 		custom.aud[1].audlc = (u_short *)ZTWO_PADDR(ch1);
490*4882a593Smuzhiyun 		custom.aud[1].audlen = size;
491*4882a593Smuzhiyun 		custom.dmacon = AMI_AUDIO_8;
492*4882a593Smuzhiyun 	} else {
493*4882a593Smuzhiyun 		size >>= 1;
494*4882a593Smuzhiyun 		custom.aud[0].audlc = (u_short *)ZTWO_PADDR(ch0);
495*4882a593Smuzhiyun 		custom.aud[0].audlen = size;
496*4882a593Smuzhiyun 		custom.aud[1].audlc = (u_short *)ZTWO_PADDR(ch1);
497*4882a593Smuzhiyun 		custom.aud[1].audlen = size;
498*4882a593Smuzhiyun 		if (dmasound.volume_left == 64 && dmasound.volume_right == 64) {
499*4882a593Smuzhiyun 			/* We can play pseudo 14-bit only with the maximum volume */
500*4882a593Smuzhiyun 			ch3 = ch0+write_sq_block_size_quarter;
501*4882a593Smuzhiyun 			ch2 = ch1+write_sq_block_size_quarter;
502*4882a593Smuzhiyun 			custom.aud[2].audvol = 1;  /* we are being affected by the beeps */
503*4882a593Smuzhiyun 			custom.aud[3].audvol = 1;  /* restoring volume here helps a bit */
504*4882a593Smuzhiyun 			custom.aud[2].audlc = (u_short *)ZTWO_PADDR(ch2);
505*4882a593Smuzhiyun 			custom.aud[2].audlen = size;
506*4882a593Smuzhiyun 			custom.aud[3].audlc = (u_short *)ZTWO_PADDR(ch3);
507*4882a593Smuzhiyun 			custom.aud[3].audlen = size;
508*4882a593Smuzhiyun 			custom.dmacon = AMI_AUDIO_14;
509*4882a593Smuzhiyun 		} else {
510*4882a593Smuzhiyun 			custom.aud[2].audvol = 0;
511*4882a593Smuzhiyun 			custom.aud[3].audvol = 0;
512*4882a593Smuzhiyun 			custom.dmacon = AMI_AUDIO_8;
513*4882a593Smuzhiyun 		}
514*4882a593Smuzhiyun 	}
515*4882a593Smuzhiyun 	write_sq.front = (write_sq.front+1) % write_sq.max_count;
516*4882a593Smuzhiyun 	write_sq.active |= AMI_PLAY_LOADED;
517*4882a593Smuzhiyun }
518*4882a593Smuzhiyun 
519*4882a593Smuzhiyun 
AmiPlay(void)520*4882a593Smuzhiyun static void AmiPlay(void)
521*4882a593Smuzhiyun {
522*4882a593Smuzhiyun 	int minframes = 1;
523*4882a593Smuzhiyun 
524*4882a593Smuzhiyun 	custom.intena = IF_AUD0;
525*4882a593Smuzhiyun 
526*4882a593Smuzhiyun 	if (write_sq.active & AMI_PLAY_LOADED) {
527*4882a593Smuzhiyun 		/* There's already a frame loaded */
528*4882a593Smuzhiyun 		custom.intena = IF_SETCLR | IF_AUD0;
529*4882a593Smuzhiyun 		return;
530*4882a593Smuzhiyun 	}
531*4882a593Smuzhiyun 
532*4882a593Smuzhiyun 	if (write_sq.active & AMI_PLAY_PLAYING)
533*4882a593Smuzhiyun 		/* Increase threshold: frame 1 is already being played */
534*4882a593Smuzhiyun 		minframes = 2;
535*4882a593Smuzhiyun 
536*4882a593Smuzhiyun 	if (write_sq.count < minframes) {
537*4882a593Smuzhiyun 		/* Nothing to do */
538*4882a593Smuzhiyun 		custom.intena = IF_SETCLR | IF_AUD0;
539*4882a593Smuzhiyun 		return;
540*4882a593Smuzhiyun 	}
541*4882a593Smuzhiyun 
542*4882a593Smuzhiyun 	if (write_sq.count <= minframes &&
543*4882a593Smuzhiyun 	    write_sq.rear_size < write_sq.block_size && !write_sq.syncing) {
544*4882a593Smuzhiyun 		/* hmmm, the only existing frame is not
545*4882a593Smuzhiyun 		 * yet filled and we're not syncing?
546*4882a593Smuzhiyun 		 */
547*4882a593Smuzhiyun 		custom.intena = IF_SETCLR | IF_AUD0;
548*4882a593Smuzhiyun 		return;
549*4882a593Smuzhiyun 	}
550*4882a593Smuzhiyun 
551*4882a593Smuzhiyun 	AmiPlayNextFrame(minframes);
552*4882a593Smuzhiyun 
553*4882a593Smuzhiyun 	custom.intena = IF_SETCLR | IF_AUD0;
554*4882a593Smuzhiyun }
555*4882a593Smuzhiyun 
556*4882a593Smuzhiyun 
AmiInterrupt(int irq,void * dummy)557*4882a593Smuzhiyun static irqreturn_t AmiInterrupt(int irq, void *dummy)
558*4882a593Smuzhiyun {
559*4882a593Smuzhiyun 	int minframes = 1;
560*4882a593Smuzhiyun 
561*4882a593Smuzhiyun 	custom.intena = IF_AUD0;
562*4882a593Smuzhiyun 
563*4882a593Smuzhiyun 	if (!write_sq.active) {
564*4882a593Smuzhiyun 		/* Playing was interrupted and sq_reset() has already cleared
565*4882a593Smuzhiyun 		 * the sq variables, so better don't do anything here.
566*4882a593Smuzhiyun 		 */
567*4882a593Smuzhiyun 		WAKE_UP(write_sq.sync_queue);
568*4882a593Smuzhiyun 		return IRQ_HANDLED;
569*4882a593Smuzhiyun 	}
570*4882a593Smuzhiyun 
571*4882a593Smuzhiyun 	if (write_sq.active & AMI_PLAY_PLAYING) {
572*4882a593Smuzhiyun 		/* We've just finished a frame */
573*4882a593Smuzhiyun 		write_sq.count--;
574*4882a593Smuzhiyun 		WAKE_UP(write_sq.action_queue);
575*4882a593Smuzhiyun 	}
576*4882a593Smuzhiyun 
577*4882a593Smuzhiyun 	if (write_sq.active & AMI_PLAY_LOADED)
578*4882a593Smuzhiyun 		/* Increase threshold: frame 1 is already being played */
579*4882a593Smuzhiyun 		minframes = 2;
580*4882a593Smuzhiyun 
581*4882a593Smuzhiyun 	/* Shift the flags */
582*4882a593Smuzhiyun 	write_sq.active = (write_sq.active<<1) & AMI_PLAY_MASK;
583*4882a593Smuzhiyun 
584*4882a593Smuzhiyun 	if (!write_sq.active)
585*4882a593Smuzhiyun 		/* No frame is playing, disable audio DMA */
586*4882a593Smuzhiyun 		StopDMA();
587*4882a593Smuzhiyun 
588*4882a593Smuzhiyun 	custom.intena = IF_SETCLR | IF_AUD0;
589*4882a593Smuzhiyun 
590*4882a593Smuzhiyun 	if (write_sq.count >= minframes)
591*4882a593Smuzhiyun 		/* Try to play the next frame */
592*4882a593Smuzhiyun 		AmiPlay();
593*4882a593Smuzhiyun 
594*4882a593Smuzhiyun 	if (!write_sq.active)
595*4882a593Smuzhiyun 		/* Nothing to play anymore.
596*4882a593Smuzhiyun 		   Wake up a process waiting for audio output to drain. */
597*4882a593Smuzhiyun 		WAKE_UP(write_sq.sync_queue);
598*4882a593Smuzhiyun 	return IRQ_HANDLED;
599*4882a593Smuzhiyun }
600*4882a593Smuzhiyun 
601*4882a593Smuzhiyun /*** Mid level stuff *********************************************************/
602*4882a593Smuzhiyun 
603*4882a593Smuzhiyun 
604*4882a593Smuzhiyun /*
605*4882a593Smuzhiyun  * /dev/mixer abstraction
606*4882a593Smuzhiyun  */
607*4882a593Smuzhiyun 
AmiMixerInit(void)608*4882a593Smuzhiyun static void __init AmiMixerInit(void)
609*4882a593Smuzhiyun {
610*4882a593Smuzhiyun 	dmasound.volume_left = 64;
611*4882a593Smuzhiyun 	dmasound.volume_right = 64;
612*4882a593Smuzhiyun 	custom.aud[0].audvol = dmasound.volume_left;
613*4882a593Smuzhiyun 	custom.aud[3].audvol = 1;	/* For pseudo 14bit */
614*4882a593Smuzhiyun 	custom.aud[1].audvol = dmasound.volume_right;
615*4882a593Smuzhiyun 	custom.aud[2].audvol = 1;	/* For pseudo 14bit */
616*4882a593Smuzhiyun 	dmasound.treble = 50;
617*4882a593Smuzhiyun }
618*4882a593Smuzhiyun 
AmiMixerIoctl(u_int cmd,u_long arg)619*4882a593Smuzhiyun static int AmiMixerIoctl(u_int cmd, u_long arg)
620*4882a593Smuzhiyun {
621*4882a593Smuzhiyun 	int data;
622*4882a593Smuzhiyun 	switch (cmd) {
623*4882a593Smuzhiyun 	    case SOUND_MIXER_READ_DEVMASK:
624*4882a593Smuzhiyun 		    return IOCTL_OUT(arg, SOUND_MASK_VOLUME | SOUND_MASK_TREBLE);
625*4882a593Smuzhiyun 	    case SOUND_MIXER_READ_RECMASK:
626*4882a593Smuzhiyun 		    return IOCTL_OUT(arg, 0);
627*4882a593Smuzhiyun 	    case SOUND_MIXER_READ_STEREODEVS:
628*4882a593Smuzhiyun 		    return IOCTL_OUT(arg, SOUND_MASK_VOLUME);
629*4882a593Smuzhiyun 	    case SOUND_MIXER_READ_VOLUME:
630*4882a593Smuzhiyun 		    return IOCTL_OUT(arg,
631*4882a593Smuzhiyun 			    VOLUME_AMI_TO_VOXWARE(dmasound.volume_left) |
632*4882a593Smuzhiyun 			    VOLUME_AMI_TO_VOXWARE(dmasound.volume_right) << 8);
633*4882a593Smuzhiyun 	    case SOUND_MIXER_WRITE_VOLUME:
634*4882a593Smuzhiyun 		    IOCTL_IN(arg, data);
635*4882a593Smuzhiyun 		    return IOCTL_OUT(arg, dmasound_set_volume(data));
636*4882a593Smuzhiyun 	    case SOUND_MIXER_READ_TREBLE:
637*4882a593Smuzhiyun 		    return IOCTL_OUT(arg, dmasound.treble);
638*4882a593Smuzhiyun 	    case SOUND_MIXER_WRITE_TREBLE:
639*4882a593Smuzhiyun 		    IOCTL_IN(arg, data);
640*4882a593Smuzhiyun 		    return IOCTL_OUT(arg, dmasound_set_treble(data));
641*4882a593Smuzhiyun 	}
642*4882a593Smuzhiyun 	return -EINVAL;
643*4882a593Smuzhiyun }
644*4882a593Smuzhiyun 
645*4882a593Smuzhiyun 
AmiWriteSqSetup(void)646*4882a593Smuzhiyun static int AmiWriteSqSetup(void)
647*4882a593Smuzhiyun {
648*4882a593Smuzhiyun 	write_sq_block_size_half = write_sq.block_size>>1;
649*4882a593Smuzhiyun 	write_sq_block_size_quarter = write_sq_block_size_half>>1;
650*4882a593Smuzhiyun 	return 0;
651*4882a593Smuzhiyun }
652*4882a593Smuzhiyun 
653*4882a593Smuzhiyun 
AmiStateInfo(char * buffer,size_t space)654*4882a593Smuzhiyun static int AmiStateInfo(char *buffer, size_t space)
655*4882a593Smuzhiyun {
656*4882a593Smuzhiyun 	int len = 0;
657*4882a593Smuzhiyun 	len += sprintf(buffer+len, "\tsound.volume_left = %d [0...64]\n",
658*4882a593Smuzhiyun 		       dmasound.volume_left);
659*4882a593Smuzhiyun 	len += sprintf(buffer+len, "\tsound.volume_right = %d [0...64]\n",
660*4882a593Smuzhiyun 		       dmasound.volume_right);
661*4882a593Smuzhiyun 	if (len >= space) {
662*4882a593Smuzhiyun 		printk(KERN_ERR "dmasound_paula: overflowed state buffer alloc.\n") ;
663*4882a593Smuzhiyun 		len = space ;
664*4882a593Smuzhiyun 	}
665*4882a593Smuzhiyun 	return len;
666*4882a593Smuzhiyun }
667*4882a593Smuzhiyun 
668*4882a593Smuzhiyun 
669*4882a593Smuzhiyun /*** Machine definitions *****************************************************/
670*4882a593Smuzhiyun 
671*4882a593Smuzhiyun static SETTINGS def_hard = {
672*4882a593Smuzhiyun 	.format	= AFMT_S8,
673*4882a593Smuzhiyun 	.stereo	= 0,
674*4882a593Smuzhiyun 	.size	= 8,
675*4882a593Smuzhiyun 	.speed	= 8000
676*4882a593Smuzhiyun } ;
677*4882a593Smuzhiyun 
678*4882a593Smuzhiyun static SETTINGS def_soft = {
679*4882a593Smuzhiyun 	.format	= AFMT_U8,
680*4882a593Smuzhiyun 	.stereo	= 0,
681*4882a593Smuzhiyun 	.size	= 8,
682*4882a593Smuzhiyun 	.speed	= 8000
683*4882a593Smuzhiyun } ;
684*4882a593Smuzhiyun 
685*4882a593Smuzhiyun static MACHINE machAmiga = {
686*4882a593Smuzhiyun 	.name		= "Amiga",
687*4882a593Smuzhiyun 	.name2		= "AMIGA",
688*4882a593Smuzhiyun 	.owner		= THIS_MODULE,
689*4882a593Smuzhiyun 	.dma_alloc	= AmiAlloc,
690*4882a593Smuzhiyun 	.dma_free	= AmiFree,
691*4882a593Smuzhiyun 	.irqinit	= AmiIrqInit,
692*4882a593Smuzhiyun #ifdef MODULE
693*4882a593Smuzhiyun 	.irqcleanup	= AmiIrqCleanUp,
694*4882a593Smuzhiyun #endif /* MODULE */
695*4882a593Smuzhiyun 	.init		= AmiInit,
696*4882a593Smuzhiyun 	.silence	= AmiSilence,
697*4882a593Smuzhiyun 	.setFormat	= AmiSetFormat,
698*4882a593Smuzhiyun 	.setVolume	= AmiSetVolume,
699*4882a593Smuzhiyun 	.setTreble	= AmiSetTreble,
700*4882a593Smuzhiyun 	.play		= AmiPlay,
701*4882a593Smuzhiyun 	.mixer_init	= AmiMixerInit,
702*4882a593Smuzhiyun 	.mixer_ioctl	= AmiMixerIoctl,
703*4882a593Smuzhiyun 	.write_sq_setup	= AmiWriteSqSetup,
704*4882a593Smuzhiyun 	.state_info	= AmiStateInfo,
705*4882a593Smuzhiyun 	.min_dsp_speed	= 8000,
706*4882a593Smuzhiyun 	.version	= ((DMASOUND_PAULA_REVISION<<8) | DMASOUND_PAULA_EDITION),
707*4882a593Smuzhiyun 	.hardware_afmts	= (AFMT_S8 | AFMT_S16_BE), /* h'ware-supported formats *only* here */
708*4882a593Smuzhiyun 	.capabilities	= DSP_CAP_BATCH          /* As per SNDCTL_DSP_GETCAPS */
709*4882a593Smuzhiyun };
710*4882a593Smuzhiyun 
711*4882a593Smuzhiyun 
712*4882a593Smuzhiyun /*** Config & Setup **********************************************************/
713*4882a593Smuzhiyun 
714*4882a593Smuzhiyun 
amiga_audio_probe(struct platform_device * pdev)715*4882a593Smuzhiyun static int __init amiga_audio_probe(struct platform_device *pdev)
716*4882a593Smuzhiyun {
717*4882a593Smuzhiyun 	dmasound.mach = machAmiga;
718*4882a593Smuzhiyun 	dmasound.mach.default_hard = def_hard ;
719*4882a593Smuzhiyun 	dmasound.mach.default_soft = def_soft ;
720*4882a593Smuzhiyun 	return dmasound_init();
721*4882a593Smuzhiyun }
722*4882a593Smuzhiyun 
amiga_audio_remove(struct platform_device * pdev)723*4882a593Smuzhiyun static int __exit amiga_audio_remove(struct platform_device *pdev)
724*4882a593Smuzhiyun {
725*4882a593Smuzhiyun 	dmasound_deinit();
726*4882a593Smuzhiyun 	return 0;
727*4882a593Smuzhiyun }
728*4882a593Smuzhiyun 
729*4882a593Smuzhiyun static struct platform_driver amiga_audio_driver = {
730*4882a593Smuzhiyun 	.remove = __exit_p(amiga_audio_remove),
731*4882a593Smuzhiyun 	.driver   = {
732*4882a593Smuzhiyun 		.name	= "amiga-audio",
733*4882a593Smuzhiyun 	},
734*4882a593Smuzhiyun };
735*4882a593Smuzhiyun 
736*4882a593Smuzhiyun module_platform_driver_probe(amiga_audio_driver, amiga_audio_probe);
737*4882a593Smuzhiyun 
738*4882a593Smuzhiyun MODULE_LICENSE("GPL");
739*4882a593Smuzhiyun MODULE_ALIAS("platform:amiga-audio");
740