patch-2.3.99-pre4 linux/drivers/sound/dmasound/dmasound_atari.c

Next file: linux/drivers/sound/dmasound/dmasound_awacs.c
Previous file: linux/drivers/sound/dmasound/dmasound.h
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.99-pre3/linux/drivers/sound/dmasound/dmasound_atari.c linux/drivers/sound/dmasound/dmasound_atari.c
@@ -0,0 +1,1560 @@
+
+/*
+ *  linux/drivers/sound/dmasound_atari.c
+ *
+ *  Atari DMA Sound Driver
+ *
+ *  See linux/drivers/sound/dmasound_core.c for copyright and credits
+ */
+
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/soundcard.h>
+#include <linux/mm.h>
+
+#include <asm/pgalloc.h>
+#include <asm/uaccess.h>
+#include <asm/atariints.h>
+#include <asm/atari_stram.h>
+
+#include "dmasound.h"
+
+
+extern void atari_microwire_cmd(int cmd);
+
+
+static int is_falcon;
+static int write_sq_ignore_int = 0;	/* ++TeSche: used for Falcon */
+
+static int expand_bal;	/* Balance factor for expanding (not volume!) */
+static int expand_data;	/* Data for expanding */
+
+
+/*** Translations ************************************************************/
+
+
+/* ++TeSche: radically changed for new expanding purposes...
+ *
+ * These two routines now deal with copying/expanding/translating the samples
+ * from user space into our buffer at the right frequency. They take care about
+ * how much data there's actually to read, how much buffer space there is and
+ * to convert samples into the right frequency/encoding. They will only work on
+ * complete samples so it may happen they leave some bytes in the input stream
+ * if the user didn't write a multiple of the current sample size. They both
+ * return the number of bytes they've used from both streams so you may detect
+ * such a situation. Luckily all programs should be able to cope with that.
+ *
+ * I think I've optimized anything as far as one can do in plain C, all
+ * variables should fit in registers and the loops are really short. There's
+ * one loop for every possible situation. Writing a more generalized and thus
+ * parameterized loop would only produce slower code. Feel free to optimize
+ * this in assembler if you like. :)
+ *
+ * I think these routines belong here because they're not yet really hardware
+ * independent, especially the fact that the Falcon can play 16bit samples
+ * only in stereo is hardcoded in both of them!
+ *
+ * ++geert: split in even more functions (one per format)
+ */
+
+static ssize_t ata_ct_law(const u_char *userPtr, size_t userCount,
+			  u_char frame[], ssize_t *frameUsed,
+			  ssize_t frameLeft);
+static ssize_t ata_ct_s8(const u_char *userPtr, size_t userCount,
+			 u_char frame[], ssize_t *frameUsed,
+			 ssize_t frameLeft);
+static ssize_t ata_ct_u8(const u_char *userPtr, size_t userCount,
+			 u_char frame[], ssize_t *frameUsed,
+			 ssize_t frameLeft);
+static ssize_t ata_ct_s16be(const u_char *userPtr, size_t userCount,
+			    u_char frame[], ssize_t *frameUsed,
+			    ssize_t frameLeft);
+static ssize_t ata_ct_u16be(const u_char *userPtr, size_t userCount,
+			    u_char frame[], ssize_t *frameUsed,
+			    ssize_t frameLeft);
+static ssize_t ata_ct_s16le(const u_char *userPtr, size_t userCount,
+			    u_char frame[], ssize_t *frameUsed,
+			    ssize_t frameLeft);
+static ssize_t ata_ct_u16le(const u_char *userPtr, size_t userCount,
+			    u_char frame[], ssize_t *frameUsed,
+			    ssize_t frameLeft);
+static ssize_t ata_ctx_law(const u_char *userPtr, size_t userCount,
+			   u_char frame[], ssize_t *frameUsed,
+			   ssize_t frameLeft);
+static ssize_t ata_ctx_s8(const u_char *userPtr, size_t userCount,
+			  u_char frame[], ssize_t *frameUsed,
+			  ssize_t frameLeft);
+static ssize_t ata_ctx_u8(const u_char *userPtr, size_t userCount,
+			  u_char frame[], ssize_t *frameUsed,
+			  ssize_t frameLeft);
+static ssize_t ata_ctx_s16be(const u_char *userPtr, size_t userCount,
+			     u_char frame[], ssize_t *frameUsed,
+			     ssize_t frameLeft);
+static ssize_t ata_ctx_u16be(const u_char *userPtr, size_t userCount,
+			     u_char frame[], ssize_t *frameUsed,
+			     ssize_t frameLeft);
+static ssize_t ata_ctx_s16le(const u_char *userPtr, size_t userCount,
+			     u_char frame[], ssize_t *frameUsed,
+			     ssize_t frameLeft);
+static ssize_t ata_ctx_u16le(const u_char *userPtr, size_t userCount,
+			     u_char frame[], ssize_t *frameUsed,
+			     ssize_t frameLeft);
+
+
+/*** Low level stuff *********************************************************/
+
+
+static void AtaOpen(void);
+static void AtaRelease(void);
+static void *AtaAlloc(unsigned int size, int flags);
+static void AtaFree(void *, unsigned int size);
+static int AtaIrqInit(void);
+#ifdef MODULE
+static void AtaIrqCleanUp(void);
+#endif /* MODULE */
+static int AtaSetBass(int bass);
+static int AtaSetTreble(int treble);
+static void TTSilence(void);
+static void TTInit(void);
+static int TTSetFormat(int format);
+static int TTSetVolume(int volume);
+static int TTSetGain(int gain);
+static void FalconSilence(void);
+static void FalconInit(void);
+static int FalconSetFormat(int format);
+static int FalconSetVolume(int volume);
+static void AtaPlayNextFrame(int index);
+static void AtaPlay(void);
+static void AtaInterrupt(int irq, void *dummy, struct pt_regs *fp);
+
+/*** Mid level stuff *********************************************************/
+
+static void TTMixerInit(void);
+static void FalconMixerInit(void);
+static int AtaMixerIoctl(u_int cmd, u_long arg);
+static int TTMixerIoctl(u_int cmd, u_long arg);
+static int FalconMixerIoctl(u_int cmd, u_long arg);
+static void AtaWriteSqSetup(void);
+static void AtaSqOpen(void);
+static int TTStateInfo(char *buffer);
+static int FalconStateInfo(char *buffer);
+
+
+/*** Translations ************************************************************/
+
+
+static ssize_t ata_ct_law(const u_char *userPtr, size_t userCount,
+			  u_char frame[], ssize_t *frameUsed,
+			  ssize_t frameLeft)
+{
+	char *table = dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8
+							  : dmasound_alaw2dma8;
+	ssize_t count, used;
+	u_char *p = &frame[*frameUsed];
+
+	count = min(userCount, frameLeft);
+	if (dmasound.soft.stereo)
+		count &= ~1;
+	used = count;
+	while (count > 0) {
+		u_char data;
+		if (get_user(data, userPtr++))
+			return -EFAULT;
+		*p++ = table[data];
+		count--;
+	}
+	*frameUsed += used;
+	return used;
+}
+
+
+static ssize_t ata_ct_s8(const u_char *userPtr, size_t userCount,
+			 u_char frame[], ssize_t *frameUsed,
+			 ssize_t frameLeft)
+{
+	ssize_t count, used;
+	void *p = &frame[*frameUsed];
+
+	count = min(userCount, frameLeft);
+	if (dmasound.soft.stereo)
+		count &= ~1;
+	used = count;
+	if (copy_from_user(p, userPtr, count))
+		return -EFAULT;
+	*frameUsed += used;
+	return used;
+}
+
+
+static ssize_t ata_ct_u8(const u_char *userPtr, size_t userCount,
+			 u_char frame[], ssize_t *frameUsed,
+			 ssize_t frameLeft)
+{
+	ssize_t count, used;
+
+	if (!dmasound.soft.stereo) {
+		u_char *p = &frame[*frameUsed];
+		count = min(userCount, frameLeft);
+		used = count;
+		while (count > 0) {
+			u_char data;
+			if (get_user(data, userPtr++))
+				return -EFAULT;
+			*p++ = data ^ 0x80;
+			count--;
+		}
+	} else {
+		u_short *p = (u_short *)&frame[*frameUsed];
+		count = min(userCount, frameLeft)>>1;
+		used = count*2;
+		while (count > 0) {
+			u_short data;
+			if (get_user(data, ((u_short *)userPtr)++))
+				return -EFAULT;
+			*p++ = data ^ 0x8080;
+			count--;
+		}
+	}
+	*frameUsed += used;
+	return used;
+}
+
+
+static ssize_t ata_ct_s16be(const u_char *userPtr, size_t userCount,
+			    u_char frame[], ssize_t *frameUsed,
+			    ssize_t frameLeft)
+{
+	ssize_t count, used;
+
+	if (!dmasound.soft.stereo) {
+		u_short *p = (u_short *)&frame[*frameUsed];
+		count = min(userCount, frameLeft)>>1;
+		used = count*2;
+		while (count > 0) {
+			u_short data;
+			if (get_user(data, ((u_short *)userPtr)++))
+				return -EFAULT;
+			*p++ = data;
+			*p++ = data;
+			count--;
+		}
+		*frameUsed += used*2;
+	} else {
+		void *p = (u_short *)&frame[*frameUsed];
+		count = min(userCount, frameLeft) & ~3;
+		used = count;
+		if (copy_from_user(p, userPtr, count))
+			return -EFAULT;
+		*frameUsed += used;
+	}
+	return used;
+}
+
+
+static ssize_t ata_ct_u16be(const u_char *userPtr, size_t userCount,
+			    u_char frame[], ssize_t *frameUsed,
+			    ssize_t frameLeft)
+{
+	ssize_t count, used;
+
+	if (!dmasound.soft.stereo) {
+		u_short *p = (u_short *)&frame[*frameUsed];
+		count = min(userCount, frameLeft)>>1;
+		used = count*2;
+		while (count > 0) {
+			u_short data;
+			if (get_user(data, ((u_short *)userPtr)++))
+				return -EFAULT;
+			data ^= 0x8000;
+			*p++ = data;
+			*p++ = data;
+			count--;
+		}
+		*frameUsed += used*2;
+	} else {
+		u_long *p = (u_long *)&frame[*frameUsed];
+		count = min(userCount, frameLeft)>>2;
+		used = count*4;
+		while (count > 0) {
+			u_long data;
+			if (get_user(data, ((u_int *)userPtr)++))
+				return -EFAULT;
+			*p++ = data ^ 0x80008000;
+			count--;
+		}
+		*frameUsed += used;
+	}
+	return used;
+}
+
+
+static ssize_t ata_ct_s16le(const u_char *userPtr, size_t userCount,
+			    u_char frame[], ssize_t *frameUsed,
+			    ssize_t frameLeft)
+{
+	ssize_t count, used;
+
+	count = frameLeft;
+	if (!dmasound.soft.stereo) {
+		u_short *p = (u_short *)&frame[*frameUsed];
+		count = min(userCount, frameLeft)>>1;
+		used = count*2;
+		while (count > 0) {
+			u_short data;
+			if (get_user(data, ((u_short *)userPtr)++))
+				return -EFAULT;
+			data = le2be16(data);
+			*p++ = data;
+			*p++ = data;
+			count--;
+		}
+		*frameUsed += used*2;
+	} else {
+		u_long *p = (u_long *)&frame[*frameUsed];
+		count = min(userCount, frameLeft)>>2;
+		used = count*4;
+		while (count > 0) {
+			u_long data;
+			if (get_user(data, ((u_int *)userPtr)++))
+				return -EFAULT;
+			data = le2be16dbl(data);
+			*p++ = data;
+			count--;
+		}
+		*frameUsed += used;
+	}
+	return used;
+}
+
+
+static ssize_t ata_ct_u16le(const u_char *userPtr, size_t userCount,
+			    u_char frame[], ssize_t *frameUsed,
+			    ssize_t frameLeft)
+{
+	ssize_t count, used;
+
+	count = frameLeft;
+	if (!dmasound.soft.stereo) {
+		u_short *p = (u_short *)&frame[*frameUsed];
+		count = min(userCount, frameLeft)>>1;
+		used = count*2;
+		while (count > 0) {
+			u_short data;
+			if (get_user(data, ((u_short *)userPtr)++))
+				return -EFAULT;
+			data = le2be16(data) ^ 0x8000;
+			*p++ = data;
+			*p++ = data;
+		}
+		*frameUsed += used*2;
+	} else {
+		u_long *p = (u_long *)&frame[*frameUsed];
+		count = min(userCount, frameLeft)>>2;
+		used = count;
+		while (count > 0) {
+			u_long data;
+			if (get_user(data, ((u_int *)userPtr)++))
+				return -EFAULT;
+			data = le2be16dbl(data) ^ 0x80008000;
+			*p++ = data;
+			count--;
+		}
+		*frameUsed += used;
+	}
+	return used;
+}
+
+
+static ssize_t ata_ctx_law(const u_char *userPtr, size_t userCount,
+			   u_char frame[], ssize_t *frameUsed,
+			   ssize_t frameLeft)
+{
+	char *table = dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8
+							  : dmasound_alaw2dma8;
+	/* this should help gcc to stuff everything into registers */
+	long bal = expand_bal;
+	long hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
+	ssize_t used, usedf;
+
+	used = userCount;
+	usedf = frameLeft;
+	if (!dmasound.soft.stereo) {
+		u_char *p = &frame[*frameUsed];
+		u_char data = expand_data;
+		while (frameLeft) {
+			u_char c;
+			if (bal < 0) {
+				if (!userCount)
+					break;
+				if (get_user(c, userPtr++))
+					return -EFAULT;
+				data = table[c];
+				userCount--;
+				bal += hSpeed;
+			}
+			*p++ = data;
+			frameLeft--;
+			bal -= sSpeed;
+		}
+		expand_data = data;
+	} else {
+		u_short *p = (u_short *)&frame[*frameUsed];
+		u_short data = expand_data;
+		while (frameLeft >= 2) {
+			u_char c;
+			if (bal < 0) {
+				if (userCount < 2)
+					break;
+				if (get_user(c, userPtr++))
+					return -EFAULT;
+				data = table[c] << 8;
+				if (get_user(c, userPtr++))
+					return -EFAULT;
+				data |= table[c];
+				userCount -= 2;
+				bal += hSpeed;
+			}
+			*p++ = data;
+			frameLeft -= 2;
+			bal -= sSpeed;
+		}
+		expand_data = data;
+	}
+	expand_bal = bal;
+	used -= userCount;
+	*frameUsed += usedf-frameLeft;
+	return used;
+}
+
+
+static ssize_t ata_ctx_s8(const u_char *userPtr, size_t userCount,
+			  u_char frame[], ssize_t *frameUsed,
+			  ssize_t frameLeft)
+{
+	/* this should help gcc to stuff everything into registers */
+	long bal = expand_bal;
+	long hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
+	ssize_t used, usedf;
+
+	used = userCount;
+	usedf = frameLeft;
+	if (!dmasound.soft.stereo) {
+		u_char *p = &frame[*frameUsed];
+		u_char data = expand_data;
+		while (frameLeft) {
+			if (bal < 0) {
+				if (!userCount)
+					break;
+				if (get_user(data, userPtr++))
+					return -EFAULT;
+				userCount--;
+				bal += hSpeed;
+			}
+			*p++ = data;
+			frameLeft--;
+			bal -= sSpeed;
+		}
+		expand_data = data;
+	} else {
+		u_short *p = (u_short *)&frame[*frameUsed];
+		u_short data = expand_data;
+		while (frameLeft >= 2) {
+			if (bal < 0) {
+				if (userCount < 2)
+					break;
+				if (get_user(data, ((u_short *)userPtr)++))
+					return -EFAULT;
+				userCount -= 2;
+				bal += hSpeed;
+			}
+			*p++ = data;
+			frameLeft -= 2;
+			bal -= sSpeed;
+		}
+		expand_data = data;
+	}
+	expand_bal = bal;
+	used -= userCount;
+	*frameUsed += usedf-frameLeft;
+	return used;
+}
+
+
+static ssize_t ata_ctx_u8(const u_char *userPtr, size_t userCount,
+			  u_char frame[], ssize_t *frameUsed,
+			  ssize_t frameLeft)
+{
+	/* this should help gcc to stuff everything into registers */
+	long bal = expand_bal;
+	long hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
+	ssize_t used, usedf;
+
+	used = userCount;
+	usedf = frameLeft;
+	if (!dmasound.soft.stereo) {
+		u_char *p = &frame[*frameUsed];
+		u_char data = expand_data;
+		while (frameLeft) {
+			if (bal < 0) {
+				if (!userCount)
+					break;
+				if (get_user(data, userPtr++))
+					return -EFAULT;
+				data ^= 0x80;
+				userCount--;
+				bal += hSpeed;
+			}
+			*p++ = data;
+			frameLeft--;
+			bal -= sSpeed;
+		}
+		expand_data = data;
+	} else {
+		u_short *p = (u_short *)&frame[*frameUsed];
+		u_short data = expand_data;
+		while (frameLeft >= 2) {
+			if (bal < 0) {
+				if (userCount < 2)
+					break;
+				if (get_user(data, ((u_short *)userPtr)++))
+					return -EFAULT;
+				data ^= 0x8080;
+				userCount -= 2;
+				bal += hSpeed;
+			}
+			*p++ = data;
+			frameLeft -= 2;
+			bal -= sSpeed;
+		}
+		expand_data = data;
+	}
+	expand_bal = bal;
+	used -= userCount;
+	*frameUsed += usedf-frameLeft;
+	return used;
+}
+
+
+static ssize_t ata_ctx_s16be(const u_char *userPtr, size_t userCount,
+			     u_char frame[], ssize_t *frameUsed,
+			     ssize_t frameLeft)
+{
+	/* this should help gcc to stuff everything into registers */
+	long bal = expand_bal;
+	long hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
+	ssize_t used, usedf;
+
+	used = userCount;
+	usedf = frameLeft;
+	if (!dmasound.soft.stereo) {
+		u_short *p = (u_short *)&frame[*frameUsed];
+		u_short data = expand_data;
+		while (frameLeft >= 4) {
+			if (bal < 0) {
+				if (userCount < 2)
+					break;
+				if (get_user(data, ((u_short *)userPtr)++))
+					return -EFAULT;
+				userCount -= 2;
+				bal += hSpeed;
+			}
+			*p++ = data;
+			*p++ = data;
+			frameLeft -= 4;
+			bal -= sSpeed;
+		}
+		expand_data = data;
+	} else {
+		u_long *p = (u_long *)&frame[*frameUsed];
+		u_long data = expand_data;
+		while (frameLeft >= 4) {
+			if (bal < 0) {
+				if (userCount < 4)
+					break;
+				if (get_user(data, ((u_int *)userPtr)++))
+					return -EFAULT;
+				userCount -= 4;
+				bal += hSpeed;
+			}
+			*p++ = data;
+			frameLeft -= 4;
+			bal -= sSpeed;
+		}
+		expand_data = data;
+	}
+	expand_bal = bal;
+	used -= userCount;
+	*frameUsed += usedf-frameLeft;
+	return used;
+}
+
+
+static ssize_t ata_ctx_u16be(const u_char *userPtr, size_t userCount,
+			     u_char frame[], ssize_t *frameUsed,
+			     ssize_t frameLeft)
+{
+	/* this should help gcc to stuff everything into registers */
+	long bal = expand_bal;
+	long hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
+	ssize_t used, usedf;
+
+	used = userCount;
+	usedf = frameLeft;
+	if (!dmasound.soft.stereo) {
+		u_short *p = (u_short *)&frame[*frameUsed];
+		u_short data = expand_data;
+		while (frameLeft >= 4) {
+			if (bal < 0) {
+				if (userCount < 2)
+					break;
+				if (get_user(data, ((u_short *)userPtr)++))
+					return -EFAULT;
+				data ^= 0x8000;
+				userCount -= 2;
+				bal += hSpeed;
+			}
+			*p++ = data;
+			*p++ = data;
+			frameLeft -= 4;
+			bal -= sSpeed;
+		}
+		expand_data = data;
+	} else {
+		u_long *p = (u_long *)&frame[*frameUsed];
+		u_long data = expand_data;
+		while (frameLeft >= 4) {
+			if (bal < 0) {
+				if (userCount < 4)
+					break;
+				if (get_user(data, ((u_int *)userPtr)++))
+					return -EFAULT;
+				data ^= 0x80008000;
+				userCount -= 4;
+				bal += hSpeed;
+			}
+			*p++ = data;
+			frameLeft -= 4;
+			bal -= sSpeed;
+		}
+		expand_data = data;
+	}
+	expand_bal = bal;
+	used -= userCount;
+	*frameUsed += usedf-frameLeft;
+	return used;
+}
+
+
+static ssize_t ata_ctx_s16le(const u_char *userPtr, size_t userCount,
+			     u_char frame[], ssize_t *frameUsed,
+			     ssize_t frameLeft)
+{
+	/* this should help gcc to stuff everything into registers */
+	long bal = expand_bal;
+	long hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
+	ssize_t used, usedf;
+
+	used = userCount;
+	usedf = frameLeft;
+	if (!dmasound.soft.stereo) {
+		u_short *p = (u_short *)&frame[*frameUsed];
+		u_short data = expand_data;
+		while (frameLeft >= 4) {
+			if (bal < 0) {
+				if (userCount < 2)
+					break;
+				if (get_user(data, ((u_short *)userPtr)++))
+					return -EFAULT;
+				data = le2be16(data);
+				userCount -= 2;
+				bal += hSpeed;
+			}
+			*p++ = data;
+			*p++ = data;
+			frameLeft -= 4;
+			bal -= sSpeed;
+		}
+		expand_data = data;
+	} else {
+		u_long *p = (u_long *)&frame[*frameUsed];
+		u_long data = expand_data;
+		while (frameLeft >= 4) {
+			if (bal < 0) {
+				if (userCount < 4)
+					break;
+				if (get_user(data, ((u_int *)userPtr)++))
+					return -EFAULT;
+				data = le2be16dbl(data);
+				userCount -= 4;
+				bal += hSpeed;
+			}
+			*p++ = data;
+			frameLeft -= 4;
+			bal -= sSpeed;
+		}
+		expand_data = data;
+	}
+	expand_bal = bal;
+	used -= userCount;
+	*frameUsed += usedf-frameLeft;
+	return used;
+}
+
+
+static ssize_t ata_ctx_u16le(const u_char *userPtr, size_t userCount,
+			     u_char frame[], ssize_t *frameUsed,
+			     ssize_t frameLeft)
+{
+	/* this should help gcc to stuff everything into registers */
+	long bal = expand_bal;
+	long hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
+	ssize_t used, usedf;
+
+	used = userCount;
+	usedf = frameLeft;
+	if (!dmasound.soft.stereo) {
+		u_short *p = (u_short *)&frame[*frameUsed];
+		u_short data = expand_data;
+		while (frameLeft >= 4) {
+			if (bal < 0) {
+				if (userCount < 2)
+					break;
+				if (get_user(data, ((u_short *)userPtr)++))
+					return -EFAULT;
+				data = le2be16(data) ^ 0x8000;
+				userCount -= 2;
+				bal += hSpeed;
+			}
+			*p++ = data;
+			*p++ = data;
+			frameLeft -= 4;
+			bal -= sSpeed;
+		}
+		expand_data = data;
+	} else {
+		u_long *p = (u_long *)&frame[*frameUsed];
+		u_long data = expand_data;
+		while (frameLeft >= 4) {
+			if (bal < 0) {
+				if (userCount < 4)
+					break;
+				if (get_user(data, ((u_int *)userPtr)++))
+					return -EFAULT;
+				data = le2be16dbl(data) ^ 0x80008000;
+				userCount -= 4;
+				bal += hSpeed;
+			}
+			*p++ = data;
+			frameLeft -= 4;
+			bal -= sSpeed;
+		}
+		expand_data = data;
+	}
+	expand_bal = bal;
+	used -= userCount;
+	*frameUsed += usedf-frameLeft;
+	return used;
+}
+
+
+static TRANS transTTNormal = {
+	ct_ulaw:	ata_ct_law,
+	ct_alaw:	ata_ct_law,
+	ct_s8:		ata_ct_s8,
+	ct_u8:		ata_ct_u8,
+};
+
+static TRANS transTTExpanding = {
+	ct_ulaw:	ata_ctx_law,
+	ct_alaw:	ata_ctx_law,
+	ct_s8:		ata_ctx_s8,
+	ct_u8:		ata_ctx_u8,
+};
+
+static TRANS transFalconNormal = {
+	ct_ulaw:	ata_ct_law,
+	ct_alaw:	ata_ct_law,
+	ct_s8:		ata_ct_s8,
+	ct_u8:		ata_ct_u8,
+	ct_s16be:	ata_ct_s16be,
+	ct_u16be:	ata_ct_u16be,
+	ct_s16le:	ata_ct_s16le,
+	ct_u16le:	ata_ct_u16le
+};
+
+static TRANS transFalconExpanding = {
+	ct_ulaw:	ata_ctx_law,
+	ct_alaw:	ata_ctx_law,
+	ct_s8:		ata_ctx_s8,
+	ct_u8:		ata_ctx_u8,
+	ct_s16be:	ata_ctx_s16be,
+	ct_u16be:	ata_ctx_u16be,
+	ct_s16le:	ata_ctx_s16le,
+	ct_u16le:	ata_ctx_u16le,
+};
+
+
+/*** Low level stuff *********************************************************/
+
+
+
+/*
+ * Atari (TT/Falcon)
+ */
+
+static void AtaOpen(void)
+{
+	MOD_INC_USE_COUNT;
+}
+
+static void AtaRelease(void)
+{
+	MOD_DEC_USE_COUNT;
+}
+
+static void *AtaAlloc(unsigned int size, int flags)
+{
+	return atari_stram_alloc( size, NULL, "dmasound" );
+}
+
+static void AtaFree(void *obj, unsigned int size)
+{
+	atari_stram_free( obj );
+}
+
+static int __init AtaIrqInit(void)
+{
+	/* Set up timer A. Timer A
+	   will receive a signal upon end of playing from the sound
+	   hardware. Furthermore Timer A is able to count events
+	   and will cause an interrupt after a programmed number
+	   of events. So all we need to keep the music playing is
+	   to provide the sound hardware with new data upon
+	   an interrupt from timer A. */
+	mfp.tim_ct_a = 0;	/* ++roman: Stop timer before programming! */
+	mfp.tim_dt_a = 1;	/* Cause interrupt after first event. */
+	mfp.tim_ct_a = 8;	/* Turn on event counting. */
+	/* Register interrupt handler. */
+	request_irq(IRQ_MFP_TIMA, AtaInterrupt, IRQ_TYPE_SLOW, "DMA sound",
+		    AtaInterrupt);
+	mfp.int_en_a |= 0x20;	/* Turn interrupt on. */
+	mfp.int_mk_a |= 0x20;
+	return 1;
+}
+
+#ifdef MODULE
+static void AtaIrqCleanUp(void)
+{
+	mfp.tim_ct_a = 0;	/* stop timer */
+	mfp.int_en_a &= ~0x20;	/* turn interrupt off */
+	free_irq(IRQ_MFP_TIMA, AtaInterrupt);
+}
+#endif /* MODULE */
+
+
+#define TONE_VOXWARE_TO_DB(v) \
+	(((v) < 0) ? -12 : ((v) > 100) ? 12 : ((v) - 50) * 6 / 25)
+#define TONE_DB_TO_VOXWARE(v) (((v) * 25 + ((v) > 0 ? 5 : -5)) / 6 + 50)
+
+
+static int AtaSetBass(int bass)
+{
+	dmasound.bass = TONE_VOXWARE_TO_DB(bass);
+	atari_microwire_cmd(MW_LM1992_BASS(dmasound.bass));
+	return TONE_DB_TO_VOXWARE(dmasound.bass);
+}
+
+
+static int AtaSetTreble(int treble)
+{
+	dmasound.treble = TONE_VOXWARE_TO_DB(treble);
+	atari_microwire_cmd(MW_LM1992_TREBLE(dmasound.treble));
+	return TONE_DB_TO_VOXWARE(dmasound.treble);
+}
+
+
+
+/*
+ * TT
+ */
+
+
+static void TTSilence(void)
+{
+	tt_dmasnd.ctrl = DMASND_CTRL_OFF;
+	atari_microwire_cmd(MW_LM1992_PSG_HIGH); /* mix in PSG signal 1:1 */
+}
+
+
+static void TTInit(void)
+{
+	int mode, i, idx;
+	const int freq[4] = {50066, 25033, 12517, 6258};
+
+	/* search a frequency that fits into the allowed error range */
+
+	idx = -1;
+	for (i = 0; i < arraysize(freq); i++)
+		/* this isn't as much useful for a TT than for a Falcon, but
+		 * then it doesn't hurt very much to implement it for a TT too.
+		 */
+		if ((100 * abs(dmasound.soft.speed - freq[i]) / freq[i]) < catchRadius)
+			idx = i;
+	if (idx > -1) {
+		dmasound.soft.speed = freq[idx];
+		dmasound.trans_write = &transTTNormal;
+	} else
+		dmasound.trans_write = &transTTExpanding;
+
+	TTSilence();
+	dmasound.hard = dmasound.soft;
+
+	if (dmasound.hard.speed > 50066) {
+		/* we would need to squeeze the sound, but we won't do that */
+		dmasound.hard.speed = 50066;
+		mode = DMASND_MODE_50KHZ;
+		dmasound.trans_write = &transTTNormal;
+	} else if (dmasound.hard.speed > 25033) {
+		dmasound.hard.speed = 50066;
+		mode = DMASND_MODE_50KHZ;
+	} else if (dmasound.hard.speed > 12517) {
+		dmasound.hard.speed = 25033;
+		mode = DMASND_MODE_25KHZ;
+	} else if (dmasound.hard.speed > 6258) {
+		dmasound.hard.speed = 12517;
+		mode = DMASND_MODE_12KHZ;
+	} else {
+		dmasound.hard.speed = 6258;
+		mode = DMASND_MODE_6KHZ;
+	}
+
+	tt_dmasnd.mode = (dmasound.hard.stereo ?
+			  DMASND_MODE_STEREO : DMASND_MODE_MONO) |
+		DMASND_MODE_8BIT | mode;
+
+	expand_bal = -dmasound.soft.speed;
+}
+
+
+static int TTSetFormat(int format)
+{
+	/* TT sound DMA supports only 8bit modes */
+
+	switch (format) {
+	case AFMT_QUERY:
+		return dmasound.soft.format;
+	case AFMT_MU_LAW:
+	case AFMT_A_LAW:
+	case AFMT_S8:
+	case AFMT_U8:
+		break;
+	default:
+		format = AFMT_S8;
+	}
+
+	dmasound.soft.format = format;
+	dmasound.soft.size = 8;
+	if (dmasound.minDev == SND_DEV_DSP) {
+		dmasound.dsp.format = format;
+		dmasound.dsp.size = 8;
+	}
+	TTInit();
+
+	return format;
+}
+
+
+#define VOLUME_VOXWARE_TO_DB(v) \
+	(((v) < 0) ? -40 : ((v) > 100) ? 0 : ((v) * 2) / 5 - 40)
+#define VOLUME_DB_TO_VOXWARE(v) ((((v) + 40) * 5 + 1) / 2)
+
+
+static int TTSetVolume(int volume)
+{
+	dmasound.volume_left = VOLUME_VOXWARE_TO_DB(volume & 0xff);
+	atari_microwire_cmd(MW_LM1992_BALLEFT(dmasound.volume_left));
+	dmasound.volume_right = VOLUME_VOXWARE_TO_DB((volume & 0xff00) >> 8);
+	atari_microwire_cmd(MW_LM1992_BALRIGHT(dmasound.volume_right));
+	return VOLUME_DB_TO_VOXWARE(dmasound.volume_left) |
+	       (VOLUME_DB_TO_VOXWARE(dmasound.volume_right) << 8);
+}
+
+
+#define GAIN_VOXWARE_TO_DB(v) \
+	(((v) < 0) ? -80 : ((v) > 100) ? 0 : ((v) * 4) / 5 - 80)
+#define GAIN_DB_TO_VOXWARE(v) ((((v) + 80) * 5 + 1) / 4)
+
+static int TTSetGain(int gain)
+{
+	dmasound.gain = GAIN_VOXWARE_TO_DB(gain);
+	atari_microwire_cmd(MW_LM1992_VOLUME(dmasound.gain));
+	return GAIN_DB_TO_VOXWARE(dmasound.gain);
+}
+
+
+
+/*
+ * Falcon
+ */
+
+
+static void FalconSilence(void)
+{
+	/* stop playback, set sample rate 50kHz for PSG sound */
+	tt_dmasnd.ctrl = DMASND_CTRL_OFF;
+	tt_dmasnd.mode = DMASND_MODE_50KHZ | DMASND_MODE_STEREO | DMASND_MODE_8BIT;
+	tt_dmasnd.int_div = 0; /* STE compatible divider */
+	tt_dmasnd.int_ctrl = 0x0;
+	tt_dmasnd.cbar_src = 0x0000; /* no matrix inputs */
+	tt_dmasnd.cbar_dst = 0x0000; /* no matrix outputs */
+	tt_dmasnd.dac_src = 1; /* connect ADC to DAC, disconnect matrix */
+	tt_dmasnd.adc_src = 3; /* ADC Input = PSG */
+}
+
+
+static void FalconInit(void)
+{
+	int divider, i, idx;
+	const int freq[8] = {49170, 32780, 24585, 19668, 16390, 12292, 9834, 8195};
+
+	/* search a frequency that fits into the allowed error range */
+
+	idx = -1;
+	for (i = 0; i < arraysize(freq); i++)
+		/* if we will tolerate 3% error 8000Hz->8195Hz (2.38%) would
+		 * be playable without expanding, but that now a kernel runtime
+		 * option
+		 */
+		if ((100 * abs(dmasound.soft.speed - freq[i]) / freq[i]) < catchRadius)
+			idx = i;
+	if (idx > -1) {
+		dmasound.soft.speed = freq[idx];
+		dmasound.trans_write = &transFalconNormal;
+	} else
+		dmasound.trans_write = &transFalconExpanding;
+
+	FalconSilence();
+	dmasound.hard = dmasound.soft;
+
+	if (dmasound.hard.size == 16) {
+		/* the Falcon can play 16bit samples only in stereo */
+		dmasound.hard.stereo = 1;
+	}
+
+	if (dmasound.hard.speed > 49170) {
+		/* we would need to squeeze the sound, but we won't do that */
+		dmasound.hard.speed = 49170;
+		divider = 1;
+		dmasound.trans_write = &transFalconNormal;
+	} else if (dmasound.hard.speed > 32780) {
+		dmasound.hard.speed = 49170;
+		divider = 1;
+	} else if (dmasound.hard.speed > 24585) {
+		dmasound.hard.speed = 32780;
+		divider = 2;
+	} else if (dmasound.hard.speed > 19668) {
+		dmasound.hard.speed = 24585;
+		divider = 3;
+	} else if (dmasound.hard.speed > 16390) {
+		dmasound.hard.speed = 19668;
+		divider = 4;
+	} else if (dmasound.hard.speed > 12292) {
+		dmasound.hard.speed = 16390;
+		divider = 5;
+	} else if (dmasound.hard.speed > 9834) {
+		dmasound.hard.speed = 12292;
+		divider = 7;
+	} else if (dmasound.hard.speed > 8195) {
+		dmasound.hard.speed = 9834;
+		divider = 9;
+	} else {
+		dmasound.hard.speed = 8195;
+		divider = 11;
+	}
+	tt_dmasnd.int_div = divider;
+
+	/* Setup Falcon sound DMA for playback */
+	tt_dmasnd.int_ctrl = 0x4; /* Timer A int at play end */
+	tt_dmasnd.track_select = 0x0; /* play 1 track, track 1 */
+	tt_dmasnd.cbar_src = 0x0001; /* DMA(25MHz) --> DAC */
+	tt_dmasnd.cbar_dst = 0x0000;
+	tt_dmasnd.rec_track_select = 0;
+	tt_dmasnd.dac_src = 2; /* connect matrix to DAC */
+	tt_dmasnd.adc_src = 0; /* ADC Input = Mic */
+
+	tt_dmasnd.mode = (dmasound.hard.stereo ?
+			  DMASND_MODE_STEREO : DMASND_MODE_MONO) |
+		((dmasound.hard.size == 8) ?
+		 DMASND_MODE_8BIT : DMASND_MODE_16BIT) |
+		DMASND_MODE_6KHZ;
+
+	expand_bal = -dmasound.soft.speed;
+}
+
+
+static int FalconSetFormat(int format)
+{
+	int size;
+	/* Falcon sound DMA supports 8bit and 16bit modes */
+
+	switch (format) {
+	case AFMT_QUERY:
+		return dmasound.soft.format;
+	case AFMT_MU_LAW:
+	case AFMT_A_LAW:
+	case AFMT_U8:
+	case AFMT_S8:
+		size = 8;
+		break;
+	case AFMT_S16_BE:
+	case AFMT_U16_BE:
+	case AFMT_S16_LE:
+	case AFMT_U16_LE:
+		size = 16;
+		break;
+	default: /* :-) */
+		size = 8;
+		format = AFMT_S8;
+	}
+
+	dmasound.soft.format = format;
+	dmasound.soft.size = size;
+	if (dmasound.minDev == SND_DEV_DSP) {
+		dmasound.dsp.format = format;
+		dmasound.dsp.size = dmasound.soft.size;
+	}
+
+	FalconInit();
+
+	return format;
+}
+
+
+/* This is for the Falcon output *attenuation* in 1.5dB steps,
+ * i.e. output level from 0 to -22.5dB in -1.5dB steps.
+ */
+#define VOLUME_VOXWARE_TO_ATT(v) \
+	((v) < 0 ? 15 : (v) > 100 ? 0 : 15 - (v) * 3 / 20)
+#define VOLUME_ATT_TO_VOXWARE(v) (100 - (v) * 20 / 3)
+
+
+static int FalconSetVolume(int volume)
+{
+	dmasound.volume_left = VOLUME_VOXWARE_TO_ATT(volume & 0xff);
+	dmasound.volume_right = VOLUME_VOXWARE_TO_ATT((volume & 0xff00) >> 8);
+	tt_dmasnd.output_atten = dmasound.volume_left << 8 | dmasound.volume_right << 4;
+	return VOLUME_ATT_TO_VOXWARE(dmasound.volume_left) |
+	       VOLUME_ATT_TO_VOXWARE(dmasound.volume_right) << 8;
+}
+
+
+static void AtaPlayNextFrame(int index)
+{
+	char *start, *end;
+
+	/* used by AtaPlay() if all doubts whether there really is something
+	 * to be played are already wiped out.
+	 */
+	start = write_sq.buffers[write_sq.front];
+	end = start+((write_sq.count == index) ? write_sq.rear_size
+					       : write_sq.block_size);
+	/* end might not be a legal virtual address. */
+	DMASNDSetEnd(virt_to_phys(end - 1) + 1);
+	DMASNDSetBase(virt_to_phys(start));
+	/* Since only an even number of samples per frame can
+	   be played, we might lose one byte here. (TO DO) */
+	write_sq.front = (write_sq.front+1) % write_sq.max_count;
+	write_sq.active++;
+	tt_dmasnd.ctrl = DMASND_CTRL_ON | DMASND_CTRL_REPEAT;
+}
+
+
+static void AtaPlay(void)
+{
+	/* ++TeSche: Note that write_sq.active is no longer just a flag but
+	 * holds the number of frames the DMA is currently programmed for
+	 * instead, may be 0, 1 (currently being played) or 2 (pre-programmed).
+	 *
+	 * Changes done to write_sq.count and write_sq.active are a bit more
+	 * subtle again so now I must admit I also prefer disabling the irq
+	 * here rather than considering all possible situations. But the point
+	 * is that disabling the irq doesn't have any bad influence on this
+	 * version of the driver as we benefit from having pre-programmed the
+	 * DMA wherever possible: There's no need to reload the DMA at the
+	 * exact time of an interrupt but only at some time while the
+	 * pre-programmed frame is playing!
+	 */
+	atari_disable_irq(IRQ_MFP_TIMA);
+
+	if (write_sq.active == 2 ||	/* DMA is 'full' */
+	    write_sq.count <= 0) {	/* nothing to do */
+		atari_enable_irq(IRQ_MFP_TIMA);
+		return;
+	}
+
+	if (write_sq.active == 0) {
+		/* looks like there's nothing 'in' the DMA yet, so try
+		 * to put two frames into it (at least one is available).
+		 */
+		if (write_sq.count == 1 &&
+		    write_sq.rear_size < write_sq.block_size &&
+		    !write_sq.syncing) {
+			/* hmmm, the only existing frame is not
+			 * yet filled and we're not syncing?
+			 */
+			atari_enable_irq(IRQ_MFP_TIMA);
+			return;
+		}
+		AtaPlayNextFrame(1);
+		if (write_sq.count == 1) {
+			/* no more frames */
+			atari_enable_irq(IRQ_MFP_TIMA);
+			return;
+		}
+		if (write_sq.count == 2 &&
+		    write_sq.rear_size < write_sq.block_size &&
+		    !write_sq.syncing) {
+			/* hmmm, there were two frames, but the second
+			 * one is not yet filled and we're not syncing?
+			 */
+			atari_enable_irq(IRQ_MFP_TIMA);
+			return;
+		}
+		AtaPlayNextFrame(2);
+	} else {
+		/* there's already a frame being played so we may only stuff
+		 * one new into the DMA, but even if this may be the last
+		 * frame existing the previous one is still on write_sq.count.
+		 */
+		if (write_sq.count == 2 &&
+		    write_sq.rear_size < write_sq.block_size &&
+		    !write_sq.syncing) {
+			/* hmmm, the only existing frame is not
+			 * yet filled and we're not syncing?
+			 */
+			atari_enable_irq(IRQ_MFP_TIMA);
+			return;
+		}
+		AtaPlayNextFrame(2);
+	}
+	atari_enable_irq(IRQ_MFP_TIMA);
+}
+
+
+static void AtaInterrupt(int irq, void *dummy, struct pt_regs *fp)
+{
+#if 0
+	/* ++TeSche: if you should want to test this... */
+	static int cnt = 0;
+	if (write_sq.active == 2)
+		if (++cnt == 10) {
+			/* simulate losing an interrupt */
+			cnt = 0;
+			return;
+		}
+#endif
+
+	if (write_sq_ignore_int && is_falcon) {
+		/* ++TeSche: Falcon only: ignore first irq because it comes
+		 * immediately after starting a frame. after that, irqs come
+		 * (almost) like on the TT.
+		 */
+		write_sq_ignore_int = 0;
+		return;
+	}
+
+	if (!write_sq.active) {
+		/* playing was interrupted and sq_reset() has already cleared
+		 * the sq variables, so better don't do anything here.
+		 */
+		WAKE_UP(write_sq.sync_queue);
+		return;
+	}
+
+	/* Probably ;) one frame is finished. Well, in fact it may be that a
+	 * pre-programmed one is also finished because there has been a long
+	 * delay in interrupt delivery and we've completely lost one, but
+	 * there's no way to detect such a situation. In such a case the last
+	 * frame will be played more than once and the situation will recover
+	 * as soon as the irq gets through.
+	 */
+	write_sq.count--;
+	write_sq.active--;
+
+	if (!write_sq.active) {
+		tt_dmasnd.ctrl = DMASND_CTRL_OFF;
+		write_sq_ignore_int = 1;
+	}
+
+	WAKE_UP(write_sq.action_queue);
+	/* At least one block of the queue is free now
+	   so wake up a writing process blocked because
+	   of a full queue. */
+
+	if ((write_sq.active != 1) || (write_sq.count != 1))
+		/* We must be a bit carefully here: write_sq.count indicates the
+		 * number of buffers used and not the number of frames to be
+		 * played. If write_sq.count==1 and write_sq.active==1 that
+		 * means the only remaining frame was already programmed
+		 * earlier (and is currently running) so we mustn't call
+		 * AtaPlay() here, otherwise we'll play one frame too much.
+		 */
+		AtaPlay();
+
+	if (!write_sq.active) WAKE_UP(write_sq.sync_queue);
+	/* We are not playing after AtaPlay(), so there
+	   is nothing to play any more. Wake up a process
+	   waiting for audio output to drain. */
+}
+
+
+/*** Mid level stuff *********************************************************/
+
+
+/*
+ * /dev/mixer abstraction
+ */
+
+#define RECLEVEL_VOXWARE_TO_GAIN(v)	\
+	((v) < 0 ? 0 : (v) > 100 ? 15 : (v) * 3 / 20)
+#define RECLEVEL_GAIN_TO_VOXWARE(v)	(((v) * 20 + 2) / 3)
+
+
+static void __init TTMixerInit(void)
+{
+	atari_microwire_cmd(MW_LM1992_VOLUME(0));
+	dmasound.volume_left = 0;
+	atari_microwire_cmd(MW_LM1992_BALLEFT(0));
+	dmasound.volume_right = 0;
+	atari_microwire_cmd(MW_LM1992_BALRIGHT(0));
+	atari_microwire_cmd(MW_LM1992_TREBLE(0));
+	atari_microwire_cmd(MW_LM1992_BASS(0));
+}
+
+static void __init FalconMixerInit(void)
+{
+	dmasound.volume_left = (tt_dmasnd.output_atten & 0xf00) >> 8;
+	dmasound.volume_right = (tt_dmasnd.output_atten & 0xf0) >> 4;
+}
+
+static int AtaMixerIoctl(u_int cmd, u_long arg)
+{
+	int data;
+	switch (cmd) {
+	    case SOUND_MIXER_READ_SPEAKER:
+		    if (is_falcon || MACH_IS_TT) {
+			    int porta;
+			    cli();
+			    sound_ym.rd_data_reg_sel = 14;
+			    porta = sound_ym.rd_data_reg_sel;
+			    sti();
+			    return IOCTL_OUT(arg, porta & 0x40 ? 0 : 100);
+		    }
+		    break;
+	    case SOUND_MIXER_WRITE_VOLUME:
+		    IOCTL_IN(arg, data);
+		    return IOCTL_OUT(arg, dmasound_set_volume(data));
+	    case SOUND_MIXER_WRITE_SPEAKER:
+		    if (is_falcon || MACH_IS_TT) {
+			    int porta;
+			    IOCTL_IN(arg, data);
+			    cli();
+			    sound_ym.rd_data_reg_sel = 14;
+			    porta = (sound_ym.rd_data_reg_sel & ~0x40) |
+				    (data < 50 ? 0x40 : 0);
+			    sound_ym.wd_data = porta;
+			    sti();
+			    return IOCTL_OUT(arg, porta & 0x40 ? 0 : 100);
+		    }
+	}
+	return -EINVAL;
+}
+
+
+static int TTMixerIoctl(u_int cmd, u_long arg)
+{
+	int data;
+	switch (cmd) {
+	    case SOUND_MIXER_READ_RECMASK:
+		return IOCTL_OUT(arg, 0);
+	    case SOUND_MIXER_READ_DEVMASK:
+		return IOCTL_OUT(arg,
+				 SOUND_MASK_VOLUME | SOUND_MASK_TREBLE | SOUND_MASK_BASS |
+				 (MACH_IS_TT ? SOUND_MASK_SPEAKER : 0));
+	    case SOUND_MIXER_READ_STEREODEVS:
+		return IOCTL_OUT(arg, SOUND_MASK_VOLUME);
+	    case SOUND_MIXER_READ_VOLUME:
+		return IOCTL_OUT(arg,
+				 VOLUME_DB_TO_VOXWARE(dmasound.volume_left) |
+				 (VOLUME_DB_TO_VOXWARE(dmasound.volume_right) << 8));
+	    case SOUND_MIXER_READ_BASS:
+		return IOCTL_OUT(arg, TONE_DB_TO_VOXWARE(dmasound.bass));
+	    case SOUND_MIXER_READ_TREBLE:
+		return IOCTL_OUT(arg, TONE_DB_TO_VOXWARE(dmasound.treble));
+	    case SOUND_MIXER_READ_OGAIN:
+		return IOCTL_OUT(arg, GAIN_DB_TO_VOXWARE(dmasound.gain));
+	    case SOUND_MIXER_WRITE_BASS:
+		IOCTL_IN(arg, data);
+		return IOCTL_OUT(arg, dmasound_set_bass(data));
+	    case SOUND_MIXER_WRITE_TREBLE:
+		IOCTL_IN(arg, data);
+		return IOCTL_OUT(arg, dmasound_set_treble(data));
+	    case SOUND_MIXER_WRITE_OGAIN:
+		IOCTL_IN(arg, data);
+		return IOCTL_OUT(arg, dmasound_set_gain(data));
+	}
+	return AtaMixerIoctl(cmd, arg);
+}
+
+static int FalconMixerIoctl(u_int cmd, u_long arg)
+{
+	int data;
+	switch (cmd) {
+	    case SOUND_MIXER_READ_RECMASK:
+		return IOCTL_OUT(arg, SOUND_MASK_MIC);
+	    case SOUND_MIXER_READ_DEVMASK:
+		return IOCTL_OUT(arg, SOUND_MASK_VOLUME | SOUND_MASK_MIC | SOUND_MASK_SPEAKER);
+	    case SOUND_MIXER_READ_STEREODEVS:
+		return IOCTL_OUT(arg, SOUND_MASK_VOLUME | SOUND_MASK_MIC);
+	    case SOUND_MIXER_READ_VOLUME:
+		return IOCTL_OUT(arg,
+			VOLUME_ATT_TO_VOXWARE(dmasound.volume_left) |
+			VOLUME_ATT_TO_VOXWARE(dmasound.volume_right) << 8);
+	    case SOUND_MIXER_READ_CAPS:
+		return IOCTL_OUT(arg, SOUND_CAP_EXCL_INPUT);
+	    case SOUND_MIXER_WRITE_MIC:
+		IOCTL_IN(arg, data);
+		tt_dmasnd.input_gain =
+			RECLEVEL_VOXWARE_TO_GAIN(data & 0xff) << 4 |
+			RECLEVEL_VOXWARE_TO_GAIN(data >> 8 & 0xff);
+		/* fall thru, return set value */
+	    case SOUND_MIXER_READ_MIC:
+		return IOCTL_OUT(arg,
+			RECLEVEL_GAIN_TO_VOXWARE(tt_dmasnd.input_gain >> 4 & 0xf) |
+			RECLEVEL_GAIN_TO_VOXWARE(tt_dmasnd.input_gain & 0xf) << 8);
+	}
+	return AtaMixerIoctl(cmd, arg);
+}
+
+static void AtaWriteSqSetup(void)
+{
+	write_sq_ignore_int = 0;
+}
+
+static void AtaSqOpen(void)
+{
+	write_sq_ignore_int = 1;
+}
+
+static int TTStateInfo(char *buffer)
+{
+	int len = 0;
+	len += sprintf(buffer+len, "\tsound.volume_left = %ddB [-40...0]\n",
+		       dmasound.volume_left);
+	len += sprintf(buffer+len, "\tsound.volume_right = %ddB [-40...0]\n",
+		       dmasound.volume_right);
+	len += sprintf(buffer+len, "\tsound.bass = %ddB [-12...+12]\n",
+		       dmasound.bass);
+	len += sprintf(buffer+len, "\tsound.treble = %ddB [-12...+12]\n",
+		       dmasound.treble);
+	return len;
+}
+
+static int FalconStateInfo(char *buffer)
+{
+	int len = 0;
+	len += sprintf(buffer+len, "\tsound.volume_left = %ddB [-22.5...0]\n",
+		       dmasound.volume_left);
+	len += sprintf(buffer+len, "\tsound.volume_right = %ddB [-22.5...0]\n",
+		       dmasound.volume_right);
+	return len;
+}
+
+
+/*** Machine definitions *****************************************************/
+
+
+static MACHINE machTT = {
+	name:		"Atari",
+	name2:		"TT",
+	open:		AtaOpen,
+	release:	AtaRelease,
+	dma_alloc:	AtaAlloc,
+	dma_free:	AtaFree,
+	irqinit:	AtaIrqInit,
+#ifdef MODULE
+	irqcleanup:	AtaIrqCleanUp,
+#endif /* MODULE */
+	init:		TTInit,
+	silence:	TTSilence,
+	setFormat:	TTSetFormat,
+	setVolume:	TTSetVolume,
+	setBass:	AtaSetBass,
+	setTreble:	AtaSetTreble,
+	setGain:	TTSetGain,
+	play:		AtaPlay,
+	mixer_init:	TTMixerInit,
+	mixer_ioctl:	TTMixerIoctl,
+	write_sq_setup:	AtaWriteSqSetup,
+	sq_open:	AtaSqOpen,
+	state_info:	TTStateInfo,
+	min_dsp_speed:	6258,
+};
+
+static MACHINE machFalcon = {
+	name:		"Atari",
+	name2:		"FALCON",
+	dma_alloc:	AtaAlloc,
+	dma_free:	AtaFree,
+	irqinit:	AtaIrqInit,
+#ifdef MODULE
+	irqcleanup:	AtaIrqCleanUp,
+#endif /* MODULE */
+	init:		FalconInit,
+	silence:	FalconSilence,
+	setFormat:	FalconSetFormat,
+	setVolume:	FalconSetVolume,
+	setBass:	AtaSetBass,
+	setTreble:	AtaSetTreble,
+	play:		AtaPlay,
+	mixer_init:	FalconMixerInit,
+	mixer_ioctl:	FalconMixerIoctl,
+	write_sq_setup:	AtaWriteSqSetup,
+	sq_open:	AtaSqOpen,
+	state_info:	FalconStateInfo,
+	min_dsp_speed:	8195,
+};
+
+
+/*** Config & Setup **********************************************************/
+
+
+static int __init dmasound_atari_init(void)
+{
+	if (MACH_IS_ATARI && ATARIHW_PRESENT(PCM_8BIT)) {
+	    if (ATARIHW_PRESENT(CODEC)) {
+		dmasound.mach = machFalcon;
+		is_falcon = 1;
+	    } else if (ATARIHW_PRESENT(MICROWIRE)) {
+		dmasound.mach = machTT;
+		is_falcon = 0;
+	    } else
+		return -ENODEV;
+	    if ((mfp.int_en_a & mfp.int_mk_a & 0x20) == 0)
+		return dmasound_init();
+	    else {
+		printk("DMA sound driver: Timer A interrupt already in use\n");
+		return -EBUSY;
+	    }
+	}
+	return -ENODEV;
+}
+
+static void __exit dmasound_atari_cleanup(void)
+{
+	dmasound_deinit();
+}
+
+module_init(dmasound_atari_init);
+module_exit(dmasound_atari_cleanup);

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)