#include <sys/param.h>
#include <sys/systm.h>
#include <sys/errno.h>
#include <sys/ioctl.h>
#include <sys/syslog.h>
#include <sys/device.h>
#include <sys/buf.h>
#include <sys/fcntl.h>
#include <sys/malloc.h>
#include <sys/kernel.h>
#include <sys/timeout.h>
#include <machine/cpu.h>
#include <machine/intr.h>
#include <machine/bus.h>
#include <machine/cpufunc.h>
#include <sys/audioio.h>
#include <dev/audio_if.h>
#include <dev/isa/isavar.h>
#include <dev/isa/isadmavar.h>
#include <dev/ic/ics2101reg.h>
#include <dev/ic/cs4231reg.h>
#include <dev/ic/ad1848reg.h>
#include <dev/isa/ics2101var.h>
#include <dev/isa/ad1848var.h>
#include "gusreg.h"
#include "gusvar.h"
#ifdef AUDIO_DEBUG
#define GUSPLAYDEBUG
#define DPRINTF(x) if (gusdebug) printf x
#define DMAPRINTF(x) if (gusdmadebug) printf x
int gusdebug = 0;
int gusdmadebug = 0;
#else
#define DPRINTF(x)
#define DMAPRINTF(x)
#endif
int gus_dostereo = 1;
#define NDMARECS 2048
#ifdef GUSPLAYDEBUG
int gusstats = 0;
struct dma_record dmarecords[NDMARECS];
int dmarecord_index = 0;
#endif
struct cfdriver gus_cd = {
NULL, "gus", DV_DULL
};
const int gus_irq_map[] = {
IRQUNK, IRQUNK, 1, 3, IRQUNK, 2, IRQUNK, 4, IRQUNK, 1, IRQUNK, 5, 6,
IRQUNK, IRQUNK, 7
};
const int gus_drq_map[] = {
DRQUNK, 1, DRQUNK, 2, DRQUNK, 3, 4, 5
};
const int gus_base_addrs[] = {
0x210, 0x220, 0x230, 0x240, 0x250, 0x260
};
const int gus_addrs = sizeof(gus_base_addrs) / sizeof(gus_base_addrs[0]);
static const int gus_max_frequency[] = {
44100,
41160,
38587,
36317,
34300,
32494,
30870,
29400,
28063,
26843,
25725,
24696,
23746,
22866,
22050,
21289,
20580,
19916,
19293
};
static const unsigned short gus_log_volumes[512] = {
0x0000,
0x0700, 0x07ff, 0x0880, 0x08ff, 0x0940, 0x0980, 0x09c0, 0x09ff, 0x0a20,
0x0a40, 0x0a60, 0x0a80, 0x0aa0, 0x0ac0, 0x0ae0, 0x0aff, 0x0b10, 0x0b20,
0x0b30, 0x0b40, 0x0b50, 0x0b60, 0x0b70, 0x0b80, 0x0b90, 0x0ba0, 0x0bb0,
0x0bc0, 0x0bd0, 0x0be0, 0x0bf0, 0x0bff, 0x0c08, 0x0c10, 0x0c18, 0x0c20,
0x0c28, 0x0c30, 0x0c38, 0x0c40, 0x0c48, 0x0c50, 0x0c58, 0x0c60, 0x0c68,
0x0c70, 0x0c78, 0x0c80, 0x0c88, 0x0c90, 0x0c98, 0x0ca0, 0x0ca8, 0x0cb0,
0x0cb8, 0x0cc0, 0x0cc8, 0x0cd0, 0x0cd8, 0x0ce0, 0x0ce8, 0x0cf0, 0x0cf8,
0x0cff, 0x0d04, 0x0d08, 0x0d0c, 0x0d10, 0x0d14, 0x0d18, 0x0d1c, 0x0d20,
0x0d24, 0x0d28, 0x0d2c, 0x0d30, 0x0d34, 0x0d38, 0x0d3c, 0x0d40, 0x0d44,
0x0d48, 0x0d4c, 0x0d50, 0x0d54, 0x0d58, 0x0d5c, 0x0d60, 0x0d64, 0x0d68,
0x0d6c, 0x0d70, 0x0d74, 0x0d78, 0x0d7c, 0x0d80, 0x0d84, 0x0d88, 0x0d8c,
0x0d90, 0x0d94, 0x0d98, 0x0d9c, 0x0da0, 0x0da4, 0x0da8, 0x0dac, 0x0db0,
0x0db4, 0x0db8, 0x0dbc, 0x0dc0, 0x0dc4, 0x0dc8, 0x0dcc, 0x0dd0, 0x0dd4,
0x0dd8, 0x0ddc, 0x0de0, 0x0de4, 0x0de8, 0x0dec, 0x0df0, 0x0df4, 0x0df8,
0x0dfc, 0x0dff, 0x0e02, 0x0e04, 0x0e06, 0x0e08, 0x0e0a, 0x0e0c, 0x0e0e,
0x0e10, 0x0e12, 0x0e14, 0x0e16, 0x0e18, 0x0e1a, 0x0e1c, 0x0e1e, 0x0e20,
0x0e22, 0x0e24, 0x0e26, 0x0e28, 0x0e2a, 0x0e2c, 0x0e2e, 0x0e30, 0x0e32,
0x0e34, 0x0e36, 0x0e38, 0x0e3a, 0x0e3c, 0x0e3e, 0x0e40, 0x0e42, 0x0e44,
0x0e46, 0x0e48, 0x0e4a, 0x0e4c, 0x0e4e, 0x0e50, 0x0e52, 0x0e54, 0x0e56,
0x0e58, 0x0e5a, 0x0e5c, 0x0e5e, 0x0e60, 0x0e62, 0x0e64, 0x0e66, 0x0e68,
0x0e6a, 0x0e6c, 0x0e6e, 0x0e70, 0x0e72, 0x0e74, 0x0e76, 0x0e78, 0x0e7a,
0x0e7c, 0x0e7e, 0x0e80, 0x0e82, 0x0e84, 0x0e86, 0x0e88, 0x0e8a, 0x0e8c,
0x0e8e, 0x0e90, 0x0e92, 0x0e94, 0x0e96, 0x0e98, 0x0e9a, 0x0e9c, 0x0e9e,
0x0ea0, 0x0ea2, 0x0ea4, 0x0ea6, 0x0ea8, 0x0eaa, 0x0eac, 0x0eae, 0x0eb0,
0x0eb2, 0x0eb4, 0x0eb6, 0x0eb8, 0x0eba, 0x0ebc, 0x0ebe, 0x0ec0, 0x0ec2,
0x0ec4, 0x0ec6, 0x0ec8, 0x0eca, 0x0ecc, 0x0ece, 0x0ed0, 0x0ed2, 0x0ed4,
0x0ed6, 0x0ed8, 0x0eda, 0x0edc, 0x0ede, 0x0ee0, 0x0ee2, 0x0ee4, 0x0ee6,
0x0ee8, 0x0eea, 0x0eec, 0x0eee, 0x0ef0, 0x0ef2, 0x0ef4, 0x0ef6, 0x0ef8,
0x0efa, 0x0efc, 0x0efe, 0x0eff, 0x0f01, 0x0f02, 0x0f03, 0x0f04, 0x0f05,
0x0f06, 0x0f07, 0x0f08, 0x0f09, 0x0f0a, 0x0f0b, 0x0f0c, 0x0f0d, 0x0f0e,
0x0f0f, 0x0f10, 0x0f11, 0x0f12, 0x0f13, 0x0f14, 0x0f15, 0x0f16, 0x0f17,
0x0f18, 0x0f19, 0x0f1a, 0x0f1b, 0x0f1c, 0x0f1d, 0x0f1e, 0x0f1f, 0x0f20,
0x0f21, 0x0f22, 0x0f23, 0x0f24, 0x0f25, 0x0f26, 0x0f27, 0x0f28, 0x0f29,
0x0f2a, 0x0f2b, 0x0f2c, 0x0f2d, 0x0f2e, 0x0f2f, 0x0f30, 0x0f31, 0x0f32,
0x0f33, 0x0f34, 0x0f35, 0x0f36, 0x0f37, 0x0f38, 0x0f39, 0x0f3a, 0x0f3b,
0x0f3c, 0x0f3d, 0x0f3e, 0x0f3f, 0x0f40, 0x0f41, 0x0f42, 0x0f43, 0x0f44,
0x0f45, 0x0f46, 0x0f47, 0x0f48, 0x0f49, 0x0f4a, 0x0f4b, 0x0f4c, 0x0f4d,
0x0f4e, 0x0f4f, 0x0f50, 0x0f51, 0x0f52, 0x0f53, 0x0f54, 0x0f55, 0x0f56,
0x0f57, 0x0f58, 0x0f59, 0x0f5a, 0x0f5b, 0x0f5c, 0x0f5d, 0x0f5e, 0x0f5f,
0x0f60, 0x0f61, 0x0f62, 0x0f63, 0x0f64, 0x0f65, 0x0f66, 0x0f67, 0x0f68,
0x0f69, 0x0f6a, 0x0f6b, 0x0f6c, 0x0f6d, 0x0f6e, 0x0f6f, 0x0f70, 0x0f71,
0x0f72, 0x0f73, 0x0f74, 0x0f75, 0x0f76, 0x0f77, 0x0f78, 0x0f79, 0x0f7a,
0x0f7b, 0x0f7c, 0x0f7d, 0x0f7e, 0x0f7f, 0x0f80, 0x0f81, 0x0f82, 0x0f83,
0x0f84, 0x0f85, 0x0f86, 0x0f87, 0x0f88, 0x0f89, 0x0f8a, 0x0f8b, 0x0f8c,
0x0f8d, 0x0f8e, 0x0f8f, 0x0f90, 0x0f91, 0x0f92, 0x0f93, 0x0f94, 0x0f95,
0x0f96, 0x0f97, 0x0f98, 0x0f99, 0x0f9a, 0x0f9b, 0x0f9c, 0x0f9d, 0x0f9e,
0x0f9f, 0x0fa0, 0x0fa1, 0x0fa2, 0x0fa3, 0x0fa4, 0x0fa5, 0x0fa6, 0x0fa7,
0x0fa8, 0x0fa9, 0x0faa, 0x0fab, 0x0fac, 0x0fad, 0x0fae, 0x0faf, 0x0fb0,
0x0fb1, 0x0fb2, 0x0fb3, 0x0fb4, 0x0fb5, 0x0fb6, 0x0fb7, 0x0fb8, 0x0fb9,
0x0fba, 0x0fbb, 0x0fbc, 0x0fbd, 0x0fbe, 0x0fbf, 0x0fc0, 0x0fc1, 0x0fc2,
0x0fc3, 0x0fc4, 0x0fc5, 0x0fc6, 0x0fc7, 0x0fc8, 0x0fc9, 0x0fca, 0x0fcb,
0x0fcc, 0x0fcd, 0x0fce, 0x0fcf, 0x0fd0, 0x0fd1, 0x0fd2, 0x0fd3, 0x0fd4,
0x0fd5, 0x0fd6, 0x0fd7, 0x0fd8, 0x0fd9, 0x0fda, 0x0fdb, 0x0fdc, 0x0fdd,
0x0fde, 0x0fdf, 0x0fe0, 0x0fe1, 0x0fe2, 0x0fe3, 0x0fe4, 0x0fe5, 0x0fe6,
0x0fe7, 0x0fe8, 0x0fe9, 0x0fea, 0x0feb, 0x0fec, 0x0fed, 0x0fee, 0x0fef,
0x0ff0, 0x0ff1, 0x0ff2, 0x0ff3, 0x0ff4, 0x0ff5, 0x0ff6, 0x0ff7, 0x0ff8,
0x0ff9, 0x0ffa, 0x0ffb, 0x0ffc, 0x0ffd, 0x0ffe, 0x0fff};
const struct audio_hw_if gus_hw_if = {
.open = gusopen,
.close = gusclose,
.set_params = gus_set_params,
.round_blocksize = gus_round_blocksize,
.commit_settings = gus_commit_settings,
.start_output = gus_dma_output,
.start_input = gus_dma_input,
.halt_output = gus_halt_out_dma,
.halt_input = gus_halt_in_dma,
.set_port = gus_mixer_set_port,
.get_port = gus_mixer_get_port,
.query_devinfo = gus_mixer_query_devinfo,
.allocm = gus_malloc,
.freem = gus_free,
.round_buffersize = gus_round,
};
static const struct audio_hw_if gusmax_hw_if = {
.open = gusmaxopen,
.close = gusmax_close,
.set_params = gusmax_set_params,
.round_blocksize = gusmax_round_blocksize,
.commit_settings = gusmax_commit_settings,
.start_output = gusmax_dma_output,
.start_input = gusmax_dma_input,
.halt_output = gusmax_halt_out_dma,
.halt_input = gusmax_halt_in_dma,
.set_port = gusmax_mixer_set_port,
.get_port = gusmax_mixer_get_port,
.query_devinfo = gusmax_mixer_query_devinfo,
.allocm = ad1848_malloc,
.freem = ad1848_free,
.round_buffersize = ad1848_round,
};
int
gusopen(void *addr, int flags)
{
struct gus_softc *sc = addr;
DPRINTF(("gusopen() called\n"));
if ((flags & (FWRITE | FREAD)) == (FWRITE | FREAD) &&
sc->sc_recdrq == sc->sc_drq)
return ENXIO;
if (sc->sc_flags & GUS_OPEN)
return EBUSY;
gus_speaker_ctl(sc, (flags & FWRITE) ? SPKR_ON : SPKR_OFF);
sc->sc_flags |= GUS_OPEN;
sc->sc_dmabuf = 0;
sc->sc_playbuf = -1;
sc->sc_bufcnt = 0;
sc->sc_voc[GUS_VOICE_LEFT].start_addr = GUS_MEM_OFFSET - 1;
sc->sc_voc[GUS_VOICE_LEFT].current_addr = GUS_MEM_OFFSET;
if (HAS_CODEC(sc)) {
ad1848_open(&sc->sc_codec, flags);
sc->sc_codec.mute[AD1848_AUX1_CHANNEL] = 0;
ad1848_mute_channel(&sc->sc_codec, AD1848_AUX1_CHANNEL, 0);
if (flags & FREAD) {
sc->sc_codec.mute[AD1848_MONO_CHANNEL] = 0;
ad1848_mute_channel(&sc->sc_codec, AD1848_MONO_CHANNEL, 0);
}
} else if (flags & FREAD) {
if (HAS_MIXER(sc)) {
gusics_mic_mute(&sc->sc_mixer, 0);
} else
gus_mic_ctl(sc, SPKR_ON);
}
if (sc->sc_nbufs == 0)
gus_round_blocksize(sc, GUS_BUFFER_MULTIPLE);
return 0;
}
int
gusmaxopen(void *addr, int flags)
{
struct ad1848_softc *ac = addr;
return gusopen(ac->parent, flags);
}
void
gus_deinterleave(struct gus_softc *sc, void *buf, int size)
{
int i;
if (size > sc->sc_blocksize) {
printf("gus: deinterleave %d > %d\n", size, sc->sc_blocksize);
return;
} else if (size < sc->sc_blocksize) {
DPRINTF(("gus: deinterleave %d < %d\n", size, sc->sc_blocksize));
}
if (sc->sc_precision == 16) {
u_short *dei = sc->sc_deintr_buf;
u_short *sbuf = buf;
size >>= 1;
for (i = 0; i < size/2-1; i++) {
dei[i] = sbuf[i*2+1];
sbuf[i+1] = sbuf[i*2+2];
}
bcopy(dei, &sbuf[size/2], i * sizeof(short));
} else {
u_char *dei = sc->sc_deintr_buf;
u_char *sbuf = buf;
for (i = 0; i < size/2-1; i++) {
dei[i] = sbuf[i*2+1];
sbuf[i+1] = sbuf[i*2+2];
}
bcopy(dei, &sbuf[size/2], i);
}
}
int
gusmax_dma_output(void *addr, void *buf, int size, void (*intr)(void *),
void *arg)
{
struct ad1848_softc *ac = addr;
return gus_dma_output(ac->parent, buf, size, intr, arg);
}
void
stereo_dmaintr(void *arg)
{
struct gus_softc *sc = arg;
struct stereo_dma_intr *sa = &sc->sc_stereo;
DMAPRINTF(("stereo_dmaintr"));
sc->sc_dmaoutintr = sa->intr;
sc->sc_outarg = sa->arg;
#ifdef GUSPLAYDEBUG
if (gusstats) {
microtime(&dmarecords[dmarecord_index].tv);
dmarecords[dmarecord_index].gusaddr = sa->dmabuf;
dmarecords[dmarecord_index].bsdaddr = sa->buffer;
dmarecords[dmarecord_index].count = sa->size;
dmarecords[dmarecord_index].channel = 1;
dmarecords[dmarecord_index++].direction = 1;
dmarecord_index = dmarecord_index % NDMARECS;
}
#endif
gusdmaout(sc, sa->flags, sa->dmabuf, (caddr_t) sa->buffer, sa->size);
sa->flags = 0;
sa->dmabuf = 0;
sa->buffer = 0;
sa->size = 0;
sa->intr = 0;
sa->arg = 0;
}
int
gus_dma_output(void *addr, void *buf, int size, void (*intr)(void *), void *arg)
{
struct gus_softc *sc = addr;
u_char *buffer = buf;
u_long boarddma;
int flags;
DMAPRINTF(("gus_dma_output %d @ %p\n", size, buf));
if (size != sc->sc_blocksize) {
DPRINTF(("gus_dma_output reqsize %d not sc_blocksize %d\n",
size, sc->sc_blocksize));
return EINVAL;
}
flags = GUSMASK_DMA_WRITE;
if (sc->sc_precision == 16)
flags |= GUSMASK_DMA_DATA_SIZE;
if (sc->sc_encoding == AUDIO_ENCODING_ULAW ||
sc->sc_encoding == AUDIO_ENCODING_ALAW ||
sc->sc_encoding == AUDIO_ENCODING_ULINEAR_BE ||
sc->sc_encoding == AUDIO_ENCODING_ULINEAR_LE)
flags |= GUSMASK_DMA_INVBIT;
if (sc->sc_channels == 2) {
if (sc->sc_precision == 16) {
if (size & 3) {
DPRINTF(("gus_dma_output: unpaired 16bit samples"));
size &= 3;
}
} else if (size & 1) {
DPRINTF(("gus_dma_output: unpaired samples"));
size &= 1;
}
if (size == 0) {
return 0;
}
gus_deinterleave(sc, (void *)buffer, size);
size >>= 1;
boarddma = size * sc->sc_dmabuf + GUS_MEM_OFFSET;
sc->sc_stereo.intr = intr;
sc->sc_stereo.arg = arg;
sc->sc_stereo.size = size;
sc->sc_stereo.dmabuf = boarddma + GUS_LEFT_RIGHT_OFFSET;
sc->sc_stereo.buffer = buffer + size;
sc->sc_stereo.flags = flags;
if (gus_dostereo) {
intr = stereo_dmaintr;
arg = sc;
}
} else
boarddma = size * sc->sc_dmabuf + GUS_MEM_OFFSET;
sc->sc_flags |= GUS_LOCKED;
sc->sc_dmaoutintr = intr;
sc->sc_outarg = arg;
#ifdef GUSPLAYDEBUG
if (gusstats) {
microtime(&dmarecords[dmarecord_index].tv);
dmarecords[dmarecord_index].gusaddr = boarddma;
dmarecords[dmarecord_index].bsdaddr = buffer;
dmarecords[dmarecord_index].count = size;
dmarecords[dmarecord_index].channel = 0;
dmarecords[dmarecord_index++].direction = 1;
dmarecord_index = dmarecord_index % NDMARECS;
}
#endif
gusdmaout(sc, flags, boarddma, (caddr_t) buffer, size);
return 0;
}
void
gusmax_close(void *addr)
{
struct ad1848_softc *ac = addr;
struct gus_softc *sc = ac->parent;
#if 0
ac->mute[AD1848_AUX1_CHANNEL] = MUTE_ALL;
ad1848_mute_channel(ac, MUTE_ALL);
#endif
ad1848_close(ac);
gusclose(sc);
}
void
gusclose(void *addr)
{
struct gus_softc *sc = addr;
DPRINTF(("gus_close: sc=%p\n", sc));
{
gus_halt_out_dma(sc);
}
{
gus_halt_in_dma(sc);
}
sc->sc_flags &= ~(GUS_OPEN|GUS_LOCKED|GUS_DMAOUT_ACTIVE|GUS_DMAIN_ACTIVE);
if (sc->sc_deintr_buf) {
free(sc->sc_deintr_buf, M_DEVBUF, 0);
sc->sc_deintr_buf = NULL;
}
gus_stop_voice(sc, GUS_VOICE_LEFT, 1);
gus_stop_voice(sc, GUS_VOICE_RIGHT, 0);
}
#ifdef AUDIO_DEBUG
int gusintrcnt;
int gusdmaintrcnt;
int gusvocintrcnt;
#endif
int
gusintr(void *arg)
{
struct gus_softc *sc = arg;
bus_space_tag_t iot = sc->sc_iot;
bus_space_handle_t ioh1 = sc->sc_ioh1;
bus_space_handle_t ioh2 = sc->sc_ioh2;
unsigned char intr;
int retval = 0;
DPRINTF(("gusintr\n"));
#ifdef AUDIO_DEBUG
gusintrcnt++;
#endif
if (HAS_CODEC(sc))
retval = ad1848_intr(&sc->sc_codec);
mtx_enter(&audio_lock);
if ((intr = bus_space_read_1(iot, ioh1, GUS_IRQ_STATUS)) & GUSMASK_IRQ_DMATC) {
DMAPRINTF(("gusintr dma flags=%x\n", sc->sc_flags));
#ifdef AUDIO_DEBUG
gusdmaintrcnt++;
#endif
retval += gus_dmaout_intr(sc);
if (sc->sc_flags & GUS_DMAIN_ACTIVE) {
SELECT_GUS_REG(iot, ioh2, GUSREG_SAMPLE_CONTROL);
intr = bus_space_read_1(iot, ioh2, GUS_DATA_HIGH);
if (intr & GUSMASK_SAMPLE_DMATC) {
retval += gus_dmain_intr(sc);
}
}
}
if (intr & (GUSMASK_IRQ_VOICE | GUSMASK_IRQ_VOLUME)) {
DMAPRINTF(("gusintr voice flags=%x\n", sc->sc_flags));
#ifdef AUDIO_DEBUG
gusvocintrcnt++;
#endif
retval += gus_voice_intr(sc);
}
if (retval) {
mtx_leave(&audio_lock);
return 1;
}
mtx_leave(&audio_lock);
return retval;
}
int gus_bufcnt[GUS_MEM_FOR_BUFFERS / GUS_BUFFER_MULTIPLE];
int gus_restart;
int gus_stops;
int gus_falsestops;
int gus_continues;
struct playcont {
struct timeval tv;
u_int playbuf;
u_int dmabuf;
u_char bufcnt;
u_char vaction;
u_char voccntl;
u_char volcntl;
u_long curaddr;
u_long endaddr;
} playstats[NDMARECS];
int playcntr;
void
gus_dmaout_timeout(void *arg)
{
struct gus_softc *sc = arg;
bus_space_tag_t iot = sc->sc_iot;
bus_space_handle_t ioh2 = sc->sc_ioh2;
printf("%s: dmaout timeout\n", sc->sc_dev.dv_xname);
mtx_enter(&audio_lock);
SELECT_GUS_REG(iot, ioh2, GUSREG_DMA_CONTROL);
bus_space_write_1(iot, ioh2, GUS_DATA_HIGH, 0);
#if 0
isa_dmaabort(sc->sc_dev.dv_parent, sc->sc_drq);
#endif
gus_dmaout_dointr(sc);
mtx_leave(&audio_lock);
}
int
gus_dmaout_intr(struct gus_softc *sc)
{
bus_space_tag_t iot = sc->sc_iot;
bus_space_handle_t ioh2 = sc->sc_ioh2;
SELECT_GUS_REG(iot, ioh2, GUSREG_DMA_CONTROL);
if (bus_space_read_1(iot, ioh2, GUS_DATA_HIGH) & GUSMASK_DMA_IRQPEND) {
timeout_del(&sc->sc_dma_tmo);
gus_dmaout_dointr(sc);
return 1;
}
return 0;
}
void
gus_dmaout_dointr(struct gus_softc *sc)
{
bus_space_tag_t iot = sc->sc_iot;
bus_space_handle_t ioh2 = sc->sc_ioh2;
isa_dmadone(sc->sc_dev.dv_parent, sc->sc_drq);
sc->sc_flags &= ~GUS_DMAOUT_ACTIVE;
DMAPRINTF(("gus_dmaout_dointr %d @ %p\n", sc->sc_dmaoutcnt,
sc->sc_dmaoutaddr));
if (sc->sc_dmabuf == sc->sc_nbufs - 1) {
int i;
switch (sc->sc_encoding) {
case AUDIO_ENCODING_SLINEAR_LE:
case AUDIO_ENCODING_SLINEAR_BE:
if (sc->sc_precision == 8)
goto byte;
for (i = 1; i <= 2; i++)
guspoke(iot, ioh2, sc->sc_gusaddr -
(sc->sc_nbufs - 1) * sc->sc_chanblocksize - i,
sc->sc_dmaoutaddr[sc->sc_dmaoutcnt-i]);
break;
case AUDIO_ENCODING_ULINEAR_LE:
case AUDIO_ENCODING_ULINEAR_BE:
guspoke(iot, ioh2, sc->sc_gusaddr -
(sc->sc_nbufs - 1) * sc->sc_chanblocksize - 2,
guspeek(iot, ioh2,
sc->sc_gusaddr + sc->sc_chanblocksize - 2));
case AUDIO_ENCODING_ALAW:
case AUDIO_ENCODING_ULAW:
byte:
guspoke(iot, ioh2, sc->sc_gusaddr -
(sc->sc_nbufs - 1) * sc->sc_chanblocksize - 1,
guspeek(iot, ioh2,
sc->sc_gusaddr + sc->sc_chanblocksize - 1));
break;
}
}
if (sc->sc_dmaoutintr == stereo_dmaintr) {
(*sc->sc_dmaoutintr)(sc->sc_outarg);
return;
}
sc->sc_flags &= ~GUS_LOCKED;
if (sc->sc_voc[GUS_VOICE_LEFT].voccntl &
GUSMASK_VOICE_STOPPED) {
if (sc->sc_flags & GUS_PLAYING) {
printf("%s: playing yet stopped?\n", sc->sc_dev.dv_xname);
}
sc->sc_bufcnt++;
gus_start_playing(sc, sc->sc_dmabuf);
gus_restart++;
} else {
if (++sc->sc_bufcnt == 2) {
if (sc->sc_dmabuf == 0 &&
sc->sc_playbuf == sc->sc_nbufs - 1) {
sc->sc_voc[GUS_VOICE_LEFT].voccntl |= GUSMASK_LOOP_ENABLE;
sc->sc_voc[GUS_VOICE_LEFT].volcntl &= ~GUSMASK_VOICE_ROLL;
playstats[playcntr].vaction = 3;
} else {
sc->sc_voc[GUS_VOICE_LEFT].voccntl &= ~GUSMASK_LOOP_ENABLE;
sc->sc_voc[GUS_VOICE_LEFT].volcntl |= GUSMASK_VOICE_ROLL;
playstats[playcntr].vaction = 4;
}
#ifdef GUSPLAYDEBUG
if (gusstats) {
microtime(&playstats[playcntr].tv);
playstats[playcntr].endaddr = sc->sc_voc[GUS_VOICE_LEFT].end_addr;
playstats[playcntr].voccntl = sc->sc_voc[GUS_VOICE_LEFT].voccntl;
playstats[playcntr].volcntl = sc->sc_voc[GUS_VOICE_LEFT].volcntl;
playstats[playcntr].playbuf = sc->sc_playbuf;
playstats[playcntr].dmabuf = sc->sc_dmabuf;
playstats[playcntr].bufcnt = sc->sc_bufcnt;
playstats[playcntr++].curaddr = gus_get_curaddr(sc, GUS_VOICE_LEFT);
playcntr = playcntr % NDMARECS;
}
#endif
bus_space_write_1(iot, ioh2, GUS_VOICE_SELECT, GUS_VOICE_LEFT);
SELECT_GUS_REG(iot, ioh2, GUSREG_VOICE_CNTL);
bus_space_write_1(iot, ioh2, GUS_DATA_HIGH, sc->sc_voc[GUS_VOICE_LEFT].voccntl);
SELECT_GUS_REG(iot, ioh2, GUSREG_VOLUME_CONTROL);
bus_space_write_1(iot, ioh2, GUS_DATA_HIGH, sc->sc_voc[GUS_VOICE_LEFT].volcntl);
}
}
gus_bufcnt[sc->sc_bufcnt-1]++;
sc->sc_dmabuf = (sc->sc_dmabuf + 1) % sc->sc_nbufs;
if (sc->sc_dmaoutintr && sc->sc_bufcnt < sc->sc_nbufs) {
void (*pfunc)(void *) = sc->sc_dmaoutintr;
void *arg = sc->sc_outarg;
sc->sc_outarg = 0;
sc->sc_dmaoutintr = 0;
(*pfunc)(arg);
}
}
int
gus_voice_intr(struct gus_softc *sc)
{
bus_space_tag_t iot = sc->sc_iot;
bus_space_handle_t ioh2 = sc->sc_ioh2;
int ignore = 0, voice, rval = 0;
unsigned char intr, status;
while(1) {
SELECT_GUS_REG(iot, ioh2, GUSREG_IRQ_STATUS);
intr = bus_space_read_1(iot, ioh2, GUS_DATA_HIGH);
if ((intr & (GUSMASK_WIRQ_VOLUME | GUSMASK_WIRQ_VOICE))
== (GUSMASK_WIRQ_VOLUME | GUSMASK_WIRQ_VOICE))
return rval;
if ((intr & GUSMASK_WIRQ_VOICE) == 0) {
rval = 1;
voice = intr & GUSMASK_WIRQ_VOICEMASK;
if ((1 << voice) & ignore)
break;
ignore |= 1 << voice;
SELECT_GUS_REG(iot, ioh2, GUSREG_VOICE_CNTL+0x80);
status = bus_space_read_1(iot, ioh2, GUS_DATA_HIGH);
if (status & GUSMASK_VOICE_STOPPED) {
if (voice != GUS_VOICE_LEFT) {
DMAPRINTF(("%s: spurious voice %d stop?\n",
sc->sc_dev.dv_xname, voice));
gus_stop_voice(sc, voice, 0);
continue;
}
gus_stop_voice(sc, voice, 1);
gus_stop_voice(sc, GUS_VOICE_RIGHT, 0);
sc->sc_bufcnt--;
if (sc->sc_bufcnt > 0) {
printf("%s: stopped voice not drained? (%x)\n",
sc->sc_dev.dv_xname, sc->sc_bufcnt);
gus_falsestops++;
sc->sc_playbuf = (sc->sc_playbuf + 1) % sc->sc_nbufs;
gus_start_playing(sc, sc->sc_playbuf);
} else if (sc->sc_bufcnt < 0) {
panic("%s: negative bufcnt in stopped voice",
sc->sc_dev.dv_xname);
} else {
sc->sc_playbuf = -1;
gus_stops++;
}
} else if (sc->sc_bufcnt != 0) {
gus_continues++;
if (gus_continue_playing(sc, voice)) {
gus_stop_voice(sc, GUS_VOICE_LEFT, 1);
gus_stop_voice(sc, GUS_VOICE_RIGHT, 0);
sc->sc_playbuf = -1;
gus_stops++;
}
}
if (sc->sc_dmaoutintr && !(sc->sc_flags & GUS_LOCKED)) {
if (sc->sc_dmaoutintr == stereo_dmaintr)
printf("gusdmaout botch?\n");
else {
void (*pfunc)(void *) = sc->sc_dmaoutintr;
void *arg = sc->sc_outarg;
sc->sc_outarg = 0;
sc->sc_dmaoutintr = 0;
(*pfunc)(arg);
}
}
}
}
return 0;
}
void
gus_start_playing(struct gus_softc *sc, int bufno)
{
bus_space_tag_t iot = sc->sc_iot;
bus_space_handle_t ioh2 = sc->sc_ioh2;
if (sc->sc_bufcnt == 1) {
sc->sc_voc[GUS_VOICE_LEFT].voccntl &= ~(GUSMASK_LOOP_ENABLE);
sc->sc_voc[GUS_VOICE_LEFT].volcntl &= ~(GUSMASK_VOICE_ROLL);
} else {
if (bufno == sc->sc_nbufs - 1) {
sc->sc_voc[GUS_VOICE_LEFT].voccntl |= GUSMASK_LOOP_ENABLE;
sc->sc_voc[GUS_VOICE_LEFT].volcntl &= ~(GUSMASK_VOICE_ROLL);
} else {
sc->sc_voc[GUS_VOICE_LEFT].voccntl &= ~GUSMASK_LOOP_ENABLE;
sc->sc_voc[GUS_VOICE_LEFT].volcntl |= GUSMASK_VOICE_ROLL;
}
}
bus_space_write_1(iot, ioh2, GUS_VOICE_SELECT, GUS_VOICE_LEFT);
SELECT_GUS_REG(iot, ioh2, GUSREG_VOICE_CNTL);
bus_space_write_1(iot, ioh2, GUS_DATA_HIGH, sc->sc_voc[GUS_VOICE_LEFT].voccntl);
SELECT_GUS_REG(iot, ioh2, GUSREG_VOLUME_CONTROL);
bus_space_write_1(iot, ioh2, GUS_DATA_HIGH, sc->sc_voc[GUS_VOICE_LEFT].volcntl);
sc->sc_voc[GUS_VOICE_LEFT].current_addr =
GUS_MEM_OFFSET + sc->sc_chanblocksize * bufno;
sc->sc_voc[GUS_VOICE_LEFT].end_addr =
sc->sc_voc[GUS_VOICE_LEFT].current_addr + sc->sc_chanblocksize - 1;
sc->sc_voc[GUS_VOICE_RIGHT].current_addr =
sc->sc_voc[GUS_VOICE_LEFT].current_addr +
(gus_dostereo && sc->sc_channels == 2 ? GUS_LEFT_RIGHT_OFFSET : 0);
sc->sc_voc[GUS_VOICE_RIGHT].voccntl |= GUSMASK_LOOP_ENABLE;
sc->sc_voc[GUS_VOICE_RIGHT].volcntl &= ~(GUSMASK_VOICE_ROLL);
#ifdef GUSPLAYDEBUG
if (gusstats) {
microtime(&playstats[playcntr].tv);
playstats[playcntr].curaddr = sc->sc_voc[GUS_VOICE_LEFT].current_addr;
playstats[playcntr].voccntl = sc->sc_voc[GUS_VOICE_LEFT].voccntl;
playstats[playcntr].volcntl = sc->sc_voc[GUS_VOICE_LEFT].volcntl;
playstats[playcntr].endaddr = sc->sc_voc[GUS_VOICE_LEFT].end_addr;
playstats[playcntr].playbuf = bufno;
playstats[playcntr].dmabuf = sc->sc_dmabuf;
playstats[playcntr].bufcnt = sc->sc_bufcnt;
playstats[playcntr++].vaction = 5;
playcntr = playcntr % NDMARECS;
}
#endif
bus_space_write_1(iot, ioh2, GUS_VOICE_SELECT, GUS_VOICE_RIGHT);
SELECT_GUS_REG(iot, ioh2, GUSREG_VOICE_CNTL);
bus_space_write_1(iot, ioh2, GUS_DATA_HIGH, sc->sc_voc[GUS_VOICE_RIGHT].voccntl);
SELECT_GUS_REG(iot, ioh2, GUSREG_VOLUME_CONTROL);
bus_space_write_1(iot, ioh2, GUS_DATA_HIGH, sc->sc_voc[GUS_VOICE_RIGHT].volcntl);
gus_start_voice(sc, GUS_VOICE_RIGHT, 0);
gus_start_voice(sc, GUS_VOICE_LEFT, 1);
if (sc->sc_playbuf == -1)
sc->sc_playbuf = bufno;
}
int
gus_continue_playing(struct gus_softc *sc, int voice)
{
bus_space_tag_t iot = sc->sc_iot;
bus_space_handle_t ioh2 = sc->sc_ioh2;
SELECT_GUS_REG(iot, ioh2, GUSREG_VOICE_CNTL);
bus_space_write_1(iot, ioh2, GUS_DATA_HIGH, sc->sc_voc[voice].voccntl & ~(GUSMASK_VOICE_IRQ));
sc->sc_playbuf = (sc->sc_playbuf + 1) % sc->sc_nbufs;
if (--sc->sc_bufcnt == 0) {
DPRINTF(("gus: bufcnt 0 on continuing voice?\n"));
}
if (sc->sc_playbuf == sc->sc_dmabuf && (sc->sc_flags & GUS_LOCKED)) {
printf("%s: continue into active dmabuf?\n", sc->sc_dev.dv_xname);
return 1;
}
gus_set_endaddr(sc, voice, GUS_MEM_OFFSET +
sc->sc_chanblocksize * (sc->sc_playbuf + 1) - 1);
if (sc->sc_bufcnt < 2) {
sc->sc_voc[voice].voccntl &= ~GUSMASK_LOOP_ENABLE;
sc->sc_voc[voice].volcntl &= ~GUSMASK_VOICE_ROLL;
playstats[playcntr].vaction = 0;
} else {
if (sc->sc_playbuf == sc->sc_nbufs - 1) {
sc->sc_voc[voice].voccntl |= GUSMASK_LOOP_ENABLE;
sc->sc_voc[voice].volcntl &= ~GUSMASK_VOICE_ROLL;
playstats[playcntr].vaction = 1;
} else {
sc->sc_voc[voice].voccntl &= ~GUSMASK_LOOP_ENABLE;
sc->sc_voc[voice].volcntl |= GUSMASK_VOICE_ROLL;
playstats[playcntr].vaction = 2;
}
}
#ifdef GUSPLAYDEBUG
if (gusstats) {
microtime(&playstats[playcntr].tv);
playstats[playcntr].curaddr = gus_get_curaddr(sc, voice);
playstats[playcntr].voccntl = sc->sc_voc[voice].voccntl;
playstats[playcntr].volcntl = sc->sc_voc[voice].volcntl;
playstats[playcntr].endaddr = sc->sc_voc[voice].end_addr;
playstats[playcntr].playbuf = sc->sc_playbuf;
playstats[playcntr].dmabuf = sc->sc_dmabuf;
playstats[playcntr++].bufcnt = sc->sc_bufcnt;
playcntr = playcntr % NDMARECS;
}
#endif
SELECT_GUS_REG(iot, ioh2, GUSREG_VOICE_CNTL);
bus_space_write_1(iot, ioh2, GUS_DATA_HIGH, sc->sc_voc[voice].voccntl);
SELECT_GUS_REG(iot, ioh2, GUSREG_VOLUME_CONTROL);
bus_space_write_1(iot, ioh2, GUS_DATA_HIGH, sc->sc_voc[voice].volcntl);
return 0;
}
void
gusdmaout(struct gus_softc *sc, int flags, u_long gusaddr, caddr_t buffaddr,
int length)
{
unsigned char c = (unsigned char) flags;
bus_space_tag_t iot = sc->sc_iot;
bus_space_handle_t ioh2 = sc->sc_ioh2;
DMAPRINTF(("gusdmaout flags=%x scflags=%x\n", flags, sc->sc_flags));
sc->sc_gusaddr = gusaddr;
if (sc->sc_drq >= 4) {
c |= GUSMASK_DMA_WIDTH;
gusaddr = convert_to_16bit(gusaddr);
}
c |= GUSMASK_DMA_ENABLE | GUSMASK_DMA_R0 | GUSMASK_DMA_IRQ;
SELECT_GUS_REG(iot, ioh2, GUSREG_DMA_CONTROL);
bus_space_write_1(iot, ioh2, GUS_DATA_HIGH, 0);
sc->sc_dmaoutaddr = (u_char *) buffaddr;
sc->sc_dmaoutcnt = length;
isa_dmastart(sc->sc_dev.dv_parent, sc->sc_drq, buffaddr, length,
NULL, DMAMODE_WRITE, BUS_DMA_NOWAIT);
sc->sc_flags |= GUS_DMAOUT_ACTIVE;
SELECT_GUS_REG(iot, ioh2, GUSREG_DMA_START);
bus_space_write_2(iot, ioh2, GUS_DATA_LOW, (int) (gusaddr >> 4));
SELECT_GUS_REG(iot, ioh2, GUSREG_DMA_CONTROL);
bus_space_write_1(iot, ioh2, GUS_DATA_HIGH, c);
timeout_add_sec(&sc->sc_dma_tmo, 1);
}
void
gus_start_voice(struct gus_softc *sc, int voice, int intrs)
{
bus_space_tag_t iot = sc->sc_iot;
bus_space_handle_t ioh2 = sc->sc_ioh2;
u_long start;
u_long current;
u_long end;
start = sc->sc_voc[voice].start_addr;
current = sc->sc_voc[voice].current_addr;
end = sc->sc_voc[voice].end_addr;
if (sc->sc_voc[voice].voccntl & GUSMASK_DATA_SIZE16) {
start = convert_to_16bit(start-1);
current = convert_to_16bit(current);
end = convert_to_16bit(end);
}
bus_space_write_1(iot, ioh2, GUS_VOICE_SELECT, (unsigned char) voice);
SELECT_GUS_REG(iot, ioh2, GUSREG_START_ADDR_HIGH);
bus_space_write_2(iot, ioh2, GUS_DATA_LOW, ADDR_HIGH(start));
SELECT_GUS_REG(iot, ioh2, GUSREG_START_ADDR_LOW);
bus_space_write_2(iot, ioh2, GUS_DATA_LOW, ADDR_LOW(start));
SELECT_GUS_REG(iot, ioh2, GUSREG_CUR_ADDR_HIGH);
bus_space_write_2(iot, ioh2, GUS_DATA_LOW, ADDR_HIGH(current));
SELECT_GUS_REG(iot, ioh2, GUSREG_CUR_ADDR_LOW);
bus_space_write_2(iot, ioh2, GUS_DATA_LOW, ADDR_LOW(current));
SELECT_GUS_REG(iot, ioh2, GUSREG_END_ADDR_HIGH);
bus_space_write_2(iot, ioh2, GUS_DATA_LOW, ADDR_HIGH(end));
SELECT_GUS_REG(iot, ioh2, GUSREG_END_ADDR_LOW);
bus_space_write_2(iot, ioh2, GUS_DATA_LOW, ADDR_LOW(end));
if (intrs) {
sc->sc_flags |= GUS_PLAYING;
sc->sc_voc[voice].voccntl |= GUSMASK_VOICE_IRQ;
DMAPRINTF(("gus voice playing=%x\n", sc->sc_flags));
} else
sc->sc_voc[voice].voccntl &= ~GUSMASK_VOICE_IRQ;
sc->sc_voc[voice].voccntl &= ~(GUSMASK_VOICE_STOPPED |
GUSMASK_STOP_VOICE);
SELECT_GUS_REG(iot, ioh2, GUSREG_START_VOLUME);
bus_space_write_1(iot, ioh2, GUS_DATA_HIGH, 0x00);
SELECT_GUS_REG(iot, ioh2, GUSREG_END_VOLUME);
bus_space_write_1(iot, ioh2, GUS_DATA_HIGH, sc->sc_voc[voice].current_volume >> 4);
SELECT_GUS_REG(iot, ioh2, GUSREG_CUR_VOLUME);
bus_space_write_2(iot, ioh2, GUS_DATA_LOW, 0x00);
SELECT_GUS_REG(iot, ioh2, GUSREG_VOLUME_RATE);
bus_space_write_1(iot, ioh2, GUS_DATA_HIGH, 63);
SELECT_GUS_REG(iot, ioh2, GUSREG_VOICE_CNTL);
bus_space_write_1(iot, ioh2, GUS_DATA_HIGH, sc->sc_voc[voice].voccntl);
SELECT_GUS_REG(iot, ioh2, GUSREG_VOLUME_CONTROL);
bus_space_write_1(iot, ioh2, GUS_DATA_HIGH, 0x00);
delay(50);
SELECT_GUS_REG(iot, ioh2, GUSREG_VOICE_CNTL);
bus_space_write_1(iot, ioh2, GUS_DATA_HIGH, sc->sc_voc[voice].voccntl);
SELECT_GUS_REG(iot, ioh2, GUSREG_VOLUME_CONTROL);
bus_space_write_1(iot, ioh2, GUS_DATA_HIGH, 0x00);
}
void
gus_stop_voice(struct gus_softc *sc, int voice, int intrs_too)
{
bus_space_tag_t iot = sc->sc_iot;
bus_space_handle_t ioh2 = sc->sc_ioh2;
sc->sc_voc[voice].voccntl |= GUSMASK_VOICE_STOPPED |
GUSMASK_STOP_VOICE;
if (intrs_too) {
sc->sc_voc[voice].voccntl &= ~(GUSMASK_VOICE_IRQ);
sc->sc_flags &= ~GUS_PLAYING;
}
DMAPRINTF(("gusintr voice notplaying=%x\n", sc->sc_flags));
guspoke(iot, ioh2, 0L, 0);
bus_space_write_1(iot, ioh2, GUS_VOICE_SELECT, (unsigned char) voice);
SELECT_GUS_REG(iot, ioh2, GUSREG_CUR_VOLUME);
bus_space_write_2(iot, ioh2, GUS_DATA_LOW, 0x0000);
SELECT_GUS_REG(iot, ioh2, GUSREG_VOICE_CNTL);
bus_space_write_1(iot, ioh2, GUS_DATA_HIGH, sc->sc_voc[voice].voccntl);
delay(100);
SELECT_GUS_REG(iot, ioh2, GUSREG_CUR_VOLUME);
bus_space_write_2(iot, ioh2, GUS_DATA_LOW, 0x0000);
SELECT_GUS_REG(iot, ioh2, GUSREG_VOICE_CNTL);
bus_space_write_1(iot, ioh2, GUS_DATA_HIGH, sc->sc_voc[voice].voccntl);
SELECT_GUS_REG(iot, ioh2, GUSREG_CUR_ADDR_HIGH);
bus_space_write_2(iot, ioh2, GUS_DATA_LOW, 0x0000);
SELECT_GUS_REG(iot, ioh2, GUSREG_CUR_ADDR_LOW);
bus_space_write_2(iot, ioh2, GUS_DATA_LOW, 0x0000);
}
void
gus_set_volume(struct gus_softc *sc, int voice, int volume)
{
bus_space_tag_t iot = sc->sc_iot;
bus_space_handle_t ioh2 = sc->sc_ioh2;
unsigned int gusvol;
gusvol = gus_log_volumes[volume < 512 ? volume : 511];
sc->sc_voc[voice].current_volume = gusvol;
bus_space_write_1(iot, ioh2, GUS_VOICE_SELECT, (unsigned char) voice);
SELECT_GUS_REG(iot, ioh2, GUSREG_START_VOLUME);
bus_space_write_1(iot, ioh2, GUS_DATA_HIGH, (unsigned char) (gusvol >> 4));
SELECT_GUS_REG(iot, ioh2, GUSREG_END_VOLUME);
bus_space_write_1(iot, ioh2, GUS_DATA_HIGH, (unsigned char) (gusvol >> 4));
SELECT_GUS_REG(iot, ioh2, GUSREG_CUR_VOLUME);
bus_space_write_2(iot, ioh2, GUS_DATA_LOW, gusvol << 4);
delay(500);
bus_space_write_2(iot, ioh2, GUS_DATA_LOW, gusvol << 4);
}
int
gusmax_set_params(void *addr, int setmode, int usemode, struct audio_params *p,
struct audio_params *r)
{
struct ad1848_softc *ac = addr;
struct gus_softc *sc = ac->parent;
int error;
error = ad1848_set_params(ac, setmode, usemode, p, r);
if (error)
return error;
error = gus_set_params(sc, setmode, usemode, p, r);
return error;
}
int
gus_set_params(void *addr, int setmode, int usemode, struct audio_params *p,
struct audio_params *r)
{
struct gus_softc *sc = addr;
switch (p->encoding) {
case AUDIO_ENCODING_SLINEAR_LE:
case AUDIO_ENCODING_ULINEAR_LE:
break;
default:
return (EINVAL);
}
mtx_enter(&audio_lock);
if (p->precision == 8) {
sc->sc_voc[GUS_VOICE_LEFT].voccntl &= ~GUSMASK_DATA_SIZE16;
sc->sc_voc[GUS_VOICE_RIGHT].voccntl &= ~GUSMASK_DATA_SIZE16;
} else {
sc->sc_voc[GUS_VOICE_LEFT].voccntl |= GUSMASK_DATA_SIZE16;
sc->sc_voc[GUS_VOICE_RIGHT].voccntl |= GUSMASK_DATA_SIZE16;
}
sc->sc_encoding = p->encoding;
sc->sc_precision = p->precision;
sc->sc_channels = p->channels;
mtx_leave(&audio_lock);
if (p->sample_rate > gus_max_frequency[sc->sc_voices - GUS_MIN_VOICES])
p->sample_rate = gus_max_frequency[sc->sc_voices - GUS_MIN_VOICES];
if (setmode & AUMODE_RECORD)
sc->sc_irate = p->sample_rate;
if (setmode & AUMODE_PLAY)
sc->sc_orate = p->sample_rate;
p->bps = AUDIO_BPS(p->precision);
r->bps = AUDIO_BPS(r->precision);
p->msb = r->msb = 1;
return 0;
}
int
gusmax_round_blocksize(void *addr, int blocksize)
{
struct ad1848_softc *ac = addr;
struct gus_softc *sc = ac->parent;
return gus_round_blocksize(sc, blocksize);
}
int
gus_round_blocksize(void *addr, int blocksize)
{
struct gus_softc *sc = addr;
DPRINTF(("gus_round_blocksize called\n"));
if ((sc->sc_encoding == AUDIO_ENCODING_ULAW ||
sc->sc_encoding == AUDIO_ENCODING_ALAW) && blocksize > 32768)
blocksize = 32768;
else if (blocksize > 65536)
blocksize = 65536;
if ((blocksize % GUS_BUFFER_MULTIPLE) != 0)
blocksize = (blocksize / GUS_BUFFER_MULTIPLE + 1) *
GUS_BUFFER_MULTIPLE;
if (sc->sc_deintr_buf) {
free(sc->sc_deintr_buf, M_DEVBUF, 0);
sc->sc_deintr_buf = NULL;
}
sc->sc_deintr_buf = malloc(blocksize/2, M_DEVBUF, M_WAITOK);
sc->sc_blocksize = blocksize;
sc->sc_nbufs = 2;
gus_set_chan_addrs(sc);
return blocksize;
}
int
gus_get_out_gain(caddr_t addr)
{
struct gus_softc *sc = (struct gus_softc *) addr;
DPRINTF(("gus_get_out_gain called\n"));
return sc->sc_ogain / 2;
}
inline void
gus_set_voices(struct gus_softc *sc, int voices)
{
bus_space_tag_t iot = sc->sc_iot;
bus_space_handle_t ioh2 = sc->sc_ioh2;
SELECT_GUS_REG(iot, ioh2, GUSREG_ACTIVE_VOICES);
bus_space_write_1(iot, ioh2, GUS_DATA_HIGH, (voices-1) | 0xc0);
sc->sc_voices = voices;
}
int
gusmax_commit_settings(void *addr)
{
struct ad1848_softc *ac = addr;
struct gus_softc *sc = ac->parent;
int error;
error = ad1848_commit_settings(ac);
if (error)
return error;
return gus_commit_settings(sc);
}
int
gus_commit_settings(void *addr)
{
struct gus_softc *sc = addr;
DPRINTF(("gus_commit_settings called (gain = %d)\n",sc->sc_ogain));
mtx_enter(&audio_lock);
gus_set_recrate(sc, sc->sc_irate);
gus_set_volume(sc, GUS_VOICE_LEFT, sc->sc_ogain);
gus_set_volume(sc, GUS_VOICE_RIGHT, sc->sc_ogain);
gus_set_samprate(sc, GUS_VOICE_LEFT, sc->sc_orate);
gus_set_samprate(sc, GUS_VOICE_RIGHT, sc->sc_orate);
mtx_leave(&audio_lock);
gus_set_chan_addrs(sc);
return 0;
}
void
gus_set_chan_addrs(struct gus_softc *sc)
{
if (sc->sc_channels == 2)
sc->sc_chanblocksize = sc->sc_blocksize/2;
else
sc->sc_chanblocksize = sc->sc_blocksize;
sc->sc_voc[GUS_VOICE_LEFT].start_addr = GUS_MEM_OFFSET - 1;
sc->sc_voc[GUS_VOICE_RIGHT].start_addr =
(gus_dostereo && sc->sc_channels == 2 ? GUS_LEFT_RIGHT_OFFSET : 0)
+ GUS_MEM_OFFSET - 1;
sc->sc_voc[GUS_VOICE_RIGHT].current_addr =
sc->sc_voc[GUS_VOICE_RIGHT].start_addr + 1;
sc->sc_voc[GUS_VOICE_RIGHT].end_addr =
sc->sc_voc[GUS_VOICE_RIGHT].start_addr +
sc->sc_nbufs * sc->sc_chanblocksize;
}
void
gus_set_samprate(struct gus_softc *sc, int voice, int freq)
{
bus_space_tag_t iot = sc->sc_iot;
bus_space_handle_t ioh2 = sc->sc_ioh2;
unsigned int fc;
u_long temp, f = (u_long) freq;
temp = (u_long) gus_max_frequency[sc->sc_voices-GUS_MIN_VOICES];
fc = (unsigned int)(((f << 9L) + (temp >> 1L)) / temp);
fc <<= 1;
bus_space_write_1(iot, ioh2, GUS_VOICE_SELECT, (unsigned char) voice);
SELECT_GUS_REG(iot, ioh2, GUSREG_FREQ_CONTROL);
bus_space_write_2(iot, ioh2, GUS_DATA_LOW, fc);
sc->sc_voc[voice].rate = freq;
}
void
gus_set_recrate(struct gus_softc *sc, u_long rate)
{
bus_space_tag_t iot = sc->sc_iot;
bus_space_handle_t ioh2 = sc->sc_ioh2;
u_char realrate;
DPRINTF(("gus_set_recrate %lu\n", rate));
#if 0
realrate = 9878400/(16*(rate+2));
#endif
realrate = (9878400 >> 4)/rate - 2;
SELECT_GUS_REG(iot, ioh2, GUSREG_SAMPLE_FREQ);
bus_space_write_1(iot, ioh2, GUS_DATA_HIGH, realrate);
}
int
gus_speaker_ctl(void *addr, int newstate)
{
struct gus_softc *sc = (struct gus_softc *) addr;
bus_space_tag_t iot = sc->sc_iot;
bus_space_handle_t ioh1 = sc->sc_ioh1;
if ((newstate == SPKR_ON) &&
(sc->sc_mixcontrol & GUSMASK_LINE_OUT)) {
sc->sc_mixcontrol &= ~GUSMASK_LINE_OUT;
bus_space_write_1(iot, ioh1, GUS_MIX_CONTROL, sc->sc_mixcontrol);
}
if ((newstate == SPKR_OFF) &&
(sc->sc_mixcontrol & GUSMASK_LINE_OUT) == 0) {
sc->sc_mixcontrol |= GUSMASK_LINE_OUT;
bus_space_write_1(iot, ioh1, GUS_MIX_CONTROL, sc->sc_mixcontrol);
}
return 0;
}
int
gus_linein_ctl(void *addr, int newstate)
{
struct gus_softc *sc = (struct gus_softc *) addr;
bus_space_tag_t iot = sc->sc_iot;
bus_space_handle_t ioh1 = sc->sc_ioh1;
if ((newstate == SPKR_ON) &&
(sc->sc_mixcontrol & GUSMASK_LINE_IN)) {
sc->sc_mixcontrol &= ~GUSMASK_LINE_IN;
bus_space_write_1(iot, ioh1, GUS_MIX_CONTROL, sc->sc_mixcontrol);
}
if ((newstate == SPKR_OFF) &&
(sc->sc_mixcontrol & GUSMASK_LINE_IN) == 0) {
sc->sc_mixcontrol |= GUSMASK_LINE_IN;
bus_space_write_1(iot, ioh1, GUS_MIX_CONTROL, sc->sc_mixcontrol);
}
return 0;
}
int
gus_mic_ctl(void *addr, int newstate)
{
struct gus_softc *sc = (struct gus_softc *) addr;
bus_space_tag_t iot = sc->sc_iot;
bus_space_handle_t ioh1 = sc->sc_ioh1;
if ((newstate == SPKR_ON) &&
(sc->sc_mixcontrol & GUSMASK_MIC_IN) == 0) {
sc->sc_mixcontrol |= GUSMASK_MIC_IN;
bus_space_write_1(iot, ioh1, GUS_MIX_CONTROL, sc->sc_mixcontrol);
}
if ((newstate == SPKR_OFF) &&
(sc->sc_mixcontrol & GUSMASK_MIC_IN)) {
sc->sc_mixcontrol &= ~GUSMASK_MIC_IN;
bus_space_write_1(iot, ioh1, GUS_MIX_CONTROL, sc->sc_mixcontrol);
}
return 0;
}
void
gus_set_endaddr(struct gus_softc *sc, int voice, u_long addr)
{
bus_space_tag_t iot = sc->sc_iot;
bus_space_handle_t ioh2 = sc->sc_ioh2;
sc->sc_voc[voice].end_addr = addr;
if (sc->sc_voc[voice].voccntl & GUSMASK_DATA_SIZE16)
addr = convert_to_16bit(addr);
SELECT_GUS_REG(iot, ioh2, GUSREG_END_ADDR_HIGH);
bus_space_write_2(iot, ioh2, GUS_DATA_LOW, ADDR_HIGH(addr));
SELECT_GUS_REG(iot, ioh2, GUSREG_END_ADDR_LOW);
bus_space_write_2(iot, ioh2, GUS_DATA_LOW, ADDR_LOW(addr));
}
#ifdef GUSPLAYDEBUG
void
gus_set_curaddr(struct gus_softc *sc, int voice, u_long addr)
{
bus_space_tag_t iot = sc->sc_iot;
bus_space_handle_t ioh2 = sc->sc_ioh2;
sc->sc_voc[voice].current_addr = addr;
if (sc->sc_voc[voice].voccntl & GUSMASK_DATA_SIZE16)
addr = convert_to_16bit(addr);
bus_space_write_1(iot, ioh2, GUS_VOICE_SELECT, (unsigned char) voice);
SELECT_GUS_REG(iot, ioh2, GUSREG_CUR_ADDR_HIGH);
bus_space_write_2(iot, ioh2, GUS_DATA_LOW, ADDR_HIGH(addr));
SELECT_GUS_REG(iot, ioh2, GUSREG_CUR_ADDR_LOW);
bus_space_write_2(iot, ioh2, GUS_DATA_LOW, ADDR_LOW(addr));
}
u_long
gus_get_curaddr(struct gus_softc *sc, int voice)
{
bus_space_tag_t iot = sc->sc_iot;
bus_space_handle_t ioh2 = sc->sc_ioh2;
u_long addr;
bus_space_write_1(iot, ioh2, GUS_VOICE_SELECT, (unsigned char) voice);
SELECT_GUS_REG(iot, ioh2, GUSREG_CUR_ADDR_HIGH|GUSREG_READ);
addr = (bus_space_read_2(iot, ioh2, GUS_DATA_LOW) & 0x1fff) << 7;
SELECT_GUS_REG(iot, ioh2, GUSREG_CUR_ADDR_LOW|GUSREG_READ);
addr |= (bus_space_read_2(iot, ioh2, GUS_DATA_LOW) >> 9L) & 0x7f;
if (sc->sc_voc[voice].voccntl & GUSMASK_DATA_SIZE16)
addr = (addr & 0xc0000) | ((addr & 0x1ffff) << 1);
DPRINTF(("gus voice %d curaddr %ld end_addr %ld\n",
voice, addr, sc->sc_voc[voice].end_addr));
return(addr);
}
#endif
u_long
convert_to_16bit(u_long address)
{
u_long old_address;
old_address = address;
address >>= 1;
address &= 0x0001ffffL;
address |= (old_address & 0x000c0000L);
return (address);
}
void
guspoke(bus_space_tag_t iot, bus_space_handle_t ioh2, long address,
unsigned char value)
{
SELECT_GUS_REG(iot, ioh2, GUSREG_DRAM_ADDR_LOW);
bus_space_write_2(iot, ioh2, GUS_DATA_LOW, (unsigned int) (address & 0xffff));
SELECT_GUS_REG(iot, ioh2, GUSREG_DRAM_ADDR_HIGH);
bus_space_write_1(iot, ioh2, GUS_DATA_HIGH, (unsigned char) ((address >> 16) & 0xff));
bus_space_write_1(iot, ioh2, GUS_DRAM_DATA, value);
}
unsigned char
guspeek(bus_space_tag_t iot, bus_space_handle_t ioh2, u_long address)
{
SELECT_GUS_REG(iot, ioh2, GUSREG_DRAM_ADDR_LOW);
bus_space_write_2(iot, ioh2, GUS_DATA_LOW, (unsigned int) (address & 0xffff));
SELECT_GUS_REG(iot, ioh2, GUSREG_DRAM_ADDR_HIGH);
bus_space_write_1(iot, ioh2, GUS_DATA_HIGH, (unsigned char) ((address >> 16) & 0xff));
return (unsigned char) bus_space_read_1(iot, ioh2, GUS_DRAM_DATA);
}
void
gusreset(struct gus_softc *sc, int voices)
{
bus_space_tag_t iot = sc->sc_iot;
bus_space_handle_t ioh1 = sc->sc_ioh1;
bus_space_handle_t ioh2 = sc->sc_ioh2;
bus_space_handle_t ioh4 = sc->sc_ioh4;
int i;
mtx_enter(&audio_lock);
SELECT_GUS_REG(iot, ioh2, GUSREG_RESET);
bus_space_write_1(iot, ioh2, GUS_DATA_HIGH, 0x00);
delay(500);
SELECT_GUS_REG(iot, ioh2, GUSREG_RESET);
bus_space_write_1(iot, ioh2, GUS_DATA_HIGH, GUSMASK_MASTER_RESET);
delay(500);
if (ioh4 != (bus_space_handle_t)NULL) {
bus_space_write_1(iot, ioh4, GUS_MIDI_CONTROL, MIDI_RESET);
delay(500);
bus_space_write_1(iot, ioh4, GUS_MIDI_CONTROL, 0x00);
}
SELECT_GUS_REG(iot, ioh2, GUSREG_DMA_CONTROL);
bus_space_write_1(iot, ioh2, GUS_DATA_HIGH, 0x00);
SELECT_GUS_REG(iot, ioh2, GUSREG_TIMER_CONTROL);
bus_space_write_1(iot, ioh2, GUS_DATA_HIGH, 0x00);
SELECT_GUS_REG(iot, ioh2, GUSREG_SAMPLE_CONTROL);
bus_space_write_1(iot, ioh2, GUS_DATA_HIGH, 0x00);
gus_set_voices(sc, voices);
bus_space_read_1(iot, ioh1, GUS_IRQ_STATUS);
SELECT_GUS_REG(iot, ioh2, GUSREG_DMA_CONTROL);
bus_space_read_1(iot, ioh2, GUS_DATA_HIGH);
SELECT_GUS_REG(iot, ioh2, GUSREG_SAMPLE_CONTROL);
bus_space_read_1(iot, ioh2, GUS_DATA_HIGH);
SELECT_GUS_REG(iot, ioh2, GUSREG_IRQ_STATUS);
bus_space_read_1(iot, ioh2, GUS_DATA_HIGH);
for(i = 0; i < voices; i++) {
bus_space_write_1(iot, ioh2, GUS_VOICE_SELECT, (unsigned char) i);
SELECT_GUS_REG(iot, ioh2, GUSREG_VOICE_CNTL);
sc->sc_voc[i].voccntl = GUSMASK_VOICE_STOPPED |
GUSMASK_STOP_VOICE;
bus_space_write_1(iot, ioh2, GUS_DATA_HIGH, sc->sc_voc[i].voccntl);
sc->sc_voc[i].volcntl = GUSMASK_VOLUME_STOPPED |
GUSMASK_STOP_VOLUME;
SELECT_GUS_REG(iot, ioh2, GUSREG_VOLUME_CONTROL);
bus_space_write_1(iot, ioh2, GUS_DATA_HIGH, sc->sc_voc[i].volcntl);
delay(100);
gus_set_samprate(sc, i, 8000);
SELECT_GUS_REG(iot, ioh2, GUSREG_START_ADDR_HIGH);
bus_space_write_2(iot, ioh2, GUS_DATA_LOW, 0x0000);
SELECT_GUS_REG(iot, ioh2, GUSREG_START_ADDR_LOW);
bus_space_write_2(iot, ioh2, GUS_DATA_LOW, 0x0000);
SELECT_GUS_REG(iot, ioh2, GUSREG_END_ADDR_HIGH);
bus_space_write_2(iot, ioh2, GUS_DATA_LOW, 0x0000);
SELECT_GUS_REG(iot, ioh2, GUSREG_END_ADDR_LOW);
bus_space_write_2(iot, ioh2, GUS_DATA_LOW, 0x0000);
SELECT_GUS_REG(iot, ioh2, GUSREG_VOLUME_RATE);
bus_space_write_1(iot, ioh2, GUS_DATA_HIGH, 0x01);
SELECT_GUS_REG(iot, ioh2, GUSREG_START_VOLUME);
bus_space_write_1(iot, ioh2, GUS_DATA_HIGH, 0x10);
SELECT_GUS_REG(iot, ioh2, GUSREG_END_VOLUME);
bus_space_write_1(iot, ioh2, GUS_DATA_HIGH, 0xe0);
SELECT_GUS_REG(iot, ioh2, GUSREG_CUR_VOLUME);
bus_space_write_2(iot, ioh2, GUS_DATA_LOW, 0x0000);
SELECT_GUS_REG(iot, ioh2, GUSREG_CUR_ADDR_HIGH);
bus_space_write_2(iot, ioh2, GUS_DATA_LOW, 0x0000);
SELECT_GUS_REG(iot, ioh2, GUSREG_CUR_ADDR_LOW);
bus_space_write_2(iot, ioh2, GUS_DATA_LOW, 0x0000);
SELECT_GUS_REG(iot, ioh2, GUSREG_PAN_POS);
bus_space_write_1(iot, ioh2, GUS_DATA_HIGH, 0x07);
}
bus_space_read_1(iot, ioh1, GUS_IRQ_STATUS);
SELECT_GUS_REG(iot, ioh2, GUSREG_DMA_CONTROL);
bus_space_read_1(iot, ioh2, GUS_DATA_HIGH);
SELECT_GUS_REG(iot, ioh2, GUSREG_SAMPLE_CONTROL);
bus_space_read_1(iot, ioh2, GUS_DATA_HIGH);
SELECT_GUS_REG(iot, ioh2, GUSREG_IRQ_STATUS);
bus_space_read_1(iot, ioh2, GUS_DATA_HIGH);
SELECT_GUS_REG(iot, ioh2, GUSREG_RESET);
bus_space_write_1(iot, ioh2, GUS_DATA_HIGH, GUSMASK_MASTER_RESET | GUSMASK_DAC_ENABLE |
GUSMASK_IRQ_ENABLE);
mtx_leave(&audio_lock);
}
int
gus_init_cs4231(struct gus_softc *sc)
{
bus_space_tag_t iot = sc->sc_iot;
bus_space_handle_t ioh1 = sc->sc_ioh1;
int port = sc->sc_iobase;
u_char ctrl;
ctrl = (port & 0xf0) >> 4;
ctrl |= GUS_MAX_CODEC_ENABLE;
if (sc->sc_drq >= 4)
ctrl |= GUS_MAX_RECCHAN16;
if (sc->sc_recdrq >= 4)
ctrl |= GUS_MAX_PLAYCHAN16;
bus_space_write_1(iot, ioh1, GUS_MAX_CTRL, ctrl);
sc->sc_codec.sc_iot = sc->sc_iot;
sc->sc_codec.sc_iobase = port+GUS_MAX_CODEC_BASE;
if (ad1848_mapprobe(&sc->sc_codec, sc->sc_codec.sc_iobase) == 0) {
sc->sc_flags &= ~GUS_CODEC_INSTALLED;
return (0);
} else {
struct ad1848_volume vol = {AUDIO_MAX_GAIN, AUDIO_MAX_GAIN};
sc->sc_flags |= GUS_CODEC_INSTALLED;
sc->sc_codec.parent = sc;
sc->sc_codec.sc_drq = sc->sc_recdrq;
sc->sc_codec.sc_recdrq = sc->sc_drq;
sc->sc_mixcontrol &= ~GUSMASK_LINE_IN;
sc->sc_mixcontrol |= GUSMASK_MIC_IN;
bus_space_write_1(iot, ioh1, GUS_MIX_CONTROL, sc->sc_mixcontrol);
ad1848_attach(&sc->sc_codec);
ad1848_set_mic_gain(&sc->sc_codec, &vol);
return (1);
}
}
int
gus_set_in_gain(caddr_t addr, u_int gain, u_char balance)
{
DPRINTF(("gus_set_in_gain called\n"));
return 0;
}
int
gus_get_in_gain(caddr_t addr)
{
DPRINTF(("gus_get_in_gain called\n"));
return 0;
}
int
gusmax_dma_input(void *addr, void *buf, int size, void (*callback)(void *),
void *arg)
{
struct ad1848_softc *sc = addr;
return gus_dma_input(sc->parent, buf, size, callback, arg);
}
int
gus_dma_input(void *addr, void *buf, int size, void (*callback)(void *),
void *arg)
{
struct gus_softc *sc = addr;
bus_space_tag_t iot = sc->sc_iot;
bus_space_handle_t ioh2 = sc->sc_ioh2;
u_char dmac;
DMAPRINTF(("gus_dma_input called\n"));
if (sc->sc_precision == 16) {
return EINVAL;
}
dmac = GUSMASK_SAMPLE_IRQ|GUSMASK_SAMPLE_START;
if (sc->sc_recdrq >= 4)
dmac |= GUSMASK_SAMPLE_DATA16;
if (sc->sc_encoding == AUDIO_ENCODING_ULAW ||
sc->sc_encoding == AUDIO_ENCODING_ALAW ||
sc->sc_encoding == AUDIO_ENCODING_ULINEAR_LE ||
sc->sc_encoding == AUDIO_ENCODING_ULINEAR_BE)
dmac |= GUSMASK_SAMPLE_INVBIT;
if (sc->sc_channels == 2)
dmac |= GUSMASK_SAMPLE_STEREO;
isa_dmastart(sc->sc_dev.dv_parent, sc->sc_recdrq, buf, size,
NULL, DMAMODE_READ, BUS_DMA_NOWAIT);
DMAPRINTF(("gus_dma_input isadma_started\n"));
sc->sc_flags |= GUS_DMAIN_ACTIVE;
sc->sc_dmainintr = callback;
sc->sc_inarg = arg;
sc->sc_dmaincnt = size;
sc->sc_dmainaddr = buf;
SELECT_GUS_REG(iot, ioh2, GUSREG_SAMPLE_CONTROL);
bus_space_write_1(iot, ioh2, GUS_DATA_HIGH, dmac);
DMAPRINTF(("gus_dma_input returning\n"));
return 0;
}
int
gus_dmain_intr(struct gus_softc *sc)
{
void (*callback)(void *);
void *arg;
DMAPRINTF(("gus_dmain_intr called\n"));
if (sc->sc_dmainintr) {
isa_dmadone(sc->sc_dev.dv_parent, sc->sc_recdrq);
callback = sc->sc_dmainintr;
arg = sc->sc_inarg;
sc->sc_dmainaddr = 0;
sc->sc_dmaincnt = 0;
sc->sc_dmainintr = 0;
sc->sc_inarg = 0;
sc->sc_flags &= ~GUS_DMAIN_ACTIVE;
DMAPRINTF(("calling dmain_intr callback %p(%p)\n", callback, arg));
(*callback)(arg);
return 1;
} else {
DMAPRINTF(("gus_dmain_intr false?\n"));
return 0;
}
}
int
gusmax_halt_out_dma(void *addr)
{
struct ad1848_softc *sc = addr;
return gus_halt_out_dma(sc->parent);
}
int
gusmax_halt_in_dma(void *addr)
{
struct ad1848_softc *sc = addr;
return gus_halt_in_dma(sc->parent);
}
int
gus_halt_out_dma(void *addr)
{
struct gus_softc *sc = addr;
bus_space_tag_t iot = sc->sc_iot;
bus_space_handle_t ioh2 = sc->sc_ioh2;
mtx_enter(&audio_lock);
DMAPRINTF(("gus_halt_out_dma called\n"));
SELECT_GUS_REG(iot, ioh2, GUSREG_DMA_CONTROL);
bus_space_write_1(iot, ioh2, GUS_DATA_HIGH, 0);
timeout_del(&sc->sc_dma_tmo);
isa_dmaabort(sc->sc_dev.dv_parent, sc->sc_drq);
sc->sc_flags &= ~(GUS_DMAOUT_ACTIVE|GUS_LOCKED);
sc->sc_dmaoutintr = 0;
sc->sc_outarg = 0;
sc->sc_dmaoutaddr = 0;
sc->sc_dmaoutcnt = 0;
sc->sc_dmabuf = 0;
sc->sc_bufcnt = 0;
sc->sc_playbuf = -1;
gus_stop_voice(sc, GUS_VOICE_LEFT, 1);
gus_stop_voice(sc, GUS_VOICE_RIGHT, 0);
mtx_leave(&audio_lock);
return 0;
}
int
gus_halt_in_dma(void *addr)
{
struct gus_softc *sc = addr;
bus_space_tag_t iot = sc->sc_iot;
bus_space_handle_t ioh2 = sc->sc_ioh2;
mtx_enter(&audio_lock);
DMAPRINTF(("gus_halt_in_dma called\n"));
SELECT_GUS_REG(iot, ioh2, GUSREG_SAMPLE_CONTROL);
bus_space_write_1(iot, ioh2, GUS_DATA_HIGH,
bus_space_read_1(iot, ioh2, GUS_DATA_HIGH) & ~(GUSMASK_SAMPLE_START|GUSMASK_SAMPLE_IRQ));
isa_dmaabort(sc->sc_dev.dv_parent, sc->sc_recdrq);
sc->sc_flags &= ~GUS_DMAIN_ACTIVE;
sc->sc_dmainintr = 0;
sc->sc_inarg = 0;
sc->sc_dmainaddr = 0;
sc->sc_dmaincnt = 0;
mtx_leave(&audio_lock);
return 0;
}
ad1848_devmap_t gusmapping[] = {
{GUSMAX_DAC_LVL, AD1848_KIND_LVL, AD1848_AUX1_CHANNEL},
{GUSMAX_LINE_IN_LVL, AD1848_KIND_LVL, AD1848_LINE_CHANNEL},
{GUSMAX_MONO_LVL, AD1848_KIND_LVL, AD1848_MONO_CHANNEL},
{GUSMAX_CD_LVL, AD1848_KIND_LVL, AD1848_AUX2_CHANNEL},
{GUSMAX_MONITOR_LVL, AD1848_KIND_LVL, AD1848_MONITOR_CHANNEL},
{GUSMAX_OUT_LVL, AD1848_KIND_LVL, AD1848_DAC_CHANNEL},
{GUSMAX_DAC_MUTE, AD1848_KIND_MUTE, AD1848_AUX1_CHANNEL},
{GUSMAX_LINE_IN_MUTE, AD1848_KIND_MUTE, AD1848_LINE_CHANNEL},
{GUSMAX_MONO_MUTE, AD1848_KIND_MUTE, AD1848_MONO_CHANNEL},
{GUSMAX_CD_MUTE, AD1848_KIND_MUTE, AD1848_AUX2_CHANNEL},
{GUSMAX_MONITOR_MUTE, AD1848_KIND_MUTE, AD1848_MONITOR_CHANNEL},
{GUSMAX_REC_LVL, AD1848_KIND_RECORDGAIN, -1},
{GUSMAX_RECORD_SOURCE, AD1848_KIND_RECORDSOURCE, -1}
};
int nummap = sizeof(gusmapping) / sizeof(gusmapping[0]);
int
gusmax_mixer_get_port(void *addr, mixer_ctrl_t *cp)
{
struct ad1848_softc *ac = addr;
struct gus_softc *sc = ac->parent;
struct ad1848_volume vol;
int error = ad1848_mixer_get_port(ac, gusmapping, nummap, cp);
if (error != ENXIO)
return (error);
error = EINVAL;
switch (cp->dev) {
case GUSMAX_SPEAKER_LVL:
if (cp->type == AUDIO_MIXER_VALUE) {
if (sc->sc_mixcontrol & GUSMASK_LINE_OUT)
vol.left = vol.right = AUDIO_MAX_GAIN;
else
vol.left = vol.right = AUDIO_MIN_GAIN;
error = 0;
ad1848_from_vol(cp, &vol);
}
break;
case GUSMAX_SPEAKER_MUTE:
if (cp->type == AUDIO_MIXER_ENUM) {
cp->un.ord = sc->sc_mixcontrol & GUSMASK_LINE_OUT ? 1 : 0;
error = 0;
}
break;
default:
error = ENXIO;
break;
}
return(error);
}
int
gus_mixer_get_port(void *addr, mixer_ctrl_t *cp)
{
struct gus_softc *sc = addr;
struct ics2101_softc *ic = &sc->sc_mixer;
struct ad1848_volume vol;
int error = EINVAL;
DPRINTF(("gus_mixer_get_port: dev=%d type=%d\n", cp->dev, cp->type));
if (!HAS_MIXER(sc) && cp->dev > GUSICS_MASTER_MUTE)
return ENXIO;
switch (cp->dev) {
case GUSICS_MIC_IN_MUTE:
if (cp->type == AUDIO_MIXER_ENUM) {
if (HAS_MIXER(sc))
cp->un.ord = ic->sc_mute[GUSMIX_CHAN_MIC][ICSMIX_LEFT];
else
cp->un.ord =
sc->sc_mixcontrol & GUSMASK_MIC_IN ? 0 : 1;
error = 0;
}
break;
case GUSICS_LINE_IN_MUTE:
if (cp->type == AUDIO_MIXER_ENUM) {
if (HAS_MIXER(sc))
cp->un.ord = ic->sc_mute[GUSMIX_CHAN_LINE][ICSMIX_LEFT];
else
cp->un.ord =
sc->sc_mixcontrol & GUSMASK_LINE_IN ? 1 : 0;
error = 0;
}
break;
case GUSICS_MASTER_MUTE:
if (cp->type == AUDIO_MIXER_ENUM) {
if (HAS_MIXER(sc))
cp->un.ord = ic->sc_mute[GUSMIX_CHAN_MASTER][ICSMIX_LEFT];
else
cp->un.ord =
sc->sc_mixcontrol & GUSMASK_LINE_OUT ? 1 : 0;
error = 0;
}
break;
case GUSICS_DAC_MUTE:
if (cp->type == AUDIO_MIXER_ENUM) {
cp->un.ord = ic->sc_mute[GUSMIX_CHAN_DAC][ICSMIX_LEFT];
error = 0;
}
break;
case GUSICS_CD_MUTE:
if (cp->type == AUDIO_MIXER_ENUM) {
cp->un.ord = ic->sc_mute[GUSMIX_CHAN_CD][ICSMIX_LEFT];
error = 0;
}
break;
case GUSICS_MASTER_LVL:
if (cp->type == AUDIO_MIXER_VALUE) {
vol.left = ic->sc_setting[GUSMIX_CHAN_MASTER][ICSMIX_LEFT];
vol.right = ic->sc_setting[GUSMIX_CHAN_MASTER][ICSMIX_RIGHT];
if (ad1848_from_vol(cp, &vol))
error = 0;
}
break;
case GUSICS_MIC_IN_LVL:
if (cp->type == AUDIO_MIXER_VALUE) {
vol.left = ic->sc_setting[GUSMIX_CHAN_MIC][ICSMIX_LEFT];
vol.right = ic->sc_setting[GUSMIX_CHAN_MIC][ICSMIX_RIGHT];
if (ad1848_from_vol(cp, &vol))
error = 0;
}
break;
case GUSICS_LINE_IN_LVL:
if (cp->type == AUDIO_MIXER_VALUE) {
vol.left = ic->sc_setting[GUSMIX_CHAN_LINE][ICSMIX_LEFT];
vol.right = ic->sc_setting[GUSMIX_CHAN_LINE][ICSMIX_RIGHT];
if (ad1848_from_vol(cp, &vol))
error = 0;
}
break;
case GUSICS_CD_LVL:
if (cp->type == AUDIO_MIXER_VALUE) {
vol.left = ic->sc_setting[GUSMIX_CHAN_CD][ICSMIX_LEFT];
vol.right = ic->sc_setting[GUSMIX_CHAN_CD][ICSMIX_RIGHT];
if (ad1848_from_vol(cp, &vol))
error = 0;
}
break;
case GUSICS_DAC_LVL:
if (cp->type == AUDIO_MIXER_VALUE) {
vol.left = ic->sc_setting[GUSMIX_CHAN_DAC][ICSMIX_LEFT];
vol.right = ic->sc_setting[GUSMIX_CHAN_DAC][ICSMIX_RIGHT];
if (ad1848_from_vol(cp, &vol))
error = 0;
}
break;
case GUSICS_RECORD_SOURCE:
if (cp->type == AUDIO_MIXER_ENUM) {
cp->un.ord = 0;
}
break;
default:
return ENXIO;
}
return error;
}
void
gusics_master_mute(struct ics2101_softc *ic, int mute)
{
ics2101_mix_mute(ic, GUSMIX_CHAN_MASTER, ICSMIX_LEFT, mute);
ics2101_mix_mute(ic, GUSMIX_CHAN_MASTER, ICSMIX_RIGHT, mute);
}
void
gusics_mic_mute(struct ics2101_softc *ic, int mute)
{
ics2101_mix_mute(ic, GUSMIX_CHAN_MIC, ICSMIX_LEFT, mute);
ics2101_mix_mute(ic, GUSMIX_CHAN_MIC, ICSMIX_RIGHT, mute);
}
void
gusics_linein_mute(struct ics2101_softc *ic, int mute)
{
ics2101_mix_mute(ic, GUSMIX_CHAN_LINE, ICSMIX_LEFT, mute);
ics2101_mix_mute(ic, GUSMIX_CHAN_LINE, ICSMIX_RIGHT, mute);
}
void
gusics_cd_mute(struct ics2101_softc *ic, int mute)
{
ics2101_mix_mute(ic, GUSMIX_CHAN_CD, ICSMIX_LEFT, mute);
ics2101_mix_mute(ic, GUSMIX_CHAN_CD, ICSMIX_RIGHT, mute);
}
void
gusics_dac_mute(struct ics2101_softc *ic, int mute)
{
ics2101_mix_mute(ic, GUSMIX_CHAN_DAC, ICSMIX_LEFT, mute);
ics2101_mix_mute(ic, GUSMIX_CHAN_DAC, ICSMIX_RIGHT, mute);
}
int
gusmax_mixer_set_port(void *addr, mixer_ctrl_t *cp)
{
struct ad1848_softc *ac = addr;
struct gus_softc *sc = ac->parent;
struct ad1848_volume vol;
int error = ad1848_mixer_set_port(ac, gusmapping, nummap, cp);
if (error != ENXIO)
return (error);
DPRINTF(("gusmax_mixer_set_port: dev=%d type=%d\n", cp->dev, cp->type));
switch (cp->dev) {
case GUSMAX_SPEAKER_LVL:
if (cp->type == AUDIO_MIXER_VALUE &&
cp->un.value.num_channels == 1) {
if (ad1848_to_vol(cp, &vol)) {
gus_speaker_ctl(sc, vol.left > AUDIO_MIN_GAIN ?
SPKR_ON : SPKR_OFF);
error = 0;
}
}
break;
case GUSMAX_SPEAKER_MUTE:
if (cp->type == AUDIO_MIXER_ENUM) {
gus_speaker_ctl(sc, cp->un.ord ? SPKR_OFF : SPKR_ON);
error = 0;
}
break;
default:
return ENXIO;
}
return error;
}
int
gus_mixer_set_port(void *addr, mixer_ctrl_t *cp)
{
struct gus_softc *sc = addr;
struct ics2101_softc *ic = &sc->sc_mixer;
struct ad1848_volume vol;
int error = EINVAL;
DPRINTF(("gus_mixer_set_port: dev=%d type=%d\n", cp->dev, cp->type));
if (!HAS_MIXER(sc) && cp->dev > GUSICS_MASTER_MUTE)
return ENXIO;
switch (cp->dev) {
case GUSICS_MIC_IN_MUTE:
if (cp->type == AUDIO_MIXER_ENUM) {
DPRINTF(("mic mute %d\n", cp->un.ord));
if (HAS_MIXER(sc)) {
gusics_mic_mute(ic, cp->un.ord);
}
gus_mic_ctl(sc, cp->un.ord ? SPKR_OFF : SPKR_ON);
error = 0;
}
break;
case GUSICS_LINE_IN_MUTE:
if (cp->type == AUDIO_MIXER_ENUM) {
DPRINTF(("linein mute %d\n", cp->un.ord));
if (HAS_MIXER(sc)) {
gusics_linein_mute(ic, cp->un.ord);
}
gus_linein_ctl(sc, cp->un.ord ? SPKR_OFF : SPKR_ON);
error = 0;
}
break;
case GUSICS_MASTER_MUTE:
if (cp->type == AUDIO_MIXER_ENUM) {
DPRINTF(("master mute %d\n", cp->un.ord));
if (HAS_MIXER(sc)) {
gusics_master_mute(ic, cp->un.ord);
}
gus_speaker_ctl(sc, cp->un.ord ? SPKR_OFF : SPKR_ON);
error = 0;
}
break;
case GUSICS_DAC_MUTE:
if (cp->type == AUDIO_MIXER_ENUM) {
gusics_dac_mute(ic, cp->un.ord);
error = 0;
}
break;
case GUSICS_CD_MUTE:
if (cp->type == AUDIO_MIXER_ENUM) {
gusics_cd_mute(ic, cp->un.ord);
error = 0;
}
break;
case GUSICS_MASTER_LVL:
if (cp->type == AUDIO_MIXER_VALUE) {
if (ad1848_to_vol(cp, &vol)) {
ics2101_mix_attenuate(ic,
GUSMIX_CHAN_MASTER,
ICSMIX_LEFT,
vol.left);
ics2101_mix_attenuate(ic,
GUSMIX_CHAN_MASTER,
ICSMIX_RIGHT,
vol.right);
error = 0;
}
}
break;
case GUSICS_MIC_IN_LVL:
if (cp->type == AUDIO_MIXER_VALUE) {
if (ad1848_to_vol(cp, &vol)) {
ics2101_mix_attenuate(ic,
GUSMIX_CHAN_MIC,
ICSMIX_LEFT,
vol.left);
ics2101_mix_attenuate(ic,
GUSMIX_CHAN_MIC,
ICSMIX_RIGHT,
vol.right);
error = 0;
}
}
break;
case GUSICS_LINE_IN_LVL:
if (cp->type == AUDIO_MIXER_VALUE) {
if (ad1848_to_vol(cp, &vol)) {
ics2101_mix_attenuate(ic,
GUSMIX_CHAN_LINE,
ICSMIX_LEFT,
vol.left);
ics2101_mix_attenuate(ic,
GUSMIX_CHAN_LINE,
ICSMIX_RIGHT,
vol.right);
error = 0;
}
}
break;
case GUSICS_CD_LVL:
if (cp->type == AUDIO_MIXER_VALUE) {
if (ad1848_to_vol(cp, &vol)) {
ics2101_mix_attenuate(ic,
GUSMIX_CHAN_CD,
ICSMIX_LEFT,
vol.left);
ics2101_mix_attenuate(ic,
GUSMIX_CHAN_CD,
ICSMIX_RIGHT,
vol.right);
error = 0;
}
}
break;
case GUSICS_DAC_LVL:
if (cp->type == AUDIO_MIXER_VALUE) {
if (ad1848_to_vol(cp, &vol)) {
ics2101_mix_attenuate(ic,
GUSMIX_CHAN_DAC,
ICSMIX_LEFT,
vol.left);
ics2101_mix_attenuate(ic,
GUSMIX_CHAN_DAC,
ICSMIX_RIGHT,
vol.right);
error = 0;
}
}
break;
case GUSICS_RECORD_SOURCE:
if (cp->type == AUDIO_MIXER_ENUM && cp->un.ord == 0) {
error = 0;
}
break;
default:
return ENXIO;
}
return error;
}
int
gusmax_mixer_query_devinfo(void *addr, mixer_devinfo_t *dip)
{
DPRINTF(("gusmax_query_devinfo: index=%d\n", dip->index));
switch(dip->index) {
#if 0
case GUSMAX_MIC_IN_LVL:
dip->type = AUDIO_MIXER_VALUE;
dip->mixer_class = GUSMAX_INPUT_CLASS;
dip->prev = AUDIO_MIXER_LAST;
dip->next = GUSMAX_MIC_IN_MUTE;
strlcpy(dip->label.name, AudioNmicrophone, sizeof dip->label.name);
dip->un.v.num_channels = 2;
strlcpy(dip->un.v.units.name, AudioNvolume,
sizeof dip->un.v.units.name);
break;
#endif
case GUSMAX_MONO_LVL:
dip->type = AUDIO_MIXER_VALUE;
dip->mixer_class = GUSMAX_INPUT_CLASS;
dip->prev = AUDIO_MIXER_LAST;
dip->next = GUSMAX_MONO_MUTE;
strlcpy(dip->label.name, AudioNmicrophone, sizeof dip->label.name);
dip->un.v.num_channels = 1;
strlcpy(dip->un.v.units.name, AudioNvolume,
sizeof dip->un.v.units.name);
break;
case GUSMAX_DAC_LVL:
dip->type = AUDIO_MIXER_VALUE;
dip->mixer_class = GUSMAX_INPUT_CLASS;
dip->prev = AUDIO_MIXER_LAST;
dip->next = GUSMAX_DAC_MUTE;
strlcpy(dip->label.name, AudioNdac, sizeof dip->label.name);
dip->un.v.num_channels = 2;
strlcpy(dip->un.v.units.name, AudioNvolume,
sizeof dip->un.v.units.name);
break;
case GUSMAX_LINE_IN_LVL:
dip->type = AUDIO_MIXER_VALUE;
dip->mixer_class = GUSMAX_INPUT_CLASS;
dip->prev = AUDIO_MIXER_LAST;
dip->next = GUSMAX_LINE_IN_MUTE;
strlcpy(dip->label.name, AudioNline, sizeof dip->label.name);
dip->un.v.num_channels = 2;
strlcpy(dip->un.v.units.name, AudioNvolume,
sizeof dip->un.v.units.name);
break;
case GUSMAX_CD_LVL:
dip->type = AUDIO_MIXER_VALUE;
dip->mixer_class = GUSMAX_INPUT_CLASS;
dip->prev = AUDIO_MIXER_LAST;
dip->next = GUSMAX_CD_MUTE;
strlcpy(dip->label.name, AudioNcd, sizeof dip->label.name);
dip->un.v.num_channels = 2;
strlcpy(dip->un.v.units.name, AudioNvolume,
sizeof dip->un.v.units.name);
break;
case GUSMAX_MONITOR_LVL:
dip->type = AUDIO_MIXER_VALUE;
dip->mixer_class = GUSMAX_MONITOR_CLASS;
dip->next = GUSMAX_MONITOR_MUTE;
dip->prev = AUDIO_MIXER_LAST;
strlcpy(dip->label.name, AudioNmonitor, sizeof dip->label.name);
dip->un.v.num_channels = 1;
strlcpy(dip->un.v.units.name, AudioNvolume,
sizeof dip->un.v.units.name);
break;
case GUSMAX_OUT_LVL:
dip->type = AUDIO_MIXER_VALUE;
dip->mixer_class = GUSMAX_MONITOR_CLASS;
dip->prev = dip->next = AUDIO_MIXER_LAST;
strlcpy(dip->label.name, AudioNoutput, sizeof dip->label.name);
dip->un.v.num_channels = 2;
strlcpy(dip->un.v.units.name, AudioNvolume,
sizeof dip->un.v.units.name);
break;
case GUSMAX_SPEAKER_LVL:
dip->type = AUDIO_MIXER_VALUE;
dip->mixer_class = GUSMAX_MONITOR_CLASS;
dip->prev = AUDIO_MIXER_LAST;
dip->next = GUSMAX_SPEAKER_MUTE;
strlcpy(dip->label.name, AudioNmaster, sizeof dip->label.name);
dip->un.v.num_channels = 2;
strlcpy(dip->un.v.units.name, AudioNvolume,
sizeof dip->un.v.units.name);
break;
case GUSMAX_LINE_IN_MUTE:
dip->mixer_class = GUSMAX_INPUT_CLASS;
dip->type = AUDIO_MIXER_ENUM;
dip->prev = GUSMAX_LINE_IN_LVL;
dip->next = AUDIO_MIXER_LAST;
goto mute;
case GUSMAX_DAC_MUTE:
dip->mixer_class = GUSMAX_INPUT_CLASS;
dip->type = AUDIO_MIXER_ENUM;
dip->prev = GUSMAX_DAC_LVL;
dip->next = AUDIO_MIXER_LAST;
goto mute;
case GUSMAX_CD_MUTE:
dip->mixer_class = GUSMAX_INPUT_CLASS;
dip->type = AUDIO_MIXER_ENUM;
dip->prev = GUSMAX_CD_LVL;
dip->next = AUDIO_MIXER_LAST;
goto mute;
case GUSMAX_MONO_MUTE:
dip->mixer_class = GUSMAX_INPUT_CLASS;
dip->type = AUDIO_MIXER_ENUM;
dip->prev = GUSMAX_MONO_LVL;
dip->next = AUDIO_MIXER_LAST;
goto mute;
case GUSMAX_MONITOR_MUTE:
dip->mixer_class = GUSMAX_OUTPUT_CLASS;
dip->type = AUDIO_MIXER_ENUM;
dip->prev = GUSMAX_MONITOR_LVL;
dip->next = AUDIO_MIXER_LAST;
goto mute;
case GUSMAX_SPEAKER_MUTE:
dip->mixer_class = GUSMAX_OUTPUT_CLASS;
dip->type = AUDIO_MIXER_ENUM;
dip->prev = GUSMAX_SPEAKER_LVL;
dip->next = AUDIO_MIXER_LAST;
mute:
strlcpy(dip->label.name, AudioNmute, sizeof dip->label.name);
dip->un.e.num_mem = 2;
strlcpy(dip->un.e.member[0].label.name, AudioNoff,
sizeof dip->un.e.member[0].label.name);
dip->un.e.member[0].ord = 0;
strlcpy(dip->un.e.member[1].label.name, AudioNon,
sizeof dip->un.e.member[1].label.name);
dip->un.e.member[1].ord = 1;
break;
case GUSMAX_REC_LVL:
dip->type = AUDIO_MIXER_VALUE;
dip->mixer_class = GUSMAX_RECORD_CLASS;
dip->prev = AUDIO_MIXER_LAST;
dip->next = GUSMAX_RECORD_SOURCE;
strlcpy(dip->label.name, AudioNrecord, sizeof dip->label.name);
dip->un.v.num_channels = 2;
strlcpy(dip->un.v.units.name, AudioNvolume, sizeof dip->un.v.units.name);
break;
case GUSMAX_RECORD_SOURCE:
dip->mixer_class = GUSMAX_RECORD_CLASS;
dip->type = AUDIO_MIXER_ENUM;
dip->prev = GUSMAX_REC_LVL;
dip->next = AUDIO_MIXER_LAST;
strlcpy(dip->label.name, AudioNsource, sizeof dip->label.name);
dip->un.e.num_mem = 4;
strlcpy(dip->un.e.member[0].label.name, AudioNoutput,
sizeof dip->un.e.member[0].label.name);
dip->un.e.member[0].ord = DAC_IN_PORT;
strlcpy(dip->un.e.member[1].label.name, AudioNmicrophone,
sizeof dip->un.e.member[1].label.name);
dip->un.e.member[1].ord = MIC_IN_PORT;
strlcpy(dip->un.e.member[2].label.name, AudioNdac,
sizeof dip->un.e.member[2].label.name);
dip->un.e.member[2].ord = AUX1_IN_PORT;
strlcpy(dip->un.e.member[3].label.name, AudioNline,
sizeof dip->un.e.member[3].label.name);
dip->un.e.member[3].ord = LINE_IN_PORT;
break;
case GUSMAX_INPUT_CLASS:
dip->type = AUDIO_MIXER_CLASS;
dip->mixer_class = GUSMAX_INPUT_CLASS;
dip->next = dip->prev = AUDIO_MIXER_LAST;
strlcpy(dip->label.name, AudioCinputs, sizeof dip->label.name);
break;
case GUSMAX_OUTPUT_CLASS:
dip->type = AUDIO_MIXER_CLASS;
dip->mixer_class = GUSMAX_OUTPUT_CLASS;
dip->next = dip->prev = AUDIO_MIXER_LAST;
strlcpy(dip->label.name, AudioCoutputs, sizeof dip->label.name);
break;
case GUSMAX_MONITOR_CLASS:
dip->type = AUDIO_MIXER_CLASS;
dip->mixer_class = GUSMAX_MONITOR_CLASS;
dip->next = dip->prev = AUDIO_MIXER_LAST;
strlcpy(dip->label.name, AudioCmonitor, sizeof dip->label.name);
break;
case GUSMAX_RECORD_CLASS:
dip->type = AUDIO_MIXER_CLASS;
dip->mixer_class = GUSMAX_RECORD_CLASS;
dip->next = dip->prev = AUDIO_MIXER_LAST;
strlcpy(dip->label.name, AudioCrecord, sizeof dip->label.name);
break;
default:
return ENXIO;
}
DPRINTF(("AUDIO_MIXER_DEVINFO: name=%s\n", dip->label.name));
return 0;
}
int
gus_mixer_query_devinfo(void *addr, mixer_devinfo_t *dip)
{
struct gus_softc *sc = addr;
DPRINTF(("gusmax_query_devinfo: index=%d\n", dip->index));
if (!HAS_MIXER(sc) && dip->index > GUSICS_MASTER_MUTE)
return ENXIO;
switch(dip->index) {
case GUSICS_MIC_IN_LVL:
dip->type = AUDIO_MIXER_VALUE;
dip->mixer_class = GUSICS_INPUT_CLASS;
dip->prev = AUDIO_MIXER_LAST;
dip->next = GUSICS_MIC_IN_MUTE;
strlcpy(dip->label.name, AudioNmicrophone,
sizeof dip->label.name);
dip->un.v.num_channels = 2;
strlcpy(dip->un.v.units.name, AudioNvolume,
sizeof dip->un.v.units.name);
break;
case GUSICS_LINE_IN_LVL:
dip->type = AUDIO_MIXER_VALUE;
dip->mixer_class = GUSICS_INPUT_CLASS;
dip->prev = AUDIO_MIXER_LAST;
dip->next = GUSICS_LINE_IN_MUTE;
strlcpy(dip->label.name, AudioNline, sizeof dip->label.name);
dip->un.v.num_channels = 2;
strlcpy(dip->un.v.units.name, AudioNvolume,
sizeof dip->un.v.units.name);
break;
case GUSICS_CD_LVL:
dip->type = AUDIO_MIXER_VALUE;
dip->mixer_class = GUSICS_INPUT_CLASS;
dip->prev = AUDIO_MIXER_LAST;
dip->next = GUSICS_CD_MUTE;
strlcpy(dip->label.name, AudioNcd, sizeof dip->label.name);
dip->un.v.num_channels = 2;
strlcpy(dip->un.v.units.name, AudioNvolume,
sizeof dip->un.v.units.name);
break;
case GUSICS_DAC_LVL:
dip->type = AUDIO_MIXER_VALUE;
dip->mixer_class = GUSICS_INPUT_CLASS;
dip->prev = AUDIO_MIXER_LAST;
dip->next = GUSICS_DAC_MUTE;
strlcpy(dip->label.name, AudioNdac, sizeof dip->label.name);
dip->un.v.num_channels = 2;
strlcpy(dip->un.v.units.name, AudioNvolume,
sizeof dip->un.v.units.name);
break;
case GUSICS_MASTER_LVL:
dip->type = AUDIO_MIXER_VALUE;
dip->mixer_class = GUSICS_OUTPUT_CLASS;
dip->prev = AUDIO_MIXER_LAST;
dip->next = GUSICS_MASTER_MUTE;
strlcpy(dip->label.name, AudioNmaster, sizeof dip->label.name);
dip->un.v.num_channels = 2;
strlcpy(dip->un.v.units.name, AudioNvolume,
sizeof dip->un.v.units.name);
break;
case GUSICS_LINE_IN_MUTE:
dip->mixer_class = GUSICS_INPUT_CLASS;
dip->type = AUDIO_MIXER_ENUM;
dip->prev = GUSICS_LINE_IN_LVL;
dip->next = AUDIO_MIXER_LAST;
goto mute;
case GUSICS_DAC_MUTE:
dip->mixer_class = GUSICS_INPUT_CLASS;
dip->type = AUDIO_MIXER_ENUM;
dip->prev = GUSICS_DAC_LVL;
dip->next = AUDIO_MIXER_LAST;
goto mute;
case GUSICS_CD_MUTE:
dip->mixer_class = GUSICS_INPUT_CLASS;
dip->type = AUDIO_MIXER_ENUM;
dip->prev = GUSICS_CD_LVL;
dip->next = AUDIO_MIXER_LAST;
goto mute;
case GUSICS_MIC_IN_MUTE:
dip->mixer_class = GUSICS_INPUT_CLASS;
dip->type = AUDIO_MIXER_ENUM;
dip->prev = GUSICS_MIC_IN_LVL;
dip->next = AUDIO_MIXER_LAST;
goto mute;
case GUSICS_MASTER_MUTE:
dip->mixer_class = GUSICS_OUTPUT_CLASS;
dip->type = AUDIO_MIXER_ENUM;
dip->prev = GUSICS_MASTER_LVL;
dip->next = AUDIO_MIXER_LAST;
mute:
strlcpy(dip->label.name, AudioNmute, sizeof dip->label.name);
dip->un.e.num_mem = 2;
strlcpy(dip->un.e.member[0].label.name, AudioNoff,
sizeof dip->un.e.member[0].label.name);
dip->un.e.member[0].ord = 0;
strlcpy(dip->un.e.member[1].label.name, AudioNon,
sizeof dip->un.e.member[1].label.name);
dip->un.e.member[1].ord = 1;
break;
case GUSICS_RECORD_SOURCE:
dip->mixer_class = GUSICS_RECORD_CLASS;
dip->type = AUDIO_MIXER_ENUM;
dip->prev = dip->next = AUDIO_MIXER_LAST;
strlcpy(dip->label.name, AudioNsource, sizeof dip->label.name);
dip->un.e.num_mem = 1;
strlcpy(dip->un.e.member[0].label.name, AudioNoutput,
sizeof dip->un.e.member[0].label.name);
dip->un.e.member[0].ord = GUSICS_MASTER_LVL;
break;
case GUSICS_INPUT_CLASS:
dip->type = AUDIO_MIXER_CLASS;
dip->mixer_class = GUSICS_INPUT_CLASS;
dip->next = dip->prev = AUDIO_MIXER_LAST;
strlcpy(dip->label.name, AudioCinputs, sizeof dip->label.name);
break;
case GUSICS_OUTPUT_CLASS:
dip->type = AUDIO_MIXER_CLASS;
dip->mixer_class = GUSICS_OUTPUT_CLASS;
dip->next = dip->prev = AUDIO_MIXER_LAST;
strlcpy(dip->label.name, AudioCoutputs, sizeof dip->label.name);
break;
case GUSICS_RECORD_CLASS:
dip->type = AUDIO_MIXER_CLASS;
dip->mixer_class = GUSICS_RECORD_CLASS;
dip->next = dip->prev = AUDIO_MIXER_LAST;
strlcpy(dip->label.name, AudioCrecord, sizeof dip->label.name);
break;
default:
return ENXIO;
}
DPRINTF(("AUDIO_MIXER_DEVINFO: name=%s\n", dip->label.name));
return 0;
}
void *
gus_malloc(void *addr, int direction, size_t size, int pool, int flags)
{
struct gus_softc *sc = addr;
int drq;
if (direction == AUMODE_PLAY)
drq = sc->sc_drq;
else
drq = sc->sc_recdrq;
return isa_malloc(sc->sc_isa, drq, size, pool, flags);
}
void
gus_free(void *addr, void *ptr, int pool)
{
isa_free(ptr, pool);
}
size_t
gus_round(void *addr, int direction, size_t size)
{
if (size > MAX_ISADMA)
size = MAX_ISADMA;
return size;
}
void
gus_init_ics2101(struct gus_softc *sc)
{
struct ics2101_softc *ic = &sc->sc_mixer;
sc->sc_mixer.sc_iot = sc->sc_iot;
sc->sc_mixer.sc_selio = GUS_MIXER_SELECT;
sc->sc_mixer.sc_selio_ioh = sc->sc_ioh3;
sc->sc_mixer.sc_dataio = GUS_MIXER_DATA;
sc->sc_mixer.sc_dataio_ioh = sc->sc_ioh2;
sc->sc_mixer.sc_flags = (sc->sc_revision == 5) ? ICS_FLIP : 0;
ics2101_mix_attenuate(ic,
GUSMIX_CHAN_MIC,
ICSMIX_LEFT,
ICSMIX_MIN_ATTN);
ics2101_mix_attenuate(ic,
GUSMIX_CHAN_MIC,
ICSMIX_RIGHT,
ICSMIX_MIN_ATTN);
gusics_mic_mute(ic, 1);
gus_mic_ctl(sc, SPKR_ON);
ics2101_mix_attenuate(ic,
GUSMIX_CHAN_LINE,
ICSMIX_LEFT,
ICSMIX_MIN_ATTN);
ics2101_mix_attenuate(ic,
GUSMIX_CHAN_LINE,
ICSMIX_RIGHT,
ICSMIX_MIN_ATTN);
ics2101_mix_attenuate(ic,
GUSMIX_CHAN_CD,
ICSMIX_LEFT,
ICSMIX_MIN_ATTN);
ics2101_mix_attenuate(ic,
GUSMIX_CHAN_CD,
ICSMIX_RIGHT,
ICSMIX_MIN_ATTN);
ics2101_mix_attenuate(ic,
GUSMIX_CHAN_DAC,
ICSMIX_LEFT,
ICSMIX_MIN_ATTN);
ics2101_mix_attenuate(ic,
GUSMIX_CHAN_DAC,
ICSMIX_RIGHT,
ICSMIX_MIN_ATTN);
ics2101_mix_attenuate(ic,
ICSMIX_CHAN_4,
ICSMIX_LEFT,
ICSMIX_MAX_ATTN);
ics2101_mix_attenuate(ic,
ICSMIX_CHAN_4,
ICSMIX_RIGHT,
ICSMIX_MAX_ATTN);
ics2101_mix_attenuate(ic,
GUSMIX_CHAN_MASTER,
ICSMIX_LEFT,
ICSMIX_MIN_ATTN);
ics2101_mix_attenuate(ic,
GUSMIX_CHAN_MASTER,
ICSMIX_RIGHT,
ICSMIX_MIN_ATTN);
gusics_cd_mute(ic, 0);
gusics_dac_mute(ic, 0);
gusics_linein_mute(ic, 0);
return;
}
void
gus_subattach(struct gus_softc *sc, struct isa_attach_args *ia)
{
int i;
bus_space_tag_t iot;
unsigned char c,d,m;
u_long s;
iot = sc->sc_iot;
c = bus_space_read_1(iot, sc->sc_ioh3, GUS_BOARD_REV);
if (c != 0xff)
sc->sc_revision = c;
else
sc->sc_revision = 0;
SELECT_GUS_REG(iot, sc->sc_ioh2, GUSREG_RESET);
bus_space_write_1(iot, sc->sc_ioh2, GUS_DATA_HIGH, 0x00);
gusreset(sc, GUS_MAX_VOICES);
gusreset(sc, GUS_MIN_VOICES);
m = GUSMASK_LINE_IN|GUSMASK_LINE_OUT;
c = ((unsigned char) gus_irq_map[ia->ia_irq]) | GUSMASK_BOTH_RQ;
if (sc->sc_recdrq == sc->sc_drq)
d = (unsigned char) (gus_drq_map[sc->sc_drq] |
GUSMASK_BOTH_RQ);
else
d = (unsigned char) (gus_drq_map[sc->sc_drq] |
gus_drq_map[sc->sc_recdrq] << 3);
s = intr_disable();
bus_space_write_1(iot, sc->sc_ioh1, GUS_REG_CONTROL, GUS_REG_IRQCTL);
bus_space_write_1(iot, sc->sc_ioh1, GUS_MIX_CONTROL, m);
bus_space_write_1(iot, sc->sc_ioh1, GUS_IRQCTL_CONTROL, 0x00);
bus_space_write_1(iot, sc->sc_ioh1, 0x0f, 0x00);
bus_space_write_1(iot, sc->sc_ioh1, GUS_MIX_CONTROL, m);
bus_space_write_1(iot, sc->sc_ioh1, GUS_DMA_CONTROL, d | 0x80);
bus_space_write_1(iot, sc->sc_ioh1, GUS_MIX_CONTROL,
m | GUSMASK_CONTROL_SEL);
bus_space_write_1(iot, sc->sc_ioh1, GUS_IRQ_CONTROL, c);
bus_space_write_1(iot, sc->sc_ioh1, GUS_MIX_CONTROL, m);
bus_space_write_1(iot, sc->sc_ioh1, GUS_DMA_CONTROL, d);
bus_space_write_1(iot, sc->sc_ioh1, GUS_MIX_CONTROL,
m | GUSMASK_CONTROL_SEL);
bus_space_write_1(iot, sc->sc_ioh1, GUS_IRQ_CONTROL, c);
bus_space_write_1(iot, sc->sc_ioh2, GUS_VOICE_SELECT, 0x00);
bus_space_write_1(iot, sc->sc_ioh1, GUS_MIX_CONTROL,
(m | GUSMASK_LATCHES) & ~(GUSMASK_LINE_OUT|GUSMASK_LINE_IN));
bus_space_write_1(iot, sc->sc_ioh2, GUS_VOICE_SELECT, 0x00);
intr_restore(s);
sc->sc_mixcontrol =
(m | GUSMASK_LATCHES) & ~(GUSMASK_LINE_OUT|GUSMASK_LINE_IN);
sc->sc_codec.sc_isa = sc->sc_isa;
if (sc->sc_revision >= 5 && sc->sc_revision <= 9) {
sc->sc_flags |= GUS_MIXER_INSTALLED;
gus_init_ics2101(sc);
}
if (sc->sc_revision < 10 || !gus_init_cs4231(sc)) {
if (sc->sc_drq != -1) {
if (isa_dmamap_create(sc->sc_isa, sc->sc_drq,
MAX_ISADMA, BUS_DMA_NOWAIT|BUS_DMA_ALLOCNOW)) {
printf("%s: can't create map for drq %d\n",
sc->sc_dev.dv_xname, sc->sc_drq);
return;
}
}
if (sc->sc_recdrq != -1 && sc->sc_recdrq != sc->sc_drq) {
if (isa_dmamap_create(sc->sc_isa, sc->sc_recdrq,
MAX_ISADMA, BUS_DMA_NOWAIT|BUS_DMA_ALLOCNOW)) {
printf("%s: can't create map for drq %d\n",
sc->sc_dev.dv_xname, sc->sc_recdrq);
return;
}
}
}
timeout_set(&sc->sc_dma_tmo, gus_dmaout_timeout, sc);
SELECT_GUS_REG(iot, sc->sc_ioh2, GUSREG_RESET);
guspoke(iot, sc->sc_ioh2, 0L, 0x00);
for(i = 1; i < 1024; i++) {
u_long loc;
if (guspeek(iot, sc->sc_ioh2, 0L) != 0)
break;
loc = i << 10;
guspoke(iot, sc->sc_ioh2, loc, 0xaa);
if (guspeek(iot, sc->sc_ioh2, loc) != 0xaa)
break;
}
sc->sc_dsize = i;
printf(": ver %d", sc->sc_revision);
if (sc->sc_revision >= 10)
printf(", MAX");
else {
if (HAS_MIXER(sc))
printf(", ICS2101 mixer");
if (HAS_CODEC(sc))
printf(", %s codec/mixer", sc->sc_codec.chip_name);
}
printf(", %dKB DRAM, ", sc->sc_dsize);
if (sc->sc_recdrq == sc->sc_drq) {
printf("half-duplex");
} else {
printf("full-duplex, record drq %d", sc->sc_recdrq);
}
printf("\n");
sc->sc_ih = isa_intr_establish(ia->ia_ic, ia->ia_irq,
IST_EDGE, IPL_AUDIO | IPL_MPSAFE,
gusintr, sc , sc->sc_dev.dv_xname);
sc->sc_irate = sc->sc_orate = 44100;
sc->sc_encoding = AUDIO_ENCODING_SLINEAR_LE;
sc->sc_precision = 16;
sc->sc_voc[GUS_VOICE_LEFT].voccntl |= GUSMASK_DATA_SIZE16;
sc->sc_voc[GUS_VOICE_RIGHT].voccntl |= GUSMASK_DATA_SIZE16;
sc->sc_channels = 1;
sc->sc_ogain = 340;
gus_commit_settings(sc);
bus_space_write_1(iot, sc->sc_ioh2, GUS_VOICE_SELECT,
(u_char)GUS_VOICE_LEFT);
SELECT_GUS_REG(iot, sc->sc_ioh2, GUSREG_PAN_POS);
bus_space_write_1(iot, sc->sc_ioh2, GUS_DATA_HIGH, GUS_PAN_FULL_LEFT);
bus_space_write_1(iot, sc->sc_ioh2, GUS_VOICE_SELECT,
(u_char)GUS_VOICE_RIGHT);
SELECT_GUS_REG(iot, sc->sc_ioh2, GUSREG_PAN_POS);
bus_space_write_1(iot, sc->sc_ioh2, GUS_DATA_HIGH, GUS_PAN_FULL_RIGHT);
if (HAS_CODEC(sc)) {
audio_attach_mi(&gusmax_hw_if, (void *)&sc->sc_codec, NULL,
&sc->sc_dev);
} else {
audio_attach_mi(&gus_hw_if, (void *)sc, NULL, &sc->sc_dev);
}
}
int
gus_test_iobase(bus_space_tag_t iot, int iobase)
{
bus_space_handle_t ioh1, ioh2, ioh3, ioh4;
u_char s1, s2;
int rv = 0;
if (bus_space_map(iot, iobase, GUS_NPORT1, 0, &ioh1))
return 0;
if (bus_space_map(iot, iobase+GUS_IOH2_OFFSET, GUS_NPORT2, 0, &ioh2))
goto bad1;
if (bus_space_map(iot, iobase+GUS_IOH3_OFFSET, GUS_NPORT3, 0, &ioh3))
goto bad2;
if (bus_space_map(iot, iobase+GUS_IOH4_OFFSET, GUS_NPORT4, 0, &ioh4))
goto bad3;
mtx_enter(&audio_lock);
delay(500);
SELECT_GUS_REG(iot, ioh2, GUSREG_RESET);
bus_space_write_1(iot, ioh2, GUS_DATA_HIGH, 0x00);
delay(500);
SELECT_GUS_REG(iot, ioh2, GUSREG_RESET);
bus_space_write_1(iot, ioh2, GUS_DATA_HIGH, GUSMASK_MASTER_RESET);
delay(500);
mtx_leave(&audio_lock);
s1 = guspeek(iot, ioh2, 0L);
s2 = guspeek(iot, ioh2, 1L);
guspoke(iot, ioh2, 0L, 0xaa);
guspoke(iot, ioh2, 1L, 0x55);
if (guspeek(iot, ioh2, 0L) != 0xaa)
goto bad;
guspoke(iot, ioh2, 0L, s1);
guspoke(iot, ioh2, 1L, s2);
rv = 1;
bad:
bus_space_unmap(iot, ioh4, GUS_NPORT4);
bad3:
bus_space_unmap(iot, ioh3, GUS_NPORT3);
bad2:
bus_space_unmap(iot, ioh2, GUS_NPORT2);
bad1:
bus_space_unmap(iot, ioh1, GUS_NPORT1);
return rv;
}