Import of Luigi Rizzo's sound code. For more information about the driver

check out the README that is included.

Submitted by:	Luigi Rizzo <luigi@labinfo.iet.unipi.it>
This commit is contained in:
John-Mark Gurney 1997-09-14 21:42:12 +00:00
parent 634a86ba38
commit b7da7b3e2a
22 changed files with 16980 additions and 0 deletions

1509
sys/dev/pcm/isa/mss.c Normal file

File diff suppressed because it is too large Load Diff

250
sys/dev/pcm/isa/mss.h Normal file
View File

@ -0,0 +1,250 @@
/*
* file: mss.h
*
* (C) 1997 Luigi Rizzo (luigi@iet.unipi.it)
*
* This file contains information and macro definitions for
* AD1848-compatible devices, used in the MSS/WSS compatible boards.
*
*/
/*
*
The codec part of the board is seen as a set of 4 registers mapped
at the base address for the board (default 0x534). Note that some
(early) boards implemented 4 additional registers 4 location before
(usually 0x530) to store configuration information. This is a source
of confusion in that one never knows what address to specify. The
(current) convention is to use the old address (0x530) in the kernel
configuration file and consider MSS registers start four location
ahead.
*
*/
/*
* The four visible registers of the MSS :
*
*/
#define io_Index_Addr(d) ((d)->io_base + 4)
#define IA_BUSY 0x80 /* readonly, set when busy */
#define IA_MCE 0x40 /* the MCE bit. */
/*
* the MCE bit must be set whenever the current mode of the
* codec is changed; this in particular is true for the
* Data Format (I8, I28) and Interface Config(I9) registers.
* Only exception are CEN and PEN which can be changed on the fly.
* The DAC output is muted when MCE is set.
*/
#define IA_TRD 0x20 /* Transfer request disable */
/*
* When TRD is set, DMA transfers cease when the INT bit in
* the MSS status reg is set. Must be cleared for automode
* DMA, set otherwise.
*/
#define IA_AMASK 0x1f /* mask for indirect address */
#define io_Indexed_Data(d) ((d)->io_base+1+4)
/*
* data to be transferred to the indirect register addressed
* by index addr. During init and sw. powerdown, cannot be
* written to, and is always read as 0x80 (consistent with the
* busy flag).
*/
#define io_Status(d) ((d)->io_base+2+4)
#define IS_CUL 0x80 /* capture upper/lower */
#define IS_CLR 0x40 /* capture left/right */
#define IS_CRDY 0x20 /* capture ready for programmed i/o */
#define IS_SER 0x10 /* sample error (overrun/underrun) */
#define IS_PUL 0x08 /* playback upper/lower */
#define IS_PLR 0x04 /* playback left/right */
#define IS_PRDY 0x02 /* playback ready for programmed i/o */
#define IS_INT 0x01 /* int status (1 = active) */
/*
* IS_INT is clreared by any write to the status register.
*/
#define io_Polled_IO(d) ((d)->io_base+3+4)
/*
* this register is used in case of polled i/o
*/
/*
* The MSS has a set of 16 (or 32 depending on the model) indirect
* registers accessible through the data port by specifying the
* appropriate address in the address register.
*
* The 16 low registers are uniformly handled in AD1848/CS4248 compatible
* mode (often called MODE1). For the upper 16 registers there are
* some differences among different products, mainly Crystal uses them
* differently from OPTi.
*
*/
/*
* volume registers
*/
#define I6_MUTE 0x80
/*
* register I9 -- interface configuration.
*/
#define I9_PEN 0x01 /* playback enable */
#define I9_CEN 0x02 /* capture enable */
/*
* values used in bd_flags
*/
#define BD_F_MCE_BIT 0x0001
#define BD_F_IRQ_OK 0x0002
#define BD_F_TMR_RUN 0x0004
/*
* sound/ad1848_mixer.h
*
* Definitions for the mixer of AD1848 and compatible codecs.
*
* Copyright by Hannu Savolainen 1994
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met: 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. 2.
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* The AD1848 codec has generic input lines called Line, Aux1 and Aux2.
* Soundcard manufacturers have connected actual inputs (CD, synth, line,
* etc) to these inputs in different order. Therefore it's difficult
* to assign mixer channels to to these inputs correctly. The following
* contains two alternative mappings. The first one is for GUS MAX and
* the second is just a generic one (line1, line2 and line3).
* (Actually this is not a mapping but rather some kind of interleaving
* solution).
*/
#define GUSMAX_MIXER
#ifdef GUSMAX_MIXER
#define MODE1_REC_DEVICES \
(SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD|SOUND_MASK_IMIX)
#define MODE1_MIXER_DEVICES \
(SOUND_MASK_SYNTH | SOUND_MASK_MIC | SOUND_MASK_CD | \
SOUND_MASK_IGAIN | SOUND_MASK_PCM|SOUND_MASK_IMIX)
#define MODE2_MIXER_DEVICES \
(SOUND_MASK_SYNTH | SOUND_MASK_LINE | SOUND_MASK_MIC | \
SOUND_MASK_CD | SOUND_MASK_SPEAKER | SOUND_MASK_IGAIN | \
SOUND_MASK_PCM | SOUND_MASK_IMIX)
#else /* Generic mapping */
#define MODE1_REC_DEVICES \
(SOUND_MASK_LINE3 | SOUND_MASK_MIC | SOUND_MASK_LINE1|SOUND_MASK_IMIX)
#define MODE1_MIXER_DEVICES \
(SOUND_MASK_LINE1 | SOUND_MASK_MIC | SOUND_MASK_LINE2 | \
SOUND_MASK_IGAIN | SOUND_MASK_PCM | SOUND_MASK_IMIX)
#define MODE2_MIXER_DEVICES \
(SOUND_MASK_LINE1 | SOUND_MASK_MIC | SOUND_MASK_LINE2 | \
SOUND_MASK_LINE3 | SOUND_MASK_SPEAKER | \
SOUND_MASK_IGAIN | SOUND_MASK_PCM | SOUND_MASK_IMIX)
#endif
#define OPTI931_MIXER_DEVICES \
(SOUND_MASK_VOLUME | SOUND_MASK_SYNTH | SOUND_MASK_PCM | \
SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_IGAIN )
/*
* Most of the mixer entries work in backwards. Setting the polarity field
* makes them to work correctly.
*
* The channel numbering used by individual soundcards is not fixed.
* Some cards have assigned different meanings for the AUX1, AUX2
* and LINE inputs. Some have different features...
* The current version doesn't try to compensate this.
*
*/
mixer_ent mix_devices[32][2] = { /* As used in GUS MAX */
MIX_ENT(SOUND_MIXER_VOLUME, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 0, 5, 5, 1, 0, 5),
MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 6, 7, 1, 0, 6),
MIX_ENT(SOUND_MIXER_SPEAKER, 26, 1, 0, 4, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_LINE, 18, 1, 0, 5, 19, 1, 0, 5),
MIX_ENT(SOUND_MIXER_MIC, 0, 0, 5, 1, 1, 0, 5, 1),
MIX_ENT(SOUND_MIXER_CD, 2, 1, 0, 5, 3, 1, 0, 5),
MIX_ENT(SOUND_MIXER_IMIX, 13, 1, 2, 6, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4),
MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_LINE1, 2, 1, 0, 5, 3, 1, 0, 5),
MIX_ENT(SOUND_MIXER_LINE2, 4, 1, 0, 5, 5, 1, 0, 5),
MIX_ENT(SOUND_MIXER_LINE3, 18, 1, 0, 5, 19, 1, 0, 5)
};
mixer_ent opti931_devices[32][2] = { /* for the opti931 */
MIX_ENT(SOUND_MIXER_VOLUME, 22, 1, 1, 5, 23, 1, 1, 5),
MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 1, 4, 5, 1, 1, 4),
MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 5, 7, 1, 0, 5),
MIX_ENT(SOUND_MIXER_SPEAKER, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_LINE, 18, 1, 1, 4, 19, 1, 1, 4),
MIX_ENT(SOUND_MIXER_MIC, 0, 0, 5, 1, 1, 0, 5, 1),
MIX_ENT(SOUND_MIXER_CD, 2, 1, 1, 4, 3, 1, 1, 4),
MIX_ENT(SOUND_MIXER_IMIX, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4),
MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_LINE1, 2, 1, 0, 5, 3, 1, 0, 5),
MIX_ENT(SOUND_MIXER_LINE2, 4, 1, 0, 5, 5, 1, 0, 5),
MIX_ENT(SOUND_MIXER_LINE3, 18, 1, 0, 5, 19, 1, 0, 5)
};
static u_short default_mixer_levels[SOUND_MIXER_NRDEVICES] = {
0x5a5a, /* Master Volume */
0x3232, /* Bass */
0x3232, /* Treble */
0x4b4b, /* FM */
0x4040, /* PCM */
0x4b4b, /* PC Speaker */
0x2020, /* Ext Line */
0x4040, /* Mic */
0x4b4b, /* CD */
0x0000, /* Recording monitor */
0x4b4b, /* SB PCM */
0x4b4b, /* Recording level */
0x2525, /* Input gain */
0x0000, /* Output gain */
/* 0x4040, Line1 */
0x0000, /* Line1 */
0x0000, /* Line2 */
0x1515 /* Line3 (usually line in)*/
};

1080
sys/dev/pcm/isa/sb.c Normal file

File diff suppressed because it is too large Load Diff

387
sys/dev/pcm/isa/sb.h Normal file
View File

@ -0,0 +1,387 @@
/*
* file: sbcard.h
*/
typedef struct _sbdev_info {
} sbdev_info ;
extern int sbc_major, sbc_minor ;
/*
* sound blaster registers
*/
#define DSP_RESET (io_base + 0x6)
#define DSP_READ (io_base + 0xA)
#define DSP_WRITE (io_base + 0xC)
#define DSP_COMMAND (io_base + 0xC)
#define DSP_STATUS (io_base + 0xC)
#define DSP_DATA_AVAIL (io_base + 0xE)
#define DSP_DATA_AVL16 (io_base + 0xF)
#define MIXER_ADDR (io_base + 0x4)
#define MIXER_DATA (io_base + 0x5)
#define OPL3_LEFT (io_base + 0x0)
#define OPL3_RIGHT (io_base + 0x2)
#define OPL3_BOTH (io_base + 0x8)
/*
* DSP Commands. There are many, and in many cases they are used explicitly
*/
#define DSP_DAC8 0x10 /* direct DAC output */
#define DSP_CMD_DAC8 0x14 /* single cycle 8-bit dma out */
#define DSP_CMD_DAC2 0x16 /* 2-bit adpcm dma out (cont) */
#define DSP_CMD_DAC2S 0x17 /* 2-bit adpcm dma out (start) */
#define DSP_CMD_DAC8_A 0x1c /* auto 8-bit dma out */
#define DSP_CMD_DAC2S_A 0x1f /* auto 2-bit adpcm dma out (start) */
#define DSP_ADC8 0x20 /* direct ADC input */
#define DSP_CMD_ADC8 0x24 /* single cycle 8-bit dma in */
#define DSP_CMD_ADC8_A 0x2c /* auto 8-bit dma out */
#define DSP_CMD_O16 0xb0
#define DSP_CMD_I16 0xb8
#define DSP_CMD_O8 0xc0
#define DSP_CMD_I8 0xc8
#define DSP_MODE_U8MONO 0x00
#define DSP_MODE_U8STEREO 0x20
#define DSP_MODE_S16MONO 0x10
#define DSP_MODE_S16STEREO 0x30
#define DSP_CMD_SPKON 0xD1
#define DSP_CMD_SPKOFF 0xD3
#define DSP_CMD_SPKR(on) (0xD1 | (on ? 0:2))
#define DSP_CMD_DMAON 0xD0 /* ??? the comment says Halt DMA */
#define DSP_CMD_DMAOFF 0xD4 /* ??? comment says continue dma */
#define DSP_CMD_DMAHALT 0xD0
#define DSP_CMD_TCONST 0x40 /* set time constant */
#define DSP_CMD_HSSIZE 0x48 /* high speed dma count */
#define DSP_CMD_HSDAC 0x91 /* high speed dac */
#define DSP_CMD_HSADC 0x99 /* high speed adc */
#define DSP_CMD_GETVER 0xE1
#define DSP_CMD_GETID 0xE7 /* return id bytes */
/* prepare for dma input */
#define DSP_CMD_DMAMODE(stereo, bit16) (0xA0 | (stereo ? 8:0) | (bit16 ? 4:0))
#define DSP_CMD_OUT16 0x41 /* send parms for dma out on sb16 */
#define DSP_CMD_IN16 0x42 /* send parms for dma in on sb16 */
#if 0 /*** unknown ***/
/*
* D9 and D5 are used on the sb16 on close... maybe a reset of
* some subsystem ?
*/
#define DSP_CMD_D9 0xD9
#define DSP_CMD_D5 0xD5
#define DSP_CMD_FA 0xFA /* get version from prosonic*/
#define DSP_CMD_FB 0xFB /* set irq/dma for prosonic*/
#endif
/*
* in fact, for the SB16, dma commands are as follows:
*
* cmd, mode, len_low, len_high.
*
* cmd is a combination of DSP_DMA16 or DSP_DMA8 and
*/
#define DSP_DMA16 0xb0
#define DSP_DMA8 0xc0
# define DSP_F16_DAC 0x00
# define DSP_F16_ADC 0x08
# define DSP_F16_AUTO 0x04
# define DSP_F16_FIFO_ON 0x02
/*
* mode is a combination of the following:
*/
#define DSP_F16_STEREO 0x20
#define DSP_F16_SIGNED 0x10
#define IMODE_NONE 0
#define IMODE_OUTPUT PCM_ENABLE_OUTPUT
#define IMODE_INPUT PCM_ENABLE_INPUT
#define IMODE_INIT 3
#define IMODE_MIDI 4
#define NORMAL_MIDI 0
#define UART_MIDI 1
/*
* values used for bd_flags in SoundBlaster driver
*/
#define BD_F_HISPEED 0x0001 /* doing high speed ... */
#define BD_F_JAZZ16 0x0002 /* jazz16 detected */
#define BD_F_JAZZ16_2 0x0004 /* jazz16 type 2 */
#define BD_F_DUP_MIDI 0x0008 /* duplex midi */
#define BD_F_MIX_MASK 0x0070 /* up to 8 mixers (I know of 3) */
#define BD_F_MIX_CT1335 0x0010 /* CT1335 */
#define BD_F_MIX_CT1345 0x0020 /* CT1345 */
#define BD_F_MIX_CT1745 0x0030 /* CT1745 */
#define BD_F_SB16 0x0100 /* this is a SB16 */
#define BD_F_NOREC 0x0200 /* recording not supported on this board */
#define BD_F_MIDIBUSY 0x0400 /* midi busy */
/*
* sound/sb_mixer.h
*
* Definitions for the SB Pro and SB16 mixers
*
* Copyright by Hannu Savolainen 1993
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met: 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. 2.
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Modified: Hunyue Yau Jan 6 1994 Added defines for the Sound Galaxy NX Pro
* mixer.
*
*/
#define SBPRO_RECORDING_DEVICES \
(SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD)
/* Same as SB Pro, unless I find otherwise */
#define SGNXPRO_RECORDING_DEVICES SBPRO_RECORDING_DEVICES
#define SBPRO_MIXER_DEVICES \
(SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_LINE | SOUND_MASK_MIC | \
SOUND_MASK_CD | SOUND_MASK_VOLUME)
/*
* SG NX Pro has treble and bass settings on the mixer. The 'speaker' channel
* is the COVOX/DisneySoundSource emulation volume control on the mixer. It
* does NOT control speaker volume. Should have own mask eventually?
*/
#define SGNXPRO_MIXER_DEVICES \
(SBPRO_MIXER_DEVICES | SOUND_MASK_BASS | \
SOUND_MASK_TREBLE | SOUND_MASK_SPEAKER )
#define SB16_RECORDING_DEVICES \
(SOUND_MASK_SYNTH | SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD)
#define SB16_MIXER_DEVICES \
(SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_SPEAKER | \
SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | \
SOUND_MASK_IGAIN | SOUND_MASK_OGAIN | \
SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE)
/*
* Mixer registers
*
* NOTE! RECORD_SRC == IN_FILTER
*/
/*
* Mixer registers of SB Pro
*/
#define VOC_VOL 0x04
#define MIC_VOL 0x0A
#define MIC_MIX 0x0A
#define RECORD_SRC 0x0C
#define IN_FILTER 0x0C
#define OUT_FILTER 0x0E
#define MASTER_VOL 0x22
#define FM_VOL 0x26
#define CD_VOL 0x28
#define LINE_VOL 0x2E
#define IRQ_NR 0x80
#define DMA_NR 0x81
#define IRQ_STAT 0x82
/*
* Additional registers on the SG NX Pro
*/
#define COVOX_VOL 0x42
#define TREBLE_LVL 0x44
#define BASS_LVL 0x46
#define FREQ_HI (1 << 3)/* Use High-frequency ANFI filters */
#define FREQ_LOW 0 /* Use Low-frequency ANFI filters */
#define FILT_ON 0 /* Yes, 0 to turn it on, 1 for off */
#define FILT_OFF (1 << 5)
#define MONO_DAC 0x00
#define STEREO_DAC 0x02
/*
* Mixer registers of SB16
*/
#define SB16_IMASK_L 0x3d
#define SB16_IMASK_R 0x3e
#define SB16_OMASK 0x3c
#ifndef __SB_MIXER_C__
mixer_tab sbpro_mix;
mixer_tab sb16_mix;
#ifdef __SGNXPRO__
mixer_tab sgnxpro_mix;
#endif
static u_char sb16_recmasks_L[SOUND_MIXER_NRDEVICES];
static u_char sb16_recmasks_R[SOUND_MIXER_NRDEVICES];
#else /* __SB_MIXER_C__ defined */
mixer_tab sbpro_mix = {
PMIX_ENT(SOUND_MIXER_VOLUME, 0x22, 7, 4, 0x22, 3, 4),
PMIX_ENT(SOUND_MIXER_BASS, 0x00, 0, 0, 0x00, 0, 0),
PMIX_ENT(SOUND_MIXER_TREBLE, 0x00, 0, 0, 0x00, 0, 0),
PMIX_ENT(SOUND_MIXER_SYNTH, 0x26, 7, 4, 0x26, 3, 4),
PMIX_ENT(SOUND_MIXER_PCM, 0x04, 7, 4, 0x04, 3, 4),
PMIX_ENT(SOUND_MIXER_SPEAKER, 0x00, 0, 0, 0x00, 0, 0),
PMIX_ENT(SOUND_MIXER_LINE, 0x2e, 7, 4, 0x2e, 3, 4),
PMIX_ENT(SOUND_MIXER_MIC, 0x0a, 2, 3, 0x00, 0, 0),
PMIX_ENT(SOUND_MIXER_CD, 0x28, 7, 4, 0x28, 3, 4),
PMIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0),
PMIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0),
PMIX_ENT(SOUND_MIXER_RECLEV, 0x00, 0, 0, 0x00, 0, 0)
};
#ifdef __SGNXPRO__
mixer_tab sgnxpro_mix = {
PMIX_ENT(SOUND_MIXER_VOLUME, 0x22, 7, 4, 0x22, 3, 4),
PMIX_ENT(SOUND_MIXER_BASS, 0x46, 2, 3, 0x00, 0, 0),
PMIX_ENT(SOUND_MIXER_TREBLE, 0x44, 2, 3, 0x00, 0, 0),
PMIX_ENT(SOUND_MIXER_SYNTH, 0x26, 7, 4, 0x26, 3, 4),
PMIX_ENT(SOUND_MIXER_PCM, 0x04, 7, 4, 0x04, 3, 4),
PMIX_ENT(SOUND_MIXER_SPEAKER, 0x42, 2, 3, 0x00, 0, 0),
PMIX_ENT(SOUND_MIXER_LINE, 0x2e, 7, 4, 0x2e, 3, 4),
PMIX_ENT(SOUND_MIXER_MIC, 0x0a, 2, 3, 0x00, 0, 0),
PMIX_ENT(SOUND_MIXER_CD, 0x28, 7, 4, 0x28, 3, 4),
PMIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0),
PMIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0),
PMIX_ENT(SOUND_MIXER_RECLEV, 0x00, 0, 0, 0x00, 0, 0),
PMIX_ENT(SOUND_MIXER_IGAIN, 0x00, 0, 0, 0x00, 0, 0),
PMIX_ENT(SOUND_MIXER_OGAIN, 0x00, 0, 0, 0x00, 0, 0)
};
#endif
mixer_tab sb16_mix = {
PMIX_ENT(SOUND_MIXER_VOLUME, 0x30, 3, 5, 0x31, 3, 5),
PMIX_ENT(SOUND_MIXER_BASS, 0x46, 4, 4, 0x47, 4, 4),
PMIX_ENT(SOUND_MIXER_TREBLE, 0x44, 4, 4, 0x45, 4, 4),
PMIX_ENT(SOUND_MIXER_SYNTH, 0x34, 3, 5, 0x35, 3, 5),
PMIX_ENT(SOUND_MIXER_PCM, 0x32, 3, 5, 0x33, 3, 5),
PMIX_ENT(SOUND_MIXER_SPEAKER, 0x3b, 6, 2, 0x00, 0, 0),
PMIX_ENT(SOUND_MIXER_LINE, 0x38, 3, 5, 0x39, 3, 5),
PMIX_ENT(SOUND_MIXER_MIC, 0x3a, 3, 5, 0x00, 0, 0),
PMIX_ENT(SOUND_MIXER_CD, 0x36, 3, 5, 0x37, 3, 5),
PMIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0),
PMIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0),
PMIX_ENT(SOUND_MIXER_RECLEV, 0x3f, 6, 2, 0x40, 6, 2), /* Obsol,Use IGAIN*/
PMIX_ENT(SOUND_MIXER_IGAIN, 0x3f, 6, 2, 0x40, 6, 2),
PMIX_ENT(SOUND_MIXER_OGAIN, 0x41, 6, 2, 0x42, 6, 2)
};
#ifdef SM_GAMES /* Master volume is lower and PCM & FM
* volumes higher than with SB Pro. This
* improves the sound quality */
static u_short levels[SOUND_MIXER_NRDEVICES] =
{
0x2020, /* Master Volume */
0x4b4b, /* Bass */
0x4b4b, /* Treble */
0x6464, /* FM */
0x6464, /* PCM */
0x4b4b, /* PC Speaker */
0x4b4b, /* Ext Line */
0x0000, /* Mic */
0x4b4b, /* CD */
0x4b4b, /* Recording monitor */
0x4b4b, /* SB PCM */
0x4b4b, /* Recording level */
0x4b4b, /* Input gain */
0x4b4b}; /* Output gain */
#else /* If the user selected just plain SB Pro */
static u_short levels[SOUND_MIXER_NRDEVICES] =
{
0x5a5a, /* Master Volume */
0x4b4b, /* Bass */
0x4b4b, /* Treble */
0x4b4b, /* FM */
0x4b4b, /* PCM */
0x4b4b, /* PC Speaker */
0x4b4b, /* Ext Line */
0x1010, /* Mic */
0x4b4b, /* CD */
0x4b4b, /* Recording monitor */
0x4b4b, /* SB PCM */
0x4b4b, /* Recording level */
0x4b4b, /* Input gain */
0x4b4b}; /* Output gain */
#endif /* SM_GAMES */
static u_char sb16_recmasks_L[SOUND_MIXER_NRDEVICES] =
{
0x00, /* SOUND_MIXER_VOLUME */
0x00, /* SOUND_MIXER_BASS */
0x00, /* SOUND_MIXER_TREBLE */
0x40, /* SOUND_MIXER_SYNTH */
0x00, /* SOUND_MIXER_PCM */
0x00, /* SOUND_MIXER_SPEAKER */
0x10, /* SOUND_MIXER_LINE */
0x01, /* SOUND_MIXER_MIC */
0x04, /* SOUND_MIXER_CD */
0x00, /* SOUND_MIXER_IMIX */
0x00, /* SOUND_MIXER_ALTPCM */
0x00, /* SOUND_MIXER_RECLEV */
0x00, /* SOUND_MIXER_IGAIN */
0x00 /* SOUND_MIXER_OGAIN */
};
static u_char sb16_recmasks_R[SOUND_MIXER_NRDEVICES] =
{
0x00, /* SOUND_MIXER_VOLUME */
0x00, /* SOUND_MIXER_BASS */
0x00, /* SOUND_MIXER_TREBLE */
0x20, /* SOUND_MIXER_SYNTH */
0x00, /* SOUND_MIXER_PCM */
0x00, /* SOUND_MIXER_SPEAKER */
0x08, /* SOUND_MIXER_LINE */
0x01, /* SOUND_MIXER_MIC */
0x02, /* SOUND_MIXER_CD */
0x00, /* SOUND_MIXER_IMIX */
0x00, /* SOUND_MIXER_ALTPCM */
0x00, /* SOUND_MIXER_RECLEV */
0x00, /* SOUND_MIXER_IGAIN */
0x00 /* SOUND_MIXER_OGAIN */
};
/*
* Recording sources (SB Pro)
*/
#endif /* __SB_MIXER_C__ */
#define SRC_MIC 1 /* Select Microphone recording source */
#define SRC_CD 3 /* Select CD recording source */
#define SRC_LINE 7 /* Use Line-in for recording source */

