From: millert Date: Thu, 27 Apr 2000 02:19:41 +0000 (+0000) Subject: Driver for C-Media CMI8x38 Audio Chip present on several all-in-one X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=7cdebd38b3fb66f9f1a8a98f53d0400c09cf03a2;p=openbsd Driver for C-Media CMI8x38 Audio Chip present on several all-in-one motherboards (ie: bultin audio + video). Written by Takuya SHIOZAKI. --- diff --git a/sys/arch/i386/conf/GENERIC b/sys/arch/i386/conf/GENERIC index 15fa27557b5..4c5ad6554f3 100644 --- a/sys/arch/i386/conf/GENERIC +++ b/sys/arch/i386/conf/GENERIC @@ -1,4 +1,4 @@ -# $OpenBSD: GENERIC,v 1.182 2000/04/16 14:10:19 aaron Exp $ +# $OpenBSD: GENERIC,v 1.183 2000/04/27 02:19:42 millert Exp $ # $NetBSD: GENERIC,v 1.48 1996/05/20 18:17:23 mrg Exp $ # # GENERIC -- everything that's currently supported @@ -314,6 +314,7 @@ eap* at pci? dev ? function ? # Ensoniq AudioPCI S5016 eso* at pci? dev ? function ? # ESS Solo-1 PCI AudioDrive sv* at pci? dev ? function ? # S3 SonicVibes (S3 617) neo* at pci? dev ? function ? # NeoMagic 256AV/ZX +cmpci* at pci? dev ? function ? # C-Media CMI8338/8738 sb0 at isa? port 0x220 irq 5 drq 1 # SoundBlaster sb* at isapnp? ess* at isapnp? # ESS Tech ES188[78], ES888 @@ -352,6 +353,7 @@ audio* at eap? audio* at eso? audio* at sv? audio* at neo? +audio* at cmpci? #audio* at uaudio? bktr0 at pci? dev ? function ? diff --git a/sys/dev/pci/cmpci.c b/sys/dev/pci/cmpci.c new file mode 100644 index 00000000000..9ba82a8034e --- /dev/null +++ b/sys/dev/pci/cmpci.c @@ -0,0 +1,1577 @@ +/* $OpenBSD: cmpci.c,v 1.1 2000/04/27 02:19:41 millert Exp $ */ + +/* + * Copyright (c) 2000 Takuya SHIOZAKI + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +/* + * C-Media CMI8x38 Audio Chip Support. + * + * TODO: + * - Legacy MPU, OPL and Joystick support (but, I have no interest...) + * - SPDIF support + * + */ + +#undef CMPCI_SPDIF_SUPPORT /* XXX: not working */ + +#if defined(AUDIO_DEBUG) || defined(DEBUG) +#define DPRINTF(x) printf x +#else +#define DPRINTF(x) +#endif + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include + +/* + * Low-level HW interface + */ +static __inline uint8_t cmpci_mixerreg_read __P((struct cmpci_softc *, + uint8_t)); +static __inline void cmpci_mixerreg_write __P((struct cmpci_softc *, + uint8_t, uint8_t)); +static __inline void cmpci_reg_partial_write_4 __P((struct cmpci_softc *, + int, int, + uint32_t, uint32_t)); +static __inline void cmpci_reg_set_4 __P((struct cmpci_softc *, + int, uint32_t)); +static __inline void cmpci_reg_clear_4 __P((struct cmpci_softc *, + int, uint32_t)); +static int cmpci_rate_to_index __P((int)); +static __inline int cmpci_index_to_rate __P((int)); +static __inline int cmpci_index_to_divider __P((int)); + +static int cmpci_adjust __P((int, int)); +static void cmpci_set_mixer_gain __P((struct cmpci_softc *, int)); +static int cmpci_set_in_ports __P((struct cmpci_softc *, int)); + + +/* + * autoconf interface + */ +int cmpci_match __P((struct device *, void *, void *)); +void cmpci_attach __P((struct device *, struct device *, void *)); + +struct cfdriver cmpci_cd = { + NULL, "cmpci", DV_DULL +}; + +struct cfattach cmpci_ca = { + sizeof (struct cmpci_softc), cmpci_match, cmpci_attach +}; + +struct audio_device cmpci_device = { + "CMI PCI Audio", + "", + "cmpci" +}; + +/* interrupt */ +int cmpci_intr __P((void *)); + + +/* + * DMA stuff + */ +int cmpci_alloc_dmamem __P((struct cmpci_softc *, + size_t, int, int, caddr_t *)); +int cmpci_free_dmamem __P((struct cmpci_softc *, caddr_t, int)); +struct cmpci_dmanode * cmpci_find_dmamem __P((struct cmpci_softc *, + caddr_t)); + +/* + * Interface to machine independent layer + */ +int cmpci_open __P((void *, int)); +void cmpci_close __P((void *)); +int cmpci_query_encoding __P((void *, struct audio_encoding *)); +int cmpci_set_params __P((void *, int, int, + struct audio_params *, + struct audio_params *)); +int cmpci_round_blocksize __P((void *, int)); +int cmpci_halt_output __P((void *)); +int cmpci_halt_input __P((void *)); +int cmpci_getdev __P((void *, struct audio_device *)); +int cmpci_set_port __P((void *, mixer_ctrl_t *)); +int cmpci_get_port __P((void *, mixer_ctrl_t *)); +int cmpci_query_devinfo __P((void *, mixer_devinfo_t *)); +void *cmpci_malloc __P((void *, u_long, int, int)); +void cmpci_free __P((void *, void *, int)); +u_long cmpci_round_buffersize __P((void *, u_long)); +int cmpci_mappage __P((void *, void *, int, int)); +int cmpci_get_props __P((void *)); +int cmpci_trigger_output __P((void *, void *, void *, int, + void (*)(void *), void *, + struct audio_params *)); +int cmpci_trigger_input __P((void *, void *, void *, int, + void (*)(void *), void *, + struct audio_params *)); + +struct audio_hw_if cmpci_hw_if = { + cmpci_open, /* open */ + cmpci_close, /* close */ + NULL, /* drain */ + cmpci_query_encoding, /* query_encoding */ + cmpci_set_params, /* set_params */ + cmpci_round_blocksize, /* round_blocksize */ + NULL, /* commit_settings */ + NULL, /* init_output */ + NULL, /* init_input */ + NULL, /* start_output */ + NULL, /* start_input */ + cmpci_halt_output, /* halt_output */ + cmpci_halt_input, /* halt_input */ + NULL, /* speaker_ctl */ + cmpci_getdev, /* getdev */ + NULL, /* setfd */ + cmpci_set_port, /* set_port */ + cmpci_get_port, /* get_port */ + cmpci_query_devinfo, /* query_devinfo */ + cmpci_malloc, /* malloc */ + cmpci_free, /* free */ + cmpci_round_buffersize, /* round_buffersize */ + cmpci_mappage, /* mappage */ + cmpci_get_props, /* get_props */ + cmpci_trigger_output, /* trigger_output */ + cmpci_trigger_input /* trigger_input */ +}; + + +/* + * Low-level HW interface + */ + +/* mixer register read/write */ +static __inline uint8_t +cmpci_mixerreg_read(sc, no) + struct cmpci_softc *sc; + uint8_t no; +{ + uint8_t ret; + bus_space_write_1(sc->sc_iot, sc->sc_ioh, CMPCI_REG_SBADDR, no); + delay(10); + ret = bus_space_read_1(sc->sc_iot, sc->sc_ioh, CMPCI_REG_SBDATA); + delay(10); + return ret; +} + +static __inline void +cmpci_mixerreg_write(sc, no, val) + struct cmpci_softc *sc; + uint8_t no, val; +{ + bus_space_write_1(sc->sc_iot, sc->sc_ioh, CMPCI_REG_SBADDR, no); + delay(10); + bus_space_write_1(sc->sc_iot, sc->sc_ioh, CMPCI_REG_SBDATA, val); + delay(10); +} + +/* register partial write */ +static __inline void +cmpci_reg_partial_write_4(sc, no, shift, mask, val) + struct cmpci_softc *sc; + int no, shift; + uint32_t mask, val; +{ + bus_space_write_4(sc->sc_iot, sc->sc_ioh, no, + (val<sc_iot, sc->sc_ioh, no) & ~(mask<sc_iot, sc->sc_ioh, no, + (bus_space_read_4(sc->sc_iot, sc->sc_ioh, no) | mask)); + delay(10); +} + +static __inline void +cmpci_reg_clear_4(sc, no, mask) + struct cmpci_softc *sc; + int no; + uint32_t mask; +{ + bus_space_write_4(sc->sc_iot, sc->sc_ioh, no, + (bus_space_read_4(sc->sc_iot, sc->sc_ioh, no) & ~mask)); + delay(10); +} + + +/* rate */ +struct { + int rate; + int divider; +} cmpci_rate_table[CMPCI_REG_NUMRATE] = { +#define _RATE(n) { n, CMPCI_REG_RATE_ ## n } + _RATE(5512), + _RATE(8000), + _RATE(11025), + _RATE(16000), + _RATE(22050), + _RATE(32000), + _RATE(44100), + _RATE(48000) +#undef _RATE +}; + +int +cmpci_rate_to_index(rate) + int rate; +{ + int i; + for (i=0; ipa_id) == PCI_VENDOR_CMI && + (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_CMI_CMI8338A || + PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_CMI_CMI8338B || + PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_CMI_CMI8738)) + return 1; + + return 0; +} + +void +cmpci_attach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct cmpci_softc *sc = (struct cmpci_softc *)self; + struct pci_attach_args *pa = (struct pci_attach_args *)aux; + pci_intr_handle_t ih; + char const *intrstr; + int i, v; + + /* map I/O space */ + if (pci_mapreg_map(pa, CMPCI_PCI_IOBASEREG, PCI_MAPREG_TYPE_IO, 0, + &sc->sc_iot, &sc->sc_ioh, NULL, NULL)) { + printf("\n%s: failed to map I/O space\n", sc->sc_dev.dv_xname); + return; + } + + /* interrupt */ + if (pci_intr_map(pa->pa_pc, pa->pa_intrtag, pa->pa_intrpin, + pa->pa_intrline, &ih)) { + printf("\n%s: failed to map interrupt\n", sc->sc_dev.dv_xname); + return; + } + intrstr = pci_intr_string(pa->pa_pc, ih); + sc->sc_ih = pci_intr_establish(pa->pa_pc, ih, IPL_AUDIO, cmpci_intr, + sc, sc->sc_dev.dv_xname); + + if (sc->sc_ih == NULL) { + printf("\n%s: couldn't establish interrupt", + sc->sc_dev.dv_xname); + if (intrstr) + printf(" at %s", intrstr); + printf("\n"); + return; + } + printf(": %s\n", intrstr); + + sc->sc_dmat = pa->pa_dmat; + + audio_attach_mi(&cmpci_hw_if, sc, &sc->sc_dev); + + cmpci_mixerreg_write(sc, CMPCI_SB16_MIXER_RESET, 0); + cmpci_mixerreg_write(sc, CMPCI_SB16_MIXER_ADCMIX_L, 0); + cmpci_mixerreg_write(sc, CMPCI_SB16_MIXER_ADCMIX_R, 0); + cmpci_mixerreg_write(sc, CMPCI_SB16_MIXER_OUTMIX, + CMPCI_SB16_SW_CD|CMPCI_SB16_SW_MIC| + CMPCI_SB16_SW_LINE); + for (i = 0; i < CMPCI_NDEVS; i++) { + switch(i) { + case CMPCI_MIC_VOL: + case CMPCI_LINE_IN_VOL: + v = 0; + break; + case CMPCI_BASS: + case CMPCI_TREBLE: + v = CMPCI_ADJUST_GAIN(sc, AUDIO_MAX_GAIN / 2); + break; + case CMPCI_CD_IN_MUTE: + case CMPCI_MIC_IN_MUTE: + case CMPCI_LINE_IN_MUTE: + case CMPCI_FM_IN_MUTE: + case CMPCI_CD_SWAP: + case CMPCI_MIC_SWAP: + case CMPCI_LINE_SWAP: + case CMPCI_FM_SWAP: + v = 0; + break; + case CMPCI_CD_OUT_MUTE: + case CMPCI_MIC_OUT_MUTE: + case CMPCI_LINE_OUT_MUTE: + v = 1; + break; + default: + v = CMPCI_ADJUST_GAIN(sc, AUDIO_MAX_GAIN / 2); + } + sc->gain[i][CMPCI_LEFT] = sc->gain[i][CMPCI_RIGHT] = v; + cmpci_set_mixer_gain(sc, i); + } +} + +int +cmpci_intr(handle) + void *handle; +{ + struct cmpci_softc *sc = handle; + uint32_t intrstat; + int s; + + intrstat = bus_space_read_4(sc->sc_iot, sc->sc_ioh, + CMPCI_REG_INTR_STATUS); + delay(10); + + if (!(intrstat & CMPCI_REG_ANY_INTR)) + return 0; + + /* disable and reset intr */ + s = splaudio(); + if (intrstat & CMPCI_REG_CH0_INTR) + cmpci_reg_clear_4(sc, CMPCI_REG_INTR_CTRL, + CMPCI_REG_CH0_INTR_ENABLE); + if (intrstat&CMPCI_REG_CH1_INTR) + cmpci_reg_clear_4(sc, CMPCI_REG_INTR_CTRL, + CMPCI_REG_CH1_INTR_ENABLE); + splx(s); + + if (intrstat & CMPCI_REG_CH0_INTR) { + if (sc->sc_play.intr) + (*sc->sc_play.intr)(sc->sc_play.intr_arg); + } + if (intrstat & CMPCI_REG_CH1_INTR) { + if (sc->sc_rec.intr) + (*sc->sc_rec.intr)(sc->sc_rec.intr_arg); + } + + /* enable intr */ + s = splaudio(); + if ( intrstat & CMPCI_REG_CH0_INTR ) + cmpci_reg_set_4(sc, CMPCI_REG_INTR_CTRL, + CMPCI_REG_CH0_INTR_ENABLE); + if (intrstat & CMPCI_REG_CH1_INTR) + cmpci_reg_set_4(sc, CMPCI_REG_INTR_CTRL, + CMPCI_REG_CH1_INTR_ENABLE); + splx(s); + + return 0; +} + +/* open/close */ +int +cmpci_open(handle, flags) + void *handle; + int flags; +{ + struct cmpci_softc *sc = handle; + (void)sc; + (void)flags; + + return 0; +} + +void +cmpci_close(handle) + void *handle; +{ + (void)handle; +} + +int +cmpci_query_encoding(handle, fp) + void *handle; + struct audio_encoding *fp; +{ + struct cmpci_softc *sc = handle; + (void)sc; + + switch (fp->index) { + case 0: + strcpy(fp->name, AudioEulinear); + fp->encoding = AUDIO_ENCODING_ULINEAR; + fp->precision = 8; + fp->flags = AUDIO_ENCODINGFLAG_EMULATED; + break; + case 1: + strcpy(fp->name, AudioEmulaw); + fp->encoding = AUDIO_ENCODING_ULAW; + fp->precision = 8; + fp->flags = AUDIO_ENCODINGFLAG_EMULATED; + break; + case 2: + strcpy(fp->name, AudioEalaw); + fp->encoding = AUDIO_ENCODING_ALAW; + fp->precision = 8; + fp->flags = AUDIO_ENCODINGFLAG_EMULATED; + break; + case 3: + strcpy(fp->name, AudioEslinear); + fp->encoding = AUDIO_ENCODING_SLINEAR; + fp->precision = 8; + fp->flags = 0; + break; + case 4: + strcpy(fp->name, AudioEslinear_le); + fp->encoding = AUDIO_ENCODING_SLINEAR_LE; + fp->precision = 16; + fp->flags = 0; + break; + case 5: + strcpy(fp->name, AudioEulinear_le); + fp->encoding = AUDIO_ENCODING_ULINEAR_LE; + fp->precision = 16; + fp->flags = AUDIO_ENCODINGFLAG_EMULATED; + break; + case 6: + strcpy(fp->name, AudioEslinear_be); + fp->encoding = AUDIO_ENCODING_SLINEAR_BE; + fp->precision = 16; + fp->flags = AUDIO_ENCODINGFLAG_EMULATED; + break; + case 7: + strcpy(fp->name, AudioEulinear_be); + fp->encoding = AUDIO_ENCODING_ULINEAR_BE; + fp->precision = 16; + fp->flags = AUDIO_ENCODINGFLAG_EMULATED; + break; + default: + return EINVAL; + } + return 0; +} + + +int +cmpci_set_params(handle, setmode, usemode, play, rec) + void *handle; + int setmode, usemode; + struct audio_params *play, *rec; +{ + int i; + struct cmpci_softc *sc = handle; + + for (i=0; i<2; i++) { + int md_format; + int md_divide; + int md_index; + int mode; + struct audio_params *p; + + switch (i) { + case 0: + mode = AUMODE_PLAY; + p = play; + break; + case 1: + mode = AUMODE_RECORD; + p = rec; + break; + } + + if (!(setmode & mode)) + continue; + + /* format */ + p->sw_code = NULL; + switch (p->channels) { + case 1: + md_format = CMPCI_REG_FORMAT_MONO; + break; + case 2: + md_format = CMPCI_REG_FORMAT_STEREO; + break; + default: + return (EINVAL); + } + switch (p->encoding) { + case AUDIO_ENCODING_ULAW: + if (p->precision != 8) + return (EINVAL); + if (mode & AUMODE_PLAY) { + p->factor = 2; + p->sw_code = mulaw_to_slinear16; + md_format |= CMPCI_REG_FORMAT_16BIT; + } else + p->sw_code = ulinear8_to_mulaw; + md_format |= CMPCI_REG_FORMAT_8BIT; + break; + case AUDIO_ENCODING_ALAW: + if (p->precision != 8) + return (EINVAL); + if (mode & AUMODE_PLAY) { + p->factor = 2; + p->sw_code = alaw_to_slinear16; + md_format |= CMPCI_REG_FORMAT_16BIT; + } else + p->sw_code = ulinear8_to_alaw; + md_format |= CMPCI_REG_FORMAT_8BIT; + break; + case AUDIO_ENCODING_SLINEAR_LE: + switch (p->precision) { + case 8: + p->sw_code = change_sign8; + md_format |= CMPCI_REG_FORMAT_8BIT; + break; + case 16: + md_format |= CMPCI_REG_FORMAT_16BIT; + break; + default: + return (EINVAL); + } + break; + case AUDIO_ENCODING_SLINEAR_BE: + switch (p->precision) { + case 8: + md_format |= CMPCI_REG_FORMAT_8BIT; + p->sw_code = change_sign8; + break; + case 16: + md_format |= CMPCI_REG_FORMAT_16BIT; + p->sw_code = swap_bytes; + break; + default: + return (EINVAL); + } + break; + case AUDIO_ENCODING_ULINEAR_LE: + switch ( p->precision ) { + case 8: + md_format |= CMPCI_REG_FORMAT_8BIT; + break; + case 16: + md_format |= CMPCI_REG_FORMAT_16BIT; + p->sw_code = change_sign16; + break; + default: + return (EINVAL); + } + break; + case AUDIO_ENCODING_ULINEAR_BE: + switch (p->precision) { + case 8: + md_format |= CMPCI_REG_FORMAT_8BIT; + break; + case 16: + md_format |= CMPCI_REG_FORMAT_16BIT; + if ( mode&AUMODE_PLAY ) + p->sw_code = swap_bytes_change_sign16; + else + p->sw_code = change_sign16_swap_bytes; + break; + default: + return (EINVAL); + } + break; + default: + return (EINVAL); + } + if (mode & AUMODE_PLAY) + cmpci_reg_partial_write_4(sc, + CMPCI_REG_CHANNEL_FORMAT, + CMPCI_REG_CH0_FORMAT_SHIFT, + CMPCI_REG_CH0_FORMAT_MASK, + md_format); + else + cmpci_reg_partial_write_4(sc, + CMPCI_REG_CHANNEL_FORMAT, + CMPCI_REG_CH1_FORMAT_SHIFT, + CMPCI_REG_CH1_FORMAT_MASK, + md_format); + /* sample rate */ + md_index = cmpci_rate_to_index(p->sample_rate); + md_divide = cmpci_index_to_divider(md_index); + p->sample_rate = cmpci_index_to_rate(md_index); +#if 0 + DPRINTF(("%s: sample:%d, divider=%d\n", + sc->sc_dev.dv_xname, (int)p->sample_rate, md_divide)); +#endif + if (mode & AUMODE_PLAY) { + cmpci_reg_partial_write_4(sc, + CMPCI_REG_FUNC_1, + CMPCI_REG_DAC_FS_SHIFT, + CMPCI_REG_DAC_FS_MASK, + md_divide); +#ifdef CMPCI_SPDIF_SUPPORT + switch (md_divide) { + case CMPCI_REG_RATE_44100: + cmpci_reg_clear_4(sc, CMPCI_REG_MISC, + CMPCI_REG_SPDIF_48K); + cmpci_reg_clear_4(sc, CMPCI_REG_FUNC_1, + CMPCI_REG_SPDIF_LOOP); + cmpci_reg_set_4(sc, CMPCI_REG_FUNC_1, + CMPCI_REG_SPDIF0_ENABLE); + break; + case CMPCI_REG_RATE_48000: + cmpci_reg_set_4(sc, CMPCI_REG_MISC, + CMPCI_REG_SPDIF_48K); + cmpci_reg_clear_4(sc, CMPCI_REG_FUNC_1, + CMPCI_REG_SPDIF_LOOP); + cmpci_reg_set_4(sc, CMPCI_REG_FUNC_1, + CMPCI_REG_SPDIF0_ENABLE); + break; + default: + cmpci_reg_clear_4(sc, CMPCI_REG_FUNC_1, + CMPCI_REG_SPDIF0_ENABLE); + cmpci_reg_set_4(sc, CMPCI_REG_FUNC_1, + CMPCI_REG_SPDIF_LOOP); + } +#endif + } else { + cmpci_reg_partial_write_4(sc, + CMPCI_REG_FUNC_1, + CMPCI_REG_ADC_FS_SHIFT, + CMPCI_REG_ADC_FS_MASK, + md_divide); +#ifdef CMPCI_SPDIF_SUPPORT + if ( sc->in_mask&CMPCI_SPDIF_IN) { + switch (md_divide) { + case CMPCI_REG_RATE_44100: + cmpci_reg_set_4(sc, CMPCI_REG_FUNC_1, + CMPCI_REG_SPDIF1_ENABLE); + break; + default: + return EINVAL; + } + } else + cmpci_reg_clear_4(sc, CMPCI_REG_FUNC_1, + CMPCI_REG_SPDIF1_ENABLE); +#endif + } + } + return 0; +} + +/* ARGSUSED */ +int +cmpci_round_blocksize(handle, block) + void *handle; + int block; +{ + return (block & -4); +} + +int +cmpci_halt_output(handle) + void *handle; +{ + struct cmpci_softc *sc = handle; + int s; + + s = splaudio(); + sc->sc_play.intr = NULL; + cmpci_reg_clear_4(sc, CMPCI_REG_INTR_CTRL, CMPCI_REG_CH0_INTR_ENABLE); + cmpci_reg_clear_4(sc, CMPCI_REG_FUNC_0, CMPCI_REG_CH0_ENABLE); + /* wait for reset DMA */ + cmpci_reg_set_4(sc, CMPCI_REG_FUNC_0, CMPCI_REG_CH0_RESET); + delay(10); + cmpci_reg_clear_4(sc, CMPCI_REG_FUNC_0, CMPCI_REG_CH0_RESET); + splx(s); + + return 0; +} + +int +cmpci_halt_input(handle) + void *handle; +{ + struct cmpci_softc *sc = handle; + int s; + + s = splaudio(); + sc->sc_rec.intr = NULL; + cmpci_reg_clear_4(sc, CMPCI_REG_INTR_CTRL, CMPCI_REG_CH1_INTR_ENABLE); + cmpci_reg_clear_4(sc, CMPCI_REG_FUNC_0, CMPCI_REG_CH1_ENABLE); + /* wait for reset DMA */ + cmpci_reg_set_4(sc, CMPCI_REG_FUNC_0, CMPCI_REG_CH1_RESET); + delay(10); + cmpci_reg_clear_4(sc, CMPCI_REG_FUNC_0, CMPCI_REG_CH1_RESET); + splx(s); + + return 0; +} + +int +cmpci_getdev(handle, retp) + void *handle; + struct audio_device *retp; +{ + *retp = cmpci_device; + return 0; +} + + +/* mixer device information */ +int +cmpci_query_devinfo(handle, dip) + void *handle; + mixer_devinfo_t *dip; +{ + struct cmpci_softc *sc = handle; + (void)sc; + + switch (dip->index) { + case CMPCI_MASTER_VOL: + dip->type = AUDIO_MIXER_VALUE; + dip->mixer_class = CMPCI_OUTPUT_CLASS; + dip->prev = dip->next = AUDIO_MIXER_LAST; + strcpy(dip->label.name, AudioNmaster); + dip->un.v.num_channels = 2; + strcpy(dip->un.v.units.name, AudioNvolume); + return 0; + case CMPCI_FM_VOL: + dip->type = AUDIO_MIXER_VALUE; + dip->mixer_class = CMPCI_INPUT_CLASS; + dip->prev = AUDIO_MIXER_LAST; + dip->next = CMPCI_FM_IN_MUTE; + strcpy(dip->label.name, AudioNfmsynth); + dip->un.v.num_channels = 2; + strcpy(dip->un.v.units.name, AudioNvolume); + return 0; + case CMPCI_CD_VOL: + dip->type = AUDIO_MIXER_VALUE; + dip->mixer_class = CMPCI_INPUT_CLASS; + dip->prev = AUDIO_MIXER_LAST; + dip->next = CMPCI_CD_IN_MUTE; + strcpy(dip->label.name, AudioNcd); + dip->un.v.num_channels = 2; + strcpy(dip->un.v.units.name, AudioNvolume); + return 0; + case CMPCI_VOICE_VOL: + dip->type = AUDIO_MIXER_VALUE; + dip->mixer_class = CMPCI_OUTPUT_CLASS; + dip->prev = AUDIO_MIXER_LAST; + dip->next = AUDIO_MIXER_LAST; + strcpy(dip->label.name, AudioNdac); + dip->un.v.num_channels = 2; + strcpy(dip->un.v.units.name, AudioNvolume); + return 0; + case CMPCI_OUTPUT_CLASS: + dip->type = AUDIO_MIXER_CLASS; + dip->mixer_class = CMPCI_INPUT_CLASS; + dip->next = dip->prev = AUDIO_MIXER_LAST; + strcpy(dip->label.name, AudioCoutputs); + return 0; + case CMPCI_MIC_VOL: + dip->type = AUDIO_MIXER_VALUE; + dip->mixer_class = CMPCI_INPUT_CLASS; + dip->prev = AUDIO_MIXER_LAST; + dip->next = CMPCI_MIC_IN_MUTE; + strcpy(dip->label.name, AudioNmicrophone); + dip->un.v.num_channels = 1; + strcpy(dip->un.v.units.name, AudioNvolume); + return 0; + case CMPCI_LINE_IN_VOL: + dip->type = AUDIO_MIXER_VALUE; + dip->mixer_class = CMPCI_INPUT_CLASS; + dip->prev = AUDIO_MIXER_LAST; + dip->next = CMPCI_LINE_IN_MUTE; + strcpy(dip->label.name, AudioNline); + dip->un.v.num_channels = 2; + strcpy(dip->un.v.units.name, AudioNvolume); + return 0; + case CMPCI_RECORD_SOURCE: + dip->mixer_class = CMPCI_RECORD_CLASS; + dip->prev = dip->next = AUDIO_MIXER_LAST; + strcpy(dip->label.name, AudioNsource); + dip->type = AUDIO_MIXER_SET; +#ifdef CMPCI_SPDIF_SUPPORT + dip->un.s.num_mem = 5; +#else + dip->un.s.num_mem = 4; +#endif + strcpy(dip->un.s.member[0].label.name, AudioNmicrophone); + dip->un.s.member[0].mask = 1 << CMPCI_MIC_VOL; + strcpy(dip->un.s.member[1].label.name, AudioNcd); + dip->un.s.member[1].mask = 1 << CMPCI_CD_VOL; + strcpy(dip->un.s.member[2].label.name, AudioNline); + dip->un.s.member[2].mask = 1 << CMPCI_LINE_IN_VOL; + strcpy(dip->un.s.member[3].label.name, AudioNfmsynth); + dip->un.s.member[3].mask = 1 << CMPCI_FM_VOL; +#ifdef CMPCI_SPDIF_SUPPORT + strcpy(dip->un.s.member[4].label.name, CmpciNspdif); + dip->un.s.member[4].mask = 1 << CMPCI_SPDIF_IN; +#endif + return 0; + case CMPCI_BASS: + dip->prev = dip->next = AUDIO_MIXER_LAST; + strcpy(dip->label.name, AudioNbass); + dip->type = AUDIO_MIXER_VALUE; + dip->mixer_class = CMPCI_EQUALIZATION_CLASS; + dip->un.v.num_channels = 2; + strcpy(dip->un.v.units.name, AudioNbass); + return 0; + case CMPCI_TREBLE: + dip->prev = dip->next = AUDIO_MIXER_LAST; + strcpy(dip->label.name, AudioNtreble); + dip->type = AUDIO_MIXER_VALUE; + dip->mixer_class = CMPCI_EQUALIZATION_CLASS; + dip->un.v.num_channels = 2; + strcpy(dip->un.v.units.name, AudioNtreble); + return 0; + case CMPCI_RECORD_CLASS: + dip->type = AUDIO_MIXER_CLASS; + dip->mixer_class = CMPCI_RECORD_CLASS; + dip->next = dip->prev = AUDIO_MIXER_LAST; + strcpy(dip->label.name, AudioCrecord); + return 0; + case CMPCI_INPUT_CLASS: + dip->type = AUDIO_MIXER_CLASS; + dip->mixer_class = CMPCI_INPUT_CLASS; + dip->next = dip->prev = AUDIO_MIXER_LAST; + strcpy(dip->label.name, AudioCinputs); + return 0; + case CMPCI_PCSPEAKER: + dip->type = AUDIO_MIXER_VALUE; + dip->mixer_class = CMPCI_INPUT_CLASS; + dip->prev = dip->next = AUDIO_MIXER_LAST; + strcpy(dip->label.name, "pc_speaker"); + dip->un.v.num_channels = 1; + strcpy(dip->un.v.units.name, AudioNvolume); + return 0; + case CMPCI_INPUT_GAIN: + dip->type = AUDIO_MIXER_VALUE; + dip->mixer_class = CMPCI_INPUT_CLASS; + dip->prev = dip->next = AUDIO_MIXER_LAST; + strcpy(dip->label.name, AudioNinput); + dip->un.v.num_channels = 2; + strcpy(dip->un.v.units.name, AudioNvolume); + return 0; + case CMPCI_OUTPUT_GAIN: + dip->type = AUDIO_MIXER_VALUE; + dip->mixer_class = CMPCI_OUTPUT_CLASS; + dip->prev = dip->next = AUDIO_MIXER_LAST; + strcpy(dip->label.name, AudioNoutput); + dip->un.v.num_channels = 2; + strcpy(dip->un.v.units.name, AudioNvolume); + return 0; + case CMPCI_AGC: + dip->type = AUDIO_MIXER_ENUM; + dip->mixer_class = CMPCI_INPUT_CLASS; + dip->prev = dip->next = AUDIO_MIXER_LAST; + strcpy(dip->label.name, "agc"); + 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 CMPCI_EQUALIZATION_CLASS: + dip->type = AUDIO_MIXER_CLASS; + dip->mixer_class = CMPCI_EQUALIZATION_CLASS; + dip->next = dip->prev = AUDIO_MIXER_LAST; + strcpy(dip->label.name, AudioCequalization); + return 0; + case CMPCI_CD_IN_MUTE: + dip->prev = CMPCI_CD_VOL; + dip->next = CMPCI_CD_SWAP; + dip->mixer_class = CMPCI_INPUT_CLASS; + goto mute; + case CMPCI_MIC_IN_MUTE: + dip->prev = CMPCI_MIC_VOL; + dip->next = CMPCI_MIC_SWAP; + dip->mixer_class = CMPCI_INPUT_CLASS; + goto mute; + case CMPCI_LINE_IN_MUTE: + dip->prev = CMPCI_LINE_IN_VOL; + dip->next = CMPCI_LINE_SWAP; + dip->mixer_class = CMPCI_INPUT_CLASS; + goto mute; + case CMPCI_FM_IN_MUTE: + dip->prev = CMPCI_FM_VOL; + dip->next = CMPCI_FM_SWAP; + dip->mixer_class = CMPCI_INPUT_CLASS; + goto mute; + case CMPCI_CD_SWAP: + dip->prev = CMPCI_CD_IN_MUTE; + dip->next = CMPCI_CD_OUT_MUTE; + goto swap; + case CMPCI_MIC_SWAP: + dip->prev = CMPCI_MIC_IN_MUTE; + dip->next = CMPCI_MIC_OUT_MUTE; + goto swap; + case CMPCI_LINE_SWAP: + dip->prev = CMPCI_LINE_IN_MUTE; + dip->next = CMPCI_LINE_OUT_MUTE; + goto swap; + case CMPCI_FM_SWAP: + dip->prev = CMPCI_FM_IN_MUTE; + dip->next = AUDIO_MIXER_LAST; + swap: + dip->mixer_class = CMPCI_INPUT_CLASS; + strcpy(dip->label.name, AudioNswap); + goto mute1; + case CMPCI_CD_OUT_MUTE: + dip->prev = CMPCI_CD_SWAP; + dip->next = AUDIO_MIXER_LAST; + dip->mixer_class = CMPCI_OUTPUT_CLASS; + goto mute; + case CMPCI_MIC_OUT_MUTE: + dip->prev = CMPCI_MIC_SWAP; + dip->next = AUDIO_MIXER_LAST; + dip->mixer_class = CMPCI_OUTPUT_CLASS; + goto mute; + case CMPCI_LINE_OUT_MUTE: + dip->prev = CMPCI_LINE_SWAP; + dip->next = AUDIO_MIXER_LAST; + dip->mixer_class = CMPCI_OUTPUT_CLASS; + mute: + strcpy(dip->label.name, AudioNmute); + mute1: + 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; +} + +int +cmpci_alloc_dmamem(sc, size, type, flags, r_addr) + struct cmpci_softc *sc; + size_t size; + int type, flags; + caddr_t *r_addr; +{ + int ret = 0; + struct cmpci_dmanode *n; + int w; + + if ( NULL == (n=malloc(sizeof(struct cmpci_dmanode), type, flags)) ) { + ret = ENOMEM; + goto quit; + } + + w = (flags & M_NOWAIT) ? BUS_DMA_NOWAIT : BUS_DMA_WAITOK; +#define CMPCI_DMABUF_ALIGN 0x4 +#define CMPCI_DMABUF_BOUNDARY 0x0 + n->cd_tag = sc->sc_dmat; + n->cd_size = size; + if ( (ret=bus_dmamem_alloc(n->cd_tag, n->cd_size, + CMPCI_DMABUF_ALIGN, CMPCI_DMABUF_BOUNDARY, + n->cd_segs, + sizeof(n->cd_segs)/sizeof(n->cd_segs[0]), + &n->cd_nsegs, w)) ) + goto mfree; + if ( (ret=bus_dmamem_map(n->cd_tag, n->cd_segs, n->cd_nsegs, n->cd_size, + &n->cd_addr, w | BUS_DMA_COHERENT)) ) + goto dmafree; + if ( (ret=bus_dmamap_create(n->cd_tag, n->cd_size, 1, n->cd_size, 0, + w, &n->cd_map)) ) + goto unmap; + if ( (ret=bus_dmamap_load(n->cd_tag, n->cd_map, n->cd_addr, n->cd_size, + NULL, w)) ) + goto destroy; + + n->cd_next = sc->sc_dmap; + sc->sc_dmap = n; + *r_addr = KVADDR(n); + return 0; + +destroy: + bus_dmamap_destroy(n->cd_tag, n->cd_map); +unmap: + bus_dmamem_unmap(n->cd_tag, n->cd_addr, n->cd_size); +dmafree: + bus_dmamem_free(n->cd_tag, + n->cd_segs, sizeof(n->cd_segs)/sizeof(n->cd_segs[0])); +mfree: + free(n, type); +quit: + return ret; +} + +int +cmpci_free_dmamem(sc, addr, type) + struct cmpci_softc *sc; + caddr_t addr; + int type; +{ + struct cmpci_dmanode **nnp; + + for (nnp = &sc->sc_dmap; *nnp; nnp = &(*nnp)->cd_next) { + if ((*nnp)->cd_addr == addr) { + struct cmpci_dmanode *n = *nnp; + + bus_dmamap_unload(n->cd_tag, n->cd_map); + bus_dmamap_destroy(n->cd_tag, n->cd_map); + bus_dmamem_unmap(n->cd_tag, n->cd_addr, n->cd_size); + bus_dmamem_free(n->cd_tag, n->cd_segs, + sizeof(n->cd_segs)/sizeof(n->cd_segs[0])); + free(n, type); + return 0; + } + } + return -1; +} + +struct cmpci_dmanode * +cmpci_find_dmamem(sc, addr) + struct cmpci_softc *sc; + caddr_t addr; +{ + struct cmpci_dmanode *p; + for (p = sc->sc_dmap; p; p = p->cd_next) { + if (KVADDR(p) == (void *)addr) + break; + } + return p; +} + +#if 0 +void +cmpci_print_dmamem __P((struct cmpci_dmanode *p)); +void +cmpci_print_dmamem(p) + struct cmpci_dmanode *p; +{ + DPRINTF(("DMA at virt:%p, dmaseg:%p, mapseg:%p, size:%p\n", + (void *)p->cd_addr, (void *)p->cd_segs[0].ds_addr, + (void *)DMAADDR(p), (void *)p->cd_size)); +} +#endif /* DEBUG */ + +void * +cmpci_malloc(handle, size, type, flags) + void *handle; + u_long size; + int type, flags; +{ + struct cmpci_softc *sc = handle; + caddr_t addr; + + if ( cmpci_alloc_dmamem(sc, size, type, flags, &addr) ) + return NULL; + return addr; +} + +void +cmpci_free(handle, addr, type) + void *handle; + void *addr; + int type; +{ + struct cmpci_softc *sc = handle; + + cmpci_free_dmamem(sc, addr, type); +} + +#define MAXVAL 256 +int +cmpci_adjust(val, mask) + int val, mask; +{ + val += (MAXVAL - mask) >> 1; + if (val >= MAXVAL) + val = MAXVAL-1; + return val & mask; +} + +void +cmpci_set_mixer_gain(sc, port) + struct cmpci_softc *sc; + int port; +{ + int src; + + switch (port) { + case CMPCI_MIC_VOL: + src = CMPCI_SB16_MIXER_MIC; + break; + case CMPCI_MASTER_VOL: + src = CMPCI_SB16_MIXER_MASTER_L; + break; + case CMPCI_LINE_IN_VOL: + src = CMPCI_SB16_MIXER_LINE_L; + break; + case CMPCI_VOICE_VOL: + src = CMPCI_SB16_MIXER_VOICE_L; + break; + case CMPCI_FM_VOL: + src = CMPCI_SB16_MIXER_FM_L; + break; + case CMPCI_CD_VOL: + src = CMPCI_SB16_MIXER_CDDA_L; + break; + case CMPCI_INPUT_GAIN: + src = CMPCI_SB16_MIXER_INGAIN_L; + break; + case CMPCI_OUTPUT_GAIN: + src = CMPCI_SB16_MIXER_OUTGAIN_L; + break; + case CMPCI_TREBLE: + src = CMPCI_SB16_MIXER_TREBLE_L; + break; + case CMPCI_BASS: + src = CMPCI_SB16_MIXER_BASS_L; + break; + case CMPCI_PCSPEAKER: + cmpci_mixerreg_write(sc, CMPCI_SB16_MIXER_SPEAKER, + sc->gain[port][CMPCI_LEFT]); + return; + default: + return; + } + cmpci_mixerreg_write(sc, src, sc->gain[port][CMPCI_LEFT]); + cmpci_mixerreg_write(sc, CMPCI_SB16_MIXER_L_TO_R(src), + sc->gain[port][CMPCI_RIGHT]); +} + +int +cmpci_set_in_ports(sc, mask) + struct cmpci_softc *sc; + int mask; +{ + int bitsl, bitsr; + + if (mask & ~((1<in_mask = mask; + + return 0; +} + +int +cmpci_set_port(handle, cp) + void *handle; + mixer_ctrl_t *cp; +{ + struct cmpci_softc *sc = handle; + int lgain, rgain; + int mask, bits; + int lmask, rmask, lbits, rbits; + int mute, swap; + + switch (cp->dev) { + case CMPCI_TREBLE: + case CMPCI_BASS: + case CMPCI_PCSPEAKER: + case CMPCI_INPUT_GAIN: + case CMPCI_OUTPUT_GAIN: + case CMPCI_MIC_VOL: + case CMPCI_LINE_IN_VOL: + case CMPCI_VOICE_VOL: + case CMPCI_FM_VOL: + case CMPCI_CD_VOL: + case CMPCI_MASTER_VOL: + if (cp->type != AUDIO_MIXER_VALUE) + return EINVAL; + switch (cp->dev) { + case CMPCI_MIC_VOL: + if (cp->un.value.num_channels != 1) + return EINVAL; + + lgain = rgain = CMPCI_ADJUST_MIC_GAIN(sc, + cp->un.value.level[AUDIO_MIXER_LEVEL_MONO]); + break; + case CMPCI_PCSPEAKER: + if (cp->un.value.num_channels != 1) + return EINVAL; + /* FALLTHROUGH */ + case CMPCI_INPUT_GAIN: + case CMPCI_OUTPUT_GAIN: + lgain = rgain = CMPCI_ADJUST_2_GAIN(sc, + cp->un.value.level[AUDIO_MIXER_LEVEL_MONO]); + break; + default: + switch (cp->un.value.num_channels) { + case 1: + lgain = rgain = CMPCI_ADJUST_GAIN(sc, + cp->un.value.level[AUDIO_MIXER_LEVEL_MONO]); + break; + case 2: + lgain = CMPCI_ADJUST_GAIN(sc, + cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT]); + rgain = CMPCI_ADJUST_GAIN(sc, + cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT]); + break; + default: + return EINVAL; + } + break; + } + sc->gain[cp->dev][CMPCI_LEFT] = lgain; + sc->gain[cp->dev][CMPCI_RIGHT] = rgain; + + cmpci_set_mixer_gain(sc, cp->dev); + break; + + case CMPCI_RECORD_SOURCE: + if (cp->type != AUDIO_MIXER_SET) + return EINVAL; +#ifdef CMPCI_SPDIF_SUPPORT + if ( cp->un.mask&(1<un.mask = 1<un.mask); + + case CMPCI_AGC: + cmpci_mixerreg_write(sc, CMPCI_SB16_MIXER_AGC, cp->un.ord & 1); + break; + case CMPCI_CD_OUT_MUTE: + mask = CMPCI_SB16_SW_CD; + goto omute; + case CMPCI_MIC_OUT_MUTE: + mask = CMPCI_SB16_SW_MIC; + goto omute; + case CMPCI_LINE_OUT_MUTE: + mask = CMPCI_SB16_SW_LINE; + omute: + if (cp->type != AUDIO_MIXER_ENUM) + return EINVAL; + bits = cmpci_mixerreg_read(sc, CMPCI_SB16_MIXER_OUTMIX); + sc->gain[cp->dev][CMPCI_LR] = cp->un.ord != 0; + if (cp->un.ord) + bits = bits & ~mask; + else + bits = bits | mask; + cmpci_mixerreg_write(sc, CMPCI_SB16_MIXER_OUTMIX, bits); + break; + + case CMPCI_MIC_IN_MUTE: + case CMPCI_MIC_SWAP: + lmask = rmask = CMPCI_SB16_SW_MIC; + goto imute; + case CMPCI_CD_IN_MUTE: + case CMPCI_CD_SWAP: + lmask = CMPCI_SB16_SW_CD_L; + rmask = CMPCI_SB16_SW_CD_R; + goto imute; + case CMPCI_LINE_IN_MUTE: + case CMPCI_LINE_SWAP: + lmask = CMPCI_SB16_SW_LINE_L; + rmask = CMPCI_SB16_SW_LINE_R; + goto imute; + case CMPCI_FM_IN_MUTE: + case CMPCI_FM_SWAP: + lmask = CMPCI_SB16_SW_FM_L; + rmask = CMPCI_SB16_SW_FM_R; + imute: + if (cp->type != AUDIO_MIXER_ENUM) + return EINVAL; + mask = lmask | rmask; + lbits = cmpci_mixerreg_read(sc, CMPCI_SB16_MIXER_ADCMIX_L) + & ~mask; + rbits = cmpci_mixerreg_read(sc, CMPCI_SB16_MIXER_ADCMIX_R) + & ~mask; + sc->gain[cp->dev][CMPCI_LR] = cp->un.ord != 0; + if (CMPCI_IS_IN_MUTE(cp->dev)) { + mute = cp->dev; + swap = mute - CMPCI_CD_IN_MUTE + CMPCI_CD_SWAP; + } else { + swap = cp->dev; + mute = swap + CMPCI_CD_IN_MUTE - CMPCI_CD_SWAP; + } + if (sc->gain[swap][CMPCI_LR]) { + mask = lmask; + lmask = rmask; + rmask = mask; + } + if (!sc->gain[mute][CMPCI_LR]) { + lbits = lbits | lmask; + rbits = rbits | rmask; + } + cmpci_mixerreg_write(sc, CMPCI_SB16_MIXER_ADCMIX_L, lbits); + cmpci_mixerreg_write(sc, CMPCI_SB16_MIXER_ADCMIX_R, rbits); + break; + + default: + return EINVAL; + } + + return 0; +} + +int +cmpci_get_port(handle, cp) + void *handle; + mixer_ctrl_t *cp; +{ + struct cmpci_softc *sc = handle; + + switch (cp->dev) { + case CMPCI_MIC_VOL: + case CMPCI_LINE_IN_VOL: + if (cp->un.value.num_channels != 1) + return EINVAL; + /* FALLTHROUGH */ + case CMPCI_TREBLE: + case CMPCI_BASS: + case CMPCI_PCSPEAKER: + case CMPCI_INPUT_GAIN: + case CMPCI_OUTPUT_GAIN: + case CMPCI_VOICE_VOL: + case CMPCI_FM_VOL: + case CMPCI_CD_VOL: + case CMPCI_MASTER_VOL: + switch (cp->un.value.num_channels) { + case 1: + cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = + sc->gain[cp->dev][CMPCI_LEFT]; + break; + case 2: + cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = + sc->gain[cp->dev][CMPCI_LEFT]; + cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = + sc->gain[cp->dev][CMPCI_RIGHT]; + break; + default: + return EINVAL; + } + break; + + case CMPCI_RECORD_SOURCE: + cp->un.mask = sc->in_mask; + break; + + case CMPCI_AGC: + cp->un.ord = cmpci_mixerreg_read(sc, CMPCI_SB16_MIXER_AGC); + break; + + case CMPCI_CD_IN_MUTE: + case CMPCI_MIC_IN_MUTE: + case CMPCI_LINE_IN_MUTE: + case CMPCI_FM_IN_MUTE: + case CMPCI_CD_SWAP: + case CMPCI_MIC_SWAP: + case CMPCI_LINE_SWAP: + case CMPCI_FM_SWAP: + case CMPCI_CD_OUT_MUTE: + case CMPCI_MIC_OUT_MUTE: + case CMPCI_LINE_OUT_MUTE: + cp->un.ord = sc->gain[cp->dev][CMPCI_LR]; + break; + + default: + return EINVAL; + } + + return 0; +} + +/* ARGSUSED */ +u_long +cmpci_round_buffersize(handle, bufsize) + void *handle; + u_long bufsize; +{ + if (bufsize > 0x10000) + bufsize = 0x10000; + + return bufsize; +} + +int +cmpci_mappage(handle, addr, offset, prot) + void *handle; + void *addr; + int offset; + int prot; +{ + struct cmpci_softc *sc = handle; + struct cmpci_dmanode *p; + + if ( offset < 0 || (p = cmpci_find_dmamem(sc, addr)) == NULL) + return -1; + + return bus_dmamem_mmap(p->cd_tag, p->cd_segs, + sizeof(p->cd_segs)/sizeof(p->cd_segs[0]), + offset, prot, BUS_DMA_WAITOK); +} + +/* ARGSUSED */ +int +cmpci_get_props(handle) + void *handle; +{ + return AUDIO_PROP_MMAP | AUDIO_PROP_INDEPENDENT | AUDIO_PROP_FULLDUPLEX; +} + + +int +cmpci_trigger_output(handle, start, end, blksize, intr, arg, param) + void *handle; + void *start, *end; + int blksize; + void (*intr) __P((void *)); + void *arg; + struct audio_params *param; +{ + struct cmpci_softc *sc = handle; + struct cmpci_dmanode *p; + int bps; + + sc->sc_play.intr = intr; + sc->sc_play.intr_arg = arg; + bps = param->channels * param->precision * param->factor / 8; + if (!bps) + return EINVAL; + + /* set DMA frame */ + if (!(p = cmpci_find_dmamem(sc, start))) + return EINVAL; + bus_space_write_4(sc->sc_iot, sc->sc_ioh, CMPCI_REG_DMA0_BASE, + DMAADDR(p)); + delay(10); + bus_space_write_2(sc->sc_iot, sc->sc_ioh, CMPCI_REG_DMA0_BYTES, + ((caddr_t)end-(caddr_t)start+1)/bps-1); + delay(10); + + /* set interrupt count */ + bus_space_write_2(sc->sc_iot, sc->sc_ioh, CMPCI_REG_DMA0_SAMPLES, + (blksize+bps-1)/bps-1); + delay(10); + + /* start DMA */ + cmpci_reg_clear_4(sc, CMPCI_REG_FUNC_0, CMPCI_REG_CH0_DIR); /* PLAY */ + cmpci_reg_set_4(sc, CMPCI_REG_INTR_CTRL, CMPCI_REG_CH0_INTR_ENABLE); + cmpci_reg_set_4(sc, CMPCI_REG_FUNC_0, CMPCI_REG_CH0_ENABLE); + + return 0; +} + +int +cmpci_trigger_input(handle, start, end, blksize, intr, arg, param) + void *handle; + void *start, *end; + int blksize; + void (*intr) __P((void *)); + void *arg; + struct audio_params *param; +{ + struct cmpci_softc *sc = handle; + struct cmpci_dmanode *p; + int bps; + + sc->sc_rec.intr = intr; + sc->sc_rec.intr_arg = arg; + bps = param->channels*param->precision*param->factor/8; + if (!bps) + return EINVAL; + + /* set DMA frame */ + if (!(p = cmpci_find_dmamem(sc, start))) + return EINVAL; + bus_space_write_4(sc->sc_iot, sc->sc_ioh, CMPCI_REG_DMA1_BASE, + DMAADDR(p)); + delay(10); + bus_space_write_2(sc->sc_iot, sc->sc_ioh, CMPCI_REG_DMA1_BYTES, + ((caddr_t)end-(caddr_t)start+1)/bps-1); + delay(10); + + /* set interrupt count */ + bus_space_write_2(sc->sc_iot, sc->sc_ioh, CMPCI_REG_DMA1_SAMPLES, + (blksize+bps-1)/bps-1); + delay(10); + + /* start DMA */ + cmpci_reg_set_4(sc, CMPCI_REG_FUNC_0, CMPCI_REG_CH1_DIR); /* REC */ + cmpci_reg_set_4(sc, CMPCI_REG_INTR_CTRL, CMPCI_REG_CH1_INTR_ENABLE); + cmpci_reg_set_4(sc, CMPCI_REG_FUNC_0, CMPCI_REG_CH1_ENABLE); + + return 0; +} diff --git a/sys/dev/pci/cmpcireg.h b/sys/dev/pci/cmpcireg.h new file mode 100644 index 00000000000..7bbb4606ac3 --- /dev/null +++ b/sys/dev/pci/cmpcireg.h @@ -0,0 +1,191 @@ +/* $OpenBSD: cmpcireg.h,v 1.1 2000/04/27 02:19:41 millert Exp $ */ + +/* + * Copyright (c) 2000 Takuya SHIOZAKI + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +/* C-Media CMI8x38 Audio Chip Support */ + +#ifndef _DEV_PCI_CMPCIREG_H_ +#define _DEV_PCI_CMPCIREG_H_ + +/* + * PCI Configuration Registers + */ + +#define CMPCI_PCI_IOBASEREG (PCI_MAPREG_START) + + +/* + * I/O Space + */ + +#define CMPCI_REG_FUNC_0 0x00 +# define CMPCI_REG_CH0_DIR 0x00000001 +# define CMPCI_REG_CH1_DIR 0x00000002 +# define CMPCI_REG_CH0_PAUSE 0x00000004 +# define CMPCI_REG_CH1_PAUSE 0x00000008 +# define CMPCI_REG_CH0_ENABLE 0x00010000 +# define CMPCI_REG_CH1_ENABLE 0x00020000 +# define CMPCI_REG_CH0_RESET 0x00040000 +# define CMPCI_REG_CH1_RESET 0x00080000 + +#define CMPCI_REG_FUNC_1 0x04 +# define CMPCI_REG_JOY_ENABLE 0x00000002 +# define CMPCI_REG_UART_ENABLE 0x00000004 +# define CMPCI_REG_LEGACY_ENABLE 0x00000008 +# define CMPCI_REG_BREQ 0x00000010 +# define CMPCI_REG_MCBINTR_ENABLE 0x00000020 +# define CMPCI_REG_SPDIFOUT_DAC 0x00000040 +# define CMPCI_REG_SPDIF_LOOP 0x00000080 +# define CMPCI_REG_SPDIF0_ENABLE 0x00000100 +# define CMPCI_REG_SPDIF1_ENABLE 0x00000200 +# define CMPCI_REG_DAC_FS_SHIFT 10 +# define CMPCI_REG_DAC_FS_MASK 0x00000007 +# define CMPCI_REG_ADC_FS_SHIFT 13 +# define CMPCI_REG_ADC_FS_MASK 0x00000007 + +#define CMPCI_REG_CHANNEL_FORMAT 0x08 +# define CMPCI_REG_CH0_FORMAT_SHIFT 0 +# define CMPCI_REG_CH0_FORMAT_MASK 0x00000003 +# define CMPCI_REG_CH1_FORMAT_SHIFT 2 +# define CMPCI_REG_CH1_FORMAT_MASK 0x00000003 +# define CMPCI_REG_FORMAT_MONO 0x00000000 +# define CMPCI_REG_FORMAT_STEREO 0x00000001 +# define CMPCI_REG_FORMAT_8BIT 0x00000000 +# define CMPCI_REG_FORMAT_16BIT 0x00000002 + +#define CMPCI_REG_INTR_CTRL 0x0c +# define CMPCI_REG_CH0_INTR_ENABLE 0x00010000 +# define CMPCI_REG_CH1_INTR_ENABLE 0x00020000 +# define CMPCI_REG_TDMA_INTR_ENABLE 0x00040000 + +#define CMPCI_REG_INTR_STATUS 0x10 +# define CMPCI_REG_CH0_INTR 0x00000001 +# define CMPCI_REG_CH1_INTR 0x00000002 +# define CMPCI_REG_CH0_BUSY 0x00000004 +# define CMPCI_REG_CH1_BUSY 0x00000008 +# define CMPCI_REG_LEGACY_STEREO 0x00000010 +# define CMPCI_REG_LEGACY_HDMA 0x00000020 +# define CMPCI_REG_DMASTAT 0x00000040 +# define CMPCI_REG_XDO46 0x00000080 +# define CMPCI_REG_HTDMA_INTR 0x00004000 +# define CMPCI_REG_LTDMA_INTR 0x00008000 +# define CMPCI_REG_UART_INTR 0x00010000 +# define CMPCI_REG_MCB_INTR 0x04000000 +# define CMPCI_REG_VCO 0x08000000 +# define CMPCI_REG_ANY_INTR 0x80000000 + +#define CMPCI_REG_LEGACY_CTRL 0x14 +# define CMPCI_REG_LEGACY_SPDIF_ENABLE 0x00200000 +# define CMPCI_REG_SPDIF_COPYRIGHT 0x00400000 +# define CMPCI_REG_XSPDIF_ENABLE 0x00800000 +# define CMPCI_REG_FMSEL_SHIFT 24 +# define CMPCI_REG_FMSEL_MASK 0x00000003 +# define CMPCI_REG_VSBSEL_SHIFT 26 +# define CMPCI_REG_VSBSEL_MASK 0x00000003 +# define CMPCI_REG_VMPUSEL_SHIFT 29 +# define CMPCI_REG_VMPUSEL_MASK 0x00000003 + +#define CMPCI_REG_MISC 0x18 +# define CMPCI_REG_SPDIF_48K 0x00008000 +# define CMPCI_REG_FM_ENABLE 0x00080000 + + +#define CMPCI_REG_SBDATA 0x22 +#define CMPCI_REG_SBADDR 0x23 +# define CMPCI_SB16_MIXER_RESET 0x00 +# define CMPCI_SB16_MIXER_MASTER_L 0x30 +# define CMPCI_SB16_MIXER_MASTER_R 0x31 +# define CMPCI_SB16_MIXER_VOICE_L 0x32 +# define CMPCI_SB16_MIXER_VOICE_R 0x33 +# define CMPCI_SB16_MIXER_FM_L 0x34 +# define CMPCI_SB16_MIXER_FM_R 0x35 +# define CMPCI_SB16_MIXER_CDDA_L 0x36 +# define CMPCI_SB16_MIXER_CDDA_R 0x37 +# define CMPCI_SB16_MIXER_LINE_L 0x38 +# define CMPCI_SB16_MIXER_LINE_R 0x39 +# define CMPCI_SB16_MIXER_MIC 0x3A +# define CMPCI_SB16_MIXER_SPEAKER 0x3B +# define CMPCI_SB16_MIXER_OUTMIX 0x3C +# define CMPCI_SB16_SW_MIC 0x01 +# define CMPCI_SB16_SW_CD_R 0x02 +# define CMPCI_SB16_SW_CD_L 0x04 +# define CMPCI_SB16_SW_CD (CMPCI_SB16_SW_CD_L|CMPCI_SB16_SW_CD_R) +# define CMPCI_SB16_SW_LINE_R 0x08 +# define CMPCI_SB16_SW_LINE_L 0x10 +# define CMPCI_SB16_SW_LINE (CMPCI_SB16_SW_LINE_L|CMPCI_SB16_SW_LINE_R) +# define CMPCI_SB16_SW_FM_R 0x20 +# define CMPCI_SB16_SW_FM_L 0x40 +# define CMPCI_SB16_SW_FM (CMPCI_SB16_SW_FM_L|CMPCI_SB16_SW_FM_R) +# define CMPCI_SB16_MIXER_ADCMIX_L 0x3D +# define CMPCI_SB16_MIXER_ADCMIX_R 0x3E +# define CMPCI_SB16_MIXER_FM_SRC_R 0x20 +# define CMPCI_SB16_MIXER_LINE_SRC_R 0x08 +# define CMPCI_SB16_MIXER_CD_SRC_R 0x02 +# define CMPCI_SB16_MIXER_MIC_SRC 0x01 +# define CMPCI_SB16_MIXER_SRC_R_TO_L(v) ((v) << 1) + +# define CMPCI_SB16_MIXER_INGAIN_L 0x3F +# define CMPCI_SB16_MIXER_INGAIN_R 0x40 +# define CMPCI_SB16_MIXER_OUTGAIN_L 0x41 +# define CMPCI_SB16_MIXER_OUTGAIN_R 0x42 +# define CMPCI_SB16_MIXER_AGC 0x43 +# define CMPCI_SB16_MIXER_TREBLE_L 0x44 +# define CMPCI_SB16_MIXER_TREBLE_R 0x45 +# define CMPCI_SB16_MIXER_BASS_L 0x46 +# define CMPCI_SB16_MIXER_BASS_R 0x47 +# define CMPCI_SB16_MIXER_L_TO_R(addr) ((addr)+1) + +# define CMPCI_ADJUST_MIC_GAIN(sc, x) cmpci_adjust((x), 0xf8) +# define CMPCI_ADJUST_GAIN(sc, x) cmpci_adjust((x), 0xf8) +# define CMPCI_ADJUST_2_GAIN(sc, x) cmpci_adjust((x), 0xc0) + +#define CMPCI_REG_MPU_BASE 0x40 +#define CMPCI_REG_MPU_SIZE 0x10 +#define CMPCI_REG_FM_BASE 0x50 +#define CMPCI_REG_FM_SIZE 0x10 + +#define CMPCI_REG_DMA0_BASE 0x80 +#define CMPCI_REG_DMA0_BYTES 0x84 +#define CMPCI_REG_DMA0_SAMPLES 0x86 +#define CMPCI_REG_DMA1_BASE 0x88 +#define CMPCI_REG_DMA1_BYTES 0x8C +#define CMPCI_REG_DMA1_SAMPLES 0x8E + + +/* sample rate */ +#define CMPCI_REG_RATE_5512 0 +#define CMPCI_REG_RATE_11025 1 +#define CMPCI_REG_RATE_22050 2 +#define CMPCI_REG_RATE_44100 3 +#define CMPCI_REG_RATE_8000 4 +#define CMPCI_REG_RATE_16000 5 +#define CMPCI_REG_RATE_32000 6 +#define CMPCI_REG_RATE_48000 7 +#define CMPCI_REG_NUMRATE 8 + +#endif /* _DEV_PCI_CMPCIREG_H_ */ diff --git a/sys/dev/pci/cmpcivar.h b/sys/dev/pci/cmpcivar.h new file mode 100644 index 00000000000..ff25718983a --- /dev/null +++ b/sys/dev/pci/cmpcivar.h @@ -0,0 +1,128 @@ +/* $OpenBSD: cmpcivar.h,v 1.1 2000/04/27 02:19:41 millert Exp $ */ + +/* + * Copyright (c) 2000 Takuya SHIOZAKI + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +/* C-Media CMI8x38 Audio Chip Support */ + +/* + * DMA pool + */ +struct cmpci_dmanode { + bus_dma_tag_t cd_tag; + int cd_nsegs; + bus_dma_segment_t cd_segs[1]; + bus_dmamap_t cd_map; + caddr_t cd_addr; + size_t cd_size; + struct cmpci_dmanode *cd_next; +}; + +typedef struct cmpci_dmanode *cmpci_dmapool_t; +#define KVADDR(dma) ((void *)(dma)->cd_addr) +#define DMAADDR(dma) ((dma)->cd_map->dm_segs[0].ds_addr) + + +/* + * Mixer - SoundBraster16 Compatible + */ +#define CMPCI_MASTER_VOL 0 +#define CMPCI_FM_VOL 1 +#define CMPCI_CD_VOL 2 +#define CMPCI_VOICE_VOL 3 +#define CMPCI_OUTPUT_CLASS 4 + +#define CMPCI_MIC_VOL 5 +#define CMPCI_LINE_IN_VOL 6 +#define CMPCI_RECORD_SOURCE 7 +#define CMPCI_TREBLE 8 +#define CMPCI_BASS 9 +#define CMPCI_RECORD_CLASS 10 +#define CMPCI_INPUT_CLASS 11 + +#define CMPCI_PCSPEAKER 12 +#define CMPCI_INPUT_GAIN 13 +#define CMPCI_OUTPUT_GAIN 14 +#define CMPCI_AGC 15 +#define CMPCI_EQUALIZATION_CLASS 16 + +#define CMPCI_CD_IN_MUTE 17 +#define CMPCI_MIC_IN_MUTE 18 +#define CMPCI_LINE_IN_MUTE 19 +#define CMPCI_FM_IN_MUTE 20 + +#define CMPCI_CD_SWAP 21 +#define CMPCI_MIC_SWAP 22 +#define CMPCI_LINE_SWAP 23 +#define CMPCI_FM_SWAP 24 + +#define CMPCI_CD_OUT_MUTE 25 +#define CMPCI_MIC_OUT_MUTE 26 +#define CMPCI_LINE_OUT_MUTE 27 + +#ifdef CMPCI_SPDIF_SUPPORT +#define CMPCI_SPDIF_IN 28 +#define CMPCI_SPDIF_IN_MUTE 29 +#define CmpciNspdif "spdif" +#define CMPCI_NDEVS 30 +#else +#define CMPCI_NDEVS 28 +#endif + +#define CMPCI_IS_IN_MUTE(x) ((x) < CMPCI_CD_SWAP) + + +/* + * softc + */ +struct cmpci_softc { + struct device sc_dev; + + /* I/O Base device */ + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; + + /* intr handle */ + pci_intr_handle_t * sc_ih; + + /* DMA */ + bus_dma_tag_t sc_dmat; + cmpci_dmapool_t sc_dmap; + + /* each channel */ + struct { + void (*intr) __P((void *)); + void *intr_arg; + } sc_play, sc_rec; + + /* mixer */ + uint8_t gain[CMPCI_NDEVS][2]; +#define CMPCI_LEFT 0 +#define CMPCI_RIGHT 1 +#define CMPCI_LR 0 + uint16_t in_mask; +}; diff --git a/sys/dev/pci/files.pci b/sys/dev/pci/files.pci index d5134cf451a..87d4006aac9 100644 --- a/sys/dev/pci/files.pci +++ b/sys/dev/pci/files.pci @@ -1,4 +1,4 @@ -# $OpenBSD: files.pci,v 1.72 2000/04/18 19:35:31 jason Exp $ +# $OpenBSD: files.pci,v 1.73 2000/04/27 02:19:41 millert Exp $ # $NetBSD: files.pci,v 1.20 1996/09/24 17:47:15 christos Exp $ # # Config file and device description for machine-independent PCI code. @@ -253,3 +253,8 @@ file dev/pci/pucdata.c puc # device declaration in sys/conf/files attach an at pci with an_pci file dev/pci/if_an_pci.c an_pci + +# C-Media CMI8x38 Audio Chip +device cmpci: audio, auconv, mulaw +attach cmpci at pci +file dev/pci/cmpci.c cmpci