
/* linux/drivers/sound/dmasound.c */

/*

   OSS/Free compatible Atari TT/Falcon and Amiga DMA sound driver for Linux/m68k

   (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: fixed atari_stram_alloc() call, the timer programming
   and several race conditions

   1995/9/14    ++roman: 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    ++TeSche: Fixed a bug in sq_write and changed /dev/audio
   converting to play at 12517Hz instead of 6258Hz.

   1995/9/23    ++TeSche: 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@ac3.maus.de, ++TeSche: 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   ++TeSche: 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: useful hints and bug fixes, cross-checked it for
   Falcons

   1996/3/9     ++geert: 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: 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: modularization

 */


#include <linux/module.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/major.h>
#include <linux/config.h>
#include <linux/fcntl.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/malloc.h>

#include <asm/setup.h>
#include <asm/system.h>
#include <asm/irq.h>
#include <asm/pgtable.h>
#include <asm/uaccess.h>

#ifdef CONFIG_ATARI
#include <asm/atarihw.h>
#include <asm/atariints.h>
#endif				/* CONFIG_ATARI */
#ifdef CONFIG_AMIGA
#include <asm/amigahw.h>
#include <asm/amigaints.h>
#endif				/* CONFIG_AMIGA */

#include "dmasound.h"
#include <linux/soundcard.h>


#ifdef MODULE
static int      chrdev_registered = 0;
static int      irq_installed = 0;

#endif				/* MODULE */
static char   **sound_buffers = NULL;


#ifdef CONFIG_ATARI
extern void     atari_microwire_cmd(int cmd);

#endif				/* CONFIG_ATARI */

#ifdef CONFIG_AMIGA
   /*
    * 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)

#endif				/* CONFIG_AMIGA */


/*** Some declarations *******************************************************/


#define DMASND_TT		1
#define DMASND_FALCON		2
#define DMASND_AMIGA		3

#define MAX_CATCH_RADIUS	10
#define MIN_BUFFERS		4
#define MIN_BUFSIZE 		4
#define MAX_BUFSIZE		128	/* Limit for Amiga */

static int      catchRadius = 0, numBufs = 4, bufSize = 32;


#define arraysize(x)	(sizeof(x)/sizeof(*(x)))
#define min(x, y)	((x) < (y) ? (x) : (y))
#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 *)(arg)); \
      if (error) return error; \
 } while (0)
#define IOCTL_OUT(arg, ret)	ioctl_return((int *)(arg), ret)


/*** Some low level helpers **************************************************/


/* 8 bit mu-law */

static char     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 */

static char     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
};


#ifdef HAS_16BIT_TABLES

/* 16 bit mu-law */

static char     ulaw2dma16[] =
{
	-32124, -31100, -30076, -29052, -28028, -27004, -25980, -24956,
	-23932, -22908, -21884, -20860, -19836, -18812, -17788, -16764,
	-15996, -15484, -14972, -14460, -13948, -13436, -12924, -12412,
	-11900, -11388, -10876, -10364, -9852, -9340, -8828, -8316,
	-7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140,
	-5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092,
	-3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004,
	-2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980,
	-1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436,
	-1372, -1308, -1244, -1180, -1116, -1052, -988, -924,
	-876, -844, -812, -780, -748, -716, -684, -652,
	-620, -588, -556, -524, -492, -460, -428, -396,
	-372, -356, -340, -324, -308, -292, -276, -260,
	-244, -228, -212, -196, -180, -164, -148, -132,
	-120, -112, -104, -96, -88, -80, -72, -64,
	-56, -48, -40, -32, -24, -16, -8, 0,
	32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956,
	23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764,
	15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412,
	11900, 11388, 10876, 10364, 9852, 9340, 8828, 8316,
	7932, 7676, 7420, 7164, 6908, 6652, 6396, 6140,
	5884, 5628, 5372, 5116, 4860, 4604, 4348, 4092,
	3900, 3772, 3644, 3516, 3388, 3260, 3132, 3004,
	2876, 2748, 2620, 2492, 2364, 2236, 2108, 1980,
	1884, 1820, 1756, 1692, 1628, 1564, 1500, 1436,
	1372, 1308, 1244, 1180, 1116, 1052, 988, 924,
	876, 844, 812, 780, 748, 716, 684, 652,
	620, 588, 556, 524, 492, 460, 428, 396,
	372, 356, 340, 324, 308, 292, 276, 260,
	244, 228, 212, 196, 180, 164, 148, 132,
	120, 112, 104, 96, 88, 80, 72, 64,
	56, 48, 40, 32, 24, 16, 8, 0,
};

/* 16 bit A-law */

static char     alaw2dma16[] =
{
	-5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736,
	-7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784,
	-2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368,
	-3776, -3648, -4032, -3904, -3264, -3136, -3520, -3392,
	-22016, -20992, -24064, -23040, -17920, -16896, -19968, -18944,
	-30208, -29184, -32256, -31232, -26112, -25088, -28160, -27136,
	-11008, -10496, -12032, -11520, -8960, -8448, -9984, -9472,
	-15104, -14592, -16128, -15616, -13056, -12544, -14080, -13568,
	-344, -328, -376, -360, -280, -264, -312, -296,
	-472, -456, -504, -488, -408, -392, -440, -424,
	-88, -72, -120, -104, -24, -8, -56, -40,
	-216, -200, -248, -232, -152, -136, -184, -168,
	-1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184,
	-1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696,
	-688, -656, -752, -720, -560, -528, -624, -592,
	-944, -912, -1008, -976, -816, -784, -880, -848,
	5504, 5248, 6016, 5760, 4480, 4224, 4992, 4736,
	7552, 7296, 8064, 7808, 6528, 6272, 7040, 6784,
	2752, 2624, 3008, 2880, 2240, 2112, 2496, 2368,
	3776, 3648, 4032, 3904, 3264, 3136, 3520, 3392,
	22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944,
	30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136,
	11008, 10496, 12032, 11520, 8960, 8448, 9984, 9472,
	15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568,
	344, 328, 376, 360, 280, 264, 312, 296,
	472, 456, 504, 488, 408, 392, 440, 424,
	88, 72, 120, 104, 24, 8, 56, 40,
	216, 200, 248, 232, 152, 136, 184, 168,
	1376, 1312, 1504, 1440, 1120, 1056, 1248, 1184,
	1888, 1824, 2016, 1952, 1632, 1568, 1760, 1696,
	688, 656, 752, 720, 560, 528, 624, 592,
	944, 912, 1008, 976, 816, 784, 880, 848,
};

#endif				/* HAS_16BIT_TABLES */


#ifdef HAS_14BIT_TABLES

/* 14 bit mu-law (LSB) */

static char     alaw2dma14l[] =
{
	33, 33, 33, 33, 33, 33, 33, 33,
	33, 33, 33, 33, 33, 33, 33, 33,
	33, 33, 33, 33, 33, 33, 33, 33,
	33, 33, 33, 33, 33, 33, 33, 33,
	1, 1, 1, 1, 1, 1, 1, 1,
	1, 1, 1, 1, 1, 1, 1, 1,
	49, 17, 49, 17, 49, 17, 49, 17,
	49, 17, 49, 17, 49, 17, 49, 17,
	41, 57, 9, 25, 41, 57, 9, 25,
	41, 57, 9, 25, 41, 57, 9, 25,
	37, 45, 53, 61, 5, 13, 21, 29,
	37, 45, 53, 61, 5, 13, 21, 29,
	35, 39, 43, 47, 51, 55, 59, 63,
	3, 7, 11, 15, 19, 23, 27, 31,
	34, 36, 38, 40, 42, 44, 46, 48,
	50, 52, 54, 56, 58, 60, 62, 0,
	31, 31, 31, 31, 31, 31, 31, 31,
	31, 31, 31, 31, 31, 31, 31, 31,
	31, 31, 31, 31, 31, 31, 31, 31,
	31, 31, 31, 31, 31, 31, 31, 31,
	63, 63, 63, 63, 63, 63, 63, 63,
	63, 63, 63, 63, 63, 63, 63, 63,
	15, 47, 15, 47, 15, 47, 15, 47,
	15, 47, 15, 47, 15, 47, 15, 47,
	23, 7, 55, 39, 23, 7, 55, 39,
	23, 7, 55, 39, 23, 7, 55, 39,
	27, 19, 11, 3, 59, 51, 43, 35,
	27, 19, 11, 3, 59, 51, 43, 35,
	29, 25, 21, 17, 13, 9, 5, 1,
	61, 57, 53, 49, 45, 41, 37, 33,
	30, 28, 26, 24, 22, 20, 18, 16,
	14, 12, 10, 8, 6, 4, 2, 0
};

/* 14 bit A-law (LSB) */

static char     alaw2dma14l[] =
{
	32, 32, 32, 32, 32, 32, 32, 32,
	32, 32, 32, 32, 32, 32, 32, 32,
	16, 48, 16, 48, 16, 48, 16, 48,
	16, 48, 16, 48, 16, 48, 16, 48,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	42, 46, 34, 38, 58, 62, 50, 54,
	10, 14, 2, 6, 26, 30, 18, 22,
	42, 46, 34, 38, 58, 62, 50, 54,
	10, 14, 2, 6, 26, 30, 18, 22,
	40, 56, 8, 24, 40, 56, 8, 24,
	40, 56, 8, 24, 40, 56, 8, 24,
	20, 28, 4, 12, 52, 60, 36, 44,
	20, 28, 4, 12, 52, 60, 36, 44,
	32, 32, 32, 32, 32, 32, 32, 32,
	32, 32, 32, 32, 32, 32, 32, 32,
	48, 16, 48, 16, 48, 16, 48, 16,
	48, 16, 48, 16, 48, 16, 48, 16,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	22, 18, 30, 26, 6, 2, 14, 10,
	54, 50, 62, 58, 38, 34, 46, 42,
	22, 18, 30, 26, 6, 2, 14, 10,
	54, 50, 62, 58, 38, 34, 46, 42,
	24, 8, 56, 40, 24, 8, 56, 40,
	24, 8, 56, 40, 24, 8, 56, 40,
	44, 36, 60, 52, 12, 4, 28, 20,
	44, 36, 60, 52, 12, 4, 28, 20
};

#endif				/* HAS_14BIT_TABLES */


/*** Translations ************************************************************/


#ifdef CONFIG_ATARI
static long     ata_ct_law(const u_char * userPtr, unsigned long userCount,
			u_char frame[], long *frameUsed, long frameLeft);
static long     ata_ct_s8(const u_char * userPtr, unsigned long userCount,
			u_char frame[], long *frameUsed, long frameLeft);
static long     ata_ct_u8(const u_char * userPtr, unsigned long userCount,
			u_char frame[], long *frameUsed, long frameLeft);
static long     ata_ct_s16be(const u_char * userPtr, unsigned long userCount,
			u_char frame[], long *frameUsed, long frameLeft);
static long     ata_ct_u16be(const u_char * userPtr, unsigned long userCount,
			u_char frame[], long *frameUsed, long frameLeft);
static long     ata_ct_s16le(const u_char * userPtr, unsigned long userCount,
			u_char frame[], long *frameUsed, long frameLeft);
static long     ata_ct_u16le(const u_char * userPtr, unsigned long userCount,
			u_char frame[], long *frameUsed, long frameLeft);
static long     ata_ctx_law(const u_char * userPtr, unsigned long userCount,
			u_char frame[], long *frameUsed, long frameLeft);
static long     ata_ctx_s8(const u_char * userPtr, unsigned long userCount,
			u_char frame[], long *frameUsed, long frameLeft);
static long     ata_ctx_u8(const u_char * userPtr, unsigned long userCount,
			u_char frame[], long *frameUsed, long frameLeft);
static long     ata_ctx_s16be(const u_char * userPtr, unsigned long userCount,
			u_char frame[], long *frameUsed, long frameLeft);
static long     ata_ctx_u16be(const u_char * userPtr, unsigned long userCount,
			u_char frame[], long *frameUsed, long frameLeft);
static long     ata_ctx_s16le(const u_char * userPtr, unsigned long userCount,
			u_char frame[], long *frameUsed, long frameLeft);
static long     ata_ctx_u16le(const u_char * userPtr, unsigned long userCount,
			u_char frame[], long *frameUsed, long frameLeft);

#endif				/* CONFIG_ATARI */

#ifdef CONFIG_AMIGA
static long     ami_ct_law(const u_char * userPtr, unsigned long userCount,
			u_char frame[], long *frameUsed, long frameLeft);