1509
sys/dev/sound/isa/mss.c Normal file

File diff suppressed because it is too large Load Diff

250
sys/dev/sound/isa/mss.h Normal file
View File

@ -0,0 +1,250 @@
/*
* file: mss.h
*
* (C) 1997 Luigi Rizzo (luigi@iet.unipi.it)
*
* This file contains information and macro definitions for
* AD1848-compatible devices, used in the MSS/WSS compatible boards.
*
*/
/*
*
The codec part of the board is seen as a set of 4 registers mapped
at the base address for the board (default 0x534). Note that some
(early) boards implemented 4 additional registers 4 location before
(usually 0x530) to store configuration information. This is a source
of confusion in that one never knows what address to specify. The
(current) convention is to use the old address (0x530) in the kernel
configuration file and consider MSS registers start four location
ahead.
*
*/
/*
* The four visible registers of the MSS :
*
*/
#define io_Index_Addr(d) ((d)->io_base + 4)
#define IA_BUSY 0x80 /* readonly, set when busy */
#define IA_MCE 0x40 /* the MCE bit. */
/*
* the MCE bit must be set whenever the current mode of the
* codec is changed; this in particular is true for the
* Data Format (I8, I28) and Interface Config(I9) registers.
* Only exception are CEN and PEN which can be changed on the fly.
* The DAC output is muted when MCE is set.
*/
#define IA_TRD 0x20 /* Transfer request disable */
/*
* When TRD is set, DMA transfers cease when the INT bit in
* the MSS status reg is set. Must be cleared for automode
* DMA, set otherwise.
*/
#define IA_AMASK 0x1f /* mask for indirect address */
#define io_Indexed_Data(d) ((d)->io_base+1+4)
/*
* data to be transferred to the indirect register addressed
* by index addr. During init and sw. powerdown, cannot be
* written to, and is always read as 0x80 (consistent with the
* busy flag).
*/
#define io_Status(d) ((d)->io_base+2+4)
#define IS_CUL 0x80 /* capture upper/lower */
#define IS_CLR 0x40 /* capture left/right */
#define IS_CRDY 0x20 /* capture ready for programmed i/o */
#define IS_SER 0x10 /* sample error (overrun/underrun) */
#define IS_PUL 0x08 /* playback upper/lower */
#define IS_PLR 0x04 /* playback left/right */
#define IS_PRDY 0x02 /* playback ready for programmed i/o */
#define IS_INT 0x01 /* int status (1 = active) */
/*
* IS_INT is clreared by any write to the status register.
*/
#define io_Polled_IO(d) ((d)->io_base+3+4)
/*
* this register is used in case of polled i/o
*/
/*
* The MSS has a set of 16 (or 32 depending on the model) indirect
* registers accessible through the data port by specifying the
* appropriate address in the address register.
*
* The 16 low registers are uniformly handled in AD1848/CS4248 compatible
* mode (often called MODE1). For the upper 16 registers there are
* some differences among different products, mainly Crystal uses them
* differently from OPTi.
*
*/
/*
* volume registers
*/
#define I6_MUTE 0x80
/*
* register I9 -- interface configuration.
*/
#define I9_PEN 0x01 /* playback enable */
#define I9_CEN 0x02 /* capture enable */
/*
* values used in bd_flags
*/
#define BD_F_MCE_BIT 0x0001
#define BD_F_IRQ_OK 0x0002
#define BD_F_TMR_RUN 0x0004
/*
* sound/ad1848_mixer.h
*
* Definitions for the mixer of AD1848 and compatible codecs.
*
* Copyright by Hannu Savolainen 1994
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met: 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. 2.
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* The AD1848 codec has generic input lines called Line, Aux1 and Aux2.
* Soundcard manufacturers have connected actual inputs (CD, synth, line,
* etc) to these inputs in different order. Therefore it's difficult
* to assign mixer channels to to these inputs correctly. The following
* contains two alternative mappings. The first one is for GUS MAX and
* the second is just a generic one (line1, line2 and line3).
* (Actually this is not a mapping but rather some kind of interleaving
* solution).
*/
#define GUSMAX_MIXER
#ifdef GUSMAX_MIXER
#define MODE1_REC_DEVICES \
(SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD|SOUND_MASK_IMIX)
#define MODE1_MIXER_DEVICES \
(SOUND_MASK_SYNTH | SOUND_MASK_MIC | SOUND_MASK_CD | \
SOUND_MASK_IGAIN | SOUND_MASK_PCM|SOUND_MASK_IMIX)
#define MODE2_MIXER_DEVICES \
(SOUND_MASK_SYNTH | SOUND_MASK_LINE | SOUND_MASK_MIC | \
SOUND_MASK_CD | SOUND_MASK_SPEAKER | SOUND_MASK_IGAIN | \
SOUND_MASK_PCM | SOUND_MASK_IMIX)
#else /* Generic mapping */
#define MODE1_REC_DEVICES \
(SOUND_MASK_LINE3 | SOUND_MASK_MIC | SOUND_MASK_LINE1|SOUND_MASK_IMIX)
#define MODE1_MIXER_DEVICES \
(SOUND_MASK_LINE1 | SOUND_MASK_MIC | SOUND_MASK_LINE2 | \
SOUND_MASK_IGAIN | SOUND_MASK_PCM | SOUND_MASK_IMIX)
#define MODE2_MIXER_DEVICES \
(SOUND_MASK_LINE1 | SOUND_MASK_MIC | SOUND_MASK_LINE2 | \
SOUND_MASK_LINE3 | SOUND_MASK_SPEAKER | \
SOUND_MASK_IGAIN | SOUND_MASK_PCM | SOUND_MASK_IMIX)
#endif
#define OPTI931_MIXER_DEVICES \
(SOUND_MASK_VOLUME | SOUND_MASK_SYNTH | SOUND_MASK_PCM | \
SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_IGAIN )
/*
* Most of the mixer entries work in backwards. Setting the polarity field
* makes them to work correctly.
*
* The channel numbering used by individual soundcards is not fixed.
* Some cards have assigned different meanings for the AUX1, AUX2
* and LINE inputs. Some have different features...
* The current version doesn't try to compensate this.
*
*/
mixer_ent mix_devices[32][2] = { /* As used in GUS MAX */
MIX_ENT(SOUND_MIXER_VOLUME, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 0, 5, 5, 1, 0, 5),
MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 6, 7, 1, 0, 6),
MIX_ENT(SOUND_MIXER_SPEAKER, 26, 1, 0, 4, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_LINE, 18, 1, 0, 5, 19, 1, 0, 5),
MIX_ENT(SOUND_MIXER_MIC, 0, 0, 5, 1, 1, 0, 5, 1),
MIX_ENT(SOUND_MIXER_CD, 2, 1, 0, 5, 3, 1, 0, 5),
MIX_ENT(SOUND_MIXER_IMIX, 13, 1, 2, 6, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4),
MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_LINE1, 2, 1, 0, 5, 3, 1, 0, 5),
MIX_ENT(SOUND_MIXER_LINE2, 4, 1, 0, 5, 5, 1, 0, 5),
MIX_ENT(SOUND_MIXER_LINE3, 18, 1, 0, 5, 19, 1, 0, 5)
};
mixer_ent opti931_devices[32][2] = { /* for the opti931 */
MIX_ENT(SOUND_MIXER_VOLUME, 22, 1, 1, 5, 23, 1, 1, 5),
MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 1, 4, 5, 1, 1, 4),
MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 5, 7, 1, 0, 5),
MIX_ENT(SOUND_MIXER_SPEAKER, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_LINE, 18, 1, 1, 4, 19, 1, 1, 4),
MIX_ENT(SOUND_MIXER_MIC, 0, 0, 5, 1, 1, 0, 5, 1),
MIX_ENT(SOUND_MIXER_CD, 2, 1, 1, 4, 3, 1, 1, 4),
MIX_ENT(SOUND_MIXER_IMIX, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4),
MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_LINE1, 2, 1, 0, 5, 3, 1, 0, 5),
MIX_ENT(SOUND_MIXER_LINE2, 4, 1, 0, 5, 5, 1, 0, 5),
MIX_ENT(SOUND_MIXER_LINE3, 18, 1, 0, 5, 19, 1, 0, 5)
};
static u_short default_mixer_levels[SOUND_MIXER_NRDEVICES] = {
0x5a5a, /* Master Volume */
0x3232, /* Bass */
0x3232, /* Treble */
0x4b4b, /* FM */
0x4040, /* PCM */
0x4b4b, /* PC Speaker */
0x2020, /* Ext Line */
0x4040, /* Mic */
0x4b4b, /* CD */
0x0000, /* Recording monitor */
0x4b4b, /* SB PCM */
0x4b4b, /* Recording level */
0x2525, /* Input gain */
0x0000, /* Output gain */
/* 0x4040, Line1 */
0x0000, /* Line1 */
0x0000, /* Line2 */
0x1515 /* Line3 (usually line in)*/
};

1080
sys/dev/sound/isa/sb.c Normal file

File diff suppressed because it is too large Load Diff

387
sys/dev/sound/isa/sb.h Normal file
View File

