These files have been repo-copied to dev/sound/* (making way for pcm

and midi to share the backend files).
This commit is contained in:
Peter Wemm 1999-11-21 15:58:07 +00:00
parent 3e0c8d78f3
commit 959b26f66f
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=53506
28 changed files with 0 additions and 11278 deletions

View file

@ -1,335 +0,0 @@
/*
* Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
* All rights reserved.
*
* 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.
*
* $FreeBSD$
*/
#include <dev/pcm/sound.h>
#include <dev/pcm/ac97.h>
#define AC97_MUTE 0x8000
#define AC97_REG_RESET 0x00
#define AC97_MIX_MASTER 0x02
#define AC97_MIX_PHONES 0x04
#define AC97_MIX_MONO 0x06
#define AC97_MIX_TONE 0x08
#define AC97_MIX_BEEP 0x0a
#define AC97_MIX_PHONE 0x0c
#define AC97_MIX_MIC 0x0e
#define AC97_MIX_LINE 0x10
#define AC97_MIX_CD 0x12
#define AC97_MIX_VIDEO 0x14
#define AC97_MIX_AUX 0x16
#define AC97_MIX_PCM 0x18
#define AC97_REG_RECSEL 0x1a
#define AC97_MIX_RGAIN 0x1c
#define AC97_MIX_MGAIN 0x1e
#define AC97_REG_GEN 0x20
#define AC97_REG_3D 0x22
#define AC97_REG_POWER 0x26
#define AC97_REG_ID1 0x7c
#define AC97_REG_ID2 0x7e
struct ac97mixtable_entry {
int reg:8;
unsigned bits:4;
unsigned ofs:4;
unsigned stereo:1;
unsigned mute:1;
unsigned recidx:4;
unsigned mask:1;
};
struct ac97_info {
ac97_read *read;
ac97_write *write;
void *devinfo;
char id[4];
char rev;
unsigned caps, se;
struct ac97mixtable_entry mix[32];
};
struct ac97_codecid {
u_int32_t id;
char *name;
};
static const struct ac97mixtable_entry ac97mixtable_default[32] = {
[SOUND_MIXER_VOLUME] = { AC97_MIX_MASTER, 5, 0, 1, 1, 6, 0 },
[SOUND_MIXER_BASS] = { AC97_MIX_TONE, 4, 8, 0, 0, 0, 1 },
[SOUND_MIXER_TREBLE] = { AC97_MIX_TONE, 4, 0, 0, 0, 0, 1 },
[SOUND_MIXER_PCM] = { AC97_MIX_PCM, 5, 0, 1, 1, 0, 0 },
[SOUND_MIXER_SPEAKER] = { AC97_MIX_BEEP, 4, 1, 0, 1, 0, 0 },
[SOUND_MIXER_LINE] = { AC97_MIX_LINE, 5, 0, 1, 1, 5, 0 },
[SOUND_MIXER_MIC] = { AC97_MIX_MIC, 5, 0, 0, 1, 1, 0 },
[SOUND_MIXER_CD] = { AC97_MIX_CD, 5, 0, 1, 1, 2, 0 },
[SOUND_MIXER_LINE1] = { AC97_MIX_AUX, 5, 0, 1, 1, 4, 0 },
[SOUND_MIXER_VIDEO] = { AC97_MIX_VIDEO, 5, 0, 1, 1, 3, 0 },
[SOUND_MIXER_RECLEV] = { -AC97_MIX_RGAIN, 4, 0, 1, 1, 0, 0 }
};
static const unsigned ac97mixdevs =
SOUND_MASK_VOLUME |
SOUND_MASK_PCM | SOUND_MASK_SPEAKER | SOUND_MASK_LINE |
SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_LINE1 |
SOUND_MASK_VIDEO | SOUND_MASK_RECLEV;
static const unsigned ac97recdevs =
SOUND_MASK_VOLUME | SOUND_MASK_LINE | SOUND_MASK_MIC |
SOUND_MASK_CD | SOUND_MASK_LINE1 | SOUND_MASK_VIDEO;
static struct ac97_codecid ac97codecid[] = {
{ 0x414B4D00, "Asahi Kasei AK4540" },
{ 0x43525900, "Cirrus Logic CS4297" },
{ 0x83847600, "SigmaTel STAC????" },
{ 0x83847604, "SigmaTel STAC9701/3/4/5" },
{ 0x83847605, "SigmaTel STAC9704" },
{ 0x83847608, "SigmaTel STAC9708" },
{ 0x83847609, "SigmaTel STAC9721" },
{ 0, NULL }
};
static char *ac97enhancement[] = {
"",
"Analog Devices Phat Stereo",
"Creative Stereo Enhancement",
"National Semi 3D Stereo Enhancement",
"Yamaha Ymersion",
"BBE 3D Stereo Enhancement",
"Crystal Semi 3D Stereo Enhancement",
"Qsound QXpander",
"Spatializer 3D Stereo Enhancement",
"SRS 3D Stereo Enhancement",
"Platform Tech 3D Stereo Enhancement",
"AKM 3D Audio",
"Aureal Stereo Enhancement",
"Aztech 3D Enhancement",
"Binaura 3D Audio Enhancement",
"ESS Technology Stereo Enhancement",
"Harman International VMAx",
"Nvidea 3D Stereo Enhancement",
"Philips Incredible Sound",
"Texas Instruments 3D Stereo Enhancement",
"VLSI Technology 3D Stereo Enhancement",
"TriTech 3D Stereo Enhancement",
"Realtek 3D Stereo Enhancement",
"Samsung 3D Stereo Enhancement",
"Wolfson Microelectronics 3D Enhancement",
"Delta Integration 3D Enhancement",
"SigmaTel 3D Enhancement",
"Reserved 27",
"Rockwell 3D Stereo Enhancement",
"Reserved 29",
"Reserved 30",
"Reserved 31"
};
static char *ac97feature[] = {
"mic channel",
"reserved",
"tone",
"simulated stereo",
"headphone",
"bass boost",
"18 bit DAC",
"20 bit DAC",
"18 bit ADC",
"20 bit ADC"
};
static int
ac97_setrecsrc(struct ac97_info *codec, int channel)
{
struct ac97mixtable_entry *e = &codec->mix[channel];
if (e->recidx > 0) {
int val = e->recidx - 1;
val |= val << 8;
codec->write(codec->devinfo, AC97_REG_RECSEL, val);
return 0;
} else return -1;
}
static int
ac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned right)
{
struct ac97mixtable_entry *e = &codec->mix[channel];
if (e->reg != 0) {
int max, val;
if (!e->stereo) right = left;
if (e->reg > 0) {
left = 100 - left;
right = 100 - right;
}
max = (1 << e->bits) - 1;
left = (left * max) / 100;
right = (right * max) / 100;
val = (left << 8) | right;
left = (left * 100) / max;
right = (right * 100) / max;
if (e->reg > 0) {
left = 100 - left;
right = 100 - right;
}
if (!e->stereo) {
val &= max;
val <<= e->ofs;
if (e->mask) {
int cur = codec->read(codec->devinfo, e->reg);
val |= cur & ~(max << e->ofs);
}
}
if (left == 0 && right == 0 && e->mute == 1) val = AC97_MUTE;
codec->write(codec->devinfo, abs(e->reg), val);
return left | (right << 8);
} else return -1;
}
#if 0
static int
ac97_getmixer(struct ac97_info *codec, int channel)
{
struct ac97mixtable_entry *e = &codec->mix[channel];
if (channel < SOUND_MIXER_NRDEVICES && e->reg != 0) {
int max, val, volume;
max = (1 << e->bits) - 1;
val = codec->read(codec->devinfo, e->reg);
if (val == AC97_MUTE && e->mute == 1) volume = 0;
else {
if (e->stereo == 0) val >>= e->ofs;
val &= max;
volume = (val * 100) / max;
if (e->reg > 0) volume = 100 - volume;
}
return volume;
} else return -1;
}
#endif
static unsigned
ac97_init(struct ac97_info *codec)
{
unsigned i, j;
u_int32_t id;
for (i = 0; i < 32; i++) codec->mix[i] = ac97mixtable_default[i];
codec->write(codec->devinfo, AC97_REG_POWER, 0);
codec->write(codec->devinfo, AC97_REG_RESET, 0);
DELAY(10000);
i = codec->read(codec->devinfo, AC97_REG_RESET);
codec->caps = i & 0x03ff;
codec->se = (i & 0x7c00) >> 10;
id = (codec->read(codec->devinfo, AC97_REG_ID1) << 16) |
codec->read(codec->devinfo, AC97_REG_ID2);
codec->rev = id & 0x000000ff;
codec->write(codec->devinfo, AC97_MIX_MASTER, 0x20);
if ((codec->read(codec->devinfo, AC97_MIX_MASTER) & 0x20) == 0x20)
codec->mix[SOUND_MIXER_VOLUME].bits++;
codec->write(codec->devinfo, AC97_MIX_MASTER, 0x00);
if (bootverbose) {
printf("ac97: codec id 0x%8x", id);
for (i = 0; ac97codecid[i].id; i++) {
if (ac97codecid[i].id == id) printf(" (%s)", ac97codecid[i].name);
}
printf("\nac97: codec features ");
for (i = j = 0; i < 10; i++) {
if (codec->caps & (1 << i)) {
printf("%s%s", j? ", " : "", ac97feature[i]);
j++;
}
}
printf("%s%d bit master volume", j? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits);
printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]);
}
if ((codec->read(codec->devinfo, AC97_REG_POWER) & 2) == 0)
printf("ac97: dac not ready\n");
return 0;
}
struct ac97_info *
ac97_create(void *devinfo, ac97_read *rd, ac97_write *wr)
{
struct ac97_info *codec;
codec = (struct ac97_info *)malloc(sizeof *codec, M_DEVBUF, M_NOWAIT);
if (codec != NULL) {
codec->read = rd;
codec->write = wr;
codec->devinfo = devinfo;
}
return codec;
}
static int
ac97mix_init(snd_mixer *m)
{
struct ac97_info *codec = mix_getdevinfo(m);
if (codec == NULL) return -1;
ac97_init(codec);
mix_setdevs(m, ac97mixdevs | ((codec->caps & 4)? SOUND_MASK_BASS | SOUND_MASK_TREBLE : 0));
mix_setrecdevs(m, ac97recdevs);
return 0;
}
static int
ac97mix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right)
{
struct ac97_info *codec = mix_getdevinfo(m);
if (codec == NULL) return -1;
return ac97_setmixer(codec, dev, left, right);
}
static int
ac97mix_setrecsrc(snd_mixer *m, u_int32_t src)
{
int i;
struct ac97_info *codec = mix_getdevinfo(m);
if (codec == NULL) return -1;
for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
if ((src & (1 << i)) != 0) break;
return (ac97_setrecsrc(codec, i) == 0)? 1 << i : -1;
}
snd_mixer ac97_mixer = {
"AC97 mixer",
ac97mix_init,
ac97mix_set,
ac97mix_setrecsrc,
};

View file

@ -1,35 +0,0 @@
/*
* Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
* All rights reserved.
*
* 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.
*
* $FreeBSD$
*/
typedef u_int32_t (ac97_read)(void *devinfo, int regno);
typedef void (ac97_write)(void *devinfo, int regno, u_int32_t data);
extern snd_mixer ac97_mixer;
struct ac97_info;
struct ac97_info *ac97_create(void *devinfo, ac97_read *rd, ac97_write *wr);

View file

@ -1,746 +0,0 @@
/*
* Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
* Portions Copyright by Luigi Rizzo - 1997-99
* All rights reserved.
*
* 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.
*
* $FreeBSD$
*/
#include <dev/pcm/sound.h>
#define MIN_CHUNK_SIZE 256 /* for uiomove etc. */
#define DMA_ALIGN_THRESHOLD 4
#define DMA_ALIGN_MASK (~(DMA_ALIGN_THRESHOLD - 1))
#define ISA_DMA(b) (((b)->chan >= 0 && (b)->chan != 4 && (b)->chan < 8))
#define CANCHANGE(c) (!(c)->buffer.dl)
static void chn_stintr(pcm_channel *c);
/*
* 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 length (rl,fl):
0 rp,rl fp,fl bufsize
|__________>____________>________|
FREE d READY w FREE
READY: data written from the process and ready to be sent to the DAC;
FREE: free part of the buffer.
Both regions can wrap around the end of the buffer. At initialization,
READY is empty, FREE takes all the available space, and dma is
idle. dl contains the length of the current DMA transfer, dl=0
means that the dma is idle.
The two boundaries (rp,fp) in the buffers are advanced by DMA [d]
and write() [w] operations. The first portion of the READY region
is used for DMA transfers. The transfer is started at rp and with
chunks of length dl. During DMA operations, dsp_wr_dmaupdate()
updates rp, rl and fl tracking the ISA DMA engine as the transfer
makes progress.
When a new block is written, fp advances and rl,fl are updated
accordingly.
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. When a
DMA transfer is complete, an interrupt causes dsp_wrintr() to be
called which 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 dsp_wr_dmaupdate() which changes rp, rl and fl.
The sistem tries to make all DMA transfers use the same size,
play_blocksize or rec_blocksize. The size is either selected by
the user, or computed by the system to correspond to about .25s of
audio. The blocksize must be within a range which is currently:
min(5ms, 40 bytes) ... 1/2 buffer size.
When there aren't enough data (write) or space (read), a transfer
is started with a reduced size.
To reduce problems in case of overruns, the routine which fills up
the buffer should initialize (e.g. by repeating the last value) a
reasonably long area after the last block so that no noise is
produced on overruns.
*
*/
/* XXX this is broken: in the event a bounce buffer is used, data never
* gets copied in or out of the real buffer. fix requires mods to isa_dma.c
* and possibly fixes to other autodma mode clients
*/
static void
chn_isadmabounce(pcm_channel *c)
{
if (ISA_DMA(&c->buffer)) {
/* tell isa_dma to bounce data in/out */
} else panic("chn_isadmabounce called on invalid channel");
}
static int
chn_polltrigger(pcm_channel *c)
{
snd_dbuf *b = &c->buffer;
unsigned lim = (c->flags & CHN_F_HAS_SIZE)? c->blocksize : 1;
int trig = 0;
if (c->flags & CHN_F_MAPPED)
trig = ((b->int_count > b->prev_int_count) || b->first_poll);
else trig = (((c->direction == PCMDIR_PLAY)? b->fl : b->rl) >= lim);
return trig;
}
static int
chn_pollreset(pcm_channel *c)
{
snd_dbuf *b = &c->buffer;
if (c->flags & CHN_F_MAPPED) b->prev_int_count = b->int_count;
b->first_poll = 0;
return 1;
}
/*
* chn_dmadone() updates pointers and wakes up any process sleeping
* or waiting on a select().
* Must be called at spltty().
*/
static void
chn_dmadone(pcm_channel *c)
{
snd_dbuf *b = &c->buffer;
chn_dmaupdate(c);
if (ISA_DMA(b)) chn_isadmabounce(c); /* sync bounce buffer */
wakeup(b);
b->int_count++;
if (b->sel.si_pid && chn_polltrigger(c)) selwakeup(&b->sel);
}
/*
* chn_dmaupdate() tracks the status of a dma transfer,
* updating pointers. It must be called at spltty().
*
* NOTE: when we are using auto dma in the device, rl might become
* negative.
*/
void
chn_dmaupdate(pcm_channel *c)
{
snd_dbuf *b = &c->buffer;
int delta, hwptr = chn_getptr(c);
if (c->direction == PCMDIR_PLAY) {
delta = (b->bufsize + hwptr - b->rp) % b->bufsize;
b->rp = hwptr;
b->rl -= delta;
b->fl += delta;
} else {
delta = (b->bufsize + hwptr - b->fp) % b->bufsize;
b->fp = hwptr;
b->rl += delta;
b->fl -= delta;
}
b->total += delta;
}
/*
* Write interrupt routine. Can be called from other places (e.g.
* to start a paused transfer), but with interrupts disabled.
*/
static void
chn_wrintr(pcm_channel *c)
{
snd_dbuf *b = &c->buffer;
int start;
if (b->dl) chn_dmadone(c);
/*
* start another dma operation only if have ready data in the buffer,
* there is no pending abort, have a full-duplex device, or have a
* half duplex device and there is no pending op on the other side.
*
* Force transfers to be aligned to a boundary of 4, which is
* needed when doing stereo and 16-bit.
*/
if (c->flags & CHN_F_MAPPED) start = c->flags & CHN_F_TRIGGERED;
else start = (b->rl >= DMA_ALIGN_THRESHOLD && !(c->flags & CHN_F_ABORTING));
if (start) {
int l;
chn_dmaupdate(c);
l = min(b->rl, c->blocksize) & DMA_ALIGN_MASK;
if (c->flags & CHN_F_MAPPED) l = c->blocksize;
/*
* check if we need to reprogram the DMA on the sound card.
* This happens if the size has changed _and_ the new size
* is smaller, or it matches the blocksize.
*
* 0 <= l <= blocksize
* 0 <= dl <= blocksize
* reprog if (dl == 0 || l != dl)
* was:
* l != b->dl && (b->dl == 0 || l < b->dl || l == c->blocksize)
*/
if (b->dl == 0 || l != b->dl) {
/* size has changed. Stop and restart */
DEB(printf("wrintr: bsz %d -> %d, rp %d rl %d\n",
b->dl, l, b->rp, b->rl));
if (b->dl) chn_trigger(c, PCMTRIG_STOP);
b->dl = l; /* record new transfer size */
chn_trigger(c, PCMTRIG_START);
}
} else {
/* cannot start a new dma transfer */
DEB(printf("cannot start wr-dma flags 0x%08x rp %d rl %d\n",
c->flags, b->rp, b->rl));
if (b->dl) { /* was active */
b->dl = 0;
chn_trigger(c, PCMTRIG_STOP);
#if 0
if (c->flags & CHN_F_WRITING)
DEB(printf("got wrint while reloading\n"));
else if (b->rl <= 0) /* XXX added 980110 lr */
chn_resetbuf(c);
#endif
}
}
}
/*
* 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
chn_write(pcm_channel *c, struct uio *buf)
{
int a, l, w, timeout, ret = 0;
long s;
snd_dbuf *b = &c->buffer;
if (c->flags & CHN_F_WRITING) {
/* This shouldn't happen and is actually silly
* - will never wake up, just timeout; why not sleep on b?
*/
tsleep(&s, PZERO, "pcmwrW", hz);
return EBUSY;
}
a = (1 << c->align) - 1;
c->flags |= CHN_F_WRITING;
while ((c->smegcnt + buf->uio_resid) > a) {
s = spltty();
chn_dmaupdate(c);
splx(s);
if (b->fl < DMA_ALIGN_THRESHOLD) {
if (c->flags & CHN_F_NBIO) break;
timeout = (buf->uio_resid >= b->dl)? hz : 1;
ret = tsleep(b, PRIBIO | PCATCH, "pcmwr", timeout);
if (ret == EINTR) chn_abort(c);
if (ret == EINTR || ret == ERESTART) break;
ret = 0;
continue;
}
/* ensure we always have a whole number of samples */
l = min(b->fl, b->bufsize - b->fp) & ~a;
if (l == 0) break;
w = c->feeder->feed(c->feeder, c, b->buf + b->fp, l, buf);
if (w == 0) panic("no feed");
s = spltty();
b->rl += w;
b->fl -= w;
b->fp = (b->fp + w) % b->bufsize;
splx(s);
if (b->rl && !b->dl) chn_stintr(c);
}
if ((ret == 0) && (buf->uio_resid > 0)) {
l = buf->uio_resid;
if ((c->smegcnt + l) >= SMEGBUFSZ) panic("resid overflow %d", l);
uiomove(c->smegbuf + c->smegcnt, l, buf);
c->smegcnt += l;
}
c->flags &= ~CHN_F_WRITING;
return ret;
}
/*
* SOUND INPUT
*
The input part is similar to the output one, with a circular buffer
split in two regions, and boundaries advancing because of read() calls
[r] or dma operation [d]. At initialization, as for the write
routine, READY is empty, and FREE takes all the space.
0 rp,rl fp,fl bufsize
|__________>____________>________|
FREE r READY d FREE
Operation is as follows: upon user read (dsp_read_body()) a DMA read
is started if not already active (marked by b->dl > 0),
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, with a preferred constant transfer size equal to
rec_blocksize, and fallback to smaller sizes if no space is available.
*/
/* read interrupt routine. Must be called with interrupts blocked. */
static void
chn_rdintr(pcm_channel *c)
{
snd_dbuf *b = &c->buffer;
int start;
if (b->dl) chn_dmadone(c);
DEB(printf("rdintr: start dl %d, rp:rl %d:%d, fp:fl %d:%d\n",
b->dl, b->rp, b->rl, b->fp, b->fl));
/* Restart if have enough free space to absorb overruns */
if (c->flags & CHN_F_MAPPED) start = c->flags & CHN_F_TRIGGERED;
else start = (b->fl > 0x200 && !(c->flags & CHN_F_ABORTING));
if (start) {
int l = min(b->fl - 0x100, c->blocksize);
if (c->flags & CHN_F_MAPPED) l = c->blocksize;
l &= DMA_ALIGN_MASK ; /* realign sizes */
DEB(printf("rdintr: dl %d -> %d\n", b->dl, l);)
if (l != b->dl) {
/* size has changed. Stop and restart */
if (b->dl) {
chn_trigger(c, PCMTRIG_STOP);
chn_dmaupdate(c);
l = min(b->fl - 0x100, c->blocksize);
l &= DMA_ALIGN_MASK ; /* realign sizes */
}
b->dl = l;
chn_trigger(c, PCMTRIG_START);
}
} else {
if (b->dl) { /* was active */
b->dl = 0;
chn_dmaupdate(c);
chn_trigger(c, PCMTRIG_STOP);
}
}
}
/*
* 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.
*
* 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
chn_read(pcm_channel *c, struct uio *buf)
{
int w, l, timeout, limit, ret = 0;
long s;
snd_dbuf *b = &c->buffer;
if (c->flags & CHN_F_READING) {
/* This shouldn't happen and is actually silly */
tsleep(&s, PZERO, "pcmrdR", hz);
return (EBUSY);
}
if (!b->rl & !b->dl) chn_stintr(c);
c->flags |= CHN_F_READING;
limit = buf->uio_resid - c->blocksize;
if (limit < 0) limit = 0;
while (buf->uio_resid > limit) {
s = spltty();
chn_dmaupdate(c);
splx(s);
if (b->rl < DMA_ALIGN_THRESHOLD) {
if (c->flags & CHN_F_NBIO) break;
timeout = (buf->uio_resid - limit >= b->dl)? hz : 1;
ret = tsleep(b, PRIBIO | PCATCH, "pcmrd", timeout);
if (ret == EINTR) chn_abort(c);
if (ret == EINTR || ret == ERESTART) break;
ret = 0;
continue;
}
/* ensure we always have a whole number of samples */
l = min(b->rl, b->bufsize - b->rp) & DMA_ALIGN_MASK;
w = c->feeder->feed(c->feeder, c, b->buf + b->rp, l, buf);
s = spltty();
b->rl -= w;
b->fl += w;
b->rp = (b->rp + w) % b->bufsize;
splx(s);
}
c->flags &= ~CHN_F_READING;
return ret;
}
void
chn_intr(pcm_channel *c)
{
/* if (!c->buffer.dl) chn_reinit(c);
*/ if (c->direction == PCMDIR_PLAY) chn_wrintr(c); else chn_rdintr(c);
}
static void
chn_stintr(pcm_channel *c)
{
u_long s;
s = spltty();
chn_intr(c);
splx(s);
}
static void
chn_dma_setmap(void *arg, bus_dma_segment_t *segs, int nseg, int error)
{
snd_dbuf *b = (snd_dbuf *)arg;
if (bootverbose) {
printf("pcm: setmap %lx, %lx; ", (unsigned long)segs->ds_addr,
(unsigned long)segs->ds_len);
printf("%p -> %lx\n", b->buf, (unsigned long)vtophys(b->buf));
}
}
int
chn_allocbuf(snd_dbuf *b, bus_dma_tag_t parent_dmat)
{
if (bus_dmamem_alloc(parent_dmat, (void **)&b->buf,
BUS_DMA_NOWAIT, &b->dmamap)) return -1;
if (bus_dmamap_load(parent_dmat, b->dmamap, b->buf,
b->bufsize, chn_dma_setmap, b, 0)) return -1;
return 0;
}
void
chn_resetbuf(pcm_channel *c)
{
snd_dbuf *b = &c->buffer;
u_int16_t data, *p;
u_int32_t i;
c->smegcnt = 0;
c->buffer.sample_size = 1;
c->buffer.sample_size <<= (c->hwfmt & AFMT_STEREO)? 1 : 0;
c->buffer.sample_size <<= (c->hwfmt & AFMT_16BIT)? 1 : 0;
/* rely on bufsize & 3 == 0 */
if (c->hwfmt & AFMT_SIGNED) data = 0x00; else data = 0x80;
if (c->hwfmt & AFMT_16BIT) data <<= 8; else data |= data << 8;
if (c->hwfmt & AFMT_BIGENDIAN)
data = ((data >> 8) & 0x00ff) | ((data << 8) & 0xff00);
for (i = 0, p = (u_int16_t *)b->buf; i < b->bufsize; i += 2)
*p++ = data;
b->rp = b->fp = 0;
b->dl = b->rl = 0;
b->prev_total = b->total = 0;
b->prev_int_count = b->int_count = 0;
b->first_poll = 1;
b->fl = b->bufsize;
}
void
buf_isadma(snd_dbuf *b, int go)
{
if (ISA_DMA(b)) {
if (go == PCMTRIG_START) isa_dmastart(b->dir | B_RAW, b->buf,
b->bufsize, b->chan);
else {
isa_dmastop(b->chan);
isa_dmadone(b->dir | B_RAW, b->buf, b->bufsize,
b->chan);
}
} else panic("buf_isadma called on invalid channel");
}
int
buf_isadmaptr(snd_dbuf *b)
{
if (ISA_DMA(b)) {
int i = b->dl? isa_dmastatus(b->chan) : b->bufsize;
if (i < 0) i = 0;
return b->bufsize - i;
} else panic("buf_isadmaptr called on invalid channel");
return -1;
}
/*
* snd_sync waits until the space in the given channel goes above
* a threshold. The threshold is checked against fl or rl respectively.
* Assume that the condition can become true, do not check here...
*/
int
chn_sync(pcm_channel *c, int threshold)
{
u_long s, rdy;
int ret;
snd_dbuf *b = &c->buffer;
for (;;) {
s = spltty();
chn_dmaupdate(c);
rdy = (c->direction == PCMDIR_PLAY)? b->fl : b->rl;
if (rdy <= threshold) {
ret = tsleep((caddr_t)b, PRIBIO | PCATCH, "pcmsyn", 1);
splx(s);
if (ret == ERESTART || ret == EINTR) {
printf("tsleep returns %d\n", ret);
return -1;
}
} else break;
}
splx(s);
return 0;
}
int
chn_poll(pcm_channel *c, int ev, struct proc *p)
{
snd_dbuf *b = &c->buffer;
u_long s = spltty();
if (b->dl) chn_dmaupdate(c);
splx(s);
if (chn_polltrigger(c) && chn_pollreset(c)) return ev;
else {
selrecord(p, &b->sel);
return 0;
}
}
/*
* chn_abort is a non-blocking function which aborts a pending
* DMA transfer and flushes the buffers.
* It returns the number of bytes that have not been transferred.
*/
int
chn_abort(pcm_channel *c)
{
long s;
int missing = 0;
snd_dbuf *b = &c->buffer;
s = spltty();
if (b->dl) {
b->dl = 0;
c->flags &= ~((c->direction == PCMDIR_PLAY)? CHN_F_WRITING : CHN_F_READING);
chn_trigger(c, PCMTRIG_ABORT);
chn_dmadone(c);
}
missing = b->rl;
splx(s);
return missing;
}
/*
* this routine tries to flush the dma transfer. It is called
* on a close. We immediately abort any read DMA
* operation, and then wait for the play buffer to drain.
*/
int
chn_flush(pcm_channel *c)
{
int ret, count = 10;
snd_dbuf *b = &c->buffer;
DEB(printf("snd_flush c->flags 0x%08x\n", c->flags));
c->flags |= CHN_F_CLOSING;
if (c->direction != PCMDIR_PLAY) chn_abort(c);
else while (b->dl) {
/* still pending output data. */
ret = tsleep((caddr_t)b, PRIBIO | PCATCH, "pcmflu", hz);
chn_dmaupdate(c);
DEB(printf("snd_sync: now rl : fl %d : %d\n", b->rl, b->fl));
if (ret == EINTR) {
printf("tsleep returns %d\n", ret);
return -1;
}
if (ret && --count == 0) {
printf("timeout flushing dbuf_out, cnt 0x%x flags 0x%x\n",
b->rl, c->flags);
break;
}
}
c->flags &= ~CHN_F_CLOSING;
if (c->direction == PCMDIR_PLAY) chn_abort(c);
return 0;
}
int
chn_reset(pcm_channel *c)
{
chn_abort(c);
c->flags &= CHN_F_RESET;
chn_resetbuf(c);
c->flags |= CHN_F_INIT;
return 0;
}
int
chn_reinit(pcm_channel *c)
{
if ((c->flags & CHN_F_INIT) && CANCHANGE(c)) {
chn_setformat(c, c->format);
chn_setspeed(c, c->speed);
chn_setblocksize(c, c->blocksize);
chn_setvolume(c, (c->volume >> 8) & 0xff, c->volume & 0xff);
c->flags &= ~CHN_F_INIT;
return 1;
}
return 0;
}
int
chn_init(pcm_channel *c, void *devinfo, int dir)
{
c->flags = 0;
c->feeder = &feeder_root;
c->buffer.chan = -1;
c->devinfo = c->init(devinfo, &c->buffer, c, dir);
chn_setdir(c, dir);
return 0;
}
int
chn_setdir(pcm_channel *c, int dir)
{
c->direction = dir;
if (ISA_DMA(&c->buffer))
c->buffer.dir = (dir == PCMDIR_PLAY)? B_WRITE : B_READ;
return c->setdir(c->devinfo, c->direction);
}
int
chn_setvolume(pcm_channel *c, int left, int right)
{
/* could add a feeder for volume changing if channel returns -1 */
if (CANCHANGE(c)) {
return -1;
}
c->volume = (left << 8) | right;
c->flags |= CHN_F_INIT;
return 0;
}
int
chn_setspeed(pcm_channel *c, int speed)
{
/* could add a feeder for rate conversion */
if (CANCHANGE(c)) {
c->speed = c->setspeed(c->devinfo, speed);
return c->speed;
}
c->speed = speed;
c->flags |= CHN_F_INIT;
return 0;
}
int
chn_setformat(pcm_channel *c, u_int32_t fmt)
{
if (CANCHANGE(c)) {
c->hwfmt = c->format = fmt;
c->hwfmt = chn_feedchain(c);
chn_resetbuf(c);
c->setformat(c->devinfo, c->hwfmt);
return fmt;
}
c->format = fmt;
c->flags |= CHN_F_INIT;
return 0;
}
int
chn_setblocksize(pcm_channel *c, int blksz)
{
if (CANCHANGE(c)) {
c->flags &= ~CHN_F_HAS_SIZE;
if (blksz >= 2) c->flags |= CHN_F_HAS_SIZE;
blksz = abs(blksz);
if (blksz < 2) blksz = (c->buffer.sample_size * c->speed) >> 2;
RANGE(blksz, 1024, c->buffer.bufsize / 4);
blksz &= ~3;
c->blocksize = c->setblocksize(c->devinfo, blksz);
return c->blocksize;
}
c->blocksize = blksz;
c->flags |= CHN_F_INIT;
return 0;
}
int
chn_trigger(pcm_channel *c, int go)
{
return c->trigger(c->devinfo, go);
}
int
chn_getptr(pcm_channel *c)
{
return c->getptr(c->devinfo);
}
pcmchan_caps *
chn_getcaps(pcm_channel *c)
{
return c->getcaps(c->devinfo);
}