static long     ami_ct_s8(const u_char * userPtr, unsigned long userCount,
			u_char frame[], long *frameUsed, long frameLeft);
static long     ami_ct_u8(const u_char * userPtr, unsigned long userCount,
			u_char frame[], long *frameUsed, long frameLeft);
static long     ami_ct_s16be(const u_char * userPtr, unsigned long userCount,
			u_char frame[], long *frameUsed, long frameLeft);
static long     ami_ct_u16be(const u_char * userPtr, unsigned long userCount,
			u_char frame[], long *frameUsed, long frameLeft);
static long     ami_ct_s16le(const u_char * userPtr, unsigned long userCount,
			u_char frame[], long *frameUsed, long frameLeft);
static long     ami_ct_u16le(const u_char * userPtr, unsigned long userCount,
			u_char frame[], long *frameUsed, long frameLeft);

#endif				/* CONFIG_AMIGA */


/*** Machine definitions *****************************************************/


typedef struct
  {
	  int             type;
	  void           *(*dma_alloc) (unsigned int, int);
	  void            (*dma_free) (void *, unsigned int);
	  int             (*irqinit) (void);
#ifdef MODULE
	  void            (*irqcleanup) (void);
#endif				/* MODULE */
	  void            (*init) (void);
	  void            (*silence) (void);
	  int             (*setFormat) (int);
	  int             (*setVolume) (int);
	  int             (*setBass) (int);
	  int             (*setTreble) (int);
	  void            (*play) (void);
  }
MACHINE;


/*** Low level stuff *********************************************************/


typedef struct
  {
	  int             format;	/* AFMT_* */
	  int             stereo;	/* 0 = mono, 1 = stereo */
	  int             size;	/* 8/16 bit */
	  int             speed;	/* speed */
  }
SETTINGS;

typedef struct
  {
	  long            (*ct_ulaw) (const u_char *, unsigned long, u_char *, long *, long);
	  long            (*ct_alaw) (const u_char *, unsigned long, u_char *, long *, long);
	  long            (*ct_s8) (const u_char *, unsigned long, u_char *, long *, long);
	  long            (*ct_u8) (const u_char *, unsigned long, u_char *, long *, long);
	  long            (*ct_s16be) (const u_char *, unsigned long, u_char *, long *, long);
	  long            (*ct_u16be) (const u_char *, unsigned long, u_char *, long *, long);
	  long            (*ct_s16le) (const u_char *, unsigned long, u_char *, long *, long);
	  long            (*ct_u16le) (const u_char *, unsigned long, u_char *, long *, long);
  }
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;	/* supported translations */
	  int             volume_left;	/* volume (range is machine dependent) */
	  int             volume_right;
	  int             bass;	/* tone (range is machine dependent) */
	  int             treble;
	  int             minDev;	/* minor device number currently open */
#ifdef CONFIG_ATARI
	  int             bal;	/* balance factor for expanding (not volume!) */
	  u_long          data;	/* data for expanding */
#endif				/* CONFIG_ATARI */
  };

static struct sound_settings sound;


#ifdef CONFIG_ATARI
static void    *AtaAlloc(unsigned int size, int flags);
static void     AtaFree(void *, unsigned int size);
static int      AtaIrqInit(void);

#ifdef MODULE
static void     AtaIrqCleanUp(void);

#endif				/* MODULE */
static int      AtaSetBass(int bass);
static int      AtaSetTreble(int treble);
static void     TTSilence(void);
static void     TTInit(void);
static int      TTSetFormat(int format);
static int      TTSetVolume(int volume);
static void     FalconSilence(void);
static void     FalconInit(void);
static int      FalconSetFormat(int format);
static int      FalconSetVolume(int volume);
static void     ata_sq_play_next_frame(int index);
static void     AtaPlay(void);
static void     ata_sq_interrupt(int irq, void *dummy, struct pt_regs *fp);

#endif				/* CONFIG_ATARI */

#ifdef CONFIG_AMIGA
static void    *AmiAlloc(unsigned int size, int flags);
static void     AmiFree(void *, unsigned int);
static int      AmiIrqInit(void);

#ifdef MODULE
static void     AmiIrqCleanUp(void);

#endif				/* MODULE */
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     ami_sq_play_next_frame(int index);
static void     AmiPlay(void);
static void     ami_sq_interrupt(int irq, void *dummy, struct pt_regs *fp);

#endif				/* CONFIG_AMIGA */


/*** Mid level stuff *********************************************************/


static void     sound_silence(void);
static void     sound_init(void);
static int      sound_set_format(int format);
static int      sound_set_speed(int speed);
static int      sound_set_stereo(int stereo);
static int      sound_set_volume(int volume);

#ifdef CONFIG_ATARI
static int      sound_set_bass(int bass);

#endif				/* CONFIG_ATARI */
static int      sound_set_treble(int treble);
static long     sound_copy_translate(const u_char * userPtr,
				     unsigned long userCount,
				     u_char frame[], long *frameUsed,
				     long frameLeft);


/*
 * /dev/mixer abstraction
 */

struct sound_mixer
  {
	  int             busy;
  };

static struct sound_mixer mixer;

static void     mixer_init(void);
static int      mixer_open(int open_mode);
static int      mixer_release(void);
static int      mixer_ioctl(struct inode *inode, struct file *file, u_int cmd,
			    u_long arg);


/*
 * Sound queue stuff, the heart of the driver
 */

struct sound_queue
  {
	  int             max_count, block_size;
	  char          **buffers;

	  /* 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: 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             playing;
	  struct wait_queue *write_queue, *open_queue, *sync_queue;
	  int             open_mode;
	  int             busy, syncing;
#ifdef CONFIG_ATARI
	  int             ignore_int;	/* ++TeSche: used for Falcon */
#endif				/* CONFIG_ATARI */
#ifdef CONFIG_AMIGA
	  int             block_size_half, block_size_quarter;
#endif				/* CONFIG_AMIGA */
  };

static struct sound_queue sq;

#define sq_block_address(i)	(sq.buffers[i])
#define SIGNAL_RECEIVED	(signal_pending(current))
#define NON_BLOCKING(open_mode)	(open_mode & O_NONBLOCK)
#define ONE_SECOND	HZ	/* in jiffies (100ths of a second) */
#define NO_TIME_LIMIT	0xffffffff
#define SLEEP(queue, time_limit) \
	current->timeout = jiffies+(time_limit); \
	interruptible_sleep_on(&queue);
#define WAKE_UP(queue)	(wake_up_interruptible(&queue))

static void     sq_init(int numBufs, int bufSize, char **buffers);
static void     sq_play(void);
static long     sq_write(const char *src, unsigned long uLeft);
static int      sq_open(int open_mode);
static void     sq_reset(void);
static int      sq_sync(void);
static int      sq_release(void);


/*
 * /dev/sndstat
 */

struct sound_state
  {
	  int             busy;
	  char            buf[512];
	  int             len, ptr;
  };

static struct sound_state state;

static void     state_init(void);
static int      state_open(int open_mode);
static int      state_release(void);
static long     state_read(char *dest, unsigned long count);


/*** High level stuff ********************************************************/


static int      sound_open(struct inode *inode, struct file *file);
static int      sound_fsync(struct inode *inode, struct file *filp);
static void     sound_release(struct inode *inode, struct file *file);
static long long sound_lseek(struct inode *inode, struct file *file,
			     long long offset, int orig);
static long     sound_read(struct inode *inode, struct file *file, char *buf,
			   unsigned long count);
static long     sound_write(struct inode *inode, struct file *file,
			    const char *buf, unsigned long count);
static inline int
ioctl_return(int *addr, int value)
{
	if (value < 0)
		return (value);

	return put_user(value, addr);
}
static int      unknown_minor_dev(char *fname, int dev);
static int      sound_ioctl(struct inode *inode, struct file *file, u_int cmd,
			    u_long arg);


/*** Config & Setup **********************************************************/


void            soundcard_init(void);
void            dmasound_setup(char *str, int *ints);
void            sound_setup(char *str, int *ints);	/* ++Martin: stub for now */


/*** 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)
 */

#ifdef CONFIG_ATARI
static long
ata_ct_law(const u_char * userPtr, unsigned long userCount,
	   u_char frame[], long *frameUsed, long frameLeft)
{
	char           *table = sound.soft.format == AFMT_MU_LAW ? ulaw2dma8 : alaw2dma8;
	long            count, used;
	u_char         *p = &frame[*frameUsed];

	count = min(userCount, frameLeft);
	if (sound.soft.stereo)
		count &= ~1;
	used = count;
	while (count > 0)
	  {
		  u_char          data;

		  get_user(data, userPtr++);
		  *p++ = table[data];
		  count--;
	  }
	*frameUsed += used;
	return (used);
}


static long
ata_ct_s8(const u_char * userPtr, unsigned long userCount,
	  u_char frame[], long *frameUsed, long frameLeft)
{
	long            count, used;
	void           *p = &frame[*frameUsed];

	count = min(userCount, frameLeft);
	if (sound.soft.stereo)
		count &= ~1;
	used = count;
	copy_from_user(p, userPtr, count);
	*frameUsed += used;
	return (used);
}


static long
ata_ct_u8(const u_char * userPtr, unsigned long userCount,
	  u_char frame[], long *frameUsed, long frameLeft)
{
	long            count, used;

	if (!sound.soft.stereo)
	  {
		  u_char         *p = &frame[*frameUsed];

		  count = min(userCount, frameLeft);
		  used = count;
		  while (count > 0)
		    {
			    u_char          data;

			    get_user(data, userPtr++);
			    *p++ = data ^ 0x80;
			    count--;
		    }
	} else
	  {
		  u_short        *p = (u_short *) & frame[*frameUsed];

		  count = min(userCount, frameLeft) >> 1;
		  used = count * 2;
		  while (count > 0)
		    {
			    u_short         data;

			    get_user(data, ((u_short *) userPtr)++);
			    *p++ = data ^ 0x8080;
			    count--;
		    }
	  }
	*frameUsed += used;
	return (used);
}


static long
ata_ct_s16be(const u_char * userPtr, unsigned long userCount,
	     u_char frame[], long *frameUsed, long frameLeft)
{
	long            count, used;
	u_long          data;

	if (!sound.soft.stereo)
	  {
		  u_short        *p = (u_short *) & frame[*frameUsed];

		  count = min(userCount, frameLeft) >> 1;
		  used = count * 2;
		  while (count > 0)
		    {
			    get_user(data, ((u_short *) userPtr)++);
			    *p++ = data;
			    *p++ = data;
			    count--;
		    }
		  *frameUsed += used * 2;
	} else
	  {
		  void           *p = (u_short *) & frame[*frameUsed];

		  count = min(userCount, frameLeft) & ~3;
		  used = count;
		  copy_from_user(p, userPtr, count);
		  *frameUsed += used;
	  }
	return (used);
}


static long
ata_ct_u16be(const u_char * userPtr, unsigned long userCount,
	     u_char frame[], long *frameUsed, long frameLeft)
{
	long            count, used;
	u_long          data;

	if (!sound.soft.stereo)
	  {
		  u_short        *p = (u_short *) & frame[*frameUsed];

		  count = min(userCount, frameLeft) >> 1;
		  used = count * 2;
		  while (count > 0)
		    {
			    get_user(data, ((u_short *) userPtr)++);
			    data ^= 0x8000;
			    *p++ = data;
			    *p++ = data;
			    count--;
		    }
		  *frameUsed += used * 2;
	} else
	  {
		  u_long         *p = (u_long *) & frame[*frameUsed];

		  count = min(userCount, frameLeft) >> 2;
		  used = count * 4;
		  while (count > 0)
		    {
			    get_user(data, ((u_int *) userPtr)++);
			    *p++ = data ^ 0x80008000;
			    count--;
		    }
		  *frameUsed += used;
	  }
	return (used);
}


static long
ata_ct_s16le(const u_char * userPtr, unsigned long userCount,
	     u_char frame[], long *frameUsed, long frameLeft)
{
	long            count, used;
	u_long          data;

	count = frameLeft;
	if (!sound.soft.stereo)
	  {
		  u_short        *p = (u_short *) & frame[*frameUsed];

		  count = min(userCount, frameLeft) >> 1;
		  used = count * 2;
		  while (count > 0)
		    {
			    get_user(data, ((u_short *) userPtr)++);
			    data = le2be16(data);
			    *p++ = data;
			    *p++ = data;
			    count--;
		    }
		  *frameUsed += used * 2;
	} else
	  {
		  u_long         *p = (u_long *) & frame[*frameUsed];

		  count = min(userCount, frameLeft) >> 2;
		  used = count * 4;
		  while (count > 0)
		    {
			    get_user(data, ((u_int *) userPtr)++);
			    data = le2be16dbl(data);
			    *p++ = data;
			    count--;
		    }
		  *frameUsed += used;
	  }
	return (used);
}


