audio drivers. (not updated for trunk.)

sb16: port of isa sb16 driver to user-space. Port by Peter Boonstoppel.
es1371: By Laurens Bronwasser.
This commit is contained in:
Ben Gras 2007-11-23 11:30:50 +00:00
parent 1327804478
commit 0d2d8c6db2
22 changed files with 3523 additions and 0 deletions

74
drivers/audio/AC97.h Executable file
View File

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

34
drivers/audio/README Normal file
View File

@ -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)

138
drivers/audio/es1371/.depend Executable file
View File

@ -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

44
drivers/audio/es1371/Makefile Executable file
View File

@ -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

196
drivers/audio/es1371/SRC.c Executable file
View File

@ -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;
}

43
drivers/audio/es1371/SRC.h Executable file
View File

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

264
drivers/audio/es1371/codec.c Executable file
View File

@ -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;
}

44
drivers/audio/es1371/codec.h Executable file
View File

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

588
drivers/audio/es1371/es1371.c Executable file
View File

@ -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 <DspFragmentSize> 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);
}

138
drivers/audio/es1371/es1371.h Executable file
View File

@ -0,0 +1,138 @@
#ifndef ES1371_H
#define ES1371_H
#include <sys/types.h>
#include "../../drivers.h"
#include "../../libpci/pci.h"
#include <sys/ioc_sound.h>
#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 */

27
drivers/audio/es1371/wait.c Executable file
View File

@ -0,0 +1,27 @@
#include "../../drivers.h"
#include <sys/types.h>
#include <time.h>
#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;
}

10
drivers/audio/es1371/wait.h Executable file
View File

@ -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);

View File

@ -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

View File

@ -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;
}

View File

@ -0,0 +1,96 @@
#ifndef AUDIO_FW_H
#define AUDIO_FW_H
#include "../../drivers.h"
#include <sys/ioc_sound.h>
/* 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 */

68
drivers/audio/sb16/.depend Executable file
View File

@ -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

41
drivers/audio/sb16/Makefile Executable file
View File

@ -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

253
drivers/audio/sb16/mixer.c Executable file
View File

@ -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;
}

View File

@ -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

445
drivers/audio/sb16/sb16.c Executable file
View File

@ -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);
}

173
drivers/audio/sb16/sb16.h Executable file
View File

@ -0,0 +1,173 @@
#ifndef SB16_H
#define SB16_H
#include <minix/sound.h>
#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 */

View File

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