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:
parent
1327804478
commit
0d2d8c6db2
74
drivers/audio/AC97.h
Executable file
74
drivers/audio/AC97.h
Executable 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
34
drivers/audio/README
Normal 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
138
drivers/audio/es1371/.depend
Executable 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
44
drivers/audio/es1371/Makefile
Executable 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
196
drivers/audio/es1371/SRC.c
Executable 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
43
drivers/audio/es1371/SRC.h
Executable 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
264
drivers/audio/es1371/codec.c
Executable 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
44
drivers/audio/es1371/codec.h
Executable 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
588
drivers/audio/es1371/es1371.c
Executable 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
138
drivers/audio/es1371/es1371.h
Executable 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
27
drivers/audio/es1371/wait.c
Executable 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
10
drivers/audio/es1371/wait.h
Executable 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);
|
||||
|
9
drivers/audio/framework/Makefile
Normal file
9
drivers/audio/framework/Makefile
Normal 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
|
825
drivers/audio/framework/audio_fw.c
Executable file
825
drivers/audio/framework/audio_fw.c
Executable 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;
|
||||
}
|
96
drivers/audio/framework/audio_fw.h
Executable file
96
drivers/audio/framework/audio_fw.h
Executable 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
68
drivers/audio/sb16/.depend
Executable 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
41
drivers/audio/sb16/Makefile
Executable 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
253
drivers/audio/sb16/mixer.c
Executable 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;
|
||||
}
|
10
drivers/audio/sb16/mixer.h
Normal file
10
drivers/audio/sb16/mixer.h
Normal 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
445
drivers/audio/sb16/sb16.c
Executable 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
173
drivers/audio/sb16/sb16.h
Executable 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 */
|
@ -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 */
|
||||
|
Loading…
x
Reference in New Issue
Block a user