diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-04-19 04:00:00 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-04-19 04:00:00 +0000 |
commit | 46e045034336a2cc90c1798cd7cc07af744ddfd6 (patch) | |
tree | 3b9b51fc482e729f663d25333e77fbed9aaa939a /drivers/sound | |
parent | 31dc59d503a02e84c4de98826452acaeb56dc15a (diff) |
Merge with Linux 2.3.99-pre4.
Diffstat (limited to 'drivers/sound')
27 files changed, 6726 insertions, 6080 deletions
diff --git a/drivers/sound/Makefile b/drivers/sound/Makefile index 046379d37..876daf485 100644 --- a/drivers/sound/Makefile +++ b/drivers/sound/Makefile @@ -35,7 +35,6 @@ obj- := # Each configuration option enables a list of files. obj-$(CONFIG_SOUND) += soundcore.o -obj-$(CONFIG_DMASOUND) += dmasound.o obj-$(CONFIG_SOUND_OSS) += sound.o obj-$(CONFIG_SOUND_CS4232) += cs4232.o ad1848.o @@ -81,6 +80,17 @@ obj-$(CONFIG_SOUND_ESSSOLO1) += esssolo1.o obj-$(CONFIG_SOUND_MAESTRO) += maestro.o obj-$(CONFIG_SOUND_TRIDENT) += trident.o ac97_codec.o +ifeq ($(CONFIG_DMASOUND),y) + SUB_DIRS += dmasound + MOD_SUB_DIRS += dmasound + obj-y += dmasound/dmasound.o +else + ifeq ($(CONFIG_DMASOUND),m) + MOD_SUB_DIRS += dmasound + endif +endif + + # Declare multi-part drivers. list-multi := sound.o gus.o pas2.o sb.o sb_lib.o softoss2.o vidc_mod.o \ @@ -138,10 +148,6 @@ MX_OBJS := $(sort $(filter $(export-objs), $(obj-m))) MI_OBJS := $(sort $(filter-out $(export-objs), $(int-m))) MIX_OBJS := $(sort $(filter $(export-objs), $(int-m))) -ifeq ($(CONFIG_LOWLEVEL_SOUND),y) - O_OBJS += lowlevel/lowlevel.o -endif - include $(TOPDIR)/Rules.make diff --git a/drivers/sound/ad1816.c b/drivers/sound/ad1816.c index e91d71909..bdab8a85b 100644 --- a/drivers/sound/ad1816.c +++ b/drivers/sound/ad1816.c @@ -1266,8 +1266,7 @@ static int __initdata dma = -1; static int __initdata dma2 = -1; #if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE -struct pci_dev *ad1816_dev = NULL, - *mpu_dev = NULL; +struct pci_dev *ad1816_dev = NULL; static int activated = 1; diff --git a/drivers/sound/aedsp16.c b/drivers/sound/aedsp16.c index 74b961521..6ea812a38 100644 --- a/drivers/sound/aedsp16.c +++ b/drivers/sound/aedsp16.c @@ -1383,6 +1383,7 @@ static int __init setup_aedsp16(char *str) mss_base = ints[4]; mpu_base = ints[5]; mpu_irq = ints[6]; + return 1; } __setup("aedsp16=", setup_aedsp16); diff --git a/drivers/sound/dev_table.c b/drivers/sound/dev_table.c index 0a1e2981c..4a1a39506 100644 --- a/drivers/sound/dev_table.c +++ b/drivers/sound/dev_table.c @@ -26,15 +26,13 @@ int sound_install_audiodrv(int vers, char *name, struct audio_driver *driver, struct audio_operations *op; int l, num; - if (vers != AUDIO_DRIVER_VERSION || driver_size > sizeof(struct audio_driver)) - { + if (vers != AUDIO_DRIVER_VERSION || driver_size > sizeof(struct audio_driver)) { printk(KERN_ERR "Sound: Incompatible audio driver for %s\n", name); return -(EINVAL); } num = sound_alloc_audiodev(); - if (num == -1) - { + if (num == -1) { printk(KERN_ERR "sound: Too many audio drivers\n"); return -(EBUSY); } @@ -47,8 +45,7 @@ int sound_install_audiodrv(int vers, char *name, struct audio_driver *driver, if (sound_nblocks < 1024) sound_nblocks++; - if (d == NULL || op == NULL) - { + if (d == NULL || op == NULL) { printk(KERN_ERR "Sound: Can't allocate driver for (%s)\n", name); sound_unload_audiodev(num); return -(ENOMEM); @@ -91,14 +88,12 @@ int sound_install_mixer(int vers, char *name, struct mixer_operations *driver, int n = sound_alloc_mixerdev(); - if (n == -1) - { + if (n == -1) { printk(KERN_ERR "Sound: Too many mixer drivers\n"); return -EBUSY; } if (vers != MIXER_DRIVER_VERSION || - driver_size > sizeof(struct mixer_operations)) - { + driver_size > sizeof(struct mixer_operations)) { printk(KERN_ERR "Sound: Incompatible mixer driver for %s\n", name); return -EINVAL; } @@ -110,8 +105,7 @@ int sound_install_mixer(int vers, char *name, struct mixer_operations *driver, if (sound_nblocks < 1024) sound_nblocks++; - if (op == NULL) - { + if (op == NULL) { printk(KERN_ERR "Sound: Can't allocate mixer driver for (%s)\n", name); return -ENOMEM; } @@ -131,8 +125,7 @@ int sound_install_mixer(int vers, char *name, struct mixer_operations *driver, void sound_unload_audiodev(int dev) { - if (dev != -1) - { + if (dev != -1) { DMAbuf_deinit(dev); audio_devs[dev] = NULL; unregister_sound_dsp((dev<<4)+3); @@ -165,10 +158,8 @@ int sound_alloc_synthdev(void) { int i; - for (i = 0; i < MAX_SYNTH_DEV; i++) - { - if (synth_devs[i] == NULL) - { + for (i = 0; i < MAX_SYNTH_DEV; i++) { + if (synth_devs[i] == NULL) { if (i >= num_synths) num_synths++; return i; @@ -192,10 +183,8 @@ int sound_alloc_timerdev(void) { int i; - for (i = 0; i < MAX_TIMER_DEV; i++) - { - if (sound_timer_devs[i] == NULL) - { + for (i = 0; i < MAX_TIMER_DEV; i++) { + if (sound_timer_devs[i] == NULL) { if (i >= num_sound_timers) num_sound_timers++; return i; @@ -206,8 +195,7 @@ int sound_alloc_timerdev(void) void sound_unload_mixerdev(int dev) { - if (dev != -1) - { + if (dev != -1) { mixer_devs[dev] = NULL; unregister_sound_mixer(dev<<4); num_mixers--; @@ -216,8 +204,7 @@ void sound_unload_mixerdev(int dev) void sound_unload_mididev(int dev) { - if (dev != -1) - { + if (dev != -1) { midi_devs[dev] = NULL; unregister_sound_midi((dev<<4)+2); } diff --git a/drivers/sound/dev_table.h b/drivers/sound/dev_table.h index a5525a4bf..4c71c1265 100644 --- a/drivers/sound/dev_table.h +++ b/drivers/sound/dev_table.h @@ -133,9 +133,6 @@ struct dma_buffparms char neutral_byte; int dma; /* DMA channel */ -#ifdef OS_DMA_PARMS - OS_DMA_PARMS -#endif int applic_profile; /* Application profile (APF_*) */ /* Interrupt callback stuff */ void (*audio_callback) (int dev, int parm); @@ -347,7 +344,6 @@ struct sound_timer_operations }; #ifdef _DEV_TABLE_C_ - struct audio_operations *audio_devs[MAX_AUDIO_DEV] = {NULL}; int num_audiodevs = 0; struct mixer_operations *mixer_devs[MAX_MIXER_DEV] = {NULL}; @@ -356,21 +352,13 @@ struct synth_operations *synth_devs[MAX_SYNTH_DEV+MAX_MIDI_DEV] = {NULL}; int num_synths = 0; struct midi_operations *midi_devs[MAX_MIDI_DEV] = {NULL}; int num_midis = 0; -#ifndef EXCLUDE_TIMERS + extern struct sound_timer_operations default_sound_timer; struct sound_timer_operations *sound_timer_devs[MAX_TIMER_DEV] = { &default_sound_timer, NULL }; int num_sound_timers = 1; #else -struct sound_timer_operations *sound_timer_devs[MAX_TIMER_DEV] = { - NULL -}; -int num_sound_timers = 0; -#endif - - -#else extern struct audio_operations *audio_devs[MAX_AUDIO_DEV]; extern int num_audiodevs; extern struct mixer_operations *mixer_devs[MAX_MIXER_DEV]; @@ -384,8 +372,6 @@ extern int num_sound_timers; #endif /* _DEV_TABLE_C_ */ extern int sound_map_buffer (int dev, struct dma_buffparms *dmap, buffmem_desc *info); -int sndtable_probe (int unit, struct address_info *hw_config); -int sndtable_start_card (int unit, struct address_info *hw_config); void sound_timer_init (struct sound_lowlev_timer *t, char *name); void sound_dma_intr (int dev, struct dma_buffparms *dmap, int chan); diff --git a/drivers/sound/dmasound.c b/drivers/sound/dmasound.c deleted file mode 100644 index b6298ca83..000000000 --- a/drivers/sound/dmasound.c +++ /dev/null @@ -1,5821 +0,0 @@ - -/* linux/drivers/sound/dmasound.c */ - -/* - -OSS/Free compatible Atari TT/Falcon and Amiga DMA sound driver for Linux/m68k -Extended to support Power Macintosh for Linux/ppc by Paul Mackerras - -(c) 1995 by Michael Schlueter & Michael Marte - -Michael Schlueter (michael@duck.syd.de) did the basic structure of the VFS -interface and the u-law to signed byte conversion. - -Michael Marte (marte@informatik.uni-muenchen.de) did the sound queue, -/dev/mixer, /dev/sndstat and complemented the VFS interface. He would like -to thank: -Michael Schlueter for initial ideas and documentation on the MFP and -the DMA sound hardware. -Therapy? for their CD 'Troublegum' which really made me rock. - -/dev/sndstat is based on code by Hannu Savolainen, the author of the -VoxWare family of drivers. - -This file is subject to the terms and conditions of the GNU General Public -License. See the file COPYING in the main directory of this archive -for more details. - -History: -1995/8/25 first release - -1995/9/02 ++roman: 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 - -1998-06-10 ++andreas: converted to use sound_core - -*/ - - -#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 <linux/sound.h> -#include <linux/init.h> -#include <linux/delay.h> - -#if defined(__mc68000__) || defined(CONFIG_APUS) -#include <asm/setup.h> -#endif -#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> -#include <asm/atari_stram.h> -#endif /* CONFIG_ATARI */ -#ifdef CONFIG_AMIGA -#include <asm/amigahw.h> -#include <asm/amigaints.h> -#endif /* CONFIG_AMIGA */ -#ifdef CONFIG_PPC -#include <linux/adb.h> -#include <linux/cuda.h> -#include <linux/pmu.h> -#include <asm/prom.h> -#include <asm/machdep.h> -#include <asm/io.h> -#include <asm/dbdma.h> -#include <asm/feature.h> -#include "awacs_defs.h" -#include <linux/nvram.h> -#include <linux/vt_kern.h> -#endif /* CONFIG_PPC */ - -#include "dmasound.h" -#include <linux/soundcard.h> - -#define HAS_8BIT_TABLES - -#ifdef MODULE -static int sq_unit = -1; -static int mixer_unit = -1; -static int state_unit = -1; -static int irq_installed = 0; -#endif /* MODULE */ -static char **sound_buffers = NULL; -#ifdef CONFIG_PPC -static char **sound_read_buffers = NULL; -#endif - -#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 */ - -#ifdef CONFIG_PPC -/* - * Interrupt numbers and addresses, obtained from the device tree. - */ -static int awacs_irq, awacs_tx_irq, awacs_rx_irq; -static volatile struct awacs_regs *awacs; -static volatile struct dbdma_regs *awacs_txdma, *awacs_rxdma; -static int awacs_rate_index; -static int awacs_subframe; -static int awacs_spkr_vol; -static struct device_node* awacs_node; - -static int awacs_revision; -#define AWACS_BURGUNDY 100 /* fake revision # for burgundy */ - -/* - * Space for the DBDMA command blocks. - */ -static void *awacs_tx_cmd_space; -static volatile struct dbdma_cmd *awacs_tx_cmds; - -static void *awacs_rx_cmd_space; -static volatile struct dbdma_cmd *awacs_rx_cmds; - -/* - * Cached values of AWACS registers (we can't read them). - * Except on the burgundy. XXX - */ -int awacs_reg[5]; - -#define HAS_16BIT_TABLES -#undef HAS_8BIT_TABLES - -/* - * Stuff for outputting a beep. The values range from -327 to +327 - * so we can multiply by an amplitude in the range 0..100 to get a - * signed short value to put in the output buffer. - */ -static short beep_wform[256] = { - 0, 40, 79, 117, 153, 187, 218, 245, - 269, 288, 304, 316, 323, 327, 327, 324, - 318, 310, 299, 288, 275, 262, 249, 236, - 224, 213, 204, 196, 190, 186, 183, 182, - 182, 183, 186, 189, 192, 196, 200, 203, - 206, 208, 209, 209, 209, 207, 204, 201, - 197, 193, 188, 183, 179, 174, 170, 166, - 163, 161, 160, 159, 159, 160, 161, 162, - 164, 166, 168, 169, 171, 171, 171, 170, - 169, 167, 163, 159, 155, 150, 144, 139, - 133, 128, 122, 117, 113, 110, 107, 105, - 103, 103, 103, 103, 104, 104, 105, 105, - 105, 103, 101, 97, 92, 86, 78, 68, - 58, 45, 32, 18, 3, -11, -26, -41, - -55, -68, -79, -88, -95, -100, -102, -102, - -99, -93, -85, -75, -62, -48, -33, -16, - 0, 16, 33, 48, 62, 75, 85, 93, - 99, 102, 102, 100, 95, 88, 79, 68, - 55, 41, 26, 11, -3, -18, -32, -45, - -58, -68, -78, -86, -92, -97, -101, -103, - -105, -105, -105, -104, -104, -103, -103, -103, - -103, -105, -107, -110, -113, -117, -122, -128, - -133, -139, -144, -150, -155, -159, -163, -167, - -169, -170, -171, -171, -171, -169, -168, -166, - -164, -162, -161, -160, -159, -159, -160, -161, - -163, -166, -170, -174, -179, -183, -188, -193, - -197, -201, -204, -207, -209, -209, -209, -208, - -206, -203, -200, -196, -192, -189, -186, -183, - -182, -182, -183, -186, -190, -196, -204, -213, - -224, -236, -249, -262, -275, -288, -299, -310, - -318, -324, -327, -327, -323, -316, -304, -288, - -269, -245, -218, -187, -153, -117, -79, -40, -}; - -#define BEEP_SRATE 22050 /* 22050 Hz sample rate */ -#define BEEP_BUFLEN 512 -#define BEEP_VOLUME 15 /* 0 - 100 */ - -static int beep_volume = BEEP_VOLUME; -static int beep_playing = 0; -static int awacs_beep_state = 0; -static short *beep_buf; -static volatile struct dbdma_cmd *beep_dbdma_cmd; -static void (*orig_mksound)(unsigned int, unsigned int); -static int is_pbook_3400; -static unsigned char *latch_base; -static int is_pbook_G3; -static unsigned char *macio_base; - -/* Burgundy functions */ -static void awacs_burgundy_wcw(unsigned addr,unsigned newval); -static unsigned awacs_burgundy_rcw(unsigned addr); -static void awacs_burgundy_write_volume(unsigned address, int volume); -static int awacs_burgundy_read_volume(unsigned address); -static void awacs_burgundy_write_mvolume(unsigned address, int volume); -static int awacs_burgundy_read_mvolume(unsigned address); - -#ifdef CONFIG_PMAC_PBOOK -/* - * Stuff for restoring after a sleep. - */ -static int awacs_sleep_notify(struct pmu_sleep_notifier *self, int when); -struct pmu_sleep_notifier awacs_sleep_notifier = { - awacs_sleep_notify, SLEEP_LEVEL_SOUND, -}; -#endif /* CONFIG_PMAC_PBOOK */ - -#endif /* CONFIG_PPC */ - -/*** Some declarations *******************************************************/ - - -#define DMASND_TT 1 -#define DMASND_FALCON 2 -#define DMASND_AMIGA 3 -#define DMASND_AWACS 4 - -#define MAX_CATCH_RADIUS 10 -#define MIN_BUFFERS 4 -#define MIN_BUFSIZE 4 -#define MAX_BUFSIZE 128 /* Limit for Amiga */ - -static int catchRadius = 0; -static int numBufs = 4, bufSize = 32; -#ifdef CONFIG_PPC -static int numReadBufs = 4, readbufSize = 32; -#endif - -MODULE_PARM(catchRadius, "i"); -MODULE_PARM(numBufs, "i"); -MODULE_PARM(bufSize, "i"); -MODULE_PARM(numReadBufs, "i"); -MODULE_PARM(readbufSize, "i"); - -#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 **************************************************/ - -#ifdef HAS_8BIT_TABLES -/* 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 -}; -#endif /* HAS_8BIT_TABLES */ - -#ifdef HAS_16BIT_TABLES - -/* 16 bit mu-law */ - -static short 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 short 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 ssize_t ata_ct_law(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t ata_ct_s8(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t ata_ct_u8(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t ata_ct_s16be(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t ata_ct_u16be(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t ata_ct_s16le(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t ata_ct_u16le(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t ata_ctx_law(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t ata_ctx_s8(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t ata_ctx_u8(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t ata_ctx_s16be(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t ata_ctx_u16be(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t ata_ctx_s16le(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t ata_ctx_u16le(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -#endif /* CONFIG_ATARI */ - -#ifdef CONFIG_AMIGA -static ssize_t ami_ct_law(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t ami_ct_s8(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t ami_ct_u8(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t ami_ct_s16be(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t ami_ct_u16be(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t ami_ct_s16le(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t ami_ct_u16le(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -#endif /* CONFIG_AMIGA */ - -#ifdef CONFIG_PPC -static ssize_t pmac_ct_law(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t pmac_ct_s8(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t pmac_ct_u8(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t pmac_ct_s16(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t pmac_ct_u16(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t pmac_ctx_law(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t pmac_ctx_s8(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t pmac_ctx_u8(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t pmac_ctx_s16(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t pmac_ctx_u16(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t pmac_ct_s16_read(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t pmac_ct_u16_read(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -#endif /* CONFIG_PPC */ - -/*** 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); - int (*setGain)(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 { - ssize_t (*ct_ulaw)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); - ssize_t (*ct_alaw)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); - ssize_t (*ct_s8)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); - ssize_t (*ct_u8)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); - ssize_t (*ct_s16be)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); - ssize_t (*ct_u16be)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); - ssize_t (*ct_s16le)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); - ssize_t (*ct_u16le)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); -} TRANS; - -struct sound_settings { - MACHINE mach; /* machine dependent things */ - SETTINGS hard; /* hardware settings */ - SETTINGS soft; /* software settings */ - SETTINGS dsp; /* /dev/dsp default settings */ - TRANS *trans; /* supported translations */ -#if defined(CONFIG_PPC) - TRANS *read_trans; /* supported translations */ -#endif - int volume_left; /* volume (range is machine dependent) */ - int volume_right; - int bass; /* tone (range is machine dependent) */ - int treble; - int gain; - int minDev; /* minor device number currently open */ -#if defined(CONFIG_ATARI) || defined(CONFIG_PPC) - 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 int TTSetGain(int gain); -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 */ - -#ifdef CONFIG_PPC -static void *PMacAlloc(unsigned int size, int flags); -static void PMacFree(void *ptr, unsigned int size); -static int PMacIrqInit(void); -#ifdef MODULE -static void PMacIrqCleanup(void); -#endif /* MODULE */ -static void PMacSilence(void); -static void PMacInit(void); -static void PMacPlay(void); -static void PMacRecord(void); -static int PMacSetFormat(int format); -static int PMacSetVolume(int volume); -static void pmac_awacs_tx_intr(int irq, void *devid, struct pt_regs *regs); -static void pmac_awacs_rx_intr(int irq, void *devid, struct pt_regs *regs); -static void pmac_awacs_intr(int irq, void *devid, struct pt_regs *regs); -static void awacs_write(int val); -static int awacs_get_volume(int reg, int lshift); -static int awacs_volume_setter(int volume, int n, int mute, int lshift); -static void awacs_mksound(unsigned int hz, unsigned int ticks); -static void awacs_nosound(unsigned long xx); -#endif /* CONFIG_PPC */ - -/*** 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 */ -#if defined(CONFIG_ATARI) || defined(CONFIG_AMIGA) -static int sound_set_treble(int treble); -#endif /* CONFIG_ATARI || CONFIG_AMIGA */ -static ssize_t sound_copy_translate(const u_char *userPtr, - size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -#ifdef CONFIG_PPC -static ssize_t sound_copy_translate_read(const u_char *userPtr, - size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -#endif - - -/* - * /dev/mixer abstraction - */ - -struct sound_mixer { - int busy; - int modify_counter; -}; - -static struct sound_mixer mixer; - -/* - * Sound queue stuff, the heart of the driver - */ - -struct sound_queue { - int max_count, block_size; - char **buffers; - int max_active; - - /* it shouldn't be necessary to declare any of these volatile */ - int front, rear, count; - int rear_size; - /* - * The use of the playing field depends on the hardware - * - * Atari, PMac: The number of frames that are loaded/playing - * - * Amiga: Bit 0 is set: a frame is loaded - * Bit 1 is set: a frame is playing - */ - int active; - wait_queue_head_t action_queue, open_queue, sync_queue; - int 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; -#ifdef CONFIG_PPC -static struct sound_queue read_sq; -#endif - -#define sq_block_address(i) (sq.buffers[i]) -#define SIGNAL_RECEIVED (signal_pending(current)) -#define NON_BLOCKING(open_mode) (open_mode & O_NONBLOCK) -#define ONE_SECOND HZ /* in jiffies (100ths of a second) */ -#define NO_TIME_LIMIT 0xffffffff -#define SLEEP(queue, time_limit) \ - interruptible_sleep_on_timeout(&queue, (time_limit)); -#define WAKE_UP(queue) (wake_up_interruptible(&queue)) - -/* - * /dev/sndstat - */ - -struct sound_state { - int busy; - char buf[512]; - int len, ptr; -}; - -static struct sound_state state; - -/*** Common stuff ********************************************************/ - -static long long sound_lseek(struct file *file, long long offset, int orig); -static inline int ioctl_return(int *addr, int value) -{ - if (value < 0) - return(value); - - return put_user(value, addr); -} - - -/*** Config & Setup **********************************************************/ - - -void dmasound_init(void); -static int dmasound_setup(char *str); - - -/*** 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 ssize_t ata_ct_law(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - char *table = sound.soft.format == AFMT_MU_LAW ? ulaw2dma8 : alaw2dma8; - ssize_t 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; - if (get_user(data, userPtr++)) - return -EFAULT; - *p++ = table[data]; - count--; - } - *frameUsed += used; - return used; -} - - -static ssize_t ata_ct_s8(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - ssize_t count, used; - void *p = &frame[*frameUsed]; - - count = min(userCount, frameLeft); - if (sound.soft.stereo) - count &= ~1; - used = count; - if (copy_from_user(p, userPtr, count)) - return -EFAULT; - *frameUsed += used; - return(used); -} - - -static ssize_t ata_ct_u8(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - ssize_t count, used; - - if (!sound.soft.stereo) { - u_char *p = &frame[*frameUsed]; - count = min(userCount, frameLeft); - used = count; - while (count > 0) { - u_char data; - if (get_user(data, userPtr++)) - return -EFAULT; - *p++ = data ^ 0x80; - count--; - } - } else { - u_short *p = (u_short *)&frame[*frameUsed]; - count = min(userCount, frameLeft)>>1; - used = count*2; - while (count > 0) { - u_short data; - if (get_user(data, ((u_short *)userPtr)++)) - return -EFAULT; - *p++ = data ^ 0x8080; - count--; - } - } - *frameUsed += used; - return(used); -} - - -static ssize_t ata_ct_s16be(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - ssize_t count, used; - - if (!sound.soft.stereo) { - u_short *p = (u_short *)&frame[*frameUsed]; - count = min(userCount, frameLeft)>>1; - used = count*2; - while (count > 0) { - u_short data; - if (get_user(data, ((u_short *)userPtr)++)) - return -EFAULT; - *p++ = data; - *p++ = data; - count--; - } - *frameUsed += used*2; - } else { - void *p = (u_short *)&frame[*frameUsed]; - count = min(userCount, frameLeft) & ~3; - used = count; - if (copy_from_user(p, userPtr, count)) - return -EFAULT; - *frameUsed += used; - } - return(used); -} - - -static ssize_t ata_ct_u16be(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - ssize_t count, used; - - if (!sound.soft.stereo) { - u_short *p = (u_short *)&frame[*frameUsed]; - count = min(userCount, frameLeft)>>1; - used = count*2; - while (count > 0) { - u_short data; - if (get_user(data, ((u_short *)userPtr)++)) - return -EFAULT; - data ^= 0x8000; - *p++ = data; - *p++ = data; - count--; - } - *frameUsed += used*2; - } else { - u_long *p = (u_long *)&frame[*frameUsed]; - count = min(userCount, frameLeft)>>2; - used = count*4; - while (count > 0) { - u_long data; - if (get_user(data, ((u_int *)userPtr)++)) - return -EFAULT; - *p++ = data ^ 0x80008000; - count--; - } - *frameUsed += used; - } - return(used); -} - - -static ssize_t ata_ct_s16le(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - ssize_t count, used; - - count = frameLeft; - if (!sound.soft.stereo) { - u_short *p = (u_short *)&frame[*frameUsed]; - count = min(userCount, frameLeft)>>1; - used = count*2; - while (count > 0) { - u_short data; - if (get_user(data, ((u_short *)userPtr)++)) - return -EFAULT; - data = le2be16(data); - *p++ = data; - *p++ = data; - count--; - } - *frameUsed += used*2; - } else { - u_long *p = (u_long *)&frame[*frameUsed]; - count = min(userCount, frameLeft)>>2; - used = count*4; - while (count > 0) { - u_long data; - if (get_user(data, ((u_int *)userPtr)++)) - return -EFAULT; - data = le2be16dbl(data); - *p++ = data; - count--; - } - *frameUsed += used; - } - return(used); -} - - -static ssize_t ata_ct_u16le(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - ssize_t count, used; - - count = frameLeft; - if (!sound.soft.stereo) { - u_short *p = (u_short *)&frame[*frameUsed]; - count = min(userCount, frameLeft)>>1; - used = count*2; - while (count > 0) { - u_short data; - if (get_user(data, ((u_short *)userPtr)++)) - return -EFAULT; - data = le2be16(data) ^ 0x8000; - *p++ = data; - *p++ = data; - } - *frameUsed += used*2; - } else { - u_long *p = (u_long *)&frame[*frameUsed]; - count = min(userCount, frameLeft)>>2; - used = count; - while (count > 0) { - u_long data; - if (get_user(data, ((u_int *)userPtr)++)) - return -EFAULT; - data = le2be16dbl(data) ^ 0x80008000; - *p++ = data; - count--; - } - *frameUsed += used; - } - return(used); -} - - -static ssize_t ata_ctx_law(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - char *table = sound.soft.format == AFMT_MU_LAW ? ulaw2dma8 : alaw2dma8; - /* this should help gcc to stuff everything into registers */ - long bal = sound.bal; - long hSpeed = sound.hard.speed, sSpeed = sound.soft.speed; - ssize_t used, usedf; - - used = userCount; - usedf = frameLeft; - if (!sound.soft.stereo) { - u_char *p = &frame[*frameUsed]; - u_char data = sound.data; - while (frameLeft) { - u_char c; - if (bal < 0) { - if (!userCount) - break; - if (get_user(c, userPtr++)) - return -EFAULT; - data = table[c]; - userCount--; - bal += hSpeed; - } - *p++ = data; - frameLeft--; - bal -= sSpeed; - } - sound.data = data; - } else { - u_short *p = (u_short *)&frame[*frameUsed]; - u_short data = sound.data; - while (frameLeft >= 2) { - u_char c; - if (bal < 0) { - if (userCount < 2) - break; - if (get_user(c, userPtr++)) - return -EFAULT; - data = table[c] << 8; - if (get_user(c, userPtr++)) - return -EFAULT; - data |= table[c]; - userCount -= 2; - bal += hSpeed; - } - *p++ = data; - frameLeft -= 2; - bal -= sSpeed; - } - sound.data = data; - } - sound.bal = bal; - used -= userCount; - *frameUsed += usedf-frameLeft; - return(used); -} - - -static ssize_t ata_ctx_s8(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - /* this should help gcc to stuff everything into registers */ - long bal = sound.bal; - long hSpeed = sound.hard.speed, sSpeed = sound.soft.speed; - ssize_t used, usedf; - - used = userCount; - usedf = frameLeft; - if (!sound.soft.stereo) { - u_char *p = &frame[*frameUsed]; - u_char data = sound.data; - while (frameLeft) { - if (bal < 0) { - if (!userCount) - break; - if (get_user(data, userPtr++)) - return -EFAULT; - userCount--; - bal += hSpeed; - } - *p++ = data; - frameLeft--; - bal -= sSpeed; - } - sound.data = data; - } else { - u_short *p = (u_short *)&frame[*frameUsed]; - u_short data = sound.data; - while (frameLeft >= 2) { - if (bal < 0) { - if (userCount < 2) - break; - if (get_user(data, ((u_short *)userPtr)++)) - return -EFAULT; - userCount -= 2; - bal += hSpeed; - } - *p++ = data; - frameLeft -= 2; - bal -= sSpeed; - } - sound.data = data; - } - sound.bal = bal; - used -= userCount; - *frameUsed += usedf-frameLeft; - return(used); -} - - -static ssize_t ata_ctx_u8(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - /* this should help gcc to stuff everything into registers */ - long bal = sound.bal; - long hSpeed = sound.hard.speed, sSpeed = sound.soft.speed; - ssize_t used, usedf; - - used = userCount; - usedf = frameLeft; - if (!sound.soft.stereo) { - u_char *p = &frame[*frameUsed]; - u_char data = sound.data; - while (frameLeft) { - if (bal < 0) { - if (!userCount) - break; - if (get_user(data, userPtr++)) - return -EFAULT; - data ^= 0x80; - userCount--; - bal += hSpeed; - } - *p++ = data; - frameLeft--; - bal -= sSpeed; - } - sound.data = data; - } else { - u_short *p = (u_short *)&frame[*frameUsed]; - u_short data = sound.data; - while (frameLeft >= 2) { - if (bal < 0) { - if (userCount < 2) - break; - if (get_user(data, ((u_short *)userPtr)++)) - return -EFAULT; - data ^= 0x8080; - userCount -= 2; - bal += hSpeed; - } - *p++ = data; - frameLeft -= 2; - bal -= sSpeed; - } - sound.data = data; - } - sound.bal = bal; - used -= userCount; - *frameUsed += usedf-frameLeft; - return(used); -} - - -static ssize_t ata_ctx_s16be(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - /* this should help gcc to stuff everything into registers */ - long bal = sound.bal; - long hSpeed = sound.hard.speed, sSpeed = sound.soft.speed; - ssize_t used, usedf; - - used = userCount; - usedf = frameLeft; - if (!sound.soft.stereo) { - u_short *p = (u_short *)&frame[*frameUsed]; - u_short data = sound.data; - while (frameLeft >= 4) { - if (bal < 0) { - if (userCount < 2) - break; - if (get_user(data, ((u_short *)userPtr)++)) - return -EFAULT; - userCount -= 2; - bal += hSpeed; - } - *p++ = data; - *p++ = data; - frameLeft -= 4; - bal -= sSpeed; - } - sound.data = data; - } else { - u_long *p = (u_long *)&frame[*frameUsed]; - u_long data = sound.data; - while (frameLeft >= 4) { - if (bal < 0) { - if (userCount < 4) - break; - if (get_user(data, ((u_int *)userPtr)++)) - return -EFAULT; - userCount -= 4; - bal += hSpeed; - } - *p++ = data; - frameLeft -= 4; - bal -= sSpeed; - } - sound.data = data; - } - sound.bal = bal; - used -= userCount; - *frameUsed += usedf-frameLeft; - return(used); -} - - -static ssize_t ata_ctx_u16be(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - /* this should help gcc to stuff everything into registers */ - long bal = sound.bal; - long hSpeed = sound.hard.speed, sSpeed = sound.soft.speed; - ssize_t used, usedf; - - used = userCount; - usedf = frameLeft; - if (!sound.soft.stereo) { - u_short *p = (u_short *)&frame[*frameUsed]; - u_short data = sound.data; - while (frameLeft >= 4) { - if (bal < 0) { - if (userCount < 2) - break; - if (get_user(data, ((u_short *)userPtr)++)) - return -EFAULT; - data ^= 0x8000; - userCount -= 2; - bal += hSpeed; - } - *p++ = data; - *p++ = data; - frameLeft -= 4; - bal -= sSpeed; - } - sound.data = data; - } else { - u_long *p = (u_long *)&frame[*frameUsed]; - u_long data = sound.data; - while (frameLeft >= 4) { - if (bal < 0) { - if (userCount < 4) - break; - if (get_user(data, ((u_int *)userPtr)++)) - return -EFAULT; - data ^= 0x80008000; - userCount -= 4; - bal += hSpeed; - } - *p++ = data; - frameLeft -= 4; - bal -= sSpeed; - } - sound.data = data; - } - sound.bal = bal; - used -= userCount; - *frameUsed += usedf-frameLeft; - return(used); -} - - -static ssize_t ata_ctx_s16le(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - /* this should help gcc to stuff everything into registers */ - long bal = sound.bal; - long hSpeed = sound.hard.speed, sSpeed = sound.soft.speed; - ssize_t used, usedf; - - used = userCount; - usedf = frameLeft; - if (!sound.soft.stereo) { - u_short *p = (u_short *)&frame[*frameUsed]; - u_short data = sound.data; - while (frameLeft >= 4) { - if (bal < 0) { - if (userCount < 2) - break; - if (get_user(data, ((u_short *)userPtr)++)) - return -EFAULT; - data = le2be16(data); - userCount -= 2; - bal += hSpeed; - } - *p++ = data; - *p++ = data; - frameLeft -= 4; - bal -= sSpeed; - } - sound.data = data; - } else { - u_long *p = (u_long *)&frame[*frameUsed]; - u_long data = sound.data; - while (frameLeft >= 4) { - if (bal < 0) { - if (userCount < 4) - break; - if (get_user(data, ((u_int *)userPtr)++)) - return -EFAULT; - data = le2be16dbl(data); - userCount -= 4; - bal += hSpeed; - } - *p++ = data; - frameLeft -= 4; - bal -= sSpeed; - } - sound.data = data; - } - sound.bal = bal; - used -= userCount; - *frameUsed += usedf-frameLeft; - return(used); -} - - -static ssize_t ata_ctx_u16le(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - /* this should help gcc to stuff everything into registers */ - long bal = sound.bal; - long hSpeed = sound.hard.speed, sSpeed = sound.soft.speed; - ssize_t used, usedf; - - used = userCount; - usedf = frameLeft; - if (!sound.soft.stereo) { - u_short *p = (u_short *)&frame[*frameUsed]; - u_short data = sound.data; - while (frameLeft >= 4) { - if (bal < 0) { - if (userCount < 2) - break; - if (get_user(data, ((u_short *)userPtr)++)) - return -EFAULT; - data = le2be16(data) ^ 0x8000; - userCount -= 2; - bal += hSpeed; - } - *p++ = data; - *p++ = data; - frameLeft -= 4; - bal -= sSpeed; - } - sound.data = data; - } else { - u_long *p = (u_long *)&frame[*frameUsed]; - u_long data = sound.data; - while (frameLeft >= 4) { - if (bal < 0) { - if (userCount < 4) - break; - if (get_user(data, ((u_int *)userPtr)++)) - return -EFAULT; - data = le2be16dbl(data) ^ 0x80008000; - userCount -= 4; - bal += hSpeed; - } - *p++ = data; - frameLeft -= 4; - bal -= sSpeed; - } - sound.data = data; - } - sound.bal = bal; - used -= userCount; - *frameUsed += usedf-frameLeft; - return(used); -} -#endif /* CONFIG_ATARI */ - - -#ifdef CONFIG_AMIGA -static ssize_t ami_ct_law(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - char *table = sound.soft.format == AFMT_MU_LAW ? ulaw2dma8 : alaw2dma8; - ssize_t count, used; - - if (!sound.soft.stereo) { - u_char *p = &frame[*frameUsed]; - count = min(userCount, frameLeft) & ~1; - used = count; - while (count > 0) { - u_char data; - if (get_user(data, userPtr++)) - return -EFAULT; - *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; - if (get_user(data, userPtr++)) - return -EFAULT; - *left++ = table[data]; - if (get_user(data, userPtr++)) - return -EFAULT; - *right++ = table[data]; - count--; - } - } - *frameUsed += used; - return(used); -} - - -static ssize_t ami_ct_s8(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, ssize_t frameLeft) -{ - ssize_t count, used; - - if (!sound.soft.stereo) { - void *p = &frame[*frameUsed]; - count = min(userCount, frameLeft) & ~1; - used = count; - if (copy_from_user(p, userPtr, count)) - return -EFAULT; - } else { - u_char *left = &frame[*frameUsed>>1]; - u_char *right = left+sq.block_size_half; - count = min(userCount, frameLeft)>>1 & ~1; - used = count*2; - while (count > 0) { - if (get_user(*left++, userPtr++) - || get_user(*right++, userPtr++)) - return -EFAULT; - count--; - } - } - *frameUsed += used; - return(used); -} - - -static ssize_t ami_ct_u8(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, ssize_t frameLeft) -{ - ssize_t count, used; - - if (!sound.soft.stereo) { - char *p = &frame[*frameUsed]; - count = min(userCount, frameLeft) & ~1; - used = count; - while (count > 0) { - u_char data; - if (get_user(data, userPtr++)) - return -EFAULT; - *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; - if (get_user(data, userPtr++)) - return -EFAULT; - *left++ = data ^ 0x80; - if (get_user(data, userPtr++)) - return -EFAULT; - *right++ = data ^ 0x80; - count--; - } - } - *frameUsed += used; - return(used); -} - - -static ssize_t ami_ct_s16be(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - ssize_t count, used; - u_short 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) { - if (get_user(data, ((u_short *)userPtr)++)) - return -EFAULT; - *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) { - if (get_user(data, ((u_short *)userPtr)++)) - return -EFAULT; - *lefth++ = data>>8; - *leftl++ = (data>>2) & 0x3f; - if (get_user(data, ((u_short *)userPtr)++)) - return -EFAULT; - *righth++ = data>>8; - *rightl++ = (data>>2) & 0x3f; - count--; - } - } - *frameUsed += used; - return(used); -} - - -static ssize_t ami_ct_u16be(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - ssize_t count, used; - u_short 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) { - if (get_user(data, ((u_short *)userPtr)++)) - return -EFAULT; - 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) { - if (get_user(data, ((u_short *)userPtr)++)) - return -EFAULT; - data ^= 0x8000; - *lefth++ = data>>8; - *leftl++ = (data>>2) & 0x3f; - if (get_user(data, ((u_short *)userPtr)++)) - return -EFAULT; - data ^= 0x8000; - *righth++ = data>>8; - *rightl++ = (data>>2) & 0x3f; - count--; - } - } - *frameUsed += used; - return(used); -} - - -static ssize_t ami_ct_s16le(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - ssize_t count, used; - u_short 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) { - if (get_user(data, ((u_short *)userPtr)++)) - return -EFAULT; - 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) { - if (get_user(data, ((u_short *)userPtr)++)) - return -EFAULT; - data = le2be16(data); - *lefth++ = data>>8; - *leftl++ = (data>>2) & 0x3f; - if (get_user(data, ((u_short *)userPtr)++)) - return -EFAULT; - data = le2be16(data); - *righth++ = data>>8; - *rightl++ = (data>>2) & 0x3f; - count--; - } - } - *frameUsed += used; - return(used); -} - - -static ssize_t ami_ct_u16le(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - ssize_t count, used; - u_short 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) { - if (get_user(data, ((u_short *)userPtr)++)) - return -EFAULT; - 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) { - if (get_user(data, ((u_short *)userPtr)++)) - return -EFAULT; - data = le2be16(data) ^ 0x8000; - *lefth++ = data>>8; - *leftl++ = (data>>2) & 0x3f; - if (get_user(data, ((u_short *)userPtr)++)) - return -EFAULT; - data = le2be16(data) ^ 0x8000; - *righth++ = data>>8; - *rightl++ = (data>>2) & 0x3f; - count--; - } - } - *frameUsed += used; - return(used); -} -#endif /* CONFIG_AMIGA */ - -#ifdef CONFIG_PPC -static ssize_t pmac_ct_law(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - short *table = sound.soft.format == AFMT_MU_LAW ? ulaw2dma16: alaw2dma16; - ssize_t count, used; - short *p = (short *) &frame[*frameUsed]; - int val, stereo = sound.soft.stereo; - - frameLeft >>= 2; - if (stereo) - userCount >>= 1; - used = count = min(userCount, frameLeft); - while (count > 0) { - u_char data; - if (get_user(data, userPtr++)) - return -EFAULT; - val = table[data]; - *p++ = val; - if (stereo) { - if (get_user(data, userPtr++)) - return -EFAULT; - val = table[data]; - } - *p++ = val; - count--; - } - *frameUsed += used * 4; - return stereo? used * 2: used; -} - - -static ssize_t pmac_ct_s8(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - ssize_t count, used; - short *p = (short *) &frame[*frameUsed]; - int val, stereo = sound.soft.stereo; - - frameLeft >>= 2; - if (stereo) - userCount >>= 1; - used = count = min(userCount, frameLeft); - while (count > 0) { - u_char data; - if (get_user(data, userPtr++)) - return -EFAULT; - val = data << 8; - *p++ = val; - if (stereo) { - if (get_user(data, userPtr++)) - return -EFAULT; - val = data << 8; - } - *p++ = val; - count--; - } - *frameUsed += used * 4; - return stereo? used * 2: used; -} - - -static ssize_t pmac_ct_u8(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - ssize_t count, used; - short *p = (short *) &frame[*frameUsed]; - int val, stereo = sound.soft.stereo; - - frameLeft >>= 2; - if (stereo) - userCount >>= 1; - used = count = min(userCount, frameLeft); - while (count > 0) { - u_char data; - if (get_user(data, userPtr++)) - return -EFAULT; - val = (data ^ 0x80) << 8; - *p++ = val; - if (stereo) { - if (get_user(data, userPtr++)) - return -EFAULT; - val = (data ^ 0x80) << 8; - } - *p++ = val; - count--; - } - *frameUsed += used * 4; - return stereo? used * 2: used; -} - - -static ssize_t pmac_ct_s16(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - ssize_t count, used; - int stereo = sound.soft.stereo; - short *fp = (short *) &frame[*frameUsed]; - - frameLeft >>= 2; - userCount >>= (stereo? 2: 1); - used = count = min(userCount, frameLeft); - if (!stereo) { - short *up = (short *) userPtr; - while (count > 0) { - short data; - if (get_user(data, up++)) - return -EFAULT; - *fp++ = data; - *fp++ = data; - count--; - } - } else { - if (copy_from_user(fp, userPtr, count * 4)) - return -EFAULT; - } - *frameUsed += used * 4; - return stereo? used * 4: used * 2; -} - -static ssize_t pmac_ct_u16(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - ssize_t count, used; - int mask = (sound.soft.format == AFMT_U16_LE? 0x0080: 0x8000); - int stereo = sound.soft.stereo; - short *fp = (short *) &frame[*frameUsed]; - short *up = (short *) userPtr; - - frameLeft >>= 2; - userCount >>= (stereo? 2: 1); - used = count = min(userCount, frameLeft); - while (count > 0) { - int data; - if (get_user(data, up++)) - return -EFAULT; - data ^= mask; - *fp++ = data; - if (stereo) { - if (get_user(data, up++)) - return -EFAULT; - data ^= mask; - } - *fp++ = data; - count--; - } - *frameUsed += used * 4; - return stereo? used * 4: used * 2; -} - - -static ssize_t pmac_ctx_law(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - unsigned short *table = (unsigned short *) - (sound.soft.format == AFMT_MU_LAW ? ulaw2dma16: alaw2dma16); - unsigned int data = sound.data; - unsigned int *p = (unsigned int *) &frame[*frameUsed]; - int bal = sound.bal; - int hSpeed = sound.hard.speed, sSpeed = sound.soft.speed; - int utotal, ftotal; - int stereo = sound.soft.stereo; - - frameLeft >>= 2; - if (stereo) - userCount >>= 1; - ftotal = frameLeft; - utotal = userCount; - while (frameLeft) { - u_char c; - if (bal < 0) { - if (userCount == 0) - break; - if (get_user(c, userPtr++)) - return -EFAULT; - data = table[c]; - if (stereo) { - if (get_user(c, userPtr++)) - return -EFAULT; - data = (data << 16) + table[c]; - } else - data = (data << 16) + data; - userCount--; - bal += hSpeed; - } - *p++ = data; - frameLeft--; - bal -= sSpeed; - } - sound.bal = bal; - sound.data = data; - *frameUsed += (ftotal - frameLeft) * 4; - utotal -= userCount; - return stereo? utotal * 2: utotal; -} - - -static ssize_t pmac_ctx_s8(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - unsigned int *p = (unsigned int *) &frame[*frameUsed]; - unsigned int data = sound.data; - int bal = sound.bal; - int hSpeed = sound.hard.speed, sSpeed = sound.soft.speed; - int stereo = sound.soft.stereo; - int utotal, ftotal; - - frameLeft >>= 2; - if (stereo) - userCount >>= 1; - ftotal = frameLeft; - utotal = userCount; - while (frameLeft) { - u_char c; - if (bal < 0) { - if (userCount == 0) - break; - if (get_user(c, userPtr++)) - return -EFAULT; - data = c << 8; - if (stereo) { - if (get_user(c, userPtr++)) - return -EFAULT; - data = (data << 16) + (c << 8); - } else - data = (data << 16) + data; - userCount--; - bal += hSpeed; - } - *p++ = data; - frameLeft--; - bal -= sSpeed; - } - sound.bal = bal; - sound.data = data; - *frameUsed += (ftotal - frameLeft) * 4; - utotal -= userCount; - return stereo? utotal * 2: utotal; -} - - -static ssize_t pmac_ctx_u8(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - unsigned int *p = (unsigned int *) &frame[*frameUsed]; - unsigned int data = sound.data; - int bal = sound.bal; - int hSpeed = sound.hard.speed, sSpeed = sound.soft.speed; - int stereo = sound.soft.stereo; - int utotal, ftotal; - - frameLeft >>= 2; - if (stereo) - userCount >>= 1; - ftotal = frameLeft; - utotal = userCount; - while (frameLeft) { - u_char c; - if (bal < 0) { - if (userCount == 0) - break; - if (get_user(c, userPtr++)) - return -EFAULT; - data = (c ^ 0x80) << 8; - if (stereo) { - if (get_user(c, userPtr++)) - return -EFAULT; - data = (data << 16) + ((c ^ 0x80) << 8); - } else - data = (data << 16) + data; - userCount--; - bal += hSpeed; - } - *p++ = data; - frameLeft--; - bal -= sSpeed; - } - sound.bal = bal; - sound.data = data; - *frameUsed += (ftotal - frameLeft) * 4; - utotal -= userCount; - return stereo? utotal * 2: utotal; -} - - -static ssize_t pmac_ctx_s16(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - unsigned int *p = (unsigned int *) &frame[*frameUsed]; - unsigned int data = sound.data; - unsigned short *up = (unsigned short *) userPtr; - int bal = sound.bal; - int hSpeed = sound.hard.speed, sSpeed = sound.soft.speed; - int stereo = sound.soft.stereo; - int utotal, ftotal; - - frameLeft >>= 2; - userCount >>= (stereo? 2: 1); - ftotal = frameLeft; - utotal = userCount; - while (frameLeft) { - unsigned short c; - if (bal < 0) { - if (userCount == 0) - break; - if (get_user(data, up++)) - return -EFAULT; - if (stereo) { - if (get_user(c, up++)) - return -EFAULT; - data = (data << 16) + c; - } else - data = (data << 16) + data; - userCount--; - bal += hSpeed; - } - *p++ = data; - frameLeft--; - bal -= sSpeed; - } - sound.bal = bal; - sound.data = data; - *frameUsed += (ftotal - frameLeft) * 4; - utotal -= userCount; - return stereo? utotal * 4: utotal * 2; -} - - -static ssize_t pmac_ctx_u16(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - int mask = (sound.soft.format == AFMT_U16_LE? 0x0080: 0x8000); - unsigned int *p = (unsigned int *) &frame[*frameUsed]; - unsigned int data = sound.data; - unsigned short *up = (unsigned short *) userPtr; - int bal = sound.bal; - int hSpeed = sound.hard.speed, sSpeed = sound.soft.speed; - int stereo = sound.soft.stereo; - int utotal, ftotal; - - frameLeft >>= 2; - userCount >>= (stereo? 2: 1); - ftotal = frameLeft; - utotal = userCount; - while (frameLeft) { - unsigned short c; - if (bal < 0) { - if (userCount == 0) - break; - if (get_user(data, up++)) - return -EFAULT; - data ^= mask; - if (stereo) { - if (get_user(c, up++)) - return -EFAULT; - data = (data << 16) + (c ^ mask); - } else - data = (data << 16) + data; - userCount--; - bal += hSpeed; - } - *p++ = data; - frameLeft--; - bal -= sSpeed; - } - sound.bal = bal; - sound.data = data; - *frameUsed += (ftotal - frameLeft) * 4; - utotal -= userCount; - return stereo? utotal * 4: utotal * 2; -} - -static ssize_t pmac_ct_s8_read(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - ssize_t count, used; - short *p = (short *) &frame[*frameUsed]; - int val, stereo = sound.soft.stereo; - - frameLeft >>= 2; - if (stereo) - userCount >>= 1; - used = count = min(userCount, frameLeft); - while (count > 0) { - u_char data; - - val = *p++; - data = val >> 8; - if (put_user(data, (u_char *)userPtr++)) - return -EFAULT; - if (stereo) { - val = *p; - data = val >> 8; - if (put_user(data, (u_char *)userPtr++)) - return -EFAULT; - } - p++; - count--; - } - *frameUsed += used * 4; - return stereo? used * 2: used; -} - - -static ssize_t pmac_ct_u8_read(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - ssize_t count, used; - short *p = (short *) &frame[*frameUsed]; - int val, stereo = sound.soft.stereo; - - frameLeft >>= 2; - if (stereo) - userCount >>= 1; - used = count = min(userCount, frameLeft); - while (count > 0) { - u_char data; - - val = *p++; - data = (val >> 8) ^ 0x80; - if (put_user(data, (u_char *)userPtr++)) - return -EFAULT; - if (stereo) { - val = *p; - data = (val >> 8) ^ 0x80; - if (put_user(data, (u_char *)userPtr++)) - return -EFAULT; - } - p++; - count--; - } - *frameUsed += used * 4; - return stereo? used * 2: used; -} - - -static ssize_t pmac_ct_s16_read(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - ssize_t count, used; - int stereo = sound.soft.stereo; - short *fp = (short *) &frame[*frameUsed]; - - frameLeft >>= 2; - userCount >>= (stereo? 2: 1); - used = count = min(userCount, frameLeft); - if (!stereo) { - short *up = (short *) userPtr; - while (count > 0) { - short data; - data = *fp; - if (put_user(data, up++)) - return -EFAULT; - fp+=2; - count--; - } - } else { - if (copy_to_user((u_char *)userPtr, fp, count * 4)) - return -EFAULT; - } - *frameUsed += used * 4; - return stereo? used * 4: used * 2; -} - -static ssize_t pmac_ct_u16_read(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - ssize_t count, used; - int mask = (sound.soft.format == AFMT_U16_LE? 0x0080: 0x8000); - int stereo = sound.soft.stereo; - short *fp = (short *) &frame[*frameUsed]; - short *up = (short *) userPtr; - - frameLeft >>= 2; - userCount >>= (stereo? 2: 1); - used = count = min(userCount, frameLeft); - while (count > 0) { - int data; - - data = *fp++; - data ^= mask; - if (put_user(data, up++)) - return -EFAULT; - if (stereo) { - data = *fp; - data ^= mask; - if (put_user(data, up++)) - return -EFAULT; - } - fp++; - count--; - } - *frameUsed += used * 4; - return stereo? used * 4: used * 2; -} - - -#endif /* CONFIG_PPC */ - - -#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 */ - -#ifdef CONFIG_PPC -static TRANS transAwacsNormal = { - pmac_ct_law, pmac_ct_law, pmac_ct_s8, pmac_ct_u8, - pmac_ct_s16, pmac_ct_u16, pmac_ct_s16, pmac_ct_u16 -}; - -static TRANS transAwacsExpand = { - pmac_ctx_law, pmac_ctx_law, pmac_ctx_s8, pmac_ctx_u8, - pmac_ctx_s16, pmac_ctx_u16, pmac_ctx_s16, pmac_ctx_u16 -}; - -static TRANS transAwacsNormalRead = { - NULL, NULL, pmac_ct_s8_read, pmac_ct_u8_read, - pmac_ct_s16_read, pmac_ct_u16_read, pmac_ct_s16_read, pmac_ct_u16_read -}; -#endif /* CONFIG_PPC */ - -/*** Low level stuff *********************************************************/ - - -#ifdef CONFIG_ATARI - -/* - * Atari (TT/Falcon) - */ - -static void *AtaAlloc(unsigned int size, int flags) -{ - return( atari_stram_alloc( size, NULL, "dmasound" )); -} - -static void AtaFree(void *obj, unsigned int size) -{ - atari_stram_free( obj ); -} - -static int __init AtaIrqInit(void) -{ - /* Set up timer A. Timer A - will receive a signal upon end of playing from the sound - hardware. Furthermore Timer A is able to count events - and will cause an interrupt after a programmed number - of events. So all we need to keep the music playing is - to provide the sound hardware with new data upon - an interrupt from timer A. */ - mfp.tim_ct_a = 0; /* ++roman: Stop timer before programming! */ - mfp.tim_dt_a = 1; /* Cause interrupt after first event. */ - mfp.tim_ct_a = 8; /* Turn on event counting. */ - /* Register interrupt handler. */ - request_irq(IRQ_MFP_TIMA, 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)); -} - - -#define GAIN_VOXWARE_TO_DB(v) \ - (((v) < 0) ? -80 : ((v) > 100) ? 0 : ((v) * 4) / 5 - 80) -#define GAIN_DB_TO_VOXWARE(v) ((((v) + 80) * 5 + 1) / 4) - -static int TTSetGain(int gain) -{ - sound.gain = GAIN_VOXWARE_TO_DB(gain); - atari_microwire_cmd(MW_LM1992_VOLUME(sound.gain)); - return GAIN_DB_TO_VOXWARE(sound.gain); -} - - - -/* - * Falcon - */ - - -static void FalconSilence(void) -{ - /* stop playback, set sample rate 50kHz for PSG sound */ - tt_dmasnd.ctrl = DMASND_CTRL_OFF; - tt_dmasnd.mode = DMASND_MODE_50KHZ | DMASND_MODE_STEREO | DMASND_MODE_8BIT; - tt_dmasnd.int_div = 0; /* STE compatible divider */ - tt_dmasnd.int_ctrl = 0x0; - tt_dmasnd.cbar_src = 0x0000; /* no matrix inputs */ - tt_dmasnd.cbar_dst = 0x0000; /* no matrix outputs */ - tt_dmasnd.dac_src = 1; /* connect ADC to DAC, disconnect matrix */ - tt_dmasnd.adc_src = 3; /* ADC Input = PSG */ -} - - -static void FalconInit(void) -{ - int divider, i, idx; - const int freq[8] = {49170, 32780, 24585, 19668, 16390, 12292, 9834, 8195}; - - /* search a frequency that fits into the allowed error range */ - - idx = -1; - for (i = 0; i < arraysize(freq); i++) - /* if we will tolerate 3% error 8000Hz->8195Hz (2.38%) would - * be playable without expanding, but that now a kernel runtime - * option - */ - if ((100 * abs(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(virt_to_phys(end - 1) + 1); - DMASNDSetBase(virt_to_phys(start)); - /* Since only an even number of samples per frame can - be played, we might lose one byte here. (TO DO) */ - sq.front = (sq.front+1) % sq.max_count; - sq.active++; - tt_dmasnd.ctrl = DMASND_CTRL_ON | DMASND_CTRL_REPEAT; -} - - -static void AtaPlay(void) -{ - /* ++TeSche: Note that sq.active is no longer just a flag but holds - * the number of frames the DMA is currently programmed for instead, - * may be 0, 1 (currently being played) or 2 (pre-programmed). - * - * Changes done to sq.count and sq.active are a bit more subtle again - * so now I must admit I also prefer disabling the irq here rather - * than considering all possible situations. But the point is that - * disabling the irq doesn't have any bad influence on this version of - * the driver as we benefit from having pre-programmed the DMA - * wherever possible: There's no need to reload the DMA at the exact - * time of an interrupt but only at some time while the pre-programmed - * frame is playing! - */ - atari_disable_irq(IRQ_MFP_TIMA); - - if (sq.active == 2 || /* DMA is 'full' */ - sq.count <= 0) { /* nothing to do */ - atari_enable_irq(IRQ_MFP_TIMA); - return; - } - - if (sq.active == 0) { - /* looks like there's nothing 'in' the DMA yet, so try - * to put two frames into it (at least one is available). - */ - if (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.active == 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.active) { - /* 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.active--; - - if (!sq.active) { - tt_dmasnd.ctrl = DMASND_CTRL_OFF; - sq.ignore_int = 1; - } - - WAKE_UP(sq.action_queue); - /* At least one block of the queue is free now - so wake up a writing process blocked because - of a full queue. */ - - if ((sq.active != 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.active==1 that means - * the only remaining frame was already programmed earlier - * (and is currently running) so we mustn't call AtaPlay() - * here, otherwise we'll play one frame too much. - */ - AtaPlay(); - - if (!sq.active) 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 - */ - -#define StopDMA() custom.aud[0].audvol = custom.aud[1].audvol = 0; \ - custom.aud[2].audvol = custom.aud[3].audvol = 0; \ - custom.dmacon = AMI_AUDIO_OFF; - -static void *AmiAlloc(unsigned int size, int flags) -{ - return amiga_chip_alloc((long)size, "dmasound"); -} - -static void AmiFree(void *obj, unsigned int size) -{ - amiga_chip_free (obj); -} - -static int __init AmiIrqInit(void) -{ - /* turn off DMA for audio channels */ - StopDMA(); - - /* Register interrupt handler. */ - if (request_irq(IRQ_AMIGA_AUD0, 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 */ - StopDMA(); - /* release the interrupt */ - free_irq(IRQ_AMIGA_AUD0, ami_sq_interrupt); -} -#endif /* MODULE */ - -static void AmiSilence(void) -{ - /* turn off DMA for audio channels */ - StopDMA(); -} - - -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; - if (sound.hard.size == 16) { - if (sound.volume_left == 64 && sound.volume_right == 64) { - custom.aud[2].audvol = 1; - custom.aud[3].audvol = 1; - } else { - custom.aud[2].audvol = 0; - custom.aud[3].audvol = 0; - } - } - return(VOLUME_AMI_TO_VOXWARE(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; - } - - custom.aud[0].audvol = sound.volume_left; - custom.aud[1].audvol = sound.volume_right; - 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.aud[2].audvol = 0; - custom.aud[3].audvol = 0; - custom.dmacon = AMI_AUDIO_8; - } - } - sq.front = (sq.front+1) % sq.max_count; - sq.active |= AMI_PLAY_LOADED; -} - - -static void AmiPlay(void) -{ - int minframes = 1; - - custom.intena = IF_AUD0; - - if (sq.active & AMI_PLAY_LOADED) { - /* There's already a frame loaded */ - custom.intena = IF_SETCLR | IF_AUD0; - return; - } - - if (sq.active & 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; - - custom.intena = IF_AUD0; - - if (!sq.active) { - /* 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.active & AMI_PLAY_PLAYING) { - /* We've just finished a frame */ - sq.count--; - WAKE_UP(sq.action_queue); - } - - if (sq.active & AMI_PLAY_LOADED) - /* Increase threshold: frame 1 is already being played */ - minframes = 2; - - /* Shift the flags */ - sq.active = (sq.active<<1) & AMI_PLAY_MASK; - - if (!sq.active) - /* No frame is playing, disable audio DMA */ - StopDMA(); - - custom.intena = IF_SETCLR | IF_AUD0; - - if (sq.count >= minframes) - /* Try to play the next frame */ - AmiPlay(); - - if (!sq.active) - /* Nothing to play anymore. - Wake up a process waiting for audio output to drain. */ - WAKE_UP(sq.sync_queue); -} -#endif /* CONFIG_AMIGA */ - -#ifdef CONFIG_PPC - -/* - * PCI PowerMac, with AWACS and DBDMA. - */ - -static void *PMacAlloc(unsigned int size, int flags) -{ - return kmalloc(size, flags); -} - -static void PMacFree(void *ptr, unsigned int size) -{ - kfree(ptr); -} - -static int __init PMacIrqInit(void) -{ - if (request_irq(awacs_irq, pmac_awacs_intr, 0, "AWACS", 0) - || request_irq(awacs_tx_irq, pmac_awacs_tx_intr, 0, "AWACS out", 0) - || request_irq(awacs_rx_irq, pmac_awacs_rx_intr, 0, "AWACS in", 0)) - return 0; - return 1; -} - -#ifdef MODULE -static void PMacIrqCleanup(void) -{ - /* turn off output dma */ - out_le32(&awacs_txdma->control, RUN<<16); - /* disable interrupts from awacs interface */ - out_le32(&awacs->control, in_le32(&awacs->control) & 0xfff); -#ifdef CONFIG_PMAC_PBOOK - if (is_pbook_G3) { - feature_clear(awacs_node, FEATURE_Sound_power); - feature_clear(awacs_node, FEATURE_Sound_CLK_enable); - } -#endif - free_irq(awacs_irq, 0); - free_irq(awacs_tx_irq, 0); - free_irq(awacs_rx_irq, 0); - kfree(awacs_tx_cmd_space); - if (awacs_rx_cmd_space) - kfree(awacs_rx_cmd_space); - if (beep_buf) - kfree(beep_buf); - kd_mksound = orig_mksound; -#ifdef CONFIG_PMAC_PBOOK - pmu_unregister_sleep_notifier(&awacs_sleep_notifier); -#endif -} -#endif /* MODULE */ - -static void PMacSilence(void) -{ - /* turn off output dma */ - out_le32(&awacs_txdma->control, RUN<<16); -} - -static int awacs_freqs[8] = { - 44100, 29400, 22050, 17640, 14700, 11025, 8820, 7350 -}; -static int awacs_freqs_ok[8] = { 1, 1, 1, 1, 1, 1, 1, 1 }; - -static void PMacInit(void) -{ - int i, tolerance; - - switch (sound.soft.format) { - case AFMT_S16_LE: - case AFMT_U16_LE: - sound.hard.format = AFMT_S16_LE; - break; - default: - sound.hard.format = AFMT_S16_BE; - break; - } - sound.hard.stereo = 1; - sound.hard.size = 16; - - /* - * If we have a sample rate which is within catchRadius percent - * of the requested value, we don't have to expand the samples. - * Otherwise choose the next higher rate. - * N.B.: burgundy awacs (iMac and later) only works at 44100 Hz. - */ - i = 8; - do { - tolerance = catchRadius * awacs_freqs[--i] / 100; - if (awacs_freqs_ok[i] - && sound.soft.speed <= awacs_freqs[i] + tolerance) - break; - } while (i > 0); - if (sound.soft.speed >= awacs_freqs[i] - tolerance) - sound.trans = &transAwacsNormal; - else - sound.trans = &transAwacsExpand; - sound.read_trans = &transAwacsNormalRead; - sound.hard.speed = awacs_freqs[i]; - awacs_rate_index = i; - - /* XXX disable error interrupt on burgundy for now */ - out_le32(&awacs->control, MASK_IEPC | (i << 8) | 0x11 - | (awacs_revision < AWACS_BURGUNDY? MASK_IEE: 0)); - awacs_reg[1] = (awacs_reg[1] & ~MASK_SAMPLERATE) | (i << 3); - awacs_write(awacs_reg[1] | MASK_ADDR1); - out_le32(&awacs->byteswap, sound.hard.format != AFMT_S16_BE); - - /* We really want to execute a DMA stop command, after the AWACS - * is initialized. - * For reasons I don't understand, it stops the hissing noise - * common to many PowerBook G3 systems (like mine :-). - */ - out_le32(&awacs_txdma->control, (RUN|WAKE|FLUSH|PAUSE) << 16); - st_le16(&beep_dbdma_cmd->command, DBDMA_STOP); - out_le32(&awacs_txdma->cmdptr, virt_to_bus(beep_dbdma_cmd)); - out_le32(&awacs_txdma->control, RUN | (RUN << 16)); - - sound.bal = -sound.soft.speed; -} - -static int PMacSetFormat(int format) -{ - int size; - - 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: /* :-) */ - printk(KERN_ERR "dmasound: unknown format 0x%x, using AFMT_U8\n", - format); - size = 8; - format = AFMT_U8; - } - - sound.soft.format = format; - sound.soft.size = size; - if (sound.minDev == SND_DEV_DSP) { - sound.dsp.format = format; - sound.dsp.size = size; - } - - PMacInit(); - - return format; -} - -#define AWACS_VOLUME_TO_MASK(x) (15 - ((((x) - 1) * 15) / 99)) -#define AWACS_MASK_TO_VOLUME(y) (100 - ((y) * 99 / 15)) - -static int awacs_get_volume(int reg, int lshift) -{ - int volume; - - volume = AWACS_MASK_TO_VOLUME((reg >> lshift) & 0xf); - volume |= AWACS_MASK_TO_VOLUME(reg & 0xf) << 8; - return volume; -} - -static int awacs_volume_setter(int volume, int n, int mute, int lshift) -{ - int r1, rn; - - if (mute && volume == 0) { - r1 = awacs_reg[1] | mute; - } else { - r1 = awacs_reg[1] & ~mute; - rn = awacs_reg[n] & ~(0xf | (0xf << lshift)); - rn |= ((AWACS_VOLUME_TO_MASK(volume & 0xff) & 0xf) << lshift); - rn |= AWACS_VOLUME_TO_MASK((volume >> 8) & 0xff) & 0xf; - awacs_reg[n] = rn; - awacs_write((n << 12) | rn); - volume = awacs_get_volume(rn, lshift); - } - if (r1 != awacs_reg[1]) { - awacs_reg[1] = r1; - awacs_write(r1 | MASK_ADDR1); - } - return volume; -} - -static int PMacSetVolume(int volume) -{ - return awacs_volume_setter(volume, 2, MASK_AMUTE, 6); -} - -static void PMacPlay(void) -{ - volatile struct dbdma_cmd *cp; - int i, count; - unsigned long flags; - - save_flags(flags); cli(); - if (awacs_beep_state) { - /* sound takes precedence over beeps */ - out_le32(&awacs_txdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16); - out_le32(&awacs->control, - (in_le32(&awacs->control) & ~0x1f00) - | (awacs_rate_index << 8)); - out_le32(&awacs->byteswap, sound.hard.format != AFMT_S16_BE); - out_le32(&awacs_txdma->cmdptr, virt_to_bus(&(awacs_tx_cmds[(sq.front+sq.active) % sq.max_count]))); - - beep_playing = 0; - awacs_beep_state = 0; - } - i = sq.front + sq.active; - if (i >= sq.max_count) - i -= sq.max_count; - while (sq.active < 2 && sq.active < sq.count) { - count = (sq.count == sq.active + 1)?sq.rear_size:sq.block_size; - if (count < sq.block_size && !sq.syncing) - /* last block not yet filled, and we're not syncing. */ - break; - cp = &awacs_tx_cmds[i]; - st_le16(&cp->req_count, count); - st_le16(&cp->xfer_status, 0); - if (++i >= sq.max_count) - i = 0; - out_le16(&awacs_tx_cmds[i].command, DBDMA_STOP); - out_le16(&cp->command, OUTPUT_MORE + INTR_ALWAYS); - if (sq.active == 0) - out_le32(&awacs_txdma->cmdptr, virt_to_bus(cp)); - out_le32(&awacs_txdma->control, ((RUN|WAKE) << 16) + (RUN|WAKE)); - ++sq.active; - } - restore_flags(flags); -} - - -static void PMacRecord(void) -{ - unsigned long flags; - - if (read_sq.active) - return; - - save_flags(flags); cli(); - - /* This is all we have to do......Just start it up. - */ - out_le32(&awacs_rxdma->control, ((RUN|WAKE) << 16) + (RUN|WAKE)); - read_sq.active = 1; - - restore_flags(flags); -} - - -static void -pmac_awacs_tx_intr(int irq, void *devid, struct pt_regs *regs) -{ - int i = sq.front; - int stat; - volatile struct dbdma_cmd *cp; - - while (sq.active > 0) { - cp = &awacs_tx_cmds[i]; - stat = ld_le16(&cp->xfer_status); - if ((stat & ACTIVE) == 0) - break; /* this frame is still going */ - --sq.count; - --sq.active; - if (++i >= sq.max_count) - i = 0; - } - if (i != sq.front) - WAKE_UP(sq.action_queue); - sq.front = i; - - PMacPlay(); - - if (!sq.active) - WAKE_UP(sq.sync_queue); -} - - -static void -pmac_awacs_rx_intr(int irq, void *devid, struct pt_regs *regs) -{ - - /* For some reason on my PowerBook G3, I get one interrupt - * when the interrupt vector is installed (like something is - * pending). This happens before the dbdma is initialize by - * us, so I just check the command pointer and if it is zero, - * just blow it off. - */ - if (in_le32(&awacs_rxdma->cmdptr) == 0) - return; - - /* We also want to blow 'em off when shutting down. - */ - if (read_sq.active == 0) - return; - - /* Check multiple buffers in case we were held off from - * interrupt processing for a long time. Geeze, I really hope - * this doesn't happen. - */ - while (awacs_rx_cmds[read_sq.rear].xfer_status) { - - /* Clear status and move on to next buffer. - */ - awacs_rx_cmds[read_sq.rear].xfer_status = 0; - read_sq.rear++; - - /* Wrap the buffer ring. - */ - if (read_sq.rear >= read_sq.max_active) - read_sq.rear = 0; - - /* If we have caught up to the front buffer, bump it. - * This will cause weird (but not fatal) results if the - * read loop is currently using this buffer. The user is - * behind in this case anyway, so weird things are going - * to happen. - */ - if (read_sq.rear == read_sq.front) { - read_sq.front++; - if (read_sq.front >= read_sq.max_active) - read_sq.front = 0; - } - } - - WAKE_UP(read_sq.action_queue); -} - - -static void -pmac_awacs_intr(int irq, void *devid, struct pt_regs *regs) -{ - int ctrl = in_le32(&awacs->control); - - if (ctrl & MASK_PORTCHG) { - /* do something when headphone is plugged/unplugged? */ - } - if (ctrl & MASK_CNTLERR) { - int err = (in_le32(&awacs->codec_stat) & MASK_ERRCODE) >> 16; - if (err != 0 && awacs_revision < AWACS_BURGUNDY) - printk(KERN_ERR "AWACS: error %x\n", err); - } - /* Writing 1s to the CNTLERR and PORTCHG bits clears them... */ - out_le32(&awacs->control, ctrl); -} - -static void -awacs_write(int val) -{ - if (awacs_revision >= AWACS_BURGUNDY) - return; - while (in_le32(&awacs->codec_ctrl) & MASK_NEWECMD) - ; /* XXX should have timeout */ - out_le32(&awacs->codec_ctrl, val | (awacs_subframe << 22)); -} - -static void awacs_nosound(unsigned long xx) -{ - unsigned long flags; - - save_flags(flags); cli(); - if (beep_playing) { - st_le16(&beep_dbdma_cmd->command, DBDMA_STOP); - out_le32(&awacs_txdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16); - out_le32(&awacs->control, - (in_le32(&awacs->control) & ~0x1f00) - | (awacs_rate_index << 8)); - out_le32(&awacs->byteswap, sound.hard.format != AFMT_S16_BE); - beep_playing = 0; - } - restore_flags(flags); -} - -static struct timer_list beep_timer = { - NULL, NULL, 0, 0, awacs_nosound -}; - -static void awacs_mksound(unsigned int hz, unsigned int ticks) -{ - unsigned long flags; - int beep_speed = 0; - int srate; - int period, ncycles, nsamples; - int i, j, f; - short *p; - static int beep_hz_cache; - static int beep_nsamples_cache; - static int beep_volume_cache; - - for (i = 0; i < 8 && awacs_freqs[i] >= BEEP_SRATE; ++i) - if (awacs_freqs_ok[i]) - beep_speed = i; - srate = awacs_freqs[beep_speed]; - - if (hz <= srate / BEEP_BUFLEN || hz > srate / 2) { -#if 1 - /* this is a hack for broken X server code */ - hz = 750; - ticks = 12; -#else - /* cancel beep currently playing */ - awacs_nosound(0); - return; -#endif - } - save_flags(flags); cli(); - del_timer(&beep_timer); - if (ticks) { - beep_timer.expires = jiffies + ticks; - add_timer(&beep_timer); - } - if (beep_playing || sq.active || beep_buf == NULL) { - restore_flags(flags); - return; /* too hard, sorry :-( */ - } - beep_playing = 1; - st_le16(&beep_dbdma_cmd->command, OUTPUT_MORE + BR_ALWAYS); - restore_flags(flags); - - if (hz == beep_hz_cache && beep_volume == beep_volume_cache) { - nsamples = beep_nsamples_cache; - } else { - period = srate * 256 / hz; /* fixed point */ - ncycles = BEEP_BUFLEN * 256 / period; - nsamples = (period * ncycles) >> 8; - f = ncycles * 65536 / nsamples; - j = 0; - p = beep_buf; - for (i = 0; i < nsamples; ++i, p += 2) { - p[0] = p[1] = beep_wform[j >> 8] * beep_volume; - j = (j + f) & 0xffff; - } - beep_hz_cache = hz; - beep_volume_cache = beep_volume; - beep_nsamples_cache = nsamples; - } - - st_le16(&beep_dbdma_cmd->req_count, nsamples*4); - st_le16(&beep_dbdma_cmd->xfer_status, 0); - st_le32(&beep_dbdma_cmd->cmd_dep, virt_to_bus(beep_dbdma_cmd)); - st_le32(&beep_dbdma_cmd->phy_addr, virt_to_bus(beep_buf)); - awacs_beep_state = 1; - - save_flags(flags); cli(); - if (beep_playing) { /* i.e. haven't been terminated already */ - out_le32(&awacs_txdma->control, (RUN|WAKE|FLUSH|PAUSE) << 16); - out_le32(&awacs->control, - (in_le32(&awacs->control) & ~0x1f00) - | (beep_speed << 8)); - out_le32(&awacs->byteswap, 0); - out_le32(&awacs_txdma->cmdptr, virt_to_bus(beep_dbdma_cmd)); - out_le32(&awacs_txdma->control, RUN | (RUN << 16)); - } - restore_flags(flags); -} - -#ifdef CONFIG_PMAC_PBOOK -/* - * Save state when going to sleep, restore it afterwards. - */ -static int awacs_sleep_notify(struct pmu_sleep_notifier *self, int when) -{ - switch (when) { - case PBOOK_SLEEP_NOW: - /* XXX we should stop any dma in progress when going to sleep - and restart it when we wake. */ - PMacSilence(); - disable_irq(awacs_irq); - disable_irq(awacs_tx_irq); - if (is_pbook_G3) { - feature_clear(awacs_node, FEATURE_Sound_CLK_enable); - feature_clear(awacs_node, FEATURE_Sound_power); - } - break; - case PBOOK_WAKE: - /* There is still a problem on wake. Sound seems to work fine - if I launch mpg123 and resumes fine if mpg123 was playing, - but the console beep is dead until I do something with the - mixer. Probably yet another timing issue */ - if (!feature_test(awacs_node, FEATURE_Sound_CLK_enable) - || !feature_test(awacs_node, FEATURE_Sound_power)) { - /* these aren't present on the 3400 AFAIK -- paulus */ - feature_set(awacs_node, FEATURE_Sound_CLK_enable); - feature_set(awacs_node, FEATURE_Sound_power); - mdelay(1000); - } - out_le32(&awacs->control, MASK_IEPC - | (awacs_rate_index << 8) | 0x11 - | (awacs_revision < AWACS_BURGUNDY? MASK_IEE: 0)); - awacs_write(awacs_reg[0] | MASK_ADDR0); - awacs_write(awacs_reg[1] | MASK_ADDR1); - awacs_write(awacs_reg[2] | MASK_ADDR2); - awacs_write(awacs_reg[4] | MASK_ADDR4); - out_le32(&awacs->byteswap, sound.hard.format != AFMT_S16_BE); - enable_irq(awacs_irq); - enable_irq(awacs_tx_irq); - if (awacs_revision == 3) { - mdelay(100); - awacs_write(0x6000); - mdelay(2); - awacs_write(awacs_reg[1] | MASK_ADDR1); - } - /* enable CD sound input */ - if (macio_base && is_pbook_G3) { - out_8(macio_base + 0x37, 3); - } else if (is_pbook_3400) { - feature_set(awacs_node, FEATURE_IOBUS_enable); - udelay(10); - in_8(latch_base + 0x190); - } - /* Resume pending sounds. */ - PMacPlay(); - } - return PBOOK_SLEEP_OK; -} -#endif /* CONFIG_PMAC_PBOOK */ - - -/* All the burgundy functions: */ - -/* Waits for busy flag to clear */ -inline static void -awacs_burgundy_busy_wait(void) -{ - while (in_le32(&awacs->codec_ctrl) & MASK_NEWECMD) - ; -} - -inline static void -awacs_burgundy_extend_wait(void) -{ - while (!(in_le32(&awacs->codec_stat) & MASK_EXTEND)) - ; - while (in_le32(&awacs->codec_stat) & MASK_EXTEND) - ; -} - -static void -awacs_burgundy_wcw(unsigned addr, unsigned val) -{ - out_le32(&awacs->codec_ctrl, addr + 0x200c00 + (val & 0xff)); - awacs_burgundy_busy_wait(); - out_le32(&awacs->codec_ctrl, addr + 0x200d00 +((val>>8) & 0xff)); - awacs_burgundy_busy_wait(); - out_le32(&awacs->codec_ctrl, addr + 0x200e00 +((val>>16) & 0xff)); - awacs_burgundy_busy_wait(); - out_le32(&awacs->codec_ctrl, addr + 0x200f00 +((val>>24) & 0xff)); - awacs_burgundy_busy_wait(); -} - -static unsigned -awacs_burgundy_rcw(unsigned addr) -{ - unsigned val = 0; - unsigned long flags; - - /* should have timeouts here */ - save_flags(flags); cli(); - - out_le32(&awacs->codec_ctrl, addr + 0x100000); - awacs_burgundy_busy_wait(); - awacs_burgundy_extend_wait(); - val += (in_le32(&awacs->codec_stat) >> 4) & 0xff; - - out_le32(&awacs->codec_ctrl, addr + 0x100100); - awacs_burgundy_busy_wait(); - awacs_burgundy_extend_wait(); - val += ((in_le32(&awacs->codec_stat)>>4) & 0xff) <<8; - - out_le32(&awacs->codec_ctrl, addr + 0x100200); - awacs_burgundy_busy_wait(); - awacs_burgundy_extend_wait(); - val += ((in_le32(&awacs->codec_stat)>>4) & 0xff) <<16; - - out_le32(&awacs->codec_ctrl, addr + 0x100300); - awacs_burgundy_busy_wait(); - awacs_burgundy_extend_wait(); - val += ((in_le32(&awacs->codec_stat)>>4) & 0xff) <<24; - - restore_flags(flags); - - return val; -} - - -static void -awacs_burgundy_wcb(unsigned addr, unsigned val) -{ - out_le32(&awacs->codec_ctrl, addr + 0x300000 + (val & 0xff)); - awacs_burgundy_busy_wait(); -} - -static unsigned -awacs_burgundy_rcb(unsigned addr) -{ - unsigned val = 0; - unsigned long flags; - - /* should have timeouts here */ - save_flags(flags); cli(); - - out_le32(&awacs->codec_ctrl, addr + 0x100000); - awacs_burgundy_busy_wait(); - awacs_burgundy_extend_wait(); - val += (in_le32(&awacs->codec_stat) >> 4) & 0xff; - - restore_flags(flags); - - return val; -} - -static int -awacs_burgundy_check(void) -{ - /* Checks to see the chip is alive and kicking */ - int error = in_le32(&awacs->codec_ctrl) & MASK_ERRCODE; - - return error == 0xf0000; -} - -static int -awacs_burgundy_init(void) -{ - if (awacs_burgundy_check()) { - printk(KERN_WARNING "AWACS: disabled by MacOS :-(\n"); - return 1; - } - - awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_OUTPUTENABLES, - DEF_BURGUNDY_OUTPUTENABLES); - awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, - DEF_BURGUNDY_MORE_OUTPUTENABLES); - awacs_burgundy_wcw(MASK_ADDR_BURGUNDY_OUTPUTSELECTS, - DEF_BURGUNDY_OUTPUTSELECTS); - - awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_INPSEL21, - DEF_BURGUNDY_INPSEL21); - awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_INPSEL3, - DEF_BURGUNDY_INPSEL3); - awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_GAINCD, - DEF_BURGUNDY_GAINCD); - awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_GAINLINE, - DEF_BURGUNDY_GAINLINE); - awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_GAINMIC, - DEF_BURGUNDY_GAINMIC); - awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_GAINMODEM, - DEF_BURGUNDY_GAINMODEM); - - awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_ATTENSPEAKER, - DEF_BURGUNDY_ATTENSPEAKER); - awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_ATTENLINEOUT, - DEF_BURGUNDY_ATTENLINEOUT); - awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_ATTENHP, - DEF_BURGUNDY_ATTENHP); - - awacs_burgundy_wcw(MASK_ADDR_BURGUNDY_MASTER_VOLUME, - DEF_BURGUNDY_MASTER_VOLUME); - awacs_burgundy_wcw(MASK_ADDR_BURGUNDY_VOLCD, - DEF_BURGUNDY_VOLCD); - awacs_burgundy_wcw(MASK_ADDR_BURGUNDY_VOLLINE, - DEF_BURGUNDY_VOLLINE); - awacs_burgundy_wcw(MASK_ADDR_BURGUNDY_VOLMIC, - DEF_BURGUNDY_VOLMIC); - return 0; -} - -static void -awacs_burgundy_write_volume(unsigned address, int volume) -{ - int hardvolume,lvolume,rvolume; - - lvolume = (volume & 0xff) ? (volume & 0xff) + 155 : 0; - rvolume = ((volume >>8)&0xff) ? ((volume >> 8)&0xff ) + 155 : 0; - - hardvolume = lvolume + (rvolume << 16); - - awacs_burgundy_wcw(address, hardvolume); -} - -static int -awacs_burgundy_read_volume(unsigned address) -{ - int softvolume,wvolume; - - wvolume = awacs_burgundy_rcw(address); - - softvolume = (wvolume & 0xff) - 155; - softvolume += (((wvolume >> 16) & 0xff) - 155)<<8; - - return softvolume > 0 ? softvolume : 0; -} - - - - -static int -awacs_burgundy_read_mvolume(unsigned address) -{ - int lvolume,rvolume,wvolume; - - wvolume = awacs_burgundy_rcw(address); - - wvolume &= 0xffff; - - rvolume = (wvolume & 0xff) - 155; - lvolume = ((wvolume & 0xff00)>>8) - 155; - - return lvolume + (rvolume << 8); -} - - -static void -awacs_burgundy_write_mvolume(unsigned address, int volume) -{ - int lvolume,rvolume,hardvolume; - - lvolume = (volume &0xff) ? (volume & 0xff) + 155 :0; - rvolume = ((volume >>8) & 0xff) ? (volume >> 8) + 155 :0; - - hardvolume = lvolume + (rvolume << 8); - hardvolume += (hardvolume << 16); - - awacs_burgundy_wcw(address, hardvolume); -} - -/* End burgundy functions */ - - - - - -/* Turn on sound output, needed on G3 desktop powermacs */ -static void -awacs_enable_amp(int spkr_vol) -{ - struct adb_request req; - - awacs_spkr_vol = spkr_vol; - if (sys_ctrler != SYS_CTRLER_CUDA) - return; - - /* turn on headphones */ - cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC, - 0x8a, 4, 0); - while (!req.complete) cuda_poll(); - cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC, - 0x8a, 6, 0); - while (!req.complete) cuda_poll(); - - /* turn on speaker */ - cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC, - 0x8a, 3, (100 - (spkr_vol & 0xff)) * 32 / 100); - while (!req.complete) cuda_poll(); - cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC, - 0x8a, 5, (100 - ((spkr_vol >> 8) & 0xff)) * 32 / 100); - while (!req.complete) cuda_poll(); - - cuda_request(&req, NULL, 5, CUDA_PACKET, - CUDA_GET_SET_IIC, 0x8a, 1, 0x29); - while (!req.complete) cuda_poll(); -} - -#endif /* CONFIG_PPC */ - -/*** Machine definitions *****************************************************/ - - -#ifdef CONFIG_ATARI -static MACHINE machTT = { - DMASND_TT, AtaAlloc, AtaFree, AtaIrqInit, -#ifdef MODULE - AtaIrqCleanUp, -#endif /* MODULE */ - TTInit, TTSilence, TTSetFormat, TTSetVolume, - AtaSetBass, AtaSetTreble, TTSetGain, - AtaPlay -}; - -static MACHINE machFalcon = { - DMASND_FALCON, AtaAlloc, AtaFree, AtaIrqInit, -#ifdef MODULE - AtaIrqCleanUp, -#endif /* MODULE */ - FalconInit, FalconSilence, FalconSetFormat, FalconSetVolume, - AtaSetBass, AtaSetTreble, NULL, - 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, NULL, - AmiPlay -}; -#endif /* CONFIG_AMIGA */ - -#ifdef CONFIG_PPC -static MACHINE machPMac = { - DMASND_AWACS, PMacAlloc, PMacFree, PMacIrqInit, -#ifdef MODULE - PMacIrqCleanup, -#endif /* MODULE */ - PMacInit, PMacSilence, PMacSetFormat, PMacSetVolume, - NULL, NULL, NULL, /* bass, treble, gain */ - PMacPlay -}; -#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); -} - -static int sound_set_gain(int gain) -{ - return sound.mach.setGain ? sound.mach.setGain(gain) : 100; -} -#endif /* CONFIG_ATARI */ - -#if defined(CONFIG_ATARI) || defined(CONFIG_AMIGA) -static int sound_set_treble(int treble) -{ - return(sound.mach.setTreble ? (*sound.mach.setTreble)(treble) : 50); -} -#endif /* CONFIG_ATARI || CONFIG_AMIGA */ - - -static ssize_t sound_copy_translate(const u_char *userPtr, - size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - ssize_t (*ct_func)(const u_char *, size_t, u_char *, ssize_t *, ssize_t) = 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; -} - -#ifdef CONFIG_PPC -static ssize_t sound_copy_translate_read(const u_char *userPtr, - size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - ssize_t (*ct_func)(const u_char *, size_t, u_char *, ssize_t *, ssize_t) = NULL; - - switch (sound.soft.format) { - case AFMT_MU_LAW: - ct_func = sound.read_trans->ct_ulaw; - break; - case AFMT_A_LAW: - ct_func = sound.read_trans->ct_alaw; - break; - case AFMT_S8: - ct_func = sound.read_trans->ct_s8; - break; - case AFMT_U8: - ct_func = sound.read_trans->ct_u8; - break; - case AFMT_S16_BE: - ct_func = sound.read_trans->ct_s16be; - break; - case AFMT_U16_BE: - ct_func = sound.read_trans->ct_u16be; - break; - case AFMT_S16_LE: - ct_func = sound.read_trans->ct_s16le; - break; - case AFMT_U16_LE: - ct_func = sound.read_trans->ct_u16le; - break; - } - if (ct_func) - return ct_func(userPtr, userCount, frame, frameUsed, frameLeft); - else - return 0; -} -#endif - - -/* - * /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 int mixer_open(struct inode *inode, struct file *file) -{ - MOD_INC_USE_COUNT; - mixer.busy = 1; - return 0; -} - - -static int mixer_release(struct inode *inode, struct file *file) -{ - mixer.busy = 0; - MOD_DEC_USE_COUNT; - return 0; -} - - -static int mixer_ioctl(struct inode *inode, struct file *file, u_int cmd, - u_long arg) -{ - int data; - if (_SIOC_DIR(cmd) & _SIOC_WRITE) - mixer.modify_counter++; - if (cmd == OSS_GETVERSION) - return IOCTL_OUT(arg, SOUND_VERSION); - switch (sound.mach.type) { -#ifdef CONFIG_ATARI - case DMASND_FALCON: - switch (cmd) { - case SOUND_MIXER_INFO: { - mixer_info info; - strncpy(info.id, "FALCON", sizeof(info.id)); - strncpy(info.name, "FALCON", sizeof(info.name)); - info.name[sizeof(info.name)-1] = 0; - info.modify_counter = mixer.modify_counter; - copy_to_user_ret((int *)arg, &info, sizeof(info), -EFAULT); - return 0; - } - 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_INFO: { - mixer_info info; - strncpy(info.id, "TT", sizeof(info.id)); - strncpy(info.name, "TT", sizeof(info.name)); - info.name[sizeof(info.name)-1] = 0; - info.modify_counter = mixer.modify_counter; - copy_to_user_ret((int *)arg, &info, sizeof(info), -EFAULT); - return 0; - } - case SOUND_MIXER_READ_DEVMASK: - return IOCTL_OUT(arg, - SOUND_MASK_VOLUME | SOUND_MASK_TREBLE | SOUND_MASK_BASS | - (MACH_IS_TT ? SOUND_MASK_SPEAKER : 0)); - case SOUND_MIXER_READ_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_OGAIN: - return IOCTL_OUT(arg, GAIN_DB_TO_VOXWARE(sound.gain)); - case SOUND_MIXER_READ_SPEAKER: - { - int porta; - if (MACH_IS_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); - } - } - break; - 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_OGAIN: - IOCTL_IN(arg, data); - return IOCTL_OUT(arg, sound_set_gain(data)); - case SOUND_MIXER_WRITE_SPEAKER: - if (MACH_IS_TT) { - int porta; - IOCTL_IN(arg, data); - cli(); - sound_ym.rd_data_reg_sel = 14; - porta = (sound_ym.rd_data_reg_sel & ~0x40) | - (data < 50 ? 0x40 : 0); - sound_ym.wd_data = porta; - sti(); - return IOCTL_OUT(arg, porta & 0x40 ? 0 : 100); - } - } - break; -#endif /* CONFIG_ATARI */ - -#ifdef CONFIG_AMIGA - case DMASND_AMIGA: - switch (cmd) { - case SOUND_MIXER_INFO: { - mixer_info info; - strncpy(info.id, "AMIGA", sizeof(info.id)); - strncpy(info.name, "AMIGA", sizeof(info.name)); - info.name[sizeof(info.name)-1] = 0; - info.modify_counter = mixer.modify_counter; - copy_to_user_ret((int *)arg, &info, sizeof(info), -EFAULT); - return 0; - } - 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 */ - -#ifdef CONFIG_PPC - case DMASND_AWACS: - /* Different IOCTLS for burgundy*/ - if (awacs_revision < AWACS_BURGUNDY) { - switch (cmd) { - case SOUND_MIXER_INFO: { - mixer_info info; - strncpy(info.id, "AWACS", sizeof(info.id)); - strncpy(info.name, "AWACS", sizeof(info.name)); - info.name[sizeof(info.name)-1] = 0; - info.modify_counter = mixer.modify_counter; - copy_to_user_ret((int *)arg, &info, - sizeof(info), -EFAULT); - return 0; - } - case SOUND_MIXER_READ_DEVMASK: - data = SOUND_MASK_VOLUME | SOUND_MASK_SPEAKER - | SOUND_MASK_LINE | SOUND_MASK_MIC - | SOUND_MASK_CD | SOUND_MASK_RECLEV - | SOUND_MASK_ALTPCM - | SOUND_MASK_MONITOR; - return IOCTL_OUT(arg, data); - case SOUND_MIXER_READ_RECMASK: - data = SOUND_MASK_LINE | SOUND_MASK_MIC - | SOUND_MASK_CD; - return IOCTL_OUT(arg, data); - case SOUND_MIXER_READ_RECSRC: - data = 0; - if (awacs_reg[0] & MASK_MUX_AUDIN) - data |= SOUND_MASK_LINE; - if (awacs_reg[0] & MASK_MUX_MIC) - data |= SOUND_MASK_MIC; - if (awacs_reg[0] & MASK_MUX_CD) - data |= SOUND_MASK_CD; - if (awacs_reg[1] & MASK_LOOPTHRU) - data |= SOUND_MASK_MONITOR; - return IOCTL_OUT(arg, data); - case SOUND_MIXER_WRITE_RECSRC: - IOCTL_IN(arg, data); - data &= (SOUND_MASK_LINE - | SOUND_MASK_MIC | SOUND_MASK_CD - | SOUND_MASK_MONITOR); - awacs_reg[0] &= ~(MASK_MUX_CD | MASK_MUX_MIC - | MASK_MUX_AUDIN); - awacs_reg[1] &= ~MASK_LOOPTHRU; - if (data & SOUND_MASK_LINE) - awacs_reg[0] |= MASK_MUX_AUDIN; - if (data & SOUND_MASK_MIC) - awacs_reg[0] |= MASK_MUX_MIC; - if (data & SOUND_MASK_CD) - awacs_reg[0] |= MASK_MUX_CD; - if (data & SOUND_MASK_MONITOR) - awacs_reg[1] |= MASK_LOOPTHRU; - awacs_write(awacs_reg[0] | MASK_ADDR0); - awacs_write(awacs_reg[1] | MASK_ADDR1); - return IOCTL_OUT(arg, data); - case SOUND_MIXER_READ_STEREODEVS: - data = SOUND_MASK_VOLUME | SOUND_MASK_SPEAKER - | SOUND_MASK_RECLEV; - return IOCTL_OUT(arg, data); - case SOUND_MIXER_READ_CAPS: - return IOCTL_OUT(arg, 0); - case SOUND_MIXER_READ_VOLUME: - data = (awacs_reg[1] & MASK_AMUTE)? 0: - awacs_get_volume(awacs_reg[2], 6); - return IOCTL_OUT(arg, data); - case SOUND_MIXER_WRITE_VOLUME: - IOCTL_IN(arg, data); - return IOCTL_OUT(arg, sound_set_volume(data)); - case SOUND_MIXER_READ_SPEAKER: - if (awacs_revision == 3 - && sys_ctrler == SYS_CTRLER_CUDA) - data = awacs_spkr_vol; - else - data = (awacs_reg[1] & MASK_CMUTE)? 0: - awacs_get_volume(awacs_reg[4], 6); - return IOCTL_OUT(arg, data); - case SOUND_MIXER_WRITE_SPEAKER: - IOCTL_IN(arg, data); - if (awacs_revision == 3 - && sys_ctrler == SYS_CTRLER_CUDA) - awacs_enable_amp(data); - else - data = awacs_volume_setter(data, 4, MASK_CMUTE, 6); - return IOCTL_OUT(arg, data); - case SOUND_MIXER_WRITE_ALTPCM: /* really bell volume */ - IOCTL_IN(arg, data); - beep_volume = data & 0xff; - /* fall through */ - case SOUND_MIXER_READ_ALTPCM: - return IOCTL_OUT(arg, beep_volume); - case SOUND_MIXER_WRITE_LINE: - IOCTL_IN(arg, data); - awacs_reg[0] &= ~MASK_MUX_AUDIN; - if ((data & 0xff) >= 50) - awacs_reg[0] |= MASK_MUX_AUDIN; - awacs_write(MASK_ADDR0 | awacs_reg[0]); - /* fall through */ - case SOUND_MIXER_READ_LINE: - data = (awacs_reg[0] & MASK_MUX_AUDIN)? 100: 0; - return IOCTL_OUT(arg, data); - case SOUND_MIXER_WRITE_MIC: - IOCTL_IN(arg, data); - data &= 0xff; - awacs_reg[0] &= ~(MASK_MUX_MIC | MASK_GAINLINE); - if (data >= 25) { - awacs_reg[0] |= MASK_MUX_MIC; - if (data >= 75) - awacs_reg[0] |= MASK_GAINLINE; - } - awacs_write(MASK_ADDR0 | awacs_reg[0]); - /* fall through */ - case SOUND_MIXER_READ_MIC: - data = (awacs_reg[0] & MASK_MUX_MIC)? - (awacs_reg[0] & MASK_GAINLINE? 100: 50): 0; - return IOCTL_OUT(arg, data); - case SOUND_MIXER_WRITE_CD: - IOCTL_IN(arg, data); - awacs_reg[0] &= ~MASK_MUX_CD; - if ((data & 0xff) >= 50) - awacs_reg[0] |= MASK_MUX_CD; - awacs_write(MASK_ADDR0 | awacs_reg[0]); - /* fall through */ - case SOUND_MIXER_READ_CD: - data = (awacs_reg[0] & MASK_MUX_CD)? 100: 0; - return IOCTL_OUT(arg, data); - case SOUND_MIXER_WRITE_RECLEV: - IOCTL_IN(arg, data); - data = awacs_volume_setter(data, 0, 0, 4); - return IOCTL_OUT(arg, data); - case SOUND_MIXER_READ_RECLEV: - data = awacs_get_volume(awacs_reg[0], 4); - return IOCTL_OUT(arg, data); - } - break; - } else { - /* We are, we are, we are... Burgundy or better */ - switch(cmd) { - case SOUND_MIXER_INFO: { - mixer_info info; - strncpy(info.id, "AWACS", sizeof(info.id)); - strncpy(info.name, "AWACS", sizeof(info.name)); - info.name[sizeof(info.name)-1] = 0; - info.modify_counter = mixer.modify_counter; - copy_to_user_ret((int *)arg, &info, - sizeof(info), -EFAULT); - return 0; - } - case SOUND_MIXER_READ_DEVMASK: - data = SOUND_MASK_VOLUME | SOUND_MASK_CD | - SOUND_MASK_LINE | SOUND_MASK_MIC | - SOUND_MASK_SPEAKER | SOUND_MASK_ALTPCM; - return IOCTL_OUT(arg, data); - case SOUND_MIXER_READ_RECMASK: - data = SOUND_MASK_LINE | SOUND_MASK_MIC - | SOUND_MASK_CD; - return IOCTL_OUT(arg, data); - case SOUND_MIXER_READ_RECSRC: - data = 0; - if (awacs_reg[0] & MASK_MUX_AUDIN) - data |= SOUND_MASK_LINE; - if (awacs_reg[0] & MASK_MUX_MIC) - data |= SOUND_MASK_MIC; - if (awacs_reg[0] & MASK_MUX_CD) - data |= SOUND_MASK_CD; - return IOCTL_OUT(arg, data); - case SOUND_MIXER_WRITE_RECSRC: - IOCTL_IN(arg, data); - data &= (SOUND_MASK_LINE - | SOUND_MASK_MIC | SOUND_MASK_CD); - awacs_reg[0] &= ~(MASK_MUX_CD | MASK_MUX_MIC - | MASK_MUX_AUDIN); - if (data & SOUND_MASK_LINE) - awacs_reg[0] |= MASK_MUX_AUDIN; - if (data & SOUND_MASK_MIC) - awacs_reg[0] |= MASK_MUX_MIC; - if (data & SOUND_MASK_CD) - awacs_reg[0] |= MASK_MUX_CD; - awacs_write(awacs_reg[0] | MASK_ADDR0); - return IOCTL_OUT(arg, data); - case SOUND_MIXER_READ_STEREODEVS: - data = SOUND_MASK_VOLUME | SOUND_MASK_SPEAKER - | SOUND_MASK_RECLEV | SOUND_MASK_CD - | SOUND_MASK_LINE; - return IOCTL_OUT(arg, data); - case SOUND_MIXER_READ_CAPS: - return IOCTL_OUT(arg, 0); - case SOUND_MIXER_WRITE_VOLUME: - IOCTL_IN(arg, data); - awacs_burgundy_write_mvolume(MASK_ADDR_BURGUNDY_MASTER_VOLUME, data); - /* Fall through */ - case SOUND_MIXER_READ_VOLUME: - return IOCTL_OUT(arg, awacs_burgundy_read_mvolume(MASK_ADDR_BURGUNDY_MASTER_VOLUME)); - case SOUND_MIXER_WRITE_SPEAKER: - IOCTL_IN(arg, data); - - if (!(data & 0xff)) { - /* Mute the left speaker */ - awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, - awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES) & ~0x2); - } else { - /* Unmute the left speaker */ - awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, - awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES) | 0x2); - } - if (!(data & 0xff00)) { - /* Mute the right speaker */ - awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, - awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES) & ~0x4); - } else { - /* Unmute the right speaker */ - awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, - awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES) | 0x4); - } - - data = (((data&0xff)*16)/100 > 0xf ? 0xf : - (((data&0xff)*16)/100)) + - ((((data>>8)*16)/100 > 0xf ? 0xf : - ((((data>>8)*16)/100)))<<4); - - awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_ATTENSPEAKER, ~data); - /* Fall through */ - case SOUND_MIXER_READ_SPEAKER: - data = awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_ATTENSPEAKER); - data = (((data & 0xf)*100)/16) + ((((data>>4)*100)/16)<<8); - return IOCTL_OUT(arg, ~data); - case SOUND_MIXER_WRITE_ALTPCM: /* really bell volume */ - IOCTL_IN(arg, data); - beep_volume = data & 0xff; - /* fall through */ - case SOUND_MIXER_READ_ALTPCM: - return IOCTL_OUT(arg, beep_volume); - case SOUND_MIXER_WRITE_LINE: - IOCTL_IN(arg, data); - awacs_burgundy_write_volume(MASK_ADDR_BURGUNDY_VOLLINE, data); - - /* fall through */ - case SOUND_MIXER_READ_LINE: - data = awacs_burgundy_read_volume(MASK_ADDR_BURGUNDY_VOLLINE); - return IOCTL_OUT(arg, data); - case SOUND_MIXER_WRITE_MIC: - IOCTL_IN(arg, data); - /* Mic is mono device */ - data = (data << 8) + (data << 24); - awacs_burgundy_write_volume(MASK_ADDR_BURGUNDY_VOLMIC, data); - /* fall through */ - case SOUND_MIXER_READ_MIC: - data = awacs_burgundy_read_volume(MASK_ADDR_BURGUNDY_VOLMIC); - data <<= 24; - return IOCTL_OUT(arg, data); - case SOUND_MIXER_WRITE_CD: - IOCTL_IN(arg, data); - awacs_burgundy_write_volume(MASK_ADDR_BURGUNDY_VOLCD, data); - /* fall through */ - case SOUND_MIXER_READ_CD: - data = awacs_burgundy_read_volume(MASK_ADDR_BURGUNDY_VOLCD); - return IOCTL_OUT(arg, data); - case SOUND_MIXER_WRITE_RECLEV: - IOCTL_IN(arg, data); - data = awacs_volume_setter(data, 0, 0, 4); - return IOCTL_OUT(arg, data); - case SOUND_MIXER_READ_RECLEV: - data = awacs_get_volume(awacs_reg[0], 4); - return IOCTL_OUT(arg, data); - case SOUND_MIXER_OUTMASK: - break; - case SOUND_MIXER_OUTSRC: - break; - } - break; - } -#endif - } - - return -EINVAL; -} - - -static struct file_operations mixer_fops = -{ - llseek: sound_lseek, - ioctl: mixer_ioctl, - open: mixer_open, - release: mixer_release, -}; - - -static void __init mixer_init(void) -{ -#ifndef MODULE - int mixer_unit; -#endif - mixer_unit = register_sound_mixer(&mixer_fops, -1); - if (mixer_unit < 0) - return; - - mixer.busy = 0; - 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 */ - } -} - - -/* - * Sound queue stuff, the heart of the driver - */ - - -static int sq_allocate_buffers(void) -{ - int i; - - if (sound_buffers) - return 0; - sound_buffers = kmalloc (numBufs * sizeof(char *), GFP_KERNEL); - if (!sound_buffers) - return -ENOMEM; - 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; - return -ENOMEM; - } - } - return 0; -} - - -static void sq_release_buffers(void) -{ - int i; - - if (sound_buffers) { - for (i = 0; i < numBufs; i++) - sound.mach.dma_free (sound_buffers[i], bufSize << 10); - kfree (sound_buffers); - sound_buffers = 0; - } -} - - -#ifdef CONFIG_PPC -static int sq_allocate_read_buffers(void) -{ - int i; - int j; - - if (sound_read_buffers) - return 0; - sound_read_buffers = kmalloc(numReadBufs * sizeof(char *), GFP_KERNEL); - if (!sound_read_buffers) - return -ENOMEM; - for (i = 0; i < numBufs; i++) { - sound_read_buffers[i] = sound.mach.dma_alloc (readbufSize<<10, - GFP_KERNEL); - if (!sound_read_buffers[i]) { - while (i--) - sound.mach.dma_free (sound_read_buffers[i], - readbufSize << 10); - kfree (sound_read_buffers); - sound_read_buffers = 0; - return -ENOMEM; - } - /* XXXX debugging code */ - for (j=0; j<readbufSize; j++) { - sound_read_buffers[i][j] = 0xef; - } - } - return 0; -} - -static void sq_release_read_buffers(void) -{ - int i; - volatile struct dbdma_cmd *cp; - - - if (sound_read_buffers) { - cp = awacs_rx_cmds; - for (i = 0; i < numReadBufs; i++,cp++) { - st_le16(&cp->command, DBDMA_STOP); - } - /* We should probably wait for the thing to stop before we - release the memory */ - for (i = 0; i < numBufs; i++) - sound.mach.dma_free (sound_read_buffers[i], - bufSize << 10); - kfree (sound_read_buffers); - sound_read_buffers = 0; - } -} -#endif - - -static void sq_setup(int numBufs, int bufSize, char **write_buffers) -{ -#ifdef CONFIG_PPC - int i; - volatile struct dbdma_cmd *cp; -#endif /* CONFIG_PPC */ - - sq.max_count = numBufs; - sq.max_active = numBufs; - sq.block_size = bufSize; - sq.buffers = write_buffers; - - sq.front = sq.count = 0; - sq.rear = -1; - sq.syncing = 0; - sq.active = 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 */ -#ifdef CONFIG_PPC - cp = awacs_tx_cmds; - memset((void *) cp, 0, (numBufs + 1) * sizeof(struct dbdma_cmd)); - for (i = 0; i < numBufs; ++i, ++cp) { - st_le32(&cp->phy_addr, virt_to_bus(write_buffers[i])); - } - st_le16(&cp->command, DBDMA_NOP + BR_ALWAYS); - st_le32(&cp->cmd_dep, virt_to_bus(awacs_tx_cmds)); - out_le32(&awacs_txdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16); - out_le32(&awacs_txdma->cmdptr, virt_to_bus(awacs_tx_cmds)); -#endif /* CONFIG_PPC */ -} - -#ifdef CONFIG_PPC -static void read_sq_setup(int numBufs, int bufSize, char **read_buffers) -{ - int i; - volatile struct dbdma_cmd *cp; - - read_sq.max_count = numBufs; - read_sq.max_active = numBufs; - read_sq.block_size = bufSize; - read_sq.buffers = read_buffers; - - read_sq.front = read_sq.count = 0; - read_sq.rear = 0; - read_sq.rear_size = 0; - read_sq.syncing = 0; - read_sq.active = 0; - - cp = awacs_rx_cmds; - memset((void *) cp, 0, (numBufs + 1) * sizeof(struct dbdma_cmd)); - - /* Set dma buffers up in a loop */ - for (i = 0; i < numBufs; i++,cp++) { - st_le32(&cp->phy_addr, virt_to_bus(read_buffers[i])); - st_le16(&cp->command, INPUT_MORE + INTR_ALWAYS); - st_le16(&cp->req_count, read_sq.block_size); - st_le16(&cp->xfer_status, 0); - } - - /* The next two lines make the thing loop around. - */ - st_le16(&cp->command, DBDMA_NOP + BR_ALWAYS); - st_le32(&cp->cmd_dep, virt_to_bus(awacs_rx_cmds)); - - /* Don't start until the first read is done. - * This will also abort any operations in progress if the DMA - * happens to be running (and it shouldn't). - */ - out_le32(&awacs_rxdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16); - out_le32(&awacs_rxdma->cmdptr, virt_to_bus(awacs_rx_cmds)); - -} -#endif /* CONFIG_PPC */ - - -static void sq_play(void) -{ - (*sound.mach.play)(); -} - - -/* ++TeSche: radically changed this one too */ - -static ssize_t sq_write(struct file *file, const char *src, size_t uLeft, - loff_t *ppos) -{ - ssize_t uWritten = 0; - u_char *dest; - ssize_t 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); - if (uUsed <= 0) - return uUsed; - src += uUsed; - uWritten += uUsed; - uLeft -= uUsed; - sq.rear_size = bUsed; - } - - do { - while (sq.count == sq.max_active) { - sq_play(); - if (NON_BLOCKING(sq.open_mode)) - return uWritten > 0 ? uWritten : -EAGAIN; - SLEEP(sq.action_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); - if (uUsed <= 0) - break; - 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 uUsed < 0? uUsed: uWritten; -} - - -/***********/ - -#ifdef CONFIG_PPC - -/* Here is how the values are used for reading. - * The value 'active' simply indicates the DMA is running. This is - * done so the driver semantics are DMA starts when the first read is - * posted. The value 'front' indicates the buffer we should next - * send to the user. The value 'rear' indicates the buffer the DMA is - * currently filling. When 'front' == 'rear' the buffer "ring" is - * empty (we always have an empty available). The 'rear_size' is used - * to track partial offsets into the current buffer. Right now, I just keep - * the DMA running. If the reader can't keep up, the interrupt tosses - * the oldest buffer. We could also shut down the DMA in this case. - */ -static ssize_t sq_read(struct file *file, char *dst, size_t uLeft, - loff_t *ppos) -{ - - ssize_t uRead, bLeft, bUsed, uUsed; - - if (uLeft == 0) - return 0; - - if (!read_sq.active) - PMacRecord(); /* Kick off the record process. */ - - uRead = 0; - - /* Move what the user requests, depending upon other options. - */ - while (uLeft > 0) { - - /* When front == rear, the DMA is not done yet. - */ - while (read_sq.front == read_sq.rear) { - if (NON_BLOCKING(read_sq.open_mode)) { - return uRead > 0 ? uRead : -EAGAIN; - } - SLEEP(read_sq.action_queue, ONE_SECOND); - if (SIGNAL_RECEIVED) - return uRead > 0 ? uRead : -EINTR; - } - - /* The amount we move is either what is left in the - * current buffer or what the user wants. - */ - bLeft = read_sq.block_size - read_sq.rear_size; - bUsed = read_sq.rear_size; - uUsed = sound_copy_translate_read(dst, uLeft, - read_sq.buffers[read_sq.front], &bUsed, bLeft); - if (uUsed <= 0) - return uUsed; - dst += uUsed; - uRead += uUsed; - uLeft -= uUsed; - read_sq.rear_size += bUsed; - if (read_sq.rear_size >= read_sq.block_size) { - read_sq.rear_size = 0; - read_sq.front++; - if (read_sq.front >= read_sq.max_active) - read_sq.front = 0; - } - } - return uRead; -} -#endif - -static int sq_open(struct inode *inode, struct file *file) -{ - int rc = 0; - - MOD_INC_USE_COUNT; - if (file->f_mode & FMODE_WRITE) { - if (sq.busy) { - rc = -EBUSY; - if (NON_BLOCKING(file->f_flags)) - goto err_out; - rc = -EINTR; - while (sq.busy) { - SLEEP(sq.open_queue, ONE_SECOND); - if (SIGNAL_RECEIVED) - goto err_out; - } - } - sq.busy = 1; /* Let's play spot-the-race-condition */ - - if (sq_allocate_buffers()) goto err_out_nobusy; - - sq_setup(numBufs, bufSize<<10,sound_buffers); - sq.open_mode = file->f_mode; - } - - -#ifdef CONFIG_PPC - if (file->f_mode & FMODE_READ) { - if (read_sq.busy) { - rc = -EBUSY; - if (NON_BLOCKING(file->f_flags)) - goto err_out; - rc = -EINTR; - while (read_sq.busy) { - SLEEP(read_sq.open_queue, ONE_SECOND); - if (SIGNAL_RECEIVED) - goto err_out; - } - rc = 0; - } - read_sq.busy = 1; - if (sq_allocate_read_buffers()) goto err_out_nobusy; - - read_sq_setup(numReadBufs,readbufSize<<10, sound_read_buffers); - read_sq.open_mode = file->f_mode; - } -#endif - -#ifdef CONFIG_ATARI - sq.ignore_int = 1; -#endif /* CONFIG_ATARI */ - sound.minDev = MINOR(inode->i_rdev) & 0x0f; - sound.soft = sound.dsp; - sound.hard = sound.dsp; - sound_init(); - if ((MINOR(inode->i_rdev) & 0x0f) == SND_DEV_AUDIO) { - sound_set_speed(8000); - sound_set_stereo(0); - sound_set_format(AFMT_MU_LAW); - } - -#if 0 - if (file->f_mode == FMODE_READ) { - /* Start dma'ing straight away */ - PMacRecord(); - } -#endif - - return 0; - -err_out_nobusy: - if (file->f_mode & FMODE_WRITE) { - sq.busy = 0; - WAKE_UP(sq.open_queue); - } -#ifdef CONFIG_PPC - if (file->f_mode & FMODE_READ) { - read_sq.busy = 0; - WAKE_UP(read_sq.open_queue); - } -#endif -err_out: - MOD_DEC_USE_COUNT; - return rc; -} - - -static void sq_reset(void) -{ - sound_silence(); - sq.active = 0; - sq.count = 0; - sq.front = (sq.rear+1) % sq.max_count; -} - - -static int sq_fsync(struct file *filp, struct dentry *dentry) -{ - int rc = 0; - - sq.syncing = 1; - sq_play(); /* there may be an incomplete frame waiting */ - - while (sq.active) { - 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(struct inode *inode, struct file *file) -{ - int rc = 0; - - if (sq.busy) - rc = sq_fsync(file, file->f_dentry); - sound.soft = sound.dsp; - sound.hard = sound.dsp; - sound_silence(); - -#ifdef CONFIG_PPC - sq_release_read_buffers(); -#endif - sq_release_buffers(); - MOD_DEC_USE_COUNT; - - /* There is probably a DOS atack here. They change the mode flag. */ - /* XXX add check here */ -#ifdef CONFIG_PPC - if (file->f_mode & FMODE_READ) { - read_sq.busy = 0; - WAKE_UP(read_sq.open_queue); - } -#endif - - if (file->f_mode & FMODE_WRITE) { - 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; -} - - -static int sq_ioctl(struct inode *inode, struct file *file, u_int cmd, - u_long arg) -{ - u_long fmt; - int data; - int size, nbufs; - audio_buf_info info; - - switch (cmd) { - case SNDCTL_DSP_RESET: - sq_reset(); - return 0; - case SNDCTL_DSP_POST: - case SNDCTL_DSP_SYNC: - return sq_fsync(file, file->f_dentry); - - /* ++TeSche: before changing any of these it's - * probably wise to wait until sound playing has - * settled down. */ - case SNDCTL_DSP_SPEED: - sq_fsync(file, file->f_dentry); - IOCTL_IN(arg, data); - return IOCTL_OUT(arg, sound_set_speed(data)); - case SNDCTL_DSP_STEREO: - sq_fsync(file, file->f_dentry); - IOCTL_IN(arg, data); - return IOCTL_OUT(arg, sound_set_stereo(data)); - case SOUND_PCM_WRITE_CHANNELS: - sq_fsync(file, file->f_dentry); - IOCTL_IN(arg, data); - return IOCTL_OUT(arg, sound_set_stereo(data-1)+1); - case SNDCTL_DSP_SETFMT: - sq_fsync(file, file->f_dentry); - 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: - size = sq.block_size - * sound.soft.size * (sound.soft.stereo + 1) - / (sound.hard.size * (sound.hard.stereo + 1)); - return IOCTL_OUT(arg, size); - case SNDCTL_DSP_SUBDIVIDE: - break; - case SNDCTL_DSP_SETFRAGMENT: - if (sq.count || sq.active || sq.syncing) - return -EINVAL; - IOCTL_IN(arg, size); - nbufs = size >> 16; - if (nbufs < 2 || nbufs > numBufs) - nbufs = numBufs; - size &= 0xffff; - if (size >= 8 && size <= 29) { - size = 1 << size; - size *= sound.hard.size * (sound.hard.stereo + 1); - size /= sound.soft.size * (sound.soft.stereo + 1); - if (size > (bufSize << 10)) - size = bufSize << 10; - } else - size = bufSize << 10; - sq_setup(numBufs, size, sound_buffers); - sq.max_active = nbufs; - return 0; - case SNDCTL_DSP_GETOSPACE: - info.fragments = sq.max_active - sq.count; - info.fragstotal = sq.max_active; - info.fragsize = sq.block_size; - info.bytes = info.fragments * info.fragsize; - if (copy_to_user((void *)arg, &info, sizeof(info))) - return -EFAULT; - return 0; - - default: - return mixer_ioctl(inode, file, cmd, arg); - } - return -EINVAL; -} - - - -static struct file_operations sq_fops = -{ - llseek: sound_lseek, - write: sq_write, - ioctl: sq_ioctl, - open: sq_open, - release: sq_release, -#ifdef CONFIG_PPC - read: sq_read, /* sq_read */ -#endif -}; - - -static void __init sq_init(void) -{ -#ifndef MODULE - int sq_unit; -#endif - sq_unit = register_sound_dsp(&sq_fops, -1); - if (sq_unit < 0) - return; - - init_waitqueue_head(&sq.action_queue); - init_waitqueue_head(&sq.open_queue); - init_waitqueue_head(&sq.sync_queue); - -#ifdef CONFIG_PPC - init_waitqueue_head(&read_sq.action_queue); - init_waitqueue_head(&read_sq.open_queue); - init_waitqueue_head(&read_sq.sync_queue); -#endif - - sq.busy = 0; -#ifdef CONFIG_PPC - read_sq.busy = 0; -#endif - - /* 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_U8; - 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 */ -#ifdef CONFIG_PPC - case DMASND_AWACS: - sound.dsp.speed = 8000; - break; -#endif /* CONFIG_PPC */ - } - - /* before the first open to /dev/dsp this wouldn't be set */ - sound.soft = sound.dsp; - sound.hard = sound.dsp; - - sound_silence(); -} - -/* - * /dev/sndstat - */ - - -/* state.buf should not overflow! */ - -static int state_open(struct inode *inode, struct file *file) -{ - char *buffer = state.buf, *mach = ""; -#ifdef CONFIG_PPC - char awacs_buf[64]; -#endif - int len = 0; - - if (state.busy) - return -EBUSY; - - MOD_INC_USE_COUNT; - 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 */ -#ifdef CONFIG_PPC - case DMASND_AWACS: - sprintf(awacs_buf, "PowerMac (AWACS rev %d) ", awacs_revision); - mach = awacs_buf; - break; -#endif /* CONFIG_PPC */ - } - 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" - " sq.max_active = %d\n", - sq.block_size, sq.max_count, sq.max_active); - len += sprintf(buffer+len, "\tsq.count = %d sq.rear_size = %d\n", sq.count, - sq.rear_size); - len += sprintf(buffer+len, "\tsq.active = %d sq.syncing = %d\n", - sq.active, sq.syncing); - state.len = len; - return 0; -} - - -static int state_release(struct inode *inode, struct file *file) -{ - state.busy = 0; - MOD_DEC_USE_COUNT; - return 0; -} - - -static ssize_t state_read(struct file *file, char *buf, size_t count, - loff_t *ppos) -{ - int n = state.len - state.ptr; - if (n > count) - n = count; - if (n <= 0) - return 0; - if (copy_to_user(buf, &state.buf[state.ptr], n)) - return -EFAULT; - state.ptr += n; - return n; -} - - -static struct file_operations state_fops = -{ - llseek: sound_lseek, - read: state_read, - open: state_open, - release: state_release, -}; - - -static void __init state_init(void) -{ -#ifndef MODULE - int state_unit; -#endif - state_unit = register_sound_special(&state_fops, SND_DEV_STATUS); - if (state_unit < 0) - return; - state.busy = 0; -} - - -/*** Common stuff ********************************************************/ - -static long long sound_lseek(struct file *file, long long offset, int orig) -{ - return -ESPIPE; -} - - -/*** Config & Setup **********************************************************/ - - -void __init dmasound_init(void) -{ - int has_sound = 0; -#ifdef CONFIG_PPC - struct device_node *np; -#endif - -#if defined(__mc68000__) || defined(CONFIG_APUS) - 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 */ - } -#endif /* __mc68000__||CONFIG_APUS */ - -#ifdef CONFIG_PPC - awacs_subframe = 0; - awacs_revision = 0; - np = find_devices("awacs"); - if (np == 0) { - /* - * powermac G3 models have a node called "davbus" - * with a child called "sound". - */ - struct device_node *sound; - np = find_devices("davbus"); - sound = find_devices("sound"); - if (sound != 0 && sound->parent == np) { - unsigned int *prop, l, i; - prop = (unsigned int *) - get_property(sound, "sub-frame", 0); - if (prop != 0 && *prop >= 0 && *prop < 16) - awacs_subframe = *prop; - if (device_is_compatible(sound, "burgundy")) - awacs_revision = AWACS_BURGUNDY; - - /* look for a property saying what sample rates - are available */ - for (i = 0; i < 8; ++i) - awacs_freqs_ok[i] = 0; - prop = (unsigned int *) get_property - (sound, "sample-rates", &l); - if (prop == 0) - prop = (unsigned int *) get_property - (sound, "output-frame-rates", &l); - if (prop != 0) { - for (l /= sizeof(int); l > 0; --l) { - /* sometimes the rate is in the - high-order 16 bits (?) */ - unsigned int r = *prop++; - if (r >= 0x10000) - r >>= 16; - for (i = 0; i < 8; ++i) { - if (r == awacs_freqs[i]) { - awacs_freqs_ok[i] = 1; - break; - } - } - } - } else { - /* assume just 44.1k is OK */ - awacs_freqs_ok[0] = 1; - } - } - } - if (np != NULL && np->n_addrs >= 3 && np->n_intrs >= 3) { - int vol; - sound.mach = machPMac; - has_sound = 1; - - awacs = (volatile struct awacs_regs *) - ioremap(np->addrs[0].address, 0x80); - awacs_txdma = (volatile struct dbdma_regs *) - ioremap(np->addrs[1].address, 0x100); - awacs_rxdma = (volatile struct dbdma_regs *) - ioremap(np->addrs[2].address, 0x100); - - awacs_irq = np->intrs[0].line; - awacs_tx_irq = np->intrs[1].line; - awacs_rx_irq = np->intrs[2].line; - - awacs_tx_cmd_space = kmalloc((numBufs + 4) * sizeof(struct dbdma_cmd), - GFP_KERNEL); - if (awacs_tx_cmd_space == NULL) { - printk(KERN_ERR "DMA sound driver: Not enough buffer memory, driver disabled!\n"); - return; - } - awacs_node = np; -#ifdef CONFIG_PMAC_PBOOK - if (machine_is_compatible("PowerBook1,1") - || machine_is_compatible("AAPL,PowerBook1998")) { - feature_set(np, FEATURE_Sound_CLK_enable); - feature_set(np, FEATURE_Sound_power); - /* Shorter delay will not work */ - mdelay(1000); - } -#endif - awacs_tx_cmds = (volatile struct dbdma_cmd *) - DBDMA_ALIGN(awacs_tx_cmd_space); - - - awacs_rx_cmd_space = kmalloc((numReadBufs + 4) * sizeof(struct dbdma_cmd), - GFP_KERNEL); - if (awacs_rx_cmd_space == NULL) { - printk("DMA sound driver: No memory for input"); - } - awacs_rx_cmds = (volatile struct dbdma_cmd *) - DBDMA_ALIGN(awacs_rx_cmd_space); - - - - awacs_reg[0] = MASK_MUX_CD; - awacs_reg[1] = MASK_LOOPTHRU | MASK_PAROUT; - /* get default volume from nvram */ - vol = (~nvram_read_byte(0x1308) & 7) << 1; - awacs_reg[2] = vol + (vol << 6); - awacs_reg[4] = vol + (vol << 6); - out_le32(&awacs->control, 0x11); - awacs_write(awacs_reg[0] + MASK_ADDR0); - awacs_write(awacs_reg[1] + MASK_ADDR1); - awacs_write(awacs_reg[2] + MASK_ADDR2); - awacs_write(awacs_reg[4] + MASK_ADDR4); - - /* Initialize recent versions of the awacs */ - if (awacs_revision == 0) { - awacs_revision = - (in_le32(&awacs->codec_stat) >> 12) & 0xf; - if (awacs_revision == 3) { - mdelay(100); - awacs_write(0x6000); - mdelay(2); - awacs_write(awacs_reg[1] + MASK_ADDR1); - awacs_enable_amp(100 * 0x101); - } - } - if (awacs_revision >= AWACS_BURGUNDY) - awacs_burgundy_init(); - - /* Initialize beep stuff */ - beep_dbdma_cmd = awacs_tx_cmds + (numBufs + 1); - orig_mksound = kd_mksound; - kd_mksound = awacs_mksound; - beep_buf = (short *) kmalloc(BEEP_BUFLEN * 4, GFP_KERNEL); - if (beep_buf == NULL) - printk(KERN_WARNING "dmasound: no memory for " - "beep buffer\n"); -#ifdef CONFIG_PMAC_PBOOK - pmu_register_sleep_notifier(&awacs_sleep_notifier); -#endif /* CONFIG_PMAC_PBOOK */ - - /* Powerbooks have odd ways of enabling inputs such as - an expansion-bay CD or sound from an internal modem - or a PC-card modem. */ - if (machine_is_compatible("AAPL,3400/2400") - || machine_is_compatible("AAPL,3500")) { - is_pbook_3400 = 1; - /* - * Enable CD and PC-card sound inputs. - * This is done by reading from address - * f301a000, + 0x10 to enable the expansion-bay - * CD sound input, + 0x80 to enable the PC-card - * sound input. The 0x100 enables the SCSI bus - * terminator power. - */ - latch_base = (unsigned char *) ioremap - (0xf301a000, 0x1000); - in_8(latch_base + 0x190); - } else if (machine_is_compatible("PowerBook1,1") - || machine_is_compatible("AAPL,PowerBook1998")) { - struct device_node* mio; - macio_base = 0; - is_pbook_G3 = 1; - for (mio = np->parent; mio; mio = mio->parent) { - if (strcmp(mio->name, "mac-io") == 0 - && mio->n_addrs > 0) { - macio_base = (unsigned char *) ioremap - (mio->addrs[0].address, 0x40); - break; - } - } - /* enable CD sound input */ - if (macio_base) - out_8(macio_base + 0x37, 3); - } - } -#endif /* CONFIG_PPC */ - - if (!has_sound) - return; - - /* Set up sound queue, /dev/audio and /dev/dsp. */ - - /* Set default settings. */ - sq_init(); - - /* Set up /dev/sndstat. */ - state_init(); - - /* Set up /dev/mixer. */ - mixer_init(); - - if (!sound.mach.irqinit()) { - printk(KERN_ERR "DMA sound driver: Interrupt initialization failed\n"); - return; - } -#ifdef MODULE - irq_installed = 1; -#endif - - printk(KERN_INFO "DMA sound driver installed, using %d buffers of %dk.\n", - numBufs, bufSize); - - return; -} - - -static int __init dmasound_setup(char *str) -{ - int ints[6]; - - str = get_options(str, ARRAY_SIZE(ints), 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"); - return 0; - } - - return 1; -} - -__setup("dmasound=", dmasound_setup); - - -#ifdef MODULE - -int init_module(void) -{ - dmasound_init(); - return 0; -} - - -void cleanup_module(void) -{ - if (irq_installed) { - sound_silence(); - sound.mach.irqcleanup(); - } - -#ifdef CONFIG_PPC - sq_release_read_buffers(); -#endif - sq_release_buffers(); - - if (mixer_unit >= 0) - unregister_sound_mixer(mixer_unit); - if (state_unit >= 0) - unregister_sound_special(state_unit); - if (sq_unit >= 0) - unregister_sound_dsp(sq_unit); -} - -#endif /* MODULE */ diff --git a/drivers/sound/dmasound.h b/drivers/sound/dmasound.h deleted file mode 100644 index fec3ad84e..000000000 --- a/drivers/sound/dmasound.h +++ /dev/null @@ -1,36 +0,0 @@ - -/* 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/dmasound/.cvsignore b/drivers/sound/dmasound/.cvsignore new file mode 100644 index 000000000..88fff7fc4 --- /dev/null +++ b/drivers/sound/dmasound/.cvsignore @@ -0,0 +1,5 @@ +.depend +.*.flags +.defines +local.h +configure diff --git a/drivers/sound/dmasound/Config.in b/drivers/sound/dmasound/Config.in new file mode 100644 index 000000000..c5f4c3c53 --- /dev/null +++ b/drivers/sound/dmasound/Config.in @@ -0,0 +1,27 @@ +# drivers/sound/dmasound/Config.in + +if [ "$CONFIG_ATARI" = "y" ]; then + dep_tristate ' Atari DMA sound support' CONFIG_DMASOUND_ATARI $CONFIG_SOUND +fi +if [ "$CONFIG_ALL_PPC" = "y" ]; then + dep_tristate ' PowerMac DMA sound support' CONFIG_DMASOUND_AWACS $CONFIG_SOUND +fi +if [ "$CONFIG_AMIGA" = "y" -o "$CONFIG_APUS" = "y" ]; then + dep_tristate ' Amiga DMA sound support' CONFIG_DMASOUND_PAULA $CONFIG_SOUND +fi +if [ "$CONFIG_Q40" = "y" ]; then + dep_tristate ' Q40 sound support' CONFIG_DMASOUND_Q40 $CONFIG_SOUND +fi +if [ "$CONFIG_DMASOUND_ATARI" = "y" -o \ + "$CONFIG_DMASOUND_AWACS" = "y" -o \ + "$CONFIG_DMASOUND_PAULA" = "y" -o \ + "$CONFIG_DMASOUND_Q40" = "y" ]; then + define_tristate CONFIG_DMASOUND y +else + if [ "$CONFIG_DMASOUND_ATARI" = "m" -o \ + "$CONFIG_DMASOUND_AWACS" = "m" -o \ + "$CONFIG_DMASOUND_PAULA" = "m" -o \ + "$CONFIG_DMASOUND_Q40" = "m" ]; then + define_tristate CONFIG_DMASOUND m + fi +fi diff --git a/drivers/sound/dmasound/Makefile b/drivers/sound/dmasound/Makefile new file mode 100644 index 000000000..535146de2 --- /dev/null +++ b/drivers/sound/dmasound/Makefile @@ -0,0 +1,38 @@ +# +# Makefile for the DMA sound driver +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are now in the main makefile... + +O_TARGET := +O_OBJS := +OX_OBJS := +M_OBJS := +MX_OBJS := + +export-objs := dmasound_core.o + +obj-$(CONFIG_DMASOUND_ATARI) += dmasound_core.o dmasound_atari.o +obj-$(CONFIG_DMASOUND_AWACS) += dmasound_core.o dmasound_awacs.o +obj-$(CONFIG_DMASOUND_PAULA) += dmasound_core.o dmasound_paula.o +obj-$(CONFIG_DMASOUND_Q40) += dmasound_core.o dmasound_q40.o + +# Files that are both resident and modular: remove from modular. + +obj-m := $(filter-out $(obj-y), $(obj-m)) + +# Translate to Rules.make lists. + +O_OBJS := $(filter-out $(export-objs), $(obj-y)) +OX_OBJS := $(filter $(export-objs), $(obj-y)) +M_OBJS := $(sort $(filter-out $(export-objs), $(obj-m))) +MX_OBJS := $(sort $(filter $(export-objs), $(obj-m))) + +ifeq ($(CONFIG_DMASOUND),y) + O_TARGET = dmasound.o +endif + +include $(TOPDIR)/Rules.make diff --git a/drivers/sound/dmasound/dmasound.h b/drivers/sound/dmasound/dmasound.h new file mode 100644 index 000000000..985785e74 --- /dev/null +++ b/drivers/sound/dmasound/dmasound.h @@ -0,0 +1,246 @@ + +/* + * 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. + */ + + +#include <linux/config.h> + + +#define SND_NDEVS 256 /* Number of supported devices */ +#define SND_DEV_CTL 0 /* Control port /dev/mixer */ +#define SND_DEV_SEQ 1 /* Sequencer output /dev/sequencer (FM + synthesizer and MIDI output) */ +#define SND_DEV_MIDIN 2 /* Raw midi access */ +#define SND_DEV_DSP 3 /* Digitized voice /dev/dsp */ +#define SND_DEV_AUDIO 4 /* Sparc compatible /dev/audio */ +#define SND_DEV_DSP16 5 /* Like /dev/dsp but 16 bits/sample */ +#define SND_DEV_STATUS 6 /* /dev/sndstat */ +/* #7 not in use now. Was in 2.4. Free for use after v3.0. */ +#define SND_DEV_SEQ2 8 /* /dev/sequencer, level 2 interface */ +#define SND_DEV_SNDPROC 9 /* /dev/sndproc for programmable devices */ +#define SND_DEV_PSS SND_DEV_SNDPROC + +#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 + + +#define MAX_CATCH_RADIUS 10 +#define MIN_BUFFERS 4 +#define MIN_BUFSIZE 4 /* in KB */ +#define MAX_BUFSIZE 128 /* Limit for Amiga in KB */ + + +#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) + +static inline int ioctl_return(int *addr, int value) +{ + return value < 0 ? value : put_user(value, addr); +} + + + /* + * Configuration + */ + +#undef HAS_8BIT_TABLES +#undef HAS_14BIT_TABLES +#undef HAS_16BIT_TABLES +#undef HAS_RECORD + +#if defined(CONFIG_DMASOUND_ATARI) || defined(CONFIG_DMASOUND_ATARI_MODULE) ||\ + defined(CONFIG_DMASOUND_PAULA) || defined(CONFIG_DMASOUND_PAULA_MODULE) ||\ + defined(CONFIG_DMASOUND_Q40) || defined(CONFIG_DMASOUND_Q40_MODULE) +#define HAS_8BIT_TABLES +#endif +#if defined(CONFIG_DMASOUND_AWACS) || defined(CONFIG_DMASOUND_AWACS_MODULE) +#define HAS_16BIT_TABLES +#define HAS_RECORD +#endif + + + /* + * Initialization + */ + +extern int dmasound_init(void); +extern void dmasound_deinit(void); + + + /* + * Machine definitions + */ + +typedef struct { + const char *name; + const char *name2; + void (*open)(void); + void (*release)(void); + 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); + int (*setGain)(int); + void (*play)(void); + void (*record)(void); /* optional */ + void (*mixer_init)(void); /* optional */ + int (*mixer_ioctl)(u_int, u_long); /* optional */ + void (*write_sq_setup)(void); /* optional */ + void (*read_sq_setup)(void); /* optional */ + void (*sq_open)(void); /* optional */ + int (*state_info)(char *); /* optional */ + void (*abort_read)(void); /* optional */ + int min_dsp_speed; +} 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 { + ssize_t (*ct_ulaw)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); + ssize_t (*ct_alaw)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); + ssize_t (*ct_s8)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); + ssize_t (*ct_u8)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); + ssize_t (*ct_s16be)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); + ssize_t (*ct_u16be)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); + ssize_t (*ct_s16le)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); + ssize_t (*ct_u16le)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); +} TRANS; + +struct sound_settings { + MACHINE mach; /* machine dependent things */ + SETTINGS hard; /* hardware settings */ + SETTINGS soft; /* software settings */ + SETTINGS dsp; /* /dev/dsp default settings */ + TRANS *trans_write; /* supported translations */ +#ifdef HAS_RECORD + TRANS *trans_read; /* supported translations */ +#endif + int volume_left; /* volume (range is machine dependent) */ + int volume_right; + int bass; /* tone (range is machine dependent) */ + int treble; + int gain; + int minDev; /* minor device number currently open */ +}; + +extern struct sound_settings dmasound; + +extern char dmasound_ulaw2dma8[]; +extern char dmasound_alaw2dma8[]; +extern short dmasound_ulaw2dma16[]; +extern short dmasound_alaw2dma16[]; + + + /* + * Mid level stuff + */ + +static inline int dmasound_set_volume(int volume) +{ + return dmasound.mach.setVolume(volume); +} + +static inline int dmasound_set_bass(int bass) +{ + return dmasound.mach.setBass ? dmasound.mach.setBass(bass) : 50; +} + +static inline int dmasound_set_treble(int treble) +{ + return dmasound.mach.setTreble ? dmasound.mach.setTreble(treble) : 50; +} + +static inline int dmasound_set_gain(int gain) +{ + return dmasound.mach.setGain ? dmasound.mach.setGain(gain) : 100; +} + + + /* + * Sound queue stuff, the heart of the driver + */ + +struct sound_queue { + /* buffers allocated for this queue */ + int numBufs; + int bufSize; /* in bytes */ + char **buffers; + + /* current parameters */ + int max_count; + int block_size; /* in bytes */ + int max_active; + + /* it shouldn't be necessary to declare any of these volatile */ + int front, rear, count; + int rear_size; + /* + * The use of the playing field depends on the hardware + * + * Atari, PMac: The number of frames that are loaded/playing + * + * Amiga: Bit 0 is set: a frame is loaded + * Bit 1 is set: a frame is playing + */ + int active; + wait_queue_head_t action_queue, open_queue, sync_queue; + int open_mode; + int busy, syncing; +}; + +#define SLEEP(queue) interruptible_sleep_on_timeout(&queue, HZ) +#define WAKE_UP(queue) (wake_up_interruptible(&queue)) + +extern struct sound_queue dmasound_write_sq; +extern struct sound_queue dmasound_read_sq; + +#define write_sq dmasound_write_sq +#define read_sq dmasound_read_sq + +extern int dmasound_catchRadius; + +#define catchRadius dmasound_catchRadius + diff --git a/drivers/sound/dmasound/dmasound_atari.c b/drivers/sound/dmasound/dmasound_atari.c new file mode 100644 index 000000000..ca6f07cbb --- /dev/null +++ b/drivers/sound/dmasound/dmasound_atari.c @@ -0,0 +1,1560 @@ + +/* + * linux/drivers/sound/dmasound_atari.c + * + * Atari DMA Sound Driver + * + * See linux/drivers/sound/dmasound_core.c for copyright and credits + */ + + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/soundcard.h> +#include <linux/mm.h> + +#include <asm/pgalloc.h> +#include <asm/uaccess.h> +#include <asm/atariints.h> +#include <asm/atari_stram.h> + +#include "dmasound.h" + + +extern void atari_microwire_cmd(int cmd); + + +static int is_falcon; +static int write_sq_ignore_int = 0; /* ++TeSche: used for Falcon */ + +static int expand_bal; /* Balance factor for expanding (not volume!) */ +static int expand_data; /* Data for expanding */ + + +/*** Translations ************************************************************/ + + +/* ++TeSche: radically changed for new expanding purposes... + * + * These two routines now deal with copying/expanding/translating the samples + * from user space into our buffer at the right frequency. They take care about + * how much data there's actually to read, how much buffer space there is and + * to convert samples into the right frequency/encoding. They will only work on + * complete samples so it may happen they leave some bytes in the input stream + * if the user didn't write a multiple of the current sample size. They both + * return the number of bytes they've used from both streams so you may detect + * such a situation. Luckily all programs should be able to cope with that. + * + * I think I've optimized anything as far as one can do in plain C, all + * variables should fit in registers and the loops are really short. There's + * one loop for every possible situation. Writing a more generalized and thus + * parameterized loop would only produce slower code. Feel free to optimize + * this in assembler if you like. :) + * + * I think these routines belong here because they're not yet really hardware + * independent, especially the fact that the Falcon can play 16bit samples + * only in stereo is hardcoded in both of them! + * + * ++geert: split in even more functions (one per format) + */ + +static ssize_t ata_ct_law(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t ata_ct_s8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t ata_ct_u8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t ata_ct_s16be(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t ata_ct_u16be(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t ata_ct_s16le(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t ata_ct_u16le(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t ata_ctx_law(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t ata_ctx_s8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t ata_ctx_u8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t ata_ctx_s16be(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t ata_ctx_u16be(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t ata_ctx_s16le(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t ata_ctx_u16le(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); + + +/*** Low level stuff *********************************************************/ + + +static void AtaOpen(void); +static void AtaRelease(void); +static void *AtaAlloc(unsigned int size, int flags); +static void AtaFree(void *, unsigned int size); +static int AtaIrqInit(void); +#ifdef MODULE +static void AtaIrqCleanUp(void); +#endif /* MODULE */ +static int AtaSetBass(int bass); +static int AtaSetTreble(int treble); +static void TTSilence(void); +static void TTInit(void); +static int TTSetFormat(int format); +static int TTSetVolume(int volume); +static int TTSetGain(int gain); +static void FalconSilence(void); +static void FalconInit(void); +static int FalconSetFormat(int format); +static int FalconSetVolume(int volume); +static void AtaPlayNextFrame(int index); +static void AtaPlay(void); +static void AtaInterrupt(int irq, void *dummy, struct pt_regs *fp); + +/*** Mid level stuff *********************************************************/ + +static void TTMixerInit(void); +static void FalconMixerInit(void); +static int AtaMixerIoctl(u_int cmd, u_long arg); +static int TTMixerIoctl(u_int cmd, u_long arg); +static int FalconMixerIoctl(u_int cmd, u_long arg); +static void AtaWriteSqSetup(void); +static void AtaSqOpen(void); +static int TTStateInfo(char *buffer); +static int FalconStateInfo(char *buffer); + + +/*** Translations ************************************************************/ + + +static ssize_t ata_ct_law(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + char *table = dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8 + : dmasound_alaw2dma8; + ssize_t count, used; + u_char *p = &frame[*frameUsed]; + + count = min(userCount, frameLeft); + if (dmasound.soft.stereo) + count &= ~1; + used = count; + while (count > 0) { + u_char data; + if (get_user(data, userPtr++)) + return -EFAULT; + *p++ = table[data]; + count--; + } + *frameUsed += used; + return used; +} + + +static ssize_t ata_ct_s8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + void *p = &frame[*frameUsed]; + + count = min(userCount, frameLeft); + if (dmasound.soft.stereo) + count &= ~1; + used = count; + if (copy_from_user(p, userPtr, count)) + return -EFAULT; + *frameUsed += used; + return used; +} + + +static ssize_t ata_ct_u8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + + if (!dmasound.soft.stereo) { + u_char *p = &frame[*frameUsed]; + count = min(userCount, frameLeft); + used = count; + while (count > 0) { + u_char data; + if (get_user(data, userPtr++)) + return -EFAULT; + *p++ = data ^ 0x80; + count--; + } + } else { + u_short *p = (u_short *)&frame[*frameUsed]; + count = min(userCount, frameLeft)>>1; + used = count*2; + while (count > 0) { + u_short data; + if (get_user(data, ((u_short *)userPtr)++)) + return -EFAULT; + *p++ = data ^ 0x8080; + count--; + } + } + *frameUsed += used; + return used; +} + + +static ssize_t ata_ct_s16be(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + + if (!dmasound.soft.stereo) { + u_short *p = (u_short *)&frame[*frameUsed]; + count = min(userCount, frameLeft)>>1; + used = count*2; + while (count > 0) { + u_short data; + if (get_user(data, ((u_short *)userPtr)++)) + return -EFAULT; + *p++ = data; + *p++ = data; + count--; + } + *frameUsed += used*2; + } else { + void *p = (u_short *)&frame[*frameUsed]; + count = min(userCount, frameLeft) & ~3; + used = count; + if (copy_from_user(p, userPtr, count)) + return -EFAULT; + *frameUsed += used; + } + return used; +} + + +static ssize_t ata_ct_u16be(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + + if (!dmasound.soft.stereo) { + u_short *p = (u_short *)&frame[*frameUsed]; + count = min(userCount, frameLeft)>>1; + used = count*2; + while (count > 0) { + u_short data; + if (get_user(data, ((u_short *)userPtr)++)) + return -EFAULT; + data ^= 0x8000; + *p++ = data; + *p++ = data; + count--; + } + *frameUsed += used*2; + } else { + u_long *p = (u_long *)&frame[*frameUsed]; + count = min(userCount, frameLeft)>>2; + used = count*4; + while (count > 0) { + u_long data; + if (get_user(data, ((u_int *)userPtr)++)) + return -EFAULT; + *p++ = data ^ 0x80008000; + count--; + } + *frameUsed += used; + } + return used; +} + + +static ssize_t ata_ct_s16le(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + + count = frameLeft; + if (!dmasound.soft.stereo) { + u_short *p = (u_short *)&frame[*frameUsed]; + count = min(userCount, frameLeft)>>1; + used = count*2; + while (count > 0) { + u_short data; + if (get_user(data, ((u_short *)userPtr)++)) + return -EFAULT; + data = le2be16(data); + *p++ = data; + *p++ = data; + count--; + } + *frameUsed += used*2; + } else { + u_long *p = (u_long *)&frame[*frameUsed]; + count = min(userCount, frameLeft)>>2; + used = count*4; + while (count > 0) { + u_long data; + if (get_user(data, ((u_int *)userPtr)++)) + return -EFAULT; + data = le2be16dbl(data); + *p++ = data; + count--; + } + *frameUsed += used; + } + return used; +} + + +static ssize_t ata_ct_u16le(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + + count = frameLeft; + if (!dmasound.soft.stereo) { + u_short *p = (u_short *)&frame[*frameUsed]; + count = min(userCount, frameLeft)>>1; + used = count*2; + while (count > 0) { + u_short data; + if (get_user(data, ((u_short *)userPtr)++)) + return -EFAULT; + data = le2be16(data) ^ 0x8000; + *p++ = data; + *p++ = data; + } + *frameUsed += used*2; + } else { + u_long *p = (u_long *)&frame[*frameUsed]; + count = min(userCount, frameLeft)>>2; + used = count; + while (count > 0) { + u_long data; + if (get_user(data, ((u_int *)userPtr)++)) + return -EFAULT; + data = le2be16dbl(data) ^ 0x80008000; + *p++ = data; + count--; + } + *frameUsed += used; + } + return used; +} + + +static ssize_t ata_ctx_law(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + char *table = dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8 + : dmasound_alaw2dma8; + /* this should help gcc to stuff everything into registers */ + long bal = expand_bal; + long hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; + ssize_t used, usedf; + + used = userCount; + usedf = frameLeft; + if (!dmasound.soft.stereo) { + u_char *p = &frame[*frameUsed]; + u_char data = expand_data; + while (frameLeft) { + u_char c; + if (bal < 0) { + if (!userCount) + break; + if (get_user(c, userPtr++)) + return -EFAULT; + data = table[c]; + userCount--; + bal += hSpeed; + } + *p++ = data; + frameLeft--; + bal -= sSpeed; + } + expand_data = data; + } else { + u_short *p = (u_short *)&frame[*frameUsed]; + u_short data = expand_data; + while (frameLeft >= 2) { + u_char c; + if (bal < 0) { + if (userCount < 2) + break; + if (get_user(c, userPtr++)) + return -EFAULT; + data = table[c] << 8; + if (get_user(c, userPtr++)) + return -EFAULT; + data |= table[c]; + userCount -= 2; + bal += hSpeed; + } + *p++ = data; + frameLeft -= 2; + bal -= sSpeed; + } + expand_data = data; + } + expand_bal = bal; + used -= userCount; + *frameUsed += usedf-frameLeft; + return used; +} + + +static ssize_t ata_ctx_s8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + /* this should help gcc to stuff everything into registers */ + long bal = expand_bal; + long hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; + ssize_t used, usedf; + + used = userCount; + usedf = frameLeft; + if (!dmasound.soft.stereo) { + u_char *p = &frame[*frameUsed]; + u_char data = expand_data; + while (frameLeft) { + if (bal < 0) { + if (!userCount) + break; + if (get_user(data, userPtr++)) + return -EFAULT; + userCount--; + bal += hSpeed; + } + *p++ = data; + frameLeft--; + bal -= sSpeed; + } + expand_data = data; + } else { + u_short *p = (u_short *)&frame[*frameUsed]; + u_short data = expand_data; + while (frameLeft >= 2) { + if (bal < 0) { + if (userCount < 2) + break; + if (get_user(data, ((u_short *)userPtr)++)) + return -EFAULT; + userCount -= 2; + bal += hSpeed; + } + *p++ = data; + frameLeft -= 2; + bal -= sSpeed; + } + expand_data = data; + } + expand_bal = bal; + used -= userCount; + *frameUsed += usedf-frameLeft; + return used; +} + + +static ssize_t ata_ctx_u8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + /* this should help gcc to stuff everything into registers */ + long bal = expand_bal; + long hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; + ssize_t used, usedf; + + used = userCount; + usedf = frameLeft; + if (!dmasound.soft.stereo) { + u_char *p = &frame[*frameUsed]; + u_char data = expand_data; + while (frameLeft) { + if (bal < 0) { + if (!userCount) + break; + if (get_user(data, userPtr++)) + return -EFAULT; + data ^= 0x80; + userCount--; + bal += hSpeed; + } + *p++ = data; + frameLeft--; + bal -= sSpeed; + } + expand_data = data; + } else { + u_short *p = (u_short *)&frame[*frameUsed]; + u_short data = expand_data; + while (frameLeft >= 2) { + if (bal < 0) { + if (userCount < 2) + break; + if (get_user(data, ((u_short *)userPtr)++)) + return -EFAULT; + data ^= 0x8080; + userCount -= 2; + bal += hSpeed; + } + *p++ = data; + frameLeft -= 2; + bal -= sSpeed; + } + expand_data = data; + } + expand_bal = bal; + used -= userCount; + *frameUsed += usedf-frameLeft; + return used; +} + + +static ssize_t ata_ctx_s16be(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + /* this should help gcc to stuff everything into registers */ + long bal = expand_bal; + long hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; + ssize_t used, usedf; + + used = userCount; + usedf = frameLeft; + if (!dmasound.soft.stereo) { + u_short *p = (u_short *)&frame[*frameUsed]; + u_short data = expand_data; + while (frameLeft >= 4) { + if (bal < 0) { + if (userCount < 2) + break; + if (get_user(data, ((u_short *)userPtr)++)) + return -EFAULT; + userCount -= 2; + bal += hSpeed; + } + *p++ = data; + *p++ = data; + frameLeft -= 4; + bal -= sSpeed; + } + expand_data = data; + } else { + u_long *p = (u_long *)&frame[*frameUsed]; + u_long data = expand_data; + while (frameLeft >= 4) { + if (bal < 0) { + if (userCount < 4) + break; + if (get_user(data, ((u_int *)userPtr)++)) + return -EFAULT; + userCount -= 4; + bal += hSpeed; + } + *p++ = data; + frameLeft -= 4; + bal -= sSpeed; + } + expand_data = data; + } + expand_bal = bal; + used -= userCount; + *frameUsed += usedf-frameLeft; + return used; +} + + +static ssize_t ata_ctx_u16be(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + /* this should help gcc to stuff everything into registers */ + long bal = expand_bal; + long hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; + ssize_t used, usedf; + + used = userCount; + usedf = frameLeft; + if (!dmasound.soft.stereo) { + u_short *p = (u_short *)&frame[*frameUsed]; + u_short data = expand_data; + while (frameLeft >= 4) { + if (bal < 0) { + if (userCount < 2) + break; + if (get_user(data, ((u_short *)userPtr)++)) + return -EFAULT; + data ^= 0x8000; + userCount -= 2; + bal += hSpeed; + } + *p++ = data; + *p++ = data; + frameLeft -= 4; + bal -= sSpeed; + } + expand_data = data; + } else { + u_long *p = (u_long *)&frame[*frameUsed]; + u_long data = expand_data; + while (frameLeft >= 4) { + if (bal < 0) { + if (userCount < 4) + break; + if (get_user(data, ((u_int *)userPtr)++)) + return -EFAULT; + data ^= 0x80008000; + userCount -= 4; + bal += hSpeed; + } + *p++ = data; + frameLeft -= 4; + bal -= sSpeed; + } + expand_data = data; + } + expand_bal = bal; + used -= userCount; + *frameUsed += usedf-frameLeft; + return used; +} + + +static ssize_t ata_ctx_s16le(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + /* this should help gcc to stuff everything into registers */ + long bal = expand_bal; + long hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; + ssize_t used, usedf; + + used = userCount; + usedf = frameLeft; + if (!dmasound.soft.stereo) { + u_short *p = (u_short *)&frame[*frameUsed]; + u_short data = expand_data; + while (frameLeft >= 4) { + if (bal < 0) { + if (userCount < 2) + break; + if (get_user(data, ((u_short *)userPtr)++)) + return -EFAULT; + data = le2be16(data); + userCount -= 2; + bal += hSpeed; + } + *p++ = data; + *p++ = data; + frameLeft -= 4; + bal -= sSpeed; + } + expand_data = data; + } else { + u_long *p = (u_long *)&frame[*frameUsed]; + u_long data = expand_data; + while (frameLeft >= 4) { + if (bal < 0) { + if (userCount < 4) + break; + if (get_user(data, ((u_int *)userPtr)++)) + return -EFAULT; + data = le2be16dbl(data); + userCount -= 4; + bal += hSpeed; + } + *p++ = data; + frameLeft -= 4; + bal -= sSpeed; + } + expand_data = data; + } + expand_bal = bal; + used -= userCount; + *frameUsed += usedf-frameLeft; + return used; +} + + +static ssize_t ata_ctx_u16le(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + /* this should help gcc to stuff everything into registers */ + long bal = expand_bal; + long hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; + ssize_t used, usedf; + + used = userCount; + usedf = frameLeft; + if (!dmasound.soft.stereo) { + u_short *p = (u_short *)&frame[*frameUsed]; + u_short data = expand_data; + while (frameLeft >= 4) { + if (bal < 0) { + if (userCount < 2) + break; + if (get_user(data, ((u_short *)userPtr)++)) + return -EFAULT; + data = le2be16(data) ^ 0x8000; + userCount -= 2; + bal += hSpeed; + } + *p++ = data; + *p++ = data; + frameLeft -= 4; + bal -= sSpeed; + } + expand_data = data; + } else { + u_long *p = (u_long *)&frame[*frameUsed]; + u_long data = expand_data; + while (frameLeft >= 4) { + if (bal < 0) { + if (userCount < 4) + break; + if (get_user(data, ((u_int *)userPtr)++)) + return -EFAULT; + data = le2be16dbl(data) ^ 0x80008000; + userCount -= 4; + bal += hSpeed; + } + *p++ = data; + frameLeft -= 4; + bal -= sSpeed; + } + expand_data = data; + } + expand_bal = bal; + used -= userCount; + *frameUsed += usedf-frameLeft; + return used; +} + + +static TRANS transTTNormal = { + ct_ulaw: ata_ct_law, + ct_alaw: ata_ct_law, + ct_s8: ata_ct_s8, + ct_u8: ata_ct_u8, +}; + +static TRANS transTTExpanding = { + ct_ulaw: ata_ctx_law, + ct_alaw: ata_ctx_law, + ct_s8: ata_ctx_s8, + ct_u8: ata_ctx_u8, +}; + +static TRANS transFalconNormal = { + ct_ulaw: ata_ct_law, + ct_alaw: ata_ct_law, + ct_s8: ata_ct_s8, + ct_u8: ata_ct_u8, + ct_s16be: ata_ct_s16be, + ct_u16be: ata_ct_u16be, + ct_s16le: ata_ct_s16le, + ct_u16le: ata_ct_u16le +}; + +static TRANS transFalconExpanding = { + ct_ulaw: ata_ctx_law, + ct_alaw: ata_ctx_law, + ct_s8: ata_ctx_s8, + ct_u8: ata_ctx_u8, + ct_s16be: ata_ctx_s16be, + ct_u16be: ata_ctx_u16be, + ct_s16le: ata_ctx_s16le, + ct_u16le: ata_ctx_u16le, +}; + + +/*** Low level stuff *********************************************************/ + + + +/* + * Atari (TT/Falcon) + */ + +static void AtaOpen(void) +{ + MOD_INC_USE_COUNT; +} + +static void AtaRelease(void) +{ + MOD_DEC_USE_COUNT; +} + +static void *AtaAlloc(unsigned int size, int flags) +{ + return atari_stram_alloc( size, NULL, "dmasound" ); +} + +static void AtaFree(void *obj, unsigned int size) +{ + atari_stram_free( obj ); +} + +static int __init AtaIrqInit(void) +{ + /* Set up timer A. Timer A + will receive a signal upon end of playing from the sound + hardware. Furthermore Timer A is able to count events + and will cause an interrupt after a programmed number + of events. So all we need to keep the music playing is + to provide the sound hardware with new data upon + an interrupt from timer A. */ + mfp.tim_ct_a = 0; /* ++roman: Stop timer before programming! */ + mfp.tim_dt_a = 1; /* Cause interrupt after first event. */ + mfp.tim_ct_a = 8; /* Turn on event counting. */ + /* Register interrupt handler. */ + request_irq(IRQ_MFP_TIMA, AtaInterrupt, IRQ_TYPE_SLOW, "DMA sound", + AtaInterrupt); + mfp.int_en_a |= 0x20; /* Turn interrupt on. */ + mfp.int_mk_a |= 0x20; + return 1; +} + +#ifdef MODULE +static void AtaIrqCleanUp(void) +{ + mfp.tim_ct_a = 0; /* stop timer */ + mfp.int_en_a &= ~0x20; /* turn interrupt off */ + free_irq(IRQ_MFP_TIMA, AtaInterrupt); +} +#endif /* MODULE */ + + +#define TONE_VOXWARE_TO_DB(v) \ + (((v) < 0) ? -12 : ((v) > 100) ? 12 : ((v) - 50) * 6 / 25) +#define TONE_DB_TO_VOXWARE(v) (((v) * 25 + ((v) > 0 ? 5 : -5)) / 6 + 50) + + +static int AtaSetBass(int bass) +{ + dmasound.bass = TONE_VOXWARE_TO_DB(bass); + atari_microwire_cmd(MW_LM1992_BASS(dmasound.bass)); + return TONE_DB_TO_VOXWARE(dmasound.bass); +} + + +static int AtaSetTreble(int treble) +{ + dmasound.treble = TONE_VOXWARE_TO_DB(treble); + atari_microwire_cmd(MW_LM1992_TREBLE(dmasound.treble)); + return TONE_DB_TO_VOXWARE(dmasound.treble); +} + + + +/* + * TT + */ + + +static void TTSilence(void) +{ + tt_dmasnd.ctrl = DMASND_CTRL_OFF; + atari_microwire_cmd(MW_LM1992_PSG_HIGH); /* mix in PSG signal 1:1 */ +} + + +static void TTInit(void) +{ + int mode, i, idx; + const int freq[4] = {50066, 25033, 12517, 6258}; + + /* search a frequency that fits into the allowed error range */ + + idx = -1; + for (i = 0; i < arraysize(freq); i++) + /* this isn't as much useful for a TT than for a Falcon, but + * then it doesn't hurt very much to implement it for a TT too. + */ + if ((100 * abs(dmasound.soft.speed - freq[i]) / freq[i]) < catchRadius) + idx = i; + if (idx > -1) { + dmasound.soft.speed = freq[idx]; + dmasound.trans_write = &transTTNormal; + } else + dmasound.trans_write = &transTTExpanding; + + TTSilence(); + dmasound.hard = dmasound.soft; + + if (dmasound.hard.speed > 50066) { + /* we would need to squeeze the sound, but we won't do that */ + dmasound.hard.speed = 50066; + mode = DMASND_MODE_50KHZ; + dmasound.trans_write = &transTTNormal; + } else if (dmasound.hard.speed > 25033) { + dmasound.hard.speed = 50066; + mode = DMASND_MODE_50KHZ; + } else if (dmasound.hard.speed > 12517) { + dmasound.hard.speed = 25033; + mode = DMASND_MODE_25KHZ; + } else if (dmasound.hard.speed > 6258) { + dmasound.hard.speed = 12517; + mode = DMASND_MODE_12KHZ; + } else { + dmasound.hard.speed = 6258; + mode = DMASND_MODE_6KHZ; + } + + tt_dmasnd.mode = (dmasound.hard.stereo ? + DMASND_MODE_STEREO : DMASND_MODE_MONO) | + DMASND_MODE_8BIT | mode; + + expand_bal = -dmasound.soft.speed; +} + + +static int TTSetFormat(int format) +{ + /* TT sound DMA supports only 8bit modes */ + + switch (format) { + case AFMT_QUERY: + return dmasound.soft.format; + case AFMT_MU_LAW: + case AFMT_A_LAW: + case AFMT_S8: + case AFMT_U8: + break; + default: + format = AFMT_S8; + } + + dmasound.soft.format = format; + dmasound.soft.size = 8; + if (dmasound.minDev == SND_DEV_DSP) { + dmasound.dsp.format = format; + dmasound.dsp.size = 8; + } + TTInit(); + + return format; +} + + +#define VOLUME_VOXWARE_TO_DB(v) \ + (((v) < 0) ? -40 : ((v) > 100) ? 0 : ((v) * 2) / 5 - 40) +#define VOLUME_DB_TO_VOXWARE(v) ((((v) + 40) * 5 + 1) / 2) + + +static int TTSetVolume(int volume) +{ + dmasound.volume_left = VOLUME_VOXWARE_TO_DB(volume & 0xff); + atari_microwire_cmd(MW_LM1992_BALLEFT(dmasound.volume_left)); + dmasound.volume_right = VOLUME_VOXWARE_TO_DB((volume & 0xff00) >> 8); + atari_microwire_cmd(MW_LM1992_BALRIGHT(dmasound.volume_right)); + return VOLUME_DB_TO_VOXWARE(dmasound.volume_left) | + (VOLUME_DB_TO_VOXWARE(dmasound.volume_right) << 8); +} + + +#define GAIN_VOXWARE_TO_DB(v) \ + (((v) < 0) ? -80 : ((v) > 100) ? 0 : ((v) * 4) / 5 - 80) +#define GAIN_DB_TO_VOXWARE(v) ((((v) + 80) * 5 + 1) / 4) + +static int TTSetGain(int gain) +{ + dmasound.gain = GAIN_VOXWARE_TO_DB(gain); + atari_microwire_cmd(MW_LM1992_VOLUME(dmasound.gain)); + return GAIN_DB_TO_VOXWARE(dmasound.gain); +} + + + +/* + * Falcon + */ + + +static void FalconSilence(void) +{ + /* stop playback, set sample rate 50kHz for PSG sound */ + tt_dmasnd.ctrl = DMASND_CTRL_OFF; + tt_dmasnd.mode = DMASND_MODE_50KHZ | DMASND_MODE_STEREO | DMASND_MODE_8BIT; + tt_dmasnd.int_div = 0; /* STE compatible divider */ + tt_dmasnd.int_ctrl = 0x0; + tt_dmasnd.cbar_src = 0x0000; /* no matrix inputs */ + tt_dmasnd.cbar_dst = 0x0000; /* no matrix outputs */ + tt_dmasnd.dac_src = 1; /* connect ADC to DAC, disconnect matrix */ + tt_dmasnd.adc_src = 3; /* ADC Input = PSG */ +} + + +static void FalconInit(void) +{ + int divider, i, idx; + const int freq[8] = {49170, 32780, 24585, 19668, 16390, 12292, 9834, 8195}; + + /* search a frequency that fits into the allowed error range */ + + idx = -1; + for (i = 0; i < arraysize(freq); i++) + /* if we will tolerate 3% error 8000Hz->8195Hz (2.38%) would + * be playable without expanding, but that now a kernel runtime + * option + */ + if ((100 * abs(dmasound.soft.speed - freq[i]) / freq[i]) < catchRadius) + idx = i; + if (idx > -1) { + dmasound.soft.speed = freq[idx]; + dmasound.trans_write = &transFalconNormal; + } else + dmasound.trans_write = &transFalconExpanding; + + FalconSilence(); + dmasound.hard = dmasound.soft; + + if (dmasound.hard.size == 16) { + /* the Falcon can play 16bit samples only in stereo */ + dmasound.hard.stereo = 1; + } + + if (dmasound.hard.speed > 49170) { + /* we would need to squeeze the sound, but we won't do that */ + dmasound.hard.speed = 49170; + divider = 1; + dmasound.trans_write = &transFalconNormal; + } else if (dmasound.hard.speed > 32780) { + dmasound.hard.speed = 49170; + divider = 1; + } else if (dmasound.hard.speed > 24585) { + dmasound.hard.speed = 32780; + divider = 2; + } else if (dmasound.hard.speed > 19668) { + dmasound.hard.speed = 24585; + divider = 3; + } else if (dmasound.hard.speed > 16390) { + dmasound.hard.speed = 19668; + divider = 4; + } else if (dmasound.hard.speed > 12292) { + dmasound.hard.speed = 16390; + divider = 5; + } else if (dmasound.hard.speed > 9834) { + dmasound.hard.speed = 12292; + divider = 7; + } else if (dmasound.hard.speed > 8195) { + dmasound.hard.speed = 9834; + divider = 9; + } else { + dmasound.hard.speed = 8195; + divider = 11; + } + tt_dmasnd.int_div = divider; + + /* Setup Falcon sound DMA for playback */ + tt_dmasnd.int_ctrl = 0x4; /* Timer A int at play end */ + tt_dmasnd.track_select = 0x0; /* play 1 track, track 1 */ + tt_dmasnd.cbar_src = 0x0001; /* DMA(25MHz) --> DAC */ + tt_dmasnd.cbar_dst = 0x0000; + tt_dmasnd.rec_track_select = 0; + tt_dmasnd.dac_src = 2; /* connect matrix to DAC */ + tt_dmasnd.adc_src = 0; /* ADC Input = Mic */ + + tt_dmasnd.mode = (dmasound.hard.stereo ? + DMASND_MODE_STEREO : DMASND_MODE_MONO) | + ((dmasound.hard.size == 8) ? + DMASND_MODE_8BIT : DMASND_MODE_16BIT) | + DMASND_MODE_6KHZ; + + expand_bal = -dmasound.soft.speed; +} + + +static int FalconSetFormat(int format) +{ + int size; + /* Falcon sound DMA supports 8bit and 16bit modes */ + + switch (format) { + case AFMT_QUERY: + return dmasound.soft.format; + case AFMT_MU_LAW: + case AFMT_A_LAW: + case AFMT_U8: + case AFMT_S8: + size = 8; + break; + case AFMT_S16_BE: + case AFMT_U16_BE: + case AFMT_S16_LE: + case AFMT_U16_LE: + size = 16; + break; + default: /* :-) */ + size = 8; + format = AFMT_S8; + } + + dmasound.soft.format = format; + dmasound.soft.size = size; + if (dmasound.minDev == SND_DEV_DSP) { + dmasound.dsp.format = format; + dmasound.dsp.size = dmasound.soft.size; + } + + FalconInit(); + + return format; +} + + +/* This is for the Falcon output *attenuation* in 1.5dB steps, + * i.e. output level from 0 to -22.5dB in -1.5dB steps. + */ +#define VOLUME_VOXWARE_TO_ATT(v) \ + ((v) < 0 ? 15 : (v) > 100 ? 0 : 15 - (v) * 3 / 20) +#define VOLUME_ATT_TO_VOXWARE(v) (100 - (v) * 20 / 3) + + +static int FalconSetVolume(int volume) +{ + dmasound.volume_left = VOLUME_VOXWARE_TO_ATT(volume & 0xff); + dmasound.volume_right = VOLUME_VOXWARE_TO_ATT((volume & 0xff00) >> 8); + tt_dmasnd.output_atten = dmasound.volume_left << 8 | dmasound.volume_right << 4; + return VOLUME_ATT_TO_VOXWARE(dmasound.volume_left) | + VOLUME_ATT_TO_VOXWARE(dmasound.volume_right) << 8; +} + + +static void AtaPlayNextFrame(int index) +{ + char *start, *end; + + /* used by AtaPlay() if all doubts whether there really is something + * to be played are already wiped out. + */ + start = write_sq.buffers[write_sq.front]; + end = start+((write_sq.count == index) ? write_sq.rear_size + : write_sq.block_size); + /* end might not be a legal virtual address. */ + DMASNDSetEnd(virt_to_phys(end - 1) + 1); + DMASNDSetBase(virt_to_phys(start)); + /* Since only an even number of samples per frame can + be played, we might lose one byte here. (TO DO) */ + write_sq.front = (write_sq.front+1) % write_sq.max_count; + write_sq.active++; + tt_dmasnd.ctrl = DMASND_CTRL_ON | DMASND_CTRL_REPEAT; +} + + +static void AtaPlay(void) +{ + /* ++TeSche: Note that write_sq.active is no longer just a flag but + * holds the number of frames the DMA is currently programmed for + * instead, may be 0, 1 (currently being played) or 2 (pre-programmed). + * + * Changes done to write_sq.count and write_sq.active are a bit more + * subtle again so now I must admit I also prefer disabling the irq + * here rather than considering all possible situations. But the point + * is that disabling the irq doesn't have any bad influence on this + * version of the driver as we benefit from having pre-programmed the + * DMA wherever possible: There's no need to reload the DMA at the + * exact time of an interrupt but only at some time while the + * pre-programmed frame is playing! + */ + atari_disable_irq(IRQ_MFP_TIMA); + + if (write_sq.active == 2 || /* DMA is 'full' */ + write_sq.count <= 0) { /* nothing to do */ + atari_enable_irq(IRQ_MFP_TIMA); + return; + } + + if (write_sq.active == 0) { + /* looks like there's nothing 'in' the DMA yet, so try + * to put two frames into it (at least one is available). + */ + if (write_sq.count == 1 && + write_sq.rear_size < write_sq.block_size && + !write_sq.syncing) { + /* hmmm, the only existing frame is not + * yet filled and we're not syncing? + */ + atari_enable_irq(IRQ_MFP_TIMA); + return; + } + AtaPlayNextFrame(1); + if (write_sq.count == 1) { + /* no more frames */ + atari_enable_irq(IRQ_MFP_TIMA); + return; + } + if (write_sq.count == 2 && + write_sq.rear_size < write_sq.block_size && + !write_sq.syncing) { + /* hmmm, there were two frames, but the second + * one is not yet filled and we're not syncing? + */ + atari_enable_irq(IRQ_MFP_TIMA); + return; + } + AtaPlayNextFrame(2); + } else { + /* there's already a frame being played so we may only stuff + * one new into the DMA, but even if this may be the last + * frame existing the previous one is still on write_sq.count. + */ + if (write_sq.count == 2 && + write_sq.rear_size < write_sq.block_size && + !write_sq.syncing) { + /* hmmm, the only existing frame is not + * yet filled and we're not syncing? + */ + atari_enable_irq(IRQ_MFP_TIMA); + return; + } + AtaPlayNextFrame(2); + } + atari_enable_irq(IRQ_MFP_TIMA); +} + + +static void AtaInterrupt(int irq, void *dummy, struct pt_regs *fp) +{ +#if 0 + /* ++TeSche: if you should want to test this... */ + static int cnt = 0; + if (write_sq.active == 2) + if (++cnt == 10) { + /* simulate losing an interrupt */ + cnt = 0; + return; + } +#endif + + if (write_sq_ignore_int && is_falcon) { + /* ++TeSche: Falcon only: ignore first irq because it comes + * immediately after starting a frame. after that, irqs come + * (almost) like on the TT. + */ + write_sq_ignore_int = 0; + return; + } + + if (!write_sq.active) { + /* playing was interrupted and sq_reset() has already cleared + * the sq variables, so better don't do anything here. + */ + WAKE_UP(write_sq.sync_queue); + return; + } + + /* Probably ;) one frame is finished. Well, in fact it may be that a + * pre-programmed one is also finished because there has been a long + * delay in interrupt delivery and we've completely lost one, but + * there's no way to detect such a situation. In such a case the last + * frame will be played more than once and the situation will recover + * as soon as the irq gets through. + */ + write_sq.count--; + write_sq.active--; + + if (!write_sq.active) { + tt_dmasnd.ctrl = DMASND_CTRL_OFF; + write_sq_ignore_int = 1; + } + + WAKE_UP(write_sq.action_queue); + /* At least one block of the queue is free now + so wake up a writing process blocked because + of a full queue. */ + + if ((write_sq.active != 1) || (write_sq.count != 1)) + /* We must be a bit carefully here: write_sq.count indicates the + * number of buffers used and not the number of frames to be + * played. If write_sq.count==1 and write_sq.active==1 that + * means the only remaining frame was already programmed + * earlier (and is currently running) so we mustn't call + * AtaPlay() here, otherwise we'll play one frame too much. + */ + AtaPlay(); + + if (!write_sq.active) WAKE_UP(write_sq.sync_queue); + /* We are not playing after AtaPlay(), so there + is nothing to play any more. Wake up a process + waiting for audio output to drain. */ +} + + +/*** Mid level stuff *********************************************************/ + + +/* + * /dev/mixer abstraction + */ + +#define RECLEVEL_VOXWARE_TO_GAIN(v) \ + ((v) < 0 ? 0 : (v) > 100 ? 15 : (v) * 3 / 20) +#define RECLEVEL_GAIN_TO_VOXWARE(v) (((v) * 20 + 2) / 3) + + +static void __init TTMixerInit(void) +{ + atari_microwire_cmd(MW_LM1992_VOLUME(0)); + dmasound.volume_left = 0; + atari_microwire_cmd(MW_LM1992_BALLEFT(0)); + dmasound.volume_right = 0; + atari_microwire_cmd(MW_LM1992_BALRIGHT(0)); + atari_microwire_cmd(MW_LM1992_TREBLE(0)); + atari_microwire_cmd(MW_LM1992_BASS(0)); +} + +static void __init FalconMixerInit(void) +{ + dmasound.volume_left = (tt_dmasnd.output_atten & 0xf00) >> 8; + dmasound.volume_right = (tt_dmasnd.output_atten & 0xf0) >> 4; +} + +static int AtaMixerIoctl(u_int cmd, u_long arg) +{ + int data; + switch (cmd) { + case SOUND_MIXER_READ_SPEAKER: + if (is_falcon || MACH_IS_TT) { + int porta; + cli(); + sound_ym.rd_data_reg_sel = 14; + porta = sound_ym.rd_data_reg_sel; + sti(); + return IOCTL_OUT(arg, porta & 0x40 ? 0 : 100); + } + break; + case SOUND_MIXER_WRITE_VOLUME: + IOCTL_IN(arg, data); + return IOCTL_OUT(arg, dmasound_set_volume(data)); + case SOUND_MIXER_WRITE_SPEAKER: + if (is_falcon || MACH_IS_TT) { + int porta; + IOCTL_IN(arg, data); + cli(); + sound_ym.rd_data_reg_sel = 14; + porta = (sound_ym.rd_data_reg_sel & ~0x40) | + (data < 50 ? 0x40 : 0); + sound_ym.wd_data = porta; + sti(); + return IOCTL_OUT(arg, porta & 0x40 ? 0 : 100); + } + } + return -EINVAL; +} + + +static int TTMixerIoctl(u_int cmd, u_long arg) +{ + int data; + switch (cmd) { + case SOUND_MIXER_READ_RECMASK: + return IOCTL_OUT(arg, 0); + case SOUND_MIXER_READ_DEVMASK: + return IOCTL_OUT(arg, + SOUND_MASK_VOLUME | SOUND_MASK_TREBLE | SOUND_MASK_BASS | + (MACH_IS_TT ? SOUND_MASK_SPEAKER : 0)); + case SOUND_MIXER_READ_STEREODEVS: + return IOCTL_OUT(arg, SOUND_MASK_VOLUME); + case SOUND_MIXER_READ_VOLUME: + return IOCTL_OUT(arg, + VOLUME_DB_TO_VOXWARE(dmasound.volume_left) | + (VOLUME_DB_TO_VOXWARE(dmasound.volume_right) << 8)); + case SOUND_MIXER_READ_BASS: + return IOCTL_OUT(arg, TONE_DB_TO_VOXWARE(dmasound.bass)); + case SOUND_MIXER_READ_TREBLE: + return IOCTL_OUT(arg, TONE_DB_TO_VOXWARE(dmasound.treble)); + case SOUND_MIXER_READ_OGAIN: + return IOCTL_OUT(arg, GAIN_DB_TO_VOXWARE(dmasound.gain)); + case SOUND_MIXER_WRITE_BASS: + IOCTL_IN(arg, data); + return IOCTL_OUT(arg, dmasound_set_bass(data)); + case SOUND_MIXER_WRITE_TREBLE: + IOCTL_IN(arg, data); + return IOCTL_OUT(arg, dmasound_set_treble(data)); + case SOUND_MIXER_WRITE_OGAIN: + IOCTL_IN(arg, data); + return IOCTL_OUT(arg, dmasound_set_gain(data)); + } + return AtaMixerIoctl(cmd, arg); +} + +static int FalconMixerIoctl(u_int cmd, u_long arg) +{ + int data; + switch (cmd) { + case SOUND_MIXER_READ_RECMASK: + return IOCTL_OUT(arg, SOUND_MASK_MIC); + case SOUND_MIXER_READ_DEVMASK: + return IOCTL_OUT(arg, SOUND_MASK_VOLUME | SOUND_MASK_MIC | SOUND_MASK_SPEAKER); + case SOUND_MIXER_READ_STEREODEVS: + return IOCTL_OUT(arg, SOUND_MASK_VOLUME | SOUND_MASK_MIC); + case SOUND_MIXER_READ_VOLUME: + return IOCTL_OUT(arg, + VOLUME_ATT_TO_VOXWARE(dmasound.volume_left) | + VOLUME_ATT_TO_VOXWARE(dmasound.volume_right) << 8); + case SOUND_MIXER_READ_CAPS: + return IOCTL_OUT(arg, SOUND_CAP_EXCL_INPUT); + case SOUND_MIXER_WRITE_MIC: + IOCTL_IN(arg, data); + tt_dmasnd.input_gain = + RECLEVEL_VOXWARE_TO_GAIN(data & 0xff) << 4 | + RECLEVEL_VOXWARE_TO_GAIN(data >> 8 & 0xff); + /* fall thru, return set value */ + case SOUND_MIXER_READ_MIC: + return IOCTL_OUT(arg, + RECLEVEL_GAIN_TO_VOXWARE(tt_dmasnd.input_gain >> 4 & 0xf) | + RECLEVEL_GAIN_TO_VOXWARE(tt_dmasnd.input_gain & 0xf) << 8); + } + return AtaMixerIoctl(cmd, arg); +} + +static void AtaWriteSqSetup(void) +{ + write_sq_ignore_int = 0; +} + +static void AtaSqOpen(void) +{ + write_sq_ignore_int = 1; +} + +static int TTStateInfo(char *buffer) +{ + int len = 0; + len += sprintf(buffer+len, "\tsound.volume_left = %ddB [-40...0]\n", + dmasound.volume_left); + len += sprintf(buffer+len, "\tsound.volume_right = %ddB [-40...0]\n", + dmasound.volume_right); + len += sprintf(buffer+len, "\tsound.bass = %ddB [-12...+12]\n", + dmasound.bass); + len += sprintf(buffer+len, "\tsound.treble = %ddB [-12...+12]\n", + dmasound.treble); + return len; +} + +static int FalconStateInfo(char *buffer) +{ + int len = 0; + len += sprintf(buffer+len, "\tsound.volume_left = %ddB [-22.5...0]\n", + dmasound.volume_left); + len += sprintf(buffer+len, "\tsound.volume_right = %ddB [-22.5...0]\n", + dmasound.volume_right); + return len; +} + + +/*** Machine definitions *****************************************************/ + + +static MACHINE machTT = { + name: "Atari", + name2: "TT", + open: AtaOpen, + release: AtaRelease, + dma_alloc: AtaAlloc, + dma_free: AtaFree, + irqinit: AtaIrqInit, +#ifdef MODULE + irqcleanup: AtaIrqCleanUp, +#endif /* MODULE */ + init: TTInit, + silence: TTSilence, + setFormat: TTSetFormat, + setVolume: TTSetVolume, + setBass: AtaSetBass, + setTreble: AtaSetTreble, + setGain: TTSetGain, + play: AtaPlay, + mixer_init: TTMixerInit, + mixer_ioctl: TTMixerIoctl, + write_sq_setup: AtaWriteSqSetup, + sq_open: AtaSqOpen, + state_info: TTStateInfo, + min_dsp_speed: 6258, +}; + +static MACHINE machFalcon = { + name: "Atari", + name2: "FALCON", + dma_alloc: AtaAlloc, + dma_free: AtaFree, + irqinit: AtaIrqInit, +#ifdef MODULE + irqcleanup: AtaIrqCleanUp, +#endif /* MODULE */ + init: FalconInit, + silence: FalconSilence, + setFormat: FalconSetFormat, + setVolume: FalconSetVolume, + setBass: AtaSetBass, + setTreble: AtaSetTreble, + play: AtaPlay, + mixer_init: FalconMixerInit, + mixer_ioctl: FalconMixerIoctl, + write_sq_setup: AtaWriteSqSetup, + sq_open: AtaSqOpen, + state_info: FalconStateInfo, + min_dsp_speed: 8195, +}; + + +/*** Config & Setup **********************************************************/ + + +static int __init dmasound_atari_init(void) +{ + if (MACH_IS_ATARI && ATARIHW_PRESENT(PCM_8BIT)) { + if (ATARIHW_PRESENT(CODEC)) { + dmasound.mach = machFalcon; + is_falcon = 1; + } else if (ATARIHW_PRESENT(MICROWIRE)) { + dmasound.mach = machTT; + is_falcon = 0; + } else + return -ENODEV; + if ((mfp.int_en_a & mfp.int_mk_a & 0x20) == 0) + return dmasound_init(); + else { + printk("DMA sound driver: Timer A interrupt already in use\n"); + return -EBUSY; + } + } + return -ENODEV; +} + +static void __exit dmasound_atari_cleanup(void) +{ + dmasound_deinit(); +} + +module_init(dmasound_atari_init); +module_exit(dmasound_atari_cleanup); diff --git a/drivers/sound/dmasound/dmasound_awacs.c b/drivers/sound/dmasound/dmasound_awacs.c new file mode 100644 index 000000000..55669c65c --- /dev/null +++ b/drivers/sound/dmasound/dmasound_awacs.c @@ -0,0 +1,2113 @@ + +/* + * linux/drivers/sound/dmasound_awacs.c + * + * PowerMac DMA Sound Driver + * + * See linux/drivers/sound/dmasound_core.c for copyright and credits + */ + + +#include <linux/module.h> +#include <linux/config.h> +#include <linux/malloc.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/soundcard.h> +#include <linux/adb.h> +#include <linux/nvram.h> +#include <linux/vt_kern.h> +#include <linux/cuda.h> + +#include <asm/uaccess.h> +#include <asm/prom.h> +#include <asm/machdep.h> +#include <asm/io.h> +#include <asm/dbdma.h> + +#include "awacs_defs.h" +#include "dmasound.h" + + +/* + * Interrupt numbers and addresses, obtained from the device tree. + */ +static int awacs_irq, awacs_tx_irq, awacs_rx_irq; +static volatile struct awacs_regs *awacs; +static volatile struct dbdma_regs *awacs_txdma, *awacs_rxdma; +static int awacs_rate_index; +static int awacs_subframe; +static int awacs_spkr_vol; +static struct device_node* awacs_node; + +static char awacs_name[64]; +static int awacs_revision; +#define AWACS_BURGUNDY 100 /* fake revision # for burgundy */ + +/* + * Space for the DBDMA command blocks. + */ +static void *awacs_tx_cmd_space; +static volatile struct dbdma_cmd *awacs_tx_cmds; + +static void *awacs_rx_cmd_space; +static volatile struct dbdma_cmd *awacs_rx_cmds; + +/* + * Cached values of AWACS registers (we can't read them). + * Except on the burgundy. XXX + */ +int awacs_reg[5]; + +#define HAS_16BIT_TABLES +#undef HAS_8BIT_TABLES + +/* + * Stuff for outputting a beep. The values range from -327 to +327 + * so we can multiply by an amplitude in the range 0..100 to get a + * signed short value to put in the output buffer. + */ +static short beep_wform[256] = { + 0, 40, 79, 117, 153, 187, 218, 245, + 269, 288, 304, 316, 323, 327, 327, 324, + 318, 310, 299, 288, 275, 262, 249, 236, + 224, 213, 204, 196, 190, 186, 183, 182, + 182, 183, 186, 189, 192, 196, 200, 203, + 206, 208, 209, 209, 209, 207, 204, 201, + 197, 193, 188, 183, 179, 174, 170, 166, + 163, 161, 160, 159, 159, 160, 161, 162, + 164, 166, 168, 169, 171, 171, 171, 170, + 169, 167, 163, 159, 155, 150, 144, 139, + 133, 128, 122, 117, 113, 110, 107, 105, + 103, 103, 103, 103, 104, 104, 105, 105, + 105, 103, 101, 97, 92, 86, 78, 68, + 58, 45, 32, 18, 3, -11, -26, -41, + -55, -68, -79, -88, -95, -100, -102, -102, + -99, -93, -85, -75, -62, -48, -33, -16, + 0, 16, 33, 48, 62, 75, 85, 93, + 99, 102, 102, 100, 95, 88, 79, 68, + 55, 41, 26, 11, -3, -18, -32, -45, + -58, -68, -78, -86, -92, -97, -101, -103, + -105, -105, -105, -104, -104, -103, -103, -103, + -103, -105, -107, -110, -113, -117, -122, -128, + -133, -139, -144, -150, -155, -159, -163, -167, + -169, -170, -171, -171, -171, -169, -168, -166, + -164, -162, -161, -160, -159, -159, -160, -161, + -163, -166, -170, -174, -179, -183, -188, -193, + -197, -201, -204, -207, -209, -209, -209, -208, + -206, -203, -200, -196, -192, -189, -186, -183, + -182, -182, -183, -186, -190, -196, -204, -213, + -224, -236, -249, -262, -275, -288, -299, -310, + -318, -324, -327, -327, -323, -316, -304, -288, + -269, -245, -218, -187, -153, -117, -79, -40, +}; + +#define BEEP_SRATE 22050 /* 22050 Hz sample rate */ +#define BEEP_BUFLEN 512 +#define BEEP_VOLUME 15 /* 0 - 100 */ + +static int beep_volume = BEEP_VOLUME; +static int beep_playing = 0; +static int awacs_beep_state = 0; +static short *beep_buf; +static volatile struct dbdma_cmd *beep_dbdma_cmd; +static void (*orig_mksound)(unsigned int, unsigned int); +static int is_pbook_3400; +static unsigned char *latch_base; +static int is_pbook_G3; +static unsigned char *macio_base; + +/* Burgundy functions */ +static void awacs_burgundy_wcw(unsigned addr,unsigned newval); +static unsigned awacs_burgundy_rcw(unsigned addr); +static void awacs_burgundy_write_volume(unsigned address, int volume); +static int awacs_burgundy_read_volume(unsigned address); +static void awacs_burgundy_write_mvolume(unsigned address, int volume); +static int awacs_burgundy_read_mvolume(unsigned address); + +#ifdef CONFIG_PMAC_PBOOK +/* + * Stuff for restoring after a sleep. + */ +static int awacs_sleep_notify(struct pmu_sleep_notifier *self, int when); +struct pmu_sleep_notifier awacs_sleep_notifier = { + awacs_sleep_notify, SLEEP_LEVEL_SOUND, +}; +#endif /* CONFIG_PMAC_PBOOK */ + +static int expand_bal; /* Balance factor for expanding (not volume!) */ +static int expand_data; /* Data for expanding */ + + +/*** Translations ************************************************************/ + + +/* ++TeSche: radically changed for new expanding purposes... + * + * These two routines now deal with copying/expanding/translating the samples + * from user space into our buffer at the right frequency. They take care about + * how much data there's actually to read, how much buffer space there is and + * to convert samples into the right frequency/encoding. They will only work on + * complete samples so it may happen they leave some bytes in the input stream + * if the user didn't write a multiple of the current sample size. They both + * return the number of bytes they've used from both streams so you may detect + * such a situation. Luckily all programs should be able to cope with that. + * + * I think I've optimized anything as far as one can do in plain C, all + * variables should fit in registers and the loops are really short. There's + * one loop for every possible situation. Writing a more generalized and thus + * parameterized loop would only produce slower code. Feel free to optimize + * this in assembler if you like. :) + * + * I think these routines belong here because they're not yet really hardware + * independent, especially the fact that the Falcon can play 16bit samples + * only in stereo is hardcoded in both of them! + * + * ++geert: split in even more functions (one per format) + */ + +static ssize_t pmac_ct_law(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t pmac_ct_s8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t pmac_ct_u8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t pmac_ct_s16(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t pmac_ct_u16(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t pmac_ctx_law(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t pmac_ctx_s8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t pmac_ctx_u8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t pmac_ctx_s16(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t pmac_ctx_u16(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t pmac_ct_s16_read(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t pmac_ct_u16_read(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); + + +/*** Low level stuff *********************************************************/ + + +static void PMacOpen(void); +static void PMacRelease(void); +static void *PMacAlloc(unsigned int size, int flags); +static void PMacFree(void *ptr, unsigned int size); +static int PMacIrqInit(void); +#ifdef MODULE +static void PMacIrqCleanup(void); +#endif +static void PMacSilence(void); +static void PMacInit(void); +static int PMacSetFormat(int format); +static int PMacSetVolume(int volume); +static void PMacPlay(void); +static void PMacRecord(void); +static void pmac_awacs_tx_intr(int irq, void *devid, struct pt_regs *regs); +static void pmac_awacs_rx_intr(int irq, void *devid, struct pt_regs *regs); +static void pmac_awacs_intr(int irq, void *devid, struct pt_regs *regs); +static void awacs_write(int val); +static int awacs_get_volume(int reg, int lshift); +static int awacs_volume_setter(int volume, int n, int mute, int lshift); +static void awacs_mksound(unsigned int hz, unsigned int ticks); +static void awacs_nosound(unsigned long xx); + + +/*** Mid level stuff **********************************************************/ + + +static int PMacMixerIoctl(u_int cmd, u_long arg); +static void PMacWriteSqSetup(void); +static void PMacReadSqSetup(void); +static void PMacAbortRead(void); + + +/*** Translations ************************************************************/ + + +static ssize_t pmac_ct_law(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + short *table = dmasound.soft.format == AFMT_MU_LAW + ? dmasound_ulaw2dma16 : dmasound_alaw2dma16; + ssize_t count, used; + short *p = (short *) &frame[*frameUsed]; + int val, stereo = dmasound.soft.stereo; + + frameLeft >>= 2; + if (stereo) + userCount >>= 1; + used = count = min(userCount, frameLeft); + while (count > 0) { + u_char data; + if (get_user(data, userPtr++)) + return -EFAULT; + val = table[data]; + *p++ = val; + if (stereo) { + if (get_user(data, userPtr++)) + return -EFAULT; + val = table[data]; + } + *p++ = val; + count--; + } + *frameUsed += used * 4; + return stereo? used * 2: used; +} + + +static ssize_t pmac_ct_s8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + short *p = (short *) &frame[*frameUsed]; + int val, stereo = dmasound.soft.stereo; + + frameLeft >>= 2; + if (stereo) + userCount >>= 1; + used = count = min(userCount, frameLeft); + while (count > 0) { + u_char data; + if (get_user(data, userPtr++)) + return -EFAULT; + val = data << 8; + *p++ = val; + if (stereo) { + if (get_user(data, userPtr++)) + return -EFAULT; + val = data << 8; + } + *p++ = val; + count--; + } + *frameUsed += used * 4; + return stereo? used * 2: used; +} + + +static ssize_t pmac_ct_u8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + short *p = (short *) &frame[*frameUsed]; + int val, stereo = dmasound.soft.stereo; + + frameLeft >>= 2; + if (stereo) + userCount >>= 1; + used = count = min(userCount, frameLeft); + while (count > 0) { + u_char data; + if (get_user(data, userPtr++)) + return -EFAULT; + val = (data ^ 0x80) << 8; + *p++ = val; + if (stereo) { + if (get_user(data, userPtr++)) + return -EFAULT; + val = (data ^ 0x80) << 8; + } + *p++ = val; + count--; + } + *frameUsed += used * 4; + return stereo? used * 2: used; +} + + +static ssize_t pmac_ct_s16(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + int stereo = dmasound.soft.stereo; + short *fp = (short *) &frame[*frameUsed]; + + frameLeft >>= 2; + userCount >>= (stereo? 2: 1); + used = count = min(userCount, frameLeft); + if (!stereo) { + short *up = (short *) userPtr; + while (count > 0) { + short data; + if (get_user(data, up++)) + return -EFAULT; + *fp++ = data; + *fp++ = data; + count--; + } + } else { + if (copy_from_user(fp, userPtr, count * 4)) + return -EFAULT; + } + *frameUsed += used * 4; + return stereo? used * 4: used * 2; +} + +static ssize_t pmac_ct_u16(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + int mask = (dmasound.soft.format == AFMT_U16_LE? 0x0080: 0x8000); + int stereo = dmasound.soft.stereo; + short *fp = (short *) &frame[*frameUsed]; + short *up = (short *) userPtr; + + frameLeft >>= 2; + userCount >>= (stereo? 2: 1); + used = count = min(userCount, frameLeft); + while (count > 0) { + int data; + if (get_user(data, up++)) + return -EFAULT; + data ^= mask; + *fp++ = data; + if (stereo) { + if (get_user(data, up++)) + return -EFAULT; + data ^= mask; + } + *fp++ = data; + count--; + } + *frameUsed += used * 4; + return stereo? used * 4: used * 2; +} + + +static ssize_t pmac_ctx_law(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + unsigned short *table = (unsigned short *) + (dmasound.soft.format == AFMT_MU_LAW + ? dmasound_ulaw2dma16 : dmasound_alaw2dma16); + unsigned int data = expand_data; + unsigned int *p = (unsigned int *) &frame[*frameUsed]; + int bal = expand_bal; + int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; + int utotal, ftotal; + int stereo = dmasound.soft.stereo; + + frameLeft >>= 2; + if (stereo) + userCount >>= 1; + ftotal = frameLeft; + utotal = userCount; + while (frameLeft) { + u_char c; + if (bal < 0) { + if (userCount == 0) + break; + if (get_user(c, userPtr++)) + return -EFAULT; + data = table[c]; + if (stereo) { + if (get_user(c, userPtr++)) + return -EFAULT; + data = (data << 16) + table[c]; + } else + data = (data << 16) + data; + userCount--; + bal += hSpeed; + } + *p++ = data; + frameLeft--; + bal -= sSpeed; + } + expand_bal = bal; + expand_data = data; + *frameUsed += (ftotal - frameLeft) * 4; + utotal -= userCount; + return stereo? utotal * 2: utotal; +} + + +static ssize_t pmac_ctx_s8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + unsigned int *p = (unsigned int *) &frame[*frameUsed]; + unsigned int data = expand_data; + int bal = expand_bal; + int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; + int stereo = dmasound.soft.stereo; + int utotal, ftotal; + + frameLeft >>= 2; + if (stereo) + userCount >>= 1; + ftotal = frameLeft; + utotal = userCount; + while (frameLeft) { + u_char c; + if (bal < 0) { + if (userCount == 0) + break; + if (get_user(c, userPtr++)) + return -EFAULT; + data = c << 8; + if (stereo) { + if (get_user(c, userPtr++)) + return -EFAULT; + data = (data << 16) + (c << 8); + } else + data = (data << 16) + data; + userCount--; + bal += hSpeed; + } + *p++ = data; + frameLeft--; + bal -= sSpeed; + } + expand_bal = bal; + expand_data = data; + *frameUsed += (ftotal - frameLeft) * 4; + utotal -= userCount; + return stereo? utotal * 2: utotal; +} + + +static ssize_t pmac_ctx_u8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + unsigned int *p = (unsigned int *) &frame[*frameUsed]; + unsigned int data = expand_data; + int bal = expand_bal; + int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; + int stereo = dmasound.soft.stereo; + int utotal, ftotal; + + frameLeft >>= 2; + if (stereo) + userCount >>= 1; + ftotal = frameLeft; + utotal = userCount; + while (frameLeft) { + u_char c; + if (bal < 0) { + if (userCount == 0) + break; + if (get_user(c, userPtr++)) + return -EFAULT; + data = (c ^ 0x80) << 8; + if (stereo) { + if (get_user(c, userPtr++)) + return -EFAULT; + data = (data << 16) + ((c ^ 0x80) << 8); + } else + data = (data << 16) + data; + userCount--; + bal += hSpeed; + } + *p++ = data; + frameLeft--; + bal -= sSpeed; + } + expand_bal = bal; + expand_data = data; + *frameUsed += (ftotal - frameLeft) * 4; + utotal -= userCount; + return stereo? utotal * 2: utotal; +} + + +static ssize_t pmac_ctx_s16(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + unsigned int *p = (unsigned int *) &frame[*frameUsed]; + unsigned int data = expand_data; + unsigned short *up = (unsigned short *) userPtr; + int bal = expand_bal; + int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; + int stereo = dmasound.soft.stereo; + int utotal, ftotal; + + frameLeft >>= 2; + userCount >>= (stereo? 2: 1); + ftotal = frameLeft; + utotal = userCount; + while (frameLeft) { + unsigned short c; + if (bal < 0) { + if (userCount == 0) + break; + if (get_user(data, up++)) + return -EFAULT; + if (stereo) { + if (get_user(c, up++)) + return -EFAULT; + data = (data << 16) + c; + } else + data = (data << 16) + data; + userCount--; + bal += hSpeed; + } + *p++ = data; + frameLeft--; + bal -= sSpeed; + } + expand_bal = bal; + expand_data = data; + *frameUsed += (ftotal - frameLeft) * 4; + utotal -= userCount; + return stereo? utotal * 4: utotal * 2; +} + + +static ssize_t pmac_ctx_u16(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + int mask = (dmasound.soft.format == AFMT_U16_LE? 0x0080: 0x8000); + unsigned int *p = (unsigned int *) &frame[*frameUsed]; + unsigned int data = expand_data; + unsigned short *up = (unsigned short *) userPtr; + int bal = expand_bal; + int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; + int stereo = dmasound.soft.stereo; + int utotal, ftotal; + + frameLeft >>= 2; + userCount >>= (stereo? 2: 1); + ftotal = frameLeft; + utotal = userCount; + while (frameLeft) { + unsigned short c; + if (bal < 0) { + if (userCount == 0) + break; + if (get_user(data, up++)) + return -EFAULT; + data ^= mask; + if (stereo) { + if (get_user(c, up++)) + return -EFAULT; + data = (data << 16) + (c ^ mask); + } else + data = (data << 16) + data; + userCount--; + bal += hSpeed; + } + *p++ = data; + frameLeft--; + bal -= sSpeed; + } + expand_bal = bal; + expand_data = data; + *frameUsed += (ftotal - frameLeft) * 4; + utotal -= userCount; + return stereo? utotal * 4: utotal * 2; +} + +static ssize_t pmac_ct_s8_read(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + short *p = (short *) &frame[*frameUsed]; + int val, stereo = dmasound.soft.stereo; + + frameLeft >>= 2; + if (stereo) + userCount >>= 1; + used = count = min(userCount, frameLeft); + while (count > 0) { + u_char data; + + val = *p++; + data = val >> 8; + if (put_user(data, (u_char *)userPtr++)) + return -EFAULT; + if (stereo) { + val = *p; + data = val >> 8; + if (put_user(data, (u_char *)userPtr++)) + return -EFAULT; + } + p++; + count--; + } + *frameUsed += used * 4; + return stereo? used * 2: used; +} + + +static ssize_t pmac_ct_u8_read(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + short *p = (short *) &frame[*frameUsed]; + int val, stereo = dmasound.soft.stereo; + + frameLeft >>= 2; + if (stereo) + userCount >>= 1; + used = count = min(userCount, frameLeft); + while (count > 0) { + u_char data; + + val = *p++; + data = (val >> 8) ^ 0x80; + if (put_user(data, (u_char *)userPtr++)) + return -EFAULT; + if (stereo) { + val = *p; + data = (val >> 8) ^ 0x80; + if (put_user(data, (u_char *)userPtr++)) + return -EFAULT; + } + p++; + count--; + } + *frameUsed += used * 4; + return stereo? used * 2: used; +} + + +static ssize_t pmac_ct_s16_read(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + int stereo = dmasound.soft.stereo; + short *fp = (short *) &frame[*frameUsed]; + + frameLeft >>= 2; + userCount >>= (stereo? 2: 1); + used = count = min(userCount, frameLeft); + if (!stereo) { + short *up = (short *) userPtr; + while (count > 0) { + short data; + data = *fp; + if (put_user(data, up++)) + return -EFAULT; + fp+=2; + count--; + } + } else { + if (copy_to_user((u_char *)userPtr, fp, count * 4)) + return -EFAULT; + } + *frameUsed += used * 4; + return stereo? used * 4: used * 2; +} + +static ssize_t pmac_ct_u16_read(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + int mask = (dmasound.soft.format == AFMT_U16_LE? 0x0080: 0x8000); + int stereo = dmasound.soft.stereo; + short *fp = (short *) &frame[*frameUsed]; + short *up = (short *) userPtr; + + frameLeft >>= 2; + userCount >>= (stereo? 2: 1); + used = count = min(userCount, frameLeft); + while (count > 0) { + int data; + + data = *fp++; + data ^= mask; + if (put_user(data, up++)) + return -EFAULT; + if (stereo) { + data = *fp; + data ^= mask; + if (put_user(data, up++)) + return -EFAULT; + } + fp++; + count--; + } + *frameUsed += used * 4; + return stereo? used * 4: used * 2; +} + + +static TRANS transAwacsNormal = { + ct_ulaw: pmac_ct_law, + ct_alaw: pmac_ct_law, + ct_s8: pmac_ct_s8, + ct_u8: pmac_ct_u8, + ct_s16be: pmac_ct_s16, + ct_u16be: pmac_ct_u16, + ct_s16le: pmac_ct_s16, + ct_u16le: pmac_ct_u16, +}; + +static TRANS transAwacsExpand = { + ct_ulaw: pmac_ctx_law, + ct_alaw: pmac_ctx_law, + ct_s8: pmac_ctx_s8, + ct_u8: pmac_ctx_u8, + ct_s16be: pmac_ctx_s16, + ct_u16be: pmac_ctx_u16, + ct_s16le: pmac_ctx_s16, + ct_u16le: pmac_ctx_u16, +}; + +static TRANS transAwacsNormalRead = { + ct_s8: pmac_ct_s8_read, + ct_u8: pmac_ct_u8_read, + ct_s16be: pmac_ct_s16_read, + ct_u16be: pmac_ct_u16_read, + ct_s16le: pmac_ct_s16_read, + ct_u16le: pmac_ct_u16_read, +}; + +/*** Low level stuff *********************************************************/ + + + +/* + * PCI PowerMac, with AWACS and DBDMA. + */ + +static void PMacOpen(void) +{ + MOD_INC_USE_COUNT; +} + +static void PMacRelease(void) +{ + MOD_DEC_USE_COUNT; +} + +static void *PMacAlloc(unsigned int size, int flags) +{ + return kmalloc(size, flags); +} + +static void PMacFree(void *ptr, unsigned int size) +{ + kfree(ptr); +} + +static int __init PMacIrqInit(void) +{ + if (request_irq(awacs_irq, pmac_awacs_intr, 0, "AWACS", 0) + || request_irq(awacs_tx_irq, pmac_awacs_tx_intr, 0, "AWACS out", 0) + || request_irq(awacs_rx_irq, pmac_awacs_rx_intr, 0, "AWACS in", 0)) + return 0; + return 1; +} + +#ifdef MODULE +static void PMacIrqCleanup(void) +{ + /* turn off output dma */ + out_le32(&awacs_txdma->control, RUN<<16); + /* disable interrupts from awacs interface */ + out_le32(&awacs->control, in_le32(&awacs->control) & 0xfff); +#ifdef CONFIG_PMAC_PBOOK + if (is_pbook_G3) { + feature_clear(awacs_node, FEATURE_Sound_power); + feature_clear(awacs_node, FEATURE_Sound_CLK_enable); + } +#endif + free_irq(awacs_irq, 0); + free_irq(awacs_tx_irq, 0); + free_irq(awacs_rx_irq, 0); + kfree(awacs_tx_cmd_space); + if (awacs_rx_cmd_space) + kfree(awacs_rx_cmd_space); + if (beep_buf) + kfree(beep_buf); + kd_mksound = orig_mksound; +#ifdef CONFIG_PMAC_PBOOK + pmu_unregister_sleep_notifier(&awacs_sleep_notifier); +#endif +} +#endif /* MODULE */ + +static void PMacSilence(void) +{ + /* turn off output dma */ + out_le32(&awacs_txdma->control, RUN<<16); +} + +static int awacs_freqs[8] = { + 44100, 29400, 22050, 17640, 14700, 11025, 8820, 7350 +}; +static int awacs_freqs_ok[8] = { 1, 1, 1, 1, 1, 1, 1, 1 }; + +static void PMacInit(void) +{ + int i, tolerance; + + switch (dmasound.soft.format) { + case AFMT_S16_LE: + case AFMT_U16_LE: + dmasound.hard.format = AFMT_S16_LE; + break; + default: + dmasound.hard.format = AFMT_S16_BE; + break; + } + dmasound.hard.stereo = 1; + dmasound.hard.size = 16; + + /* + * If we have a sample rate which is within catchRadius percent + * of the requested value, we don't have to expand the samples. + * Otherwise choose the next higher rate. + * N.B.: burgundy awacs (iMac and later) only works at 44100 Hz. + */ + i = 8; + do { + tolerance = catchRadius * awacs_freqs[--i] / 100; + if (awacs_freqs_ok[i] + && dmasound.soft.speed <= awacs_freqs[i] + tolerance) + break; + } while (i > 0); + if (dmasound.soft.speed >= awacs_freqs[i] - tolerance) + dmasound.trans_write = &transAwacsNormal; + else + dmasound.trans_write = &transAwacsExpand; + dmasound.trans_read = &transAwacsNormalRead; + dmasound.hard.speed = awacs_freqs[i]; + awacs_rate_index = i; + + /* XXX disable error interrupt on burgundy for now */ + out_le32(&awacs->control, MASK_IEPC | (i << 8) | 0x11 + | (awacs_revision < AWACS_BURGUNDY? MASK_IEE: 0)); + awacs_reg[1] = (awacs_reg[1] & ~MASK_SAMPLERATE) | (i << 3); + awacs_write(awacs_reg[1] | MASK_ADDR1); + out_le32(&awacs->byteswap, dmasound.hard.format != AFMT_S16_BE); + + /* We really want to execute a DMA stop command, after the AWACS + * is initialized. + * For reasons I don't understand, it stops the hissing noise + * common to many PowerBook G3 systems (like mine :-). + */ + out_le32(&awacs_txdma->control, (RUN|WAKE|FLUSH|PAUSE) << 16); + st_le16(&beep_dbdma_cmd->command, DBDMA_STOP); + out_le32(&awacs_txdma->cmdptr, virt_to_bus(beep_dbdma_cmd)); + out_le32(&awacs_txdma->control, RUN | (RUN << 16)); + + expand_bal = -dmasound.soft.speed; +} + +static int PMacSetFormat(int format) +{ + int size; + + switch (format) { + case AFMT_QUERY: + return dmasound.soft.format; + case AFMT_MU_LAW: + case AFMT_A_LAW: + case AFMT_U8: + case AFMT_S8: + size = 8; + break; + case AFMT_S16_BE: + case AFMT_U16_BE: + case AFMT_S16_LE: + case AFMT_U16_LE: + size = 16; + break; + default: /* :-) */ + printk(KERN_ERR "dmasound: unknown format 0x%x, using AFMT_U8\n", + format); + size = 8; + format = AFMT_U8; + } + + dmasound.soft.format = format; + dmasound.soft.size = size; + if (dmasound.minDev == SND_DEV_DSP) { + dmasound.dsp.format = format; + dmasound.dsp.size = size; + } + + PMacInit(); + + return format; +} + +#define AWACS_VOLUME_TO_MASK(x) (15 - ((((x) - 1) * 15) / 99)) +#define AWACS_MASK_TO_VOLUME(y) (100 - ((y) * 99 / 15)) + +static int awacs_get_volume(int reg, int lshift) +{ + int volume; + + volume = AWACS_MASK_TO_VOLUME((reg >> lshift) & 0xf); + volume |= AWACS_MASK_TO_VOLUME(reg & 0xf) << 8; + return volume; +} + +static int awacs_volume_setter(int volume, int n, int mute, int lshift) +{ + int r1, rn; + + if (mute && volume == 0) { + r1 = awacs_reg[1] | mute; + } else { + r1 = awacs_reg[1] & ~mute; + rn = awacs_reg[n] & ~(0xf | (0xf << lshift)); + rn |= ((AWACS_VOLUME_TO_MASK(volume & 0xff) & 0xf) << lshift); + rn |= AWACS_VOLUME_TO_MASK((volume >> 8) & 0xff) & 0xf; + awacs_reg[n] = rn; + awacs_write((n << 12) | rn); + volume = awacs_get_volume(rn, lshift); + } + if (r1 != awacs_reg[1]) { + awacs_reg[1] = r1; + awacs_write(r1 | MASK_ADDR1); + } + return volume; +} + +static int PMacSetVolume(int volume) +{ + return awacs_volume_setter(volume, 2, MASK_AMUTE, 6); +} + +static void PMacPlay(void) +{ + volatile struct dbdma_cmd *cp; + int i, count; + unsigned long flags; + + save_flags(flags); cli(); + if (awacs_beep_state) { + /* sound takes precedence over beeps */ + out_le32(&awacs_txdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16); + out_le32(&awacs->control, + (in_le32(&awacs->control) & ~0x1f00) + | (awacs_rate_index << 8)); + out_le32(&awacs->byteswap, dmasound.hard.format != AFMT_S16_BE); + out_le32(&awacs_txdma->cmdptr, virt_to_bus(&(awacs_tx_cmds[(write_sq.front+write_sq.active) % write_sq.max_count]))); + + beep_playing = 0; + awacs_beep_state = 0; + } + i = write_sq.front + write_sq.active; + if (i >= write_sq.max_count) + i -= write_sq.max_count; + while (write_sq.active < 2 && write_sq.active < write_sq.count) { + count = (write_sq.count == write_sq.active + 1)?write_sq.rear_size:write_sq.block_size; + if (count < write_sq.block_size && !write_sq.syncing) + /* last block not yet filled, and we're not syncing. */ + break; + cp = &awacs_tx_cmds[i]; + st_le16(&cp->req_count, count); + st_le16(&cp->xfer_status, 0); + if (++i >= write_sq.max_count) + i = 0; + out_le16(&awacs_tx_cmds[i].command, DBDMA_STOP); + out_le16(&cp->command, OUTPUT_MORE + INTR_ALWAYS); + if (write_sq.active == 0) + out_le32(&awacs_txdma->cmdptr, virt_to_bus(cp)); + out_le32(&awacs_txdma->control, ((RUN|WAKE) << 16) + (RUN|WAKE)); + ++write_sq.active; + } + restore_flags(flags); +} + + +static void PMacRecord(void) +{ + unsigned long flags; + + if (read_sq.active) + return; + + save_flags(flags); cli(); + + /* This is all we have to do......Just start it up. + */ + out_le32(&awacs_rxdma->control, ((RUN|WAKE) << 16) + (RUN|WAKE)); + read_sq.active = 1; + + restore_flags(flags); +} + + +static void +pmac_awacs_tx_intr(int irq, void *devid, struct pt_regs *regs) +{ + int i = write_sq.front; + int stat; + volatile struct dbdma_cmd *cp; + + while (write_sq.active > 0) { + cp = &awacs_tx_cmds[i]; + stat = ld_le16(&cp->xfer_status); + if ((stat & ACTIVE) == 0) + break; /* this frame is still going */ + --write_sq.count; + --write_sq.active; + if (++i >= write_sq.max_count) + i = 0; + } + if (i != write_sq.front) + WAKE_UP(write_sq.action_queue); + write_sq.front = i; + + PMacPlay(); + + if (!write_sq.active) + WAKE_UP(write_sq.sync_queue); +} + + +static void +pmac_awacs_rx_intr(int irq, void *devid, struct pt_regs *regs) +{ + + /* For some reason on my PowerBook G3, I get one interrupt + * when the interrupt vector is installed (like something is + * pending). This happens before the dbdma is initialize by + * us, so I just check the command pointer and if it is zero, + * just blow it off. + */ + if (in_le32(&awacs_rxdma->cmdptr) == 0) + return; + + /* We also want to blow 'em off when shutting down. + */ + if (read_sq.active == 0) + return; + + /* Check multiple buffers in case we were held off from + * interrupt processing for a long time. Geeze, I really hope + * this doesn't happen. + */ + while (awacs_rx_cmds[read_sq.rear].xfer_status) { + + /* Clear status and move on to next buffer. + */ + awacs_rx_cmds[read_sq.rear].xfer_status = 0; + read_sq.rear++; + + /* Wrap the buffer ring. + */ + if (read_sq.rear >= read_sq.max_active) + read_sq.rear = 0; + + /* If we have caught up to the front buffer, bump it. + * This will cause weird (but not fatal) results if the + * read loop is currently using this buffer. The user is + * behind in this case anyway, so weird things are going + * to happen. + */ + if (read_sq.rear == read_sq.front) { + read_sq.front++; + if (read_sq.front >= read_sq.max_active) + read_sq.front = 0; + } + } + + WAKE_UP(read_sq.action_queue); +} + + +static void +pmac_awacs_intr(int irq, void *devid, struct pt_regs *regs) +{ + int ctrl = in_le32(&awacs->control); + + if (ctrl & MASK_PORTCHG) { + /* do something when headphone is plugged/unplugged? */ + } + if (ctrl & MASK_CNTLERR) { + int err = (in_le32(&awacs->codec_stat) & MASK_ERRCODE) >> 16; + if (err != 0 && awacs_revision < AWACS_BURGUNDY) + printk(KERN_ERR "AWACS: error %x\n", err); + } + /* Writing 1s to the CNTLERR and PORTCHG bits clears them... */ + out_le32(&awacs->control, ctrl); +} + +static void +awacs_write(int val) +{ + if (awacs_revision >= AWACS_BURGUNDY) + return; + while (in_le32(&awacs->codec_ctrl) & MASK_NEWECMD) + ; /* XXX should have timeout */ + out_le32(&awacs->codec_ctrl, val | (awacs_subframe << 22)); +} + +static void awacs_nosound(unsigned long xx) +{ + unsigned long flags; + + save_flags(flags); cli(); + if (beep_playing) { + st_le16(&beep_dbdma_cmd->command, DBDMA_STOP); + out_le32(&awacs_txdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16); + out_le32(&awacs->control, + (in_le32(&awacs->control) & ~0x1f00) + | (awacs_rate_index << 8)); + out_le32(&awacs->byteswap, dmasound.hard.format != AFMT_S16_BE); + beep_playing = 0; + } + restore_flags(flags); +} + +static struct timer_list beep_timer = { + NULL, NULL, 0, 0, awacs_nosound +}; + +static void awacs_mksound(unsigned int hz, unsigned int ticks) +{ + unsigned long flags; + int beep_speed = 0; + int srate; + int period, ncycles, nsamples; + int i, j, f; + short *p; + static int beep_hz_cache; + static int beep_nsamples_cache; + static int beep_volume_cache; + + for (i = 0; i < 8 && awacs_freqs[i] >= BEEP_SRATE; ++i) + if (awacs_freqs_ok[i]) + beep_speed = i; + srate = awacs_freqs[beep_speed]; + + if (hz <= srate / BEEP_BUFLEN || hz > srate / 2) { +#if 1 + /* this is a hack for broken X server code */ + hz = 750; + ticks = 12; +#else + /* cancel beep currently playing */ + awacs_nosound(0); + return; +#endif + } + save_flags(flags); cli(); + del_timer(&beep_timer); + if (ticks) { + beep_timer.expires = jiffies + ticks; + add_timer(&beep_timer); + } + if (beep_playing || write_sq.active || beep_buf == NULL) { + restore_flags(flags); + return; /* too hard, sorry :-( */ + } + beep_playing = 1; + st_le16(&beep_dbdma_cmd->command, OUTPUT_MORE + BR_ALWAYS); + restore_flags(flags); + + if (hz == beep_hz_cache && beep_volume == beep_volume_cache) { + nsamples = beep_nsamples_cache; + } else { + period = srate * 256 / hz; /* fixed point */ + ncycles = BEEP_BUFLEN * 256 / period; + nsamples = (period * ncycles) >> 8; + f = ncycles * 65536 / nsamples; + j = 0; + p = beep_buf; + for (i = 0; i < nsamples; ++i, p += 2) { + p[0] = p[1] = beep_wform[j >> 8] * beep_volume; + j = (j + f) & 0xffff; + } + beep_hz_cache = hz; + beep_volume_cache = beep_volume; + beep_nsamples_cache = nsamples; + } + + st_le16(&beep_dbdma_cmd->req_count, nsamples*4); + st_le16(&beep_dbdma_cmd->xfer_status, 0); + st_le32(&beep_dbdma_cmd->cmd_dep, virt_to_bus(beep_dbdma_cmd)); + st_le32(&beep_dbdma_cmd->phy_addr, virt_to_bus(beep_buf)); + awacs_beep_state = 1; + + save_flags(flags); cli(); + if (beep_playing) { /* i.e. haven't been terminated already */ + out_le32(&awacs_txdma->control, (RUN|WAKE|FLUSH|PAUSE) << 16); + out_le32(&awacs->control, + (in_le32(&awacs->control) & ~0x1f00) + | (beep_speed << 8)); + out_le32(&awacs->byteswap, 0); + out_le32(&awacs_txdma->cmdptr, virt_to_bus(beep_dbdma_cmd)); + out_le32(&awacs_txdma->control, RUN | (RUN << 16)); + } + restore_flags(flags); +} + +#ifdef CONFIG_PMAC_PBOOK +/* + * Save state when going to sleep, restore it afterwards. + */ +static int awacs_sleep_notify(struct pmu_sleep_notifier *self, int when) +{ + switch (when) { + case PBOOK_SLEEP_NOW: + /* XXX we should stop any dma in progress when going to sleep + and restart it when we wake. */ + PMacSilence(); + disable_irq(awacs_irq); + disable_irq(awacs_tx_irq); + if (is_pbook_G3) { + feature_clear(awacs_node, FEATURE_Sound_CLK_enable); + feature_clear(awacs_node, FEATURE_Sound_power); + } + break; + case PBOOK_WAKE: + /* There is still a problem on wake. Sound seems to work fine + if I launch mpg123 and resumes fine if mpg123 was playing, + but the console beep is dead until I do something with the + mixer. Probably yet another timing issue */ + if (!feature_test(awacs_node, FEATURE_Sound_CLK_enable) + || !feature_test(awacs_node, FEATURE_Sound_power)) { + /* these aren't present on the 3400 AFAIK -- paulus */ + feature_set(awacs_node, FEATURE_Sound_CLK_enable); + feature_set(awacs_node, FEATURE_Sound_power); + mdelay(1000); + } + out_le32(&awacs->control, MASK_IEPC + | (awacs_rate_index << 8) | 0x11 + | (awacs_revision < AWACS_BURGUNDY? MASK_IEE: 0)); + awacs_write(awacs_reg[0] | MASK_ADDR0); + awacs_write(awacs_reg[1] | MASK_ADDR1); + awacs_write(awacs_reg[2] | MASK_ADDR2); + awacs_write(awacs_reg[4] | MASK_ADDR4); + out_le32(&awacs->byteswap, dmasound.hard.format != AFMT_S16_BE); + enable_irq(awacs_irq); + enable_irq(awacs_tx_irq); + if (awacs_revision == 3) { + mdelay(100); + awacs_write(0x6000); + mdelay(2); + awacs_write(awacs_reg[1] | MASK_ADDR1); + } + /* enable CD sound input */ + if (macio_base && is_pbook_G3) { + out_8(macio_base + 0x37, 3); + } else if (is_pbook_3400) { + feature_set(awacs_node, FEATURE_IOBUS_enable); + udelay(10); + in_8(latch_base + 0x190); + } + /* Resume pending sounds. */ + PMacPlay(); + } + return PBOOK_SLEEP_OK; +} +#endif /* CONFIG_PMAC_PBOOK */ + + +/* All the burgundy functions: */ + +/* Waits for busy flag to clear */ +inline static void +awacs_burgundy_busy_wait(void) +{ + while (in_le32(&awacs->codec_ctrl) & MASK_NEWECMD) + ; +} + +inline static void +awacs_burgundy_extend_wait(void) +{ + while (!(in_le32(&awacs->codec_stat) & MASK_EXTEND)) + ; + while (in_le32(&awacs->codec_stat) & MASK_EXTEND) + ; +} + +static void +awacs_burgundy_wcw(unsigned addr, unsigned val) +{ + out_le32(&awacs->codec_ctrl, addr + 0x200c00 + (val & 0xff)); + awacs_burgundy_busy_wait(); + out_le32(&awacs->codec_ctrl, addr + 0x200d00 +((val>>8) & 0xff)); + awacs_burgundy_busy_wait(); + out_le32(&awacs->codec_ctrl, addr + 0x200e00 +((val>>16) & 0xff)); + awacs_burgundy_busy_wait(); + out_le32(&awacs->codec_ctrl, addr + 0x200f00 +((val>>24) & 0xff)); + awacs_burgundy_busy_wait(); +} + +static unsigned +awacs_burgundy_rcw(unsigned addr) +{ + unsigned val = 0; + unsigned long flags; + + /* should have timeouts here */ + save_flags(flags); cli(); + + out_le32(&awacs->codec_ctrl, addr + 0x100000); + awacs_burgundy_busy_wait(); + awacs_burgundy_extend_wait(); + val += (in_le32(&awacs->codec_stat) >> 4) & 0xff; + + out_le32(&awacs->codec_ctrl, addr + 0x100100); + awacs_burgundy_busy_wait(); + awacs_burgundy_extend_wait(); + val += ((in_le32(&awacs->codec_stat)>>4) & 0xff) <<8; + + out_le32(&awacs->codec_ctrl, addr + 0x100200); + awacs_burgundy_busy_wait(); + awacs_burgundy_extend_wait(); + val += ((in_le32(&awacs->codec_stat)>>4) & 0xff) <<16; + + out_le32(&awacs->codec_ctrl, addr + 0x100300); + awacs_burgundy_busy_wait(); + awacs_burgundy_extend_wait(); + val += ((in_le32(&awacs->codec_stat)>>4) & 0xff) <<24; + + restore_flags(flags); + + return val; +} + + +static void +awacs_burgundy_wcb(unsigned addr, unsigned val) +{ + out_le32(&awacs->codec_ctrl, addr + 0x300000 + (val & 0xff)); + awacs_burgundy_busy_wait(); +} + +static unsigned +awacs_burgundy_rcb(unsigned addr) +{ + unsigned val = 0; + unsigned long flags; + + /* should have timeouts here */ + save_flags(flags); cli(); + + out_le32(&awacs->codec_ctrl, addr + 0x100000); + awacs_burgundy_busy_wait(); + awacs_burgundy_extend_wait(); + val += (in_le32(&awacs->codec_stat) >> 4) & 0xff; + + restore_flags(flags); + + return val; +} + +static int +awacs_burgundy_check(void) +{ + /* Checks to see the chip is alive and kicking */ + int error = in_le32(&awacs->codec_ctrl) & MASK_ERRCODE; + + return error == 0xf0000; +} + +static int +awacs_burgundy_init(void) +{ + if (awacs_burgundy_check()) { + printk(KERN_WARNING "AWACS: disabled by MacOS :-(\n"); + return 1; + } + + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_OUTPUTENABLES, + DEF_BURGUNDY_OUTPUTENABLES); + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, + DEF_BURGUNDY_MORE_OUTPUTENABLES); + awacs_burgundy_wcw(MASK_ADDR_BURGUNDY_OUTPUTSELECTS, + DEF_BURGUNDY_OUTPUTSELECTS); + + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_INPSEL21, + DEF_BURGUNDY_INPSEL21); + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_INPSEL3, + DEF_BURGUNDY_INPSEL3); + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_GAINCD, + DEF_BURGUNDY_GAINCD); + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_GAINLINE, + DEF_BURGUNDY_GAINLINE); + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_GAINMIC, + DEF_BURGUNDY_GAINMIC); + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_GAINMODEM, + DEF_BURGUNDY_GAINMODEM); + + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_ATTENSPEAKER, + DEF_BURGUNDY_ATTENSPEAKER); + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_ATTENLINEOUT, + DEF_BURGUNDY_ATTENLINEOUT); + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_ATTENHP, + DEF_BURGUNDY_ATTENHP); + + awacs_burgundy_wcw(MASK_ADDR_BURGUNDY_MASTER_VOLUME, + DEF_BURGUNDY_MASTER_VOLUME); + awacs_burgundy_wcw(MASK_ADDR_BURGUNDY_VOLCD, + DEF_BURGUNDY_VOLCD); + awacs_burgundy_wcw(MASK_ADDR_BURGUNDY_VOLLINE, + DEF_BURGUNDY_VOLLINE); + awacs_burgundy_wcw(MASK_ADDR_BURGUNDY_VOLMIC, + DEF_BURGUNDY_VOLMIC); + return 0; +} + +static void +awacs_burgundy_write_volume(unsigned address, int volume) +{ + int hardvolume,lvolume,rvolume; + + lvolume = (volume & 0xff) ? (volume & 0xff) + 155 : 0; + rvolume = ((volume >>8)&0xff) ? ((volume >> 8)&0xff ) + 155 : 0; + + hardvolume = lvolume + (rvolume << 16); + + awacs_burgundy_wcw(address, hardvolume); +} + +static int +awacs_burgundy_read_volume(unsigned address) +{ + int softvolume,wvolume; + + wvolume = awacs_burgundy_rcw(address); + + softvolume = (wvolume & 0xff) - 155; + softvolume += (((wvolume >> 16) & 0xff) - 155)<<8; + + return softvolume > 0 ? softvolume : 0; +} + + + + +static int +awacs_burgundy_read_mvolume(unsigned address) +{ + int lvolume,rvolume,wvolume; + + wvolume = awacs_burgundy_rcw(address); + + wvolume &= 0xffff; + + rvolume = (wvolume & 0xff) - 155; + lvolume = ((wvolume & 0xff00)>>8) - 155; + + return lvolume + (rvolume << 8); +} + + +static void +awacs_burgundy_write_mvolume(unsigned address, int volume) +{ + int lvolume,rvolume,hardvolume; + + lvolume = (volume &0xff) ? (volume & 0xff) + 155 :0; + rvolume = ((volume >>8) & 0xff) ? (volume >> 8) + 155 :0; + + hardvolume = lvolume + (rvolume << 8); + hardvolume += (hardvolume << 16); + + awacs_burgundy_wcw(address, hardvolume); +} + +/* End burgundy functions */ + + + + + +/* Turn on sound output, needed on G3 desktop powermacs */ +static void +awacs_enable_amp(int spkr_vol) +{ + struct adb_request req; + + awacs_spkr_vol = spkr_vol; + if (sys_ctrler != SYS_CTRLER_CUDA) + return; + + /* turn on headphones */ + cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC, + 0x8a, 4, 0); + while (!req.complete) cuda_poll(); + cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC, + 0x8a, 6, 0); + while (!req.complete) cuda_poll(); + + /* turn on speaker */ + cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC, + 0x8a, 3, (100 - (spkr_vol & 0xff)) * 32 / 100); + while (!req.complete) cuda_poll(); + cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC, + 0x8a, 5, (100 - ((spkr_vol >> 8) & 0xff)) * 32 / 100); + while (!req.complete) cuda_poll(); + + cuda_request(&req, NULL, 5, CUDA_PACKET, + CUDA_GET_SET_IIC, 0x8a, 1, 0x29); + while (!req.complete) cuda_poll(); +} + + +/*** Mid level stuff *********************************************************/ + + +/* + * /dev/mixer abstraction + */ + +static int PMacMixerIoctl(u_int cmd, u_long arg) +{ + int data; + /* Different IOCTLS for burgundy*/ + if (awacs_revision < AWACS_BURGUNDY) { + switch (cmd) { + case SOUND_MIXER_READ_DEVMASK: + data = SOUND_MASK_VOLUME | SOUND_MASK_SPEAKER + | SOUND_MASK_LINE | SOUND_MASK_MIC + | SOUND_MASK_CD | SOUND_MASK_RECLEV + | SOUND_MASK_ALTPCM + | SOUND_MASK_MONITOR; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_READ_RECMASK: + data = SOUND_MASK_LINE | SOUND_MASK_MIC + | SOUND_MASK_CD; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_READ_RECSRC: + data = 0; + if (awacs_reg[0] & MASK_MUX_AUDIN) + data |= SOUND_MASK_LINE; + if (awacs_reg[0] & MASK_MUX_MIC) + data |= SOUND_MASK_MIC; + if (awacs_reg[0] & MASK_MUX_CD) + data |= SOUND_MASK_CD; + if (awacs_reg[1] & MASK_LOOPTHRU) + data |= SOUND_MASK_MONITOR; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_WRITE_RECSRC: + IOCTL_IN(arg, data); + data &= (SOUND_MASK_LINE + | SOUND_MASK_MIC | SOUND_MASK_CD + | SOUND_MASK_MONITOR); + awacs_reg[0] &= ~(MASK_MUX_CD | MASK_MUX_MIC + | MASK_MUX_AUDIN); + awacs_reg[1] &= ~MASK_LOOPTHRU; + if (data & SOUND_MASK_LINE) + awacs_reg[0] |= MASK_MUX_AUDIN; + if (data & SOUND_MASK_MIC) + awacs_reg[0] |= MASK_MUX_MIC; + if (data & SOUND_MASK_CD) + awacs_reg[0] |= MASK_MUX_CD; + if (data & SOUND_MASK_MONITOR) + awacs_reg[1] |= MASK_LOOPTHRU; + awacs_write(awacs_reg[0] | MASK_ADDR0); + awacs_write(awacs_reg[1] | MASK_ADDR1); + return IOCTL_OUT(arg, data); + case SOUND_MIXER_READ_STEREODEVS: + data = SOUND_MASK_VOLUME | SOUND_MASK_SPEAKER + | SOUND_MASK_RECLEV; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_READ_CAPS: + return IOCTL_OUT(arg, 0); + case SOUND_MIXER_READ_VOLUME: + data = (awacs_reg[1] & MASK_AMUTE)? 0: + awacs_get_volume(awacs_reg[2], 6); + return IOCTL_OUT(arg, data); + case SOUND_MIXER_WRITE_VOLUME: + IOCTL_IN(arg, data); + return IOCTL_OUT(arg, dmasound_set_volume(data)); + case SOUND_MIXER_READ_SPEAKER: + if (awacs_revision == 3 + && sys_ctrler == SYS_CTRLER_CUDA) + data = awacs_spkr_vol; + else + data = (awacs_reg[1] & MASK_CMUTE)? 0: + awacs_get_volume(awacs_reg[4], 6); + return IOCTL_OUT(arg, data); + case SOUND_MIXER_WRITE_SPEAKER: + IOCTL_IN(arg, data); + if (awacs_revision == 3 + && sys_ctrler == SYS_CTRLER_CUDA) + awacs_enable_amp(data); + else + data = awacs_volume_setter(data, 4, MASK_CMUTE, 6); + return IOCTL_OUT(arg, data); + case SOUND_MIXER_WRITE_ALTPCM: /* really bell volume */ + IOCTL_IN(arg, data); + beep_volume = data & 0xff; + /* fall through */ + case SOUND_MIXER_READ_ALTPCM: + return IOCTL_OUT(arg, beep_volume); + case SOUND_MIXER_WRITE_LINE: + IOCTL_IN(arg, data); + awacs_reg[0] &= ~MASK_MUX_AUDIN; + if ((data & 0xff) >= 50) + awacs_reg[0] |= MASK_MUX_AUDIN; + awacs_write(MASK_ADDR0 | awacs_reg[0]); + /* fall through */ + case SOUND_MIXER_READ_LINE: + data = (awacs_reg[0] & MASK_MUX_AUDIN)? 100: 0; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_WRITE_MIC: + IOCTL_IN(arg, data); + data &= 0xff; + awacs_reg[0] &= ~(MASK_MUX_MIC | MASK_GAINLINE); + if (data >= 25) { + awacs_reg[0] |= MASK_MUX_MIC; + if (data >= 75) + awacs_reg[0] |= MASK_GAINLINE; + } + awacs_write(MASK_ADDR0 | awacs_reg[0]); + /* fall through */ + case SOUND_MIXER_READ_MIC: + data = (awacs_reg[0] & MASK_MUX_MIC)? + (awacs_reg[0] & MASK_GAINLINE? 100: 50): 0; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_WRITE_CD: + IOCTL_IN(arg, data); + awacs_reg[0] &= ~MASK_MUX_CD; + if ((data & 0xff) >= 50) + awacs_reg[0] |= MASK_MUX_CD; + awacs_write(MASK_ADDR0 | awacs_reg[0]); + /* fall through */ + case SOUND_MIXER_READ_CD: + data = (awacs_reg[0] & MASK_MUX_CD)? 100: 0; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_WRITE_RECLEV: + IOCTL_IN(arg, data); + data = awacs_volume_setter(data, 0, 0, 4); + return IOCTL_OUT(arg, data); + case SOUND_MIXER_READ_RECLEV: + data = awacs_get_volume(awacs_reg[0], 4); + return IOCTL_OUT(arg, data); + } + } else { + /* We are, we are, we are... Burgundy or better */ + switch(cmd) { + case SOUND_MIXER_READ_DEVMASK: + data = SOUND_MASK_VOLUME | SOUND_MASK_CD | + SOUND_MASK_LINE | SOUND_MASK_MIC | + SOUND_MASK_SPEAKER | SOUND_MASK_ALTPCM; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_READ_RECMASK: + data = SOUND_MASK_LINE | SOUND_MASK_MIC + | SOUND_MASK_CD; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_READ_RECSRC: + data = 0; + if (awacs_reg[0] & MASK_MUX_AUDIN) + data |= SOUND_MASK_LINE; + if (awacs_reg[0] & MASK_MUX_MIC) + data |= SOUND_MASK_MIC; + if (awacs_reg[0] & MASK_MUX_CD) + data |= SOUND_MASK_CD; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_WRITE_RECSRC: + IOCTL_IN(arg, data); + data &= (SOUND_MASK_LINE + | SOUND_MASK_MIC | SOUND_MASK_CD); + awacs_reg[0] &= ~(MASK_MUX_CD | MASK_MUX_MIC + | MASK_MUX_AUDIN); + if (data & SOUND_MASK_LINE) + awacs_reg[0] |= MASK_MUX_AUDIN; + if (data & SOUND_MASK_MIC) + awacs_reg[0] |= MASK_MUX_MIC; + if (data & SOUND_MASK_CD) + awacs_reg[0] |= MASK_MUX_CD; + awacs_write(awacs_reg[0] | MASK_ADDR0); + return IOCTL_OUT(arg, data); + case SOUND_MIXER_READ_STEREODEVS: + data = SOUND_MASK_VOLUME | SOUND_MASK_SPEAKER + | SOUND_MASK_RECLEV | SOUND_MASK_CD + | SOUND_MASK_LINE; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_READ_CAPS: + return IOCTL_OUT(arg, 0); + case SOUND_MIXER_WRITE_VOLUME: + IOCTL_IN(arg, data); + awacs_burgundy_write_mvolume(MASK_ADDR_BURGUNDY_MASTER_VOLUME, data); + /* Fall through */ + case SOUND_MIXER_READ_VOLUME: + return IOCTL_OUT(arg, awacs_burgundy_read_mvolume(MASK_ADDR_BURGUNDY_MASTER_VOLUME)); + case SOUND_MIXER_WRITE_SPEAKER: + IOCTL_IN(arg, data); + + if (!(data & 0xff)) { + /* Mute the left speaker */ + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, + awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES) & ~0x2); + } else { + /* Unmute the left speaker */ + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, + awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES) | 0x2); + } + if (!(data & 0xff00)) { + /* Mute the right speaker */ + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, + awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES) & ~0x4); + } else { + /* Unmute the right speaker */ + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, + awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES) | 0x4); + } + + data = (((data&0xff)*16)/100 > 0xf ? 0xf : + (((data&0xff)*16)/100)) + + ((((data>>8)*16)/100 > 0xf ? 0xf : + ((((data>>8)*16)/100)))<<4); + + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_ATTENSPEAKER, ~data); + /* Fall through */ + case SOUND_MIXER_READ_SPEAKER: + data = awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_ATTENSPEAKER); + data = (((data & 0xf)*100)/16) + ((((data>>4)*100)/16)<<8); + return IOCTL_OUT(arg, ~data); + case SOUND_MIXER_WRITE_ALTPCM: /* really bell volume */ + IOCTL_IN(arg, data); + beep_volume = data & 0xff; + /* fall through */ + case SOUND_MIXER_READ_ALTPCM: + return IOCTL_OUT(arg, beep_volume); + case SOUND_MIXER_WRITE_LINE: + IOCTL_IN(arg, data); + awacs_burgundy_write_volume(MASK_ADDR_BURGUNDY_VOLLINE, data); + + /* fall through */ + case SOUND_MIXER_READ_LINE: + data = awacs_burgundy_read_volume(MASK_ADDR_BURGUNDY_VOLLINE); + return IOCTL_OUT(arg, data); + case SOUND_MIXER_WRITE_MIC: + IOCTL_IN(arg, data); + /* Mic is mono device */ + data = (data << 8) + (data << 24); + awacs_burgundy_write_volume(MASK_ADDR_BURGUNDY_VOLMIC, data); + /* fall through */ + case SOUND_MIXER_READ_MIC: + data = awacs_burgundy_read_volume(MASK_ADDR_BURGUNDY_VOLMIC); + data <<= 24; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_WRITE_CD: + IOCTL_IN(arg, data); + awacs_burgundy_write_volume(MASK_ADDR_BURGUNDY_VOLCD, data); + /* fall through */ + case SOUND_MIXER_READ_CD: + data = awacs_burgundy_read_volume(MASK_ADDR_BURGUNDY_VOLCD); + return IOCTL_OUT(arg, data); + case SOUND_MIXER_WRITE_RECLEV: + IOCTL_IN(arg, data); + data = awacs_volume_setter(data, 0, 0, 4); + return IOCTL_OUT(arg, data); + case SOUND_MIXER_READ_RECLEV: + data = awacs_get_volume(awacs_reg[0], 4); + return IOCTL_OUT(arg, data); + case SOUND_MIXER_OUTMASK: + break; + case SOUND_MIXER_OUTSRC: + break; + } + } + return -EINVAL; +} + + +static void PMacWriteSqSetup(void) +{ + int i; + volatile struct dbdma_cmd *cp; + + cp = awacs_tx_cmds; + memset((void *)cp, 0, (write_sq.numBufs+1) * sizeof(struct dbdma_cmd)); + for (i = 0; i < write_sq.numBufs; ++i, ++cp) { + st_le32(&cp->phy_addr, virt_to_bus(write_sq.buffers[i])); + } + st_le16(&cp->command, DBDMA_NOP + BR_ALWAYS); + st_le32(&cp->cmd_dep, virt_to_bus(awacs_tx_cmds)); + out_le32(&awacs_txdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16); + out_le32(&awacs_txdma->cmdptr, virt_to_bus(awacs_tx_cmds)); +} + +static void PMacReadSqSetup(void) +{ + int i; + volatile struct dbdma_cmd *cp; + + cp = awacs_rx_cmds; + memset((void *)cp, 0, (read_sq.numBufs+1) * sizeof(struct dbdma_cmd)); + + /* Set dma buffers up in a loop */ + for (i = 0; i < read_sq.numBufs; i++,cp++) { + st_le32(&cp->phy_addr, virt_to_bus(read_sq.buffers[i])); + st_le16(&cp->command, INPUT_MORE + INTR_ALWAYS); + st_le16(&cp->req_count, read_sq.block_size); + st_le16(&cp->xfer_status, 0); + } + + /* The next two lines make the thing loop around. + */ + st_le16(&cp->command, DBDMA_NOP + BR_ALWAYS); + st_le32(&cp->cmd_dep, virt_to_bus(awacs_rx_cmds)); + + /* Don't start until the first read is done. + * This will also abort any operations in progress if the DMA + * happens to be running (and it shouldn't). + */ + out_le32(&awacs_rxdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16); + out_le32(&awacs_rxdma->cmdptr, virt_to_bus(awacs_rx_cmds)); + +} + +static void PMacAbortRead(void) +{ + int i; + volatile struct dbdma_cmd *cp; + + cp = awacs_rx_cmds; + for (i = 0; i < read_sq.numBufs; i++,cp++) + st_le16(&cp->command, DBDMA_STOP); + /* + * We should probably wait for the thing to stop before we + * release the memory + */ +} + + +/*** Machine definitions *****************************************************/ + + +static MACHINE machPMac = { + name: awacs_name, + name2: "AWACS", + open: PMacOpen, + release: PMacRelease, + dma_alloc: PMacAlloc, + dma_free: PMacFree, + irqinit: PMacIrqInit, +#ifdef MODULE + irqcleanup: PMacIrqCleanup, +#endif /* MODULE */ + init: PMacInit, + silence: PMacSilence, + setFormat: PMacSetFormat, + setVolume: PMacSetVolume, + play: PMacPlay, + record: PMacRecord, + mixer_ioctl: PMacMixerIoctl, + write_sq_setup: PMacWriteSqSetup, + read_sq_setup: PMacReadSqSetup, + abort_read: PMacAbortRead, + min_dsp_speed: 8000 +}; + + +/*** Config & Setup **********************************************************/ + + +int __init dmasound_awacs_init(void) +{ + struct device_node *np; + + if (_machine != _MACH_Pmac) + return -ENODEV; + + awacs_subframe = 0; + awacs_revision = 0; + np = find_devices("awacs"); + if (np == 0) { + /* + * powermac G3 models have a node called "davbus" + * with a child called "sound". + */ + struct device_node *sound; + np = find_devices("davbus"); + sound = find_devices("sound"); + if (sound != 0 && sound->parent == np) { + unsigned int *prop, l, i; + prop = (unsigned int *) + get_property(sound, "sub-frame", 0); + if (prop != 0 && *prop >= 0 && *prop < 16) + awacs_subframe = *prop; + if (device_is_compatible(sound, "burgundy")) + awacs_revision = AWACS_BURGUNDY; + + /* look for a property saying what sample rates + are available */ + for (i = 0; i < 8; ++i) + awacs_freqs_ok[i] = 0; + prop = (unsigned int *) get_property + (sound, "sample-rates", &l); + if (prop == 0) + prop = (unsigned int *) get_property + (sound, "output-frame-rates", &l); + if (prop != 0) { + for (l /= sizeof(int); l > 0; --l) { + /* sometimes the rate is in the + high-order 16 bits (?) */ + unsigned int r = *prop++; + if (r >= 0x10000) + r >>= 16; + for (i = 0; i < 8; ++i) { + if (r == awacs_freqs[i]) { + awacs_freqs_ok[i] = 1; + break; + } + } + } + } else { + /* assume just 44.1k is OK */ + awacs_freqs_ok[0] = 1; + } + } + } + if (np != NULL && np->n_addrs >= 3 && np->n_intrs >= 3) { + int vol; + dmasound.mach = machPMac; + + awacs = (volatile struct awacs_regs *) + ioremap(np->addrs[0].address, 0x80); + awacs_txdma = (volatile struct dbdma_regs *) + ioremap(np->addrs[1].address, 0x100); + awacs_rxdma = (volatile struct dbdma_regs *) + ioremap(np->addrs[2].address, 0x100); + + awacs_irq = np->intrs[0].line; + awacs_tx_irq = np->intrs[1].line; + awacs_rx_irq = np->intrs[2].line; + + awacs_tx_cmd_space = kmalloc((write_sq.numBufs + 4) * sizeof(struct dbdma_cmd), + GFP_KERNEL); + if (awacs_tx_cmd_space == NULL) { + printk(KERN_ERR "DMA sound driver: Not enough buffer memory, driver disabled!\n"); + return -ENOMEM; + } + awacs_node = np; +#ifdef CONFIG_PMAC_PBOOK + if (machine_is_compatible("PowerBook1,1") + || machine_is_compatible("AAPL,PowerBook1998")) { + feature_set(np, FEATURE_Sound_CLK_enable); + feature_set(np, FEATURE_Sound_power); + /* Shorter delay will not work */ + mdelay(1000); + } +#endif + awacs_tx_cmds = (volatile struct dbdma_cmd *) + DBDMA_ALIGN(awacs_tx_cmd_space); + + + awacs_rx_cmd_space = kmalloc((read_sq.numBufs + 4) * sizeof(struct dbdma_cmd), + GFP_KERNEL); + if (awacs_rx_cmd_space == NULL) { + printk("DMA sound driver: No memory for input"); + } + awacs_rx_cmds = (volatile struct dbdma_cmd *) + DBDMA_ALIGN(awacs_rx_cmd_space); + + + + awacs_reg[0] = MASK_MUX_CD; + awacs_reg[1] = MASK_LOOPTHRU | MASK_PAROUT; + /* get default volume from nvram */ + vol = (~nvram_read_byte(0x1308) & 7) << 1; + awacs_reg[2] = vol + (vol << 6); + awacs_reg[4] = vol + (vol << 6); + out_le32(&awacs->control, 0x11); + awacs_write(awacs_reg[0] + MASK_ADDR0); + awacs_write(awacs_reg[1] + MASK_ADDR1); + awacs_write(awacs_reg[2] + MASK_ADDR2); + awacs_write(awacs_reg[4] + MASK_ADDR4); + + /* Initialize recent versions of the awacs */ + if (awacs_revision == 0) { + awacs_revision = + (in_le32(&awacs->codec_stat) >> 12) & 0xf; + if (awacs_revision == 3) { + mdelay(100); + awacs_write(0x6000); + mdelay(2); + awacs_write(awacs_reg[1] + MASK_ADDR1); + awacs_enable_amp(100 * 0x101); + } + } + if (awacs_revision >= AWACS_BURGUNDY) + awacs_burgundy_init(); + + /* Initialize beep stuff */ + beep_dbdma_cmd = awacs_tx_cmds + (write_sq.numBufs + 1); + orig_mksound = kd_mksound; + kd_mksound = awacs_mksound; + beep_buf = (short *) kmalloc(BEEP_BUFLEN * 4, GFP_KERNEL); + if (beep_buf == NULL) + printk(KERN_WARNING "dmasound: no memory for " + "beep buffer\n"); +#ifdef CONFIG_PMAC_PBOOK + pmu_register_sleep_notifier(&awacs_sleep_notifier); +#endif /* CONFIG_PMAC_PBOOK */ + + /* Powerbooks have odd ways of enabling inputs such as + an expansion-bay CD or sound from an internal modem + or a PC-card modem. */ + if (machine_is_compatible("AAPL,3400/2400") + || machine_is_compatible("AAPL,3500")) { + is_pbook_3400 = 1; + /* + * Enable CD and PC-card sound inputs. + * This is done by reading from address + * f301a000, + 0x10 to enable the expansion-bay + * CD sound input, + 0x80 to enable the PC-card + * sound input. The 0x100 enables the SCSI bus + * terminator power. + */ + latch_base = (unsigned char *) ioremap + (0xf301a000, 0x1000); + in_8(latch_base + 0x190); + } else if (machine_is_compatible("PowerBook1,1") + || machine_is_compatible("AAPL,PowerBook1998")) { + struct device_node* mio; + macio_base = 0; + is_pbook_G3 = 1; + for (mio = np->parent; mio; mio = mio->parent) { + if (strcmp(mio->name, "mac-io") == 0 + && mio->n_addrs > 0) { + macio_base = (unsigned char *) ioremap + (mio->addrs[0].address, 0x40); + break; + } + } + /* enable CD sound input */ + if (macio_base) + out_8(macio_base + 0x37, 3); + } + sprintf(awacs_name, "PowerMac (AWACS rev %d) ", + awacs_revision); + return dmasound_init(); + } + return -ENODEV; +} + +static void __exit dmasound_awacs_cleanup(void) +{ + dmasound_deinit(); +} + +module_init(dmasound_awacs_init); +module_exit(dmasound_awacs_cleanup); diff --git a/drivers/sound/dmasound/dmasound_core.c b/drivers/sound/dmasound/dmasound_core.c new file mode 100644 index 000000000..594ee925c --- /dev/null +++ b/drivers/sound/dmasound/dmasound_core.c @@ -0,0 +1,1313 @@ + +/* + * linux/drivers/sound/dmasound.c + * + * + * OSS/Free compatible Atari TT/Falcon and Amiga DMA sound driver for + * Linux/m68k + * Extended to support Power Macintosh for Linux/ppc by Paul Mackerras + * + * (c) 1995 by Michael Schlueter & Michael Marte + * + * Michael Schlueter (michael@duck.syd.de) did the basic structure of the VFS + * interface and the u-law to signed byte conversion. + * + * Michael Marte (marte@informatik.uni-muenchen.de) did the sound queue, + * /dev/mixer, /dev/sndstat and complemented the VFS interface. He would like + * to thank: + * - Michael Schlueter for initial ideas and documentation on the MFP and + * the DMA sound hardware. + * - Therapy? for their CD 'Troublegum' which really made me rock. + * + * /dev/sndstat is based on code by Hannu Savolainen, the author of the + * VoxWare family of drivers. + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. + * + * History: + * + * 1995/8/25 First release + * + * 1995/9/02 Roman Hodek: + * - Fixed atari_stram_alloc() call, the timer + * programming and several race conditions + * 1995/9/14 Roman Hodek: + * - After some discussion with Michael Schlueter, + * revised the interrupt disabling + * - Slightly speeded up U8->S8 translation by using + * long operations where possible + * - Added 4:3 interpolation for /dev/audio + * + * 1995/9/20 Torsten Scherer: + * - Fixed a bug in sq_write and changed /dev/audio + * converting to play at 12517Hz instead of 6258Hz. + * + * 1995/9/23 Torsten Scherer: + * - Changed sq_interrupt() and sq_play() to pre-program + * the DMA for another frame while there's still one + * running. This allows the IRQ response to be + * arbitrarily delayed and playing will still continue. + * + * 1995/10/14 Guenther Kelleter, Torsten Scherer: + * - Better support for Falcon audio (the Falcon doesn't + * raise an IRQ at the end of a frame, but at the + * beginning instead!). uses 'if (codec_dma)' in lots + * of places to simply switch between Falcon and TT + * code. + * + * 1995/11/06 Torsten Scherer: + * - Started introducing a hardware abstraction scheme + * (may perhaps also serve for Amigas?) + * - Can now play samples at almost all frequencies by + * means of a more generalized expand routine + * - Takes a good deal of care to cut data only at + * sample sizes + * - Buffer size is now a kernel runtime option + * - Implemented fsync() & several minor improvements + * Guenther Kelleter: + * - Useful hints and bug fixes + * - Cross-checked it for Falcons + * + * 1996/3/9 Geert Uytterhoeven: + * - Support added for Amiga, A-law, 16-bit little + * endian. + * - Unification to drivers/sound/dmasound.c. + * + * 1996/4/6 Martin Mitchell: + * - Updated to 1.3 kernel. + * + * 1996/6/13 Topi Kanerva: + * - Fixed things that were broken (mainly the amiga + * 14-bit routines) + * - /dev/sndstat shows now the real hardware frequency + * - The lowpass filter is disabled by default now + * + * 1996/9/25 Geert Uytterhoeven: + * - Modularization + * + * 1998/6/10 Andreas Schwab: + * - Converted to use sound_core + * + * 1999/12/28 Richard Zidlicky: + * - Added support for Q40 + * + * 2000/2/27 Geert Uytterhoeven: + * - Clean up and split the code into 4 parts: + * o dmasound_core: machine-independent code + * o dmasound_atari: Atari TT and Falcon support + * o dmasound_awacs: Apple PowerMac support + * o dmasound_paula: Amiga support + * + * 2000/3/25 Geert Uytterhoeven: + * - Integration of dmasound_q40 + * - Small clean ups + */ + + +#include <linux/module.h> +#include <linux/malloc.h> +#include <linux/sound.h> +#include <linux/init.h> +#include <linux/soundcard.h> + +#include <asm/uaccess.h> + +#include "dmasound.h" + + + /* + * Declarations + */ + +int dmasound_catchRadius = 0; +static unsigned int numWriteBufs = 4; +static unsigned int writeBufSize = 32; /* in KB! */ +#ifdef HAS_RECORD +static unsigned int numReadBufs = 4; +static unsigned int readBufSize = 32; /* in KB! */ +#endif + +MODULE_PARM(dmasound_catchRadius, "i"); +MODULE_PARM(numWriteBufs, "i"); +MODULE_PARM(writeBufSize, "i"); +MODULE_PARM(numReadBufs, "i"); +MODULE_PARM(readBufSize, "i"); + +#ifdef MODULE +static int sq_unit = -1; +static int mixer_unit = -1; +static int state_unit = -1; +static int irq_installed = 0; +#endif /* MODULE */ + + + /* + * Conversion tables + */ + +#ifdef HAS_8BIT_TABLES +/* 8 bit mu-law */ + +char dmasound_ulaw2dma8[] = { + -126, -122, -118, -114, -110, -106, -102, -98, + -94, -90, -86, -82, -78, -74, -70, -66, + -63, -61, -59, -57, -55, -53, -51, -49, + -47, -45, -43, -41, -39, -37, -35, -33, + -31, -30, -29, -28, -27, -26, -25, -24, + -23, -22, -21, -20, -19, -18, -17, -16, + -16, -15, -15, -14, -14, -13, -13, -12, + -12, -11, -11, -10, -10, -9, -9, -8, + -8, -8, -7, -7, -7, -7, -6, -6, + -6, -6, -5, -5, -5, -5, -4, -4, + -4, -4, -4, -4, -3, -3, -3, -3, + -3, -3, -3, -3, -2, -2, -2, -2, + -2, -2, -2, -2, -2, -2, -2, -2, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 0, + 125, 121, 117, 113, 109, 105, 101, 97, + 93, 89, 85, 81, 77, 73, 69, 65, + 62, 60, 58, 56, 54, 52, 50, 48, + 46, 44, 42, 40, 38, 36, 34, 32, + 30, 29, 28, 27, 26, 25, 24, 23, + 22, 21, 20, 19, 18, 17, 16, 15, + 15, 14, 14, 13, 13, 12, 12, 11, + 11, 10, 10, 9, 9, 8, 8, 7, + 7, 7, 6, 6, 6, 6, 5, 5, + 5, 5, 4, 4, 4, 4, 3, 3, + 3, 3, 3, 3, 2, 2, 2, 2, + 2, 2, 2, 2, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* 8 bit A-law */ + +char dmasound_alaw2dma8[] = { + -22, -21, -24, -23, -18, -17, -20, -19, + -30, -29, -32, -31, -26, -25, -28, -27, + -11, -11, -12, -12, -9, -9, -10, -10, + -15, -15, -16, -16, -13, -13, -14, -14, + -86, -82, -94, -90, -70, -66, -78, -74, + -118, -114, -126, -122, -102, -98, -110, -106, + -43, -41, -47, -45, -35, -33, -39, -37, + -59, -57, -63, -61, -51, -49, -55, -53, + -2, -2, -2, -2, -2, -2, -2, -2, + -2, -2, -2, -2, -2, -2, -2, -2, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -6, -6, -6, -6, -5, -5, -5, -5, + -8, -8, -8, -8, -7, -7, -7, -7, + -3, -3, -3, -3, -3, -3, -3, -3, + -4, -4, -4, -4, -4, -4, -4, -4, + 21, 20, 23, 22, 17, 16, 19, 18, + 29, 28, 31, 30, 25, 24, 27, 26, + 10, 10, 11, 11, 8, 8, 9, 9, + 14, 14, 15, 15, 12, 12, 13, 13, + 86, 82, 94, 90, 70, 66, 78, 74, + 118, 114, 126, 122, 102, 98, 110, 106, + 43, 41, 47, 45, 35, 33, 39, 37, + 59, 57, 63, 61, 51, 49, 55, 53, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 5, 5, 5, 5, 4, 4, 4, 4, + 7, 7, 7, 7, 6, 6, 6, 6, + 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3 +}; +#endif /* HAS_8BIT_TABLES */ + +#ifdef HAS_16BIT_TABLES + +/* 16 bit mu-law */ + +short dmasound_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 */ + +short dmasound_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 + + /* + * Unused for now. Where are the MSB parts anyway?? + */ + +/* 14 bit mu-law (LSB) */ + +char dmasound_ulaw2dma14l[] = { + 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) */ + +char dmasound_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 */ + + + /* + * Common stuff + */ + +static long long sound_lseek(struct file *file, long long offset, int orig) +{ + return -ESPIPE; +} + + + /* + * Mid level stuff + */ + +struct sound_settings dmasound; + +static inline void sound_silence(void) +{ + /* update hardware settings one more */ + dmasound.mach.init(); + + dmasound.mach.silence(); +} + +static inline void sound_init(void) +{ + dmasound.mach.init(); +} + +static inline int sound_set_format(int format) +{ + return dmasound.mach.setFormat(format); +} + +static int sound_set_speed(int speed) +{ + if (speed < 0) + return dmasound.soft.speed; + + dmasound.soft.speed = speed; + dmasound.mach.init(); + if (dmasound.minDev == SND_DEV_DSP) + dmasound.dsp.speed = dmasound.soft.speed; + + return dmasound.soft.speed; +} + +static int sound_set_stereo(int stereo) +{ + if (stereo < 0) + return dmasound.soft.stereo; + + stereo = !!stereo; /* should be 0 or 1 now */ + + dmasound.soft.stereo = stereo; + if (dmasound.minDev == SND_DEV_DSP) + dmasound.dsp.stereo = stereo; + dmasound.mach.init(); + + return stereo; +} + +static ssize_t sound_copy_translate(TRANS *trans, const u_char *userPtr, + size_t userCount, u_char frame[], + ssize_t *frameUsed, ssize_t frameLeft) +{ + ssize_t (*ct_func)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); + + switch (dmasound.soft.format) { + case AFMT_MU_LAW: + ct_func = trans->ct_ulaw; + break; + case AFMT_A_LAW: + ct_func = trans->ct_alaw; + break; + case AFMT_S8: + ct_func = trans->ct_s8; + break; + case AFMT_U8: + ct_func = trans->ct_u8; + break; + case AFMT_S16_BE: + ct_func = trans->ct_s16be; + break; + case AFMT_U16_BE: + ct_func = trans->ct_u16be; + break; + case AFMT_S16_LE: + ct_func = trans->ct_s16le; + break; + case AFMT_U16_LE: + ct_func = trans->ct_u16le; + break; + default: + return 0; + } + return ct_func(userPtr, userCount, frame, frameUsed, frameLeft); +} + + + /* + * /dev/mixer abstraction + */ + +static struct { + int busy; + int modify_counter; +} mixer; + +static int mixer_open(struct inode *inode, struct file *file) +{ + MOD_INC_USE_COUNT; + dmasound.mach.open(); + mixer.busy = 1; + return 0; +} + +static int mixer_release(struct inode *inode, struct file *file) +{ + mixer.busy = 0; + dmasound.mach.release(); + MOD_DEC_USE_COUNT; + return 0; +} +static int mixer_ioctl(struct inode *inode, struct file *file, u_int cmd, + u_long arg) +{ + if (_SIOC_DIR(cmd) & _SIOC_WRITE) + mixer.modify_counter++; + switch (cmd) { + case OSS_GETVERSION: + return IOCTL_OUT(arg, SOUND_VERSION); + case SOUND_MIXER_INFO: + { + mixer_info info; + strncpy(info.id, dmasound.mach.name2, sizeof(info.id)); + strncpy(info.name, dmasound.mach.name2, sizeof(info.name)); + info.name[sizeof(info.name)-1] = 0; + info.modify_counter = mixer.modify_counter; + copy_to_user_ret((int *)arg, &info, sizeof(info), -EFAULT); + return 0; + } + } + if (dmasound.mach.mixer_ioctl) + return dmasound.mach.mixer_ioctl(cmd, arg); + return -EINVAL; +} + +static struct file_operations mixer_fops = +{ + llseek: sound_lseek, + ioctl: mixer_ioctl, + open: mixer_open, + release: mixer_release, +}; + +static void __init mixer_init(void) +{ +#ifndef MODULE + int mixer_unit; +#endif + mixer_unit = register_sound_mixer(&mixer_fops, -1); + if (mixer_unit < 0) + return; + + mixer.busy = 0; + dmasound.treble = 0; + dmasound.bass = 0; + if (dmasound.mach.mixer_init) + dmasound.mach.mixer_init(); +} + + + /* + * Sound queue stuff, the heart of the driver + */ + +struct sound_queue dmasound_write_sq; +#ifdef HAS_RECORD +struct sound_queue dmasound_read_sq; +#endif + +static int sq_allocate_buffers(struct sound_queue *sq, int num, int size) +{ + int i; + + if (sq->buffers) + return 0; + sq->numBufs = num; + sq->bufSize = size; + sq->buffers = kmalloc (num * sizeof(char *), GFP_KERNEL); + if (!sq->buffers) + return -ENOMEM; + for (i = 0; i < num; i++) { + sq->buffers[i] = dmasound.mach.dma_alloc(size, GFP_KERNEL); + if (!sq->buffers[i]) { + while (i--) + dmasound.mach.dma_free(sq->buffers[i], size); + kfree(sq->buffers); + sq->buffers = 0; + return -ENOMEM; + } + } + return 0; +} + +static void sq_release_buffers(struct sound_queue *sq) +{ + int i; + + if (sq->buffers) { + if (sq != &write_sq && dmasound.mach.abort_read) + dmasound.mach.abort_read(); + for (i = 0; i < sq->numBufs; i++) + dmasound.mach.dma_free(sq->buffers[i], sq->bufSize); + kfree(sq->buffers); + sq->buffers = NULL; + } +} + +static void sq_setup(struct sound_queue *sq, int max_count, int max_active, + int block_size) +{ + void (*setup_func)(void); + + sq->max_count = max_count; + sq->max_active = max_active; + sq->block_size = block_size; + + sq->front = sq->count = sq->rear_size = 0; + sq->syncing = 0; + sq->active = 0; + + if (sq == &write_sq) { + sq->rear = -1; + setup_func = dmasound.mach.write_sq_setup; + } else { + sq->rear = 0; + setup_func = dmasound.mach.read_sq_setup; + } + if (setup_func) + setup_func(); +} + +static inline void sq_play(void) +{ + dmasound.mach.play(); +} + +static ssize_t sq_write(struct file *file, const char *src, size_t uLeft, + loff_t *ppos) +{ + ssize_t uWritten = 0; + u_char *dest; + ssize_t 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 write_sq.rear isn't affected by the interrupt.) + */ + + if (write_sq.count > 0 && + (bLeft = write_sq.block_size-write_sq.rear_size) > 0) { + dest = write_sq.buffers[write_sq.rear]; + bUsed = write_sq.rear_size; + uUsed = sound_copy_translate(dmasound.trans_write, src, uLeft, + dest, &bUsed, bLeft); + if (uUsed <= 0) + return uUsed; + src += uUsed; + uWritten += uUsed; + uLeft -= uUsed; + write_sq.rear_size = bUsed; + } + + do { + while (write_sq.count == write_sq.max_active) { + sq_play(); + if (write_sq.open_mode & O_NONBLOCK) + return uWritten > 0 ? uWritten : -EAGAIN; + SLEEP(write_sq.action_queue); + if (signal_pending(current)) + return uWritten > 0 ? uWritten : -EINTR; + } + + /* Here, we can avoid disabling the interrupt by first + * copying and translating the data, and then updating + * the write_sq variables. Until this is done, the interrupt + * won't see the new frame and we can work on it + * undisturbed. + */ + + dest = write_sq.buffers[(write_sq.rear+1) % write_sq.max_count]; + bUsed = 0; + bLeft = write_sq.block_size; + uUsed = sound_copy_translate(dmasound.trans_write, src, uLeft, + dest, &bUsed, bLeft); + if (uUsed <= 0) + break; + src += uUsed; + uWritten += uUsed; + uLeft -= uUsed; + if (bUsed) { + write_sq.rear = (write_sq.rear+1) % write_sq.max_count; + write_sq.rear_size = bUsed; + write_sq.count++; + } + } while (bUsed); /* uUsed may have been 0 */ + + sq_play(); + + return uUsed < 0? uUsed: uWritten; +} + +#ifdef HAS_RECORD + /* + * Here is how the values are used for reading. + * The value 'active' simply indicates the DMA is running. This is done + * so the driver semantics are DMA starts when the first read is posted. + * The value 'front' indicates the buffer we should next send to the user. + * The value 'rear' indicates the buffer the DMA is currently filling. + * When 'front' == 'rear' the buffer "ring" is empty (we always have an + * empty available). The 'rear_size' is used to track partial offsets + * into the current buffer. Right now, I just keep the DMA running. If + * the reader can't keep up, the interrupt tosses the oldest buffer. We + * could also shut down the DMA in this case. + */ + +static ssize_t sq_read(struct file *file, char *dst, size_t uLeft, + loff_t *ppos) +{ + + ssize_t uRead, bLeft, bUsed, uUsed; + + if (uLeft == 0) + return 0; + + if (!read_sq.active && dmasound.mach.record) + dmasound.mach.record(); /* Kick off the record process. */ + + uRead = 0; + + /* Move what the user requests, depending upon other options. + */ + while (uLeft > 0) { + + /* When front == rear, the DMA is not done yet. + */ + while (read_sq.front == read_sq.rear) { + if (read_sq.open_mode & O_NONBLOCK) { + return uRead > 0 ? uRead : -EAGAIN; + } + SLEEP(read_sq.action_queue); + if (signal_pending(current)) + return uRead > 0 ? uRead : -EINTR; + } + + /* The amount we move is either what is left in the + * current buffer or what the user wants. + */ + bLeft = read_sq.block_size - read_sq.rear_size; + bUsed = read_sq.rear_size; + uUsed = sound_copy_translate(dmasound.trans_read, dst, uLeft, + read_sq.buffers[read_sq.front], + &bUsed, bLeft); + if (uUsed <= 0) + return uUsed; + dst += uUsed; + uRead += uUsed; + uLeft -= uUsed; + read_sq.rear_size += bUsed; + if (read_sq.rear_size >= read_sq.block_size) { + read_sq.rear_size = 0; + read_sq.front++; + if (read_sq.front >= read_sq.max_active) + read_sq.front = 0; + } + } + return uRead; +} +#endif /* HAS_RECORD */ + +static inline void sq_init_waitqueue(struct sound_queue *sq) +{ + init_waitqueue_head(&sq->action_queue); + init_waitqueue_head(&sq->open_queue); + init_waitqueue_head(&sq->sync_queue); + sq->busy = 0; +} + +static inline void sq_wake_up(struct sound_queue *sq, struct file *file, + mode_t mode) +{ + if (file->f_mode & mode) { + sq->busy = 0; + WAKE_UP(sq->open_queue); + } +} + +static int sq_open2(struct sound_queue *sq, struct file *file, mode_t mode, + int numbufs, int bufsize) +{ + int rc = 0; + + if (file->f_mode & mode) { + if (sq->busy) { + rc = -EBUSY; + if (file->f_flags & O_NONBLOCK) + return rc; + rc = -EINTR; + while (sq->busy) { + SLEEP(sq->open_queue); + if (signal_pending(current)) + return rc; + } + rc = 0; + } + sq->busy = 1; /* Let's play spot-the-race-condition */ + + if (sq_allocate_buffers(sq, numbufs, bufsize)) { + sq_wake_up(sq, file, mode); + return rc; + } + + sq_setup(sq, numbufs, numbufs, bufsize); + sq->open_mode = file->f_mode; + } + return rc; +} + +#define write_sq_init_waitqueue() sq_init_waitqueue(&write_sq) +#define write_sq_wake_up(file) sq_wake_up(&write_sq, file, FMODE_WRITE) +#define write_sq_release_buffers() sq_release_buffers(&write_sq) +#define write_sq_open(file) \ + sq_open2(&write_sq, file, FMODE_WRITE, numWriteBufs, writeBufSize << 10) + +#ifdef HAS_RECORD +#define read_sq_init_waitqueue() sq_init_waitqueue(&read_sq) +#define read_sq_wake_up(file) sq_wake_up(&read_sq, file, FMODE_READ) +#define read_sq_release_buffers() sq_release_buffers(&read_sq) +#define read_sq_open(file) \ + sq_open2(&read_sq, file, FMODE_READ, numReadBufs, readBufSize << 10) +#else /* !HAS_RECORD */ +#define read_sq_init_waitqueue() do {} while (0) +#define read_sq_wake_up(file) do {} while (0) +#define read_sq_release_buffers() do {} while (0) +#define read_sq_open(file) (0) +#endif /* !HAS_RECORD */ + +static int sq_open(struct inode *inode, struct file *file) +{ + int rc; + + MOD_INC_USE_COUNT; + dmasound.mach.open(); + if ((rc = write_sq_open(file)) || (rc = read_sq_open(file))) { + dmasound.mach.release(); + MOD_DEC_USE_COUNT; + return rc; + } + + if (dmasound.mach.sq_open) + dmasound.mach.sq_open(); + dmasound.minDev = MINOR(inode->i_rdev) & 0x0f; + dmasound.soft = dmasound.dsp; + dmasound.hard = dmasound.dsp; + sound_init(); + if ((MINOR(inode->i_rdev) & 0x0f) == SND_DEV_AUDIO) { + sound_set_speed(8000); + sound_set_stereo(0); + sound_set_format(AFMT_MU_LAW); + } + +#if 0 + if (file->f_mode == FMODE_READ && dmasound.mach.record) { + /* Start dma'ing straight away */ + dmasound.mach.record(); + } +#endif + + return 0; +} + +static void sq_reset(void) +{ + sound_silence(); + write_sq.active = 0; + write_sq.count = 0; + write_sq.front = (write_sq.rear+1) % write_sq.max_count; +} + +static int sq_fsync(struct file *filp, struct dentry *dentry) +{ + int rc = 0; + + write_sq.syncing = 1; + sq_play(); /* there may be an incomplete frame waiting */ + + while (write_sq.active) { + SLEEP(write_sq.sync_queue); + if (signal_pending(current)) { + /* While waiting for audio output to drain, an + * interrupt occurred. Stop audio output immediately + * and clear the queue. */ + sq_reset(); + rc = -EINTR; + break; + } + } + + write_sq.syncing = 0; + return rc; +} + +static int sq_release(struct inode *inode, struct file *file) +{ + int rc = 0; + + if (write_sq.busy) + rc = sq_fsync(file, file->f_dentry); + dmasound.soft = dmasound.dsp; + dmasound.hard = dmasound.dsp; + sound_silence(); + + write_sq_release_buffers(); + read_sq_release_buffers(); + dmasound.mach.release(); + MOD_DEC_USE_COUNT; + + /* There is probably a DOS atack here. They change the mode flag. */ + /* XXX add check here */ + read_sq_wake_up(file); + write_sq_wake_up(file); + + /* 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; +} + +static int sq_ioctl(struct inode *inode, struct file *file, u_int cmd, + u_long arg) +{ + u_long fmt; + int data; + int size, nbufs; + audio_buf_info info; + + switch (cmd) { + case SNDCTL_DSP_RESET: + sq_reset(); + return 0; + case SNDCTL_DSP_POST: + case SNDCTL_DSP_SYNC: + return sq_fsync(file, file->f_dentry); + + /* ++TeSche: before changing any of these it's + * probably wise to wait until sound playing has + * settled down. */ + case SNDCTL_DSP_SPEED: + sq_fsync(file, file->f_dentry); + IOCTL_IN(arg, data); + return IOCTL_OUT(arg, sound_set_speed(data)); + case SNDCTL_DSP_STEREO: + sq_fsync(file, file->f_dentry); + IOCTL_IN(arg, data); + return IOCTL_OUT(arg, sound_set_stereo(data)); + case SOUND_PCM_WRITE_CHANNELS: + sq_fsync(file, file->f_dentry); + IOCTL_IN(arg, data); + return IOCTL_OUT(arg, sound_set_stereo(data-1)+1); + case SNDCTL_DSP_SETFMT: + sq_fsync(file, file->f_dentry); + IOCTL_IN(arg, data); + return IOCTL_OUT(arg, sound_set_format(data)); + case SNDCTL_DSP_GETFMTS: + fmt = 0; + if (dmasound.trans_write) { + if (dmasound.trans_write->ct_ulaw) + fmt |= AFMT_MU_LAW; + if (dmasound.trans_write->ct_alaw) + fmt |= AFMT_A_LAW; + if (dmasound.trans_write->ct_s8) + fmt |= AFMT_S8; + if (dmasound.trans_write->ct_u8) + fmt |= AFMT_U8; + if (dmasound.trans_write->ct_s16be) + fmt |= AFMT_S16_BE; + if (dmasound.trans_write->ct_u16be) + fmt |= AFMT_U16_BE; + if (dmasound.trans_write->ct_s16le) + fmt |= AFMT_S16_LE; + if (dmasound.trans_write->ct_u16le) + fmt |= AFMT_U16_LE; + } + return IOCTL_OUT(arg, fmt); + case SNDCTL_DSP_GETBLKSIZE: + size = write_sq.block_size + * dmasound.soft.size * (dmasound.soft.stereo + 1) + / (dmasound.hard.size * (dmasound.hard.stereo + 1)); + return IOCTL_OUT(arg, size); + case SNDCTL_DSP_SUBDIVIDE: + break; + case SNDCTL_DSP_SETFRAGMENT: + if (write_sq.count || write_sq.active || write_sq.syncing) + return -EINVAL; + IOCTL_IN(arg, size); + nbufs = size >> 16; + if (nbufs < 2 || nbufs > write_sq.numBufs) + nbufs = write_sq.numBufs; + size &= 0xffff; + if (size >= 8 && size <= 29) { + size = 1 << size; + size *= dmasound.hard.size * (dmasound.hard.stereo + 1); + size /= dmasound.soft.size * (dmasound.soft.stereo + 1); + if (size > write_sq.bufSize) + size = write_sq.bufSize; + } else + size = write_sq.bufSize; + sq_setup(&write_sq, write_sq.numBufs, nbufs, size); + return 0; + case SNDCTL_DSP_GETOSPACE: + info.fragments = write_sq.max_active - write_sq.count; + info.fragstotal = write_sq.max_active; + info.fragsize = write_sq.block_size; + info.bytes = info.fragments * info.fragsize; + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + + default: + return mixer_ioctl(inode, file, cmd, arg); + } + return -EINVAL; +} + +static struct file_operations sq_fops = +{ + llseek: sound_lseek, + write: sq_write, + ioctl: sq_ioctl, + open: sq_open, + release: sq_release, +#ifdef HAS_RECORD + read: sq_read, +#endif +}; + +static void __init sq_init(void) +{ +#ifndef MODULE + int sq_unit; +#endif + sq_unit = register_sound_dsp(&sq_fops, -1); + if (sq_unit < 0) + return; + + write_sq_init_waitqueue(); + read_sq_init_waitqueue(); + + /* 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! + */ + dmasound.dsp.format = AFMT_U8; + dmasound.dsp.stereo = 0; + dmasound.dsp.size = 8; + + /* set minimum rate possible without expanding */ + dmasound.dsp.speed = dmasound.mach.min_dsp_speed; + + /* before the first open to /dev/dsp this wouldn't be set */ + dmasound.soft = dmasound.dsp; + dmasound.hard = dmasound.dsp; + + sound_silence(); +} + + + /* + * /dev/sndstat + */ + +static struct { + int busy; + char buf[512]; /* state.buf should not overflow! */ + int len, ptr; +} state; + +static int state_open(struct inode *inode, struct file *file) +{ + char *buffer = state.buf; + int len = 0; + + if (state.busy) + return -EBUSY; + + MOD_INC_USE_COUNT; + dmasound.mach.open(); + state.ptr = 0; + state.busy = 1; + + len += sprintf(buffer+len, "%sDMA sound driver:\n", dmasound.mach.name); + + len += sprintf(buffer+len, "\tsound.format = 0x%x", + dmasound.soft.format); + switch (dmasound.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", + dmasound.soft.speed, dmasound.hard.speed); + len += sprintf(buffer+len, "\tsound.stereo = 0x%x (%s)\n", + dmasound.soft.stereo, + dmasound.soft.stereo ? "stereo" : "mono"); + if (dmasound.mach.state_info) + len += dmasound.mach.state_info(buffer); + len += sprintf(buffer+len, "\tsq.block_size = %d sq.max_count = %d" + " sq.max_active = %d\n", + write_sq.block_size, write_sq.max_count, + write_sq.max_active); + len += sprintf(buffer+len, "\tsq.count = %d sq.rear_size = %d\n", + write_sq.count, write_sq.rear_size); + len += sprintf(buffer+len, "\tsq.active = %d sq.syncing = %d\n", + write_sq.active, write_sq.syncing); + state.len = len; + return 0; +} + +static int state_release(struct inode *inode, struct file *file) +{ + state.busy = 0; + dmasound.mach.release(); + MOD_DEC_USE_COUNT; + return 0; +} + +static ssize_t state_read(struct file *file, char *buf, size_t count, + loff_t *ppos) +{ + int n = state.len - state.ptr; + if (n > count) + n = count; + if (n <= 0) + return 0; + if (copy_to_user(buf, &state.buf[state.ptr], n)) + return -EFAULT; + state.ptr += n; + return n; +} + +static struct file_operations state_fops = +{ + llseek: sound_lseek, + read: state_read, + open: state_open, + release: state_release, +}; + +static void __init state_init(void) +{ +#ifndef MODULE + int state_unit; +#endif + state_unit = register_sound_special(&state_fops, SND_DEV_STATUS); + if (state_unit < 0) + return; + state.busy = 0; +} + + + /* + * Config & Setup + * + * This function is called by _one_ chipset-specific driver + */ + +int __init dmasound_init(void) +{ + if (irq_installed) + return -EBUSY; + + /* Set up sound queue, /dev/audio and /dev/dsp. */ + + /* Set default settings. */ + sq_init(); + + /* Set up /dev/sndstat. */ + state_init(); + + /* Set up /dev/mixer. */ + mixer_init(); + + if (!dmasound.mach.irqinit()) { + printk(KERN_ERR "DMA sound driver: Interrupt initialization failed\n"); + return -ENODEV; + } +#ifdef MODULE + irq_installed = 1; +#endif + + printk(KERN_INFO "DMA sound driver installed, using %d buffers of %dk.\n", + numWriteBufs, writeBufSize); + + return 0; +} + +#ifdef MODULE + +void dmasound_deinit(void) +{ + if (irq_installed) { + sound_silence(); + dmasound.mach.irqcleanup(); + } + + write_sq_release_buffers(); + read_sq_release_buffers(); + + if (mixer_unit >= 0) + unregister_sound_mixer(mixer_unit); + if (state_unit >= 0) + unregister_sound_special(state_unit); + if (sq_unit >= 0) + unregister_sound_dsp(sq_unit); +} + +#else /* !MODULE */ + +static int __init dmasound_setup(char *str) +{ + int ints[6]; + + str = get_options(str, ARRAY_SIZE(ints), 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", numWriteBufs); + else + numWriteBufs = ints[1]; + if (ints[2] < MIN_BUFSIZE || ints[2] > MAX_BUFSIZE) + printk("dmasound_setup: illegal buffer size, using default = %dK\n", writeBufSize); + else + writeBufSize = ints[2]; + break; + case 0: + break; + default: + printk("dmasound_setup: illegal number of arguments\n"); + return 0; + } + return 1; +} + +__setup("dmasound=", dmasound_setup); + +#endif /* !MODULE */ + + + /* + * Visible symbols for modules + */ + +EXPORT_SYMBOL(dmasound); +EXPORT_SYMBOL(dmasound_init); +#ifdef MODULE +EXPORT_SYMBOL(dmasound_deinit); +#endif +EXPORT_SYMBOL(dmasound_write_sq); +#ifdef HAS_RECORD +EXPORT_SYMBOL(dmasound_read_sq); +#endif +EXPORT_SYMBOL(dmasound_catchRadius); +#ifdef HAS_8BIT_TABLES +EXPORT_SYMBOL(dmasound_ulaw2dma8); +EXPORT_SYMBOL(dmasound_alaw2dma8); +#endif +#ifdef HAS_16BIT_TABLES +EXPORT_SYMBOL(dmasound_ulaw2dma16); +EXPORT_SYMBOL(dmasound_alaw2dma16); +#endif +#ifdef HAS_14BIT_TABLES +EXPORT_SYMBOL(dmasound_ulaw2dma14l); +EXPORT_SYMBOL(dmasound_ulaw2dma14h); +EXPORT_SYMBOL(dmasound_alaw2dma14l); +EXPORT_SYMBOL(dmasound_alaw2dma14h); +#endif + diff --git a/drivers/sound/dmasound/dmasound_paula.c b/drivers/sound/dmasound/dmasound_paula.c new file mode 100644 index 000000000..97e9e6cf8 --- /dev/null +++ b/drivers/sound/dmasound/dmasound_paula.c @@ -0,0 +1,690 @@ + +/* + * linux/drivers/sound/dmasound_paula.c + * + * Amiga DMA Sound Driver + * + * See linux/drivers/sound/dmasound_core.c for copyright and credits + */ + + +#include <linux/module.h> +#include <linux/mm.h> +#include <linux/init.h> +#include <linux/ioport.h> +#include <linux/soundcard.h> + +#include <asm/uaccess.h> +#include <asm/setup.h> +#include <asm/amigahw.h> +#include <asm/amigaints.h> + +#include "dmasound.h" + + + /* + * The minimum period for audio depends on htotal (for OCS/ECS/AGA) + * (Imported from arch/m68k/amiga/amisound.c) + * + * FIXME: if amifb is not used, there should be a method to change htotal + */ + +extern volatile u_short amiga_audio_min_period; + + + /* + * amiga_mksound() should be able to restore the period after beeping + * (Imported from arch/m68k/amiga/amisound.c) + */ + +extern u_short amiga_audio_period; + + + /* + * Audio DMA masks + */ + +#define AMI_AUDIO_OFF (DMAF_AUD0 | DMAF_AUD1 | DMAF_AUD2 | DMAF_AUD3) +#define AMI_AUDIO_8 (DMAF_SETCLR | DMAF_MASTER | DMAF_AUD0 | DMAF_AUD1) +#define AMI_AUDIO_14 (AMI_AUDIO_8 | DMAF_AUD2 | DMAF_AUD3) + + + /* + * Helper pointers for 16(14)-bit sound + */ + +static int write_sq_block_size_half, write_sq_block_size_quarter; + + +/*** Low level stuff *********************************************************/ + + +static void AmiOpen(void); +static void AmiRelease(void); +static void *AmiAlloc(unsigned int size, int flags); +static void AmiFree(void *obj, unsigned int size); +static int AmiIrqInit(void); +#ifdef MODULE +static void AmiIrqCleanUp(void); +#endif +static void AmiSilence(void); +static void AmiInit(void); +static int AmiSetFormat(int format); +static int AmiSetVolume(int volume); +static int AmiSetTreble(int treble); +static void AmiPlayNextFrame(int index); +static void AmiPlay(void); +static void AmiInterrupt(int irq, void *dummy, struct pt_regs *fp); + + +/*** Mid level stuff *********************************************************/ + +static void AmiMixerInit(void); +static int AmiMixerIoctl(u_int cmd, u_long arg); +static void AmiWriteSqSetup(void); +static int AmiStateInfo(char *buffer); + + +/*** Translations ************************************************************/ + +/* ++TeSche: radically changed for new expanding purposes... + * + * These two routines now deal with copying/expanding/translating the samples + * from user space into our buffer at the right frequency. They take care about + * how much data there's actually to read, how much buffer space there is and + * to convert samples into the right frequency/encoding. They will only work on + * complete samples so it may happen they leave some bytes in the input stream + * if the user didn't write a multiple of the current sample size. They both + * return the number of bytes they've used from both streams so you may detect + * such a situation. Luckily all programs should be able to cope with that. + * + * I think I've optimized anything as far as one can do in plain C, all + * variables should fit in registers and the loops are really short. There's + * one loop for every possible situation. Writing a more generalized and thus + * parameterized loop would only produce slower code. Feel free to optimize + * this in assembler if you like. :) + * + * I think these routines belong here because they're not yet really hardware + * independent, especially the fact that the Falcon can play 16bit samples + * only in stereo is hardcoded in both of them! + * + * ++geert: split in even more functions (one per format) + */ + + + /* + * Native format + */ + +static ssize_t ami_ct_s8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, ssize_t frameLeft) +{ + ssize_t count, used; + + if (!dmasound.soft.stereo) { + void *p = &frame[*frameUsed]; + count = min(userCount, frameLeft) & ~1; + used = count; + if (copy_from_user(p, userPtr, count)) + return -EFAULT; + } else { + u_char *left = &frame[*frameUsed>>1]; + u_char *right = left+write_sq_block_size_half; + count = min(userCount, frameLeft)>>1 & ~1; + used = count*2; + while (count > 0) { + if (get_user(*left++, userPtr++) + || get_user(*right++, userPtr++)) + return -EFAULT; + count--; + } + } + *frameUsed += used; + return used; +} + + + /* + * Copy and convert 8 bit data + */ + +#define GENERATE_AMI_CT8(funcname, convsample) \ +static ssize_t funcname(const u_char *userPtr, size_t userCount, \ + u_char frame[], ssize_t *frameUsed, \ + ssize_t frameLeft) \ +{ \ + ssize_t count, used; \ + \ + if (!dmasound.soft.stereo) { \ + u_char *p = &frame[*frameUsed]; \ + count = min(userCount, frameLeft) & ~1; \ + used = count; \ + while (count > 0) { \ + u_char data; \ + if (get_user(data, userPtr++)) \ + return -EFAULT; \ + *p++ = convsample(data); \ + count--; \ + } \ + } else { \ + u_char *left = &frame[*frameUsed>>1]; \ + u_char *right = left+write_sq_block_size_half; \ + count = min(userCount, frameLeft)>>1 & ~1; \ + used = count*2; \ + while (count > 0) { \ + u_char data; \ + if (get_user(data, userPtr++)) \ + return -EFAULT; \ + *left++ = convsample(data); \ + if (get_user(data, userPtr++)) \ + return -EFAULT; \ + *right++ = convsample(data); \ + count--; \ + } \ + } \ + *frameUsed += used; \ + return used; \ +} + +#define AMI_CT_ULAW(x) (dmasound_ulaw2dma8[(x)]) +#define AMI_CT_ALAW(x) (dmasound_alaw2dma8[(x)]) +#define AMI_CT_U8(x) ((x) ^ 0x80) + +GENERATE_AMI_CT8(ami_ct_ulaw, AMI_CT_ULAW) +GENERATE_AMI_CT8(ami_ct_alaw, AMI_CT_ALAW) +GENERATE_AMI_CT8(ami_ct_u8, AMI_CT_U8) + + + /* + * Copy and convert 16 bit data + */ + +#define GENERATE_AMI_CT_16(funcname, convsample) \ +static ssize_t funcname(const u_char *userPtr, size_t userCount, \ + u_char frame[], ssize_t *frameUsed, \ + ssize_t frameLeft) \ +{ \ + ssize_t count, used; \ + u_short data; \ + \ + if (!dmasound.soft.stereo) { \ + u_char *high = &frame[*frameUsed>>1]; \ + u_char *low = high+write_sq_block_size_half; \ + count = min(userCount, frameLeft)>>1 & ~1; \ + used = count*2; \ + while (count > 0) { \ + if (get_user(data, ((u_short *)userPtr)++)) \ + return -EFAULT; \ + data = convsample(data); \ + *high++ = data>>8; \ + *low++ = (data>>2) & 0x3f; \ + count--; \ + } \ + } else { \ + u_char *lefth = &frame[*frameUsed>>2]; \ + u_char *leftl = lefth+write_sq_block_size_quarter; \ + u_char *righth = lefth+write_sq_block_size_half; \ + u_char *rightl = righth+write_sq_block_size_quarter; \ + count = min(userCount, frameLeft)>>2 & ~1; \ + used = count*4; \ + while (count > 0) { \ + if (get_user(data, ((u_short *)userPtr)++)) \ + return -EFAULT; \ + data = convsample(data); \ + *lefth++ = data>>8; \ + *leftl++ = (data>>2) & 0x3f; \ + if (get_user(data, ((u_short *)userPtr)++)) \ + return -EFAULT; \ + data = convsample(data); \ + *righth++ = data>>8; \ + *rightl++ = (data>>2) & 0x3f; \ + count--; \ + } \ + } \ + *frameUsed += used; \ + return used; \ +} + +#define AMI_CT_S16BE(x) (x) +#define AMI_CT_U16BE(x) ((x) ^ 0x8000) +#define AMI_CT_S16LE(x) (le2be16((x))) +#define AMI_CT_U16LE(x) (le2be16((x)) ^ 0x8000) + +GENERATE_AMI_CT_16(ami_ct_s16be, AMI_CT_S16BE) +GENERATE_AMI_CT_16(ami_ct_u16be, AMI_CT_U16BE) +GENERATE_AMI_CT_16(ami_ct_s16le, AMI_CT_S16LE) +GENERATE_AMI_CT_16(ami_ct_u16le, AMI_CT_U16LE) + + +static TRANS transAmiga = { + ct_ulaw: ami_ct_ulaw, + ct_alaw: ami_ct_alaw, + ct_s8: ami_ct_s8, + ct_u8: ami_ct_u8, + ct_s16be: ami_ct_s16be, + ct_u16be: ami_ct_u16be, + ct_s16le: ami_ct_s16le, + ct_u16le: ami_ct_u16le, +}; + +/*** Low level stuff *********************************************************/ + + +static void AmiOpen(void) +{ + MOD_INC_USE_COUNT; +} + +static void AmiRelease(void) +{ + MOD_DEC_USE_COUNT; +} + +static inline void StopDMA(void) +{ + custom.aud[0].audvol = custom.aud[1].audvol = 0; + custom.aud[2].audvol = custom.aud[3].audvol = 0; + custom.dmacon = AMI_AUDIO_OFF; +} + +static void *AmiAlloc(unsigned int size, int flags) +{ + return amiga_chip_alloc((long)size, "dmasound [Paula]"); +} + +static void AmiFree(void *obj, unsigned int size) +{ + amiga_chip_free (obj); +} + +static int __init AmiIrqInit(void) +{ + /* turn off DMA for audio channels */ + StopDMA(); + + /* Register interrupt handler. */ + if (request_irq(IRQ_AMIGA_AUD0, AmiInterrupt, 0, "DMA sound", + AmiInterrupt)) + return 0; + return 1; +} + +#ifdef MODULE +static void AmiIrqCleanUp(void) +{ + /* turn off DMA for audio channels */ + StopDMA(); + /* release the interrupt */ + free_irq(IRQ_AMIGA_AUD0, AmiInterrupt); +} +#endif /* MODULE */ + +static void AmiSilence(void) +{ + /* turn off DMA for audio channels */ + StopDMA(); +} + + +static void AmiInit(void) +{ + int period, i; + + AmiSilence(); + + if (dmasound.soft.speed) + period = amiga_colorclock/dmasound.soft.speed-1; + else + period = amiga_audio_min_period; + dmasound.hard = dmasound.soft; + dmasound.trans_write = &transAmiga; + + if (period < amiga_audio_min_period) { + /* we would need to squeeze the sound, but we won't do that */ + period = amiga_audio_min_period; + } else if (period > 65535) { + period = 65535; + } + dmasound.hard.speed = amiga_colorclock/(period+1); + + for (i = 0; i < 4; i++) + custom.aud[i].audper = period; + amiga_audio_period = period; + + 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 dmasound.soft.format; + case AFMT_MU_LAW: + case AFMT_A_LAW: + case AFMT_U8: + case AFMT_S8: + size = 8; + break; + case AFMT_S16_BE: + case AFMT_U16_BE: + case AFMT_S16_LE: + case AFMT_U16_LE: + size = 16; + break; + default: /* :-) */ + size = 8; + format = AFMT_S8; + } + + dmasound.soft.format = format; + dmasound.soft.size = size; + if (dmasound.minDev == SND_DEV_DSP) { + dmasound.dsp.format = format; + dmasound.dsp.size = dmasound.soft.size; + } + AmiInit(); + + return format; +} + + +#define VOLUME_VOXWARE_TO_AMI(v) \ + (((v) < 0) ? 0 : ((v) > 100) ? 64 : ((v) * 64)/100) +#define VOLUME_AMI_TO_VOXWARE(v) ((v)*100/64) + +static int AmiSetVolume(int volume) +{ + dmasound.volume_left = VOLUME_VOXWARE_TO_AMI(volume & 0xff); + custom.aud[0].audvol = dmasound.volume_left; + dmasound.volume_right = VOLUME_VOXWARE_TO_AMI((volume & 0xff00) >> 8); + custom.aud[1].audvol = dmasound.volume_right; + if (dmasound.hard.size == 16) { + if (dmasound.volume_left == 64 && dmasound.volume_right == 64) { + custom.aud[2].audvol = 1; + custom.aud[3].audvol = 1; + } else { + custom.aud[2].audvol = 0; + custom.aud[3].audvol = 0; + } + } + return VOLUME_AMI_TO_VOXWARE(dmasound.volume_left) | + (VOLUME_AMI_TO_VOXWARE(dmasound.volume_right) << 8); +} + +static int AmiSetTreble(int treble) +{ + dmasound.treble = treble; + if (treble < 50) + ciaa.pra &= ~0x02; + else + ciaa.pra |= 0x02; + return treble; +} + + +#define AMI_PLAY_LOADED 1 +#define AMI_PLAY_PLAYING 2 +#define AMI_PLAY_MASK 3 + + +static void AmiPlayNextFrame(int index) +{ + u_char *start, *ch0, *ch1, *ch2, *ch3; + u_long size; + + /* used by AmiPlay() if all doubts whether there really is something + * to be played are already wiped out. + */ + start = write_sq.buffers[write_sq.front]; + size = (write_sq.count == index ? write_sq.rear_size + : write_sq.block_size)>>1; + + if (dmasound.hard.stereo) { + ch0 = start; + ch1 = start+write_sq_block_size_half; + size >>= 1; + } else { + ch0 = start; + ch1 = start; + } + + custom.aud[0].audvol = dmasound.volume_left; + custom.aud[1].audvol = dmasound.volume_right; + if (dmasound.hard.size == 8) { + custom.aud[0].audlc = (u_short *)ZTWO_PADDR(ch0); + custom.aud[0].audlen = size; + custom.aud[1].audlc = (u_short *)ZTWO_PADDR(ch1); + custom.aud[1].audlen = size; + custom.dmacon = AMI_AUDIO_8; + } else { + size >>= 1; + custom.aud[0].audlc = (u_short *)ZTWO_PADDR(ch0); + custom.aud[0].audlen = size; + custom.aud[1].audlc = (u_short *)ZTWO_PADDR(ch1); + custom.aud[1].audlen = size; + if (dmasound.volume_left == 64 && dmasound.volume_right == 64) { + /* We can play pseudo 14-bit only with the maximum volume */ + ch3 = ch0+write_sq_block_size_quarter; + ch2 = ch1+write_sq_block_size_quarter; + custom.aud[2].audvol = 1; /* we are being affected by the beeps */ + custom.aud[3].audvol = 1; /* restoring volume here helps a bit */ + custom.aud[2].audlc = (u_short *)ZTWO_PADDR(ch2); + custom.aud[2].audlen = size; + custom.aud[3].audlc = (u_short *)ZTWO_PADDR(ch3); + custom.aud[3].audlen = size; + custom.dmacon = AMI_AUDIO_14; + } else { + custom.aud[2].audvol = 0; + custom.aud[3].audvol = 0; + custom.dmacon = AMI_AUDIO_8; + } + } + write_sq.front = (write_sq.front+1) % write_sq.max_count; + write_sq.active |= AMI_PLAY_LOADED; +} + + +static void AmiPlay(void) +{ + int minframes = 1; + + custom.intena = IF_AUD0; + + if (write_sq.active & AMI_PLAY_LOADED) { + /* There's already a frame loaded */ + custom.intena = IF_SETCLR | IF_AUD0; + return; + } + + if (write_sq.active & AMI_PLAY_PLAYING) + /* Increase threshold: frame 1 is already being played */ + minframes = 2; + + if (write_sq.count < minframes) { + /* Nothing to do */ + custom.intena = IF_SETCLR | IF_AUD0; + return; + } + + if (write_sq.count <= minframes && + write_sq.rear_size < write_sq.block_size && !write_sq.syncing) { + /* hmmm, the only existing frame is not + * yet filled and we're not syncing? + */ + custom.intena = IF_SETCLR | IF_AUD0; + return; + } + + AmiPlayNextFrame(minframes); + + custom.intena = IF_SETCLR | IF_AUD0; +} + + +static void AmiInterrupt(int irq, void *dummy, struct pt_regs *fp) +{ + int minframes = 1; + + custom.intena = IF_AUD0; + + if (!write_sq.active) { + /* Playing was interrupted and sq_reset() has already cleared + * the sq variables, so better don't do anything here. + */ + WAKE_UP(write_sq.sync_queue); + return; + } + + if (write_sq.active & AMI_PLAY_PLAYING) { + /* We've just finished a frame */ + write_sq.count--; + WAKE_UP(write_sq.action_queue); + } + + if (write_sq.active & AMI_PLAY_LOADED) + /* Increase threshold: frame 1 is already being played */ + minframes = 2; + + /* Shift the flags */ + write_sq.active = (write_sq.active<<1) & AMI_PLAY_MASK; + + if (!write_sq.active) + /* No frame is playing, disable audio DMA */ + StopDMA(); + + custom.intena = IF_SETCLR | IF_AUD0; + + if (write_sq.count >= minframes) + /* Try to play the next frame */ + AmiPlay(); + + if (!write_sq.active) + /* Nothing to play anymore. + Wake up a process waiting for audio output to drain. */ + WAKE_UP(write_sq.sync_queue); +} + +/*** Mid level stuff *********************************************************/ + + +/* + * /dev/mixer abstraction + */ + +static void __init AmiMixerInit(void) +{ + dmasound.volume_left = 64; + dmasound.volume_right = 64; + custom.aud[0].audvol = dmasound.volume_left; + custom.aud[3].audvol = 1; /* For pseudo 14bit */ + custom.aud[1].audvol = dmasound.volume_right; + custom.aud[2].audvol = 1; /* For pseudo 14bit */ + dmasound.treble = 50; +} + +static int AmiMixerIoctl(u_int cmd, u_long arg) +{ + int data; + switch (cmd) { + case SOUND_MIXER_READ_DEVMASK: + return IOCTL_OUT(arg, SOUND_MASK_VOLUME | SOUND_MASK_TREBLE); + case SOUND_MIXER_READ_RECMASK: + return IOCTL_OUT(arg, 0); + case SOUND_MIXER_READ_STEREODEVS: + return IOCTL_OUT(arg, SOUND_MASK_VOLUME); + case SOUND_MIXER_READ_VOLUME: + return IOCTL_OUT(arg, + VOLUME_AMI_TO_VOXWARE(dmasound.volume_left) | + VOLUME_AMI_TO_VOXWARE(dmasound.volume_right) << 8); + case SOUND_MIXER_WRITE_VOLUME: + IOCTL_IN(arg, data); + return IOCTL_OUT(arg, dmasound_set_volume(data)); + case SOUND_MIXER_READ_TREBLE: + return IOCTL_OUT(arg, dmasound.treble); + case SOUND_MIXER_WRITE_TREBLE: + IOCTL_IN(arg, data); + return IOCTL_OUT(arg, dmasound_set_treble(data)); + } + return -EINVAL; +} + + +static void AmiWriteSqSetup(void) +{ + write_sq_block_size_half = write_sq.block_size>>1; + write_sq_block_size_quarter = write_sq_block_size_half>>1; +} + + +static int AmiStateInfo(char *buffer) +{ + int len = 0; + len += sprintf(buffer+len, "\tsound.volume_left = %d [0...64]\n", + dmasound.volume_left); + len += sprintf(buffer+len, "\tsound.volume_right = %d [0...64]\n", + dmasound.volume_right); + return len; +} + + +/*** Machine definitions *****************************************************/ + + +static MACHINE machAmiga = { + name: "Amiga", + name2: "AMIGA", + open: AmiOpen, + release: AmiRelease, + dma_alloc: AmiAlloc, + dma_free: AmiFree, + irqinit: AmiIrqInit, +#ifdef MODULE + irqcleanup: AmiIrqCleanUp, +#endif /* MODULE */ + init: AmiInit, + silence: AmiSilence, + setFormat: AmiSetFormat, + setVolume: AmiSetVolume, + setTreble: AmiSetTreble, + play: AmiPlay, + mixer_init: AmiMixerInit, + mixer_ioctl: AmiMixerIoctl, + write_sq_setup: AmiWriteSqSetup, + state_info: AmiStateInfo, + min_dsp_speed: 8000 +}; + + +/*** Config & Setup **********************************************************/ + + +int __init dmasound_paula_init(void) +{ + int err; + + if (MACH_IS_AMIGA && AMIGAHW_PRESENT(AMI_AUDIO)) { + if (!request_mem_region(CUSTOM_PHYSADDR+0xa0, 0x40, + "dmasound [Paula]")) + return -EBUSY; + dmasound.mach = machAmiga; + err = dmasound_init(); + if (err) + release_mem_region(CUSTOM_PHYSADDR+0xa0, 0x40); + return err; + } else + return -ENODEV; +} + +static void __exit dmasound_paula_cleanup(void) +{ + dmasound_deinit(); + release_mem_region(CUSTOM_PHYSADDR+0xa0, 0x40); +} + +module_init(dmasound_paula_init); +module_exit(dmasound_paula_cleanup); diff --git a/drivers/sound/dmasound/dmasound_q40.c b/drivers/sound/dmasound/dmasound_q40.c new file mode 100644 index 000000000..1c4d43d94 --- /dev/null +++ b/drivers/sound/dmasound/dmasound_q40.c @@ -0,0 +1,587 @@ + +/* + * linux/drivers/sound/dmasound_q40.c + * + * Q40 DMA Sound Driver + * + * See linux/drivers/sound/dmasound_core.c for copyright and credits + */ + + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/malloc.h> +#include <linux/soundcard.h> + +#include <asm/uaccess.h> +#include <asm/q40_master.h> + +#include "dmasound.h" + + +static int expand_bal; /* Balance factor for expanding (not volume!) */ +static int expand_data; /* Data for expanding */ + + +/*** Low level stuff *********************************************************/ + + +static void Q40Open(void); +static void Q40Release(void); +static void *Q40Alloc(unsigned int size, int flags); +static void Q40Free(void *, unsigned int); +static int Q40IrqInit(void); +#ifdef MODULE +static void Q40IrqCleanUp(void); +#endif +static void Q40Silence(void); +static void Q40Init(void); +static int Q40SetFormat(int format); +static int Q40SetVolume(int volume); +static void Q40PlayNextFrame(int index); +static void Q40Play(void); +static void Q40StereoInterrupt(int irq, void *dummy, struct pt_regs *fp); +static void Q40MonoInterrupt(int irq, void *dummy, struct pt_regs *fp); +static void Q40Interrupt(void); + + +/*** Mid level stuff *********************************************************/ + + +#if 1 +/* userCount, frameUsed, frameLeft == byte counts */ +static ssize_t q40_ct_law(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + char *table = dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8: dmasound_alaw2dma8; + ssize_t count, used; + u_char *p = (u_char *) &frame[*frameUsed]; + + used = count = min(userCount, frameLeft); + if (copy_from_user(p,userPtr,count)) + return -EFAULT; + while (count > 0) { + *p = table[*p]+128; + p++; + count--; + } + *frameUsed += used ; + return used; +} +#else +static ssize_t q40_ct_law(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + char *table = dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8: dmasound_alaw2dma8; + ssize_t count, used; + u_char *p = (u_char *) &frame[*frameUsed]; + u_char val; + int stereo = sound.soft.stereo; + + + frameLeft >>= 1; + if (stereo) + userCount >>= 1; + used = count = min(userCount, frameLeft); + while (count > 0) { + u_char data; + if (get_user(data, userPtr++)) + return -EFAULT; + val = table[data]+128; + *p++ = val; + if (stereo) { + if (get_user(data, userPtr++)) + return -EFAULT; + val = table[data]+128; + } + *p++ = val; + count--; + } + *frameUsed += used * 2; + return stereo? used * 2: used; +} +#endif + +#if 1 +static ssize_t q40_ct_s8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + u_char *p = (u_char *) &frame[*frameUsed]; + + used = count = min(userCount, frameLeft); + if (copy_from_user(p,userPtr,count)) + return -EFAULT; + while (count > 0) { + *p = *p + 128; + p++; + count--; + } + *frameUsed += used; + return used; +} +#else +static ssize_t q40_ct_s8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + u_char *p = (u_char *) &frame[*frameUsed]; + u_char val; + int stereo = dmasound.soft.stereo; + + frameLeft >>= 1; + if (stereo) + userCount >>= 1; + used = count = min(userCount, frameLeft); + while (count > 0) { + u_char data; + if (get_user(data, userPtr++)) + return -EFAULT; + val = data + 128; + *p++ = val; + if (stereo) { + if (get_user(data, userPtr++)) + return -EFAULT; + val = data + 128; + } + *p++ = val; + count--; + } + *frameUsed += used * 2; + return stereo? used * 2: used; +} +#endif + +#if 1 +static ssize_t q40_ct_u8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + u_char *p = (u_char *) &frame[*frameUsed]; + + used = count = min(userCount, frameLeft); + if (copy_from_user(p,userPtr,count)) + return -EFAULT; + *frameUsed += used; + return used; +} +#else +static ssize_t q40_ct_u8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + u_char *p = (u_char *) &frame[*frameUsed]; + u_char val; + int stereo = dmasound.soft.stereo; + + + frameLeft >>= 1; + if (stereo) + userCount >>= 1; + used = count = min(userCount, frameLeft); + while (count > 0) { + u_char data; + if (get_user(data, userPtr++)) + return -EFAULT; + val = data; + *p++ = val; + if (stereo) { + if (get_user(data, userPtr++)) + return -EFAULT; + val = data; + } + *p++ = val; + count--; + } + *frameUsed += used * 2; + return stereo? used * 2: used; +} +#endif + +/* a bit too complicated to optimise right now ..*/ +static ssize_t q40_ctx_law(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + unsigned char *table = (unsigned char *) + (dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8: dmasound_alaw2dma8); + unsigned int data = expand_data; + u_char *p = (u_char *) &frame[*frameUsed]; + int bal = expand_bal; + int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; + int utotal, ftotal; + + ftotal = frameLeft; + utotal = userCount; + while (frameLeft) { + u_char c; + if (bal < 0) { + if (userCount == 0) + break; + if (get_user(c, userPtr++)) + return -EFAULT; + data = table[c]; + data += 0x80; + userCount--; + bal += hSpeed; + } + *p++ = data; + frameLeft--; + bal -= sSpeed; + } + expand_bal = bal; + expand_data = data; + *frameUsed += (ftotal - frameLeft); + utotal -= userCount; + return utotal; +} + + +static ssize_t q40_ctx_s8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + u_char *p = (u_char *) &frame[*frameUsed]; + unsigned int data = expand_data; + int bal = expand_bal; + int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; + int utotal, ftotal; + + + ftotal = frameLeft; + utotal = userCount; + while (frameLeft) { + u_char c; + if (bal < 0) { + if (userCount == 0) + break; + if (get_user(c, userPtr++)) + return -EFAULT; + data = c ; + data += 0x80; + userCount--; + bal += hSpeed; + } + *p++ = data; + frameLeft--; + bal -= sSpeed; + } + expand_bal = bal; + expand_data = data; + *frameUsed += (ftotal - frameLeft); + utotal -= userCount; + return utotal; +} + + +static ssize_t q40_ctx_u8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + u_char *p = (u_char *) &frame[*frameUsed]; + unsigned int data = expand_data; + int bal = expand_bal; + int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; + int utotal, ftotal; + + ftotal = frameLeft; + utotal = userCount; + while (frameLeft) { + u_char c; + if (bal < 0) { + if (userCount == 0) + break; + if (get_user(c, userPtr++)) + return -EFAULT; + data = c ; + userCount--; + bal += hSpeed; + } + *p++ = data; + frameLeft--; + bal -= sSpeed; + } + expand_bal = bal; + expand_data = data; + *frameUsed += (ftotal - frameLeft) ; + utotal -= userCount; + return utotal; +} + + +static TRANS transQ40Normal = { + q40_ct_law, q40_ct_law, q40_ct_s8, q40_ct_u8, NULL, NULL, NULL, NULL +}; + +static TRANS transQ40Expanding = { + q40_ctx_law, q40_ctx_law, q40_ctx_s8, q40_ctx_u8, NULL, NULL, NULL, NULL +}; + + +/*** Low level stuff *********************************************************/ + + +static void Q40Open(void) +{ + MOD_INC_USE_COUNT; +} + +static void Q40Release(void) +{ + MOD_DEC_USE_COUNT; +} + + +static void *Q40Alloc(unsigned int size, int flags) +{ + return kmalloc(size, flags); /* change to vmalloc */ +} + +static void Q40Free(void *ptr, unsigned int size) +{ + kfree(ptr); +} + +static int __init Q40IrqInit(void) +{ + /* Register interrupt handler. */ + request_irq(Q40_IRQ_SAMPLE, Q40StereoInterrupt, 0, + "DMA sound", Q40Interrupt); + + return(1); +} + + +#ifdef MODULE +static void Q40IrqCleanUp(void) +{ + master_outb(0,SAMPLE_ENABLE_REG); + free_irq(Q40_IRQ_SAMPLE, Q40Interrupt); +} +#endif /* MODULE */ + + +static void Q40Silence(void) +{ + master_outb(0,SAMPLE_ENABLE_REG); + *DAC_LEFT=*DAC_RIGHT=0; +} + +static char *q40_pp=NULL; +static unsigned int q40_sc=0; + +static void Q40PlayNextFrame(int index) +{ + u_char *start; + u_long size; + u_char speed; + + /* used by Q40Play() if all doubts whether there really is something + * to be played are already wiped out. + */ + start = write_sq.buffers[write_sq.front]; + size = (write_sq.count == index ? write_sq.rear_size : write_sq.block_size); + + q40_pp=start; + q40_sc=size; + + write_sq.front = (write_sq.front+1) % write_sq.max_count; + write_sq.active++; + + speed=(dmasound.hard.speed==10000 ? 0 : 1); + + master_outb( 0,SAMPLE_ENABLE_REG); + free_irq(Q40_IRQ_SAMPLE, Q40Interrupt); + if (dmasound.soft.stereo) + request_irq(Q40_IRQ_SAMPLE, Q40StereoInterrupt, 0, + "Q40 sound", Q40Interrupt); + else + request_irq(Q40_IRQ_SAMPLE, Q40MonoInterrupt, 0, + "Q40 sound", Q40Interrupt); + + master_outb( speed, SAMPLE_RATE_REG); + master_outb( 1,SAMPLE_CLEAR_REG); + master_outb( 1,SAMPLE_ENABLE_REG); +} + +static void Q40Play(void) +{ + unsigned long flags; + + if (write_sq.active || write_sq.count<=0 ) { + /* There's already a frame loaded */ + return; + } + + /* nothing in the queue */ + if (write_sq.count <= 1 && write_sq.rear_size < write_sq.block_size && !write_sq.syncing) { + /* hmmm, the only existing frame is not + * yet filled and we're not syncing? + */ + return; + } + save_flags(flags); cli(); + Q40PlayNextFrame(1); + restore_flags(flags); +} + +static void Q40StereoInterrupt(int irq, void *dummy, struct pt_regs *fp) +{ + if (q40_sc>1){ + *DAC_LEFT=*q40_pp++; + *DAC_RIGHT=*q40_pp++; + q40_sc -=2; + master_outb(1,SAMPLE_CLEAR_REG); + }else Q40Interrupt(); +} +static void Q40MonoInterrupt(int irq, void *dummy, struct pt_regs *fp) +{ + if (q40_sc>0){ + *DAC_LEFT=*q40_pp; + *DAC_RIGHT=*q40_pp++; + q40_sc --; + master_outb(1,SAMPLE_CLEAR_REG); + }else Q40Interrupt(); +} +static void Q40Interrupt(void) +{ + if (!write_sq.active) { + /* playing was interrupted and sq_reset() has already cleared + * the sq variables, so better don't do anything here. + */ + WAKE_UP(write_sq.sync_queue); + master_outb(0,SAMPLE_ENABLE_REG); /* better safe */ + goto exit; + } else write_sq.active=0; + write_sq.count--; + Q40Play(); + + if (q40_sc<2) + { /* there was nothing to play, disable irq */ + master_outb(0,SAMPLE_ENABLE_REG); + *DAC_LEFT=*DAC_RIGHT=0; + } + WAKE_UP(write_sq.action_queue); + + exit: + master_outb(1,SAMPLE_CLEAR_REG); +} + + +static void Q40Init(void) +{ + int i, idx; + const int freq[] = {10000, 20000}; + + /* search a frequency that fits into the allowed error range */ + + idx = -1; + for (i = 0; i < 2; i++) + if ((100 * abs(dmasound.soft.speed - freq[i]) / freq[i]) <= catchRadius) + idx = i; + + dmasound.hard = dmasound.soft; + /*sound.hard.stereo=1;*/ /* no longer true */ + dmasound.hard.size=8; + + if (idx > -1) { + dmasound.soft.speed = freq[idx]; + dmasound.trans_write = &transQ40Normal; + } else + dmasound.trans_write = &transQ40Expanding; + + Q40Silence(); + + if (dmasound.hard.speed > 20000) { + /* we would need to squeeze the sound, but we won't do that */ + dmasound.hard.speed = 20000; + dmasound.trans_write = &transQ40Normal; + } else if (dmasound.hard.speed > 10000) { + dmasound.hard.speed = 20000; + } else { + dmasound.hard.speed = 10000; + } + expand_bal = -dmasound.soft.speed; +} + + +static int Q40SetFormat(int format) +{ + /* Q40 sound supports only 8bit modes */ + + switch (format) { + case AFMT_QUERY: + return(dmasound.soft.format); + case AFMT_MU_LAW: + case AFMT_A_LAW: + case AFMT_S8: + case AFMT_U8: + break; + default: + format = AFMT_S8; + } + + dmasound.soft.format = format; + dmasound.soft.size = 8; + if (dmasound.minDev == SND_DEV_DSP) { + dmasound.dsp.format = format; + dmasound.dsp.size = 8; + } + Q40Init(); + + return(format); +} + +static int Q40SetVolume(int volume) +{ + return 0; +} + + +/*** Machine definitions *****************************************************/ + + +static MACHINE machQ40 = { + name: "Q40", + name2: "Q40", + open: Q40Open, + release: Q40Release, + dma_alloc: Q40Alloc, + dma_free: Q40Free, + irqinit: Q40IrqInit, +#ifdef MODULE + irqcleanup: Q40IrqCleanUp, +#endif /* MODULE */ + init: Q40Init, + silence: Q40Silence, + setFormat: Q40SetFormat, + setVolume: Q40SetVolume, + play: Q40Play +}; + + +/*** Config & Setup **********************************************************/ + + +int __init dmasound_q40_init(void) +{ + if (MACH_IS_Q40) { + dmasound.mach = machQ40; + return dmasound_init(); + } else + return -ENODEV; +} + +static void __exit dmasound_q40_cleanup(void) +{ + dmasound_deinit(); +} + +module_init(dmasound_q40_init); +module_exit(dmasound_q40_cleanup); diff --git a/drivers/sound/mad16.c b/drivers/sound/mad16.c index bf605dbca..5ba9236a3 100644 --- a/drivers/sound/mad16.c +++ b/drivers/sound/mad16.c @@ -791,7 +791,7 @@ static int __init probe_mad16_mpu(struct address_info *hw_config) mad_write(MC3_PORT, tmp | 0x04); hw_config->driver_use_1 = SB_MIDI_ONLY; - return sb_dsp_detect(hw_config, 0, 0); + return sb_dsp_detect(hw_config, 0, 0, NULL); #else /* assuming all later Mozart cards are identified as * either 82C928 or Mozart. If so, following code attempts diff --git a/drivers/sound/sb.h b/drivers/sound/sb.h index 6e24d6e89..f0afbe177 100644 --- a/drivers/sound/sb.h +++ b/drivers/sound/sb.h @@ -70,6 +70,13 @@ struct mixer_def { typedef struct mixer_def mixer_tab[32][2]; typedef struct mixer_def mixer_ent; +struct sb_module_options +{ + int esstype; /* ESS chip type */ + int acer; /* Do acer notebook init? */ + int sm_games; /* Logitech soundman games? */ +}; + typedef struct sb_devc { int dev; @@ -128,7 +135,10 @@ typedef struct sb_devc { int input_opened; int midi_broken; void (*midi_input_intr) (int dev, unsigned char data); - void *midi_irq_cookie; /* IRQ cookie for the midi */ + void *midi_irq_cookie; /* IRQ cookie for the midi */ + + struct sb_module_options sbmo; /* Module options */ + } sb_devc; /* @@ -147,7 +157,7 @@ int sb_dsp_get_byte(sb_devc * devc); int sb_dsp_reset (sb_devc *devc); void sb_setmixer (sb_devc *devc, unsigned int port, unsigned int value); unsigned int sb_getmixer (sb_devc *devc, unsigned int port); -int sb_dsp_detect (struct address_info *hw_config, int pci, int pciio); +int sb_dsp_detect (struct address_info *hw_config, int pci, int pciio, struct sb_module_options *sbmo); int sb_dsp_init (struct address_info *hw_config); void sb_dsp_unload(struct address_info *hw_config, int sbmpu); int sb_mixer_init(sb_devc *devc); @@ -163,7 +173,6 @@ int sb_common_mixer_set(sb_devc * devc, int dev, int left, int right); int sb_audio_open(int dev, int mode); void sb_audio_close(int dev); -extern int acer; extern sb_devc *last_sb; /* From sb_common.c */ diff --git a/drivers/sound/sb_card.c b/drivers/sound/sb_card.c index 557a971a4..513e6aa24 100644 --- a/drivers/sound/sb_card.c +++ b/drivers/sound/sb_card.c @@ -32,6 +32,10 @@ * 13-03-2000 Added some more cards, thanks to Torsten Werner. * Removed joystick and wavetable code, there are better places for them. * Code cleanup plus some fixes. + * Alessandro Zummo <azummo@ita.flashnet.it> + * + * 26-03-2000 Fixed acer, esstype and sm_games module options. + * Alessandro Zummo <azummo@ita.flashnet.it> * */ @@ -51,6 +55,22 @@ static int sbmpu = 0; extern void *smw_free; +/* + * Note DMA2 of -1 has the right meaning in the SB16 driver as well + * as here. It will cause either an error if it is needed or a fallback + * to the 8bit channel. + */ + +static int __initdata mpu_io = 0; +static int __initdata io = -1; +static int __initdata irq = -1; +static int __initdata dma = -1; +static int __initdata dma16 = -1; /* Set this for modules that need it */ +static int __initdata type = 0; /* Can set this to a specific card type */ +static int __initdata esstype = 0; /* ESS chip type */ +static int __initdata acer = 0; /* Do acer notebook init? */ +static int __initdata sm_games = 0; /* Logitech soundman games? */ + static void __init attach_sb_card(struct address_info *hw_config) { if(!sb_dsp_init(hw_config)) @@ -60,6 +80,8 @@ static void __init attach_sb_card(struct address_info *hw_config) static int __init probe_sb(struct address_info *hw_config) { + struct sb_module_options sbmo; + if (hw_config->io_base == -1 || hw_config->dma == -1 || hw_config->irq == -1) { printk(KERN_ERR "sb_card: I/O, IRQ, and DMA are mandatory\n"); @@ -127,14 +149,21 @@ iobase=0x%x irq=%d lo_dma=%d hi_dma=%d\n", } #endif - /* This is useless since is done by sb_dsp_detect - azummo */ + /* This is useless since it is done by sb_dsp_detect - azummo */ if (check_region(hw_config->io_base, 16)) { printk(KERN_ERR "sb_card: I/O port 0x%x is already in use\n\n", hw_config->io_base); return 0; } - return sb_dsp_detect(hw_config, 0, 0); + + /* Setup extra module options */ + + sbmo.acer = acer; + sbmo.sm_games = sm_games; + sbmo.esstype = esstype; + + return sb_dsp_detect(hw_config, 0, 0, &sbmo); } static void __exit unload_sb(struct address_info *hw_config) @@ -143,25 +172,11 @@ static void __exit unload_sb(struct address_info *hw_config) sb_dsp_unload(hw_config, sbmpu); } -extern int esstype; /* ESS chip type */ - static struct address_info cfg; static struct address_info cfg_mpu; struct pci_dev *sb_dev = NULL, *mpu_dev = NULL; -/* - * Note DMA2 of -1 has the right meaning in the SB16 driver as well - * as here. It will cause either an error if it is needed or a fallback - * to the 8bit channel. - */ - -static int __initdata mpu_io = 0; -static int __initdata io = -1; -static int __initdata irq = -1; -static int __initdata dma = -1; -static int __initdata dma16 = -1; /* Set this for modules that need it */ -static int __initdata type = 0; /* Can set this to a specific card type */ #if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE diff --git a/drivers/sound/sb_common.c b/drivers/sound/sb_common.c index 80c282a5d..ad9808049 100644 --- a/drivers/sound/sb_common.c +++ b/drivers/sound/sb_common.c @@ -53,14 +53,6 @@ static unsigned char jazz_dma_bits[] = { 0, 1, 0, 2, 0, 3, 0, 4 }; -/* Do acer notebook init? */ -int acer = 0; - -/* soundman games? */ -int sm_games = 0; - -extern int esstype; - void *smw_free = NULL; /* @@ -503,12 +495,16 @@ static void relocate_ess1688(sb_devc * devc) #endif } -int sb_dsp_detect(struct address_info *hw_config, int pci, int pciio) +int sb_dsp_detect(struct address_info *hw_config, int pci, int pciio, struct sb_module_options *sbmo) { sb_devc sb_info; sb_devc *devc = &sb_info; memset((char *) &sb_info, 0, sizeof(sb_info)); /* Zero everything */ + + /* Copy module options in place */ + if(sbmo) memcpy(&devc->sbmo, sbmo, sizeof(struct sb_module_options)); + sb_info.my_mididev = -1; sb_info.my_mixerdev = -1; sb_info.dev = -1; @@ -553,7 +549,7 @@ int sb_dsp_detect(struct address_info *hw_config, int pci, int pciio) printk("Yamaha PCI mode.\n"); } - if (acer) + if (devc->sbmo.acer) { cli(); inb(devc->base + 0x09); @@ -1295,10 +1291,6 @@ void unload_sbmpu(struct address_info *hw_config) unload_uart401(hw_config); } -MODULE_PARM(acer, "i"); -MODULE_PARM(sm_games, "i"); -MODULE_PARM(esstype, "i"); - EXPORT_SYMBOL(sb_dsp_init); EXPORT_SYMBOL(sb_dsp_detect); EXPORT_SYMBOL(sb_dsp_unload); diff --git a/drivers/sound/sb_ess.c b/drivers/sound/sb_ess.c index b6c9f0aee..dfb59016b 100644 --- a/drivers/sound/sb_ess.c +++ b/drivers/sound/sb_ess.c @@ -196,8 +196,6 @@ #define ESSTYPE_LIKE20 -1 /* Mimic 2.0 behaviour */ #define ESSTYPE_DETECT 0 /* Mimic 2.0 behaviour */ -int esstype = ESSTYPE_DETECT; /* module parameter in sb_card.c */ - #define SUBMDL_ES1788 0x10 /* Subtype ES1788 for specific handling */ #define SUBMDL_ES1868 0x11 /* Subtype ES1868 for specific handling */ #define SUBMDL_ES1869 0x12 /* Subtype ES1869 for specific handling */ @@ -1066,7 +1064,7 @@ int ess_init(sb_devc * devc, struct address_info *hw_config) char *chip = NULL; int submodel = -1; - switch (esstype) { + switch (devc->sbmo.esstype) { case ESSTYPE_DETECT: case ESSTYPE_LIKE20: break; @@ -1098,12 +1096,12 @@ int ess_init(sb_devc * devc, struct address_info *hw_config) submodel = SUBMDL_ES1888; break; default: - printk (KERN_ERR "Invalid esstype=%d specified\n", esstype); + printk (KERN_ERR "Invalid esstype=%d specified\n", devc->sbmo.esstype); return 0; }; if (submodel != -1) { devc->submodel = submodel; - sprintf (modelname, "ES%d", esstype); + sprintf (modelname, "ES%d", devc->sbmo.esstype); chip = modelname; }; if (chip == NULL && (ess_minor & 0x0f) < 8) { @@ -1116,7 +1114,7 @@ FKS_test (devc); * If Nothing detected yet, and we want 2.0 behaviour... * Then let's assume it's ES1688. */ - if (chip == NULL && esstype == ESSTYPE_LIKE20) { + if (chip == NULL && devc->sbmo.esstype == ESSTYPE_LIKE20) { chip = "ES1688"; }; @@ -1185,11 +1183,11 @@ FKS_test (devc); printk ( KERN_INFO "ESS chip %s %s%s\n" , chip - , ( esstype == ESSTYPE_DETECT || esstype == ESSTYPE_LIKE20 + , ( devc->sbmo.esstype == ESSTYPE_DETECT || devc->sbmo.esstype == ESSTYPE_LIKE20 ? "detected" : "specified" ) - , ( esstype == ESSTYPE_LIKE20 + , ( devc->sbmo.esstype == ESSTYPE_LIKE20 ? " (kernel 2.0 compatible)" : "" ) diff --git a/drivers/sound/sb_mixer.c b/drivers/sound/sb_mixer.c index a955db7e3..5a5487b16 100644 --- a/drivers/sound/sb_mixer.c +++ b/drivers/sound/sb_mixer.c @@ -642,11 +642,10 @@ static void sb_mixer_reset(sb_devc * devc) { char name[32]; int i; - extern int sm_games; sprintf(name, "SB_%d", devc->sbmixnum); - if (sm_games) + if (devc->sbmo.sm_games) devc->levels = load_mixer_volumes(name, smg_default_levels, 1); else devc->levels = load_mixer_volumes(name, sb_default_levels, 1); diff --git a/drivers/sound/sound_calls.h b/drivers/sound/sound_calls.h index 8d450ca66..ffa0c1caf 100644 --- a/drivers/sound/sound_calls.h +++ b/drivers/sound/sound_calls.h @@ -89,10 +89,3 @@ void sound_timer_syncinterval(unsigned int new_usecs); /* From midi_synth.c */ void do_midi_msg (int synthno, unsigned char *msg, int mlen); - -#ifdef FIXED_LOWLEVEL_SOUND -/* From aedsp16.c */ -int InitAEDSP16_SBPRO(struct address_info *hw_config); -int InitAEDSP16_MSS(struct address_info *hw_config); -int InitAEDSP16_MPU401(struct address_info *hw_config); -#endif diff --git a/drivers/sound/sound_core.c b/drivers/sound/sound_core.c index 217bdb605..c0d6dd06a 100644 --- a/drivers/sound/sound_core.c +++ b/drivers/sound/sound_core.c @@ -218,7 +218,7 @@ static void sound_remove_unit(struct sound_unit **list, int unit) static struct sound_unit *chains[16]; /** - * register_sound_special + * register_sound_special - register a special sound node * @fops: File operations for the driver * @unit: Unit number to allocate * @@ -288,7 +288,7 @@ int register_sound_special(struct file_operations *fops, int unit) EXPORT_SYMBOL(register_sound_special); /** - * register_sound_mixer + * register_sound_mixer - register a mixer device * @fops: File operations for the driver * @dev: Unit number to allocate * @@ -306,7 +306,7 @@ int register_sound_mixer(struct file_operations *fops, int dev) EXPORT_SYMBOL(register_sound_mixer); /** - * register_sound_midi + * register_sound_midi - register a midi device * @fops: File operations for the driver * @dev: Unit number to allocate * @@ -329,7 +329,7 @@ EXPORT_SYMBOL(register_sound_midi); */ /** - * register_sound_dsp + * register_sound_dsp - register a DSP device * @fops: File operations for the driver * @dev: Unit number to allocate * @@ -350,7 +350,7 @@ int register_sound_dsp(struct file_operations *fops, int dev) EXPORT_SYMBOL(register_sound_dsp); /** - * register_sound_synth + * register_sound_synth - register a synth device * @fops: File operations for the driver * @dev: Unit number to allocate * @@ -369,7 +369,7 @@ int register_sound_synth(struct file_operations *fops, int dev) EXPORT_SYMBOL(register_sound_synth); /** - * unregister_sound_special + * unregister_sound_special - unregister a special sound device * @unit: Unit number to allocate * * Release a sound device that was allocated with register_sound_special. @@ -385,7 +385,7 @@ void unregister_sound_special(int unit) EXPORT_SYMBOL(unregister_sound_special); /** - * unregister_sound_mixer + * unregister_sound_mixer - unregister a mixer * @unit: Unit number to allocate * * Release a sound device that was allocated with register_sound_mixer. @@ -400,7 +400,7 @@ void unregister_sound_mixer(int unit) EXPORT_SYMBOL(unregister_sound_mixer); /** - * unregister_sound_midi + * unregister_sound_midi - unregister a midi device * @unit: Unit number to allocate * * Release a sound device that was allocated with register_sound_midi. @@ -415,7 +415,7 @@ void unregister_sound_midi(int unit) EXPORT_SYMBOL(unregister_sound_midi); /** - * unregister_sound_dsp + * unregister_sound_dsp - unregister a DSP device * @unit: Unit number to allocate * * Release a sound device that was allocated with register_sound_dsp. @@ -433,7 +433,7 @@ void unregister_sound_dsp(int unit) EXPORT_SYMBOL(unregister_sound_dsp); /** - * unregister_sound_synth + * unregister_sound_synth - unregister a synth device * @unit: Unit number to allocate * * Release a sound device that was allocated with register_sound_synth. diff --git a/drivers/sound/soundcard.c b/drivers/sound/soundcard.c index f3b008f0c..f222592ea 100644 --- a/drivers/sound/soundcard.c +++ b/drivers/sound/soundcard.c @@ -46,14 +46,6 @@ #include "soundmodule.h" -#if defined(CONFIG_LOWLEVEL_SOUND) && !defined MODULE -extern void sound_preinit_lowlevel_drivers(void); -extern void sound_init_lowlevel_drivers(void); -#endif - -/* From obsolete legacy.h */ -#define SELECTED_SOUND_OPTIONS 0x0 - struct notifier_block *sound_locker=(struct notifier_block *)0; static int lock_depth = 0; @@ -65,7 +57,6 @@ static int lock_depth = 0; #endif static int chrdev_registered = 0; -static int is_unloading = 0; /* * Table for permanently allocated memory (used when unloading the module) @@ -80,7 +71,6 @@ int sound_dmap_flag = 1; int sound_dmap_flag = 0; #endif -static int soundcard_configured = 0; static char dma_alloc_map[MAX_DMA_CHANNELS] = {0}; #define DMA_MAP_UNAVAIL 0 @@ -101,17 +91,14 @@ int *load_mixer_volumes(char *name, int *levels, int present) { int i, n; - for (i = 0; i < num_mixer_volumes; i++) - { - if (strcmp(name, mixer_vols[i].name) == 0) - { + for (i = 0; i < num_mixer_volumes; i++) { + if (strcmp(name, mixer_vols[i].name) == 0) { if (present) mixer_vols[i].num = i; return mixer_vols[i].levels; } } - if (num_mixer_volumes >= MAX_MIXER_DEV) - { + if (num_mixer_volumes >= MAX_MIXER_DEV) { printk(KERN_ERR "Sound: Too many mixers (%s)\n", name); return levels; } @@ -229,26 +216,15 @@ static long long sound_lseek(struct file *file, long long offset, int orig) static int sound_open(struct inode *inode, struct file *file) { - int dev, retval; + int dev = MINOR(inode->i_rdev); + int retval; - if (is_unloading) { - /* printk(KERN_ERR "Sound: Driver partially removed. Can't open device\n");*/ - return -EBUSY; - } - dev = MINOR(inode->i_rdev); - if (!soundcard_configured && dev != SND_DEV_CTL && dev != SND_DEV_STATUS) { - /* printk("SoundCard Error: The sound system has not been configured\n");*/ - return -ENXIO; - } DEB(printk("sound_open(dev=%d)\n", dev)); if ((dev >= SND_NDEVS) || (dev < 0)) { /* printk(KERN_ERR "Invalid minor device %d\n", dev);*/ return -ENXIO; } switch (dev & 0x0f) { - case SND_DEV_STATUS: - break; - case SND_DEV_CTL: dev >>= 4; if (dev >= 0 && dev < MAX_MIXER_DEV && mixer_devs[dev] == NULL) { @@ -296,7 +272,6 @@ static int sound_release(struct inode *inode, struct file *file) DEB(printk("sound_release(dev=%d)\n", dev)); switch (dev & 0x0f) { - case SND_DEV_STATUS: case SND_DEV_CTL: break; @@ -470,45 +445,38 @@ static int sound_mmap(struct file *file, struct vm_area_struct *vma) dev_class = dev & 0x0f; dev >>= 4; - if (dev_class != SND_DEV_DSP && dev_class != SND_DEV_DSP16 && dev_class != SND_DEV_AUDIO) - { -/* printk("Sound: mmap() not supported for other than audio devices\n");*/ + if (dev_class != SND_DEV_DSP && dev_class != SND_DEV_DSP16 && dev_class != SND_DEV_AUDIO) { + printk(KERN_ERR "Sound: mmap() not supported for other than audio devices\n"); return -EINVAL; } if (vma->vm_flags & VM_WRITE) /* Map write and read/write to the output buf */ dmap = audio_devs[dev]->dmap_out; else if (vma->vm_flags & VM_READ) dmap = audio_devs[dev]->dmap_in; - else - { -/* printk("Sound: Undefined mmap() access\n");*/ + else { + printk(KERN_ERR "Sound: Undefined mmap() access\n"); return -EINVAL; } - if (dmap == NULL) - { -/* printk("Sound: mmap() error. dmap == NULL\n");*/ + if (dmap == NULL) { + printk(KERN_ERR "Sound: mmap() error. dmap == NULL\n"); return -EIO; } - if (dmap->raw_buf == NULL) - { -/* printk("Sound: mmap() called when raw_buf == NULL\n");*/ + if (dmap->raw_buf == NULL) { + printk(KERN_ERR "Sound: mmap() called when raw_buf == NULL\n"); return -EIO; } - if (dmap->mapping_flags) - { -/* printk("Sound: mmap() called twice for the same DMA buffer\n");*/ + if (dmap->mapping_flags) { + printk(KERN_ERR "Sound: mmap() called twice for the same DMA buffer\n"); return -EIO; } - if (vma->vm_pgoff != 0) - { -/* printk("Sound: mmap() offset must be 0.\n");*/ + if (vma->vm_pgoff != 0) { + printk(KERN_ERR "Sound: mmap() offset must be 0.\n"); return -EINVAL; } size = vma->vm_end - vma->vm_start; - if (size != dmap->bytes_in_use) - { + if (size != dmap->bytes_in_use) { printk(KERN_WARNING "Sound: mmap() size = %ld. Should be %d\n", size, dmap->bytes_in_use); } if (remap_page_range(vma->vm_start, virt_to_phys(dmap->raw_buf), @@ -546,19 +514,14 @@ struct file_operations oss_sound_fops = static int create_special_devices(void) { int seq1,seq2; - int sndstat=register_sound_special(&oss_sound_fops, 6); - if(sndstat==-1) - goto bad1; seq1=register_sound_special(&oss_sound_fops, 1); if(seq1==-1) - goto bad2; + goto bad; seq2=register_sound_special(&oss_sound_fops, 8); if(seq2!=-1) return 0; unregister_sound_special(1); -bad2: - unregister_sound_special(6); -bad1: +bad: return -1; } @@ -594,30 +557,27 @@ soundcard_make_name(char *buf, char *name, int idx) { /* Register/unregister audio entries */ static void soundcard_register_devfs (int do_register) { - char name_buf[32]; - int i, j, num; - - for (i = 0; i < sizeof (dev_list) / sizeof *dev_list; i++) - { - num = (dev_list[i].num == NULL) ? 0 : *dev_list[i].num; - for (j = 0; j < num || j == 0; j++) - { - soundcard_make_name (name_buf, dev_list[i].name, j); - if (do_register) - devfs_register (NULL, name_buf, 0, DEVFS_FL_NONE, - SOUND_MAJOR, dev_list[i].minor+ (j* 0x10), - S_IFCHR | dev_list[i].mode, 0, 0, - &oss_sound_fops, NULL); - else - { - devfs_handle_t de; - - de = devfs_find_handle (NULL, name_buf, 0, 0, 0, + char name_buf[32]; + int i, j, num; + + for (i = 0; i < sizeof (dev_list) / sizeof *dev_list; i++) { + num = (dev_list[i].num == NULL) ? 0 : *dev_list[i].num; + for (j = 0; j < num || j == 0; j++) { + soundcard_make_name (name_buf, dev_list[i].name, j); + if (do_register) + devfs_register (NULL, name_buf, 0, DEVFS_FL_NONE, + SOUND_MAJOR, dev_list[i].minor+ (j* 0x10), + S_IFCHR | dev_list[i].mode, 0, 0, + &oss_sound_fops, NULL); + else { + devfs_handle_t de; + + de = devfs_find_handle (NULL, name_buf, 0, 0, 0, DEVFS_SPECIAL_CHR, 0); - devfs_unregister (de); - } + devfs_unregister (de); + } + } } - } } #ifdef MODULE @@ -638,10 +598,6 @@ soundcard_init(void) chrdev_registered = 1; #endif - soundcard_configured = 1; - - audio_init_devices(); - soundcard_register_devfs(1); /* register after we know # of devices */ } @@ -650,14 +606,9 @@ soundcard_init(void) static void destroy_special_devices(void) { unregister_sound_special(6); - unregister_sound_special(1); unregister_sound_special(8); } -static int sound[20] = { - 0 -}; - static int dmabuf = 0; static int dmabug = 0; @@ -668,12 +619,12 @@ int init_module(void) { int err; +#ifdef CONFIG_PCI if(dmabug) isa_dma_bridge_buggy = dmabug; - +#endif err = create_special_devices(); - if (err) - { + if (err) { printk(KERN_ERR "sound: driver already loaded/included in kernel\n"); return err; } @@ -695,10 +646,8 @@ void cleanup_module(void) int i; if (MOD_IN_USE) - { return; - } - remove_proc_entry("sound", NULL); + soundcard_register_devfs (0); if (chrdev_registered) destroy_special_devices(); @@ -708,17 +657,13 @@ void cleanup_module(void) sequencer_unload(); for (i = 0; i < MAX_DMA_CHANNELS; i++) - { - if (dma_alloc_map[i] != DMA_MAP_UNAVAIL) - { + if (dma_alloc_map[i] != DMA_MAP_UNAVAIL) { printk(KERN_ERR "Sound: Hmm, DMA%d was left allocated - fixed\n", i); sound_free_dma(i); } - } + for (i = 0; i < sound_nblocks; i++) - { vfree(sound_mem_blocks[i]); - } } #endif @@ -739,16 +684,14 @@ int sound_open_dma(int chn, char *deviceID) { unsigned long flags; - if (!valid_dma(chn)) - { + if (!valid_dma(chn)) { printk(KERN_ERR "sound_open_dma: Invalid DMA channel %d\n", chn); return 1; } save_flags(flags); cli(); - if (dma_alloc_map[chn] != DMA_MAP_FREE) - { + if (dma_alloc_map[chn] != DMA_MAP_FREE) { printk("sound_open_dma: DMA channel %d busy or not allocated (%d)\n", chn, dma_alloc_map[chn]); restore_flags(flags); return 1; @@ -760,8 +703,7 @@ int sound_open_dma(int chn, char *deviceID) void sound_free_dma(int chn) { - if (dma_alloc_map[chn] == DMA_MAP_UNAVAIL) - { + if (dma_alloc_map[chn] == DMA_MAP_UNAVAIL) { /* printk( "sound_free_dma: Bad access to DMA channel %d\n", chn); */ return; } @@ -776,8 +718,7 @@ void sound_close_dma(int chn) save_flags(flags); cli(); - if (dma_alloc_map[chn] != DMA_MAP_BUSY) - { + if (dma_alloc_map[chn] != DMA_MAP_BUSY) { printk(KERN_ERR "sound_close_dma: Bad access to DMA channel %d\n", chn); restore_flags(flags); return; @@ -799,8 +740,7 @@ void request_sound_timer(int count) { extern unsigned long seq_time; - if (count < 0) - { + if (count < 0) { seq_timer.expires = (-count) + jiffies; add_timer(&seq_timer); return; @@ -879,8 +819,7 @@ void sound_notifier_chain_register(struct notifier_block *bl) * Normalise the lock count by calling the entry directly. We * have to call the module as it owns its own use counter */ - while(ct<lock_depth) - { + while(ct<lock_depth) { bl->notifier_call(bl, 1, 0); ct++; } diff --git a/drivers/sound/trix.c b/drivers/sound/trix.c index d36da3f9c..986544401 100644 --- a/drivers/sound/trix.c +++ b/drivers/sound/trix.c @@ -318,7 +318,7 @@ static int __init probe_trix_sb(struct address_info *hw_config) sb_initialized = 1; hw_config->name = "AudioTrix SB"; - return sb_dsp_detect(hw_config, 0, 0); + return sb_dsp_detect(hw_config, 0, 0, NULL); } static void __init attach_trix_sb(struct address_info *hw_config) diff --git a/drivers/sound/via82cxxx_audio.c b/drivers/sound/via82cxxx_audio.c index 5745a9037..7f88b8177 100644 --- a/drivers/sound/via82cxxx_audio.c +++ b/drivers/sound/via82cxxx_audio.c @@ -486,7 +486,7 @@ static int __init via_probe_sb(struct address_info *hw_config) return 0; } DPRINTK("EXIT after sb_dsp_detect\n"); - return sb_dsp_detect(hw_config, 0, 0); + return sb_dsp_detect(hw_config, 0, 0, NULL); } |