View file

@ -1,81 +0,0 @@
/*
* Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
* All rights reserved.
*
* 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.
*
* $FreeBSD$
*/
int chn_reinit(pcm_channel *c);
int chn_write(pcm_channel *c, struct uio *buf);
int chn_read(pcm_channel *c, struct uio *buf);
int chn_sync(pcm_channel *c, int threshold);
int chn_flush(pcm_channel *c);
int chn_poll(pcm_channel *c, int ev, struct proc *p);
int chn_init(pcm_channel *c, void *devinfo, int dir);
int chn_setdir(pcm_channel *c, int dir);
int chn_reset(pcm_channel *c);
int chn_setvolume(pcm_channel *c, int left, int right);
int chn_setspeed(pcm_channel *c, int speed);
int chn_setformat(pcm_channel *c, u_int32_t fmt);
int chn_setblocksize(pcm_channel *c, int blksz);
int chn_trigger(pcm_channel *c, int go);
int chn_getptr(pcm_channel *c);
pcmchan_caps *chn_getcaps(pcm_channel *c);
int chn_allocbuf(snd_dbuf *b, bus_dma_tag_t parent_dmat);
void chn_resetbuf(pcm_channel *c);
void chn_intr(pcm_channel *c);
void chn_dmaupdate(pcm_channel *c);
int chn_abort(pcm_channel *c);
void buf_isadma(snd_dbuf *b, int go);
int buf_isadmaptr(snd_dbuf *b);
int chn_feedchain(pcm_channel *c);
extern pcm_feeder feeder_root;
#define PCMDIR_PLAY 1
#define PCMDIR_REC -1
#define PCMTRIG_START 1
#define PCMTRIG_STOP 0
#define PCMTRIG_ABORT -1
#define CHN_F_READING 0x00000001 /* have a pending read */
#define CHN_F_WRITING 0x00000002 /* have a pending write */
#define CHN_F_CLOSING 0x00000004 /* a pending close */
#define CHN_F_ABORTING 0x00000008 /* a pending abort */
#define CHN_F_PENDING_IO (CHN_F_READING | CHN_F_WRITING)
#define CHN_F_RUNNING 0x00000010 /* dma is running */
#define CHN_F_TRIGGERED 0x00000020
#define CHN_F_BUSY 0x00001000 /* has been opened */
#define CHN_F_HAS_SIZE 0x00002000 /* user set block size */
#define CHN_F_NBIO 0x00004000 /* do non-blocking i/o */
#define CHN_F_INIT 0x00008000 /* changed parameters. need init */
#define CHN_F_MAPPED 0x00010000 /* has been mmap()ed */
#define CHN_F_RESET (CHN_F_BUSY)

View file

@ -1,162 +0,0 @@
/*
* Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
* All rights reserved.
*
* 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.
*
* $FreeBSD$
*/
typedef struct _snd_mixer snd_mixer;
typedef struct _snd_dbuf snd_dbuf;
typedef struct _snddev_info snddev_info;
typedef struct _pcmchan_caps pcmchan_caps;
typedef struct _pcm_feeder pcm_feeder;
typedef struct _pcm_channel pcm_channel;
typedef int (mix_set_t)(snd_mixer *m, unsigned dev, unsigned left, unsigned right);
typedef int (mix_recsrc_t)(snd_mixer *m, u_int32_t src);
typedef int (mix_init_t)(snd_mixer *m);
struct _snd_mixer {
char name[64];
mix_init_t *init;
mix_set_t *set;
mix_recsrc_t *setrecsrc;
void *devinfo;
u_int32_t devs;
u_int32_t recdevs;
u_int32_t recsrc;
u_int16_t level[32];
};
/*
* descriptor of a dma buffer. See dmabuf.c for documentation.
* (rp,rl) and (fp,fl) identify the READY and FREE regions of the
* buffer. dl contains the length used for dma transfer, dl>0 also
* means that the channel is busy and there is a DMA transfer in progress.
*/
struct _snd_dbuf {
char *buf;
int bufsize;
volatile int rp, fp; /* pointers to the ready and free area */
volatile int dl; /* transfer size */
volatile int rl, fl; /* lenght of ready and free areas. */
volatile u_int32_t int_count, prev_int_count;
int chan, dir; /* dma channel */
int sample_size; /* 1, 2, 4 */
struct selinfo sel;
u_long total; /* total bytes processed */
u_long prev_total; /* copy of the above when GETxPTR called */
int first_poll;
bus_dmamap_t dmamap;
};
typedef int (pcmfeed_init_t)(pcm_feeder *feeder);
typedef int (pcmfeed_free_t)(pcm_feeder *feeder);
typedef int (pcmfeed_feed_t)(pcm_feeder *feeder, pcm_channel *c, u_int8_t *buffer,
u_int32_t count, struct uio *stream);
struct _pcm_feeder {
char name[16];
int align;
pcmfeed_init_t *init;
pcmfeed_free_t *free;
pcmfeed_feed_t *feed;
void *data;
pcm_feeder *source;
};
struct _pcmchan_caps {
u_int32_t minspeed, maxspeed;
u_int32_t formats, bestfmt;
};
typedef void *(pcmchan_init_t)(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir);
typedef int (pcmchan_setdir_t)(void *data, int dir);
typedef int (pcmchan_setformat_t)(void *data, u_int32_t format);
typedef int (pcmchan_setspeed_t)(void *data, u_int32_t speed);
typedef int (pcmchan_setblocksize_t)(void *data, u_int32_t blocksize);
typedef int (pcmchan_trigger_t)(void *data, int go);
typedef int (pcmchan_getptr_t)(void *data);
typedef pcmchan_caps *(pcmchan_getcaps_t)(void *data);
struct _pcm_channel {
pcmchan_init_t *init;
pcmchan_setdir_t *setdir;
pcmchan_setformat_t *setformat;
pcmchan_setspeed_t *setspeed;
pcmchan_setblocksize_t *setblocksize;
pcmchan_trigger_t *trigger;
pcmchan_getptr_t *getptr;
pcmchan_getcaps_t *getcaps;
pcm_feeder *feeder;
u_int32_t align;
int volume;
u_int32_t speed;
u_int32_t flags;
u_int32_t format, hwfmt;
u_int32_t blocksize;
int direction;
snd_dbuf buffer;
#define SMEGBUFSZ 4
u_int8_t smegbuf[SMEGBUFSZ];
u_int32_t smegcnt;
void *devinfo;
};
#define SND_STATUSLEN 64
/* descriptor of audio device */
struct _snddev_info {
pcm_channel *play, *rec, **aplay, **arec, fakechan;
unsigned playcount, reccount, chancount;
snd_mixer mixer;
u_long magic;
unsigned flags;
void *devinfo;
char status[SND_STATUSLEN];
};
/* mixer description structure and macros - these should go away,
* only sb.[ch] and mss.[ch] use them
*/
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 MIX_NONE(name) MIX_ENT(name, 0,0,0,0, 0,0,0,0)

View file

@ -1,559 +0,0 @@
/*
* Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
* All rights reserved.
*
* 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.
*
* $FreeBSD$
*/
#include <sys/param.h>
#include <sys/queue.h>
#include <sys/kernel.h>
#include <dev/pcm/sound.h>
static int getchns(snddev_info *d, int chan, pcm_channel **rdch, pcm_channel **wrch);
static pcm_channel *
allocchn(snddev_info *d, int direction)
{
pcm_channel *chns = (direction == PCMDIR_PLAY)? d->play : d->rec;
int i, cnt = (direction == PCMDIR_PLAY)? d->playcount : d->reccount;
for (i = 0; i < cnt; i++) {
if (!(chns[i].flags & CHN_F_BUSY)) {
chns[i].flags |= CHN_F_BUSY;
return &chns[i];
}
}
return NULL;
}
static int
getchns(snddev_info *d, int chan, pcm_channel **rdch, pcm_channel **wrch)
{
if ((d->flags & SD_F_PRIO_SET) == SD_F_PRIO_SET)
panic("read and write both prioritised");
if (d->flags & SD_F_SIMPLEX) {
*rdch = (d->flags & SD_F_PRIO_RD)? d->arec[chan] : &d->fakechan;
*wrch = (d->flags & SD_F_PRIO_WR)? d->aplay[chan] : &d->fakechan;
} else {
*rdch = d->arec[chan];
*wrch = d->aplay[chan];
}
return 0;
}
static void
setchns(snddev_info *d, int chan)
{
if ((d->flags & SD_F_PRIO_SET) == SD_F_PRIO_SET)
panic("read and write both prioritised");
d->flags |= SD_F_DIR_SET;
if (d->flags & SD_F_EVILSB16) {
if ((d->flags & SD_F_PRIO_RD) && (d->aplay[chan])) {
pcm_channel *tmp;
tmp = d->arec[chan];
d->arec[chan] = d->aplay[chan];
d->aplay[chan] = tmp;
}
if (d->aplay[chan]) chn_setdir(d->aplay[chan], PCMDIR_PLAY);
if (d->arec[chan]) chn_setdir(d->arec[chan], PCMDIR_REC);
}
}
int
dsp_open(snddev_info *d, int chan, int oflags, int devtype)
{
pcm_channel *rdch = NULL, *wrch = NULL;
u_int32_t fmt;
if (chan >= d->chancount) return ENODEV;
if (d->aplay[chan] || d->arec[chan]) return EBUSY;
if (oflags & FREAD) {
rdch = allocchn(d, PCMDIR_REC);
if (!rdch) return EBUSY;
}
if (oflags & FWRITE) {
wrch = allocchn(d, PCMDIR_PLAY);
if (!wrch) {
if (rdch) rdch->flags &= ~CHN_F_BUSY;
return EBUSY;
}
}
d->aplay[chan] = wrch;
d->arec[chan] = rdch;
switch (devtype) {
case SND_DEV_DSP16:
fmt = AFMT_S16_LE;
break;
case SND_DEV_DSP:
fmt = AFMT_U8;
break;
case SND_DEV_AUDIO:
fmt = AFMT_MU_LAW;
break;
case SND_DEV_NORESET:
fmt = 0;
break;
default:
return ENXIO;
}
if (rdch) {
chn_reset(rdch);
if (oflags & O_NONBLOCK) rdch->flags |= CHN_F_NBIO;
if (fmt) {
rdch->volume = (100 << 8) | 100;
rdch->format = fmt;
rdch->speed = DSP_DEFAULT_SPEED;
rdch->blocksize = 2048;
}
}
if (wrch) {
chn_reset(wrch);
if (oflags & O_NONBLOCK) wrch->flags |= CHN_F_NBIO;
if (fmt) {
wrch->volume = (100 << 8) | 100;
wrch->format = fmt;
wrch->speed = DSP_DEFAULT_SPEED;
wrch->blocksize = 2048;
}
}
return 0;
}
int
dsp_close(snddev_info *d, int chan, int devtype)
{
pcm_channel *rdch, *wrch;
d->flags &= ~SD_F_TRANSIENT;
rdch = d->arec[chan];
wrch = d->aplay[chan];
if (rdch) {
chn_abort(rdch);
rdch->flags &= ~(CHN_F_BUSY | CHN_F_RUNNING | CHN_F_MAPPED);
}
if (wrch) wrch->flags &= ~(CHN_F_BUSY | CHN_F_RUNNING | CHN_F_MAPPED);
d->aplay[chan] = NULL;
d->arec[chan] = NULL;
return 0;
}
int
dsp_read(snddev_info *d, int chan, struct uio *buf, int flag)
{
pcm_channel *rdch, *wrch;
if (!(d->flags & SD_F_PRIO_SET)) d->flags |= SD_F_PRIO_RD;
if (!(d->flags & SD_F_DIR_SET)) setchns(d, chan);
getchns(d, chan, &rdch, &wrch);
if (!rdch || !(rdch->flags & CHN_F_BUSY))
panic("dsp_read: non%s channel", rdch? "busy" : "existant");
if (rdch->flags & CHN_F_MAPPED) return EINVAL;
if (!(rdch->flags & CHN_F_RUNNING)) {
rdch->flags |= CHN_F_RUNNING;
chn_reinit(rdch);
}
return chn_read(rdch, buf);
}
int
dsp_write(snddev_info *d, int chan, struct uio *buf, int flag)
{
pcm_channel *rdch, *wrch;
if (!(d->flags & SD_F_PRIO_SET)) d->flags |= SD_F_PRIO_WR;
if (!(d->flags & SD_F_DIR_SET)) setchns(d, chan);
getchns(d, chan, &rdch, &wrch);
if (!wrch || !(wrch->flags & CHN_F_BUSY))
panic("dsp_write: non%s channel", wrch? "busy" : "existant");
if (wrch->flags & CHN_F_MAPPED) return EINVAL;
if (!(wrch->flags & CHN_F_RUNNING)) {
wrch->flags |= CHN_F_RUNNING;
chn_reinit(wrch);
}
return chn_write(wrch, buf);
}
int
dsp_ioctl(snddev_info *d, int chan, u_long cmd, caddr_t arg)
{
int ret = 0, *arg_i = (int *)arg;
u_long s;
pcm_channel *wrch = NULL, *rdch = NULL;
rdch = d->arec[chan];
wrch = d->aplay[chan];
/*
* all routines are called with int. blocked. Make sure that
* ints are re-enabled when calling slow or blocking functions!
*/
s = spltty();
switch(cmd) {
/*
* we start with the new ioctl interface.
*/
case AIONWRITE: /* how many bytes can write ? */
if (wrch && wrch->buffer.dl) chn_dmaupdate(wrch);
*arg_i = wrch? wrch->buffer.fl : 0;
break;
case AIOSSIZE: /* set the current blocksize */
{
struct snd_size *p = (struct snd_size *)arg;
splx(s);
if (wrch) chn_setblocksize(wrch, p->play_size);
if (rdch) chn_setblocksize(rdch, p->rec_size);
}
/* FALLTHROUGH */
case AIOGSIZE: /* get the current blocksize */
{
struct snd_size *p = (struct snd_size *)arg;
if (wrch) p->play_size = wrch->blocksize;
if (rdch) p->rec_size = rdch->blocksize;
}
break;
case AIOSFMT:
{
snd_chan_param *p = (snd_chan_param *)arg;
splx(s);
if (wrch) {
chn_setformat(wrch, p->play_format);
chn_setspeed(wrch, p->play_rate);
}
if (rdch) {
chn_setformat(rdch, p->rec_format);
chn_setspeed(rdch, p->rec_rate);
}
}
/* FALLTHROUGH */
case AIOGFMT:
{
snd_chan_param *p = (snd_chan_param *)arg;
p->play_rate = wrch? wrch->speed : 0;
p->rec_rate = rdch? rdch->speed : 0;
p->play_format = wrch? wrch->format : 0;
p->rec_format = rdch? rdch->format : 0;
}
break;
case AIOGCAP: /* get capabilities */
{
snd_capabilities *p = (snd_capabilities *)arg;
pcmchan_caps *pcaps = NULL, *rcaps = NULL;
if (rdch) rcaps = chn_getcaps(rdch);
if (wrch) pcaps = chn_getcaps(wrch);
p->rate_min = max(rcaps? rcaps->minspeed : 0,
pcaps? pcaps->minspeed : 0);
p->rate_max = min(rcaps? rcaps->maxspeed : 1000000,
pcaps? pcaps->maxspeed : 1000000);
p->bufsize = min(rdch? rdch->buffer.bufsize : 1000000,
wrch? wrch->buffer.bufsize : 1000000);
/* XXX bad on sb16 */
p->formats = (rcaps? rcaps->formats : 0xffffffff) &
(pcaps? pcaps->formats : 0xffffffff);
p->mixers = 1; /* default: one mixer */
p->inputs = d->mixer.devs;
p->left = p->right = 100;
}
break;
case AIOSTOP:
if (*arg_i == AIOSYNC_PLAY && wrch) *arg_i = chn_abort(wrch);
else if (*arg_i == AIOSYNC_CAPTURE && rdch) *arg_i = chn_abort(rdch);
else {
splx(s);
printf("AIOSTOP: bad channel 0x%x\n", *arg_i);
*arg_i = 0;
}
break;
case AIOSYNC:
printf("AIOSYNC chan 0x%03lx pos %lu unimplemented\n",
((snd_sync_parm *)arg)->chan, ((snd_sync_parm *)arg)->pos);
break;
/*
* here follow the standard ioctls (filio.h etc.)
*/
case FIONREAD: /* get # bytes to read */
if (rdch && rdch->buffer.dl) chn_dmaupdate(rdch);
*arg_i = rdch? rdch->buffer.rl : 0;
break;
case FIOASYNC: /*set/clear async i/o */
DEB( printf("FIOASYNC\n") ; )
break;
case SNDCTL_DSP_NONBLOCK:
case FIONBIO: /* set/clear non-blocking i/o */
if (rdch) rdch->flags &= ~CHN_F_NBIO;
if (wrch) wrch->flags &= ~CHN_F_NBIO;
if (*arg_i) {
if (rdch) rdch->flags |= CHN_F_NBIO;
if (wrch) wrch->flags |= CHN_F_NBIO;
}
break;
/*
* Finally, here is the linux-compatible ioctl interface
*/
#define THE_REAL_SNDCTL_DSP_GETBLKSIZE _IOWR('P', 4, int)
case THE_REAL_SNDCTL_DSP_GETBLKSIZE:
case SNDCTL_DSP_GETBLKSIZE:
*arg_i = wrch? wrch->blocksize : 0; /* XXX rdch? */
break ;
case SNDCTL_DSP_SETBLKSIZE:
splx(s);
if (wrch) chn_setblocksize(wrch, *arg_i);
if (rdch) chn_setblocksize(rdch, *arg_i);
break;
case SNDCTL_DSP_RESET:
DEB(printf("dsp reset\n"));
if (wrch) chn_abort(wrch);
if (rdch) chn_abort(rdch);
break;
case SNDCTL_DSP_SYNC:
DEB(printf("dsp sync\n"));
splx(s);
if (wrch) chn_sync(wrch, wrch->buffer.bufsize - 4);
break;
case SNDCTL_DSP_SPEED:
splx(s);
if (wrch) chn_setspeed(wrch, *arg_i);
if (rdch) chn_setspeed(rdch, *arg_i);
/* fallthru */
case SOUND_PCM_READ_RATE:
*arg_i = wrch? wrch->speed : rdch->speed;
break;
case SNDCTL_DSP_STEREO:
splx(s);
if (wrch) chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) |
((*arg_i)? AFMT_STEREO : 0));
if (rdch) chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) |
((*arg_i)? AFMT_STEREO : 0));
*arg_i = ((wrch? wrch->format : rdch->format) & AFMT_STEREO)? 1 : 0;
break;
case SOUND_PCM_WRITE_CHANNELS:
splx(s);
if (wrch) chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) |
((*arg_i == 2)? AFMT_STEREO : 0));
if (rdch) chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) |
((*arg_i == 2)? AFMT_STEREO : 0));
/* fallthru */
case SOUND_PCM_READ_CHANNELS:
*arg_i = ((wrch? wrch->format : rdch->format) & AFMT_STEREO)? 2 : 1;
break;
case SNDCTL_DSP_GETFMTS: /* returns a mask of supported fmts */
*arg_i = wrch? chn_getcaps(wrch)->formats : chn_getcaps(rdch)->formats;
break ;
case SNDCTL_DSP_SETFMT: /* sets _one_ format */
splx(s);
if (wrch) chn_setformat(wrch, (*arg_i) | (wrch->format & AFMT_STEREO));
if (rdch) chn_setformat(rdch, (*arg_i) | (rdch->format & AFMT_STEREO));
*arg_i = (wrch? wrch->format: rdch->format) & ~AFMT_STEREO;
break;
case SNDCTL_DSP_SUBDIVIDE:
/* XXX watch out, this is RW! */
DEB(printf("SNDCTL_DSP_SUBDIVIDE unimplemented\n");)
break;
case SNDCTL_DSP_SETFRAGMENT:
/* XXX watch out, this is RW! */
DEB(printf("SNDCTL_DSP_SETFRAGMENT 0x%08x\n", *(int *)arg));
{
int bytes = 1 << min(*arg_i & 0xffff, 16);
int count = (*arg_i >> 16) & 0xffff;
pcm_channel *c = wrch? wrch : rdch;
splx(s);
if (rdch) chn_setblocksize(rdch, bytes);
if (wrch) chn_setblocksize(wrch, bytes);
/* eg: 4dwave can only interrupt at buffer midpoint, so
* it will force blocksize == bufsize/2
*/
count = c->buffer.bufsize / c->blocksize;
bytes = ffs(c->blocksize) - 1;
*arg_i = (count << 16) | bytes;
}
break;
case SNDCTL_DSP_GETISPACE:
/* return space available in the input queue */
{
audio_buf_info *a = (audio_buf_info *)arg;
if (rdch) {
snd_dbuf *b = &rdch->buffer;
if (b->dl) chn_dmaupdate(rdch);
a->bytes = b->fl;
a->fragments = 1;
a->fragstotal = b->bufsize / rdch->blocksize;
a->fragsize = rdch->blocksize;
}
}
break;
case SNDCTL_DSP_GETOSPACE:
/* return space available in the output queue */
{
audio_buf_info *a = (audio_buf_info *)arg;
if (wrch) {
snd_dbuf *b = &wrch->buffer;
if (b->dl) chn_dmaupdate(wrch);
a->bytes = b->fl;
a->fragments = 1;
a->fragstotal = b->bufsize / wrch->blocksize;
a->fragsize = wrch->blocksize;
}
}
break;
case SNDCTL_DSP_GETIPTR:
{
count_info *a = (count_info *)arg;
if (rdch) {
snd_dbuf *b = &rdch->buffer;
if (b->dl) chn_dmaupdate(rdch);
a->bytes = b->total;
a->blocks = (b->total - b->prev_total) / rdch->blocksize;
a->ptr = b->fp;
b->prev_total += a->blocks * rdch->blocksize;
} else ret = EINVAL;
}
break;
case SNDCTL_DSP_GETOPTR:
{
count_info *a = (count_info *)arg;
if (wrch) {
snd_dbuf *b = &wrch->buffer;
if (b->dl) chn_dmaupdate(wrch);
a->bytes = b->total;
a->blocks = (b->total - b->prev_total) / wrch->blocksize;
a->ptr = b->rp;
b->prev_total += a->blocks * wrch->blocksize;
} else ret = EINVAL;
}
break;
case SNDCTL_DSP_GETCAPS:
*arg_i = DSP_CAP_REALTIME | DSP_CAP_MMAP | DSP_CAP_TRIGGER;
if (rdch && wrch && !(d->flags & SD_F_SIMPLEX))
*arg_i |= DSP_CAP_DUPLEX;
break;
case SOUND_PCM_READ_BITS:
*arg_i = ((wrch? wrch->format : rdch->format) & AFMT_16BIT)? 16 : 8;
break;
case SNDCTL_DSP_SETTRIGGER:
if (rdch) {
rdch->flags &= ~CHN_F_TRIGGERED;
if (*arg_i & PCM_ENABLE_INPUT)
rdch->flags |= CHN_F_TRIGGERED;
chn_intr(rdch);
}
if (wrch) {
wrch->flags &= ~CHN_F_TRIGGERED;
if (*arg_i & PCM_ENABLE_OUTPUT)
wrch->flags |= CHN_F_TRIGGERED;
chn_intr(wrch);
}
break;
case SNDCTL_DSP_GETTRIGGER:
*arg_i = 0;
if (wrch && wrch->flags & CHN_F_TRIGGERED)
*arg_i |= PCM_ENABLE_OUTPUT;
if (rdch && rdch->flags & CHN_F_TRIGGERED)
*arg_i |= PCM_ENABLE_INPUT;
break;
case SNDCTL_DSP_MAPINBUF:
case SNDCTL_DSP_MAPOUTBUF:
case SNDCTL_DSP_SETSYNCRO:
/* undocumented */
case SNDCTL_DSP_POST:
case SOUND_PCM_WRITE_FILTER:
case SOUND_PCM_READ_FILTER:
/* dunno what these do, don't sound important */
default:
DEB(printf("default ioctl snd%d fn 0x%08x fail\n", unit, cmd));
ret = EINVAL;
break;
}
splx(s);
return ret;
}
int
dsp_poll(snddev_info *d, int chan, int events, struct proc *p)
{
int ret = 0, e;
pcm_channel *wrch = NULL, *rdch = NULL;
getchns(d, chan, &rdch, &wrch);
e = events & (POLLOUT | POLLWRNORM);
if (wrch && e) ret |= chn_poll(wrch, e, p);
e = events & (POLLIN | POLLRDNORM);
if (rdch && e) ret |= chn_poll(rdch, e, p);
return ret;
}
int
dsp_mmap(snddev_info *d, int chan, vm_offset_t offset, int nprot)
{
pcm_channel *wrch = NULL, *rdch = NULL, *c = NULL;
getchns(d, chan, &rdch, &wrch);
/* XXX this is broken by line 204 of vm/device_pager.c, so force write buffer */
if (1 || (wrch && (nprot & PROT_WRITE))) c = wrch;
else if (rdch && (nprot & PROT_READ)) c = rdch;
if (c) {
c->flags |= CHN_F_MAPPED;
return atop(vtophys(c->buffer.buf + offset));
}
return -1;
}

View file

@ -1,37 +0,0 @@
/*
* Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
* All rights reserved.
*
* 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.
*
* $FreeBSD$
*/
int dsp_open(snddev_info *d, int chan, int oflags, int devtype);
int dsp_close(snddev_info *d, int chan, int devtype);
int dsp_read(snddev_info *d, int chan, struct uio *buf, int flag);
int dsp_write(snddev_info *d, int chan, struct uio *buf, int flag);
int dsp_ioctl(snddev_info *d, int chan, u_long cmd, caddr_t arg);
int dsp_poll(snddev_info *d, int chan, int events, struct proc *p);
int dsp_mmap(snddev_info *d, int chan, vm_offset_t offset, int nprot);

View file

@ -1,115 +0,0 @@
/*
* Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
* All rights reserved.
*
* 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.
*
* $FreeBSD$
*/
#include "pcm.h"
#include <dev/pcm/sound.h>
/* channel interface */
static void *fkchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir);
static int fkchan_setdir(void *data, int dir);
static int fkchan_setformat(void *data, u_int32_t format);
static int fkchan_setspeed(void *data, u_int32_t speed);
static int fkchan_setblocksize(void *data, u_int32_t blocksize);
static int fkchan_trigger(void *data, int go);
static int fkchan_getptr(void *data);
static pcmchan_caps *fkchan_getcaps(void *data);
static pcmchan_caps fk_caps = {
4000, 48000,
AFMT_STEREO | AFMT_U8 | AFMT_S8 | AFMT_S16_LE | AFMT_S16_BE | AFMT_U16_LE | AFMT_U16_BE
};
static pcm_channel fk_chantemplate = {
fkchan_init,
fkchan_setdir,
fkchan_setformat,
fkchan_setspeed,
fkchan_setblocksize,
fkchan_trigger,
fkchan_getptr,
fkchan_getcaps,
};
/* channel interface */
static void *
fkchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir)
{
b->bufsize = 16384;
b->buf = malloc(b->bufsize, M_DEVBUF, M_NOWAIT);
return (void *)0xbabef00d;
}
static int
fkchan_setdir(void *data, int dir)
{
return 0;
}
static int
fkchan_setformat(void *data, u_int32_t format)
{
return 0;
}
static int
fkchan_setspeed(void *data, u_int32_t speed)
{
return speed;
}
static int
fkchan_setblocksize(void *data, u_int32_t blocksize)
{
return blocksize;
}
static int
fkchan_trigger(void *data, int go)
{
return 0;
}
static int
fkchan_getptr(void *data)
{
return 0;
}
static pcmchan_caps *
fkchan_getcaps(void *data)
{
return &fk_caps;
}
int
fkchan_setup(pcm_channel *c)
{
*c = fk_chantemplate;
return 0;
}

View file