@ -0,0 +1,387 @@
/*
* file: sbcard.h
*/
typedef struct _sbdev_info {
} sbdev_info ;
extern int sbc_major, sbc_minor ;
/*
* sound blaster registers
*/
#define DSP_RESET (io_base + 0x6)
#define DSP_READ (io_base + 0xA)
#define DSP_WRITE (io_base + 0xC)
#define DSP_COMMAND (io_base + 0xC)
#define DSP_STATUS (io_base + 0xC)
#define DSP_DATA_AVAIL (io_base + 0xE)
#define DSP_DATA_AVL16 (io_base + 0xF)
#define MIXER_ADDR (io_base + 0x4)
#define MIXER_DATA (io_base + 0x5)
#define OPL3_LEFT (io_base + 0x0)
#define OPL3_RIGHT (io_base + 0x2)
#define OPL3_BOTH (io_base + 0x8)
/*
* DSP Commands. There are many, and in many cases they are used explicitly
*/
#define DSP_DAC8 0x10 /* direct DAC output */
#define DSP_CMD_DAC8 0x14 /* single cycle 8-bit dma out */
#define DSP_CMD_DAC2 0x16 /* 2-bit adpcm dma out (cont) */
#define DSP_CMD_DAC2S 0x17 /* 2-bit adpcm dma out (start) */
#define DSP_CMD_DAC8_A 0x1c /* auto 8-bit dma out */
#define DSP_CMD_DAC2S_A 0x1f /* auto 2-bit adpcm dma out (start) */
#define DSP_ADC8 0x20 /* direct ADC input */
#define DSP_CMD_ADC8 0x24 /* single cycle 8-bit dma in */
#define DSP_CMD_ADC8_A 0x2c /* auto 8-bit dma out */
#define DSP_CMD_O16 0xb0
#define DSP_CMD_I16 0xb8
#define DSP_CMD_O8 0xc0
#define DSP_CMD_I8 0xc8
#define DSP_MODE_U8MONO 0x00
#define DSP_MODE_U8STEREO 0x20
#define DSP_MODE_S16MONO 0x10
#define DSP_MODE_S16STEREO 0x30
#define DSP_CMD_SPKON 0xD1
#define DSP_CMD_SPKOFF 0xD3
#define DSP_CMD_SPKR(on) (0xD1 | (on ? 0:2))
#define DSP_CMD_DMAON 0xD0 /* ??? the comment says Halt DMA */
#define DSP_CMD_DMAOFF 0xD4 /* ??? comment says continue dma */
#define DSP_CMD_DMAHALT 0xD0
#define DSP_CMD_TCONST 0x40 /* set time constant */
#define DSP_CMD_HSSIZE 0x48 /* high speed dma count */
#define DSP_CMD_HSDAC 0x91 /* high speed dac */
#define DSP_CMD_HSADC 0x99 /* high speed adc */
#define DSP_CMD_GETVER 0xE1
#define DSP_CMD_GETID 0xE7 /* return id bytes */
/* prepare for dma input */
#define DSP_CMD_DMAMODE(stereo, bit16) (0xA0 | (stereo ? 8:0) | (bit16 ? 4:0))
#define DSP_CMD_OUT16 0x41 /* send parms for dma out on sb16 */
#define DSP_CMD_IN16 0x42 /* send parms for dma in on sb16 */
#if 0 /*** unknown ***/
/*
* D9 and D5 are used on the sb16 on close... maybe a reset of
* some subsystem ?
*/
#define DSP_CMD_D9 0xD9
#define DSP_CMD_D5 0xD5
#define DSP_CMD_FA 0xFA /* get version from prosonic*/
#define DSP_CMD_FB 0xFB /* set irq/dma for prosonic*/
#endif
/*
* in fact, for the SB16, dma commands are as follows:
*
* cmd, mode, len_low, len_high.
*
* cmd is a combination of DSP_DMA16 or DSP_DMA8 and
*/
#define DSP_DMA16 0xb0
#define DSP_DMA8 0xc0
# define DSP_F16_DAC 0x00
# define DSP_F16_ADC 0x08
# define DSP_F16_AUTO 0x04
# define DSP_F16_FIFO_ON 0x02
/*
* mode is a combination of the following:
*/
#define DSP_F16_STEREO 0x20
#define DSP_F16_SIGNED 0x10
#define IMODE_NONE 0
#define IMODE_OUTPUT PCM_ENABLE_OUTPUT
#define IMODE_INPUT PCM_ENABLE_INPUT
#define IMODE_INIT 3
#define IMODE_MIDI 4
#define NORMAL_MIDI 0
#define UART_MIDI 1
/*
* values used for bd_flags in SoundBlaster driver
*/
#define BD_F_HISPEED 0x0001 /* doing high speed ... */
#define BD_F_JAZZ16 0x0002 /* jazz16 detected */
#define BD_F_JAZZ16_2 0x0004 /* jazz16 type 2 */
#define BD_F_DUP_MIDI 0x0008 /* duplex midi */
#define BD_F_MIX_MASK 0x0070 /* up to 8 mixers (I know of 3) */
#define BD_F_MIX_CT1335 0x0010 /* CT1335 */
#define BD_F_MIX_CT1345 0x0020 /* CT1345 */
#define BD_F_MIX_CT1745 0x0030 /* CT1745 */
#define BD_F_SB16 0x0100 /* this is a SB16 */
#define BD_F_NOREC 0x0200 /* recording not supported on this board */
#define BD_F_MIDIBUSY 0x0400 /* midi busy */
/*
* sound/sb_mixer.h
*
* Definitions for the SB Pro and SB16 mixers
*
* Copyright by Hannu Savolainen 1993
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met: 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. 2.
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Modified: Hunyue Yau Jan 6 1994 Added defines for the Sound Galaxy NX Pro
* mixer.
*
*/
#define SBPRO_RECORDING_DEVICES \
(SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD)
/* Same as SB Pro, unless I find otherwise */
#define SGNXPRO_RECORDING_DEVICES SBPRO_RECORDING_DEVICES
#define SBPRO_MIXER_DEVICES \
(SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_LINE | SOUND_MASK_MIC | \
SOUND_MASK_CD | SOUND_MASK_VOLUME)
/*
* SG NX Pro has treble and bass settings on the mixer. The 'speaker' channel
* is the COVOX/DisneySoundSource emulation volume control on the mixer. It
* does NOT control speaker volume. Should have own mask eventually?
*/
#define SGNXPRO_MIXER_DEVICES \
(SBPRO_MIXER_DEVICES | SOUND_MASK_BASS | \
SOUND_MASK_TREBLE | SOUND_MASK_SPEAKER )
#define SB16_RECORDING_DEVICES \
(SOUND_MASK_SYNTH | SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD)
#define SB16_MIXER_DEVICES \
(SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_SPEAKER | \
SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | \
SOUND_MASK_IGAIN | SOUND_MASK_OGAIN | \
SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE)
/*
* Mixer registers
*
* NOTE! RECORD_SRC == IN_FILTER
*/
/*
* Mixer registers of SB Pro
*/
#define VOC_VOL 0x04
#define MIC_VOL 0x0A
#define MIC_MIX 0x0A
#define RECORD_SRC 0x0C
#define IN_FILTER 0x0C
#define OUT_FILTER 0x0E
#define MASTER_VOL 0x22
#define FM_VOL 0x26
#define CD_VOL 0x28
#define LINE_VOL 0x2E
#define IRQ_NR 0x80
#define DMA_NR 0x81
#define IRQ_STAT 0x82
/*
* Additional registers on the SG NX Pro
*/
#define COVOX_VOL 0x42
#define TREBLE_LVL 0x44
#define BASS_LVL 0x46
#define FREQ_HI (1 << 3)/* Use High-frequency ANFI filters */
#define FREQ_LOW 0 /* Use Low-frequency ANFI filters */
#define FILT_ON 0 /* Yes, 0 to turn it on, 1 for off */
#define FILT_OFF (1 << 5)
#define MONO_DAC 0x00
#define STEREO_DAC 0x02
/*
* Mixer registers of SB16
*/
#define SB16_IMASK_L 0x3d
#define SB16_IMASK_R 0x3e
#define SB16_OMASK 0x3c
#ifndef __SB_MIXER_C__
mixer_tab sbpro_mix;
mixer_tab sb16_mix;
#ifdef __SGNXPRO__
mixer_tab sgnxpro_mix;
#endif
static u_char sb16_recmasks_L[SOUND_MIXER_NRDEVICES];
static u_char sb16_recmasks_R[SOUND_MIXER_NRDEVICES];
#else /* __SB_MIXER_C__ defined */
mixer_tab sbpro_mix = {
PMIX_ENT(SOUND_MIXER_VOLUME, 0x22, 7, 4, 0x22, 3, 4),
PMIX_ENT(SOUND_MIXER_BASS, 0x00, 0, 0, 0x00, 0, 0),
PMIX_ENT(SOUND_MIXER_TREBLE, 0x00, 0, 0, 0x00, 0, 0),
PMIX_ENT(SOUND_MIXER_SYNTH, 0x26, 7, 4, 0x26, 3, 4),
PMIX_ENT(SOUND_MIXER_PCM, 0x04, 7, 4, 0x04, 3, 4),
PMIX_ENT(SOUND_MIXER_SPEAKER, 0x00, 0, 0, 0x00, 0, 0),
PMIX_ENT(SOUND_MIXER_LINE, 0x2e, 7, 4, 0x2e, 3, 4),
PMIX_ENT(SOUND_MIXER_MIC, 0x0a, 2, 3, 0x00, 0, 0),
PMIX_ENT(SOUND_MIXER_CD, 0x28, 7, 4, 0x28, 3, 4),
PMIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0),
PMIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0),
PMIX_ENT(SOUND_MIXER_RECLEV, 0x00, 0, 0, 0x00, 0, 0)
};
#ifdef __SGNXPRO__
mixer_tab sgnxpro_mix = {
PMIX_ENT(SOUND_MIXER_VOLUME, 0x22, 7, 4, 0x22, 3, 4),
PMIX_ENT(SOUND_MIXER_BASS, 0x46, 2, 3, 0x00, 0, 0),
PMIX_ENT(SOUND_MIXER_TREBLE, 0x44, 2, 3, 0x00, 0, 0),
PMIX_ENT(SOUND_MIXER_SYNTH, 0x26, 7, 4, 0x26, 3, 4),
PMIX_ENT(SOUND_MIXER_PCM, 0x04, 7, 4, 0x04, 3, 4),
PMIX_ENT(SOUND_MIXER_SPEAKER, 0x42, 2, 3, 0x00, 0, 0),
PMIX_ENT(SOUND_MIXER_LINE, 0x2e, 7, 4, 0x2e, 3, 4),
PMIX_ENT(SOUND_MIXER_MIC, 0x0a, 2, 3, 0x00, 0, 0),
PMIX_ENT(SOUND_MIXER_CD, 0x28, 7, 4, 0x28, 3, 4),
PMIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0),
PMIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0),
PMIX_ENT(SOUND_MIXER_RECLEV, 0x00, 0, 0, 0x00, 0, 0),
PMIX_ENT(SOUND_MIXER_IGAIN, 0x00, 0, 0, 0x00, 0, 0),
PMIX_ENT(SOUND_MIXER_OGAIN, 0x00, 0, 0, 0x00, 0, 0)
};
#endif
mixer_tab sb16_mix = {
PMIX_ENT(SOUND_MIXER_VOLUME, 0x30, 3, 5, 0x31, 3, 5),
PMIX_ENT(SOUND_MIXER_BASS, 0x46, 4, 4, 0x47, 4, 4),
PMIX_ENT(SOUND_MIXER_TREBLE, 0x44, 4, 4, 0x45, 4, 4),
PMIX_ENT(SOUND_MIXER_SYNTH, 0x34, 3, 5, 0x35, 3, 5),
PMIX_ENT(SOUND_MIXER_PCM, 0x32, 3, 5, 0x33, 3, 5),
PMIX_ENT(SOUND_MIXER_SPEAKER, 0x3b, 6, 2, 0x00, 0, 0),
PMIX_ENT(SOUND_MIXER_LINE, 0x38, 3, 5, 0x39, 3, 5),
PMIX_ENT(SOUND_MIXER_MIC, 0x3a, 3, 5, 0x00, 0, 0),
PMIX_ENT(SOUND_MIXER_CD, 0x36, 3, 5, 0x37, 3, 5),
PMIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0),
PMIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0),
PMIX_ENT(SOUND_MIXER_RECLEV, 0x3f, 6, 2, 0x40, 6, 2), /* Obsol,Use IGAIN*/
PMIX_ENT(SOUND_MIXER_IGAIN, 0x3f, 6, 2, 0x40, 6, 2),
PMIX_ENT(SOUND_MIXER_OGAIN, 0x41, 6, 2, 0x42, 6, 2)
};
#ifdef SM_GAMES /* Master volume is lower and PCM & FM
* volumes higher than with SB Pro. This
* improves the sound quality */
static u_short levels[SOUND_MIXER_NRDEVICES] =
{
0x2020, /* Master Volume */
0x4b4b, /* Bass */
0x4b4b, /* Treble */
0x6464, /* FM */
0x6464, /* PCM */
0x4b4b, /* PC Speaker */
0x4b4b, /* Ext Line */
0x0000, /* Mic */
0x4b4b, /* CD */
0x4b4b, /* Recording monitor */
0x4b4b, /* SB PCM */
0x4b4b, /* Recording level */
0x4b4b, /* Input gain */
0x4b4b}; /* Output gain */
#else /* If the user selected just plain SB Pro */
static u_short levels[SOUND_MIXER_NRDEVICES] =
{
0x5a5a, /* Master Volume */
0x4b4b, /* Bass */
0x4b4b, /* Treble */
0x4b4b, /* FM */
0x4b4b, /* PCM */
0x4b4b, /* PC Speaker */
0x4b4b, /* Ext Line */
0x1010, /* Mic */
0x4b4b, /* CD */
0x4b4b, /* Recording monitor */
0x4b4b, /* SB PCM */
0x4b4b, /* Recording level */
0x4b4b, /* Input gain */
0x4b4b}; /* Output gain */
#endif /* SM_GAMES */
static u_char sb16_recmasks_L[SOUND_MIXER_NRDEVICES] =
{
0x00, /* SOUND_MIXER_VOLUME */
0x00, /* SOUND_MIXER_BASS */
0x00, /* SOUND_MIXER_TREBLE */
0x40, /* SOUND_MIXER_SYNTH */
0x00, /* SOUND_MIXER_PCM */
0x00, /* SOUND_MIXER_SPEAKER */
0x10, /* SOUND_MIXER_LINE */
0x01, /* SOUND_MIXER_MIC */
0x04, /* SOUND_MIXER_CD */
0x00, /* SOUND_MIXER_IMIX */
0x00, /* SOUND_MIXER_ALTPCM */
0x00, /* SOUND_MIXER_RECLEV */
0x00, /* SOUND_MIXER_IGAIN */
0x00 /* SOUND_MIXER_OGAIN */
};
static u_char sb16_recmasks_R[SOUND_MIXER_NRDEVICES] =
{
0x00, /* SOUND_MIXER_VOLUME */
0x00, /* SOUND_MIXER_BASS */
0x00, /* SOUND_MIXER_TREBLE */
0x20, /* SOUND_MIXER_SYNTH */
0x00, /* SOUND_MIXER_PCM */
0x00, /* SOUND_MIXER_SPEAKER */
0x08, /* SOUND_MIXER_LINE */
0x01, /* SOUND_MIXER_MIC */
0x02, /* SOUND_MIXER_CD */
0x00, /* SOUND_MIXER_IMIX */
0x00, /* SOUND_MIXER_ALTPCM */
0x00, /* SOUND_MIXER_RECLEV */
0x00, /* SOUND_MIXER_IGAIN */
0x00 /* SOUND_MIXER_OGAIN */
};
/*
* Recording sources (SB Pro)
*/
#endif /* __SB_MIXER_C__ */
#define SRC_MIC 1 /* Select Microphone recording source */
#define SRC_CD 3 /* Select CD recording source */
#define SRC_LINE 7 /* Use Line-in for recording source */

1080
sys/dev/sound/isa/sb16.c Normal file

File diff suppressed because it is too large Load Diff

1080
sys/dev/sound/isa/sb8.c Normal file

File diff suppressed because it is too large Load Diff

78
sys/i386/isa/snd/README Normal file
View File

