diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/sound/dmasound.c | 3435 | ||||
-rw-r--r-- | drivers/sound/dmasound.h | 36 | ||||
-rw-r--r-- | drivers/sound/sound_pnp.c | 1513 |
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 |