@ -1,416 +0,0 @@
/*
* Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
* All rights reserved.
*
* 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.
*
* $FreeBSD$
*/
#include <dev/pcm/sound.h>
static int chn_addfeeder(pcm_channel *c, pcm_feeder *f);
static int chn_removefeeder(pcm_channel *c);
#define FEEDBUFSZ 8192
static unsigned char ulaw_to_u8[] = {
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,
};
static unsigned char u8_to_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,
};
/*****************************************************************************/
static int
feed_root(pcm_feeder *feeder, pcm_channel *ch, u_int8_t *buffer, u_int32_t count, struct uio *stream)
{
int ret, tmp = 0, c = 0;
if (!count) panic("feed_root: count == 0");
count &= ~((1 << ch->align) - 1);
if (!count) panic("feed_root: aligned count == 0");
if (ch->smegcnt > 0) {
c = min(ch->smegcnt, count);
bcopy(ch->smegbuf, buffer, c);
ch->smegcnt -= c;
}
while ((stream->uio_resid > 0) && (c < count)) {
tmp = stream->uio_resid;
ret = uiomove(buffer + c, count - c, stream);
if (ret) panic("feed_root: uiomove failed");
tmp -= stream->uio_resid;
c += tmp;
}
if (!c) panic("feed_root: uiomove didn't");
return c;
}
pcm_feeder feeder_root = { "root", 0, NULL, NULL, feed_root };
/*****************************************************************************/
static int
feed_8to16(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream)
{
int i, j, k;
k = f->source->feed(f->source, c, b, count / 2, stream);
j = k - 1;
i = j * 2 + 1;
while (i > 0 && j >= 0) {
b[i--] = b[j--];
b[i--] = 0;
}
return k * 2;
}
static pcm_feeder feeder_8to16 = { "8to16", 0, NULL, NULL, feed_8to16 };
/*****************************************************************************/
static int
feed_16to8_init(pcm_feeder *f)
{
f->data = malloc(FEEDBUFSZ, M_DEVBUF, M_NOWAIT);
return (f->data == NULL);
}
static int
feed_16to8_free(pcm_feeder *f)
{
if (f->data) free(f->data, M_DEVBUF);
f->data = NULL;
return 0;
}
static int
feed_16to8le(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream)
{
u_int32_t i = 0, toget = count * 2;
int j = 1, k;
k = f->source->feed(f->source, c, f->data, min(toget, FEEDBUFSZ), stream);
while (j < k) {
b[i++] = ((u_int8_t *)f->data)[j];
j += 2;
}
return i;
}
static pcm_feeder feeder_16to8le =
{ "16to8le", 1, feed_16to8_init, feed_16to8_free, feed_16to8le };
/*****************************************************************************/
static int
feed_monotostereo8(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream)
{
int i, j, k = f->source->feed(f->source, c, b, count / 2, stream);
j = k - 1;
i = j * 2 + 1;
while (i > 0 && j >= 0) {
b[i--] = b[j];
b[i--] = b[j];
j--;
}
return k * 2;
}
static pcm_feeder feeder_monotostereo8 =
{ "monotostereo8", 0, NULL, NULL, feed_monotostereo8 };
/*****************************************************************************/
static int
feed_stereotomono8_init(pcm_feeder *f)
{
f->data = malloc(FEEDBUFSZ, M_DEVBUF, M_NOWAIT);
return (f->data == NULL);
}
static int
feed_stereotomono8_free(pcm_feeder *f)
{
if (f->data) free(f->data, M_DEVBUF);
f->data = NULL;
return 0;
}
static int
feed_stereotomono8(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream)
{
u_int32_t i = 0, toget = count * 2;
int j = 0, k;
k = f->source->feed(f->source, c, f->data, min(toget, FEEDBUFSZ), stream);
while (j < k) {
b[i++] = ((u_int8_t *)f->data)[j];
j += 2;
}
return i;
}
static pcm_feeder feeder_stereotomono8 =
{ "stereotomono8", 1, feed_stereotomono8_init, feed_stereotomono8_free,
feed_stereotomono8 };
/*****************************************************************************/
static int
feed_endian(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream)
{
u_int8_t t;
int i = 0, j = f->source->feed(f->source, c, b, count, stream);
while (i < j) {
t = b[i];
b[i] = b[i + 1];
b[i + 1] = t;
i += 2;
}
return i;
}
static pcm_feeder feeder_endian = { "endian", -1, NULL, NULL, feed_endian };
/*****************************************************************************/
static int
feed_sign(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream)
{
int i = 0, j = f->source->feed(f->source, c, b, count, stream);
int ssz = (int)f->data, ofs = ssz - 1;
while (i < j) {
b[i + ofs] ^= 0x80;
i += ssz;
}
return i;
}
static pcm_feeder feeder_sign8 =
{ "sign8", 0, NULL, NULL, feed_sign, (void *)1 };
static pcm_feeder feeder_sign16 =
{ "sign16", -1, NULL, NULL, feed_sign, (void *)2 };
/*****************************************************************************/
static int
feed_table(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream)
{
int i = 0, j = f->source->feed(f->source, c, b, count, stream);
while (i < j) {
b[i] = ((u_int8_t *)f->data)[b[i]];
i++;
}
return i;
}
static pcm_feeder feeder_ulawtou8 =
{ "ulawtou8", 0, NULL, NULL, feed_table, ulaw_to_u8 };
static pcm_feeder feeder_u8toulaw =
{ "u8toulaw", 0, NULL, NULL, feed_table, u8_to_ulaw };
/*****************************************************************************/
struct fmtspec {
int stereo;
int sign;
int bit16;
int bigendian;
int ulaw;
int bad;
};
struct fmtcvt {
pcm_feeder *f;
struct fmtspec ispec, ospec;
};
struct fmtcvt cvttab[] = {
{&feeder_ulawtou8, {-1, 0, 0, 0, 1}, {-1, 0, 0, 0, 0}},
{&feeder_u8toulaw, {-1, 0, 0, 0, 0}, {-1, 0, 0, 0, 1}},
{&feeder_sign8, {-1, 0, 0, 0, 0}, {-1, 1, 0, 0, 0}},
{&feeder_sign8, {-1, 1, 0, 0, 0}, {-1, 0, 0, 0, 0}},
{&feeder_monotostereo8, { 0, -1, 0, 0, -1}, { 1, -1, 0, 0, -1}},
{&feeder_stereotomono8, { 1, -1, 0, 0, -1}, { 0, -1, 0, 0, -1}},
{&feeder_sign16, {-1, 0, 1, 0, 0}, {-1, 1, 1, 0, 0}},
{&feeder_sign16, {-1, 1, 1, 0, 0}, {-1, 0, 1, 0, 0}},
{&feeder_8to16, {-1, -1, 0, 0, 0}, {-1, -1, 1, 0, 0}},
{&feeder_16to8le, {-1, -1, 1, 0, 0}, {-1, -1, 0, 0, 0}},
{&feeder_endian, {-1, -1, 1, 0, 0}, {-1, -1, 1, 1, 0}},
{&feeder_endian, {-1, -1, 1, 1, 0}, {-1, -1, 1, 0, 0}},
};
#define FEEDERTABSZ (sizeof(cvttab) / sizeof(struct fmtcvt))
static int
getspec(u_int32_t fmt, struct fmtspec *spec)
{
spec->stereo = (fmt & AFMT_STEREO)? 1 : 0;
spec->sign = (fmt & AFMT_SIGNED)? 1 : 0;
spec->bit16 = (fmt & AFMT_16BIT)? 1 : 0;
spec->bigendian = (fmt & AFMT_BIGENDIAN)? 1 : 0;
spec->ulaw = (fmt & AFMT_MU_LAW)? 1 : 0;
spec->bad = (fmt & (AFMT_A_LAW | AFMT_MPEG))? 1 : 0;
return 0;
}
static int
cmp(int x, int y)
{
return (x == -1 || x == y || y == -1)? 1 : 0;
}
static int
cmpspec(struct fmtspec *x, struct fmtspec *y)
{
int i = 0;
if (cmp(x->stereo, y->stereo)) i |= 0x01;
if (cmp(x->sign, y->sign)) i |= 0x02;
if (cmp(x->bit16, y->bit16)) i |= 0x04;
if (cmp(x->bigendian, y->bigendian)) i |= 0x08;
if (cmp(x->ulaw, y->ulaw)) i |= 0x10;
return i;
}
static int
cvtapply(pcm_channel *c, struct fmtcvt *cvt, struct fmtspec *s)
{
int i = cmpspec(s, &cvt->ospec);
chn_addfeeder(c, cvt->f);
if (cvt->ospec.stereo != -1) s->stereo = cvt->ospec.stereo;
if (cvt->ospec.sign != -1) s->sign = cvt->ospec.sign;
if (cvt->ospec.bit16 != -1) s->bit16 = cvt->ospec.bit16;
if (cvt->ospec.bigendian != -1) s->bigendian = cvt->ospec.bigendian;
if (cvt->ospec.ulaw != -1) s->ulaw = cvt->ospec.ulaw;
return i;
}
int
chn_feedchain(pcm_channel *c)
{
int i, chosen, iter;
u_int32_t mask;
struct fmtspec s, t;
struct fmtcvt *e;
while (chn_removefeeder(c) != -1);
c->align = 0;
if ((c->format & chn_getcaps(c)->formats) == c->format)
return c->format;
getspec(c->format, &s);
if (s.bad) return -1;
getspec(chn_getcaps(c)->bestfmt, &t);
mask = (~cmpspec(&s, &t)) & 0x1f;
iter = 0;
do {
if (mask == 0 || iter >= 8) break;
chosen = -1;
for (i = 0; i < FEEDERTABSZ && chosen == -1; i++) {
e = &cvttab[i];
if ((cmpspec(&s, &e->ispec) == 0x1f) &&
((~cmpspec(&e->ispec, &e->ospec)) & mask))
chosen = i;
}
if (chosen != -1) mask &= cvtapply(c, &cvttab[chosen], &s);
iter++;
} while (chosen != -1);
return (iter < 8)? chn_getcaps(c)->bestfmt : -1;
}
static int
chn_addfeeder(pcm_channel *c, pcm_feeder *f)
{
pcm_feeder *n;
n = malloc(sizeof(pcm_feeder), M_DEVBUF, M_NOWAIT);
*n = *f;
n->source = c->feeder;
c->feeder = n;
if (n->init) n->init(n);
if (n->align > 0) c->align += n->align;
else if (n->align < 0 && c->align < -n->align) c->align -= n->align;
return 0;
}
static int
chn_removefeeder(pcm_channel *c)
{
pcm_feeder *f;
if (c->feeder == &feeder_root) return -1;
f = c->feeder->source;
if (c->feeder->free) c->feeder->free(c->feeder);
free(c->feeder, M_DEVBUF);
c->feeder = f;
return 0;
}

View file

@ -1,622 +0,0 @@
/*
* Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
* Copyright Luigi Rizzo, 1997,1998
* Copyright by Hannu Savolainen 1994, 1995
* All rights reserved.
*
* 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.
*
* $FreeBSD$
*/
#include <dev/pcm/sound.h>
#if NPCM > 0 && NPNP > 0
#include <dev/pcm/isa/ad1816.h>
struct ad1816_info;
struct ad1816_chinfo {
struct ad1816_info *parent;
pcm_channel *channel;
snd_dbuf *buffer;
int dir;
};
struct ad1816_info {
struct resource *io_base; /* primary I/O address for the board */
int io_rid;
struct resource *irq;
int irq_rid;
struct resource *drq1; /* play */
int drq1_rid;
struct resource *drq2; /* rec */
int drq2_rid;
bus_dma_tag_t parent_dmat;
struct ad1816_chinfo pch, rch;
};
static driver_intr_t ad1816_intr;
static int ad1816_probe(device_t dev);
static int ad1816_attach(device_t dev);
/* IO primitives */
static int ad1816_wait_init(struct ad1816_info *ad1816, int x);
static u_short ad1816_read(struct ad1816_info *ad1816, u_int reg);
static void ad1816_write(struct ad1816_info *ad1816, u_int reg, u_short data);
static int ad1816mix_init(snd_mixer *m);
static int ad1816mix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right);
static int ad1816mix_setrecsrc(snd_mixer *m, u_int32_t src);
static snd_mixer ad1816_mixer = {
"ad1816 mixer",
ad1816mix_init,
ad1816mix_set,
ad1816mix_setrecsrc,
};
static devclass_t pcm_devclass;
/* channel interface */
static void *ad1816chan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir);
static int ad1816chan_setdir(void *data, int dir);
static int ad1816chan_setformat(void *data, u_int32_t format);
static int ad1816chan_setspeed(void *data, u_int32_t speed);
static int ad1816chan_setblocksize(void *data, u_int32_t blocksize);
static int ad1816chan_trigger(void *data, int go);
static int ad1816chan_getptr(void *data);
static pcmchan_caps *ad1816chan_getcaps(void *data);
static pcmchan_caps ad1816_caps = {
4000, 55200,
AFMT_STEREO | AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW,
AFMT_STEREO | AFMT_S16_LE
};
static pcm_channel ad1816_chantemplate = {
ad1816chan_init,
ad1816chan_setdir,
ad1816chan_setformat,
ad1816chan_setspeed,
ad1816chan_setblocksize,
ad1816chan_trigger,
ad1816chan_getptr,
ad1816chan_getcaps,
};
#define FULL_DUPLEX(x) (pcm_getflags(x) & SD_F_SIMPLEX)
#define AD1816_MUTE 31 /* value for mute */
static int
port_rd(struct resource *port, int off)
{
if (port)
return bus_space_read_1(rman_get_bustag(port),
rman_get_bushandle(port),
off);
else
return -1;
}
static void
port_wr(struct resource *port, int off, u_int8_t data)
{
if (port)
return bus_space_write_1(rman_get_bustag(port),
rman_get_bushandle(port),
off, data);
}
static int
io_rd(struct ad1816_info *ad1816, int reg)
{
return port_rd(ad1816->io_base, reg);
}
static void
io_wr(struct ad1816_info *ad1816, int reg, u_int8_t data)
{
return port_wr(ad1816->io_base, reg, data);
}
static void
ad1816_intr(void *arg)
{
struct ad1816_info *ad1816 = (struct ad1816_info *)arg;
unsigned char c, served = 0;
/* get interupt status */
c = io_rd(ad1816, AD1816_INT);
/* check for stray interupts */
if (c & ~(AD1816_INTRCI | AD1816_INTRPI)) {
printf("pcm: stray int (%x)\n", c);
c &= AD1816_INTRCI | AD1816_INTRPI;
}
/* check for capture interupt */
if (ad1816->rch.buffer->dl && (c & AD1816_INTRCI)) {
chn_intr(ad1816->rch.channel);
served |= AD1816_INTRCI; /* cp served */
}
/* check for playback interupt */
if (ad1816->pch.buffer->dl && (c & AD1816_INTRPI)) {
chn_intr(ad1816->pch.channel);
served |= AD1816_INTRPI; /* pb served */
}
if (served == 0) {
/* this probably means this is not a (working) ad1816 chip, */
/* or an error in dma handling */
printf("pcm: int without reason (%x)\n", c);
c = 0;
} else c &= ~served;
io_wr(ad1816, AD1816_INT, c);
c = io_rd(ad1816, AD1816_INT);
if (c != 0) printf("pcm: int clear failed (%x)\n", c);
}
static int
ad1816_wait_init(struct ad1816_info *ad1816, int x)
{
int n = 0; /* to shut up the compiler... */
for (; x--;)
if ((n = (io_rd(ad1816, AD1816_ALE) & AD1816_BUSY)) == 0) DELAY(10);
else return n;
printf("ad1816_wait_init failed 0x%02x.\n", n);
return -1;
}
static unsigned short
ad1816_read(struct ad1816_info *ad1816, unsigned int reg)
{
int flags;
u_short x = 0;
/* we don't want to be blocked here */
flags = spltty();
if (ad1816_wait_init(ad1816, 100) == -1) return 0;
io_wr(ad1816, AD1816_ALE, 0);
io_wr(ad1816, AD1816_ALE, (reg & AD1816_ALEMASK));
if (ad1816_wait_init(ad1816, 100) == -1) return 0;
x = (io_rd(ad1816, AD1816_HIGH) << 8) | io_rd(ad1816, AD1816_LOW);
splx(flags);
return x;
}
static void
ad1816_write(struct ad1816_info *ad1816, unsigned int reg, unsigned short data)
{
int flags;
flags = spltty();
if (ad1816_wait_init(ad1816, 100) == -1) return;
io_wr(ad1816, AD1816_ALE, (reg & AD1816_ALEMASK));
io_wr(ad1816, AD1816_LOW, (data & 0x000000ff));
io_wr(ad1816, AD1816_HIGH, (data & 0x0000ff00) >> 8);
splx(flags);
}
static int
ad1816mix_init(snd_mixer *m)
{
mix_setdevs(m, AD1816_MIXER_DEVICES);
mix_setrecdevs(m, AD1816_REC_DEVICES);
return 0;
}
static int
ad1816mix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right)
{
struct ad1816_info *ad1816 = mix_getdevinfo(m);
u_short reg = 0;
/* Scale volumes */
left = AD1816_MUTE - (AD1816_MUTE * left) / 100;
right = AD1816_MUTE - (AD1816_MUTE * right) / 100;
reg = (left << 8) | right;
/* do channel selective muting if volume is zero */
if (left == AD1816_MUTE) reg |= 0x8000;
if (right == AD1816_MUTE) reg |= 0x0080;
switch (dev) {
case SOUND_MIXER_VOLUME: /* Register 14 master volume */
ad1816_write(ad1816, 14, reg);
break;
case SOUND_MIXER_CD: /* Register 15 cd */
case SOUND_MIXER_LINE1:
ad1816_write(ad1816, 15, reg);
break;
case SOUND_MIXER_SYNTH: /* Register 16 synth */
ad1816_write(ad1816, 16, reg);
break;
case SOUND_MIXER_PCM: /* Register 4 pcm */
ad1816_write(ad1816, 4, reg);
break;
case SOUND_MIXER_LINE:
case SOUND_MIXER_LINE3: /* Register 18 line in */
ad1816_write(ad1816, 18, reg);
break;
case SOUND_MIXER_MIC: /* Register 19 mic volume */
ad1816_write(ad1816, 19, reg & ~0xff); /* mic is mono */
break;
case SOUND_MIXER_IGAIN:
/* and now to something completely different ... */
ad1816_write(ad1816, 20, ((ad1816_read(ad1816, 20) & ~0x0f0f)
| (((AD1816_MUTE - left) / 2) << 8) /* four bits of adc gain */
| ((AD1816_MUTE - right) / 2)));
break;
default:
printf("ad1816_mixer_set(): unknown device.\n");
break;
}
return left | (right << 8);
}
static int
ad1816mix_setrecsrc(snd_mixer *m, u_int32_t src)
{
struct ad1816_info *ad1816 = mix_getdevinfo(m);
int dev;
switch (src) {
case SOUND_MASK_LINE:
case SOUND_MASK_LINE3:
dev = 0x00;
break;
case SOUND_MASK_CD:
case SOUND_MASK_LINE1:
dev = 0x20;
break;
case SOUND_MASK_MIC:
default:
dev = 0x50;
src = SOUND_MASK_MIC;
}
dev |= dev << 8;
ad1816_write(ad1816, 20, (ad1816_read(ad1816, 20) & ~0x7070) | dev);
return src;
}
/* channel interface */
static void *
ad1816chan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir)
{
struct ad1816_info *ad1816 = devinfo;
struct ad1816_chinfo *ch = (dir == PCMDIR_PLAY)? &ad1816->pch : &ad1816->rch;
ch->parent = ad1816;
ch->channel = c;
ch->buffer = b;
ch->buffer->bufsize = DSP_BUFFSIZE;
if (chn_allocbuf(ch->buffer, ad1816->parent_dmat) == -1) return NULL;
return ch;
}
static int
ad1816chan_setdir(void *data, int dir)
{
struct ad1816_chinfo *ch = data;
struct ad1816_info *ad1816 = ch->parent;
ch->buffer->chan = rman_get_start((dir == PCMDIR_PLAY)?
ad1816->drq1 : ad1816->drq2);
ch->dir = dir;
return 0;
}
static int
ad1816chan_setformat(void *data, u_int32_t format)
{
struct ad1816_chinfo *ch = data;
struct ad1816_info *ad1816 = ch->parent;
int fmt = AD1816_U8, reg;
if (ch->dir == PCMDIR_PLAY) {
reg = AD1816_PLAY;
ad1816_write(ad1816, 8, 0x0000); /* reset base and current counter */
ad1816_write(ad1816, 9, 0x0000); /* for playback and capture */
} else {
reg = AD1816_CAPT;
ad1816_write(ad1816, 10, 0x0000);
ad1816_write(ad1816, 11, 0x0000);
}
switch (format & ~AFMT_STEREO) {
case AFMT_A_LAW:
fmt = AD1816_ALAW;
break;
case AFMT_MU_LAW:
fmt = AD1816_MULAW;
break;
case AFMT_S16_LE:
fmt = AD1816_S16LE;
break;
case AFMT_S16_BE:
fmt = AD1816_S16BE;
break;
case AFMT_U8:
fmt = AD1816_U8;
break;
}
if (format & AFMT_STEREO) fmt |= AD1816_STEREO;
io_wr(ad1816, reg, fmt);
return format;
}
static int
ad1816chan_setspeed(void *data, u_int32_t speed)
{
struct ad1816_chinfo *ch = data;
struct ad1816_info *ad1816 = ch->parent;
RANGE(speed, 4000, 55200);
ad1816_write(ad1816, (ch->dir == PCMDIR_PLAY)? 2 : 3, speed);
return speed;
}
static int
ad1816chan_setblocksize(void *data, u_int32_t blocksize)
{
return blocksize;
}
static int
ad1816chan_trigger(void *data, int go)
{
struct ad1816_chinfo *ch = data;
struct ad1816_info *ad1816 = ch->parent;
int wr, reg;
buf_isadma(ch->buffer, go);
wr = (ch->dir == PCMDIR_PLAY);
reg = wr? AD1816_PLAY : AD1816_CAPT;
switch (go) {
case PCMTRIG_START:
/* start only if not already running */
if (!(io_rd(ad1816, reg) & AD1816_ENABLE)) {
int cnt = ((ch->buffer->dl) >> 2) - 1;
ad1816_write(ad1816, wr? 8 : 10, cnt); /* count */
ad1816_write(ad1816, 1, ad1816_read(ad1816, 1) |
(wr? 0x8000 : 0x4000)); /* enable int */
/* enable playback */
io_wr(ad1816, reg, io_rd(ad1816, reg) | AD1816_ENABLE);
if (!(io_rd(ad1816, reg) & AD1816_ENABLE))
printf("ad1816: failed to start %s DMA!\n",
wr? "play" : "rec");
}
break;
case PCMTRIG_STOP:
case PCMTRIG_ABORT: /* XXX check this... */
/* we don't test here if it is running... */
if (wr) {
ad1816_write(ad1816, 1, ad1816_read(ad1816, 1) &
~(wr? 0x8000 : 0x4000));
/* disable int */
io_wr(ad1816, reg, io_rd(ad1816, reg) & ~AD1816_ENABLE);
/* disable playback */
if (io_rd(ad1816, reg) & AD1816_ENABLE)
printf("ad1816: failed to stop %s DMA!\n",
wr? "play" : "rec");
ad1816_write(ad1816, wr? 8 : 10, 0); /* reset base cnt */
ad1816_write(ad1816, wr? 9 : 11, 0); /* reset cur cnt */
}
break;
}
return 0;
}
static int
ad1816chan_getptr(void *data)
{
struct ad1816_chinfo *ch = data;
return buf_isadmaptr(ch->buffer);
}
static pcmchan_caps *
ad1816chan_getcaps(void *data)
{
return &ad1816_caps;
}
static void
ad1816_release_resources(struct ad1816_info *ad1816, device_t dev)
{
if (ad1816->irq) {
bus_release_resource(dev, SYS_RES_IRQ, ad1816->irq_rid,
ad1816->irq);
ad1816->irq = 0;
}
if (ad1816->drq1) {
bus_release_resource(dev, SYS_RES_DRQ, ad1816->drq1_rid,
ad1816->drq1);
ad1816->drq1 = 0;
}
if (ad1816->drq2) {
bus_release_resource(dev, SYS_RES_DRQ, ad1816->drq2_rid,
ad1816->drq2);
ad1816->drq2 = 0;
}
if (ad1816->io_base) {
bus_release_resource(dev, SYS_RES_IOPORT, ad1816->io_rid,
ad1816->io_base);
ad1816->io_base = 0;
}
free(ad1816, M_DEVBUF);
}
static int
ad1816_alloc_resources(struct ad1816_info *ad1816, device_t dev)
{
int ok = 1, pdma, rdma;
if (!ad1816->io_base)
ad1816->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, &ad1816->io_rid,
0, ~0, 1, RF_ACTIVE);
if (!ad1816->irq)
ad1816->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &ad1816->irq_rid,
0, ~0, 1, RF_ACTIVE);
if (!ad1816->drq1)
ad1816->drq1 = bus_alloc_resource(dev, SYS_RES_DRQ, &ad1816->drq1_rid,
0, ~0, 1, RF_ACTIVE);
if (ad1816->drq2_rid >= 0 && !ad1816->drq2)
ad1816->drq2 = bus_alloc_resource(dev, SYS_RES_DRQ, &ad1816->drq2_rid,
0, ~0, 1, RF_ACTIVE);
if (!ad1816->io_base || !ad1816->drq1 || !ad1816->irq) ok = 0;
if (ad1816->drq2_rid >= 0 && !ad1816->drq2) ok = 0;
if (ok) {
pdma = rman_get_start(ad1816->drq1);
isa_dma_acquire(pdma);
isa_dmainit(pdma, DSP_BUFFSIZE);
if (ad1816->drq2) {
rdma = rman_get_start(ad1816->drq2);
isa_dma_acquire(rdma);
isa_dmainit(rdma, DSP_BUFFSIZE);
} else rdma = pdma;
if (pdma == rdma)
pcm_setflags(dev, pcm_getflags(dev) | SD_F_SIMPLEX);
}
return ok;
}
static int
ad1816_init(struct ad1816_info *ad1816, device_t dev)
{
ad1816_write(ad1816, 1, 0x2); /* disable interrupts */
ad1816_write(ad1816, 32, 0x90F0); /* SoundSys Mode, split fmt */
ad1816_write(ad1816, 5, 0x8080); /* FM volume mute */
ad1816_write(ad1816, 6, 0x8080); /* I2S1 volume mute */
ad1816_write(ad1816, 7, 0x8080); /* I2S0 volume mute */
ad1816_write(ad1816, 17, 0x8888); /* VID Volume mute */
ad1816_write(ad1816, 20, 0x5050); /* recsrc mic, agc off */
/* adc gain is set to 0 */
return 0;
}
static int
ad1816_probe(device_t dev)
{
char *s = NULL;
u_int32_t logical_id = isa_get_logicalid(dev);
switch (logical_id) {
case 0x80719304: /* ADS7180 */
s = "Terratec Soundsystem BASE 1";
break;
}
if (s) {
device_set_desc(dev, s);
return 0;
}
return ENXIO;
}
static int
ad1816_attach(device_t dev)
{
struct ad1816_info *ad1816;
snddev_info *d = device_get_softc(dev);
void *ih;
char status[SND_STATUSLEN];
ad1816 = (struct ad1816_info *)malloc(sizeof *ad1816, M_DEVBUF, M_NOWAIT);
if (!ad1816) return ENXIO;
bzero(ad1816, sizeof *ad1816);
ad1816->io_rid = 2;
ad1816->irq_rid = 0;
ad1816->drq1_rid = 0;
ad1816->drq2_rid = 1;
if (!ad1816_alloc_resources(ad1816, dev)) goto no;
ad1816_init(ad1816, dev);
mixer_init(d, &ad1816_mixer, ad1816);
bus_setup_intr(dev, ad1816->irq, INTR_TYPE_TTY, ad1816_intr, ad1816, &ih);
if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0,
/*lowaddr*/BUS_SPACE_MAXADDR_24BIT,
/*highaddr*/BUS_SPACE_MAXADDR,
/*filter*/NULL, /*filterarg*/NULL,
/*maxsize*/DSP_BUFFSIZE, /*nsegments*/1,
/*maxsegz*/0x3ffff,
/*flags*/0, &ad1816->parent_dmat) != 0) {
device_printf(dev, "unable to create dma tag\n");
goto no;
}
snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld drq %ld",
rman_get_start(ad1816->io_base),
rman_get_start(ad1816->irq),
rman_get_start(ad1816->drq1));
if (FULL_DUPLEX(dev)) snprintf(status + strlen(status),
SND_STATUSLEN - strlen(status), ":%ld",
rman_get_start(ad1816->drq1));
if (pcm_register(dev, ad1816, 1, 1)) goto no;
pcm_addchan(dev, PCMDIR_REC, &ad1816_chantemplate, ad1816);
pcm_addchan(dev, PCMDIR_PLAY, &ad1816_chantemplate, ad1816);
pcm_setstatus(dev, status);
return 0;
no:
ad1816_release_resources(ad1816, dev);
return ENXIO;
}
static device_method_t ad1816_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, ad1816_probe),
DEVMETHOD(device_attach, ad1816_attach),
{ 0, 0 }
};
static driver_t ad1816_driver = {
"pcm",
ad1816_methods,
sizeof(snddev_info),
};
DRIVER_MODULE(ad1816, isa, ad1816_driver, pcm_devclass, 0, 0);
#endif

View file

@ -1,71 +0,0 @@
/*
* (C) 1997 Luigi Rizzo (luigi@iet.unipi.it)
*
* This file contains information and macro definitions for
* the ad1816 chip
*
* $FreeBSD$
*/
/* AD1816 register macros */
#define AD1816_ALE 0 /* indirect reg access */
#define AD1816_INT 1 /* interupt status */
#define AD1816_LOW 2 /* indirect low byte */
#define AD1816_HIGH 3 /* indirect high byte */
#if 0
#define ad1816_pioD(d) ((d)->io_base+4) /* PIO debug */
#define ad1816_pios(d) ((d)->io_base+5) /* PIO status */
#define ad1816_piod(d) ((d)->io_base+6) /* PIO data */
#endif
/* values for playback/capture config:
bits: 0 enable/disable
1 pio/dma
2 stereo/mono
3 companded/linearPCM
4-5 format : 00 8bit linear (uncomp)
00 8bit mulaw (comp)
01 16bit le (uncomp)
01 8bit alaw (comp)
11 16bit be (uncomp)
*/
#define AD1816_PLAY 8 /* playback config */
#define AD1816_CAPT 9 /* capture config */
#define AD1816_BUSY 0x80 /* chip is busy */
#define AD1816_ALEMASK 0x3F /* mask for indirect adr. */
#if 0
#define AD1816_INTRSI 0x01 /* sb intr */
#define AD1816_INTRGI 0x02 /* game intr */
#define AD1816_INTRRI 0x04 /* ring intr */
#define AD1816_INTRDI 0x08 /* dsp intr */
#define AD1816_INTRVI 0x10 /* vol intr */
#define AD1816_INTRTI 0x20 /* timer intr */
#endif
#define AD1816_INTRCI 0x40 /* capture intr */
#define AD1816_INTRPI 0x80 /* playback intr */
/* PIO stuff is not supplied here */
/* playback / capture config */
#define AD1816_ENABLE 0x01 /* enable pl/cp */
#define AD1816_PIO 0x02 /* use pio */
#define AD1816_STEREO 0x04
#define AD1816_COMP 0x08 /* data is companded */
#define AD1816_U8 0x00 /* 8 bit linear pcm */
#define AD1816_MULAW 0x08 /* 8 bit mulaw */
#define AD1816_ALAW 0x18 /* 8 bit alaw */
#define AD1816_S16LE 0x10 /* 16 bit linear little endian */
#define AD1816_S16BE 0x30 /* 16 bit linear big endian */
#define AD1816_FORMASK 0x38 /* format mask */
#define AD1816_REC_DEVICES \
(SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD)
#define AD1816_MIXER_DEVICES \
(SOUND_MASK_VOLUME | SOUND_MASK_PCM | SOUND_MASK_SYNTH | \
SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_IGAIN)

View file

@ -1,170 +0,0 @@
/*-
* Copyright (c) 1999 Doug Rabson
* All rights reserved.
*
* 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.
*
* $FreeBSD$
*/
#include <dev/pcm/sound.h>
#include <dev/pcm/isa/sb.h>
#if NPCM > 0
static int
es1888_dspready(u_int32_t port)
{
return ((inb(port + SBDSP_STATUS) & 0x80) == 0);
}
static int
es1888_dspwr(u_int32_t port, u_char val)
{
int i;
for (i = 0; i < 1000; i++) {
if (es1888_dspready(port)) {
outb(port + SBDSP_CMD, val);
return 0;
}
if (i > 10) DELAY((i > 100)? 1000 : 10);
}
return ENXIO;
}
static u_int
es1888_get_byte(u_int32_t port)
{
int i;
for (i = 1000; i > 0; i--) {
if (inb(port + DSP_DATA_AVAIL) & 0x80)
return inb(port + DSP_READ);
else
DELAY(20);
}
return 0xffff;
}
static int
es1888_reset(u_int32_t port)
{
outb(port + SBDSP_RST, 3);
DELAY(100);
outb(port + SBDSP_RST, 0);
if (es1888_get_byte(port) != 0xAA) {
return ENXIO; /* Sorry */
}
return 0;
}
static void
es1888_configuration_mode(void)
{
/*
* Emit the Read-Sequence-Key to enter configuration
* mode. Note this only works after a reset (or after bit 2 of
* mixer register 0x40 is set).
*
* 3 reads from 0x229 in a row guarantees reset of key
* sequence to beginning.
*/
inb(0x229);
inb(0x229);
inb(0x229);
inb(0x22b); /* state 1 */
inb(0x229); /* state 2 */
inb(0x22b); /* state 3 */
inb(0x229); /* state 4 */
inb(0x229); /* state 5 */
inb(0x22b); /* state 6 */
inb(0x229); /* state 7 */
}
static void
es1888_set_port(u_int32_t port)
{
es1888_configuration_mode();
inb(port);
}
static void
es1888_identify(driver_t *driver, device_t parent)
{
u_int32_t lo, hi;
device_t dev;
es1888_set_port(0x220);
if (es1888_reset(0x220))
return;
/*
* Check identification bytes for es1888.
*/
if (es1888_dspwr(0x220, 0xe7))
return;
hi = es1888_get_byte(0x220);
lo = es1888_get_byte(0x220);
if (hi != 0x68 || (lo & 0xf0) != 0x80)
return;
/*
* Program irq and drq.
*/
if (es1888_dspwr(0x220, 0xc6) /* enter extended mode */
|| es1888_dspwr(0x220, 0xb1) /* write register b1 */
|| es1888_dspwr(0x220, 0x14) /* enable irq 5 */
|| es1888_dspwr(0x220, 0xb2) /* write register b1 */
|| es1888_dspwr(0x220, 0x18)) /* enable drq 1 */
return;
/*
* Create the device and program its resources.
*/
dev = BUS_ADD_CHILD(parent, ISA_ORDER_PNP, NULL, -1);
bus_set_resource(dev, SYS_RES_IOPORT, 0, 0x220, 0x10);
bus_set_resource(dev, SYS_RES_IRQ, 0, 5, 1);
bus_set_resource(dev, SYS_RES_DRQ, 0, 1, 1);
isa_set_vendorid(dev, PNP_EISAID("ESS1888"));
isa_set_logicalid(dev, PNP_EISAID("ESS1888"));
}
static device_method_t es1888_methods[] = {
/* Device interface */
DEVMETHOD(device_identify, es1888_identify),
{ 0, 0 }
};
static driver_t es1888_driver = {
"pcm",
es1888_methods,
1, /* no softc */
};
static devclass_t pcm_devclass;
DRIVER_MODULE(es1888, isa, es1888_driver, pcm_devclass, 0, 0);
#endif /* NPCM > 0 */