static long
ata_ct_u16le(const u_char * userPtr, unsigned long userCount,
	     u_char frame[], long *frameUsed, long frameLeft)
{
	long            count, used;
	u_long          data;

	count = frameLeft;
	if (!sound.soft.stereo)
	  {
		  u_short        *p = (u_short *) & frame[*frameUsed];

		  count = min(userCount, frameLeft) >> 1;
		  used = count * 2;
		  while (count > 0)
		    {
			    get_user(data, ((u_short *) userPtr)++);
			    data = le2be16(data) ^ 0x8000;
			    *p++ = data;
			    *p++ = data;
		    }
		  *frameUsed += used * 2;
	} else
	  {
		  u_long         *p = (u_long *) & frame[*frameUsed];

		  count = min(userCount, frameLeft) >> 2;
		  used = count;
		  while (count > 0)
		    {
			    get_user(data, ((u_int *) userPtr)++);
			    data = le2be16dbl(data) ^ 0x80008000;
			    *p++ = data;
			    count--;
		    }
		  *frameUsed += used;
	  }
	return (used);
}


static long
ata_ctx_law(const u_char * userPtr, unsigned long userCount,
	    u_char frame[], long *frameUsed, long frameLeft)
{
	char           *table = sound.soft.format == AFMT_MU_LAW ? ulaw2dma8 : alaw2dma8;

	/* this should help gcc to stuff everything into registers */
	u_long          data = sound.data;
	long            bal = sound.bal;
	long            hSpeed = sound.hard.speed, sSpeed = sound.soft.speed;
	long            used, usedf;

	used = userCount;
	usedf = frameLeft;
	if (!sound.soft.stereo)
	  {
		  u_char         *p = &frame[*frameUsed];

		  while (frameLeft)
		    {
			    u_char          c;

			    if (bal < 0)
			      {
				      if (!userCount)
					      break;
				      get_user(c, userPtr++);
				      data = table[c];
				      userCount--;
				      bal += hSpeed;
			      }
			    *p++ = data;
			    frameLeft--;
			    bal -= sSpeed;
		    }
	} else
	  {
		  u_short        *p = (u_short *) & frame[*frameUsed];

		  while (frameLeft >= 2)
		    {
			    u_char          c;

			    if (bal < 0)
			      {
				      if (userCount < 2)
					      break;
				      get_user(c, userPtr++);
				      data = table[c] << 8;
				      get_user(c, userPtr++);
				      data |= table[c];
				      userCount -= 2;
				      bal += hSpeed;
			      }
			    *p++ = data;
			    frameLeft -= 2;
			    bal -= sSpeed;
		    }
	  }
	sound.bal = bal;
	sound.data = data;
	used -= userCount;
	*frameUsed += usedf - frameLeft;
	return (used);
}


static long
ata_ctx_s8(const u_char * userPtr, unsigned long userCount,
	   u_char frame[], long *frameUsed, long frameLeft)
{
	/* this should help gcc to stuff everything into registers */
	u_long          data = sound.data;
	long            bal = sound.bal;
	long            hSpeed = sound.hard.speed, sSpeed = sound.soft.speed;
	long            used, usedf;

	used = userCount;
	usedf = frameLeft;
	if (!sound.soft.stereo)
	  {
		  u_char         *p = &frame[*frameUsed];

		  while (frameLeft)
		    {
			    if (bal < 0)
			      {
				      if (!userCount)
					      break;
				      get_user(data, userPtr++);
				      userCount--;
				      bal += hSpeed;
			      }
			    *p++ = data;
			    frameLeft--;
			    bal -= sSpeed;
		    }
	} else
	  {
		  u_short        *p = (u_short *) & frame[*frameUsed];

		  while (frameLeft >= 2)
		    {
			    if (bal < 0)
			      {
				      if (userCount < 2)
					      break;
				      get_user(data, ((u_short *) userPtr)++);
				      userCount -= 2;
				      bal += hSpeed;
			      }
			    *p++ = data;
			    frameLeft -= 2;
			    bal -= sSpeed;
		    }
	  }
	sound.bal = bal;
	sound.data = data;
	used -= userCount;
	*frameUsed += usedf - frameLeft;
	return (used);
}


static long
ata_ctx_u8(const u_char * userPtr, unsigned long userCount,
	   u_char frame[], long *frameUsed, long frameLeft)
{
	/* this should help gcc to stuff everything into registers */
	u_long          data = sound.data;
	long            bal = sound.bal;
	long            hSpeed = sound.hard.speed, sSpeed = sound.soft.speed;
	long            used, usedf;

	used = userCount;
	usedf = frameLeft;
	if (!sound.soft.stereo)
	  {
		  u_char         *p = &frame[*frameUsed];

		  while (frameLeft)
		    {
			    if (bal < 0)
			      {
				      if (!userCount)
					      break;
				      get_user(data, userPtr++);
				      data ^= 0x80;
				      userCount--;
				      bal += hSpeed;
			      }
			    *p++ = data;
			    frameLeft--;
			    bal -= sSpeed;
		    }
	} else
	  {
		  u_short        *p = (u_short *) & frame[*frameUsed];

		  while (frameLeft >= 2)
		    {
			    if (bal < 0)
			      {
				      if (userCount < 2)
					      break;
				      get_user(data, ((u_short *) userPtr)++);
				      data ^= 0x8080;
				      userCount -= 2;
				      bal += hSpeed;
			      }
			    *p++ = data;
			    frameLeft -= 2;
			    bal -= sSpeed;
		    }
	  }
	sound.bal = bal;
	sound.data = data;
	used -= userCount;
	*frameUsed += usedf - frameLeft;
	return (used);
}


static long
ata_ctx_s16be(const u_char * userPtr, unsigned long userCount,
	      u_char frame[], long *frameUsed, long frameLeft)
{
	/* this should help gcc to stuff everything into registers */
	u_long          data = sound.data;
	long            bal = sound.bal;
	long            hSpeed = sound.hard.speed, sSpeed = sound.soft.speed;
	long            used, usedf;

	used = userCount;
	usedf = frameLeft;
	if (!sound.soft.stereo)
	  {
		  u_short        *p = (u_short *) & frame[*frameUsed];

		  while (frameLeft >= 4)
		    {
			    if (bal < 0)
			      {
				      if (userCount < 2)
					      break;
				      get_user(data, ((u_short *) userPtr)++);
				      userCount -= 2;
				      bal += hSpeed;
			      }
			    *p++ = data;
			    *p++ = data;
			    frameLeft -= 4;
			    bal -= sSpeed;
		    }
	} else
	  {
		  u_long         *p = (u_long *) & frame[*frameUsed];

		  while (frameLeft >= 4)
		    {
			    if (bal < 0)
			      {
				      if (userCount < 4)
					      break;
				      get_user(data, ((u_int *) userPtr)++);
				      userCount -= 4;
				      bal += hSpeed;
			      }
			    *p++ = data;
			    frameLeft -= 4;
			    bal -= sSpeed;
		    }
	  }
	sound.bal = bal;
	sound.data = data;
	used -= userCount;
	*frameUsed += usedf - frameLeft;
	return (used);
}


static long
ata_ctx_u16be(const u_char * userPtr, unsigned long userCount,
	      u_char frame[], long *frameUsed, long frameLeft)
{
	/* this should help gcc to stuff everything into registers */
	u_long          data = sound.data;
	long            bal = sound.bal;
	long            hSpeed = sound.hard.speed, sSpeed = sound.soft.speed;
	long            used, usedf;

	used = userCount;
	usedf = frameLeft;
	if (!sound.soft.stereo)
	  {
		  u_short        *p = (u_short *) & frame[*frameUsed];

		  while (frameLeft >= 4)
		    {
			    if (bal < 0)
			      {
				      if (userCount < 2)
					      break;
				      get_user(data, ((u_short *) userPtr)++);
				      data ^= 0x8000;
				      userCount -= 2;
				      bal += hSpeed;
			      }
			    *p++ = data;
			    *p++ = data;
			    frameLeft -= 4;
			    bal -= sSpeed;
		    }
	} else
	  {
		  u_long         *p = (u_long *) & frame[*frameUsed];

		  while (frameLeft >= 4)
		    {
			    if (bal < 0)
			      {
				      if (userCount < 4)
					      break;
				      get_user(data, ((u_int *) userPtr)++);
				      data ^= 0x80008000;
				      userCount -= 4;
				      bal += hSpeed;
			      }
			    *p++ = data;
			    frameLeft -= 4;
			    bal -= sSpeed;
		    }
	  }
	sound.bal = bal;
	sound.data = data;
	used -= userCount;
	*frameUsed += usedf - frameLeft;
	return (used);
}


static long
ata_ctx_s16le(const u_char * userPtr, unsigned long userCount,
	      u_char frame[], long *frameUsed, long frameLeft)
{
	/* this should help gcc to stuff everything into registers */
	u_long          data = sound.data;
	long            bal = sound.bal;
	long            hSpeed = sound.hard.speed, sSpeed = sound.soft.speed;
	long            used, usedf;

	used = userCount;
	usedf = frameLeft;
	if (!sound.soft.stereo)
	  {
		  u_short        *p = (u_short *) & frame[*frameUsed];

		  while (frameLeft >= 4)
		    {
			    if (bal < 0)
			      {
				      if (userCount < 2)
					      break;
				      get_user(data, ((u_short *) userPtr)++);
				      data = le2be16(data);
				      userCount -= 2;
				      bal += hSpeed;
			      }
			    *p++ = data;
			    *p++ = data;
			    frameLeft -= 4;
			    bal -= sSpeed;
		    }
	} else
	  {
		  u_long         *p = (u_long *) & frame[*frameUsed];

		  while (frameLeft >= 4)
		    {
			    if (bal < 0)
			      {
				      if (userCount < 4)
					      break;
				      get_user(data, ((u_int *) userPtr)++);
				      data = le2be16dbl(data);
				      userCount -= 4;
				      bal += hSpeed;
			      }
			    *p++ = data;
			    frameLeft -= 4;
			    bal -= sSpeed;
		    }
	  }
	sound.bal = bal;
	sound.data = data;
	used -= userCount;
	*frameUsed += usedf - frameLeft;
	return (used);
}


static long
ata_ctx_u16le(const u_char * userPtr, unsigned long userCount,
	      u_char frame[], long *frameUsed, long frameLeft)
{
	/* this should help gcc to stuff everything into registers */
	u_long          data = sound.data;
	long            bal = sound.bal;
	long            hSpeed = sound.hard.speed, sSpeed = sound.soft.speed;
	long            used, usedf;

	used = userCount;
	usedf = frameLeft;
	if (!sound.soft.stereo)
	  {
		  u_short        *p = (u_short *) & frame[*frameUsed];

		  while (frameLeft >= 4)
		    {
			    if (bal < 0)
			      {
				      if (userCount < 2)
					      break;
				      get_user(data, ((u_short *) userPtr)++);
				      data = le2be16(data) ^ 0x8000;
				      userCount -= 2;
				      bal += hSpeed;
			      }
			    *p++ = data;
			    *p++ = data;
			    frameLeft -= 4;
			    bal -= sSpeed;
		    }
	} else
	  {
		  u_long         *p = (u_long *) & frame[*frameUsed];

		  while (frameLeft >= 4)
		    {
			    if (bal < 0)
			      {
				      if (userCount < 4)
					      break;
				      get_user(data, ((u_int *) userPtr)++);
				      data = le2be16dbl(data) ^ 0x80008000;
				      userCount -= 4;
				      bal += hSpeed;
			      }
			    *p++ = data;
			    frameLeft -= 4;
			    bal -= sSpeed;
		    }
	  }
	sound.bal = bal;
	sound.data = data;
	used -= userCount;
	*frameUsed += usedf - frameLeft;
	return (used);
}
#endif				/* CONFIG_ATARI */


