diff options
author | Niklas Hallqvist <niklas@cvs.openbsd.org> | 1999-06-22 16:20:05 +0000 |
---|---|---|
committer | Niklas Hallqvist <niklas@cvs.openbsd.org> | 1999-06-22 16:20:05 +0000 |
commit | a601110ffc694a533e5b4f168b2e10cf9d3ddbf0 (patch) | |
tree | 1f48ce560ee0fb7857b6873f482a6c47d39f1926 | |
parent | 528d286197b15f29a45f19428713e168139ab154 (diff) |
ESS audio driver ported from NetBSD, some mixer problems with X-based mixers
otherwise OK (mixerctl works quite OK, very strange).
At the moment it is polled only, but it works quite OK that way too.
-rw-r--r-- | sys/dev/isa/ess.c | 2642 | ||||
-rw-r--r-- | sys/dev/isa/ess_isapnp.c | 105 | ||||
-rw-r--r-- | sys/dev/isa/essreg.h | 306 | ||||
-rw-r--r-- | sys/dev/isa/essvar.h | 159 | ||||
-rw-r--r-- | sys/dev/isa/files.isa | 9 | ||||
-rw-r--r-- | sys/dev/isa/files.isapnp | 5 | ||||
-rw-r--r-- | sys/dev/isa/opl_ess.c | 105 | ||||
-rw-r--r-- | sys/dev/isa/pnpdevs | 8 |
8 files changed, 3333 insertions, 6 deletions
diff --git a/sys/dev/isa/ess.c b/sys/dev/isa/ess.c new file mode 100644 index 00000000000..f623751350d --- /dev/null +++ b/sys/dev/isa/ess.c @@ -0,0 +1,2642 @@ +/* $OpenBSD: ess.c,v 1.1 1999/06/22 16:20:03 niklas Exp $ */ +/* $NetBSD: ess.c,v 1.44 1999/03/19 12:40:21 mycroft 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. + */ + +/* +**++ +** +** ess.c +** +** FACILITY: +** +** DIGITAL Network Appliance Reference Design (DNARD) +** +** MODULE DESCRIPTION: +** +** This module contains the device driver for the ESS +** Technologies 1888/1887/888 sound chip. The code in sbdsp.c was +** used as a reference point when implementing this driver. +** +** AUTHORS: +** +** Blair Fidler Software Engineering Australia +** Gold Coast, Australia. +** +** CREATION DATE: +** +** March 10, 1997. +** +** MODIFICATION HISTORY: +** +** Heavily modified by Lennart Augustsson and Charles M. Hannum for +** bus_dma, changes to audio interface, and many bug fixes. +** ESS1788 support by Nathan J. Williams and Charles M. Hannum. +**-- +*/ + +#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 <machine/cpu.h> +#include <machine/intr.h> +#include <machine/bus.h> + +#include <sys/audioio.h> +#include <dev/audio_if.h> +#include <dev/auconv.h> +#include <dev/mulaw.h> + +#include <dev/isa/isavar.h> +#include <dev/isa/isadmavar.h> + +#include <dev/isa/essvar.h> +#include <dev/isa/essreg.h> + +#ifdef AUDIO_DEBUG +#define DPRINTF(x) if (essdebug) printf x +#define DPRINTFN(n,x) if (essdebug>(n)) printf x +int essdebug = 0; +#else +#define DPRINTF(x) +#define DPRINTFN(n,x) +#endif + +#if 0 +unsigned uuu; +#define EREAD1(t, h, a) (uuu=bus_space_read_1(t, h, a),printf("EREAD %02x=%02x\n", ((int)h&0xfff)+a, uuu),uuu) +#define EWRITE1(t, h, a, d) (printf("EWRITE %02x=%02x\n", ((int)h & 0xfff)+a, d), bus_space_write_1(t, h, a, d)) +#else +#define EREAD1(t, h, a) bus_space_read_1(t, h, a) +#define EWRITE1(t, h, a, d) bus_space_write_1(t, h, a, d) +#endif + +struct cfdriver ess_cd = { + NULL, "ess", DV_DULL +}; + +int ess_setup_sc __P((struct ess_softc *, int)); + +int ess_open __P((void *, int)); +void ess_1788_close __P((void *)); +void ess_1888_close __P((void *)); +int ess_getdev __P((void *, struct audio_device *)); +int ess_drain __P((void *)); + +int ess_query_encoding __P((void *, struct audio_encoding *)); + +int ess_set_params __P((void *, int, int, struct audio_params *, + struct audio_params *)); + +int ess_round_blocksize __P((void *, int)); + +int ess_audio1_trigger_output __P((void *, void *, void *, int, + void (*)(void *), void *, struct audio_params *)); +int ess_audio2_trigger_output __P((void *, void *, void *, int, + void (*)(void *), void *, struct audio_params *)); +int ess_audio1_trigger_input __P((void *, void *, void *, int, + void (*)(void *), void *, struct audio_params *)); +int ess_audio1_halt __P((void *)); +int ess_audio2_halt __P((void *)); +int ess_audio1_intr __P((void *)); +int ess_audio2_intr __P((void *)); +void ess_audio1_poll __P((void *)); +void ess_audio2_poll __P((void *)); + +int ess_speaker_ctl __P((void *, int)); + +int ess_getdev __P((void *, struct audio_device *)); + +int ess_set_port __P((void *, mixer_ctrl_t *)); +int ess_get_port __P((void *, mixer_ctrl_t *)); + +void *ess_malloc __P((void *, unsigned long, int, int)); +void ess_free __P((void *, void *, int)); +unsigned long ess_round_buffersize __P((void *, unsigned long)); +int ess_mappage __P((void *, void *, int, int)); + + +int ess_query_devinfo __P((void *, mixer_devinfo_t *)); +int ess_1788_get_props __P((void *)); +int ess_1888_get_props __P((void *)); + +void ess_speaker_on __P((struct ess_softc *)); +void ess_speaker_off __P((struct ess_softc *)); + +int ess_config_addr __P((struct ess_softc *)); +void ess_config_irq __P((struct ess_softc *)); +void ess_config_drq __P((struct ess_softc *)); +void ess_setup __P((struct ess_softc *)); +int ess_identify __P((struct ess_softc *)); + +int ess_reset __P((struct ess_softc *)); +void ess_set_gain __P((struct ess_softc *, int, int)); +int ess_set_in_port __P((struct ess_softc *, int)); +int ess_set_in_ports __P((struct ess_softc *, int)); +u_int ess_srtotc __P((u_int)); +u_int ess_srtofc __P((u_int)); +u_char ess_get_dsp_status __P((struct ess_softc *)); +u_char ess_dsp_read_ready __P((struct ess_softc *)); +u_char ess_dsp_write_ready __P((struct ess_softc *sc)); +int ess_rdsp __P((struct ess_softc *)); +int ess_wdsp __P((struct ess_softc *, u_char)); +u_char ess_read_x_reg __P((struct ess_softc *, u_char)); +int ess_write_x_reg __P((struct ess_softc *, u_char, u_char)); +void ess_clear_xreg_bits __P((struct ess_softc *, u_char, u_char)); +void ess_set_xreg_bits __P((struct ess_softc *, u_char, u_char)); +u_char ess_read_mix_reg __P((struct ess_softc *, u_char)); +void ess_write_mix_reg __P((struct ess_softc *, u_char, u_char)); +void ess_clear_mreg_bits __P((struct ess_softc *, u_char, u_char)); +void ess_set_mreg_bits __P((struct ess_softc *, u_char, u_char)); + +static char *essmodel[] = { + "unsupported", + "1888", + "1887", + "888", + "1788" +}; + +struct audio_device ess_device = { + "ESS Technology", + "x", + "ess" +}; + +/* + * Define our interface to the higher level audio driver. + */ + +struct audio_hw_if ess_1788_hw_if = { + ess_open, + ess_1788_close, + ess_drain, + ess_query_encoding, + ess_set_params, + ess_round_blocksize, + NULL, + NULL, + NULL, + NULL, + NULL, + ess_audio1_halt, + ess_audio1_halt, + ess_speaker_ctl, + ess_getdev, + NULL, + ess_set_port, + ess_get_port, + ess_query_devinfo, + ess_malloc, + ess_free, + ess_round_buffersize, + ess_mappage, + ess_1788_get_props, + ess_audio1_trigger_output, + ess_audio1_trigger_input, +}; + +struct audio_hw_if ess_1888_hw_if = { + ess_open, + ess_1888_close, + ess_drain, + ess_query_encoding, + ess_set_params, + ess_round_blocksize, + NULL, + NULL, + NULL, + NULL, + NULL, + ess_audio2_halt, + ess_audio1_halt, + ess_speaker_ctl, + ess_getdev, + NULL, + ess_set_port, + ess_get_port, + ess_query_devinfo, + ess_malloc, + ess_free, + ess_round_buffersize, + ess_mappage, + ess_1888_get_props, + ess_audio2_trigger_output, + ess_audio1_trigger_input, +}; + +#ifdef AUDIO_DEBUG +void ess_printsc __P((struct ess_softc *)); +void ess_dump_mixer __P((struct ess_softc *)); + +void +ess_printsc(sc) + struct ess_softc *sc; +{ + int i; + + printf("open %d iobase 0x%x outport %u inport %u speaker %s\n", + (int)sc->sc_open, sc->sc_iobase, sc->out_port, + sc->in_port, sc->spkr_state ? "on" : "off"); + + printf("audio1: dmachan %d irq %d nintr %lu intr %p arg %p\n", + sc->sc_audio1.drq, sc->sc_audio1.irq, sc->sc_audio1.nintr, + sc->sc_audio1.intr, sc->sc_audio1.arg); + + if (sc->sc_model != ESS_1788) { + printf("audio2: dmachan %d irq %d nintr %lu intr %p arg %p\n", + sc->sc_audio2.drq, sc->sc_audio2.irq, sc->sc_audio2.nintr, + sc->sc_audio2.intr, sc->sc_audio2.arg); + } + + printf("gain:"); + for (i = 0; i < sc->ndevs; i++) + printf(" %u,%u", sc->gain[i][ESS_LEFT], sc->gain[i][ESS_RIGHT]); + printf("\n"); +} + +void +ess_dump_mixer(sc) + struct ess_softc *sc; +{ + printf("ESS_DAC_PLAY_VOL: mix reg 0x%02x=0x%02x\n", + 0x7C, ess_read_mix_reg(sc, 0x7C)); + printf("ESS_MIC_PLAY_VOL: mix reg 0x%02x=0x%02x\n", + 0x1A, ess_read_mix_reg(sc, 0x1A)); + printf("ESS_LINE_PLAY_VOL: mix reg 0x%02x=0x%02x\n", + 0x3E, ess_read_mix_reg(sc, 0x3E)); + printf("ESS_SYNTH_PLAY_VOL: mix reg 0x%02x=0x%02x\n", + 0x36, ess_read_mix_reg(sc, 0x36)); + printf("ESS_CD_PLAY_VOL: mix reg 0x%02x=0x%02x\n", + 0x38, ess_read_mix_reg(sc, 0x38)); + printf("ESS_AUXB_PLAY_VOL: mix reg 0x%02x=0x%02x\n", + 0x3A, ess_read_mix_reg(sc, 0x3A)); + printf("ESS_MASTER_VOL: mix reg 0x%02x=0x%02x\n", + 0x32, ess_read_mix_reg(sc, 0x32)); + printf("ESS_PCSPEAKER_VOL: mix reg 0x%02x=0x%02x\n", + 0x3C, ess_read_mix_reg(sc, 0x3C)); + printf("ESS_DAC_REC_VOL: mix reg 0x%02x=0x%02x\n", + 0x69, ess_read_mix_reg(sc, 0x69)); + printf("ESS_MIC_REC_VOL: mix reg 0x%02x=0x%02x\n", + 0x68, ess_read_mix_reg(sc, 0x68)); + printf("ESS_LINE_REC_VOL: mix reg 0x%02x=0x%02x\n", + 0x6E, ess_read_mix_reg(sc, 0x6E)); + printf("ESS_SYNTH_REC_VOL: mix reg 0x%02x=0x%02x\n", + 0x6B, ess_read_mix_reg(sc, 0x6B)); + printf("ESS_CD_REC_VOL: mix reg 0x%02x=0x%02x\n", + 0x6A, ess_read_mix_reg(sc, 0x6A)); + printf("ESS_AUXB_REC_VOL: mix reg 0x%02x=0x%02x\n", + 0x6C, ess_read_mix_reg(sc, 0x6C)); + printf("ESS_RECORD_VOL: x reg 0x%02x=0x%02x\n", + 0xB4, ess_read_x_reg(sc, 0xB4)); + printf("Audio 1 play vol (unused): mix reg 0x%02x=0x%02x\n", + 0x14, ess_read_mix_reg(sc, 0x14)); + + printf("ESS_MIC_PREAMP: x reg 0x%02x=0x%02x\n", + ESS_XCMD_PREAMP_CTRL, ess_read_x_reg(sc, ESS_XCMD_PREAMP_CTRL)); + printf("ESS_RECORD_MONITOR: x reg 0x%02x=0x%02x\n", + ESS_XCMD_AUDIO_CTRL, ess_read_x_reg(sc, ESS_XCMD_AUDIO_CTRL)); + printf("Record source: mix reg 0x%02x=0x%02x, 0x%02x=0x%02x\n", + ESS_MREG_ADC_SOURCE, ess_read_mix_reg(sc, ESS_MREG_ADC_SOURCE), + ESS_MREG_AUDIO2_CTRL2, ess_read_mix_reg(sc, ESS_MREG_AUDIO2_CTRL2)); +} + +#endif + +/* + * Configure the ESS chip for the desired audio base address. + */ +int +ess_config_addr(sc) + struct ess_softc *sc; +{ + int iobase = sc->sc_iobase; + bus_space_tag_t iot = sc->sc_iot; + + /* + * Configure using the System Control Register method. This + * method is used when the AMODE line is tied high, which is + * the case for the Shark, but not for the evaluation board. + */ + + bus_space_handle_t scr_access_ioh; + bus_space_handle_t scr_ioh; + u_short scr_value; + + /* + * Set the SCR bit to enable audio. + */ + scr_value = ESS_SCR_AUDIO_ENABLE; + + /* + * Set the SCR bits necessary to select the specified audio + * base address. + */ + switch(iobase) { + case 0x220: + scr_value |= ESS_SCR_AUDIO_220; + break; + case 0x230: + scr_value |= ESS_SCR_AUDIO_230; + break; + case 0x240: + scr_value |= ESS_SCR_AUDIO_240; + break; + case 0x250: + scr_value |= ESS_SCR_AUDIO_250; + break; + default: + printf("ess: configured iobase 0x%x invalid\n", iobase); + return (1); + break; + } + + /* + * Get a mapping for the System Control Register (SCR) access + * registers and the SCR data registers. + */ + if (bus_space_map(iot, ESS_SCR_ACCESS_BASE, ESS_SCR_ACCESS_PORTS, + 0, &scr_access_ioh)) { + printf("ess: can't map SCR access registers\n"); + return (1); + } + if (bus_space_map(iot, ESS_SCR_BASE, ESS_SCR_PORTS, + 0, &scr_ioh)) { + printf("ess: can't map SCR registers\n"); + bus_space_unmap(iot, scr_access_ioh, ESS_SCR_ACCESS_PORTS); + return (1); + } + + /* Unlock the SCR. */ + EWRITE1(iot, scr_access_ioh, ESS_SCR_UNLOCK, 0); + + /* Write the base address information into SCR[0]. */ + EWRITE1(iot, scr_ioh, ESS_SCR_INDEX, 0); + EWRITE1(iot, scr_ioh, ESS_SCR_DATA, scr_value); + + /* Lock the SCR. */ + EWRITE1(iot, scr_access_ioh, ESS_SCR_LOCK, 0); + + /* Unmap the SCR access ports and the SCR data ports. */ + bus_space_unmap(iot, scr_access_ioh, ESS_SCR_ACCESS_PORTS); + bus_space_unmap(iot, scr_ioh, ESS_SCR_PORTS); + + return 0; +} + + +/* + * Configure the ESS chip for the desired IRQ and DMA channels. + * ESS ISA + * -------- + * IRQA irq9 + * IRQB irq5 + * IRQC irq7 + * IRQD irq10 + * IRQE irq15 + * + * DRQA drq0 + * DRQB drq1 + * DRQC drq3 + * DRQD drq5 + */ +void +ess_config_irq(sc) + struct ess_softc *sc; +{ + int v; + + DPRINTFN(2,("ess_config_irq\n")); + + if (sc->sc_model == ESS_1887 && + sc->sc_audio1.irq == sc->sc_audio2.irq && + sc->sc_audio1.irq != -1) { + /* Use new method, both interrupts are the same. */ + v = ESS_IS_SELECT_IRQ; /* enable intrs */ + switch (sc->sc_audio1.irq) { + case 5: + v |= ESS_IS_INTRB; + break; + case 7: + v |= ESS_IS_INTRC; + break; + case 9: + v |= ESS_IS_INTRA; + break; + case 10: + v |= ESS_IS_INTRD; + break; + case 15: + v |= ESS_IS_INTRE; + break; +#ifdef DIAGNOSTIC + default: + printf("ess_config_irq: configured irq %d not supported for Audio 1\n", + sc->sc_audio1.irq); + return; +#endif + } + /* Set the IRQ */ + ess_write_mix_reg(sc, ESS_MREG_INTR_ST, v); + return; + } + + if (sc->sc_model == ESS_1887) { + /* Tell the 1887 to use the old interrupt method. */ + ess_write_mix_reg(sc, ESS_MREG_INTR_ST, ESS_IS_ES1888); + } + + if (sc->sc_audio1.polled) { + /* Turn off Audio1 interrupts. */ + v = 0; + } else { + /* Configure Audio 1 for the appropriate IRQ line. */ + v = ESS_IRQ_CTRL_MASK | ESS_IRQ_CTRL_EXT; /* All intrs on */ + switch (sc->sc_audio1.irq) { + case 5: + v |= ESS_IRQ_CTRL_INTRB; + break; + case 7: + v |= ESS_IRQ_CTRL_INTRC; + break; + case 9: + v |= ESS_IRQ_CTRL_INTRA; + break; + case 10: + v |= ESS_IRQ_CTRL_INTRD; + break; +#ifdef DIAGNOSTIC + default: + printf("ess: configured irq %d not supported for Audio 1\n", + sc->sc_audio1.irq); + return; +#endif + } + } + ess_write_x_reg(sc, ESS_XCMD_IRQ_CTRL, v); + + if (sc->sc_model == ESS_1788) + return; + + if (sc->sc_audio2.polled) { + /* Turn off Audio2 interrupts. */ + ess_clear_mreg_bits(sc, ESS_MREG_AUDIO2_CTRL2, + ESS_AUDIO2_CTRL2_IRQ2_ENABLE); + } else { + /* Audio2 is hardwired to INTRE in this mode. */ + ess_set_mreg_bits(sc, ESS_MREG_AUDIO2_CTRL2, + ESS_AUDIO2_CTRL2_IRQ2_ENABLE); + } +} + + +void +ess_config_drq(sc) + struct ess_softc *sc; +{ + int v; + + DPRINTFN(2,("ess_config_drq\n")); + + /* Configure Audio 1 (record) for DMA on the appropriate channel. */ + v = ESS_DRQ_CTRL_PU | ESS_DRQ_CTRL_EXT; + switch (sc->sc_audio1.drq) { + case 0: + v |= ESS_DRQ_CTRL_DRQA; + break; + case 1: + v |= ESS_DRQ_CTRL_DRQB; + break; + case 3: + v |= ESS_DRQ_CTRL_DRQC; + break; +#ifdef DIAGNOSTIC + default: + printf("ess_config_drq: configured dma chan %d not supported for Audio 1\n", + sc->sc_audio1.drq); + return; +#endif + } + /* Set DRQ1 */ + ess_write_x_reg(sc, ESS_XCMD_DRQ_CTRL, v); + + if (sc->sc_model == ESS_1788) + return; + + /* Configure DRQ2 */ + v = ESS_AUDIO2_CTRL3_DRQ_PD; + switch (sc->sc_audio2.drq) { + case 0: + v |= ESS_AUDIO2_CTRL3_DRQA; + break; + case 1: + v |= ESS_AUDIO2_CTRL3_DRQB; + break; + case 3: + v |= ESS_AUDIO2_CTRL3_DRQC; + break; + case 5: + v |= ESS_AUDIO2_CTRL3_DRQD; + break; +#ifdef DIAGNOSTIC + default: + printf("ess_config_drq: configured dma chan %d not supported for Audio 2\n", + sc->sc_audio2.drq); + return; +#endif + } + ess_write_mix_reg(sc, ESS_MREG_AUDIO2_CTRL3, v); + /* Enable DMA 2 */ + ess_set_mreg_bits(sc, ESS_MREG_AUDIO2_CTRL2, + ESS_AUDIO2_CTRL2_DMA_ENABLE); +} + +/* + * Set up registers after a reset. + */ +void +ess_setup(sc) + struct ess_softc *sc; +{ + + ess_config_irq(sc); + ess_config_drq(sc); + + DPRINTFN(2,("ess_setup: done\n")); +} + +/* + * Determine the model of ESS chip we are talking to. Currently we + * only support ES1888, ES1887 and ES888. The method of determining + * the chip is based on the information on page 27 of the ES1887 data + * sheet. + * + * This routine sets the values of sc->sc_model and sc->sc_version. + */ +int +ess_identify(sc) + struct ess_softc *sc; +{ + u_char reg1; + u_char reg2; + u_char reg3; + + sc->sc_model = ESS_UNSUPPORTED; + sc->sc_version = 0; + + + /* + * 1. Check legacy ID bytes. These should be 0x68 0x8n, where + * n >= 8 for an ES1887 or an ES888. Other values indicate + * earlier (unsupported) chips. + */ + ess_wdsp(sc, ESS_ACMD_LEGACY_ID); + + if ((reg1 = ess_rdsp(sc)) != 0x68) { + printf("ess: First ID byte wrong (0x%02x)\n", reg1); + return 1; + } + + reg2 = ess_rdsp(sc); + if (((reg2 & 0xf0) != 0x80) || + ((reg2 & 0x0f) < 8)) { + printf("ess: Second ID byte wrong (0x%02x)\n", reg2); + return 1; + } + + /* + * Store the ID bytes as the version. + */ + sc->sc_version = (reg1 << 8) + reg2; + + + /* + * 2. Verify we can change bit 2 in mixer register 0x64. This + * should be possible on all supported chips. + */ + reg1 = ess_read_mix_reg(sc, ESS_MREG_VOLUME_CTRL); + reg2 = reg1 ^ 0x04; /* toggle bit 2 */ + + ess_write_mix_reg(sc, ESS_MREG_VOLUME_CTRL, reg2); + + if (ess_read_mix_reg(sc, ESS_MREG_VOLUME_CTRL) != reg2) { + printf("ess: Hardware error (unable to toggle bit 2 of mixer register 0x64)\n"); + return 1; + } + + /* + * Restore the original value of mixer register 0x64. + */ + ess_write_mix_reg(sc, ESS_MREG_VOLUME_CTRL, reg1); + + + /* + * 3. Verify we can change the value of mixer register + * ESS_MREG_SAMPLE_RATE. + * This is possible on the 1888/1887/888, but not on the 1788. + * It is not necessary to restore the value of this mixer register. + */ + reg1 = ess_read_mix_reg(sc, ESS_MREG_SAMPLE_RATE); + reg2 = reg1 ^ 0xff; /* toggle all bits */ + + ess_write_mix_reg(sc, ESS_MREG_SAMPLE_RATE, reg2); + + if (ess_read_mix_reg(sc, ESS_MREG_SAMPLE_RATE) != reg2) { + /* If we got this far before failing, it's a 1788. */ + sc->sc_model = ESS_1788; + } else { + /* + * 4. Determine if we can change bit 5 in mixer register 0x64. + * This determines whether we have an ES1887: + * + * - can change indicates ES1887 + * - can't change indicates ES1888 or ES888 + */ + reg1 = ess_read_mix_reg(sc, ESS_MREG_VOLUME_CTRL); + reg2 = reg1 ^ 0x20; /* toggle bit 5 */ + + ess_write_mix_reg(sc, ESS_MREG_VOLUME_CTRL, reg2); + + if (ess_read_mix_reg(sc, ESS_MREG_VOLUME_CTRL) == reg2) { + sc->sc_model = ESS_1887; + + /* + * Restore the original value of mixer register 0x64. + */ + ess_write_mix_reg(sc, ESS_MREG_VOLUME_CTRL, reg1); + } else { + /* + * 5. Determine if we can change the value of mixer + * register 0x69 independently of mixer register + * 0x68. This determines which chip we have: + * + * - can modify idependently indicates ES888 + * - register 0x69 is an alias of 0x68 indicates ES1888 + */ + reg1 = ess_read_mix_reg(sc, 0x68); + reg2 = ess_read_mix_reg(sc, 0x69); + reg3 = reg2 ^ 0xff; /* toggle all bits */ + + /* + * Write different values to each register. + */ + ess_write_mix_reg(sc, 0x68, reg2); + ess_write_mix_reg(sc, 0x69, reg3); + + if (ess_read_mix_reg(sc, 0x68) == reg2 && + ess_read_mix_reg(sc, 0x69) == reg3) + sc->sc_model = ESS_888; + else + sc->sc_model = ESS_1888; + + /* + * Restore the original value of the registers. + */ + ess_write_mix_reg(sc, 0x68, reg1); + ess_write_mix_reg(sc, 0x69, reg2); + } + } + + return 0; +} + + +int +ess_setup_sc(sc, doinit) + struct ess_softc *sc; + int doinit; +{ + /* Reset the chip. */ + if (ess_reset(sc) != 0) { + DPRINTF(("ess_setup_sc: couldn't reset chip\n")); + return (1); + } + + /* Identify the ESS chip, and check that it is supported. */ + if (ess_identify(sc)) { + DPRINTF(("ess_setup_sc: couldn't identify\n")); + return (1); + } + + return (0); +} + +/* + * Probe for the ESS hardware. + */ +int +essmatch(sc) + struct ess_softc *sc; +{ + if (!ESS_BASE_VALID(sc->sc_iobase)) { + printf("ess: configured iobase 0x%x invalid\n", sc->sc_iobase); + return (0); + } + + /* Configure the ESS chip for the desired audio base address. */ + if (ess_config_addr(sc)) + return (0); + + if (ess_setup_sc(sc, 1)) + return (0); + + if (sc->sc_model == ESS_UNSUPPORTED) { + DPRINTF(("ess: Unsupported model\n")); + return (0); + } + + /* Check that requested DMA channels are valid and different. */ + if (!ESS_DRQ1_VALID(sc->sc_audio1.drq)) { + printf("ess: record drq %d invalid\n", sc->sc_audio1.drq); + return (0); + } + if (!isa_drq_isfree(sc->sc_isa, sc->sc_audio1.drq)) + return (0); + if (sc->sc_model != ESS_1788) { + if (!ESS_DRQ2_VALID(sc->sc_audio2.drq)) { + printf("ess: play drq %d invalid\n", sc->sc_audio2.drq); + return (0); + } + if (sc->sc_audio1.drq == sc->sc_audio2.drq) { + printf("ess: play and record drq both %d\n", + sc->sc_audio1.drq); + return (0); + } + if (!isa_drq_isfree(sc->sc_isa, sc->sc_audio2.drq)) + return (0); + } + + /* + * The 1887 has an additional IRQ mode where both channels are mapped + * to the same IRQ. + */ + if (sc->sc_model == ESS_1887 && + sc->sc_audio1.irq == sc->sc_audio2.irq && + sc->sc_audio1.irq != -1 && + ESS_IRQ12_VALID(sc->sc_audio1.irq)) + goto irq_not1888; + + /* Check that requested IRQ lines are valid and different. */ + if (sc->sc_audio1.irq != -1 && + !ESS_IRQ1_VALID(sc->sc_audio1.irq)) { + printf("ess: record irq %d invalid\n", sc->sc_audio1.irq); + return (0); + } + if (sc->sc_model != ESS_1788) { + if (sc->sc_audio2.irq != -1 && + !ESS_IRQ2_VALID(sc->sc_audio2.irq)) { + printf("ess: play irq %d invalid\n", sc->sc_audio2.irq); + return (0); + } + if (sc->sc_audio1.irq == sc->sc_audio2.irq && + sc->sc_audio1.irq != -1) { + printf("ess: play and record irq both %d\n", + sc->sc_audio1.irq); + return (0); + } + } + +irq_not1888: + /* XXX should we check IRQs as well? */ + + return (1); +} + + +/* + * Attach hardware to driver, attach hardware driver to audio + * pseudo-device driver. + */ +void +essattach(sc) + struct ess_softc *sc; +{ + struct audio_attach_args arg; + struct audio_params pparams, rparams; + int i; + u_int v; + + if (ess_setup_sc(sc, 0)) { + printf(": setup failed\n"); + return; + } + + printf(": ESS Technology ES%s [version 0x%04x]\n", + essmodel[sc->sc_model], sc->sc_version); + + sc->sc_audio1.irq = -1; + sc->sc_audio2.irq = -1; + + sc->sc_audio1.polled = sc->sc_audio1.irq == -1; + if (!sc->sc_audio1.polled) { + sc->sc_audio1.ih = isa_intr_establish(sc->sc_ic, + sc->sc_audio1.irq, sc->sc_audio1.ist, IPL_AUDIO, + ess_audio1_intr, sc, sc->sc_dev.dv_xname); + printf("%s: audio1 interrupting at irq %d\n", + sc->sc_dev.dv_xname, sc->sc_audio1.irq); + } else + printf("%s: audio1 polled\n", sc->sc_dev.dv_xname); + if (isa_dmamap_create(sc->sc_isa, sc->sc_audio1.drq, + MAX_ISADMA, BUS_DMA_NOWAIT|BUS_DMA_ALLOCNOW)) { + printf("%s: can't create map for drq %d\n", + sc->sc_dev.dv_xname, sc->sc_audio1.drq); + return; + } + + if (sc->sc_model != ESS_1788) { + sc->sc_audio2.polled = sc->sc_audio2.irq == -1; + if (!sc->sc_audio2.polled) { + sc->sc_audio2.ih = isa_intr_establish(sc->sc_ic, + sc->sc_audio2.irq, sc->sc_audio2.ist, IPL_AUDIO, + ess_audio2_intr, sc, sc->sc_dev.dv_xname); + printf("%s: audio2 interrupting at irq %d\n", + sc->sc_dev.dv_xname, sc->sc_audio2.irq); + } else + printf("%s: audio2 polled\n", sc->sc_dev.dv_xname); + if (isa_dmamap_create(sc->sc_isa, sc->sc_audio2.drq, + MAX_ISADMA, BUS_DMA_NOWAIT|BUS_DMA_ALLOCNOW)) { + printf("%s: can't create map for drq %d\n", + sc->sc_dev.dv_xname, sc->sc_audio2.drq); + return; + } + } + + /* + * Set record and play parameters to default values defined in + * generic audio driver. + */ + pparams = audio_default; + rparams = audio_default; + ess_set_params(sc, AUMODE_RECORD|AUMODE_PLAY, 0, &pparams, &rparams); + + /* Do a hardware reset on the mixer. */ + ess_write_mix_reg(sc, ESS_MIX_RESET, ESS_MIX_RESET); + + /* + * Set volume of Audio 1 to zero and disable Audio 1 DAC input + * to playback mixer, since playback is always through Audio 2. + */ + if (sc->sc_model != ESS_1788) + ess_write_mix_reg(sc, ESS_MREG_VOLUME_VOICE, 0); + ess_wdsp(sc, ESS_ACMD_DISABLE_SPKR); + + if (sc->sc_model == ESS_1788) { + ess_write_mix_reg(sc, ESS_MREG_ADC_SOURCE, ESS_SOURCE_MIC); + sc->in_port = ESS_SOURCE_MIC; + sc->ndevs = ESS_1788_NDEVS; + } else { + /* + * Set hardware record source to use output of the record + * mixer. We do the selection of record source in software by + * setting the gain of the unused sources to zero. (See + * ess_set_in_ports.) + */ + ess_write_mix_reg(sc, ESS_MREG_ADC_SOURCE, ESS_SOURCE_MIXER); + sc->in_mask = 1 << ESS_MIC_REC_VOL; + sc->ndevs = ESS_1888_NDEVS; + ess_clear_mreg_bits(sc, ESS_MREG_AUDIO2_CTRL2, 0x10); + ess_set_mreg_bits(sc, ESS_MREG_AUDIO2_CTRL2, 0x08); + } + + /* + * Set gain on each mixer device to a sensible value. + * Devices not normally used are turned off, and other devices + * are set to 50% volume. + */ + for (i = 0; i < sc->ndevs; i++) { + switch (i) { + case ESS_MIC_PLAY_VOL: + case ESS_LINE_PLAY_VOL: + case ESS_CD_PLAY_VOL: + case ESS_AUXB_PLAY_VOL: + case ESS_DAC_REC_VOL: + case ESS_LINE_REC_VOL: + case ESS_SYNTH_REC_VOL: + case ESS_CD_REC_VOL: + case ESS_AUXB_REC_VOL: + v = 0; + break; + default: + v = ESS_4BIT_GAIN(AUDIO_MAX_GAIN / 2); + break; + } + sc->gain[i][ESS_LEFT] = sc->gain[i][ESS_RIGHT] = v; + ess_set_gain(sc, i, 1); + } + + ess_setup(sc); + + /* Disable the speaker until the device is opened. */ + ess_speaker_off(sc); + sc->spkr_state = SPKR_OFF; + + sprintf(ess_device.name, "ES%s", essmodel[sc->sc_model]); + sprintf(ess_device.version, "0x%04x", sc->sc_version); + + if (sc->sc_model == ESS_1788) + audio_attach_mi(&ess_1788_hw_if, sc, &sc->sc_dev); + else + audio_attach_mi(&ess_1888_hw_if, sc, &sc->sc_dev); + + arg.type = AUDIODEV_TYPE_OPL; + arg.hwif = 0; + arg.hdl = 0; + (void)config_found(&sc->sc_dev, &arg, audioprint); + +#ifdef AUDIO_DEBUG + if (essdebug > 0) + ess_printsc(sc); +#endif +} + +/* + * Various routines to interface to higher level audio driver + */ + +int +ess_open(addr, flags) + void *addr; + int flags; +{ + struct ess_softc *sc = addr; + + DPRINTF(("ess_open: sc=%p\n", sc)); + + if (sc->sc_open != 0 || ess_reset(sc) != 0) + return ENXIO; + + ess_setup(sc); /* because we did a reset */ + + sc->sc_open = 1; + + DPRINTF(("ess_open: opened\n")); + + return (0); +} + +void +ess_1788_close(addr) + void *addr; +{ + struct ess_softc *sc = addr; + + DPRINTF(("ess_1788_close: sc=%p\n", sc)); + + ess_speaker_off(sc); + sc->spkr_state = SPKR_OFF; + + ess_audio1_halt(sc); + + sc->sc_open = 0; + DPRINTF(("ess_1788_close: closed\n")); +} + +void +ess_1888_close(addr) + void *addr; +{ + struct ess_softc *sc = addr; + + DPRINTF(("ess_1888_close: sc=%p\n", sc)); + + ess_speaker_off(sc); + sc->spkr_state = SPKR_OFF; + + ess_audio1_halt(sc); + ess_audio2_halt(sc); + + sc->sc_open = 0; + DPRINTF(("ess_1888_close: closed\n")); +} + +/* + * Wait for FIFO to drain, and analog section to settle. + * XXX should check FIFO empty bit. + */ +int +ess_drain(addr) + void *addr; +{ + tsleep(addr, PWAIT | PCATCH, "essdr", hz/20); /* XXX */ + return (0); +} + +/* XXX should use reference count */ +int +ess_speaker_ctl(addr, newstate) + void *addr; + int newstate; +{ + struct ess_softc *sc = addr; + + if ((newstate == SPKR_ON) && (sc->spkr_state == SPKR_OFF)) { + ess_speaker_on(sc); + sc->spkr_state = SPKR_ON; + } + if ((newstate == SPKR_OFF) && (sc->spkr_state == SPKR_ON)) { + ess_speaker_off(sc); + sc->spkr_state = SPKR_OFF; + } + return (0); +} + +int +ess_getdev(addr, retp) + void *addr; + struct audio_device *retp; +{ + *retp = ess_device; + return (0); +} + +int +ess_query_encoding(addr, fp) + void *addr; + struct audio_encoding *fp; +{ + /*struct ess_softc *sc = addr;*/ + + switch (fp->index) { + case 0: + strcpy(fp->name, AudioEulinear); + fp->encoding = AUDIO_ENCODING_ULINEAR; + fp->precision = 8; + fp->flags = 0; + return (0); + case 1: + strcpy(fp->name, AudioEmulaw); + fp->encoding = AUDIO_ENCODING_ULAW; + fp->precision = 8; + fp->flags = AUDIO_ENCODINGFLAG_EMULATED; + return (0); + case 2: + strcpy(fp->name, AudioEalaw); + fp->encoding = AUDIO_ENCODING_ALAW; + fp->precision = 8; + fp->flags = AUDIO_ENCODINGFLAG_EMULATED; + return (0); + case 3: + strcpy(fp->name, AudioEslinear); + fp->encoding = AUDIO_ENCODING_SLINEAR; + fp->precision = 8; + fp->flags = 0; + return (0); + case 4: + strcpy(fp->name, AudioEslinear_le); + fp->encoding = AUDIO_ENCODING_SLINEAR_LE; + fp->precision = 16; + fp->flags = 0; + return (0); + case 5: + strcpy(fp->name, AudioEulinear_le); + fp->encoding = AUDIO_ENCODING_ULINEAR_LE; + fp->precision = 16; + fp->flags = 0; + return (0); + case 6: + strcpy(fp->name, AudioEslinear_be); + fp->encoding = AUDIO_ENCODING_SLINEAR_BE; + fp->precision = 16; + fp->flags = AUDIO_ENCODINGFLAG_EMULATED; + return (0); + case 7: + strcpy(fp->name, AudioEulinear_be); + fp->encoding = AUDIO_ENCODING_ULINEAR_BE; + fp->precision = 16; + fp->flags = AUDIO_ENCODINGFLAG_EMULATED; + return (0); + default: + return EINVAL; + } + return (0); +} + +int +ess_set_params(addr, setmode, usemode, play, rec) + void *addr; + int setmode, usemode; + struct audio_params *play, *rec; +{ + struct ess_softc *sc = addr; + struct audio_params *p; + int mode; + int rate; + + DPRINTF(("ess_set_params: set=%d use=%d\n", setmode, usemode)); + + /* + * The ES1887 manual (page 39, `Full-Duplex DMA Mode') claims that in + * full-duplex operation the sample rates must be the same for both + * channels. This appears to be false; the only bit in common is the + * clock source selection. However, we'll be conservative here. + * - mycroft + */ + if (play->sample_rate != rec->sample_rate && + usemode == (AUMODE_PLAY | AUMODE_RECORD)) { + if (setmode == AUMODE_PLAY) { + rec->sample_rate = play->sample_rate; + setmode |= AUMODE_RECORD; + } else if (setmode == AUMODE_RECORD) { + play->sample_rate = rec->sample_rate; + setmode |= AUMODE_PLAY; + } else + return (EINVAL); + } + + for (mode = AUMODE_RECORD; mode != -1; + mode = mode == AUMODE_RECORD ? AUMODE_PLAY : -1) { + if ((setmode & mode) == 0) + continue; + + p = mode == AUMODE_PLAY ? play : rec; + + if (p->sample_rate < ESS_MINRATE || + p->sample_rate > ESS_MAXRATE || + (p->precision != 8 && p->precision != 16) || + (p->channels != 1 && p->channels != 2)) + return (EINVAL); + + p->factor = 1; + p->sw_code = 0; + switch (p->encoding) { + case AUDIO_ENCODING_SLINEAR_BE: + case AUDIO_ENCODING_ULINEAR_BE: + if (p->precision == 16) + p->sw_code = swap_bytes; + break; + case AUDIO_ENCODING_SLINEAR_LE: + case AUDIO_ENCODING_ULINEAR_LE: + break; + case AUDIO_ENCODING_ULAW: + if (mode == AUMODE_PLAY) { + p->factor = 2; + p->sw_code = mulaw_to_ulinear16; + } else + p->sw_code = ulinear8_to_mulaw; + break; + case AUDIO_ENCODING_ALAW: + if (mode == AUMODE_PLAY) { + p->factor = 2; + p->sw_code = alaw_to_ulinear16; + } else + p->sw_code = ulinear8_to_alaw; + break; + default: + return (EINVAL); + } + } + + if (usemode == AUMODE_RECORD) + rate = rec->sample_rate; + else + rate = play->sample_rate; + + ess_write_x_reg(sc, ESS_XCMD_SAMPLE_RATE, ess_srtotc(rate)); + ess_write_x_reg(sc, ESS_XCMD_FILTER_CLOCK, ess_srtofc(rate)); + + if (sc->sc_model != ESS_1788) { + ess_write_mix_reg(sc, ESS_MREG_SAMPLE_RATE, ess_srtotc(rate)); + ess_write_mix_reg(sc, ESS_MREG_FILTER_CLOCK, ess_srtofc(rate)); + } + + return (0); +} + +int +ess_audio1_trigger_output(addr, start, end, blksize, intr, arg, param) + void *addr; + void *start, *end; + int blksize; + void (*intr) __P((void *)); + void *arg; + struct audio_params *param; +{ + struct ess_softc *sc = addr; + u_int8_t reg; + + DPRINTFN(1, ("ess_audio1_trigger_output: sc=%p start=%p end=%p blksize=%d intr=%p(%p)\n", + addr, start, end, blksize, intr, arg)); + + if (sc->sc_audio1.active) + panic("ess_audio1_trigger_output: already running"); + + sc->sc_audio1.active = 1; + sc->sc_audio1.intr = intr; + sc->sc_audio1.arg = arg; + if (sc->sc_audio1.polled) { + sc->sc_audio1.dmapos = 0; + sc->sc_audio1.buffersize = (char *)end - (char *)start; + sc->sc_audio1.dmacount = 0; + sc->sc_audio1.blksize = blksize; + timeout(ess_audio1_poll, sc, hz/30); + } + + reg = ess_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; + } + ess_write_x_reg(sc, ESS_XCMD_AUDIO_CTRL, reg); + + reg = ess_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; + ess_write_x_reg(sc, ESS_XCMD_AUDIO1_CTRL1, reg); + + isa_dmastart(sc->sc_isa, sc->sc_audio1.drq, start, + (char *)end - (char *)start, NULL, + DMAMODE_WRITE | DMAMODE_LOOP, BUS_DMA_NOWAIT); + + /* Program transfer count registers with 2's complement of count. */ + blksize = -blksize; + ess_write_x_reg(sc, ESS_XCMD_XFER_COUNTLO, blksize); + ess_write_x_reg(sc, ESS_XCMD_XFER_COUNTHI, blksize >> 8); + + /* Use 4 bytes per output DMA. */ + ess_set_xreg_bits(sc, ESS_XCMD_DEMAND_CTRL, ESS_DEMAND_CTRL_DEMAND_4); + + /* Start auto-init DMA */ + ess_wdsp(sc, ESS_ACMD_ENABLE_SPKR); + reg = ess_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; + ess_write_x_reg(sc, ESS_XCMD_AUDIO1_CTRL2, reg); + + return (0); +} + +int +ess_audio2_trigger_output(addr, start, end, blksize, intr, arg, param) + void *addr; + void *start, *end; + int blksize; + void (*intr) __P((void *)); + void *arg; + struct audio_params *param; +{ + struct ess_softc *sc = addr; + u_int8_t reg; + + DPRINTFN(1, ("ess_audio2_trigger_output: sc=%p start=%p end=%p blksize=%d intr=%p(%p)\n", + addr, start, end, blksize, intr, arg)); + + if (sc->sc_audio2.active) + panic("ess_audio2_trigger_output: already running"); + + sc->sc_audio2.active = 1; + sc->sc_audio2.intr = intr; + sc->sc_audio2.arg = arg; + if (sc->sc_audio2.polled) { + sc->sc_audio2.dmapos = 0; + sc->sc_audio2.buffersize = (char *)end - (char *)start; + sc->sc_audio2.dmacount = 0; + sc->sc_audio2.blksize = blksize; + timeout(ess_audio2_poll, sc, hz/30); + } + + reg = ess_read_mix_reg(sc, ESS_MREG_AUDIO2_CTRL2); + if (param->precision * param->factor == 16) + reg |= ESS_AUDIO2_CTRL2_FIFO_SIZE; + else + reg &= ~ESS_AUDIO2_CTRL2_FIFO_SIZE; + if (param->channels == 2) + reg |= ESS_AUDIO2_CTRL2_CHANNELS; + else + reg &= ~ESS_AUDIO2_CTRL2_CHANNELS; + if (param->encoding == AUDIO_ENCODING_SLINEAR_BE || + param->encoding == AUDIO_ENCODING_SLINEAR_LE) + reg |= ESS_AUDIO2_CTRL2_FIFO_SIGNED; + else + reg &= ~ESS_AUDIO2_CTRL2_FIFO_SIGNED; + ess_write_mix_reg(sc, ESS_MREG_AUDIO2_CTRL2, reg); + + isa_dmastart(sc->sc_isa, sc->sc_audio2.drq, start, + (char *)end - (char *)start, NULL, + DMAMODE_WRITE | DMAMODE_LOOP, BUS_DMA_NOWAIT); + + if (IS16BITDRQ(sc->sc_audio2.drq)) + blksize >>= 1; /* use word count for 16 bit DMA */ + /* Program transfer count registers with 2's complement of count. */ + blksize = -blksize; + ess_write_mix_reg(sc, ESS_MREG_XFER_COUNTLO, blksize); + ess_write_mix_reg(sc, ESS_MREG_XFER_COUNTHI, blksize >> 8); + + reg = ess_read_mix_reg(sc, ESS_MREG_AUDIO2_CTRL1); + if (IS16BITDRQ(sc->sc_audio2.drq)) + reg |= ESS_AUDIO2_CTRL1_XFER_SIZE; + else + reg &= ~ESS_AUDIO2_CTRL1_XFER_SIZE; + reg |= ESS_AUDIO2_CTRL1_DEMAND_8; + reg |= ESS_AUDIO2_CTRL1_DAC_ENABLE | ESS_AUDIO2_CTRL1_FIFO_ENABLE | + ESS_AUDIO2_CTRL1_AUTO_INIT; + ess_write_mix_reg(sc, ESS_MREG_AUDIO2_CTRL1, reg); + + return (0); +} + +int +ess_audio1_trigger_input(addr, start, end, blksize, intr, arg, param) + void *addr; + void *start, *end; + int blksize; + void (*intr) __P((void *)); + void *arg; + struct audio_params *param; +{ + struct ess_softc *sc = addr; + u_int8_t reg; + + DPRINTFN(1, ("ess_audio1_trigger_input: sc=%p start=%p end=%p blksize=%d intr=%p(%p)\n", + addr, start, end, blksize, intr, arg)); + + if (sc->sc_audio1.active) + panic("ess_audio1_trigger_input: already running"); + + sc->sc_audio1.active = 1; + sc->sc_audio1.intr = intr; + sc->sc_audio1.arg = arg; + if (sc->sc_audio1.polled) { + sc->sc_audio1.dmapos = 0; + sc->sc_audio1.buffersize = (char *)end - (char *)start; + sc->sc_audio1.dmacount = 0; + sc->sc_audio1.blksize = blksize; + timeout(ess_audio1_poll, sc, hz/30); + } + + reg = ess_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; + } + ess_write_x_reg(sc, ESS_XCMD_AUDIO_CTRL, reg); + + reg = ess_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; + ess_write_x_reg(sc, ESS_XCMD_AUDIO1_CTRL1, reg); + + isa_dmastart(sc->sc_isa, sc->sc_audio1.drq, start, + (char *)end - (char *)start, NULL, + DMAMODE_READ | DMAMODE_LOOP, BUS_DMA_NOWAIT); + + /* Program transfer count registers with 2's complement of count. */ + blksize = -blksize; + ess_write_x_reg(sc, ESS_XCMD_XFER_COUNTLO, blksize); + ess_write_x_reg(sc, ESS_XCMD_XFER_COUNTHI, blksize >> 8); + + /* Use 4 bytes per input DMA. */ + ess_set_xreg_bits(sc, ESS_XCMD_DEMAND_CTRL, ESS_DEMAND_CTRL_DEMAND_4); + + /* Start auto-init DMA */ + ess_wdsp(sc, ESS_ACMD_DISABLE_SPKR); + reg = ess_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; + ess_write_x_reg(sc, ESS_XCMD_AUDIO1_CTRL2, reg); + + return (0); +} + +int +ess_audio1_halt(addr) + void *addr; +{ + struct ess_softc *sc = addr; + + DPRINTF(("ess_audio1_halt: sc=%p\n", sc)); + + if (sc->sc_audio1.active) { + ess_clear_xreg_bits(sc, ESS_XCMD_AUDIO1_CTRL2, + ESS_AUDIO1_CTRL2_FIFO_ENABLE); + isa_dmaabort(sc->sc_isa, sc->sc_audio1.drq); + if (sc->sc_audio1.polled) + untimeout(ess_audio1_poll, sc); + sc->sc_audio1.active = 0; + } + + return (0); +} + +int +ess_audio2_halt(addr) + void *addr; +{ + struct ess_softc *sc = addr; + + DPRINTF(("ess_audio2_halt: sc=%p\n", sc)); + + if (sc->sc_audio2.active) { + ess_clear_mreg_bits(sc, ESS_MREG_AUDIO2_CTRL1, + ESS_AUDIO2_CTRL1_DAC_ENABLE | + ESS_AUDIO2_CTRL1_FIFO_ENABLE); + isa_dmaabort(sc->sc_isa, sc->sc_audio2.drq); + if (sc->sc_audio2.polled) + untimeout(ess_audio2_poll, sc); + sc->sc_audio2.active = 0; + } + + return (0); +} + +int +ess_audio1_intr(arg) + void *arg; +{ + struct ess_softc *sc = arg; + u_int8_t reg; + + DPRINTFN(1,("ess_audio1_intr: intr=%p\n", sc->sc_audio1.intr)); + + /* Check and clear interrupt on Audio1. */ + reg = EREAD1(sc->sc_iot, sc->sc_ioh, ESS_DSP_RW_STATUS); + if ((reg & ESS_DSP_READ_OFLOW) == 0) + return (0); + reg = EREAD1(sc->sc_iot, sc->sc_ioh, ESS_CLEAR_INTR); + + sc->sc_audio1.nintr++; + + if (sc->sc_audio1.active) { + (*sc->sc_audio1.intr)(sc->sc_audio1.arg); + return (1); + } else + return (0); +} + +int +ess_audio2_intr(arg) + void *arg; +{ + struct ess_softc *sc = arg; + u_int8_t reg; + + DPRINTFN(1,("ess_audio2_intr: intr=%p\n", sc->sc_audio2.intr)); + + /* Check and clear interrupt on Audio2. */ + reg = ess_read_mix_reg(sc, ESS_MREG_AUDIO2_CTRL2); + if ((reg & ESS_AUDIO2_CTRL2_IRQ_LATCH) == 0) + return (0); + reg &= ~ESS_AUDIO2_CTRL2_IRQ_LATCH; + ess_write_mix_reg(sc, ESS_MREG_AUDIO2_CTRL2, reg); + + sc->sc_audio2.nintr++; + + if (sc->sc_audio2.active) { + (*sc->sc_audio2.intr)(sc->sc_audio2.arg); + return (1); + } else + return (0); +} + +void +ess_audio1_poll(addr) + void *addr; +{ + struct ess_softc *sc = addr; + int dmapos, dmacount; + + if (!sc->sc_audio1.active) + return; + + sc->sc_audio1.nintr++; + + dmapos = isa_dmacount(sc->sc_isa, sc->sc_audio1.drq); + dmacount = sc->sc_audio1.dmapos - dmapos; + if (dmacount < 0) + dmacount += sc->sc_audio1.buffersize; + sc->sc_audio1.dmapos = dmapos; +#if 1 + dmacount += sc->sc_audio1.dmacount; + while (dmacount > sc->sc_audio1.blksize) { + dmacount -= sc->sc_audio1.blksize; + (*sc->sc_audio1.intr)(sc->sc_audio1.arg); + } + sc->sc_audio1.dmacount = dmacount; +#else + (*sc->sc_audio1.intr)(sc->sc_audio1.arg, dmacount); +#endif + + timeout(ess_audio1_poll, sc, hz/30); +} + +void +ess_audio2_poll(addr) + void *addr; +{ + struct ess_softc *sc = addr; + int dmapos, dmacount; + + if (!sc->sc_audio2.active) + return; + + sc->sc_audio2.nintr++; + + dmapos = isa_dmacount(sc->sc_isa, sc->sc_audio2.drq); + dmacount = sc->sc_audio2.dmapos - dmapos; + if (dmacount < 0) + dmacount += sc->sc_audio2.buffersize; + sc->sc_audio2.dmapos = dmapos; +#if 1 + dmacount += sc->sc_audio2.dmacount; + while (dmacount > sc->sc_audio2.blksize) { + dmacount -= sc->sc_audio2.blksize; + (*sc->sc_audio2.intr)(sc->sc_audio2.arg); + } + sc->sc_audio2.dmacount = dmacount; +#else + (*sc->sc_audio2.intr)(sc->sc_audio2.arg, dmacount); +#endif + + timeout(ess_audio2_poll, sc, hz/30); +} + +int +ess_round_blocksize(addr, blk) + void *addr; + int blk; +{ + return (blk & -8); /* round for max DMA size */ +} + +int +ess_set_port(addr, cp) + void *addr; + mixer_ctrl_t *cp; +{ + struct ess_softc *sc = addr; + int lgain, rgain; + + DPRINTFN(5,("ess_set_port: port=%d num_channels=%d\n", + cp->dev, cp->un.value.num_channels)); + + switch (cp->dev) { + /* + * The following mixer ports are all stereo. If we get a + * single-channel gain value passed in, then we duplicate it + * to both left and right channels. + */ + case ESS_MASTER_VOL: + case ESS_DAC_PLAY_VOL: + case ESS_MIC_PLAY_VOL: + case ESS_LINE_PLAY_VOL: + case ESS_SYNTH_PLAY_VOL: + case ESS_CD_PLAY_VOL: + case ESS_AUXB_PLAY_VOL: + case ESS_RECORD_VOL: + if (cp->type != AUDIO_MIXER_VALUE) + return EINVAL; + + switch (cp->un.value.num_channels) { + case 1: + lgain = rgain = ESS_4BIT_GAIN( + cp->un.value.level[AUDIO_MIXER_LEVEL_MONO]); + break; + case 2: + lgain = ESS_4BIT_GAIN( + cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT]); + rgain = ESS_4BIT_GAIN( + cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT]); + break; + default: + return EINVAL; + } + + sc->gain[cp->dev][ESS_LEFT] = lgain; + sc->gain[cp->dev][ESS_RIGHT] = rgain; + ess_set_gain(sc, cp->dev, 1); + return (0); + + /* + * The PC speaker port is mono. If we get a stereo gain value + * passed in, then we return EINVAL. + */ + case ESS_PCSPEAKER_VOL: + if (cp->un.value.num_channels != 1) + return EINVAL; + + sc->gain[cp->dev][ESS_LEFT] = sc->gain[cp->dev][ESS_RIGHT] = + ESS_3BIT_GAIN(cp->un.value.level[AUDIO_MIXER_LEVEL_MONO]); + ess_set_gain(sc, cp->dev, 1); + return (0); + + case ESS_RECORD_SOURCE: + if (sc->sc_model == ESS_1788) { + if (cp->type == AUDIO_MIXER_ENUM) + return (ess_set_in_port(sc, cp->un.ord)); + else + return (EINVAL); + } else { + if (cp->type == AUDIO_MIXER_SET) + return (ess_set_in_ports(sc, cp->un.mask)); + else + return (EINVAL); + } + return (0); + + case ESS_RECORD_MONITOR: + if (cp->type != AUDIO_MIXER_ENUM) + return EINVAL; + + if (cp->un.ord) + /* Enable monitor */ + ess_set_xreg_bits(sc, ESS_XCMD_AUDIO_CTRL, + ESS_AUDIO_CTRL_MONITOR); + else + /* Disable monitor */ + ess_clear_xreg_bits(sc, ESS_XCMD_AUDIO_CTRL, + ESS_AUDIO_CTRL_MONITOR); + return (0); + } + + if (sc->sc_model == ESS_1788) + return (EINVAL); + + switch (cp->dev) { + case ESS_DAC_REC_VOL: + case ESS_MIC_REC_VOL: + case ESS_LINE_REC_VOL: + case ESS_SYNTH_REC_VOL: + case ESS_CD_REC_VOL: + case ESS_AUXB_REC_VOL: + if (cp->type != AUDIO_MIXER_VALUE) + return EINVAL; + + switch (cp->un.value.num_channels) { + case 1: + lgain = rgain = ESS_4BIT_GAIN( + cp->un.value.level[AUDIO_MIXER_LEVEL_MONO]); + break; + case 2: + lgain = ESS_4BIT_GAIN( + cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT]); + rgain = ESS_4BIT_GAIN( + cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT]); + break; + default: + return EINVAL; + } + + sc->gain[cp->dev][ESS_LEFT] = lgain; + sc->gain[cp->dev][ESS_RIGHT] = rgain; + ess_set_gain(sc, cp->dev, 1); + return (0); + + case ESS_MIC_PREAMP: + if (cp->type != AUDIO_MIXER_ENUM) + return EINVAL; + + if (cp->un.ord) + /* Enable microphone preamp */ + ess_set_xreg_bits(sc, ESS_XCMD_PREAMP_CTRL, + ESS_PREAMP_CTRL_ENABLE); + else + /* Disable microphone preamp */ + ess_clear_xreg_bits(sc, ESS_XCMD_PREAMP_CTRL, + ESS_PREAMP_CTRL_ENABLE); + return (0); + } + + return (EINVAL); +} + +int +ess_get_port(addr, cp) + void *addr; + mixer_ctrl_t *cp; +{ + struct ess_softc *sc = addr; + + DPRINTFN(5,("ess_get_port: port=%d\n", cp->dev)); + + switch (cp->dev) { + case ESS_MASTER_VOL: + case ESS_DAC_PLAY_VOL: + case ESS_MIC_PLAY_VOL: + case ESS_LINE_PLAY_VOL: + case ESS_SYNTH_PLAY_VOL: + case ESS_CD_PLAY_VOL: + case ESS_AUXB_PLAY_VOL: + case ESS_RECORD_VOL: + switch (cp->un.value.num_channels) { + case 1: + cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = + sc->gain[cp->dev][ESS_LEFT]; + break; + case 2: + cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = + sc->gain[cp->dev][ESS_LEFT]; + cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = + sc->gain[cp->dev][ESS_RIGHT]; + break; + default: + return EINVAL; + } + return (0); + + case ESS_PCSPEAKER_VOL: + if (cp->un.value.num_channels != 1) + return EINVAL; + + cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = + sc->gain[cp->dev][ESS_LEFT]; + return (0); + + case ESS_RECORD_SOURCE: + if (sc->sc_model == ESS_1788) + cp->un.ord = sc->in_port; + else + cp->un.mask = sc->in_mask; + return (0); + + case ESS_RECORD_MONITOR: + cp->un.ord = (ess_read_x_reg(sc, ESS_XCMD_AUDIO_CTRL) & + ESS_AUDIO_CTRL_MONITOR) ? 1 : 0; + return (0); + } + + if (sc->sc_model == ESS_1788) + return (EINVAL); + + switch (cp->dev) { + case ESS_DAC_REC_VOL: + case ESS_MIC_REC_VOL: + case ESS_LINE_REC_VOL: + case ESS_SYNTH_REC_VOL: + case ESS_CD_REC_VOL: + case ESS_AUXB_REC_VOL: + switch (cp->un.value.num_channels) { + case 1: + cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = + sc->gain[cp->dev][ESS_LEFT]; + break; + case 2: + cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = + sc->gain[cp->dev][ESS_LEFT]; + cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = + sc->gain[cp->dev][ESS_RIGHT]; + break; + default: + return EINVAL; + } + return (0); + + case ESS_MIC_PREAMP: + cp->un.ord = (ess_read_x_reg(sc, ESS_XCMD_PREAMP_CTRL) & + ESS_PREAMP_CTRL_ENABLE) ? 1 : 0; + return (0); + } + + return (EINVAL); +} + +int +ess_query_devinfo(addr, dip) + void *addr; + mixer_devinfo_t *dip; +{ + struct ess_softc *sc = addr; + + DPRINTFN(5,("ess_query_devinfo: model=%d index=%d\n", + sc->sc_model, dip->index)); + + /* + * REVISIT: There are some slight differences between the + * mixers on the different ESS chips, which can + * be sorted out using the chip model rather than a + * separate mixer model. + * This is currently coded assuming an ES1887; we + * need to work out which bits are not applicable to + * the other models (1888 and 888). + */ + switch (dip->index) { + case ESS_DAC_PLAY_VOL: + dip->mixer_class = ESS_INPUT_CLASS; + dip->next = dip->prev = AUDIO_MIXER_LAST; + strcpy(dip->label.name, AudioNdac); + dip->type = AUDIO_MIXER_VALUE; + dip->un.v.num_channels = 2; + strcpy(dip->un.v.units.name, AudioNvolume); + return (0); + + case ESS_MIC_PLAY_VOL: + dip->mixer_class = ESS_INPUT_CLASS; + dip->prev = AUDIO_MIXER_LAST; + if (sc->sc_model == ESS_1788) + dip->next = AUDIO_MIXER_LAST; + else + dip->next = ESS_MIC_PREAMP; + strcpy(dip->label.name, AudioNmicrophone); + dip->type = AUDIO_MIXER_VALUE; + dip->un.v.num_channels = 2; + strcpy(dip->un.v.units.name, AudioNvolume); + return (0); + + case ESS_LINE_PLAY_VOL: + dip->mixer_class = ESS_INPUT_CLASS; + dip->next = dip->prev = AUDIO_MIXER_LAST; + strcpy(dip->label.name, AudioNline); + dip->type = AUDIO_MIXER_VALUE; + dip->un.v.num_channels = 2; + strcpy(dip->un.v.units.name, AudioNvolume); + return (0); + + case ESS_SYNTH_PLAY_VOL: + dip->mixer_class = ESS_INPUT_CLASS; + dip->next = dip->prev = AUDIO_MIXER_LAST; + strcpy(dip->label.name, AudioNfmsynth); + dip->type = AUDIO_MIXER_VALUE; + dip->un.v.num_channels = 2; + strcpy(dip->un.v.units.name, AudioNvolume); + return (0); + + case ESS_CD_PLAY_VOL: + dip->mixer_class = ESS_INPUT_CLASS; + dip->next = dip->prev = AUDIO_MIXER_LAST; + strcpy(dip->label.name, AudioNcd); + dip->type = AUDIO_MIXER_VALUE; + dip->un.v.num_channels = 2; + strcpy(dip->un.v.units.name, AudioNvolume); + return (0); + + case ESS_AUXB_PLAY_VOL: + dip->mixer_class = ESS_INPUT_CLASS; + dip->next = dip->prev = AUDIO_MIXER_LAST; + strcpy(dip->label.name, "auxb"); + dip->type = AUDIO_MIXER_VALUE; + dip->un.v.num_channels = 2; + strcpy(dip->un.v.units.name, AudioNvolume); + return (0); + + case ESS_INPUT_CLASS: + dip->mixer_class = ESS_INPUT_CLASS; + dip->next = dip->prev = AUDIO_MIXER_LAST; + strcpy(dip->label.name, AudioCinputs); + dip->type = AUDIO_MIXER_CLASS; + return (0); + + case ESS_MASTER_VOL: + dip->mixer_class = ESS_OUTPUT_CLASS; + dip->next = dip->prev = AUDIO_MIXER_LAST; + strcpy(dip->label.name, AudioNmaster); + dip->type = AUDIO_MIXER_VALUE; + dip->un.v.num_channels = 2; + strcpy(dip->un.v.units.name, AudioNvolume); + return (0); + + case ESS_PCSPEAKER_VOL: + dip->mixer_class = ESS_OUTPUT_CLASS; + dip->next = dip->prev = AUDIO_MIXER_LAST; + strcpy(dip->label.name, "pc_speaker"); + dip->type = AUDIO_MIXER_VALUE; + dip->un.v.num_channels = 1; + strcpy(dip->un.v.units.name, AudioNvolume); + return (0); + + case ESS_OUTPUT_CLASS: + dip->mixer_class = ESS_OUTPUT_CLASS; + dip->next = dip->prev = AUDIO_MIXER_LAST; + strcpy(dip->label.name, AudioCoutputs); + dip->type = AUDIO_MIXER_CLASS; + return (0); + + case ESS_RECORD_VOL: + dip->mixer_class = ESS_RECORD_CLASS; + dip->next = dip->prev = AUDIO_MIXER_LAST; + strcpy(dip->label.name, AudioNrecord); + dip->type = AUDIO_MIXER_VALUE; + dip->un.v.num_channels = 2; + strcpy(dip->un.v.units.name, AudioNvolume); + return (0); + + case ESS_RECORD_SOURCE: + dip->mixer_class = ESS_RECORD_CLASS; + dip->next = dip->prev = AUDIO_MIXER_LAST; + strcpy(dip->label.name, AudioNsource); + if (sc->sc_model == ESS_1788) { + /* + * The 1788 doesn't use the input mixer control that + * the 1888 uses, because it's a pain when you only + * have one mixer. + * Perhaps it could be emulated by keeping both sets of + * gain values, and doing a `context switch' of the + * mixer registers when shifting from playing to + * recording. + */ + dip->type = AUDIO_MIXER_ENUM; + dip->un.e.num_mem = 4; + strcpy(dip->un.e.member[0].label.name, AudioNmicrophone); + dip->un.e.member[0].ord = ESS_SOURCE_MIC; + strcpy(dip->un.e.member[1].label.name, AudioNline); + dip->un.e.member[1].ord = ESS_SOURCE_LINE; + strcpy(dip->un.e.member[2].label.name, AudioNcd); + dip->un.e.member[2].ord = ESS_SOURCE_CD; + strcpy(dip->un.e.member[3].label.name, AudioNmixerout); + dip->un.e.member[3].ord = ESS_SOURCE_MIXER; + } else { + dip->type = AUDIO_MIXER_SET; + dip->un.s.num_mem = 6; + strcpy(dip->un.s.member[0].label.name, AudioNdac); + dip->un.s.member[0].mask = 1 << ESS_DAC_REC_VOL; + strcpy(dip->un.s.member[1].label.name, AudioNmicrophone); + dip->un.s.member[1].mask = 1 << ESS_MIC_REC_VOL; + strcpy(dip->un.s.member[2].label.name, AudioNline); + dip->un.s.member[2].mask = 1 << ESS_LINE_REC_VOL; + strcpy(dip->un.s.member[3].label.name, AudioNfmsynth); + dip->un.s.member[3].mask = 1 << ESS_SYNTH_REC_VOL; + strcpy(dip->un.s.member[4].label.name, AudioNcd); + dip->un.s.member[4].mask = 1 << ESS_CD_REC_VOL; + strcpy(dip->un.s.member[5].label.name, "auxb"); + dip->un.s.member[5].mask = 1 << ESS_AUXB_REC_VOL; + } + return (0); + + case ESS_RECORD_CLASS: + dip->mixer_class = ESS_RECORD_CLASS; + dip->next = dip->prev = AUDIO_MIXER_LAST; + strcpy(dip->label.name, AudioCrecord); + dip->type = AUDIO_MIXER_CLASS; + return (0); + + case ESS_RECORD_MONITOR: + dip->prev = dip->next = AUDIO_MIXER_LAST; + strcpy(dip->label.name, AudioNmonitor); + dip->type = AUDIO_MIXER_ENUM; + dip->mixer_class = ESS_MONITOR_CLASS; + dip->un.e.num_mem = 2; + strcpy(dip->un.e.member[0].label.name, AudioNoff); + dip->un.e.member[0].ord = 0; + strcpy(dip->un.e.member[1].label.name, AudioNon); + dip->un.e.member[1].ord = 1; + return (0); + + case ESS_MONITOR_CLASS: + dip->mixer_class = ESS_MONITOR_CLASS; + dip->next = dip->prev = AUDIO_MIXER_LAST; + strcpy(dip->label.name, AudioCmonitor); + dip->type = AUDIO_MIXER_CLASS; + return (0); + } + + if (sc->sc_model == ESS_1788) + return (ENXIO); + + switch (dip->index) { + case ESS_DAC_REC_VOL: + dip->mixer_class = ESS_RECORD_CLASS; + dip->next = dip->prev = AUDIO_MIXER_LAST; + strcpy(dip->label.name, AudioNdac); + dip->type = AUDIO_MIXER_VALUE; + dip->un.v.num_channels = 2; + strcpy(dip->un.v.units.name, AudioNvolume); + return (0); + + case ESS_MIC_REC_VOL: + dip->mixer_class = ESS_RECORD_CLASS; + dip->next = dip->prev = AUDIO_MIXER_LAST; + strcpy(dip->label.name, AudioNmicrophone); + dip->type = AUDIO_MIXER_VALUE; + dip->un.v.num_channels = 2; + strcpy(dip->un.v.units.name, AudioNvolume); + return (0); + + case ESS_LINE_REC_VOL: + dip->mixer_class = ESS_RECORD_CLASS; + dip->next = dip->prev = AUDIO_MIXER_LAST; + strcpy(dip->label.name, AudioNline); + dip->type = AUDIO_MIXER_VALUE; + dip->un.v.num_channels = 2; + strcpy(dip->un.v.units.name, AudioNvolume); + return (0); + + case ESS_SYNTH_REC_VOL: + dip->mixer_class = ESS_RECORD_CLASS; + dip->next = dip->prev = AUDIO_MIXER_LAST; + strcpy(dip->label.name, AudioNfmsynth); + dip->type = AUDIO_MIXER_VALUE; + dip->un.v.num_channels = 2; + strcpy(dip->un.v.units.name, AudioNvolume); + return (0); + + case ESS_CD_REC_VOL: + dip->mixer_class = ESS_RECORD_CLASS; + dip->next = dip->prev = AUDIO_MIXER_LAST; + strcpy(dip->label.name, AudioNcd); + dip->type = AUDIO_MIXER_VALUE; + dip->un.v.num_channels = 2; + strcpy(dip->un.v.units.name, AudioNvolume); + return (0); + + case ESS_AUXB_REC_VOL: + dip->mixer_class = ESS_RECORD_CLASS; + dip->next = dip->prev = AUDIO_MIXER_LAST; + strcpy(dip->label.name, "auxb"); + dip->type = AUDIO_MIXER_VALUE; + dip->un.v.num_channels = 2; + strcpy(dip->un.v.units.name, AudioNvolume); + return (0); + + case ESS_MIC_PREAMP: + dip->mixer_class = ESS_INPUT_CLASS; + dip->prev = ESS_MIC_PLAY_VOL; + dip->next = AUDIO_MIXER_LAST; + strcpy(dip->label.name, AudioNpreamp); + dip->type = AUDIO_MIXER_ENUM; + dip->un.e.num_mem = 2; + strcpy(dip->un.e.member[0].label.name, AudioNoff); + dip->un.e.member[0].ord = 0; + strcpy(dip->un.e.member[1].label.name, AudioNon); + dip->un.e.member[1].ord = 1; + return (0); + } + + return (ENXIO); +} + +void * +ess_malloc(addr, size, pool, flags) + void *addr; + unsigned long size; + int pool, flags; +{ + struct ess_softc *sc = addr; + int drq; + + if (sc->sc_model != ESS_1788) + drq = sc->sc_audio2.drq; + else + drq = sc->sc_audio1.drq; + return (isa_malloc(sc->sc_isa, drq, size, pool, flags)); +} + +void +ess_free(addr, ptr, pool) + void *addr; + void *ptr; + int pool; +{ + isa_free(ptr, pool); +} + +unsigned long +ess_round_buffersize(addr, size) + void *addr; + unsigned long size; +{ + if (size > MAX_ISADMA) + size = MAX_ISADMA; + return (size); +} + +int +ess_mappage(addr, mem, off, prot) + void *addr; + void *mem; + int off; + int prot; +{ + return (isa_mappage(mem, off, prot)); +} + +int +ess_1788_get_props(addr) + void *addr; +{ + + return (AUDIO_PROP_MMAP | AUDIO_PROP_INDEPENDENT); +} + +int +ess_1888_get_props(addr) + void *addr; +{ + + return (AUDIO_PROP_MMAP | AUDIO_PROP_INDEPENDENT | AUDIO_PROP_FULLDUPLEX); +} + +/* ============================================ + * Generic functions for ess, not used by audio h/w i/f + * ============================================= + */ + +/* + * Reset the chip. + * Return non-zero if the chip isn't detected. + */ +int +ess_reset(sc) + struct ess_softc *sc; +{ + bus_space_tag_t iot = sc->sc_iot; + bus_space_handle_t ioh = sc->sc_ioh; + + sc->sc_audio1.active = 0; + sc->sc_audio2.active = 0; + + EWRITE1(iot, ioh, ESS_DSP_RESET, ESS_RESET_EXT); + delay(10000); + EWRITE1(iot, ioh, ESS_DSP_RESET, 0); + if (ess_rdsp(sc) != ESS_MAGIC) + return (1); + + /* Enable access to the ESS extension commands. */ + ess_wdsp(sc, ESS_ACMD_ENABLE_EXT); + + return (0); +} + +void +ess_set_gain(sc, port, on) + struct ess_softc *sc; + int port; + int on; +{ + int gain, left, right; + int mix; + int src; + int stereo; + + /* + * Most gain controls are found in the mixer registers and + * are stereo. Any that are not, must set mix and stereo as + * required. + */ + mix = 1; + stereo = 1; + + switch (port) { + case ESS_MASTER_VOL: + src = ESS_MREG_VOLUME_MASTER; + break; + case ESS_DAC_PLAY_VOL: + if (sc->sc_model == ESS_1788) + src = ESS_MREG_VOLUME_VOICE; + else + src = 0x7C; + break; + case ESS_MIC_PLAY_VOL: + src = ESS_MREG_VOLUME_MIC; + break; + case ESS_LINE_PLAY_VOL: + src = ESS_MREG_VOLUME_LINE; + break; + case ESS_SYNTH_PLAY_VOL: + src = ESS_MREG_VOLUME_SYNTH; + break; + case ESS_CD_PLAY_VOL: + src = ESS_MREG_VOLUME_CD; + break; + case ESS_AUXB_PLAY_VOL: + src = ESS_MREG_VOLUME_AUXB; + break; + case ESS_PCSPEAKER_VOL: + src = ESS_MREG_VOLUME_PCSPKR; + stereo = 0; + break; + case ESS_DAC_REC_VOL: + src = 0x69; + break; + case ESS_MIC_REC_VOL: + src = 0x68; + break; + case ESS_LINE_REC_VOL: + src = 0x6E; + break; + case ESS_SYNTH_REC_VOL: + src = 0x6B; + break; + case ESS_CD_REC_VOL: + src = 0x6A; + break; + case ESS_AUXB_REC_VOL: + src = 0x6C; + break; + case ESS_RECORD_VOL: + src = ESS_XCMD_VOLIN_CTRL; + mix = 0; + break; + default: + return; + } + + /* 1788 doesn't have a separate recording mixer */ + if (sc->sc_model == ESS_1788 && mix && src > 0x62) + return; + + if (on) { + left = sc->gain[port][ESS_LEFT]; + right = sc->gain[port][ESS_RIGHT]; + } else { + left = right = 0; + } + + if (stereo) + gain = ESS_STEREO_GAIN(left, right); + else + gain = ESS_MONO_GAIN(left); + + if (mix) + ess_write_mix_reg(sc, src, gain); + else + ess_write_x_reg(sc, src, gain); +} + +/* Set the input device on devices without an input mixer. */ +int +ess_set_in_port(sc, ord) + struct ess_softc *sc; + int ord; +{ + mixer_devinfo_t di; + int i; + + DPRINTF(("ess_set_in_port: ord=0x%x\n", ord)); + + /* + * Get the device info for the record source control, + * including the list of available sources. + */ + di.index = ESS_RECORD_SOURCE; + if (ess_query_devinfo(sc, &di)) + return EINVAL; + + /* See if the given ord value was anywhere in the list. */ + for (i = 0; i < di.un.e.num_mem; i++) { + if (ord == di.un.e.member[i].ord) + break; + } + if (i == di.un.e.num_mem) + return EINVAL; + + ess_write_mix_reg(sc, ESS_MREG_ADC_SOURCE, ord); + + sc->in_port = ord; + return (0); +} + +/* Set the input device levels on input-mixer-enabled devices. */ +int +ess_set_in_ports(sc, mask) + struct ess_softc *sc; + int mask; +{ + mixer_devinfo_t di; + int i, port; + + DPRINTF(("ess_set_in_ports: mask=0x%x\n", mask)); + + /* + * Get the device info for the record source control, + * including the list of available sources. + */ + di.index = ESS_RECORD_SOURCE; + if (ess_query_devinfo(sc, &di)) + return EINVAL; + + /* + * Set or disable the record volume control for each of the + * possible sources. + */ + for (i = 0; i < di.un.s.num_mem; i++) { + /* + * Calculate the source port number from its mask. + */ + port = ffs(di.un.s.member[i].mask); + + /* + * Set the source gain: + * to the current value if source is enabled + * to zero if source is disabled + */ + ess_set_gain(sc, port, mask & di.un.s.member[i].mask); + } + + sc->in_mask = mask; + return (0); +} + +void +ess_speaker_on(sc) + struct ess_softc *sc; +{ + /* Disable mute on left- and right-master volume. */ + ess_clear_mreg_bits(sc, ESS_MREG_VOLUME_LEFT, ESS_VOLUME_MUTE); + ess_clear_mreg_bits(sc, ESS_MREG_VOLUME_RIGHT, ESS_VOLUME_MUTE); +} + +void +ess_speaker_off(sc) + struct ess_softc *sc; +{ + /* Enable mute on left- and right-master volume. */ + ess_set_mreg_bits(sc, ESS_MREG_VOLUME_LEFT, ESS_VOLUME_MUTE); + ess_set_mreg_bits(sc, ESS_MREG_VOLUME_RIGHT, ESS_VOLUME_MUTE); +} + +/* + * Calculate the time constant for the requested sampling rate. + */ +u_int +ess_srtotc(rate) + u_int rate; +{ + u_int tc; + + /* The following formulae 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 reuqested sampling rate. + */ +u_int +ess_srtofc(rate) + u_int rate; +{ + /* + * The following formula is derived from the information in + * the ES1887 data sheet, based on a roll-off frequency of + * 87%. + */ + return (256 - 200279L / rate); +} + + +/* + * Return the status of the DSP. + */ +u_char +ess_get_dsp_status(sc) + struct ess_softc *sc; +{ + return (EREAD1(sc->sc_iot, sc->sc_ioh, ESS_DSP_RW_STATUS)); +} + + +/* + * Return the read status of the DSP: 1 -> DSP ready for reading + * 0 -> DSP not ready for reading + */ +u_char +ess_dsp_read_ready(sc) + struct ess_softc *sc; +{ + return ((ess_get_dsp_status(sc) & ESS_DSP_READ_READY) ? 1 : 0); +} + + +/* + * Return the write status of the DSP: 1 -> DSP ready for writing + * 0 -> DSP not ready for writing + */ +u_char +ess_dsp_write_ready(sc) + struct ess_softc *sc; +{ + return ((ess_get_dsp_status(sc) & ESS_DSP_WRITE_BUSY) ? 0 : 1); +} + + +/* + * Read a byte from the DSP. + */ +int +ess_rdsp(sc) + struct ess_softc *sc; +{ + bus_space_tag_t iot = sc->sc_iot; + bus_space_handle_t ioh = sc->sc_ioh; + int i; + + for (i = ESS_READ_TIMEOUT; i > 0; --i) { + if (ess_dsp_read_ready(sc)) { + i = EREAD1(iot, ioh, ESS_DSP_READ); + DPRINTFN(8,("ess_rdsp() = 0x%02x\n", i)); + return i; + } else + delay(10); + } + + DPRINTF(("ess_rdsp: timed out\n")); + return (-1); +} + +/* + * Write a byte to the DSP. + */ +int +ess_wdsp(sc, v) + struct ess_softc *sc; + u_char v; +{ + bus_space_tag_t iot = sc->sc_iot; + bus_space_handle_t ioh = sc->sc_ioh; + int i; + + DPRINTFN(8,("ess_wdsp(0x%02x)\n", v)); + + for (i = ESS_WRITE_TIMEOUT; i > 0; --i) { + if (ess_dsp_write_ready(sc)) { + EWRITE1(iot, ioh, ESS_DSP_WRITE, v); + return (0); + } else + delay(10); + } + + DPRINTF(("ess_wdsp(0x%02x): timed out\n", v)); + return (-1); +} + +/* + * Write a value to one of the ESS extended registers. + */ +int +ess_write_x_reg(sc, reg, val) + struct ess_softc *sc; + u_char reg; + u_char val; +{ + int error; + + DPRINTFN(2,("ess_write_x_reg: %02x=%02x\n", reg, val)); + if ((error = ess_wdsp(sc, reg)) == 0) + error = ess_wdsp(sc, val); + + return error; +} + +/* + * Read the value of one of the ESS extended registers. + */ +u_char +ess_read_x_reg(sc, reg) + struct ess_softc *sc; + u_char reg; +{ + int error; + int val; + + if ((error = ess_wdsp(sc, 0xC0)) == 0) + error = ess_wdsp(sc, reg); + if (error) + DPRINTF(("Error reading extended register 0x%02x\n", reg)); +/* REVISIT: what if an error is returned above? */ + val = ess_rdsp(sc); + DPRINTFN(2,("ess_read_x_reg: %02x=%02x\n", reg, val)); + return val; +} + +void +ess_clear_xreg_bits(sc, reg, mask) + struct ess_softc *sc; + u_char reg; + u_char mask; +{ + if (ess_write_x_reg(sc, reg, ess_read_x_reg(sc, reg) & ~mask) == -1) + DPRINTF(("Error clearing bits in extended register 0x%02x\n", + reg)); +} + +void +ess_set_xreg_bits(sc, reg, mask) + struct ess_softc *sc; + u_char reg; + u_char mask; +{ + if (ess_write_x_reg(sc, reg, ess_read_x_reg(sc, reg) | mask) == -1) + DPRINTF(("Error setting bits in extended register 0x%02x\n", + reg)); +} + + +/* + * Write a value to one of the ESS mixer registers. + */ +void +ess_write_mix_reg(sc, reg, val) + struct ess_softc *sc; + u_char reg; + u_char val; +{ + bus_space_tag_t iot = sc->sc_iot; + bus_space_handle_t ioh = sc->sc_ioh; + int s; + + DPRINTFN(2,("ess_write_mix_reg: %x=%x\n", reg, val)); + + s = splaudio(); + EWRITE1(iot, ioh, ESS_MIX_REG_SELECT, reg); + EWRITE1(iot, ioh, ESS_MIX_REG_DATA, val); + splx(s); +} + +/* + * Read the value of one of the ESS mixer registers. + */ +u_char +ess_read_mix_reg(sc, reg) + struct ess_softc *sc; + u_char reg; +{ + bus_space_tag_t iot = sc->sc_iot; + bus_space_handle_t ioh = sc->sc_ioh; + int s; + u_char val; + + s = splaudio(); + EWRITE1(iot, ioh, ESS_MIX_REG_SELECT, reg); + val = EREAD1(iot, ioh, ESS_MIX_REG_DATA); + splx(s); + + DPRINTFN(2,("ess_read_mix_reg: %x=%x\n", reg, val)); + return val; +} + +void +ess_clear_mreg_bits(sc, reg, mask) + struct ess_softc *sc; + u_char reg; + u_char mask; +{ + ess_write_mix_reg(sc, reg, ess_read_mix_reg(sc, reg) & ~mask); +} + +void +ess_set_mreg_bits(sc, reg, mask) + struct ess_softc *sc; + u_char reg; + u_char mask; +{ + ess_write_mix_reg(sc, reg, ess_read_mix_reg(sc, reg) | mask); +} diff --git a/sys/dev/isa/ess_isapnp.c b/sys/dev/isa/ess_isapnp.c new file mode 100644 index 00000000000..5e1179df724 --- /dev/null +++ b/sys/dev/isa/ess_isapnp.c @@ -0,0 +1,105 @@ +/* $OpenBSD: ess_isapnp.c,v 1.1 1999/06/22 16:20:03 niklas Exp $ */ +/* $NetBSD: ess_isa.c,v 1.4 1999/03/18 20:57:11 mycroft Exp $ */ + +/*- + * Copyright (c) 1999 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Nathan J. Williams. + * + * 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 the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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 <machine/cpu.h> +#include <machine/bus.h> + +#include <dev/isa/isavar.h> + +#include <dev/isa/essreg.h> +#include <dev/isa/essvar.h> + +#ifdef ESS_ISAPNP_DEBUG +#define DPRINTF(x) printf x +#else +#define DPRINTF(x) +#endif + +int ess_isapnp_probe __P((struct device *, void *, void *)); +void ess_isapnp_attach __P((struct device *, struct device *, void *)); + +struct cfattach ess_isapnp_ca = { + sizeof(struct ess_softc), ess_isapnp_probe, ess_isapnp_attach +}; + +int +ess_isapnp_probe(parent, match, aux) + struct device *parent; + void *match, *aux; +{ + return 1; +} + +void ess_isapnp_attach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct ess_softc *sc = (void *)self; + struct isa_attach_args *ia = aux; + + printf("\n"); + + sc->sc_ic = ia->ia_ic; + sc->sc_iot = ia->ia_iot; + sc->sc_ioh = ia->ipa_io[0].h; + sc->sc_iobase = ia->ipa_io[0].base; + + sc->sc_audio1.irq = ia->ipa_irq[0].num; + sc->sc_audio1.ist = ia->ipa_irq[0].type; + sc->sc_audio1.drq = ia->ipa_drq[0].num; + sc->sc_audio2.irq = ia->ipa_irq[0].num; + sc->sc_audio2.ist = ia->ipa_irq[0].type; + sc->sc_audio2.drq = ia->ipa_drq[1].num; + + sc->sc_isa = parent->dv_parent; + + if (!essmatch(sc)) { + printf("%s: essmatch failed\n", sc->sc_dev.dv_xname); + return; + } + + printf("%s:", sc->sc_dev.dv_xname); + + essattach(sc); +} diff --git a/sys/dev/isa/essreg.h b/sys/dev/isa/essreg.h new file mode 100644 index 00000000000..77ac01b2919 --- /dev/null +++ b/sys/dev/isa/essreg.h @@ -0,0 +1,306 @@ +/* $OpenBSD: essreg.h,v 1.1 1999/06/22 16:20:03 niklas Exp $ */ +/* $NetBSD: essreg.h,v 1.11 1999/03/19 12:40:21 mycroft 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: essreg.h,v $ $Revision: 1.1 $ (SHARK) $Date: 1999/06/22 16:20:03 $ +** +**++ +** +** 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_BASE_VALID(base) ((base) == 0x220 || (base) == 0x230 || (base) == 0x240 || (base) == 0x250) + +/* + * 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_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/isa/essvar.h b/sys/dev/isa/essvar.h new file mode 100644 index 00000000000..d48b129302f --- /dev/null +++ b/sys/dev/isa/essvar.h @@ -0,0 +1,159 @@ +/* $OpenBSD: essvar.h,v 1.1 1999/06/22 16:20:03 niklas Exp $ */ +/* $NetBSD: essvar.h,v 1.14 1999/03/18 06:03:31 mycroft 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: essvar.h,v $ $Revision: 1.1 $ (SHARK) $Date: 1999/06/22 16:20:03 $ +** +**++ +** +** essvar.h +** +** FACILITY: +** +** DIGITAL Network Appliance Reference Design (DNARD) +** +** MODULE DESCRIPTION: +** +** This module contains the structure definitions and function +** prototypes for the ESS Technologies 1887/888 sound chip +** driver. +** +** AUTHORS: +** +** Blair Fidler Software Engineering Australia +** Gold Coast, Australia. +** +** CREATION DATE: +** +** May 12, 1997. +** +** MODIFICATION HISTORY: +** +**-- +*/ +#define ESS_DAC_PLAY_VOL 0 +#define ESS_MIC_PLAY_VOL 1 +#define ESS_LINE_PLAY_VOL 2 +#define ESS_SYNTH_PLAY_VOL 3 +#define ESS_CD_PLAY_VOL 4 +#define ESS_AUXB_PLAY_VOL 5 +#define ESS_INPUT_CLASS 6 + +#define ESS_MASTER_VOL 7 +#define ESS_PCSPEAKER_VOL 8 +#define ESS_OUTPUT_CLASS 9 + +#define ESS_RECORD_MONITOR 10 +#define ESS_MONITOR_CLASS 11 + +#define ESS_RECORD_VOL 12 +#define ESS_RECORD_SOURCE 13 +#define ESS_RECORD_CLASS 14 + +#define ESS_1788_NDEVS 15 + +#define ESS_DAC_REC_VOL 15 +#define ESS_MIC_REC_VOL 16 +#define ESS_LINE_REC_VOL 17 +#define ESS_SYNTH_REC_VOL 18 +#define ESS_CD_REC_VOL 19 +#define ESS_AUXB_REC_VOL 20 +#define ESS_MIC_PREAMP 21 + +#define ESS_1888_NDEVS 22 +#define ESS_MAX_NDEVS 22 + +struct ess_audio_channel +{ + int drq; /* DMA channel */ +#define IS16BITDRQ(drq) ((drq) >= 4) + int irq; /* IRQ line for this DMA channel */ + int ist; + void *ih; /* interrupt vectoring */ + u_long nintr; /* number of interrupts taken */ + void (*intr)__P((void*)); /* ISR for DMA complete */ + void *arg; /* arg for intr() */ + + /* Status information */ + int active; /* boolean: channel in use? */ + + /* Polling state */ + int polled; /* 1 if channel is polled */ + int dmapos; /* last DMA pointer */ + int buffersize; /* size of DMA buffer */ + /* (The following is only needed due to the stupid block interface.) */ + int dmacount; /* leftover partial block */ + int blksize; /* current block size */ +}; + +struct ess_softc +{ + struct device sc_dev; /* base device */ + struct device *sc_isa; + isa_chipset_tag_t sc_ic; + bus_space_tag_t sc_iot; /* tag */ + bus_space_handle_t sc_ioh; /* handle */ + + int sc_iobase; /* I/O port base address */ + + u_short sc_open; /* reference count of open calls */ + + int ndevs; + u_char gain[ESS_MAX_NDEVS][2]; /* kept in input levels */ +#define ESS_LEFT 0 +#define ESS_RIGHT 1 + + u_int out_port; /* output port */ + u_int in_mask; /* input ports */ + u_int in_port; /* XXX needed for MI interface */ + + u_int spkr_state; /* non-null is on */ + + struct ess_audio_channel sc_audio1; /* audio channel for record */ + struct ess_audio_channel sc_audio2; /* audio channel for playback */ + + u_int sc_model; +#define ESS_UNSUPPORTED 0 +#define ESS_1888 1 +#define ESS_1887 2 +#define ESS_888 3 +#define ESS_1788 4 + + u_int sc_version; /* Legacy ES688/ES1688 ID */ +}; + +int essmatch __P((struct ess_softc *)); +void essattach __P((struct ess_softc *)); + diff --git a/sys/dev/isa/files.isa b/sys/dev/isa/files.isa index 415610fd485..bed6e32209b 100644 --- a/sys/dev/isa/files.isa +++ b/sys/dev/isa/files.isa @@ -1,4 +1,4 @@ -# $OpenBSD: files.isa,v 1.52 1999/03/02 08:12:44 deraadt Exp $ +# $OpenBSD: files.isa,v 1.53 1999/06/22 16:20:04 niklas Exp $ # $NetBSD: files.isa,v 1.21 1996/05/16 03:45:55 mycroft Exp $ # # Config file and device description for machine-independent ISA code. @@ -283,6 +283,13 @@ file dev/isa/wss.c wss needs-flag attach wss at isa with wss_isa file dev/isa/wss_isa.c wss & (wss_isa | wss_isapnp) needs-flag +# ESS Technology ES1887/ES888/ES1888 +device ess { } : audio, isa_dma, mulaw, auconv, midibus +file dev/isa/ess.c ess needs-flag + +attach opl at ess with opl_ess +file dev/isa/opl_ess.c opl_ess + # Gravis UltraSound & UltraSound MAX. # Use the "flags" keyword in a config file to specify an extra DMA # channel for full-duplex operation. diff --git a/sys/dev/isa/files.isapnp b/sys/dev/isa/files.isapnp index 034f0cc571c..75cfa74129c 100644 --- a/sys/dev/isa/files.isapnp +++ b/sys/dev/isa/files.isapnp @@ -1,4 +1,4 @@ -# $OpenBSD: files.isapnp,v 1.14 1999/03/04 22:14:35 deraadt Exp $ +# $OpenBSD: files.isapnp,v 1.15 1999/06/22 16:20:03 niklas Exp $ # $NetBSD: files.isapnp,v 1.7 1997/10/16 17:16:36 matt Exp $ # # Config file and device description for machine-independent ISAPnP code. @@ -28,6 +28,9 @@ file dev/isa/sb_isapnp.c sb & (sb_isa | sb_isapnp) needs-flag attach wss at isapnp with wss_isapnp file dev/isa/wss_isapnp.c wss & (wss_isa | wss_isapnp) needs-flag +attach ess at isapnp with ess_isapnp +file dev/isa/ess_isapnp.c ess & ess_isapnp needs-flag + attach le at isapnp with le_isapnp file dev/isa/if_le_isapnp.c le_isapnp diff --git a/sys/dev/isa/opl_ess.c b/sys/dev/isa/opl_ess.c new file mode 100644 index 00000000000..9dc327fb938 --- /dev/null +++ b/sys/dev/isa/opl_ess.c @@ -0,0 +1,105 @@ +/* $OpenBSD: opl_ess.c,v 1.1 1999/06/22 16:20:04 niklas Exp $ */ +/* $NetBSD: opl_ess.c,v 1.3 1998/12/08 14:26:57 augustss Exp $ */ + +/* + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Author: Lennart Augustsson + * + * 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 the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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/isa/isavar.h> +#include <dev/isa/essvar.h> + +extern int ess_speaker_ctl __P((void *, int)); + +int opl_ess_match __P((struct device *, void *, void *)); +void opl_ess_attach __P((struct device *, struct device *, void *)); + +struct cfattach opl_ess_ca = { + sizeof (struct opl_softc), opl_ess_match, opl_ess_attach +}; + +int +opl_ess_match(parent, match, aux) + struct device *parent; + void *match; + void *aux; +{ + struct audio_attach_args *aa = (struct audio_attach_args *)aux; + struct ess_softc *ssc = (struct ess_softc *)parent; + struct opl_softc sc; + + if (aa->type != AUDIODEV_TYPE_OPL) + return (0); + memset(&sc, 0, sizeof sc); + sc.ioh = ssc->sc_ioh; + sc.iot = ssc->sc_iot; + return (opl_find(&sc)); +} + +void +opl_ess_attach(parent, self, aux) + struct device *parent; + struct device *self; + void *aux; +{ + struct ess_softc *ssc = (struct ess_softc *)parent; + struct opl_softc *sc = (struct opl_softc *)self; + + sc->ioh = ssc->sc_ioh; + sc->iot = ssc->sc_iot; + sc->offs = 0; + sc->spkrctl = ess_speaker_ctl; + sc->spkrarg = ssc; + strcpy(sc->syn.name, "ESS "); + + opl_attach(sc); +} diff --git a/sys/dev/isa/pnpdevs b/sys/dev/isa/pnpdevs index 755228fab14..d86cec56009 100644 --- a/sys/dev/isa/pnpdevs +++ b/sys/dev/isa/pnpdevs @@ -1,4 +1,4 @@ -# $OpenBSD: pnpdevs,v 1.73 1999/06/16 06:57:51 deraadt Exp $ +# $OpenBSD: pnpdevs,v 1.74 1999/06/22 16:20:04 niklas Exp $ # # NOTE: All `com' devices also need pccom identifiers. @@ -444,9 +444,9 @@ sb CTL0042 # SB AWE64 Value sb CTL0043 # Creative ViBRA16X PnP sb CTL0044 # SB AWE64 Gold sb CTL0045 # SB AWE64 Value -sb ESS1868 # ESS ES1868 Plug and Play AudioD -sb ESS1869 # ESS ES1869 Plug and Play AudioD -sb ESS1878 # ESS ES1878 Plug and Play AudioD +ess ESS1868 # ESS ES1868 Plug and Play AudioD +ess ESS1869 # ESS ES1869 Plug and Play AudioD +ess ESS1878 # ESS ES1878 Plug and Play AudioD wss OPT9310 # OPTi Audio 16 (possibly a wss instead of sb) ym @@@1001 # ALS100+ ym YMH0021 # OPL3-SA2 Sound Board |