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