#ifdef CONFIG_AMIGA
static long
ami_ct_law(const u_char * userPtr, unsigned long userCount,
	   u_char frame[], long *frameUsed, long frameLeft)
{
	char           *table = sound.soft.format == AFMT_MU_LAW ? ulaw2dma8 : alaw2dma8;
	long            count, used;

	if (!sound.soft.stereo)
	  {
		  u_char         *p = &frame[*frameUsed];

		  count = min(userCount, frameLeft) & ~1;
		  used = count;
		  while (count > 0)
		    {
			    u_char          data;

			    get_user(data, userPtr++);
			    *p++ = table[data];
			    count--;
		    }
	} else
	  {
		  u_char         *left = &frame[*frameUsed >> 1];
		  u_char         *right = left + sq.block_size_half;

		  count = min(userCount, frameLeft) >> 1 & ~1;
		  used = count * 2;
		  while (count > 0)
		    {
			    u_char          data;

			    get_user(data, userPtr++);
			    *left++ = table[data];
			    get_user(data, userPtr++);
			    *right++ = table[data];
			    count--;
		    }
	  }
	*frameUsed += used;
	return (used);
}


static long
ami_ct_s8(const u_char * userPtr, unsigned long userCount,
	  u_char frame[], long *frameUsed, long frameLeft)
{
	long            count, used;

	if (!sound.soft.stereo)
	  {
		  void           *p = &frame[*frameUsed];

		  count = min(userCount, frameLeft) & ~1;
		  used = count;
		  copy_from_user(p, userPtr, count);
	} else
	  {
		  u_char         *left = &frame[*frameUsed >> 1];
		  u_char         *right = left + sq.block_size_half;

		  count = min(userCount, frameLeft) >> 1 & ~1;
		  used = count * 2;
		  while (count > 0)
		    {
			    get_user(*left++, userPtr++);
			    get_user(*right++, userPtr++);
			    count--;
		    }
	  }
	*frameUsed += used;
	return (used);
}


static long
ami_ct_u8(const u_char * userPtr, unsigned long userCount,
	  u_char frame[], long *frameUsed, long frameLeft)
{
	long            count, used;

	if (!sound.soft.stereo)
	  {
		  char           *p = &frame[*frameUsed];

		  count = min(userCount, frameLeft) & ~1;
		  used = count;
		  while (count > 0)
		    {
			    u_char          data;

			    get_user(data, userPtr++);
			    *p++ = data ^ 0x80;
			    count--;
		    }
	} else
	  {
		  u_char         *left = &frame[*frameUsed >> 1];
		  u_char         *right = left + sq.block_size_half;

		  count = min(userCount, frameLeft) >> 1 & ~1;
		  used = count * 2;
		  while (count > 0)
		    {
			    u_char          data;

			    get_user(data, userPtr++);
			    *left++ = data ^ 0x80;
			    get_user(data, userPtr++);
			    *right++ = data ^ 0x80;
			    count--;
		    }
	  }
	*frameUsed += used;
	return (used);
}


static long
ami_ct_s16be(const u_char * userPtr, unsigned long userCount,
	     u_char frame[], long *frameUsed, long frameLeft)
{
	long            count, used;
	u_long          data;

	if (!sound.soft.stereo)
	  {
		  u_char         *high = &frame[*frameUsed >> 1];
		  u_char         *low = high + sq.block_size_half;

		  count = min(userCount, frameLeft) >> 1 & ~1;
		  used = count * 2;
		  while (count > 0)
		    {
			    get_user(data, ((u_short *) userPtr)++);
			    *high++ = data >> 8;
			    *low++ = (data >> 2) & 0x3f;
			    count--;
		    }
	} else
	  {
		  u_char         *lefth = &frame[*frameUsed >> 2];
		  u_char         *leftl = lefth + sq.block_size_quarter;
		  u_char         *righth = lefth + sq.block_size_half;
		  u_char         *rightl = righth + sq.block_size_quarter;

		  count = min(userCount, frameLeft) >> 2 & ~1;
		  used = count * 4;
		  while (count > 0)
		    {
			    get_user(data, ((u_short *) userPtr)++);
			    *lefth++ = data >> 8;
			    *leftl++ = (data >> 2) & 0x3f;
			    get_user(data, ((u_short *) userPtr)++);
			    *righth++ = data >> 8;
			    *rightl++ = (data >> 2) & 0x3f;
			    count--;
		    }
	  }
	*frameUsed += used;
	return (used);
}


static long
ami_ct_u16be(const u_char * userPtr, unsigned long userCount,
	     u_char frame[], long *frameUsed, long frameLeft)
{
	long            count, used;
	u_long          data;

	if (!sound.soft.stereo)
	  {
		  u_char         *high = &frame[*frameUsed >> 1];
		  u_char         *low = high + sq.block_size_half;

		  count = min(userCount, frameLeft) >> 1 & ~1;
		  used = count * 2;
		  while (count > 0)
		    {
			    get_user(data, ((u_short *) userPtr)++);
			    data ^= 0x8000;
			    *high++ = data >> 8;
			    *low++ = (data >> 2) & 0x3f;
			    count--;
		    }
	} else
	  {
		  u_char         *lefth = &frame[*frameUsed >> 2];
		  u_char         *leftl = lefth + sq.block_size_quarter;
		  u_char         *righth = lefth + sq.block_size_half;
		  u_char         *rightl = righth + sq.block_size_quarter;

		  count = min(userCount, frameLeft) >> 2 & ~1;
		  used = count * 4;
		  while (count > 0)
		    {
			    get_user(data, ((u_short *) userPtr)++);
			    data ^= 0x8000;
			    *lefth++ = data >> 8;
			    *leftl++ = (data >> 2) & 0x3f;
			    get_user(data, ((u_short *) userPtr)++);
			    data ^= 0x8000;
			    *righth++ = data >> 8;
			    *rightl++ = (data >> 2) & 0x3f;
			    count--;
		    }
	  }
	*frameUsed += used;
	return (used);
}


static long
ami_ct_s16le(const u_char * userPtr, unsigned long userCount,
	     u_char frame[], long *frameUsed, long frameLeft)
{
	long            count, used;
	u_long          data;

	if (!sound.soft.stereo)
	  {
		  u_char         *high = &frame[*frameUsed >> 1];
		  u_char         *low = high + sq.block_size_half;

		  count = min(userCount, frameLeft) >> 1 & ~1;
		  used = count * 2;
		  while (count > 0)
		    {
			    get_user(data, ((u_short *) userPtr)++);
			    data = le2be16(data);
			    *high++ = data >> 8;
			    *low++ = (data >> 2) & 0x3f;
			    count--;
		    }
	} else
	  {
		  u_char         *lefth = &frame[*frameUsed >> 2];
		  u_char         *leftl = lefth + sq.block_size_quarter;
		  u_char         *righth = lefth + sq.block_size_half;
		  u_char         *rightl = righth + sq.block_size_quarter;

		  count = min(userCount, frameLeft) >> 2 & ~1;
		  used = count * 4;
		  while (count > 0)
		    {
			    get_user(data, ((u_short *) userPtr)++);
			    data = le2be16(data);
			    *lefth++ = data >> 8;
			    *leftl++ = (data >> 2) & 0x3f;
			    get_user(data, ((u_short *) userPtr)++);
			    data = le2be16(data);
			    *righth++ = data >> 8;
			    *rightl++ = (data >> 2) & 0x3f;
			    count--;
		    }
	  }
	*frameUsed += used;
	return (used);
}


static long
ami_ct_u16le(const u_char * userPtr, unsigned long userCount,
	     u_char frame[], long *frameUsed, long frameLeft)
{
	long            count, used;
	u_long          data;

	if (!sound.soft.stereo)
	  {
		  u_char         *high = &frame[*frameUsed >> 1];
		  u_char         *low = high + sq.block_size_half;

		  count = min(userCount, frameLeft) >> 1 & ~1;
		  used = count * 2;
		  while (count > 0)
		    {
			    get_user(data, ((u_short *) userPtr)++);
			    data = le2be16(data) ^ 0x8000;
			    *high++ = data >> 8;
			    *low++ = (data >> 2) & 0x3f;
			    count--;
		    }
	} else
	  {
		  u_char         *lefth = &frame[*frameUsed >> 2];
		  u_char         *leftl = lefth + sq.block_size_quarter;
		  u_char         *righth = lefth + sq.block_size_half;
		  u_char         *rightl = righth + sq.block_size_quarter;

		  count = min(userCount, frameLeft) >> 2 & ~1;
		  used = count * 4;
		  while (count > 0)
		    {
			    get_user(data, ((u_short *) userPtr)++);
			    data = le2be16(data) ^ 0x8000;
			    *lefth++ = data >> 8;
			    *leftl++ = (data >> 2) & 0x3f;
			    get_user(data, ((u_short *) userPtr)++);
			    data = le2be16(data) ^ 0x8000;
			    *righth++ = data >> 8;
			    *rightl++ = (data >> 2) & 0x3f;
			    count--;
		    }
	  }
	*frameUsed += used;
	return (used);
}
#endif				/* CONFIG_AMIGA */


#ifdef CONFIG_ATARI
static TRANS    transTTNormal =
{
     ata_ct_law, ata_ct_law, ata_ct_s8, ata_ct_u8, NULL, NULL, NULL, NULL
};

static TRANS    transTTExpanding =
{
 ata_ctx_law, ata_ctx_law, ata_ctx_s8, ata_ctx_u8, NULL, NULL, NULL, NULL
};

static TRANS    transFalconNormal =
{
ata_ct_law, ata_ct_law, ata_ct_s8, ata_ct_u8, ata_ct_s16be, ata_ct_u16be,
	ata_ct_s16le, ata_ct_u16le
};

static TRANS    transFalconExpanding =
{
	ata_ctx_law, ata_ctx_law, ata_ctx_s8, ata_ctx_u8, ata_ctx_s16be,
	ata_ctx_u16be, ata_ctx_s16le, ata_ctx_u16le
};

#endif				/* CONFIG_ATARI */

#ifdef CONFIG_AMIGA
static TRANS    transAmiga =
{
ami_ct_law, ami_ct_law, ami_ct_s8, ami_ct_u8, ami_ct_s16be, ami_ct_u16be,
	ami_ct_s16le, ami_ct_u16le
};

#endif				/* CONFIG_AMIGA */


/*** Low level stuff *********************************************************/


#ifdef CONFIG_ATARI

/*
 * Atari (TT/Falcon)
 */

static void    *
AtaAlloc(unsigned int size, int flags)
{
	int             order;
	unsigned int    a_size;

	order = 0;
	a_size = PAGE_SIZE;
	while (a_size < size)
	  {
		  order++;
		  a_size <<= 1;
	  }
	return (void *) __get_dma_pages(flags, order);
}

static void
AtaFree(void *obj, unsigned int size)
{
	int             order;
	unsigned int    a_size;

	order = 0;
	a_size = PAGE_SIZE;
	while (a_size < size)
	  {
		  order++;
		  a_size <<= 1;
	  }
	free_pages((unsigned long) obj, order);
}

static int
AtaIrqInit(void)
{
	/* Set up timer A. Timer A
	   will receive a signal upon end of playing from the sound
	   hardware. Furthermore Timer A is able to count events
	   and will cause an interrupt after a programmed number
	   of events. So all we need to keep the music playing is
	   to provide the sound hardware with new data upon
	   an interrupt from timer A. */
	mfp.tim_ct_a = 0;	/* ++roman: Stop timer before programming! */
	mfp.tim_dt_a = 1;	/* Cause interrupt after first event. */
	mfp.tim_ct_a = 8;	/* Turn on event counting. */
	/* Register interrupt handler. */
	request_irq(IRQ_MFP_TIMA, ata_sq_interrupt, IRQ_TYPE_SLOW,
		    "DMA sound", ata_sq_interrupt);
	mfp.int_en_a |= 0x20;	/* Turn interrupt on. */
	mfp.int_mk_a |= 0x20;
	return (1);
}

#ifdef MODULE
static void
AtaIrqCleanUp(void)
{
	mfp.tim_ct_a = 0;	/* stop timer */
	mfp.int_en_a &= ~0x20;	/* turn interrupt off */
	free_irq(IRQ_MFP_TIMA, ata_sq_interrupt);
}
#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)
{
	sound.bass = TONE_VOXWARE_TO_DB(bass);
	atari_microwire_cmd(MW_LM1992_BASS(sound.bass));
	return (TONE_DB_TO_VOXWARE(sound.bass));
}