File diff suppressed because it is too large Load diff

View file

@ -1,374 +0,0 @@
/*
* 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 MSS_INDEX (0 + 4)
#define MSS_IDXBUSY 0x80 /* readonly, set when busy */
#define MSS_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 MSS_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 MSS_IDXMASK 0x1f /* mask for indirect address */
#define MSS_IDATA (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 MSS_STATUS (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.
*/
#if 0
#define io_Polled_IO(d) ((d)->io_base+3+4)
/*
* this register is used in case of polled i/o
*/
#endif
/*
* 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
#define BD_F_MSS_OFFSET 0x0008 /* offset mss writes by -4 */
#define BD_F_DUPLEX 0x0010
/*
* 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 MSS_REC_DEVICES \
(SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD|SOUND_MASK_IMIX)
/*
* Table of mixer registers. There is a default table for the
* AD1848/CS423x clones, and one for the OPTI931. As more MSS
* clones come out, there ought to be more tables.
*
* Fields in the table are : polarity, register, offset, bits
*
* 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...
*
* Following there is a macro ...MIXER_DEVICES which is a bitmap
* of all non-zero fields in the table.
* MODE1_MIXER_DEVICES is the basic mixer of the 1848 in mode 1
* registers I0..I15)
*
*/
mixer_ent mix_devices[32][2] = {
MIX_NONE(SOUND_MIXER_VOLUME),
MIX_NONE(SOUND_MIXER_BASS),
MIX_NONE(SOUND_MIXER_TREBLE),
MIX_ENT(SOUND_MIXER_SYNTH, 2, 1, 0, 5, 3, 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, 4, 1, 0, 5, 5, 1, 0, 5),
MIX_ENT(SOUND_MIXER_IMIX, 13, 1, 2, 6, 0, 0, 0, 0),
MIX_NONE(SOUND_MIXER_ALTPCM),
MIX_NONE(SOUND_MIXER_RECLEV),
MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4),
MIX_NONE(SOUND_MIXER_OGAIN),
MIX_NONE(SOUND_MIXER_LINE1),
MIX_NONE(SOUND_MIXER_LINE2),
MIX_NONE(SOUND_MIXER_LINE3),
};
#define MODE2_MIXER_DEVICES \
(SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_SPEAKER | \
SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | \
SOUND_MASK_IMIX | SOUND_MASK_IGAIN )
#define MODE1_MIXER_DEVICES \
(SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_MIC | \
SOUND_MASK_CD | SOUND_MASK_IMIX | SOUND_MASK_IGAIN )
/*
* entries for the opti931...
*/
mixer_ent opti931_devices[32][2] = { /* for the opti931 */
MIX_ENT(SOUND_MIXER_VOLUME, 22, 1, 1, 5, 23, 1, 1, 5),
MIX_NONE(SOUND_MIXER_BASS),
MIX_NONE(SOUND_MIXER_TREBLE),
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_NONE(SOUND_MIXER_SPEAKER),
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_NONE(SOUND_MIXER_IMIX),
MIX_NONE(SOUND_MIXER_ALTPCM),
MIX_NONE(SOUND_MIXER_RECLEV),
MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4),
MIX_NONE(SOUND_MIXER_OGAIN),
MIX_ENT(SOUND_MIXER_LINE1, 16, 1, 1, 4, 17, 1, 1, 4),
MIX_NONE(SOUND_MIXER_LINE2),
MIX_NONE(SOUND_MIXER_LINE3),
};
#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 | SOUND_MASK_LINE1 )
/*-
* Copyright (c) 1999 Doug Rabson
* All rights reserved.
*
* 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.
*
* $FreeBSD$
*/
/*
* Register definitions for the Yamaha OPL3-SA[23x].
*/
#define OPL3SAx_POWER 0x01 /* Power Management (R/W) */
#define OPL3SAx_POWER_PDX 0x01 /* Set to 1 to halt oscillator */
#define OPL3SAx_POWER_PDN 0x02 /* Set to 1 to power down */
#define OPL3SAx_POWER_PSV 0x04 /* Set to 1 to power save */
#define OPL3SAx_POWER_ADOWN 0x20 /* Analog power (?) */
#define OPL3SAx_SYSTEM 0x02 /* System control (R/W) */
#define OPL3SAx_SYSTEM_VZE 0x01 /* I2S audio routing */
#define OPL3SAx_SYSTEM_IDSEL 0x03 /* SB compat version select */
#define OPL3SAx_SYSTEM_SBHE 0x80 /* 0 for AT bus, 1 for XT bus */
#define OPL3SAx_IRQCONF 0x03 /* Interrupt configuration (R/W */
#define OPL3SAx_IRQCONF_WSSA 0x01 /* WSS interrupts through IRQA */
#define OPL3SAx_IRQCONF_SBA 0x02 /* WSS interrupts through IRQA */
#define OPL3SAx_IRQCONF_MPUA 0x04 /* WSS interrupts through IRQA */
#define OPL3SAx_IRQCONF_OPL3A 0x08 /* WSS interrupts through IRQA */
#define OPL3SAx_IRQCONF_WSSB 0x10 /* WSS interrupts through IRQB */
#define OPL3SAx_IRQCONF_SBB 0x20 /* WSS interrupts through IRQB */
#define OPL3SAx_IRQCONF_MPUB 0x40 /* WSS interrupts through IRQB */
#define OPL3SAx_IRQCONF_OPL3B 0x80 /* WSS interrupts through IRQB */
#define OPL3SAx_IRQSTATUSA 0x04 /* Interrupt (IRQ-A) Status (RO) */
#define OPL3SAx_IRQSTATUSB 0x05 /* Interrupt (IRQ-B) Status (RO) */
#define OPL3SAx_IRQSTATUS_PI 0x01 /* Playback Flag of CODEC */
#define OPL3SAx_IRQSTATUS_CI 0x02 /* Recording Flag of CODEC */
#define OPL3SAx_IRQSTATUS_TI 0x04 /* Timer Flag of CODEC */
#define OPL3SAx_IRQSTATUS_SB 0x08 /* SB compat Playback Interrupt Flag */
#define OPL3SAx_IRQSTATUS_MPU 0x10 /* MPU401 Interrupt Flag */
#define OPL3SAx_IRQSTATUS_OPL3 0x20 /* Internal FM Timer Flag */
#define OPL3SAx_IRQSTATUS_MV 0x40 /* HW Volume Interrupt Flag */
#define OPL3SAx_IRQSTATUS_PI 0x01 /* Playback Flag of CODEC */
#define OPL3SAx_IRQSTATUS_CI 0x02 /* Recording Flag of CODEC */
#define OPL3SAx_IRQSTATUS_TI 0x04 /* Timer Flag of CODEC */
#define OPL3SAx_IRQSTATUS_SB 0x08 /* SB compat Playback Interrupt Flag */
#define OPL3SAx_IRQSTATUS_MPU 0x10 /* MPU401 Interrupt Flag */
#define OPL3SAx_IRQSTATUS_OPL3 0x20 /* Internal FM Timer Flag */
#define OPL3SAx_IRQSTATUS_MV 0x40 /* HW Volume Interrupt Flag */
#define OPL3SAx_DMACONF 0x06 /* DMA configuration (R/W) */
#define OPL3SAx_DMACONF_WSSPA 0x01 /* WSS Playback on DMA-A */
#define OPL3SAx_DMACONF_WSSRA 0x02 /* WSS Recording on DMA-A */
#define OPL3SAx_DMACONF_SBA 0x02 /* SB Playback on DMA-A */
#define OPL3SAx_DMACONF_WSSPB 0x10 /* WSS Playback on DMA-A */
#define OPL3SAx_DMACONF_WSSRB 0x20 /* WSS Recording on DMA-A */
#define OPL3SAx_DMACONF_SBB 0x20 /* SB Playback on DMA-A */
#define OPL3SAx_VOLUMEL 0x07 /* Master Volume Left (R/W) */
#define OPL3SAx_VOLUMEL_MVL 0x0f /* Attenuation level */
#define OPL3SAx_VOLUMEL_MVLM 0x80 /* Mute */
#define OPL3SAx_VOLUMER 0x08 /* Master Volume Right (R/W) */
#define OPL3SAx_VOLUMER_MVR 0x0f /* Attenuation level */
#define OPL3SAx_VOLUMER_MVRM 0x80 /* Mute */
#define OPL3SAx_MIC 0x09 /* MIC Volume (R/W) */
#define OPL3SAx_VOLUMER_MCV 0x1f /* Attenuation level */
#define OPL3SAx_VOLUMER_MICM 0x80 /* Mute */
#define OPL3SAx_MISC 0x0a /* Miscellaneous */
#define OPL3SAx_MISC_VER 0x07 /* Version */
#define OPL3SAx_MISC_MODE 0x08 /* SB or WSS mode */
#define OPL3SAx_MISC_MCSW 0x10 /* */
#define OPL3SAx_MISC_VEN 0x80 /* Enable hardware volume control */
#define OPL3SAx_WSSDMA 0x0b /* WSS DMA Counter (RW) (4 regs) */
#define OPL3SAx_WSSIRQSCAN 0x0f /* WSS Interrupt Scan out/in (R/W) */
#define OPL3SAx_WSSIRQSCAN_SPI 0x01
#define OPL3SAx_WSSIRQSCAN_SCI 0x02
#define OPL3SAx_WSSIRQSCAN_STI 0x04
#define OPL3SAx_SBSTATE 0x10 /* SB compat Internal State (R/W) */
#define OPL3SAx_SBSTATE_SBPDR 0x01 /* SB Power Down Request */
#define OPL3SAx_SBSTATE_SE 0x02 /* Scan Enable */
#define OPL3SAx_SBSTATE_SM 0x04 /* Scan Mode */
#define OPL3SAx_SBSTATE_SS 0x08 /* Scan Select */
#define OPL3SAx_SBSTATE_SBPDA 0x80 /* SB Power Down Acknowledge */
#define OPL3SAx_SBDATA 0x11 /* SB compat State Scan Data (R/W) */
#define OPL3SAx_DIGITALPOWER 0x12 /* Digital Partial Power Down (R/W) */
#define OPL3SAx_DIGITALPOWER_PnP 0x01
#define OPL3SAx_DIGITALPOWER_SB 0x02
#define OPL3SAx_DIGITALPOWER_WSSP 0x04
#define OPL3SAx_DIGITALPOWER_WSSR 0x08
#define OPL3SAx_DIGITALPOWER_FM 0x10
#define OPL3SAx_DIGITALPOWER_MCLK0 0x20
#define OPL3SAx_DIGITALPOWER_MPU 0x40
#define OPL3SAx_DIGITALPOWER_JOY 0x80
#define OPL3SAx_ANALOGPOWER 0x13 /* Analog Partial Power Down (R/W) */
#define OPL3SAx_ANALOGPOWER_WIDE 0x01
#define OPL3SAx_ANALOGPOWER_SBDAC 0x02
#define OPL3SAx_ANALOGPOWER_DA 0x04
#define OPL3SAx_ANALOGPOWER_AD 0x08
#define OPL3SAx_ANALOGPOWER_FMDAC 0x10
#define OPL3SAx_WIDE 0x14 /* Enhanced control(WIDE) (R/W) */
#define OPL3SAx_WIDE_WIDEL 0x07 /* Wide level on Left Channel */
#define OPL3SAx_WIDE_WIDER 0x70 /* Wide level on Right Channel */
#define OPL3SAx_BASS 0x15 /* Enhanced control(BASS) (R/W) */
#define OPL3SAx_BASS_BASSL 0x07 /* Bass level on Left Channel */
#define OPL3SAx_BASS_BASSR 0x70 /* Bass level on Right Channel */
#define OPL3SAx_TREBLE 0x16 /* Enhanced control(TREBLE) (R/W) */
#define OPL3SAx_TREBLE_TREBLEL 0x07 /* Treble level on Left Channel */
#define OPL3SAx_TREBLE_TREBLER 0x70 /* Treble level on Right Channel */
#define OPL3SAx_HWVOL 0x17 /* HW Volume IRQ Configuration (R/W) */
#define OPL3SAx_HWVOL_IRQA 0x10 /* HW Volume IRQ on IRQ-A */
#define OPL3SAx_HWVOL_IRQB 0x20 /* HW Volume IRQ on IRQ-B */

File diff suppressed because it is too large Load diff

View file

@ -1,382 +0,0 @@
/*
* file: sbcard.h
* $FreeBSD$
*/
typedef struct _sbdev_info {
} sbdev_info ;
extern int sbc_major, sbc_minor ;
/*
* sound blaster registers
*/
#define SBDSP_RST 0x6
#define DSP_READ 0xA
#define DSP_WRITE 0xC
#define SBDSP_CMD 0xC
#define SBDSP_STATUS 0xC
#define DSP_DATA_AVAIL 0xE
#define DSP_DATA_AVL16 0xF
#define SB_MIX_ADDR 0x4
#define SB_MIX_DATA 0x5
#if 0
#define OPL3_LEFT (io_base + 0x0)
#define OPL3_RIGHT (io_base + 0x2)
#define OPL3_BOTH (io_base + 0x8)
#endif
/*
* DSP Commands. There are many, and in many cases they are used explicitly
*/
/* these are not used except for programmed I/O (not in this driver) */
#define DSP_DAC8 0x10 /* direct DAC output */
#define DSP_ADC8 0x20 /* direct ADC input */
/* these should be used in the SB 1.0 */
#define DSP_CMD_DAC8 0x14 /* single cycle 8-bit dma out */
#define DSP_CMD_ADC8 0x24 /* single cycle 8-bit dma in */
/* these should be used in the SB 2.0 and 2.01 */
#define DSP_CMD_DAC8_AUTO 0x1c /* auto 8-bit dma out */
#define DSP_CMD_ADC8_AUTO 0x2c /* auto 8-bit dma out */
#define DSP_CMD_HSSIZE 0x48 /* high speed dma count */
#define DSP_CMD_HSDAC_AUTO 0x90 /* high speed dac, auto */
#define DSP_CMD_HSADC_AUTO 0x98 /* high speed adc, auto */
/* SBPro commands. Some cards (JAZZ, SMW) also support 16 bits */
/* prepare for dma input */
#define DSP_CMD_DMAMODE(stereo, bit16) (0xA0 | (stereo ? 8:0) | (bit16 ? 4:0))
#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_DAC2S_AUTO 0x1f /* auto 2-bit adpcm dma out (start) */
/* SB16 commands */
#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_DMAPAUSE_8 0xD0
#define DSP_CMD_DMAPAUSE_16 0xD5
#define DSP_CMD_DMAEXIT_8 0xDA
#define DSP_CMD_DMAEXIT_16 0xD9
#define DSP_CMD_TCONST 0x40 /* set time constant */
#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 */
#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 ***/
#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_SB16X 0x0200 /* this is a vibra16X or clone */
#define BD_F_MIDIBUSY 0x0400 /* midi busy */
#define BD_F_ESS 0x0800 /* this is an ESS chip */
/*
* on some SB16 cards, at times I swap DMA channels. Remember this
* so that they can be restored later.
*/
#define BD_F_SWAPPED 0x1000 /* have swapped DMA channels */
#define BD_F_DMARUN 0x2000
#define BD_F_DMARUN2 0x4000
#define BD_F_DUPLEX 0x8000
/*
* 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 ess_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, 4, 4, 0x22, 0, 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, 4, 4, 0x26, 0, 4),
PMIX_ENT(SOUND_MIXER_PCM, 0x04, 4, 4, 0x04, 0, 4),
PMIX_ENT(SOUND_MIXER_SPEAKER, 0x00, 0, 0, 0x00, 0, 0),
PMIX_ENT(SOUND_MIXER_LINE, 0x2e, 4, 4, 0x2e, 0, 4),
PMIX_ENT(SOUND_MIXER_MIC, 0x0a, 0, 3, 0x00, 0, 0),
PMIX_ENT(SOUND_MIXER_CD, 0x28, 4, 4, 0x28, 0, 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)
};
mixer_tab ess_mix = {
PMIX_ENT(SOUND_MIXER_VOLUME, 0x32, 4, 4, 0x32, 0, 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, 0x36, 4, 4, 0x26, 0, 4),
PMIX_ENT(SOUND_MIXER_PCM, 0x14, 4, 4, 0x04, 0, 4),
PMIX_ENT(SOUND_MIXER_SPEAKER, 0x3c, 0, 3, 0x00, 0, 0),
PMIX_ENT(SOUND_MIXER_LINE, 0x3e, 4, 4, 0x2e, 0, 4),
PMIX_ENT(SOUND_MIXER_MIC, 0x1a, 4, 4, 0x1a, 0, 4),
PMIX_ENT(SOUND_MIXER_CD, 0x38, 4, 4, 0x28, 0, 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, 4, 4, 0x22, 0, 4),
PMIX_ENT(SOUND_MIXER_BASS, 0x46, 0, 3, 0x00, 0, 0),
PMIX_ENT(SOUND_MIXER_TREBLE, 0x44, 0, 3, 0x00, 0, 0),
PMIX_ENT(SOUND_MIXER_SYNTH, 0x26, 4, 4, 0x26, 0, 4),
PMIX_ENT(SOUND_MIXER_PCM, 0x04, 4, 4, 0x04, 0, 4),
PMIX_ENT(SOUND_MIXER_SPEAKER, 0x42, 0, 3, 0x00, 0, 0),
PMIX_ENT(SOUND_MIXER_LINE, 0x2e, 4, 4, 0x2e, 0, 4),
PMIX_ENT(SOUND_MIXER_MIC, 0x0a, 0, 3, 0x00, 0, 0),
PMIX_ENT(SOUND_MIXER_CD, 0x28, 4, 4, 0x28, 0, 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)
};
#if 0
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 */
};
#endif
/*
* 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 */

View file

@ -1,203 +0,0 @@
/*
* Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
* All rights reserved.
*
* 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.
*
* $FreeBSD$
*/
#include <dev/pcm/sound.h>
static u_int16_t snd_mixerdefaults[SOUND_MIXER_NRDEVICES] = {
[SOUND_MIXER_VOLUME] = 75,
[SOUND_MIXER_BASS] = 50,
[SOUND_MIXER_TREBLE] = 50,
[SOUND_MIXER_PCM] = 75,
[SOUND_MIXER_SPEAKER] = 75,
[SOUND_MIXER_LINE] = 75,
[SOUND_MIXER_MIC] = 0,
[SOUND_MIXER_CD] = 75,
[SOUND_MIXER_LINE1] = 75,
[SOUND_MIXER_VIDEO] = 75,
[SOUND_MIXER_RECLEV] = 0,
[SOUND_MIXER_OGAIN] = 50,
};
int
mixer_init(snddev_info *d, snd_mixer *m, void *devinfo)
{
if (d == NULL) return -1;
d->mixer = *m;
d->mixer.devinfo = devinfo;
bzero(&d->mixer.level, sizeof d->mixer.level);
if (d->mixer.init != NULL && d->mixer.init(&d->mixer) == 0) {
int i;
for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
u_int16_t v = snd_mixerdefaults[i];
mixer_set(d, i, v | (v << 8));
}
mixer_setrecsrc(d, SOUND_MASK_MIC);
return 0;
} else return -1;
}
int
mixer_set(snddev_info *d, unsigned dev, unsigned lev)
{
if (d == NULL || d->mixer.set == NULL) return -1;
if ((dev < SOUND_MIXER_NRDEVICES) && (d->mixer.devs & (1 << dev))) {
unsigned l = min((lev & 0x00ff), 100);
unsigned r = min(((lev & 0xff00) >> 8), 100);
int v = d->mixer.set(&d->mixer, dev, l, r);
if (v >= 0) d->mixer.level[dev] = v;
return 0;
} else return -1;
}
int
mixer_get(snddev_info *d, int dev)
{
if (d == NULL) return -1;
if (dev < SOUND_MIXER_NRDEVICES && (d->mixer.devs & (1 << dev)))
return d->mixer.level[dev];
else return -1;
}
int
mixer_setrecsrc(snddev_info *d, u_int32_t src)
{
if (d == NULL || d->mixer.setrecsrc == NULL) return -1;
src &= d->mixer.recdevs;
if (src == 0) src = SOUND_MASK_MIC;
d->mixer.recsrc = d->mixer.setrecsrc(&d->mixer, src);
return 0;
}
int
mixer_getrecsrc(snddev_info *d)
{
if (d == NULL) return -1;
return d->mixer.recsrc;
}
int
mixer_ioctl(snddev_info *d, u_long cmd, caddr_t arg)
{
int ret, *arg_i = (int *)arg;
if ((cmd & MIXER_WRITE(0)) == MIXER_WRITE(0)) {
int j = cmd & 0xff;
if (j == SOUND_MIXER_RECSRC) ret = mixer_setrecsrc(d, *arg_i);
else ret = mixer_set(d, j, *arg_i);
return (ret == 0)? 0 : ENXIO;
}
if ((cmd & MIXER_READ(0)) == MIXER_READ(0)) {
int v = -1, j = cmd & 0xff;
switch (j) {
case SOUND_MIXER_DEVMASK:
case SOUND_MIXER_CAPS:
case SOUND_MIXER_STEREODEVS:
v = d->mixer.devs;
break;
case SOUND_MIXER_RECMASK:
v = d->mixer.recdevs;
break;
case SOUND_MIXER_RECSRC:
v = mixer_getrecsrc(d);
break;
default:
v = mixer_get(d, j);
}
*arg_i = v;
return (v != -1)? 0 : ENXIO;
}
return ENXIO;
}
void
mix_setdevs(snd_mixer *m, u_int32_t v)
{
m->devs = v;
}
void
mix_setrecdevs(snd_mixer *m, u_int32_t v)
{
m->recdevs = v;
}
u_int32_t
mix_getdevs(snd_mixer *m)
{
return m->devs;
}
u_int32_t
mix_getrecdevs(snd_mixer *m)
{
return m->recdevs;
}
void *
mix_getdevinfo(snd_mixer *m)
{
return m->devinfo;
}
/*
* The various mixers use a variety of bitmasks etc. The Voxware
* driver had a very nice technique to describe a mixer and interface
* to it. A table defines, for each channel, which register, bits,
* offset, polarity to use. This procedure creates the new value
* using the table and the old value.
*/
void
change_bits(mixer_tab *t, u_char *regval, int dev, int chn, int newval)
{
u_char mask;
int shift;
DEB(printf("ch_bits dev %d ch %d val %d old 0x%02x "
"r %d p %d bit %d off %d\n",
dev, chn, newval, *regval,
(*t)[dev][chn].regno, (*t)[dev][chn].polarity,
(*t)[dev][chn].nbits, (*t)[dev][chn].bitoffs ) );
if ( (*t)[dev][chn].polarity == 1) /* reverse */
newval = 100 - newval ;
mask = (1 << (*t)[dev][chn].nbits) - 1;
newval = (int) ((newval * mask) + 50) / 100; /* Scale it */
shift = (*t)[dev][chn].bitoffs /*- (*t)[dev][LEFT_CHN].nbits + 1*/;
*regval &= ~(mask << shift); /* Filter out the previous value */
*regval |= (newval & mask) << shift; /* Set the new value */
}

View file

@ -1,42 +0,0 @@
/*
* Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
* All rights reserved.
*
* 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.
*
* $FreeBSD$
*/
extern int mixer_init(snddev_info *d, snd_mixer *m, void *devinfo);
extern int mixer_set(snddev_info *d, unsigned dev, unsigned lev);
extern int mixer_get(snddev_info *d, int dev);
extern int mixer_setrecsrc(snddev_info *d, u_int32_t src);
extern int mixer_getrecsrc(snddev_info *d);
extern int mixer_ioctl(snddev_info *d, u_long cmd, caddr_t arg);
extern void change_bits(mixer_tab *t, u_char *regval, int dev, int chn, int newval);
void mix_setdevs(snd_mixer *m, u_int32_t v);
void mix_setrecdevs(snd_mixer *m, u_int32_t v);
u_int32_t mix_getdevs(snd_mixer *m);
u_int32_t mix_getrecdevs(snd_mixer *m);
void *mix_getdevinfo(snd_mixer *m);

View file