@ -0,0 +1,78 @@
--- A new FreeBSD sound driver ---
by Luigi Rizzo (luigi@iet.unipi.it)
This is an experimental version of the sound driver for FreeBSD.
I have almost completely rewritten the main parts of the code,
starting from the Voxware 3.5-alpha release with patches from
Amancio Hasty. The only file which is largely similar to the original
ones is "soundcard.h" (for compatibility reasons, since it contains
the definition of all the ioctls).
Visit http://www.iet.unipi.it/~luigi/FreeBSD.html for the latest
information on the drivers. There you can obtain the latest source
package that includes documentation on the design of the driver.
CARD SUPPORT INFORMATION:
For PnP cards, I also include the vendor_id and serial numbers of
cards I have encountered.
CS4236: PnP id 0x3642630e
works like a charm. All modes, including full duplex, supported in
MSS mode.
CS4237: PnP id 0x3742630e
I had early reports of success with this board, which is almost
the same as the CS4236.
CS4232: PnP id 0x3242630e
this chip is reported as broken in the OSS documentation. As a
matter of fact, on my Intel Zappa motherboard, I have problems in
make it use the secondary DMA channel. I have it working in
half duplex (both capture and playback) in SB3.2 emulation,
and working in playback mode in MSS emulation.
OPTi931: PnP id 0x3109143e
The data sheets of this chip are very cryptic. I have it working
in full duplex in all modes _except_ capture of uLAW/ALAW data.
I am strongly convinced of a bug in the chip. I have sent email
to OPTI but got no reply so far. In SB emulation mode the
driver does not work yet (maybe I do not initialize it the
right way).
Another bug seems to affect full duplex operation -- it appears
that at times DMA transfer are requested but not counted by
the device. In normal DMA mode this causes deadlocks. The only
solution I have found is to fetch the count from the ISA DMA
registers, but this does not seem to work very well either.
SB16 PnP: PnP id 0xXX008c0e
There are many such cards (plain SB16 PnP, AWE32, AWE64, Vibra16,
etc. all differing in the PnP id (and with different synthesis
devices, which we do not support anyways).
Since 970903 the driver supports them all, both capture and
playback, 8 and 16 bits.
GusPnP: PnP id 0x0100561e
I have code to recognize the board as MSS, but have not tested it
since I don't own the board. Hopefully someone will test it soon.
OPTI925: PnP id 0x2509143e
there is code to recognize it as a SB clone. I have reports that
it probes ok, but not sure if it works.
OPTI930:
should work as an MSS clone, but support for it is not implemented
yet.

1509
sys/i386/isa/snd/ad1848.c Normal file

File diff suppressed because it is too large Load Diff

262
sys/i386/isa/snd/clones.c Normal file
View File

@ -0,0 +1,262 @@
/*
* sound/clones.c
*
* init code for enabling clone cards to work in sb/mss emulation.
*
* Note -- this code is currently unused!
*
* Copyright by Luigi Rizzo - 1997
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met: 1. Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer. 2. Redistributions in binary form must reproduce the
* above copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* This file has been written using information from various sources
* in the Voxware 3.5 distribution.
*/
#include <i386/isa/snd/sound.h>
#if NPCM > 0
/*
* Known clones card include:
*
* Trix (emulating MSS)
* MAD16 (emulating MSS)
* OPTi930 -- same as the OPTi931, but no PnP ?
*/
#ifdef JAZZ16
/*
* Initialization of a Media Vision ProSonic 16 Soundcard. The function
* initializes a ProSonic 16 like PROS.EXE does for DOS. It sets the base
* address, the DMA-channels, interrupts and enables the joystickport.
*
* Also used by Jazz 16 (same card, different name)
*
* written 1994 by Rainer Vranken E-Mail:
* rvranken@polaris.informatik.uni-essen.de
*/
#ifdef SM_WAVE
/*
* Logitech Soundman Wave detection and initialization by Hannu Savolainen.
*
* There is a microcontroller (8031) in the SM Wave card for MIDI emulation.
* it's located at address MPU_BASE+4. MPU_BASE+7 is a SM Wave specific
* control register for MC reset, SCSI, OPL4 and DSP (future expansion)
* address decoding. Otherwise the SM Wave is just a ordinary MV Jazz16 based
* soundcard.
*/
static void
smw_putmem(int base, int addr, u_char val)
{
u_long s;
s = spltty();
outb(base + 1, addr & 0xff); /* Low address bits */
outb(base + 2, addr >> 8); /* High address bits */
outb(base, val); /* Data */
splx(s);
}
static u_char
smw_getmem(int base, int addr)
{
u_long s;
u_char val;
s = spltty();
outb(base + 1, addr & 0xff); /* Low address bits */
outb(base + 2, addr >> 8); /* High address bits */
val = inb(base); /* Data */
splx(s);
return val;
}
#ifdef SMW_MIDI0001_INCLUDED
#include </sys/i386/isa/snd/smw-midi0001.h>
#else
u_char *smw_ucode = NULL;
int smw_ucodeLen = 0;
#endif /* SWM_MIDI0001_INCLUDED */
static int
initialize_smw(int mpu_base)
{
int i, mp_base = mpu_base + 4; /* Microcontroller base */
u_char control;
/*
* Reset the microcontroller so that the RAM can be accessed
*/
control = inb(mpu_base + 7);
outb(mpu_base + 7, control | 3); /* Set last two bits to 1 (?) */
outb(mpu_base + 7, (control & 0xfe) | 2); /* xxxxxxx0 resets the mc */
DELAY(3000); /* Wait at least 1ms */
outb(mpu_base + 7, control & 0xfc); /* xxxxxx00 enables RAM */
/*
* Detect microcontroller by probing the 8k RAM area
*/
smw_putmem(mp_base, 0, 0x00);
smw_putmem(mp_base, 1, 0xff);
DELAY(10);
if (smw_getmem(mp_base, 0) != 0x00 || smw_getmem(mp_base, 1) != 0xff) {
printf("\nSM Wave: No microcontroller RAM detected (%02x, %02x)\n",
smw_getmem(mp_base, 0), smw_getmem(mp_base, 1));
return 0; /* No RAM */
}
/*
* There is RAM so assume it's really a SM Wave
*/
if (smw_ucodeLen > 0) {
if (smw_ucodeLen != 8192) {
printf("\nSM Wave: Invalid microcode (MIDI0001.BIN) length\n");
return 1;
}
/*
* Download microcode
*/
for (i = 0; i < 8192; i++)
smw_putmem(mp_base, i, smw_ucode[i]);
/*
* Verify microcode
*/
for (i = 0; i < 8192; i++)
if (smw_getmem(mp_base, i) != smw_ucode[i]) {
printf("SM Wave: Microcode verification failed\n");
return 0;
}
}
control = 0;
#ifdef SMW_SCSI_IRQ
/*
* Set the SCSI interrupt (IRQ2/9, IRQ3 or IRQ10). The SCSI interrupt
* is disabled by default.
*
* Btw the Zilog 5380 SCSI controller is located at MPU base + 0x10.
*/
{
static u_char scsi_irq_bits[] =
{0, 0, 3, 1, 0, 0, 0, 0, 0, 3, 2, 0, 0, 0, 0, 0};
control |= scsi_irq_bits[SMW_SCSI_IRQ] << 6;
}
#endif
#ifdef SMW_OPL4_ENABLE
/*
* Make the OPL4 chip visible on the PC bus at 0x380.
*
* There is no need to enable this feature since VoxWare doesn't support
* OPL4 yet. Also there is no RAM in SM Wave so enabling OPL4 is
* pretty useless.
*/
control |= 0x10; /* Uses IRQ12 if bit 0x20 == 0 */
/* control |= 0x20; Uncomment this if you want to use IRQ7 */
#endif
outb(mpu_base + 7, control | 0x03); /* xxxxxx11 restarts */
return 1;
}
#endif
/*
* this is only called during the probe. Variables are found in
* sb_probed.
*/
static sbdev_info sb_probed ;
static int
initialize_ProSonic16(snddev_info *d)
{
int x;
static u_char int_translat[16] =
{0, 0, 2, 3, 0, 1, 0, 4, 0, 2, 5, 0, 0, 0, 0, 6},
dma_translat[8] =
{0, 1, 0, 2, 0, 3, 0, 4};
struct address_info *mpu_config;
int mpu_base, mpu_irq;
if ((mpu_config = NULL)) {
mpu_base = mpu_config->io_base;
mpu_irq = mpu_config->irq;
} else {
mpu_base = mpu_irq = 0;
}
outb(0x201, 0xAF); /* ProSonic/Jazz16 wakeup */
DELAY(15000); /* wait at least 10 milliseconds */
outb(0x201, 0x50);
outb(0x201, (sb_probed.io_base & 0x70) | ((mpu_base & 0x30) >> 4));
if (sb_reset_dsp(sb_probed.io_base)) { /* OK. We have at least a SB */
/* Check the version number of ProSonic (I guess) */
if (!sb_cmd(sb_probed.io_base, 0xFA))
return 1;
if (sb_get_byte(sb_probed.io_base) != 0x12)
return 1;
if (sb_cmd(sb_probed.io_base, 0xFB) && /* set DMA and irq */
sb_cmd(sb_probed.io_base,
(dma_translat[JAZZ_DMA16]<<4)|dma_translat[sb_probed.dma1]) &&
sb_cmd(sb_probed.io_base,
(int_translat[mpu_irq]<<4)|int_translat[sb_probed.irq])) {
d->bf_flags |= BD_F_JAZZ16 ;
if (mpu_base == 0)
printf("Jazz16: No MPU401 devices configured "
"- MIDI port not initialized\n");
#ifdef SM_WAVE
if (mpu_base != 0)
if (initialize_smw(mpu_base))
d->bf_flags |= BD_F_JAZZ16_2 ;
#endif
/* sb_dsp_disable_midi(); */
}
return 1; /* There was at least a SB */
}
return 0; /* No SB or ProSonic16 detected */
}
#endif /* ifdef JAZZ16 */
#endif /* NPCM */

864
sys/i386/isa/snd/dmabuf.c Normal file
View File

@ -0,0 +1,864 @@
/*
* snd/dmabuf.c
*
* New DMA routines -- Luigi Rizzo, 19 jul 97
* This file implements the new DMA routines for the sound driver.
*
* Copyright by Luigi Rizzo - 1997
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met: 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. 2.
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <i386/isa/snd/sound.h>
#include <i386/isa/snd/ulaw.h>
#define MIN_CHUNK_SIZE 256 /* for uiomove etc. */
#define DMA_ALIGN_BITS 2 /* i.e. 4 bytes */
#define DMA_ALIGN_THRESHOLD (1<< DMA_ALIGN_BITS)
#define DMA_ALIGN_MASK (~ (DMA_ALIGN_THRESHOLD - 1))
static void dsp_wr_dmadone(snddev_info *d);
static void dsp_rd_dmadone(snddev_info *d);
/*
*
SOUND OUTPUT
We use a circular buffer to store samples directed to the DAC.
The buffer is split into three variable-size regions, each identified
by an offset in the buffer (dp,rp,fp) and a lenght (dl,rl,fl).
0 dp,dl rp,rl fp,fl bufsize
|__________|_____>______|_____________|________|
FREE DMA READY FREE
READY region: contains data written from the process and ready
to be sent to the DAC;
FREE region: is the empty region of the buffer, where a process
can write new data.
DMA region: contains data being sent to the DAC by the DMA engine.
the actual boundary between the FREE and READY regions is in
fact within the DMA region (indicated by the > symbol above),
and advances as the DMA engine makes progress.
Both the "READY" and "FREE" regions can wrap around the end of the
buffer. The "DMA" region can only wrap if AUTO DMA is used, otherwise
it cannot cross the end of the buffer.
Since dl can be updated on the fly, dl0 marks the value used when the
operation was started. When using AUTO DMA, bufsize-(count in the ISA DMA
register) directly reflects the position of dp.
At initialization, DMA and READY are empty, and FREE takes all the
available space:
dp = rp = fp = 0 ; -- beginning of buffer
dl0 = dl = 0 ; -- meaning no dma activity
rl = 0 ; -- meaning no data ready
fl = bufsize ;
The DMA region is also empty whenever there is no DMA activity, for
whatever reason (e.g. no ready data, or output is paused).
The code works as follows: the user write routine dsp_write_body()
fills up the READY region with new data (reclaiming space from the
FREE region) and starts the write DMA engine if inactive (activity
is indicated by d->flags & SND_F_WR_DMA ). The size of each
DMA transfer is chosen according to a series of rules which will be
discussed later. When a DMA transfer is complete, an interrupt causes
dsp_wrintr() to be called which empties the DMA region, extends
the FREE region and possibly starts the next transfer.
In some cases, the code tries to track the current status of DMA
operations by calling isa_dmastatus() and advancing the boundary
between FREE and DMA regions accordingly.
The size of a DMA transfer is selected according to the following
rules:
1. when not using AUTO DMA, do not wrap around the end of the
buffer, and do not let fp move too close to the end of the
buffer;
2. do not use more than half of the buffer size.
This serves to allow room for a next write operation concurrent
with the dma transfer, and to reduce the time which is necessary
to wait before a pending dma will end (optionally, the max
size could be further limited to a fixed amount of play time,
depending on number of channels, sample size and sample speed);
3. use the current blocksize (either specified by the user, or
corresponding roughly to 0.25s of data);
*
*/
/*
* dsp_wr_dmadone moves the write DMA region into the FREE region.
* It is assumed to be called at spltty() and with a write dma
* previously started.
*/
static void
dsp_wr_dmadone(snddev_info *d)
{
snd_dbuf *b = & (d->dbuf_out) ;
isa_dmadone(B_WRITE, b->buf + b->dp, b->dl, d->dma1);
b->fl += b->dl ; /* make dl bytes free */
/*
* XXX here it would be more efficient to record if there
* actually is a sleeping process, but this should still work.
*/
wakeup(b); /* wakeup possible sleepers */
if (d->wsel.si_pid &&
( !(d->flags & SND_F_HAS_SIZE) || b->fl >= d->play_blocksize ) )
selwakeup( & d->wsel );
b->dp = b->rp ;
b->dl0 = b->dl = 0 ;
}
/*
* The following function tracks the status of a (write) dma transfer,
* and moves the boundary between the FREE and the DMA regions.
* It works under the following assumptions:
* - the DMA engine is running;
* - the routine is called with interrupts blocked.
* BEWARE: when using AUTO DMA, dl can go negative! We assume that it
* does not wrap!
*/
void
dsp_wr_dmaupdate(snddev_info *d)
{
snd_dbuf *b = & (d->dbuf_out) ;
int tmp;
tmp = b->dl - isa_dmastatus1(d->dma1) ;
tmp &= DMA_ALIGN_MASK; /* align... */
if (tmp > 0) { /* we have some new data */
b->dp += tmp;
if (b->dp >= b->bufsize)
b->dp -= b->bufsize;
b->dl -= tmp ;
b->fl += tmp ;
}
}
/*
* Write interrupt routine. Can be called from other places, but
* with interrupts disabled.
*/
void
dsp_wrintr(snddev_info *d)
{
snd_dbuf *b = & (d->dbuf_out) ;
int cb_reason = SND_CB_WR ;
DEB(printf("dsp_wrintr: start on dl %d, rl %d, fl %d\n",
b->dl, b->rl, b->fl));
if (d->flags & SND_F_WR_DMA) { /* dma was active */
b->int_count++;
d->flags &= ~SND_F_WR_DMA;
cb_reason = SND_CB_WR | SND_CB_RESTART ;
dsp_wr_dmadone(d);
} else
cb_reason = SND_CB_WR | SND_CB_START ;
/*
* start another dma operation only if have ready data in the
* buffer, there is no pending abort, have a full-duplex device
* (dma1 != dma2) or have half duplex device and there is no
* pending op on the other side.
*
* Force transfer to be aligned to a boundary of 4, which is
* needed when doing stereo and 16-bit. We could make this
* adaptive, but why bother for now...
*/
if ( b->rl >= DMA_ALIGN_THRESHOLD &&
! (d->flags & SND_F_ABORTING) &&
( (d->dma1 != d->dma2) || ! (d->flags & SND_F_READING) ) ) {
b->dl = min(b->rl, b->bufsize - b->rp ) ; /* do not wrap */
b->dl = min(b->dl, d->play_blocksize ); /* avoid too large transfer */
b->dl &= DMA_ALIGN_MASK ; /* realign things */
b->rl -= b->dl ;
b->rp += b->dl ;
if (b->rp == b->bufsize)
b->rp = 0 ;
/*
* now try to avoid too small dma transfers in the near future.
* This can happen if I let rp start too close to the end of
* the buffer. If this happens, and have enough data, try to
* split the available block in two approx. equal parts.
* XXX this code can go when we use auto dma.
*/
if (b->bufsize - b->rp < MIN_CHUNK_SIZE &&
b->bufsize - b->dp > 2*MIN_CHUNK_SIZE) {
b->dl = (b->bufsize - b->dp) / 2;
b->dl &= ~3 ; /* align to a boundary of 4 */
b->rl += (b->rp - (b->dp + b->dl) ) ;
b->rp = b->dp + b->dl ; /* no need to check for wraps */
}
/*
* how important is the order of operations ?
*/
if (b->dl == 0) {
printf("ouch... want to start for 0 bytes!\n");
goto ferma;
}
b->dl0 = b->dl ; /* XXX */
if (d->callback)
d->callback(d, cb_reason ); /* start/restart dma */
isa_dmastart( B_WRITE , b->buf + b->dp, b->dl, d->dma1);
d->flags |= SND_F_WR_DMA;
} else {
ferma:
if (d->callback && (cb_reason & SND_CB_REASON_MASK) == SND_CB_RESTART )
d->callback(d, SND_CB_WR | SND_CB_STOP ); /* stop dma */
/*
* if switching to read, should start the read dma...
*/
if ( d->dma1 == d->dma2 && (d->flags & SND_F_READING) )
dsp_rdintr(d);
DEB(printf("cannot start wr-dma flags 0x%08x dma_dl %d rl %d\n",
d->flags, isa_dmastatus1(d->dma1), b->rl));
}
}
/*
* user write routine
*
* advance the boundary between READY and FREE, fill the space with
* uiomove(), and possibly start DMA. Do the above until the transfer
* is complete.
*
* To minimize latency in case a pending DMA transfer is about to end,
* we do the transfer in pieces of increasing sizes, extending the
* READY area at every checkpoint. In the (necessary) assumption that
* memory bandwidth is larger than the rate at which the dma consumes
* data, we reduce the latency to something proportional to the length
* of the first piece, while keeping the overhead low and being able
* to feed the DMA with large blocks.
*/
int
dsp_write_body(snddev_info *d, struct uio *buf)
{
int timeout = 1, n, l, bsz, ret = 0 ;
long s;
snd_dbuf *b = & (d->dbuf_out) ;
/* assume d->flags |= SND_F_WRITING ; has been done before */
/*
* bsz is the max size for the next transfer. If the dma was
* idle, we want it as large as possible. Otherwise, start with
* a small block to avoid underruns if we are close to the end of
* the previous operation.
*/
bsz = (d->flags & SND_F_WR_DMA) ? MIN_CHUNK_SIZE : b->bufsize ;
while ( n = buf->uio_resid ) {
l = min (n, bsz); /* at most n bytes ... */
s = spltty(); /* no interrupts here ... */
/*
* if i) the dma engine is running, ii) we do not have enough space
* in the FREE region, and iii) the current DMA transfer might let
* us complete the _whole_ transfer without sleeping, or we are doing
* non-blocking I/O, then try to extend the FREE region.
* Otherwise do not bother, we will need to sleep anyways, and
* make the timeout longer.
*/
if ( d->flags & SND_F_WR_DMA && b->fl < l &&
( b->fl + b->dl >= n || d->flags & SND_F_NBIO ) )
dsp_wr_dmaupdate(d); /* should really change timeout... */
else
timeout = hz;
l = min( l, b->fl ); /* no more than avail. space */
l = min( l, b->bufsize - b->fp ); /* do not wrap ... */
DEB(printf("dsp_write_body: prepare %d bytes out of %d\n", l,n));
/*
* at this point, we assume that if l==0 the dma engine
* must (or will, in cause it is paused) be running.
*/
if (l == 0) { /* no space, must sleep */
if (d->flags & SND_F_NBIO) {
/* unless of course we are doing non-blocking i/o */
splx(s);
break;
}
DEB(printf("dsp_write_body: l=0, (fl %d) sleeping\n", b->fl));
ret = tsleep( (caddr_t)b, PRIBIO|PCATCH, "dspwr", timeout);
if (ret == EINTR)
d->flags |= SND_F_ABORTING ;
splx(s);
if (ret == ERESTART || ret == EINTR)
break ;
timeout = min(2*timeout, hz);
continue;
}
splx(s);
timeout = 1 ; /* we got some data... */
/*
* copy data to the buffer, and possibly do format
* conversions (here, from ULAW to U8).
* NOTE: I can use fp here since it is not modified by the
* interrupt routines.
*/
ret = uiomove(b->buf + b->fp, l, buf) ;
if (ret !=0 ) { /* an error occurred ... */
printf("uiomove error %d\n", ret);
break ;
}
if (d->flags & SND_F_XLAT8)
translate_bytes(ulaw_dsp, b->buf + b->fp, l);
s = spltty(); /* no interrupts here ... */
b->rl += l ; /* this more ready bytes */
b->fl -= l ; /* this less free bytes */
b->fp += l ;
if (b->fp >= b->bufsize) /* handle wraps */
b->fp -= b->bufsize ;
if ( !(d->flags & SND_F_WR_DMA) ) {/* dma was idle, restart it */
if ( (d->flags & (SND_F_INIT|SND_F_WR_DMA|SND_F_RD_DMA)) ==
SND_F_INIT) {
/* want to init but no pending DMA activity */
splx(s);
d->callback(d, SND_CB_INIT); /* this is slow! */
s = spltty();
}
dsp_wrintr(d) ;
}
splx(s) ;
bsz = min(b->bufsize, bsz*2);
}
s = spltty(); /* no interrupts here ... */
d->flags &= ~SND_F_WRITING ;
if (d->flags & SND_F_ABORTING) {
d->flags &= ~SND_F_ABORTING;
splx(s);
dsp_wrabort(d);
}
splx(s) ;
return ret ;
}
/*
* SOUND INPUT
*
The input part is similar to the output one. The only difference is in
the ordering of regions, which is the following:
0 rp,rl dp,dl fp,fl bufsize
|__________|____________|______>______|________|
FREE READY DMA FREE
and the fact that input data are in the READY region.
At initialization, as for the write routine, DMA and READY are empty,
and FREE takes all the space:
dp = rp = fp = 0 ; -- beginning of buffer
dl0 = dl = 0 ; -- meaning no dma activity
rl = 0 ; -- meaning no data ready
fl = bufsize ;
Operation is as follows: upon user read (dsp_read_body()) a DMA read
is started if not already active (marked by d->flags & SND_F_RD_DMA),
then as soon as data are available in the READY region they are
transferred to the user buffer, thus advancing the boundary between FREE
and READY. Upon interrupts, caused by a completion of a DMA transfer,
the READY region is extended and possibly a new transfer is started.
When necessary, isa_dmastatus() is called to advance the boundary
between READY and DMA to the real position.
The rules to choose the size of the new DMA area are similar to
the other case, i.e:
1. if not using AUTO mode, do not wrap around the end of the
buffer, and do not let fp move too close to the end of the
buffer;
2. do not use more than half the buffer size; this serves to
leave room for the next dma operation.
3. use the default blocksize, either user-specified, or
corresponding to 0.25s of data;
*
*/
/*
* dsp_rd_dmadone moves bytes in the input buffer from DMA region to
* READY region. We assume it is called at spltty() and with dl>0
*/
static void
dsp_rd_dmadone(snddev_info *d)
{
snd_dbuf *b = & (d->dbuf_in) ;
isa_dmadone(B_READ, b->buf + b->dp, b->dl, d->dma2);
b->rl += b->dl ; /* make dl bytes available */
wakeup(b) ; /* wakeup possibly sleeping processes */
if (d->rsel.si_pid &&
( !(d->flags & SND_F_HAS_SIZE) || b->rl >= d->rec_blocksize ) )
selwakeup( & d->rsel );
b->dp = b->fp ;
b->dl0 = b->dl = 0 ;
}
/*
* The following function tracks the status of a (read) dma transfer,
* and moves the boundary between the READY and the DMA regions.
* It works under the following assumptions:
* - the DMA engine is running;
* - the function is called with interrupts blocked.
*/
void
dsp_rd_dmaupdate(snddev_info *d)
{
snd_dbuf *b = & (d->dbuf_in) ;
int tmp ;
tmp = b->dl - isa_dmastatus1(d->dma2) ;
tmp &= DMA_ALIGN_MASK; /* align... */
if (tmp > 0) { /* we have some data */
b->dp += tmp;
if (b->dp >= b->bufsize)
b->dp -= b->bufsize;
b->dl -= tmp ;
b->rl += tmp ;
}
}
/*
* read interrupt routine. Must be called with interrupts blocked.
*/
void
dsp_rdintr(snddev_info *d)
{
snd_dbuf *b = & (d->dbuf_in) ;
int cb_reason = SND_CB_RD ;
DEB(printf("dsp_rdintr: start dl = %d fp %d blocksize %d\n",
b->dl, b->fp, d->rec_blocksize));
if (d->flags & SND_F_RD_DMA) { /* dma was active */
b->int_count++;
d->flags &= ~SND_F_RD_DMA;
cb_reason = SND_CB_RD | SND_CB_RESTART ;
dsp_rd_dmadone(d);
} else
cb_reason = SND_CB_RD | SND_CB_START ;
/*
* Same checks as in the write case (mutatis mutandis) to decide
* whether or not to restart a dma transfer.
*/
if ( b->fl >= DMA_ALIGN_THRESHOLD &&
((d->flags & (SND_F_ABORTING|SND_F_CLOSING)) == 0) &&
( (d->dma1 != d->dma2) || ( (d->flags & SND_F_WRITING) == 0) ) ) {
b->dl = min(b->fl, b->bufsize - b->fp ) ; /* do not wrap */
b->dl = min(b->dl, d->rec_blocksize );
b->dl &= DMA_ALIGN_MASK ; /* realign sizes */
b->fl -= b->dl ;
b->fp += b->dl ;
if (b->fp == b->bufsize)
b->fp = 0 ;
/*
* now try to avoid too small dma transfers in the near future.
* This can happen if I let fp start too close to the end of
* the buffer. If this happens, and have enough data, try to
* split the available block in two approx. equal parts.
*/
if (b->bufsize - b->fp < MIN_CHUNK_SIZE &&
b->bufsize - b->dp > 2*MIN_CHUNK_SIZE) {
b->dl = (b->bufsize - b->dp) / 2;
b->dl &= DMA_ALIGN_MASK ; /* align to multiples of 3 */
b->fl += (b->fp - (b->dp + b->dl) ) ;
b->fp = b->dp + b->dl ; /* no need to check that fp wraps */
}
if (b->dl == 0) {
printf("ouch! want to read 0 bytes\n");
goto ferma;
}
b->dl0 = b->dl ; /* XXX */
if (d->callback)
d->callback(d, cb_reason); /* restart_dma(); */
isa_dmastart( B_READ , b->buf + b->dp, b->dl, d->dma2);
d->flags |= SND_F_RD_DMA;
} else {
ferma:
if (d->callback && (cb_reason & SND_CB_REASON_MASK) == SND_CB_RESTART)
d->callback(d, SND_CB_RD | SND_CB_STOP);
/*
* if switching to write, start write dma engine
*/
if ( d->dma1 == d->dma2 && (d->flags & SND_F_WRITING) )
dsp_wrintr(d) ;
DEB(printf("cannot start rd-dma flags 0x%08x dma_dl %d fl %d\n",
d->flags, isa_dmastatus1(d->dma2), b->fl));
}
}
/*
* body of user-read routine
*
* Start DMA if not active; wait for READY not empty.
* Transfer data from READY region using uiomove(), advance boundary
* between FREE and READY. Repeat until transfer is complete.
*
* To avoid excessive latency in freeing up space for the DMA
* engine, transfers are done in blocks of increasing size, so that
* the latency is proportional to the size of the smallest block, but
* we have a low overhead and are able to feed the dma engine with
* large blocks.
*
* When we enter this routine, we assume that d->flags |= SND_F_READING
* was done before.
*
* NOTE: in the current version, read will not return more than
* blocksize bytes at once (unless more are already available), to
* avoid that requests using very large buffers block for too long.
*/
int
dsp_read_body(snddev_info *d, struct uio *buf)
{
int limit, l, n, bsz, ret = 0 ;
long s;
snd_dbuf *b = & (d->dbuf_in) ;
int timeout = 1 ; /* counter of how many ticks we sleep */
/*
* "limit" serves to return after at most one blocksize of data
* (unless more are already available). Otherwise, things like
* cat /dev/audio would use a 64K buffer and would start returning
* data after a _very_ long time...
* Note -- some applications depend on reads not returning short
* blocks. But I believe these apps are broken, since interrupted
* system calls might return short reads anyways, and the
* application should better check that.
*/
if (buf->uio_resid > d->rec_blocksize)
limit = buf->uio_resid - d->rec_blocksize;
else
limit = 0;
bsz = MIN_CHUNK_SIZE ; /* the current transfer (doubles at each step) */
while ( (n = buf->uio_resid) > limit ) {
DEB(printf("dsp_read_body: start waiting for %d bytes\n", n));
/*
* here compute how many bytes to transfer, enforcing various
* limitations:
*/
l = min (n, bsz); /* 1': at most bsz bytes ... */
s = spltty(); /* no interrupts here ! */
/*
* if i) the dma engine is running, ii) we do not have enough
* ready bytes, and iii) the current DMA transfer could give
* us what we need, or we are doing non-blocking IO, then try
* to extend the READY region.
* Otherwise do not bother, we will need to sleep anyways,
* and make the timeout longer.
*/
if ( d->flags & SND_F_RD_DMA && b->rl < l &&
( d->flags & SND_F_NBIO || b->rl + b->dl >= n - limit ) )
dsp_rd_dmaupdate(d);
else
timeout = hz ;
l = min( l, b->rl ); /* 2': no more than avail. data */
l = min( l, b->bufsize - b->rp ); /* 3': do not wrap buffer. */
/* the above limitation can be removed if we use auto DMA
* on the ISA controller. But then we have to make a check
* when doing the uiomove...
*/
if ( !(d->flags & SND_F_RD_DMA) ) { /* dma was idle, start it */
/*
* There are two reasons the dma can be idle: either this
* is the first read, or the buffer has become full. In
* the latter case, the dma cannot be restarted until
* we have removed some data, which will be true at the
* second round.
*
* Call dsp_rdintr to start the dma. It would be nice to
* have a "need" field in the snd_dbuf, so that we do
* not start a long operation unnecessarily. However,
* the restart code will ask for at most d->blocksize
* bytes, and since we are sure we are the only reader,
* and the routine is not interrupted, we patch and
* restore d->blocksize around the call. A bit dirty,
* but it works, and saves some overhead :)
*/
int old_bs = d->rec_blocksize;
if ( (d->flags & (SND_F_INIT|SND_F_WR_DMA|SND_F_RD_DMA)) ==
SND_F_INIT) {
/* want to init but no pending DMA activity */
splx(s);
d->callback(d, SND_CB_INIT); /* this is slow! */
s = spltty();
}
if (l < MIN_CHUNK_SIZE)
d->rec_blocksize = MIN_CHUNK_SIZE ;
else if (l < d->rec_blocksize)
d->rec_blocksize = l ;
dsp_rdintr(d);
d->rec_blocksize = old_bs ;
}
if (l == 0) {
/*
* If, after all these efforts, we still have no data ready,
* then we must sleep (unless of course we have doing
* non-blocking i/o. But use exponential delays, starting
* at 1 tick and doubling each time.
*/
if (d->flags & SND_F_NBIO) {
splx(s);
break;
}
DEB(printf("dsp_read_body: sleeping %d waiting for %d bytes\n",
timeout, buf->uio_resid));
ret = tsleep( (caddr_t)b, PRIBIO | PCATCH , "dsprd", timeout ) ;
if (ret == EINTR)
d->flags |= SND_F_ABORTING ;
splx(s); /* necessary before the goto again... */
if (ret == ERESTART || ret == EINTR)
break ;
DEB(printf("woke up, ret %d, rl %d\n", ret, b->rl));
timeout = min(timeout*2, hz);
continue;
}
splx(s);
timeout = 1 ; /* we got some data, so reset exp. wait */
/*
* if we are using /dev/audio and the device does not
* support it natively, we should do a format conversion.
* (in this case from uLAW to natural format).
* This can be messy in that it can require an intermediate
* buffer, and also screw up the byte count.
*/
/*
* NOTE: I _can_ use rp here because it is not modified by the
* interrupt routines.
*/
if (d->flags & SND_F_XLAT8)
translate_bytes(dsp_ulaw, b->buf + b->rp, l);
ret = uiomove(b->buf + b->rp, l, buf) ;
if (ret !=0 ) /* an error occurred ... */
break ;
s = spltty(); /* no interrupts here ... */
b->fl += l ; /* this more free bytes */
b->rl -= l ; /* this less ready bytes */
b->rp += l ; /* advance ready pointer */
if (b->rp == b->bufsize) /* handle wraps */
b->rp = 0 ;
splx(s) ;
bsz = min(b->bufsize, bsz*2);
}
s = spltty(); /* no interrupts here ... */
d->flags &= ~SND_F_READING ;
if (d->flags & SND_F_ABORTING) {
d->flags |= ~SND_F_ABORTING;
splx(s);
dsp_rdabort(d);
}
splx(s) ;
return ret ;
}
/*
* short routine to initialize a dma buffer descriptor (usually
* located in the XXX_desc structure). The first parameter is
* the buffer size, the second one specifies the dma channel in use
* At the moment we do not support more than 64K, since for some
* cards I need to switch between dma1 and dma2. The channel is
* unused.
*/
void
alloc_dbuf(snd_dbuf *b, int size, int chan)
{
if (size > 0x10000)
panic("max supported size is 64k");
b->buf = contigmalloc(size, M_DEVBUF, M_NOWAIT,
0ul, 0xfffffful, 1ul, 0x10000ul);
/* should check that it does not fail... */
b->dp = b->rp = b->fp = 0 ;
b->dl0 = b->dl = b->rl = 0 ;
b->bufsize = b->fl = size ;
}
void
reset_dbuf(snd_dbuf *b)
{
b->dp = b->rp = b->fp = 0 ;
b->dl0 = b->dl = b->rl = 0 ;
b->fl = b->bufsize ;
}
/*
* snd_sync waits until the space in the given channel goes above
* a threshold. chan = 1 : play, 2: capture. The threshold is
* checked against fl or rl respectively.
* Assume that the condition can become true, do not check here...
*/
int
snd_sync(snddev_info *d, int chan, int threshold)
{
u_long s;
int ret;
snd_dbuf *b;
b = (chan == 1) ? &(d->dbuf_out ) : &(d->dbuf_in ) ;
for (;;) {
s=spltty();
if ( chan==1 )
dsp_wr_dmaupdate(d);
else
dsp_rd_dmaupdate(d);
if ( (chan == 1 && b->fl <= threshold) ||
(chan == 2 && b->rl <= threshold) ) {
ret = tsleep((caddr_t)b, PRIBIO|PCATCH, "sndsyn", 1);
splx(s);
if (ret == ERESTART || ret == EINTR) {
printf("tsleep returns %d\n", ret);
return -1 ;
}
} else
break;
}
splx(s);
return 0 ;
}
/*
* dsp_wrabort(d) and dsp_rdabort(d) are non-blocking functions
* which abort a pending DMA transfer and flush the buffers.
*/
int
dsp_wrabort(snddev_info *d)
{
long s;
int missing = 0;
snd_dbuf *b = & (d->dbuf_out) ;
s = spltty();
if ( d->flags & SND_F_WR_DMA ) {
d->flags &= ~(SND_F_WR_DMA | SND_F_WRITING);
if (d->callback)
d->callback(d, SND_CB_WR | SND_CB_ABORT);
missing = isa_dmastop(d->dma1) ; /* this many missing bytes... */
b->rl += missing ; /* which are part of the ready area */
b->rp -= missing ;
if (b->rp < 0)
b->rp += b->bufsize;
DEB(printf("dsp_wrabort: stopped after %d bytes out of %d\n",
b->dl - missing, b->dl));
b->dl -= missing;
dsp_wr_dmadone(d);
missing = b->rl;
}
reset_dbuf(b);
splx(s);
return missing;
}
int
dsp_rdabort(snddev_info *d)
{
long s;
int missing = 0;
snd_dbuf *b = & (d->dbuf_in) ;
s = spltty();
if ( d->flags & SND_F_RD_DMA ) {
d->flags &= ~(SND_F_RD_DMA | SND_F_READING);
if (d->callback)
d->callback(d, SND_CB_RD | SND_CB_ABORT);
missing = isa_dmastop(d->dma2) ; /* this many missing bytes... */
b->fl += missing ; /* which are part of the free area */
b->fp -= missing ;
if (b->fp < 0)
b->fp += b->bufsize;
DEB(printf("dsp_rdabort: stopped after %d bytes out of %d\n",
b->dl - missing, b->dl));
b->dl -= missing;
dsp_rd_dmadone(d);
missing = b->rl ;
}
reset_dbuf(b);
splx(s);
return missing;
}
/*
* this routine tries to flush the dma transfer. It is called
* on a close. The caller must set SND_F_CLOSING, and insure that
* interrupts are enabled. We immediately abort any read DMA
* operation, and then wait for the play buffer to drain.
*/
int
snd_flush(snddev_info *d)
{
int ret, res, res1;
int count=10;
DEB(printf("snd_flush d->flags 0x%08x\n", d->flags));
dsp_rdabort(d);
if ( d->flags & SND_F_WR_DMA ) {
/* close write */
while ( d->flags & SND_F_WR_DMA ) {
/*
* still pending output data.
*/
ret = tsleep( (caddr_t)&(d->dbuf_out), PRIBIO|PCATCH, "dmafl1", hz);
if (ret == ERESTART || ret == EINTR) {
printf("tsleep returns %d\n", ret);
return -1 ;
}
if ( ret && --count == 0) {
printf("timeout flushing dma1, cnt 0x%x flags 0x%08x\n",
isa_dmastatus1(d->dma1), d->flags);
return -1 ;
}
}
d->flags &= ~SND_F_CLOSING ;
}
reset_dbuf(& (d->dbuf_out) );
return 0 ;
}
/*
* end of new code for dma buffer handling
*/

View File

@ -0,0 +1,802 @@
/*
* snd/dmabuf.c
*
* New DMA routines -- Luigi Rizzo, 05 sep 97
* This file implements the new DMA routines for the sound driver.
* AUTO DMA MODE (ISA DMA SIDE).
*
* Copyright by Luigi Rizzo - 1997
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <i386/isa/snd/sound.h>
#include <i386/isa/snd/ulaw.h>
#define MIN_CHUNK_SIZE 256 /* for uiomove etc. */
#define DMA_ALIGN_BITS 2 /* i.e. 4 bytes */
#define DMA_ALIGN_THRESHOLD (1<< DMA_ALIGN_BITS)
#define DMA_ALIGN_MASK (~ (DMA_ALIGN_THRESHOLD - 1))
static void dsp_wr_dmadone(snddev_info *d);
static void dsp_rd_dmadone(snddev_info *d);
/*
*
SOUND OUTPUT
We use a circular buffer to store samples directed to the DAC.
The buffer is split into two variable-size regions, each identified
by an offset in the buffer (rp,fp) and a lenght (rl,fl).
0 rp,rl fp,fl bufsize
|__________|____________|________|
FREE d> READY w> FREE
READY region: contains data written from the process and ready
to be sent to the DAC;
FREE region: is the empty region of the buffer, where a process
can write new data.
The first block of the READY region is used for DMA transfers. The
transfer is started at rp and with chunks of length dl0 computed
according to some rules. During DMA operations, rp advances (d>)
and rl,fl are updated by dsp_wr_dmaupdate(). When a new block is
written, fp advances (w>) and rl,fl are updated accordingly.
Both the "READY" and "FREE" regions can wrap around the end of the
buffer.
At initialization, READY is empty, FREE takes all the
available space, and dma is idle
dl0 contains the new blocksize to be used in dma transfers (passed to
the callback). dl contains the previous blocksize (needed when the
codec uses AUTO DMA).
rp = fp = 0 ; -- beginning of buffer
dl0 = dl = 0 ;
rl = 0 ; -- meaning no data ready
fl = bufsize ;
The code works as follows: the user write routine dsp_write_body()
fills up the READY region with new data (reclaiming space from the
FREE region) and starts the write DMA engine if inactive (activity
is indicated by d->flags & SND_F_WR_DMA ). The size of each
DMA transfer is chosen according to a series of rules which will be
discussed later. When a DMA transfer is complete, an interrupt causes
dsp_wrintr() to be called which empties the DMA region, extends
the FREE region and possibly starts the next transfer.
In some cases, the code tries to track the current status of DMA
operations by calling isa_dmastatus() and advancing the boundary
between FREE and DMA regions accordingly.
The size of a DMA transfer is selected according to the following
rules:
1. when not using AUTO DMA, do not wrap around the end of the
buffer, and do not let fp move too close to the end of the
buffer;
2. do not use more than half of the buffer size.
This serves to allow room for a next write operation concurrent
with the dma transfer, and to reduce the time which is necessary
to wait before a pending dma will end (optionally, the max
size could be further limited to a fixed amount of play time,
depending on number of channels, sample size and sample speed);
3. use the current blocksize (either specified by the user, or
corresponding roughly to 0.25s of data);
*
*/
/*
* dsp_wr_dmadone moves the write DMA region into the FREE region.
* It is assumed to be called at spltty() and with a write dma
* previously started.
*/
static void
dsp_wr_dmadone(snddev_info *d)
{
snd_dbuf *b = & (d->dbuf_out) ;
dsp_wr_dmaupdate(d);
/*
* XXX here it would be more efficient to record if there
* actually is a sleeping process, but this should still work.
*/
wakeup(b); /* wakeup possible sleepers */
if (d->wsel.si_pid &&
( !(d->flags & SND_F_HAS_SIZE) || b->fl >= d->play_blocksize ) )
selwakeup( & d->wsel );
}
/*
* The following function tracks the status of a (write) dma transfer,
* and moves the boundary between the FREE and the DMA regions.
* It works under the following assumptions:
* - the DMA engine is active;
* - the routine is called with interrupts blocked.
*/
void
dsp_wr_dmaupdate(snddev_info *d)
{
snd_dbuf *b = & (d->dbuf_out) ;
int tmp, delta;
tmp = b->bufsize - isa_dmastatus1(d->dma1) ;
tmp &= DMA_ALIGN_MASK; /* align... */
delta = tmp - b->rp;
if (delta < 0) /* wrapped */
delta += b->bufsize ;
b->rp = tmp;
b->rl -= delta ;
b->fl += delta ;
}
/*
* Write interrupt routine. Can be called from other places, but
* with interrupts disabled.
*/
void
dsp_wrintr(snddev_info *d)
{
snd_dbuf *b = & (d->dbuf_out) ;
DEB(printf("dsp_wrintr: start on dl %d, rl %d, fl %d\n",
b->dl, b->rl, b->fl));
if (d->flags & SND_F_WR_DMA) { /* dma was active */
b->int_count++;
d->flags &= ~SND_F_WR_DMA;
dsp_wr_dmadone(d);
} else
b->dl = 0 ;
/*
* start another dma operation only if have ready data in the
* buffer, there is no pending abort, have a full-duplex device
* (dma1 != dma2) or have half duplex device and there is no
* pending op on the other side.
*
* Force transfer to be aligned to a boundary of 4, which is
* needed when doing stereo and 16-bit. We could make this
* adaptive, but why bother for now...
*/
if ( b->rl >= DMA_ALIGN_THRESHOLD &&
! (d->flags & SND_F_ABORTING) &&
( (d->dma1 != d->dma2) || ! (d->flags & SND_F_READING) ) ) {
int l = min(b->rl, d->play_blocksize ); /* avoid too large transfer */
l &= DMA_ALIGN_MASK ; /* realign things */
b->dl0 = l ;
if (d->callback)
d->callback(d, SND_CB_WR | SND_CB_START );
d->flags |= SND_F_WR_DMA;
b->dl = b->dl0;
} else {
if (b->dl > 0) { /* was active */
b->dl0 = 0;
d->callback(d, SND_CB_WR | SND_CB_STOP ); /* stop dma */
}
/*
* if switching to read, should start the read dma...
*/
if ( d->dma1 == d->dma2 && (d->flags & SND_F_READING) )
dsp_rdintr(d);
DEB(printf("cannot start wr-dma flags 0x%08x dma_dl %d rl %d\n",
d->flags, isa_dmastatus1(d->dma1), b->rl));
}
}
/*
* user write routine
*
* advance the boundary between READY and FREE, fill the space with
* uiomove(), and possibly start DMA. Do the above until the transfer
* is complete.
*
* To minimize latency in case a pending DMA transfer is about to end,
* we do the transfer in pieces of increasing sizes, extending the
* READY area at every checkpoint. In the (necessary) assumption that
* memory bandwidth is larger than the rate at which the dma consumes
* data, we reduce the latency to something proportional to the length
* of the first piece, while keeping the overhead low and being able
* to feed the DMA with large blocks.
*/
int
dsp_write_body(snddev_info *d, struct uio *buf)
{
int timeout = 1, n, l, bsz, ret = 0 ;
long s;
snd_dbuf *b = & (d->dbuf_out) ;
/* assume d->flags |= SND_F_WRITING ; has been done before */
/*
* bsz is the max size for the next transfer. If the dma was
* idle, we want it as large as possible. Otherwise, start with
* a small block to avoid underruns if we are close to the end of
* the previous operation.
*/
bsz = (d->flags & SND_F_WR_DMA) ? MIN_CHUNK_SIZE : b->bufsize ;
while ( n = buf->uio_resid ) {
l = min (n, bsz); /* at most n bytes ... */
s = spltty(); /* no interrupts here ... */
/*
* if i) the dma engine is running, ii) we do not have enough space
* in the FREE region, and iii) the current DMA transfer might let
* us complete the _whole_ transfer without sleeping, or we are doing
* non-blocking I/O, then try to extend the FREE region.
* Otherwise do not bother, we will need to sleep anyways, and
* make the timeout longer.
* Note that the check for iii) is only approximate since we
* might have fewer than dl0 residual bytes.
*/
if ( d->flags & SND_F_WR_DMA && b->fl < l &&
( b->fl + b->dl0 >= n || d->flags & SND_F_NBIO ) )
dsp_wr_dmaupdate(d); /* should really change timeout... */
else
timeout = hz;
l = min( l, b->fl ); /* no more than avail. space */
DEB(printf("dsp_write_body: prepare %d bytes out of %d\n", l,n));
/*
* at this point, we assume that if l==0 the dma engine
* must (or will, in cause it is paused) be running.
*/
if (l == 0) { /* no space, must sleep */
if (d->flags & SND_F_NBIO) {
/* unless of course we are doing non-blocking i/o */
splx(s);
break;
}
DEB(printf("dsp_write_body: l=0, (fl %d) sleeping\n", b->fl));
ret = tsleep( (caddr_t)b, PRIBIO|PCATCH, "dspwr", timeout);
if (ret == EINTR)
d->flags |= SND_F_ABORTING ;
splx(s);
if (ret == ERESTART || ret == EINTR)
break ;
timeout = min(2*timeout, hz);
continue;
}
splx(s);
timeout = 1 ; /* we got some data... */
/*
* copy data to the buffer, and possibly do format
* conversions (here, from ULAW to U8).
* NOTE: I can use fp here since it is not modified by the
* interrupt routines.
*/
if (b->fp + l > b->bufsize) {
int l1 = b->bufsize - b->fp ;
uiomove(b->buf + b->fp, l1, buf) ;
uiomove(b->buf, l - l1, buf) ;
if (d->flags & SND_F_XLAT8) {
translate_bytes(ulaw_dsp, b->buf + b->fp, l1);
translate_bytes(ulaw_dsp, b->buf , l - l1);
}
} else {
uiomove(b->buf + b->fp, l, buf) ;
if (d->flags & SND_F_XLAT8)
translate_bytes(ulaw_dsp, b->buf + b->fp, l);
}
s = spltty(); /* no interrupts here ... */
b->rl += l ; /* this more ready bytes */
b->fl -= l ; /* this less free bytes */
b->fp += l ;
if (b->fp >= b->bufsize) /* handle wraps */
b->fp -= b->bufsize ;
if ( !(d->flags & SND_F_WR_DMA) ) /* dma was idle, restart it */
dsp_wrintr(d) ;
splx(s) ;
bsz = min(b->bufsize, bsz*2);
}
s = spltty(); /* no interrupts here ... */
d->flags &= ~SND_F_WRITING ;
if (d->flags & SND_F_ABORTING) {
d->flags &= ~SND_F_ABORTING;
splx(s);
dsp_wrabort(d);
}
splx(s) ;
return ret ;
}
/*
* SOUND INPUT
*
The input part is similar to the output one. The only difference is in
the ordering of regions, which is the following:
0 rp,rl fp,fl bufsize
|__________|____________|________|
FREE r> READY d> FREE
and the fact that input data are in the READY region.
At initialization, as for the write routine, READY is empty,
and FREE takes all the space:
rp = fp = 0 ; -- beginning of buffer
dl0 = dl = 0 ;
rl = 0 ; -- meaning no data ready
fl = bufsize ;
Operation is as follows: upon user read (dsp_read_body()) a DMA read
is started if not already active (marked by d->flags & SND_F_RD_DMA),
then as soon as data are available in the READY region they are
transferred to the user buffer, thus advancing the boundary between FREE
and READY. Upon interrupts, caused by a completion of a DMA transfer,
the READY region is extended and possibly a new transfer is started.
When necessary, dsp_rd_dmaupdate() is called to advance fp (and update
rl,fl accordingly). Upon user reads, rp is advanced and rl,fl are
updated accordingly.
The rules to choose the size of the new DMA area are similar to
the other case, i.e:
1. if not using AUTO mode, do not wrap around the end of the
buffer, and do not let fp move too close to the end of the
buffer;
2. do not use more than half the buffer size; this serves to
leave room for the next dma operation.
3. use the default blocksize, either user-specified, or
corresponding to 0.25s of data;
*
*/
/*
* dsp_rd_dmadone moves bytes in the input buffer from DMA region to
* READY region. We assume it is called at spltty() and with dl>0
*/
static void
dsp_rd_dmadone(snddev_info *d)
{
snd_dbuf *b = & (d->dbuf_in) ;
dsp_rd_dmaupdate(d);
b->rl += b->dl ; /* make dl bytes available */
wakeup(b) ; /* wakeup possibly sleeping processes */
if (d->rsel.si_pid &&
( !(d->flags & SND_F_HAS_SIZE) || b->rl >= d->rec_blocksize ) )
selwakeup( & d->rsel );
b->dp = b->fp ;
b->dl0 = b->dl = 0 ;
}
/*
* The following function tracks the status of a (read) dma transfer,
* and moves the boundary between the READY and the DMA regions.
* It works under the following assumptions:
* - the DMA engine is running;
* - the function is called with interrupts blocked.
*/
void
dsp_rd_dmaupdate(snddev_info *d)
{
snd_dbuf *b = & (d->dbuf_in) ;
int tmp ;
tmp = b->bufsize - isa_dmastatus1(d->dma2) ;
tmp &= DMA_ALIGN_MASK; /* align... */
delta = tmp - b->fp;
if (delta < 0) /* wrapped */
delta += b->bufsize ;
b->fp = tmp;
b->fl -= delta ;
b->rl += delta ;
}
/*
* read interrupt routine. Must be called with interrupts blocked.
*/
void
dsp_rdintr(snddev_info *d)
{
snd_dbuf *b = & (d->dbuf_in) ;
if (d->flags & SND_F_RD_DMA) { /* dma was active */
b->int_count++;
d->flags &= ~SND_F_RD_DMA;
dsp_rd_dmadone(d);
} else
b->dl = 0 ;
/*
* Same checks as in the write case (mutatis mutandis) to decide
* whether or not to restart a dma transfer.
*/
if ( b->fl >= DMA_ALIGN_THRESHOLD &&
((d->flags & (SND_F_ABORTING|SND_F_CLOSING)) == 0) &&
( (d->dma1 != d->dma2) || ( (d->flags & SND_F_WRITING) == 0) ) ) {
int l = min(b->fl, d->rec_blocksize);
l &= DMA_ALIGN_MASK ; /* realign sizes */
b->dl0 = l ;
if (d->callback)
d->callback(d, SND_CB_RD | SND_CB_START );
d->flags |= SND_F_RD_DMA;
b->dl = b->dl0;
} else {
if (b->dl > 0) {
b->dl0 = 0 ; /* was active */
d->callback(d, SND_CB_RD | SND_CB_STOP);
}
/*
* if switching to write, start write dma engine
*/
if ( d->dma1 == d->dma2 && (d->flags & SND_F_WRITING) )
dsp_wrintr(d) ;
DEB(printf("cannot start rd-dma flags 0x%08x dma_dl %d fl %d\n",
d->flags, isa_dmastatus1(d->dma2), b->fl));
}
}
/*
* body of user-read routine
*
* Start DMA if not active; wait for READY not empty.
* Transfer data from READY region using uiomove(), advance boundary
* between FREE and READY. Repeat until transfer is complete.
*
* To avoid excessive latency in freeing up space for the DMA
* engine, transfers are done in blocks of increasing size, so that
* the latency is proportional to the size of the smallest block, but
* we have a low overhead and are able to feed the dma engine with
* large blocks.
*
* When we enter this routine, we assume that d->flags |= SND_F_READING
* was done before.
*
* NOTE: in the current version, read will not return more than
* blocksize bytes at once (unless more are already available), to
* avoid that requests using very large buffers block for too long.
*/
int
dsp_read_body(snddev_info *d, struct uio *buf)
{
int limit, l, n, bsz, ret = 0 ;
long s;
snd_dbuf *b = & (d->dbuf_in) ;
int timeout = 1 ; /* counter of how many ticks we sleep */
/*
* "limit" serves to return after at most one blocksize of data
* (unless more are already available). Otherwise, things like
* cat /dev/audio would use a 64K buffer and would start returning
* data after a _very_ long time...
* Note -- some applications depend on reads not returning short
* blocks. But I believe these apps are broken, since interrupted
* system calls might return short reads anyways, and the
* application should better check that.
*/
if (buf->uio_resid > d->rec_blocksize)
limit = buf->uio_resid - d->rec_blocksize;
else
limit = 0;
bsz = MIN_CHUNK_SIZE ; /* the current transfer (doubles at each step) */
while ( (n = buf->uio_resid) > limit ) {
DEB(printf("dsp_read_body: start waiting for %d bytes\n", n));
/*
* here compute how many bytes to transfer, enforcing various
* limitations:
*/
l = min (n, bsz); /* 1': at most bsz bytes ... */
s = spltty(); /* no interrupts here ! */
/*
* if i) the dma engine is running, ii) we do not have enough
* ready bytes, and iii) the current DMA transfer could give
* us what we need, or we are doing non-blocking IO, then try
* to extend the READY region.
* Otherwise do not bother, we will need to sleep anyways,
* and make the timeout longer.
*/
if ( d->flags & SND_F_RD_DMA && b->rl < l &&
( d->flags & SND_F_NBIO || b->rl + b->dl >= n - limit ) )
dsp_rd_dmaupdate(d);
else
timeout = hz ;
l = min( l, b->rl ); /* 2': no more than avail. data */
if ( !(d->flags & SND_F_RD_DMA) ) { /* dma was idle, start it */
/*
* There are two reasons the dma can be idle: either this
* is the first read, or the buffer has become full. In
* the latter case, the dma cannot be restarted until
* we have removed some data, which will be true at the
* second round.
*
* Call dsp_rdintr to start the dma. It would be nice to
* have a "need" field in the snd_dbuf, so that we do
* not start a long operation unnecessarily. However,
* the restart code will ask for at most d->blocksize
* bytes, and since we are sure we are the only reader,
* and the routine is not interrupted, we patch and
* restore d->blocksize around the call. A bit dirty,
* but it works, and saves some overhead :)
*/
int old_bs = d->rec_blocksize;
if ( (d->flags & (SND_F_INIT|SND_F_WR_DMA|SND_F_RD_DMA)) ==
SND_F_INIT) {
/* want to init but no pending DMA activity */
splx(s);
d->callback(d, SND_CB_INIT); /* this is slow! */
s = spltty();
}
if (l < MIN_CHUNK_SIZE)
d->rec_blocksize = MIN_CHUNK_SIZE ;
else if (l < d->rec_blocksize)
d->rec_blocksize = l ;
d->dl0 = d->rec_blocksize ;
dsp_rdintr(d);
d->rec_blocksize = old_bs ;
}
if (l == 0) {
/*
* If, after all these efforts, we still have no data ready,
* then we must sleep (unless of course we have doing
* non-blocking i/o. But use exponential delays, starting
* at 1 tick and doubling each time.
*/
if (d->flags & SND_F_NBIO) {
splx(s);
break;
}
DEB(printf("dsp_read_body: sleeping %d waiting for %d bytes\n",
timeout, buf->uio_resid));
ret = tsleep( (caddr_t)b, PRIBIO | PCATCH , "dsprd", timeout ) ;
if (ret == EINTR)
d->flags |= SND_F_ABORTING ;
splx(s); /* necessary before the goto again... */
if (ret == ERESTART || ret == EINTR)
break ;
DEB(printf("woke up, ret %d, rl %d\n", ret, b->rl));
timeout = min(timeout*2, hz);
continue;
}
splx(s);
timeout = 1 ; /* we got some data, so reset exp. wait */
/*
* if we are using /dev/audio and the device does not
* support it natively, we should do a format conversion.
* (in this case from uLAW to natural format).
* This can be messy in that it can require an intermediate
* buffer, and also screw up the byte count.
*/
/*
* NOTE: I _can_ use rp here because it is not modified by the
* interrupt routines.
*/
if (d->flags & SND_F_XLAT8)
translate_bytes(dsp_ulaw, b->buf + b->rp, l);
ret = uiomove(b->buf + b->rp, l, buf) ;
if (ret !=0 ) /* an error occurred ... */
break ;
s = spltty(); /* no interrupts here ... */
b->fl += l ; /* this more free bytes */
b->rl -= l ; /* this less ready bytes */
b->rp += l ; /* advance ready pointer */
if (b->rp == b->bufsize) /* handle wraps */
b->rp = 0 ;
splx(s) ;
bsz = min(b->bufsize, bsz*2);
}
s = spltty(); /* no interrupts here ... */
d->flags &= ~SND_F_READING ;
if (d->flags & SND_F_ABORTING) {
d->flags |= ~SND_F_ABORTING;
splx(s);
dsp_rdabort(d);
}
splx(s) ;
return ret ;
}
/*
* short routine to initialize a dma buffer descriptor (usually
* located in the XXX_desc structure). The first parameter is
* the buffer size, the second one specifies that a 16-bit dma channel
* is used (hence the buffer must be properly aligned).
*/
void
alloc_dbuf(snd_dbuf *b, int size, int chan)
{
if (size > 0x10000)
panic("max supported size is 64k");
b->buf = contigmalloc(size, M_DEVBUF, M_NOWAIT,
0ul, 0xfffffful, 1ul, 0x10000ul);
/* should check that it does not fail... */
b->dp = b->rp = b->fp = 0 ;
b->dl0 = b->dl = b->rl = 0 ;
b->bufsize = b->fl = size ;
}
void
reset_dbuf(snd_dbuf *b)
{
b->dp = b->rp = b->fp = 0 ;
b->dl0 = b->dl = b->rl = 0 ;
b->fl = b->bufsize ;
}
/*
* snd_sync waits until the space in the given channel goes above
* a threshold. chan = 1 : play, 2: capture. The threshold is
* checked against fl or rl respectively.
* Assume that the condition can become true, do not check here...
*/
int
snd_sync(snddev_info *d, int chan, int threshold)
{
u_long s;
int ret;
snd_dbuf *b;
b = (chan == 1) ? &(d->dbuf_out ) : &(d->dbuf_in ) ;
for (;;) {
s=spltty();
if ( chan==1 )
dsp_wr_dmaupdate(d);
else
dsp_rd_dmaupdate(d);
if ( (chan == 1 && b->fl <= threshold) ||
(chan == 2 && b->rl <= threshold) ) {
ret = tsleep((caddr_t)b, PRIBIO|PCATCH, "sndsyn", 1);
splx(s);
if (ret == ERESTART || ret == EINTR) {
printf("tsleep returns %d\n", ret);
return -1 ;
}
} else
break;
}
splx(s);
return 0 ;
}
/*
* dsp_wrabort(d) and dsp_rdabort(d) are non-blocking functions
* which abort a pending DMA transfer and flush the buffers.
*/
int
dsp_wrabort(snddev_info *d)
{
long s;
int missing = 0;
snd_dbuf *b = & (d->dbuf_out) ;
s = spltty();
if ( d->flags & SND_F_WR_DMA ) {
d->flags &= ~ ( SND_F_WR_DMA | SND_F_WRITING ) ;
if (d->callback)
d->callback(d, SND_CB_WR | SND_CB_ABORT);
missing = isa_dmastop(d->dma1) ; /* this many missing bytes... */
b->rl += missing ; /* which are part of the ready area */
b->rp -= missing ;
if (b->rp < 0)
b->rp += b->bufsize;
DEB(printf("dsp_wrabort: stopped after %d bytes out of %d\n",
b->dl - missing, b->dl));
b->dl -= missing;
dsp_wr_dmadone(d);
missing = b->rl;
}
reset_dbuf(b);
splx(s);
return missing;
}
int
dsp_rdabort(snddev_info *d)
{
long s;
int missing = 0;
snd_dbuf *b = & (d->dbuf_in) ;
s = spltty();
if ( d->flags & SND_F_RD_DMA ) {
d->flags &= ~ ( SND_F_RD_DMA | SND_F_READING ) ;
if (d->callback)
d->callback(d, SND_CB_RD | SND_CB_ABORT);
missing = isa_dmastop(d->dma2) ; /* this many missing bytes... */
b->fl += missing ; /* which are part of the free area */
b->fp -= missing ;
if (b->fp < 0)
b->fp += b->bufsize;
DEB(printf("dsp_rdabort: stopped after %d bytes out of %d\n",
b->dl - missing, b->dl));
b->dl -= missing;
dsp_rd_dmadone(d);
missing = b->rl ;
}
reset_dbuf(b);
splx(s);
return missing;
}
/*
* this routine tries to flush the dma transfer. It is called
* on a close. The caller must set SND_F_CLOSING, and insure that
* interrupts are enabled. We immediately abort any read DMA
* operation, and then wait for the play buffer to drain.
*/
int
snd_flush(snddev_info *d)
{
int ret, res, res1;
int count=10;
DEB(printf("snd_flush d->flags 0x%08x\n", d->flags));
dsp_rdabort(d);
if ( d->flags & SND_F_WR_DMA ) {
/* close write */
while ( d->flags & SND_F_WR_DMA ) {
/*
* still pending output data.
*/
ret = tsleep( (caddr_t)&(d->dbuf_out), PRIBIO|PCATCH, "dmafl1", hz);
if (ret == ERESTART || ret == EINTR) {
printf("tsleep returns %d\n", ret);
return -1 ;
}
if ( ret && --count == 0) {
printf("timeout flushing dma1, cnt 0x%x flags 0x%08x\n",
isa_dmastatus1(d->dma1), d->flags);
return -1 ;
}
}
d->flags &= ~SND_F_CLOSING ;
}
reset_dbuf(& (d->dbuf_out) );
return 0 ;
}
/*
* end of new code for dma buffer handling
*/

250
sys/i386/isa/snd/mss.h Normal file
View File

@ -0,0 +1,250 @@
/*
* file: mss.h
*
* (C) 1997 Luigi Rizzo (luigi@iet.unipi.it)
*
* This file contains information and macro definitions for
* AD1848-compatible devices, used in the MSS/WSS compatible boards.
*
*/
/*
*
The codec part of the board is seen as a set of 4 registers mapped
at the base address for the board (default 0x534). Note that some
(early) boards implemented 4 additional registers 4 location before
(usually 0x530) to store configuration information. This is a source
of confusion in that one never knows what address to specify. The
(current) convention is to use the old address (0x530) in the kernel
configuration file and consider MSS registers start four location
ahead.
*
*/
/*
* The four visible registers of the MSS :
*
*/
#define io_Index_Addr(d) ((d)->io_base + 4)
#define IA_BUSY 0x80 /* readonly, set when busy */
#define IA_MCE 0x40 /* the MCE bit. */
/*
* the MCE bit must be set whenever the current mode of the
* codec is changed; this in particular is true for the
* Data Format (I8, I28) and Interface Config(I9) registers.
* Only exception are CEN and PEN which can be changed on the fly.
* The DAC output is muted when MCE is set.
*/
#define IA_TRD 0x20 /* Transfer request disable */
/*
* When TRD is set, DMA transfers cease when the INT bit in
* the MSS status reg is set. Must be cleared for automode
* DMA, set otherwise.
*/
#define IA_AMASK 0x1f /* mask for indirect address */
#define io_Indexed_Data(d) ((d)->io_base+1+4)
/*
* data to be transferred to the indirect register addressed
* by index addr. During init and sw. powerdown, cannot be
* written to, and is always read as 0x80 (consistent with the
* busy flag).
*/
#define io_Status(d) ((d)->io_base+2+4)
#define IS_CUL 0x80 /* capture upper/lower */
#define IS_CLR 0x40 /* capture left/right */
#define IS_CRDY 0x20 /* capture ready for programmed i/o */
#define IS_SER 0x10 /* sample error (overrun/underrun) */
#define IS_PUL 0x08 /* playback upper/lower */
#define IS_PLR 0x04 /* playback left/right */
#define IS_PRDY 0x02 /* playback ready for programmed i/o */
#define IS_INT 0x01 /* int status (1 = active) */
/*
* IS_INT is clreared by any write to the status register.
*/
#define io_Polled_IO(d) ((d)->io_base+3+4)
/*
* this register is used in case of polled i/o
*/
/*
* The MSS has a set of 16 (or 32 depending on the model) indirect
* registers accessible through the data port by specifying the
* appropriate address in the address register.
*
* The 16 low registers are uniformly handled in AD1848/CS4248 compatible
* mode (often called MODE1). For the upper 16 registers there are
* some differences among different products, mainly Crystal uses them
* differently from OPTi.
*
*/
/*
* volume registers
*/
#define I6_MUTE 0x80
/*
* register I9 -- interface configuration.
*/
#define I9_PEN 0x01 /* playback enable */
#define I9_CEN 0x02 /* capture enable */
/*
* values used in bd_flags
*/
#define BD_F_MCE_BIT 0x0001
#define BD_F_IRQ_OK 0x0002
#define BD_F_TMR_RUN 0x0004
/*
* sound/ad1848_mixer.h
*
* Definitions for the mixer of AD1848 and compatible codecs.
*
* Copyright by Hannu Savolainen 1994
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met: 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. 2.
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* The AD1848 codec has generic input lines called Line, Aux1 and Aux2.
* Soundcard manufacturers have connected actual inputs (CD, synth, line,
* etc) to these inputs in different order. Therefore it's difficult
* to assign mixer channels to to these inputs correctly. The following
* contains two alternative mappings. The first one is for GUS MAX and
* the second is just a generic one (line1, line2 and line3).
* (Actually this is not a mapping but rather some kind of interleaving
* solution).
*/
#define GUSMAX_MIXER
#ifdef GUSMAX_MIXER
#define MODE1_REC_DEVICES \
(SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD|SOUND_MASK_IMIX)
#define MODE1_MIXER_DEVICES \
(SOUND_MASK_SYNTH | SOUND_MASK_MIC | SOUND_MASK_CD | \
SOUND_MASK_IGAIN | SOUND_MASK_PCM|SOUND_MASK_IMIX)
#define MODE2_MIXER_DEVICES \
(SOUND_MASK_SYNTH | SOUND_MASK_LINE | SOUND_MASK_MIC | \
SOUND_MASK_CD | SOUND_MASK_SPEAKER | SOUND_MASK_IGAIN | \
SOUND_MASK_PCM | SOUND_MASK_IMIX)
#else /* Generic mapping */
#define MODE1_REC_DEVICES \
(SOUND_MASK_LINE3 | SOUND_MASK_MIC | SOUND_MASK_LINE1|SOUND_MASK_IMIX)
#define MODE1_MIXER_DEVICES \
(SOUND_MASK_LINE1 | SOUND_MASK_MIC | SOUND_MASK_LINE2 | \
SOUND_MASK_IGAIN | SOUND_MASK_PCM | SOUND_MASK_IMIX)
#define MODE2_MIXER_DEVICES \
(SOUND_MASK_LINE1 | SOUND_MASK_MIC | SOUND_MASK_LINE2 | \
SOUND_MASK_LINE3 | SOUND_MASK_SPEAKER | \
SOUND_MASK_IGAIN | SOUND_MASK_PCM | SOUND_MASK_IMIX)
#endif
#define OPTI931_MIXER_DEVICES \
(SOUND_MASK_VOLUME | SOUND_MASK_SYNTH | SOUND_MASK_PCM | \
SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_IGAIN )
/*
* Most of the mixer entries work in backwards. Setting the polarity field
* makes them to work correctly.
*
* The channel numbering used by individual soundcards is not fixed.
* Some cards have assigned different meanings for the AUX1, AUX2
* and LINE inputs. Some have different features...
* The current version doesn't try to compensate this.
*
*/
mixer_ent mix_devices[32][2] = { /* As used in GUS MAX */
MIX_ENT(SOUND_MIXER_VOLUME, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 0, 5, 5, 1, 0, 5),
MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 6, 7, 1, 0, 6),
MIX_ENT(SOUND_MIXER_SPEAKER, 26, 1, 0, 4, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_LINE, 18, 1, 0, 5, 19, 1, 0, 5),
MIX_ENT(SOUND_MIXER_MIC, 0, 0, 5, 1, 1, 0, 5, 1),
MIX_ENT(SOUND_MIXER_CD, 2, 1, 0, 5, 3, 1, 0, 5),
MIX_ENT(SOUND_MIXER_IMIX, 13, 1, 2, 6, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4),
MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_LINE1, 2, 1, 0, 5, 3, 1, 0, 5),
MIX_ENT(SOUND_MIXER_LINE2, 4, 1, 0, 5, 5, 1, 0, 5),
MIX_ENT(SOUND_MIXER_LINE3, 18, 1, 0, 5, 19, 1, 0, 5)
};
mixer_ent opti931_devices[32][2] = { /* for the opti931 */
MIX_ENT(SOUND_MIXER_VOLUME, 22, 1, 1, 5, 23, 1, 1, 5),
MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 1, 4, 5, 1, 1, 4),
MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 5, 7, 1, 0, 5),
MIX_ENT(SOUND_MIXER_SPEAKER, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_LINE, 18, 1, 1, 4, 19, 1, 1, 4),
MIX_ENT(SOUND_MIXER_MIC, 0, 0, 5, 1, 1, 0, 5, 1),
MIX_ENT(SOUND_MIXER_CD, 2, 1, 1, 4, 3, 1, 1, 4),
MIX_ENT(SOUND_MIXER_IMIX, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4),
MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT(SOUND_MIXER_LINE1, 2, 1, 0, 5, 3, 1, 0, 5),
MIX_ENT(SOUND_MIXER_LINE2, 4, 1, 0, 5, 5, 1, 0, 5),
MIX_ENT(SOUND_MIXER_LINE3, 18, 1, 0, 5, 19, 1, 0, 5)
};
static u_short default_mixer_levels[SOUND_MIXER_NRDEVICES] = {
0x5a5a, /* Master Volume */
0x3232, /* Bass */
0x3232, /* Treble */
0x4b4b, /* FM */
0x4040, /* PCM */
0x4b4b, /* PC Speaker */
0x2020, /* Ext Line */
0x4040, /* Mic */
0x4b4b, /* CD */
0x0000, /* Recording monitor */
0x4b4b, /* SB PCM */
0x4b4b, /* Recording level */
0x2525, /* Input gain */
0x0000, /* Output gain */
/* 0x4040, Line1 */
0x0000, /* Line1 */
0x0000, /* Line2 */
0x1515 /* Line3 (usually line in)*/
};

