[Feature] add GA346 baseline version

Change-Id: Ic62933698569507dcf98240cdf5d9931ae34348f
diff --git a/src/kernel/linux/v4.19/sound/oss/.gitignore b/src/kernel/linux/v4.19/sound/oss/.gitignore
new file mode 100644
index 0000000..12a3920
--- /dev/null
+++ b/src/kernel/linux/v4.19/sound/oss/.gitignore
@@ -0,0 +1,3 @@
+#Ignore generated files
+pss_boot.h
+trix_boot.h
diff --git a/src/kernel/linux/v4.19/sound/oss/dmasound/Kconfig b/src/kernel/linux/v4.19/sound/oss/dmasound/Kconfig
new file mode 100644
index 0000000..f456574
--- /dev/null
+++ b/src/kernel/linux/v4.19/sound/oss/dmasound/Kconfig
@@ -0,0 +1,45 @@
+config DMASOUND_ATARI
+	tristate "Atari DMA sound support"
+	depends on ATARI && SOUND
+	select DMASOUND
+	help
+	  If you want to use the internal audio of your Atari in Linux, answer
+	  Y to this question. This will provide a Sun-like /dev/audio,
+	  compatible with the Linux/i386 sound system. Otherwise, say N.
+
+	  This driver is also available as a module ( = code which can be
+	  inserted in and removed from the running kernel whenever you
+	  want). If you want to compile it as a module, say M here and read
+	  <file:Documentation/kbuild/modules.txt>.
+
+config DMASOUND_PAULA
+	tristate "Amiga DMA sound support"
+	depends on AMIGA && SOUND
+	select DMASOUND
+	help
+	  If you want to use the internal audio of your Amiga in Linux, answer
+	  Y to this question. This will provide a Sun-like /dev/audio,
+	  compatible with the Linux/i386 sound system. Otherwise, say N.
+
+	  This driver is also available as a module ( = code which can be
+	  inserted in and removed from the running kernel whenever you
+	  want). If you want to compile it as a module, say M here and read
+	  <file:Documentation/kbuild/modules.txt>.
+
+config DMASOUND_Q40
+	tristate "Q40 sound support"
+	depends on Q40 && SOUND
+	select DMASOUND
+	help
+	  If you want to use the internal audio of your Q40 in Linux, answer
+	  Y to this question. This will provide a Sun-like /dev/audio,
+	  compatible with the Linux/i386 sound system. Otherwise, say N.
+
+	  This driver is also available as a module ( = code which can be
+	  inserted in and removed from the running kernel whenever you
+	  want). If you want to compile it as a module, say M here and read
+	  <file:Documentation/kbuild/modules.txt>.
+
+config DMASOUND
+	tristate
+	select SOUND_OSS_CORE
diff --git a/src/kernel/linux/v4.19/sound/oss/dmasound/Makefile b/src/kernel/linux/v4.19/sound/oss/dmasound/Makefile
new file mode 100644
index 0000000..3c15316
--- /dev/null
+++ b/src/kernel/linux/v4.19/sound/oss/dmasound/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for the DMA sound driver
+#
+
+obj-$(CONFIG_DMASOUND_ATARI)	+= dmasound_core.o dmasound_atari.o
+obj-$(CONFIG_DMASOUND_PAULA)	+= dmasound_core.o dmasound_paula.o
+obj-$(CONFIG_DMASOUND_Q40)	+= dmasound_core.o dmasound_q40.o
diff --git a/src/kernel/linux/v4.19/sound/oss/dmasound/dmasound.h b/src/kernel/linux/v4.19/sound/oss/dmasound/dmasound.h
new file mode 100644
index 0000000..c1c52b4
--- /dev/null
+++ b/src/kernel/linux/v4.19/sound/oss/dmasound/dmasound.h
@@ -0,0 +1,262 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _dmasound_h_
+/*
+ *  linux/sound/oss/dmasound/dmasound.h
+ *
+ *
+ *  Minor numbers for the sound driver.
+ *
+ *  Unfortunately Creative called the codec chip of SB as a DSP. For this
+ *  reason the /dev/dsp is reserved for digitized audio use. There is a
+ *  device for true DSP processors but it will be called something else.
+ *  In v3.0 it's /dev/sndproc but this could be a temporary solution.
+ */
+#define _dmasound_h_
+
+#include <linux/types.h>
+
+#define SND_NDEVS	256	/* Number of supported devices */
+#define SND_DEV_CTL	0	/* Control port /dev/mixer */
+#define SND_DEV_SEQ	1	/* Sequencer output /dev/sequencer (FM
+				   synthesizer and MIDI output) */
+#define SND_DEV_MIDIN	2	/* Raw midi access */
+#define SND_DEV_DSP	3	/* Digitized voice /dev/dsp */
+#define SND_DEV_AUDIO	4	/* Sparc compatible /dev/audio */
+#define SND_DEV_DSP16	5	/* Like /dev/dsp but 16 bits/sample */
+#define SND_DEV_STATUS	6	/* /dev/sndstat */
+/* #7 not in use now. Was in 2.4. Free for use after v3.0. */
+#define SND_DEV_SEQ2	8	/* /dev/sequencer, level 2 interface */
+#define SND_DEV_SNDPROC 9	/* /dev/sndproc for programmable devices */
+#define SND_DEV_PSS	SND_DEV_SNDPROC
+
+/* switch on various prinks */
+#define DEBUG_DMASOUND 1
+
+#define MAX_AUDIO_DEV	5
+#define MAX_MIXER_DEV	4
+#define MAX_SYNTH_DEV	3
+#define MAX_MIDI_DEV	6
+#define MAX_TIMER_DEV	3
+
+#define MAX_CATCH_RADIUS	10
+
+#define le2be16(x)	(((x)<<8 & 0xff00) | ((x)>>8 & 0x00ff))
+#define le2be16dbl(x)	(((x)<<8 & 0xff00ff00) | ((x)>>8 & 0x00ff00ff))
+
+#define IOCTL_IN(arg, ret) \
+	do { int error = get_user(ret, (int __user *)(arg)); \
+		if (error) return error; \
+	} while (0)
+#define IOCTL_OUT(arg, ret)	ioctl_return((int __user *)(arg), ret)
+
+static inline int ioctl_return(int __user *addr, int value)
+{
+	return value < 0 ? value : put_user(value, addr);
+}
+
+
+    /*
+     *  Configuration
+     */
+
+#undef HAS_8BIT_TABLES
+
+#if defined(CONFIG_DMASOUND_ATARI) || defined(CONFIG_DMASOUND_ATARI_MODULE) ||\
+    defined(CONFIG_DMASOUND_PAULA) || defined(CONFIG_DMASOUND_PAULA_MODULE) ||\
+    defined(CONFIG_DMASOUND_Q40) || defined(CONFIG_DMASOUND_Q40_MODULE)
+#define HAS_8BIT_TABLES
+#define MIN_BUFFERS	4
+#define MIN_BUFSIZE	(1<<12)	/* in bytes (- where does this come from ?) */
+#define MIN_FRAG_SIZE	8	/* not 100% sure about this */
+#define MAX_BUFSIZE	(1<<17)	/* Limit for Amiga is 128 kb */
+#define MAX_FRAG_SIZE	15	/* allow *4 for mono-8 => stereo-16 (for multi) */
+
+#else /* is pmac and multi is off */
+
+#define MIN_BUFFERS	2
+#define MIN_BUFSIZE	(1<<8)	/* in bytes */
+#define MIN_FRAG_SIZE	8
+#define MAX_BUFSIZE	(1<<18)	/* this is somewhat arbitrary for pmac */
+#define MAX_FRAG_SIZE	16	/* need to allow *4 for mono-8 => stereo-16 */
+#endif
+
+#define DEFAULT_N_BUFFERS 4
+#define DEFAULT_BUFF_SIZE (1<<15)
+
+    /*
+     *  Initialization
+     */
+
+extern int dmasound_init(void);
+#ifdef MODULE
+extern void dmasound_deinit(void);
+#else
+#define dmasound_deinit()	do { } while (0)
+#endif
+
+/* description of the set-up applies to either hard or soft settings */
+
+typedef struct {
+    int format;		/* AFMT_* */
+    int stereo;		/* 0 = mono, 1 = stereo */
+    int size;		/* 8/16 bit*/
+    int speed;		/* speed */
+} SETTINGS;
+
+    /*
+     *  Machine definitions
+     */
+
+typedef struct {
+    const char *name;
+    const char *name2;
+    struct module *owner;
+    void *(*dma_alloc)(unsigned int, gfp_t);
+    void (*dma_free)(void *, unsigned int);
+    int (*irqinit)(void);
+#ifdef MODULE
+    void (*irqcleanup)(void);
+#endif
+    void (*init)(void);
+    void (*silence)(void);
+    int (*setFormat)(int);
+    int (*setVolume)(int);
+    int (*setBass)(int);
+    int (*setTreble)(int);
+    int (*setGain)(int);
+    void (*play)(void);
+    void (*record)(void);		/* optional */
+    void (*mixer_init)(void);		/* optional */
+    int (*mixer_ioctl)(u_int, u_long);	/* optional */
+    int (*write_sq_setup)(void);	/* optional */
+    int (*read_sq_setup)(void);		/* optional */
+    int (*sq_open)(fmode_t);		/* optional */
+    int (*state_info)(char *, size_t);	/* optional */
+    void (*abort_read)(void);		/* optional */
+    int min_dsp_speed;
+    int max_dsp_speed;
+    int version ;
+    int hardware_afmts ;		/* OSS says we only return h'ware info */
+					/* when queried via SNDCTL_DSP_GETFMTS */
+    int capabilities ;		/* low-level reply to SNDCTL_DSP_GETCAPS */
+    SETTINGS default_hard ;	/* open() or init() should set something valid */
+    SETTINGS default_soft ;	/* you can make it look like old OSS, if you want to */
+} MACHINE;
+
+    /*
+     *  Low level stuff
+     */
+
+typedef struct {
+    ssize_t (*ct_ulaw)(const u_char __user *, size_t, u_char *, ssize_t *, ssize_t);
+    ssize_t (*ct_alaw)(const u_char __user *, size_t, u_char *, ssize_t *, ssize_t);
+    ssize_t (*ct_s8)(const u_char __user *, size_t, u_char *, ssize_t *, ssize_t);
+    ssize_t (*ct_u8)(const u_char __user *, size_t, u_char *, ssize_t *, ssize_t);
+    ssize_t (*ct_s16be)(const u_char __user *, size_t, u_char *, ssize_t *, ssize_t);
+    ssize_t (*ct_u16be)(const u_char __user *, size_t, u_char *, ssize_t *, ssize_t);
+    ssize_t (*ct_s16le)(const u_char __user *, size_t, u_char *, ssize_t *, ssize_t);
+    ssize_t (*ct_u16le)(const u_char __user *, size_t, u_char *, ssize_t *, ssize_t);
+} TRANS;
+
+struct sound_settings {
+    MACHINE mach;	/* machine dependent things */
+    SETTINGS hard;	/* hardware settings */
+    SETTINGS soft;	/* software settings */
+    SETTINGS dsp;	/* /dev/dsp default settings */
+    TRANS *trans_write;	/* supported translations */
+    int volume_left;	/* volume (range is machine dependent) */
+    int volume_right;
+    int bass;		/* tone (range is machine dependent) */
+    int treble;
+    int gain;
+    int minDev;		/* minor device number currently open */
+    spinlock_t lock;
+};
+
+extern struct sound_settings dmasound;
+
+#ifdef HAS_8BIT_TABLES
+extern char dmasound_ulaw2dma8[];
+extern char dmasound_alaw2dma8[];
+#endif
+
+    /*
+     *  Mid level stuff
+     */
+
+static inline int dmasound_set_volume(int volume)
+{
+	return dmasound.mach.setVolume(volume);
+}
+
+static inline int dmasound_set_bass(int bass)
+{
+	return dmasound.mach.setBass ? dmasound.mach.setBass(bass) : 50;
+}
+
+static inline int dmasound_set_treble(int treble)
+{
+	return dmasound.mach.setTreble ? dmasound.mach.setTreble(treble) : 50;
+}
+
+static inline int dmasound_set_gain(int gain)
+{
+	return dmasound.mach.setGain ? dmasound.mach.setGain(gain) : 100;
+}
+
+
+    /*
+     * Sound queue stuff, the heart of the driver
+     */
+
+struct sound_queue {
+    /* buffers allocated for this queue */
+    int numBufs;		/* real limits on what the user can have */
+    int bufSize;		/* in bytes */
+    char **buffers;
+
+    /* current parameters */
+    int locked ;		/* params cannot be modified when != 0 */
+    int user_frags ;		/* user requests this many */
+    int user_frag_size ;	/* of this size */
+    int max_count;		/* actual # fragments <= numBufs */
+    int block_size;		/* internal block size in bytes */
+    int max_active;		/* in-use fragments <= max_count */
+
+    /* it shouldn't be necessary to declare any of these volatile */
+    int front, rear, count;
+    int rear_size;
+    /*
+     *	The use of the playing field depends on the hardware
+     *
+     *	Atari, PMac: The number of frames that are loaded/playing
+     *
+     *	Amiga: Bit 0 is set: a frame is loaded
+     *	       Bit 1 is set: a frame is playing
+     */
+    int active;
+    wait_queue_head_t action_queue, open_queue, sync_queue;
+    int non_blocking;
+    int busy, syncing, xruns, died;
+};
+
+#define WAKE_UP(queue)		(wake_up_interruptible(&queue))
+
+extern struct sound_queue dmasound_write_sq;
+#define write_sq	dmasound_write_sq
+
+extern int dmasound_catchRadius;
+#define catchRadius	dmasound_catchRadius
+
+/* define the value to be put in the byte-swap reg in mac-io
+   when we want it to swap for us.
+*/
+#define BS_VAL 1
+
+#define SW_INPUT_VOLUME_SCALE	4
+#define SW_INPUT_VOLUME_DEFAULT	(128 / SW_INPUT_VOLUME_SCALE)
+
+extern int expand_read_bal;	/* Balance factor for reading */
+extern uint software_input_volume; /* software implemented recording volume! */
+
+#endif /* _dmasound_h_ */
diff --git a/src/kernel/linux/v4.19/sound/oss/dmasound/dmasound_atari.c b/src/kernel/linux/v4.19/sound/oss/dmasound/dmasound_atari.c
new file mode 100644
index 0000000..a1a2979
--- /dev/null
+++ b/src/kernel/linux/v4.19/sound/oss/dmasound/dmasound_atari.c
@@ -0,0 +1,1620 @@
+/*
+ *  linux/sound/oss/dmasound/dmasound_atari.c
+ *
+ *  Atari TT and Falcon DMA Sound Driver
+ *
+ *  See linux/sound/oss/dmasound/dmasound_core.c for copyright and credits
+ *  prior to 28/01/2001
+ *
+ *  28/01/2001 [0.1] Iain Sandoe
+ *		     - added versioning
+ *		     - put in and populated the hardware_afmts field.
+ *             [0.2] - put in SNDCTL_DSP_GETCAPS value.
+ *  01/02/2001 [0.3] - put in default hard/soft settings.
+ */
+
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/soundcard.h>
+#include <linux/mm.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+
+#include <linux/uaccess.h>
+#include <asm/atariints.h>
+#include <asm/atari_stram.h>
+
+#include "dmasound.h"
+
+#define DMASOUND_ATARI_REVISION 0
+#define DMASOUND_ATARI_EDITION 3
+
+extern void atari_microwire_cmd(int cmd);
+
+static int is_falcon;
+static int write_sq_ignore_int;	/* ++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 __user *userPtr, size_t userCount,
+			  u_char frame[], ssize_t *frameUsed,
+			  ssize_t frameLeft);
+static ssize_t ata_ct_s8(const u_char __user *userPtr, size_t userCount,
+			 u_char frame[], ssize_t *frameUsed,
+			 ssize_t frameLeft);
+static ssize_t ata_ct_u8(const u_char __user *userPtr, size_t userCount,
+			 u_char frame[], ssize_t *frameUsed,
+			 ssize_t frameLeft);
+static ssize_t ata_ct_s16be(const u_char __user *userPtr, size_t userCount,
+			    u_char frame[], ssize_t *frameUsed,
+			    ssize_t frameLeft);
+static ssize_t ata_ct_u16be(const u_char __user *userPtr, size_t userCount,
+			    u_char frame[], ssize_t *frameUsed,
+			    ssize_t frameLeft);
+static ssize_t ata_ct_s16le(const u_char __user *userPtr, size_t userCount,
+			    u_char frame[], ssize_t *frameUsed,
+			    ssize_t frameLeft);
+static ssize_t ata_ct_u16le(const u_char __user *userPtr, size_t userCount,
+			    u_char frame[], ssize_t *frameUsed,
+			    ssize_t frameLeft);
+static ssize_t ata_ctx_law(const u_char __user *userPtr, size_t userCount,
+			   u_char frame[], ssize_t *frameUsed,
+			   ssize_t frameLeft);
+static ssize_t ata_ctx_s8(const u_char __user *userPtr, size_t userCount,
+			  u_char frame[], ssize_t *frameUsed,
+			  ssize_t frameLeft);
+static ssize_t ata_ctx_u8(const u_char __user *userPtr, size_t userCount,
+			  u_char frame[], ssize_t *frameUsed,
+			  ssize_t frameLeft);
+static ssize_t ata_ctx_s16be(const u_char __user *userPtr, size_t userCount,
+			     u_char frame[], ssize_t *frameUsed,
+			     ssize_t frameLeft);
+static ssize_t ata_ctx_u16be(const u_char __user *userPtr, size_t userCount,
+			     u_char frame[], ssize_t *frameUsed,
+			     ssize_t frameLeft);
+static ssize_t ata_ctx_s16le(const u_char __user *userPtr, size_t userCount,
+			     u_char frame[], ssize_t *frameUsed,
+			     ssize_t frameLeft);
+static ssize_t ata_ctx_u16le(const u_char __user *userPtr, size_t userCount,
+			     u_char frame[], ssize_t *frameUsed,
+			     ssize_t frameLeft);
+
+
+/*** Low level stuff *********************************************************/
+
+
+static void *AtaAlloc(unsigned int size, gfp_t 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 irqreturn_t AtaInterrupt(int irq, void *dummy);
+
+/*** 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 int AtaWriteSqSetup(void);
+static int AtaSqOpen(fmode_t mode);
+static int TTStateInfo(char *buffer, size_t space);
+static int FalconStateInfo(char *buffer, size_t space);
+
+
+/*** Translations ************************************************************/
+
+
+static ssize_t ata_ct_law(const u_char __user *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_t(unsigned long, 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 __user *userPtr, size_t userCount,
+			 u_char frame[], ssize_t *frameUsed,
+			 ssize_t frameLeft)
+{
+	ssize_t count, used;
+	void *p = &frame[*frameUsed];
+
+	count = min_t(unsigned long, 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 __user *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_t(unsigned long, 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_t(unsigned long, userCount, frameLeft)>>1;
+		used = count*2;
+		while (count > 0) {
+			u_short data;
+			if (get_user(data, (u_short __user *)userPtr))
+				return -EFAULT;
+			userPtr += 2;
+			*p++ = data ^ 0x8080;
+			count--;
+		}
+	}
+	*frameUsed += used;
+	return used;
+}
+
+
+static ssize_t ata_ct_s16be(const u_char __user *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_t(unsigned long, userCount, frameLeft)>>1;
+		used = count*2;
+		while (count > 0) {
+			u_short data;
+			if (get_user(data, (u_short __user *)userPtr))
+				return -EFAULT;
+			userPtr += 2;
+			*p++ = data;
+			*p++ = data;
+			count--;
+		}
+		*frameUsed += used*2;
+	} else {
+		void *p = (u_short *)&frame[*frameUsed];
+		count = min_t(unsigned long, 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 __user *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_t(unsigned long, userCount, frameLeft)>>1;
+		used = count*2;
+		while (count > 0) {
+			u_short data;
+			if (get_user(data, (u_short __user *)userPtr))
+				return -EFAULT;
+			userPtr += 2;
+			data ^= 0x8000;
+			*p++ = data;
+			*p++ = data;
+			count--;
+		}
+		*frameUsed += used*2;
+	} else {
+		u_long *p = (u_long *)&frame[*frameUsed];
+		count = min_t(unsigned long, userCount, frameLeft)>>2;
+		used = count*4;
+		while (count > 0) {
+			u_int data;
+			if (get_user(data, (u_int __user *)userPtr))
+				return -EFAULT;
+			userPtr += 4;
+			*p++ = data ^ 0x80008000;
+			count--;
+		}
+		*frameUsed += used;
+	}
+	return used;
+}
+
+
+static ssize_t ata_ct_s16le(const u_char __user *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_t(unsigned long, userCount, frameLeft)>>1;
+		used = count*2;
+		while (count > 0) {
+			u_short data;
+			if (get_user(data, (u_short __user *)userPtr))
+				return -EFAULT;
+			userPtr += 2;
+			data = le2be16(data);
+			*p++ = data;
+			*p++ = data;
+			count--;
+		}
+		*frameUsed += used*2;
+	} else {
+		u_long *p = (u_long *)&frame[*frameUsed];
+		count = min_t(unsigned long, userCount, frameLeft)>>2;
+		used = count*4;
+		while (count > 0) {
+			u_long data;
+			if (get_user(data, (u_int __user *)userPtr))
+				return -EFAULT;
+			userPtr += 4;
+			data = le2be16dbl(data);
+			*p++ = data;
+			count--;
+		}
+		*frameUsed += used;
+	}
+	return used;
+}
+
+
+static ssize_t ata_ct_u16le(const u_char __user *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_t(unsigned long, userCount, frameLeft)>>1;
+		used = count*2;
+		while (count > 0) {
+			u_short data;
+			if (get_user(data, (u_short __user *)userPtr))
+				return -EFAULT;
+			userPtr += 2;
+			data = le2be16(data) ^ 0x8000;
+			*p++ = data;
+			*p++ = data;
+		}
+		*frameUsed += used*2;
+	} else {
+		u_long *p = (u_long *)&frame[*frameUsed];
+		count = min_t(unsigned long, userCount, frameLeft)>>2;
+		used = count;
+		while (count > 0) {
+			u_long data;
+			if (get_user(data, (u_int __user *)userPtr))
+				return -EFAULT;
+			userPtr += 4;
+			data = le2be16dbl(data) ^ 0x80008000;
+			*p++ = data;
+			count--;
+		}
+		*frameUsed += used;
+	}
+	return used;
+}
+
+
+static ssize_t ata_ctx_law(const u_char __user *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 __user *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 __user *)userPtr))
+					return -EFAULT;
+				userPtr += 2;
+				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 __user *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 __user *)userPtr))
+					return -EFAULT;
+				userPtr += 2;
+				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 __user *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 __user *)userPtr))
+					return -EFAULT;
+				userPtr += 2;
+				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 __user *)userPtr))
+					return -EFAULT;
+				userPtr += 4;
+				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 __user *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 __user *)userPtr))
+					return -EFAULT;
+				userPtr += 2;
+				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 __user *)userPtr))
+					return -EFAULT;
+				userPtr += 4;
+				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 __user *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 __user *)userPtr))
+					return -EFAULT;
+				userPtr += 2;
+				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 __user *)userPtr))
+					return -EFAULT;
+				userPtr += 4;
+				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 __user *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 __user *)userPtr))
+					return -EFAULT;
+				userPtr += 2;
+				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 __user *)userPtr))
+					return -EFAULT;
+				userPtr += 4;
+				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 *AtaAlloc(unsigned int size, gfp_t flags)
+{
+	return atari_stram_alloc(size, "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. */
+	st_mfp.tim_ct_a = 0;	/* ++roman: Stop timer before programming! */
+	st_mfp.tim_dt_a = 1;	/* Cause interrupt after first event. */
+	st_mfp.tim_ct_a = 8;	/* Turn on event counting. */
+	/* Register interrupt handler. */
+	if (request_irq(IRQ_MFP_TIMA, AtaInterrupt, 0, "DMA sound",
+			AtaInterrupt))
+		return 0;
+	st_mfp.int_en_a |= 0x20;	/* Turn interrupt on. */
+	st_mfp.int_mk_a |= 0x20;
+	return 1;
+}
+
+#ifdef MODULE
+static void AtaIrqCleanUp(void)
+{
+	st_mfp.tim_ct_a = 0;		/* stop timer */
+	st_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 < ARRAY_SIZE(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 < ARRAY_SIZE(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 irqreturn_t AtaInterrupt(int irq, void *dummy)
+{
+#if 0
+	/* ++TeSche: if you should want to test this... */
+	static int cnt;
+	if (write_sq.active == 2)
+		if (++cnt == 10) {
+			/* simulate losing an interrupt */
+			cnt = 0;
+			return IRQ_HANDLED;
+		}
+#endif
+	spin_lock(&dmasound.lock);
+	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;
+		goto out;
+	}
+
+	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);
+		goto out;
+	}
+
+	/* 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. */
+out:
+	spin_unlock(&dmasound.lock);
+	return IRQ_HANDLED;
+}
+
+
+/*** 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;
+	unsigned long flags;
+	switch (cmd) {
+	    case SOUND_MIXER_READ_SPEAKER:
+		    if (is_falcon || MACH_IS_TT) {
+			    int porta;
+			    spin_lock_irqsave(&dmasound.lock, flags);
+			    sound_ym.rd_data_reg_sel = 14;
+			    porta = sound_ym.rd_data_reg_sel;
+			    spin_unlock_irqrestore(&dmasound.lock, flags);
+			    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);
+			    spin_lock_irqsave(&dmasound.lock, flags);
+			    sound_ym.rd_data_reg_sel = 14;
+			    porta = (sound_ym.rd_data_reg_sel & ~0x40) |
+				    (data < 50 ? 0x40 : 0);
+			    sound_ym.wd_data = porta;
+			    spin_unlock_irqrestore(&dmasound.lock, flags);
+			    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 int AtaWriteSqSetup(void)
+{
+	write_sq_ignore_int = 0;
+	return 0 ;
+}
+
+static int AtaSqOpen(fmode_t mode)
+{
+	write_sq_ignore_int = 1;
+	return 0 ;
+}
+
+static int TTStateInfo(char *buffer, size_t space)
+{
+	int len = 0;
+	len += sprintf(buffer+len, "\tvol left  %ddB [-40...  0]\n",
+		       dmasound.volume_left);
+	len += sprintf(buffer+len, "\tvol right %ddB [-40...  0]\n",
+		       dmasound.volume_right);
+	len += sprintf(buffer+len, "\tbass      %ddB [-12...+12]\n",
+		       dmasound.bass);
+	len += sprintf(buffer+len, "\ttreble    %ddB [-12...+12]\n",
+		       dmasound.treble);
+	if (len >= space) {
+		printk(KERN_ERR "dmasound_atari: overflowed state buffer alloc.\n") ;
+		len = space ;
+	}
+	return len;
+}
+
+static int FalconStateInfo(char *buffer, size_t space)
+{
+	int len = 0;
+	len += sprintf(buffer+len, "\tvol left  %ddB [-22.5 ... 0]\n",
+		       dmasound.volume_left);
+	len += sprintf(buffer+len, "\tvol right %ddB [-22.5 ... 0]\n",
+		       dmasound.volume_right);
+	if (len >= space) {
+		printk(KERN_ERR "dmasound_atari: overflowed state buffer alloc.\n") ;
+		len = space ;
+	}
+	return len;
+}
+
+
+/*** Machine definitions *****************************************************/
+
+static SETTINGS def_hard_falcon = {
+	.format		= AFMT_S8,
+	.stereo		= 0,
+	.size		= 8,
+	.speed		= 8195
+} ;
+
+static SETTINGS def_hard_tt = {
+	.format	= AFMT_S8,
+	.stereo	= 0,
+	.size	= 8,
+	.speed	= 12517
+} ;
+
+static SETTINGS def_soft = {
+	.format	= AFMT_U8,
+	.stereo	= 0,
+	.size	= 8,
+	.speed	= 8000
+} ;
+
+static __initdata MACHINE machTT = {
+	.name		= "Atari",
+	.name2		= "TT",
+	.owner		= THIS_MODULE,
+	.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,
+	.version	= ((DMASOUND_ATARI_REVISION<<8) | DMASOUND_ATARI_EDITION),
+	.hardware_afmts	= AFMT_S8,  /* h'ware-supported formats *only* here */
+	.capabilities	=  DSP_CAP_BATCH	/* As per SNDCTL_DSP_GETCAPS */
+};
+
+static __initdata 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,
+	.version	= ((DMASOUND_ATARI_REVISION<<8) | DMASOUND_ATARI_EDITION),
+	.hardware_afmts	= (AFMT_S8 | AFMT_S16_BE), /* h'ware-supported formats *only* here */
+	.capabilities	=  DSP_CAP_BATCH	/* As per SNDCTL_DSP_GETCAPS */
+};
+
+
+/*** Config & Setup **********************************************************/
+
+
+static int __init dmasound_atari_init(void)
+{
+	if (MACH_IS_ATARI && ATARIHW_PRESENT(PCM_8BIT)) {
+	    if (ATARIHW_PRESENT(CODEC)) {
+		dmasound.mach = machFalcon;
+		dmasound.mach.default_soft = def_soft ;
+		dmasound.mach.default_hard = def_hard_falcon ;
+		is_falcon = 1;
+	    } else if (ATARIHW_PRESENT(MICROWIRE)) {
+		dmasound.mach = machTT;
+		dmasound.mach.default_soft = def_soft ;
+		dmasound.mach.default_hard = def_hard_tt ;
+		is_falcon = 0;
+	    } else
+		return -ENODEV;
+	    if ((st_mfp.int_en_a & st_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);
+MODULE_LICENSE("GPL");
diff --git a/src/kernel/linux/v4.19/sound/oss/dmasound/dmasound_core.c b/src/kernel/linux/v4.19/sound/oss/dmasound/dmasound_core.c
new file mode 100644
index 0000000..fc9bcd4
--- /dev/null
+++ b/src/kernel/linux/v4.19/sound/oss/dmasound/dmasound_core.c
@@ -0,0 +1,1600 @@
+/*
+ *  linux/sound/oss/dmasound/dmasound_core.c
+ *
+ *
+ *  OSS/Free compatible Atari TT/Falcon and Amiga DMA sound driver for
+ *  Linux/m68k
+ *  Extended to support Power Macintosh for Linux/ppc by Paul Mackerras
+ *
+ *  (c) 1995 by Michael Schlueter & Michael Marte
+ *
+ *  Michael Schlueter (michael@duck.syd.de) did the basic structure of the VFS
+ *  interface and the u-law to signed byte conversion.
+ *
+ *  Michael Marte (marte@informatik.uni-muenchen.de) did the sound queue,
+ *  /dev/mixer, /dev/sndstat and complemented the VFS interface. He would like
+ *  to thank:
+ *    - Michael Schlueter for initial ideas and documentation on the MFP and
+ *	the DMA sound hardware.
+ *    - Therapy? for their CD 'Troublegum' which really made me rock.
+ *
+ *  /dev/sndstat is based on code by Hannu Savolainen, the author of the
+ *  VoxWare family of drivers.
+ *
+ *  This file is subject to the terms and conditions of the GNU General Public
+ *  License.  See the file COPYING in the main directory of this archive
+ *  for more details.
+ *
+ *  History:
+ *
+ *	1995/8/25	First release
+ *
+ *	1995/9/02	Roman Hodek:
+ *			  - Fixed atari_stram_alloc() call, the timer
+ *			    programming and several race conditions
+ *	1995/9/14	Roman Hodek:
+ *			  - After some discussion with Michael Schlueter,
+ *			    revised the interrupt disabling
+ *			  - Slightly speeded up U8->S8 translation by using
+ *			    long operations where possible
+ *			  - Added 4:3 interpolation for /dev/audio
+ *
+ *	1995/9/20	Torsten Scherer:
+ *			  - Fixed a bug in sq_write and changed /dev/audio
+ *			    converting to play at 12517Hz instead of 6258Hz.
+ *
+ *	1995/9/23	Torsten Scherer:
+ *			  - Changed sq_interrupt() and sq_play() to pre-program
+ *			    the DMA for another frame while there's still one
+ *			    running. This allows the IRQ response to be
+ *			    arbitrarily delayed and playing will still continue.
+ *
+ *	1995/10/14	Guenther Kelleter, Torsten Scherer:
+ *			  - Better support for Falcon audio (the Falcon doesn't
+ *			    raise an IRQ at the end of a frame, but at the
+ *			    beginning instead!). uses 'if (codec_dma)' in lots
+ *			    of places to simply switch between Falcon and TT
+ *			    code.
+ *
+ *	1995/11/06	Torsten Scherer:
+ *			  - Started introducing a hardware abstraction scheme
+ *			    (may perhaps also serve for Amigas?)
+ *			  - Can now play samples at almost all frequencies by
+ *			    means of a more generalized expand routine
+ *			  - Takes a good deal of care to cut data only at
+ *			    sample sizes
+ *			  - Buffer size is now a kernel runtime option
+ *			  - Implemented fsync() & several minor improvements
+ *			Guenther Kelleter:
+ *			  - Useful hints and bug fixes
+ *			  - Cross-checked it for Falcons
+ *
+ *	1996/3/9	Geert Uytterhoeven:
+ *			  - Support added for Amiga, A-law, 16-bit little
+ *			    endian.
+ *			  - Unification to drivers/sound/dmasound.c.
+ *
+ *	1996/4/6	Martin Mitchell:
+ *			  - Updated to 1.3 kernel.
+ *
+ *	1996/6/13       Topi Kanerva:
+ *			  - Fixed things that were broken (mainly the amiga
+ *			    14-bit routines)
+ *			  - /dev/sndstat shows now the real hardware frequency
+ *			  - The lowpass filter is disabled by default now
+ *
+ *	1996/9/25	Geert Uytterhoeven:
+ *			  - Modularization
+ *
+ *	1998/6/10	Andreas Schwab:
+ *			  - Converted to use sound_core
+ *
+ *	1999/12/28	Richard Zidlicky:
+ *			  - Added support for Q40
+ *
+ *	2000/2/27	Geert Uytterhoeven:
+ *			  - Clean up and split the code into 4 parts:
+ *			      o dmasound_core: machine-independent code
+ *			      o dmasound_atari: Atari TT and Falcon support
+ *			      o dmasound_awacs: Apple PowerMac support
+ *			      o dmasound_paula: Amiga support
+ *
+ *	2000/3/25	Geert Uytterhoeven:
+ *			  - Integration of dmasound_q40
+ *			  - Small clean ups
+ *
+ *	2001/01/26 [1.0] Iain Sandoe
+ *			  - make /dev/sndstat show revision & edition info.
+ *			  - since dmasound.mach.sq_setup() can fail on pmac
+ *			    its type has been changed to int and the returns
+ *			    are checked.
+ *		   [1.1]  - stop missing translations from being called.
+ *	2001/02/08 [1.2]  - remove unused translation tables & move machine-
+ *			    specific tables to low-level.
+ *			  - return correct info. for SNDCTL_DSP_GETFMTS.
+ *		   [1.3]  - implement SNDCTL_DSP_GETCAPS fully.
+ *		   [1.4]  - make /dev/sndstat text length usage deterministic.
+ *			  - make /dev/sndstat call to low-level
+ *			    dmasound.mach.state_info() pass max space to ll driver.
+ *			  - tidy startup banners and output info.
+ *		   [1.5]  - tidy up a little (removed some unused #defines in
+ *			    dmasound.h)
+ *			  - fix up HAS_RECORD conditionalisation.
+ *			  - add record code in places it is missing...
+ *			  - change buf-sizes to bytes to allow < 1kb for pmac
+ *			    if user param entry is < 256 the value is taken to
+ *			    be in kb > 256 is taken to be in bytes.
+ *			  - make default buff/frag params conditional on
+ *			    machine to allow smaller values for pmac.
+ *			  - made the ioctls, read & write comply with the OSS
+ *			    rules on setting params.
+ *			  - added parsing of _setup() params for record.
+ *	2001/04/04 [1.6]  - fix bug where sample rates higher than maximum were
+ *			    being reported as OK.
+ *			  - fix open() to return -EBUSY as per OSS doc. when
+ *			    audio is in use - this is independent of O_NOBLOCK.
+ *			  - fix bug where SNDCTL_DSP_POST was blocking.
+ */
+
+ /* Record capability notes 30/01/2001:
+  * At present these observations apply only to pmac LL driver (the only one
+  * that can do record, at present).  However, if other LL drivers for machines
+  * with record are added they may apply.
+  *
+  * The fragment parameters for the record and play channels are separate.
+  * However, if the driver is opened O_RDWR there is no way (in the current OSS
+  * API) to specify their values independently for the record and playback
+  * channels.  Since the only common factor between the input & output is the
+  * sample rate (on pmac) it should be possible to open /dev/dspX O_WRONLY and
+  * /dev/dspY O_RDONLY.  The input & output channels could then have different
+  * characteristics (other than the first that sets sample rate claiming the
+  * right to set it for ever).  As it stands, the format, channels, number of
+  * bits & sample rate are assumed to be common.  In the future perhaps these
+  * should be the responsibility of the LL driver - and then if a card really
+  * does not share items between record & playback they can be specified
+  * separately.
+*/
+
+/* Thread-safeness of shared_resources notes: 31/01/2001
+ * If the user opens O_RDWR and then splits record & play between two threads
+ * both of which inherit the fd - and then starts changing things from both
+ * - we will have difficulty telling.
+ *
+ * It's bad application coding - but ...
+ * TODO: think about how to sort this out... without bogging everything down in
+ * semaphores.
+ *
+ * Similarly, the OSS spec says "all changes to parameters must be between
+ * open() and the first read() or write(). - and a bit later on (by
+ * implication) "between SNDCTL_DSP_RESET and the first read() or write() after
+ * it".  If the app is multi-threaded and this rule is broken between threads
+ * we will have trouble spotting it - and the fault will be rather obscure :-(
+ *
+ * We will try and put out at least a kmsg if we see it happen... but I think
+ * it will be quite hard to trap it with an -EXXX return... because we can't
+ * see the fault until after the damage is done.
+*/
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/sound.h>
+#include <linux/init.h>
+#include <linux/soundcard.h>
+#include <linux/poll.h>
+#include <linux/mutex.h>
+#include <linux/sched/signal.h>
+
+#include <linux/uaccess.h>
+
+#include "dmasound.h"
+
+#define DMASOUND_CORE_REVISION 1
+#define DMASOUND_CORE_EDITION 6
+
+    /*
+     *  Declarations
+     */
+
+static DEFINE_MUTEX(dmasound_core_mutex);
+int dmasound_catchRadius = 0;
+module_param(dmasound_catchRadius, int, 0);
+
+static unsigned int numWriteBufs = DEFAULT_N_BUFFERS;
+module_param(numWriteBufs, int, 0);
+static unsigned int writeBufSize = DEFAULT_BUFF_SIZE ;	/* in bytes */
+module_param(writeBufSize, int, 0);
+
+MODULE_LICENSE("GPL");
+
+#ifdef MODULE
+static int sq_unit = -1;
+static int mixer_unit = -1;
+static int state_unit = -1;
+static int irq_installed;
+#endif /* MODULE */
+
+/* control over who can modify resources shared between play/record */
+static fmode_t shared_resource_owner;
+static int shared_resources_initialised;
+
+    /*
+     *  Mid level stuff
+     */
+
+struct sound_settings dmasound = {
+	.lock = __SPIN_LOCK_UNLOCKED(dmasound.lock)
+};
+
+static inline void sound_silence(void)
+{
+	dmasound.mach.silence(); /* _MUST_ stop DMA */
+}
+
+static inline int sound_set_format(int format)
+{
+	return dmasound.mach.setFormat(format);
+}
+
+
+static int sound_set_speed(int speed)
+{
+	if (speed < 0)
+		return dmasound.soft.speed;
+
+	/* trap out-of-range speed settings.
+	   at present we allow (arbitrarily) low rates - using soft
+	   up-conversion - but we can't allow > max because there is
+	   no soft down-conversion.
+	*/
+	if (dmasound.mach.max_dsp_speed &&
+	   (speed > dmasound.mach.max_dsp_speed))
+		speed = dmasound.mach.max_dsp_speed ;
+
+	dmasound.soft.speed = speed;
+
+	if (dmasound.minDev == SND_DEV_DSP)
+		dmasound.dsp.speed = dmasound.soft.speed;
+
+	return dmasound.soft.speed;
+}
+
+static int sound_set_stereo(int stereo)
+{
+	if (stereo < 0)
+		return dmasound.soft.stereo;
+
+	stereo = !!stereo;    /* should be 0 or 1 now */
+
+	dmasound.soft.stereo = stereo;
+	if (dmasound.minDev == SND_DEV_DSP)
+		dmasound.dsp.stereo = stereo;
+
+	return stereo;
+}
+
+static ssize_t sound_copy_translate(TRANS *trans, const u_char __user *userPtr,
+				    size_t userCount, u_char frame[],
+				    ssize_t *frameUsed, ssize_t frameLeft)
+{
+	ssize_t (*ct_func)(const u_char __user *, size_t, u_char *, ssize_t *, ssize_t);
+
+	switch (dmasound.soft.format) {
+	    case AFMT_MU_LAW:
+		ct_func = trans->ct_ulaw;
+		break;
+	    case AFMT_A_LAW:
+		ct_func = trans->ct_alaw;
+		break;
+	    case AFMT_S8:
+		ct_func = trans->ct_s8;
+		break;
+	    case AFMT_U8:
+		ct_func = trans->ct_u8;
+		break;
+	    case AFMT_S16_BE:
+		ct_func = trans->ct_s16be;
+		break;
+	    case AFMT_U16_BE:
+		ct_func = trans->ct_u16be;
+		break;
+	    case AFMT_S16_LE:
+		ct_func = trans->ct_s16le;
+		break;
+	    case AFMT_U16_LE:
+		ct_func = trans->ct_u16le;
+		break;
+	    default:
+		return 0;
+	}
+	/* if the user has requested a non-existent translation don't try
+	   to call it but just return 0 bytes moved
+	*/
+	if (ct_func)
+		return ct_func(userPtr, userCount, frame, frameUsed, frameLeft);
+	return 0;
+}
+
+    /*
+     *  /dev/mixer abstraction
+     */
+
+static struct {
+    int busy;
+    int modify_counter;
+} mixer;
+
+static int mixer_open(struct inode *inode, struct file *file)
+{
+	mutex_lock(&dmasound_core_mutex);
+	if (!try_module_get(dmasound.mach.owner)) {
+		mutex_unlock(&dmasound_core_mutex);
+		return -ENODEV;
+	}
+	mixer.busy = 1;
+	mutex_unlock(&dmasound_core_mutex);
+	return 0;
+}
+
+static int mixer_release(struct inode *inode, struct file *file)
+{
+	mutex_lock(&dmasound_core_mutex);
+	mixer.busy = 0;
+	module_put(dmasound.mach.owner);
+	mutex_unlock(&dmasound_core_mutex);
+	return 0;
+}
+
+static int mixer_ioctl(struct file *file, u_int cmd, u_long arg)
+{
+	if (_SIOC_DIR(cmd) & _SIOC_WRITE)
+	    mixer.modify_counter++;
+	switch (cmd) {
+	    case OSS_GETVERSION:
+		return IOCTL_OUT(arg, SOUND_VERSION);
+	    case SOUND_MIXER_INFO:
+		{
+		    mixer_info info;
+		    memset(&info, 0, sizeof(info));
+		    strlcpy(info.id, dmasound.mach.name2, sizeof(info.id));
+		    strlcpy(info.name, dmasound.mach.name2, sizeof(info.name));
+		    info.modify_counter = mixer.modify_counter;
+		    if (copy_to_user((void __user *)arg, &info, sizeof(info)))
+			    return -EFAULT;
+		    return 0;
+		}
+	}
+	if (dmasound.mach.mixer_ioctl)
+	    return dmasound.mach.mixer_ioctl(cmd, arg);
+	return -EINVAL;
+}
+
+static long mixer_unlocked_ioctl(struct file *file, u_int cmd, u_long arg)
+{
+	int ret;
+
+	mutex_lock(&dmasound_core_mutex);
+	ret = mixer_ioctl(file, cmd, arg);
+	mutex_unlock(&dmasound_core_mutex);
+
+	return ret;
+}
+
+static const struct file_operations mixer_fops =
+{
+	.owner		= THIS_MODULE,
+	.llseek		= no_llseek,
+	.unlocked_ioctl	= mixer_unlocked_ioctl,
+	.open		= mixer_open,
+	.release	= mixer_release,
+};
+
+static void mixer_init(void)
+{
+#ifndef MODULE
+	int mixer_unit;
+#endif
+	mixer_unit = register_sound_mixer(&mixer_fops, -1);
+	if (mixer_unit < 0)
+		return;
+
+	mixer.busy = 0;
+	dmasound.treble = 0;
+	dmasound.bass = 0;
+	if (dmasound.mach.mixer_init)
+	    dmasound.mach.mixer_init();
+}
+
+
+    /*
+     *  Sound queue stuff, the heart of the driver
+     */
+
+struct sound_queue dmasound_write_sq;
+static void sq_reset_output(void) ;
+
+static int sq_allocate_buffers(struct sound_queue *sq, int num, int size)
+{
+	int i;
+
+	if (sq->buffers)
+		return 0;
+	sq->numBufs = num;
+	sq->bufSize = size;
+	sq->buffers = kmalloc_array (num, sizeof(char *), GFP_KERNEL);
+	if (!sq->buffers)
+		return -ENOMEM;
+	for (i = 0; i < num; i++) {
+		sq->buffers[i] = dmasound.mach.dma_alloc(size, GFP_KERNEL);
+		if (!sq->buffers[i]) {
+			while (i--)
+				dmasound.mach.dma_free(sq->buffers[i], size);
+			kfree(sq->buffers);
+			sq->buffers = NULL;
+			return -ENOMEM;
+		}
+	}
+	return 0;
+}
+
+static void sq_release_buffers(struct sound_queue *sq)
+{
+	int i;
+
+	if (sq->buffers) {
+		for (i = 0; i < sq->numBufs; i++)
+			dmasound.mach.dma_free(sq->buffers[i], sq->bufSize);
+		kfree(sq->buffers);
+		sq->buffers = NULL;
+	}
+}
+
+
+static int sq_setup(struct sound_queue *sq)
+{
+	int (*setup_func)(void) = NULL;
+	int hard_frame ;
+
+	if (sq->locked) { /* are we already set? - and not changeable */
+#ifdef DEBUG_DMASOUND
+printk("dmasound_core: tried to sq_setup a locked queue\n") ;
+#endif
+		return -EINVAL ;
+	}
+	sq->locked = 1 ; /* don't think we have a race prob. here _check_ */
+
+	/* make sure that the parameters are set up
+	   This should have been done already...
+	*/
+
+	dmasound.mach.init();
+
+	/* OK.  If the user has set fragment parameters explicitly, then we
+	   should leave them alone... as long as they are valid.
+	   Invalid user fragment params can occur if we allow the whole buffer
+	   to be used when the user requests the fragments sizes (with no soft
+	   x-lation) and then the user subsequently sets a soft x-lation that
+	   requires increased internal buffering.
+
+	   Othwerwise (if the user did not set them) OSS says that we should
+	   select frag params on the basis of 0.5 s output & 0.1 s input
+	   latency. (TODO.  For now we will copy in the defaults.)
+	*/
+
+	if (sq->user_frags <= 0) {
+		sq->max_count = sq->numBufs ;
+		sq->max_active = sq->numBufs ;
+		sq->block_size = sq->bufSize;
+		/* set up the user info */
+		sq->user_frags = sq->numBufs ;
+		sq->user_frag_size = sq->bufSize ;
+		sq->user_frag_size *=
+			(dmasound.soft.size * (dmasound.soft.stereo+1) ) ;
+		sq->user_frag_size /=
+			(dmasound.hard.size * (dmasound.hard.stereo+1) ) ;
+	} else {
+		/* work out requested block size */
+		sq->block_size = sq->user_frag_size ;
+		sq->block_size *=
+			(dmasound.hard.size * (dmasound.hard.stereo+1) ) ;
+		sq->block_size /=
+			(dmasound.soft.size * (dmasound.soft.stereo+1) ) ;
+		/* the user wants to write frag-size chunks */
+		sq->block_size *= dmasound.hard.speed ;
+		sq->block_size /= dmasound.soft.speed ;
+		/* this only works for size values which are powers of 2 */
+		hard_frame =
+			(dmasound.hard.size * (dmasound.hard.stereo+1))/8 ;
+		sq->block_size +=  (hard_frame - 1) ;
+		sq->block_size &= ~(hard_frame - 1) ; /* make sure we are aligned */
+		/* let's just check for obvious mistakes */
+		if ( sq->block_size <= 0 || sq->block_size > sq->bufSize) {
+#ifdef DEBUG_DMASOUND
+printk("dmasound_core: invalid frag size (user set %d)\n", sq->user_frag_size) ;
+#endif
+			sq->block_size = sq->bufSize ;
+		}
+		if ( sq->user_frags <= sq->numBufs ) {
+			sq->max_count = sq->user_frags ;
+			/* if user has set max_active - then use it */
+			sq->max_active = (sq->max_active <= sq->max_count) ?
+				sq->max_active : sq->max_count ;
+		} else {
+#ifdef DEBUG_DMASOUND
+printk("dmasound_core: invalid frag count (user set %d)\n", sq->user_frags) ;
+#endif
+			sq->max_count =
+			sq->max_active = sq->numBufs ;
+		}
+	}
+	sq->front = sq->count = sq->rear_size = 0;
+	sq->syncing = 0;
+	sq->active = 0;
+
+	if (sq == &write_sq) {
+	    sq->rear = -1;
+	    setup_func = dmasound.mach.write_sq_setup;
+	}
+	if (setup_func)
+	    return setup_func();
+	return 0 ;
+}
+
+static inline void sq_play(void)
+{
+	dmasound.mach.play();
+}
+
+static ssize_t sq_write(struct file *file, const char __user *src, size_t uLeft,
+			loff_t *ppos)
+{
+	ssize_t uWritten = 0;
+	u_char *dest;
+	ssize_t uUsed = 0, bUsed, bLeft;
+	unsigned long flags ;
+
+	/* ++TeSche: Is something like this necessary?
+	 * Hey, that's an honest question! Or does any other part of the
+	 * filesystem already checks this situation? I really don't know.
+	 */
+	if (uLeft == 0)
+		return 0;
+
+	/* implement any changes we have made to the soft/hard params.
+	   this is not satisfactory really, all we have done up to now is to
+	   say what we would like - there hasn't been any real checking of capability
+	*/
+
+	if (shared_resources_initialised == 0) {
+		dmasound.mach.init() ;
+		shared_resources_initialised = 1 ;
+	}
+
+	/* set up the sq if it is not already done. This may seem a dumb place
+	   to do it - but it is what OSS requires.  It means that write() can
+	   return memory allocation errors.  To avoid this possibility use the
+	   GETBLKSIZE or GETOSPACE ioctls (after you've fiddled with all the
+	   params you want to change) - these ioctls also force the setup.
+	*/
+
+	if (write_sq.locked == 0) {
+		if ((uWritten = sq_setup(&write_sq)) < 0) return uWritten ;
+		uWritten = 0 ;
+	}
+
+/* FIXME: I think that this may be the wrong behaviour when we get strapped
+	for time and the cpu is close to being (or actually) behind in sending data.
+	- because we've lost the time that the N samples, already in the buffer,
+	would have given us to get here with the next lot from the user.
+*/
+	/* The interrupt doesn't start to play the last, incomplete frame.
+	 * Thus we can append to it without disabling the interrupts! (Note
+	 * also that write_sq.rear isn't affected by the interrupt.)
+	 */
+
+	/* as of 1.6 this behaviour changes if SNDCTL_DSP_POST has been issued:
+	   this will mimic the behaviour of syncing and allow the sq_play() to
+	   queue a partial fragment.  Since sq_play() may/will be called from
+	   the IRQ handler - at least on Pmac we have to deal with it.
+	   The strategy - possibly not optimum - is to kill _POST status if we
+	   get here.  This seems, at least, reasonable - in the sense that POST
+	   is supposed to indicate that we might not write before the queue
+	   is drained - and if we get here in time then it does not apply.
+	*/
+
+	spin_lock_irqsave(&dmasound.lock, flags);
+	write_sq.syncing &= ~2 ; /* take out POST status */
+	spin_unlock_irqrestore(&dmasound.lock, flags);
+
+	if (write_sq.count > 0 &&
+	    (bLeft = write_sq.block_size-write_sq.rear_size) > 0) {
+		dest = write_sq.buffers[write_sq.rear];
+		bUsed = write_sq.rear_size;
+		uUsed = sound_copy_translate(dmasound.trans_write, src, uLeft,
+					     dest, &bUsed, bLeft);
+		if (uUsed <= 0)
+			return uUsed;
+		src += uUsed;
+		uWritten += uUsed;
+		uLeft = (uUsed <= uLeft) ? (uLeft - uUsed) : 0 ; /* paranoia */
+		write_sq.rear_size = bUsed;
+	}
+
+	while (uLeft) {
+		DEFINE_WAIT(wait);
+
+		while (write_sq.count >= write_sq.max_active) {
+			prepare_to_wait(&write_sq.action_queue, &wait, TASK_INTERRUPTIBLE);
+			sq_play();
+			if (write_sq.non_blocking) {
+				finish_wait(&write_sq.action_queue, &wait);
+				return uWritten > 0 ? uWritten : -EAGAIN;
+			}
+			if (write_sq.count < write_sq.max_active)
+				break;
+
+			schedule_timeout(HZ);
+			if (signal_pending(current)) {
+				finish_wait(&write_sq.action_queue, &wait);
+				return uWritten > 0 ? uWritten : -EINTR;
+			}
+		}
+
+		finish_wait(&write_sq.action_queue, &wait);
+
+		/* Here, we can avoid disabling the interrupt by first
+		 * copying and translating the data, and then updating
+		 * the write_sq variables. Until this is done, the interrupt
+		 * won't see the new frame and we can work on it
+		 * undisturbed.
+		 */
+
+		dest = write_sq.buffers[(write_sq.rear+1) % write_sq.max_count];
+		bUsed = 0;
+		bLeft = write_sq.block_size;
+		uUsed = sound_copy_translate(dmasound.trans_write, src, uLeft,
+					     dest, &bUsed, bLeft);
+		if (uUsed <= 0)
+			break;
+		src += uUsed;
+		uWritten += uUsed;
+		uLeft = (uUsed <= uLeft) ? (uLeft - uUsed) : 0 ; /* paranoia */
+		if (bUsed) {
+			write_sq.rear = (write_sq.rear+1) % write_sq.max_count;
+			write_sq.rear_size = bUsed;
+			write_sq.count++;
+		}
+	} /* uUsed may have been 0 */
+
+	sq_play();
+
+	return uUsed < 0? uUsed: uWritten;
+}
+
+static __poll_t sq_poll(struct file *file, struct poll_table_struct *wait)
+{
+	__poll_t mask = 0;
+	int retVal;
+	
+	if (write_sq.locked == 0) {
+		if ((retVal = sq_setup(&write_sq)) < 0)
+			return retVal;
+		return 0;
+	}
+	if (file->f_mode & FMODE_WRITE )
+		poll_wait(file, &write_sq.action_queue, wait);
+	if (file->f_mode & FMODE_WRITE)
+		if (write_sq.count < write_sq.max_active || write_sq.block_size - write_sq.rear_size > 0)
+			mask |= EPOLLOUT | EPOLLWRNORM;
+	return mask;
+
+}
+
+static inline void sq_init_waitqueue(struct sound_queue *sq)
+{
+	init_waitqueue_head(&sq->action_queue);
+	init_waitqueue_head(&sq->open_queue);
+	init_waitqueue_head(&sq->sync_queue);
+	sq->busy = 0;
+}
+
+#if 0 /* blocking open() */
+static inline void sq_wake_up(struct sound_queue *sq, struct file *file,
+			      fmode_t mode)
+{
+	if (file->f_mode & mode) {
+		sq->busy = 0; /* CHECK: IS THIS OK??? */
+		WAKE_UP(sq->open_queue);
+	}
+}
+#endif
+
+static int sq_open2(struct sound_queue *sq, struct file *file, fmode_t mode,
+		    int numbufs, int bufsize)
+{
+	int rc = 0;
+
+	if (file->f_mode & mode) {
+		if (sq->busy) {
+#if 0 /* blocking open() */
+			rc = -EBUSY;
+			if (file->f_flags & O_NONBLOCK)
+				return rc;
+			rc = -EINTR;
+			if (wait_event_interruptible(sq->open_queue, !sq->busy))
+				return rc;
+			rc = 0;
+#else
+			/* OSS manual says we will return EBUSY regardless
+			   of O_NOBLOCK.
+			*/
+			return -EBUSY ;
+#endif
+		}
+		sq->busy = 1; /* Let's play spot-the-race-condition */
+
+		/* allocate the default number & size of buffers.
+		   (i.e. specified in _setup() or as module params)
+		   can't be changed at the moment - but _could_ be perhaps
+		   in the setfragments ioctl.
+		*/
+		if (( rc = sq_allocate_buffers(sq, numbufs, bufsize))) {
+#if 0 /* blocking open() */
+			sq_wake_up(sq, file, mode);
+#else
+			sq->busy = 0 ;
+#endif
+			return rc;
+		}
+
+		sq->non_blocking = file->f_flags & O_NONBLOCK;
+	}
+	return rc;
+}
+
+#define write_sq_init_waitqueue()	sq_init_waitqueue(&write_sq)
+#if 0 /* blocking open() */
+#define write_sq_wake_up(file)		sq_wake_up(&write_sq, file, FMODE_WRITE)
+#endif
+#define write_sq_release_buffers()	sq_release_buffers(&write_sq)
+#define write_sq_open(file)	\
+	sq_open2(&write_sq, file, FMODE_WRITE, numWriteBufs, writeBufSize )
+
+static int sq_open(struct inode *inode, struct file *file)
+{
+	int rc;
+
+	mutex_lock(&dmasound_core_mutex);
+	if (!try_module_get(dmasound.mach.owner)) {
+		mutex_unlock(&dmasound_core_mutex);
+		return -ENODEV;
+	}
+
+	rc = write_sq_open(file); /* checks the f_mode */
+	if (rc)
+		goto out;
+	if (file->f_mode & FMODE_READ) {
+		/* TODO: if O_RDWR, release any resources grabbed by write part */
+		rc = -ENXIO ; /* I think this is what is required by open(2) */
+		goto out;
+	}
+
+	if (dmasound.mach.sq_open)
+	    dmasound.mach.sq_open(file->f_mode);
+
+	/* CHECK whether this is sensible - in the case that dsp0 could be opened
+	  O_RDONLY and dsp1 could be opened O_WRONLY
+	*/
+
+	dmasound.minDev = iminor(inode) & 0x0f;
+
+	/* OK. - we should make some attempt at consistency. At least the H'ware
+	   options should be set with a valid mode.  We will make it that the LL
+	   driver must supply defaults for hard & soft params.
+	*/
+
+	if (shared_resource_owner == 0) {
+		/* you can make this AFMT_U8/mono/8K if you want to mimic old
+		   OSS behaviour - while we still have soft translations ;-) */
+		dmasound.soft = dmasound.mach.default_soft ;
+		dmasound.dsp = dmasound.mach.default_soft ;
+		dmasound.hard = dmasound.mach.default_hard ;
+	}
+
+#ifndef DMASOUND_STRICT_OSS_COMPLIANCE
+	/* none of the current LL drivers can actually do this "native" at the moment
+	   OSS does not really require us to supply /dev/audio if we can't do it.
+	*/
+	if (dmasound.minDev == SND_DEV_AUDIO) {
+		sound_set_speed(8000);
+		sound_set_stereo(0);
+		sound_set_format(AFMT_MU_LAW);
+	}
+#endif
+	mutex_unlock(&dmasound_core_mutex);
+	return 0;
+ out:
+	module_put(dmasound.mach.owner);
+	mutex_unlock(&dmasound_core_mutex);
+	return rc;
+}
+
+static void sq_reset_output(void)
+{
+	sound_silence(); /* this _must_ stop DMA, we might be about to lose the buffers */
+	write_sq.active = 0;
+	write_sq.count = 0;
+	write_sq.rear_size = 0;
+	/* write_sq.front = (write_sq.rear+1) % write_sq.max_count;*/
+	write_sq.front = 0 ;
+	write_sq.rear = -1 ; /* same as for set-up */
+
+	/* OK - we can unlock the parameters and fragment settings */
+	write_sq.locked = 0 ;
+	write_sq.user_frags = 0 ;
+	write_sq.user_frag_size = 0 ;
+}
+
+static void sq_reset(void)
+{
+	sq_reset_output() ;
+	/* we could consider resetting the shared_resources_owner here... but I
+	   think it is probably still rather non-obvious to application writer
+	*/
+
+	/* we release everything else though */
+	shared_resources_initialised = 0 ;
+}
+
+static int sq_fsync(void)
+{
+	int rc = 0;
+	int timeout = 5;
+
+	write_sq.syncing |= 1;
+	sq_play();	/* there may be an incomplete frame waiting */
+
+	while (write_sq.active) {
+		wait_event_interruptible_timeout(write_sq.sync_queue,
+						 !write_sq.active, HZ);
+		if (signal_pending(current)) {
+			/* While waiting for audio output to drain, an
+			 * interrupt occurred.  Stop audio output immediately
+			 * and clear the queue. */
+			sq_reset_output();
+			rc = -EINTR;
+			break;
+		}
+		if (!--timeout) {
+			printk(KERN_WARNING "dmasound: Timeout draining output\n");
+			sq_reset_output();
+			rc = -EIO;
+			break;
+		}
+	}
+
+	/* flag no sync regardless of whether we had a DSP_POST or not */
+	write_sq.syncing = 0 ;
+	return rc;
+}
+
+static int sq_release(struct inode *inode, struct file *file)
+{
+	int rc = 0;
+
+	mutex_lock(&dmasound_core_mutex);
+
+	if (file->f_mode & FMODE_WRITE) {
+		if (write_sq.busy)
+			rc = sq_fsync();
+
+		sq_reset_output() ; /* make sure dma is stopped and all is quiet */
+		write_sq_release_buffers();
+		write_sq.busy = 0;
+	}
+
+	if (file->f_mode & shared_resource_owner) { /* it's us that has them */
+		shared_resource_owner = 0 ;
+		shared_resources_initialised = 0 ;
+		dmasound.hard = dmasound.mach.default_hard ;
+	}
+
+	module_put(dmasound.mach.owner);
+
+#if 0 /* blocking open() */
+	/* Wake up a process waiting for the queue being released.
+	 * Note: There may be several processes waiting for a call
+	 * to open() returning. */
+
+	/* Iain: hmm I don't understand this next comment ... */
+	/* There is probably a DOS atack here. They change the mode flag. */
+	/* XXX add check here,*/
+	read_sq_wake_up(file); /* checks f_mode */
+	write_sq_wake_up(file); /* checks f_mode */
+#endif /* blocking open() */
+
+	mutex_unlock(&dmasound_core_mutex);
+
+	return rc;
+}
+
+/* here we see if we have a right to modify format, channels, size and so on
+   if no-one else has claimed it already then we do...
+
+   TODO: We might change this to mask O_RDWR such that only one or the other channel
+   is the owner - if we have problems.
+*/
+
+static int shared_resources_are_mine(fmode_t md)
+{
+	if (shared_resource_owner)
+		return (shared_resource_owner & md) != 0;
+	else {
+		shared_resource_owner = md ;
+		return 1 ;
+	}
+}
+
+/* if either queue is locked we must deny the right to change shared params
+*/
+
+static int queues_are_quiescent(void)
+{
+	if (write_sq.locked)
+		return 0 ;
+	return 1 ;
+}
+
+/* check and set a queue's fragments per user's wishes...
+   we will check against the pre-defined literals and the actual sizes.
+   This is a bit fraught - because soft translations can mess with our
+   buffer requirements *after* this call - OSS says "call setfrags first"
+*/
+
+/* It is possible to replace all the -EINVAL returns with an override that
+   just puts the allowable value in.  This may be what many OSS apps require
+*/
+
+static int set_queue_frags(struct sound_queue *sq, int bufs, int size)
+{
+	if (sq->locked) {
+#ifdef DEBUG_DMASOUND
+printk("dmasound_core: tried to set_queue_frags on a locked queue\n") ;
+#endif
+		return -EINVAL ;
+	}
+
+	if ((size < MIN_FRAG_SIZE) || (size > MAX_FRAG_SIZE))
+		return -EINVAL ;
+	size = (1<<size) ; /* now in bytes */
+	if (size > sq->bufSize)
+		return -EINVAL ; /* this might still not work */
+
+	if (bufs <= 0)
+		return -EINVAL ;
+	if (bufs > sq->numBufs) /* the user is allowed say "don't care" with 0x7fff */
+		bufs = sq->numBufs ;
+
+	/* there is, currently, no way to specify max_active separately
+	   from max_count.  This could be a LL driver issue - I guess
+	   if there is a requirement for these values to be different then
+	  we will have to pass that info. up to this level.
+	*/
+	sq->user_frags =
+	sq->max_active = bufs ;
+	sq->user_frag_size = size ;
+
+	return 0 ;
+}
+
+static int sq_ioctl(struct file *file, u_int cmd, u_long arg)
+{
+	int val, result;
+	u_long fmt;
+	int data;
+	int size, nbufs;
+	audio_buf_info info;
+
+	switch (cmd) {
+	case SNDCTL_DSP_RESET:
+		sq_reset();
+		return 0;
+		break ;
+	case SNDCTL_DSP_GETFMTS:
+		fmt = dmasound.mach.hardware_afmts ; /* this is what OSS says.. */
+		return IOCTL_OUT(arg, fmt);
+		break ;
+	case SNDCTL_DSP_GETBLKSIZE:
+		/* this should tell the caller about bytes that the app can
+		   read/write - the app doesn't care about our internal buffers.
+		   We force sq_setup() here as per OSS 1.1 (which should
+		   compute the values necessary).
+		   Since there is no mechanism to specify read/write separately, for
+		   fds opened O_RDWR, the write_sq values will, arbitrarily, overwrite
+		   the read_sq ones.
+		*/
+		size = 0 ;
+		if (file->f_mode & FMODE_WRITE) {
+			if ( !write_sq.locked )
+				sq_setup(&write_sq) ;
+			size = write_sq.user_frag_size ;
+		}
+		return IOCTL_OUT(arg, size);
+		break ;
+	case SNDCTL_DSP_POST:
+		/* all we are going to do is to tell the LL that any
+		   partial frags can be queued for output.
+		   The LL will have to clear this flag when last output
+		   is queued.
+		*/
+		write_sq.syncing |= 0x2 ;
+		sq_play() ;
+		return 0 ;
+	case SNDCTL_DSP_SYNC:
+		/* This call, effectively, has the same behaviour as SNDCTL_DSP_RESET
+		   except that it waits for output to finish before resetting
+		   everything - read, however, is killed immediately.
+		*/
+		result = 0 ;
+		if (file->f_mode & FMODE_WRITE) {
+			result = sq_fsync();
+			sq_reset_output() ;
+		}
+		/* if we are the shared resource owner then release them */
+		if (file->f_mode & shared_resource_owner)
+			shared_resources_initialised = 0 ;
+		return result ;
+		break ;
+	case SOUND_PCM_READ_RATE:
+		return IOCTL_OUT(arg, dmasound.soft.speed);
+	case SNDCTL_DSP_SPEED:
+		/* changing this on the fly will have weird effects on the sound.
+		   Where there are rate conversions implemented in soft form - it
+		   will cause the _ctx_xxx() functions to be substituted.
+		   However, there doesn't appear to be any reason to dis-allow it from
+		   a driver pov.
+		*/
+		if (shared_resources_are_mine(file->f_mode)) {
+			IOCTL_IN(arg, data);
+			data = sound_set_speed(data) ;
+			shared_resources_initialised = 0 ;
+			return IOCTL_OUT(arg, data);
+		} else
+			return -EINVAL ;
+		break ;
+	/* OSS says these next 4 actions are undefined when the device is
+	   busy/active - we will just return -EINVAL.
+	   To be allowed to change one - (a) you have to own the right
+	    (b) the queue(s) must be quiescent
+	*/
+	case SNDCTL_DSP_STEREO:
+		if (shared_resources_are_mine(file->f_mode) &&
+		    queues_are_quiescent()) {
+			IOCTL_IN(arg, data);
+			shared_resources_initialised = 0 ;
+			return IOCTL_OUT(arg, sound_set_stereo(data));
+		} else
+			return -EINVAL ;
+		break ;
+	case SOUND_PCM_WRITE_CHANNELS:
+		if (shared_resources_are_mine(file->f_mode) &&
+		    queues_are_quiescent()) {
+			IOCTL_IN(arg, data);
+			/* the user might ask for 20 channels, we will return 1 or 2 */
+			shared_resources_initialised = 0 ;
+			return IOCTL_OUT(arg, sound_set_stereo(data-1)+1);
+		} else
+			return -EINVAL ;
+		break ;
+	case SNDCTL_DSP_SETFMT:
+		if (shared_resources_are_mine(file->f_mode) &&
+		    queues_are_quiescent()) {
+		    	int format;
+			IOCTL_IN(arg, data);
+			shared_resources_initialised = 0 ;
+			format = sound_set_format(data);
+			result = IOCTL_OUT(arg, format);
+			if (result < 0)
+				return result;
+			if (format != data && data != AFMT_QUERY)
+				return -EINVAL;
+			return 0;
+		} else
+			return -EINVAL ;
+	case SNDCTL_DSP_SUBDIVIDE:
+		return -EINVAL ;
+	case SNDCTL_DSP_SETFRAGMENT:
+		/* we can do this independently for the two queues - with the
+		   proviso that for fds opened O_RDWR we cannot separate the
+		   actions and both queues will be set per the last call.
+		   NOTE: this does *NOT* actually set the queue up - merely
+		   registers our intentions.
+		*/
+		IOCTL_IN(arg, data);
+		result = 0 ;
+		nbufs = (data >> 16) & 0x7fff ; /* 0x7fff is 'use maximum' */
+		size = data & 0xffff;
+		if (file->f_mode & FMODE_WRITE) {
+			result = set_queue_frags(&write_sq, nbufs, size) ;
+			if (result)
+				return result ;
+		}
+		/* NOTE: this return value is irrelevant - OSS specifically says that
+		   the value is 'random' and that the user _must_ check the actual
+		   frags values using SNDCTL_DSP_GETBLKSIZE or similar */
+		return IOCTL_OUT(arg, data);
+		break ;
+	case SNDCTL_DSP_GETOSPACE:
+		/*
+		*/
+		if (file->f_mode & FMODE_WRITE) {
+			if ( !write_sq.locked )
+				sq_setup(&write_sq) ;
+			info.fragments = write_sq.max_active - write_sq.count;
+			info.fragstotal = write_sq.max_active;
+			info.fragsize = write_sq.user_frag_size;
+			info.bytes = info.fragments * info.fragsize;
+			if (copy_to_user((void __user *)arg, &info, sizeof(info)))
+				return -EFAULT;
+			return 0;
+		} else
+			return -EINVAL ;
+		break ;
+	case SNDCTL_DSP_GETCAPS:
+		val = dmasound.mach.capabilities & 0xffffff00;
+		return IOCTL_OUT(arg,val);
+
+	default:
+		return mixer_ioctl(file, cmd, arg);
+	}
+	return -EINVAL;
+}
+
+static long sq_unlocked_ioctl(struct file *file, u_int cmd, u_long arg)
+{
+	int ret;
+
+	mutex_lock(&dmasound_core_mutex);
+	ret = sq_ioctl(file, cmd, arg);
+	mutex_unlock(&dmasound_core_mutex);
+
+	return ret;
+}
+
+static const struct file_operations sq_fops =
+{
+	.owner		= THIS_MODULE,
+	.llseek		= no_llseek,
+	.write		= sq_write,
+	.poll		= sq_poll,
+	.unlocked_ioctl	= sq_unlocked_ioctl,
+	.open		= sq_open,
+	.release	= sq_release,
+};
+
+static int sq_init(void)
+{
+	const struct file_operations *fops = &sq_fops;
+#ifndef MODULE
+	int sq_unit;
+#endif
+
+	sq_unit = register_sound_dsp(fops, -1);
+	if (sq_unit < 0) {
+		printk(KERN_ERR "dmasound_core: couldn't register fops\n") ;
+		return sq_unit ;
+	}
+
+	write_sq_init_waitqueue();
+
+	/* These parameters will be restored for every clean open()
+	 * in the case of multiple open()s (e.g. dsp0 & dsp1) they
+	 * will be set so long as the shared resources have no owner.
+	 */
+
+	if (shared_resource_owner == 0) {
+		dmasound.soft = dmasound.mach.default_soft ;
+		dmasound.hard = dmasound.mach.default_hard ;
+		dmasound.dsp = dmasound.mach.default_soft ;
+		shared_resources_initialised = 0 ;
+	}
+	return 0 ;
+}
+
+
+    /*
+     *  /dev/sndstat
+     */
+
+/* we allow more space for record-enabled because there are extra output lines.
+   the number here must include the amount we are prepared to give to the low-level
+   driver.
+*/
+
+#define STAT_BUFF_LEN 768
+
+/* this is how much space we will allow the low-level driver to use
+   in the stat buffer.  Currently, 2 * (80 character line + <NL>).
+   We do not police this (it is up to the ll driver to be honest).
+*/
+
+#define LOW_LEVEL_STAT_ALLOC 162
+
+static struct {
+    int busy;
+    char buf[STAT_BUFF_LEN];	/* state.buf should not overflow! */
+    int len, ptr;
+} state;
+
+/* publish this function for use by low-level code, if required */
+
+static char *get_afmt_string(int afmt)
+{
+        switch(afmt) {
+            case AFMT_MU_LAW:
+                return "mu-law";
+                break;
+            case AFMT_A_LAW:
+                return "A-law";
+                break;
+            case AFMT_U8:
+                return "unsigned 8 bit";
+                break;
+            case AFMT_S8:
+                return "signed 8 bit";
+                break;
+            case AFMT_S16_BE:
+                return "signed 16 bit BE";
+                break;
+            case AFMT_U16_BE:
+                return "unsigned 16 bit BE";
+                break;
+            case AFMT_S16_LE:
+                return "signed 16 bit LE";
+                break;
+            case AFMT_U16_LE:
+                return "unsigned 16 bit LE";
+                break;
+	    case 0:
+		return "format not set" ;
+		break ;
+            default:
+                break ;
+        }
+        return "ERROR: Unsupported AFMT_XXXX code" ;
+}
+
+static int state_open(struct inode *inode, struct file *file)
+{
+	char *buffer = state.buf;
+	int len = 0;
+	int ret;
+
+	mutex_lock(&dmasound_core_mutex);
+	ret = -EBUSY;
+	if (state.busy)
+		goto out;
+
+	ret = -ENODEV;
+	if (!try_module_get(dmasound.mach.owner))
+		goto out;
+
+	state.ptr = 0;
+	state.busy = 1;
+
+	len += sprintf(buffer+len, "%sDMA sound driver rev %03d :\n",
+		dmasound.mach.name, (DMASOUND_CORE_REVISION<<4) +
+		((dmasound.mach.version>>8) & 0x0f));
+	len += sprintf(buffer+len,
+		"Core driver edition %02d.%02d : %s driver edition %02d.%02d\n",
+		DMASOUND_CORE_REVISION, DMASOUND_CORE_EDITION, dmasound.mach.name2,
+		(dmasound.mach.version >> 8), (dmasound.mach.version & 0xff)) ;
+
+	/* call the low-level module to fill in any stat info. that it has
+	   if present.  Maximum buffer usage is specified.
+	*/
+
+	if (dmasound.mach.state_info)
+		len += dmasound.mach.state_info(buffer+len,
+			(size_t) LOW_LEVEL_STAT_ALLOC) ;
+
+	/* make usage of the state buffer as deterministic as poss.
+	   exceptional conditions could cause overrun - and this is flagged as
+	   a kernel error.
+	*/
+
+	/* formats and settings */
+
+	len += sprintf(buffer+len,"\t\t === Formats & settings ===\n") ;
+	len += sprintf(buffer+len,"Parameter %20s%20s\n","soft","hard") ;
+	len += sprintf(buffer+len,"Format   :%20s%20s\n",
+		get_afmt_string(dmasound.soft.format),
+		get_afmt_string(dmasound.hard.format));
+
+	len += sprintf(buffer+len,"Samp Rate:%14d s/sec%14d s/sec\n",
+		       dmasound.soft.speed, dmasound.hard.speed);
+
+	len += sprintf(buffer+len,"Channels :%20s%20s\n",
+		       dmasound.soft.stereo ? "stereo" : "mono",
+		       dmasound.hard.stereo ? "stereo" : "mono" );
+
+	/* sound queue status */
+
+	len += sprintf(buffer+len,"\t\t === Sound Queue status ===\n");
+	len += sprintf(buffer+len,"Allocated:%8s%6s\n","Buffers","Size") ;
+	len += sprintf(buffer+len,"%9s:%8d%6d\n",
+		"write", write_sq.numBufs, write_sq.bufSize) ;
+	len += sprintf(buffer+len,
+		"Current  : MaxFrg FragSiz MaxAct Frnt Rear "
+		"Cnt RrSize A B S L  xruns\n") ;
+	len += sprintf(buffer+len,"%9s:%7d%8d%7d%5d%5d%4d%7d%2d%2d%2d%2d%7d\n",
+		"write", write_sq.max_count, write_sq.block_size,
+		write_sq.max_active, write_sq.front, write_sq.rear,
+		write_sq.count, write_sq.rear_size, write_sq.active,
+		write_sq.busy, write_sq.syncing, write_sq.locked, write_sq.xruns) ;
+#ifdef DEBUG_DMASOUND
+printk("dmasound: stat buffer used %d bytes\n", len) ;
+#endif
+
+	if (len >= STAT_BUFF_LEN)
+		printk(KERN_ERR "dmasound_core: stat buffer overflowed!\n");
+
+	state.len = len;
+	ret = 0;
+out:
+	mutex_unlock(&dmasound_core_mutex);
+	return ret;
+}
+
+static int state_release(struct inode *inode, struct file *file)
+{
+	mutex_lock(&dmasound_core_mutex);
+	state.busy = 0;
+	module_put(dmasound.mach.owner);
+	mutex_unlock(&dmasound_core_mutex);
+	return 0;
+}
+
+static ssize_t state_read(struct file *file, char __user *buf, size_t count,
+			  loff_t *ppos)
+{
+	int n = state.len - state.ptr;
+	if (n > count)
+		n = count;
+	if (n <= 0)
+		return 0;
+	if (copy_to_user(buf, &state.buf[state.ptr], n))
+		return -EFAULT;
+	state.ptr += n;
+	return n;
+}
+
+static const struct file_operations state_fops = {
+	.owner		= THIS_MODULE,
+	.llseek		= no_llseek,
+	.read		= state_read,
+	.open		= state_open,
+	.release	= state_release,
+};
+
+static int state_init(void)
+{
+#ifndef MODULE
+	int state_unit;
+#endif
+	state_unit = register_sound_special(&state_fops, SND_DEV_STATUS);
+	if (state_unit < 0)
+		return state_unit ;
+	state.busy = 0;
+	return 0 ;
+}
+
+
+    /*
+     *  Config & Setup
+     *
+     *  This function is called by _one_ chipset-specific driver
+     */
+
+int dmasound_init(void)
+{
+	int res ;
+#ifdef MODULE
+	if (irq_installed)
+		return -EBUSY;
+#endif
+
+	/* Set up sound queue, /dev/audio and /dev/dsp. */
+
+	/* Set default settings. */
+	if ((res = sq_init()) < 0)
+		return res ;
+
+	/* Set up /dev/sndstat. */
+	if ((res = state_init()) < 0)
+		return res ;
+
+	/* Set up /dev/mixer. */
+	mixer_init();
+
+	if (!dmasound.mach.irqinit()) {
+		printk(KERN_ERR "DMA sound driver: Interrupt initialization failed\n");
+		return -ENODEV;
+	}
+#ifdef MODULE
+	irq_installed = 1;
+#endif
+
+	printk(KERN_INFO "%s DMA sound driver rev %03d installed\n",
+		dmasound.mach.name, (DMASOUND_CORE_REVISION<<4) +
+		((dmasound.mach.version>>8) & 0x0f));
+	printk(KERN_INFO
+		"Core driver edition %02d.%02d : %s driver edition %02d.%02d\n",
+		DMASOUND_CORE_REVISION, DMASOUND_CORE_EDITION, dmasound.mach.name2,
+		(dmasound.mach.version >> 8), (dmasound.mach.version & 0xff)) ;
+	printk(KERN_INFO "Write will use %4d fragments of %7d bytes as default\n",
+		numWriteBufs, writeBufSize) ;
+	return 0;
+}
+
+#ifdef MODULE
+
+void dmasound_deinit(void)
+{
+	if (irq_installed) {
+		sound_silence();
+		dmasound.mach.irqcleanup();
+		irq_installed = 0;
+	}
+
+	write_sq_release_buffers();
+
+	if (mixer_unit >= 0)
+		unregister_sound_mixer(mixer_unit);
+	if (state_unit >= 0)
+		unregister_sound_special(state_unit);
+	if (sq_unit >= 0)
+		unregister_sound_dsp(sq_unit);
+}
+
+#else /* !MODULE */
+
+static int dmasound_setup(char *str)
+{
+	int ints[6], size;
+
+	str = get_options(str, ARRAY_SIZE(ints), ints);
+
+	/* check the bootstrap parameter for "dmasound=" */
+
+	/* FIXME: other than in the most naive of cases there is no sense in these
+	 *	  buffers being other than powers of two.  This is not checked yet.
+	 */
+
+	switch (ints[0]) {
+	case 3:
+		if ((ints[3] < 0) || (ints[3] > MAX_CATCH_RADIUS))
+			printk("dmasound_setup: invalid catch radius, using default = %d\n", catchRadius);
+		else
+			catchRadius = ints[3];
+		/* fall through */
+	case 2:
+		if (ints[1] < MIN_BUFFERS)
+			printk("dmasound_setup: invalid number of buffers, using default = %d\n", numWriteBufs);
+		else
+			numWriteBufs = ints[1];
+		/* fall through */
+	case 1:
+		if ((size = ints[2]) < 256) /* check for small buffer specs */
+			size <<= 10 ;
+                if (size < MIN_BUFSIZE || size > MAX_BUFSIZE)
+                        printk("dmasound_setup: invalid write buffer size, using default = %d\n", writeBufSize);
+                else
+                        writeBufSize = size;
+	case 0:
+		break;
+	default:
+		printk("dmasound_setup: invalid number of arguments\n");
+		return 0;
+	}
+	return 1;
+}
+
+__setup("dmasound=", dmasound_setup);
+
+#endif /* !MODULE */
+
+    /*
+     *  Conversion tables
+     */
+
+#ifdef HAS_8BIT_TABLES
+/* 8 bit mu-law */
+
+char dmasound_ulaw2dma8[] = {
+	-126,	-122,	-118,	-114,	-110,	-106,	-102,	-98,
+	-94,	-90,	-86,	-82,	-78,	-74,	-70,	-66,
+	-63,	-61,	-59,	-57,	-55,	-53,	-51,	-49,
+	-47,	-45,	-43,	-41,	-39,	-37,	-35,	-33,
+	-31,	-30,	-29,	-28,	-27,	-26,	-25,	-24,
+	-23,	-22,	-21,	-20,	-19,	-18,	-17,	-16,
+	-16,	-15,	-15,	-14,	-14,	-13,	-13,	-12,
+	-12,	-11,	-11,	-10,	-10,	-9,	-9,	-8,
+	-8,	-8,	-7,	-7,	-7,	-7,	-6,	-6,
+	-6,	-6,	-5,	-5,	-5,	-5,	-4,	-4,
+	-4,	-4,	-4,	-4,	-3,	-3,	-3,	-3,
+	-3,	-3,	-3,	-3,	-2,	-2,	-2,	-2,
+	-2,	-2,	-2,	-2,	-2,	-2,	-2,	-2,
+	-1,	-1,	-1,	-1,	-1,	-1,	-1,	-1,
+	-1,	-1,	-1,	-1,	-1,	-1,	-1,	-1,
+	-1,	-1,	-1,	-1,	-1,	-1,	-1,	0,
+	125,	121,	117,	113,	109,	105,	101,	97,
+	93,	89,	85,	81,	77,	73,	69,	65,
+	62,	60,	58,	56,	54,	52,	50,	48,
+	46,	44,	42,	40,	38,	36,	34,	32,
+	30,	29,	28,	27,	26,	25,	24,	23,
+	22,	21,	20,	19,	18,	17,	16,	15,
+	15,	14,	14,	13,	13,	12,	12,	11,
+	11,	10,	10,	9,	9,	8,	8,	7,
+	7,	7,	6,	6,	6,	6,	5,	5,
+	5,	5,	4,	4,	4,	4,	3,	3,
+	3,	3,	3,	3,	2,	2,	2,	2,
+	2,	2,	2,	2,	1,	1,	1,	1,
+	1,	1,	1,	1,	1,	1,	1,	1,
+	0,	0,	0,	0,	0,	0,	0,	0,
+	0,	0,	0,	0,	0,	0,	0,	0,
+	0,	0,	0,	0,	0,	0,	0,	0
+};
+
+/* 8 bit A-law */
+
+char dmasound_alaw2dma8[] = {
+	-22,	-21,	-24,	-23,	-18,	-17,	-20,	-19,
+	-30,	-29,	-32,	-31,	-26,	-25,	-28,	-27,
+	-11,	-11,	-12,	-12,	-9,	-9,	-10,	-10,
+	-15,	-15,	-16,	-16,	-13,	-13,	-14,	-14,
+	-86,	-82,	-94,	-90,	-70,	-66,	-78,	-74,
+	-118,	-114,	-126,	-122,	-102,	-98,	-110,	-106,
+	-43,	-41,	-47,	-45,	-35,	-33,	-39,	-37,
+	-59,	-57,	-63,	-61,	-51,	-49,	-55,	-53,
+	-2,	-2,	-2,	-2,	-2,	-2,	-2,	-2,
+	-2,	-2,	-2,	-2,	-2,	-2,	-2,	-2,
+	-1,	-1,	-1,	-1,	-1,	-1,	-1,	-1,
+	-1,	-1,	-1,	-1,	-1,	-1,	-1,	-1,
+	-6,	-6,	-6,	-6,	-5,	-5,	-5,	-5,
+	-8,	-8,	-8,	-8,	-7,	-7,	-7,	-7,
+	-3,	-3,	-3,	-3,	-3,	-3,	-3,	-3,
+	-4,	-4,	-4,	-4,	-4,	-4,	-4,	-4,
+	21,	20,	23,	22,	17,	16,	19,	18,
+	29,	28,	31,	30,	25,	24,	27,	26,
+	10,	10,	11,	11,	8,	8,	9,	9,
+	14,	14,	15,	15,	12,	12,	13,	13,
+	86,	82,	94,	90,	70,	66,	78,	74,
+	118,	114,	126,	122,	102,	98,	110,	106,
+	43,	41,	47,	45,	35,	33,	39,	37,
+	59,	57,	63,	61,	51,	49,	55,	53,
+	1,	1,	1,	1,	1,	1,	1,	1,
+	1,	1,	1,	1,	1,	1,	1,	1,
+	0,	0,	0,	0,	0,	0,	0,	0,
+	0,	0,	0,	0,	0,	0,	0,	0,
+	5,	5,	5,	5,	4,	4,	4,	4,
+	7,	7,	7,	7,	6,	6,	6,	6,
+	2,	2,	2,	2,	2,	2,	2,	2,
+	3,	3,	3,	3,	3,	3,	3,	3
+};
+#endif /* HAS_8BIT_TABLES */
+
+    /*
+     *  Visible symbols for modules
+     */
+
+EXPORT_SYMBOL(dmasound);
+EXPORT_SYMBOL(dmasound_init);
+#ifdef MODULE
+EXPORT_SYMBOL(dmasound_deinit);
+#endif
+EXPORT_SYMBOL(dmasound_write_sq);
+EXPORT_SYMBOL(dmasound_catchRadius);
+#ifdef HAS_8BIT_TABLES
+EXPORT_SYMBOL(dmasound_ulaw2dma8);
+EXPORT_SYMBOL(dmasound_alaw2dma8);
+#endif
diff --git a/src/kernel/linux/v4.19/sound/oss/dmasound/dmasound_paula.c b/src/kernel/linux/v4.19/sound/oss/dmasound/dmasound_paula.c
new file mode 100644
index 0000000..81eb82c
--- /dev/null
+++ b/src/kernel/linux/v4.19/sound/oss/dmasound/dmasound_paula.c
@@ -0,0 +1,738 @@
+/*
+ *  linux/sound/oss/dmasound/dmasound_paula.c
+ *
+ *  Amiga `Paula' DMA Sound Driver
+ *
+ *  See linux/sound/oss/dmasound/dmasound_core.c for copyright and credits
+ *  prior to 28/01/2001
+ *
+ *  28/01/2001 [0.1] Iain Sandoe
+ *		     - added versioning
+ *		     - put in and populated the hardware_afmts field.
+ *             [0.2] - put in SNDCTL_DSP_GETCAPS value.
+ *	       [0.3] - put in constraint on state buffer usage.
+ *	       [0.4] - put in default hard/soft settings
+*/
+
+
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/soundcard.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+
+#include <linux/uaccess.h>
+#include <asm/setup.h>
+#include <asm/amigahw.h>
+#include <asm/amigaints.h>
+#include <asm/machdep.h>
+
+#include "dmasound.h"
+
+#define DMASOUND_PAULA_REVISION 0
+#define DMASOUND_PAULA_EDITION 4
+
+#define custom amiga_custom
+   /*
+    *	The minimum period for audio depends on htotal (for OCS/ECS/AGA)
+    *	(Imported from arch/m68k/amiga/amisound.c)
+    */
+
+extern volatile u_short amiga_audio_min_period;
+
+
+   /*
+    *	amiga_mksound() should be able to restore the period after beeping
+    *	(Imported from arch/m68k/amiga/amisound.c)
+    */
+
+extern u_short amiga_audio_period;
+
+
+   /*
+    *	Audio DMA masks
+    */
+
+#define AMI_AUDIO_OFF	(DMAF_AUD0 | DMAF_AUD1 | DMAF_AUD2 | DMAF_AUD3)
+#define AMI_AUDIO_8	(DMAF_SETCLR | DMAF_MASTER | DMAF_AUD0 | DMAF_AUD1)
+#define AMI_AUDIO_14	(AMI_AUDIO_8 | DMAF_AUD2 | DMAF_AUD3)
+
+
+    /*
+     *  Helper pointers for 16(14)-bit sound
+     */
+
+static int write_sq_block_size_half, write_sq_block_size_quarter;
+
+
+/*** Low level stuff *********************************************************/
+
+
+static void *AmiAlloc(unsigned int size, gfp_t flags);
+static void AmiFree(void *obj, unsigned int size);
+static int AmiIrqInit(void);
+#ifdef MODULE
+static void AmiIrqCleanUp(void);
+#endif
+static void AmiSilence(void);
+static void AmiInit(void);
+static int AmiSetFormat(int format);
+static int AmiSetVolume(int volume);
+static int AmiSetTreble(int treble);
+static void AmiPlayNextFrame(int index);
+static void AmiPlay(void);
+static irqreturn_t AmiInterrupt(int irq, void *dummy);
+
+#ifdef CONFIG_HEARTBEAT
+
+    /*
+     *  Heartbeat interferes with sound since the 7 kHz low-pass filter and the
+     *  power LED are controlled by the same line.
+     */
+
+static void (*saved_heartbeat)(int) = NULL;
+
+static inline void disable_heartbeat(void)
+{
+	if (mach_heartbeat) {
+	    saved_heartbeat = mach_heartbeat;
+	    mach_heartbeat = NULL;
+	}
+	AmiSetTreble(dmasound.treble);
+}
+
+static inline void enable_heartbeat(void)
+{
+	if (saved_heartbeat)
+	    mach_heartbeat = saved_heartbeat;
+}
+#else /* !CONFIG_HEARTBEAT */
+#define disable_heartbeat()	do { } while (0)
+#define enable_heartbeat()	do { } while (0)
+#endif /* !CONFIG_HEARTBEAT */
+
+
+/*** Mid level stuff *********************************************************/
+
+static void AmiMixerInit(void);
+static int AmiMixerIoctl(u_int cmd, u_long arg);
+static int AmiWriteSqSetup(void);
+static int AmiStateInfo(char *buffer, size_t space);
+
+
+/*** 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)
+ */
+
+
+    /*
+     *  Native format
+     */
+
+static ssize_t ami_ct_s8(const u_char __user *userPtr, size_t userCount,
+			 u_char frame[], ssize_t *frameUsed, ssize_t frameLeft)
+{
+	ssize_t count, used;
+
+	if (!dmasound.soft.stereo) {
+		void *p = &frame[*frameUsed];
+		count = min_t(unsigned long, userCount, frameLeft) & ~1;
+		used = count;
+		if (copy_from_user(p, userPtr, count))
+			return -EFAULT;
+	} else {
+		u_char *left = &frame[*frameUsed>>1];
+		u_char *right = left+write_sq_block_size_half;
+		count = min_t(unsigned long, userCount, frameLeft)>>1 & ~1;
+		used = count*2;
+		while (count > 0) {
+			if (get_user(*left++, userPtr++)
+			    || get_user(*right++, userPtr++))
+				return -EFAULT;
+			count--;
+		}
+	}
+	*frameUsed += used;
+	return used;
+}
+
+
+    /*
+     *  Copy and convert 8 bit data
+     */
+
+#define GENERATE_AMI_CT8(funcname, convsample)				\
+static ssize_t funcname(const u_char __user *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_t(size_t, userCount, frameLeft) & ~1;	\
+		used = count;						\
+		while (count > 0) {					\
+			u_char data;					\
+			if (get_user(data, userPtr++))			\
+				return -EFAULT;				\
+			*p++ = convsample(data);			\
+			count--;					\
+		}							\
+	} else {							\
+		u_char *left = &frame[*frameUsed>>1];			\
+		u_char *right = left+write_sq_block_size_half;		\
+		count = min_t(size_t, userCount, frameLeft)>>1 & ~1;	\
+		used = count*2;						\
+		while (count > 0) {					\
+			u_char data;					\
+			if (get_user(data, userPtr++))			\
+				return -EFAULT;				\
+			*left++ = convsample(data);			\
+			if (get_user(data, userPtr++))			\
+				return -EFAULT;				\
+			*right++ = convsample(data);			\
+			count--;					\
+		}							\
+	}								\
+	*frameUsed += used;						\
+	return used;							\
+}
+
+#define AMI_CT_ULAW(x)	(dmasound_ulaw2dma8[(x)])
+#define AMI_CT_ALAW(x)	(dmasound_alaw2dma8[(x)])
+#define AMI_CT_U8(x)	((x) ^ 0x80)
+
+GENERATE_AMI_CT8(ami_ct_ulaw, AMI_CT_ULAW)
+GENERATE_AMI_CT8(ami_ct_alaw, AMI_CT_ALAW)
+GENERATE_AMI_CT8(ami_ct_u8, AMI_CT_U8)
+
+
+    /*
+     *  Copy and convert 16 bit data
+     */
+
+#define GENERATE_AMI_CT_16(funcname, convsample)			\
+static ssize_t funcname(const u_char __user *userPtr, size_t userCount,	\
+			u_char frame[], ssize_t *frameUsed,		\
+			ssize_t frameLeft)				\
+{									\
+	const u_short __user *ptr = (const u_short __user *)userPtr;	\
+	ssize_t count, used;						\
+	u_short data;							\
+									\
+	if (!dmasound.soft.stereo) {					\
+		u_char *high = &frame[*frameUsed>>1];			\
+		u_char *low = high+write_sq_block_size_half;		\
+		count = min_t(size_t, userCount, frameLeft)>>1 & ~1;	\
+		used = count*2;						\
+		while (count > 0) {					\
+			if (get_user(data, ptr++))			\
+				return -EFAULT;				\
+			data = convsample(data);			\
+			*high++ = data>>8;				\
+			*low++ = (data>>2) & 0x3f;			\
+			count--;					\
+		}							\
+	} else {							\
+		u_char *lefth = &frame[*frameUsed>>2];			\
+		u_char *leftl = lefth+write_sq_block_size_quarter;	\
+		u_char *righth = lefth+write_sq_block_size_half;	\
+		u_char *rightl = righth+write_sq_block_size_quarter;	\
+		count = min_t(size_t, userCount, frameLeft)>>2 & ~1;	\
+		used = count*4;						\
+		while (count > 0) {					\
+			if (get_user(data, ptr++))			\
+				return -EFAULT;				\
+			data = convsample(data);			\
+			*lefth++ = data>>8;				\
+			*leftl++ = (data>>2) & 0x3f;			\
+			if (get_user(data, ptr++))			\
+				return -EFAULT;				\
+			data = convsample(data);			\
+			*righth++ = data>>8;				\
+			*rightl++ = (data>>2) & 0x3f;			\
+			count--;					\
+		}							\
+	}								\
+	*frameUsed += used;						\
+	return used;							\
+}
+
+#define AMI_CT_S16BE(x)	(x)
+#define AMI_CT_U16BE(x)	((x) ^ 0x8000)
+#define AMI_CT_S16LE(x)	(le2be16((x)))
+#define AMI_CT_U16LE(x)	(le2be16((x)) ^ 0x8000)
+
+GENERATE_AMI_CT_16(ami_ct_s16be, AMI_CT_S16BE)
+GENERATE_AMI_CT_16(ami_ct_u16be, AMI_CT_U16BE)
+GENERATE_AMI_CT_16(ami_ct_s16le, AMI_CT_S16LE)
+GENERATE_AMI_CT_16(ami_ct_u16le, AMI_CT_U16LE)
+
+
+static TRANS transAmiga = {
+	.ct_ulaw	= ami_ct_ulaw,
+	.ct_alaw	= ami_ct_alaw,
+	.ct_s8		= ami_ct_s8,
+	.ct_u8		= ami_ct_u8,
+	.ct_s16be	= ami_ct_s16be,
+	.ct_u16be	= ami_ct_u16be,
+	.ct_s16le	= ami_ct_s16le,
+	.ct_u16le	= ami_ct_u16le,
+};
+
+/*** Low level stuff *********************************************************/
+
+static inline void StopDMA(void)
+{
+	custom.aud[0].audvol = custom.aud[1].audvol = 0;
+	custom.aud[2].audvol = custom.aud[3].audvol = 0;
+	custom.dmacon = AMI_AUDIO_OFF;
+	enable_heartbeat();
+}
+
+static void *AmiAlloc(unsigned int size, gfp_t flags)
+{
+	return amiga_chip_alloc((long)size, "dmasound [Paula]");
+}
+
+static void AmiFree(void *obj, unsigned int size)
+{
+	amiga_chip_free (obj);
+}
+
+static int __init AmiIrqInit(void)
+{
+	/* turn off DMA for audio channels */
+	StopDMA();
+
+	/* Register interrupt handler. */
+	if (request_irq(IRQ_AMIGA_AUD0, AmiInterrupt, 0, "DMA sound",
+			AmiInterrupt))
+		return 0;
+	return 1;
+}
+
+#ifdef MODULE
+static void AmiIrqCleanUp(void)
+{
+	/* turn off DMA for audio channels */
+	StopDMA();
+	/* release the interrupt */
+	free_irq(IRQ_AMIGA_AUD0, AmiInterrupt);
+}
+#endif /* MODULE */
+
+static void AmiSilence(void)
+{
+	/* turn off DMA for audio channels */
+	StopDMA();
+}
+
+
+static void AmiInit(void)
+{
+	int period, i;
+
+	AmiSilence();
+
+	if (dmasound.soft.speed)
+		period = amiga_colorclock/dmasound.soft.speed-1;
+	else
+		period = amiga_audio_min_period;
+	dmasound.hard = dmasound.soft;
+	dmasound.trans_write = &transAmiga;
+
+	if (period < amiga_audio_min_period) {
+		/* we would need to squeeze the sound, but we won't do that */
+		period = amiga_audio_min_period;
+	} else if (period > 65535) {
+		period = 65535;
+	}
+	dmasound.hard.speed = amiga_colorclock/(period+1);
+
+	for (i = 0; i < 4; i++)
+		custom.aud[i].audper = period;
+	amiga_audio_period = period;
+}
+
+
+static int AmiSetFormat(int format)
+{
+	int size;
+
+	/* Amiga sound DMA supports 8bit and 16bit (pseudo 14 bit) 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;
+	}
+	AmiInit();
+
+	return format;
+}
+
+
+#define VOLUME_VOXWARE_TO_AMI(v) \
+	(((v) < 0) ? 0 : ((v) > 100) ? 64 : ((v) * 64)/100)
+#define VOLUME_AMI_TO_VOXWARE(v) ((v)*100/64)
+
+static int AmiSetVolume(int volume)
+{
+	dmasound.volume_left = VOLUME_VOXWARE_TO_AMI(volume & 0xff);
+	custom.aud[0].audvol = dmasound.volume_left;
+	dmasound.volume_right = VOLUME_VOXWARE_TO_AMI((volume & 0xff00) >> 8);
+	custom.aud[1].audvol = dmasound.volume_right;
+	if (dmasound.hard.size == 16) {
+		if (dmasound.volume_left == 64 && dmasound.volume_right == 64) {
+			custom.aud[2].audvol = 1;
+			custom.aud[3].audvol = 1;
+		} else {
+			custom.aud[2].audvol = 0;
+			custom.aud[3].audvol = 0;
+		}
+	}
+	return VOLUME_AMI_TO_VOXWARE(dmasound.volume_left) |
+	       (VOLUME_AMI_TO_VOXWARE(dmasound.volume_right) << 8);
+}
+
+static int AmiSetTreble(int treble)
+{
+	dmasound.treble = treble;
+	if (treble < 50)
+		ciaa.pra &= ~0x02;
+	else
+		ciaa.pra |= 0x02;
+	return treble;
+}
+
+
+#define AMI_PLAY_LOADED		1
+#define AMI_PLAY_PLAYING	2
+#define AMI_PLAY_MASK		3
+
+
+static void AmiPlayNextFrame(int index)
+{
+	u_char *start, *ch0, *ch1, *ch2, *ch3;
+	u_long size;
+
+	/* used by AmiPlay() if all doubts whether there really is something
+	 * to be played are already wiped out.
+	 */
+	start = write_sq.buffers[write_sq.front];
+	size = (write_sq.count == index ? write_sq.rear_size
+					: write_sq.block_size)>>1;
+
+	if (dmasound.hard.stereo) {
+		ch0 = start;
+		ch1 = start+write_sq_block_size_half;
+		size >>= 1;
+	} else {
+		ch0 = start;
+		ch1 = start;
+	}
+
+	disable_heartbeat();
+	custom.aud[0].audvol = dmasound.volume_left;
+	custom.aud[1].audvol = dmasound.volume_right;
+	if (dmasound.hard.size == 8) {
+		custom.aud[0].audlc = (u_short *)ZTWO_PADDR(ch0);
+		custom.aud[0].audlen = size;
+		custom.aud[1].audlc = (u_short *)ZTWO_PADDR(ch1);
+		custom.aud[1].audlen = size;
+		custom.dmacon = AMI_AUDIO_8;
+	} else {
+		size >>= 1;
+		custom.aud[0].audlc = (u_short *)ZTWO_PADDR(ch0);
+		custom.aud[0].audlen = size;
+		custom.aud[1].audlc = (u_short *)ZTWO_PADDR(ch1);
+		custom.aud[1].audlen = size;
+		if (dmasound.volume_left == 64 && dmasound.volume_right == 64) {
+			/* We can play pseudo 14-bit only with the maximum volume */
+			ch3 = ch0+write_sq_block_size_quarter;
+			ch2 = ch1+write_sq_block_size_quarter;
+			custom.aud[2].audvol = 1;  /* we are being affected by the beeps */
+			custom.aud[3].audvol = 1;  /* restoring volume here helps a bit */
+			custom.aud[2].audlc = (u_short *)ZTWO_PADDR(ch2);
+			custom.aud[2].audlen = size;
+			custom.aud[3].audlc = (u_short *)ZTWO_PADDR(ch3);
+			custom.aud[3].audlen = size;
+			custom.dmacon = AMI_AUDIO_14;
+		} else {
+			custom.aud[2].audvol = 0;
+			custom.aud[3].audvol = 0;
+			custom.dmacon = AMI_AUDIO_8;
+		}
+	}
+	write_sq.front = (write_sq.front+1) % write_sq.max_count;
+	write_sq.active |= AMI_PLAY_LOADED;
+}
+
+
+static void AmiPlay(void)
+{
+	int minframes = 1;
+
+	custom.intena = IF_AUD0;
+
+	if (write_sq.active & AMI_PLAY_LOADED) {
+		/* There's already a frame loaded */
+		custom.intena = IF_SETCLR | IF_AUD0;
+		return;
+	}
+
+	if (write_sq.active & AMI_PLAY_PLAYING)
+		/* Increase threshold: frame 1 is already being played */
+		minframes = 2;
+
+	if (write_sq.count < minframes) {
+		/* Nothing to do */
+		custom.intena = IF_SETCLR | IF_AUD0;
+		return;
+	}
+
+	if (write_sq.count <= minframes &&
+	    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?
+		 */
+		custom.intena = IF_SETCLR | IF_AUD0;
+		return;
+	}
+
+	AmiPlayNextFrame(minframes);
+
+	custom.intena = IF_SETCLR | IF_AUD0;
+}
+
+
+static irqreturn_t AmiInterrupt(int irq, void *dummy)
+{
+	int minframes = 1;
+
+	custom.intena = IF_AUD0;
+
+	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 IRQ_HANDLED;
+	}
+
+	if (write_sq.active & AMI_PLAY_PLAYING) {
+		/* We've just finished a frame */
+		write_sq.count--;
+		WAKE_UP(write_sq.action_queue);
+	}
+
+	if (write_sq.active & AMI_PLAY_LOADED)
+		/* Increase threshold: frame 1 is already being played */
+		minframes = 2;
+
+	/* Shift the flags */
+	write_sq.active = (write_sq.active<<1) & AMI_PLAY_MASK;
+
+	if (!write_sq.active)
+		/* No frame is playing, disable audio DMA */
+		StopDMA();
+
+	custom.intena = IF_SETCLR | IF_AUD0;
+
+	if (write_sq.count >= minframes)
+		/* Try to play the next frame */
+		AmiPlay();
+
+	if (!write_sq.active)
+		/* Nothing to play anymore.
+		   Wake up a process waiting for audio output to drain. */
+		WAKE_UP(write_sq.sync_queue);
+	return IRQ_HANDLED;
+}
+
+/*** Mid level stuff *********************************************************/
+
+
+/*
+ * /dev/mixer abstraction
+ */
+
+static void __init AmiMixerInit(void)
+{
+	dmasound.volume_left = 64;
+	dmasound.volume_right = 64;
+	custom.aud[0].audvol = dmasound.volume_left;
+	custom.aud[3].audvol = 1;	/* For pseudo 14bit */
+	custom.aud[1].audvol = dmasound.volume_right;
+	custom.aud[2].audvol = 1;	/* For pseudo 14bit */
+	dmasound.treble = 50;
+}
+
+static int AmiMixerIoctl(u_int cmd, u_long arg)
+{
+	int data;
+	switch (cmd) {
+	    case SOUND_MIXER_READ_DEVMASK:
+		    return IOCTL_OUT(arg, SOUND_MASK_VOLUME | SOUND_MASK_TREBLE);
+	    case SOUND_MIXER_READ_RECMASK:
+		    return IOCTL_OUT(arg, 0);
+	    case SOUND_MIXER_READ_STEREODEVS:
+		    return IOCTL_OUT(arg, SOUND_MASK_VOLUME);
+	    case SOUND_MIXER_READ_VOLUME:
+		    return IOCTL_OUT(arg,
+			    VOLUME_AMI_TO_VOXWARE(dmasound.volume_left) |
+			    VOLUME_AMI_TO_VOXWARE(dmasound.volume_right) << 8);
+	    case SOUND_MIXER_WRITE_VOLUME:
+		    IOCTL_IN(arg, data);
+		    return IOCTL_OUT(arg, dmasound_set_volume(data));
+	    case SOUND_MIXER_READ_TREBLE:
+		    return IOCTL_OUT(arg, dmasound.treble);
+	    case SOUND_MIXER_WRITE_TREBLE:
+		    IOCTL_IN(arg, data);
+		    return IOCTL_OUT(arg, dmasound_set_treble(data));
+	}
+	return -EINVAL;
+}
+
+
+static int AmiWriteSqSetup(void)
+{
+	write_sq_block_size_half = write_sq.block_size>>1;
+	write_sq_block_size_quarter = write_sq_block_size_half>>1;
+	return 0;
+}
+
+
+static int AmiStateInfo(char *buffer, size_t space)
+{
+	int len = 0;
+	len += sprintf(buffer+len, "\tsound.volume_left = %d [0...64]\n",
+		       dmasound.volume_left);
+	len += sprintf(buffer+len, "\tsound.volume_right = %d [0...64]\n",
+		       dmasound.volume_right);
+	if (len >= space) {
+		printk(KERN_ERR "dmasound_paula: overflowed state buffer alloc.\n") ;
+		len = space ;
+	}
+	return len;
+}
+
+
+/*** Machine definitions *****************************************************/
+
+static SETTINGS def_hard = {
+	.format	= AFMT_S8,
+	.stereo	= 0,
+	.size	= 8,
+	.speed	= 8000
+} ;
+
+static SETTINGS def_soft = {
+	.format	= AFMT_U8,
+	.stereo	= 0,
+	.size	= 8,
+	.speed	= 8000
+} ;
+
+static MACHINE machAmiga = {
+	.name		= "Amiga",
+	.name2		= "AMIGA",
+	.owner		= THIS_MODULE,
+	.dma_alloc	= AmiAlloc,
+	.dma_free	= AmiFree,
+	.irqinit	= AmiIrqInit,
+#ifdef MODULE
+	.irqcleanup	= AmiIrqCleanUp,
+#endif /* MODULE */
+	.init		= AmiInit,
+	.silence	= AmiSilence,
+	.setFormat	= AmiSetFormat,
+	.setVolume	= AmiSetVolume,
+	.setTreble	= AmiSetTreble,
+	.play		= AmiPlay,
+	.mixer_init	= AmiMixerInit,
+	.mixer_ioctl	= AmiMixerIoctl,
+	.write_sq_setup	= AmiWriteSqSetup,
+	.state_info	= AmiStateInfo,
+	.min_dsp_speed	= 8000,
+	.version	= ((DMASOUND_PAULA_REVISION<<8) | DMASOUND_PAULA_EDITION),
+	.hardware_afmts	= (AFMT_S8 | AFMT_S16_BE), /* h'ware-supported formats *only* here */
+	.capabilities	= DSP_CAP_BATCH          /* As per SNDCTL_DSP_GETCAPS */
+};
+
+
+/*** Config & Setup **********************************************************/
+
+
+static int __init amiga_audio_probe(struct platform_device *pdev)
+{
+	dmasound.mach = machAmiga;
+	dmasound.mach.default_hard = def_hard ;
+	dmasound.mach.default_soft = def_soft ;
+	return dmasound_init();
+}
+
+static int __exit amiga_audio_remove(struct platform_device *pdev)
+{
+	dmasound_deinit();
+	return 0;
+}
+
+static struct platform_driver amiga_audio_driver = {
+	.remove = __exit_p(amiga_audio_remove),
+	.driver   = {
+		.name	= "amiga-audio",
+	},
+};
+
+module_platform_driver_probe(amiga_audio_driver, amiga_audio_probe);
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:amiga-audio");
diff --git a/src/kernel/linux/v4.19/sound/oss/dmasound/dmasound_q40.c b/src/kernel/linux/v4.19/sound/oss/dmasound/dmasound_q40.c
new file mode 100644
index 0000000..be4fe15
--- /dev/null
+++ b/src/kernel/linux/v4.19/sound/oss/dmasound/dmasound_q40.c
@@ -0,0 +1,638 @@
+/*
+ *  linux/sound/oss/dmasound/dmasound_q40.c
+ *
+ *  Q40 DMA Sound Driver
+ *
+ *  See linux/sound/oss/dmasound/dmasound_core.c for copyright and credits
+ *  prior to 28/01/2001
+ *
+ *  28/01/2001 [0.1] Iain Sandoe
+ *		     - added versioning
+ *		     - put in and populated the hardware_afmts field.
+ *             [0.2] - put in SNDCTL_DSP_GETCAPS value.
+ *	       [0.3] - put in default hard/soft settings.
+ */
+
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/soundcard.h>
+#include <linux/interrupt.h>
+
+#include <linux/uaccess.h>
+#include <asm/q40ints.h>
+#include <asm/q40_master.h>
+
+#include "dmasound.h"
+
+#define DMASOUND_Q40_REVISION 0
+#define DMASOUND_Q40_EDITION 3
+
+static int expand_bal;	/* Balance factor for expanding (not volume!) */
+static int expand_data;	/* Data for expanding */
+
+
+/*** Low level stuff *********************************************************/
+
+
+static void *Q40Alloc(unsigned int size, gfp_t flags);
+static void Q40Free(void *, unsigned int);
+static int Q40IrqInit(void);
+#ifdef MODULE
+static void Q40IrqCleanUp(void);
+#endif
+static void Q40Silence(void);
+static void Q40Init(void);
+static int Q40SetFormat(int format);
+static int Q40SetVolume(int volume);
+static void Q40PlayNextFrame(int index);
+static void Q40Play(void);
+static irqreturn_t Q40StereoInterrupt(int irq, void *dummy);
+static irqreturn_t Q40MonoInterrupt(int irq, void *dummy);
+static void Q40Interrupt(void);
+
+
+/*** Mid level stuff *********************************************************/
+
+
+
+/* userCount, frameUsed, frameLeft == byte counts */
+static ssize_t q40_ct_law(const u_char __user *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 = (u_char *) &frame[*frameUsed];
+
+	used = count = min_t(size_t, userCount, frameLeft);
+	if (copy_from_user(p,userPtr,count))
+	  return -EFAULT;
+	while (count > 0) {
+		*p = table[*p]+128;
+		p++;
+		count--;
+	}
+	*frameUsed += used ;
+	return used;
+}
+
+
+static ssize_t q40_ct_s8(const u_char __user *userPtr, size_t userCount,
+			  u_char frame[], ssize_t *frameUsed,
+			  ssize_t frameLeft)
+{
+	ssize_t count, used;
+	u_char *p = (u_char *) &frame[*frameUsed];
+
+	used = count = min_t(size_t, userCount, frameLeft);
+	if (copy_from_user(p,userPtr,count))
+	  return -EFAULT;
+	while (count > 0) {
+		*p = *p + 128;
+		p++;
+		count--;
+	}
+	*frameUsed += used;
+	return used;
+}
+
+static ssize_t q40_ct_u8(const u_char __user *userPtr, size_t userCount,
+			  u_char frame[], ssize_t *frameUsed,
+			  ssize_t frameLeft)
+{
+	ssize_t count, used;
+	u_char *p = (u_char *) &frame[*frameUsed];
+
+	used = count = min_t(size_t, userCount, frameLeft);
+	if (copy_from_user(p,userPtr,count))
+	  return -EFAULT;
+	*frameUsed += used;
+	return used;
+}
+
+
+/* a bit too complicated to optimise right now ..*/
+static ssize_t q40_ctx_law(const u_char __user *userPtr, size_t userCount,
+			    u_char frame[], ssize_t *frameUsed,
+			    ssize_t frameLeft)
+{
+	unsigned char *table = (unsigned char *)
+		(dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8: dmasound_alaw2dma8);
+	unsigned int data = expand_data;
+	u_char *p = (u_char *) &frame[*frameUsed];
+	int bal = expand_bal;
+	int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
+	int utotal, ftotal;
+
+	ftotal = frameLeft;
+	utotal = userCount;
+	while (frameLeft) {
+		u_char c;
+		if (bal < 0) {
+			if (userCount == 0)
+				break;
+			if (get_user(c, userPtr++))
+				return -EFAULT;
+			data = table[c];
+			data += 0x80;
+			userCount--;
+			bal += hSpeed;
+		}
+		*p++ = data;
+		frameLeft--;
+		bal -= sSpeed;
+	}
+	expand_bal = bal;
+	expand_data = data;
+	*frameUsed += (ftotal - frameLeft);
+	utotal -= userCount;
+	return utotal;
+}
+
+
+static ssize_t q40_ctx_s8(const u_char __user *userPtr, size_t userCount,
+			   u_char frame[], ssize_t *frameUsed,
+			   ssize_t frameLeft)
+{
+	u_char *p = (u_char *) &frame[*frameUsed];
+	unsigned int data = expand_data;
+	int bal = expand_bal;
+	int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
+	int utotal, ftotal;
+
+
+	ftotal = frameLeft;
+	utotal = userCount;
+	while (frameLeft) {
+		u_char c;
+		if (bal < 0) {
+			if (userCount == 0)
+				break;
+			if (get_user(c, userPtr++))
+				return -EFAULT;
+			data = c ;
+			data += 0x80;
+			userCount--;
+			bal += hSpeed;
+		}
+		*p++ = data;
+		frameLeft--;
+		bal -= sSpeed;
+	}
+	expand_bal = bal;
+	expand_data = data;
+	*frameUsed += (ftotal - frameLeft);
+	utotal -= userCount;
+	return utotal;
+}
+
+
+static ssize_t q40_ctx_u8(const u_char __user *userPtr, size_t userCount,
+			   u_char frame[], ssize_t *frameUsed,
+			   ssize_t frameLeft)
+{
+	u_char *p = (u_char *) &frame[*frameUsed];
+	unsigned int data = expand_data;
+	int bal = expand_bal;
+	int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
+	int utotal, ftotal;
+
+	ftotal = frameLeft;
+	utotal = userCount;
+	while (frameLeft) {
+		u_char c;
+		if (bal < 0) {
+			if (userCount == 0)
+				break;
+			if (get_user(c, userPtr++))
+				return -EFAULT;
+			data = c ;
+			userCount--;
+			bal += hSpeed;
+		}
+		*p++ = data;
+		frameLeft--;
+		bal -= sSpeed;
+	}
+	expand_bal = bal;
+	expand_data = data;
+	*frameUsed += (ftotal - frameLeft) ;
+	utotal -= userCount;
+	return utotal;
+}
+
+/* compressing versions */
+static ssize_t q40_ctc_law(const u_char __user *userPtr, size_t userCount,
+			    u_char frame[], ssize_t *frameUsed,
+			    ssize_t frameLeft)
+{
+	unsigned char *table = (unsigned char *)
+		(dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8: dmasound_alaw2dma8);
+	unsigned int data = expand_data;
+	u_char *p = (u_char *) &frame[*frameUsed];
+	int bal = expand_bal;
+	int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
+	int utotal, ftotal;
+ 
+	ftotal = frameLeft;
+	utotal = userCount;
+	while (frameLeft) {
+		u_char c;
+		while(bal<0) {
+			if (userCount == 0)
+				goto lout;
+			if (!(bal<(-hSpeed))) {
+				if (get_user(c, userPtr))
+					return -EFAULT;
+				data = 0x80 + table[c];
+			}
+			userPtr++;
+			userCount--;
+			bal += hSpeed;
+		}
+		*p++ = data;
+		frameLeft--;
+		bal -= sSpeed;
+	}
+ lout:
+	expand_bal = bal;
+	expand_data = data;
+	*frameUsed += (ftotal - frameLeft);
+	utotal -= userCount;
+	return utotal;
+}
+
+
+static ssize_t q40_ctc_s8(const u_char __user *userPtr, size_t userCount,
+			   u_char frame[], ssize_t *frameUsed,
+			   ssize_t frameLeft)
+{
+	u_char *p = (u_char *) &frame[*frameUsed];
+	unsigned int data = expand_data;
+	int bal = expand_bal;
+	int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
+	int utotal, ftotal;
+
+	ftotal = frameLeft;
+	utotal = userCount;
+	while (frameLeft) {
+		u_char c;
+		while (bal < 0) {
+			if (userCount == 0)
+				goto lout;
+			if (!(bal<(-hSpeed))) {
+				if (get_user(c, userPtr))
+					return -EFAULT;
+				data = c + 0x80;
+			}
+			userPtr++;
+			userCount--;
+			bal += hSpeed;
+		}
+		*p++ = data;
+		frameLeft--;
+		bal -= sSpeed;
+	}
+ lout:
+	expand_bal = bal;
+	expand_data = data;
+	*frameUsed += (ftotal - frameLeft);
+	utotal -= userCount;
+	return utotal;
+}
+
+
+static ssize_t q40_ctc_u8(const u_char __user *userPtr, size_t userCount,
+			   u_char frame[], ssize_t *frameUsed,
+			   ssize_t frameLeft)
+{
+	u_char *p = (u_char *) &frame[*frameUsed];
+	unsigned int data = expand_data;
+	int bal = expand_bal;
+	int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
+	int utotal, ftotal;
+
+	ftotal = frameLeft;
+	utotal = userCount;
+	while (frameLeft) {
+		u_char c;
+		while (bal < 0) {
+			if (userCount == 0)
+				goto lout;
+			if (!(bal<(-hSpeed))) {
+				if (get_user(c, userPtr))
+					return -EFAULT;
+				data = c ;
+			}
+			userPtr++;
+			userCount--;
+			bal += hSpeed;
+		}
+		*p++ = data;
+		frameLeft--;
+		bal -= sSpeed;
+	}
+ lout:
+	expand_bal = bal;
+	expand_data = data;
+	*frameUsed += (ftotal - frameLeft) ;
+	utotal -= userCount;
+	return utotal;
+}
+
+
+static TRANS transQ40Normal = {
+	q40_ct_law, q40_ct_law, q40_ct_s8, q40_ct_u8, NULL, NULL, NULL, NULL
+};
+
+static TRANS transQ40Expanding = {
+	q40_ctx_law, q40_ctx_law, q40_ctx_s8, q40_ctx_u8, NULL, NULL, NULL, NULL
+};
+
+static TRANS transQ40Compressing = {
+	q40_ctc_law, q40_ctc_law, q40_ctc_s8, q40_ctc_u8, NULL, NULL, NULL, NULL
+};
+
+
+/*** Low level stuff *********************************************************/
+
+static void *Q40Alloc(unsigned int size, gfp_t flags)
+{
+         return kmalloc(size, flags); /* change to vmalloc */
+}
+
+static void Q40Free(void *ptr, unsigned int size)
+{
+	kfree(ptr);
+}
+
+static int __init Q40IrqInit(void)
+{
+	/* Register interrupt handler. */
+	if (request_irq(Q40_IRQ_SAMPLE, Q40StereoInterrupt, 0,
+		    "DMA sound", Q40Interrupt))
+		return 0;
+
+	return(1);
+}
+
+
+#ifdef MODULE
+static void Q40IrqCleanUp(void)
+{
+        master_outb(0,SAMPLE_ENABLE_REG);
+	free_irq(Q40_IRQ_SAMPLE, Q40Interrupt);
+}
+#endif /* MODULE */
+
+
+static void Q40Silence(void)
+{
+        master_outb(0,SAMPLE_ENABLE_REG);
+	*DAC_LEFT=*DAC_RIGHT=127;
+}
+
+static char *q40_pp;
+static unsigned int q40_sc;
+
+static void Q40PlayNextFrame(int index)
+{
+	u_char *start;
+	u_long size;
+	u_char speed;
+	int error;
+
+	/* used by Q40Play() if all doubts whether there really is something
+	 * to be played are already wiped out.
+	 */
+	start = write_sq.buffers[write_sq.front];
+	size = (write_sq.count == index ? write_sq.rear_size : write_sq.block_size);
+
+	q40_pp=start;
+	q40_sc=size;
+
+	write_sq.front = (write_sq.front+1) % write_sq.max_count;
+	write_sq.active++;
+
+	speed=(dmasound.hard.speed==10000 ? 0 : 1);
+
+	master_outb( 0,SAMPLE_ENABLE_REG);
+	free_irq(Q40_IRQ_SAMPLE, Q40Interrupt);
+	if (dmasound.soft.stereo)
+		error = request_irq(Q40_IRQ_SAMPLE, Q40StereoInterrupt, 0,
+				    "Q40 sound", Q40Interrupt);
+	  else
+		error = request_irq(Q40_IRQ_SAMPLE, Q40MonoInterrupt, 0,
+				    "Q40 sound", Q40Interrupt);
+	if (error && printk_ratelimit())
+		pr_err("Couldn't register sound interrupt\n");
+
+	master_outb( speed, SAMPLE_RATE_REG);
+	master_outb( 1,SAMPLE_CLEAR_REG);
+	master_outb( 1,SAMPLE_ENABLE_REG);
+}
+
+static void Q40Play(void)
+{
+        unsigned long flags;
+
+	if (write_sq.active || write_sq.count<=0 ) {
+		/* There's already a frame loaded */
+		return;
+	}
+
+	/* nothing in the queue */
+	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?
+		  */
+	         return;
+	}
+	spin_lock_irqsave(&dmasound.lock, flags);
+	Q40PlayNextFrame(1);
+	spin_unlock_irqrestore(&dmasound.lock, flags);
+}
+
+static irqreturn_t Q40StereoInterrupt(int irq, void *dummy)
+{
+	spin_lock(&dmasound.lock);
+        if (q40_sc>1){
+            *DAC_LEFT=*q40_pp++;
+	    *DAC_RIGHT=*q40_pp++;
+	    q40_sc -=2;
+	    master_outb(1,SAMPLE_CLEAR_REG);
+	}else Q40Interrupt();
+	spin_unlock(&dmasound.lock);
+	return IRQ_HANDLED;
+}
+static irqreturn_t Q40MonoInterrupt(int irq, void *dummy)
+{
+	spin_lock(&dmasound.lock);
+        if (q40_sc>0){
+            *DAC_LEFT=*q40_pp;
+	    *DAC_RIGHT=*q40_pp++;
+	    q40_sc --;
+	    master_outb(1,SAMPLE_CLEAR_REG);
+	}else Q40Interrupt();
+	spin_unlock(&dmasound.lock);
+	return IRQ_HANDLED;
+}
+static void Q40Interrupt(void)
+{
+	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);
+		   master_outb(0,SAMPLE_ENABLE_REG); /* better safe */
+		   goto exit;
+	} else write_sq.active=0;
+	write_sq.count--;
+	Q40Play();
+
+	if (q40_sc<2)
+	      { /* there was nothing to play, disable irq */
+		master_outb(0,SAMPLE_ENABLE_REG);
+		*DAC_LEFT=*DAC_RIGHT=127;
+	      }
+	WAKE_UP(write_sq.action_queue);
+
+ exit:
+	master_outb(1,SAMPLE_CLEAR_REG);
+}
+
+
+static void Q40Init(void)
+{
+	int i, idx;
+	const int freq[] = {10000, 20000};
+
+	/* search a frequency that fits into the allowed error range */
+
+	idx = -1;
+	for (i = 0; i < 2; i++)
+		if ((100 * abs(dmasound.soft.speed - freq[i]) / freq[i]) <= catchRadius)
+			idx = i;
+
+	dmasound.hard = dmasound.soft;
+	/*sound.hard.stereo=1;*/ /* no longer true */
+	dmasound.hard.size=8;
+
+	if (idx > -1) {
+		dmasound.soft.speed = freq[idx];
+		dmasound.trans_write = &transQ40Normal;
+	} else
+		dmasound.trans_write = &transQ40Expanding;
+
+	Q40Silence();
+
+	if (dmasound.hard.speed > 20200) {
+		/* squeeze the sound, we do that */
+		dmasound.hard.speed = 20000;
+		dmasound.trans_write = &transQ40Compressing;
+	} else if (dmasound.hard.speed > 10000) {
+		dmasound.hard.speed = 20000;
+	} else {
+		dmasound.hard.speed = 10000;
+	}
+	expand_bal = -dmasound.soft.speed;
+}
+
+
+static int Q40SetFormat(int format)
+{
+	/* Q40 sound 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;
+	}
+	Q40Init();
+
+	return(format);
+}
+
+static int Q40SetVolume(int volume)
+{
+    return 0;
+}
+
+
+/*** Machine definitions *****************************************************/
+
+static SETTINGS def_hard = {
+	.format	= AFMT_U8,
+	.stereo	= 0,
+	.size	= 8,
+	.speed	= 10000
+} ;
+
+static SETTINGS def_soft = {
+	.format	= AFMT_U8,
+	.stereo	= 0,
+	.size	= 8,
+	.speed	= 8000
+} ;
+
+static MACHINE machQ40 = {
+	.name		= "Q40",
+	.name2		= "Q40",
+	.owner		= THIS_MODULE,
+	.dma_alloc	= Q40Alloc,
+	.dma_free	= Q40Free,
+	.irqinit	= Q40IrqInit,
+#ifdef MODULE
+	.irqcleanup	= Q40IrqCleanUp,
+#endif /* MODULE */
+	.init		= Q40Init,
+	.silence	= Q40Silence,
+	.setFormat	= Q40SetFormat,
+	.setVolume	= Q40SetVolume,
+	.play		= Q40Play,
+ 	.min_dsp_speed	= 10000,
+	.version	= ((DMASOUND_Q40_REVISION<<8) | DMASOUND_Q40_EDITION),
+	.hardware_afmts	= AFMT_U8, /* h'ware-supported formats *only* here */
+	.capabilities	= DSP_CAP_BATCH  /* As per SNDCTL_DSP_GETCAPS */
+};
+
+
+/*** Config & Setup **********************************************************/
+
+
+static int __init dmasound_q40_init(void)
+{
+	if (MACH_IS_Q40) {
+	    dmasound.mach = machQ40;
+	    dmasound.mach.default_hard = def_hard ;
+	    dmasound.mach.default_soft = def_soft ;
+	    return dmasound_init();
+	} else
+	    return -ENODEV;
+}
+
+static void __exit dmasound_q40_cleanup(void)
+{
+	dmasound_deinit();
+}
+
+module_init(dmasound_q40_init);
+module_exit(dmasound_q40_cleanup);
+
+MODULE_DESCRIPTION("Q40/Q60 sound driver");
+MODULE_LICENSE("GPL");