@ -1,693 +0,0 @@
/*
* Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
* All rights reserved.
*
* 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.
*
* $FreeBSD$
*/
#include "pci.h"
#include "pcm.h"
#include <dev/pcm/sound.h>
#include <dev/pcm/ac97.h>
#include <dev/pcm/pci/aureal.h>
#include <pci/pcireg.h>
#include <pci/pcivar.h>
#if NPCI != 0
/* PCI IDs of supported chips */
#define AU8820_PCI_ID 0x000112eb
/* channel interface */
static void *auchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir);
static int auchan_setdir(void *data, int dir);
static int auchan_setformat(void *data, u_int32_t format);
static int auchan_setspeed(void *data, u_int32_t speed);
static int auchan_setblocksize(void *data, u_int32_t blocksize);
static int auchan_trigger(void *data, int go);
static int auchan_getptr(void *data);
static pcmchan_caps *auchan_getcaps(void *data);
static pcmchan_caps au_playcaps = {
4000, 48000,
AFMT_STEREO | AFMT_U8 | AFMT_S16_LE,
AFMT_STEREO | AFMT_S16_LE
};
static pcmchan_caps au_reccaps = {
4000, 48000,
AFMT_STEREO | AFMT_U8 | AFMT_S16_LE,
AFMT_STEREO | AFMT_S16_LE
};
static pcm_channel au_chantemplate = {
auchan_init,
auchan_setdir,
auchan_setformat,
auchan_setspeed,
auchan_setblocksize,
auchan_trigger,
auchan_getptr,
auchan_getcaps,
};
/* -------------------------------------------------------------------- */
static u_int32_t au_rdcd(void *arg, int regno);
static void au_wrcd(void *arg, int regno, u_int32_t data);
struct au_info;
struct au_chinfo {
struct au_info *parent;
pcm_channel *channel;
snd_dbuf *buffer;
int dir;
};
struct au_info {
int unit;
bus_space_tag_t st[3];
bus_space_handle_t sh[3];
bus_dma_tag_t parent_dmat;
u_int32_t x[32], y[128];
char z[128];
u_int32_t routes[4], interrupts;
struct au_chinfo pch;
};
static int au_init(device_t dev, struct au_info *au);
static void au_intr(void *);
/* -------------------------------------------------------------------- */
static u_int32_t
au_rd(struct au_info *au, int mapno, int regno, int size)
{
switch(size) {
case 1:
return bus_space_read_1(au->st[mapno], au->sh[mapno], regno);
case 2:
return bus_space_read_2(au->st[mapno], au->sh[mapno], regno);
case 4:
return bus_space_read_4(au->st[mapno], au->sh[mapno], regno);
default:
return 0xffffffff;
}
}
static void
au_wr(struct au_info *au, int mapno, int regno, u_int32_t data, int size)
{
switch(size) {
case 1:
bus_space_write_1(au->st[mapno], au->sh[mapno], regno, data);
break;
case 2:
bus_space_write_2(au->st[mapno], au->sh[mapno], regno, data);
break;
case 4:
bus_space_write_4(au->st[mapno], au->sh[mapno], regno, data);
break;
}
}
static u_int32_t
au_rdcd(void *arg, int regno)
{
struct au_info *au = (struct au_info *)arg;
int i=0, j=0;
regno<<=16;
au_wr(au, 0, AU_REG_CODECIO, regno, 4);
while (j<50) {
i=au_rd(au, 0, AU_REG_CODECIO, 4);
if ((i & 0x00ff0000) == (regno | 0x00800000)) break;
DELAY(j * 200 + 2000);
j++;
}
if (j==50) printf("pcm%d: codec timeout reading register %x (%x)\n",
au->unit, (regno & AU_CDC_REGMASK)>>16, i);
return i & AU_CDC_DATAMASK;
}
static void
au_wrcd(void *arg, int regno, u_int32_t data)
{
struct au_info *au = (struct au_info *)arg;
int i, j, tries;
i=j=tries=0;
do {
while (j<50 && (i & AU_CDC_WROK) == 0) {
i=au_rd(au, 0, AU_REG_CODECST, 4);
DELAY(2000);
j++;
}
if (j==50) printf("codec timeout during write of register %x, data %x\n",
regno, data);
au_wr(au, 0, AU_REG_CODECIO, (regno<<16) | AU_CDC_REGSET | data, 4);
/* DELAY(20000);
i=au_rdcd(au, regno);
*/ tries++;
} while (0); /* (i != data && tries < 3); */
/*
if (tries == 3) printf("giving up writing 0x%4x to codec reg %2x\n", data, regno);
*/
}
static void
au_setbit(u_int32_t *p, char bit, u_int32_t value)
{
p += bit >> 5;
bit &= 0x1f;
*p &= ~ (1 << bit);
*p |= (value << bit);
}
static void
au_addroute(struct au_info *au, int a, int b, int route)
{
int j = 0x1099c+(a<<2);
if (au->x[a] != a+0x67) j = AU_REG_RTBASE+(au->x[a]<<2);
au_wr(au, 0, AU_REG_RTBASE+(route<<2), 0xffffffff, 4);
au_wr(au, 0, j, route | (b<<7), 4);
au->y[route]=au->x[a];
au->x[a]=route;
au->z[route]=a & 0x000000ff;
au_setbit(au->routes, route, 1);
}
static void
au_delroute(struct au_info *au, int route)
{
int i;
int j=au->z[route];
au_setbit(au->routes, route, 0);
au->z[route]=0x1f;
i=au_rd(au, 0, AU_REG_RTBASE+(route<<2), 4);
au_wr(au, 0, AU_REG_RTBASE+(au->y[route]<<2), i, 4);
au->y[i & 0x7f]=au->y[route];
au_wr(au, 0, AU_REG_RTBASE+(route<<2), 0xfffffffe, 4);
if (au->x[j] == route) au->x[j]=au->y[route];
au->y[route]=0x7f;
}
static void
au_encodec(struct au_info *au, char channel)
{
au_wr(au, 0, AU_REG_CODECEN,
au_rd(au, 0, AU_REG_CODECEN, 4) | (1 << (channel + 8)), 4);
}
static void
au_clrfifo(struct au_info *au, u_int32_t c)
{
u_int32_t i;
for (i=0; i<32; i++) au_wr(au, 0, AU_REG_FIFOBASE+(c<<7)+(i<<2), 0, 4);
}
static void
au_setadb(struct au_info *au, u_int32_t c, u_int32_t enable)
{
int x;
x = au_rd(au, 0, AU_REG_ADB, 4);
x &= ~(1 << c);
x |= (enable << c);
au_wr(au, 0, AU_REG_ADB, x, 4);
}
static void
au_prepareoutput(struct au_chinfo *ch, u_int32_t format)
{
struct au_info *au = ch->parent;
int i, stereo = (format & AFMT_STEREO)? 1 : 0;
u_int32_t baseaddr = vtophys(ch->buffer->buf);
au_wr(au, 0, 0x1061c, 0, 4);
au_wr(au, 0, 0x10620, 0, 4);
au_wr(au, 0, 0x10624, 0, 4);
switch(format & ~AFMT_STEREO) {
case 1:
i=0xb000;
break;
case 2:
i=0xf000;
break;
case 8:
i=0x7000;
break;
case 16:
i=0x23000;
break;
default:
i=0x3000;
}
au_wr(au, 0, 0x10200, baseaddr, 4);
au_wr(au, 0, 0x10204, baseaddr+0x1000, 4);
au_wr(au, 0, 0x10208, baseaddr+0x2000, 4);
au_wr(au, 0, 0x1020c, baseaddr+0x3000, 4);
au_wr(au, 0, 0x10400, 0xdeffffff, 4);
au_wr(au, 0, 0x10404, 0xfcffffff, 4);
au_wr(au, 0, 0x10580, i, 4);
au_wr(au, 0, 0x10210, baseaddr, 4);
au_wr(au, 0, 0x10214, baseaddr+0x1000, 4);
au_wr(au, 0, 0x10218, baseaddr+0x2000, 4);
au_wr(au, 0, 0x1021c, baseaddr+0x3000, 4);
au_wr(au, 0, 0x10408, 0x00fff000 | 0x56000000 | 0x00000fff, 4);
au_wr(au, 0, 0x1040c, 0x00fff000 | 0x74000000 | 0x00000fff, 4);
au_wr(au, 0, 0x10584, i, 4);
au_wr(au, 0, 0x0f800, stereo? 0x00030032 : 0x00030030, 4);
au_wr(au, 0, 0x0f804, stereo? 0x00030032 : 0x00030030, 4);
au_addroute(au, 0x11, 0, 0x58);
au_addroute(au, 0x11, stereo? 0 : 1, 0x59);
}
/* channel interface */
static void *
auchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir)
{
struct au_info *au = devinfo;
struct au_chinfo *ch = (dir == PCMDIR_PLAY)? &au->pch : NULL;
ch->parent = au;
ch->channel = c;
ch->buffer = b;
ch->buffer->bufsize = AU_BUFFSIZE;
if (chn_allocbuf(ch->buffer, au->parent_dmat) == -1) return NULL;
return ch;
}
static int
auchan_setdir(void *data, int dir)
{
struct au_chinfo *ch = data;
if (dir == PCMDIR_PLAY) {
} else {
}
ch->dir = dir;
return 0;
}
static int
auchan_setformat(void *data, u_int32_t format)
{
struct au_chinfo *ch = data;
if (ch->dir == PCMDIR_PLAY) au_prepareoutput(ch, format);
return 0;
}
static int
auchan_setspeed(void *data, u_int32_t speed)
{
struct au_chinfo *ch = data;
if (ch->dir == PCMDIR_PLAY) {
} else {
}
return speed;
}
static int
auchan_setblocksize(void *data, u_int32_t blocksize)
{
return blocksize;
}
static int
auchan_trigger(void *data, int go)
{
struct au_chinfo *ch = data;
struct au_info *au = ch->parent;
if (ch->dir == PCMDIR_PLAY) {
au_setadb(au, 0x11, (go)? 1 : 0);
if (!go) {
au_wr(au, 0, 0xf800, 0, 4);
au_wr(au, 0, 0xf804, 0, 4);
au_delroute(au, 0x58);
au_delroute(au, 0x59);
}
} else {
}
return 0;
}
static int
auchan_getptr(void *data)
{
struct au_chinfo *ch = data;
struct au_info *au = ch->parent;
if (ch->dir == PCMDIR_PLAY) {
return au_rd(au, 0, AU_REG_UNK2, 4) & (AU_BUFFSIZE-1);
} else {
return 0;
}
}
static pcmchan_caps *
auchan_getcaps(void *data)
{
struct au_chinfo *ch = data;
return (ch->dir == PCMDIR_PLAY)? &au_playcaps : &au_reccaps;
}
/* The interrupt handler */
static void
au_intr (void *p)
{
struct au_info *au = p;
u_int32_t intsrc, i;
au->interrupts++;
intsrc=au_rd(au, 0, AU_REG_IRQSRC, 4);
printf("pcm%d: interrupt with src %x\n", au->unit, intsrc);
if (intsrc & AU_IRQ_FATAL) printf("pcm%d: fatal error irq\n", au->unit);
if (intsrc & AU_IRQ_PARITY) printf("pcm%d: parity error irq\n", au->unit);
if (intsrc & AU_IRQ_UNKNOWN) {
(void)au_rd(au, 0, AU_REG_UNK1, 4);
au_wr(au, 0, AU_REG_UNK1, 0, 4);
au_wr(au, 0, AU_REG_UNK1, 0x10000, 4);
}
if (intsrc & AU_IRQ_PCMOUT) {
i=au_rd(au, 0, AU_REG_UNK2, 4) & (AU_BUFFSIZE-1);
chn_intr(au->pch.channel);
(void)au_rd(au, 0, AU_REG_UNK3, 4);
(void)au_rd(au, 0, AU_REG_UNK4, 4);
(void)au_rd(au, 0, AU_REG_UNK5, 4);
}
/* don't support midi
if (intsrc & AU_IRQ_MIDI) {
i=au_rd(au, 0, 0x11004, 4);
j=10;
while (i & 0xff) {
if (j-- <= 0) break;
i=au_rd(au, 0, 0x11000, 4);
if ((au->midi_stat & 1) && (au->midi_out))
au->midi_out(au->midi_devno, i);
i=au_rd(au, 0, 0x11004);
}
}
*/
au_wr(au, 0, AU_REG_IRQSRC, intsrc & 0x7ff, 4);
au_rd(au, 0, AU_REG_IRQSRC, 4);
}
/* -------------------------------------------------------------------- */
/* Probe and attach the card */
static int
au_init(device_t dev, struct au_info *au)
{
u_int32_t i, j;
au_wr(au, 0, AU_REG_IRQGLOB, 0xffffffff, 4);
DELAY(100000);
/* init codec */
/* cold reset */
for (i=0; i<32; i++) {
au_wr(au, 0, AU_REG_CODECCHN+(i<<2), 0, 4);
DELAY(10000);
}
if (1) {
au_wr(au, 0, AU_REG_CODECST, 0x8068, 4);
DELAY(10000);
au_wr(au, 0, AU_REG_CODECST, 0x00e8, 4);
DELAY(10000);
} else {
au_wr(au, 0, AU_REG_CODECST, 0x00a8, 4);
DELAY(100000);
au_wr(au, 0, AU_REG_CODECST, 0x80a8, 4);
DELAY(100000);
au_wr(au, 0, AU_REG_CODECST, 0x80e8, 4);
DELAY(100000);
au_wr(au, 0, AU_REG_CODECST, 0x80a8, 4);
DELAY(100000);
au_wr(au, 0, AU_REG_CODECST, 0x00a8, 4);
DELAY(100000);
au_wr(au, 0, AU_REG_CODECST, 0x00e8, 4);
DELAY(100000);
}
/* init */
for (i=0; i<32; i++) {
au_wr(au, 0, AU_REG_CODECCHN+(i<<2), 0, 4);
DELAY(10000);
}
au_wr(au, 0, AU_REG_CODECST, 0xe8, 4);
DELAY(10000);
au_wr(au, 0, AU_REG_CODECEN, 0, 4);
/* setup codec */
i=j=0;
while (j<100 && (i & AU_CDC_READY)==0) {
i=au_rd(au, 0, AU_REG_CODECST, 4);
DELAY(1000);
j++;
}
if (j==100) device_printf(dev, "codec not ready, status 0x%x\n", i);
/* init adb */
/*au->x5c=0;*/
for (i=0; i<32; i++) au->x[i]=i+0x67;
for (i=0; i<128; i++) au->y[i]=0x7f;
for (i=0; i<128; i++) au->z[i]=0x1f;
au_wr(au, 0, AU_REG_ADB, 0, 4);
for (i=0; i<124; i++) au_wr(au, 0, AU_REG_RTBASE+(i<<2), 0xffffffff, 4);
/* test */
i=au_rd(au, 0, 0x107c0, 4);
if (i!=0xdeadbeef) device_printf(dev, "dma check failed: 0x%x\n", i);
/* install mixer */
au_wr(au, 0, AU_REG_IRQGLOB,
au_rd(au, 0, AU_REG_IRQGLOB, 4) | AU_IRQ_ENABLE, 4);
/* braindead but it's what the oss/linux driver does
* for (i=0; i<0x80000000; i++) au_wr(au, 0, i<<2, 0, 4);
*/
au->routes[0]=au->routes[1]=au->routes[2]=au->routes[3]=0;
/*au->x1e4=0;*/
/* attach channel */
au_addroute(au, 0x11, 0x48, 0x02);
au_addroute(au, 0x11, 0x49, 0x03);
au_encodec(au, 0);
au_encodec(au, 1);
for (i=0; i<48; i++) au_wr(au, 0, 0xf800+(i<<2), 0x20, 4);
for (i=2; i<6; i++) au_wr(au, 0, 0xf800+(i<<2), 0, 4);
au_wr(au, 0, 0xf8c0, 0x0843, 4);
for (i=0; i<4; i++) au_clrfifo(au, i);
return (0);
}
static int
au_testirq(struct au_info *au)
{
au_wr(au, 0, AU_REG_UNK1, 0x80001000, 4);
au_wr(au, 0, AU_REG_IRQEN, 0x00001030, 4);
au_wr(au, 0, AU_REG_IRQSRC, 0x000007ff, 4);
DELAY(1000000);
if (au->interrupts==0) printf("pcm%d: irq test failed\n", au->unit);
/* this apparently generates an irq */
return 0;
}
static int
au_pci_probe(device_t dev)
{
if (pci_get_devid(dev) == AU8820_PCI_ID) {
device_set_desc(dev, "Aureal Vortex 8820");
return 0;
}
return ENXIO;
}
static int
au_pci_attach(device_t dev)
{
snddev_info *d;
u_int32_t data;
struct au_info *au;
int type[10];
int regid[10];
struct resource *reg[10];
int i, j, mapped = 0;
int irqid;
struct resource *irq = 0;
void *ih = 0;
struct ac97_info *codec;
char status[SND_STATUSLEN];
d = device_get_softc(dev);
if ((au = malloc(sizeof(*au), M_DEVBUF, M_NOWAIT)) == NULL) {
device_printf(dev, "cannot allocate softc\n");
return ENXIO;
}
bzero(au, sizeof(*au));
au->unit = device_get_unit(dev);
data = pci_read_config(dev, PCIR_COMMAND, 2);
data |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN);
pci_write_config(dev, PCIR_COMMAND, data, 2);
data = pci_read_config(dev, PCIR_COMMAND, 2);
j=0;
/* XXX dfr: is this strictly necessary? */
for (i=0; i<PCI_MAXMAPS_0; i++) {
#if 0
/* Slapped wrist: config_id and map are private structures */
if (bootverbose) {
printf("pcm%d: map %d - allocating ", unit, i+1);
printf("0x%x bytes of ", 1<<config_id->map[i].ln2size);
printf("%s space ", (config_id->map[i].type & PCI_MAPPORT)?
"io" : "memory");
printf("at 0x%x...", config_id->map[i].base);
}
#endif
regid[j] = PCIR_MAPS + i*4;
type[j] = SYS_RES_MEMORY;
reg[j] = bus_alloc_resource(dev, type[j], &regid[j],
0, ~0, 1, RF_ACTIVE);
if (!reg[j]) {
type[j] = SYS_RES_IOPORT;
reg[j] = bus_alloc_resource(dev, type[j], &regid[j],
0, ~0, 1, RF_ACTIVE);
}
if (reg[j]) {
au->st[i] = rman_get_bustag(reg[j]);
au->sh[i] = rman_get_bushandle(reg[j]);
mapped++;
}
#if 0
if (bootverbose) printf("%s\n", mapped? "ok" : "failed");
#endif
if (mapped) j++;
if (j == 10) {
/* XXX */
device_printf(dev, "too many resources");
goto bad;
}
}
#if 0
if (j < config_id->nummaps) {
printf("pcm%d: unable to map a required resource\n", unit);
free(au, M_DEVBUF);
return;
}
#endif
au_wr(au, 0, AU_REG_IRQEN, 0, 4);
irqid = 0;
irq = bus_alloc_resource(dev, SYS_RES_IRQ, &irqid,
0, ~0, 1, RF_ACTIVE | RF_SHAREABLE);
if (!irq
|| bus_setup_intr(dev, irq, INTR_TYPE_TTY, au_intr, au, &ih)) {
device_printf(dev, "unable to map interrupt\n");
goto bad;
}
if (au_testirq(au)) device_printf(dev, "irq test failed\n");
if (au_init(dev, au) == -1) {
device_printf(dev, "unable to initialize the card\n");
goto bad;
}
codec = ac97_create(au, au_rdcd, au_wrcd);
if (codec == NULL) goto bad;
mixer_init(d, &ac97_mixer, codec);
if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0,
/*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
/*highaddr*/BUS_SPACE_MAXADDR,
/*filter*/NULL, /*filterarg*/NULL,
/*maxsize*/AU_BUFFSIZE, /*nsegments*/1, /*maxsegz*/0x3ffff,
/*flags*/0, &au->parent_dmat) != 0) {
device_printf(dev, "unable to create dma tag\n");
goto bad;
}
snprintf(status, SND_STATUSLEN, "at %s 0x%lx irq %ld",
(type[0] == SYS_RES_IOPORT)? "io" : "memory",
rman_get_start(reg[0]), rman_get_start(irq));
if (pcm_register(dev, au, 1, 1)) goto bad;
/* pcm_addchan(dev, PCMDIR_REC, &au_chantemplate, au); */
pcm_addchan(dev, PCMDIR_PLAY, &au_chantemplate, au);
pcm_setstatus(dev, status);
return 0;
bad:
if (au) free(au, M_DEVBUF);
for (i = 0; i < j; i++)
bus_release_resource(dev, type[i], regid[i], reg[i]);
if (ih) bus_teardown_intr(dev, irq, ih);
if (irq) bus_release_resource(dev, SYS_RES_IRQ, irqid, irq);
return ENXIO;
}
static device_method_t au_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, au_pci_probe),
DEVMETHOD(device_attach, au_pci_attach),
{ 0, 0 }
};
static driver_t au_driver = {
"pcm",
au_methods,
sizeof(snddev_info),
};
static devclass_t pcm_devclass;
DRIVER_MODULE(au, pci, au_driver, pcm_devclass, 0, 0);
#endif /* NPCI != 0 */

View file

@ -1,99 +0,0 @@
/*
* Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
* All rights reserved.
*
* 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.
*
* $FreeBSD$
*/
#ifndef _AU8820_REG_H
#define _AU8820_REG_H
#define AU_BUFFSIZE 0x4000
#define AU_REG_FIFOBASE 0x0e000
#define AU_REG_UNK2 0x105c0
#define AU_REG_UNK3 0x10600
#define AU_REG_UNK4 0x10604
#define AU_REG_UNK5 0x10608
#define AU_REG_RTBASE 0x10800
#define AU_REG_ADB 0x10a00
#define AU_REG_CODECCHN 0x11880
#define AU_REG_CODECST 0x11984
#define AU_CDC_RUN 0x00000040
#define AU_CDC_WROK 0x00000100
#define AU_CDC_RESET 0x00008000
#define AU_REG_CODECIO 0x11988
#define AU_CDC_DATAMASK 0x0000ffff
#define AU_CDC_REGMASK 0x007f0000
#define AU_CDC_REGSET 0x00800000
#define AU_CDC_READY 0x04000000
#define AU_REG_CODECEN 0x11990
#define AU_CDC_CHAN1EN 0x00000100
#define AU_CDC_CHAN2EN 0x00000200
#define AU_REG_UNK1 0x1199c
#define AU_REG_IRQSRC 0x12800
#define AU_IRQ_FATAL 0x0001
#define AU_IRQ_PARITY 0x0002
#define AU_IRQ_PCMOUT 0x0020
#define AU_IRQ_UNKNOWN 0x1000
#define AU_IRQ_MIDI 0x2000
#define AU_REG_IRQEN 0x12804
#define AU_REG_IRQGLOB 0x1280c
#define AU_IRQ_ENABLE 0x4000
#define AC97_MUTE 0x8000
#define AC97_REG_RESET 0x00
#define AC97_MIX_MASTER 0x02
#define AC97_MIX_PHONES 0x04
#define AC97_MIX_MONO 0x06
#define AC97_MIX_TONE 0x08
#define AC97_MIX_BEEP 0x0a
#define AC97_MIX_PHONE 0x0c
#define AC97_MIX_MIC 0x0e
#define AC97_MIX_LINE 0x10
#define AC97_MIX_CD 0x12
#define AC97_MIX_VIDEO 0x14
#define AC97_MIX_AUX 0x16
#define AC97_MIX_PCM 0x18
#define AC97_REG_RECSEL 0x1a
#define AC97_MIX_RGAIN 0x1c
#define AC97_MIX_MGAIN 0x1e
#define AC97_REG_GEN 0x20
#define AC97_REG_3D 0x22
#define AC97_REG_POWER 0x26
#define AC97_REG_ID1 0x7c
#define AC97_REG_ID2 0x7e
#endif

View file

@ -1,543 +0,0 @@
/*
* Support the ENSONIQ AudioPCI board based on the ES1370 and Codec
* AK4531.
*
* Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
* Copyright (c) 1998 by Joachim Kuebart. All rights reserved.
*
* 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.
*
* 3. All advertising materials mentioning features or use of this
* software must display the following acknowledgement:
* This product includes software developed by Joachim Kuebart.
*
* 4. The name of the author may not be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED ``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 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.
*
* $FreeBSD$
*/
#include "pci.h"
#include "pcm.h"
#include <dev/pcm/sound.h>
#include <dev/pcm/pci/es1370.h>
#include <pci/pcireg.h>
#include <pci/pcivar.h>
#if NPCI != 0
#define MEM_MAP_REG 0x14
/* PCI IDs of supported chips */
#define ES1370_PCI_ID 0x50001274
/* device private data */
struct es_info;
struct es_chinfo {
struct es_info *parent;
pcm_channel *channel;
snd_dbuf *buffer;
int dir;
u_int32_t fmt;
};
struct es_info {
bus_space_tag_t st;
bus_space_handle_t sh;
bus_dma_tag_t parent_dmat;
/* Contents of board's registers */
u_long ctrl;
u_long sctrl;
struct es_chinfo pch, rch;
};
/* -------------------------------------------------------------------- */
/* prototypes */
static int es_init(struct es_info *);
static void es_intr(void *);
static int write_codec(struct es_info *, u_char, u_char);
/* channel interface */
static void *eschan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir);
static int eschan_setdir(void *data, int dir);
static int eschan_setformat(void *data, u_int32_t format);
static int eschan_setspeed(void *data, u_int32_t speed);
static int eschan_setblocksize(void *data, u_int32_t blocksize);
static int eschan_trigger(void *data, int go);
static int eschan_getptr(void *data);
static pcmchan_caps *eschan_getcaps(void *data);
static pcmchan_caps es_playcaps = {
4000, 48000,
AFMT_STEREO | AFMT_U8 | AFMT_S16_LE,
AFMT_STEREO | AFMT_S16_LE
};
static pcmchan_caps es_reccaps = {
4000, 48000,
AFMT_STEREO | AFMT_U8 | AFMT_S16_LE,
AFMT_STEREO | AFMT_S16_LE
};
static pcm_channel es_chantemplate = {
eschan_init,
eschan_setdir,
eschan_setformat,
eschan_setspeed,
eschan_setblocksize,
eschan_trigger,
eschan_getptr,
eschan_getcaps,
};
/* -------------------------------------------------------------------- */
/* The mixer interface */
static int es_mixinit(snd_mixer *m);
static int es_mixset(snd_mixer *m, unsigned dev, unsigned left, unsigned right);
static int es_mixsetrecsrc(snd_mixer *m, u_int32_t src);
static snd_mixer es_mixer = {
"Ensoniq AudioPCI 1370 mixer",
es_mixinit,
es_mixset,
es_mixsetrecsrc,
};
static const struct {
unsigned volidx:4;
unsigned left:4;
unsigned right:4;
unsigned stereo:1;
unsigned recmask:13;
unsigned avail:1;
} mixtable[SOUND_MIXER_NRDEVICES] = {
[SOUND_MIXER_VOLUME] = { 0, 0x0, 0x1, 1, 0x0000, 1 },
[SOUND_MIXER_PCM] = { 1, 0x2, 0x3, 1, 0x0400, 1 },
[SOUND_MIXER_SYNTH] = { 2, 0x4, 0x5, 1, 0x0060, 1 },
[SOUND_MIXER_CD] = { 3, 0x6, 0x7, 1, 0x0006, 1 },
[SOUND_MIXER_LINE] = { 4, 0x8, 0x9, 1, 0x0018, 1 },
[SOUND_MIXER_LINE1] = { 5, 0xa, 0xb, 1, 0x1800, 1 },
[SOUND_MIXER_LINE2] = { 6, 0xc, 0x0, 0, 0x0100, 1 },
[SOUND_MIXER_LINE3] = { 7, 0xd, 0x0, 0, 0x0200, 1 },
[SOUND_MIXER_MIC] = { 8, 0xe, 0x0, 0, 0x0001, 1 },
[SOUND_MIXER_OGAIN] = { 9, 0xf, 0x0, 0, 0x0000, 1 } };
static int
es_mixinit(snd_mixer *m)
{
int i;
u_int32_t v;
v = 0;
for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
if (mixtable[i].avail) v |= (1 << i);
mix_setdevs(m, v);
v = 0;
for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
if (mixtable[i].recmask) v |= (1 << i);
mix_setrecdevs(m, v);
return 0;
}
static int
es_mixset(snd_mixer *m, unsigned dev, unsigned left, unsigned right)
{
int l, r, rl, rr;
if (!mixtable[dev].avail) return -1;
l = left;
r = mixtable[dev].stereo? right : l;
if (mixtable[dev].left == 0xf) {
rl = (l < 2)? 0x80 : 7 - (l - 2) / 14;
} else {
rl = (l < 10)? 0x80 : 15 - (l - 10) / 6;
}
if (mixtable[dev].stereo) {
rr = (r < 10)? 0x80 : 15 - (r - 10) / 6;
write_codec(mix_getdevinfo(m), mixtable[dev].right, rr);
}
write_codec(mix_getdevinfo(m), mixtable[dev].left, rl);
return l | (r << 8);
}
static int
es_mixsetrecsrc(snd_mixer *m, u_int32_t src)
{
int i, j = 0;
if (src == 0) src = 1 << SOUND_MIXER_MIC;
src &= mix_getrecdevs(m);
for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
if ((src & (1 << i)) != 0) j |= mixtable[i].recmask;
write_codec(mix_getdevinfo(m), CODEC_LIMIX1, j & 0x55);
write_codec(mix_getdevinfo(m), CODEC_RIMIX1, j & 0xaa);
write_codec(mix_getdevinfo(m), CODEC_LIMIX2, (j >> 8) & 0x17);
write_codec(mix_getdevinfo(m), CODEC_RIMIX2, (j >> 8) & 0x0f);
write_codec(mix_getdevinfo(m), CODEC_OMIX1, 0x7f);
write_codec(mix_getdevinfo(m), CODEC_OMIX2, 0x3f);
return src;
}
static int
write_codec(struct es_info *es, u_char i, u_char data)
{
int wait = 100; /* 100 msec timeout */
do {
if ((bus_space_read_4(es->st, es->sh, ES1370_REG_STATUS) &
STAT_CSTAT) == 0) {
bus_space_write_2(es->st, es->sh, ES1370_REG_CODEC,
((u_short)i << CODEC_INDEX_SHIFT) | data);
return 0;
}
DELAY(1000);
} while (--wait);
printf("pcm: write_codec timed out\n");
return -1;
}
/* -------------------------------------------------------------------- */
/* channel interface */
static void *
eschan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir)
{
struct es_info *es = devinfo;
struct es_chinfo *ch = (dir == PCMDIR_PLAY)? &es->pch : &es->rch;
ch->parent = es;
ch->channel = c;
ch->buffer = b;
ch->buffer->bufsize = ES_BUFFSIZE;
if (chn_allocbuf(ch->buffer, es->parent_dmat) == -1) return NULL;
return ch;
}
static int
eschan_setdir(void *data, int dir)
{
struct es_chinfo *ch = data;
struct es_info *es = ch->parent;
if (dir == PCMDIR_PLAY) {
bus_space_write_1(es->st, es->sh, ES1370_REG_MEMPAGE,
ES1370_REG_DAC2_FRAMEADR >> 8);
bus_space_write_4(es->st, es->sh, ES1370_REG_DAC2_FRAMEADR & 0xff,
vtophys(ch->buffer->buf));
bus_space_write_4(es->st, es->sh, ES1370_REG_DAC2_FRAMECNT & 0xff,
(ch->buffer->bufsize >> 2) - 1);
} else {
bus_space_write_1(es->st, es->sh, ES1370_REG_MEMPAGE,
ES1370_REG_ADC_FRAMEADR >> 8);
bus_space_write_4(es->st, es->sh, ES1370_REG_ADC_FRAMEADR & 0xff,
vtophys(ch->buffer->buf));
bus_space_write_4(es->st, es->sh, ES1370_REG_ADC_FRAMECNT & 0xff,
(ch->buffer->bufsize >> 2) - 1);
}
ch->dir = dir;
return 0;
}
static int
eschan_setformat(void *data, u_int32_t format)
{
struct es_chinfo *ch = data;
struct es_info *es = ch->parent;
if (ch->dir == PCMDIR_PLAY) {
es->sctrl &= ~SCTRL_P2FMT;
if (format & AFMT_S16_LE) es->sctrl |= SCTRL_P2SEB;
if (format & AFMT_STEREO) es->sctrl |= SCTRL_P2SMB;
} else {
es->sctrl &= ~SCTRL_R1FMT;
if (format & AFMT_S16_LE) es->sctrl |= SCTRL_R1SEB;
if (format & AFMT_STEREO) es->sctrl |= SCTRL_R1SMB;
}
bus_space_write_4(es->st, es->sh, ES1370_REG_SERIAL_CONTROL, es->sctrl);
ch->fmt = format;
return 0;
}
static int
eschan_setspeed(void *data, u_int32_t speed)
{
struct es_chinfo *ch = data;
struct es_info *es = ch->parent;
es->ctrl &= ~CTRL_PCLKDIV;
es->ctrl |= DAC2_SRTODIV(speed) << CTRL_SH_PCLKDIV;
bus_space_write_4(es->st, es->sh, ES1370_REG_CONTROL, es->ctrl);
/* rec/play speeds locked together - should indicate in flags */
#if 0
if (ch->direction == PCMDIR_PLAY) d->rec[0].speed = speed;
else d->play[0].speed = speed;
#endif
return speed; /* XXX calc real speed */
}
static int
eschan_setblocksize(void *data, u_int32_t blocksize)
{
return blocksize;
}
static int
eschan_trigger(void *data, int go)
{
struct es_chinfo *ch = data;
struct es_info *es = ch->parent;
unsigned cnt = ch->buffer->dl / ch->buffer->sample_size - 1;
if (ch->dir == PCMDIR_PLAY) {
if (go == PCMTRIG_START) {
int b = (ch->fmt & AFMT_S16_LE)? 2 : 1;
es->ctrl |= CTRL_DAC2_EN;
es->sctrl &= ~(SCTRL_P2ENDINC | SCTRL_P2STINC |
SCTRL_P2LOOPSEL | SCTRL_P2PAUSE |
SCTRL_P2DACSEN);
es->sctrl |= SCTRL_P2INTEN | (b << SCTRL_SH_P2ENDINC);
bus_space_write_4(es->st, es->sh,
ES1370_REG_DAC2_SCOUNT, cnt);
} else es->ctrl &= ~CTRL_DAC2_EN;
} else {
if (go == PCMTRIG_START) {
es->ctrl |= CTRL_ADC_EN;
es->sctrl &= ~SCTRL_R1LOOPSEL;
es->sctrl |= SCTRL_R1INTEN;
bus_space_write_4(es->st, es->sh,
ES1370_REG_ADC_SCOUNT, cnt);
} else es->ctrl &= ~CTRL_ADC_EN;
}
bus_space_write_4(es->st, es->sh, ES1370_REG_SERIAL_CONTROL, es->sctrl);
bus_space_write_4(es->st, es->sh, ES1370_REG_CONTROL, es->ctrl);
return 0;
}
static int
eschan_getptr(void *data)
{
struct es_chinfo *ch = data;
struct es_info *es = ch->parent;
if (ch->dir == PCMDIR_PLAY) {
bus_space_write_4(es->st, es->sh, ES1370_REG_MEMPAGE,
ES1370_REG_DAC2_FRAMECNT >> 8);
return (bus_space_read_4(es->st, es->sh,
ES1370_REG_DAC2_FRAMECNT & 0xff) >> 14) & 0x3fffc;
} else {
bus_space_write_4(es->st, es->sh, ES1370_REG_MEMPAGE,
ES1370_REG_ADC_FRAMECNT >> 8);
return (bus_space_read_4(es->st, es->sh,
ES1370_REG_ADC_FRAMECNT & 0xff) >> 14) & 0x3fffc;
}
}
static pcmchan_caps *
eschan_getcaps(void *data)
{
struct es_chinfo *ch = data;
return (ch->dir == PCMDIR_PLAY)? &es_playcaps : &es_reccaps;
}
/* The interrupt handler */
static void
es_intr (void *p)
{
struct es_info *es = p;
unsigned intsrc, sctrl;
intsrc = bus_space_read_4(es->st, es->sh, ES1370_REG_STATUS);
if ((intsrc & STAT_INTR) == 0) return;
sctrl = es->sctrl;
if (intsrc & STAT_ADC) sctrl &= ~SCTRL_R1INTEN;
if (intsrc & STAT_DAC1) sctrl &= ~SCTRL_P1INTEN;
if (intsrc & STAT_DAC2) sctrl &= ~SCTRL_P2INTEN;
bus_space_write_4(es->st, es->sh, ES1370_REG_SERIAL_CONTROL, sctrl);
bus_space_write_4(es->st, es->sh, ES1370_REG_SERIAL_CONTROL, es->sctrl);
if (intsrc & STAT_DAC2) chn_intr(es->pch.channel);
if (intsrc & STAT_ADC) chn_intr(es->rch.channel);
}
/* -------------------------------------------------------------------- */
/*
* Probe and attach the card
*/
static int
es_init(struct es_info *es)
{
es->ctrl = CTRL_CDC_EN | CTRL_SERR_DIS |
(DAC2_SRTODIV(DSP_DEFAULT_SPEED) << CTRL_SH_PCLKDIV);
bus_space_write_4(es->st, es->sh, ES1370_REG_CONTROL, es->ctrl);
es->sctrl = 0;
bus_space_write_4(es->st, es->sh, ES1370_REG_SERIAL_CONTROL, es->sctrl);
write_codec(es, CODEC_RES_PD, 3);/* No RST, PD */
write_codec(es, CODEC_CSEL, 0); /* CODEC ADC and CODEC DAC use
* {LR,B}CLK2 and run off the LRCLK2
* PLL; program DAC_SYNC=0! */
write_codec(es, CODEC_ADSEL, 0);/* Recording source is mixer */
write_codec(es, CODEC_MGAIN, 0);/* MIC amp is 0db */
return 0;
}
static int
es_pci_probe(device_t dev)
{
if (pci_get_devid(dev) == ES1370_PCI_ID) {
device_set_desc(dev, "AudioPCI ES1370");
return 0;
}
return ENXIO;
}
static int
es_pci_attach(device_t dev)
{
snddev_info *d;
u_int32_t data;
struct es_info *es = 0;
int type = 0;
int regid;
struct resource *reg = 0;
int mapped;
int irqid;
struct resource *irq = 0;
void *ih = 0;
char status[SND_STATUSLEN];
d = device_get_softc(dev);
if ((es = malloc(sizeof *es, M_DEVBUF, M_NOWAIT)) == NULL) {
device_printf(dev, "cannot allocate softc\n");
return ENXIO;
}
bzero(es, sizeof *es);
mapped = 0;
data = pci_read_config(dev, PCIR_COMMAND, 2);
if (mapped == 0 && (data & PCIM_CMD_MEMEN)) {
regid = MEM_MAP_REG;
type = SYS_RES_MEMORY;
reg = bus_alloc_resource(dev, type, &regid,
0, ~0, 1, RF_ACTIVE);
if (reg) {
es->st = rman_get_bustag(reg);
es->sh = rman_get_bushandle(reg);
mapped++;
}
}
if (mapped == 0 && (data & PCIM_CMD_PORTEN)) {
regid = PCI_MAP_REG_START;
type = SYS_RES_IOPORT;
reg = bus_alloc_resource(dev, type, &regid,
0, ~0, 1, RF_ACTIVE);
if (reg) {
es->st = rman_get_bustag(reg);
es->sh = rman_get_bushandle(reg);
mapped++;
}
}
if (mapped == 0) {
device_printf(dev, "unable to map register space\n");
goto bad;
}
if (es_init(es) == -1) {
device_printf(dev, "unable to initialize the card\n");
goto bad;
}
mixer_init(d, &es_mixer, es);
irqid = 0;
irq = bus_alloc_resource(dev, SYS_RES_IRQ, &irqid,
0, ~0, 1, RF_ACTIVE | RF_SHAREABLE);
if (!irq
|| bus_setup_intr(dev, irq, INTR_TYPE_TTY, es_intr, es, &ih)) {
device_printf(dev, "unable to map interrupt\n");
goto bad;
}
if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0,
/*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
/*highaddr*/BUS_SPACE_MAXADDR,
/*filter*/NULL, /*filterarg*/NULL,
/*maxsize*/ES_BUFFSIZE, /*nsegments*/1, /*maxsegz*/0x3ffff,
/*flags*/0, &es->parent_dmat) != 0) {
device_printf(dev, "unable to create dma tag\n");
goto bad;
}
snprintf(status, SND_STATUSLEN, "at %s 0x%lx irq %ld",
(type == SYS_RES_IOPORT)? "io" : "memory",
rman_get_start(reg), rman_get_start(irq));
if (pcm_register(dev, es, 1, 1)) goto bad;
pcm_addchan(dev, PCMDIR_REC, &es_chantemplate, es);
pcm_addchan(dev, PCMDIR_PLAY, &es_chantemplate, es);
pcm_setstatus(dev, status);
return 0;
bad:
if (es) free(es, M_DEVBUF);
if (reg) bus_release_resource(dev, type, regid, reg);
if (ih) bus_teardown_intr(dev, irq, ih);
if (irq) bus_release_resource(dev, SYS_RES_IRQ, irqid, irq);
return ENXIO;
}
static device_method_t es_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, es_pci_probe),
DEVMETHOD(device_attach, es_pci_attach),
{ 0, 0 }
};
static driver_t es_driver = {
"pcm",
es_methods,
sizeof(snddev_info),
};
static devclass_t pcm_devclass;
DRIVER_MODULE(es, pci, es_driver, pcm_devclass, 0, 0);
#endif /* NPCI != 0 */

