From 0d2d8c6db2eeffc4252c8624de11377e08890c2a Mon Sep 17 00:00:00 2001 From: Ben Gras Date: Fri, 23 Nov 2007 11:30:50 +0000 Subject: [PATCH] audio drivers. (not updated for trunk.) sb16: port of isa sb16 driver to user-space. Port by Peter Boonstoppel. es1371: By Laurens Bronwasser. --- drivers/audio/AC97.h | 74 +++ drivers/audio/README | 34 ++ drivers/audio/es1371/.depend | 138 +++++ drivers/audio/es1371/Makefile | 44 ++ drivers/audio/es1371/SRC.c | 196 +++++++ drivers/audio/es1371/SRC.h | 43 ++ drivers/audio/es1371/codec.c | 264 +++++++++ drivers/audio/es1371/codec.h | 44 ++ drivers/audio/es1371/es1371.c | 588 ++++++++++++++++++++ drivers/audio/es1371/es1371.h | 138 +++++ drivers/audio/es1371/wait.c | 27 + drivers/audio/es1371/wait.h | 10 + drivers/audio/framework/Makefile | 9 + drivers/audio/framework/audio_fw.c | 825 +++++++++++++++++++++++++++++ drivers/audio/framework/audio_fw.h | 96 ++++ drivers/audio/sb16/.depend | 68 +++ drivers/audio/sb16/Makefile | 41 ++ drivers/audio/sb16/mixer.c | 253 +++++++++ drivers/audio/sb16/mixer.h | 10 + drivers/audio/sb16/sb16.c | 445 ++++++++++++++++ drivers/audio/sb16/sb16.h | 173 ++++++ include/sys/ioc_sound.h | 3 + 22 files changed, 3523 insertions(+) create mode 100755 drivers/audio/AC97.h create mode 100644 drivers/audio/README create mode 100755 drivers/audio/es1371/.depend create mode 100755 drivers/audio/es1371/Makefile create mode 100755 drivers/audio/es1371/SRC.c create mode 100755 drivers/audio/es1371/SRC.h create mode 100755 drivers/audio/es1371/codec.c create mode 100755 drivers/audio/es1371/codec.h create mode 100755 drivers/audio/es1371/es1371.c create mode 100755 drivers/audio/es1371/es1371.h create mode 100755 drivers/audio/es1371/wait.c create mode 100755 drivers/audio/es1371/wait.h create mode 100644 drivers/audio/framework/Makefile create mode 100755 drivers/audio/framework/audio_fw.c create mode 100755 drivers/audio/framework/audio_fw.h create mode 100755 drivers/audio/sb16/.depend create mode 100755 drivers/audio/sb16/Makefile create mode 100755 drivers/audio/sb16/mixer.c create mode 100644 drivers/audio/sb16/mixer.h create mode 100755 drivers/audio/sb16/sb16.c create mode 100755 drivers/audio/sb16/sb16.h diff --git a/drivers/audio/AC97.h b/drivers/audio/AC97.h new file mode 100755 index 000000000..bcb8945de --- /dev/null +++ b/drivers/audio/AC97.h @@ -0,0 +1,74 @@ +#ifndef AC97_MIXER_H +#define AC97_MIXER_H + +/* This is a main memory cache copy of the codec's ac97 configuration + registers. See Intel's Audio Codec 97 standard (rev2.3) for info. */ + +typedef struct ac97_struct { + u16_t Reset; /* 0x00 */ + u16_t MasterVolume; /* 0x02 */ + u16_t AUXOutVolume; /* 0x04 */ + u16_t MonoVolume; /* 0x06 */ + u16_t MasterTone; /* 0x08 */ + u16_t PCBeepVolume; /* 0x0A */ + u16_t PhoneVolume; /* 0x0C */ + u16_t MicVolume; /* 0x0E */ + u16_t LineInVolume; /* 0x10 */ + u16_t CDVolume; /* 0x12 */ + u16_t VideoVolume; /* 0x14 */ + u16_t AUXInVolume; /* 0x16 */ + u16_t PCMOutVolume; /* 0x18 */ + u16_t RecordSelect; /* 0x1A */ + u16_t RecordGain; /* 0x1C */ + u16_t RecordGainMic; /* 0x1E */ + u16_t GeneralPurpose; /* 0x20 */ + u16_t Control3D; /* 0x22 */ + u16_t AudioIntAndPaging; /* 0x24 */ + u16_t PowerdownControlAndStat; /* 0x26 */ + u16_t ExtendedAudio1; /* 0x28 */ + u16_t ExtendedAudio2; /* 0x2A */ + /* ... */ + u16_t VendorID1; /* 0x7C */ + u16_t VendorID2; /* 0x7E */ +} ac97_t; + + + +/* Source and output volume control register defines */ +#define AC97_MASTER_VOLUME 0x02U /* Master out */ +#define AC97_AUX_OUT_VOLUME 0x04U /* Auxiliary out volume */ +#define AC97_MONO_VOLUME 0x06U /* Mono out volume */ +#define AC97_MASTER_TONE 0x08U /* high byte= bass, low byte= treble*/ +#define AC97_PC_BEEP_VOLUME 0x0aU /* PC speaker volume */ +#define AC97_PHONE_VOLUME 0x0cU /* Phone volume */ +#define AC97_MIC_VOLUME 0x0eU /* Mic, mono */ +#define AC97_LINE_IN_VOLUME 0x10U /* Line volume */ +#define AC97_CD_VOLUME 0x12U /* CD audio volume */ +#define AC97_VIDEO_VOLUME 0x14U /* Video (TV) volume */ +#define AC97_AUX_IN_VOLUME 0x16U /* Aux line source, left */ +#define AC97_PCM_OUT_VOLUME 0x18U /* The DACs - wav+synth */ +#define AC97_RECORD_GAIN_VOLUME 0x1cU /* Record input level */ + +/* Other CODEC control register defines */ +#define AC97_RESET 0x00U /* any write here to reset CODEC */ +#define AC97_GENERAL_PURPOSE 0x20U /* */ +#define AC97_POWERDOWN_CONTROL_STAT 0x26U /* */ +#define AC97_RECORD_SELECT 0x1aU /* record mux select */ +#define AC97_VENDOR_ID1 0x7cU /* 1st two Vendor ID bytes */ +#define AC97_VENDOR_ID2 0x7eU /* last Vendor ID byte plus rev. number */ + +/* Record Select defines */ +#define AC97_RECORD_MIC 0 +#define AC97_RECORD_CD 1 +#define AC97_RECORD_VIDEO 2 +#define AC97_RECORD_AUX 3 +#define AC97_RECORD_LINE 4 +#define AC97_RECORD_STEREO_MIX 5 +#define AC97_RECORD_MONO_MIX 6 +#define AC97_RECORD_PHONE 7 + +#define MASTER_VOL_MASK 0x1F +#define DAC_VOL_MASK 0x1F +#define AUX_IN_VOL_MASK 0x1F +#define MUTE_MASK 0x8000 +#endif /* AC97_MIXER_H */ diff --git a/drivers/audio/README b/drivers/audio/README new file mode 100644 index 000000000..b60763d3c --- /dev/null +++ b/drivers/audio/README @@ -0,0 +1,34 @@ + +***** Minix 3 Audio drivers ***** + +Directories: +framework/ Generic driver framework +sb16/ SB16 ISA driver +es1371/ ES1371 driver + + +Install driver: +* select the directory corresponding to the card you own +* run make install + + +Creating special files: +* cd /dev +* mknod audio c 13 0 +* mknod rec c 13 1 +* mknod mixer c 13 2 +* chmod 666 audio rec mixer +(for es1371 one can add a special file for the second DAC-channel, major 13, minor 3) + + +Running the driver: +* service up /usr/sbin/sb16 -dev /dev/audio +or: +* service up /usr/sbin/es1371 -dev /dev/audio + + +Minix audio tools: +recwave +playwave +mixer +(available from /usr/src/commands/ibm) diff --git a/drivers/audio/es1371/.depend b/drivers/audio/es1371/.depend new file mode 100755 index 000000000..deb8a3336 --- /dev/null +++ b/drivers/audio/es1371/.depend @@ -0,0 +1,138 @@ + +SRC.o: ../../drivers.h +SRC.o: ../../libpci/pci.h +SRC.o: /usr/include/ansi.h +SRC.o: /usr/include/errno.h +SRC.o: /usr/include/ibm/bios.h +SRC.o: /usr/include/ibm/interrupt.h +SRC.o: /usr/include/ibm/ports.h +SRC.o: /usr/include/limits.h +SRC.o: /usr/include/minix/bitmap.h +SRC.o: /usr/include/minix/callnr.h +SRC.o: /usr/include/minix/com.h +SRC.o: /usr/include/minix/config.h +SRC.o: /usr/include/minix/const.h +SRC.o: /usr/include/minix/devio.h +SRC.o: /usr/include/minix/dmap.h +SRC.o: /usr/include/minix/ioctl.h +SRC.o: /usr/include/minix/ipc.h +SRC.o: /usr/include/minix/sys_config.h +SRC.o: /usr/include/minix/syslib.h +SRC.o: /usr/include/minix/sysutil.h +SRC.o: /usr/include/minix/type.h +SRC.o: /usr/include/signal.h +SRC.o: /usr/include/stddef.h +SRC.o: /usr/include/stdlib.h +SRC.o: /usr/include/string.h +SRC.o: /usr/include/sys/dir.h +SRC.o: /usr/include/sys/ioc_sound.h +SRC.o: /usr/include/sys/types.h +SRC.o: /usr/include/unistd.h +SRC.o: SRC.c +SRC.o: SRC.h +SRC.o: es1371.h +SRC.o: wait.h + +codec.o: ../../drivers.h +codec.o: ../../libpci/pci.h +codec.o: ../AC97.h +codec.o: /usr/include/ansi.h +codec.o: /usr/include/errno.h +codec.o: /usr/include/ibm/bios.h +codec.o: /usr/include/ibm/interrupt.h +codec.o: /usr/include/ibm/ports.h +codec.o: /usr/include/limits.h +codec.o: /usr/include/minix/bitmap.h +codec.o: /usr/include/minix/callnr.h +codec.o: /usr/include/minix/com.h +codec.o: /usr/include/minix/config.h +codec.o: /usr/include/minix/const.h +codec.o: /usr/include/minix/devio.h +codec.o: /usr/include/minix/dmap.h +codec.o: /usr/include/minix/ioctl.h +codec.o: /usr/include/minix/ipc.h +codec.o: /usr/include/minix/sys_config.h +codec.o: /usr/include/minix/syslib.h +codec.o: /usr/include/minix/sysutil.h +codec.o: /usr/include/minix/type.h +codec.o: /usr/include/signal.h +codec.o: /usr/include/stddef.h +codec.o: /usr/include/stdlib.h +codec.o: /usr/include/string.h +codec.o: /usr/include/sys/dir.h +codec.o: /usr/include/sys/ioc_sound.h +codec.o: /usr/include/sys/types.h +codec.o: /usr/include/unistd.h +codec.o: SRC.h +codec.o: codec.c +codec.o: codec.h +codec.o: es1371.h +codec.o: wait.h + +es1371.o: ../../drivers.h +es1371.o: ../../libpci/pci.h +es1371.o: ../AC97.h +es1371.o: ../framework/../../drivers.h +es1371.o: ../framework/audio_fw.h +es1371.o: /usr/include/ansi.h +es1371.o: /usr/include/errno.h +es1371.o: /usr/include/ibm/bios.h +es1371.o: /usr/include/ibm/interrupt.h +es1371.o: /usr/include/ibm/ports.h +es1371.o: /usr/include/limits.h +es1371.o: /usr/include/minix/bitmap.h +es1371.o: /usr/include/minix/callnr.h +es1371.o: /usr/include/minix/com.h +es1371.o: /usr/include/minix/config.h +es1371.o: /usr/include/minix/const.h +es1371.o: /usr/include/minix/devio.h +es1371.o: /usr/include/minix/dmap.h +es1371.o: /usr/include/minix/ioctl.h +es1371.o: /usr/include/minix/ipc.h +es1371.o: /usr/include/minix/sys_config.h +es1371.o: /usr/include/minix/syslib.h +es1371.o: /usr/include/minix/sysutil.h +es1371.o: /usr/include/minix/type.h +es1371.o: /usr/include/signal.h +es1371.o: /usr/include/stddef.h +es1371.o: /usr/include/stdlib.h +es1371.o: /usr/include/string.h +es1371.o: /usr/include/sys/dir.h +es1371.o: /usr/include/sys/ioc_sound.h +es1371.o: /usr/include/sys/types.h +es1371.o: /usr/include/unistd.h +es1371.o: SRC.h +es1371.o: codec.h +es1371.o: es1371.c +es1371.o: es1371.h +es1371.o: wait.h + +wait.o: ../../drivers.h +wait.o: ../../libpci/pci.h +wait.o: /usr/include/ansi.h +wait.o: /usr/include/errno.h +wait.o: /usr/include/ibm/bios.h +wait.o: /usr/include/ibm/interrupt.h +wait.o: /usr/include/ibm/ports.h +wait.o: /usr/include/limits.h +wait.o: /usr/include/minix/bitmap.h +wait.o: /usr/include/minix/callnr.h +wait.o: /usr/include/minix/com.h +wait.o: /usr/include/minix/config.h +wait.o: /usr/include/minix/const.h +wait.o: /usr/include/minix/devio.h +wait.o: /usr/include/minix/dmap.h +wait.o: /usr/include/minix/ipc.h +wait.o: /usr/include/minix/sys_config.h +wait.o: /usr/include/minix/syslib.h +wait.o: /usr/include/minix/sysutil.h +wait.o: /usr/include/minix/type.h +wait.o: /usr/include/signal.h +wait.o: /usr/include/stddef.h +wait.o: /usr/include/stdlib.h +wait.o: /usr/include/string.h +wait.o: /usr/include/sys/dir.h +wait.o: /usr/include/sys/types.h +wait.o: /usr/include/time.h +wait.o: /usr/include/unistd.h +wait.o: wait.c diff --git a/drivers/audio/es1371/Makefile b/drivers/audio/es1371/Makefile new file mode 100755 index 000000000..3d32211b3 --- /dev/null +++ b/drivers/audio/es1371/Makefile @@ -0,0 +1,44 @@ +# Makefile for the ES1371 sounddriver (SB16) + +# directories +u = /usr +i = $u/include +s = $i/sys +m = $i/minix +b = $i/ibm +pci_dir = ../../libpci +gen_drv_dir = ../../gen_drivers/cyclic_dma + +# programs, flags, etc. +CC = exec cc +CFLAGS = -I$i +LDFLAGS = -i +LIBS = -lsys -lsysutil +PCI = $(pci_dir)/pci.o $(pci_dir)/pci_table.o + +# build local binary +all: es1371 + +es1371: es1371.o SRC.o codec.o wait.o audio_fw.o $(PCI) + $(CC) -o $@ $(LDFLAGS) es1371.o SRC.o codec.o wait.o audio_fw.o $(PCI) $(LIBS) + +audio_fw.o: ../framework/audio_fw.c ../framework/audio_fw.h + $(CC) -c ../framework/audio_fw.c + +install: /usr/sbin/es1371 +/usr/sbin/es1371: es1371 + install -o root -S 1024k -c $? $@ + +$(PCI): + cd $(pci_dir) && $(MAKE) + +# clean up local files +clean: + rm -f *.o *.bak core es1371 + +depend: + /usr/bin/mkdep "$(CC) -E $(CPPFLAGS)" *.c > .depend + +# Include generated dependencies. +include .depend + diff --git a/drivers/audio/es1371/SRC.c b/drivers/audio/es1371/SRC.c new file mode 100755 index 000000000..6fec22e94 --- /dev/null +++ b/drivers/audio/es1371/SRC.c @@ -0,0 +1,196 @@ +#include "SRC.h" + +#define SRC_RATE 48000U +#define reg(n) DSP->base + n + + +int SRCInit ( DEV_STRUCT * DSP ) +{ + u32_t i; + int retVal; + + /* Clear all SRC RAM then init - keep SRC disabled until done */ + if (WaitBitd (reg(CONC_dSRCIO_OFF), SRC_BUSY_BIT, 0, 1000)) + return (SRC_ERR_NOT_BUSY_TIMEOUT); + + pci_outl(reg(CONC_dSRCIO_OFF), SRC_DISABLE); + + for( i = 0; i < 0x80; ++i ) + if (SRC_SUCCESS != (retVal = SRCRegWrite(DSP, (u16_t)i, 0U))) + return (retVal); + + if (SRC_SUCCESS != (retVal = SRCRegWrite(DSP, SRC_SYNTH_BASE + SRC_TRUNC_N_OFF, 16 << 4))) + return (retVal); + if (SRC_SUCCESS != (retVal = SRCRegWrite(DSP, SRC_SYNTH_BASE + SRC_INT_REGS_OFF, 16 << 10))) + return (retVal); + if (SRC_SUCCESS != (retVal = SRCRegWrite(DSP, SRC_DAC_BASE + SRC_TRUNC_N_OFF, 16 << 4))) + return (retVal); + if (SRC_SUCCESS != (retVal = SRCRegWrite(DSP, SRC_DAC_BASE + SRC_INT_REGS_OFF, 16 << 10))) + return (retVal); + if (SRC_SUCCESS != (retVal = SRCRegWrite(DSP, SRC_SYNTH_LVOL, 1 << 12))) + return (retVal); + if (SRC_SUCCESS != (retVal = SRCRegWrite(DSP, SRC_SYNTH_RVOL, 1 << 12))) + return (retVal); + if (SRC_SUCCESS != (retVal = SRCRegWrite(DSP, SRC_DAC_LVOL, 1 << 12))) + return (retVal); + if (SRC_SUCCESS != (retVal = SRCRegWrite(DSP, SRC_DAC_RVOL, 1 << 12))) + return (retVal); + if (SRC_SUCCESS != (retVal = SRCRegWrite(DSP, SRC_ADC_LVOL, 1 << 12))) + return (retVal); + if (SRC_SUCCESS != (retVal = SRCRegWrite(DSP, SRC_ADC_RVOL, 1 << 12))) + return (retVal); + + /* default some rates */ + SRCSetRate(DSP, SRC_SYNTH_BASE, SRC_RATE); + SRCSetRate(DSP, SRC_DAC_BASE, SRC_RATE); + SRCSetRate(DSP, SRC_ADC_BASE, SRC_RATE); + + /* now enable the whole deal */ + if (WaitBitd (reg(CONC_dSRCIO_OFF), SRC_BUSY_BIT, 0, 1000)) + return (SRC_ERR_NOT_BUSY_TIMEOUT); + + pci_outl(reg(CONC_dSRCIO_OFF), 0UL); + + return 0; +} + + +int SRCRegRead(DEV_STRUCT * DSP, u16_t reg, u16_t *data) +{ + u32_t dtemp; + + /* wait for ready */ + if (WaitBitd (reg(CONC_dSRCIO_OFF), SRC_BUSY_BIT, 0, 1000)) + return (SRC_ERR_NOT_BUSY_TIMEOUT); + + dtemp = pci_inl(reg(CONC_dSRCIO_OFF)); + + /* assert a read request */ + pci_outl(reg(CONC_dSRCIO_OFF), + (dtemp & SRC_CTLMASK) | ((u32_t) reg << 25)); + + /* now wait for the data */ + if (WaitBitd (reg(CONC_dSRCIO_OFF), SRC_BUSY_BIT, 0, 1000)) + return (SRC_ERR_NOT_BUSY_TIMEOUT); + + if (NULL != data) + *data = (u16_t) pci_inl(reg(CONC_dSRCIO_OFF)); + + return 0; +} + + +int SRCRegWrite(DEV_STRUCT * DSP, u16_t reg, u16_t val) +{ + u32_t dtemp; + + + /* wait for ready */ + if (WaitBitd (reg(CONC_dSRCIO_OFF), SRC_BUSY_BIT, 0, 1000)) + return (SRC_ERR_NOT_BUSY_TIMEOUT); + + dtemp = pci_inl(reg(CONC_dSRCIO_OFF)); + + /* assert the write request */ + pci_outl(reg(CONC_dSRCIO_OFF), + (dtemp & SRC_CTLMASK) | SRC_WENABLE | ((u32_t) reg << 25) | val); + + return 0; +} + + +void SRCSetRate(DEV_STRUCT * DSP, char base, u16_t rate) +{ + u32_t freq, dtemp, i; + u16_t N, truncM, truncStart, wtemp; + + + if( base != SRC_ADC_BASE ) + { + /* freeze the channel */ + dtemp = base == SRC_SYNTH_BASE ? SRC_SYNTHFREEZE : SRC_DACFREEZE; + for( i = 0; i < SRC_IOPOLL_COUNT; ++i ) + if( !(pci_inl(reg(CONC_dSRCIO_OFF)) & SRC_BUSY) ) + break; + pci_outl(reg(CONC_dSRCIO_OFF), + (pci_inl(reg(CONC_dSRCIO_OFF)) & SRC_CTLMASK) | + dtemp); + + /* calculate new frequency and write it - preserve accum */ + /* please don't try to understand. */ + freq = ((u32_t) rate << 16) / 3000U; + SRCRegRead(DSP, base + SRC_INT_REGS_OFF, &wtemp); + + SRCRegWrite(DSP, base + SRC_INT_REGS_OFF, + (wtemp & 0x00ffU) | + (u16_t) (freq >> 6) & 0xfc00); + + SRCRegWrite(DSP, base + SRC_VFREQ_FRAC_OFF, (u16_t) freq >> 1); + + /* un-freeze the channel */ + dtemp = base == SRC_SYNTH_BASE ? SRC_SYNTHFREEZE : SRC_DACFREEZE; + for( i = 0; i < SRC_IOPOLL_COUNT; ++i ) + if( !(pci_inl(reg(CONC_dSRCIO_OFF)) & SRC_BUSY) ) + break; + pci_outl(reg(CONC_dSRCIO_OFF), + (pci_inl(reg(CONC_dSRCIO_OFF)) & SRC_CTLMASK) & + ~dtemp); + } + else /* setting ADC rate */ + { + /* freeze the channel */ + for( i = 0; i < SRC_IOPOLL_COUNT; ++i ) + if( !(pci_inl(reg(CONC_dSRCIO_OFF)) & SRC_BUSY) ) + break; + pci_outl(reg(CONC_dSRCIO_OFF), + (pci_inl(reg(CONC_dSRCIO_OFF)) & SRC_CTLMASK) | + SRC_ADCFREEZE); + + + + /* derive oversample ratio */ + N = rate/3000U; + if( N == 15 || N == 13 || N == 11 || N == 9 ) + --N; + SRCRegWrite(DSP, SRC_ADC_LVOL, N << 8); + SRCRegWrite(DSP, SRC_ADC_RVOL, N << 8); + + /* truncate the filter and write n/trunc_start */ + truncM = (21*N - 1) | 1; + if( rate >= 24000U ) + { + if( truncM > 239 ) + truncM = 239; + truncStart = (239 - truncM) >> 1; + SRCRegWrite(DSP, base + SRC_TRUNC_N_OFF, + (truncStart << 9) | (N << 4)); + } + else + { + if( truncM > 119 ) + truncM = 119; + truncStart = (119 - truncM) >> 1; + SRCRegWrite(DSP, base + SRC_TRUNC_N_OFF, + 0x8000U | (truncStart << 9) | (N << 4)); + } + + /* calculate new frequency and write it - preserve accum */ + freq = ((48000UL << 16) / rate) * N; + SRCRegRead(DSP, base + SRC_INT_REGS_OFF, &wtemp); + SRCRegWrite(DSP, base + SRC_INT_REGS_OFF, + (wtemp & 0x00ffU) | + (u16_t) (freq >> 6) & 0xfc00); + SRCRegWrite(DSP, base + SRC_VFREQ_FRAC_OFF, (u16_t) freq >> 1); + + /* un-freeze the channel */ + for( i = 0; i < SRC_IOPOLL_COUNT; ++i ) + if( !(pci_inl(reg(CONC_dSRCIO_OFF)) & SRC_BUSY) ) + break; + pci_outl(reg(CONC_dSRCIO_OFF), + (pci_inl(reg(CONC_dSRCIO_OFF)) & SRC_CTLMASK) & + ~SRC_ADCFREEZE); + } + return; +} + + diff --git a/drivers/audio/es1371/SRC.h b/drivers/audio/es1371/SRC.h new file mode 100755 index 000000000..7faddbe16 --- /dev/null +++ b/drivers/audio/es1371/SRC.h @@ -0,0 +1,43 @@ +#ifndef SRC_H +#define SRC_H + +#include "es1371.h" +#include "wait.h" + +_PROTOTYPE( int SRCInit, (DEV_STRUCT * DSP) ); +_PROTOTYPE( int SRCRegRead, (DEV_STRUCT * DSP, u16_t reg, u16_t *data) ); +_PROTOTYPE( int SRCRegWrite, (DEV_STRUCT * DSP, u16_t reg, u16_t val) ); +_PROTOTYPE( void SRCSetRate, (DEV_STRUCT * DSP, char src_base, u16_t rate) ); + + +/* register/base and control equates for the SRC RAM */ +#define SRC_SYNTH_FIFO 0x00 +#define SRC_DAC_FIFO 0x20 +#define SRC_ADC_FIFO 0x40 +#define SRC_SYNTH_BASE 0x70 +#define SRC_DAC_BASE 0x74 +#define SRC_ADC_BASE 0x78 +#define SRC_SYNTH_LVOL 0x7c +#define SRC_SYNTH_RVOL 0x7d +#define SRC_DAC_LVOL 0x7e +#define SRC_DAC_RVOL 0x7f +#define SRC_ADC_LVOL 0x6c +#define SRC_ADC_RVOL 0x6d + +#define SRC_TRUNC_N_OFF 0x00 +#define SRC_INT_REGS_OFF 0x01 +#define SRC_ACCUM_FRAC_OFF 0x02 +#define SRC_VFREQ_FRAC_OFF 0x03 + +/* miscellaneous control defines */ +#define SRC_IOPOLL_COUNT 0x1000UL +#define SRC_WENABLE (1UL << 24) +#define SRC_BUSY_BIT 23 +#define SRC_BUSY (1UL << SRC_BUSY_BIT) +#define SRC_DISABLE (1UL << 22) +#define SRC_SYNTHFREEZE (1UL << 21) +#define SRC_DACFREEZE (1UL << 20) +#define SRC_ADCFREEZE (1UL << 19) +#define SRC_CTLMASK 0x00780000UL + +#endif /* SRC_H */ diff --git a/drivers/audio/es1371/codec.c b/drivers/audio/es1371/codec.c new file mode 100755 index 000000000..8a110c5fe --- /dev/null +++ b/drivers/audio/es1371/codec.c @@ -0,0 +1,264 @@ +#include "codec.h" + + +/* Timeouts in milliseconds */ +#define WIP_TIMEOUT 250UL +#define DRDY_TIMEOUT 250UL + +/* The default SRC syncronization state number is 1. This state occurs + just after de-assertion of SYNC. This is supposed to be the safest + state for accessing the codec with an ES1371 Rev 1. Later versions + of the chip allegedly don't require syncronization. Be very careful + if you change this ! */ + +#define SRC_UNSYNCED 0xffffffffUL +static u32_t SrcSyncState = 0x00010000UL; + + +void CodecSetSrcSyncState (int state) +{ + if (state < 0) + SrcSyncState = SRC_UNSYNCED; + else { + SrcSyncState = (u32_t)state << 16; + SrcSyncState &= 0x00070000Ul; + } +} + + +int CodecWrite (DEV_STRUCT * pCC, u16_t wAddr, u16_t wData) +{ +u32_t dtemp, i; +u16_t wBaseAddr = pCC->base; + + /* wait for WIP bit (Write In Progress) to go away */ + /* remember, register CONC_dCODECCTL_OFF (0x14) + is a pseudo read-write register */ + if (WaitBitd (wBaseAddr + CONC_dCODECCTL_OFF, 30, 0, WIP_TIMEOUT)){ + printf("CODEC_ERR_WIP_TIMEOUT\n"); + return (CODEC_ERR_WIP_TIMEOUT); + } + if (SRC_UNSYNCED != SrcSyncState) + { + /* enable SRC state data in SRC mux */ + if (WaitBitd (wBaseAddr + CONC_dSRCIO_OFF, SRC_BUSY_BIT, 0, 1000)) + return (CODEC_ERR_SRC_NOT_BUSY_TIMEOUT); + + /* todo: why are we writing an undefined register? */ + dtemp = pci_inl(wBaseAddr + CONC_dSRCIO_OFF); + pci_outl(wBaseAddr + CONC_dSRCIO_OFF, (dtemp & SRC_CTLMASK) | + 0x00010000UL); + + /* wait for a SAFE time to write addr/data and then do it */ + /*_disable(); */ + for( i = 0; i < 0x1000UL; ++i ) + if( (pci_inl(wBaseAddr + CONC_dSRCIO_OFF) & 0x00070000UL) == + SrcSyncState ) + break; + + if (i >= 0x1000UL) { + /* _enable(); */ + return (CODEC_ERR_SRC_SYNC_TIMEOUT); + } + } + + /* A test for 5880 - prime the PCI data bus */ + { + u32_t dat = ((u32_t) wAddr << 16) | wData; + char page = pci_inb(wBaseAddr + CONC_bMEMPAGE_OFF); + + pci_outl (wBaseAddr + CONC_bMEMPAGE_OFF, dat); + + /* write addr and data */ + pci_outl(wBaseAddr + CONC_dCODECCTL_OFF, dat); + + pci_outb(wBaseAddr + CONC_bMEMPAGE_OFF, page); /* restore page reg */ + } + + if (SRC_UNSYNCED != SrcSyncState) + { + /* _enable(); */ + + /* restore SRC reg */ + if (WaitBitd (wBaseAddr + CONC_dSRCIO_OFF, SRC_BUSY_BIT, 0, 1000)) + return (CODEC_ERR_SRC_NOT_BUSY_TIMEOUT); + + pci_outl(wBaseAddr + CONC_dSRCIO_OFF, dtemp & 0xfff8ffffUL); + } + + return 0; +} + +int CodecRead (DEV_STRUCT * pCC, u16_t wAddr, u16_t *data) +{ +u32_t dtemp, i; +u16_t base = pCC->base; + + /* wait for WIP to go away */ + if (WaitBitd (base + CONC_dCODECCTL_OFF, 30, 0, WIP_TIMEOUT)) + return (CODEC_ERR_WIP_TIMEOUT); + + if (SRC_UNSYNCED != SrcSyncState) + { + /* enable SRC state data in SRC mux */ + if (WaitBitd (base + CONC_dSRCIO_OFF, SRC_BUSY_BIT, 0, 1000)) + return (CODEC_ERR_SRC_NOT_BUSY_TIMEOUT); + + dtemp = pci_inl(base + CONC_dSRCIO_OFF); + pci_outl(base + CONC_dSRCIO_OFF, (dtemp & SRC_CTLMASK) | + 0x00010000UL); + + /* wait for a SAFE time to write a read request and then do it */ + /* todo: how do we solve the lock() problem? */ + /* _disable(); */ + for( i = 0; i < 0x1000UL; ++i ) + if( (pci_inl(base + CONC_dSRCIO_OFF) & 0x00070000UL) == + SrcSyncState ) + break; + + if (i >= 0x1000UL) { + /*_enable();*/ + return (CODEC_ERR_SRC_SYNC_TIMEOUT); + } + } + + /* A test for 5880 - prime the PCI data bus */ + { + /* set bit 23, this means read in stead of write. */ + u32_t dat = ((u32_t) wAddr << 16) | (1UL << 23); + char page = pci_inb(base + CONC_bMEMPAGE_OFF); + + /* todo: why are we putting data in the mem page register??? */ + pci_outl(base + CONC_bMEMPAGE_OFF, dat); + + /* write addr w/data=0 and assert read request */ + pci_outl(base + CONC_dCODECCTL_OFF, dat); + + pci_outb(base + CONC_bMEMPAGE_OFF, page); /* restore page reg */ + + } + if (SRC_UNSYNCED != SrcSyncState) + { + + /*_enable();*/ + + /* restore SRC reg */ + if (WaitBitd (base + CONC_dSRCIO_OFF, SRC_BUSY_BIT, 0, 1000)) + return (CODEC_ERR_SRC_NOT_BUSY_TIMEOUT); + + pci_outl(base + CONC_dSRCIO_OFF, dtemp & 0xfff8ffffUL); + } + + /* now wait for the stinkin' data (DRDY = data ready) */ + if (WaitBitd (base + CONC_dCODECCTL_OFF, 31, 1, DRDY_TIMEOUT)) + return (CODEC_ERR_DATA_TIMEOUT); + + dtemp = pci_inl(base + CONC_dCODECCTL_OFF); + + if (data) + *data = (u16_t) dtemp; + + return 0; +} + + +int CodecWriteUnsynced (DEV_STRUCT * pCC, u16_t wAddr, u16_t wData) +{ + /* wait for WIP to go away */ + if (WaitBitd (pCC->base + CONC_dCODECCTL_OFF, 30, 0, WIP_TIMEOUT)) + return (CODEC_ERR_WIP_TIMEOUT); + + /* write addr and data */ + pci_outl(pCC->base + CONC_dCODECCTL_OFF, ((u32_t) wAddr << 16) | wData); + return 0; +} + + +int CodecReadUnsynced (DEV_STRUCT * pCC, u16_t wAddr, u16_t *data) +{ +u32_t dtemp; + + /* wait for WIP to go away */ + if (WaitBitd (pCC->base + CONC_dCODECCTL_OFF, 30, 0, WIP_TIMEOUT)) + return (CODEC_ERR_WIP_TIMEOUT); + + /* write addr w/data=0 and assert read request */ + pci_outl(pCC->base + CONC_dCODECCTL_OFF, ((u32_t) wAddr << 16) | (1UL << 23)); + + /* now wait for the stinkin' data (RDY) */ + if (WaitBitd (pCC->base + CONC_dCODECCTL_OFF, 31, 1, DRDY_TIMEOUT)) + return (CODEC_ERR_DATA_TIMEOUT); + + dtemp = pci_inl(pCC->base + CONC_dCODECCTL_OFF); + + if (data) + *data = (u16_t) dtemp; + + return 0; +} + +int CODECInit( DEV_STRUCT * pCC ) +{ +int retVal; + /* All powerdown modes: off */ + + retVal = CodecWrite (pCC, AC97_POWERDOWN_CONTROL_STAT, 0x0000U); + if (OK != retVal) + return (retVal); + + /* Mute Line Out & set to 0dB attenuation */ + + retVal = CodecWrite (pCC, AC97_MASTER_VOLUME, 0x0000U); + if (OK != retVal) + return (retVal); + + + retVal = CodecWrite (pCC, AC97_MONO_VOLUME, 0x8000U); + if (OK != retVal) + return (retVal); + + retVal = CodecWrite (pCC, AC97_PHONE_VOLUME, 0x8008U); + if (OK != retVal) + return (retVal); + + retVal = CodecWrite (pCC, AC97_MIC_VOLUME, 0x0008U); + if (OK != retVal) + return (retVal); + + retVal = CodecWrite (pCC, AC97_LINE_IN_VOLUME, 0x0808U); + if (OK != retVal) + return (retVal); + + retVal = CodecWrite (pCC, AC97_CD_VOLUME, 0x0808U); + if (OK != retVal) + return (retVal); + + retVal = CodecWrite (pCC, AC97_AUX_IN_VOLUME, 0x0808U); + if (OK != retVal) + return (retVal); + + retVal = CodecWrite (pCC, AC97_PCM_OUT_VOLUME, 0x0808U); + if (OK != retVal) + return (retVal); + + retVal = CodecWrite (pCC, AC97_RECORD_GAIN_VOLUME, 0x0000U); + if (OK != retVal) + return (retVal); + + /* Connect Line In to ADC */ + retVal = CodecWrite (pCC, AC97_RECORD_SELECT, 0x0404U); + if (OK != retVal) + return (retVal); + + retVal = CodecWrite (pCC, AC97_GENERAL_PURPOSE, 0x0000U); + if (OK != retVal) + return (retVal); + + return OK; +} + + + + + + diff --git a/drivers/audio/es1371/codec.h b/drivers/audio/es1371/codec.h new file mode 100755 index 000000000..beba38597 --- /dev/null +++ b/drivers/audio/es1371/codec.h @@ -0,0 +1,44 @@ +#ifndef CODEC_H +#define CODEC_H + +#include "es1371.h" +#include "wait.h" +#include "../AC97.h" +#include "SRC.h" + +#define CODEC_0DB_GAIN 0x0008 +#define CODEC_MAX_ATTN 0x003f +#define CODEC_MUTE 0x8000U + + +/* Control function defines */ +#define CODEC_CTL_4SPKR 0x00U /* 4-spkr output mode enable */ +#define CODEC_CTL_MICBOOST 0x01U /* Mic boost (+30 dB) enable */ +#define CODEC_CTL_PWRDOWN 0x02U /* power-down mode */ +#define CODEC_CTL_DOSMODE 0x03U /* A/D sync to DAC1 */ + + /* Timeout waiting for: */ +#define CODEC_ERR_WIP_TIMEOUT -1 /* write in progress complete */ +#define CODEC_ERR_DATA_TIMEOUT -2 /* data ready */ +#define CODEC_ERR_SRC_NOT_BUSY_TIMEOUT -3 /* SRC not busy */ +#define CODEC_ERR_SRC_SYNC_TIMEOUT -4 /* state #1 */ + +/* Function to inform CODEC module which AC97 vendor ID to expect */ +void CodecSetVendorId (char *tbuf); + +/* CODEC Mixer and Mode control function prototypes */ + +int CodecRead (DEV_STRUCT * pCC, u16_t wAddr, u16_t *data); +int CodecWrite (DEV_STRUCT * pCC, u16_t wAddr, u16_t wData); +void CodecSetSrcSyncState (int state); +int CodecWriteUnsynced (DEV_STRUCT * pCC, u16_t wAddr, u16_t wData); +int CodecReadUnsynced (DEV_STRUCT * pCC, u16_t wAddr, u16_t *data); + +/* + This function initializes the CODEC to a default mode. +*/ +int CODECInit( DEV_STRUCT * pCC ); + + + +#endif /* CODEC_H */ diff --git a/drivers/audio/es1371/es1371.c b/drivers/audio/es1371/es1371.c new file mode 100755 index 000000000..f83d5031a --- /dev/null +++ b/drivers/audio/es1371/es1371.c @@ -0,0 +1,588 @@ +/* This is the main file of the ES1371 sound driver + * There is no main function over here, instead the main function + * is located in the generic dma driver. All this driver does is + * implement the interface audio/audio_fw.h. All functions having the + * prefix 'drv_' are dictated by audio/audio_fw.h. The function + * prototypes you see below define a set of private helper functions. + * Control over the sample rate converter and the codec is delegated + * to SRC.c and codec.c respectively. + * + * November 2005 ES1371 driver (Laurens Bronwasser) + */ + + +#include "../framework/audio_fw.h" +#include "es1371.h" +#include "codec.h" +#include "SRC.h" +#include "../AC97.h" + + +#define reg(n) dev.base + n + +FORWARD _PROTOTYPE( int detect_hw, (void) ); +FORWARD _PROTOTYPE( int disable_int, (int sub_dev) ); +FORWARD _PROTOTYPE( int set_stereo, (u32_t stereo, int sub_dev) ); +FORWARD _PROTOTYPE( int set_bits, (u32_t nr_of_bits, int sub_dev) ); +FORWARD _PROTOTYPE( int set_sample_rate, (u32_t rate, int sub_dev) ); +FORWARD _PROTOTYPE( int set_sign, (u32_t val, int sub_dev) ); +FORWARD _PROTOTYPE( int get_max_frag_size, (u32_t * val, int *len, int sub_dev) ); +FORWARD _PROTOTYPE( int set_frag_size, (u32_t fragment_size, int sub_dev) ); +FORWARD _PROTOTYPE( int set_int_cnt, (int sub_dev) ); +FORWARD _PROTOTYPE( int AC97Write, (u16_t wAddr, u16_t wData)); +FORWARD _PROTOTYPE( int AC97Read, (u16_t wAddr, u16_t *data)); +FORWARD _PROTOTYPE( void set_nice_volume, (void) ); + +DEV_STRUCT dev; +u32_t base = 0; +aud_sub_dev_conf_t aud_conf[4]; + + +PUBLIC sub_dev_t sub_dev[4]; +PUBLIC special_file_t special_file[4]; +PUBLIC drv_t drv; + + +PUBLIC int drv_init(void) { + drv.DriverName = "ES1371"; + drv.NrOfSubDevices = 4; + drv.NrOfSpecialFiles = 4; + + sub_dev[DAC1_CHAN].readable = 0; + sub_dev[DAC1_CHAN].writable = 1; + sub_dev[DAC1_CHAN].DmaSize = 64 * 1024; + sub_dev[DAC1_CHAN].NrOfDmaFragments = 2; + sub_dev[DAC1_CHAN].MinFragmentSize = 1024; + sub_dev[DAC1_CHAN].NrOfExtraBuffers = 4; + + sub_dev[ADC1_CHAN].readable = 1; + sub_dev[ADC1_CHAN].writable = 0; + sub_dev[ADC1_CHAN].DmaSize = 64 * 1024; + sub_dev[ADC1_CHAN].NrOfDmaFragments = 2; + sub_dev[ADC1_CHAN].MinFragmentSize = 1024; + sub_dev[ADC1_CHAN].NrOfExtraBuffers = 4; + + sub_dev[MIXER].writable = 0; + sub_dev[MIXER].readable = 0; + + sub_dev[DAC2_CHAN].readable = 0; + sub_dev[DAC2_CHAN].writable = 1; + sub_dev[DAC2_CHAN].DmaSize = 64 * 1024; + sub_dev[DAC2_CHAN].NrOfDmaFragments = 2; + sub_dev[DAC2_CHAN].MinFragmentSize = 1024; + sub_dev[DAC2_CHAN].NrOfExtraBuffers = 4; + + special_file[0].minor_dev_nr = 0; + special_file[0].write_chan = DAC1_CHAN; + special_file[0].read_chan = NO_CHANNEL; + special_file[0].io_ctl = DAC1_CHAN; + + special_file[1].minor_dev_nr = 1; + special_file[1].write_chan = NO_CHANNEL; + special_file[1].read_chan = ADC1_CHAN; + special_file[1].io_ctl = ADC1_CHAN; + + special_file[2].minor_dev_nr = 2; + special_file[2].write_chan = NO_CHANNEL; + special_file[2].read_chan = NO_CHANNEL; + special_file[2].io_ctl = MIXER; + + special_file[3].minor_dev_nr = 3; + special_file[3].write_chan = DAC2_CHAN; + special_file[3].read_chan = NO_CHANNEL; + special_file[3].io_ctl = DAC2_CHAN; +} + +int drv_init_hw (void) +{ + u16_t i, j; + + /* First, detect the hardware */ + if (detect_hw() != OK) { + return EIO; + } + /* + Put HW in a nice state ... all devices enabled except joystick, + NMI enables off, clear pending NMIs if any */ + + /* PCI command register */ + pci_attr_w16 (dev.devind, PCI_CR, 0x0105); + /* set power management control/status register */ + pci_attr_w16 (dev.devind, 0xE0, 0x0000); + + pci_outb(reg(CONC_bDEVCTL_OFF), 0x00); + pci_outb(reg(CONC_bMISCCTL_OFF), 0x00); + pci_outb(reg(CONC_b4SPKR_OFF), 0x00); + pci_outb(reg(CONC_bNMIENA_OFF), 0x00); + pci_outb(reg(CONC_bNMICTL_OFF), 0x08); + pci_outw(reg(CONC_wNMISTAT_OFF), 0x0000); + pci_outb(reg(CONC_bSERCTL_OFF), 0x00); + + /* clear all cache RAM */ + for( i = 0; i < 0x10; ++i ) + { + pci_outb(reg(CONC_bMEMPAGE_OFF), i); + for( j = 0; j < 0x10; j += 4 ) + pci_outl (reg(CONC_MEMBASE_OFF) + j, 0UL); + } + /* DO NOT SWITCH THE ORDER OF SRCInit and CODECInit function calls!!! */ + /* The effect is only noticable after a cold reset (reboot) */ + if (SRCInit(&dev) != OK) { + return EIO; + } + if (CODECInit(&dev) != OK) { + return EIO; + } + set_nice_volume(); /* of course we need a nice mixer to do this */ + + /* initialize variables for each sub_device */ + for (i = 0; i < drv.NrOfSubDevices; i++) { + if(i != MIXER) { + aud_conf[i].busy = 0; + aud_conf[i].stereo = DEFAULT_STEREO; + aud_conf[i].sample_rate = DEFAULT_RATE; + aud_conf[i].nr_of_bits = DEFAULT_NR_OF_BITS; + aud_conf[i].sign = DEFAULT_SIGNED; + aud_conf[i].fragment_size = sub_dev[i].DmaSize / sub_dev[i].NrOfDmaFragments; + } + } + return OK; +} + + +PRIVATE int detect_hw(void) { + + u32_t r; + int devind; + u16_t v_id, d_id; + + /* detect_hw tries to find device and get IRQ and base address + with a little (much) help from the PCI library. + This code is quite device independent and you can copy it. + (just make sure to get the bugs out first)*/ + + pci_init(); + /* get first device and then search through the list */ + r = pci_first_dev(&devind, &v_id, &d_id); + while( r > 0 ) { + /* if we have a match...break */ + if (v_id == VENDOR_ID && d_id == DEVICE_ID) break; + r = pci_next_dev(&devind, &v_id, &d_id); + } + + /* did we find anything? */ + if (v_id != VENDOR_ID || d_id != DEVICE_ID) { + return EIO; + } + /* right here we should reserve the device, but the pci library + doesn't support global reservation of devices yet. This would + be a problem if more ES1371's were installed on this system. */ + + dev.name = pci_dev_name(v_id, d_id); + /* get base address of our device, ignore least signif. bit + this last bit thing could be device dependent, i don't know */ + dev.base = pci_attr_r32(devind, PCI_BAR) & 0xfffffffe; + /* get IRQ */ + dev.irq = pci_attr_r8(devind, PCI_ILR); + dev.revision = pci_attr_r8(devind, 0x08); + dev.d_id = d_id; + dev.v_id = v_id; + dev.devind = devind; /* pci device identifier */ + return OK; +} + + +int drv_reset(void) +{ + /* make a WARM reset */ + u16_t i; + + /* set SYNC_RES bit */ + pci_outl(reg(CONC_bDEVCTL_OFF), + pci_inl(reg(CONC_bDEVCTL_OFF)) | SYNC_RES_BIT); + + /* got to delay at least 1 usec, try 18 usec */ + for (i=0; i<100; i++) { + pci_inb(reg(0)); + } + /* clear SYNC_RES bit */ + pci_outl(reg(CONC_bDEVCTL_OFF), + pci_inl(reg(CONC_bDEVCTL_OFF)) & ~SYNC_RES_BIT); + return OK; +} + + +int drv_start(int sub_dev, int DmaMode) +{ + u32_t enable_bit, result = 0; + + /* Write default values to device in case user failed to configure. + If user did configure properly, everything is written twice. + please raise your hand if you object against to this strategy...*/ + result |= set_sample_rate(aud_conf[sub_dev].sample_rate, sub_dev); + result |= set_stereo(aud_conf[sub_dev].stereo, sub_dev); + result |= set_bits(aud_conf[sub_dev].nr_of_bits, sub_dev); + result |= set_sign(aud_conf[sub_dev].sign, sub_dev); + + /* set the interrupt count */ + result |= set_int_cnt(sub_dev); + + if (result) { + return EIO; + } + + /* if device currently paused, resume */ + drv_resume(sub_dev); + + switch(sub_dev) { + case ADC1_CHAN: enable_bit = ADC1_EN_BIT;break; + case DAC1_CHAN: enable_bit = DAC1_EN_BIT;break; + case DAC2_CHAN: enable_bit = DAC2_EN_BIT;break; + default: return EINVAL; + } + /* enable interrupts from 'sub device' */ + drv_reenable_int(sub_dev); + + /* this means GO!!! */ + pci_outl(reg(CONC_bDEVCTL_OFF), + pci_inl(reg(CONC_bDEVCTL_OFF)) | enable_bit); + + aud_conf[sub_dev].busy = 1; + return OK; +} + + +int drv_stop(int sub_dev) +{ + u32_t enable_bit; + + switch(sub_dev) { + case ADC1_CHAN: enable_bit = ADC1_EN_BIT;break; + case DAC1_CHAN: enable_bit = DAC1_EN_BIT;break; + case DAC2_CHAN: enable_bit = DAC2_EN_BIT;break; + default: return EINVAL; + } + /* stop the codec */ + pci_outl(reg(CONC_bDEVCTL_OFF), + pci_inl(reg(CONC_bDEVCTL_OFF)) & ~enable_bit); + + aud_conf[sub_dev].busy = 0; + disable_int(sub_dev); + return OK; +} + + +/* all IO-ctl's sent to the upper driver are passed to this function */ +int drv_io_ctl(int request, void * val, int * len, int sub_dev) { + + int status; + + switch(request) { + case DSPIORATE: status = set_sample_rate(*((u32_t *) val), sub_dev); break; + case DSPIOSTEREO: status = set_stereo(*((u32_t *) val), sub_dev); break; + case DSPIOBITS: status = set_bits(*((u32_t *) val), sub_dev); break; + case DSPIOSIZE: status = set_frag_size(*((u32_t *) val), sub_dev); break; + case DSPIOSIGN: status = set_sign(*((u32_t *) val), sub_dev); break; + case DSPIOMAX: status = get_max_frag_size(val, len, sub_dev);break; + case DSPIORESET: status = drv_reset(); break; + case AC97READ: status = AC97Read (*((u16_t *)val), ((u16_t *) val+2));break; + case AC97WRITE: status = AC97Write(*((u16_t *)val), *((u16_t *) val+2));break; + default: status = EINVAL; break; + } + + return OK; +} + + +int drv_get_irq(char *irq) { + *irq = dev.irq; + return OK; +} + + +int drv_get_frag_size(u32_t *frag_size, int sub_dev) { + *frag_size = aud_conf[sub_dev].fragment_size; + return OK; +} + + +int drv_set_dma(u32_t dma, u32_t length, int chan) { + /* dma length in bytes, + max is 64k long words for es1371 = 256k bytes */ + u32_t page, frame_count_reg, dma_add_reg; + + switch(chan) { + case ADC1_CHAN: page = CONC_ADCCTL_PAGE; + frame_count_reg = CONC_wADCFC_OFF; + dma_add_reg = CONC_dADCPADDR_OFF; + break; + case DAC1_CHAN: page = CONC_SYNCTL_PAGE; + frame_count_reg = CONC_wSYNFC_OFF; + dma_add_reg = CONC_dSYNPADDR_OFF; + break;; + case DAC2_CHAN: page = CONC_DACCTL_PAGE; + frame_count_reg = CONC_wDACFC_OFF; + dma_add_reg = CONC_dDACPADDR_OFF; + break;; + default: return EIO; + } + pci_outb(reg(CONC_bMEMPAGE_OFF), page); + pci_outl(reg(dma_add_reg), dma); + /* device expects long word count in stead of bytes */ + length /= 4; + /* device expects length -1 */ + pci_outl(reg(frame_count_reg), (u32_t) (length - 1)); +} + + +/* return status of the interrupt summary bit */ +int drv_int_sum(void) { + u32_t int_status; + int_status = pci_inl(reg(CONC_bINTSTAT_OFF)) & 0x80000000UL; + return int_status; +} + + +int drv_int(int sub_dev) { + u32_t int_status; + char bit; + + /* return status of interrupt bit of specified channel*/ + + switch (sub_dev) { + case DAC1_CHAN: bit = DAC1_INT_STATUS_BIT;break; + case DAC2_CHAN: bit = DAC2_INT_STATUS_BIT;break; + case ADC1_CHAN: bit = ADC1_INT_STATUS_BIT;break; + } + int_status = pci_inl(reg(CONC_bINTSTAT_OFF)) & bit; + return int_status; +} + + +int drv_reenable_int(int chan) { + u32_t i, int_en_bit; + + switch(chan) { + case ADC1_CHAN: int_en_bit = ADC1_INT_EN_BIT;break; + case DAC1_CHAN: int_en_bit = DAC1_INT_EN_BIT;break; + case DAC2_CHAN: int_en_bit = DAC2_INT_EN_BIT;break; + default: EINVAL; + } + /* clear and reenable an interrupt */ + i = pci_inl(reg(CONC_bSERFMT_OFF)); + pci_outl(reg(CONC_bSERFMT_OFF), i & ~int_en_bit); + pci_outl(reg(CONC_bSERFMT_OFF), i | int_en_bit); +} + + +int drv_pause(int sub_dev) +{ + u32_t pause_bit; + + disable_int(sub_dev); /* don't send interrupts */ + + switch(sub_dev) { + case DAC1_CHAN: pause_bit = DAC1_PAUSE_BIT;break; + case DAC2_CHAN: pause_bit = DAC2_PAUSE_BIT;break; + default: return EINVAL; + } + /* pause */ + pci_outl(reg(CONC_bSERFMT_OFF), + pci_inl(reg(CONC_bSERFMT_OFF)) | pause_bit); + return OK; +} + + +int drv_resume(int sub_dev) +{ + u32_t pause_bit = 0; + + /* todo: drv_reenable_int(sub_dev); *//* enable interrupts */ + + switch(sub_dev) { + case DAC1_CHAN: pause_bit = DAC1_PAUSE_BIT;break; + case DAC2_CHAN: pause_bit = DAC2_PAUSE_BIT;break; + default: return EINVAL; + } + /* clear pause bit */ + pci_outl(reg(CONC_bSERFMT_OFF), + pci_inl(reg(CONC_bSERFMT_OFF)) & ~pause_bit); + return OK; +} + + +PRIVATE int set_bits(u32_t nr_of_bits, int sub_dev) { + + /* set format bits for specified channel. */ + u32_t size_16_bit, i; + + switch(sub_dev) { + case ADC1_CHAN: size_16_bit = ADC1_16_8_BIT;break; + case DAC1_CHAN: size_16_bit = DAC1_16_8_BIT;break; + case DAC2_CHAN: size_16_bit = DAC2_16_8_BIT;break; + default: return EINVAL; + } + i = pci_inb(reg(CONC_bSERFMT_OFF)); + i &= ~size_16_bit; + switch(nr_of_bits) { + case 16: i |= size_16_bit;break; + case 8: break; + default: return EINVAL; + } + pci_outb(reg(CONC_bSERFMT_OFF), i); + aud_conf[sub_dev].nr_of_bits = nr_of_bits; + return OK; +} + + +PRIVATE int set_stereo(u32_t stereo, int sub_dev) { + + /* set format bits for specified channel. */ + u32_t stereo_bit, i; + switch(sub_dev) { + case ADC1_CHAN: stereo_bit = ADC1_STEREO_BIT;break; + case DAC1_CHAN: stereo_bit = DAC1_STEREO_BIT;break; + case DAC2_CHAN: stereo_bit = DAC2_STEREO_BIT;break; + default: return EINVAL; + } + i = pci_inb(reg(CONC_bSERFMT_OFF)); + i &= ~stereo_bit; + if( stereo == TRUE ) { + i |= stereo_bit; + } + pci_outb(reg(CONC_bSERFMT_OFF), i); + aud_conf[sub_dev].stereo = stereo; + return OK; +} + + +PRIVATE int set_sign(u32_t val, int sub_dev) { + return OK; +} + + +PRIVATE int set_frag_size(u32_t fragment_size, int sub_dev_nr) { + if (fragment_size > (sub_dev[sub_dev_nr].DmaSize / sub_dev[sub_dev_nr].NrOfDmaFragments) || fragment_size < sub_dev[sub_dev_nr].MinFragmentSize) { + return EINVAL; + } + aud_conf[sub_dev_nr].fragment_size = fragment_size; + return OK; +} + + +PRIVATE int set_sample_rate(u32_t rate, int sub_dev) { + u32_t SRCBaseReg; + + if (rate > MAX_RATE || rate < MIN_RATE) { + return EINVAL; + } + /* set the sample rate for the specified channel*/ + switch(sub_dev) { + case ADC1_CHAN: SRCBaseReg = SRC_ADC_BASE;break; + case DAC1_CHAN: SRCBaseReg = SRC_SYNTH_BASE;break; + case DAC2_CHAN: SRCBaseReg = SRC_DAC_BASE;break; + default: return EINVAL; + } + SRCSetRate(&dev, SRCBaseReg, rate); + aud_conf[sub_dev].sample_rate = rate; + return OK; +} + + +PRIVATE int set_int_cnt(int chan) { + /* Write interrupt count for specified channel. + After bytes, an interrupt will be generated */ + + int sample_count; u16_t int_cnt_reg; + + if (aud_conf[chan].fragment_size > (sub_dev[chan].DmaSize / sub_dev[chan].NrOfDmaFragments) + || aud_conf[chan].fragment_size < sub_dev[chan].MinFragmentSize) { + return EINVAL; + } + + switch(chan) { + case ADC1_CHAN: int_cnt_reg = CONC_wADCIC_OFF;break; + case DAC1_CHAN: int_cnt_reg = CONC_wSYNIC_OFF;break; + case DAC2_CHAN: int_cnt_reg = CONC_wDACIC_OFF;break; + default: return EINVAL; + } + + sample_count = aud_conf[chan].fragment_size; + + /* adjust sample count according to sample format */ + if( aud_conf[chan].stereo == TRUE ) sample_count >>= 1; + switch(aud_conf[chan].nr_of_bits) { + case 16: sample_count >>= 1;break; + case 8: break; + default: return EINVAL; + } + /* set the sample count - 1 for the specified channel. */ + pci_outw(reg(int_cnt_reg), sample_count - 1); + return OK; +} + + +PRIVATE int get_max_frag_size(u32_t * val, int * len, int sub_dev_nr) { + *len = sizeof(*val); + *val = (sub_dev[sub_dev_nr].DmaSize / sub_dev[sub_dev_nr].NrOfDmaFragments); + return OK; +} + + +PRIVATE int disable_int(int chan) { + u32_t i, int_en_bit; + + switch(chan) { + case ADC1_CHAN: int_en_bit = ADC1_INT_EN_BIT;break; + case DAC1_CHAN: int_en_bit = DAC1_INT_EN_BIT;break; + case DAC2_CHAN: int_en_bit = DAC2_INT_EN_BIT;break; + default: EINVAL; + } + /* clear the interrupt */ + i = pci_inl(reg(CONC_bSERFMT_OFF)); + pci_outl(reg(CONC_bSERFMT_OFF), i & ~int_en_bit); +} + + +PRIVATE void set_nice_volume(void) { + /* goofy code to set the DAC1 channel to an audibe volume + to be able to test it without using the mixer */ + + AC97Write(AC97_PCM_OUT_VOLUME, 0x0808);/* the higher, the softer */ + AC97Write(AC97_MASTER_VOLUME, 0x0101); + AC97Write(0x38, 0); /* not crucial */ + + AC97Write(AC97_LINE_IN_VOLUME, 0x0303); + AC97Write(AC97_MIC_VOLUME, 0x0303); + + /* mute record gain */ + AC97Write(AC97_RECORD_GAIN_VOLUME, 0xFFFF); + + /* Also, to be able test recording without mixer: + select ONE channel as input below. */ + + /* select LINE IN */ + /*CodecWrite(AC97_RECORD_SELECT, 0x0404);*/ + + /* select MIC */ + AC97Write(AC97_RECORD_SELECT, 0x0000); + + /* unmute record gain */ + AC97Write(AC97_RECORD_GAIN_VOLUME, 0x0000); +} + + +/* The following two functions can be used by the mixer to + control and read volume settings. */ +PRIVATE int AC97Write (u16_t addr, u16_t data) +{ + /* todo: only allow volume control, + no serial data or dev ctl please*/ + return CodecWriteUnsynced(&dev, addr, data); +} + + +PRIVATE int AC97Read (u16_t addr, u16_t *data) +{ + return CodecReadUnsynced(&dev, addr, data); +} diff --git a/drivers/audio/es1371/es1371.h b/drivers/audio/es1371/es1371.h new file mode 100755 index 000000000..309b1c0b4 --- /dev/null +++ b/drivers/audio/es1371/es1371.h @@ -0,0 +1,138 @@ +#ifndef ES1371_H +#define ES1371_H + +#include +#include "../../drivers.h" +#include "../../libpci/pci.h" +#include + +#define DAC1_CHAN 0 +#define ADC1_CHAN 1 +#define MIXER 2 +#define DAC2_CHAN 3 + +/* set your vendor and device ID's here */ +#define VENDOR_ID 0x1274 +#define DEVICE_ID 0x1371 + +/* Concert97 direct register offset defines */ +#define CONC_bDEVCTL_OFF 0x00 /* Device control/enable */ +#define CONC_bMISCCTL_OFF 0x01 /* Miscellaneous control */ +#define CONC_bGPIO_OFF 0x02 /* General purpose I/O control */ +#define CONC_bJOYCTL_OFF 0x03 /* Joystick control (decode) */ +#define CONC_bINTSTAT_OFF 0x04 /* Device interrupt status */ +#define CONC_bCODECSTAT_OFF 0x05 /* CODEC interface status */ +#define CONC_bINTSUMM_OFF 0x07 /* Interrupt summary status */ +#define CONC_b4SPKR_OFF 0x07 /* Also 4 speaker config reg */ +#define CONC_bSPDIF_ROUTE_OFF 0x07 /* Also S/PDIF route control reg */ +#define CONC_bUARTDATA_OFF 0x08 /* UART data R/W - read clears RX int */ +#define CONC_bUARTCSTAT_OFF 0x09 /* UART control and status */ +#define CONC_bUARTTEST_OFF 0x0a /* UART test control reg */ +#define CONC_bMEMPAGE_OFF 0x0c /* Memory page select */ +#define CONC_dSRCIO_OFF 0x10 /* I/O ctl/stat/data for SRC RAM */ +#define CONC_dCODECCTL_OFF 0x14 /* CODEC control - u32_t read/write */ +#define CONC_wNMISTAT_OFF 0x18 /* Legacy NMI status */ +#define CONC_bNMIENA_OFF 0x1a /* Legacy NMI enable */ +#define CONC_bNMICTL_OFF 0x1b /* Legacy control */ +#define CONC_bSERFMT_OFF 0x20 /* Serial device format */ +#define CONC_bSERCTL_OFF 0x21 /* Serial device control */ +#define CONC_bSKIPC_OFF 0x22 /* DAC skip count reg */ +#define CONC_wSYNIC_OFF 0x24 /* Synth int count in sample frames */ +#define CONC_wSYNCIC_OFF 0x26 /* Synth current int count */ +#define CONC_wDACIC_OFF 0x28 /* DAC int count in sample frames */ +#define CONC_wDACCIC_OFF 0x2a /* DAC current int count */ +#define CONC_wADCIC_OFF 0x2c /* ADC int count in sample frames */ +#define CONC_wADCCIC_OFF 0x2e /* ADC current int count */ +#define CONC_MEMBASE_OFF 0x30 /* Memory window base - 16 byte window */ + +/* Concert memory page-banked register offset defines */ +#define CONC_dSYNPADDR_OFF 0x30 /* Synth host frame PCI phys addr */ +#define CONC_wSYNFC_OFF 0x34 /* Synth host frame count in u32_t'S */ +#define CONC_wSYNCFC_OFF 0x36 /* Synth host current frame count */ +#define CONC_dDACPADDR_OFF 0x38 /* DAC host frame PCI phys addr */ +#define CONC_wDACFC_OFF 0x3c /* DAC host frame count in u32_t'S */ +#define CONC_wDACCFC_OFF 0x3e /* DAC host current frame count */ +#define CONC_dADCPADDR_OFF 0x30 /* ADC host frame PCI phys addr */ +#define CONC_wADCFC_OFF 0x34 /* ADC host frame count in u32_t'S */ +#define CONC_wADCCFC_OFF 0x36 /* ADC host current frame count */ + +/* memory page number defines */ +#define CONC_SYNRAM_PAGE 0x00 /* Synth host/serial I/F RAM */ +#define CONC_DACRAM_PAGE 0x04 /* DAC host/serial I/F RAM */ +#define CONC_ADCRAM_PAGE 0x08 /* ADC host/serial I/F RAM */ +#define CONC_SYNCTL_PAGE 0x0c /* Page bank for synth host control */ +#define CONC_DACCTL_PAGE 0x0c /* Page bank for DAC host control */ +#define CONC_ADCCTL_PAGE 0x0d /* Page bank for ADC host control */ +#define CONC_FIFO0_PAGE 0x0e /* page 0 of UART "FIFO" (rx stash) */ +#define CONC_FIFO1_PAGE 0x0f /* page 1 of UART "FIFO" (rx stash) */ + + + +/* bits for Interrupt/Chip Select Control Register (offset 0x00)*/ +#define DAC1_EN_BIT bit(6) +#define DAC2_EN_BIT bit(5) +#define ADC1_EN_BIT bit(4) +#define SYNC_RES_BIT bit(14) + +/* bits for Interrupt/Chip Select Status Register (offset 0x04)*/ +#define DAC1_INT_STATUS_BIT bit(2) +#define DAC2_INT_STATUS_BIT bit(1) +#define ADC1_INT_STATUS_BIT bit(0) + +/* some bits for Serial Interface Control Register (CONC_bSERFMT_OFF 20H) */ +#define DAC1_STEREO_BIT bit(0) /* stereo or mono format */ +#define DAC1_16_8_BIT bit(1) /* 16 or 8 bit format */ +#define DAC2_STEREO_BIT bit(2) +#define DAC2_16_8_BIT bit(3) +#define ADC1_STEREO_BIT bit(4) +#define ADC1_16_8_BIT bit(5) +#define DAC1_INT_EN_BIT bit(8) /* interupt enable bits */ +#define DAC2_INT_EN_BIT bit(9) +#define ADC1_INT_EN_BIT bit(10) +#define DAC1_PAUSE_BIT bit(11) +#define DAC2_PAUSE_BIT bit(12) + +/* Some return values */ +#define SRC_SUCCESS 0 +#define CONC_SUCCESS 0 + /* Timeout waiting for: */ +#define SRC_ERR_NOT_BUSY_TIMEOUT -1 /* SRC not busy */ +#define CONC_ERR_NO_PCI_BIOS -2 +#define CONC_ERR_DEVICE_NOT_FOUND -3 +#define CONC_ERR_SPDIF_NOT_AVAIL -4 +#define CONC_ERR_SPDIF_ROUTING_NOT_AVAIL -5 +#define CONC_ERR_4SPEAKER_NOT_AVAIL -6 +#define CONC_ERR_ECHO_NOT_AVAIL -7 + +typedef struct { + u32_t stereo; + u16_t sample_rate; + u32_t nr_of_bits; + u32_t sign; + u32_t busy; + u32_t fragment_size; +} aud_sub_dev_conf_t; + +/* Some defaults for the aud_sub_dev_conf_t*/ +#define DEFAULT_RATE 44100 /* Sample rate */ +#define DEFAULT_NR_OF_BITS 16 /* Nr. of bits per sample per channel*/ +#define DEFAULT_SIGNED 0 /* 0 = unsigned, 1 = signed */ +#define DEFAULT_STEREO 1 /* 0 = mono, 1 = stereo */ +#define MAX_RATE 44100 /* Max sample speed in KHz */ +#define MIN_RATE 4000 /* Min sample speed in KHz */ + + +typedef struct DEVSTRUCT { + char* name; + u16_t v_id; /* vendor id */ + u16_t d_id; /* device id */ + u32_t devind; /* minix pci device id, for pci configuration space */ + u32_t base; /* changed to 32 bits */ + char irq; + char revision;/* version of the device */ +} DEV_STRUCT; + +#define bit(n) 1UL << n + + +#endif /* ES1371_H */ diff --git a/drivers/audio/es1371/wait.c b/drivers/audio/es1371/wait.c new file mode 100755 index 000000000..00db6bfae --- /dev/null +++ b/drivers/audio/es1371/wait.c @@ -0,0 +1,27 @@ +#include "../../drivers.h" +#include +#include +#include "../../libpci/pci.h" + +int WaitBitd (int paddr, int bitno, int state, long tmout) +{ +unsigned long val, mask; + + mask = 1UL << bitno; + tmout *= 5000; + + if(state) { + while(tmout-- > 0) { + if((val = pci_inl(paddr)) & mask) { + return 0; + } + } + } else { + while(tmout-- > 0) { + if(!((val = pci_inl(paddr)) & mask)) { + return 0; + } + } + } + return 0; +} diff --git a/drivers/audio/es1371/wait.h b/drivers/audio/es1371/wait.h new file mode 100755 index 000000000..5df6a39a1 --- /dev/null +++ b/drivers/audio/es1371/wait.h @@ -0,0 +1,10 @@ +/* WAIT.H +// General purpose waiting routines + +// Function prototypes +*/ +int WaitBitb (int paddr, int bitno, int state, long tmout); +int WaitBitw (int paddr, int bitno, int state, long tmout); +int WaitBitd (int paddr, int bitno, int state, long tmout); +int MemWaitw (unsigned int volatile *gaddr, int bitno, int state, long tmout); + diff --git a/drivers/audio/framework/Makefile b/drivers/audio/framework/Makefile new file mode 100644 index 000000000..4273ada8c --- /dev/null +++ b/drivers/audio/framework/Makefile @@ -0,0 +1,9 @@ +CC = exec cc + +all: audio_fw.o + +audio_fw.o: + $(CC) -c audio_fw.c + +clean: + rm -f a.out *.bak core errs audio_fw.o diff --git a/drivers/audio/framework/audio_fw.c b/drivers/audio/framework/audio_fw.c new file mode 100755 index 000000000..bd380990a --- /dev/null +++ b/drivers/audio/framework/audio_fw.c @@ -0,0 +1,825 @@ +/* This file contains a standard driver for audio devices. + * It supports double dma buffering and can be configured to use + * extra buffer space beside the dma buffer. + * This driver also support sub devices, which can be independently + * opened and closed. + * + * The driver supports the following operations: + * + * m_type DEVICE PROC_NR COUNT POSITION ADRRESS + * ----------------------------------------------------------------- + * | DEV_OPEN | device | proc nr | | | | + * |-------------+---------+---------+---------+---------+---------| + * | DEV_CLOSE | device | proc nr | | | | + * |-------------+---------+---------+---------+---------+---------| + * | DEV_READ | device | proc nr | bytes | | buf ptr | + * |-------------+---------+---------+---------+---------+---------| + * | DEV_WRITE | device | proc nr | bytes | | buf ptr | + * |-------------+---------+---------+---------+---------+---------| + * | DEV_IOCTL | device | proc nr |func code| | buf ptr | + * |-------------+---------+---------+---------+---------+---------| + * | DEV_STATUS | | | | | | + * |-------------+---------+---------+---------+---------+---------| + * | HARD_INT | | | | | | + * |-------------+---------+---------+---------+---------+---------| + * | SIG_STOP | | | | | | + * ----------------------------------------------------------------- + * + * The file contains one entry point: + * + * main: main entry when driver is brought up + * + * February 2006 Updated audio framework, changed driver-framework relation (Peter Boonstoppel) + * November 2005 Created generic DMA driver framework (Laurens Bronwasser) + * August 24 2005 Ported audio driver to user space (only audio playback) (Peter Boonstoppel) + * May 20 1995 SB16 Driver: Michel R. Prevenier + */ + +#include "audio_fw.h" + +FORWARD _PROTOTYPE( int msg_open, (int minor_dev_nr) ); +FORWARD _PROTOTYPE( int msg_close, (int minor_dev_nr) ); +FORWARD _PROTOTYPE( int msg_ioctl, (message *m_ptr) ); +FORWARD _PROTOTYPE( void msg_write, (message *m_ptr) ); +FORWARD _PROTOTYPE( void msg_read, (message *m_ptr) ); +FORWARD _PROTOTYPE( void msg_hardware, (void) ); +FORWARD _PROTOTYPE( void msg_sig_stop, (void) ); +FORWARD _PROTOTYPE( void msg_status, (message *m_ptr) ); +FORWARD _PROTOTYPE( int init_driver, (void) ); +FORWARD _PROTOTYPE( int open_sub_dev, (int sub_dev_nr, int operation) ); +FORWARD _PROTOTYPE( int close_sub_dev, (int sub_dev_nr) ); +FORWARD _PROTOTYPE( void handle_int_write,(int sub_dev_nr) ); +FORWARD _PROTOTYPE( void handle_int_read,(int sub_dev_nr) ); +FORWARD _PROTOTYPE( void data_to_user, (sub_dev_t *sub_dev_ptr) ); +FORWARD _PROTOTYPE( void data_from_user, (sub_dev_t *sub_dev_ptr) ); +FORWARD _PROTOTYPE( int init_buffers, (sub_dev_t *sub_dev_ptr) ); +FORWARD _PROTOTYPE( int get_started, (sub_dev_t *sub_dev_ptr) ); +FORWARD _PROTOTYPE( void reply,(int code, int replyee, int process,int status)); +FORWARD _PROTOTYPE( int io_ctl_length, (int io_request) ); +FORWARD _PROTOTYPE( special_file_t* get_special_file, (int minor_dev_nr) ); + + +PRIVATE char io_ctl_buf[_IOCPARM_MASK]; +PRIVATE int irq_hook_id = 0; /* id of irq hook at the kernel */ +PRIVATE int irq_hook_set = FALSE; +PRIVATE device_available = 0;/*todo*/ + + +PUBLIC void main(void) +{ + int r, caller, proc_nr, chan; + message mess; + + drv_init(); + + /* Here is the main loop of the dma driver. It waits for a message, + carries it out, and sends a reply. */ + printf("%s up and running\n", drv.DriverName); + + while(1) { + receive(ANY, &mess); + caller = mess.m_source; + proc_nr = mess.PROC_NR; + + /* Now carry out the work. */ + switch(mess.m_type) { + case DEV_OPEN: /* open the special file ( = parameter) */ + r = msg_open(mess.DEVICE);break; + + case DEV_CLOSE: /* close the special file ( = parameter) */ + r = msg_close(mess.DEVICE); break; + + case DEV_IOCTL: + r = msg_ioctl(&mess); break; + + case DEV_READ: + msg_read(&mess); continue; /* don't reply */ + + case DEV_WRITE: + msg_write(&mess); continue; /* don't reply */ + + case DEV_STATUS: + msg_status(&mess);continue; /* don't reply */ + + case HARD_INT: + msg_hardware();continue; /* don't reply */ + + case SYS_SIG: + msg_sig_stop(); continue; /* don't reply */ + + default: + r = EINVAL; dprint("%s: %d uncaught msg!\n", mess.m_type ,drv.DriverName); + break; + } + /* Finally, prepare and send the reply message. */ + reply(TASK_REPLY, caller, proc_nr, r); + } + +} + +PRIVATE int init_driver(void) { + u32_t i; char irq; + static int executed = 0; + sub_dev_t* sub_dev_ptr; + + /* init variables, get dma buffers */ + for (i = 0; i < drv.NrOfSubDevices; i++) { + + sub_dev_ptr = &sub_dev[i]; + + sub_dev_ptr->Opened = FALSE; + sub_dev_ptr->DmaBusy = FALSE; + sub_dev_ptr->DmaMode = NO_DMA; + sub_dev_ptr->DmaReadNext = 0; + sub_dev_ptr->DmaFillNext = 0; + sub_dev_ptr->DmaLength = 0; + sub_dev_ptr->BufReadNext = 0; + sub_dev_ptr->BufFillNext = 0; + sub_dev_ptr->RevivePending = FALSE; + sub_dev_ptr->OutOfData = FALSE; + sub_dev_ptr->Nr = i; + } + + /* initialize hardware*/ + if (drv_init_hw() != OK) { + error("%s: Could not initialize hardware\n", drv.DriverName, 0); + return EIO; + } + + /* get irq from device driver...*/ + if (drv_get_irq(&irq) != OK) { + error("%s: init driver couldn't get IRQ", drv.DriverName, i); + return EIO; + } + /* todo: execute the rest of this function only once + we don't want to set irq policy twice */ + if (executed) return OK; + executed = TRUE; + + /* ...and register interrupt vector */ + if ((i=sys_irqsetpolicy(irq, 0, &irq_hook_id )) != OK){ + error("%s: init driver couldn't set IRQ policy", drv.DriverName, i); + return EIO; + } + irq_hook_set = TRUE; /* now msg_sig_stop knows it must unregister policy*/ + return OK; +} + + +PRIVATE int msg_open (int minor_dev_nr) { + int r, read_chan, write_chan, io_ctl; + special_file_t* special_file_ptr; + + dprint("%s: msg_open() special file %d\n", drv.DriverName, minor_dev_nr); + + special_file_ptr = get_special_file(minor_dev_nr); + if(special_file_ptr == NULL) { + return EIO; + } + + read_chan = special_file_ptr->read_chan; + write_chan = special_file_ptr->write_chan; + io_ctl = special_file_ptr->io_ctl; + + if (read_chan==NO_CHANNEL && write_chan==NO_CHANNEL && io_ctl==NO_CHANNEL) { + error("%s: No channel specified for minor device!\n", drv.DriverName, minor_dev_nr); + return EIO; + } + if (read_chan == write_chan && read_chan != NO_CHANNEL) { + error("%s: Read and write channels are equal!\n", drv.DriverName,minor_dev_nr); + return EIO; + } + /* init driver */ + if (!device_available) { + if (init_driver() != OK) { + error("%s: Couldn't init driver!\n", drv.DriverName, minor_dev_nr); + return EIO; + } else { + device_available = TRUE; + } + } + /* open the sub devices specified in the interface header file */ + if (write_chan != NO_CHANNEL) { + /* open sub device for writing */ + if (open_sub_dev(write_chan, DEV_WRITE) != OK) return EIO; + } + if (read_chan != NO_CHANNEL) { + if (open_sub_dev(read_chan, DEV_READ) != OK) return EIO; + } + if (read_chan == io_ctl || write_chan == io_ctl) { + /* io_ctl is already opened because it's the same as read or write */ + return OK; /* we're done */ + } + if (io_ctl != NO_CHANNEL) { /* Ioctl differs from read/write channels, */ + r = open_sub_dev(io_ctl, NO_DMA); /* open it explicitly */ + if (r != OK) return EIO; + } + return OK; +} + + +PRIVATE int open_sub_dev(int sub_dev_nr, int dma_mode) { + sub_dev_t* sub_dev_ptr; int i; + sub_dev_ptr = &sub_dev[sub_dev_nr]; + + /* Only one open at a time per sub device */ + if (sub_dev_ptr->Opened) { + error("%s: Sub device %d is already opened\n", drv.DriverName, sub_dev_nr); + return EBUSY; + } + if (sub_dev_ptr->DmaBusy) { + error("%s: Sub device %d is still busy\n", drv.DriverName, sub_dev_nr); + return EBUSY; + } + /* Setup variables */ + sub_dev_ptr->Opened = TRUE; + sub_dev_ptr->DmaReadNext = 0; + sub_dev_ptr->DmaFillNext = 0; + sub_dev_ptr->DmaLength = 0; + sub_dev_ptr->DmaMode = dma_mode; + sub_dev_ptr->BufReadNext = 0; + sub_dev_ptr->BufFillNext = 0; + sub_dev_ptr->BufLength = 0; + sub_dev_ptr->RevivePending = FALSE; + sub_dev_ptr->OutOfData = TRUE; + + /* arrange DMA */ + if (dma_mode != NO_DMA) { /* sub device uses DMA */ + /* allocate dma buffer and extra buffer space + and configure sub device for dma */ + if (init_buffers(sub_dev_ptr) != OK ) return EIO; + } + return OK; +} + + +PRIVATE int msg_close(int minor_dev_nr) { + + int r, read_chan, write_chan, io_ctl; + special_file_t* special_file_ptr; + + dprint("%s: msg_close() minor device %d\n", drv.DriverName, minor_dev_nr); + + special_file_ptr = get_special_file(minor_dev_nr); + if(special_file_ptr == NULL) { + return EIO; + } + + read_chan = special_file_ptr->read_chan; + write_chan = special_file_ptr->write_chan; + io_ctl = special_file_ptr->io_ctl; + + /* close all sub devices */ + if (write_chan != NO_CHANNEL) { + if (close_sub_dev(write_chan) != OK) r = EIO; + } + if (read_chan != NO_CHANNEL) { + if (close_sub_dev(read_chan) != OK) r = EIO; + } + if (read_chan == io_ctl || write_chan == io_ctl) { + /* io_ctl is already closed because it's the same as read or write */ + return r; /* we're done */ + } + /* Ioctl differs from read/write channels... */ + if (io_ctl != NO_CHANNEL) { + if (close_sub_dev(io_ctl) != OK) r = EIO; /* ...close it explicitly */ + } + return r; +} + + +PRIVATE int close_sub_dev(int sub_dev_nr) { + sub_dev_t *sub_dev_ptr; + sub_dev_ptr = &sub_dev[sub_dev_nr]; + + if (sub_dev_ptr->DmaMode == DEV_WRITE && !sub_dev_ptr->OutOfData) { + /* do nothing, still data in buffers that has to be transferred */ + sub_dev_ptr->Opened = FALSE; /* keep DMA busy */ + return OK; + } + if (sub_dev_ptr->DmaMode == NO_DMA) { + /* do nothing, there is no dma going on */ + sub_dev_ptr->Opened = FALSE; + return OK; + } + sub_dev_ptr->Opened = FALSE; + sub_dev_ptr->DmaBusy = FALSE; + /* stop the device */ + drv_stop(sub_dev_ptr->Nr); + /* free the buffers */ + free(sub_dev_ptr->DmaBuf); + free(sub_dev_ptr->ExtraBuf); +} + + +PRIVATE int msg_ioctl(message *m_ptr) +{ + int status, len, chan; + phys_bytes user_phys; + sub_dev_t *sub_dev_ptr; + special_file_t* special_file_ptr; + + dprint("%s: msg_ioctl() device %d\n", drv.DriverName, m_ptr->DEVICE); + + special_file_ptr = get_special_file(m_ptr->DEVICE); + if(special_file_ptr == NULL) { + return EIO; + } + + chan = special_file_ptr->io_ctl; + + if (chan == NO_CHANNEL) { + error("%s: No io control channel specified!\n", drv.DriverName); + return EIO; + } + /* get pointer to sub device data */ + sub_dev_ptr = &sub_dev[chan]; + + if(!sub_dev_ptr->Opened) { + error("%s: io control impossible - not opened!\n", drv.DriverName); + return EIO; + } + + /* this is a hack...todo: may we intercept reset calls? */ + if(m_ptr->REQUEST == DSPIORESET) { + device_available = FALSE; + } + /* this is confusing, _IOC_OUT bit means that there is incoming data */ + if (m_ptr->REQUEST & _IOC_OUT) { /* if there is data for us, copy it */ + len = io_ctl_length(m_ptr->REQUEST); + sys_vircopy(m_ptr->PROC_NR, D, + (vir_bytes)m_ptr->ADDRESS, SELF, D, + (vir_bytes)io_ctl_buf, len); + } + + /* all ioctl's are passed to the device specific part of the driver */ + status = drv_io_ctl(m_ptr->REQUEST, (void *)io_ctl_buf, &len, chan); + + /* _IOC_IN bit -> user expects data */ + if (status == OK && m_ptr->REQUEST & _IOC_IN) { + /* copy result back to user */ + sys_vircopy(SELF, D, (vir_bytes)io_ctl_buf, m_ptr->PROC_NR, D, (vir_bytes)m_ptr->ADDRESS, len); + } + return status; +} + + +PRIVATE void msg_write(message *m_ptr) +{ + int s, chan; sub_dev_t *sub_dev_ptr; + special_file_t* special_file_ptr; + + dprint("%s: msg_write() device %d\n", drv.DriverName, m_ptr->DEVICE); + + special_file_ptr = get_special_file(m_ptr->DEVICE); + chan = special_file_ptr->write_chan; + + if (chan == NO_CHANNEL) { + error("%s: No write channel specified!\n", drv.DriverName); + reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, EIO); + return; + } + /* get pointer to sub device data */ + sub_dev_ptr = &sub_dev[chan]; + + if (!sub_dev_ptr->DmaBusy) { /* get fragment size on first write */ + if (drv_get_frag_size(&(sub_dev_ptr->FragSize), sub_dev_ptr->Nr) != OK){ + error("%s; Failed to get fragment size!\n", drv.DriverName, 0); + return; + } + } + if(m_ptr->COUNT != sub_dev_ptr->FragSize) { + error("Fragment size does not match user's buffer length\n"); + reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, EINVAL); + return; + } + /* if we are busy with something else than writing, return EBUSY */ + if(sub_dev_ptr->DmaBusy && sub_dev_ptr->DmaMode != DEV_WRITE) { + error("Already busy with something else then writing\n"); + reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, EBUSY); + return; + } + /* unblock the FileSystem, but keep user process blocked until REVIVE*/ + reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, SUSPEND); + sub_dev_ptr->RevivePending = TRUE; + sub_dev_ptr->ReviveProcNr = m_ptr->PROC_NR; + sub_dev_ptr->UserBuf = m_ptr->ADDRESS; + sub_dev_ptr->NotifyProcNr = m_ptr->m_source; + + data_from_user(sub_dev_ptr); + + if(!sub_dev_ptr->DmaBusy) { /* Dma tranfer not yet started */ + dprint("starting audio device\n"); + get_started(sub_dev_ptr); + sub_dev_ptr->DmaMode = DEV_WRITE; /* Dma mode is writing */ + } +} + + +PRIVATE void msg_read(message *m_ptr) +{ + int s, chan; sub_dev_t *sub_dev_ptr; + special_file_t* special_file_ptr; + + dprint("%s: msg_read() device %d\n", drv.DriverName, m_ptr->DEVICE); + + special_file_ptr = get_special_file(m_ptr->DEVICE); + chan = special_file_ptr->read_chan; + + if (chan == NO_CHANNEL) { + error("%s: No read channel specified!\n", drv.DriverName); + reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, EIO); + return; + } + /* get pointer to sub device data */ + sub_dev_ptr = &sub_dev[chan]; + + if (!sub_dev_ptr->DmaBusy) { /* get fragment size on first read */ + if (drv_get_frag_size(&(sub_dev_ptr->FragSize), sub_dev_ptr->Nr) != OK){ + error("%s: Could not retrieve fragment size!\n", drv.DriverName); + reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, EIO); + return; + } + } + if(m_ptr->COUNT != sub_dev_ptr->FragSize) { + reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, EINVAL); + error("fragment size does not match message size\n"); + return; + } + /* if we are busy with something else than reading, reply EBUSY */ + if(sub_dev_ptr->DmaBusy && sub_dev_ptr->DmaMode != DEV_READ) { + reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, EBUSY); + return; + } + /* unblock the FileSystem, but keep user process blocked until REVIVE*/ + reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, SUSPEND); + sub_dev_ptr->RevivePending = TRUE; + sub_dev_ptr->ReviveProcNr = m_ptr->PROC_NR; + sub_dev_ptr->UserBuf = m_ptr->ADDRESS; + sub_dev_ptr->NotifyProcNr = m_ptr->m_source; + + if(!sub_dev_ptr->DmaBusy) { /* Dma tranfer not yet started */ + get_started(sub_dev_ptr); + sub_dev_ptr->DmaMode = DEV_READ; /* Dma mode is reading */ + return; /* no need to get data from DMA buffer at this point */ + } + /* check if data is available and possibly fill user's buffer */ + data_to_user(sub_dev_ptr); +} + + +PRIVATE void msg_hardware(void) { + + u32_t i; + int j = 0; + + dprint("%s: handling hardware message\n", drv.DriverName); + + /* while we have an interrupt */ + while ( drv_int_sum()) { + /* loop over all sub devices */ + for ( i = 0; i < drv.NrOfSubDevices; i++) { + /* if interrupt from sub device and Dma transfer + was actually busy, take care of business */ + if( drv_int(i) && sub_dev[i].DmaBusy ) { + if (sub_dev[i].DmaMode == DEV_WRITE) handle_int_write(i); + if (sub_dev[i].DmaMode == DEV_READ) handle_int_read(i); + } + } + } +} + + +PRIVATE void msg_status(message *m_ptr) +{ + int i; + + dprint("got a status message\n"); + for (i = 0; i < drv.NrOfSubDevices; i++) { + + if(sub_dev[i].ReadyToRevive) + { + m_ptr->m_type = DEV_REVIVE; /* build message */ + m_ptr->REP_PROC_NR = sub_dev[i].ReviveProcNr; + m_ptr->REP_STATUS = sub_dev[i].ReviveStatus; + send(m_ptr->m_source, m_ptr); /* send the message */ + + /* reset variables */ + sub_dev[i].ReadyToRevive = FALSE; + sub_dev[i].RevivePending = 0; + + return; /* stop after one mess, + file system will get back for other processes */ + } + } + m_ptr->m_type = DEV_NO_STATUS; + m_ptr->REP_STATUS = 0; + send(m_ptr->m_source, m_ptr); /* send DEV_NO_STATUS message */ +} + + +PRIVATE void msg_sig_stop(void) +{ + int i; char irq; + for (i = 0; i < drv.NrOfSubDevices; i++) { + drv_stop(i); /* stop all sub devices */ + } + if (irq_hook_set) { + if (sys_irqdisable(&irq_hook_id) != OK) { + error("Could not disable IRQ\n"); + } + /* get irq from device driver*/ + if (drv_get_irq(&irq) != OK) { + error("Msg SIG_STOP Couldn't get IRQ"); + } + /* remove the policy */ + if (sys_irqrmpolicy(irq, &irq_hook_id) != OK) { + error("%s: Could not disable IRQ\n",drv.DriverName); + } + } +} + + +/* handle interrupt for specified sub device; DmaMode == DEV_WRITE*/ +PRIVATE void handle_int_write(int sub_dev_nr) +{ + sub_dev_t *sub_dev_ptr; + int r; + + sub_dev_ptr = &sub_dev[sub_dev_nr]; + + dprint("Finished playing dma[%d] ", sub_dev_ptr->DmaReadNext); + sub_dev_ptr->DmaReadNext = (sub_dev_ptr->DmaReadNext + 1) % sub_dev_ptr->NrOfDmaFragments; + sub_dev_ptr->DmaLength -= 1; + + if (sub_dev_ptr->BufLength != 0) { /* Data in extra buf, copy to Dma buf */ + + dprint(" buf[%d] -> dma[%d] ", sub_dev_ptr->BufReadNext, sub_dev_ptr->DmaFillNext); + memcpy(sub_dev_ptr->DmaPtr + sub_dev_ptr->DmaFillNext * sub_dev_ptr->FragSize, + sub_dev_ptr->ExtraBuf + sub_dev_ptr->BufReadNext * sub_dev_ptr->FragSize, + sub_dev_ptr->FragSize); + + sub_dev_ptr->BufReadNext = (sub_dev_ptr->BufReadNext + 1) % sub_dev_ptr->NrOfExtraBuffers; + sub_dev_ptr->DmaFillNext = (sub_dev_ptr->DmaFillNext + 1) % sub_dev_ptr->NrOfDmaFragments; + + sub_dev_ptr->BufLength -= 1; + sub_dev_ptr->DmaLength += 1; + } + + /* space became available, possibly copy new data from user */ + data_from_user(sub_dev_ptr); + + if(sub_dev_ptr->DmaLength == 0) { /* Dma buffer empty, stop Dma transfer */ + + sub_dev_ptr->OutOfData = TRUE; /* we're out of data */ + dprint("No more work...!\n"); + if (!sub_dev_ptr->Opened) { + close_sub_dev(sub_dev_ptr->Nr); + dprint("Stopping sub device %d\n", sub_dev_ptr->Nr); + return; + } + dprint("Pausing sub device %d\n",sub_dev_ptr->Nr); + drv_pause(sub_dev_ptr->Nr); + return; + } + + dprint("\n"); + + /* confirm and reenable interrupt from this sub dev */ + drv_reenable_int(sub_dev_nr); + /* reenable irq_hook*/ + if ((r=sys_irqenable(&irq_hook_id)) != OK) { + error("%s Couldn't enable IRQ\n", drv.DriverName); + } +} + + +/* handle interrupt for specified sub device; DmaMode == DEV_READ */ +PRIVATE void handle_int_read(int sub_dev_nr) +{ + sub_dev_t *sub_dev_ptr; int r,i; + + sub_dev_ptr = &sub_dev[sub_dev_nr]; + + dprint("Device filled dma[%d]\n", sub_dev_ptr->DmaFillNext); + sub_dev_ptr->DmaLength += 1; + sub_dev_ptr->DmaFillNext = (sub_dev_ptr->DmaFillNext + 1) % sub_dev_ptr->NrOfDmaFragments; + + /* possibly copy data to user (if it is waiting for us) */ + data_to_user(sub_dev_ptr); + + if (sub_dev_ptr->DmaLength == sub_dev_ptr->NrOfDmaFragments) { /* if dma buffer full */ + + if (sub_dev_ptr->BufLength == sub_dev_ptr->NrOfExtraBuffers) { + error("All buffers full, we have a problem.\n"); + drv_stop(sub_dev_nr); /* stop the sub device */ + sub_dev_ptr->DmaBusy = FALSE; + sub_dev_ptr->ReviveStatus = 0; /* no data for user, this is a sad story */ + sub_dev_ptr->ReadyToRevive = TRUE; /* wake user up */ + return; + } + else { /* dma full, still room in extra buf; copy from dma to extra buf */ + dprint("dma full: going to copy buf[%d] <- dma[%d]\n", sub_dev_ptr->BufFillNext, + sub_dev_ptr->DmaReadNext); + memcpy(sub_dev_ptr->ExtraBuf + sub_dev_ptr->BufFillNext * sub_dev_ptr->FragSize, + sub_dev_ptr->DmaPtr + sub_dev_ptr->DmaReadNext * sub_dev_ptr->FragSize, + sub_dev_ptr->FragSize); + sub_dev_ptr->DmaLength -= 1; + sub_dev_ptr->DmaReadNext = (sub_dev_ptr->DmaReadNext + 1) % sub_dev_ptr->NrOfDmaFragments; + sub_dev_ptr->BufFillNext = (sub_dev_ptr->BufFillNext + 1) % sub_dev_ptr->NrOfExtraBuffers; + } + } + /* confirm interrupt, and reenable interrupt from this sub dev*/ + drv_reenable_int(sub_dev_ptr->Nr); + + /* reenable irq_hook*/ + if ((r=sys_irqenable(&irq_hook_id)) != OK) { + error("%s: Couldn't reenable IRQ", drv.DriverName); + } +} + + +PRIVATE int get_started(sub_dev_t *sub_dev_ptr) { + u32_t i;char c; + + /* enable interrupt messages from MINIX */ + if ((i=sys_irqenable(&irq_hook_id)) != OK) { + error("%s: Couldn't enable IRQs",drv.DriverName); + return EIO; + } + /* let the lower part of the driver start the device */ + if (drv_start(sub_dev_ptr->Nr, sub_dev_ptr->DmaMode) != OK) { + error("%s: Could not start device %d\n", drv.DriverName, sub_dev_ptr->Nr); + } + + sub_dev_ptr->DmaBusy = TRUE; /* Dma is busy from now on */ + sub_dev_ptr->DmaReadNext = 0; + return OK; +} + + +PRIVATE void data_from_user(sub_dev_t *subdev) { + + if (subdev->DmaLength == subdev->NrOfDmaFragments && + subdev->BufLength == subdev->NrOfExtraBuffers) return; /* no space */ + + if (!subdev->RevivePending) return; /* no new data waiting to be copied */ + + if (subdev->RevivePending && + subdev->ReadyToRevive) return; /* we already got this data */ + + + if (subdev->DmaLength < subdev->NrOfDmaFragments) { /* room in dma buf */ + + sys_datacopy(subdev->ReviveProcNr, + (vir_bytes)subdev->UserBuf, + SELF, + (vir_bytes)subdev->DmaPtr + subdev->DmaFillNext * subdev->FragSize, + (phys_bytes)subdev->FragSize); + + dprint(" user -> dma[%d]\n", subdev->DmaFillNext); + subdev->DmaLength += 1; + subdev->DmaFillNext = (subdev->DmaFillNext + 1) % subdev->NrOfDmaFragments; + + } else { /* room in extra buf */ + + sys_datacopy(subdev->ReviveProcNr, + (vir_bytes)subdev->UserBuf, + SELF, + (vir_bytes)subdev->ExtraBuf + subdev->BufFillNext * subdev->FragSize, + (phys_bytes)subdev->FragSize); + + dprint(" user -> buf[%d]\n", subdev->BufFillNext); + subdev->BufLength += 1; + + subdev->BufFillNext = (subdev->BufFillNext + 1) % subdev->NrOfExtraBuffers; + + } + if(subdev->OutOfData) { /* if device paused (because of lack of data) */ + subdev->OutOfData = FALSE; + drv_reenable_int(subdev->Nr); + /* reenable irq_hook*/ + if ((sys_irqenable(&irq_hook_id)) != OK) { + error("%s: Couldn't enable IRQ", drv.DriverName); + } + drv_resume(subdev->Nr); /* resume resume the sub device */ + + } + + subdev->ReviveStatus = subdev->FragSize; + subdev->ReadyToRevive = TRUE; + notify(subdev->NotifyProcNr); +} + + +PRIVATE void data_to_user(sub_dev_t *sub_dev_ptr) +{ + if (!sub_dev_ptr->RevivePending) return; /* nobody is wating for data */ + if (sub_dev_ptr->ReadyToRevive) return; /* we already filled user's buffer */ + if (sub_dev_ptr->BufLength == 0 && sub_dev_ptr->DmaLength == 0) return; + /* no data for user */ + + if(sub_dev_ptr->BufLength != 0) { /* data in extra buffer available */ + + sys_datacopy(SELF, + (vir_bytes)sub_dev_ptr->ExtraBuf + sub_dev_ptr->BufReadNext * sub_dev_ptr->FragSize, + sub_dev_ptr->ReviveProcNr, + (vir_bytes)sub_dev_ptr->UserBuf, + (phys_bytes)sub_dev_ptr->FragSize); + + dprint(" copied buf[%d] to user\n", sub_dev_ptr->BufReadNext); + /* adjust the buffer status variables */ + sub_dev_ptr->BufReadNext = (sub_dev_ptr->BufReadNext + 1) % sub_dev_ptr->NrOfExtraBuffers; + sub_dev_ptr->BufLength -= 1; + + } else { /* extra buf empty, but data in dma buf*/ + + sys_datacopy(SELF, + (vir_bytes)sub_dev_ptr->DmaPtr + sub_dev_ptr->DmaReadNext * sub_dev_ptr->FragSize, + sub_dev_ptr->ReviveProcNr, + (vir_bytes)sub_dev_ptr->UserBuf, + (phys_bytes)sub_dev_ptr->FragSize); + + dprint(" copied dma[%d] to user\n", sub_dev_ptr->DmaReadNext); + /* adjust the buffer status variables */ + sub_dev_ptr->DmaReadNext = (sub_dev_ptr->DmaReadNext + 1) % sub_dev_ptr->NrOfDmaFragments; + sub_dev_ptr->DmaLength -= 1; + } + sub_dev_ptr->ReviveStatus = sub_dev_ptr->FragSize; + sub_dev_ptr->ReadyToRevive = TRUE; /* drv_status will send REVIVE mess to FS*/ + notify(sub_dev_ptr->NotifyProcNr); /* notify the File Systam to make it + send DEV_STATUS messages*/ +} + + +PRIVATE int init_buffers(sub_dev_t *sub_dev_ptr) { +#if (CHIP == INTEL) + unsigned left; + u32_t i; + + /* allocate dma buffer space */ + if (!(sub_dev_ptr->DmaBuf = malloc(sub_dev_ptr->DmaSize + 64 * 1024))) { + error("%s: failed to allocate dma buffer for channel %d\n", drv.DriverName,i); + return EIO; + } + /* allocate extra buffer space */ + if (!(sub_dev_ptr->ExtraBuf = malloc(sub_dev_ptr->NrOfExtraBuffers * sub_dev_ptr->DmaSize / sub_dev_ptr->NrOfDmaFragments))) { + error("%s failed to allocate extra buffer for channel %d\n", drv.DriverName,i); + return EIO; + } + + sub_dev_ptr->DmaPtr = sub_dev_ptr->DmaBuf; + i = sys_umap(SELF, D, + (vir_bytes) sub_dev_ptr->DmaBuf, + (phys_bytes) sizeof(sub_dev_ptr->DmaBuf), + &(sub_dev_ptr->DmaPhys)); + + if (i != OK) { + return EIO; + } + if((left = dma_bytes_left(sub_dev_ptr->DmaPhys)) < sub_dev_ptr->DmaSize) { + /* First half of buffer crosses a 64K boundary, can't DMA into that */ + sub_dev_ptr->DmaPtr += left; + sub_dev_ptr->DmaPhys += left; + } + /* write the physical dma address and size to the device */ + drv_set_dma(sub_dev_ptr->DmaPhys, sub_dev_ptr->DmaSize, sub_dev_ptr->Nr); + return OK; + +#else /* CHIP != INTEL */ + error("%s: init_buffer() failed, CHIP != INTEL", drv.DriverName); + return EIO; +#endif /* CHIP == INTEL */ +} + + +PRIVATE void reply(int code, int replyee, int process, int status) +{ + message m; + + m.m_type = code; /* TASK_REPLY or REVIVE */ + m.REP_STATUS = status; /* result of device operation */ + m.REP_PROC_NR = process; /* which user made the request */ + send(replyee, &m); +} + + +PRIVATE int io_ctl_length(int io_request) { + io_request >>= 16; + return io_request & _IOCPARM_MASK; +} + + +PRIVATE special_file_t* get_special_file(int minor_dev_nr) { + int i; + + for(i = 0; i < drv.NrOfSpecialFiles; i++) { + if(special_file[i].minor_dev_nr == minor_dev_nr) { + return &special_file[i]; + } + } + + error("%s: No subdevice specified for minor device %d!\n", drv.DriverName, minor_dev_nr); + + return NULL; +} \ No newline at end of file diff --git a/drivers/audio/framework/audio_fw.h b/drivers/audio/framework/audio_fw.h new file mode 100755 index 000000000..bad32803d --- /dev/null +++ b/drivers/audio/framework/audio_fw.h @@ -0,0 +1,96 @@ +#ifndef AUDIO_FW_H +#define AUDIO_FW_H + +#include "../../drivers.h" +#include + + +/* change '(void)' to 'printf' to print debug info and error messages */ +#define dprint (void) +#define error printf + + + +_PROTOTYPE( void main, (void) ); + +_PROTOTYPE( int drv_init, (void) ); +_PROTOTYPE( int drv_init_hw, (void) ); +_PROTOTYPE( int drv_reset, (void) ); +_PROTOTYPE( int drv_start, (int sub_dev, int DmaMode) ); +_PROTOTYPE( int drv_stop, (int sub_dev) ); +_PROTOTYPE( int drv_set_dma, (u32_t dma, u32_t length, int chan) ); +_PROTOTYPE( int drv_reenable_int, (int chan) ); +_PROTOTYPE( int drv_int_sum, (void) ); +_PROTOTYPE( int drv_int, (int sub_dev) ); +_PROTOTYPE( int drv_pause, (int chan) ); +_PROTOTYPE( int drv_resume, (int chan) ); +_PROTOTYPE( int drv_io_ctl, (int request, void * val, int * len, int sub_dev) ); +_PROTOTYPE( int drv_get_irq, (char *irq) ); +_PROTOTYPE( int drv_get_frag_size, (u32_t *frag_size, int sub_dev) ); + + + +/* runtime status fields */ +typedef struct { + int readable; + int writable; + int DmaSize; + int NrOfDmaFragments; + int MinFragmentSize; + int NrOfExtraBuffers; + int Nr; /* sub device number */ + int Opened; /* sub device opened */ + int DmaBusy; /* is dma busy? */ + int DmaMode; /* DEV_WRITE / DEV_READ */ + int DmaReadNext; /* current dma buffer */ + int DmaFillNext; /* next dma buffer to fill */ + int DmaLength; + int BufReadNext; /* start of extra circular buffer */ + int BufFillNext; /* end of extra circular buffer */ + int BufLength; + int RevivePending; /* process waiting for this dev? */ + int ReviveStatus; /* return val when proc unblocked */ + int ReviveProcNr; /* the process to unblock */ + void *UserBuf; /* address of user's data buffer */ + int ReadyToRevive; /* are we ready to revive process?*/ + int NotifyProcNr; /* process to send notify to (FS) */ + u32_t FragSize; /* dma fragment size */ + char *DmaBuf; /* the dma buffer; extra space for + page alignment */ + phys_bytes DmaPhys; /* physical address of dma buffer */ + char* DmaPtr; /* pointer to aligned dma buffer */ + int OutOfData; /* all buffers empty? */ + char *ExtraBuf; /* don't use extra buffer;just + declare a pointer to supress + error messages */ +} sub_dev_t; + +typedef struct { + int minor_dev_nr; + int read_chan; + int write_chan; + int io_ctl; +} special_file_t; + +typedef struct { + char* DriverName; + int NrOfSubDevices; + int NrOfSpecialFiles; +} drv_t; + +EXTERN drv_t drv; +EXTERN sub_dev_t sub_dev[]; +EXTERN special_file_t special_file[]; + +/* Number of bytes you can DMA before hitting a 64K boundary: */ +#define dma_bytes_left(phys) \ + ((unsigned) (sizeof(int) == 2 ? 0 : 0x10000) - (unsigned) ((phys) & 0xFFFF)) + +#define NO_CHANNEL -1 + +#define TRUE 1 +#define FALSE 0 +#define NO_DMA 0 + + +#endif /* AUDIO_FW_H */ diff --git a/drivers/audio/sb16/.depend b/drivers/audio/sb16/.depend new file mode 100755 index 000000000..418f83b1b --- /dev/null +++ b/drivers/audio/sb16/.depend @@ -0,0 +1,68 @@ + +mixer.o: ../framework/../../drivers.h +mixer.o: ../framework/audio_fw.h +mixer.o: /usr/include/ansi.h +mixer.o: /usr/include/errno.h +mixer.o: /usr/include/ibm/bios.h +mixer.o: /usr/include/ibm/interrupt.h +mixer.o: /usr/include/ibm/ports.h +mixer.o: /usr/include/limits.h +mixer.o: /usr/include/minix/bitmap.h +mixer.o: /usr/include/minix/callnr.h +mixer.o: /usr/include/minix/com.h +mixer.o: /usr/include/minix/config.h +mixer.o: /usr/include/minix/const.h +mixer.o: /usr/include/minix/devio.h +mixer.o: /usr/include/minix/dmap.h +mixer.o: /usr/include/minix/ioctl.h +mixer.o: /usr/include/minix/ipc.h +mixer.o: /usr/include/minix/sound.h +mixer.o: /usr/include/minix/sys_config.h +mixer.o: /usr/include/minix/syslib.h +mixer.o: /usr/include/minix/sysutil.h +mixer.o: /usr/include/minix/type.h +mixer.o: /usr/include/signal.h +mixer.o: /usr/include/stddef.h +mixer.o: /usr/include/stdlib.h +mixer.o: /usr/include/string.h +mixer.o: /usr/include/sys/dir.h +mixer.o: /usr/include/sys/ioc_sound.h +mixer.o: /usr/include/sys/types.h +mixer.o: /usr/include/unistd.h +mixer.o: mixer.c +mixer.o: mixer.h +mixer.o: sb16.h + +sb16.o: ../framework/../../drivers.h +sb16.o: ../framework/audio_fw.h +sb16.o: /usr/include/ansi.h +sb16.o: /usr/include/errno.h +sb16.o: /usr/include/ibm/bios.h +sb16.o: /usr/include/ibm/interrupt.h +sb16.o: /usr/include/ibm/ports.h +sb16.o: /usr/include/limits.h +sb16.o: /usr/include/minix/bitmap.h +sb16.o: /usr/include/minix/callnr.h +sb16.o: /usr/include/minix/com.h +sb16.o: /usr/include/minix/config.h +sb16.o: /usr/include/minix/const.h +sb16.o: /usr/include/minix/devio.h +sb16.o: /usr/include/minix/dmap.h +sb16.o: /usr/include/minix/ioctl.h +sb16.o: /usr/include/minix/ipc.h +sb16.o: /usr/include/minix/sound.h +sb16.o: /usr/include/minix/sys_config.h +sb16.o: /usr/include/minix/syslib.h +sb16.o: /usr/include/minix/sysutil.h +sb16.o: /usr/include/minix/type.h +sb16.o: /usr/include/signal.h +sb16.o: /usr/include/stddef.h +sb16.o: /usr/include/stdlib.h +sb16.o: /usr/include/string.h +sb16.o: /usr/include/sys/dir.h +sb16.o: /usr/include/sys/ioc_sound.h +sb16.o: /usr/include/sys/types.h +sb16.o: /usr/include/unistd.h +sb16.o: mixer.h +sb16.o: sb16.c +sb16.o: sb16.h diff --git a/drivers/audio/sb16/Makefile b/drivers/audio/sb16/Makefile new file mode 100755 index 000000000..ffcdd3978 --- /dev/null +++ b/drivers/audio/sb16/Makefile @@ -0,0 +1,41 @@ +# Makefile for the Sound Blaster 16 driver (SB16) + +# directories +u = /usr +i = $u/include +s = $i/sys +m = $i/minix +b = $i/ibm +d = .. + +# programs, flags, etc. +CC = exec cc +CFLAGS = -I$i +LDFLAGS = -i +LIBS = -lsys -lsysutil + + +# build local binary +all build: sb16 + +sb16: sb16.o mixer.o audio_fw.o + $(CC) -o $@ $(LDFLAGS) sb16.o mixer.o audio_fw.o $(LIBS) + +audio_fw.o: ../framework/audio_fw.c ../framework/audio_fw.h + $(CC) -c ../framework/audio_fw.c + +# install with other drivers +install: /usr/sbin/sb16 +/usr/sbin/sb16: sb16 + install -o root -S 512k -c $? $@ + +# clean up local files +clean: + rm -f *.o *.bak sb16 + +depend: + /usr/bin/mkdep "$(CC) -E $(CPPFLAGS)" *.c > .depend + +# Include generated dependencies. +include .depend + diff --git a/drivers/audio/sb16/mixer.c b/drivers/audio/sb16/mixer.c new file mode 100755 index 000000000..770af03ea --- /dev/null +++ b/drivers/audio/sb16/mixer.c @@ -0,0 +1,253 @@ +#include "sb16.h" +#include "mixer.h" + + + +FORWARD _PROTOTYPE( int get_set_volume, (struct volume_level *level, int flag)); +FORWARD _PROTOTYPE( int get_set_input, (struct inout_ctrl *input, int flag, int channel)); +FORWARD _PROTOTYPE( int get_set_output, (struct inout_ctrl *output, int flag)); + + + + +/*=========================================================================* + * mixer_ioctl + *=========================================================================*/ +PUBLIC int mixer_ioctl(int request, void *val, int *len) { + int status; + + switch(request) { + case MIXIOGETVOLUME: status = get_set_volume(val, 0); break; + case MIXIOSETVOLUME: status = get_set_volume(val, 1); break; + case MIXIOGETINPUTLEFT: status = get_set_input(val, 0, 0); break; + case MIXIOGETINPUTRIGHT: status = get_set_input(val, 0, 1); break; + case MIXIOGETOUTPUT: status = get_set_output(val, 0); break; + case MIXIOSETINPUTLEFT: status = get_set_input(val, 1, 0); break; + case MIXIOSETINPUTRIGHT: status = get_set_input(val, 1, 1); break; + case MIXIOSETOUTPUT: status = get_set_output(val, 1); break; + default: status = ENOTTY; + } + + return status; +} + + +/*=========================================================================* + * mixer_init + *=========================================================================*/ +PUBLIC int mixer_init() { + /* Try to detect the mixer by writing to MIXER_DAC_LEVEL if the + * value written can be read back the mixer is there + */ + + mixer_set(MIXER_DAC_LEVEL, 0x10); /* write something to it */ + if(mixer_get(MIXER_DAC_LEVEL) != 0x10) { + dprint("sb16: Mixer not detected\n"); + return EIO; + } + + /* Enable Automatic Gain Control */ + mixer_set(MIXER_AGC, 0x01); + + dprint("Mixer detected\n"); + + return OK; +} + + + +/*=========================================================================* + * get_set_volume * + *=========================================================================*/ +PRIVATE int get_set_volume(struct volume_level *level, int flag) { + int cmd_left, cmd_right, shift, max_level; + + shift = 3; + max_level = 0x1F; + switch(level->device) { + case Master: + cmd_left = MIXER_MASTER_LEFT; + cmd_right = MIXER_MASTER_RIGHT; + break; + case Dac: + cmd_left = MIXER_DAC_LEFT; + cmd_right = MIXER_DAC_RIGHT; + break; + case Fm: + cmd_left = MIXER_FM_LEFT; + cmd_right = MIXER_FM_RIGHT; + break; + case Cd: + cmd_left = MIXER_CD_LEFT; + cmd_right = MIXER_CD_RIGHT; + break; + case Line: + cmd_left = MIXER_LINE_LEFT; + cmd_right = MIXER_LINE_RIGHT; + break; + case Mic: + cmd_left = cmd_right = MIXER_MIC_LEVEL; + break; + case Speaker: + cmd_left = cmd_right = MIXER_PC_LEVEL; + shift = 6; + max_level = 0x03; + break; + case Treble: + cmd_left = MIXER_TREBLE_LEFT; + cmd_right = MIXER_TREBLE_RIGHT; + shift = 4; + max_level = 0x0F; + break; + case Bass: + cmd_left = MIXER_BASS_LEFT; + cmd_right = MIXER_BASS_RIGHT; + shift = 4; + max_level = 0x0F; + break; + default: + return EINVAL; + } + + if(flag) { /* Set volume level */ + if(level->right < 0) level->right = 0; + else if(level->right > max_level) level->right = max_level; + if(level->left < 0) level->left = 0; + else if(level->left > max_level) level->left = max_level; + + mixer_set(cmd_right, (level->right << shift)); + mixer_set(cmd_left, (level->left << shift)); + } else { /* Get volume level */ + level->left = mixer_get(cmd_left); + level->right = mixer_get(cmd_right); + + level->left >>= shift; + level->right >>= shift; + } + + return OK; +} + + +/*=========================================================================* + * get_set_input * + *=========================================================================*/ +PRIVATE int get_set_input(struct inout_ctrl *input, int flag, int channel) { + int input_cmd, input_mask, mask, del_mask, shift; + + input_cmd = (channel == 0 ? MIXER_IN_LEFT : MIXER_IN_RIGHT); + + mask = mixer_get(input_cmd); + + switch (input->device) { + case Fm: + shift = 5; + del_mask = 0x1F; + break; + case Cd: + shift = 1; + del_mask = 0x79; + break; + case Line: + shift = 3; + del_mask = 0x67; + break; + case Mic: + shift = 0; + del_mask = 0x7E; + break; + default: + return EINVAL; + } + + if (flag) { /* Set input */ + input_mask = ((input->left == ON ? 1 : 0) << 1) | (input->right == ON ? 1 : 0); + + if (shift > 0) input_mask <<= shift; + else input_mask >>= 1; + + mask &= del_mask; + mask |= input_mask; + + mixer_set(input_cmd, mask); + } else { /* Get input */ + if (shift > 0) { + input->left = ((mask >> (shift+1)) & 1 == 1 ? ON : OFF); + input->right = ((mask >> shift) & 1 == 1 ? ON : OFF); + } else { + input->left = ((mask & 1) == 1 ? ON : OFF); + } + } + + return OK; +} + + +/*=========================================================================* + * get_set_output * + *=========================================================================*/ +PRIVATE int get_set_output(struct inout_ctrl *output, int flag) { + int output_mask, mask, del_mask, shift; + + mask = mixer_get(MIXER_OUTPUT_CTRL); + + switch (output->device) { + case Cd: + shift = 1; + del_mask = 0x79; + break; + case Line: + shift = 3; + del_mask = 0x67; + break; + case Mic: + shift = 0; + del_mask = 0x7E; + break; + default: + return EINVAL; + } + + if (flag) { /* Set input */ + output_mask = ((output->left == ON ? 1 : 0) << 1) | (output->right == ON ? 1 : 0); + + if (shift > 0) output_mask <<= shift; + else output_mask >>= 1; + + mask &= del_mask; + mask |= output_mask; + + mixer_set(MIXER_OUTPUT_CTRL, mask); + } else { /* Get input */ + if (shift > 0) { + output->left = ((mask >> (shift+1)) & 1 == 1 ? ON : OFF); + output->right = ((mask >> shift) & 1 == 1 ? ON : OFF); + } else { + output->left = ((mask & 1) == 1 ? ON : OFF); + } + } + + return OK; +} + + + +PUBLIC int mixer_set(int reg, int data) { + int i; + + sb16_outb(MIXER_REG, reg); + for(i = 0; i < 100; i++); + sb16_outb(MIXER_DATA, data); + + return OK; +} + + + +PUBLIC int mixer_get(int reg) { + int i; + + sb16_outb(MIXER_REG, reg); + for(i = 0; i < 100; i++); + return sb16_inb(MIXER_DATA) & 0xff; +} \ No newline at end of file diff --git a/drivers/audio/sb16/mixer.h b/drivers/audio/sb16/mixer.h new file mode 100644 index 000000000..687a8aaf9 --- /dev/null +++ b/drivers/audio/sb16/mixer.h @@ -0,0 +1,10 @@ +#ifndef SB16_MIXER_H +#define SB16_MIXER_H + +_PROTOTYPE( int mixer_init, (void)); +_PROTOTYPE( int mixer_ioctl, (int request, void *val, int *len)); + +_PROTOTYPE( int mixer_set, (int reg, int data)); +_PROTOTYPE( int mixer_get, (int reg)); + +#endif diff --git a/drivers/audio/sb16/sb16.c b/drivers/audio/sb16/sb16.c new file mode 100755 index 000000000..0e2a0c6f3 --- /dev/null +++ b/drivers/audio/sb16/sb16.c @@ -0,0 +1,445 @@ +/* Driver for SB16 ISA card + * Implementing audio/audio_fw.h + * + * February 2006 Integrated standalone driver with audio framework (Peter Boonstoppel) + * August 24 2005 Ported audio driver to user space (only audio playback) (Peter Boonstoppel) + * May 20 1995 SB16 Driver: Michel R. Prevenier + */ + + +#include "sb16.h" +#include "mixer.h" + + +FORWARD _PROTOTYPE( void dsp_dma_setup, (phys_bytes address, int count, int sub_dev) ); + +FORWARD _PROTOTYPE( int dsp_ioctl, (int request, void *val, int *len)); +FORWARD _PROTOTYPE( int dsp_set_size, (unsigned int size) ); +FORWARD _PROTOTYPE( int dsp_set_speed, (unsigned int speed) ); +FORWARD _PROTOTYPE( int dsp_set_stereo, (unsigned int stereo) ); +FORWARD _PROTOTYPE( int dsp_set_bits, (unsigned int bits) ); +FORWARD _PROTOTYPE( int dsp_set_sign, (unsigned int sign) ); +FORWARD _PROTOTYPE( int dsp_get_max_frag_size, (u32_t *val, int *len) ); + + +PRIVATE unsigned int DspStereo = DEFAULT_STEREO; +PRIVATE unsigned int DspSpeed = DEFAULT_SPEED; +PRIVATE unsigned int DspBits = DEFAULT_BITS; +PRIVATE unsigned int DspSign = DEFAULT_SIGN; +PRIVATE unsigned int DspFragmentSize; + +PRIVATE phys_bytes DmaPhys; +PRIVATE int running = FALSE; + + +PUBLIC sub_dev_t sub_dev[2]; +PUBLIC special_file_t special_file[3]; +PUBLIC drv_t drv; + + + +PUBLIC int drv_init(void) { + drv.DriverName = "SB16"; + drv.NrOfSubDevices = 2; + drv.NrOfSpecialFiles = 3; + + sub_dev[AUDIO].readable = 1; + sub_dev[AUDIO].writable = 1; + sub_dev[AUDIO].DmaSize = 64 * 1024; + sub_dev[AUDIO].NrOfDmaFragments = 2; + sub_dev[AUDIO].MinFragmentSize = 1024; + sub_dev[AUDIO].NrOfExtraBuffers = 4; + + sub_dev[MIXER].writable = 0; + sub_dev[MIXER].readable = 0; + + special_file[0].minor_dev_nr = 0; + special_file[0].write_chan = AUDIO; + special_file[0].read_chan = NO_CHANNEL; + special_file[0].io_ctl = AUDIO; + + special_file[1].minor_dev_nr = 1; + special_file[1].write_chan = NO_CHANNEL; + special_file[1].read_chan = AUDIO; + special_file[1].io_ctl = AUDIO; + + special_file[2].minor_dev_nr = 2; + special_file[2].write_chan = NO_CHANNEL; + special_file[2].read_chan = NO_CHANNEL; + special_file[2].io_ctl = MIXER; +} + + +PUBLIC int drv_init_hw(void) { + int i, s; + int DspVersion[2]; + dprint("drv_init_hw():\n"); + + if(drv_reset () != OK) { + dprint("sb16: No SoundBlaster card detected\n"); + return -1; + } + + DspVersion[0] = DspVersion[1] = 0; + dsp_command(DSP_GET_VERSION); /* Get DSP version bytes */ + + for(i = 1000; i; i--) { + if(sb16_inb(DSP_DATA_AVL) & 0x80) { + if(DspVersion[0] == 0) { + DspVersion[0] = sb16_inb(DSP_READ); + } else { + DspVersion[1] = sb16_inb(DSP_READ); + break; + } + } + } + + if(DspVersion[0] < 4) { + dprint("sb16: No SoundBlaster 16 compatible card detected\n"); + return -1; + } + + dprint("sb16: SoundBlaster DSP version %d.%d detected!\n", DspVersion[0], DspVersion[1]); + + /* set SB to use our IRQ and DMA channels */ + mixer_set(MIXER_SET_IRQ, (1 << (SB_IRQ / 2 - 1))); + mixer_set(MIXER_SET_DMA, (1 << SB_DMA_8 | 1 << SB_DMA_16)); + + DspFragmentSize = sub_dev[AUDIO].DmaSize / sub_dev[AUDIO].NrOfDmaFragments; + + return OK; +} + + + +PUBLIC int drv_reset(void) { + int i; + dprint("drv_reset():\n"); + + sb16_outb(DSP_RESET, 1); + for(i = 0; i < 1000; i++); /* wait a while */ + sb16_outb(DSP_RESET, 0); + + for(i = 0; i < 1000 && !(sb16_inb(DSP_DATA_AVL) & 0x80); i++); + + if(sb16_inb(DSP_READ) != 0xAA) return EIO; /* No SoundBlaster */ + + return OK; +} + + + +PUBLIC int drv_start(int channel, int DmaMode) { + dprint("drv_start():\n"); + + drv_reset(); + + dsp_dma_setup(DmaPhys, DspFragmentSize * sub_dev[channel].NrOfDmaFragments, DmaMode); + + dsp_set_speed(DspSpeed); + + /* Put the speaker on */ + if(DmaMode == DEV_WRITE) { + dsp_command (DSP_CMD_SPKON); /* put speaker on */ + + /* Program DSP with dma mode */ + dsp_command((DspBits == 8 ? DSP_CMD_8BITAUTO_OUT : DSP_CMD_16BITAUTO_OUT)); + } else { + dsp_command (DSP_CMD_SPKOFF); /* put speaker off */ + + /* Program DSP with dma mode */ + dsp_command((DspBits == 8 ? DSP_CMD_8BITAUTO_IN : DSP_CMD_16BITAUTO_IN)); + } + + /* Program DSP with transfer mode */ + if (!DspSign) { + dsp_command((DspStereo == 1 ? DSP_MODE_STEREO_US : DSP_MODE_MONO_US)); + } else { + dsp_command((DspStereo == 1 ? DSP_MODE_STEREO_S : DSP_MODE_MONO_S)); + } + + /* Give length of fragment to DSP */ + if (DspBits == 8) { /* 8 bit transfer */ + /* #bytes - 1 */ + dsp_command((DspFragmentSize - 1) >> 0); + dsp_command((DspFragmentSize - 1) >> 8); + } else { /* 16 bit transfer */ + /* #words - 1 */ + dsp_command((DspFragmentSize - 1) >> 1); + dsp_command((DspFragmentSize - 1) >> 9); + } + + running = TRUE; + + return OK; +} + + + +PUBLIC int drv_stop(int sub_dev) { + if(running) { + dprint("drv_stop():\n"); + dsp_command((DspBits == 8 ? DSP_CMD_DMA8HALT : DSP_CMD_DMA16HALT)); + running = FALSE; + drv_reenable_int(sub_dev); + } + return OK; +} + + + +PUBLIC int drv_set_dma(u32_t dma, u32_t length, int chan) { + dprint("drv_set_dma():\n"); + DmaPhys = dma; + return OK; +} + + + +PUBLIC int drv_reenable_int(int chan) { + dprint("drv_reenable_int()\n"); + sb16_inb((DspBits == 8 ? DSP_DATA_AVL : DSP_DATA16_AVL)); + return OK; +} + + + +PUBLIC int drv_int_sum(void) { + return mixer_get(MIXER_IRQ_STATUS) & 0x0F; +} + + + +PUBLIC int drv_int(int sub_dev) { + return sub_dev == AUDIO && mixer_get(MIXER_IRQ_STATUS) & 0x03; +} + + + +PUBLIC int drv_pause(int chan) { + drv_stop(chan); + return OK; +} + + + +PUBLIC int drv_resume(int chan) { + dsp_command((DspBits == 8 ? DSP_CMD_DMA8CONT : DSP_CMD_DMA16CONT)); + return OK; +} + + + +PUBLIC int drv_io_ctl(int request, void *val, int *len, int sub_dev) { + dprint("dsp_ioctl: got ioctl %d, argument: %d sub_dev: %d\n", request, val, sub_dev); + + if(sub_dev == AUDIO) { + return dsp_ioctl(request, val, len); + } else if(sub_dev == MIXER) { + return mixer_ioctl(request, val, len); + } + + return EIO; +} + + + +PUBLIC int drv_get_irq(char *irq) { + dprint("drv_get_irq():\n"); + *irq = SB_IRQ; + return OK; +} + + + +PUBLIC int drv_get_frag_size(u32_t *frag_size, int sub_dev) { + dprint("drv_get_frag_size():\n"); + *frag_size = DspFragmentSize; + return OK; +} + + + +PRIVATE int dsp_ioctl(int request, void *val, int *len) { + int status; + + switch(request) { + case DSPIORATE: status = dsp_set_speed(*((u32_t*) val)); break; + case DSPIOSTEREO: status = dsp_set_stereo(*((u32_t*) val)); break; + case DSPIOBITS: status = dsp_set_bits(*((u32_t*) val)); break; + case DSPIOSIZE: status = dsp_set_size(*((u32_t*) val)); break; + case DSPIOSIGN: status = dsp_set_sign(*((u32_t*) val)); break; + case DSPIOMAX: status = dsp_get_max_frag_size(val, len); break; + case DSPIORESET: status = drv_reset(); break; + default: status = ENOTTY; break; + } + + return status; +} + + + +PRIVATE void dsp_dma_setup(phys_bytes address, int count, int DmaMode) { + pvb_pair_t pvb[9]; + + dprint("Setting up %d bit DMA\n", DspBits); + + if(DspBits == 8) { /* 8 bit sound */ + count--; + + pv_set(pvb[0], DMA8_MASK, SB_DMA_8 | 0x04); /* Disable DMA channel */ + pv_set(pvb[1], DMA8_CLEAR, 0x00); /* Clear flip flop */ + + /* set DMA mode */ + pv_set(pvb[2], DMA8_MODE, (DmaMode == DEV_WRITE ? DMA8_AUTO_PLAY : DMA8_AUTO_REC)); + + pv_set(pvb[3], DMA8_ADDR, address >> 0); /* Low_byte of address */ + pv_set(pvb[4], DMA8_ADDR, address >> 8); /* High byte of address */ + pv_set(pvb[5], DMA8_PAGE, address >> 16); /* 64K page number */ + pv_set(pvb[6], DMA8_COUNT, count >> 0); /* Low byte of count */ + pv_set(pvb[7], DMA8_COUNT, count >> 8); /* High byte of count */ + pv_set(pvb[8], DMA8_MASK, SB_DMA_8); /* Enable DMA channel */ + + sys_voutb(pvb, 9); + } else { /* 16 bit sound */ + count -= 2; + + pv_set(pvb[0], DMA16_MASK, (SB_DMA_16 & 3) | 0x04); /* Disable DMA channel */ + + pv_set(pvb[1], DMA16_CLEAR, 0x00); /* Clear flip flop */ + + /* Set dma mode */ + pv_set(pvb[2], DMA16_MODE, (DmaMode == DEV_WRITE ? DMA16_AUTO_PLAY : DMA16_AUTO_REC)); + + pv_set(pvb[3], DMA16_ADDR, (address >> 1) & 0xFF); /* Low_byte of address */ + pv_set(pvb[4], DMA16_ADDR, (address >> 9) & 0xFF); /* High byte of address */ + pv_set(pvb[5], DMA16_PAGE, (address >> 16) & 0xFE); /* 128K page number */ + pv_set(pvb[6], DMA16_COUNT, count >> 1); /* Low byte of count */ + pv_set(pvb[7], DMA16_COUNT, count >> 9); /* High byte of count */ + pv_set(pvb[8], DMA16_MASK, SB_DMA_16 & 3); /* Enable DMA channel */ + + sys_voutb(pvb, 9); + } +} + + + +PRIVATE int dsp_set_size(unsigned int size) { + dprint("dsp_set_size(): set fragment size to %u\n", size); + + /* Sanity checks */ + if(size < sub_dev[AUDIO].MinFragmentSize || size > sub_dev[AUDIO].DmaSize / sub_dev[AUDIO].NrOfDmaFragments || size % 2 != 0) { + return EINVAL; + } + + DspFragmentSize = size; + + return OK; +} + + + +PRIVATE int dsp_set_speed(unsigned int speed) { + dprint("sb16: setting speed to %u, stereo = %d\n", speed, DspStereo); + + if(speed < DSP_MIN_SPEED || speed > DSP_MAX_SPEED) { + return EPERM; + } + + /* Soundblaster 16 can be programmed with real sample rates + * instead of time constants + * + * Since you cannot sample and play at the same time + * we set in- and output rate to the same value + */ + + dsp_command(DSP_INPUT_RATE); /* set input rate */ + dsp_command(speed >> 8); /* high byte of speed */ + dsp_command(speed); /* low byte of speed */ + dsp_command(DSP_OUTPUT_RATE); /* same for output rate */ + dsp_command(speed >> 8); + dsp_command(speed); + + DspSpeed = speed; + + return OK; +} + + + +PRIVATE int dsp_set_stereo(unsigned int stereo) { + if(stereo) { + DspStereo = 1; + } else { + DspStereo = 0; + } + + return OK; +} + + + +PRIVATE int dsp_set_bits(unsigned int bits) { + /* Sanity checks */ + if(bits != 8 && bits != 16) { + return EINVAL; + } + + DspBits = bits; + + return OK; +} + + + +PRIVATE int dsp_set_sign(unsigned int sign) { + dprint("sb16: set sign to %u\n", sign); + + DspSign = (sign > 0 ? 1 : 0); + + return OK; +} + + + +PRIVATE int dsp_get_max_frag_size(u32_t *val, int *len) { + *len = sizeof(*val); + *val = sub_dev[AUDIO].DmaSize / sub_dev[AUDIO].NrOfDmaFragments; + return OK; +} + + + +PUBLIC int dsp_command(int value) { + int i, status; + + for (i = 0; i < SB_TIMEOUT; i++) { + if((sb16_inb(DSP_STATUS) & 0x80) == 0) { + sb16_outb(DSP_COMMAND, value); + return OK; + } + } + + dprint("sb16: SoundBlaster: DSP Command(%x) timeout\n", value); + return -1; +} + + + +PUBLIC int sb16_inb(int port) { + int s, value = -1; + + if ((s=sys_inb(port, &value)) != OK) + panic("SB16DSP","sys_inb() failed", s); + + return value; +} + + + +PUBLIC void sb16_outb(int port, int value) { + int s; + + if ((s=sys_outb(port, value)) != OK) + panic("SB16DSP","sys_outb() failed", s); +} \ No newline at end of file diff --git a/drivers/audio/sb16/sb16.h b/drivers/audio/sb16/sb16.h new file mode 100755 index 000000000..75ae7e25d --- /dev/null +++ b/drivers/audio/sb16/sb16.h @@ -0,0 +1,173 @@ +#ifndef SB16_H +#define SB16_H + +#include +#include "../framework/audio_fw.h" + +#define AUDIO 0 +#define MIXER 1 + +#define SB_TIMEOUT 32000 /* timeout count */ + +/* IRQ, base address and DMA channels */ +#define SB_IRQ 7 +#define SB_BASE_ADDR 0x220 /* 0x210, 0x220, 0x230, 0x240, + * 0x250, 0x260, 0x280 + */ +#define SB_DMA_8 1 /* 0, 1, 3 */ +#define SB_DMA_16 5 /* 5, 6, 7 */ + +/* Some defaults for the DSP */ +#define DEFAULT_SPEED 22050 /* Sample rate */ +#define DEFAULT_BITS 8 /* Nr. of bits */ +#define DEFAULT_SIGN 0 /* 0 = unsigned, 1 = signed */ +#define DEFAULT_STEREO 0 /* 0 = mono, 1 = stereo */ + +/* DMA port addresses */ +#define DMA8_ADDR ((SB_DMA_8 & 3) << 1) + 0x00 +#define DMA8_COUNT ((SB_DMA_8 & 3) << 1) + 0x01 +#define DMA8_MASK 0x0A +#define DMA8_MODE 0x0B +#define DMA8_CLEAR 0x0C + + +/* If after this preprocessing stuff DMA8_PAGE is not defined + * the 8-bit DMA channel specified is not valid + */ +#if SB_DMA_8 == 0 +# define DMA8_PAGE 0x87 +#else +# if SB_DMA_8 == 1 +# define DMA8_PAGE 0x83 +# else +# if SB_DMA_8 == 3 +# define DMA8_PAGE 0x82 +# endif +# endif +#endif + + +#define DMA16_ADDR ((SB_DMA_16 & 3) << 2) + 0xC0 +#define DMA16_COUNT ((SB_DMA_16 & 3) << 2) + 0xC2 +#define DMA16_MASK 0xD4 +#define DMA16_MODE 0xD6 +#define DMA16_CLEAR 0xD8 + + +/* If after this preprocessing stuff DMA16_PAGE is not defined + * the 16-bit DMA channel specified is not valid + */ +#if SB_DMA_16 == 5 +# define DMA16_PAGE 0x8B +#else +# if SB_DMA_16 == 6 +# define DMA16_PAGE 0x89 +# else +# if SB_DMA_16 == 7 +# define DMA16_PAGE 0x8A +# endif +# endif +#endif + + +/* DMA modes */ +#define DMA16_AUTO_PLAY 0x58 + (SB_DMA_16 & 3) +#define DMA16_AUTO_REC 0x54 + (SB_DMA_16 & 3) +#define DMA8_AUTO_PLAY 0x58 + SB_DMA_8 +#define DMA8_AUTO_REC 0x54 + SB_DMA_8 + + +/* IO ports for soundblaster */ +#define DSP_RESET 0x6 + SB_BASE_ADDR +#define DSP_READ 0xA + SB_BASE_ADDR +#define DSP_WRITE 0xC + SB_BASE_ADDR +#define DSP_COMMAND 0xC + SB_BASE_ADDR +#define DSP_STATUS 0xC + SB_BASE_ADDR +#define DSP_DATA_AVL 0xE + SB_BASE_ADDR +#define DSP_DATA16_AVL 0xF + SB_BASE_ADDR +#define MIXER_REG 0x4 + SB_BASE_ADDR +#define MIXER_DATA 0x5 + SB_BASE_ADDR +#define OPL3_LEFT 0x0 + SB_BASE_ADDR +#define OPL3_RIGHT 0x2 + SB_BASE_ADDR +#define OPL3_BOTH 0x8 + SB_BASE_ADDR + + +/* DSP Commands */ +#define DSP_INPUT_RATE 0x42 /* set input sample rate */ +#define DSP_OUTPUT_RATE 0x41 /* set output sample rate */ +#define DSP_CMD_SPKON 0xD1 /* set speaker on */ +#define DSP_CMD_SPKOFF 0xD3 /* set speaker off */ +#define DSP_CMD_DMA8HALT 0xD0 /* halt DMA 8-bit operation */ +#define DSP_CMD_DMA8CONT 0xD4 /* continue DMA 8-bit operation */ +#define DSP_CMD_DMA16HALT 0xD5 /* halt DMA 16-bit operation */ +#define DSP_CMD_DMA16CONT 0xD6 /* continue DMA 16-bit operation */ +#define DSP_GET_VERSION 0xE1 /* get version number of DSP */ +#define DSP_CMD_8BITAUTO_IN 0xCE /* 8 bit auto-initialized input */ +#define DSP_CMD_8BITAUTO_OUT 0xC6 /* 8 bit auto-initialized output */ +#define DSP_CMD_16BITAUTO_IN 0xBE /* 16 bit auto-initialized input */ +#define DSP_CMD_16BITAUTO_OUT 0xB6 /* 16 bit auto-initialized output */ +#define DSP_CMD_IRQREQ8 0xF2 /* Interrupt request 8 bit */ +#define DSP_CMD_IRQREQ16 0xF3 /* Interrupt request 16 bit */ + + +/* DSP Modes */ +#define DSP_MODE_MONO_US 0x00 /* Mono unsigned */ +#define DSP_MODE_MONO_S 0x10 /* Mono signed */ +#define DSP_MODE_STEREO_US 0x20 /* Stereo unsigned */ +#define DSP_MODE_STEREO_S 0x30 /* Stereo signed */ + + +/* MIXER commands */ +#define MIXER_RESET 0x00 /* Reset */ +#define MIXER_DAC_LEVEL 0x04 /* Used for detection only */ +#define MIXER_MASTER_LEFT 0x30 /* Master volume left */ +#define MIXER_MASTER_RIGHT 0x31 /* Master volume right */ +#define MIXER_DAC_LEFT 0x32 /* Dac level left */ +#define MIXER_DAC_RIGHT 0x33 /* Dac level right */ +#define MIXER_FM_LEFT 0x34 /* Fm level left */ +#define MIXER_FM_RIGHT 0x35 /* Fm level right */ +#define MIXER_CD_LEFT 0x36 /* Cd audio level left */ +#define MIXER_CD_RIGHT 0x37 /* Cd audio level right */ +#define MIXER_LINE_LEFT 0x38 /* Line in level left */ +#define MIXER_LINE_RIGHT 0x39 /* Line in level right */ +#define MIXER_MIC_LEVEL 0x3A /* Microphone level */ +#define MIXER_PC_LEVEL 0x3B /* Pc speaker level */ +#define MIXER_OUTPUT_CTRL 0x3C /* Output control */ +#define MIXER_IN_LEFT 0x3D /* Input control left */ +#define MIXER_IN_RIGHT 0x3E /* Input control right */ +#define MIXER_GAIN_IN_LEFT 0x3F /* Input gain control left */ +#define MIXER_GAIN_IN_RIGHT 0x40 /* Input gain control right */ +#define MIXER_GAIN_OUT_LEFT 0x41 /* Output gain control left */ +#define MIXER_GAIN_OUT_RIGHT 0x42 /* Output gain control rigth */ +#define MIXER_AGC 0x43 /* Automatic gain control */ +#define MIXER_TREBLE_LEFT 0x44 /* Treble left */ +#define MIXER_TREBLE_RIGHT 0x45 /* Treble right */ +#define MIXER_BASS_LEFT 0x46 /* Bass left */ +#define MIXER_BASS_RIGHT 0x47 /* Bass right */ +#define MIXER_SET_IRQ 0x80 /* Set irq number */ +#define MIXER_SET_DMA 0x81 /* Set DMA channels */ +#define MIXER_IRQ_STATUS 0x82 /* Irq status */ + +/* Mixer constants */ +#define MIC 0x01 /* Microphone */ +#define CD_RIGHT 0x02 +#define CD_LEFT 0x04 +#define LINE_RIGHT 0x08 +#define LINE_LEFT 0x10 +#define FM_RIGHT 0x20 +#define FM_LEFT 0x40 + +/* DSP constants */ +#define DSP_MAX_SPEED 44100 /* Max sample speed in KHz */ +#define DSP_MIN_SPEED 4000 /* Min sample speed in KHz */ + + +/* Number of bytes you can DMA before hitting a 64K boundary: */ +#define dma_bytes_left(phys) \ + ((unsigned) (sizeof(int) == 2 ? 0 : 0x10000) - (unsigned) ((phys) & 0xFFFF)) + +_PROTOTYPE( int dsp_command, (int value) ); +_PROTOTYPE( int sb16_inb, (int port) ); +_PROTOTYPE( void sb16_outb, (int port, int value) ); + +#endif /* SB16_H */ diff --git a/include/sys/ioc_sound.h b/include/sys/ioc_sound.h index eb0338488..59ed32a1d 100755 --- a/include/sys/ioc_sound.h +++ b/include/sys/ioc_sound.h @@ -27,4 +27,7 @@ #define MIXIOSETINPUTRIGHT _IORW('s', 22, struct inout_ctrl) #define MIXIOSETOUTPUT _IORW('s', 23, struct inout_ctrl) +#define AC97READ _IOW('s', 8, u16_t[2]) +#define AC97WRITE _IOR('s', 9, u16_t[2]) + #endif /* _S_I_SOUND_H */ -- 2.44.0