summaryrefslogtreecommitdiff
path: root/sys/dev
diff options
context:
space:
mode:
authoraugustss <augustss@NetBSD.org>2001-09-29 14:00:57 +0000
committeraugustss <augustss@NetBSD.org>2001-09-29 14:00:57 +0000
commitc01875042370160ecdc5e48ab8ae24f3d615307d (patch)
tree52efe81a3d4640b9cb188120457dde8825cecd51 /sys/dev
parent58a0f075d2f05746ae360824c6ccb228c6568a78 (diff)
Add esl driver for Eiger Labs sound card. From Jared D. McNeill
<jmcneill@invisible.yi.org>.
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/pcmcia/esl.c990
-rw-r--r--sys/dev/pcmcia/esl_pcmcia.c252
-rw-r--r--sys/dev/pcmcia/eslreg.h308
-rw-r--r--sys/dev/pcmcia/eslvar.h92
-rw-r--r--sys/dev/pcmcia/opl_esl.c107
5 files changed, 1749 insertions, 0 deletions
diff --git a/sys/dev/pcmcia/esl.c b/sys/dev/pcmcia/esl.c
new file mode 100644
index 00000000000..a607ea5c978
--- /dev/null
+++ b/sys/dev/pcmcia/esl.c
@@ -0,0 +1,990 @@
+/* $NetBSD: esl.c,v 1.1 2001/09/29 14:00:57 augustss Exp $ */
+
+/*
+ * Copyright (c) 2001 Jared D. McNeill <jmcneill@invisible.yi.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Jared D. McNeill.
+ * 4. Neither the name of the author nor the names of any contributors may
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <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/proc.h>
+#include <sys/kernel.h>
+#include <sys/audioio.h>
+
+#include <machine/cpu.h>
+#include <machine/intr.h>
+#include <machine/bus.h>
+#include <machine/pio.h>
+
+#include <dev/audio_if.h>
+#include <dev/auconv.h>
+#include <dev/mulaw.h>
+
+#include <dev/pcmcia/pcmciavar.h>
+
+#include <dev/pcmcia/eslvar.h>
+#include <dev/pcmcia/eslreg.h>
+
+int esl_open __P((void *, int));
+void esl_close __P((void *));
+int esl_query_encoding __P((void *, struct audio_encoding *));
+int esl_set_params __P((void *, int, int, struct audio_params *,
+ struct audio_params *));
+int esl_round_blocksize __P((void *, int));
+int esl_halt_output __P((void *));
+int esl_halt_input __P((void *));
+int esl_speaker_ctl __P((void *, int));
+int esl_getdev __P((void *, struct audio_device *));
+int esl_set_port __P((void *, mixer_ctrl_t *));
+int esl_get_port __P((void *, mixer_ctrl_t *));
+int esl_query_devinfo __P((void *, mixer_devinfo_t *));
+int esl_get_props __P((void *));
+int esl_trigger_output __P((void *, void *, void *, int, void (*)(void *),
+ void *, struct audio_params *));
+
+/* Supporting subroutines */
+int esl_reset __P((struct esl_pcmcia_softc *));
+void esl_setup __P((struct esl_pcmcia_softc *));
+void esl_set_gain __P((struct esl_pcmcia_softc *, int, int));
+void esl_speaker_on __P((struct esl_pcmcia_softc *));
+void esl_speaker_off __P((struct esl_pcmcia_softc *));
+int esl_identify __P((struct esl_pcmcia_softc *));
+int esl_rdsp __P((struct esl_pcmcia_softc *));
+int esl_wdsp __P((struct esl_pcmcia_softc *, u_char));
+u_char esl_dsp_read_ready __P((struct esl_pcmcia_softc *));
+u_char esl_dsp_write_ready __P((struct esl_pcmcia_softc *));
+u_char esl_get_dsp_status __P((struct esl_pcmcia_softc *));
+u_char esl_read_x_reg __P((struct esl_pcmcia_softc *, u_char));
+int esl_write_x_reg __P((struct esl_pcmcia_softc *, u_char, u_char));
+void esl_clear_xreg_bits __P((struct esl_pcmcia_softc *, u_char, u_char));
+void esl_set_xreg_bits __P((struct esl_pcmcia_softc *, u_char, u_char));
+u_char esl_read_mix_reg __P((struct esl_pcmcia_softc *, u_char));
+void esl_write_mix_reg __P((struct esl_pcmcia_softc *, u_char, u_char));
+void esl_clear_mreg_bits __P((struct esl_pcmcia_softc *, u_char, u_char));
+void esl_set_mreg_bits __P((struct esl_pcmcia_softc *, u_char, u_char));
+void esl_read_multi_mix_reg __P((struct esl_pcmcia_softc *, u_char,
+ u_int8_t *, bus_size_t));
+u_int esl_srtotc __P((u_int));
+u_int esl_srtofc __P((u_int));
+
+struct audio_device esl_device = {
+ "AudioDrive",
+ "",
+ "esl"
+};
+
+struct audio_hw_if esl_hw_if = {
+ esl_open,
+ esl_close,
+ NULL,
+ esl_query_encoding,
+ esl_set_params,
+ esl_round_blocksize,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ esl_halt_output,
+ esl_halt_input,
+ esl_speaker_ctl,
+ esl_getdev,
+ NULL,
+ esl_set_port,
+ esl_get_port,
+ esl_query_devinfo,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ esl_get_props,
+ esl_trigger_output,
+ NULL
+};
+
+static char *eslmodel[] = {
+ "1688",
+ "688",
+};
+
+int
+esl_open(void *hdl, int flags)
+{
+
+ struct esl_pcmcia_softc *sc = hdl;
+ int i;
+
+ if (sc->sc_esl.sc_open != 0)
+ return(EBUSY);
+
+ if ((*sc->sc_enable)(sc))
+ return(ENXIO);
+
+ if (esl_reset(sc) != 0) {
+ printf("%s: esl_open: esl_reset failed\n",
+ sc->sc_esl.sc_dev.dv_xname);
+ return(ENXIO);
+ }
+
+ /* because we did a reset */
+ esl_setup(sc);
+
+ /* Set all mixer controls to sane values (since we just did a reset) */
+ for (i = 0; i < ESS_MAX_NDEVS; i++)
+ esl_set_gain(sc, i, 1);
+
+ sc->sc_esl.sc_open = 1;
+
+ /* XXX: Delay a bit */
+ delay(10000);
+
+ return(0);
+}
+
+
+void
+esl_close(void *hdl)
+{
+ struct esl_pcmcia_softc *sc = hdl;
+
+ esl_speaker_off(sc);
+
+ (*sc->sc_disable)(sc);
+
+ sc->sc_esl.sc_open = 0;
+
+ return;
+}
+
+int
+esl_query_encoding(void *hdl, struct audio_encoding *ae)
+{
+
+ switch (ae->index) {
+ case 0:
+ strcpy(ae->name, AudioEulinear);
+ ae->encoding = AUDIO_ENCODING_ULINEAR;
+ ae->precision = 8;
+ ae->flags = 0;
+ return (0);
+ case 1:
+ strcpy(ae->name, AudioEmulaw);
+ ae->encoding = AUDIO_ENCODING_ULAW;
+ ae->precision = 8;
+ ae->flags = AUDIO_ENCODINGFLAG_EMULATED;
+ return (0);
+ case 2:
+ strcpy(ae->name, AudioEalaw);
+ ae->encoding = AUDIO_ENCODING_ALAW;
+ ae->precision = 8;
+ ae->flags = AUDIO_ENCODINGFLAG_EMULATED;
+ return (0);
+ case 3:
+ strcpy(ae->name, AudioEslinear);
+ ae->encoding = AUDIO_ENCODING_SLINEAR;
+ ae->precision = 8;
+ ae->flags = 0;
+ return (0);
+ case 4:
+ strcpy(ae->name, AudioEslinear_le);
+ ae->encoding = AUDIO_ENCODING_SLINEAR_LE;
+ ae->precision = 16;
+ ae->flags = 0;
+ return (0);
+ case 5:
+ strcpy(ae->name, AudioEulinear_le);
+ ae->encoding = AUDIO_ENCODING_ULINEAR_LE;
+ ae->precision = 16;
+ ae->flags = 0;
+ return (0);
+ case 6:
+ strcpy(ae->name, AudioEslinear_be);
+ ae->encoding = AUDIO_ENCODING_SLINEAR_BE;
+ ae->precision = 16;
+ ae->flags = AUDIO_ENCODINGFLAG_EMULATED;
+ return (0);
+ case 7:
+ strcpy(ae->name, AudioEulinear_be);
+ ae->encoding = AUDIO_ENCODING_ULINEAR_BE;
+ ae->precision = 16;
+ ae->flags = AUDIO_ENCODINGFLAG_EMULATED;
+ return (0);
+ default:
+ return EINVAL;
+ }
+ return (0);
+}
+
+int
+esl_set_params(void *hdl, int setmode, int usemode,
+ struct audio_params *play, struct audio_params *rec)
+{
+ struct esl_pcmcia_softc *sc = hdl;
+ int rate;
+
+ /*
+ * I'm too lazy to make this do anything other than play
+ * -- jmcneill
+ */
+ if ((setmode & AUMODE_PLAY) == 0) {
+ printf("%s: esl_set_params: only AUMODE_PLAY is supported\n",
+ sc->sc_esl.sc_dev.dv_xname);
+ return(EINVAL);
+ }
+
+ if (play->sample_rate < ESS_MINRATE ||
+ play->sample_rate > ESS_MAXRATE ||
+ (play->precision != 8 && play->precision != 16) ||
+ (play->channels != 1 && play->channels != 2))
+ return(EINVAL);
+
+ play->factor = 1;
+ play->sw_code = NULL;
+ switch (play->encoding) {
+ case AUDIO_ENCODING_SLINEAR_BE:
+ case AUDIO_ENCODING_ULINEAR_BE:
+ if (play->precision == 16)
+ play->sw_code = swap_bytes;
+ break;
+ case AUDIO_ENCODING_SLINEAR_LE:
+ case AUDIO_ENCODING_ULINEAR_LE:
+ break;
+ case AUDIO_ENCODING_ULAW:
+ play->factor = 2;
+ play->sw_code = mulaw_to_ulinear16_le;
+ break;
+ case AUDIO_ENCODING_ALAW:
+ play->factor = 2;
+ play->sw_code = alaw_to_ulinear16_le;
+ break;
+ default:
+ return(EINVAL);
+ }
+
+ rate = play->sample_rate;
+
+ esl_write_x_reg(sc, ESS_XCMD_SAMPLE_RATE, esl_srtotc(rate));
+ esl_write_x_reg(sc, ESS_XCMD_FILTER_CLOCK, esl_srtofc(rate));
+
+ return(0);
+}
+
+int
+esl_round_blocksize(void *hdl, int bs)
+{
+
+ return((bs / 128) * 128);
+}
+
+
+int
+esl_halt_output(void *hdl)
+{
+ struct esl_pcmcia_softc *sc = hdl;
+
+ if (sc->sc_esl.active) {
+ esl_clear_xreg_bits(sc, ESS_XCMD_AUDIO1_CTRL2,
+ ESS_AUDIO1_CTRL2_FIFO_ENABLE);
+ sc->sc_esl.active = 0;
+ }
+
+ return(0);
+}
+
+int
+esl_halt_input(void *hdl)
+{
+
+ return(0);
+}
+
+int
+esl_speaker_ctl(void *hdl, int on)
+{
+
+ return(0);
+}
+
+int
+esl_getdev(void *hdl, struct audio_device *ret)
+{
+
+ *ret = esl_device;
+ return(0);
+}
+
+int
+esl_set_port(void *hdl, mixer_ctrl_t *mc)
+{
+ struct esl_pcmcia_softc *sc = hdl;
+ int lgain, rgain;
+
+ switch(mc->dev) {
+ case ESS_MASTER_VOL:
+ case ESS_DAC_PLAY_VOL:
+ case ESS_SYNTH_PLAY_VOL:
+ if (mc->type != AUDIO_MIXER_VALUE)
+ return(EINVAL);
+
+ switch(mc->un.value.num_channels) {
+ case 1:
+ lgain = rgain = ESS_4BIT_GAIN(
+ mc->un.value.level[AUDIO_MIXER_LEVEL_MONO]);
+ break;
+ case 2:
+ lgain = ESS_4BIT_GAIN(
+ mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT]);
+ rgain = ESS_4BIT_GAIN(
+ mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT]);
+ break;
+ default:
+ return(EINVAL);
+ }
+ sc->sc_esl.gain[mc->dev][ESS_LEFT] = lgain;
+ sc->sc_esl.gain[mc->dev][ESS_RIGHT] = rgain;
+ esl_set_gain(sc, mc->dev, 1);
+ return(0);
+ break;
+ default:
+ /* FALLTHROUGH */
+ }
+
+ return(EINVAL);
+}
+
+int
+esl_get_port(void *hdl, mixer_ctrl_t *mc)
+{
+ struct esl_pcmcia_softc *sc = hdl;
+
+ switch(mc->dev) {
+ case ESS_MASTER_VOL:
+ case ESS_DAC_PLAY_VOL:
+ case ESS_SYNTH_PLAY_VOL:
+ switch(mc->un.value.num_channels) {
+ case 1:
+ mc->un.value.level[AUDIO_MIXER_LEVEL_MONO] =
+ sc->sc_esl.gain[mc->dev][ESS_LEFT];
+ break;
+ case 2:
+ mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] =
+ sc->sc_esl.gain[mc->dev][ESS_LEFT];
+ mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] =
+ sc->sc_esl.gain[mc->dev][ESS_RIGHT];
+ break;
+ default:
+ return(EINVAL);
+ }
+ return(0);
+ default:
+ /* FALLTHROUGH */
+ }
+
+ return(EINVAL);
+}
+
+int
+esl_query_devinfo(void *hdl, mixer_devinfo_t *di)
+{
+
+ switch(di->index) {
+ case ESS_DAC_PLAY_VOL:
+ di->mixer_class = ESS_INPUT_CLASS;
+ di->next = di->prev = AUDIO_MIXER_LAST;
+ strcpy(di->label.name, AudioNdac);
+ di->type = AUDIO_MIXER_VALUE;
+ di->un.v.num_channels = 2;
+ strcpy(di->un.v.units.name, AudioNvolume);
+ return(0);
+ case ESS_SYNTH_PLAY_VOL:
+ di->mixer_class = ESS_INPUT_CLASS;
+ di->next = di->prev = AUDIO_MIXER_LAST;
+ strcpy(di->label.name, AudioNfmsynth);
+ di->type = AUDIO_MIXER_VALUE;
+ di->un.v.num_channels = 2;
+ strcpy(di->un.v.units.name, AudioNvolume);
+ return(0);
+ case ESS_INPUT_CLASS:
+ di->mixer_class = ESS_INPUT_CLASS;
+ di->next = di->prev = AUDIO_MIXER_LAST;
+ strcpy(di->label.name, AudioCinputs);
+ di->type = AUDIO_MIXER_CLASS;
+ return(0);
+ case ESS_MASTER_VOL:
+ di->mixer_class = ESS_OUTPUT_CLASS;
+ di->next = di->prev = AUDIO_MIXER_LAST;
+ strcpy(di->label.name, AudioNmaster);
+ di->type = AUDIO_MIXER_VALUE;
+ di->un.v.num_channels = 2;
+ strcpy(di->un.v.units.name, AudioNvolume);
+ return(0);
+ case ESS_OUTPUT_CLASS:
+ di->mixer_class = ESS_OUTPUT_CLASS;
+ di->next = di->prev = AUDIO_MIXER_LAST;
+ strcpy(di->label.name, AudioCoutputs);
+ di->type = AUDIO_MIXER_CLASS;
+ return(0);
+ default:
+ /* FALLTHROUGH */
+ }
+
+ return(ENXIO);
+}
+
+int
+esl_get_props(void *hdl)
+{
+
+ return(AUDIO_PROP_MMAP);
+}
+
+int
+esl_trigger_output(void *hdl, void *start, void *end, int blksize,
+ void (*intr)(void *), void *intrarg,
+ struct audio_params *param)
+{
+ struct esl_pcmcia_softc *sc = hdl;
+ bus_space_tag_t iot = sc->sc_pcioh.iot;
+ bus_space_handle_t ioh = sc->sc_pcioh.ioh;
+ int bs;
+ int cnt;
+ u_int8_t reg;
+
+ if (sc->sc_esl.active) {
+ printf("%s: esl_trigger_output: already running\n",
+ sc->sc_esl.sc_dev.dv_xname);
+ return(1);
+ }
+
+ sc->sc_esl.active = 1;
+ sc->sc_esl.intr = intr;
+ sc->sc_esl.arg = intrarg;
+
+ /* Stereo or Mono selection */
+ reg = esl_read_x_reg(sc, ESS_XCMD_AUDIO_CTRL);
+ if (param->channels == 2) {
+ reg &= ~ESS_AUDIO_CTRL_MONO;
+ reg |= ESS_AUDIO_CTRL_STEREO;
+ } else {
+ reg |= ESS_AUDIO_CTRL_MONO;
+ reg &= ~ESS_AUDIO_CTRL_STEREO;
+ }
+ esl_write_x_reg(sc, ESS_XCMD_AUDIO_CTRL, reg);
+
+ /* Program the FIFO (16-bit/8-bit, signed/unsigned, stereo/mono) */
+ reg = esl_read_x_reg(sc, ESS_XCMD_AUDIO1_CTRL1);
+ if (param->precision * param->factor == 16)
+ reg |= ESS_AUDIO1_CTRL1_FIFO_SIZE;
+ else
+ reg &= ~ESS_AUDIO1_CTRL1_FIFO_SIZE;
+ if (param->channels == 2)
+ reg |= ESS_AUDIO1_CTRL1_FIFO_STEREO;
+ else
+ reg &= ~ESS_AUDIO1_CTRL1_FIFO_STEREO;
+ if (param->encoding == AUDIO_ENCODING_SLINEAR_BE ||
+ param->encoding == AUDIO_ENCODING_SLINEAR_LE)
+ reg |= ESS_AUDIO1_CTRL1_FIFO_SIGNED;
+ else
+ reg &= ~ESS_AUDIO1_CTRL1_FIFO_SIGNED;
+ reg |= ESS_AUDIO1_CTRL1_FIFO_CONNECT;
+ esl_write_x_reg(sc, ESS_XCMD_AUDIO1_CTRL1, reg);
+
+ /* Program transfer count registers with 2s complement of count */
+ bs = -blksize;
+ esl_write_x_reg(sc, ESS_XCMD_XFER_COUNTLO, bs);
+ esl_write_x_reg(sc, ESS_XCMD_XFER_COUNTHI, bs >> 8);
+
+ esl_wdsp(sc, ESS_ACMD_ENABLE_SPKR);
+ reg = esl_read_x_reg(sc, ESS_XCMD_AUDIO1_CTRL2);
+ reg &= ~(ESS_AUDIO1_CTRL2_DMA_READ | ESS_AUDIO1_CTRL2_ADC_ENABLE);
+ reg |= ESS_AUDIO1_CTRL2_FIFO_ENABLE | ESS_AUDIO1_CTRL2_AUTO_INIT;
+ esl_write_x_reg(sc, ESS_XCMD_AUDIO1_CTRL2, reg);
+ cnt = (char *)end - (char *)start;
+ if (cnt == 0)
+ printf("%s: no count left\n", sc->sc_esl.sc_dev.dv_xname);
+
+ sc->sc_esl.sc_dmaaddr = sc->sc_esl.sc_dmastart = start;
+ sc->sc_esl.sc_dmaend = end;
+ sc->sc_esl.sc_blksize = blksize;
+ sc->sc_esl.sc_blkpos = 0;
+
+ /* XXX: Delay a bit */
+ delay(10000);
+
+ /* Prime the FIFO */
+ bus_space_write_multi_1(iot, ioh, ESS_FIFO_WRITE, start, ESS_FIFO_SIZE);
+ sc->sc_esl.sc_dmaaddr += ESS_FIFO_SIZE;
+ sc->sc_esl.sc_blkpos += ESS_FIFO_SIZE;
+
+ return(0);
+}
+
+/* Additional subroutines used by the above (NOT required by audio(9)) */
+
+int
+esl_init(struct esl_pcmcia_softc *sc)
+{
+ const int ENABLE[] = { 0x0, 0x9, 0xb };
+ const int ENABLE_ORDER[] = { 1, 1, 1, 2, 1, 2, 1, 1, 2, 1, 0, -1 };
+ struct audio_attach_args aa;
+ int i;
+ int model;
+ bus_space_tag_t iot = sc->sc_pcioh.iot;
+ bus_space_handle_t ioh = sc->sc_pcioh.ioh;
+
+ sc->sc_esl.sc_open = 0;
+
+ /* Initialization sequence */
+ for (i = 0; ENABLE_ORDER[i] != -1; i++)
+ bus_space_read_1(iot, ioh, ENABLE[i]);
+ if (esl_reset(sc)) {
+ printf("%s: esl_init: esl_reset failed\n",
+ sc->sc_esl.sc_dev.dv_xname);
+ return(1);
+ }
+
+ if (esl_identify(sc)) {
+ printf("%s: esl_init: esl_identify failed\n",
+ sc->sc_esl.sc_dev.dv_xname);
+ return(1);
+ }
+
+ if (!sc->sc_esl.sc_version)
+ return(1); /* Probably a Sound Blaster */
+
+ model = ESS_UNSUPPORTED;
+
+ switch (sc->sc_esl.sc_version & 0xfff0) {
+ case 0x6880:
+ if ((sc->sc_esl.sc_version & 0x0f) >= 8) {
+ model = ESS_1688;
+ } else {
+ model = ESS_688;
+ }
+ break;
+ }
+
+ if (model == ESS_UNSUPPORTED) {
+ printf("%s: unknown model 0x%04x\n",
+ sc->sc_esl.sc_dev.dv_xname, sc->sc_esl.sc_version);
+ return(1);
+ }
+
+ printf("%s: ESS AudioDrive %s [version 0x%04x]\n",
+ sc->sc_esl.sc_dev.dv_xname, eslmodel[model],
+ sc->sc_esl.sc_version);
+
+ /* Set volumes to 50% */
+ for (i = 0; i < ESS_MAX_NDEVS; i++) {
+ sc->sc_esl.gain[i][ESS_LEFT] =
+ sc->sc_esl.gain[i][ESS_RIGHT] =
+ ESS_4BIT_GAIN(AUDIO_MAX_GAIN / 2);
+ esl_set_gain(sc, i, 1);
+ }
+
+ sc->sc_audiodev = audio_attach_mi(&esl_hw_if, sc, &sc->sc_esl.sc_dev);
+
+ /* Attach the OPL device */
+ aa.type = AUDIODEV_TYPE_OPL;
+ aa.hwif = 0;
+ aa.hdl = 0;
+
+ sc->sc_opldev = config_found(&sc->sc_esl.sc_dev, &aa, audioprint);
+
+ /* Disable speaker until device is opened */
+ esl_speaker_off(sc);
+
+ sc->sc_esl.sc_open = 0;
+
+ return(0);
+}
+
+int
+esl_intr(void *hdl)
+{
+ struct esl_pcmcia_softc *sc = hdl;
+ bus_space_tag_t iot = sc->sc_pcioh.iot;
+ bus_space_handle_t ioh = sc->sc_pcioh.ioh;
+ u_int8_t reg;
+ u_char *pos;
+
+ /* Clear interrupt */
+ reg = bus_space_read_1(iot, ioh, ESS_CLEAR_INTR);
+
+ if (sc->sc_esl.active) {
+ pos = sc->sc_esl.sc_dmaaddr;
+ bus_space_write_multi_1(iot, ioh, ESS_FIFO_WRITE, pos,
+ ESS_FIFO_SIZE / 2);
+
+ sc->sc_esl.sc_blkpos += (ESS_FIFO_SIZE / 2);
+ if (sc->sc_esl.sc_blkpos > sc->sc_esl.sc_blksize) {
+ (*sc->sc_esl.intr)(sc->sc_esl.arg);
+ sc->sc_esl.sc_blkpos -= sc->sc_esl.sc_blksize;
+ }
+ pos += (ESS_FIFO_SIZE / 2);
+ if (pos >= sc->sc_esl.sc_dmaend)
+ pos = sc->sc_esl.sc_dmastart;
+
+ sc->sc_esl.sc_dmaaddr = pos;
+ }
+
+ return(1);
+}
+
+int
+esl_reset(struct esl_pcmcia_softc *sc)
+{
+ bus_space_tag_t iot = sc->sc_pcioh.iot;
+ bus_space_handle_t ioh = sc->sc_pcioh.ioh;
+
+ bus_space_write_1(iot, ioh, ESS_DSP_RESET, ESS_RESET_EXT);
+ delay(10000); /* XXX: Ugly, but ess.c does this too */
+ bus_space_write_1(iot, ioh, ESS_DSP_RESET, 0);
+ if (esl_rdsp(sc) != ESS_MAGIC)
+ return(1);
+
+ /* Enable access to the extended command set */
+ esl_wdsp(sc, ESS_ACMD_ENABLE_EXT);
+
+ return(0);
+}
+
+void
+esl_setup(struct esl_pcmcia_softc *sc)
+{
+ u_char reg;
+
+ /*
+ * Configure IRQ. Set bit 5 of B1h high to enable interrupt on
+ * FIFOHE, and keep bit 6 low.
+ */
+ reg = ESS_IRQ_CTRL_MASK | 0x20 | ESS_IRQ_CTRL_INTRA;
+ esl_write_x_reg(sc, ESS_XCMD_IRQ_CTRL, reg);
+
+ /*
+ * "Config DRQ", well not really. Instead of configuring a DRQ,
+ * we leave bits 7 and 5 of B2h low.
+ */
+ reg = 0x10 | 0x40;
+ esl_write_x_reg(sc, ESS_XCMD_DRQ_CTRL, reg);
+
+ return;
+}
+
+void
+esl_set_gain(struct esl_pcmcia_softc *sc, int port, int on)
+{
+ int gain, left, right;
+ int src;
+
+ switch(port) {
+ case ESS_MASTER_VOL:
+ src = ESS_MREG_VOLUME_MASTER;
+ break;
+ case ESS_DAC_PLAY_VOL:
+ src = ESS_MREG_VOLUME_VOICE;
+ break;
+ case ESS_SYNTH_PLAY_VOL:
+ src = ESS_MREG_VOLUME_SYNTH;
+ break;
+ default:
+ return;
+ }
+
+ if (on) {
+ left = sc->sc_esl.gain[port][ESS_LEFT];
+ right = sc->sc_esl.gain[port][ESS_RIGHT];
+ } else
+ left = right = 0;
+
+ gain = ESS_STEREO_GAIN(left, right);
+
+ esl_write_mix_reg(sc, src, gain);
+
+ return;
+}
+
+void
+esl_speaker_on(struct esl_pcmcia_softc *sc)
+{
+
+ /* Unmute the DAC */
+ esl_set_gain(sc, ESS_DAC_PLAY_VOL, 1);
+
+ return;
+}
+
+void
+esl_speaker_off(struct esl_pcmcia_softc *sc)
+{
+
+ /* Mute the DAC */
+ esl_set_gain(sc, ESS_DAC_PLAY_VOL, 0);
+
+ return;
+}
+
+int
+esl_identify(struct esl_pcmcia_softc *sc)
+{
+ u_char reg1, reg2;
+ int i;
+
+ esl_wdsp(sc, ESS_ACMD_LEGACY_ID);
+ for (i = 1000, reg1 = reg2 = 0; i; i--)
+ if (esl_dsp_read_ready(sc)) {
+ if (reg1 == 0)
+ reg1 = esl_rdsp(sc);
+ else
+ reg2 = esl_rdsp(sc);
+ }
+
+ sc->sc_esl.sc_version = (reg1 << 8) + reg2;
+
+ return(0);
+}
+
+/* Read a byte from the DSP */
+int
+esl_rdsp(struct esl_pcmcia_softc *sc)
+{
+ bus_space_tag_t iot = sc->sc_pcioh.iot;
+ bus_space_handle_t ioh = sc->sc_pcioh.ioh;
+ int i;
+
+ for (i = ESS_READ_TIMEOUT; i > 0; --i) {
+ if (esl_dsp_read_ready(sc)) {
+ i = bus_space_read_1(iot, ioh, ESS_DSP_READ);
+ return(i);
+ } else
+ delay(10);
+ }
+
+ printf("esl_rdsp: timed out\n");
+ return(-1);
+}
+
+/* Write a byte to the DSP */
+int
+esl_wdsp(struct esl_pcmcia_softc *sc, u_char v)
+{
+ bus_space_tag_t iot = sc->sc_pcioh.iot;
+ bus_space_handle_t ioh = sc->sc_pcioh.ioh;
+ int i;
+
+ for (i = ESS_WRITE_TIMEOUT; i > 0; --i) {
+ if (esl_dsp_write_ready(sc)) {
+ bus_space_write_1(iot, ioh, ESS_DSP_WRITE, v);
+ return(0);
+ } else
+ delay(10);
+ }
+
+ printf("esl_wdsp(0x%02x): timed out\n", v);
+ return(-1);
+}
+
+/* Get the read status of the DSP: 1 == Ready, 0 == Not Ready */
+u_char
+esl_dsp_read_ready(struct esl_pcmcia_softc *sc)
+{
+
+ return((esl_get_dsp_status(sc) & ESS_DSP_READ_READY) ? 1 : 0);
+}
+
+/* Get the write status of the DSP: 1 == Ready, 0 == Not Ready */
+u_char
+esl_dsp_write_ready(struct esl_pcmcia_softc *sc)
+{
+
+ return((esl_get_dsp_status(sc) & ESS_DSP_WRITE_BUSY) ? 0 : 1);
+}
+
+/* Return the status of the DSP */
+u_char
+esl_get_dsp_status(struct esl_pcmcia_softc *sc)
+{
+
+ return(bus_space_read_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh,
+ ESS_DSP_RW_STATUS));
+}
+
+/* Read a value from one of the extended registers */
+u_char
+esl_read_x_reg(struct esl_pcmcia_softc *sc, u_char reg)
+{
+ int error;
+
+ if ((error = esl_wdsp(sc, 0xC0)) == 0)
+ error = esl_wdsp(sc, reg);
+ if (error)
+ printf("esl_read_x_reg: error reading 0x%02x\n", reg);
+ return(esl_rdsp(sc));
+}
+
+/* Write a value to one of the extended registers */
+int
+esl_write_x_reg(struct esl_pcmcia_softc *sc, u_char reg, u_char val)
+{
+ int error;
+
+ if ((error = esl_wdsp(sc, reg)) == 0)
+ error = esl_wdsp(sc, val);
+
+ return(error);
+}
+
+void
+esl_clear_xreg_bits(struct esl_pcmcia_softc *sc, u_char reg, u_char mask)
+{
+
+ esl_write_x_reg(sc, reg, esl_read_x_reg(sc, reg) & ~mask);
+
+ return;
+}
+
+void
+esl_set_xreg_bits(struct esl_pcmcia_softc *sc, u_char reg, u_char mask)
+{
+
+ esl_write_x_reg(sc, reg, esl_read_x_reg(sc, reg) | mask);
+}
+
+u_char
+esl_read_mix_reg(struct esl_pcmcia_softc *sc, u_char reg)
+{
+ bus_space_tag_t iot = sc->sc_pcioh.iot;
+ bus_space_handle_t ioh = sc->sc_pcioh.ioh;
+#if 0
+ int s;
+#endif
+ u_char val;
+
+#if 0
+ s = splaudio();
+#endif
+ bus_space_write_1(iot, ioh, ESS_MIX_REG_SELECT, reg);
+ val = bus_space_read_1(iot, ioh, ESS_MIX_REG_DATA);
+#if 0
+ splx(s);
+#endif
+
+ return(val);
+}
+
+void
+esl_write_mix_reg(struct esl_pcmcia_softc *sc, u_char reg, u_char val)
+{
+ bus_space_tag_t iot = sc->sc_pcioh.iot;
+ bus_space_handle_t ioh = sc->sc_pcioh.ioh;
+#if 0
+ int s;
+#endif
+
+#if 0
+ s = splaudio();
+#endif
+ bus_space_write_1(iot, ioh, ESS_MIX_REG_SELECT, reg);
+ bus_space_write_1(iot, ioh, ESS_MIX_REG_DATA, val);
+#if 0
+ splx(s);
+#endif
+
+ return;
+}
+
+void
+esl_clear_mreg_bits(struct esl_pcmcia_softc *sc, u_char reg, u_char mask)
+{
+
+ esl_write_mix_reg(sc, reg, esl_read_mix_reg(sc, reg) & ~mask);
+
+ return;
+}
+
+void
+esl_set_mreg_bits(struct esl_pcmcia_softc *sc, u_char reg, u_char mask)
+{
+
+ esl_write_mix_reg(sc, reg, esl_read_mix_reg(sc, reg) | mask);
+
+ return;
+}
+
+void
+esl_read_multi_mix_reg(struct esl_pcmcia_softc *sc, u_char reg,
+ u_int8_t *datap, bus_size_t count)
+{
+ bus_space_tag_t iot = sc->sc_pcioh.iot;
+ bus_space_handle_t ioh = sc->sc_pcioh.ioh;
+#if 0
+ int s;
+#endif
+
+#if 0
+ s = splaudio();
+#endif
+ bus_space_write_1(iot, ioh, ESS_MIX_REG_SELECT, reg);
+ bus_space_read_multi_1(iot, ioh, ESS_MIX_REG_DATA, datap, count);
+#if 0
+ splx(s);
+#endif
+
+ return;
+}
+
+/* Calculate the time constant for the requested sampling rate */
+u_int
+esl_srtotc(u_int rate)
+{
+ u_int tc;
+
+ /* The following formulas are from the ESS data sheet. */
+ if (rate <= 22050)
+ tc = 128 - 397700L / rate;
+ else
+ tc = 256 - 795500L / rate;
+
+ return(tc);
+}
+
+/* Calculate the filter constant for the requested sampling rate */
+u_int
+esl_srtofc(u_int rate)
+{
+
+ /* From dev/isa/ess.c:ess_srtofc() rev 1.53 */
+ return(256 - 200279L / rate);
+}
diff --git a/sys/dev/pcmcia/esl_pcmcia.c b/sys/dev/pcmcia/esl_pcmcia.c
new file mode 100644
index 00000000000..146a0436bee
--- /dev/null
+++ b/sys/dev/pcmcia/esl_pcmcia.c
@@ -0,0 +1,252 @@
+/* $NetBSD: esl_pcmcia.c,v 1.1 2001/09/29 14:00:57 augustss Exp $ */
+
+/*
+ * Copyright (c) 2000 Jared D. McNeill <jmcneill@invisible.yi.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Jared D. McNeill.
+ * 4. Neither the name of the author nor the names of any contributors may
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+#include <sys/buf.h>
+#include <sys/audioio.h>
+
+#include <machine/bus.h>
+#include <machine/intr.h>
+
+#include <dev/audio_if.h>
+
+#include <dev/pcmcia/pcmciareg.h>
+#include <dev/pcmcia/pcmciavar.h>
+#include <dev/pcmcia/pcmciadevs.h>
+
+#include <dev/pcmcia/eslreg.h>
+#include <dev/pcmcia/eslvar.h>
+
+static const struct esl_pcmcia_product {
+ char *name;
+ int32_t manufacturer;
+ int32_t product;
+ char *cis_info[4];
+ int function;
+} esl_pcmcia_products[] = {
+ { PCMCIA_STR_EIGERLABS_EPX_AA2000,
+ PCMCIA_VENDOR_INVALID, PCMCIA_PRODUCT_INVALID,
+ PCMCIA_CIS_EIGERLABS_EPX_AA2000, 0 },
+
+ { NULL }
+};
+
+int esl_pcmcia_match __P((struct device *, struct cfdata *, void *));
+void esl_pcmcia_attach __P((struct device *, struct device *, void *));
+int esl_pcmcia_detach __P((struct device *, int));
+
+int esl_pcmcia_enable __P((struct esl_pcmcia_softc *));
+void esl_pcmcia_disable __P((struct esl_pcmcia_softc *));
+
+struct cfattach esl_pcmcia_ca = {
+ sizeof(struct esl_pcmcia_softc), esl_pcmcia_match, esl_pcmcia_attach,
+ esl_pcmcia_detach
+};
+
+#define ESL_NDEVS (sizeof(esl_pcmcia_products) / sizeof(esl_pcmcia_products[0]))
+
+#define esl_pcmcia_product_lookup(card, fct, n) \
+ (((card)->manufacturer != PCMCIA_VENDOR_INVALID) && \
+ ((card)->product != PCMCIA_PRODUCT_INVALID) && \
+ (esl_pcmcia_products[(n)].cis_info[0]) && \
+ (esl_pcmcia_products[(n)].cis_info[1]) && \
+ (strcmp((card)->cis1_info[0], esl_pcmcia_products[(n)].cis_info[0]) \
+ == 0) && \
+ (strcmp((card)->cis1_info[1], esl_pcmcia_products[(n)].cis_info[1]) \
+ == 0) && \
+ ((fct) == esl_pcmcia_products[(n)].function) ? \
+ &esl_pcmcia_products[(n)] : NULL)
+
+int
+esl_pcmcia_match(parent, match, aux)
+ struct device *parent;
+ struct cfdata *match;
+ void *aux;
+{
+ struct pcmcia_attach_args *pa = aux;
+ int i;
+
+ for (i = 0; i < ESL_NDEVS; i++)
+ if (esl_pcmcia_product_lookup(pa->card, pa->pf->number, i))
+ return(2);
+
+ return (0);
+}
+
+void
+esl_pcmcia_attach(parent, self, aux)
+ struct device *parent, *self;
+ void *aux;
+{
+ struct esl_pcmcia_softc *esc = (void *)self;
+ struct pcmcia_attach_args *pa = aux;
+ struct pcmcia_config_entry *cfe;
+ struct pcmcia_function *pf = pa->pf;
+ const struct esl_pcmcia_product *pp;
+ int i;
+
+ esc->sc_pf = pf;
+
+ for (cfe = SIMPLEQ_FIRST(&pf->cfe_head); cfe != NULL;
+ cfe = SIMPLEQ_NEXT(cfe, cfe_list)) {
+ if (cfe->num_memspace != 0 ||
+ cfe->num_iospace != 1)
+ continue;
+
+ if (pcmcia_io_alloc(pa->pf, cfe->iospace[0].start,
+ cfe->iospace[0].length, 0, &esc->sc_pcioh) == 0)
+ break;
+ }
+
+ if (cfe == 0) {
+ printf(": can't alloc i/o space\n");
+ goto no_config_entry;
+ }
+
+ /* Setup power management hooks */
+ esc->sc_enable = esl_pcmcia_enable;
+ esc->sc_disable = esl_pcmcia_disable;
+
+ /* Enable the card. */
+ pcmcia_function_init(pf, cfe);
+ if (pcmcia_function_enable(pf)) {
+ printf(": function enable failed\n");
+ goto enable_failed;
+ }
+
+ /* Map in the I/O space */
+ if (pcmcia_io_map(pa->pf, PCMCIA_WIDTH_AUTO, 0, esc->sc_pcioh.size,
+ &esc->sc_pcioh, &esc->sc_io_window)) {
+ printf(": can't map i/o space\n");
+ goto iomap_failed;
+ }
+
+ pp = NULL;
+ for (i = 0; i < ESL_NDEVS; i++) {
+ pp = esl_pcmcia_product_lookup(pa->card, pa->pf->number, i);
+ if (pp != NULL)
+ break;
+ }
+
+ if (pp == NULL) {
+ printf("\n");
+ panic("esl_pcmcia_attach: impossible");
+ }
+
+ printf(": %s\n", pp->name);
+
+ if (esl_init(esc)) {
+ printf("esl_init: failed\n");
+ goto init_failed;
+ }
+
+ pcmcia_function_disable(esc->sc_pf);
+ return;
+
+init_failed:
+ /* Unmap I/O space */
+ pcmcia_io_unmap(esc->sc_pf, esc->sc_io_window);
+
+iomap_failed:
+ /* Disable the device. */
+ pcmcia_function_disable(esc->sc_pf);
+
+enable_failed:
+ /* Free our I/O space. */
+ pcmcia_io_free(esc->sc_pf, &esc->sc_pcioh);
+
+no_config_entry:
+ return;
+}
+
+int
+esl_pcmcia_detach(struct device *self, int flags)
+{
+ struct esl_pcmcia_softc *esc = (void *)self;
+ int rv = 0;
+
+ if (esc->sc_io_window == -1)
+ /* Nothing to detach */
+ return(0);
+
+ if (esc->sc_opldev != NULL)
+ config_detach(esc->sc_opldev, flags);
+ if (esc->sc_audiodev != NULL)
+ rv = config_detach(esc->sc_audiodev, flags);
+ if (rv)
+ return(rv);
+
+ /* unmap i/o window and i/o space */
+ pcmcia_io_unmap(esc->sc_pf, esc->sc_io_window);
+ pcmcia_io_free(esc->sc_pf, &esc->sc_pcioh);
+
+ return (rv);
+}
+
+int
+esl_pcmcia_enable(struct esl_pcmcia_softc *sc)
+{
+
+ /* Establish an interrupt */
+ sc->sc_ih = pcmcia_intr_establish(sc->sc_pf, IPL_AUDIO, esl_intr,
+ sc);
+ if (sc->sc_ih == NULL) {
+ printf("%s: couldn't establish interrupt\n",
+ sc->sc_esl.sc_dev.dv_xname);
+ goto fail_1;
+ }
+
+ if (pcmcia_function_enable(sc->sc_pf))
+ goto fail_2;
+
+ return(0);
+
+fail_2:
+ pcmcia_intr_disestablish(sc->sc_pf, sc->sc_ih);
+fail_1:
+ return(1);
+}
+
+void
+esl_pcmcia_disable(struct esl_pcmcia_softc *sc)
+{
+
+ pcmcia_function_disable(sc->sc_pf);
+ pcmcia_intr_disestablish(sc->sc_pf, sc->sc_ih);
+
+ return;
+}
diff --git a/sys/dev/pcmcia/eslreg.h b/sys/dev/pcmcia/eslreg.h
new file mode 100644
index 00000000000..7490d890fc0
--- /dev/null
+++ b/sys/dev/pcmcia/eslreg.h
@@ -0,0 +1,308 @@
+/* $NetBSD: eslreg.h,v 1.1 2001/09/29 14:00:57 augustss Exp $ */
+/*
+ * Copyright 1997
+ * Digital Equipment Corporation. All rights reserved.
+ *
+ * This software is furnished under license and may be used and
+ * copied only in accordance with the following terms and conditions.
+ * Subject to these conditions, you may download, copy, install,
+ * use, modify and distribute this software in source and/or binary
+ * form. No title or ownership is transferred hereby.
+ *
+ * 1) Any source code used, modified or distributed must reproduce
+ * and retain this copyright notice and list of conditions as
+ * they appear in the source file.
+ *
+ * 2) No right is granted to use any trade name, trademark, or logo of
+ * Digital Equipment Corporation. Neither the "Digital Equipment
+ * Corporation" name nor any trademark or logo of Digital Equipment
+ * Corporation may be used to endorse or promote products derived
+ * from this software without the prior written permission of
+ * Digital Equipment Corporation.
+ *
+ * 3) This software is provided "AS-IS" and any express or implied
+ * warranties, including but not limited to, any implied warranties
+ * of merchantability, fitness for a particular purpose, or
+ * non-infringement are disclaimed. In no event shall DIGITAL be
+ * liable for any damages whatsoever, and in particular, DIGITAL
+ * shall not be liable for special, indirect, consequential, or
+ * incidental damages or damages for lost profits, loss of
+ * revenue or loss of use, whether such damages arise in contract,
+ * negligence, tort, under statute, in equity, at law or otherwise,
+ * even if advised of the possibility of such damage.
+ */
+
+/*
+** @(#) $RCSfile: eslreg.h,v $ $Revision: 1.1 $ (SHARK) $Date: 2001/09/29 14:00:57 $
+**
+**++
+**
+** essreg.h
+**
+** FACILITY:
+**
+** DIGITAL Network Appliance Reference Design (DNARD)
+**
+** MODULE DESCRIPTION:
+**
+** This module contains the constant definitions for the device
+** registers on the ESS Technologies 1888/1887/888 sound chip.
+**
+** AUTHORS:
+**
+** Blair Fidler Software Engineering Australia
+** Gold Coast, Australia.
+**
+** CREATION DATE:
+**
+** March 10, 1997.
+**
+** MODIFICATION HISTORY:
+**
+**--
+*/
+
+/*
+ * DSP commands. This unit handles MIDI and audio capabilities.
+ * The DSP can be reset, data/commands can be read or written to it,
+ * and it can generate interrupts. Interrupts are generated for MIDI
+ * input or DMA completion. They seem to have neglected the fact
+ * that it would be nice to have a MIDI transmission complete interrupt.
+ * Worse, the DMA engine is half-duplex. This means you need to do
+ * (timed) programmed I/O to be able to record and play simulataneously.
+ */
+#define ESS_ACMD_DAC8WRITE 0x10 /* direct-mode 8-bit DAC write */
+#define ESS_ACMD_DAC16WRITE 0x11 /* direct-mode 16-bit DAC write */
+#define ESS_ACMD_DMA8OUT 0x14 /* 8-bit linear DMA output */
+#define ESS_ACMD_DMA16OUT 0x15 /* 16-bit linear DMA output */
+#define ESS_ACMD_AUTODMA8OUT 0x1C /* auto-init 8-bit linear DMA output */
+#define ESS_ACMD_AUTODMA16OUT 0x1D /* auto-init 16-bit linear DMA output */
+#define ESS_ACMD_ADC8READ 0x20 /* direct-mode 8-bit ADC read */
+#define ESS_ACMD_ADC16READ 0x21 /* direct-mode 16-bit ADC read */
+#define ESS_ACMD_DMA8IN 0x24 /* 8-bit linear DMA input */
+#define ESS_ACMD_DMA16IN 0x25 /* 16-bit linear DMA input */
+#define ESS_ACMD_AUTODMA8IN 0x2C /* auto-init 8-bit linear DMA input */
+#define ESS_ACMD_AUTODMA16IN 0x2D /* auto-init 16-bit linear DMA input */
+#define ESS_ACMD_SETTIMECONST1 0x40 /* set time constant (1MHz base) */
+#define ESS_ACMD_SETTIMECONST15 0x41 /* set time constant (1.5MHz base) */
+#define ESS_ACMD_SETFILTER 0x42 /* set filter clock independently */
+#define ESS_ACMD_BLOCKSIZE 0x48 /* set blk size for high speed xfer */
+
+#define ESS_ACMD_DMA4OUT 0x74 /* 4-bit ADPCM DMA output */
+#define ESS_ACMD_DMA4OUTREF 0x75 /* 4-bit ADPCM DMA output with ref */
+#define ESS_ACMD_DMA2_6OUT 0x76 /* 2.6-bit ADPCM DMA output */
+#define ESS_ACMD_DMA2_6OUTREF 0x77 /* 2.6-bit ADPCM DMA output with ref */
+#define ESS_ACMD_DMA2OUT 0x7A /* 2-bit ADPCM DMA output */
+#define ESS_ACMD_DMA2OUTREF 0x7B /* 2-bit ADPCM DMA output with ref */
+#define ESS_ACMD_SILENCEOUT 0x80 /* output a block of silence */
+#define ESS_ACMD_START_AUTO_OUT 0x90 /* start auto-init 8-bit DMA output */
+#define ESS_ACMD_START_OUT 0x91 /* start 8-bit DMA output */
+#define ESS_ACMD_START_AUTO_IN 0x98 /* start auto-init 8-bit DMA input */
+#define ESS_ACMD_START_IN 0x99 /* start 8-bit DMA input */
+
+#define ESS_XCMD_SAMPLE_RATE 0xA1 /* sample rate for Audio1 channel */
+#define ESS_XCMD_FILTER_CLOCK 0xA2 /* filter clock for Audio1 channel*/
+#define ESS_XCMD_XFER_COUNTLO 0xA4 /* */
+#define ESS_XCMD_XFER_COUNTHI 0xA5 /* */
+#define ESS_XCMD_AUDIO_CTRL 0xA8 /* */
+#define ESS_AUDIO_CTRL_MONITOR 0x08 /* 0=disable/1=enable */
+#define ESS_AUDIO_CTRL_MONO 0x02 /* 0=disable/1=enable */
+#define ESS_AUDIO_CTRL_STEREO 0x01 /* 0=disable/1=enable */
+#define ESS_XCMD_PREAMP_CTRL 0xA9 /* */
+#define ESS_PREAMP_CTRL_ENABLE 0x04
+
+#define ESS_XCMD_IRQ_CTRL 0xB1 /* legacy audio interrupt control */
+#define ESS_IRQ_CTRL_INTRA 0x00
+#define ESS_IRQ_CTRL_INTRB 0x04
+#define ESS_IRQ_CTRL_INTRC 0x08
+#define ESS_IRQ_CTRL_INTRD 0x0C
+#define ESS_IRQ_CTRL_MASK 0x10
+#define ESS_IRQ_CTRL_EXT 0x40
+#define ESS_XCMD_DRQ_CTRL 0xB2 /* audio DRQ control */
+#define ESS_DRQ_CTRL_DRQA 0x04
+#define ESS_DRQ_CTRL_DRQB 0x08
+#define ESS_DRQ_CTRL_DRQC 0x0C
+#define ESS_DRQ_CTRL_PU 0x10
+#define ESS_DRQ_CTRL_EXT 0x40
+#define ESS_XCMD_VOLIN_CTRL 0xB4 /* stereo input volume control */
+#define ESS_1788_XCMD_AUDIO_CTRL0 0xB6
+#define ESS_CTRL0_SIGNED 0x00
+#define ESS_CTRL0_UNSIGNED 0x80
+#define ESS_XCMD_AUDIO1_CTRL1 0xB7 /* */
+#define ESS_AUDIO1_CTRL1_FIFO_CONNECT 0x80 /* 1=connected */
+#define ESS_AUDIO1_CTRL1_FIFO_MONO 0x40 /* 0=stereo/1=mono */
+#define ESS_AUDIO1_CTRL1_FIFO_SIGNED 0x20 /* 0=unsigned/1=signed */
+#define ESS_AUDIO1_CTRL1_FIFO_STEREO 0x08 /* 0=mono/1=stereo */
+#define ESS_AUDIO1_CTRL1_FIFO_SIZE 0x04 /* 0=8-bit/1=16-bit */
+#define ESS_XCMD_AUDIO1_CTRL2 0xB8 /* */
+#define ESS_AUDIO1_CTRL2_FIFO_ENABLE 0x01 /* 0=disable/1=enable */
+#define ESS_AUDIO1_CTRL2_DMA_READ 0x02 /* 0=DMA write/1=DMA read */
+#define ESS_AUDIO1_CTRL2_AUTO_INIT 0x04
+#define ESS_AUDIO1_CTRL2_ADC_ENABLE 0x08 /* 0=DAC mode/1=ADC mode */
+#define ESS_XCMD_DEMAND_CTRL 0xB9 /* */
+#define ESS_DEMAND_CTRL_SINGLE 0x00 /* 1-byte transfers */
+#define ESS_DEMAND_CTRL_DEMAND_2 0x01 /* 2-byte transfers */
+#define ESS_DEMAND_CTRL_DEMAND_4 0x02 /* 4-byte transfers */
+
+#define ESS_ACMD_ENABLE_EXT 0xC6 /* enable ESS extension commands */
+#define ESS_ACMD_DISABLE_EXT 0xC7 /* enable ESS extension commands */
+
+#define ESS_ACMD_PAUSE_DMA 0xD0 /* pause DMA */
+#define ESS_ACMD_ENABLE_SPKR 0xD1 /* enable Audio1 DAC input to mixer */
+#define ESS_ACMD_DISABLE_SPKR 0xD3 /* disable Audio1 DAC input to mixer */
+#define ESS_ACMD_CONT_DMA 0xD4 /* continue paused DMA */
+#define ESS_ACMD_SPKR_STATUS 0xD8 /* return Audio1 DAC status: */
+#define ESS_SPKR_OFF 0x00
+#define ESS_SPKR_ON 0xFF
+#define ESS_ACMD_VERSION 0xE1 /* get version number */
+#define ESS_ACMD_LEGACY_ID 0xE7 /* get legacy ES688/ES1688 ID bytes */
+
+#define ESS_MINRATE 4000
+#define ESS_MAXRATE 44100
+
+/*
+ * Macros to detect valid hardware configuration data.
+ */
+#define ESS_BASE_VALID(base) ((base) == 0x220 || (base) == 0x230 || (base) == 0x240 || (base) == 0x250)
+
+#define ESS_IRQ1_VALID(irq) ((irq) == 5 || (irq) == 7 || (irq) == 9 || (irq) == 10)
+
+#define ESS_IRQ2_VALID(irq) ((irq) == 15)
+
+#define ESS_IRQ12_VALID(irq) ((irq) == 5 || (irq) == 7 || (irq) == 9 || (irq) == 10 || (irq) == 15)
+
+#define ESS_DRQ1_VALID(chan) ((chan) == 0 || (chan) == 1 || (chan) == 3)
+
+#define ESS_DRQ2_VALID(chan) ((chan) == 0 || (chan) == 1 || (chan) == 3 || (chan) == 5)
+
+#define ESS_USE_AUDIO1(model) (((model) == ESS_1788) || ((model) == ESS_1868) || ((model) == ESS_1878) || ((model) == ESS_1869) || ((model) == ESS_1879))
+
+/*
+ * Macros to manipulate gain values
+ */
+#define ESS_4BIT_GAIN(x) ((x) & 0xf0)
+#define ESS_3BIT_GAIN(x) (((x) & 0xe0) >> 1)
+#define ESS_STEREO_GAIN(l, r) ((l) | ((r) >> 4))
+#define ESS_MONO_GAIN(x) ((x) >> 4)
+
+#ifdef ESS_AMODE_LOW
+/*
+ * Registers used to configure ESS chip via Read Key Sequence
+ */
+#define ESS_CONFIG_KEY_BASE 0x229
+#define ESS_CONFIG_KEY_PORTS 3
+#else
+/*
+ * Registers used to configure ESS chip via System Control Register (SCR)
+ */
+#define ESS_SCR_ACCESS_BASE 0xF9
+#define ESS_SCR_ACCESS_PORTS 3
+#define ESS_SCR_LOCK 0
+#define ESS_SCR_UNLOCK 2
+
+#define ESS_SCR_BASE 0xE0
+#define ESS_SCR_PORTS 2
+#define ESS_SCR_INDEX 0
+#define ESS_SCR_DATA 1
+
+/*
+ * Bit definitions for SCR
+ */
+#define ESS_SCR_AUDIO_ENABLE 0x04
+#define ESS_SCR_AUDIO_220 0x00
+#define ESS_SCR_AUDIO_230 0x01
+#define ESS_SCR_AUDIO_240 0x02
+#define ESS_SCR_AUDIO_250 0x03
+#endif
+
+/*****************************************************************************/
+/* DSP Timeout Definitions */
+/*****************************************************************************/
+#define ESS_READ_TIMEOUT 5000 /* number of times to try a read, 5ms*/
+#define ESS_WRITE_TIMEOUT 5000 /* number of times to try a write, 5ms */
+
+
+#define ESS_NPORT 16
+#define ESS_DSP_RESET 0x06
+#define ESS_RESET_EXT 0x03 /* reset and use second DMA */
+#define ESS_MAGIC 0xAA /* response to successful reset */
+
+#define ESS_DSP_READ 0x0A
+#define ESS_DSP_WRITE 0x0C
+
+#define ESS_CLEAR_INTR 0x0E
+
+#define ESS_FIFO_WRITE 0x0F
+
+#define ESS_DSP_RW_STATUS 0x0C
+#define ESS_DSP_WRITE_BUSY 0x80
+#define ESS_DSP_READ_READY 0x40
+#define ESS_DSP_READ_FULL 0x20 /* FIFO full */
+#define ESS_DSP_READ_EMPTY 0x10 /* FIFO empty */
+#define ESS_DSP_READ_HALF 0x08 /* FIFO half-empty */
+#define ESS_DSP_READ_IRQ 0x04 /* IRQ generated */
+#define ESS_DSP_READ_HALF_IRQ 0x02 /* " from half-empty flag change */
+#define ESS_DSP_READ_OFLOW 0x01 /* " from DMA counter overflow */
+#define ESS_DSP_READ_ANYIRQ (ESS_DSP_READ_IRQ | \
+ ESS_DSP_READ_HALF_IRQ | \
+ ESS_DSP_READ_OFLOW)
+
+#define ESS_MIX_REG_SELECT 0x04
+#define ESS_MIX_REG_DATA 0x05
+#define ESS_MIX_RESET 0x00 /* mixer reset port and value */
+
+
+/*
+ * ESS Mixer registers
+ */
+#define ESS_MREG_VOLUME_VOICE 0x14
+#define ESS_MREG_VOLUME_MIC 0x1A
+#define ESS_MREG_ADC_SOURCE 0x1C
+#define ESS_SOURCE_MIC 0x00
+#define ESS_SOURCE_CD 0x02
+#define ESS_SOURCE_LINE 0x06
+#define ESS_SOURCE_MIXER 0x07
+#define ESS_MREG_VOLUME_MASTER 0x32
+#define ESS_MREG_VOLUME_SYNTH 0x36
+#define ESS_MREG_VOLUME_CD 0x38
+#define ESS_MREG_VOLUME_AUXB 0x3A
+#define ESS_MREG_VOLUME_PCSPKR 0x3C
+#define ESS_MREG_VOLUME_LINE 0x3E
+#define ESS_MREG_VOLUME_LEFT 0x60
+#define ESS_MREG_VOLUME_RIGHT 0x62
+#define ESS_VOLUME_MUTE 0x40
+#define ESS_MREG_VOLUME_CTRL 0x64
+#define ESS_MREG_SAMPLE_RATE 0x70 /* sample rate for Audio2 channel */
+#define ESS_MREG_FILTER_CLOCK 0x72 /* filter clock for Audio2 channel */
+#define ESS_MREG_XFER_COUNTLO 0x74 /* low-byte of DMA transfer size */
+#define ESS_MREG_XFER_COUNTHI 0x76 /* high-byte of DMA transfer size */
+#define ESS_MREG_AUDIO2_CTRL1 0x78 /* control register 1 for Audio2: */
+#define ESS_AUDIO2_CTRL1_SINGLE 0x00
+#define ESS_AUDIO2_CTRL1_DEMAND_2 0x40
+#define ESS_AUDIO2_CTRL1_DEMAND_4 0x80
+#define ESS_AUDIO2_CTRL1_DEMAND_8 0xC0
+#define ESS_AUDIO2_CTRL1_XFER_SIZE 0x20 /* 0=8-bit/1=16-bit */
+#define ESS_AUDIO2_CTRL1_AUTO_INIT 0x10
+#define ESS_AUDIO2_CTRL1_FIFO_ENABLE 0x02 /* 0=disable/1=enable */
+#define ESS_AUDIO2_CTRL1_DAC_ENABLE 0x01 /* 0=disable/1=enable */
+#define ESS_MREG_AUDIO2_CTRL2 0x7A /* control register 2 for Audio2: */
+#define ESS_AUDIO2_CTRL2_FIFO_SIZE 0x01 /* 0=8-bit/1=16-bit */
+#define ESS_AUDIO2_CTRL2_CHANNELS 0x02 /* 0=mono/1=stereo */
+#define ESS_AUDIO2_CTRL2_FIFO_SIGNED 0x04 /* 0=unsigned/1=signed */
+#define ESS_AUDIO2_CTRL2_DMA_ENABLE 0x20 /* 0=disable/1=enable */
+#define ESS_AUDIO2_CTRL2_IRQ2_ENABLE 0x40
+#define ESS_AUDIO2_CTRL2_IRQ_LATCH 0x80
+#define ESS_MREG_AUDIO2_CTRL3 0x7D
+#define ESS_AUDIO2_CTRL3_DRQA 0x00
+#define ESS_AUDIO2_CTRL3_DRQB 0x01
+#define ESS_AUDIO2_CTRL3_DRQC 0x02
+#define ESS_AUDIO2_CTRL3_DRQD 0x03
+#define ESS_AUDIO2_CTRL3_DRQ_PD 0x04
+#define ESS_MREG_INTR_ST 0x7F
+#define ESS_IS_SELECT_IRQ 0x01
+#define ESS_IS_ES1888 0x00
+#define ESS_IS_INTRA 0x02
+#define ESS_IS_INTRB 0x04
+#define ESS_IS_INTRC 0x06
+#define ESS_IS_INTRD 0x08
+#define ESS_IS_INTRE 0x0A
diff --git a/sys/dev/pcmcia/eslvar.h b/sys/dev/pcmcia/eslvar.h
new file mode 100644
index 00000000000..91e80d213cc
--- /dev/null
+++ b/sys/dev/pcmcia/eslvar.h
@@ -0,0 +1,92 @@
+/* $NetBSD: eslvar.h,v 1.1 2001/09/29 14:00:57 augustss Exp $ */
+
+/*
+ * Copyright (c) 2001 Jared D. McNeill <jmcneill@invisible.yi.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Jared D. McNeill.
+ * 4. Neither the name of the author nor the names of any contributors may
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/device.h>
+
+#define ESS_DAC_PLAY_VOL 0
+#define ESS_SYNTH_PLAY_VOL 1
+#define ESS_INPUT_CLASS 2
+
+#define ESS_MASTER_VOL 3
+#define ESS_OUTPUT_CLASS 4
+
+#define ESS_MAX_NDEVS 5
+
+/* Model numbers */
+#define ESS_UNSUPPORTED -1
+#define ESS_1688 0
+#define ESS_688 1
+
+/* FIFO Size */
+#define ESS_FIFO_SIZE 256 /* in bytes */
+
+struct esl_softc
+{
+ struct device sc_dev; /* base device */
+ u_short sc_open; /* reference count of open calls */
+ u_char gain[ESS_MAX_NDEVS][2]; /* kept in input levels */
+#define ESS_LEFT 0
+#define ESS_RIGHT 1
+
+ u_long nintr; /* number of interrupts */
+ void (*intr)__P((void *)); /* ISR for FIFO Half-Empty */
+ void *arg; /* argument for intr() */
+
+ /* Pseudo-DMA state vars */
+ int active; /* are we active? */
+ u_char *sc_dmaaddr;
+ u_char *sc_dmastart;
+ u_char *sc_dmaend;
+ int sc_blksize;
+ int sc_blkpos;
+
+ u_int sc_version; /* Legacy ES688/ES1688 ID */
+};
+
+struct esl_pcmcia_softc {
+ struct esl_softc sc_esl;
+ struct pcmcia_io_handle sc_pcioh;
+ int sc_io_window;
+ struct pcmcia_function *sc_pf;
+ void *sc_ih;
+ struct device *sc_audiodev;
+ struct device *sc_opldev;
+
+ int (*sc_enable) __P((struct esl_pcmcia_softc *));
+ void (*sc_disable) __P((struct esl_pcmcia_softc *));
+};
+
+int esl_init __P((struct esl_pcmcia_softc *sc));
+int esl_intr __P((void *));
diff --git a/sys/dev/pcmcia/opl_esl.c b/sys/dev/pcmcia/opl_esl.c
new file mode 100644
index 00000000000..38eed7a3ffd
--- /dev/null
+++ b/sys/dev/pcmcia/opl_esl.c
@@ -0,0 +1,107 @@
+/* $NetBSD: opl_esl.c,v 1.1 2001/09/29 14:00:57 augustss Exp $ */
+
+/*
+ * Copyright (c) 2001 Jared D. McNeill <jmcneill@invisible.yi.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Jared D. McNeill.
+ * 4. Neither the name of the author nor the names of any contributors may
+ f
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/errno.h>
+#include <sys/device.h>
+#include <sys/malloc.h>
+#include <sys/proc.h>
+#include <sys/conf.h>
+#include <sys/select.h>
+#include <sys/audioio.h>
+#include <sys/midiio.h>
+
+#include <machine/bus.h>
+
+#include <dev/audio_if.h>
+#include <dev/midi_if.h>
+#include <dev/ic/oplreg.h>
+#include <dev/ic/oplvar.h>
+
+#include <dev/pcmcia/pcmciavar.h>
+#include <dev/pcmcia/eslvar.h>
+
+int opl_esl_match __P((struct device *, struct cfdata *, void *));
+void opl_esl_attach __P((struct device *, struct device *, void *));
+int opl_esl_detach __P((struct device *, int));
+
+struct cfattach opl_esl_ca = {
+ sizeof(struct opl_softc), opl_esl_match, opl_esl_attach,
+ opl_esl_detach
+};
+
+int
+opl_esl_match(struct device *parent, struct cfdata *match, void *aux)
+{
+ struct audio_attach_args *aa = (struct audio_attach_args *)aux;
+ struct esl_pcmcia_softc *ssc = (struct esl_pcmcia_softc *)parent;
+ struct opl_softc sc;
+
+ if (aa->type != AUDIODEV_TYPE_OPL)
+ return(0);
+ memset(&sc, 0, sizeof(sc));
+ sc.iot = ssc->sc_pcioh.iot;
+ sc.ioh = ssc->sc_pcioh.ioh;
+
+ return(opl_find(&sc));
+}
+
+void
+opl_esl_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct esl_pcmcia_softc *ssc = (struct esl_pcmcia_softc *)parent;
+ struct opl_softc *sc = (struct opl_softc *)self;
+
+ sc->iot = ssc->sc_pcioh.iot;
+ sc->ioh = ssc->sc_pcioh.ioh;
+ sc->offs = 0;
+ strcpy(sc->syn.name, "ESS AudioDrive ");
+
+ opl_attach(sc);
+}
+
+int
+opl_esl_detach(struct device *self, int flags)
+{
+ struct opl_softc *sc = (struct opl_softc *)self;
+ int rv;
+
+ rv = opl_detach(sc, flags);
+
+ return(rv);
+}