View file

@ -1,134 +0,0 @@
/*
* This supports the ENSONIQ AudioPCI board based on the ES1370.
*
* Copyright (c) 1998 Joachim Kuebart <joki@kuebart.stuttgart.netsurf.de>
* All rights reserved.
*
* 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 immediately at the beginning of the file, without modification,
* 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.
* 3. Absolutely no warranty of function or purpose is made by the author
* Joachim Kuebart.
* 4. Modifications may be freely made to this file if the above conditions
* are met.
*
* $FreeBSD$
*/
#ifndef _ES1370_REG_H
#define _ES1370_REG_H
#define ES1370_REG_CONTROL 0x00
#define ES1370_REG_STATUS 0x04
#define ES1370_REG_UART_DATA 0x08
#define ES1370_REG_UART_STATUS 0x09
#define ES1370_REG_UART_CONTROL 0x09
#define ES1370_REG_UART_TEST 0x0a
#define ES1370_REG_MEMPAGE 0x0c
#define ES1370_REG_CODEC 0x10
#define CODEC_INDEX_SHIFT 8
#define ES1370_REG_SERIAL_CONTROL 0x20
#define ES1370_REG_DAC1_SCOUNT 0x24
#define ES1370_REG_DAC2_SCOUNT 0x28
#define ES1370_REG_ADC_SCOUNT 0x2c
#define ES1370_REG_DAC1_FRAMEADR 0xc30
#define ES1370_REG_DAC1_FRAMECNT 0xc34
#define ES1370_REG_DAC2_FRAMEADR 0xc38
#define ES1370_REG_DAC2_FRAMECNT 0xc3c
#define ES1370_REG_ADC_FRAMEADR 0xd30
#define ES1370_REG_ADC_FRAMECNT 0xd34
#define DAC2_SRTODIV(x) (((1411200 + (x) / 2) / (x) - 2) & 0x1fff)
#define DAC2_DIVTOSR(x) (1411200 / ((x) + 2))
#define CTRL_ADC_STOP 0x80000000 /* 1 = ADC stopped */
#define CTRL_XCTL1 0x40000000 /* SERR pin if enabled */
#define CTRL_OPEN 0x20000000 /* no function, can be read and
* written */
#define CTRL_PCLKDIV 0x1fff0000 /* ADC/DAC2 clock divider */
#define CTRL_SH_PCLKDIV 16
#define CTRL_MSFMTSEL 0x00008000 /* MPEG serial data fmt: 0 = Sony, 1
* = I2S */
#define CTRL_M_SBB 0x00004000 /* DAC2 clock: 0 = PCLKDIV, 1 = MPEG */
#define CTRL_WTSRSEL 0x00003000 /* DAC1 clock freq: 0=5512, 1=11025,
* 2=22050, 3=44100 */
#define CTRL_SH_WTSRSEL 12
#define CTRL_DAC_SYNC 0x00000800 /* 1 = DAC2 runs off DAC1 clock */
#define CTRL_CCB_INTRM 0x00000400 /* 1 = CCB "voice" ints enabled */
#define CTRL_M_CB 0x00000200 /* recording source: 0 = ADC, 1 =
* MPEG */
#define CTRL_XCTL0 0x00000100 /* 0 = Line in, 1 = Line out */
#define CTRL_BREQ 0x00000080 /* 1 = test mode (internal mem test) */
#define CTRL_DAC1_EN 0x00000040 /* enable DAC1 */
#define CTRL_DAC2_EN 0x00000020 /* enable DAC2 */
#define CTRL_ADC_EN 0x00000010 /* enable ADC */
#define CTRL_UART_EN 0x00000008 /* enable MIDI uart */
#define CTRL_JYSTK_EN 0x00000004 /* enable Joystick port (presumably
* at address 0x200) */
#define CTRL_CDC_EN 0x00000002 /* enable serial (CODEC) interface */
#define CTRL_SERR_DIS 0x00000001 /* 1 = disable PCI SERR signal */
#define SCTRL_P2ENDINC 0x00380000 /* */
#define SCTRL_SH_P2ENDINC 19
#define SCTRL_P2STINC 0x00070000 /* */
#define SCTRL_SH_P2STINC 16
#define SCTRL_R1LOOPSEL 0x00008000 /* 0 = loop mode */
#define SCTRL_P2LOOPSEL 0x00004000 /* 0 = loop mode */
#define SCTRL_P1LOOPSEL 0x00002000 /* 0 = loop mode */
#define SCTRL_P2PAUSE 0x00001000 /* 1 = pause mode */
#define SCTRL_P1PAUSE 0x00000800 /* 1 = pause mode */
#define SCTRL_R1INTEN 0x00000400 /* enable interrupt */
#define SCTRL_P2INTEN 0x00000200 /* enable interrupt */
#define SCTRL_P1INTEN 0x00000100 /* enable interrupt */
#define SCTRL_P1SCTRLD 0x00000080 /* reload sample count register for
* DAC1 */
#define SCTRL_P2DACSEN 0x00000040 /* 1 = DAC2 play back last sample
* when disabled */
#define SCTRL_R1SEB 0x00000020 /* 1 = 16bit */
#define SCTRL_R1SMB 0x00000010 /* 1 = stereo */
#define SCTRL_R1FMT 0x00000030 /* format mask */
#define SCTRL_SH_R1FMT 4
#define SCTRL_P2SEB 0x00000008 /* 1 = 16bit */
#define SCTRL_P2SMB 0x00000004 /* 1 = stereo */
#define SCTRL_P2FMT 0x0000000c /* format mask */
#define SCTRL_SH_P2FMT 2
#define SCTRL_P1SEB 0x00000002 /* 1 = 16bit */
#define SCTRL_P1SMB 0x00000001 /* 1 = stereo */
#define SCTRL_P1FMT 0x00000003 /* format mask */
#define SCTRL_SH_P1FMT 0
#define STAT_INTR 0x80000000 /* wired or of all interrupt bits */
#define STAT_CSTAT 0x00000400 /* 1 = codec busy or codec write in
* progress */
#define STAT_CBUSY 0x00000200 /* 1 = codec busy */
#define STAT_CWRIP 0x00000100 /* 1 = codec write in progress */
#define STAT_VC 0x00000060 /* CCB int source, 0=DAC1, 1=DAC2,
* 2=ADC, 3=undef */
#define STAT_SH_VC 5
#define STAT_MCCB 0x00000010 /* CCB int pending */
#define STAT_UART 0x00000008 /* UART int pending */
#define STAT_DAC1 0x00000004 /* DAC1 int pending */
#define STAT_DAC2 0x00000002 /* DAC2 int pending */
#define STAT_ADC 0x00000001 /* ADC int pending */
#define CODEC_OMIX1 0x10
#define CODEC_OMIX2 0x11
#define CODEC_LIMIX1 0x12
#define CODEC_RIMIX1 0x13
#define CODEC_LIMIX2 0x14
#define CODEC_RIMIX2 0x15
#define CODEC_RES_PD 0x16
#define CODEC_CSEL 0x17
#define CODEC_ADSEL 0x18
#define CODEC_MGAIN 0x19
#define ES_BUFFSIZE 0x20000 /* We're PCI! Use a large buffer */
#endif

View file

@ -1,916 +0,0 @@
/*
* Support the ENSONIQ AudioPCI board and Creative Labs SoundBlaster PCI
* boards based on the ES1370, ES1371 and ES1373 chips.
*
* Copyright (c) 1999 Russell Cattelan <cattelan@thebarn.com>
* Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
* Copyright (c) 1998 by Joachim Kuebart. All rights reserved.
*
* 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.
*
* 3. All advertising materials mentioning features or use of this
* software must display the following acknowledgement:
* This product includes software developed by Joachim Kuebart.
*
* 4. The name of the author may not be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED ``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 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.
*
* $FreeBSD$
*/
/*
* Part of this code was heavily inspired by the linux driver from
* Thomas Sailer (sailer@ife.ee.ethz.ch)
* Just about everything has been touched and reworked in some way but
* the all the underlying sequences/timing/register values are from
* Thomas' code.
*
*/
#include "pci.h"
#include "pcm.h"
#include <dev/pcm/sound.h>
#include <dev/pcm/ac97.h>
#include <dev/pcm/pci/es137x.h>
#include <pci/pcireg.h>
#include <pci/pcivar.h>
#include <sys/sysctl.h>
#if NPCI != 0
static int debug = 0;
SYSCTL_INT(_debug, OID_AUTO, es_debug, CTLFLAG_RW, &debug, 0, "");
#define MEM_MAP_REG 0x14
/* PCI IDs of supported chips */
#define ES1370_PCI_ID 0x50001274
#define ES1371_PCI_ID 0x13711274
/* device private data */
struct es_info;
typedef struct es_chinfo {
struct es_info *parent;
pcm_channel *channel;
snd_dbuf *buffer;
int dir;
u_int32_t fmt;
} es_chinfo_t;
typedef struct es_info {
bus_space_tag_t st;
bus_space_handle_t sh;
bus_dma_tag_t parent_dmat;
/* Contents of board's registers */
u_long ctrl;
u_long sctrl;
struct es_chinfo pch, rch;
} es_info_t;
/* -------------------------------------------------------------------- */
/* prototypes */
static u_int es1371_wait_src_ready(es_info_t *);
static void es1371_src_write(es_info_t *, u_short, unsigned short);
static u_int es1371_adc_rate (es_info_t *, u_int, int);
static u_int es1371_dac1_rate(es_info_t *, u_int, int);
static u_int es1371_dac2_rate(es_info_t *, u_int, int);
static void es1371_wrcodec(void *, int, u_int32_t);
static u_int32_t es1371_rdcodec(void *, u_int32_t);
static int es1371_init(es_info_t *es);
static int eschan1371_setspeed(void *data, u_int32_t speed);
static int es_init(struct es_info *);
static void es_intr(void *);
static int write_codec(struct es_info *, u_char, u_char);
/* channel interface */
static void *eschan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir);
static int eschan_setdir(void *data, int dir);
static int eschan_setformat(void *data, u_int32_t format);
static int eschan_setspeed(void *data, u_int32_t speed);
static int eschan_setblocksize(void *data, u_int32_t blocksize);
static int eschan_trigger(void *data, int go);
static int eschan_getptr(void *data);
static pcmchan_caps *eschan_getcaps(void *data);
static pcmchan_caps es_playcaps = {
4000, 48000,
AFMT_STEREO | AFMT_U8 | AFMT_S16_LE,
AFMT_STEREO | AFMT_S16_LE
};
static pcmchan_caps es_reccaps = {
4000, 48000,
AFMT_STEREO | AFMT_U8 | AFMT_S16_LE,
AFMT_STEREO | AFMT_S16_LE
};
static pcm_channel es_chantemplate = {
eschan_init,
eschan_setdir,
eschan_setformat,
eschan_setspeed,
eschan_setblocksize,
eschan_trigger,
eschan_getptr,
eschan_getcaps,
};
/* -------------------------------------------------------------------- */
/* The mixer interface */
static int es_mixinit(snd_mixer *m);
static int es_mixset(snd_mixer *m, unsigned dev, unsigned left, unsigned right);
static int es_mixsetrecsrc(snd_mixer *m, u_int32_t src);
static snd_mixer es_mixer = {
"Ensoniq AudioPCI 1370 mixer",
es_mixinit,
es_mixset,
es_mixsetrecsrc,
};
static const struct {
unsigned volidx:4;
unsigned left:4;
unsigned right:4;
unsigned stereo:1;
unsigned recmask:13;
unsigned avail:1;
} mixtable[SOUND_MIXER_NRDEVICES] = {
[SOUND_MIXER_VOLUME] = { 0, 0x0, 0x1, 1, 0x0000, 1 },
[SOUND_MIXER_PCM] = { 1, 0x2, 0x3, 1, 0x0400, 1 },
[SOUND_MIXER_SYNTH] = { 2, 0x4, 0x5, 1, 0x0060, 1 },
[SOUND_MIXER_CD] = { 3, 0x6, 0x7, 1, 0x0006, 1 },
[SOUND_MIXER_LINE] = { 4, 0x8, 0x9, 1, 0x0018, 1 },
[SOUND_MIXER_LINE1] = { 5, 0xa, 0xb, 1, 0x1800, 1 },
[SOUND_MIXER_LINE2] = { 6, 0xc, 0x0, 0, 0x0100, 1 },
[SOUND_MIXER_LINE3] = { 7, 0xd, 0x0, 0, 0x0200, 1 },
[SOUND_MIXER_MIC] = { 8, 0xe, 0x0, 0, 0x0001, 1 },
[SOUND_MIXER_OGAIN] = { 9, 0xf, 0x0, 0, 0x0000, 1 } };
static int
es_mixinit(snd_mixer *m)
{
int i;
u_int32_t v;
v = 0;
for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
if (mixtable[i].avail) v |= (1 << i);
mix_setdevs(m, v);
v = 0;
for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
if (mixtable[i].recmask) v |= (1 << i);
mix_setrecdevs(m, v);
return 0;
}
static int
es_mixset(snd_mixer *m, unsigned dev, unsigned left, unsigned right)
{
int l, r, rl, rr;
if (!mixtable[dev].avail) return -1;
l = left;
r = mixtable[dev].stereo? right : l;
if (mixtable[dev].left == 0xf) {
rl = (l < 2)? 0x80 : 7 - (l - 2) / 14;
} else {
rl = (l < 10)? 0x80 : 15 - (l - 10) / 6;
}
if (mixtable[dev].stereo) {
rr = (r < 10)? 0x80 : 15 - (r - 10) / 6;
write_codec(mix_getdevinfo(m), mixtable[dev].right, rr);
}
write_codec(mix_getdevinfo(m), mixtable[dev].left, rl);
return l | (r << 8);
}
static int
es_mixsetrecsrc(snd_mixer *m, u_int32_t src)
{
int i, j = 0;
if (src == 0) src = 1 << SOUND_MIXER_MIC;
src &= mix_getrecdevs(m);
for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
if ((src & (1 << i)) != 0) j |= mixtable[i].recmask;
write_codec(mix_getdevinfo(m), CODEC_LIMIX1, j & 0x55);
write_codec(mix_getdevinfo(m), CODEC_RIMIX1, j & 0xaa);
write_codec(mix_getdevinfo(m), CODEC_LIMIX2, (j >> 8) & 0x17);
write_codec(mix_getdevinfo(m), CODEC_RIMIX2, (j >> 8) & 0x0f);
write_codec(mix_getdevinfo(m), CODEC_OMIX1, 0x7f);
write_codec(mix_getdevinfo(m), CODEC_OMIX2, 0x3f);
return src;
}
static int
write_codec(struct es_info *es, u_char i, u_char data)
{
int wait = 100; /* 100 msec timeout */
do {
if ((bus_space_read_4(es->st, es->sh, ES1370_REG_STATUS) &
STAT_CSTAT) == 0) {
bus_space_write_2(es->st, es->sh, ES1370_REG_CODEC,
((u_short)i << CODEC_INDEX_SHIFT) | data);
return 0;
}
DELAY(1000);
} while (--wait);
printf("pcm: write_codec timed out\n");
return -1;
}
/* -------------------------------------------------------------------- */
/* channel interface */
static void *
eschan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir)
{
struct es_info *es = devinfo;
struct es_chinfo *ch = (dir == PCMDIR_PLAY)? &es->pch : &es->rch;
ch->parent = es;
ch->channel = c;
ch->buffer = b;
ch->buffer->bufsize = ES_BUFFSIZE;
if (chn_allocbuf(ch->buffer, es->parent_dmat) == -1) return NULL;
return ch;
}
static int
eschan_setdir(void *data, int dir)
{
struct es_chinfo *ch = data;
struct es_info *es = ch->parent;
if (dir == PCMDIR_PLAY) {
bus_space_write_1(es->st, es->sh, ES1370_REG_MEMPAGE,
ES1370_REG_DAC2_FRAMEADR >> 8);
bus_space_write_4(es->st, es->sh, ES1370_REG_DAC2_FRAMEADR & 0xff,
vtophys(ch->buffer->buf));
bus_space_write_4(es->st, es->sh, ES1370_REG_DAC2_FRAMECNT & 0xff,
(ch->buffer->bufsize >> 2) - 1);
} else {
bus_space_write_1(es->st, es->sh, ES1370_REG_MEMPAGE,
ES1370_REG_ADC_FRAMEADR >> 8);
bus_space_write_4(es->st, es->sh, ES1370_REG_ADC_FRAMEADR & 0xff,
vtophys(ch->buffer->buf));
bus_space_write_4(es->st, es->sh, ES1370_REG_ADC_FRAMECNT & 0xff,
(ch->buffer->bufsize >> 2) - 1);
}
ch->dir = dir;
return 0;
}
static int
eschan_setformat(void *data, u_int32_t format)
{
struct es_chinfo *ch = data;
struct es_info *es = ch->parent;
if (ch->dir == PCMDIR_PLAY) {
es->sctrl &= ~SCTRL_P2FMT;
if (format & AFMT_S16_LE) es->sctrl |= SCTRL_P2SEB;
if (format & AFMT_STEREO) es->sctrl |= SCTRL_P2SMB;
} else {
es->sctrl &= ~SCTRL_R1FMT;
if (format & AFMT_S16_LE) es->sctrl |= SCTRL_R1SEB;
if (format & AFMT_STEREO) es->sctrl |= SCTRL_R1SMB;
}
bus_space_write_4(es->st, es->sh, ES1370_REG_SERIAL_CONTROL, es->sctrl);
ch->fmt = format;
return 0;
}
static int
eschan_setspeed(void *data, u_int32_t speed)
{
struct es_chinfo *ch = data;
struct es_info *es = ch->parent;
es->ctrl &= ~CTRL_PCLKDIV;
es->ctrl |= DAC2_SRTODIV(speed) << CTRL_SH_PCLKDIV;
bus_space_write_4(es->st, es->sh, ES1370_REG_CONTROL, es->ctrl);
/* rec/play speeds locked together - should indicate in flags */
#if 0
if (ch->direction == PCMDIR_PLAY) d->rec[0].speed = speed;
else d->play[0].speed = speed;
#endif
return speed; /* XXX calc real speed */
}
static int
eschan_setblocksize(void *data, u_int32_t blocksize)
{
return blocksize;
}
static int
eschan_trigger(void *data, int go)
{
struct es_chinfo *ch = data;
struct es_info *es = ch->parent;
unsigned cnt = ch->buffer->dl / ch->buffer->sample_size - 1;
if (ch->dir == PCMDIR_PLAY) {
if (go == PCMTRIG_START) {
int b = (ch->fmt & AFMT_S16_LE)? 2 : 1;
es->ctrl |= CTRL_DAC2_EN;
es->sctrl &= ~(SCTRL_P2ENDINC | SCTRL_P2STINC |
SCTRL_P2LOOPSEL | SCTRL_P2PAUSE |
SCTRL_P2DACSEN);
es->sctrl |= SCTRL_P2INTEN | (b << SCTRL_SH_P2ENDINC);
bus_space_write_4(es->st, es->sh,
ES1370_REG_DAC2_SCOUNT, cnt);
} else es->ctrl &= ~CTRL_DAC2_EN;
} else {
if (go == PCMTRIG_START) {
es->ctrl |= CTRL_ADC_EN;
es->sctrl &= ~SCTRL_R1LOOPSEL;
es->sctrl |= SCTRL_R1INTEN;
bus_space_write_4(es->st, es->sh,
ES1370_REG_ADC_SCOUNT, cnt);
} else es->ctrl &= ~CTRL_ADC_EN;
}
bus_space_write_4(es->st, es->sh, ES1370_REG_SERIAL_CONTROL, es->sctrl);
bus_space_write_4(es->st, es->sh, ES1370_REG_CONTROL, es->ctrl);
return 0;
}
static int
eschan_getptr(void *data)
{
struct es_chinfo *ch = data;
struct es_info *es = ch->parent;
if (ch->dir == PCMDIR_PLAY) {
bus_space_write_4(es->st, es->sh, ES1370_REG_MEMPAGE,
ES1370_REG_DAC2_FRAMECNT >> 8);
return (bus_space_read_4(es->st, es->sh,
ES1370_REG_DAC2_FRAMECNT & 0xff) >> 14) & 0x3fffc;
} else {
bus_space_write_4(es->st, es->sh, ES1370_REG_MEMPAGE,
ES1370_REG_ADC_FRAMECNT >> 8);
return (bus_space_read_4(es->st, es->sh,
ES1370_REG_ADC_FRAMECNT & 0xff) >> 14) & 0x3fffc;
}
}
static pcmchan_caps *
eschan_getcaps(void *data)
{
struct es_chinfo *ch = data;
return (ch->dir == PCMDIR_PLAY)? &es_playcaps : &es_reccaps;
}
/* The interrupt handler */
static void
es_intr (void *p)
{
struct es_info *es = p;
unsigned intsrc, sctrl;
intsrc = bus_space_read_4(es->st, es->sh, ES1370_REG_STATUS);
if ((intsrc & STAT_INTR) == 0) return;
sctrl = es->sctrl;
if (intsrc & STAT_ADC) sctrl &= ~SCTRL_R1INTEN;
if (intsrc & STAT_DAC1) sctrl &= ~SCTRL_P1INTEN;
if (intsrc & STAT_DAC2) sctrl &= ~SCTRL_P2INTEN;
bus_space_write_4(es->st, es->sh, ES1370_REG_SERIAL_CONTROL, sctrl);
bus_space_write_4(es->st, es->sh, ES1370_REG_SERIAL_CONTROL, es->sctrl);
if (intsrc & STAT_DAC2) chn_intr(es->pch.channel);
if (intsrc & STAT_ADC) chn_intr(es->rch.channel);
}
/* ES1371 specific code */
#define CODEC_ID_SESHIFT 10
#define CODEC_ID_SEMASK 0x1f
#define CODEC_PIRD 0x00800000 /* 0 = write AC97 register */
#define CODEC_PIADD_MASK 0x007f0000
#define CODEC_PIADD_SHIFT 16
#define CODEC_PIDAT_MASK 0x0000ffff
#define CODEC_PIDAT_SHIFT 0
#define CODEC_PORD 0x00800000 /* 0 = write AC97 register */
#define CODEC_POADD_MASK 0x007f0000
#define CODEC_POADD_SHIFT 16
#define CODEC_PODAT_MASK 0x0000ffff
#define CODEC_PODAT_SHIFT 0
#define CODEC_RDY 0x80000000 /* AC97 read data valid */
#define CODEC_WIP 0x40000000 /* AC97 write in progress */
#define ES1370_REG_CONTROL 0x00
#define ES1370_REG_SERIAL_CONTROL 0x20
#define ES1371_REG_CODEC 0x14
#define ES1371_REG_LEGACY 0x18 /* W/R: Legacy control/status register */
#define ES1371_REG_SMPRATE 0x10 /* W/R: Codec rate converter interface register */
#define ES1371_SYNC_RES (1<<14) /* Warm AC97 reset */
#define ES1371_DIS_R1 (1<<19) /* record channel accumulator update disable */
#define ES1371_DIS_P2 (1<<20) /* playback channel 2 accumulator update disable */
#define ES1371_DIS_P1 (1<<21) /* playback channel 1 accumulator update disable */
#define ES1371_DIS_SRC (1<<22) /* sample rate converter disable */
#define ES1371_SRC_RAM_BUSY (1<<23) /* R/O: sample rate memory is busy */
#define ES1371_SRC_RAM_WE (1<<24) /* R/W: read/write control for sample rate converter */
#define ES1371_SRC_RAM_ADDRO(o) (((o)&0x7f)<<25) /* address of the sample rate converter */
#define ES1371_SRC_RAM_DATAO(o) (((o)&0xffff)<<0) /* current value of the sample rate converter */
#define ES1371_SRC_RAM_DATAI(i) (((i)>>0)&0xffff) /* current value of the sample rate converter */
/*
* Sample rate converter addresses
*/
#define ES_SMPREG_DAC1 0x70
#define ES_SMPREG_DAC2 0x74
#define ES_SMPREG_ADC 0x78
#define ES_SMPREG_TRUNC_N 0x00
#define ES_SMPREG_INT_REGS 0x01
#define ES_SMPREG_VFREQ_FRAC 0x03
#define ES_SMPREG_VOL_ADC 0x6c
#define ES_SMPREG_VOL_DAC1 0x7c
#define ES_SMPREG_VOL_DAC2 0x7e
int
es1371_init(struct es_info *es)
{
int idx;
if(debug > 0) printf("es_init\n");
es->ctrl = 0;
es->sctrl = 0;
/* initialize the chips */
bus_space_write_4(es->st, es->sh, ES1370_REG_CONTROL, es->ctrl);
bus_space_write_4(es->st, es->sh, ES1370_REG_SERIAL_CONTROL, es->sctrl);
bus_space_write_4(es->st, es->sh, ES1371_REG_LEGACY, 0);
/* AC'97 warm reset to start the bitclk */
bus_space_write_4(es->st, es->sh, ES1371_REG_LEGACY, es->ctrl | ES1371_SYNC_RES);
DELAY(2000);
bus_space_write_4(es->st, es->sh, ES1370_REG_SERIAL_CONTROL,es->ctrl);
/* Init the sample rate converter */
bus_space_write_4(es->st, es->sh, ES1371_REG_SMPRATE, ES1371_DIS_SRC);
for (idx = 0; idx < 0x80; idx++)
es1371_src_write(es, idx, 0);
es1371_src_write(es, ES_SMPREG_DAC1 + ES_SMPREG_TRUNC_N, 16 << 4);
es1371_src_write(es, ES_SMPREG_DAC1 + ES_SMPREG_INT_REGS, 16 << 10);
es1371_src_write(es, ES_SMPREG_DAC2 + ES_SMPREG_TRUNC_N, 16 << 4);
es1371_src_write(es, ES_SMPREG_DAC2 + ES_SMPREG_INT_REGS, 16 << 10);
es1371_src_write(es, ES_SMPREG_VOL_ADC, 1 << 12);
es1371_src_write(es, ES_SMPREG_VOL_ADC + 1, 1 << 12);
es1371_src_write(es, ES_SMPREG_VOL_DAC1, 1 << 12);
es1371_src_write(es, ES_SMPREG_VOL_DAC1 + 1, 1 << 12);
es1371_src_write(es, ES_SMPREG_VOL_DAC2, 1 << 12);
es1371_src_write(es, ES_SMPREG_VOL_DAC2 + 1, 1 << 12);
es1371_adc_rate (es, 22050, 1);
es1371_dac1_rate(es, 22050, 1);
es1371_dac2_rate(es, 22050, 1);
/* WARNING:
* enabling the sample rate converter without properly programming
* its parameters causes the chip to lock up (the SRC busy bit will
* be stuck high, and I've found no way to rectify this other than
* power cycle)
*/
bus_space_write_4(es->st, es->sh, ES1371_REG_SMPRATE, 0);
return (0);
}
void
es1371_wrcodec(void *s, int addr, u_int32_t data)
{
/* unsigned long flags; */
int sl;
unsigned t, x;
struct es_info *es = (struct es_info*)s;
if(debug > 0) printf("wrcodec addr 0x%x data 0x%x\n",addr,data);
for (t = 0; t < 0x1000; t++)
if(!(bus_space_read_4(es->st, es->sh,(ES1371_REG_CODEC & CODEC_WIP))))
break;
sl = spltty();
/* save the current state for later */
x = bus_space_read_4(es->st, es->sh, ES1371_REG_SMPRATE);
/* enable SRC state data in SRC mux */
bus_space_write_4(es->st, es->sh, ES1371_REG_SMPRATE,
(es1371_wait_src_ready(s) &
(ES1371_DIS_SRC | ES1371_DIS_P1 | ES1371_DIS_P2 | ES1371_DIS_R1)));
/* wait for a SAFE time to write addr/data and then do it, dammit */
for (t = 0; t < 0x1000; t++)
if (( bus_space_read_4(es->st, es->sh, ES1371_REG_SMPRATE) & 0x00070000) == 0x00010000)
break;
if(debug > 2) printf("one b_s_w: 0x%x 0x%x 0x%x\n",es->sh,ES1371_REG_CODEC,
((addr << CODEC_POADD_SHIFT) & CODEC_POADD_MASK) |
((data << CODEC_PODAT_SHIFT) & CODEC_PODAT_MASK));
bus_space_write_4(es->st, es->sh,ES1371_REG_CODEC,
((addr << CODEC_POADD_SHIFT) & CODEC_POADD_MASK) |
((data << CODEC_PODAT_SHIFT) & CODEC_PODAT_MASK));
/* restore SRC reg */
es1371_wait_src_ready(s);
if(debug > 2) printf("two b_s_w: 0x%x 0x%x 0x%x\n",es->sh,ES1371_REG_SMPRATE,x);
bus_space_write_4(es->st, es->sh,ES1371_REG_SMPRATE,x);
splx(sl);
}
u_int32_t
es1371_rdcodec(void *s, u_int32_t addr)
{
/* unsigned long flags; */
int sl;
unsigned t, x;
struct es_info *es = (struct es_info *)s;
if(debug > 0) printf("rdcodec addr 0x%x ... ",addr);
for (t = 0; t < 0x1000; t++)
if (!(x = bus_space_read_4(es->st,es->sh,ES1371_REG_CODEC) & CODEC_WIP))
break;
if(debug >0) printf("loop 1 t 0x%x x 0x%x ",t,x);
sl = spltty();
/* save the current state for later */
x = bus_space_read_4(es->st, es->sh, ES1371_REG_SMPRATE);
/* enable SRC state data in SRC mux */
bus_space_write_4(es->st, es->sh, ES1371_REG_SMPRATE,
(es1371_wait_src_ready(s) &
(ES1371_DIS_SRC | ES1371_DIS_P1 | ES1371_DIS_P2 | ES1371_DIS_R1)));
/* wait for a SAFE time to write addr/data and then do it, dammit */
for (t = 0; t < 0x5000; t++)
if (( x = bus_space_read_4(es->st, es->sh, ES1371_REG_SMPRATE) & 0x00070000) == 0x00010000)
break;
if(debug >0) printf("loop 2 t 0x%x x 0x%x ",t,x);
bus_space_write_4(es->st, es->sh,ES1371_REG_CODEC,
((addr << CODEC_POADD_SHIFT) & CODEC_POADD_MASK) | CODEC_PORD);
/* restore SRC reg */
es1371_wait_src_ready(s);
bus_space_write_4(es->st,es->sh,ES1371_REG_SMPRATE,x);
splx(sl);
/* now wait for the stinkin' data (RDY) */
for (t = 0; t < 0x1000; t++)
if ((x = bus_space_read_4(es->st,es->sh,ES1371_REG_CODEC)) & CODEC_RDY)
break;
if(debug > 0) printf("loop 3 t 0x%x 0x%x ret 0x%x\n",t,x,((x & CODEC_PIDAT_MASK) >> CODEC_PIDAT_SHIFT));
return ((x & CODEC_PIDAT_MASK) >> CODEC_PIDAT_SHIFT);
}
static u_int
es1371_src_read(es_info_t *es, u_short reg){
unsigned int r;
r = es1371_wait_src_ready(es) &
(ES1371_DIS_SRC | ES1371_DIS_P1 | ES1371_DIS_P2 | ES1371_DIS_R1);
r |= ES1371_SRC_RAM_ADDRO(reg);
bus_space_write_4(es->st, es->sh,ES1371_REG_SMPRATE,r);
return ES1371_SRC_RAM_DATAI(es1371_wait_src_ready(es));
}
static void
es1371_src_write(es_info_t *es, u_short reg, u_short data){
u_int r;
r = es1371_wait_src_ready(es) &
(ES1371_DIS_SRC | ES1371_DIS_P1 | ES1371_DIS_P2 | ES1371_DIS_R1);
r |= ES1371_SRC_RAM_ADDRO(reg) | ES1371_SRC_RAM_DATAO(data);
/* printf("es1371_src_write 0x%x 0x%x\n",ES1371_REG_SMPRATE,r | ES1371_SRC_RAM_WE); */
bus_space_write_4(es->st, es->sh,ES1371_REG_SMPRATE,r | ES1371_SRC_RAM_WE);
}
static u_int
es1371_adc_rate(es_info_t *es, u_int rate, int set){
u_int n, truncm, freq, result;
if (rate > 48000)
rate = 48000;
if (rate < 4000)
rate = 4000;
n = rate / 3000;
if ((1 << n) & ((1 << 15) | (1 << 13) | (1 << 11) | (1 << 9)))
n--;
truncm = (21 * n - 1) | 1;
freq = ((48000UL << 15) / rate) * n;
result = (48000UL << 15) / (freq / n);
if (set) {
if (rate >= 24000) {
if (truncm > 239)
truncm = 239;
es1371_src_write(es, ES_SMPREG_ADC + ES_SMPREG_TRUNC_N,
(((239 - truncm) >> 1) << 9) | (n << 4));
} else {
if (truncm > 119)
truncm = 119;
es1371_src_write(es, ES_SMPREG_ADC + ES_SMPREG_TRUNC_N,
0x8000 | (((119 - truncm) >> 1) << 9) | (n << 4));
}
es1371_src_write(es, ES_SMPREG_ADC + ES_SMPREG_INT_REGS,
(es1371_src_read(es, ES_SMPREG_ADC + ES_SMPREG_INT_REGS) &
0x00ff) | ((freq >> 5) & 0xfc00));
es1371_src_write(es, ES_SMPREG_ADC + ES_SMPREG_VFREQ_FRAC, freq & 0x7fff);
es1371_src_write(es, ES_SMPREG_VOL_ADC, n << 8);
es1371_src_write(es, ES_SMPREG_VOL_ADC + 1, n << 8);
}
return result;
}
static u_int
es1371_dac1_rate(es_info_t *es, u_int rate, int set){
u_int freq, r, result;
if (rate > 48000)
rate = 48000;
if (rate < 4000)
rate = 4000;
freq = (rate << 15) / 3000;
result = (freq * 3000) >> 15;
if (set) {
r = (es1371_wait_src_ready(es) & (ES1371_DIS_SRC | ES1371_DIS_P1 | ES1371_DIS_P2 | ES1371_DIS_R1));
bus_space_write_4(es->st, es->sh,ES1371_REG_SMPRATE,r);
es1371_src_write(es, ES_SMPREG_DAC1 +
ES_SMPREG_INT_REGS,
(es1371_src_read(es,
ES_SMPREG_DAC1 + ES_SMPREG_INT_REGS) & 0x00ff) | ((freq >> 5) & 0xfc00));
es1371_src_write(es, ES_SMPREG_DAC1 + ES_SMPREG_VFREQ_FRAC, freq & 0x7fff);
r = (es1371_wait_src_ready(es) & (ES1371_DIS_SRC | ES1371_DIS_P2 | ES1371_DIS_R1));
bus_space_write_4(es->st, es->sh,ES1371_REG_SMPRATE,r);
}
return result;
}
static u_int
es1371_dac2_rate(es_info_t *es, u_int rate, int set){
u_int freq, r, result;
if (rate > 48000)
rate = 48000;
if (rate < 4000)
rate = 4000;
freq = (rate << 15) / 3000;
result = (freq * 3000) >> 15;
if (set) {
r = (es1371_wait_src_ready(es) & (ES1371_DIS_SRC | ES1371_DIS_P1 | ES1371_DIS_P2 | ES1371_DIS_R1));
bus_space_write_4(es->st, es->sh,ES1371_REG_SMPRATE,r);
/* if(debug > 0) printf("dac2_rate 0x%x\n",bus_space_read_4(es->st, es->sh,ES1371_REG_SMPRATE)); */
es1371_src_write(es, ES_SMPREG_DAC2 + ES_SMPREG_INT_REGS,
(es1371_src_read(es, ES_SMPREG_DAC2 + ES_SMPREG_INT_REGS) &
0x00ff) | ((freq >> 5) & 0xfc00));
es1371_src_write(es, ES_SMPREG_DAC2 + ES_SMPREG_VFREQ_FRAC, freq & 0x7fff);
r = (es1371_wait_src_ready(es) & (ES1371_DIS_SRC | ES1371_DIS_P1 | ES1371_DIS_R1));
bus_space_write_4(es->st, es->sh,ES1371_REG_SMPRATE,r);
/* if(debug > 0) printf("dac2_rate 0x%x\n",bus_space_read_4(es->st, es->sh,ES1371_REG_SMPRATE)); */
}
return result;
}
static u_int
es1371_wait_src_ready(es_info_t *es){
u_int t, r;
for (t = 0; t < 500; t++) {
if (!((r = bus_space_read_4(es->st, es->sh,ES1371_REG_SMPRATE)) & ES1371_SRC_RAM_BUSY)){
return r;
}
DELAY(1000);
}
printf("es1371: wait source ready timeout 0x%x [0x%x]\n", ES1371_REG_SMPRATE, r);
return 0;
}
int
eschan1371_setspeed(void *data, u_int32_t speed)
{
struct es_chinfo *ch = data;
struct es_info *es = ch->parent;
/* rec/play speeds locked together - should indicate in flags */
es1371_dac2_rate(es, speed, 1); /* play */
es1371_adc_rate (es, speed, 1); /* record */
return speed; /* XXX calc real speed */
}
/* -------------------------------------------------------------------- */
/*
* Probe and attach the card
*/
static int
es_init(struct es_info *es)
{
es->ctrl = CTRL_CDC_EN | CTRL_SERR_DIS |
(DAC2_SRTODIV(DSP_DEFAULT_SPEED) << CTRL_SH_PCLKDIV);
bus_space_write_4(es->st, es->sh, ES1370_REG_CONTROL, es->ctrl);
es->sctrl = 0;
bus_space_write_4(es->st, es->sh, ES1370_REG_SERIAL_CONTROL, es->sctrl);
write_codec(es, CODEC_RES_PD, 3);/* No RST, PD */
write_codec(es, CODEC_CSEL, 0); /* CODEC ADC and CODEC DAC use
* {LR,B}CLK2 and run off the LRCLK2
* PLL; program DAC_SYNC=0! */
write_codec(es, CODEC_ADSEL, 0);/* Recording source is mixer */
write_codec(es, CODEC_MGAIN, 0);/* MIC amp is 0db */
return 0;
}
static int
es_pci_probe(device_t dev)
{
if (pci_get_devid(dev) == ES1370_PCI_ID) {
device_set_desc(dev, "AudioPCI ES1370");
return 0;
} else if (pci_get_devid(dev) == ES1371_PCI_ID) {
device_set_desc(dev, "AudioPCI ES1371");
return 0;
}
return ENXIO;
}
static int
es_pci_attach(device_t dev)
{
snddev_info *d;
u_int32_t data;
struct es_info *es = 0;
int type = 0;
int regid;
struct resource *reg = 0;
int mapped;
int irqid;
struct resource *irq = 0;
void *ih = 0;
char status[SND_STATUSLEN];
struct ac97_info *codec;
d = device_get_softc(dev);
if ((es = malloc(sizeof *es, M_DEVBUF, M_NOWAIT)) == NULL) {
device_printf(dev, "cannot allocate softc\n");
return ENXIO;
}
bzero(es, sizeof *es);
mapped = 0;
data = pci_read_config(dev, PCIR_COMMAND, 2);
if (mapped == 0 && (data & PCIM_CMD_MEMEN)) {
regid = MEM_MAP_REG;
type = SYS_RES_MEMORY;
reg = bus_alloc_resource(dev, type, &regid,
0, ~0, 1, RF_ACTIVE);
if (reg) {
es->st = rman_get_bustag(reg);
es->sh = rman_get_bushandle(reg);
mapped++;
}
}
if (mapped == 0 && (data & PCIM_CMD_PORTEN)) {
regid = PCI_MAP_REG_START;
type = SYS_RES_IOPORT;
reg = bus_alloc_resource(dev, type, &regid,
0, ~0, 1, RF_ACTIVE);
if (reg) {
es->st = rman_get_bustag(reg);
es->sh = rman_get_bushandle(reg);
mapped++;
}
}
if (mapped == 0) {
device_printf(dev, "unable to map register space\n");
goto bad;
}
if (pci_get_devid(dev) == ES1371_PCI_ID) {
if(-1 == es1371_init(es)){
device_printf(dev, "unable to initialize the card\n");
goto bad;
}
codec = ac97_create(es,(ac97_read *)es1371_rdcodec,(ac97_write *)es1371_wrcodec);
if (codec == NULL) goto bad;
/* our init routine does everything for us */
/* set to NULL; flag mixer_init not to run the ac97_init */
/* ac97_mixer.init = NULL; */
mixer_init(d, &ac97_mixer, codec);
/* change the routine for setting speed */
es_chantemplate.setspeed = eschan1371_setspeed;
} else if (pci_get_devid(dev) == ES1370_PCI_ID) {
if (-1 == es_init(es)){
device_printf(dev, "unable to initialize the card\n");
goto bad;
}
mixer_init(d, &es_mixer, es);
}
irqid = 0;
irq = bus_alloc_resource(dev, SYS_RES_IRQ, &irqid,
0, ~0, 1, RF_ACTIVE | RF_SHAREABLE);
if (!irq
|| bus_setup_intr(dev, irq, INTR_TYPE_TTY, es_intr, es, &ih)) {
device_printf(dev, "unable to map interrupt\n");
goto bad;
}
if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0,
/*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
/*highaddr*/BUS_SPACE_MAXADDR,
/*filter*/NULL, /*filterarg*/NULL,
/*maxsize*/ES_BUFFSIZE, /*nsegments*/1, /*maxsegz*/0x3ffff,
/*flags*/0, &es->parent_dmat) != 0) {
device_printf(dev, "unable to create dma tag\n");
goto bad;
}
snprintf(status, SND_STATUSLEN, "at %s 0x%lx irq %ld",
(type == SYS_RES_IOPORT)? "io" : "memory",
rman_get_start(reg), rman_get_start(irq));
if (pcm_register(dev, es, 1, 1)) goto bad;
pcm_addchan(dev, PCMDIR_REC, &es_chantemplate, es);
pcm_addchan(dev, PCMDIR_PLAY, &es_chantemplate, es);
pcm_setstatus(dev, status);
return 0;
bad:
if (es) free(es, M_DEVBUF);
if (reg) bus_release_resource(dev, type, regid, reg);
if (ih) bus_teardown_intr(dev, irq, ih);
if (irq) bus_release_resource(dev, SYS_RES_IRQ, irqid, irq);
return ENXIO;
}
static device_method_t es_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, es_pci_probe),
DEVMETHOD(device_attach, es_pci_attach),
{ 0, 0 }
};
static driver_t es_driver = {
"pcm",
es_methods,
sizeof(snddev_info),
};
static devclass_t pcm_devclass;
DRIVER_MODULE(es, pci, es_driver, pcm_devclass, 0, 0);
#endif /* NPCI != 0 */