1080
sys/i386/isa/snd/sb_dsp.c Normal file

File diff suppressed because it is too large Load Diff

387
sys/i386/isa/snd/sbcard.h Normal file
View File

@ -0,0 +1,387 @@
/*
* file: sbcard.h
*/
typedef struct _sbdev_info {
} sbdev_info ;
extern int sbc_major, sbc_minor ;
/*
* sound blaster registers
*/
#define DSP_RESET (io_base + 0x6)
#define DSP_READ (io_base + 0xA)
#define DSP_WRITE (io_base + 0xC)
#define DSP_COMMAND (io_base + 0xC)
#define DSP_STATUS (io_base + 0xC)
#define DSP_DATA_AVAIL (io_base + 0xE)
#define DSP_DATA_AVL16 (io_base + 0xF)
#define MIXER_ADDR (io_base + 0x4)
#define MIXER_DATA (io_base + 0x5)
#define OPL3_LEFT (io_base + 0x0)
#define OPL3_RIGHT (io_base + 0x2)
#define OPL3_BOTH (io_base + 0x8)
/*
* DSP Commands. There are many, and in many cases they are used explicitly
*/
#define DSP_DAC8 0x10 /* direct DAC output */
#define DSP_CMD_DAC8 0x14 /* single cycle 8-bit dma out */
#define DSP_CMD_DAC2 0x16 /* 2-bit adpcm dma out (cont) */
#define DSP_CMD_DAC2S 0x17 /* 2-bit adpcm dma out (start) */
#define DSP_CMD_DAC8_A 0x1c /* auto 8-bit dma out */
#define DSP_CMD_DAC2S_A 0x1f /* auto 2-bit adpcm dma out (start) */
#define DSP_ADC8 0x20 /* direct ADC input */
#define DSP_CMD_ADC8 0x24 /* single cycle 8-bit dma in */
#define DSP_CMD_ADC8_A 0x2c /* auto 8-bit dma out */
#define DSP_CMD_O16 0xb0
#define DSP_CMD_I16 0xb8
#define DSP_CMD_O8 0xc0
#define DSP_CMD_I8 0xc8
#define DSP_MODE_U8MONO 0x00
#define DSP_MODE_U8STEREO 0x20
#define DSP_MODE_S16MONO 0x10
#define DSP_MODE_S16STEREO 0x30
#define DSP_CMD_SPKON 0xD1
#define DSP_CMD_SPKOFF 0xD3
#define DSP_CMD_SPKR(on) (0xD1 | (on ? 0:2))
#define DSP_CMD_DMAON 0xD0 /* ??? the comment says Halt DMA */
#define DSP_CMD_DMAOFF 0xD4 /* ??? comment says continue dma */
#define DSP_CMD_DMAHALT 0xD0
#define DSP_CMD_TCONST 0x40 /* set time constant */
#define DSP_CMD_HSSIZE 0x48 /* high speed dma count */
#define DSP_CMD_HSDAC 0x91 /* high speed dac */
#define DSP_CMD_HSADC 0x99 /* high speed adc */
#define DSP_CMD_GETVER 0xE1
#define DSP_CMD_GETID 0xE7 /* return id bytes */
/* prepare for dma input */
#define DSP_CMD_DMAMODE(stereo, bit16) (0xA0 | (stereo ? 8:0) | (bit16 ? 4:0))
#define DSP_CMD_OUT16 0x41 /* send parms for dma out on sb16 */
#define DSP_CMD_IN16 0x42 /* send parms for dma in on sb16 */
#if 0 /*** unknown ***/
/*
* D9 and D5 are used on the sb16 on close... maybe a reset of
* some subsystem ?
*/
#define DSP_CMD_D9 0xD9
#define DSP_CMD_D5 0xD5
#define DSP_CMD_FA 0xFA /* get version from prosonic*/
#define DSP_CMD_FB 0xFB /* set irq/dma for prosonic*/
#endif
/*
* in fact, for the SB16, dma commands are as follows:
*
* cmd, mode, len_low, len_high.
*
* cmd is a combination of DSP_DMA16 or DSP_DMA8 and
*/
#define DSP_DMA16 0xb0
#define DSP_DMA8 0xc0
# define DSP_F16_DAC 0x00
# define DSP_F16_ADC 0x08
# define DSP_F16_AUTO 0x04
# define DSP_F16_FIFO_ON 0x02
/*
* mode is a combination of the following:
*/
#define DSP_F16_STEREO 0x20
#define DSP_F16_SIGNED 0x10
#define IMODE_NONE 0
#define IMODE_OUTPUT PCM_ENABLE_OUTPUT
#define IMODE_INPUT PCM_ENABLE_INPUT
#define IMODE_INIT 3
#define IMODE_MIDI 4
#define NORMAL_MIDI 0
#define UART_MIDI 1
/*
* values used for bd_flags in SoundBlaster driver
*/
#define BD_F_HISPEED 0x0001 /* doing high speed ... */
#define BD_F_JAZZ16 0x0002 /* jazz16 detected */
#define BD_F_JAZZ16_2 0x0004 /* jazz16 type 2 */
#define BD_F_DUP_MIDI 0x0008 /* duplex midi */
#define BD_F_MIX_MASK 0x0070 /* up to 8 mixers (I know of 3) */
#define BD_F_MIX_CT1335 0x0010 /* CT1335 */
#define BD_F_MIX_CT1345 0x0020 /* CT1345 */
#define BD_F_MIX_CT1745 0x0030 /* CT1745 */
#define BD_F_SB16 0x0100 /* this is a SB16 */
#define BD_F_NOREC 0x0200 /* recording not supported on this board */
#define BD_F_MIDIBUSY 0x0400 /* midi busy */
/*
* sound/sb_mixer.h
*
* Definitions for the SB Pro and SB16 mixers
*
* Copyright by Hannu Savolainen 1993
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met: 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. 2.
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Modified: Hunyue Yau Jan 6 1994 Added defines for the Sound Galaxy NX Pro
* mixer.
*
*/
#define SBPRO_RECORDING_DEVICES \
(SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD)
/* Same as SB Pro, unless I find otherwise */
#define SGNXPRO_RECORDING_DEVICES SBPRO_RECORDING_DEVICES
#define SBPRO_MIXER_DEVICES \
(SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_LINE | SOUND_MASK_MIC | \
SOUND_MASK_CD | SOUND_MASK_VOLUME)
/*
* SG NX Pro has treble and bass settings on the mixer. The 'speaker' channel
* is the COVOX/DisneySoundSource emulation volume control on the mixer. It
* does NOT control speaker volume. Should have own mask eventually?
*/
#define SGNXPRO_MIXER_DEVICES \
(SBPRO_MIXER_DEVICES | SOUND_MASK_BASS | \
SOUND_MASK_TREBLE | SOUND_MASK_SPEAKER )
#define SB16_RECORDING_DEVICES \
(SOUND_MASK_SYNTH | SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD)
#define SB16_MIXER_DEVICES \
(SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_SPEAKER | \
SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | \
SOUND_MASK_IGAIN | SOUND_MASK_OGAIN | \
SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE)
/*
* Mixer registers
*
* NOTE! RECORD_SRC == IN_FILTER
*/
/*
* Mixer registers of SB Pro
*/
#define VOC_VOL 0x04
#define MIC_VOL 0x0A
#define MIC_MIX 0x0A
#define RECORD_SRC 0x0C
#define IN_FILTER 0x0C
#define OUT_FILTER 0x0E
#define MASTER_VOL 0x22
#define FM_VOL 0x26
#define CD_VOL 0x28
#define LINE_VOL 0x2E
#define IRQ_NR 0x80
#define DMA_NR 0x81
#define IRQ_STAT 0x82
/*
* Additional registers on the SG NX Pro
*/
#define COVOX_VOL 0x42
#define TREBLE_LVL 0x44
#define BASS_LVL 0x46
#define FREQ_HI (1 << 3)/* Use High-frequency ANFI filters */
#define FREQ_LOW 0 /* Use Low-frequency ANFI filters */
#define FILT_ON 0 /* Yes, 0 to turn it on, 1 for off */
#define FILT_OFF (1 << 5)
#define MONO_DAC 0x00
#define STEREO_DAC 0x02
/*
* Mixer registers of SB16
*/
#define SB16_IMASK_L 0x3d
#define SB16_IMASK_R 0x3e
#define SB16_OMASK 0x3c
#ifndef __SB_MIXER_C__
mixer_tab sbpro_mix;
mixer_tab sb16_mix;
#ifdef __SGNXPRO__
mixer_tab sgnxpro_mix;
#endif
static u_char sb16_recmasks_L[SOUND_MIXER_NRDEVICES];
static u_char sb16_recmasks_R[SOUND_MIXER_NRDEVICES];
#else /* __SB_MIXER_C__ defined */
mixer_tab sbpro_mix = {
PMIX_ENT(SOUND_MIXER_VOLUME, 0x22, 7, 4, 0x22, 3, 4),
PMIX_ENT(SOUND_MIXER_BASS, 0x00, 0, 0, 0x00, 0, 0),
PMIX_ENT(SOUND_MIXER_TREBLE, 0x00, 0, 0, 0x00, 0, 0),
PMIX_ENT(SOUND_MIXER_SYNTH, 0x26, 7, 4, 0x26, 3, 4),
PMIX_ENT(SOUND_MIXER_PCM, 0x04, 7, 4, 0x04, 3, 4),
PMIX_ENT(SOUND_MIXER_SPEAKER, 0x00, 0, 0, 0x00, 0, 0),
PMIX_ENT(SOUND_MIXER_LINE, 0x2e, 7, 4, 0x2e, 3, 4),
PMIX_ENT(SOUND_MIXER_MIC, 0x0a, 2, 3, 0x00, 0, 0),
PMIX_ENT(SOUND_MIXER_CD, 0x28, 7, 4, 0x28, 3, 4),
PMIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0),
PMIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0),
PMIX_ENT(SOUND_MIXER_RECLEV, 0x00, 0, 0, 0x00, 0, 0)
};
#ifdef __SGNXPRO__
mixer_tab sgnxpro_mix = {
PMIX_ENT(SOUND_MIXER_VOLUME, 0x22, 7, 4, 0x22, 3, 4),
PMIX_ENT(SOUND_MIXER_BASS, 0x46, 2, 3, 0x00, 0, 0),
PMIX_ENT(SOUND_MIXER_TREBLE, 0x44, 2, 3, 0x00, 0, 0),
PMIX_ENT(SOUND_MIXER_SYNTH, 0x26, 7, 4, 0x26, 3, 4),
PMIX_ENT(SOUND_MIXER_PCM, 0x04, 7, 4, 0x04, 3, 4),
PMIX_ENT(SOUND_MIXER_SPEAKER, 0x42, 2, 3, 0x00, 0, 0),
PMIX_ENT(SOUND_MIXER_LINE, 0x2e, 7, 4, 0x2e, 3, 4),
PMIX_ENT(SOUND_MIXER_MIC, 0x0a, 2, 3, 0x00, 0, 0),
PMIX_ENT(SOUND_MIXER_CD, 0x28, 7, 4, 0x28, 3, 4),
PMIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0),
PMIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0),
PMIX_ENT(SOUND_MIXER_RECLEV, 0x00, 0, 0, 0x00, 0, 0),
PMIX_ENT(SOUND_MIXER_IGAIN, 0x00, 0, 0, 0x00, 0, 0),
PMIX_ENT(SOUND_MIXER_OGAIN, 0x00, 0, 0, 0x00, 0, 0)
};
#endif
mixer_tab sb16_mix = {
PMIX_ENT(SOUND_MIXER_VOLUME, 0x30, 3, 5, 0x31, 3, 5),
PMIX_ENT(SOUND_MIXER_BASS, 0x46, 4, 4, 0x47, 4, 4),
PMIX_ENT(SOUND_MIXER_TREBLE, 0x44, 4, 4, 0x45, 4, 4),
PMIX_ENT(SOUND_MIXER_SYNTH, 0x34, 3, 5, 0x35, 3, 5),
PMIX_ENT(SOUND_MIXER_PCM, 0x32, 3, 5, 0x33, 3, 5),
PMIX_ENT(SOUND_MIXER_SPEAKER, 0x3b, 6, 2, 0x00, 0, 0),
PMIX_ENT(SOUND_MIXER_LINE, 0x38, 3, 5, 0x39, 3, 5),
PMIX_ENT(SOUND_MIXER_MIC, 0x3a, 3, 5, 0x00, 0, 0),
PMIX_ENT(SOUND_MIXER_CD, 0x36, 3, 5, 0x37, 3, 5),
PMIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0),
PMIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0),
PMIX_ENT(SOUND_MIXER_RECLEV, 0x3f, 6, 2, 0x40, 6, 2), /* Obsol,Use IGAIN*/
PMIX_ENT(SOUND_MIXER_IGAIN, 0x3f, 6, 2, 0x40, 6, 2),
PMIX_ENT(SOUND_MIXER_OGAIN, 0x41, 6, 2, 0x42, 6, 2)
};
#ifdef SM_GAMES /* Master volume is lower and PCM & FM
* volumes higher than with SB Pro. This
* improves the sound quality */
static u_short levels[SOUND_MIXER_NRDEVICES] =
{
0x2020, /* Master Volume */
0x4b4b, /* Bass */
0x4b4b, /* Treble */
0x6464, /* FM */
0x6464, /* PCM */
0x4b4b, /* PC Speaker */
0x4b4b, /* Ext Line */
0x0000, /* Mic */
0x4b4b, /* CD */
0x4b4b, /* Recording monitor */
0x4b4b, /* SB PCM */
0x4b4b, /* Recording level */
0x4b4b, /* Input gain */
0x4b4b}; /* Output gain */
#else /* If the user selected just plain SB Pro */
static u_short levels[SOUND_MIXER_NRDEVICES] =
{
0x5a5a, /* Master Volume */
0x4b4b, /* Bass */
0x4b4b, /* Treble */
0x4b4b, /* FM */
0x4b4b, /* PCM */
0x4b4b, /* PC Speaker */
0x4b4b, /* Ext Line */
0x1010, /* Mic */
0x4b4b, /* CD */
0x4b4b, /* Recording monitor */
0x4b4b, /* SB PCM */
0x4b4b, /* Recording level */
0x4b4b, /* Input gain */
0x4b4b}; /* Output gain */
#endif /* SM_GAMES */
static u_char sb16_recmasks_L[SOUND_MIXER_NRDEVICES] =
{
0x00, /* SOUND_MIXER_VOLUME */
0x00, /* SOUND_MIXER_BASS */
0x00, /* SOUND_MIXER_TREBLE */
0x40, /* SOUND_MIXER_SYNTH */
0x00, /* SOUND_MIXER_PCM */
0x00, /* SOUND_MIXER_SPEAKER */
0x10, /* SOUND_MIXER_LINE */
0x01, /* SOUND_MIXER_MIC */
0x04, /* SOUND_MIXER_CD */
0x00, /* SOUND_MIXER_IMIX */
0x00, /* SOUND_MIXER_ALTPCM */
0x00, /* SOUND_MIXER_RECLEV */
0x00, /* SOUND_MIXER_IGAIN */
0x00 /* SOUND_MIXER_OGAIN */
};
static u_char sb16_recmasks_R[SOUND_MIXER_NRDEVICES] =
{
0x00, /* SOUND_MIXER_VOLUME */
0x00, /* SOUND_MIXER_BASS */
0x00, /* SOUND_MIXER_TREBLE */
0x20, /* SOUND_MIXER_SYNTH */
0x00, /* SOUND_MIXER_PCM */
0x00, /* SOUND_MIXER_SPEAKER */
0x08, /* SOUND_MIXER_LINE */
0x01, /* SOUND_MIXER_MIC */
0x02, /* SOUND_MIXER_CD */
0x00, /* SOUND_MIXER_IMIX */
0x00, /* SOUND_MIXER_ALTPCM */
0x00, /* SOUND_MIXER_RECLEV */
0x00, /* SOUND_MIXER_IGAIN */
0x00 /* SOUND_MIXER_OGAIN */
};
/*
* Recording sources (SB Pro)
*/
#endif /* __SB_MIXER_C__ */
#define SRC_MIC 1 /* Select Microphone recording source */
#define SRC_CD 3 /* Select CD recording source */
#define SRC_LINE 7 /* Use Line-in for recording source */