static int
AtaSetTreble(int treble)
{
	sound.treble = TONE_VOXWARE_TO_DB(treble);
	atari_microwire_cmd(MW_LM1992_TREBLE(sound.treble));
	return (TONE_DB_TO_VOXWARE(sound.treble));
}



/*
 * TT
 */


static void
TTSilence(void)
{
	tt_dmasnd.ctrl = DMASND_CTRL_OFF;
	atari_microwire_cmd(MW_LM1992_PSG_HIGH);	/* mix in PSG signal 1:1 */
}


static void
TTInit(void)
{
	int             mode, i, idx;
	const int       freq[4] =
	{50066, 25033, 12517, 6258};

	/* search a frequency that fits into the allowed error range */

	idx = -1;
	for (i = 0; i < arraysize(freq); i++)
		/* this isn't as much useful for a TT than for a Falcon, but
		 * then it doesn't hurt very much to implement it for a TT too.
		 */
		if ((100 * abs(sound.soft.speed - freq[i]) / freq[i]) < catchRadius)
			idx = i;
	if (idx > -1)
	  {
		  sound.soft.speed = freq[idx];
		  sound.trans = &transTTNormal;
	} else
		sound.trans = &transTTExpanding;

	TTSilence();
	sound.hard = sound.soft;

	if (sound.hard.speed > 50066)
	  {
		  /* we would need to squeeze the sound, but we won't do that */
		  sound.hard.speed = 50066;
		  mode = DMASND_MODE_50KHZ;
		  sound.trans = &transTTNormal;
	} else if (sound.hard.speed > 25033)
	  {
		  sound.hard.speed = 50066;
		  mode = DMASND_MODE_50KHZ;
	} else if (sound.hard.speed > 12517)
	  {
		  sound.hard.speed = 25033;
		  mode = DMASND_MODE_25KHZ;
	} else if (sound.hard.speed > 6258)
	  {
		  sound.hard.speed = 12517;
		  mode = DMASND_MODE_12KHZ;
	} else
	  {
		  sound.hard.speed = 6258;
		  mode = DMASND_MODE_6KHZ;
	  }

	tt_dmasnd.mode = (sound.hard.stereo ?
			  DMASND_MODE_STEREO : DMASND_MODE_MONO) |
	    DMASND_MODE_8BIT | mode;

	sound.bal = -sound.soft.speed;
}


