diff options
| author | augustss <augustss@NetBSD.org> | 2001-09-29 14:00:57 +0000 |
|---|---|---|
| committer | augustss <augustss@NetBSD.org> | 2001-09-29 14:00:57 +0000 |
| commit | c01875042370160ecdc5e48ab8ae24f3d615307d (patch) | |
| tree | 52efe81a3d4640b9cb188120457dde8825cecd51 /sys/dev | |
| parent | 58a0f075d2f05746ae360824c6ccb228c6568a78 (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.c | 990 | ||||
| -rw-r--r-- | sys/dev/pcmcia/esl_pcmcia.c | 252 | ||||
| -rw-r--r-- | sys/dev/pcmcia/eslreg.h | 308 | ||||
| -rw-r--r-- | sys/dev/pcmcia/eslvar.h | 92 | ||||
| -rw-r--r-- | sys/dev/pcmcia/opl_esl.c | 107 |
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); +} |