1254
sys/i386/isa/snd/sound.c Normal file

File diff suppressed because it is too large Load Diff

492
sys/i386/isa/snd/sound.h Normal file
View File

@ -0,0 +1,492 @@
/*
* sound.h
*
* include file for kernel sources, sound driver.
*
* Copyright by Hannu Savolainen 1995
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include "pcm.h"
#if NPCM > 0
/*
* first, include kernel header files.
*/
#ifndef _OS_H_
#define _OS_H_
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/ioccom.h>
#include <sys/filio.h>
#include <sys/sockio.h>
#include <sys/fcntl.h>
#include <sys/tty.h>
#include <sys/proc.h>
#include <sys/kernel.h> /* for DATA_SET */
#include <sys/conf.h>
#include <sys/file.h>
#include <sys/uio.h>
#include <sys/syslog.h>
#include <sys/errno.h>
#include <sys/malloc.h>
#include <sys/buf.h>
#include <i386/isa/isa_device.h>
#include <machine/clock.h> /* for DELAY */
typedef void (irq_proc_t) (int irq);
#endif /* _OS_H_ */
/*
* descriptor of a dma buffer. See dmabuf.c for documentation.
* (rp,rl) and (fp,fl) identify the READY and FREE regions of the
* buffer. (dp,dl) identify the region currently used by the DMA.
* Only dmabuf.c should test the value of dl. The reason is that
* in auto-dma mode looking at dl alone does not make much sense,
* since the transfer potentially spans the entire buffer.
*/
typedef struct _snd_dbuf {
char *buf;
int bufsize ;
volatile int dp, rp, fp;
volatile int dl, rl, fl;
volatile int dl0; /* value used last time in dl */
int int_count;
} snd_dbuf ;
/*
* descriptor of audio operations ...
*
*/
typedef struct _snddev_info snddev_info ;
typedef int (snd_callback_t)(snddev_info *d, int reason);
struct _snddev_info {
/*
* the first part of the descriptor is filled up from a
* template.
*/
char name[64];
int type ;
int (*probe)(struct isa_device * dev);
int (*attach)(struct isa_device * dev) ;
d_open_t *open ;
d_close_t *close ;
d_read_t *read ;
d_write_t *write ;
d_ioctl_t *ioctl ;
d_select_t *select ;
irq_proc_t *isr ;
snd_callback_t *callback;
int bufsize; /* space used for buffers */
u_long audio_fmt ; /* supported audio formats */
/*
* combinations of the following flags are used as second argument in
* the callback from the dma module to the device-specific routines.
*/
#define SND_CB_RD 0x100 /* read callback */
#define SND_CB_WR 0x200 /* write callback */
#define SND_CB_REASON_MASK 0xff
#define SND_CB_START 0x01 /* start dma op */
#define SND_CB_RESTART 0x02 /* restart dma op */
#define SND_CB_STOP 0x03 /* stop dma op */
#define SND_CB_ABORT 0x04 /* abort dma op */
#define SND_CB_INIT 0x05 /* init board parameters */
/* init can only be called with int enabled and
* no pending DMA activity.
*/
/*
* whereas from here, parameters are set at runtime.
*/
int io_base ; /* primary I/O address for the board */
int alt_base ; /* some codecs are accessible as SB+WSS... */
int conf_base ; /* and the opti931 also has a config space */
int mix_base ; /* base for the mixer... */
int midi_base ; /* base for the midi */
int synth_base ; /* base for the synth */
int irq ;
int dma1, dma2 ; /* dma2=dma1 for half-duplex cards */
int bd_id ; /* used to hold board-id info, eg. sb version,
* mss codec type, etc. etc.
*/
snd_dbuf dbuf_out, dbuf_in;
int status_ptr; /* used to implement sndstat */
/*
* these parameters describe the operation of the board.
* Generic things like busy flag, speed, etc are here.
*/
volatile u_long flags ; /* 32 bits, used for various purposes. */
/*
* we have separate flags for read and write, although in some
* cases this is probably not necessary (e.g. because we cannot
* know how many processes are using the device, we cannot
* distinguish if open, close, abort are for a write or for a
* read).
*/
/*
* the following flag is used by open-close routines
* to mark the status of the device.
*/
#define SND_F_BUSY 0x0001 /* has been opened */
/*
* Only the last close for a device will propagate to the driver.
* Unfortunately, voxware uses 3 different minor numbers
* (dsp, dsp16 and audio) to access the same unit. So, if
* we want to support multiple opens and still keep track of
* what is happening, we also need a separate flag for each minor
* number. These are below...
*/
#define SND_F_BUSY_AUDIO 0x10000000
#define SND_F_BUSY_DSP 0x20000000
#define SND_F_BUSY_DSP16 0x40000000
#define SND_F_BUSY_ANY 0x70000000
/*
* the next two are used to allow only one pending operation of
* each type.
*/
#define SND_F_READING 0x0004 /* have a pending read */
#define SND_F_WRITING 0x0008 /* have a pending write */
/*
* these mark pending DMA operations. When you have pending dma ops,
* you might get interrupts, so some manipulations of the
* descriptors must be done with interrupts blocked.
*/
#define SND_F_RD_DMA 0x0010 /* read-dma active */
#define SND_F_WR_DMA 0x0020 /* write-dma active */
#define SND_F_PENDING_IN (SND_F_READING | SND_F_RD_DMA)
#define SND_F_PENDING_OUT (SND_F_WRITING | SND_F_WR_DMA)
#define SND_F_PENDING_IO (SND_F_PENDING_IN | SND_F_PENDING_OUT)
/*
* flag used to mark a pending close.
*/
#define SND_F_CLOSING 0x0040 /* a pending close */
/*
* if user has not set block size, then make it adaptive
* (0.25s, or the perhaps last read/write ?)
*/
#define SND_F_HAS_SIZE 0x0080 /* user set block size */
/*
* assorted flags related to operating mode.
*/
#define SND_F_STEREO 0x0100 /* doing stereo */
#define SND_F_NBIO 0x0200 /* do non-blocking i/o */
/*
* the user requested ulaw, but the board does not support it
* natively, so a (software) format conversion is necessary.
* The kernel is not really the place to do this, but since
* many applications expect to use /dev/audio , we do it for
* portability.
*/
#define SND_F_XLAT8 0x0400 /* u-law <--> 8-bit unsigned */
#define SND_F_XLAT16 0x0800 /* u-law <--> 16-bit signed */
/*
* these flags mark a pending abort on a r/w operation.
*/
#define SND_F_ABORTING 0x1000 /* a pending abort */
/*
* this is used to mark that board initialization is needed, e.g.
* because of a change in sampling rate, format, etc. -- It will
* be done at the next convenient time.
*/
#define SND_F_INIT 0x4000 /* changed parameters. need init */
#define SND_F_AUTO_DMA 0x8000 /* use auto-dma */
u_long bd_flags; /* board-specific flags */
int play_speed, rec_speed;
int play_blocksize, rec_blocksize; /* blocksize for io and dma ops */
u_long play_fmt, rec_fmt ; /* current audio format */
/*
* mixer parameters
*/
u_long mix_devs; /* existing devices for mixer */
u_long mix_rec_devs; /* possible recording sources */
u_long mix_recsrc; /* current recording source(s) */
u_short mix_levels[32];
struct selinfo wsel, rsel, esel ;
u_long interrupts; /* counter of interrupts */
void *device_data ; /* just in case it is needed...*/
} ;
/*
* then ioctls and other stuff
*/
#define NPCM_MAX 8 /* Number of supported devices */
/*
* Supported card ID numbers (were in soundcard.h...)
*/
#define SNDCARD_ADLIB 1
#define SNDCARD_SB 2
#define SNDCARD_PAS 3
#define SNDCARD_GUS 4
#define SNDCARD_MPU401 5
#define SNDCARD_SB16 6
#define SNDCARD_SB16MIDI 7
#define SNDCARD_UART6850 8
#define SNDCARD_GUS16 9
#define SNDCARD_MSS 10
#define SNDCARD_PSS 11
#define SNDCARD_SSCAPE 12
#define SNDCARD_PSS_MPU 13
#define SNDCARD_PSS_MSS 14
#define SNDCARD_SSCAPE_MSS 15
#define SNDCARD_TRXPRO 16
#define SNDCARD_TRXPRO_SB 17
#define SNDCARD_TRXPRO_MPU 18
#define SNDCARD_MAD16 19
#define SNDCARD_MAD16_MPU 20
#define SNDCARD_CS4232 21
#define SNDCARD_CS4232_MPU 22
#define SNDCARD_MAUI 23
#define SNDCARD_PSEUDO_MSS 24 /* MSS without WSS regs.*/
#define SNDCARD_AWE32 25
/*
* values used in bd_id for the mss boards
*/
#define MD_AD1848 0x91
#define MD_AD1845 0x92
#define MD_CS4248 0xA1
#define MD_CS4231 0xA2
#define MD_CS4231A 0xA3
#define MD_CS4232 0xA4
#define MD_CS4232A 0xA5
#define MD_CS4236 0xA6
#define MD_OPTI931 0xB1
/*
* TODO: add some card classes rather than specific types.
*/
#include <i386/isa/snd/soundcard.h>
/*
* many variables should be reduced to a range. Here define a macro
*/
#define RANGE(var, low, high) (var) = \
((var)<(low)?(low) : (var)>(high)?(high) : (var))
/*
* finally, all default parameters
*/
#define DSP_BUFFSIZE 65536 /* XXX */
#if 1 /* prepare for pnp support! */
#include "pnp.h"
#if NPNP > 0
#include <i386/isa/pnp.h> /* XXX pnp support */
#endif
#endif
/*
* Minor numbers for the sound driver.
*
* Unfortunately Creative called the codec chip of SB as a DSP. For this
* reason the /dev/dsp is reserved for digitized audio use. There is a
* device for true DSP processors but it will be called something else.
* In v3.0 it's /dev/sndproc but this could be a temporary solution.
*/
#define SND_DEV_CTL 0 /* Control port /dev/mixer */
#define SND_DEV_SEQ 1 /* Sequencer output /dev/sequencer (FM
synthesizer and MIDI output) */
#define SND_DEV_MIDIN 2 /* Raw midi access */
#define SND_DEV_DSP 3 /* Digitized voice /dev/dsp */
#define SND_DEV_AUDIO 4 /* Sparc compatible /dev/audio */
#define SND_DEV_DSP16 5 /* Like /dev/dsp but 16 bits/sample */
#define SND_DEV_STATUS 6 /* /dev/sndstat */
/* #7 not in use now. Was in 2.4. Free for use after v3.0. */
#define SND_DEV_SEQ2 8 /* /dev/sequencer, level 2 interface */
#define SND_DEV_SNDPROC 9 /* /dev/sndproc for programmable devices */
#define SND_DEV_PSS SND_DEV_SNDPROC
#define DSP_DEFAULT_SPEED 8000
#define ON 1
#define OFF 0
#define SYNTH_MAX_VOICES 32
struct voice_alloc_info {
int max_voice;
int used_voices;
int ptr; /* For device specific use */
u_short map[SYNTH_MAX_VOICES]; /* (ch << 8) | (note+1) */
int timestamp;
int alloc_times[SYNTH_MAX_VOICES];
};
struct channel_info {
int pgm_num;
int bender_value;
u_char controllers[128];
};
/*
* mixer description structure and macros
*/
struct mixer_def {
u_int regno:7;
u_int polarity:1; /* 1 means reversed */
u_int bitoffs:4;
u_int nbits:4;
};
typedef struct mixer_def mixer_ent;
typedef struct mixer_def mixer_tab[32][2];
#define MIX_ENT(name, reg_l, pol_l, pos_l, len_l, reg_r, pol_r, pos_r, len_r) \
{{reg_l, pol_l, pos_l, len_l}, {reg_r, pol_r, pos_r, len_r}}
#define PMIX_ENT(name, reg_l, pos_l, len_l, reg_r, pos_r, len_r) \
{{reg_l, 0, pos_l, len_l}, {reg_r, 0, pos_r, len_r}}
#define DDB(x) x /* XXX */
#ifndef DEB
#define DEB(x)
#endif
#ifndef DDB
#define DDB(x)
#endif
extern snddev_info pcm_info[NPCM_MAX] ;
extern snddev_info midi_info[NPCM_MAX] ;
extern snddev_info synth_info[NPCM_MAX] ;
extern u_long nsnd ;
extern snddev_info *snddev_last_probed;
int pcmprobe(struct isa_device * dev);
int midiprobe(struct isa_device * dev);
int synthprobe(struct isa_device * dev);
int pcmattach(struct isa_device * dev);
int midiattach(struct isa_device * dev);
int synthattach(struct isa_device * dev);
/*
* functions in isa.c
*/
int isa_dmastatus(int chan);
int isa_dmastop(int chan);
/*
* DMA buffer calls
*/
void dsp_wrintr(snddev_info *d);
void dsp_rdintr(snddev_info *d);
int dsp_write_body(snddev_info *d, struct uio *buf);
int dsp_read_body(snddev_info *d, struct uio *buf);
void alloc_dbuf(snd_dbuf *d, int size, int b16);
void reset_dbuf(snd_dbuf *b);
int snd_flush(snddev_info *d);
int snd_sync(snddev_info *d, int chan, int threshold);
int dsp_wrabort(snddev_info *d);
int dsp_rdabort(snddev_info *d);
void dsp_wr_dmaupdate(snddev_info *d);
void dsp_rd_dmaupdate(snddev_info *d);
d_select_t sndselect;
/*
* library functions (in sound.c)
*/
int ask_init(snddev_info *d);
void translate_bytes(u_char *table, u_char *buff, int n);
void change_bits(mixer_tab *t, u_char *regval, int dev, int chn, int newval);
int snd_conflict(int io_base);
void snd_set_blocksize(snddev_info *d);
int isa_dmastatus1(int channel);
/*
* routines in ad1848.c and sb_dsp.c which others might use
*/
int mss_detect (struct isa_device *dev);
int sb_cmd (int io_base, u_char cmd);
int sb_cmd2 (int io_base, u_char cmd, int val);
int sb_cmd3 (int io_base, u_char cmd, int val);
int sb_reset_dsp (int io_base);
void sb_setmixer (int io_base, u_int port, u_int value);
int sb_getmixer (int io_base, u_int port);
/*
* usage of flags in device config entry (config file)
*/
#define DV_F_DRQ_MASK 0x00000007 /* mask for secondary drq */
#define DV_F_DUAL_DMA 0x00000010 /* set to use secondary dma channel */
#define DV_F_DEV_MASK 0x0000ff00 /* force device type/class */
#define DV_F_DEV_SHIFT 8 /* force device type/class */
/*
* some flags are used in a device-specific manner, so that values can
* be used multiple times.
*/
#define DV_F_TRUE_MSS 0x00010000 /* mss _with_ base regs */
/* almost all modern cards do not have this set of registers,
* so it is better to make this the default behaviour
*/
#endif