static int
TTSetFormat(int format)
{
	/* TT sound DMA supports only 8bit modes */

	switch (format)
	  {
	  case AFMT_QUERY:
		  return (sound.soft.format);
	  case AFMT_MU_LAW:
	  case AFMT_A_LAW:
	  case AFMT_S8:
	  case AFMT_U8:
		  break;
	  default:
		  format = AFMT_S8;
	  }

	sound.soft.format = format;
	sound.soft.size = 8;
	if (sound.minDev == SND_DEV_DSP)
	  {
		  sound.dsp.format = format;
		  sound.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)
{
	sound.volume_left = VOLUME_VOXWARE_TO_DB(volume & 0xff);
	atari_microwire_cmd(MW_LM1992_BALLEFT(sound.volume_left));
	sound.volume_right = VOLUME_VOXWARE_TO_DB((volume & 0xff00) >> 8);
	atari_microwire_cmd(MW_LM1992_BALRIGHT(sound.volume_right));
	return (VOLUME_DB_TO_VOXWARE(sound.volume_left) |
		(VOLUME_DB_TO_VOXWARE(sound.volume_right) << 8));
}



/*
 * Falcon
 */


static void
FalconSilence(void)
{
	/* stop playback, set sample rate 50kHz for PSG sound */
	tt_dmasnd.ctrl = DMASND_CTRL_OFF;
	tt_dmasnd.mode = DMASND_MODE_50KHZ | DMASND_MODE_STEREO | DMASND_MODE_8BIT;
	tt_dmasnd.int_div = 0;	/* STE compatible divider */
	tt_dmasnd.int_ctrl = 0x0;
	tt_dmasnd.cbar_src = 0x0000;	/* no matrix inputs */
	tt_dmasnd.cbar_dst = 0x0000;	/* no matrix outputs */
	tt_dmasnd.dac_src = 1;	/* connect ADC to DAC, disconnect matrix */
	tt_dmasnd.adc_src = 3;	/* ADC Input = PSG */
}


static void
FalconInit(void)
{
	int             divider, i, idx;
	const int       freq[8] =
	{49170, 32780, 24585, 19668, 16390, 12292, 9834, 8195};

	/* search a frequency that fits into the allowed error range */

	idx = -1;
	for (i = 0; i < arraysize(freq); i++)
		/* if we will tolerate 3% error 8000Hz->8195Hz (2.38%) would
		 * be playable without expanding, but that now a kernel runtime
		 * option
		 */
		if ((100 * abs(sound.soft.speed - freq[i]) / freq[i]) < catchRadius)
			idx = i;
	if (idx > -1)
	  {
		  sound.soft.speed = freq[idx];
		  sound.trans = &transFalconNormal;
	} else
		sound.trans = &transFalconExpanding;

	FalconSilence();
	sound.hard = sound.soft;

	if (sound.hard.size == 16)
	  {
		  /* the Falcon can play 16bit samples only in stereo */
		  sound.hard.stereo = 1;
	  }
	if (sound.hard.speed > 49170)
	  {
		  /* we would need to squeeze the sound, but we won't do that */
		  sound.hard.speed = 49170;
		  divider = 1;
		  sound.trans = &transFalconNormal;
	} else if (sound.hard.speed > 32780)
	  {
		  sound.hard.speed = 49170;
		  divider = 1;
	} else if (sound.hard.speed > 24585)
	  {
		  sound.hard.speed = 32780;
		  divider = 2;
	} else if (sound.hard.speed > 19668)
	  {
		  sound.hard.speed = 24585;
		  divider = 3;
	} else if (sound.hard.speed > 16390)
	  {
		  sound.hard.speed = 19668;
		  divider = 4;
	} else if (sound.hard.speed > 12292)
	  {
		  sound.hard.speed = 16390;
		  divider = 5;
	} else if (sound.hard.speed > 9834)
	  {
		  sound.hard.speed = 12292;
		  divider = 7;
	} else if (sound.hard.speed > 8195)
	  {
		  sound.hard.speed = 9834;
		  divider = 9;
	} else
	  {
		  sound.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 = (sound.hard.stereo ?
			  DMASND_MODE_STEREO : DMASND_MODE_MONO) |
	    ((sound.hard.size == 8) ?
	     DMASND_MODE_8BIT : DMASND_MODE_16BIT) |
	    DMASND_MODE_6KHZ;

	sound.bal = -sound.soft.speed;
}


static int
FalconSetFormat(int format)
{
	int             size;

	/* Falcon sound DMA supports 8bit and 16bit modes */

	switch (format)
	  {
	  case AFMT_QUERY:
		  return (sound.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;
	  }

	sound.soft.format = format;
	sound.soft.size = size;
	if (sound.minDev == SND_DEV_DSP)
	  {
		  sound.dsp.format = format;
		  sound.dsp.size = sound.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)
{
	sound.volume_left = VOLUME_VOXWARE_TO_ATT(volume & 0xff);
	sound.volume_right = VOLUME_VOXWARE_TO_ATT((volume & 0xff00) >> 8);
	tt_dmasnd.output_atten = sound.volume_left << 8 | sound.volume_right << 4;
	return (VOLUME_ATT_TO_VOXWARE(sound.volume_left) |
		VOLUME_ATT_TO_VOXWARE(sound.volume_right) << 8);
}


static void
ata_sq_play_next_frame(int index)
{
	char           *start, *end;

	/* used by AtaPlay() if all doubts whether there really is something
	 * to be played are already wiped out.
	 */
	start = sq_block_address(sq.front);
	end = start + ((sq.count == index) ? sq.rear_size : sq.block_size);
	/* end might not be a legal virtual address. */
	DMASNDSetEnd(VTOP(end - 1) + 1);
	DMASNDSetBase(VTOP(start));
	/* Since only an even number of samples per frame can
	   be played, we might lose one byte here. (TO DO) */
	sq.front = (sq.front + 1) % sq.max_count;
	sq.playing++;
	tt_dmasnd.ctrl = DMASND_CTRL_ON | DMASND_CTRL_REPEAT;
}


static void
AtaPlay(void)
{
	/* ++TeSche: Note that sq.playing 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 sq.count and sq.playing 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 (sq.playing == 2 ||	/* DMA is 'full' */
	    sq.count <= 0)
	  {			/* nothing to do */
		  atari_enable_irq(IRQ_MFP_TIMA);
		  return;
	  }
	if (sq.playing == 0)
	  {
		  /* looks like there's nothing 'in' the DMA yet, so try
		   * to put two frames into it (at least one is available).
		   */
		  if (sq.count == 1 && sq.rear_size < sq.block_size && !sq.syncing)
		    {
			    /* hmmm, the only existing frame is not
			     * yet filled and we're not syncing?
			     */
			    atari_enable_irq(IRQ_MFP_TIMA);
			    return;
		    }
		  ata_sq_play_next_frame(1);
		  if (sq.count == 1)
		    {
			    /* no more frames */
			    atari_enable_irq(IRQ_MFP_TIMA);
			    return;
		    }
		  if (sq.count == 2 && sq.rear_size < sq.block_size && !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;
		    }
		  ata_sq_play_next_frame(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 sq.count.
		   */
		  if (sq.count == 2 && sq.rear_size < sq.block_size && !sq.syncing)
		    {
			    /* hmmm, the only existing frame is not
			     * yet filled and we're not syncing?
			     */
			    atari_enable_irq(IRQ_MFP_TIMA);
			    return;
		    }
		  ata_sq_play_next_frame(2);
	  }
	atari_enable_irq(IRQ_MFP_TIMA);
}


static void
ata_sq_interrupt(int irq, void *dummy, struct pt_regs *fp)
{
#if 0
	/* ++TeSche: if you should want to test this... */
	static int      cnt = 0;

	if (sq.playing == 2)
		if (++cnt == 10)
		  {
			  /* simulate losing an interrupt */
			  cnt = 0;
			  return;
		  }
#endif

	if (sq.ignore_int && (sound.mach.type == DMASND_FALCON))
	  {
		  /* ++TeSche: Falcon only: ignore first irq because it comes
		   * immediately after starting a frame. after that, irqs come
		   * (almost) like on the TT.
		   */
		  sq.ignore_int = 0;
		  return;
	  }
	if (!sq.playing)
	  {
		  /* playing was interrupted and sq_reset() has already cleared
		   * the sq variables, so better don't do anything here.
		   */
		  WAKE_UP(sq.sync_queue);
		  return;
	  }
	/* Probably ;) one frame is finished. Well, in fact it may be that a
	 * pre-programmed one is also finished because there has been a long
	 * delay in interrupt delivery and we've completely lost one, but
	 * there's no way to detect such a situation. In such a case the last
	 * frame will be played more than once and the situation will recover
	 * as soon as the irq gets through.
	 */
	sq.count--;
	sq.playing--;

	if (!sq.playing)
	  {
		  tt_dmasnd.ctrl = DMASND_CTRL_OFF;
		  sq.ignore_int = 1;
	  }
	WAKE_UP(sq.write_queue);
	/* At least one block of the queue is free now
	   so wake up a writing process blocked because
	   of a full queue. */

	if ((sq.playing != 1) || (sq.count != 1))
		/* We must be a bit carefully here: sq.count indicates the
		 * number of buffers used and not the number of frames to
		 * be played. If sq.count==1 and sq.playing==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 (!sq.playing)
		WAKE_UP(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. */
}
#endif				/* CONFIG_ATARI */


#ifdef CONFIG_AMIGA

/*
 * Amiga
 */


static void    *
AmiAlloc(unsigned int size, int flags)
{
	return (amiga_chip_alloc((long) size));
}

static void
AmiFree(void *obj, unsigned int size)
{
	amiga_chip_free(obj);
}

static int
AmiIrqInit(void)
{
	/* turn off DMA for audio channels */
	custom.dmacon = AMI_AUDIO_OFF;

	/* Register interrupt handler. */
	if (request_irq(IRQ_AMIGA_AUD0, ami_sq_interrupt, 0,
			"DMA sound", ami_sq_interrupt))
		return (0);
	return (1);
}

#ifdef MODULE
static void
AmiIrqCleanUp(void)
{
	/* turn off DMA for audio channels */
	custom.dmacon = AMI_AUDIO_OFF;
	/* release the interrupt */
	free_irq(IRQ_AMIGA_AUD0, ami_sq_interrupt);
}
#endif				/* MODULE */

static void
AmiSilence(void)
{
	/* turn off DMA for audio channels */
	custom.dmacon = AMI_AUDIO_OFF;
}


static void
AmiInit(void)
{
	int             period, i;

	AmiSilence();

	if (sound.soft.speed)
		period = amiga_colorclock / sound.soft.speed - 1;
	else
		period = amiga_audio_min_period;
	sound.hard = sound.soft;
	sound.trans = &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;
	  }
	sound.hard.speed = amiga_colorclock / (period + 1);

	for (i = 0; i < 4; i++)
		custom.aud[i].audper = period;
	amiga_audio_period = period;

	AmiSetTreble(50);	/* recommended for newer amiga models */
}


static int
AmiSetFormat(int format)
{
	int             size;

	/* Amiga sound DMA supports 8bit and 16bit (pseudo 14 bit) modes */

	switch (format)
	  {
	  case AFMT_QUERY:
		  return (sound.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;
	  }

	sound.soft.format = format;
	sound.soft.size = size;
	if (sound.minDev == SND_DEV_DSP)
	  {
		  sound.dsp.format = format;
		  sound.dsp.size = sound.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)
{
	sound.volume_left = VOLUME_VOXWARE_TO_AMI(volume & 0xff);
	custom.aud[0].audvol = sound.volume_left;
	sound.volume_right = VOLUME_VOXWARE_TO_AMI((volume & 0xff00) >> 8);
	custom.aud[1].audvol = sound.volume_right;
	return (VOLUME_AMI_TO_VOXWARE(sound.volume_left) |
		(VOLUME_AMI_TO_VOXWARE(sound.volume_right) << 8));
}

static int
AmiSetTreble(int treble)
{
	sound.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
ami_sq_play_next_frame(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 = sq_block_address(sq.front);
	size = (sq.count == index ? sq.rear_size : sq.block_size) >> 1;

	if (sound.hard.stereo)
	  {
		  ch0 = start;
		  ch1 = start + sq.block_size_half;
		  size >>= 1;
	} else
	  {
		  ch0 = start;
		  ch1 = start;
	  }
	if (sound.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 (sound.volume_left == 64 && sound.volume_right == 64)
		    {
			    /* We can play pseudo 14-bit only with the maximum volume */
			    ch3 = ch0 + sq.block_size_quarter;
			    ch2 = ch1 + 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.dmacon = AMI_AUDIO_8;
	  }
	sq.front = (sq.front + 1) % sq.max_count;
	sq.playing |= AMI_PLAY_LOADED;
}


static void
AmiPlay(void)
{
	int             minframes = 1;

	custom.intena = IF_AUD0;

	if (sq.playing & AMI_PLAY_LOADED)
	  {
		  /* There's already a frame loaded */
		  custom.intena = IF_SETCLR | IF_AUD0;
		  return;
	  }
	if (sq.playing & AMI_PLAY_PLAYING)
		/* Increase threshold: frame 1 is already being played */
		minframes = 2;

	if (sq.count < minframes)
	  {
		  /* Nothing to do */
		  custom.intena = IF_SETCLR | IF_AUD0;
		  return;
	  }
	if (sq.count <= minframes && sq.rear_size < sq.block_size && !sq.syncing)
	  {
		  /* hmmm, the only existing frame is not
		   * yet filled and we're not syncing?
		   */
		  custom.intena = IF_SETCLR | IF_AUD0;
		  return;
	  }
	ami_sq_play_next_frame(minframes);

	custom.intena = IF_SETCLR | IF_AUD0;
}


static void
ami_sq_interrupt(int irq, void *dummy, struct pt_regs *fp)
{
	int             minframes = 1;

	if (!sq.playing)
	  {
		  /* Playing was interrupted and sq_reset() has already cleared
		   * the sq variables, so better don't do anything here.
		   */
		  WAKE_UP(sq.sync_queue);
		  return;
	  }
	if (sq.playing & AMI_PLAY_PLAYING)
	  {
		  /* We've just finished a frame */
		  sq.count--;
		  WAKE_UP(sq.write_queue);
	  }
	if (sq.playing & AMI_PLAY_LOADED)
		/* Increase threshold: frame 1 is already being played */
		minframes = 2;

	/* Shift the flags */
	sq.playing = (sq.playing << 1) & AMI_PLAY_MASK;

	if (!sq.playing)
		/* No frame is playing, disable audio DMA */
		custom.dmacon = AMI_AUDIO_OFF;

	if (sq.count >= minframes)
		/* Try to play the next frame */
		AmiPlay();

	if (!sq.playing)
		/* Nothing to play anymore.
		   Wake up a process waiting for audio output to drain. */
		WAKE_UP(sq.sync_queue);
}
#endif				/* CONFIG_AMIGA */


/*** Machine definitions *****************************************************/


#ifdef CONFIG_ATARI
static MACHINE  machTT =
{
	DMASND_TT, AtaAlloc, AtaFree, AtaIrqInit,
#ifdef MODULE
	AtaIrqCleanUp,
#endif				/* MODULE */
   TTInit, TTSilence, TTSetFormat, TTSetVolume, AtaSetBass, AtaSetTreble,
	AtaPlay
};

static MACHINE  machFalcon =
{
	DMASND_FALCON, AtaAlloc, AtaFree, AtaIrqInit,
#ifdef MODULE
	AtaIrqCleanUp,
#endif				/* MODULE */
 FalconInit, FalconSilence, FalconSetFormat, FalconSetVolume, AtaSetBass,
	AtaSetTreble, AtaPlay
};

#endif				/* CONFIG_ATARI */

#ifdef CONFIG_AMIGA
static MACHINE  machAmiga =
{
	DMASND_AMIGA, AmiAlloc, AmiFree, AmiIrqInit,
#ifdef MODULE
	AmiIrqCleanUp,
#endif				/* MODULE */
     AmiInit, AmiSilence, AmiSetFormat, AmiSetVolume, NULL, AmiSetTreble,
	AmiPlay
};

#endif				/* CONFIG_AMIGA */


/*** Mid level stuff *********************************************************/


static void
sound_silence(void)
{
	/* update hardware settings one more */
	(*sound.mach.init) ();

	(*sound.mach.silence) ();
}


static void
sound_init(void)
{
	(*sound.mach.init) ();
}


static int
sound_set_format(int format)
{
	return (*sound.mach.setFormat) (format);
}


static int
sound_set_speed(int speed)
{
	if (speed < 0)
		return (sound.soft.speed);

	sound.soft.speed = speed;
	(*sound.mach.init) ();
	if (sound.minDev == SND_DEV_DSP)
		sound.dsp.speed = sound.soft.speed;

	return (sound.soft.speed);
}


static int
sound_set_stereo(int stereo)
{
	if (stereo < 0)
		return (sound.soft.stereo);

	stereo = !!stereo;	/* should be 0 or 1 now */

	sound.soft.stereo = stereo;
	if (sound.minDev == SND_DEV_DSP)
		sound.dsp.stereo = stereo;
	(*sound.mach.init) ();

	return (stereo);
}


static int
sound_set_volume(int volume)
{
	return (*sound.mach.setVolume) (volume);
}


#ifdef CONFIG_ATARI
static int
sound_set_bass(int bass)
{
	return (sound.mach.setBass ? (*sound.mach.setBass) (bass) : 50);
}
#endif				/* CONFIG_ATARI */


static int
sound_set_treble(int treble)
{
	return (sound.mach.setTreble ? (*sound.mach.setTreble) (treble) : 50);
}


static long
sound_copy_translate(const u_char * userPtr,
		     unsigned long userCount,
		     u_char frame[], long *frameUsed,
		     long frameLeft)
{
	long            (*ct_func) (const u_char *, unsigned long, u_char *, long *, long) = NULL;

	switch (sound.soft.format)
	  {
	  case AFMT_MU_LAW:
		  ct_func = sound.trans->ct_ulaw;
		  break;
	  case AFMT_A_LAW:
		  ct_func = sound.trans->ct_alaw;
		  break;
	  case AFMT_S8:
		  ct_func = sound.trans->ct_s8;
		  break;
	  case AFMT_U8:
		  ct_func = sound.trans->ct_u8;
		  break;
	  case AFMT_S16_BE:
		  ct_func = sound.trans->ct_s16be;
		  break;
	  case AFMT_U16_BE:
		  ct_func = sound.trans->ct_u16be;
		  break;
	  case AFMT_S16_LE:
		  ct_func = sound.trans->ct_s16le;
		  break;
	  case AFMT_U16_LE:
		  ct_func = sound.trans->ct_u16le;
		  break;
	  }
	if (ct_func)
		return (ct_func(userPtr, userCount, frame, frameUsed, frameLeft));
	else
		return (0);
}


/*
 * /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
mixer_init(void)
{
	mixer.busy = 0;
	sound.treble = 0;
	sound.bass = 0;
	switch (sound.mach.type)
	  {
#ifdef CONFIG_ATARI
	  case DMASND_TT:
		  atari_microwire_cmd(MW_LM1992_VOLUME(0));
		  sound.volume_left = 0;
		  atari_microwire_cmd(MW_LM1992_BALLEFT(0));
		  sound.volume_right = 0;
		  atari_microwire_cmd(MW_LM1992_BALRIGHT(0));
		  atari_microwire_cmd(MW_LM1992_TREBLE(0));
		  atari_microwire_cmd(MW_LM1992_BASS(0));
		  break;
	  case DMASND_FALCON:
		  sound.volume_left = (tt_dmasnd.output_atten & 0xf00) >> 8;
		  sound.volume_right = (tt_dmasnd.output_atten & 0xf0) >> 4;
		  break;
#endif				/* CONFIG_ATARI */
#ifdef CONFIG_AMIGA
	  case DMASND_AMIGA:
		  sound.volume_left = 64;
		  sound.volume_right = 64;
		  custom.aud[0].audvol = sound.volume_left;
		  custom.aud[3].audvol = 1;	/* For pseudo 14bit */
		  custom.aud[1].audvol = sound.volume_right;
		  custom.aud[2].audvol = 1;	/* For pseudo 14bit */
		  sound.treble = 50;
		  break;
#endif				/* CONFIG_AMIGA */
	  }
}


static int
mixer_open(int open_mode)
{
	if (mixer.busy)
		return (-EBUSY);
	mixer.busy = 1;
	return (0);
}


static int
mixer_release(void)
{
	mixer.busy = 0;
	return (0);
}


static int
mixer_ioctl(struct inode *inode, struct file *file, u_int cmd,
	    u_long arg)
{
	int             data;

	switch (sound.mach.type)
	  {
#ifdef CONFIG_ATARI
	  case DMASND_FALCON:
		  switch (cmd)
		    {
		    case SOUND_MIXER_READ_DEVMASK:
			    return (IOCTL_OUT(arg, SOUND_MASK_VOLUME | SOUND_MASK_MIC | SOUND_MASK_SPEAKER));
		    case SOUND_MIXER_READ_RECMASK:
			    return (IOCTL_OUT(arg, SOUND_MASK_MIC));
		    case SOUND_MIXER_READ_STEREODEVS:
			    return (IOCTL_OUT(arg, SOUND_MASK_VOLUME | SOUND_MASK_MIC));
		    case SOUND_MIXER_READ_CAPS:
			    return (IOCTL_OUT(arg, SOUND_CAP_EXCL_INPUT));
		    case SOUND_MIXER_READ_VOLUME:
			    return (IOCTL_OUT(arg,
			       VOLUME_ATT_TO_VOXWARE(sound.volume_left) |
					      VOLUME_ATT_TO_VOXWARE(sound.volume_right) << 8));
		    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));
		    case SOUND_MIXER_READ_SPEAKER:
			    {
				    int             porta;

				    cli();
				    sound_ym.rd_data_reg_sel = 14;
				    porta = sound_ym.rd_data_reg_sel;
				    sti();
				    return (IOCTL_OUT(arg, porta & 0x40 ? 0 : 100));
			    }
		    case SOUND_MIXER_WRITE_VOLUME:
			    IOCTL_IN(arg, data);
			    return (IOCTL_OUT(arg, sound_set_volume(data)));
		    case SOUND_MIXER_WRITE_SPEAKER:
			    {
				    int             porta;

				    IOCTL_IN(arg, data);
				    cli();
				    sound_ym.rd_data_reg_sel = 14;
				    porta = (sound_ym.rd_data_reg_sel & ~0x40) |
					(data < 50 ? 0x40 : 0);
				    sound_ym.wd_data = porta;
				    sti();
				    return (IOCTL_OUT(arg, porta & 0x40 ? 0 : 100));
			    }
		    }
		  break;

	  case DMASND_TT:
		  switch (cmd)
		    {
		    case SOUND_MIXER_READ_DEVMASK:
			    return (IOCTL_OUT(arg,
					      SOUND_MASK_VOLUME | SOUND_MASK_TREBLE | SOUND_MASK_BASS |
			      ((atari_mch_cookie >> 16) == ATARI_MCH_TT ?
			       SOUND_MASK_SPEAKER : 0)));
		    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_DB_TO_VOXWARE(sound.volume_left) |
					      (VOLUME_DB_TO_VOXWARE(sound.volume_right) << 8)));
		    case SOUND_MIXER_READ_BASS:
			    return (IOCTL_OUT(arg, TONE_DB_TO_VOXWARE(sound.bass)));
		    case SOUND_MIXER_READ_TREBLE:
			    return (IOCTL_OUT(arg, TONE_DB_TO_VOXWARE(sound.treble)));
		    case SOUND_MIXER_READ_SPEAKER:
			    {
				    int             porta;

				    if ((atari_mch_cookie >> 16) == ATARI_MCH_TT)
				      {
					      cli();
					      sound_ym.rd_data_reg_sel = 14;
					      porta = sound_ym.rd_data_reg_sel;
					      sti();
					      return (IOCTL_OUT(arg, porta & 0x40 ? 0 : 100));
				    } else
					    return (-EINVAL);
			    }
		    case SOUND_MIXER_WRITE_VOLUME:
			    IOCTL_IN(arg, data);
			    return (IOCTL_OUT(arg, sound_set_volume(data)));
		    case SOUND_MIXER_WRITE_BASS:
			    IOCTL_IN(arg, data);
			    return (IOCTL_OUT(arg, sound_set_bass(data)));
		    case SOUND_MIXER_WRITE_TREBLE:
			    IOCTL_IN(arg, data);
			    return (IOCTL_OUT(arg, sound_set_treble(data)));
		    case SOUND_MIXER_WRITE_SPEAKER:
			    if ((atari_mch_cookie >> 16) == ATARI_MCH_TT)
			      {
				      int             porta;

				      IOCTL_IN(arg, data);
				      cli();
				      sound_ym.rd_data_reg_sel = 14;
				      porta = (sound_ym.rd_data_reg_sel & ~0x40) |
					  (data < 50 ? 0x40 : 0);
				      sound_ym.wd_data = porta;
				      sti();
				      return (IOCTL_OUT(arg, porta & 0x40 ? 0 : 100));
			    } else
				    return (-EINVAL);
		    }
		  break;
#endif				/* CONFIG_ATARI */

#ifdef CONFIG_AMIGA
	  case DMASND_AMIGA:
		  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(sound.volume_left) |
					      VOLUME_AMI_TO_VOXWARE(sound.volume_right) << 8));
		    case SOUND_MIXER_WRITE_VOLUME:
			    IOCTL_IN(arg, data);
			    return (IOCTL_OUT(arg, sound_set_volume(data)));
		    case SOUND_MIXER_READ_TREBLE:
			    return (IOCTL_OUT(arg, sound.treble));
		    case SOUND_MIXER_WRITE_TREBLE:
			    IOCTL_IN(arg, data);
			    return (IOCTL_OUT(arg, sound_set_treble(data)));
		    }
		  break;