View file

@ -1,134 +0,0 @@
/*
* This supports the ENSONIQ AudioPCI board based on the ES1370.
*
* Copyright (c) 1998 Joachim Kuebart <joki@kuebart.stuttgart.netsurf.de>
* All rights reserved.
*
* 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 immediately at the beginning of the file, without modification,
* 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.
* 3. Absolutely no warranty of function or purpose is made by the author
* Joachim Kuebart.
* 4. Modifications may be freely made to this file if the above conditions
* are met.
*
* $FreeBSD$
*/
#ifndef _ES1370_REG_H
#define _ES1370_REG_H
#define ES1370_REG_CONTROL 0x00
#define ES1370_REG_STATUS 0x04
#define ES1370_REG_UART_DATA 0x08
#define ES1370_REG_UART_STATUS 0x09
#define ES1370_REG_UART_CONTROL 0x09
#define ES1370_REG_UART_TEST 0x0a
#define ES1370_REG_MEMPAGE 0x0c
#define ES1370_REG_CODEC 0x10
#define CODEC_INDEX_SHIFT 8
#define ES1370_REG_SERIAL_CONTROL 0x20
#define ES1370_REG_DAC1_SCOUNT 0x24
#define ES1370_REG_DAC2_SCOUNT 0x28
#define ES1370_REG_ADC_SCOUNT 0x2c
#define ES1370_REG_DAC1_FRAMEADR 0xc30
#define ES1370_REG_DAC1_FRAMECNT 0xc34
#define ES1370_REG_DAC2_FRAMEADR 0xc38
#define ES1370_REG_DAC2_FRAMECNT 0xc3c
#define ES1370_REG_ADC_FRAMEADR 0xd30
#define ES1370_REG_ADC_FRAMECNT 0xd34
#define DAC2_SRTODIV(x) (((1411200 + (x) / 2) / (x) - 2) & 0x1fff)
#define DAC2_DIVTOSR(x) (1411200 / ((x) + 2))
#define CTRL_ADC_STOP 0x80000000 /* 1 = ADC stopped */
#define CTRL_XCTL1 0x40000000 /* SERR pin if enabled */
#define CTRL_OPEN 0x20000000 /* no function, can be read and
* written */
#define CTRL_PCLKDIV 0x1fff0000 /* ADC/DAC2 clock divider */
#define CTRL_SH_PCLKDIV 16
#define CTRL_MSFMTSEL 0x00008000 /* MPEG serial data fmt: 0 = Sony, 1
* = I2S */
#define CTRL_M_SBB 0x00004000 /* DAC2 clock: 0 = PCLKDIV, 1 = MPEG */
#define CTRL_WTSRSEL 0x00003000 /* DAC1 clock freq: 0=5512, 1=11025,
* 2=22050, 3=44100 */
#define CTRL_SH_WTSRSEL 12
#define CTRL_DAC_SYNC 0x00000800 /* 1 = DAC2 runs off DAC1 clock */
#define CTRL_CCB_INTRM 0x00000400 /* 1 = CCB "voice" ints enabled */
#define CTRL_M_CB 0x00000200 /* recording source: 0 = ADC, 1 =
* MPEG */
#define CTRL_XCTL0 0x00000100 /* 0 = Line in, 1 = Line out */
#define CTRL_BREQ 0x00000080 /* 1 = test mode (internal mem test) */
#define CTRL_DAC1_EN 0x00000040 /* enable DAC1 */
#define CTRL_DAC2_EN 0x00000020 /* enable DAC2 */
#define CTRL_ADC_EN 0x00000010 /* enable ADC */
#define CTRL_UART_EN 0x00000008 /* enable MIDI uart */
#define CTRL_JYSTK_EN 0x00000004 /* enable Joystick port (presumably
* at address 0x200) */
#define CTRL_CDC_EN 0x00000002 /* enable serial (CODEC) interface */
#define CTRL_SERR_DIS 0x00000001 /* 1 = disable PCI SERR signal */
#define SCTRL_P2ENDINC 0x00380000 /* */
#define SCTRL_SH_P2ENDINC 19
#define SCTRL_P2STINC 0x00070000 /* */
#define SCTRL_SH_P2STINC 16
#define SCTRL_R1LOOPSEL 0x00008000 /* 0 = loop mode */
#define SCTRL_P2LOOPSEL 0x00004000 /* 0 = loop mode */
#define SCTRL_P1LOOPSEL 0x00002000 /* 0 = loop mode */
#define SCTRL_P2PAUSE 0x00001000 /* 1 = pause mode */
#define SCTRL_P1PAUSE 0x00000800 /* 1 = pause mode */
#define SCTRL_R1INTEN 0x00000400 /* enable interrupt */
#define SCTRL_P2INTEN 0x00000200 /* enable interrupt */
#define SCTRL_P1INTEN 0x00000100 /* enable interrupt */
#define SCTRL_P1SCTRLD 0x00000080 /* reload sample count register for
* DAC1 */
#define SCTRL_P2DACSEN 0x00000040 /* 1 = DAC2 play back last sample
* when disabled */
#define SCTRL_R1SEB 0x00000020 /* 1 = 16bit */
#define SCTRL_R1SMB 0x00000010 /* 1 = stereo */
#define SCTRL_R1FMT 0x00000030 /* format mask */
#define SCTRL_SH_R1FMT 4
#define SCTRL_P2SEB 0x00000008 /* 1 = 16bit */
#define SCTRL_P2SMB 0x00000004 /* 1 = stereo */
#define SCTRL_P2FMT 0x0000000c /* format mask */
#define SCTRL_SH_P2FMT 2
#define SCTRL_P1SEB 0x00000002 /* 1 = 16bit */
#define SCTRL_P1SMB 0x00000001 /* 1 = stereo */
#define SCTRL_P1FMT 0x00000003 /* format mask */
#define SCTRL_SH_P1FMT 0
#define STAT_INTR 0x80000000 /* wired or of all interrupt bits */
#define STAT_CSTAT 0x00000400 /* 1 = codec busy or codec write in
* progress */
#define STAT_CBUSY 0x00000200 /* 1 = codec busy */
#define STAT_CWRIP 0x00000100 /* 1 = codec write in progress */
#define STAT_VC 0x00000060 /* CCB int source, 0=DAC1, 1=DAC2,
* 2=ADC, 3=undef */
#define STAT_SH_VC 5
#define STAT_MCCB 0x00000010 /* CCB int pending */
#define STAT_UART 0x00000008 /* UART int pending */
#define STAT_DAC1 0x00000004 /* DAC1 int pending */
#define STAT_DAC2 0x00000002 /* DAC2 int pending */
#define STAT_ADC 0x00000001 /* ADC int pending */
#define CODEC_OMIX1 0x10
#define CODEC_OMIX2 0x11
#define CODEC_LIMIX1 0x12
#define CODEC_RIMIX1 0x13
#define CODEC_LIMIX2 0x14
#define CODEC_RIMIX2 0x15
#define CODEC_RES_PD 0x16
#define CODEC_CSEL 0x17
#define CODEC_ADSEL 0x18
#define CODEC_MGAIN 0x19
#define ES_BUFFSIZE 0x20000 /* We're PCI! Use a large buffer */
#endif

View file