1297
sys/i386/isa/snd/soundcard.h Normal file

File diff suppressed because it is too large Load Diff

93
sys/i386/isa/snd/ulaw.h Normal file
View File

@ -0,0 +1,93 @@
/*
* on entry: ulaw, on exit: unsigned 8 bit.
*/
static unsigned char ulaw_dsp[] = {
3, 7, 11, 15, 19, 23, 27, 31,
35, 39, 43, 47, 51, 55, 59, 63,
66, 68, 70, 72, 74, 76, 78, 80,
82, 84, 86, 88, 90, 92, 94, 96,
98, 99, 100, 101, 102, 103, 104, 105,
106, 107, 108, 109, 110, 111, 112, 113,
113, 114, 114, 115, 115, 116, 116, 117,
117, 118, 118, 119, 119, 120, 120, 121,
121, 121, 122, 122, 122, 122, 123, 123,
123, 123, 124, 124, 124, 124, 125, 125,
125, 125, 125, 125, 126, 126, 126, 126,
126, 126, 126, 126, 127, 127, 127, 127,
127, 127, 127, 127, 127, 127, 127, 127,
128, 128, 128, 128, 128, 128, 128, 128,
128, 128, 128, 128, 128, 128, 128, 128,
128, 128, 128, 128, 128, 128, 128, 128,
253, 249, 245, 241, 237, 233, 229, 225,
221, 217, 213, 209, 205, 201, 197, 193,
190, 188, 186, 184, 182, 180, 178, 176,
174, 172, 170, 168, 166, 164, 162, 160,
158, 157, 156, 155, 154, 153, 152, 151,
150, 149, 148, 147, 146, 145, 144, 143,
143, 142, 142, 141, 141, 140, 140, 139,
139, 138, 138, 137, 137, 136, 136, 135,
135, 135, 134, 134, 134, 134, 133, 133,
133, 133, 132, 132, 132, 132, 131, 131,
131, 131, 131, 131, 130, 130, 130, 130,
130, 130, 130, 130, 129, 129, 129, 129,
129, 129, 129, 129, 129, 129, 129, 129,
128, 128, 128, 128, 128, 128, 128, 128,
128, 128, 128, 128, 128, 128, 128, 128,
128, 128, 128, 128, 128, 128, 128, 128,
};
#ifndef DSP_ULAW_NOT_WANTED
/*
* on entry: unsigned 8-bit, on exit: ulaw.
*/
static unsigned char dsp_ulaw[] = {
0, 0, 0, 0, 0, 1, 1, 1,
1, 2, 2, 2, 2, 3, 3, 3,
3, 4, 4, 4, 4, 5, 5, 5,
5, 6, 6, 6, 6, 7, 7, 7,
7, 8, 8, 8, 8, 9, 9, 9,
9, 10, 10, 10, 10, 11, 11, 11,
11, 12, 12, 12, 12, 13, 13, 13,
13, 14, 14, 14, 14, 15, 15, 15,
15, 16, 16, 17, 17, 18, 18, 19,
19, 20, 20, 21, 21, 22, 22, 23,
23, 24, 24, 25, 25, 26, 26, 27,
27, 28, 28, 29, 29, 30, 30, 31,
31, 32, 33, 34, 35, 36, 37, 38,
39, 40, 41, 42, 43, 44, 45, 46,
47, 49, 51, 53, 55, 57, 59, 61,
63, 66, 70, 74, 78, 84, 92, 104,
254, 231, 219, 211, 205, 201, 197, 193,
190, 188, 186, 184, 182, 180, 178, 176,
175, 174, 173, 172, 171, 170, 169, 168,
167, 166, 165, 164, 163, 162, 161, 160,
159, 159, 158, 158, 157, 157, 156, 156,
155, 155, 154, 154, 153, 153, 152, 152,
151, 151, 150, 150, 149, 149, 148, 148,
147, 147, 146, 146, 145, 145, 144, 144,
143, 143, 143, 143, 142, 142, 142, 142,
141, 141, 141, 141, 140, 140, 140, 140,
139, 139, 139, 139, 138, 138, 138, 138,
137, 137, 137, 137, 136, 136, 136, 136,
135, 135, 135, 135, 134, 134, 134, 134,
133, 133, 133, 133, 132, 132, 132, 132,
131, 131, 131, 131, 130, 130, 130, 130,
129, 129, 129, 129, 128, 128, 128, 128,
};
#endif /* !DSP_ULAW_NOT_WANTED */