Driver for C-Media CMI8x38 Audio Chip present on several all-in-one
authormillert <millert@openbsd.org>
Thu, 27 Apr 2000 02:19:41 +0000 (02:19 +0000)
committermillert <millert@openbsd.org>
Thu, 27 Apr 2000 02:19:41 +0000 (02:19 +0000)
motherboards (ie: bultin audio + video).  Written by Takuya SHIOZAKI.

sys/arch/i386/conf/GENERIC
sys/dev/pci/cmpci.c [new file with mode: 0644]
sys/dev/pci/cmpcireg.h [new file with mode: 0644]
sys/dev/pci/cmpcivar.h [new file with mode: 0644]
sys/dev/pci/files.pci

index 15fa275..4c5ad65 100644 (file)
@@ -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 (file)
index 0000000..9ba82a8
--- /dev/null
@@ -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 <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/device.h>
+#include <sys/proc.h>
+
+#include <dev/pci/pcidevs.h>
+#include <dev/pci/pcivar.h>
+
+#include <sys/audioio.h>
+#include <dev/audio_if.h>
+#include <dev/mulaw.h>
+#include <dev/auconv.h>
+
+#include <dev/pci/cmpcireg.h>
+#include <dev/pci/cmpcivar.h>
+
+#include <machine/bus.h>
+#include <machine/intr.h>
+
+/*
+ * 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<<shift) |
+           (bus_space_read_4(sc->sc_iot, sc->sc_ioh, no) & ~(mask<<shift)));
+       delay(10);
+}
+
+/* register set/clear bit */
+static __inline void
+cmpci_reg_set_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);
+}
+
+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; i<CMPCI_REG_NUMRATE-2; i++)
+               if (rate <=
+                   (cmpci_rate_table[i].rate + cmpci_rate_table[i+1].rate) / 2)
+                       return i;
+       return i;  /* 48000 */
+}
+
+static __inline int
+cmpci_index_to_rate(index)
+       int index;
+{
+
+       return cmpci_rate_table[index].rate;
+}
+
+static __inline int
+cmpci_index_to_divider(index)
+       int index;
+{
+       return cmpci_rate_table[index].divider;
+}
+
+
+/*
+ * interface to configure the device.
+ */
+int
+cmpci_match(parent, match, aux)
+       struct device *parent;
+       void *match;
+       void *aux;
+{
+       struct pci_attach_args *pa = (struct pci_attach_args *)aux;
+
+       if (PCI_VENDOR(pa->pa_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<<CMPCI_FM_VOL) | (1<<CMPCI_LINE_IN_VOL) |
+                    (1<<CMPCI_CD_VOL) | (1<<CMPCI_MIC_VOL)
+#ifdef CMPCI_SPDIF_SUPPORT
+                    | (1<<CMPCI_SPDIF_IN)
+#endif
+                    ))
+               return EINVAL;
+       bitsr = 0;
+       if (mask & (1<<CMPCI_FM_VOL))
+               bitsr |= CMPCI_SB16_MIXER_FM_SRC_R;
+       if (mask & (1<<CMPCI_LINE_IN_VOL))
+               bitsr |= CMPCI_SB16_MIXER_LINE_SRC_R;
+       if (mask & (1<<CMPCI_CD_VOL))
+               bitsr |= CMPCI_SB16_MIXER_CD_SRC_R;
+       bitsl = CMPCI_SB16_MIXER_SRC_R_TO_L(bitsr);
+       if (mask & (1<<CMPCI_MIC_VOL)) {
+               bitsl |= CMPCI_SB16_MIXER_MIC_SRC;
+               bitsr |= CMPCI_SB16_MIXER_MIC_SRC;
+       }
+       cmpci_mixerreg_write(sc, CMPCI_SB16_MIXER_ADCMIX_L, bitsl);
+       cmpci_mixerreg_write(sc, CMPCI_SB16_MIXER_ADCMIX_R, bitsr);
+
+       sc->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<<CMPCI_SPDIF_IN) )
+                       cp->un.mask = 1<<CMPCI_SPDIF_IN;
+#endif
+               return cmpci_set_in_ports(sc, cp->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 (file)
index 0000000..7bbb460
--- /dev/null
@@ -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 (file)
index 0000000..ff25718
--- /dev/null
@@ -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;
+};
index d5134cf..87d4006 100644 (file)
@@ -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