summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1997-06-03 07:56:51 +0000
committerRalf Baechle <ralf@linux-mips.org>1997-06-03 07:56:51 +0000
commit52662ff3e66770fd7e4fc508c91056d29c08bff0 (patch)
treee400c4e30872f87452567b89da22648b1d51b3ea /drivers
parentb3eb816c798e466824ee7bde9699711b4fa8a955 (diff)
New files.
Diffstat (limited to 'drivers')
-rw-r--r--drivers/sound/dmasound.c3435
-rw-r--r--drivers/sound/dmasound.h36
-rw-r--r--drivers/sound/sound_pnp.c1513
3 files changed, 4984 insertions, 0 deletions
diff --git a/drivers/sound/dmasound.c b/drivers/sound/dmasound.c
new file mode 100644
index 000000000..b7da7932a
--- /dev/null
+++ b/drivers/sound/dmasound.c
@@ -0,0 +1,3435 @@
+
+/* 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 (current->signal & ~current->blocked)
+#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 long sound_read(struct inode *inode, struct file *file, char *buf,
+ unsigned long count)
+{
+ int dev = MINOR(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 long sound_write(struct inode *inode, struct file *file,
+ const char *buf, unsigned long count)
+{
+ int dev = MINOR(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("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("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("DMA sound driver: Interrupt initialization failed\n");
+ return;
+ }
+#ifdef MODULE
+ irq_installed = 1;
+#endif
+
+ printk("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("dmasound_setup: illegal catch radius, using default = %d\n", catchRadius);
+ else
+ catchRadius = ints[3];
+ /* fall through */
+ case 2:
+ if (ints[1] < MIN_BUFFERS)
+ printk("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("dmasound_setup: illegal buffer size, using default = %d\n", bufSize);
+ else
+ bufSize = ints[2];
+ break;
+ case 0:
+ break;
+ default:
+ printk("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("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 */
diff --git a/drivers/sound/dmasound.h b/drivers/sound/dmasound.h
new file mode 100644
index 000000000..fec3ad84e
--- /dev/null
+++ b/drivers/sound/dmasound.h
@@ -0,0 +1,36 @@
+
+/* linux/drivers/sound/dmasound.h */
+
+/*
+ * Minor numbers for the sound driver.
+ *
+ * Unfortunately Creative called the codec chip of SB as a DSP. For this
+ * reason the /dev/dsp is reserved for digitized audio use. There is a
+ * device for true DSP processors but it will be called something else.
+ * In v3.0 it's /dev/sndproc but this could be a temporary solution.
+ */
+
+#define SND_NDEVS 256 /* Number of supported devices */
+#define SND_DEV_CTL 0 /* Control port /dev/mixer */
+#define SND_DEV_SEQ 1 /* Sequencer output /dev/sequencer (FM
+ synthesizer and MIDI output) */
+#define SND_DEV_MIDIN 2 /* Raw midi access */
+#define SND_DEV_DSP 3 /* Digitized voice /dev/dsp */
+#define SND_DEV_AUDIO 4 /* Sparc compatible /dev/audio */
+#define SND_DEV_DSP16 5 /* Like /dev/dsp but 16 bits/sample */
+#define SND_DEV_STATUS 6 /* /dev/sndstat */
+/* #7 not in use now. Was in 2.4. Free for use after v3.0. */
+#define SND_DEV_SEQ2 8 /* /dev/sequencer, level 2 interface */
+#define SND_DEV_SNDPROC 9 /* /dev/sndproc for programmable devices */
+#define SND_DEV_PSS SND_DEV_SNDPROC
+
+#define DSP_DEFAULT_SPEED 8000
+
+#define ON 1
+#define OFF 0
+
+#define MAX_AUDIO_DEV 5
+#define MAX_MIXER_DEV 2
+#define MAX_SYNTH_DEV 3
+#define MAX_MIDI_DEV 6
+#define MAX_TIMER_DEV 3
diff --git a/drivers/sound/sound_pnp.c b/drivers/sound/sound_pnp.c
new file mode 100644
index 000000000..ffaa5b644
--- /dev/null
+++ b/drivers/sound/sound_pnp.c
@@ -0,0 +1,1513 @@
+/*
+ * sound/sound_pnp.c
+ *
+ * PnP soundcard support (Linux spesific)
+ */
+/*
+ * Copyright (C) by Hannu Savolainen 1993-1997
+ *
+ * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this software
+ * for more info.
+ */
+#include <linux/config.h>
+
+#include "sound_config.h"
+
+#if defined(CONFIG_SPNP)
+
+
+static struct wait_queue *maui_sleeper = NULL;
+static volatile struct snd_wait maui_sleep_flag =
+{0};
+
+extern unsigned long init_pnp (unsigned long, int *);
+
+#include "pnp.h"
+extern int *sound_osp;
+
+extern int (*pnp_ioctl) (unsigned int cmd, caddr_t arg);
+
+extern int sound_pnp_port;
+static int disabled_devices[] =
+{
+ PNP_DEVID ('G', 'R', 'V', 0x0003), /* GUS SB emulation */
+ PNP_DEVID ('G', 'R', 'V', 0x0004), /* GUS MPU emulation */
+ 0
+};
+
+static int special_devices[] =
+{
+ PNP_DEVID ('C', 'S', 'C', 0x0010), /* CS4232/6 control port */
+ PNP_DEVID ('C', 'S', 'C', 0x0002), /* CS4232/6 control port */
+ 0
+};
+
+static int pnp_sig = 0;
+
+static void
+pnp_delay (long t)
+{
+ t = (t * HZ) / 1000000; /* Convert to jiffies */
+
+
+ {
+ unsigned long tlimit;
+
+ if (t)
+ current->timeout = tlimit = jiffies + (t);
+ else
+ tlimit = (unsigned long) -1;
+ maui_sleep_flag.opts = WK_SLEEP;
+ interruptible_sleep_on (&maui_sleeper);
+ if (!(maui_sleep_flag.opts & WK_WAKEUP))
+ {
+ if (jiffies >= tlimit)
+ maui_sleep_flag.opts |= WK_TIMEOUT;
+ }
+ maui_sleep_flag.opts &= ~WK_SLEEP;
+ };
+}
+
+void
+cs4232_pnp (void *parm)
+{
+ struct pnp_dev *dev = (struct pnp_dev *) parm;
+ char *name;
+ int old_num_mixers = num_mixers;
+ int is_4232 = 0; /* CS4232 (not CS4236 or something better) */
+
+ int portmask = 0xff;
+ int irqmask = 0x01, dmamask = 0x03;
+ int opl3_driver, wss_driver;
+
+
+ if (pnp_trace)
+ printk ("CS4232/6 driver waking up\n");
+
+ if (dev->card->key == (PNP_DEVID ('C', 'S', 'C', 0x4232)))
+ is_4232 = 1;
+
+#ifndef USE_HOT_PNP_INIT
+ if (is_4232) /* CS4232 may cause lockups */
+ if (pnp_get_port (dev, 0) == NO_PORT ||
+ pnp_get_port (dev, 1) == NO_PORT ||
+ pnp_get_irq (dev, 0) == NO_IRQ ||
+ pnp_get_dma (dev, 0) == NO_DMA
+ )
+ {
+ printk ("Sound: CS4232 in PnP mode requires prior initialization by PnP BIOS\n");
+ return;
+ }
+#endif
+
+ if (dev->card && dev->card->name)
+ name = dev->card->name;
+ else
+ name = "PnP WSS";
+
+ if ((wss_driver = sndtable_identify_card ("AD1848")))
+ portmask |= 0x01; /* MSS */
+ else
+ printk ("Sound: PnP MSS/WSS device detected but no driver enabled\n");
+
+ if ((opl3_driver = sndtable_identify_card ("OPL3")))
+ portmask |= 0x02; /* OPL3 */
+ else
+ printk ("Sound: PnP OPL3/4 device detected but no driver enabled\n");
+
+ /* printk ("WSS driver %d, OPL3 driver %d\n", wss_driver, opl3_driver); */
+
+ if (!portmask) /* No drivers available */
+ return;
+
+ if (!is_4232)
+ if (!pnp_allocate_device (pnp_sig, dev, portmask, irqmask, dmamask, 0x00))
+ {
+ printk ("sound_pnp: Failed to find free resources\n");
+ return;
+ }
+
+ {
+ struct address_info hw_config;
+ int wss_base, opl3_base;
+ int irq;
+ int dma1, dma2;
+
+ if (pnp_trace)
+ printk ("Device activation OK\n");
+ wss_base = pnp_get_port (dev, 0);
+ opl3_base = pnp_get_port (dev, 1);
+ irq = pnp_get_irq (dev, 0);
+ dma1 = pnp_get_dma (dev, 0);
+ dma2 = pnp_get_dma (dev, 1);
+
+ pnp_delay (1000000);
+
+ if (pnp_trace)
+ {
+ printk ("I/O0 %03x\n", wss_base);
+ printk ("I/O1 %03x\n", opl3_base);
+ printk ("IRQ %d\n", irq);
+ printk ("DMA0 %d\n", dma1);
+ printk ("DMA1 %d\n", dma2);
+ }
+
+ if (opl3_base && opl3_driver)
+ {
+ hw_config.io_base = opl3_base;
+ hw_config.irq = 0;
+ hw_config.dma = -1;
+ hw_config.dma2 = -1;
+ hw_config.always_detect = 0;
+ hw_config.name = "";
+ hw_config.driver_use_1 = 0;
+ hw_config.driver_use_2 = 0;
+ hw_config.osp = sound_osp;
+ hw_config.card_subtype = 0;
+
+ sndtable_start_card (opl3_driver, &hw_config);
+
+ }
+
+ if (wss_base && wss_driver)
+ {
+ hw_config.io_base = wss_base;
+ hw_config.irq = irq;
+ hw_config.dma = dma1;
+ hw_config.dma2 = (dma2 == NO_DMA) ? dma1 : dma2;
+ hw_config.always_detect = 0;
+ hw_config.name = name;
+ hw_config.driver_use_1 = 0;
+ hw_config.driver_use_2 = 0;
+ hw_config.osp = sound_osp;
+ hw_config.card_subtype = 0;
+
+ sndtable_start_card (wss_driver, &hw_config);
+
+
+ if (num_mixers > old_num_mixers)
+ { /* Assume the mixer map is as suggested in the CS4232 spec */
+ AD1848_REROUTE (SOUND_MIXER_LINE1, SOUND_MIXER_LINE);
+ AD1848_REROUTE (SOUND_MIXER_LINE2, SOUND_MIXER_CD);
+ AD1848_REROUTE (SOUND_MIXER_LINE3, SOUND_MIXER_SYNTH); /* FM */
+ }
+ }
+ }
+}
+
+void
+opti82C924_pnp (void *parm)
+{
+ struct pnp_dev *dev = (struct pnp_dev *) parm;
+ char *name;
+
+ int portmask = 0xff, irqmask = 0x01, dmamask = 0x03;
+ int opl3_driver, wss_driver;
+
+ if (pnp_trace)
+ printk ("OPTi 82C924 driver waking up\n");
+
+ if (dev->card && dev->card->name)
+ name = dev->card->name;
+ else
+ name = "PnP WSS";
+
+ if ((wss_driver = sndtable_identify_card ("AD1848")))
+ portmask |= 0x01; /* MSS */
+ else
+ printk ("Sound: PnP MSS/WSS device detected but no driver enabled\n");
+
+ if ((opl3_driver = sndtable_identify_card ("OPL3")))
+ portmask |= 0x02; /* OPL3 */
+ else
+ printk ("Sound: PnP OPL3/4 device detected but no driver enabled\n");
+
+ /* printk ("WSS driver %d, OPL3 driver %d\n", wss_driver, opl3_driver); */
+
+ if (!portmask) /* No drivers available */
+ return;
+
+ if (!pnp_allocate_device (pnp_sig, dev, portmask, irqmask, dmamask, 0x00))
+ printk ("sound_pnp: Failed to find free resources\n");
+ else
+ {
+ struct address_info hw_config;
+ int wss_base, opl3_base;
+ int irq;
+ int dma1, dma2;
+
+ if (pnp_trace)
+ printk ("Device activation OK\n");
+ wss_base = pnp_get_port (dev, 1);
+ opl3_base = pnp_get_port (dev, 2);
+ irq = pnp_get_irq (dev, 0);
+ dma1 = pnp_get_dma (dev, 0);
+ dma2 = pnp_get_dma (dev, 1);
+
+ pnp_delay (2000000);
+
+ if (pnp_trace)
+ {
+ printk ("I/O0 %03x\n", wss_base);
+ printk ("I/O1 %03x\n", opl3_base);
+ printk ("IRQ %d\n", irq);
+ printk ("DMA0 %d\n", dma1);
+ printk ("DMA1 %d\n", dma2);
+ }
+
+ if (opl3_base && opl3_driver)
+ {
+ hw_config.io_base = opl3_base + 8;
+ hw_config.irq = 0;
+ hw_config.dma = -1;
+ hw_config.dma2 = -1;
+ hw_config.always_detect = 0;
+ hw_config.name = "";
+ hw_config.driver_use_1 = 0;
+ hw_config.driver_use_2 = 0;
+ hw_config.osp = sound_osp;
+ hw_config.card_subtype = 0;
+
+ sndtable_start_card (opl3_driver, &hw_config);
+
+ }
+
+ if (wss_base && wss_driver)
+ {
+ hw_config.io_base = wss_base + 4;
+ hw_config.irq = irq;
+ hw_config.dma = dma1;
+ hw_config.dma2 = (dma2 == NO_DMA) ? dma1 : dma2;
+ hw_config.always_detect = 0;
+ hw_config.name = name;
+ hw_config.driver_use_1 = 0;
+ hw_config.driver_use_2 = 0;
+ hw_config.osp = sound_osp;
+ hw_config.card_subtype = 0;
+
+ sndtable_start_card (wss_driver, &hw_config);
+
+ }
+ }
+}
+
+void
+opl3sa2_pnp (void *parm)
+{
+ struct pnp_dev *dev = (struct pnp_dev *) parm;
+ char *name;
+
+ int portmask = 0x00, irqmask = 0x01, dmamask = 0x03;
+ int opl3_driver, wss_driver, mpu_driver;
+
+ if (pnp_trace)
+ printk ("OPL3-SA2 driver waking up\n");
+
+ if (dev->card && dev->card->name)
+ name = dev->card->name;
+ else
+ name = "PnP WSS";
+
+ if ((wss_driver = sndtable_identify_card ("AD1848")))
+ portmask |= 0x02; /* MSS */
+ else
+ printk ("Sound: PnP MSS/WSS device detected but no driver enabled\n");
+
+ if ((opl3_driver = sndtable_identify_card ("OPL3")))
+ portmask |= 0x04; /* OPL3 */
+ else
+ printk ("Sound: PnP OPL3/4 device detected but no driver enabled\n");
+
+ if ((mpu_driver = sndtable_identify_card ("UART401")))
+ portmask |= 0x08; /* OPL3 */
+ else
+ printk ("Sound: PnP UART401 device detected but no driver enabled\n");
+
+ /* printk ("WSS driver %d, OPL3 driver %d\n", wss_driver, opl3_driver); */
+
+ if (!portmask) /* No drivers available */
+ return;
+
+ if (!pnp_allocate_device (pnp_sig, dev, portmask, irqmask, dmamask, 0x00))
+ printk ("sound_pnp: Failed to find free resources\n");
+ else
+ {
+ struct address_info hw_config;
+ int wss_base, opl3_base, mpu_base;
+ int irq;
+ int dma1, dma2;
+
+ if (pnp_trace)
+ printk ("Device activation OK\n");
+ wss_base = pnp_get_port (dev, 1);
+ opl3_base = pnp_get_port (dev, 2);
+ mpu_base = pnp_get_port (dev, 3);
+ irq = pnp_get_irq (dev, 0);
+ dma1 = pnp_get_dma (dev, 0);
+ dma2 = pnp_get_dma (dev, 1);
+
+ pnp_delay (1000000);
+
+ if (pnp_trace)
+ {
+ printk ("I/O0 %03x\n", wss_base);
+ printk ("I/O1 %03x\n", opl3_base);
+ printk ("I/O3 %03x\n", mpu_base);
+ printk ("IRQ %d\n", irq);
+ printk ("DMA0 %d\n", dma1);
+ printk ("DMA1 %d\n", dma2);
+ }
+
+ if (opl3_base && opl3_driver)
+ {
+ hw_config.io_base = opl3_base;
+ hw_config.irq = 0;
+ hw_config.dma = -1;
+ hw_config.dma2 = -1;
+ hw_config.always_detect = 0;
+ hw_config.name = "";
+ hw_config.driver_use_1 = 0;
+ hw_config.driver_use_2 = 0;
+ hw_config.osp = sound_osp;
+ hw_config.card_subtype = 0;
+
+ sndtable_start_card (opl3_driver, &hw_config);
+
+ }
+
+ if (wss_base && wss_driver)
+ {
+ hw_config.io_base = wss_base + 4;
+ hw_config.irq = irq;
+ hw_config.dma = dma1;
+ hw_config.dma2 = (dma2 == NO_DMA) ? dma1 : dma2;
+ hw_config.always_detect = 0;
+ hw_config.name = name;
+ hw_config.driver_use_1 = 0;
+ hw_config.driver_use_2 = 0;
+ hw_config.osp = sound_osp;
+ hw_config.card_subtype = 0;
+
+ sndtable_start_card (wss_driver, &hw_config);
+
+ }
+
+ if (mpu_base && mpu_driver)
+ {
+ hw_config.io_base = mpu_base;
+ hw_config.irq = 0;
+ hw_config.dma = -1;
+ hw_config.dma2 = -1;
+ hw_config.always_detect = 0;
+ hw_config.name = "";
+ hw_config.driver_use_1 = 0;
+ hw_config.driver_use_2 = 0;
+ hw_config.osp = sound_osp;
+ hw_config.card_subtype = 0;
+
+ sndtable_start_card (mpu_driver, &hw_config);
+
+ }
+ }
+}
+
+static unsigned char
+C931_read (int base, int reg)
+{
+ unsigned char data;
+ unsigned long flags;
+
+ save_flags (flags);
+ cli ();
+ outb ((0xE4), base);
+ outb ((reg), base + 2);
+ data = inb (base + 3);
+ restore_flags (flags);
+ return data;
+}
+
+static void
+C931_write (int base, int reg, unsigned char data)
+{
+ unsigned long flags;
+
+ save_flags (flags);
+ cli ();
+ outb ((0xE4), base);
+ outb ((reg), base + 2);
+ outb ((data), base + 3);
+ restore_flags (flags);
+}
+
+void
+opti82C931_pnp (void *parm)
+{
+ struct pnp_dev *dev = (struct pnp_dev *) parm;
+ char *name;
+
+ int portmask = 0xff, irqmask = 0x01, dmamask = 0x03;
+ int opl3_driver, wss_driver;
+
+ if (pnp_trace)
+ printk ("OPTi 82C931 driver waking up\n");
+
+ if (dev->card && dev->card->name)
+ name = dev->card->name;
+ else
+ name = "PnP WSS";
+
+ if ((wss_driver = sndtable_identify_card ("AD1848")))
+ portmask |= 0x01; /* MSS */
+ else
+ printk ("Sound: PnP MSS/WSS device detected but no driver enabled\n");
+
+ if ((opl3_driver = sndtable_identify_card ("OPL3")))
+ portmask |= 0x02; /* OPL3 */
+ else
+ printk ("Sound: PnP OPL3/4 device detected but no driver enabled\n");
+
+ /* printk ("WSS driver %d, OPL3 driver %d\n", wss_driver, opl3_driver); */
+
+ if (!portmask) /* No drivers available */
+ return;
+
+ if (!pnp_allocate_device (pnp_sig, dev, portmask, irqmask, dmamask, 0x00))
+ printk ("sound_pnp: Failed to find free resources\n");
+ else
+ {
+ struct address_info hw_config;
+ int wss_base, opl3_base, master_ctl;
+ int irq;
+ int dma1, dma2;
+
+ if (pnp_trace)
+ printk ("Device activation OK\n");
+ wss_base = pnp_get_port (dev, 0);
+ opl3_base = pnp_get_port (dev, 1);
+ master_ctl = pnp_get_port (dev, 3);
+ irq = pnp_get_irq (dev, 0);
+ dma1 = pnp_get_dma (dev, 0);
+ dma2 = pnp_get_dma (dev, 1);
+
+ if (pnp_trace)
+ {
+ int i;
+
+ printk ("I/O0 %03x\n", wss_base);
+ printk ("I/O1 %03x\n", opl3_base);
+ printk ("Master control port %x\n", master_ctl);
+ for (i = 0; i < 4; i++)
+ printk ("Port %x = %x\n", master_ctl + i, inb (master_ctl + i));
+ printk ("IRQ %d\n", irq);
+ printk ("DMA0 %d\n", dma1);
+ printk ("DMA1 %d\n", dma2);
+ }
+ {
+ unsigned char tmp;
+
+ tmp = C931_read (master_ctl, 5) | 0x20; /* Enable codec registers I16 to I31 */
+ C931_write (master_ctl, 5, tmp);
+
+ tmp = 0x82; /* MPU and WSS enabled, SB disabled */
+ C931_write (master_ctl, 6, tmp);
+ }
+
+ pnp_delay (2000000);
+
+ if (opl3_base && opl3_driver)
+ {
+ hw_config.io_base = opl3_base + 8;
+ hw_config.irq = 0;
+ hw_config.dma = -1;
+ hw_config.dma2 = -1;
+ hw_config.always_detect = 0;
+ hw_config.name = "";
+ hw_config.driver_use_1 = 0;
+ hw_config.driver_use_2 = 0;
+ hw_config.osp = sound_osp;
+ hw_config.card_subtype = 0;
+
+ sndtable_start_card (opl3_driver, &hw_config);
+
+ }
+
+ if (wss_base && wss_driver)
+ {
+ hw_config.io_base = wss_base;
+ hw_config.irq = irq;
+ hw_config.dma = dma1;
+ hw_config.dma2 = (dma2 == NO_DMA) ? dma1 : dma2;
+ hw_config.always_detect = 0;
+ hw_config.name = name;
+ hw_config.driver_use_1 = 0;
+ hw_config.driver_use_2 = 0;
+ hw_config.osp = sound_osp;
+ hw_config.card_subtype = 0;
+
+ sndtable_start_card (wss_driver, &hw_config);
+
+ ad1848_control (AD1848_SET_C930_PWD, master_ctl);
+ }
+ }
+}
+
+void
+opti82C924mpu_pnp (void *parm)
+{
+ struct pnp_dev *dev = (struct pnp_dev *) parm;
+ char *name;
+
+ int portmask = 0xff, irqmask = 0x01, dmamask = 0x03;
+ int mpu_driver;
+
+ if (pnp_trace)
+ printk ("OPTi 82C924/C925/C931 MPU driver waking up\n");
+
+ if (dev->card && dev->card->name)
+ name = dev->card->name;
+ else
+ name = "PnP MPU";
+
+ if ((mpu_driver = sndtable_identify_card ("UART401")))
+ portmask |= 0x01; /* MPU401 */
+ else
+ printk ("Sound: PnP MPU device detected but no driver enabled\n");
+
+ /* printk ("MPU driver %d\n", mpu_driver); */
+
+ if (!portmask) /* No drivers available */
+ return;
+
+ if (!pnp_allocate_device (pnp_sig, dev, portmask, irqmask, dmamask, 0x00))
+ printk ("sound_pnp: Failed to find free resources\n");
+ else
+ {
+ struct address_info hw_config;
+ int mpu_base;
+ int irq;
+
+ if (pnp_trace)
+ printk ("Device activation OK\n");
+ mpu_base = pnp_get_port (dev, 0);
+ irq = pnp_get_irq (dev, 0);
+
+ pnp_delay (1000000);
+
+ if (pnp_trace)
+ {
+ printk ("I/O %03x\n", mpu_base);
+ printk ("IRQ %d\n", irq);
+ }
+
+ if (mpu_base && mpu_driver)
+ {
+ hw_config.io_base = mpu_base;
+ hw_config.irq = irq;
+ hw_config.dma = -1;
+ hw_config.dma2 = -1;
+ hw_config.always_detect = 0;
+ hw_config.name = name;
+ hw_config.driver_use_1 = 0;
+ hw_config.driver_use_2 = 0;
+ hw_config.osp = sound_osp;
+ hw_config.card_subtype = 0;
+
+ sndtable_start_card (mpu_driver, &hw_config);
+
+ }
+ }
+}
+
+void
+cs4236mpu_pnp (void *parm)
+{
+ struct pnp_dev *dev = (struct pnp_dev *) parm;
+ char *name;
+
+ int portmask = 0xff, irqmask = 0x01, dmamask = 0x03;
+ int mpu_driver;
+
+ if (dev->card->key == (PNP_DEVID ('C', 'S', 'C', 0x4232))) /* CS4232 */
+ return;
+
+ if (pnp_trace)
+ printk ("CS4236 MPU driver waking up\n");
+
+ if (dev->card && dev->card->name)
+ name = dev->card->name;
+ else
+ name = "PnP MPU";
+
+ if ((mpu_driver = sndtable_identify_card ("UART401")))
+ portmask |= 0x01; /* MPU401 */
+ else
+ printk ("Sound: CS4236 PnP MPU device detected but no driver enabled\n");
+
+ /* printk ("MPU driver %d\n", mpu_driver); */
+
+ if (!portmask) /* No drivers available */
+ return;
+
+ if (!pnp_allocate_device (pnp_sig, dev, portmask, irqmask, dmamask, 0x00))
+ printk ("sound_pnp: Failed to find free resources\n");
+ else
+ {
+ struct address_info hw_config;
+ int mpu_base;
+ int irq;
+
+ if (pnp_trace)
+ printk ("Device activation OK\n");
+ mpu_base = pnp_get_port (dev, 0);
+ irq = pnp_get_irq (dev, 0);
+
+ pnp_delay (1500000);
+
+ if (pnp_trace)
+ {
+ printk ("I/O %03x\n", mpu_base);
+ printk ("IRQ %d\n", irq);
+ }
+
+ if (mpu_base && mpu_driver)
+ {
+ hw_config.io_base = mpu_base;
+ hw_config.irq = irq;
+ hw_config.dma = -1;
+ hw_config.dma2 = -1;
+ hw_config.always_detect = 0;
+ hw_config.name = name;
+ hw_config.driver_use_1 = 0;
+ hw_config.driver_use_2 = 0;
+ hw_config.osp = sound_osp;
+ hw_config.card_subtype = 0;
+
+ sndtable_start_card (mpu_driver, &hw_config);
+
+ }
+ }
+}
+
+void
+soundscape_pnp (void *parm)
+{
+ struct pnp_dev *dev = (struct pnp_dev *) parm;
+ char *name;
+
+ int portmask = 0xff, irqmask = 0x03, dmamask = 0x01;
+ int sscape_driver, wss_driver;
+
+ if (pnp_trace)
+ printk ("Soundscape PnP driver waking up\n");
+
+ if (dev->card && dev->card->name)
+ name = dev->card->name;
+ else
+ name = "SoundScape PnP";
+
+ if ((sscape_driver = sndtable_identify_card ("SSCAPE")))
+ portmask |= 0x01; /* MPU401 */
+ else
+ printk ("Sound: Soundscape PnP device detected but no driver enabled\n");
+
+ /* printk ("Soundscape driver %d\n", sscape_driver); */
+
+ if ((wss_driver = sndtable_identify_card ("SSCAPEMSS")))
+ portmask |= 0x01;
+ else
+ printk ("Sound: Soundscape codec device detected but no driver enabled\n");
+
+ if (!portmask) /* No drivers available */
+ return;
+
+ if (!pnp_allocate_device (pnp_sig, dev, portmask, irqmask, dmamask, 0x00))
+ printk ("sound_pnp: Failed to find free resources\n");
+ else
+ {
+ struct address_info hw_config;
+ int sscape_base;
+ int irq, irq2, dma, dma2;
+
+ if (pnp_trace)
+ printk ("Device activation OK\n");
+ sscape_base = pnp_get_port (dev, 0);
+ irq = pnp_get_irq (dev, 0);
+ irq2 = pnp_get_irq (dev, 1);
+ dma = pnp_get_dma (dev, 0);
+ dma2 = pnp_get_dma (dev, 1);
+
+ pnp_delay (1000000);
+
+ if (pnp_trace)
+ {
+ printk ("I/O %03x\n", sscape_base);
+ printk ("IRQ %d\n", irq);
+ printk ("IRQ2 %d\n", irq2);
+ printk ("DMA %d\n", dma);
+ printk ("DMA2 %d\n", dma2);
+ }
+
+ if (sscape_base && sscape_driver)
+ {
+ hw_config.io_base = sscape_base;
+ hw_config.irq = irq;
+ hw_config.dma = dma;
+ hw_config.dma2 = dma2;
+ hw_config.always_detect = 0;
+ hw_config.name = name;
+ hw_config.driver_use_1 = 0x12345678;
+ hw_config.driver_use_2 = 0;
+ hw_config.osp = sound_osp;
+ hw_config.card_subtype = 0;
+
+ sndtable_start_card (sscape_driver, &hw_config);
+ }
+
+ if (sscape_base && wss_driver)
+ {
+ hw_config.io_base = sscape_base + 8; /* The codec base */
+ hw_config.irq = irq2;
+ hw_config.dma = dma;
+ hw_config.dma2 = -1;
+ hw_config.always_detect = 0;
+ hw_config.name = name;
+ hw_config.driver_use_1 = 0;
+ hw_config.driver_use_2 = 0;
+ hw_config.osp = sound_osp;
+ hw_config.card_subtype = 0;
+
+ sndtable_start_card (wss_driver, &hw_config);
+ ad1848_control (AD1848_SET_XTAL, 1); /* 14.3 MHz is used */
+ }
+ }
+}
+
+void
+soundscape_vivo (void *parm)
+{
+ struct pnp_dev *dev = (struct pnp_dev *) parm;
+ char *name;
+
+ int portmask = 0x07, irqmask = 0x03, dmamask = 0x03;
+ int mpu_driver, wss_driver, vivo_driver;
+ int is_vivo_classic = 0;
+
+ if (pnp_trace)
+ printk ("Soundscape VIVO driver waking up\n");
+
+ if (dev->card->key == (PNP_DEVID ('E', 'N', 'S', 0x4080)))
+ is_vivo_classic = 1;
+
+ if (dev->card && dev->card->name)
+ name = dev->card->name;
+ else
+ name = "SoundScape VIVO";
+
+ if ((mpu_driver = sndtable_identify_card ("UART401")))
+ portmask |= 0x01; /* MPU401 */
+
+ /* printk ("MPU driver %d\n", mpu_driver); */
+
+ if ((wss_driver = sndtable_identify_card ("AD1848")))
+ portmask |= 0x02;
+ else
+ printk ("Sound: Soundscape codec device detected but no driver enabled\n");
+
+ if ((vivo_driver = sndtable_identify_card ("VIVO")))
+ portmask |= 0x07;
+ else
+ printk ("Sound: Soundscape VIVO/OTTO device detected but no driver installed\n");
+
+ if (!portmask) /* No drivers available */
+ return;
+
+ if (!pnp_allocate_device (pnp_sig, dev, portmask, irqmask, dmamask, 0x00))
+ printk ("sound_pnp: Failed to find free resources\n");
+ else
+ {
+ struct address_info hw_config;
+ int mpu_base, mss_base, otto_base;
+ int irq, irq2, dma, dma2;
+
+ if (pnp_trace)
+ printk ("Device activation OK\n");
+ mpu_base = pnp_get_port (dev, 0);
+ mss_base = pnp_get_port (dev, 1);
+ otto_base = pnp_get_port (dev, 2);
+ irq = pnp_get_irq (dev, 0);
+ irq2 = pnp_get_irq (dev, 1);
+ dma = pnp_get_dma (dev, 0);
+ dma2 = pnp_get_dma (dev, 1);
+ if (dma2 == NO_DMA)
+ dma2 = dma;
+
+ if (pnp_trace)
+ {
+ printk ("I/O %03x\n", mpu_base);
+ printk ("MSS I/O %03x\n", mss_base);
+ printk ("OTTO I/O %03x\n", otto_base);
+ printk ("IRQ %d\n", irq);
+ printk ("IRQ2 %d\n", irq2);
+ printk ("DMA %d\n", dma);
+ printk ("DMA2 %d\n", dma2);
+ }
+
+
+ if (mss_base && wss_driver)
+ {
+ hw_config.io_base = mss_base + 4; /* The codec base */
+ hw_config.irq = irq;
+ hw_config.dma = dma;
+ hw_config.dma2 = dma2;
+ hw_config.always_detect = 0;
+ hw_config.name = name;
+ hw_config.driver_use_1 = 0;
+ hw_config.driver_use_2 = 0;
+ hw_config.osp = sound_osp;
+ hw_config.card_subtype = 0;
+
+ sndtable_start_card (wss_driver, &hw_config);
+ }
+
+ if (otto_base && vivo_driver)
+ {
+ hw_config.io_base = otto_base;
+ hw_config.irq = irq2;
+ hw_config.dma = -1;
+ hw_config.dma2 = -1;
+ hw_config.always_detect = 0;
+ hw_config.name = name;
+ hw_config.driver_use_1 = mpu_base;
+ hw_config.driver_use_2 = 0;
+ hw_config.osp = sound_osp;
+ hw_config.card_subtype = 0;
+
+ sndtable_start_card (vivo_driver, &hw_config);
+
+ if (is_vivo_classic)
+ {
+ /*
+ * The original VIVO uses XCTL0 pin of AD1845 as a synth (un)mute bit. Turn it
+ * on _after_ the synth is initialized. Btw, XCTL1 controls 30 dB mic boost
+ * circuit.
+ */
+
+ ad1848_control (AD1848_SET_XCTL0, 1); /* Unmute */
+ }
+ AD1848_REROUTE (SOUND_MIXER_LINE1, SOUND_MIXER_SYNTH); /* AUX1 is OTTO input */
+ AD1848_REROUTE (SOUND_MIXER_LINE3, SOUND_MIXER_LINE); /* Line in */
+
+ }
+ }
+}
+
+void
+gus_pnp (void *parm)
+{
+ struct pnp_dev *dev = (struct pnp_dev *) parm;
+ char *name;
+
+ int portmask = 0x00, irqmask = 0x01, dmamask = 0x03;
+ int gus_driver, wss_driver;
+
+ if (pnp_trace)
+ printk ("GUS PnP driver waking up\n");
+
+ if (dev->card && dev->card->name)
+ name = dev->card->name;
+ else
+ name = "Ultrasound PnP";
+
+ if ((gus_driver = sndtable_identify_card ("GUSPNP")))
+ portmask |= 0x07;
+ else
+ printk ("Sound: GUS PnP device detected but no driver enabled\n");
+
+ if ((wss_driver = sndtable_identify_card ("AD1848")))
+ portmask |= 0x01; /* MAX */
+ else
+ printk ("Sound: GUS PnP codec device detected but no driver enabled\n");
+
+ if (!portmask) /* No drivers available */
+ return;
+
+ if (!pnp_allocate_device (pnp_sig, dev, portmask, irqmask, dmamask, 0x00))
+ printk ("sound_pnp: Failed to find free resources\n");
+ else
+ {
+ struct address_info hw_config;
+ int gus_base;
+ int irq;
+ int dma1, dma2;
+
+ gus_base = pnp_get_port (dev, 0);
+
+ irq = pnp_get_irq (dev, 0);
+ dma1 = pnp_get_dma (dev, 0);
+ dma2 = pnp_get_dma (dev, 1);
+
+ if (pnp_trace)
+ printk ("Device activation OK (P%x I%d D%d d%d)\n",
+ gus_base, irq, dma1, dma2);
+
+ if (gus_base && gus_driver)
+ {
+
+ hw_config.io_base = gus_base;
+ hw_config.irq = irq;
+ hw_config.dma = dma1;
+ hw_config.dma2 = (dma2 == NO_DMA) ? dma1 : dma2;
+ hw_config.always_detect = 0;
+ hw_config.name = name;
+ hw_config.driver_use_1 = 0;
+ hw_config.driver_use_2 = 0;
+ hw_config.osp = sound_osp;
+ hw_config.card_subtype = 0;
+
+ sndtable_start_card (gus_driver, &hw_config);
+ }
+ }
+}
+
+void
+sb_pnp (void *parm)
+{
+ struct pnp_dev *dev = (struct pnp_dev *) parm;
+ char *name;
+
+ int portmask = 0x00, irqmask = 0x01, dmamask = 0x03;
+ int sb_driver, mpu_driver, opl3_driver;
+
+ if (pnp_trace)
+ printk ("SB PnP driver waking up\n");
+ pnp_delay (1000000);
+
+ if (dev->card && dev->card->name)
+ name = dev->card->name;
+ else
+ name = "SoundBlaster PnP";
+
+ if ((sb_driver = sndtable_identify_card ("SBPNP")))
+ portmask |= 0x01;
+ else
+ printk ("Sound: SB PnP device detected but no driver enabled\n");
+
+ if ((mpu_driver = sndtable_identify_card ("SBMPU")))
+ portmask |= 0x02;
+ else
+ printk ("Sound: SB PnP device detected but SB MPU driver not enabled\n");
+
+ if ((opl3_driver = sndtable_identify_card ("OPL3")))
+ portmask |= 0x04;
+ else
+ printk ("Sound: SB PnP device detected but OPL3 driver not enabled\n");
+
+ if (!portmask) /* No drivers available */
+ return;
+
+ if (!pnp_allocate_device (pnp_sig, dev, portmask, irqmask, dmamask, 0x00))
+ printk ("sound_pnp: Failed to find free resources\n");
+ else
+ {
+ struct address_info hw_config;
+ int sb_base, mpu_base, opl3_base;
+ int irq;
+ int dma1, dma2;
+
+ if (pnp_trace)
+ printk ("Device activation OK\n");
+ sb_base = pnp_get_port (dev, 0);
+ mpu_base = pnp_get_port (dev, 1);
+ opl3_base = pnp_get_port (dev, 2);
+
+ irq = pnp_get_irq (dev, 0);
+ dma1 = pnp_get_dma (dev, 0);
+ dma2 = pnp_get_dma (dev, 1);
+
+ if (sb_base && sb_driver)
+ {
+ hw_config.io_base = sb_base;
+ hw_config.irq = irq;
+ hw_config.dma = dma1;
+ hw_config.dma2 = (dma2 == NO_DMA) ? dma1 : dma2;
+ hw_config.always_detect = 0;
+ hw_config.name = name;
+ hw_config.driver_use_1 = 0;
+ hw_config.driver_use_2 = 0;
+ hw_config.osp = sound_osp;
+ hw_config.card_subtype = 0;
+
+ sndtable_start_card (sb_driver, &hw_config);
+ }
+
+ if (opl3_base && opl3_driver)
+ {
+ hw_config.io_base = opl3_base;
+ hw_config.irq = 0;
+ hw_config.dma = -1;
+ hw_config.dma2 = -1;
+ hw_config.always_detect = 0;
+ hw_config.name = "";
+ hw_config.driver_use_1 = 0;
+ hw_config.driver_use_2 = 0;
+ hw_config.osp = sound_osp;
+ hw_config.card_subtype = 0;
+
+ sndtable_start_card (opl3_driver, &hw_config);
+
+ }
+
+ if (mpu_base && mpu_driver)
+ {
+ hw_config.io_base = mpu_base;
+ hw_config.irq = irq;
+ hw_config.dma = -1;
+ hw_config.dma2 = -1;
+ hw_config.always_detect = 0;
+ hw_config.name = "";
+ hw_config.driver_use_1 = 0;
+ hw_config.driver_use_2 = 0;
+ hw_config.osp = sound_osp;
+ hw_config.card_subtype = 0;
+
+ sndtable_start_card (mpu_driver, &hw_config);
+
+ }
+ }
+}
+
+void
+als_pnp (void *parm)
+{
+ struct pnp_dev *dev = (struct pnp_dev *) parm;
+ char *name;
+
+ int portmask = 0x00, irqmask = 0x01, dmamask = 0x03;
+ int sb_driver;
+
+ if (pnp_trace)
+ printk ("ALS### PnP driver waking up\n");
+
+ if (dev->card && dev->card->name)
+ name = dev->card->name;
+ else
+ name = "SB16 clone";
+
+ if ((sb_driver = sndtable_identify_card ("SBPNP")))
+ portmask |= 0x01;
+ else
+ printk ("Sound: ALS PnP device detected but no driver enabled\n");
+
+ if (!portmask) /* No drivers available */
+ return;
+
+ if (!pnp_allocate_device (pnp_sig, dev, portmask, irqmask, dmamask, 0x00))
+ printk ("sound_pnp: Failed to find free resources\n");
+ else
+ {
+ struct address_info hw_config;
+ int sb_base;
+ int irq;
+ int dma1, dma2;
+
+ if (pnp_trace)
+ printk ("Device activation OK\n");
+ sb_base = pnp_get_port (dev, 0);
+
+ irq = pnp_get_irq (dev, 0);
+ dma1 = pnp_get_dma (dev, 1);
+ dma2 = pnp_get_dma (dev, 0);
+
+ if (sb_base && sb_driver)
+ {
+ hw_config.io_base = sb_base;
+ hw_config.irq = irq;
+ hw_config.dma = dma1;
+ hw_config.dma2 = dma2;
+ hw_config.always_detect = 0;
+ hw_config.name = name;
+ hw_config.driver_use_1 = 0;
+ hw_config.driver_use_2 = 0;
+ hw_config.osp = sound_osp;
+ hw_config.card_subtype = 0;
+
+ sndtable_start_card (sb_driver, &hw_config);
+ }
+ }
+}
+
+void
+als_pnp_mpu (void *parm)
+{
+ struct pnp_dev *dev = (struct pnp_dev *) parm;
+ char *name;
+
+ int portmask = 0x00, irqmask = 0x01, dmamask = 0x03;
+ int mpu_driver;
+
+ if (pnp_trace)
+ printk ("ALS### PnP MPU driver waking up\n");
+
+ if (dev->card && dev->card->name)
+ name = dev->card->name;
+ else
+ name = "SB16 clone";
+
+ if ((mpu_driver = sndtable_identify_card ("UART401")))
+ portmask |= 0x01;
+ else
+ printk ("Sound: ALS PnP device detected but no MPU driver enabled\n");
+
+ if (!portmask) /* No drivers available */
+ return;
+
+ if (!pnp_allocate_device (pnp_sig, dev, portmask, irqmask, dmamask, 0x00))
+ printk ("sound_pnp: Failed to find free resources\n");
+ else
+ {
+ struct address_info hw_config;
+ int mpu_base;
+ int irq;
+
+ if (pnp_trace)
+ printk ("Device activation OK\n");
+ mpu_base = pnp_get_port (dev, 0);
+
+ irq = pnp_get_irq (dev, 0);
+
+ if (mpu_base && mpu_driver)
+ {
+ hw_config.io_base = mpu_base;
+ hw_config.irq = irq;
+ hw_config.dma = -1;
+ hw_config.dma2 = -1;
+ hw_config.always_detect = 0;
+ hw_config.name = name;
+ hw_config.driver_use_1 = 0;
+ hw_config.driver_use_2 = 0;
+ hw_config.osp = sound_osp;
+ hw_config.card_subtype = 0;
+
+ sndtable_start_card (mpu_driver, &hw_config);
+ }
+ }
+}
+
+void
+als_pnp_opl (void *parm)
+{
+ struct pnp_dev *dev = (struct pnp_dev *) parm;
+ char *name;
+
+ int portmask = 0x00, irqmask = 0x01, dmamask = 0x03;
+ int opl3_driver;
+
+ if (pnp_trace)
+ printk ("ALS### PnP OPL3 driver waking up\n");
+
+ if (dev->card && dev->card->name)
+ name = dev->card->name;
+ else
+ name = "SB16 clone";
+
+ if ((opl3_driver = sndtable_identify_card ("OPL3")))
+ portmask |= 0x01;
+ else
+ printk ("Sound: ALS PnP device detected but no OPL3 driver enabled\n");
+
+ if (!portmask) /* No drivers available */
+ return;
+
+ if (!pnp_allocate_device (pnp_sig, dev, portmask, irqmask, dmamask, 0x00))
+ printk ("sound_pnp: Failed to find free resources\n");
+ else
+ {
+ struct address_info hw_config;
+ int opl3_base;
+ int irq;
+
+ if (pnp_trace)
+ printk ("Device activation OK\n");
+ opl3_base = pnp_get_port (dev, 0);
+
+ irq = pnp_get_irq (dev, 0);
+
+ if (opl3_base && opl3_driver)
+ {
+ hw_config.io_base = opl3_base;
+ hw_config.irq = 0;
+ hw_config.dma = -1;
+ hw_config.dma2 = -1;
+ hw_config.always_detect = 0;
+ hw_config.name = name;
+ hw_config.driver_use_1 = 0;
+ hw_config.driver_use_2 = 0;
+ hw_config.osp = sound_osp;
+ hw_config.card_subtype = 0;
+
+ sndtable_start_card (opl3_driver, &hw_config);
+ }
+ }
+}
+
+void
+ess_pnp (void *parm)
+{
+ struct pnp_dev *dev = (struct pnp_dev *) parm;
+ char *name;
+
+ int portmask = 0x03, irqmask = 0x01, dmamask = 0x03;
+ int sb_driver, mpu_driver, opl3_driver;
+
+ if (pnp_trace)
+ printk ("ESS PnP driver waking up\n");
+
+ if (pnp_trace)
+ {
+ printk ("ESS1868: IRQB,IRQA = %x\n", pnp_readreg (dev, 0x20));
+ printk ("ESS1868: IRQD,IRQC = %x\n", pnp_readreg (dev, 0x21));
+ printk ("ESS1868: IRQF,IRQE = %x\n", pnp_readreg (dev, 0x22));
+ printk ("ESS1868: DRQB,DRQA = %x\n", pnp_readreg (dev, 0x23));
+ printk ("ESS1868: DRQD,DRQC = %x\n", pnp_readreg (dev, 0x24));
+ printk ("ESS1868: Configuration ROM Header 0 = %x\n", pnp_readreg (dev, 0x25));
+ printk ("ESS1868: Configuration ROM Header 1 = %x\n", pnp_readreg (dev, 0x26));
+ printk ("ESS1868: HW Volume IRQ = %x\n", pnp_readreg (dev, 0x27));
+ printk ("ESS1868: MPU401 IRQ = %x\n", pnp_readreg (dev, 0x28));
+ }
+
+ if (pnp_readreg (dev, 0x27) & 0x01) /* MPU401 is at logical device #3 */
+ printk ("Nonstandard ESS1868 implementation - contact support@4front-tech.com\n");
+
+ if (dev->card && dev->card->name)
+ name = dev->card->name;
+ else
+ name = "ESS AudioDrive PnP";
+
+ if ((sb_driver = sndtable_identify_card ("SBLAST")))
+ portmask |= 0x01;
+ else
+ printk ("Sound: SB PnP device detected but no driver enabled\n");
+
+ if ((mpu_driver = sndtable_identify_card ("SBMPU")))
+ portmask |= 0x02;
+ else
+ printk ("Sound: SB PnP device detected but SB MPU driver not enabled\n");
+
+ if ((opl3_driver = sndtable_identify_card ("OPL3")))
+ portmask |= 0x04;
+ else
+ printk ("Sound: SB PnP device detected but OPL3 driver not enabled\n");
+
+ if (!portmask) /* No drivers available */
+ return;
+
+ if (!pnp_allocate_device (pnp_sig, dev, portmask, irqmask, dmamask, 0x00))
+ printk ("sound_pnp: Failed to find free resources\n");
+ else
+ {
+ struct address_info hw_config;
+ int sb_base, mpu_base, opl3_base;
+ int irq;
+ int dma1, dma2;
+
+ if (pnp_trace)
+ printk ("Device activation OK\n");
+ sb_base = pnp_get_port (dev, 0);
+ opl3_base = pnp_get_port (dev, 1);
+ mpu_base = pnp_get_port (dev, 2);
+
+ irq = pnp_get_irq (dev, 0);
+ dma1 = pnp_get_dma (dev, 0);
+ /* dma2 = pnp_get_dma (dev, 1); */ dma2 = -1;
+
+ if (pnp_trace)
+ {
+ printk ("ESS PnP at %x/%x/%x, %d, %d/%d\n",
+ sb_base, mpu_base, opl3_base,
+ irq, dma1, dma2);
+ }
+
+ if (sb_base && sb_driver)
+ {
+ hw_config.io_base = sb_base;
+ hw_config.irq = irq;
+ hw_config.dma = dma1;
+ hw_config.dma2 = (dma2 == NO_DMA) ? dma1 : dma2;
+ hw_config.always_detect = 0;
+ hw_config.name = name;
+ hw_config.driver_use_1 = 0;
+ hw_config.driver_use_2 = 0;
+ hw_config.osp = NULL;
+ hw_config.card_subtype = 0;
+
+ sndtable_start_card (sb_driver, &hw_config);
+ }
+
+ if (opl3_base && opl3_driver)
+ {
+ hw_config.io_base = opl3_base;
+ hw_config.irq = 0;
+ hw_config.dma = -1;
+ hw_config.dma2 = -1;
+ hw_config.always_detect = 0;
+ hw_config.name = "";
+ hw_config.driver_use_1 = 0;
+ hw_config.driver_use_2 = 0;
+ hw_config.osp = NULL;
+ hw_config.card_subtype = 0;
+
+ sndtable_start_card (opl3_driver, &hw_config);
+
+ }
+
+ if (mpu_base && mpu_driver)
+ {
+ hw_config.io_base = mpu_base;
+ hw_config.irq = -irq;
+ hw_config.dma = -1;
+ hw_config.dma2 = -1;
+ hw_config.always_detect = 0;
+ hw_config.name = "";
+ hw_config.driver_use_1 = 0;
+ hw_config.driver_use_2 = 0;
+ hw_config.osp = NULL;
+ hw_config.card_subtype = 0;
+
+ sndtable_start_card (mpu_driver, &hw_config);
+
+ }
+ }
+}
+
+static struct pnp_sounddev pnp_devs[] =
+{
+ {PNP_DEVID ('C', 'S', 'C', 0x0000), cs4232_pnp, "CS4232"},
+ {PNP_DEVID ('C', 'S', 'C', 0x0003), cs4236mpu_pnp, "CS4236MPU"},
+ {PNP_DEVID ('G', 'R', 'V', 0x0000), gus_pnp, "GUS"},
+ {PNP_DEVID ('R', 'V', 'L', 0x0010), gus_pnp, "WAVXTREME"},
+ {PNP_DEVID ('A', 'D', 'V', 0x0010), gus_pnp, "IWAVE"},
+ {PNP_DEVID ('D', 'X', 'P', 0x0010), gus_pnp, "IWAVE"},
+ {PNP_DEVID ('Y', 'M', 'H', 0x0021), opl3sa2_pnp, "OPL3SA2"},
+ {PNP_DEVID ('O', 'P', 'T', 0x0000), opti82C924_pnp, "82C924"},
+ {PNP_DEVID ('O', 'P', 'T', 0x9250), opti82C924_pnp, "82C925"},
+ {PNP_DEVID ('O', 'P', 'T', 0x9310), opti82C931_pnp, "82C931"},
+ {PNP_DEVID ('O', 'P', 'T', 0x0002), opti82C924mpu_pnp, "82C924MPU"},
+ {PNP_DEVID ('E', 'N', 'S', 0x0000), soundscape_pnp, "SSCAPE"},
+ {PNP_DEVID ('N', 'E', 'C', 0x0000), soundscape_pnp, "NEC"},
+ {PNP_DEVID ('E', 'N', 'S', 0x1010), soundscape_vivo, "SSCAPE"},
+ {PNP_DEVID ('E', 'N', 'S', 0x1011), soundscape_vivo, "SSCAPE"},
+ {PNP_DEVID ('C', 'T', 'L', 0x0031), sb_pnp, "SB"},
+ {PNP_DEVID ('C', 'T', 'L', 0x0001), sb_pnp, "SB"},
+ {PNP_DEVID ('C', 'T', 'L', 0x0041), sb_pnp, "SB"}, /* SB32 (new revision) */
+ {PNP_DEVID ('C', 'T', 'L', 0x0042), sb_pnp, "SB"}, /* SB64 */
+ {PNP_DEVID ('C', 'T', 'L', 0x0044), sb_pnp, "SB"}, /* SB64 Gold */
+ {PNP_DEVID ('@', '@', '@', 0x0001), als_pnp, "SB"},
+ {PNP_DEVID ('@', 'X', '@', 0x0001), als_pnp_mpu, "SB"},
+ {PNP_DEVID ('@', 'H', '@', 0x0001), als_pnp_opl, "SB"},
+ {PNP_DEVID ('E', 'S', 'S', 0x1868), ess_pnp, "ESS"}
+};
+
+static int nr_pnpdevs = sizeof (pnp_devs) / sizeof (struct pnp_sounddev);
+
+static int
+pnp_activate (int id, struct pnp_dev *dev)
+{
+ int i;
+
+ for (i = 0; i < nr_pnpdevs; i++)
+ if (pnp_devs[i].id == id)
+ {
+
+ if (pnp_trace)
+ printk ("PnP dev: %08x, %s\n", id,
+ pnp_devid2asc (id));
+
+ pnp_devs[i].setup ((void *) dev);
+ return 1;
+ }
+
+ return 0;
+}
+
+void
+cs423x_special (struct pnp_dev *dev)
+{
+}
+
+void
+sound_pnp_init (int *osp)
+{
+
+ struct pnp_dev *dev;
+
+ if (pnp_sig == 0)
+ init_pnp (0, osp);
+
+ if (pnp_sig == 0)
+ if ((pnp_sig = pnp_connect ("sound")) == -1)
+ {
+ printk ("Sound: Can't connect to kernel PnP services.\n");
+ return;
+ }
+
+/*
+ * First handle some special configuration ports.
+ */
+ dev = NULL;
+ while ((dev = pnp_get_next_device (pnp_sig, dev)) != NULL)
+ {
+ int i;
+
+ for (i = 0; special_devices[i] != 0; i++)
+ if (special_devices[i] == dev->key)
+ switch (i)
+ {
+ case 0:
+ case 1:
+ cs423x_special (dev);
+ break;
+ }
+ }
+
+/*
+ * Next disable some unused sound devices so that they don't consume
+ * valuable IRQ and DMA resources.
+ */
+ dev = NULL;
+ while ((dev = pnp_get_next_device (pnp_sig, dev)) != NULL)
+ {
+ int i;
+
+ for (i = 0; disabled_devices[i] != 0; i++)
+ if (disabled_devices[i] == dev->key)
+ pnp_enable_device (dev, 0); /* Disable it */
+ }
+
+/*
+ * Then initialize drivers for known PnP devices.
+ */
+ dev = NULL;
+ while ((dev = pnp_get_next_device (pnp_sig, dev)) != NULL)
+ {
+ if (!pnp_activate (dev->key, dev))
+ {
+ /* Scan all compatible devices */
+
+ int i;
+
+ for (i = 0; i < dev->ncompat; i++)
+ if (pnp_activate (dev->compat_keys[i], dev))
+ break;
+ }
+ }
+}
+
+void
+sound_pnp_disconnect (void)
+{
+ pnp_disconnect (pnp_sig);
+}
+
+
+#endif