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:
parent
634a86ba38
commit
b7da7b3e2a
1509
sys/dev/pcm/isa/mss.c
Normal file
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
250
sys/dev/pcm/isa/mss.h
Normal 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
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
387
sys/dev/pcm/isa/sb.h
Normal 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
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
250
sys/dev/sound/isa/mss.h
Normal 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
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
387
sys/dev/sound/isa/sb.h
Normal 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
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
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
78
sys/i386/isa/snd/README
Normal 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
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
262
sys/i386/isa/snd/clones.c
Normal 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
864
sys/i386/isa/snd/dmabuf.c
Normal 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
|
||||
*/
|
802
sys/i386/isa/snd/dmabuf_auto.c
Normal file
802
sys/i386/isa/snd/dmabuf_auto.c
Normal 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
250
sys/i386/isa/snd/mss.h
Normal 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
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
387
sys/i386/isa/snd/sbcard.h
Normal 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
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
492
sys/i386/isa/snd/sound.h
Normal 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
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
93
sys/i386/isa/snd/ulaw.h
Normal 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 */
|
Loading…
Reference in New Issue
Block a user