#endif				/* CONFIG_AMIGA */
	  }

	return (-EINVAL);
}



/*
 * Sound queue stuff, the heart of the driver
 */


static void
sq_init(int numBufs, int bufSize, char **buffers)
{
	sq.max_count = numBufs;
	sq.block_size = bufSize;
	sq.buffers = buffers;

	sq.front = sq.count = 0;
	sq.rear = -1;
	sq.write_queue = sq.open_queue = sq.sync_queue = 0;
	sq.busy = 0;
	sq.syncing = 0;

	sq.playing = 0;

#ifdef CONFIG_ATARI
	sq.ignore_int = 0;
#endif				/* CONFIG_ATARI */
#ifdef CONFIG_AMIGA
	sq.block_size_half = sq.block_size >> 1;
	sq.block_size_quarter = sq.block_size_half >> 1;
#endif				/* CONFIG_AMIGA */

	sound_silence();

	/* whatever you like as startup mode for /dev/dsp,
	 * (/dev/audio hasn't got a startup mode). note that
	 * once changed a new open() will *not* restore these!
	 */
	sound.dsp.format = AFMT_S8;
	sound.dsp.stereo = 0;
	sound.dsp.size = 8;

	/* set minimum rate possible without expanding */
	switch (sound.mach.type)
	  {
#ifdef CONFIG_ATARI
	  case DMASND_TT:
		  sound.dsp.speed = 6258;
		  break;
	  case DMASND_FALCON:
		  sound.dsp.speed = 8195;
		  break;
#endif				/* CONFIG_ATARI */
#ifdef CONFIG_AMIGA
	  case DMASND_AMIGA:
		  sound.dsp.speed = 8000;
		  break;
#endif				/* CONFIG_AMIGA */
	  }

	/* before the first open to /dev/dsp this wouldn't be set */
	sound.soft = sound.dsp;
	sound.hard = sound.dsp;
}


static void
sq_play(void)
{
	(*sound.mach.play) ();
}


/* ++TeSche: radically changed this one too */

static long
sq_write(const char *src, unsigned long uLeft)
{
	long            uWritten = 0;
	u_char         *dest;
	long            uUsed, bUsed, bLeft;

	/* ++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);

	/* The interrupt doesn't start to play the last, incomplete frame.
	 * Thus we can append to it without disabling the interrupts! (Note
	 * also that sq.rear isn't affected by the interrupt.)
	 */

	if (sq.count > 0 && (bLeft = sq.block_size - sq.rear_size) > 0)
	  {
		  dest = sq_block_address(sq.rear);
		  bUsed = sq.rear_size;
		  uUsed = sound_copy_translate(src, uLeft, dest, &bUsed, bLeft);
		  src += uUsed;
		  uWritten += uUsed;
		  uLeft -= uUsed;
		  sq.rear_size = bUsed;
	  }
	do
	  {
		  while (sq.count == sq.max_count)
		    {
			    sq_play();
			    if (NON_BLOCKING(sq.open_mode))
				    return (uWritten > 0 ? uWritten : -EAGAIN);
			    SLEEP(sq.write_queue, ONE_SECOND);
			    if (SIGNAL_RECEIVED)
				    return (uWritten > 0 ? uWritten : -EINTR);
		    }

		  /* Here, we can avoid disabling the interrupt by first
		   * copying and translating the data, and then updating
		   * the sq variables. Until this is done, the interrupt
		   * won't see the new frame and we can work on it
		   * undisturbed.
		   */

		  dest = sq_block_address((sq.rear + 1) % sq.max_count);
		  bUsed = 0;
		  bLeft = sq.block_size;
		  uUsed = sound_copy_translate(src, uLeft, dest, &bUsed, bLeft);
		  src += uUsed;
		  uWritten += uUsed;
		  uLeft -= uUsed;
		  if (bUsed)
		    {
			    sq.rear = (sq.rear + 1) % sq.max_count;
			    sq.rear_size = bUsed;
			    sq.count++;
		    }
	  }
	while (bUsed);		/* uUsed may have been 0 */

	sq_play();

	return (uWritten);
}


static int
sq_open(int open_mode)
{
	if (sq.busy)
	  {
		  if (NON_BLOCKING(open_mode))
			  return (-EBUSY);
		  while (sq.busy)
		    {
			    SLEEP(sq.open_queue, ONE_SECOND);
			    if (SIGNAL_RECEIVED)
				    return (-EINTR);
		    }
	  }
	sq.open_mode = open_mode;
	sq.busy = 1;
#ifdef CONFIG_ATARI
	sq.ignore_int = 1;
#endif				/* CONFIG_ATARI */
	return (0);
}


static void
sq_reset(void)
{
	sound_silence();
	sq.playing = 0;
	sq.count = 0;
	sq.front = (sq.rear + 1) % sq.max_count;
}


static int
sq_sync(void)
{
	int             rc = 0;

	sq.syncing = 1;
	sq_play();		/* there may be an incomplete frame waiting */

	while (sq.playing)
	  {
		  SLEEP(sq.sync_queue, ONE_SECOND);
		  if (SIGNAL_RECEIVED)
		    {
			    /* While waiting for audio output to drain, an interrupt occurred.
			       Stop audio output immediately and clear the queue. */
			    sq_reset();
			    rc = -EINTR;
			    break;
		    }
	  }

	sq.syncing = 0;
	return (rc);
}


static int
sq_release(void)
{
	int             rc = 0;

	if (sq.busy)
	  {
		  rc = sq_sync();
		  sq.busy = 0;
		  WAKE_UP(sq.open_queue);
		  /* Wake up a process waiting for the queue being released.
		     Note: There may be several processes waiting for a call to open()
		     returning. */
	  }
	return (rc);
}



/*
 * /dev/sndstat
 */


static void
state_init(void)
{
	state.busy = 0;
}


/* state.buf should not overflow! */

static int
state_open(int open_mode)
{
	char           *buffer = state.buf, *mach = "";
	int             len = 0;

	if (state.busy)
		return (-EBUSY);

	state.ptr = 0;
	state.busy = 1;

	switch (sound.mach.type)
	  {
#ifdef CONFIG_ATARI
	  case DMASND_TT:
	  case DMASND_FALCON:
		  mach = "Atari ";
		  break;
#endif				/* CONFIG_ATARI */
#ifdef CONFIG_AMIGA
	  case DMASND_AMIGA:
		  mach = "Amiga ";
		  break;
#endif				/* CONFIG_AMIGA */
	  }
	len += sprintf(buffer + len, "%sDMA sound driver:\n", mach);

	len += sprintf(buffer + len, "\tsound.format = 0x%x", sound.soft.format);
	switch (sound.soft.format)
	  {
	  case AFMT_MU_LAW:
		  len += sprintf(buffer + len, " (mu-law)");
		  break;
	  case AFMT_A_LAW:
		  len += sprintf(buffer + len, " (A-law)");
		  break;
	  case AFMT_U8:
		  len += sprintf(buffer + len, " (unsigned 8 bit)");
		  break;
	  case AFMT_S8:
		  len += sprintf(buffer + len, " (signed 8 bit)");
		  break;
	  case AFMT_S16_BE:
		  len += sprintf(buffer + len, " (signed 16 bit big)");
		  break;
	  case AFMT_U16_BE:
		  len += sprintf(buffer + len, " (unsigned 16 bit big)");
		  break;
	  case AFMT_S16_LE:
		  len += sprintf(buffer + len, " (signed 16 bit little)");
		  break;
	  case AFMT_U16_LE:
		  len += sprintf(buffer + len, " (unsigned 16 bit little)");
		  break;
	  }
	len += sprintf(buffer + len, "\n");
	len += sprintf(buffer + len, "\tsound.speed = %dHz (phys. %dHz)\n",
		       sound.soft.speed, sound.hard.speed);
	len += sprintf(buffer + len, "\tsound.stereo = 0x%x (%s)\n",
	       sound.soft.stereo, sound.soft.stereo ? "stereo" : "mono");
	switch (sound.mach.type)
	  {
#ifdef CONFIG_ATARI
	  case DMASND_TT:
		  len += sprintf(buffer + len, "\tsound.volume_left = %ddB [-40...0]\n",
				 sound.volume_left);
		  len += sprintf(buffer + len, "\tsound.volume_right = %ddB [-40...0]\n",
				 sound.volume_right);
		  len += sprintf(buffer + len, "\tsound.bass = %ddB [-12...+12]\n",
				 sound.bass);
		  len += sprintf(buffer + len, "\tsound.treble = %ddB [-12...+12]\n",
				 sound.treble);
		  break;
	  case DMASND_FALCON:
		  len += sprintf(buffer + len, "\tsound.volume_left = %ddB [-22.5...0]\n",
				 sound.volume_left);
		  len += sprintf(buffer + len, "\tsound.volume_right = %ddB [-22.5...0]\n",
				 sound.volume_right);
		  break;
#endif				/* CONFIG_ATARI */
#ifdef CONFIG_AMIGA
	  case DMASND_AMIGA:
		  len += sprintf(buffer + len, "\tsound.volume_left = %d [0...64]\n",
				 sound.volume_left);
		  len += sprintf(buffer + len, "\tsound.volume_right = %d [0...64]\n",
				 sound.volume_right);
		  break;
#endif				/* CONFIG_AMIGA */
	  }
	len += sprintf(buffer + len, "\tsq.block_size = %d sq.max_count = %d\n",
		       sq.block_size, sq.max_count);
	len += sprintf(buffer + len, "\tsq.count = %d sq.rear_size = %d\n", sq.count,
		       sq.rear_size);
	len += sprintf(buffer + len, "\tsq.playing = %d sq.syncing = %d\n",
		       sq.playing, sq.syncing);
	state.len = len;
	return (0);
}