@ -1,688 +0,0 @@
/*
* Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
* All rights reserved.
*
* 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, WHETHERIN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#include "pci.h"
#include "pcm.h"
#include <dev/pcm/sound.h>
#include <dev/pcm/ac97.h>
#include <dev/pcm/pci/t4dwave.h>
#include <pci/pcireg.h>
#include <pci/pcivar.h>
#if NPCI != 0
/* -------------------------------------------------------------------- */
struct tr_info;
/* channel registers */
struct tr_chinfo {
u_int32_t cso, alpha, fms, fmc, ec;
u_int32_t lba;
u_int32_t eso, delta;
u_int32_t rvol, cvol;
u_int32_t gvsel, pan, vol, ctrl;
int index;
snd_dbuf *buffer;
pcm_channel *channel;
struct tr_info *parent;
};
/* device private data */
struct tr_info {
u_int32_t type;
bus_space_tag_t st;
bus_space_handle_t sh;
bus_dma_tag_t parent_dmat;
struct resource *reg, *irq;
int regtype, regid, irqid;
void *ih;
u_int32_t playchns;
struct tr_chinfo chinfo[TR_MAXPLAYCH];
struct tr_chinfo recchinfo;
};
/* -------------------------------------------------------------------- */
/*
* prototypes
*/
/* channel interface */
static void *trchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir);
static int trchan_setdir(void *data, int dir);
static int trchan_setformat(void *data, u_int32_t format);
static int trchan_setspeed(void *data, u_int32_t speed);
static int trchan_setblocksize(void *data, u_int32_t blocksize);
static int trchan_trigger(void *data, int go);
static int trchan_getptr(void *data);
static pcmchan_caps *trchan_getcaps(void *data);
/* talk to the codec - called from ac97.c */
static u_int32_t tr_rdcd(void *, int);
static void tr_wrcd(void *, int, u_int32_t);
/* stuff */
static int tr_init(struct tr_info *);
static void tr_intr(void *);
/* talk to the card */
static u_int32_t tr_rd(struct tr_info *, int, int);
static void tr_wr(struct tr_info *, int, u_int32_t, int);
/* manipulate playback channels */
static void tr_clrint(struct tr_info *, char);
static void tr_enaint(struct tr_info *, char, int);
static u_int32_t tr_testint(struct tr_info *, char);
static void tr_rdch(struct tr_info *, char, struct tr_chinfo *);
static void tr_wrch(struct tr_info *, char, struct tr_chinfo *);
static void tr_selch(struct tr_info *, char);
static void tr_startch(struct tr_info *, char);
static void tr_stopch(struct tr_info *, char);
/* -------------------------------------------------------------------- */
static pcmchan_caps tr_reccaps = {
4000, 48000,
AFMT_STEREO | AFMT_U8 | AFMT_S8 | AFMT_S16_LE | AFMT_U16_LE,
AFMT_STEREO | AFMT_S16_LE
};
static pcmchan_caps tr_playcaps = {
4000, 48000,
AFMT_STEREO | AFMT_U8 | AFMT_S8 | AFMT_S16_LE | AFMT_U16_LE,
AFMT_U16_LE
};
static pcm_channel tr_chantemplate = {
trchan_init,
trchan_setdir,
trchan_setformat,
trchan_setspeed,
trchan_setblocksize,
trchan_trigger,
trchan_getptr,
trchan_getcaps,
};
/* -------------------------------------------------------------------- */
static u_int32_t
tr_fmttobits(u_int32_t fmt)
{
u_int32_t bits = 0;
bits |= (fmt & AFMT_STEREO)? 0x4 : 0;
bits |= (fmt & (AFMT_S8 | AFMT_S16_LE))? 0x2 : 0;
bits |= (fmt & (AFMT_S16_LE | AFMT_U16_LE))? 0x8 : 0;
return bits;
}
/* Hardware */
static u_int32_t
tr_rd(struct tr_info *tr, int regno, int size)
{
switch(size) {
case 1:
return bus_space_read_1(tr->st, tr->sh, regno);
case 2:
return bus_space_read_2(tr->st, tr->sh, regno);
case 4:
return bus_space_read_4(tr->st, tr->sh, regno);
default:
return 0xffffffff;
}
}
static void
tr_wr(struct tr_info *tr, int regno, u_int32_t data, int size)
{
switch(size) {
case 1:
bus_space_write_1(tr->st, tr->sh, regno, data);
break;
case 2:
bus_space_write_2(tr->st, tr->sh, regno, data);
break;
case 4:
bus_space_write_4(tr->st, tr->sh, regno, data);
break;
}
}
/* ac97 codec */
static u_int32_t
tr_rdcd(void *devinfo, int regno)
{
struct tr_info *tr = (struct tr_info *)devinfo;
int i, j, treg, trw;
switch (tr->type) {
case TDX_PCI_ID:
treg=TDX_REG_CODECRD;
trw=TDX_CDC_RWSTAT;
break;
case TNX_PCI_ID:
treg=(regno & 0x100)? TNX_REG_CODEC2RD : TNX_REG_CODEC1RD;
trw=TNX_CDC_RWSTAT;
break;
default:
printf("!!! tr_rdcd defaulted !!!\n");
return 0xffffffff;
}
regno &= 0x7f;
tr_wr(tr, treg, regno | trw, 4);
j=trw;
for (i=TR_TIMEOUT_CDC; (i > 0) && (j & trw); i--) j=tr_rd(tr, treg, 4);
if (i == 0) printf("codec timeout during read of register %x\n", regno);
return (j >> TR_CDC_DATA) & 0xffff;
}
static void
tr_wrcd(void *devinfo, int regno, u_int32_t data)
{
struct tr_info *tr = (struct tr_info *)devinfo;
int i, j, treg, trw;
switch (tr->type) {
case TDX_PCI_ID:
treg=TDX_REG_CODECWR;
trw=TDX_CDC_RWSTAT;
break;
case TNX_PCI_ID:
treg=TNX_REG_CODECWR;
trw=TNX_CDC_RWSTAT | ((regno & 0x100)? TNX_CDC_SEC : 0);
break;
default:
printf("!!! tr_wrcd defaulted !!!");
return;
}
regno &= 0x7f;
#if 0
printf("tr_wrcd: reg %x was %x", regno, tr_rdcd(devinfo, regno));
#endif
j=trw;
for (i=TR_TIMEOUT_CDC; (i>0) && (j & trw); i--) j=tr_rd(tr, treg, 4);
tr_wr(tr, treg, (data << TR_CDC_DATA) | regno | trw, 4);
#if 0
printf(" - wrote %x, now %x\n", data, tr_rdcd(devinfo, regno));
#endif
if (i==0) printf("codec timeout writing %x, data %x\n", regno, data);
}
/* playback channel interrupts */
static u_int32_t
tr_testint(struct tr_info *tr, char channel)
{
return tr_rd(tr, (channel & 0x20)? TR_REG_ADDRINTB : TR_REG_ADDRINTA,
4) & (1<<(channel & 0x1f));
}
static void
tr_clrint(struct tr_info *tr, char channel)
{
tr_wr(tr, (channel & 0x20)? TR_REG_ADDRINTB : TR_REG_ADDRINTA,
1<<(channel & 0x1f), 4);
}
static void
tr_enaint(struct tr_info *tr, char channel, int enable)
{
u_int32_t reg = (channel & 0x20)? TR_REG_INTENB : TR_REG_INTENA;
u_int32_t i = tr_rd(tr, reg, 4);
channel &= 0x1f;
i &= ~(1 << channel);
i |= (enable? 1 : 0) << channel;
tr_clrint(tr, channel);
tr_wr(tr, reg, i, 4);
}
/* playback channels */
static void
tr_selch(struct tr_info *tr, char channel)
{
int i=tr_rd(tr, TR_REG_CIR, 4);
i &= ~TR_CIR_MASK;
i |= channel & 0x3f;
tr_wr(tr, TR_REG_CIR, i, 4);
}
static void
tr_startch(struct tr_info *tr, char channel)
{
tr_wr(tr, (channel & 0x20)? TR_REG_STARTB : TR_REG_STARTA,
1<<(channel & 0x1f), 4);
}
static void
tr_stopch(struct tr_info *tr, char channel)
{
tr_wr(tr, (channel & 0x20)? TR_REG_STOPB : TR_REG_STOPA,
1<<(channel & 0x1f), 4);
}
static void
tr_wrch(struct tr_info *tr, char channel, struct tr_chinfo *ch)
{
u_int32_t cr[TR_CHN_REGS], i;
ch->gvsel &= 0x00000001;
ch->fmc &= 0x00000003;
ch->fms &= 0x0000000f;
ch->ctrl &= 0x0000000f;
ch->pan &= 0x0000007f;
ch->rvol &= 0x0000007f;
ch->cvol &= 0x0000007f;
ch->vol &= 0x000000ff;
ch->ec &= 0x00000fff;
ch->alpha &= 0x00000fff;
ch->delta &= 0x0000ffff;
ch->lba &= 0x3fffffff;
cr[1]=ch->lba;
cr[3]=(ch->rvol<<7) | (ch->cvol);
cr[4]=(ch->gvsel<<31)|(ch->pan<<24)|(ch->vol<<16)|(ch->ctrl<<12)|(ch->ec);
switch (tr->type) {
case TDX_PCI_ID:
ch->cso &= 0x0000ffff;
ch->eso &= 0x0000ffff;
cr[0]=(ch->cso<<16) | (ch->alpha<<4) | (ch->fms);
cr[2]=(ch->eso<<16) | (ch->delta);
cr[3]|=0x0000c000;
break;
case TNX_PCI_ID:
ch->cso &= 0x00ffffff;
ch->eso &= 0x00ffffff;
cr[0]=((ch->delta & 0xff)<<24) | (ch->cso);
cr[2]=((ch->delta>>16)<<24) | (ch->eso);
cr[3]|=(ch->alpha<<20) | (ch->fms<<16) | (ch->fmc<<14);
break;
}
tr_selch(tr, channel);
for (i=0; i<TR_CHN_REGS; i++)
tr_wr(tr, TR_REG_CHNBASE+(i<<2), cr[i], 4);
}
static void
tr_rdch(struct tr_info *tr, char channel, struct tr_chinfo *ch)
{
u_int32_t cr[5], i;
tr_selch(tr, channel);
for (i=0; i<5; i++) cr[i]=tr_rd(tr, TR_REG_CHNBASE+(i<<2), 4);
ch->lba= (cr[1] & 0x3fffffff);
ch->fmc= (cr[3] & 0x0000c000) >> 14;
ch->rvol= (cr[3] & 0x00003f80) >> 7;
ch->cvol= (cr[3] & 0x0000007f);
ch->gvsel= (cr[4] & 0x80000000) >> 31;
ch->pan= (cr[4] & 0x7f000000) >> 24;
ch->vol= (cr[4] & 0x00ff0000) >> 16;
ch->ctrl= (cr[4] & 0x0000f000) >> 12;
ch->ec= (cr[4] & 0x00000fff);
switch(tr->type) {
case TDX_PCI_ID:
ch->cso= (cr[0] & 0xffff0000) >> 16;
ch->alpha= (cr[0] & 0x0000fff0) >> 4;
ch->fms= (cr[0] & 0x0000000f);
ch->eso= (cr[2] & 0xffff0000) >> 16;
ch->delta= (cr[2] & 0x0000ffff);
break;
case TNX_PCI_ID:
ch->cso= (cr[0] & 0x00ffffff);
ch->eso= (cr[2] & 0x00ffffff);
ch->delta= ((cr[2] & 0xff000000) >> 16) |
((cr[0] & 0xff000000) >> 24);
ch->alpha= (cr[3] & 0xfff00000) >> 20;
ch->fms= (cr[3] & 0x000f0000) >> 16;
break;
}
}
/* channel interface */
void *
trchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir)
{
struct tr_info *tr = devinfo;
struct tr_chinfo *ch;
if (dir == PCMDIR_PLAY) {
ch = &tr->chinfo[tr->playchns];
ch->index = tr->playchns++;
} else {
ch = &tr->recchinfo;
ch->index = -1;
}
ch->buffer = b;
ch->buffer->bufsize = TR_BUFFSIZE;
ch->parent = tr;
ch->channel = c;
if (chn_allocbuf(ch->buffer, tr->parent_dmat) == -1) return NULL;
else return ch;
}
static int
trchan_setdir(void *data, int dir)
{
struct tr_chinfo *ch = data;
struct tr_info *tr = ch->parent;
if (dir == PCMDIR_PLAY && ch->index >= 0) {
ch->fmc = ch->fms = ch->ec = ch->alpha = 0;
ch->lba = vtophys(ch->buffer->buf);
ch->cso = 0;
ch->eso = ch->buffer->bufsize - 1;
ch->rvol = ch->cvol = 0;
ch->gvsel = 0;
ch->pan = 0;
ch->vol = 0;
ch->ctrl = 0x01;
ch->delta = 0;
tr_wrch(tr, ch->index, ch);
tr_enaint(tr, ch->index, 1);
} else if (dir == PCMDIR_REC && ch->index == -1) {
/* set up dma mode regs */
u_int32_t i;
tr_wr(tr, TR_REG_DMAR15, 0, 1);
i = tr_rd(tr, TR_REG_DMAR11, 1) & 0x03;
tr_wr(tr, TR_REG_DMAR11, i | 0x54, 1);
/* set up base address */
tr_wr(tr, TR_REG_DMAR0, vtophys(ch->buffer->buf), 4);
/* set up buffer size */
i = tr_rd(tr, TR_REG_DMAR4, 4) & ~0x00ffffff;
tr_wr(tr, TR_REG_DMAR4, i | (ch->buffer->bufsize - 1), 4);
} else return -1;
return 0;
}
static int
trchan_setformat(void *data, u_int32_t format)
{
struct tr_chinfo *ch = data;
struct tr_info *tr = ch->parent;
u_int32_t bits = tr_fmttobits(format);
if (ch->index >= 0) {
tr_rdch(tr, ch->index, ch);
ch->eso = (ch->buffer->bufsize / ch->buffer->sample_size) - 1;
ch->ctrl = bits | 0x01;
tr_wrch(tr, ch->index, ch);
} else {
u_int32_t i;
/* set # of samples between interrupts */
i = (TR_INTSAMPLES >> ((bits & 0x08)? 1 : 0)) - 1;
tr_wr(tr, TR_REG_SBBL, i | (i << 16), 4);
/* set sample format */
i = 0x18 | (bits << 4);
tr_wr(tr, TR_REG_SBCTRL, i, 1);
}
return 0;
}
static int
trchan_setspeed(void *data, u_int32_t speed)
{
struct tr_chinfo *ch = data;
struct tr_info *tr = ch->parent;
if (ch->index >= 0) {
tr_rdch(tr, ch->index, ch);
ch->delta = (speed << 12) / 48000;
tr_wrch(tr, ch->index, ch);
return (ch->delta * 48000) >> 12;
} else {
/* setup speed */
ch->delta = (48000 << 12) / speed;
tr_wr(tr, TR_REG_SBDELTA, ch->delta, 2);
return (48000 << 12) / ch->delta;
}
return 0;
}
static int
trchan_setblocksize(void *data, u_int32_t blocksize)
{
struct tr_chinfo *ch = data;
return ch->buffer->bufsize / 2;
}
static int
trchan_trigger(void *data, int go)
{
struct tr_chinfo *ch = data;
struct tr_info *tr = ch->parent;
if (ch->index >= 0) {
if (go == PCMTRIG_START) tr_startch(tr, ch->index);
else tr_stopch(tr, ch->index);
} else {
u_int32_t i = tr_rd(tr, TR_REG_SBCTRL, 1) & ~7;
tr_wr(tr, TR_REG_SBCTRL, i | (go == PCMTRIG_START)? 1 : 0, 1);
}
return 0;
}
static int
trchan_getptr(void *data)
{
struct tr_chinfo *ch = data;
struct tr_info *tr = ch->parent;
if (ch->index >= 0) {
tr_rdch(tr, ch->index, ch);
return ch->cso * ch->buffer->sample_size;
} else return tr_rd(tr, TR_REG_DMAR0, 4) - vtophys(ch->buffer->buf);
}
static pcmchan_caps *
trchan_getcaps(void *data)
{
struct tr_chinfo *ch = data;
return (ch->index >= 0)? &tr_playcaps : &tr_reccaps;
}
/* The interrupt handler */
static void
tr_intr(void *p)
{
struct tr_info *tr = (struct tr_info *)p;
u_int32_t intsrc = tr_rd(tr, TR_REG_MISCINT, 4);
if (intsrc & TR_INT_ADDR) {
int i;
for (i = 0; i < tr->playchns; i++) {
if (tr_testint(tr, i)) {
chn_intr(tr->chinfo[i].channel);
tr_clrint(tr, i);
}
}
}
if (intsrc & TR_INT_SB) {
chn_intr(tr->recchinfo.channel);
tr_rd(tr, TR_REG_SBR9, 1);
tr_rd(tr, TR_REG_SBR10, 1);
}
}
/* -------------------------------------------------------------------- */
/*
* Probe and attach the card
*/
static int
tr_init(struct tr_info *tr)
{
if (tr->type == TDX_PCI_ID) {
tr_wr(tr, TDX_REG_CODECST, TDX_CDC_ON, 4);
} else tr_wr(tr, TNX_REG_CODECST, TNX_CDC_ON, 4);
tr_wr(tr, TR_REG_CIR, TR_CIR_MIDENA | TR_CIR_ADDRENA, 4);
tr->playchns = 0;
return 0;
}
static int
tr_pci_probe(device_t dev)
{
if (pci_get_devid(dev) == TDX_PCI_ID) {
device_set_desc(dev, "Trident 4DWave DX");
return 0;
}
if (pci_get_devid(dev) == TNX_PCI_ID) {
device_set_desc(dev, "Trident 4DWave NX");
return 0;
}
return ENXIO;
}
static int
tr_pci_attach(device_t dev)
{
snddev_info *d;
u_int32_t data;
struct tr_info *tr;
struct ac97_info *codec;
int i;
int mapped;
char status[SND_STATUSLEN];
d = device_get_softc(dev);
if ((tr = malloc(sizeof(*tr), M_DEVBUF, M_NOWAIT)) == NULL) {
device_printf(dev, "cannot allocate softc\n");
return ENXIO;
}
bzero(tr, sizeof(*tr));
tr->type = pci_get_devid(dev);
data = pci_read_config(dev, PCIR_COMMAND, 2);
data |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN);
pci_write_config(dev, PCIR_COMMAND, data, 2);
data = pci_read_config(dev, PCIR_COMMAND, 2);
mapped = 0;
/* XXX dfr: is this strictly necessary? */
for (i = 0; (mapped == 0) && (i < PCI_MAXMAPS_0); i++) {
tr->regid = PCIR_MAPS + i*4;
tr->regtype = SYS_RES_MEMORY;
tr->reg = bus_alloc_resource(dev, tr->regtype, &tr->regid,
0, ~0, 1, RF_ACTIVE);
if (!tr->reg) {
tr->regtype = SYS_RES_IOPORT;
tr->reg = bus_alloc_resource(dev, tr->regtype,
&tr->regid, 0, ~0, 1,
RF_ACTIVE);
}
if (tr->reg) {
tr->st = rman_get_bustag(tr->reg);
tr->sh = rman_get_bushandle(tr->reg);
mapped++;
}
}
if (mapped == 0) {
device_printf(dev, "unable to map register space\n");
goto bad;
}
if (tr_init(tr) == -1) {
device_printf(dev, "unable to initialize the card\n");
goto bad;
}
codec = ac97_create(tr, tr_rdcd, tr_wrcd);
if (codec == NULL) goto bad;
mixer_init(d, &ac97_mixer, codec);
tr->irqid = 0;
tr->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &tr->irqid,
0, ~0, 1, RF_ACTIVE | RF_SHAREABLE);
if (!tr->irq ||
bus_setup_intr(dev, tr->irq, INTR_TYPE_TTY, tr_intr, tr, &tr->ih)) {
device_printf(dev, "unable to map interrupt\n");
goto bad;
}
if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0,
/*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
/*highaddr*/BUS_SPACE_MAXADDR,
/*filter*/NULL, /*filterarg*/NULL,
/*maxsize*/TR_BUFFSIZE, /*nsegments*/1, /*maxsegz*/0x3ffff,
/*flags*/0, &tr->parent_dmat) != 0) {
device_printf(dev, "unable to create dma tag\n");
goto bad;
}
snprintf(status, 64, "at %s 0x%lx irq %ld",
(tr->regtype == SYS_RES_IOPORT)? "io" : "memory",
rman_get_start(tr->reg), rman_get_start(tr->irq));
if (pcm_register(dev, tr, TR_MAXPLAYCH, 1)) goto bad;
pcm_addchan(dev, PCMDIR_REC, &tr_chantemplate, tr);
for (i = 0; i < TR_MAXPLAYCH; i++)
pcm_addchan(dev, PCMDIR_PLAY, &tr_chantemplate, tr);
pcm_setstatus(dev, status);
return 0;
bad:
if (tr->reg) bus_release_resource(dev, tr->regtype, tr->regid, tr->reg);
if (tr->ih) bus_teardown_intr(dev, tr->irq, tr->ih);
if (tr->irq) bus_release_resource(dev, SYS_RES_IRQ, tr->irqid, tr->irq);
free(tr, M_DEVBUF);
return ENXIO;
}
static device_method_t tr_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, tr_pci_probe),
DEVMETHOD(device_attach, tr_pci_attach),
{ 0, 0 }
};
static driver_t tr_driver = {
"pcm",
tr_methods,
sizeof(snddev_info),
};
static devclass_t pcm_devclass;
DRIVER_MODULE(tr, pci, tr_driver, pcm_devclass, 0, 0);
#endif /* NPCI != 0 */

View file

@ -1,103 +0,0 @@
/*
* Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
* All rights reserved.
*
* 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.
*
* $FreeBSD$
*/
#ifndef _T4DWAVE_REG_H
#define _T4DWAVE_REG_H
#define TDX_PCI_ID 0x20001023
#define TNX_PCI_ID 0x20011023
#define TR_BUFFSIZE 0x8000
#define TR_TIMEOUT_CDC 0xffff
#define TR_INTSAMPLES 0x2000
#define TR_MAXPLAYCH 64
#define TR_REG_CIR 0xa0
#define TR_CIR_MASK 0x0000003f
#define TR_CIR_ADDRENA 0x00001000
#define TR_CIR_MIDENA 0x00002000
#define TR_REG_MISCINT 0xb0
#define TR_INT_ADDR 0x00000020
#define TR_INT_SB 0x00000004
#define TR_REG_DMAR0 0x00
#define TR_REG_DMAR4 0x04
#define TR_REG_DMAR11 0x0b
#define TR_REG_DMAR15 0x0f
#define TR_REG_SBR4 0x14
#define TR_REG_SBR5 0x15
#define TR_SB_INTSTATUS 0x82
#define TR_REG_SBR9 0x1e
#define TR_REG_SBR10 0x1f
#define TR_REG_SBBL 0xc0
#define TR_REG_SBCTRL 0xc4
#define TR_REG_SBDELTA 0xac
#define TR_CDC_DATA 16
#define TDX_REG_CODECWR 0x40
#define TDX_REG_CODECRD 0x44
#define TDX_CDC_RWSTAT 0x00008000
#define TDX_REG_CODECST 0x48
#define TDX_CDC_SBCTRL 0x40
#define TDX_CDC_ACTIVE 0x20
#define TDX_CDC_READY 0x10
#define TDX_CDC_ADCON 0x08
#define TDX_CDC_DACON 0x02
#define TDX_CDC_RESET 0x01
#define TDX_CDC_ON (TDX_CDC_ADCON|TDX_CDC_DACON)
#define TNX_REG_CODECWR 0x44
#define TNX_REG_CODEC1RD 0x48
#define TNX_REG_CODEC2RD 0x4c
#define TNX_CDC_RWSTAT 0x00000c00
#define TNX_CDC_SEC 0x00000100
#define TNX_REG_CODECST 0x40
#define TNX_CDC_READY2 0x40
#define TNX_CDC_ADC2ON 0x20
#define TNX_CDC_DAC2ON 0x10
#define TNX_CDC_READY1 0x08
#define TNX_CDC_ADC1ON 0x04
#define TNX_CDC_DAC1ON 0x02
#define TNX_CDC_RESET 0x01
#define TNX_CDC_ON (TNX_CDC_ADC1ON|TNX_CDC_DAC1ON)
#define TR_REG_STARTA 0x80
#define TR_REG_STOPA 0x84
#define TR_REG_ADDRINTA 0x98
#define TR_REG_INTENA 0xa4
#define TR_REG_STARTB 0xb4
#define TR_REG_STOPB 0xb8
#define TR_REG_ADDRINTB 0xd8
#define TR_REG_INTENB 0xdc
#define TR_REG_CHNBASE 0xe0
#define TR_CHN_REGS 5
#endif

View file

@ -1,448 +0,0 @@
/*
* Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
* (C) 1997 Luigi Rizzo (luigi@iet.unipi.it)
* All rights reserved.
*
* 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.
*
* $FreeBSD$
*/
#include "opt_devfs.h"
#include <dev/pcm/sound.h>
#ifdef DEVFS
#include <sys/devfsext.h>
#endif /* DEVFS */
#if NPCM > 0 /* from "pcm.h" via disgusting #include in snd/sound.h */
extern struct isa_driver pcmdriver;
static int status_isopen = 0;
static int status_init(char *buf, int size);
static int status_read(struct uio *buf);
static d_open_t sndopen;
static d_close_t sndclose;
static d_ioctl_t sndioctl;
static d_read_t sndread;
static d_write_t sndwrite;
static d_mmap_t sndmmap;
static d_poll_t sndpoll;
#define CDEV_MAJOR 30
static struct cdevsw snd_cdevsw = {
/* open */ sndopen,
/* close */ sndclose,
/* read */ sndread,
/* write */ sndwrite,
/* ioctl */ sndioctl,
/* poll */ sndpoll,
/* mmap */ sndmmap,
/* strategy */ nostrategy,
/* name */ "snd",
/* maj */ CDEV_MAJOR,
/* dump */ nodump,
/* psize */ nopsize,
/* flags */ 0,
/* bmaj */ -1
};
/* PROPOSAL:
each unit needs:
status, mixer, dsp, dspW, audio, sequencer, midi-in, seq2, sndproc = 9 devices
dspW and audio are deprecated.
dsp needs min 64 channels, will give it 256
minor = (unit << 12) + (dev << 8) + channel
currently minor = (channel << 8) + (unit << 4) + dev
nomenclature:
/dev/pcmX/dsp.(0..255)
/dev/pcmX/dspW
/dev/pcmX/audio
/dev/pcmX/status
/dev/pcmX/mixer
[etc.]
currently:
minor = (channel << 8) + (unit << 4) + dev
*/
#define PCMMINOR(x) (minor(x))
#define PCMCHAN(x) ((PCMMINOR(x) & 0x0000ff00) >> 8)
#define PCMUNIT(x) ((PCMMINOR(x) & 0x000000f0) >> 4)
#define PCMDEV(x) (PCMMINOR(x) & 0x0000000f)
#define PCMMKMINOR(u, d) (((u) & 0x0f) << 4 | ((d) & 0x0f))
static devclass_t pcm_devclass;
static snddev_info *
gsd(int unit)
{
return devclass_get_softc(pcm_devclass, unit);
}
int
pcm_addchan(device_t dev, int dir, pcm_channel *templ, void *devinfo)
{
snddev_info *d = device_get_softc(dev);
pcm_channel *ch;
ch = (dir == PCMDIR_PLAY)? &d->play[d->playcount++] : &d->rec[d->reccount++];
*ch = *templ;
chn_init(ch, devinfo, dir);
d->chancount++;
return 0;
}
int
pcm_setstatus(device_t dev, char *str)
{
snddev_info *d = device_get_softc(dev);
strncpy(d->status, str, SND_STATUSLEN);
return 0;
}
u_int32_t
pcm_getflags(device_t dev)
{
snddev_info *d = device_get_softc(dev);
return d->flags;
}
void
pcm_setflags(device_t dev, u_int32_t val)
{
snddev_info *d = device_get_softc(dev);
d->flags = val;
}
/* This is the generic init routine */
int
pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
{
int sz, unit = device_get_unit(dev);
snddev_info *d = device_get_softc(dev);
if (!pcm_devclass) {
pcm_devclass = device_get_devclass(dev);
make_dev(&snd_cdevsw, PCMMKMINOR(0, SND_DEV_STATUS),
UID_ROOT, GID_WHEEL, 0444, "sndstat");
}
make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_CTL),
UID_ROOT, GID_WHEEL, 0666, "mixer%d", unit);
make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_DSP),
UID_ROOT, GID_WHEEL, 0666, "dsp%d", unit);
make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_AUDIO),
UID_ROOT, GID_WHEEL, 0666, "audio%d", unit);
make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_DSP16),
UID_ROOT, GID_WHEEL, 0666, "dspW%d", unit);
/* XXX SND_DEV_NORESET? */
d->devinfo = devinfo;
d->chancount = d->playcount = d->reccount = 0;
sz = (numplay + numrec) * sizeof(pcm_channel *);
d->aplay = (pcm_channel **)malloc(sz, M_DEVBUF, M_NOWAIT);
if (!d->aplay) goto no;
d->arec = (pcm_channel **)malloc(sz, M_DEVBUF, M_NOWAIT);
if (!d->arec) goto no;
bzero(d->aplay, sz);
bzero(d->arec, sz);
d->play = (pcm_channel *)malloc(numplay * sizeof(pcm_channel),
M_DEVBUF, M_NOWAIT);
if (!d->play) goto no;
d->rec = (pcm_channel *)malloc(numrec * sizeof(pcm_channel),
M_DEVBUF, M_NOWAIT);
if (!d->rec) goto no;
bzero(d->play, numplay * sizeof(pcm_channel));
bzero(d->rec, numrec * sizeof(pcm_channel));
fkchan_setup(&d->fakechan);
chn_init(&d->fakechan, NULL, 0);
d->magic = MAGIC(unit); /* debugging... */
return 0;
no:
if (d->aplay) free(d->aplay, M_DEVBUF);
if (d->play) free(d->play, M_DEVBUF);
if (d->arec) free(d->arec, M_DEVBUF);
if (d->rec) free(d->rec, M_DEVBUF);
return ENXIO;
}
/*
* a small utility function which, given a device number, returns
* a pointer to the associated snddev_info struct, and sets the unit
* number.
*/
static snddev_info *
get_snddev_info(dev_t i_dev, int *unit, int *dev, int *chan)
{
int u, d, c;
u = PCMUNIT(i_dev);
d = PCMDEV(i_dev);
c = PCMCHAN(i_dev);
if (u > devclass_get_maxunit(pcm_devclass)) u = -1;
if (unit) *unit = u;
if (dev) *dev = d;
if (chan) *chan = c;
if (u < 0) return NULL;
switch(d) {
case SND_DEV_CTL: /* /dev/mixer handled by pcm */
case SND_DEV_STATUS: /* /dev/sndstat handled by pcm */
case SND_DEV_DSP:
case SND_DEV_DSP16:
case SND_DEV_AUDIO:
return gsd(u);
case SND_DEV_SEQ: /* XXX when enabled... */
case SND_DEV_SEQ2:
case SND_DEV_MIDIN:
case SND_DEV_SNDPROC: /* /dev/sndproc handled by pcm */
default:
printf("unsupported subdevice %d\n", d);
return NULL;
}
}
static int
sndopen(dev_t i_dev, int flags, int mode, struct proc *p)
{
int dev, unit, chan;
snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
DEB(printf("open snd%d subdev %d flags 0x%08x mode 0x%08x\n",
unit, dev, flags, mode));
switch(dev) {
case SND_DEV_STATUS:
if (status_isopen) return EBUSY;
status_isopen = 1;
return 0;
case SND_DEV_CTL:
return d? 0 : ENXIO;
case SND_DEV_AUDIO:
case SND_DEV_DSP:
case SND_DEV_DSP16:
case SND_DEV_NORESET:
return d? dsp_open(d, chan, flags, dev) : ENXIO;
default:
return ENXIO;
}
}
static int
sndclose(dev_t i_dev, int flags, int mode, struct proc *p)
{
int dev, unit, chan;
snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
DEB(printf("close snd%d subdev %d\n", unit, dev));
switch(dev) { /* only those for which close makes sense */
case SND_DEV_STATUS:
if (!status_isopen) return EBADF;
status_isopen = 0;
return 0;
case SND_DEV_CTL:
return d? 0 : ENXIO;
case SND_DEV_AUDIO:
case SND_DEV_DSP:
case SND_DEV_DSP16:
return d? dsp_close(d, chan, dev) : ENXIO;
default:
return ENXIO;
}
}
static int
sndread(dev_t i_dev, struct uio *buf, int flag)
{
int dev, unit, chan;
snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
DEB(printf("read snd%d subdev %d flag 0x%08x\n", unit, dev, flag));
switch(dev) {
case SND_DEV_STATUS:
return status_isopen? status_read(buf) : EBADF;
case SND_DEV_AUDIO:
case SND_DEV_DSP:
case SND_DEV_DSP16:
return d? dsp_read(d, chan, buf, flag) : EBADF;
default:
return ENXIO;
}
}
static int
sndwrite(dev_t i_dev, struct uio *buf, int flag)
{
int dev, unit, chan;
snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
DEB(printf("write snd%d subdev %d flag 0x%08x\n", unit, dev & 0xf, flag));
switch(dev) { /* only writeable devices */
case SND_DEV_DSP:
case SND_DEV_DSP16:
case SND_DEV_AUDIO:
return d? dsp_write(d, chan, buf, flag) : EBADF;
default:
return EPERM; /* for non-writeable devices ; */
}
}
static int
sndioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc * p)
{
int dev, chan;
snddev_info *d = get_snddev_info(i_dev, NULL, &dev, &chan);
if (d == NULL) return ENXIO;
switch(dev) {
case SND_DEV_CTL:
return mixer_ioctl(d, cmd, arg);
case SND_DEV_AUDIO:
case SND_DEV_DSP:
case SND_DEV_DSP16:
return dsp_ioctl(d, chan, cmd, arg);
default:
return ENXIO;
}
}
static int
sndpoll(dev_t i_dev, int events, struct proc *p)
{
int dev, chan;
snddev_info *d = get_snddev_info(i_dev, NULL, &dev, &chan);
DEB(printf("sndpoll dev 0x%04x events 0x%08x\n", i_dev, events));
if (d == NULL) return ENXIO;
switch(dev) {
case SND_DEV_AUDIO:
case SND_DEV_DSP:
case SND_DEV_DSP16:
return dsp_poll(d, chan, events, p);
default:
return (events &
(POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM)) | POLLHUP;
}
}
/*
* The mmap interface allows access to the play and read buffer,
* plus the device descriptor.
* The various blocks are accessible at the following offsets:
*
* 0x00000000 ( 0 ) : write buffer ;
* 0x01000000 (16 MB) : read buffer ;
* 0x02000000 (32 MB) : device descriptor (dangerous!)
*
* WARNING: the mmap routines assume memory areas are aligned. This
* is true (probably) for the dma buffers, but likely false for the
* device descriptor. As a consequence, we do not know where it is
* located in the requested area.
*/
static int
sndmmap(dev_t i_dev, vm_offset_t offset, int nprot)
{
int unit, dev, chan;
snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
DEB(printf("sndmmap d 0x%p dev 0x%04x ofs 0x%08x nprot 0x%08x\n",
d, dev, offset, nprot));
if (d == NULL || nprot & PROT_EXEC) return -1; /* forbidden */
switch(dev) {
case SND_DEV_AUDIO:
case SND_DEV_DSP:
case SND_DEV_DSP16:
return dsp_mmap(d, chan, offset, nprot);
default:
return -1;
}
}
static int
status_init(char *buf, int size)
{
int i;
device_t dev;
snddev_info *d;
snprintf(buf, size, "FreeBSD Audio Driver (newpcm) %s %s\n"
"Installed devices:\n", __DATE__, __TIME__);
for (i = 0; i <= devclass_get_maxunit(pcm_devclass); i++) {
d = gsd(i);
if (!d) continue;
dev = devclass_get_device(pcm_devclass, i);
if (1) snprintf(buf + strlen(buf), size - strlen(buf),
"pcm%d: <%s> %s (%d/%d channels%s)\n",
i, device_get_desc(dev), d->status,
d->playcount, d->reccount,
(!(d->flags & SD_F_SIMPLEX))? " duplex" : "");
}
return strlen(buf);
}
static int
status_read(struct uio *buf)
{
static char status_buf[4096];
static int bufptr = 0, buflen = 0;
int l;
if (status_isopen == 1) {
status_isopen++;
bufptr = 0;
buflen = status_init(status_buf, sizeof status_buf);
}
l = min(buf->uio_resid, buflen - bufptr);
bufptr += l;
return (l > 0)? uiomove(status_buf + bufptr - l, l, buf) : 0;
}
#endif /* NPCM > 0 */

View file

@ -1,182 +0,0 @@
/*
* Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
* Copyright by Hannu Savolainen 1995
* All rights reserved.
*
* 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.
*
* $FreeBSD$
*/
#ifdef KERNEL
#include "pcm.h"
#else
#error why?
#define NPCM 1
#endif
#if NPCM > 0
/*
* first, include kernel header files.
*/
#ifndef _OS_H_
#define _OS_H_
#ifdef KERNEL
#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/module.h>
#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/bus.h>
#include <sys/buf.h>
#include <machine/clock.h> /* for DELAY */
#include <machine/resource.h>
#include <machine/bus_memio.h>
#include <machine/bus_pio.h>
#include <machine/bus.h>
#include <sys/rman.h>
#include <sys/mman.h>
#include <sys/poll.h>
#include <sys/soundcard.h>
#include <isa/isavar.h>
#include <vm/vm.h>
#include <vm/pmap.h>
#else
struct isa_device { int dummy; };
#define d_open_t void
#define d_close_t void
#define d_read_t void
#define d_write_t void
#define d_ioctl_t void
#define d_select_t void
#endif /* KERNEL */
#endif /* _OS_H_ */
#include <dev/pcm/datatypes.h>
#include <dev/pcm/channel.h>
#include <dev/pcm/mixer.h>
#include <dev/pcm/dsp.h>
#define MAGIC(unit) (0xa4d10de0 + unit)
#define SD_F_SIMPLEX 0x00000001
#define SD_F_EVILSB16 0x00000002
#define SD_F_PRIO_RD 0x10000000
#define SD_F_PRIO_WR 0x20000000
#define SD_F_PRIO_SET (SD_F_PRIO_RD | SD_F_PRIO_WR)
#define SD_F_DIR_SET 0x40000000
#define SD_F_TRANSIENT 0xf0000000
/* 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))
#define DSP_BUFFSIZE (65536 - 256) /* XXX */
/* the last 256 bytes are room for buggy soundcard to overflow. */
/* make figuring out what a format is easier. got AFMT_STEREO already */
#define AFMT_16BIT (AFMT_S16_LE | AFMT_S16_BE | AFMT_U16_LE | AFMT_U16_BE)
#define AFMT_SIGNED (AFMT_S16_LE | AFMT_S16_BE | AFMT_S8)
#define AFMT_BIGENDIAN (AFMT_S16_BE | AFMT_U16_BE)
int fkchan_setup(pcm_channel *c);
#ifdef KERNEL
#include "pnp.h"
#endif /* KERNEL */
/*
* 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 /dev/sequencer */
#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. */
#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 SND_DEV_NORESET 10
#define DSP_DEFAULT_SPEED 8000
#define ON 1
#define OFF 0
#ifdef KERNEL
/*
* some macros for debugging purposes
* DDB/DEB to enable/disable debugging stuff
* BVDDB to enable debugging when bootverbose
*/
#define DDB(x) x /* XXX */
#define BVDDB(x) if (bootverbose) x
#ifndef DEB
#define DEB(x)
#endif
int pcm_addchan(device_t dev, int dir, pcm_channel *templ, void *devinfo);
int pcm_register(device_t dev, void *devinfo, int numplay, int numrec);
int pcm_setstatus(device_t dev, char *str);
u_int32_t pcm_getflags(device_t dev);
void pcm_setflags(device_t dev, u_int32_t val);
#endif /* KERNEL */
/* 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 */
/* ought to be made obsolete */
#define DV_F_DEV_MASK 0x0000ff00 /* force device type/class */
#define DV_F_DEV_SHIFT 8 /* force device type/class */
#endif