diff options
author | cg <cg@FreeBSD.org> | 2000-03-28 18:31:01 +0000 |
---|---|---|
committer | cg <cg@FreeBSD.org> | 2000-03-28 18:31:01 +0000 |
commit | fb4f8b39408019e864d8b490fc1f3a383cb74fb7 (patch) | |
tree | 118b3216cc07d068b61d69ee291a1984a8514040 /sys/dev/sound/isa/ess.c | |
parent | b852fcb1606b3b2cda4c9464e3e5d51c9152c4fd (diff) | |
download | FreeBSD-src-fb4f8b39408019e864d8b490fc1f3a383cb74fb7.zip FreeBSD-src-fb4f8b39408019e864d8b490fc1f3a383cb74fb7.tar.gz |
split up ess and sb code
rewrite ess mixer to use native registers
rewrite play/rec code to use more accurate timer when available
add code to use audio2 for playback, but disable it as no irqs are generated
Diffstat (limited to 'sys/dev/sound/isa/ess.c')
-rw-r--r-- | sys/dev/sound/isa/ess.c | 930 |
1 files changed, 930 insertions, 0 deletions
diff --git a/sys/dev/sound/isa/ess.c b/sys/dev/sound/isa/ess.c new file mode 100644 index 0000000..6072764 --- /dev/null +++ b/sys/dev/sound/isa/ess.c @@ -0,0 +1,930 @@ +/* + * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk> + * Copyright 1997,1998 Luigi Rizzo. + * + * Derived from files in the Voxware 3.5 distribution, + * Copyright by Hannu Savolainen 1994, under the same copyright + * conditions. + * 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/sound/pcm/sound.h> + +#include "sbc.h" + +#include <dev/sound/isa/sb.h> +#include <dev/sound/chip.h> + +#define ESS_BUFFSIZE (4096) +#define ABS(x) (((x) < 0)? -(x) : (x)) + +/* audio2 never generates irqs and sounds very noisy */ +#undef ESS18XX_DUPLEX + +/* more accurate clocks and split audio1/audio2 rates */ +#define ESS18XX_NEWSPEED + +/* channel interface for ESS */ +static void *esschan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir); +static int esschan_setdir(void *data, int dir); +static int esschan_setformat(void *data, u_int32_t format); +static int esschan_setspeed(void *data, u_int32_t speed); +static int esschan_setblocksize(void *data, u_int32_t blocksize); +static int esschan_trigger(void *data, int go); +static int esschan_getptr(void *data); +static pcmchan_caps *esschan_getcaps(void *data); + +static pcmchan_caps ess_playcaps = { + 5000, 49000, + AFMT_STEREO | AFMT_U8 | AFMT_S8 | AFMT_U16_LE | AFMT_S16_LE, + AFMT_STEREO | AFMT_S16_LE +}; + +static pcmchan_caps ess_reccaps = { + 5000, 49000, + AFMT_STEREO | AFMT_U8 | AFMT_S8 | AFMT_U16_LE | AFMT_S16_LE, + AFMT_STEREO | AFMT_S16_LE +}; + +static pcm_channel ess_chantemplate = { + esschan_init, + esschan_setdir, + esschan_setformat, + esschan_setspeed, + esschan_setblocksize, + esschan_trigger, + esschan_getptr, + esschan_getcaps, +}; + +struct ess_info; + +struct ess_chinfo { + struct ess_info *parent; + pcm_channel *channel; + snd_dbuf *buffer; + int dir, hwch, stopping; + u_int32_t fmt, spd; +}; + +struct ess_info { + struct resource *io_base; /* I/O address for the board */ + struct resource *irq; + struct resource *drq1; + struct resource *drq2; + bus_dma_tag_t parent_dmat; + + int type, duplex:1, newspeed:1; + u_long bd_flags; /* board-specific flags */ + struct ess_chinfo pch, rch; +}; + +static int ess_rd(struct ess_info *sc, int reg); +static void ess_wr(struct ess_info *sc, int reg, u_int8_t val); +static int ess_dspready(struct ess_info *sc); +static int ess_cmd(struct ess_info *sc, u_char val); +static int ess_cmd1(struct ess_info *sc, u_char cmd, int val); +static int ess_get_byte(struct ess_info *sc); +static void ess_setmixer(struct ess_info *sc, u_int port, u_int value); +static int ess_getmixer(struct ess_info *sc, u_int port); +static int ess_reset_dsp(struct ess_info *sc); + +static int ess_write(struct ess_info *sc, u_char reg, int val); +static int ess_read(struct ess_info *sc, u_char reg); + +static void ess_intr(void *arg); +static int ess_setupch(struct ess_info *sc, int ch, int dir, int spd, u_int32_t fmt, int len); +static int ess_start(struct ess_chinfo *ch); +static int ess_stop(struct ess_chinfo *ch); + +static int essmix_init(snd_mixer *m); +static int essmix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right); +static int essmix_setrecsrc(snd_mixer *m, u_int32_t src); + +static snd_mixer ess_mixer = { + "ESS mixer", + essmix_init, + essmix_set, + essmix_setrecsrc, +}; + +static devclass_t pcm_devclass; + +/* + * Common code for the midi and pcm functions + * + * ess_cmd write a single byte to the CMD port. + * ess_cmd1 write a CMD + 1 byte arg + * ess_cmd2 write a CMD + 2 byte arg + * ess_get_byte returns a single byte from the DSP data port + * + * ess_write is actually ess_cmd1 + * ess_read access ext. regs via ess_cmd(0xc0, reg) followed by ess_get_byte + */ + +static int +port_rd(struct resource *port, int off) +{ + return bus_space_read_1(rman_get_bustag(port), + rman_get_bushandle(port), + off); +} + +static void +port_wr(struct resource *port, int off, u_int8_t data) +{ + return bus_space_write_1(rman_get_bustag(port), + rman_get_bushandle(port), + off, data); +} + +static int +ess_rd(struct ess_info *sc, int reg) +{ + return port_rd(sc->io_base, reg); +} + +static void +ess_wr(struct ess_info *sc, int reg, u_int8_t val) +{ + port_wr(sc->io_base, reg, val); +} + +static int +ess_dspready(struct ess_info *sc) +{ + return ((ess_rd(sc, SBDSP_STATUS) & 0x80) == 0); +} + +static int +ess_dspwr(struct ess_info *sc, u_char val) +{ + int i; + + for (i = 0; i < 1000; i++) { + if (ess_dspready(sc)) { + ess_wr(sc, SBDSP_CMD, val); + return 1; + } + if (i > 10) DELAY((i > 100)? 1000 : 10); + } + printf("ess_dspwr(0x%02x) timed out.\n", val); + return 0; +} + +static int +ess_cmd(struct ess_info *sc, u_char val) +{ +#if 0 + printf("ess_cmd: %x\n", val); +#endif + return ess_dspwr(sc, val); +} + +static int +ess_cmd1(struct ess_info *sc, u_char cmd, int val) +{ +#if 0 + printf("ess_cmd1: %x, %x\n", cmd, val); +#endif + if (ess_dspwr(sc, cmd)) { + return ess_dspwr(sc, val & 0xff); + } else return 0; +} + +static void +ess_setmixer(struct ess_info *sc, u_int port, u_int value) +{ + u_long flags; + + DEB(printf("ess_setmixer: reg=%x, val=%x\n", port, value);) + flags = spltty(); + ess_wr(sc, SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */ + DELAY(10); + ess_wr(sc, SB_MIX_DATA, (u_char) (value & 0xff)); + DELAY(10); + splx(flags); +} + +static int +ess_getmixer(struct ess_info *sc, u_int port) +{ + int val; + u_long flags; + + flags = spltty(); + ess_wr(sc, SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */ + DELAY(10); + val = ess_rd(sc, SB_MIX_DATA); + DELAY(10); + splx(flags); + + return val; +} + +static int +ess_get_byte(struct ess_info *sc) +{ + int i; + + for (i = 1000; i > 0; i--) { + if (ess_rd(sc, DSP_DATA_AVAIL) & 0x80) + return ess_rd(sc, DSP_READ); + else + DELAY(20); + } + return -1; +} + +static int +ess_write(struct ess_info *sc, u_char reg, int val) +{ + return ess_cmd1(sc, reg, val); +} + +static int +ess_read(struct ess_info *sc, u_char reg) +{ + return (ess_cmd(sc, 0xc0) && ess_cmd(sc, reg))? ess_get_byte(sc) : -1; +} + +static int +ess_reset_dsp(struct ess_info *sc) +{ + ess_wr(sc, SBDSP_RST, 3); + DELAY(100); + ess_wr(sc, SBDSP_RST, 0); + if (ess_get_byte(sc) != 0xAA) { + DEB(printf("ess_reset_dsp 0x%lx failed\n", + rman_get_start(d->io_base))); + return ENXIO; /* Sorry */ + } + ess_cmd(sc, 0xc6); + return 0; +} + +static void +ess_release_resources(struct ess_info *sc, device_t dev) +{ + /* should we bus_teardown_intr here? */ + if (sc->irq) { + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq); + sc->irq = 0; + } + if (sc->drq1) { + bus_release_resource(dev, SYS_RES_DRQ, 0, sc->drq1); + sc->drq1 = 0; + } + if (sc->drq2) { + bus_release_resource(dev, SYS_RES_DRQ, 1, sc->drq2); + sc->drq2 = 0; + } + if (sc->io_base) { + bus_release_resource(dev, SYS_RES_IOPORT, 0, sc->io_base); + sc->io_base = 0; + } + free(sc, M_DEVBUF); +} + +static int +ess_alloc_resources(struct ess_info *sc, device_t dev) +{ + int rid; + + rid = 0; + if (!sc->io_base) + sc->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, + &rid, 0, ~0, 1, + RF_ACTIVE); + rid = 0; + if (!sc->irq) + sc->irq = bus_alloc_resource(dev, SYS_RES_IRQ, + &rid, 0, ~0, 1, + RF_ACTIVE); + rid = 0; + if (!sc->drq1) + sc->drq1 = bus_alloc_resource(dev, SYS_RES_DRQ, + &rid, 0, ~0, 1, + RF_ACTIVE); + rid = 1; + if (!sc->drq2) + sc->drq2 = bus_alloc_resource(dev, SYS_RES_DRQ, + &rid, 0, ~0, 1, + RF_ACTIVE); + + if (sc->io_base && sc->drq1 && sc->irq) { + isa_dma_acquire(rman_get_start(sc->drq1)); + isa_dmainit(rman_get_start(sc->drq1), ESS_BUFFSIZE); + + if (sc->drq2) { + isa_dma_acquire(rman_get_start(sc->drq2)); + isa_dmainit(rman_get_start(sc->drq2), ESS_BUFFSIZE); + } + + return 0; + } else return ENXIO; +} + +static int +ess_doattach(device_t dev, struct ess_info *sc) +{ + snddev_info *d = device_get_softc(dev); + void *ih; + char status[SND_STATUSLEN], buf[64]; + int ver; + + if (ess_alloc_resources(sc, dev)) + goto no; + if (ess_reset_dsp(sc)) + goto no; + mixer_init(d, &ess_mixer, sc); + + sc->duplex = 0; + sc->newspeed = 0; + ver = (ess_getmixer(sc, 0x40) << 8) | ess_rd(sc, SB_MIX_DATA); + snprintf(buf, sizeof buf, "ESS %x DSP", ver); + device_set_desc_copy(dev, buf); + if (bootverbose) + device_printf(dev, "ESS%x detected", ver); + + switch (ver) { + case 0x1869: + case 0x1879: +#ifdef ESS18XX_DUPLEX + sc->duplex = sc->drq2? 1 : 0; +#endif +#ifdef ESS18XX_NEWSPEED + sc->newspeed = 1; +#endif + break; + } + if (bootverbose) + printf("%s%s\n", sc->duplex? ", duplex" : "", + sc->newspeed? ", newspeed" : ""); + + if (sc->newspeed) + ess_setmixer(sc, 0x71, 0x22); + + bus_setup_intr(dev, sc->irq, INTR_TYPE_TTY, ess_intr, sc, &ih); + if (!sc->duplex) + pcm_setflags(dev, pcm_getflags(dev) | SD_F_SIMPLEX); + + 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*/ESS_BUFFSIZE, /*nsegments*/1, + /*maxsegz*/0x3ffff, + /*flags*/0, &sc->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(sc->io_base), rman_get_start(sc->irq), + rman_get_start(sc->drq1)); + if (sc->drq2) + snprintf(status + strlen(status), SND_STATUSLEN - strlen(status), + ":%ld", rman_get_start(sc->drq2)); + + if (pcm_register(dev, sc, 1, 1)) + goto no; + pcm_addchan(dev, PCMDIR_REC, &ess_chantemplate, sc); + pcm_addchan(dev, PCMDIR_PLAY, &ess_chantemplate, sc); + pcm_setstatus(dev, status); + + return 0; + +no: + ess_release_resources(sc, dev); + return ENXIO; +} + +static void +ess_intr(void *arg) +{ + struct ess_info *sc = (struct ess_info *)arg; + int src, pirq, rirq; + + src = 0; + if (ess_getmixer(sc, 0x7a) & 0x80) + src |= 2; + if (ess_rd(sc, 0x0c) & 0x01) + src |= 1; + + pirq = (src & sc->pch.hwch)? 1 : 0; + rirq = (src & sc->rch.hwch)? 1 : 0; + + if (pirq) { + if (sc->pch.stopping) { + buf_isadma(sc->pch.buffer, PCMTRIG_STOP); + sc->pch.stopping = 0; + if (sc->pch.hwch == 1) + ess_write(sc, 0xb8, ess_read(sc, 0xb8) & ~0x01); + else + ess_setmixer(sc, 0x78, ess_getmixer(sc, 0x78) & ~0x03); + } + chn_intr(sc->pch.channel); + } + + if (rirq) { + if (sc->rch.stopping) { + buf_isadma(sc->rch.buffer, PCMTRIG_STOP); + sc->rch.stopping = 0; + /* XXX: will this stop audio2? */ + ess_write(sc, 0xb8, ess_read(sc, 0xb8) & ~0x01); + } + chn_intr(sc->rch.channel); + } + + if (src & 2) + ess_setmixer(sc, 0x7a, ess_getmixer(sc, 0x7a) & ~0x80); + if (src & 1) + ess_rd(sc, DSP_DATA_AVAIL); +} + +/* utility functions for ESS */ +static u_int8_t +ess_calcspeed8(int *spd) +{ + int speed = *spd; + u_int32_t t; + + if (speed > 22000) { + t = (795500 + speed / 2) / speed; + speed = (795500 + t / 2) / t; + t = (256 - t) | 0x80; + } else { + t = (397700 + speed / 2) / speed; + speed = (397700 + t / 2) / t; + t = 128 - t; + } + *spd = speed; + return t & 0x000000ff; +} + +static u_int8_t +ess_calcspeed9(int *spd) +{ + int speed, s0, s1, use0; + u_int8_t t0, t1; + + /* rate = source / (256 - divisor) */ + /* divisor = 256 - (source / rate) */ + speed = *spd; + t0 = 256 - (793800 / speed); + s0 = 793800 / (256 - t0); + + t1 = 0x80 | (256 - (768000 / speed)); + s1 = 768000 / (256 - t1); + + use0 = (ABS(speed - s0) < ABS(speed - s1))? 1 : 0; + + *spd = use0? s0 : s1; + return use0? t0 : t1; +} + +static u_int8_t +ess_calcfilter(int spd) +{ + int cutoff; + + /* cutoff = 7160000 / (256 - divisor) */ + /* divisor = 256 - (7160000 / cutoff) */ + cutoff = (spd * 9 * 82) / 20; + return (256 - (7160000 / cutoff)); +} + +static int +ess_setupch(struct ess_info *sc, int ch, int dir, int spd, u_int32_t fmt, int len) +{ + int play = (dir == PCMDIR_PLAY)? 1 : 0; + int b16 = (fmt & AFMT_16BIT)? 1 : 0; + int stereo = (fmt & AFMT_STEREO)? 1 : 0; + int unsign = (fmt == AFMT_U8 || fmt == AFMT_U16_LE)? 1 : 0; + u_int8_t spdval, fmtval; + + + spdval = (sc->newspeed)? ess_calcspeed9(&spd) : ess_calcspeed8(&spd); + len = -len; + + if (ch == 1) { + KASSERT((dir == PCMDIR_PLAY) || (dir == PCMDIR_REC), ("ess_setupch: dir1 bad")); + /* transfer length low */ + ess_write(sc, 0xa4, len & 0x00ff); + /* transfer length high */ + ess_write(sc, 0xa5, (len & 0xff00) >> 8); + /* autoinit, dma dir */ + ess_write(sc, 0xb8, 0x04 | (play? 0x00 : 0x0a)); + /* mono/stereo */ + ess_write(sc, 0xa8, (ess_read(sc, 0xa8) & ~0x03) | (stereo? 0x01 : 0x02)); + /* demand mode, 4 bytes/xfer */ + ess_write(sc, 0xb9, 0x02); + /* sample rate */ + ess_write(sc, 0xa1, spdval); + /* filter cutoff */ + ess_write(sc, 0xa2, ess_calcfilter(spd)); + /* setup dac/adc */ + if (play) + ess_write(sc, 0xb6, unsign? 0x80 : 0x00); + /* mono, b16: signed, load signal */ + ess_write(sc, 0xb7, 0x51 | (unsign? 0x00 : 0x20)); + /* setup fifo */ + ess_write(sc, 0xb7, 0x90 | (unsign? 0x00 : 0x20) | + (b16? 0x04 : 0x00) | + (stereo? 0x08 : 0x40)); + /* irq control */ + ess_write(sc, 0xb1, (ess_read(sc, 0xb1) & 0x0f) | 0x50); + /* drq control */ + ess_write(sc, 0xb2, (ess_read(sc, 0xb2) & 0x0f) | 0x50); + } else if (ch == 2) { + KASSERT(dir == PCMDIR_PLAY, ("ess_setupch: dir2 bad")); + /* transfer length low */ + ess_setmixer(sc, 0x74, len & 0x00ff); + /* transfer length high */ + ess_setmixer(sc, 0x76, (len & 0xff00) >> 8); + /* autoinit, 4 bytes/req */ + ess_setmixer(sc, 0x78, 0x90); + fmtval = b16 | (stereo << 1) | (unsign << 2); + /* enable irq, set format */ + ess_setmixer(sc, 0x7a, 0x40 | fmtval); + if (sc->newspeed) { + /* sample rate */ + ess_setmixer(sc, 0x70, spdval); + /* filter cutoff */ + ess_setmixer(sc, 0x72, ess_calcfilter(spd)); + } + + } + return 0; +} +static int +ess_start(struct ess_chinfo *ch) +{ + struct ess_info *sc = ch->parent; + int play = (ch->dir == PCMDIR_PLAY)? 1 : 0; + + ess_setupch(sc, ch->hwch, ch->dir, ch->spd, ch->fmt, ch->buffer->dl); + ch->stopping = 0; + if (ch->hwch == 1) + ess_write(sc, 0xb8, ess_read(sc, 0xb8) | 0x01); + else + ess_setmixer(sc, 0x78, ess_getmixer(sc, 0x78) | 0x03); + if (play) + ess_cmd(sc, DSP_CMD_SPKON); + return 0; +} + +static int +ess_stop(struct ess_chinfo *ch) +{ + struct ess_info *sc = ch->parent; + int play = (ch->dir == PCMDIR_PLAY)? 1 : 0; + + ch->stopping = 1; + if (ch->hwch == 1) + ess_write(sc, 0xb8, ess_read(sc, 0xb8) & ~0x04); + else + ess_setmixer(sc, 0x78, ess_getmixer(sc, 0x78) & ~0x10); + if (play) + ess_cmd(sc, DSP_CMD_SPKOFF); + return 0; +} + +/* channel interface for ESS18xx */ +static void * +esschan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir) +{ + struct ess_info *sc = devinfo; + struct ess_chinfo *ch = (dir == PCMDIR_PLAY)? &sc->pch : &sc->rch; + + ch->parent = sc; + ch->channel = c; + ch->buffer = b; + ch->buffer->bufsize = ESS_BUFFSIZE; + if (chn_allocbuf(ch->buffer, sc->parent_dmat) == -1) + return NULL; + ch->hwch = 1; + if ((dir == PCMDIR_PLAY) && (sc->duplex)) + ch->hwch = 2; + ch->buffer->chan = rman_get_start((ch->hwch == 1)? sc->drq1 : sc->drq2); + return ch; +} + +static int +esschan_setdir(void *data, int dir) +{ + struct ess_chinfo *ch = data; + + ch->dir = dir; + return 0; +} + +static int +esschan_setformat(void *data, u_int32_t format) +{ + struct ess_chinfo *ch = data; + + ch->fmt = format; + return 0; +} + +static int +esschan_setspeed(void *data, u_int32_t speed) +{ + struct ess_chinfo *ch = data; + struct ess_info *sc = ch->parent; + + ch->spd = speed; + if (sc->newspeed) + ess_calcspeed9(&ch->spd); + else + ess_calcspeed8(&ch->spd); + return ch->spd; +} + +static int +esschan_setblocksize(void *data, u_int32_t blocksize) +{ + return blocksize; +} + +static int +esschan_trigger(void *data, int go) +{ + struct ess_chinfo *ch = data; + + if (go == PCMTRIG_EMLDMAWR) + return 0; + + switch (go) { + case PCMTRIG_START: + buf_isadma(ch->buffer, go); + ess_start(ch); + break; + + case PCMTRIG_STOP: + case PCMTRIG_ABORT: + default: + ess_stop(ch); + break; + } + return 0; +} + +static int +esschan_getptr(void *data) +{ + struct ess_chinfo *ch = data; + + return buf_isadmaptr(ch->buffer); +} + +static pcmchan_caps * +esschan_getcaps(void *data) +{ + struct ess_chinfo *ch = data; + + return (ch->dir == PCMDIR_PLAY)? &ess_playcaps : &ess_reccaps; +} + +/************************************************************/ + +static int +essmix_init(snd_mixer *m) +{ + struct ess_info *sc = mix_getdevinfo(m); + + mix_setrecdevs(m, SOUND_MASK_CD | SOUND_MASK_MIC | SOUND_MASK_LINE | + SOUND_MASK_IMIX); + + mix_setdevs(m, SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_LINE | + SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_VOLUME | + SOUND_MASK_LINE1); + + ess_setmixer(sc, 0, 0); /* reset */ + + return 0; +} + +static int +essmix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right) +{ + struct ess_info *sc = mix_getdevinfo(m); + int preg = 0, rreg = 0, l, r; + + l = (left * 15) / 100; + r = (right * 15) / 100; + switch (dev) { + case SOUND_MIXER_SYNTH: + preg = 0x36; + rreg = 0x6b; + break; + + case SOUND_MIXER_PCM: + preg = 0x14; + rreg = 0x7c; + break; + + case SOUND_MIXER_LINE: + preg = 0x3e; + rreg = 0x6e; + break; + + case SOUND_MIXER_MIC: + preg = 0x1a; + rreg = 0x68; + break; + + case SOUND_MIXER_LINE1: + preg = 0x3a; + rreg = 0x6c; + break; + + case SOUND_MIXER_CD: + preg = 0x38; + rreg = 0x6a; + break; + + case SOUND_MIXER_VOLUME: + l = left? (left * 63) / 100 : 64; + r = right? (right * 63) / 100 : 64; + ess_setmixer(sc, 0x60, l); + ess_setmixer(sc, 0x62, r); + left = (l == 64)? 0 : (l * 100) / 63; + right = (r == 64)? 0 : (r * 100) / 63; + return left | (right << 8); + } + + if (preg) + ess_setmixer(sc, preg, (l << 4) | r); + if (rreg) + ess_setmixer(sc, rreg, (l << 4) | r); + + left = (l * 100) / 15; + right = (r * 100) / 15; + + return left | (right << 8); +} + +static int +essmix_setrecsrc(snd_mixer *m, u_int32_t src) +{ + struct ess_info *sc = mix_getdevinfo(m); + u_char recdev; + + switch (src) { + case SOUND_MASK_CD: + recdev = 0x02; + break; + + case SOUND_MASK_LINE: + recdev = 0x06; + break; + + case SOUND_MASK_IMIX: + recdev = 0x05; + break; + + case SOUND_MASK_MIC: + default: + recdev = 0x00; + src = SOUND_MASK_MIC; + break; + } + + ess_setmixer(sc, 0x1c, recdev); + + return src; +} + +static int +ess_probe(device_t dev) +{ + uintptr_t func, ver, r, f; + + /* The parent device has already been probed. */ + r = BUS_READ_IVAR(device_get_parent(dev), dev, 0, &func); + if (func != SCF_PCM) + return (ENXIO); + + r = BUS_READ_IVAR(device_get_parent(dev), dev, 1, &ver); + f = (ver & 0xffff0000) >> 16; + if (!(f & BD_F_ESS)) + return (ENXIO); + + device_set_desc(dev, "ESS 18xx DSP"); + + return 0; +} + +static int +ess_attach(device_t dev) +{ + struct ess_info *sc; + + sc = (struct ess_info *)malloc(sizeof *sc, M_DEVBUF, M_NOWAIT); + if (!sc) + return ENXIO; + bzero(sc, sizeof *sc); + + return ess_doattach(dev, sc); +} + +static device_method_t ess_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, ess_probe), + DEVMETHOD(device_attach, ess_attach), + + { 0, 0 } +}; + +static driver_t ess_driver = { + "pcm", + ess_methods, + sizeof(snddev_info), +}; + +DRIVER_MODULE(ess, sbc, ess_driver, pcm_devclass, 0, 0); + +static devclass_t esscontrol_devclass; + +static struct isa_pnp_id essc_ids[] = { + {0x06007316, "ESS Control"}, + {0} +}; + +static int +esscontrol_probe(device_t dev) +{ + return ISA_PNP_PROBE(device_get_parent(dev), dev, essc_ids); +} + +static int +esscontrol_attach(device_t dev) +{ +#ifdef notyet + struct resource *io; + int rid, i, x; + + rid = 0; + io = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1, RF_ACTIVE); + x = 0; + for (i = 0; i < 0x100; i++) { + port_wr(io, 0, i); + x = port_rd(io, 1); + if ((i & 0x0f) == 0) + printf("%3.3x: ", i); + printf("%2.2x ", x); + if ((i & 0x0f) == 0x0f) + printf("\n"); + } + bus_release_resource(dev, SYS_RES_IOPORT, 0, io); + io = NULL; +#endif + + return 0; +} + +static device_method_t esscontrol_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, esscontrol_probe), + DEVMETHOD(device_attach, esscontrol_attach), + + { 0, 0 } +}; + +static driver_t esscontrol_driver = { + "esscontrol", + esscontrol_methods, + sizeof(snddev_info), +}; + +DRIVER_MODULE(esscontrol, isa, esscontrol_driver, esscontrol_devclass, 0, 0); + + + + |