static int
state_release(void)
{
	state.busy = 0;
	return (0);
}


static long
state_read(char *dest, unsigned long count)
{
	int             n = state.len - state.ptr;

	if (n > count)
		n = count;
	if (n <= 0)
		return (0);
	copy_to_user(dest, &state.buf[state.ptr], n);
	state.ptr += n;
	return (n);
}



/*** High level stuff ********************************************************/


static int
sound_open(struct inode *inode, struct file *file)
{
	int             dev = MINOR(inode->i_rdev) & 0x0f;
	int             rc = 0;

	switch (dev)
	  {
	  case SND_DEV_STATUS:
		  rc = state_open(file->f_flags);
		  break;
	  case SND_DEV_CTL:
		  rc = mixer_open(file->f_flags);
		  break;
	  case SND_DEV_DSP:
	  case SND_DEV_AUDIO:
		  rc = sq_open(file->f_flags);
		  if (rc == 0)
		    {
			    sound.minDev = dev;
			    sound.soft = sound.dsp;
			    sound.hard = sound.dsp;
			    sound_init();
			    if (dev == SND_DEV_AUDIO)
			      {
				      sound_set_speed(8000);
				      sound_set_stereo(0);
				      sound_set_format(AFMT_MU_LAW);
			      }
		    }
		  break;
	  default:
		  rc = -ENXIO;
	  }
#ifdef MODULE
	if (rc >= 0)
		MOD_INC_USE_COUNT;
#endif
	return (rc);
}


static int
sound_fsync(struct inode *inode, struct file *filp)
{
	int             dev = MINOR(inode->i_rdev) & 0x0f;

	switch (dev)
	  {
	  case SND_DEV_STATUS:
	  case SND_DEV_CTL:
		  return (0);
	  case SND_DEV_DSP:
	  case SND_DEV_AUDIO:
		  return (sq_sync());
	  default:
		  return (unknown_minor_dev("sound_fsync", dev));
	  }
}


static void
sound_release(struct inode *inode, struct file *file)
{
	int             dev = MINOR(inode->i_rdev);

	switch (dev & 0x0f)
	  {
	  case SND_DEV_STATUS:
		  state_release();
		  break;
	  case SND_DEV_CTL:
		  mixer_release();
		  break;
	  case SND_DEV_DSP:
	  case SND_DEV_AUDIO:
		  sq_release();
		  sound.soft = sound.dsp;
		  sound.hard = sound.dsp;
		  sound_silence();
		  break;
	  default:
		  unknown_minor_dev("sound_release", dev);
		  return;
	  }
#ifdef MODULE
	MOD_DEC_USE_COUNT;
#endif
}


static long long
sound_lseek(struct inode *inode, struct file *file,
	    long long offset, int orig)
{
	return -ESPIPE;
}


static ssize_t sound_read(struct file *file, char *buf, szie_t count, loff_t *ppos)
{
	int             dev = MINOR(file->f_dentry->d_inode->i_rdev);

	switch (dev & 0x0f)
	{
		case SND_DEV_STATUS:
			return (state_read(buf, count));
		case SND_DEV_CTL:
		case SND_DEV_DSP:
		case SND_DEV_AUDIO:
			return (-EPERM);
		default:
			return (unknown_minor_dev("sound_read", dev));
	}
}


static ssize_t sound_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
{
	int             dev = MINOR(file->f_dentry->d_inode->i_rdev);

	switch (dev & 0x0f)
	{
		case SND_DEV_STATUS:
		case SND_DEV_CTL:
			return (-EPERM);
		case SND_DEV_DSP:
		case SND_DEV_AUDIO:
			return (sq_write(buf, count));
		default:
			return (unknown_minor_dev("sound_write", dev));
	}
}


static int unknown_minor_dev(char *fname, int dev)
{
	/* printk("%s: Unknown minor device %d\n", fname, dev); */
	return (-ENXIO);
}


static int sound_ioctl(struct inode *inode, struct file *file, u_int cmd, u_long arg)
{
	int dev = MINOR(inode->i_rdev);
	u_long fmt;
	int data;

	switch (dev & 0x0f)
	{
		case SND_DEV_STATUS:
			return (-EPERM);
		case SND_DEV_CTL:
			return (mixer_ioctl(inode, file, cmd, arg));
		case SND_DEV_AUDIO:
		case SND_DEV_DSP:
			switch (cmd)
			{
				case SNDCTL_DSP_RESET:
					sq_reset();
					return (0);
				case SNDCTL_DSP_POST:
				case SNDCTL_DSP_SYNC:
					return (sound_fsync(inode, file));

					/* ++TeSche: before changing any of these it's probably wise to
			 		 * wait until sound playing has settled down
			 		 */
				case SNDCTL_DSP_SPEED:
					sound_fsync(inode, file);
					IOCTL_IN(arg, data);
					return (IOCTL_OUT(arg, sound_set_speed(data)));
				case SNDCTL_DSP_STEREO:
					sound_fsync(inode, file);
					IOCTL_IN(arg, data);
					return (IOCTL_OUT(arg, sound_set_stereo(data)));
				case SOUND_PCM_WRITE_CHANNELS:
					sound_fsync(inode, file);
					IOCTL_IN(arg, data);
					return (IOCTL_OUT(arg, sound_set_stereo(data - 1) + 1));
				case SNDCTL_DSP_SETFMT:
					sound_fsync(inode, file);
					IOCTL_IN(arg, data);
					return (IOCTL_OUT(arg, sound_set_format(data)));
				case SNDCTL_DSP_GETFMTS:
					fmt = 0;
					if (sound.trans)
					{
						if (sound.trans->ct_ulaw)
							fmt |= AFMT_MU_LAW;
						if (sound.trans->ct_alaw)
							fmt |= AFMT_A_LAW;
						if (sound.trans->ct_s8)
							fmt |= AFMT_S8;
						if (sound.trans->ct_u8)
							fmt |= AFMT_U8;
						if (sound.trans->ct_s16be)
							fmt |= AFMT_S16_BE;
						if (sound.trans->ct_u16be)
							fmt |= AFMT_U16_BE;
						if (sound.trans->ct_s16le)
							fmt |= AFMT_S16_LE;
						if (sound.trans->ct_u16le)
							fmt |= AFMT_U16_LE;
					}
					return (IOCTL_OUT(arg, fmt));
				case SNDCTL_DSP_GETBLKSIZE:
					return (IOCTL_OUT(arg, 10240));
				case SNDCTL_DSP_SUBDIVIDE:
		 		case SNDCTL_DSP_SETFRAGMENT:
					break;
				default:
					return (mixer_ioctl(inode, file, cmd, arg));
			}
			break;

		default:
			return (unknown_minor_dev("sound_ioctl", dev));
	}
	return (-EINVAL);
}


static struct file_operations sound_fops =
{
	sound_lseek,
	sound_read,
	sound_write,
	NULL,
	NULL,			/* select */
	sound_ioctl,
	NULL,
	sound_open,
	sound_release,
	sound_fsync
};



/*** Config & Setup **********************************************************/


void
soundcard_init(void)
{
	int             has_sound = 0;
	int             i;

	switch (m68k_machtype)
	{
#ifdef CONFIG_ATARI
		case MACH_ATARI:
			if (ATARIHW_PRESENT(PCM_8BIT))
			{
				if (ATARIHW_PRESENT(CODEC))
					sound.mach = machFalcon;
				else if (ATARIHW_PRESENT(MICROWIRE))
					sound.mach = machTT;
				else
					break;
				if ((mfp.int_en_a & mfp.int_mk_a & 0x20) == 0)
					has_sound = 1;
				else
					printk(KERN_ERR "DMA sound driver: Timer A interrupt already in use\n");
			}
			break;

#endif				/* CONFIG_ATARI */
#ifdef CONFIG_AMIGA
		case MACH_AMIGA:
			if (AMIGAHW_PRESENT(AMI_AUDIO))
			{
				sound.mach = machAmiga;
				has_sound = 1;
			}
			break;
#endif				/* CONFIG_AMIGA */
	}
	if (!has_sound)
		return;

	/* Set up sound queue, /dev/audio and /dev/dsp. */
	sound_buffers = kmalloc(numBufs * sizeof(char *), GFP_KERNEL);

	if (!sound_buffers)
	{
out_of_memory:
		printk(KERN_ERR "DMA sound driver: Not enough buffer memory, driver disabled!\n");
		return;
	}
	for (i = 0; i < numBufs; i++)
	{
		sound_buffers[i] = sound.mach.dma_alloc(bufSize << 10, GFP_KERNEL);
		if (!sound_buffers[i])
		{
			while (i--)
				sound.mach.dma_free(sound_buffers[i], bufSize << 10);
			kfree(sound_buffers);
			sound_buffers = 0;
			goto out_of_memory;
		}
	}

#ifndef MODULE
	/* Register driver with the VFS. */
	register_chrdev(SOUND_MAJOR, "sound", &sound_fops);
#endif

	sq_init(numBufs, bufSize << 10, sound_buffers);

	/* Set up /dev/sndstat. */
	state_init();

	/* Set up /dev/mixer. */
	mixer_init();

	if (!sound.mach.irqinit())
	{
		printk(KERN_ERR "DMA sound driver: Interrupt initialization failed\n");
		return;
	}
#ifdef MODULE
	irq_installed = 1;
#endif

	printk(KERN_INFO "DMA sound driver installed, using %d buffers of %dk.\n", numBufs,
	       bufSize);

	return;
}

void sound_setup(char *str, int *ints)
{
	/* ++Martin: stub, could possibly be merged with soundcard.c et al later */
}


#define MAXARGS		8	/* Should be sufficient for now */

void
dmasound_setup(char *str, int *ints)
{
	/* check the bootstrap parameter for "dmasound=" */

	switch (ints[0])
	{
		case 3:
			if ((ints[3] < 0) || (ints[3] > MAX_CATCH_RADIUS))
				printk(KERN_WARNING "dmasound_setup: illegal catch radius, using default = %d\n", catchRadius);
			else
				catchRadius = ints[3];
		  /* fall through */
		case 2:
			if (ints[1] < MIN_BUFFERS)
				printk(KERN_WARNING "dmasound_setup: illegal number of buffers, using default = %d\n", numBufs);
			else
				numBufs = ints[1];
			if (ints[2] < MIN_BUFSIZE || ints[2] > MAX_BUFSIZE)
				printk(KERN_WARNING "dmasound_setup: illegal buffer size, using default = %d\n", bufSize);
			else
				bufSize = ints[2];
			break;
		case 0:
			break;
		default:
			printk(KERN_WARNING "dmasound_setup: illegal number of arguments\n");
	}
}


#ifdef MODULE

static int      dmasound[MAXARGS] = {
	0
};

int init_module(void)
{
	int             err, i = 0;
	int             ints[MAXARGS + 1];

	while (i < MAXARGS && dmasound[i])
		ints[i + 1] = dmasound[i++];
	ints[0] = i;

	if (i)
		dmasound_setup("dmasound=", ints);

	err = register_chrdev(SOUND_MAJOR, "sound", &sound_fops);
	if (err)
	{
		printk(KERN_ERR "dmasound: driver already loaded/included in kernel\n");
		return err;
	}
	chrdev_registered = 1;
	soundcard_init();

	return 0;
}


void cleanup_module(void)
{
	int             i;

	if (MOD_IN_USE)
		return;

	if (chrdev_registered)
		unregister_chrdev(SOUND_MAJOR, "sound");

	if (irq_installed)
	{
		sound_silence();
		sound.mach.irqcleanup();
	}
	if (sound_buffers)
	{
		for (i = 0; i < numBufs; i++)
			sound.mach.dma_free(sound_buffers[i], bufSize << 10);
		kfree(sound_buffers);
	}
}

#endif				/* MODULE */
