mirror of
https://github.com/freebsd/freebsd-src
synced 2024-10-19 06:44:31 +00:00
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:
parent
3e0c8d78f3
commit
959b26f66f
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=53506
|
@ -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,
|
||||
};
|
||||
|
|
@ -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);
|
|
@ -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);
|
||||
}
|
|
@ -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)
|
|
@ -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)
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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)
|
||||
|
|
@ -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
|
@ -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 */
|
||||
|
||||
|
1347
sys/dev/pcm/isa/sb.c
1347
sys/dev/pcm/isa/sb.c
File diff suppressed because it is too large
Load diff
|
@ -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 */
|
||||
|
||||
|
|
@ -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 */
|
||||
}
|
||||
|
|
@ -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);
|
|
@ -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], ®id[j],
|
||||
0, ~0, 1, RF_ACTIVE);
|
||||
if (!reg[j]) {
|
||||
type[j] = SYS_RES_IOPORT;
|
||||
reg[j] = bus_alloc_resource(dev, type[j], ®id[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 */
|
||||
|
|
@ -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
|
|
@ -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, ®id,
|
||||
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, ®id,
|
||||
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 */
|
|
@ -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
|
|
@ -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, ®id,
|
||||
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, ®id,
|
||||
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 */
|
|
@ -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
|
|
@ -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 */
|
|
@ -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
|
|
@ -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 */
|
|
@ -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
|
Loading…
Reference in a new issue