summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--share/man/man4/Makefile8
-rw-r--r--share/man/man4/audio.44
-rw-r--r--share/man/man4/clct.423
-rw-r--r--share/man/man4/pci.44
-rw-r--r--sys/arch/i386/conf/GENERIC4
-rw-r--r--sys/dev/pci/cs4281.c1579
-rw-r--r--sys/dev/pci/cs4281reg.h323
-rw-r--r--sys/dev/pci/files.pci7
8 files changed, 1944 insertions, 8 deletions
diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile
index 4495b6bc1d4..c5f4b41fc90 100644
--- a/share/man/man4/Makefile
+++ b/share/man/man4/Makefile
@@ -1,10 +1,10 @@
-# $OpenBSD: Makefile,v 1.142 2001/01/13 15:35:58 espie Exp $
+# $OpenBSD: Makefile,v 1.143 2001/01/13 19:53:50 aaron Exp $
MAN= aac.4 ac97.4 adv.4 aha.4 ahb.4 ahc.4 aic.4 amphy.4 an.4 aria.4 ast.4 \
atalk.4 atapiscsi.4 audio.4 aue.4 auvia.4 awi.4 bpf.4 bridge.4 \
- brgphy.4 cac.4 cardbus.4 ccd.4 cd.4 ch.4 clnp.4 clcs.4 cltp.4 cmpci.4 cnw.4 \
- com.4 cue.4 cy.4 dc.4 ddb.4 de.4 dpt.4 drum.4 eap.4 ec.4 ef.4 \
- eg.4 el.4 enc.4 ep.4 esis.4 eso.4 ess.4 exphy.4 fd.4 fdc.4 fpa.4 \
+ brgphy.4 cac.4 cardbus.4 ccd.4 cd.4 ch.4 clnp.4 clcs.4 clct.4 cltp.4 \
+ cmpci.4 cnw.4 com.4 cue.4 cy.4 dc.4 ddb.4 de.4 dpt.4 drum.4 eap.4 ec.4 \
+ ef.4 eg.4 el.4 enc.4 ep.4 esis.4 eso.4 ess.4 exphy.4 fd.4 fdc.4 fpa.4 \
fms.4 fxp.4 gdt.4 gre.4 hifn.4 hsq.4 auich.4 icmp.4 icsphy.4 \
idp.4 ifmedia.4 \
inet.4 inphy.4 iophy.4 ip.4 ipl.4 ipsec.4 isa.4 isapnp.4 iso.4 isp.4 \
diff --git a/share/man/man4/audio.4 b/share/man/man4/audio.4
index c084763cf55..f7e0fe13eaa 100644
--- a/share/man/man4/audio.4
+++ b/share/man/man4/audio.4
@@ -1,4 +1,4 @@
-.\" $OpenBSD: audio.4,v 1.17 2001/01/13 15:35:58 espie Exp $
+.\" $OpenBSD: audio.4,v 1.18 2001/01/13 19:53:51 aaron Exp $
.\" $NetBSD: audio.4,v 1.20 1998/05/28 17:27:15 augustss Exp $
.\"
.\" Copyright (c) 1996 The NetBSD Foundation, Inc.
@@ -644,6 +644,8 @@ For ports using the ISA bus:
.Pp
For ports using the PCI bus:
.Xr auich 4 ,
+.Xr clcs 4 ,
+.Xr clct 4 ,
.Xr cmpci 4 ,
.Xr eap 4 ,
.Xr eso 4 ,
diff --git a/share/man/man4/clct.4 b/share/man/man4/clct.4
new file mode 100644
index 00000000000..05fd0bc817f
--- /dev/null
+++ b/share/man/man4/clct.4
@@ -0,0 +1,23 @@
+.\" $OpenBSD: clct.4,v 1.1 2001/01/13 19:53:51 aaron Exp $
+.\"
+.Dd January 13, 2001
+.Dt CLCT 4
+.Os
+.Sh NAME
+.Nm clct
+.Nd Cirrus Logic CS4281 audio device driver
+.Sh SYNOPSIS
+.Cd "clct* at pci? dev ? function ?"
+.Cd "audio* at clct?"
+.Sh DESCRIPTION
+The
+.Nm
+driver provides support for the Cirrus Logic CS4281 chip.
+.Sh SEE ALSO
+.Xr audio 4 ,
+.Xr pci 4
+.Sh HISTORY
+The
+.Nm
+device driver appeared in
+.Ox 2.9 .
diff --git a/share/man/man4/pci.4 b/share/man/man4/pci.4
index 311a5e7094a..366bc65c40e 100644
--- a/share/man/man4/pci.4
+++ b/share/man/man4/pci.4
@@ -1,4 +1,4 @@
-.\" $OpenBSD: pci.4,v 1.24 2001/01/13 15:35:58 espie Exp $
+.\" $OpenBSD: pci.4,v 1.25 2001/01/13 19:53:51 aaron Exp $
.\" $NetBSD: pci.4,v 1.29 2000/04/01 00:32:23 tsarna Exp $
.\"
.\" Copyright (c) 2000 Theo de Raadt. All rights reserved.
@@ -242,6 +242,8 @@ C-Media CMI8x38 sound devices.
Intel 82801AA/AB and 440MX sound devices.
.It Xr clcs 4
Cirrus Logic CrystalClear CS4280 sound devices.
+.It Xr clct 4
+Cirrus Logic CrystalClear CS4281 sound devices.
.El
.Ss Miscellaneous devices
.Bl -tag -width speaker -offset ind
diff --git a/sys/arch/i386/conf/GENERIC b/sys/arch/i386/conf/GENERIC
index 5c62ca13a4c..3091e43a01c 100644
--- a/sys/arch/i386/conf/GENERIC
+++ b/sys/arch/i386/conf/GENERIC
@@ -1,4 +1,4 @@
-# $OpenBSD: GENERIC,v 1.220 2001/01/11 23:38:57 espie Exp $
+# $OpenBSD: GENERIC,v 1.221 2001/01/13 19:53:50 aaron Exp $
# $NetBSD: GENERIC,v 1.48 1996/05/20 18:17:23 mrg Exp $
#
# GENERIC -- everything that's currently supported
@@ -344,6 +344,7 @@ neo* at pci? dev ? function ? # NeoMagic 256AV/ZX
cmpci* at pci? dev ? function ? # C-Media CMI8338/8738
auich* at pci? dev ? function ? # i82801 ICH AC'97 audio
clcs* at pci? dev ? function ? # CS4280 CrystalClear audio
+clct* at pci? dev ? function ? # CS4281 CrystalClear audio
fms* at pci? dev ? function ? # Forte Media FM801
auvia* at pci? dev ? function ? # VIA VT82C686A
maestro* at pci? dev ? function ? # ESS Maestro PCI
@@ -387,6 +388,7 @@ audio* at sv?
audio* at neo?
audio* at cmpci?
audio* at clcs?
+audio* at clct?
audio* at auich?
audio* at fms?
audio* at auvia?
diff --git a/sys/dev/pci/cs4281.c b/sys/dev/pci/cs4281.c
new file mode 100644
index 00000000000..273df86deec
--- /dev/null
+++ b/sys/dev/pci/cs4281.c
@@ -0,0 +1,1579 @@
+/* $OpenBSD: cs4281.c,v 1.1 2001/01/13 19:53:50 aaron Exp $ */
+/* $Tera: cs4281.c,v 1.18 2000/12/27 14:24:45 tacha Exp $ */
+
+/*
+ * Copyright (c) 2000 Tatoku Ogaito. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Tatoku Ogaito
+ * for the NetBSD Project.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
+ */
+
+/*
+ * Cirrus Logic CS4281 driver.
+ * Data sheets can be found
+ * http://www.cirrus.com/ftp/pub/4281.pdf
+ * ftp://ftp.alsa-project.org/pub/manuals/cirrus/cs4281tm.pdf
+ *
+ * TODO:
+ * 1: midi and FM support
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/fcntl.h>
+#include <sys/device.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+
+#include <dev/pci/pcidevs.h>
+#include <dev/pci/pcivar.h>
+#include <dev/pci/cs4281reg.h>
+
+#include <sys/audioio.h>
+#include <dev/audio_if.h>
+#include <dev/midi_if.h>
+#include <dev/mulaw.h>
+#include <dev/auconv.h>
+
+#include <dev/ic/ac97.h>
+
+#include <machine/bus.h>
+
+#define CSCC_PCI_BA0 0x10
+#define CSCC_PCI_BA1 0x14
+
+struct cs4281_dma {
+ bus_dmamap_t map;
+ caddr_t addr; /* real dma buffer */
+ caddr_t dum; /* dummy buffer for audio driver */
+ bus_dma_segment_t segs[1];
+ int nsegs;
+ size_t size;
+ struct cs4281_dma *next;
+};
+#define DMAADDR(p) ((p)->map->dm_segs[0].ds_addr)
+#define BUFADDR(p) ((void *)((p)->dum))
+#define KERNADDR(p) ((void *)((p)->addr))
+
+/*
+ * Software state
+ */
+struct cs4281_softc {
+ struct device sc_dev;
+
+ pci_intr_handle_t *sc_ih;
+
+ /* I/O (BA0) */
+ bus_space_tag_t ba0t;
+ bus_space_handle_t ba0h;
+
+ /* BA1 */
+ bus_space_tag_t ba1t;
+ bus_space_handle_t ba1h;
+
+ /* DMA */
+ bus_dma_tag_t sc_dmatag;
+ struct cs4281_dma *sc_dmas;
+ size_t dma_size;
+ size_t dma_align;
+
+ int hw_blocksize;
+
+ /* playback */
+ void (*sc_pintr)(void *); /* dma completion intr handler */
+ void *sc_parg; /* arg for sc_intr() */
+ char *sc_ps, *sc_pe, *sc_pn;
+ int sc_pcount;
+ int sc_pi;
+ struct cs4281_dma *sc_pdma;
+ char *sc_pbuf;
+ int (*halt_output)__P((void *));
+#ifdef DIAGNOSTIC
+ char sc_prun;
+#endif
+
+ /* capturing */
+ void (*sc_rintr)(void *); /* dma completion intr handler */
+ void *sc_rarg; /* arg for sc_intr() */
+ char *sc_rs, *sc_re, *sc_rn;
+ int sc_rcount;
+ int sc_ri;
+ struct cs4281_dma *sc_rdma;
+ char *sc_rbuf;
+ int sc_rparam; /* record format */
+ int (*halt_input)__P((void *));
+#ifdef DIAGNOSTIC
+ char sc_rrun;
+#endif
+
+#if NMIDI > 0
+ void (*sc_iintr)(void *, int); /* midi input ready handler */
+ void (*sc_ointr)(void *); /* midi output ready handler */
+ void *sc_arg;
+#endif
+
+ /* AC97 CODEC */
+ struct ac97_codec_if *codec_if;
+ struct ac97_host_if host_if;
+
+ /* Power Management */
+ char sc_suspend;
+ void *sc_powerhook; /* Power hook */
+ u_int16_t ac97_reg[CS4281_SAVE_REG_MAX + 1]; /* Save ac97 registers */
+};
+
+#define BA0READ4(sc, r) bus_space_read_4((sc)->ba0t, (sc)->ba0h, (r))
+#define BA0WRITE4(sc, r, x) bus_space_write_4((sc)->ba0t, (sc)->ba0h, (r), (x))
+
+#if defined(ENABLE_SECONDARY_CODEC)
+#define MAX_CHANNELS (4)
+#define MAX_FIFO_SIZE 32 /* 128/4 channels */
+#else
+#define MAX_CHANNELS (2)
+#define MAX_FIFO_SIZE 64 /* 128/2 channels */
+#endif
+
+int cs4281_match __P((struct device *, void *, void *));
+void cs4281_attach __P((struct device *, struct device *, void *));
+int cs4281_intr __P((void *));
+int cs4281_query_encoding __P((void *, struct audio_encoding *));
+int cs4281_set_params __P((void *, int, int, struct audio_params *,
+ struct audio_params *));
+int cs4281_halt_output __P((void *));
+int cs4281_halt_input __P((void *));
+int cs4281_getdev __P((void *, struct audio_device *));
+int cs4281_trigger_output __P((void *, void *, void *, int,
+ void (*)(void *), void *,
+ struct audio_params *));
+int cs4281_trigger_input __P((void *, void *, void *, int,
+ void (*)(void *), void *,
+ struct audio_params *));
+
+u_int8_t cs4281_sr2regval __P((int));
+void cs4281_set_dac_rate __P((struct cs4281_softc *, int));
+void cs4281_set_adc_rate __P((struct cs4281_softc *, int));
+int cs4281_init __P((struct cs4281_softc *));
+
+int cs4281_open __P((void *, int));
+void cs4281_close __P((void *));
+int cs4281_round_blocksize __P((void *, int));
+int cs4281_get_props __P((void *));
+int cs4281_attach_codec __P((void *, struct ac97_codec_if *));
+int cs4281_read_codec __P((void *, u_int8_t , u_int16_t *));
+int cs4281_write_codec __P((void *, u_int8_t, u_int16_t));
+void cs4281_reset_codec __P((void *));
+
+void cs4281_power __P((int, void *));
+
+int cs4281_mixer_set_port __P((void *, mixer_ctrl_t *));
+int cs4281_mixer_get_port __P((void *, mixer_ctrl_t *));
+int cs4281_query_devinfo __P((void *, mixer_devinfo_t *));
+void *cs4281_malloc __P((void *, u_long, int, int));
+u_long cs4281_round_buffersize __P((void *, u_long));
+void cs4281_free __P((void *, void *, int));
+int cs4281_mappage __P((void *, void *, int, int));
+
+int cs4281_allocmem __P((struct cs4281_softc *, size_t, int, int,
+ struct cs4281_dma *));
+int cs4281_src_wait __P((struct cs4281_softc *));
+
+#if defined(CS4281_DEBUG)
+#undef DPRINTF
+#undef DPRINTFN
+#define DPRINTF(x) if (cs4281_debug) printf x
+#define DPRINTFN(n,x) if (cs4281_debug>(n)) printf x
+int cs4281_debug = 5;
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+struct audio_hw_if cs4281_hw_if = {
+ cs4281_open,
+ cs4281_close,
+ NULL,
+ cs4281_query_encoding,
+ cs4281_set_params,
+ cs4281_round_blocksize,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ cs4281_halt_output,
+ cs4281_halt_input,
+ NULL,
+ cs4281_getdev,
+ NULL,
+ cs4281_mixer_set_port,
+ cs4281_mixer_get_port,
+ cs4281_query_devinfo,
+ cs4281_malloc,
+ cs4281_free,
+ cs4281_round_buffersize,
+ NULL, /* cs4281_mappage, */
+ cs4281_get_props,
+ cs4281_trigger_output,
+ cs4281_trigger_input,
+};
+
+#if NMIDI > 0
+/* Midi Interface */
+void cs4281_midi_close __P((void *));
+void cs4281_midi_getinfo __P((void *, struct midi_info *));
+int cs4281_midi_open __P((void *, int, void (*)(void *, int),
+ void (*)(void *), void *));
+int cs4281_midi_output __P((void *, int));
+
+struct midi_hw_if cs4281_midi_hw_if = {
+ cs4281_midi_open,
+ cs4281_midi_close,
+ cs4281_midi_output,
+ cs4281_midi_getinfo,
+ 0,
+};
+#endif
+
+struct cfattach clct_ca = {
+ sizeof(struct cs4281_softc), cs4281_match, cs4281_attach
+};
+
+struct cfdriver clct_cd = {
+ NULL, "clct", DV_DULL
+};
+
+struct audio_device cs4281_device = {
+ "CS4281",
+ "",
+ "cs4281"
+};
+
+
+int
+cs4281_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_CIRRUS ||
+ PCI_PRODUCT(pa->pa_id) != PCI_PRODUCT_CIRRUS_CS4281)
+ return (0);
+
+ return (1);
+}
+
+void
+cs4281_attach(parent, self, aux)
+ struct device *parent;
+ struct device *self;
+ void *aux;
+{
+ struct cs4281_softc *sc = (struct cs4281_softc *)self;
+ struct pci_attach_args *pa = (struct pci_attach_args *)aux;
+ pci_chipset_tag_t pc = pa->pa_pc;
+ char const *intrstr;
+ pci_intr_handle_t ih;
+ pcireg_t csr;
+
+ /* Map I/O register */
+ if (pci_mapreg_map(pa, CSCC_PCI_BA0,
+ PCI_MAPREG_TYPE_MEM|PCI_MAPREG_MEM_TYPE_32BIT, 0, &sc->ba0t,
+ &sc->ba0h, NULL, NULL)) {
+ printf("%s: can't map BA0 space\n", sc->sc_dev.dv_xname);
+ return;
+ }
+ if (pci_mapreg_map(pa, CSCC_PCI_BA1,
+ PCI_MAPREG_TYPE_MEM|PCI_MAPREG_MEM_TYPE_32BIT, 0, &sc->ba1t,
+ &sc->ba1h, NULL, NULL)) {
+ printf("%s: can't map BA1 space\n", sc->sc_dev.dv_xname);
+ return;
+ }
+
+ sc->sc_dmatag = pa->pa_dmat;
+
+ /* Enable the device (set bus master flag) */
+ csr = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG);
+ pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG,
+ csr | PCI_COMMAND_MASTER_ENABLE);
+
+ /* Map and establish the interrupt. */
+ if (pci_intr_map(pc, pa->pa_intrtag, pa->pa_intrpin, pa->pa_intrline,
+ &ih)) {
+ printf("%s: couldn't map interrupt\n", sc->sc_dev.dv_xname);
+ return;
+ }
+ intrstr = pci_intr_string(pc, ih);
+
+ sc->sc_ih = pci_intr_establish(pc, ih, IPL_AUDIO, cs4281_intr, sc,
+ sc->sc_dev.dv_xname);
+ if (sc->sc_ih == NULL) {
+ printf("%s: couldn't establish interrupt",sc->sc_dev.dv_xname);
+ if (intrstr != NULL)
+ printf(" at %s", intrstr);
+ printf("\n");
+ return;
+ }
+ printf(" %s\n", intrstr);
+
+ /*
+ * Sound System start-up
+ */
+ if (cs4281_init(sc) != 0)
+ return;
+
+ sc->halt_input = cs4281_halt_input;
+ sc->halt_output = cs4281_halt_output;
+
+ sc->dma_size = CS4281_BUFFER_SIZE / MAX_CHANNELS;
+ sc->dma_align = 0x10;
+ sc->hw_blocksize = sc->dma_size / 2;
+
+ /* AC 97 attachment */
+ sc->host_if.arg = sc;
+ sc->host_if.attach = cs4281_attach_codec;
+ sc->host_if.read = cs4281_read_codec;
+ sc->host_if.write = cs4281_write_codec;
+ sc->host_if.reset = cs4281_reset_codec;
+ if (ac97_attach(&sc->host_if) != 0) {
+ printf("%s: ac97_attach failed\n", sc->sc_dev.dv_xname);
+ return;
+ }
+ audio_attach_mi(&cs4281_hw_if, sc, &sc->sc_dev);
+
+#if NMIDI > 0
+ midi_attach_mi(&cs4281_midi_hw_if, sc, &sc->sc_dev);
+#endif
+
+ sc->sc_suspend = PWR_RESUME;
+ sc->sc_powerhook = powerhook_establish(cs4281_power, sc);
+}
+
+
+int
+cs4281_intr(p)
+ void *p;
+{
+ struct cs4281_softc *sc = p;
+ u_int32_t intr, val;
+ char *empty_dma;
+
+ intr = BA0READ4(sc, CS4281_HISR);
+ if (!(intr & (HISR_DMA0 | HISR_DMA1 | HISR_MIDI))) {
+ BA0WRITE4(sc, CS4281_HICR, HICR_IEV | HICR_CHGM);
+ return (0);
+ }
+ DPRINTF(("cs4281_intr:"));
+
+ if (intr & HISR_DMA0)
+ val = BA0READ4(sc, CS4281_HDSR0); /* clear intr condition */
+ if (intr & HISR_DMA1)
+ val = BA0READ4(sc, CS4281_HDSR1); /* clear intr condition */
+ BA0WRITE4(sc, CS4281_HICR, HICR_IEV | HICR_CHGM);
+
+ /* Playback Interrupt */
+ if (intr & HISR_DMA0) {
+ DPRINTF((" PB DMA 0x%x(%d)", (int)BA0READ4(sc, CS4281_DCA0),
+ (int)BA0READ4(sc, CS4281_DCC0)));
+ if (sc->sc_pintr) {
+ if ((sc->sc_pi%sc->sc_pcount) == 0)
+ sc->sc_pintr(sc->sc_parg);
+ } else {
+ printf("unexpected play intr\n");
+ }
+ /* copy buffer */
+ ++sc->sc_pi;
+ empty_dma = sc->sc_pdma->addr;
+ if (sc->sc_pi&1)
+ empty_dma += sc->hw_blocksize;
+ memcpy(empty_dma, sc->sc_pn, sc->hw_blocksize);
+ sc->sc_pn += sc->hw_blocksize;
+ if (sc->sc_pn >= sc->sc_pe)
+ sc->sc_pn = sc->sc_ps;
+ }
+ if (intr & HISR_DMA1) {
+ val = BA0READ4(sc, CS4281_HDSR1);
+ /* copy from dma */
+ DPRINTF((" CP DMA 0x%x(%d)", (int)BA0READ4(sc, CS4281_DCA1),
+ (int)BA0READ4(sc, CS4281_DCC1)));
+ ++sc->sc_ri;
+ empty_dma = sc->sc_rdma->addr;
+ if ((sc->sc_ri & 1) == 0)
+ empty_dma += sc->hw_blocksize;
+ memcpy(sc->sc_rn, empty_dma, sc->hw_blocksize);
+ if (sc->sc_rn >= sc->sc_re)
+ sc->sc_rn = sc->sc_rs;
+ if (sc->sc_rintr) {
+ if ((sc->sc_ri % sc->sc_rcount) == 0)
+ sc->sc_rintr(sc->sc_rarg);
+ } else {
+ printf("unexpected record intr\n");
+ }
+ }
+ DPRINTF(("\n"));
+ return (1);
+}
+
+int
+cs4281_query_encoding(addr, fp)
+ void *addr;
+ struct audio_encoding *fp;
+{
+ switch (fp->index) {
+ case 0:
+ strcpy(fp->name, AudioEulinear);
+ fp->encoding = AUDIO_ENCODING_ULINEAR;
+ fp->precision = 8;
+ fp->flags = 0;
+ 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 = 0;
+ break;
+ case 6:
+ strcpy(fp->name, AudioEslinear_be);
+ fp->encoding = AUDIO_ENCODING_SLINEAR_BE;
+ fp->precision = 16;
+ fp->flags = 0;
+ break;
+ case 7:
+ strcpy(fp->name, AudioEulinear_be);
+ fp->encoding = AUDIO_ENCODING_ULINEAR_BE;
+ fp->precision = 16;
+ fp->flags = 0;
+ break;
+ default:
+ return EINVAL;
+ }
+ return (0);
+}
+
+int
+cs4281_set_params(addr, setmode, usemode, play, rec)
+ void *addr;
+ int setmode, usemode;
+ struct audio_params *play, *rec;
+{
+ struct cs4281_softc *sc = addr;
+ struct audio_params *p;
+ int mode;
+
+ for (mode = AUMODE_RECORD; mode != -1;
+ mode = mode == AUMODE_RECORD ? AUMODE_PLAY : -1) {
+ if ((setmode & mode) == 0)
+ continue;
+
+ p = mode == AUMODE_PLAY ? play : rec;
+
+ if (p == play) {
+ DPRINTFN(5,("play: samp=%ld precision=%d channels=%d\n",
+ p->sample_rate, p->precision, p->channels));
+ if (p->sample_rate < 6023 || p->sample_rate > 48000 ||
+ (p->precision != 8 && p->precision != 16) ||
+ (p->channels != 1 && p->channels != 2)) {
+ return (EINVAL);
+ }
+ } else {
+ DPRINTFN(5,("rec: samp=%ld precision=%d channels=%d\n",
+ p->sample_rate, p->precision, p->channels));
+ if (p->sample_rate < 6023 || p->sample_rate > 48000 ||
+ (p->precision != 8 && p->precision != 16) ||
+ (p->channels != 1 && p->channels != 2)) {
+ return (EINVAL);
+ }
+ }
+ p->factor = 1;
+ p->sw_code = 0;
+
+ switch (p->encoding) {
+ case AUDIO_ENCODING_SLINEAR_BE:
+ break;
+ case AUDIO_ENCODING_SLINEAR_LE:
+ break;
+ case AUDIO_ENCODING_ULINEAR_BE:
+ break;
+ case AUDIO_ENCODING_ULINEAR_LE:
+ break;
+ case AUDIO_ENCODING_ULAW:
+ if (mode == AUMODE_PLAY) {
+ p->sw_code = mulaw_to_slinear8;
+ } else {
+ p->sw_code = slinear8_to_mulaw;
+ }
+ break;
+ case AUDIO_ENCODING_ALAW:
+ if (mode == AUMODE_PLAY) {
+ p->sw_code = alaw_to_slinear8;
+ } else {
+ p->sw_code = slinear8_to_alaw;
+ }
+ break;
+ default:
+ return (EINVAL);
+ }
+ }
+
+ /* set sample rate */
+ cs4281_set_dac_rate(sc, play->sample_rate);
+ cs4281_set_adc_rate(sc, rec->sample_rate);
+ return (0);
+}
+
+int
+cs4281_halt_output(addr)
+ void *addr;
+{
+ struct cs4281_softc *sc = addr;
+
+ BA0WRITE4(sc, CS4281_DCR0, BA0READ4(sc, CS4281_DCR0) | DCRn_MSK);
+#ifdef DIAGNOSTIC
+ sc->sc_prun = 0;
+#endif
+ return (0);
+}
+
+int
+cs4281_halt_input(addr)
+ void *addr;
+{
+ struct cs4281_softc *sc = addr;
+
+ BA0WRITE4(sc, CS4281_DCR1, BA0READ4(sc, CS4281_DCR1) | DCRn_MSK);
+#ifdef DIAGNOSTIC
+ sc->sc_rrun = 0;
+#endif
+ return (0);
+}
+
+/* trivial */
+int
+cs4281_getdev(addr, retp)
+ void *addr;
+ struct audio_device *retp;
+{
+ *retp = cs4281_device;
+ return (0);
+}
+
+
+int
+cs4281_trigger_output(addr, start, end, blksize, intr, arg, param)
+ void *addr;
+ void *start, *end;
+ int blksize;
+ void (*intr) __P((void *));
+ void *arg;
+ struct audio_params *param;
+{
+ struct cs4281_softc *sc = addr;
+ u_int32_t fmt=0;
+ struct cs4281_dma *p;
+ int dma_count;
+
+#ifdef DIAGNOSTIC
+ if (sc->sc_prun)
+ printf("cs4281_trigger_output: already running\n");
+ sc->sc_prun = 1;
+#endif
+
+ DPRINTF(("cs4281_trigger_output: sc=%p start=%p end=%p "
+ "blksize=%d intr=%p(%p)\n", addr, start, end, blksize, intr, arg));
+ sc->sc_pintr = intr;
+ sc->sc_parg = arg;
+
+ /* stop playback DMA */
+ BA0WRITE4(sc, CS4281_DCR0, BA0READ4(sc, CS4281_DCR0) | DCRn_MSK);
+
+ DPRINTF(("param: precision=%d factor=%d channels=%d encoding=%d\n",
+ param->precision, param->factor, param->channels,
+ param->encoding));
+ for (p = sc->sc_dmas; p != NULL && BUFADDR(p) != start; p = p->next)
+ ;
+ if (p == NULL) {
+ printf("cs4281_trigger_output: bad addr %p\n", start);
+ return (EINVAL);
+ }
+
+ sc->sc_pcount = blksize / sc->hw_blocksize;
+ sc->sc_ps = (char *)start;
+ sc->sc_pe = (char *)end;
+ sc->sc_pdma = p;
+ sc->sc_pbuf = KERNADDR(p);
+ sc->sc_pi = 0;
+ sc->sc_pn = sc->sc_ps;
+ if (blksize >= sc->dma_size) {
+ sc->sc_pn = sc->sc_ps + sc->dma_size;
+ memcpy(sc->sc_pbuf, start, sc->dma_size);
+ ++sc->sc_pi;
+ } else {
+ sc->sc_pn = sc->sc_ps + sc->hw_blocksize;
+ memcpy(sc->sc_pbuf, start, sc->hw_blocksize);
+ }
+
+ dma_count = sc->dma_size;
+ if (param->precision * param->factor != 8)
+ dma_count /= 2; /* 16 bit */
+ if (param->channels > 1)
+ dma_count /= 2; /* Stereo */
+
+ DPRINTF(("cs4281_trigger_output: DMAADDR(p)=0x%x count=%d\n",
+ (int)DMAADDR(p), dma_count));
+ BA0WRITE4(sc, CS4281_DBA0, DMAADDR(p));
+ BA0WRITE4(sc, CS4281_DBC0, dma_count-1);
+
+ /* set playback format */
+ fmt = BA0READ4(sc, CS4281_DMR0) & ~DMRn_FMTMSK;
+ if (param->precision * param->factor == 8)
+ fmt |= DMRn_SIZE8;
+ if (param->channels == 1)
+ fmt |= DMRn_MONO;
+ if (param->encoding == AUDIO_ENCODING_ULINEAR_BE ||
+ param->encoding == AUDIO_ENCODING_SLINEAR_BE)
+ fmt |= DMRn_BEND;
+ if (param->encoding == AUDIO_ENCODING_ULINEAR_BE ||
+ param->encoding == AUDIO_ENCODING_ULINEAR_LE)
+ fmt |= DMRn_USIGN;
+ BA0WRITE4(sc, CS4281_DMR0, fmt);
+
+ /* set sample rate */
+ cs4281_set_dac_rate(sc, param->sample_rate);
+
+ /* start DMA */
+ BA0WRITE4(sc, CS4281_DCR0, BA0READ4(sc, CS4281_DCR0) & ~DCRn_MSK);
+ /* Enable interrupts */
+ BA0WRITE4(sc, CS4281_HICR, HICR_IEV | HICR_CHGM);
+
+ BA0WRITE4(sc, CS4281_PPRVC, 7);
+ BA0WRITE4(sc, CS4281_PPLVC, 7);
+
+ DPRINTF(("HICR =0x%08x(expected 0x00000001)\n", BA0READ4(sc, CS4281_HICR)));
+ DPRINTF(("HIMR =0x%08x(expected 0x00f0fc3f)\n", BA0READ4(sc, CS4281_HIMR)));
+ DPRINTF(("DMR0 =0x%08x(expected 0x2???0018)\n", BA0READ4(sc, CS4281_DMR0)));
+ DPRINTF(("DCR0 =0x%08x(expected 0x00030000)\n", BA0READ4(sc, CS4281_DCR0)));
+ DPRINTF(("FCR0 =0x%08x(expected 0x81000f00)\n", BA0READ4(sc, CS4281_FCR0)));
+ DPRINTF(("DACSR=0x%08x(expected 1 for 44kHz 5 for 8kHz)\n",
+ BA0READ4(sc, CS4281_DACSR)));
+ DPRINTF(("SRCSA=0x%08x(expected 0x0b0a0100)\n", BA0READ4(sc, CS4281_SRCSA)));
+ DPRINTF(("SSPM&SSPM_PSRCEN =0x%08x(expected 0x00000010)\n",
+ BA0READ4(sc, CS4281_SSPM) & SSPM_PSRCEN));
+
+ return (0);
+}
+
+int
+cs4281_trigger_input(addr, start, end, blksize, intr, arg, param)
+ void *addr;
+ void *start, *end;
+ int blksize;
+ void (*intr) __P((void *));
+ void *arg;
+ struct audio_params *param;
+{
+ struct cs4281_softc *sc = addr;
+ struct cs4281_dma *p;
+ u_int32_t fmt=0;
+ int dma_count;
+
+ printf("cs4281_trigger_input: not implemented yet\n");
+#ifdef DIAGNOSTIC
+ if (sc->sc_rrun)
+ printf("cs4281_trigger_input: already running\n");
+ sc->sc_rrun = 1;
+#endif
+ DPRINTF(("cs4281_trigger_input: sc=%p start=%p end=%p "
+ "blksize=%d intr=%p(%p)\n", addr, start, end, blksize, intr, arg));
+ sc->sc_rintr = intr;
+ sc->sc_rarg = arg;
+
+ /* stop recording DMA */
+ BA0WRITE4(sc, CS4281_DCR1, BA0READ4(sc, CS4281_DCR1) | DCRn_MSK);
+
+ for (p = sc->sc_dmas; p && BUFADDR(p) != start; p = p->next)
+ ;
+ if (!p) {
+ printf("cs4281_trigger_input: bad addr %p\n", start);
+ return (EINVAL);
+ }
+
+ sc->sc_rcount = blksize / sc->hw_blocksize;
+ sc->sc_rs = (char *)start;
+ sc->sc_re = (char *)end;
+ sc->sc_rdma = p;
+ sc->sc_rbuf = KERNADDR(p);
+ sc->sc_ri = 0;
+ sc->sc_rn = sc->sc_rs;
+
+ dma_count = sc->dma_size;
+ if (param->precision * param->factor == 8)
+ dma_count /= 2;
+ if (param->channels > 1)
+ dma_count /= 2;
+
+ DPRINTF(("cs4281_trigger_input: DMAADDR(p)=0x%x count=%d\n",
+ (int)DMAADDR(p), dma_count));
+ BA0WRITE4(sc, CS4281_DBA1, DMAADDR(p));
+ BA0WRITE4(sc, CS4281_DBC1, dma_count-1);
+
+ /* set recording format */
+ fmt = BA0READ4(sc, CS4281_DMR1) & ~DMRn_FMTMSK;
+ if (param->precision * param->factor == 8)
+ fmt |= DMRn_SIZE8;
+ if (param->channels == 1)
+ fmt |= DMRn_MONO;
+ if (param->encoding == AUDIO_ENCODING_ULINEAR_BE ||
+ param->encoding == AUDIO_ENCODING_SLINEAR_BE)
+ fmt |= DMRn_BEND;
+ if (param->encoding == AUDIO_ENCODING_ULINEAR_BE ||
+ param->encoding == AUDIO_ENCODING_ULINEAR_LE)
+ fmt |= DMRn_USIGN;
+ BA0WRITE4(sc, CS4281_DMR1, fmt);
+
+ /* set sample rate */
+ cs4281_set_adc_rate(sc, param->sample_rate);
+
+ /* Start DMA */
+ BA0WRITE4(sc, CS4281_DCR1, BA0READ4(sc, CS4281_DCR1) & ~DCRn_MSK);
+ /* Enable interrupts */
+ BA0WRITE4(sc, CS4281_HICR, HICR_IEV | HICR_CHGM);
+
+ DPRINTF(("HICR=0x%08x\n", BA0READ4(sc, CS4281_HICR)));
+ DPRINTF(("HIMR=0x%08x\n", BA0READ4(sc, CS4281_HIMR)));
+ DPRINTF(("DMR1=0x%08x\n", BA0READ4(sc, CS4281_DMR1)));
+ DPRINTF(("DCR1=0x%08x\n", BA0READ4(sc, CS4281_DCR1)));
+
+ return (0);
+}
+
+/* convert sample rate to register value */
+u_int8_t
+cs4281_sr2regval(rate)
+ int rate;
+{
+ u_int8_t retval;
+
+ /* We don't have to change here. but anyway ... */
+ if (rate > 48000)
+ rate = 48000;
+ if (rate < 6023)
+ rate = 6023;
+
+ switch (rate) {
+ case 8000:
+ retval = 5;
+ break;
+ case 11025:
+ retval = 4;
+ break;
+ case 16000:
+ retval = 3;
+ break;
+ case 22050:
+ retval = 2;
+ break;
+ case 44100:
+ retval = 1;
+ break;
+ case 48000:
+ retval = 0;
+ break;
+ default:
+ retval = 1536000/rate; /* == 24576000/(rate*16) */
+ }
+ return (retval);
+}
+
+
+void
+cs4281_set_dac_rate(sc, rate)
+ struct cs4281_softc *sc;
+ int rate;
+{
+ BA0WRITE4(sc, CS4281_DACSR, cs4281_sr2regval(rate));
+}
+
+void
+cs4281_set_adc_rate(sc, rate)
+ struct cs4281_softc *sc;
+ int rate;
+{
+ BA0WRITE4(sc, CS4281_ADCSR, cs4281_sr2regval(rate));
+}
+
+int
+cs4281_init(sc)
+ struct cs4281_softc *sc;
+{
+ int n;
+ u_int16_t data;
+ u_int32_t dat32;
+
+ /* set "Configuration Write Protect" register to
+ * 0x4281 to allow to write */
+ BA0WRITE4(sc, CS4281_CWPR, 0x4281);
+
+ /* Start PLL out in known state */
+ BA0WRITE4(sc, CS4281_CLKCR1, 0);
+ /* Start serial ports out in known state */
+ BA0WRITE4(sc, CS4281_SERMC, 0);
+
+ /* Reset codec */
+ BA0WRITE4(sc, CS4281_ACCTL, 0);
+ delay(50); /* delay 50us */
+
+ BA0WRITE4(sc, CS4281_SPMC, 0);
+ delay(100); /* delay 100us */
+ BA0WRITE4(sc, CS4281_SPMC, SPMC_RSTN);
+#if defined(ENABLE_SECONDARY_CODEC)
+ BA0WRITE4(sc, CS4281_SPMC, SPMC_RSTN | SPCM_ASDIN2E);
+ BA0WRITE4(sc, CS4281_SERMC, SERMC_TCID);
+#endif
+ delay(50000); /* XXX: delay 50ms */
+
+ /* Turn on Sound System clocks based on ABITCLK */
+ BA0WRITE4(sc, CS4281_CLKCR1, CLKCR1_DLLP);
+ delay(50000); /* XXX: delay 50ms */
+ BA0WRITE4(sc, CS4281_CLKCR1, CLKCR1_SWCE | CLKCR1_DLLP);
+
+ /* Set enables for sections that are needed in the SSPM registers */
+ BA0WRITE4(sc, CS4281_SSPM,
+ SSPM_MIXEN | /* Mixer */
+ SSPM_CSRCEN | /* Capture SRC */
+ SSPM_PSRCEN | /* Playback SRC */
+ SSPM_JSEN | /* Joystick */
+ SSPM_ACLEN | /* AC LINK */
+ SSPM_FMEN /* FM */
+ );
+
+ /* Wait for clock stabilization */
+ n = 0;
+ while ((BA0READ4(sc, CS4281_CLKCR1)& (CLKCR1_DLLRDY | CLKCR1_CLKON))
+ != (CLKCR1_DLLRDY | CLKCR1_CLKON)) {
+ delay(100);
+ if (++n > 1000)
+ return (-1);
+ }
+
+ /* Enable ASYNC generation */
+ BA0WRITE4(sc, CS4281_ACCTL, ACCTL_ESYN);
+
+ /* Wait for Codec ready. Linux driver wait 50ms here */
+ n = 0;
+ while((BA0READ4(sc, CS4281_ACSTS) & ACSTS_CRDY) == 0) {
+ delay(100);
+ if (++n > 1000)
+ return (-1);
+ }
+
+#if defined(ENABLE_SECONDARY_CODEC)
+ /* secondary codec ready*/
+ n = 0;
+ while((BA0READ4(sc, CS4281_ACSTS2) & ACSTS2_CRDY2) == 0) {
+ delay(100);
+ if (++n > 1000)
+ return (-1);
+ }
+#endif
+
+ /* Set the serial timing configuration */
+ /* XXX: undocumented but the Linux driver do this */
+ BA0WRITE4(sc, CS4281_SERMC, SERMC_PTCAC97);
+
+ /* Wait for Codec ready signal */
+ n = 0;
+ do {
+ delay(1000);
+ if (++n > 1000) {
+ printf("%s: Timeout waiting for Codec ready\n",
+ sc->sc_dev.dv_xname);
+ return -1;
+ }
+ dat32 = BA0READ4(sc, CS4281_ACSTS) & ACSTS_CRDY;
+ } while (dat32 == 0);
+
+ /* Enable Valid Frame output on ASDOUT */
+ BA0WRITE4(sc, CS4281_ACCTL, ACCTL_ESYN | ACCTL_VFRM);
+
+ /* Wait until Codec Calibration is finished. Codec register 26h */
+ n = 0;
+ do {
+ delay(1);
+ if (++n > 1000) {
+ printf("%s: Timeout waiting for Codec calibration\n",
+ sc->sc_dev.dv_xname);
+ return -1;
+ }
+ cs4281_read_codec(sc, AC97_REG_POWER, &data);
+ } while ((data & 0x0f) != 0x0f);
+
+ /* Set the serial timing configuration again */
+ /* XXX: undocumented but the Linux driver do this */
+ BA0WRITE4(sc, CS4281_SERMC, SERMC_PTCAC97);
+
+ /* Wait until we've sampled input slots 3 & 4 as valid */
+ n = 0;
+ do {
+ delay(1000);
+ if (++n > 1000) {
+ printf("%s: Timeout waiting for sampled input slots as valid\n",
+ sc->sc_dev.dv_xname);
+ return -1;
+ }
+ dat32 = BA0READ4(sc, CS4281_ACISV) & (ACISV_ISV3 | ACISV_ISV4);
+ } while (dat32 != (ACISV_ISV3 | ACISV_ISV4));
+
+ /* Start digital data transfer of audio data to the codec */
+ BA0WRITE4(sc, CS4281_ACOSV, (ACOSV_SLV3 | ACOSV_SLV4));
+
+ cs4281_write_codec(sc, AC97_REG_HEADPHONE_VOLUME, 0);
+ cs4281_write_codec(sc, AC97_REG_MASTER_VOLUME, 0);
+
+ /* Power on the DAC */
+ cs4281_read_codec(sc, AC97_REG_POWER, &data);
+ cs4281_write_codec(sc, AC97_REG_POWER, data &= 0xfdff);
+
+ /* Wait until we sample a DAC ready state.
+ * Not documented, but Linux driver does.
+ */
+ for (n = 0; n < 32; ++n) {
+ delay(1000);
+ cs4281_read_codec(sc, AC97_REG_POWER, &data);
+ if (data & 0x02)
+ break;
+ }
+
+ /* Power on the ADC */
+ cs4281_read_codec(sc, AC97_REG_POWER, &data);
+ cs4281_write_codec(sc, AC97_REG_POWER, data &= 0xfeff);
+
+ /* Wait until we sample ADC ready state.
+ * Not documented, but Linux driver does.
+ */
+ for (n = 0; n < 32; ++n) {
+ delay(1000);
+ cs4281_read_codec(sc, AC97_REG_POWER, &data);
+ if (data & 0x01)
+ break;
+ }
+
+#if 0
+ /* Initialize SSCR register features */
+ /* XXX: hardware volume setting */
+ BA0WRITE4(sc, CS4281_SSCR, ~SSCR_HVC); /* disable HW volume setting */
+#endif
+
+ /* disable Sound Blaster Pro emulation */
+ /* XXX:
+ * Cannot set since the documents does not describe which bit is
+ * correspond to SSCR_SB. Since the reset value of SSCR is 0,
+ * we can ignore it.*/
+#if 0
+ BA0WRITE4(sc, CS4281_SSCR, SSCR_SB);
+#endif
+
+ /* map AC97 PCM playback to DMA Channel 0 */
+ /* Reset FEN bit to setup first */
+ BA0WRITE4(sc, CS4281_FCR0, (BA0READ4(sc,CS4281_FCR0) & ~FCRn_FEN));
+ /*
+ *| RS[4:0]/| |
+ *| LS[4:0] | AC97 | Slot Function
+ *|---------+--------+--------------------
+ *| 0 | 3 | Left PCM Playback
+ *| 1 | 4 | Right PCM Playback
+ *| 2 | 5 | Phone Line 1 DAC
+ *| 3 | 6 | Center PCM Playback
+ *....
+ * quoted from Table 29(p109)
+ */
+ dat32 = 0x01 << 24 | /* RS[4:0] = 1 see above */
+ 0x00 << 16 | /* LS[4:0] = 0 see above */
+ 0x0f << 8 | /* SZ[6:0] = 15 size of buffer */
+ 0x00 << 0 ; /* OF[6:0] = 0 offset */
+ BA0WRITE4(sc, CS4281_FCR0, dat32);
+ BA0WRITE4(sc, CS4281_FCR0, dat32 | FCRn_FEN);
+
+ /* map AC97 PCM record to DMA Channel 1 */
+ /* Reset FEN bit to setup first */
+ BA0WRITE4(sc, CS4281_FCR1, (BA0READ4(sc,CS4281_FCR1) & ~FCRn_FEN));
+ /*
+ *| RS[4:0]/|
+ *| LS[4:0] | AC97 | Slot Function
+ *|---------+------+-------------------
+ *| 10 | 3 | Left PCM Record
+ *| 11 | 4 | Right PCM Record
+ *| 12 | 5 | Phone Line 1 ADC
+ *| 13 | 6 | Mic ADC
+ *....
+ * quoted from Table 30(p109)
+ */
+ dat32 = 0x0b << 24 | /* RS[4:0] = 11 See above */
+ 0x0a << 16 | /* LS[4:0] = 10 See above */
+ 0x0f << 8 | /* SZ[6:0] = 15 Size of buffer */
+ 0x10 << 0 ; /* OF[6:0] = 16 offset */
+
+ /* XXX: I cannot understand why FCRn_PSH is needed here. */
+ BA0WRITE4(sc, CS4281_FCR1, dat32 | FCRn_PSH);
+ BA0WRITE4(sc, CS4281_FCR1, dat32 | FCRn_FEN);
+
+#if 0
+ /* Disable DMA Channel 2, 3 */
+ BA0WRITE4(sc, CS4281_FCR2, (BA0READ4(sc,CS4281_FCR2) & ~FCRn_FEN));
+ BA0WRITE4(sc, CS4281_FCR3, (BA0READ4(sc,CS4281_FCR3) & ~FCRn_FEN));
+#endif
+
+ /* Set the SRC Slot Assignment accordingly */
+ /*| PLSS[4:0]/
+ *| PRSS[4:0] | AC97 | Slot Function
+ *|-----------+------+----------------
+ *| 0 | 3 | Left PCM Playback
+ *| 1 | 4 | Right PCM Playback
+ *| 2 | 5 | phone line 1 DAC
+ *| 3 | 6 | Center PCM Playback
+ *| 4 | 7 | Left Surround PCM Playback
+ *| 5 | 8 | Right Surround PCM Playback
+ *......
+ *
+ *| CLSS[4:0]/
+ *| CRSS[4:0] | AC97 | Codec |Slot Function
+ *|-----------+------+-------+-----------------
+ *| 10 | 3 |Primary| Left PCM Record
+ *| 11 | 4 |Primary| Right PCM Record
+ *| 12 | 5 |Primary| Phone Line 1 ADC
+ *| 13 | 6 |Primary| Mic ADC
+ *|.....
+ *| 20 | 3 | Sec. | Left PCM Record
+ *| 21 | 4 | Sec. | Right PCM Record
+ *| 22 | 5 | Sec. | Phone Line 1 ADC
+ *| 23 | 6 | Sec. | Mic ADC
+ */
+ dat32 = 0x0b << 24 | /* CRSS[4:0] Right PCM Record(primary) */
+ 0x0a << 16 | /* CLSS[4:0] Left PCM Record(primary) */
+ 0x01 << 8 | /* PRSS[4:0] Right PCM Playback */
+ 0x00 << 0; /* PLSS[4:0] Left PCM Playback */
+ BA0WRITE4(sc, CS4281_SRCSA, dat32);
+
+ /* Set interrupt to occured at Half and Full terminal
+ * count interrupt enable for DMA channel 0 and 1.
+ * To keep DMA stop, set MSK.
+ */
+ dat32 = DCRn_HTCIE | DCRn_TCIE | DCRn_MSK;
+ BA0WRITE4(sc, CS4281_DCR0, dat32);
+ BA0WRITE4(sc, CS4281_DCR1, dat32);
+
+ /* Set Auto-Initialize Contorl enable */
+ BA0WRITE4(sc, CS4281_DMR0,
+ DMRn_DMA | DMRn_AUTO | DMRn_TR_READ);
+ BA0WRITE4(sc, CS4281_DMR1,
+ DMRn_DMA | DMRn_AUTO | DMRn_TR_WRITE);
+
+ /* Clear DMA Mask in HIMR */
+ dat32 = BA0READ4(sc, CS4281_HIMR) & 0xfffbfcff;
+ BA0WRITE4(sc, CS4281_HIMR, dat32);
+ return (0);
+}
+
+void
+cs4281_power(why, v)
+ int why;
+ void *v;
+{
+ struct cs4281_softc *sc = (struct cs4281_softc *)v;
+ int i;
+
+ DPRINTF(("%s: cs4281_power why=%d\n", sc->sc_dev.dv_xname, why));
+ if (why != PWR_RESUME) {
+ sc->sc_suspend = why;
+
+ cs4281_halt_output(sc);
+ cs4281_halt_input(sc);
+ /* Save AC97 registers */
+ for (i = 1; i <= CS4281_SAVE_REG_MAX; i++) {
+ if (i == 0x04) /* AC97_REG_MASTER_TONE */
+ continue;
+ cs4281_read_codec(sc, 2*i, &sc->ac97_reg[i>>1]);
+ }
+ /* should I powerdown here ? */
+ cs4281_write_codec(sc, AC97_REG_POWER, CS4281_POWER_DOWN_ALL);
+ } else {
+ if (sc->sc_suspend == PWR_RESUME) {
+ printf("cs4281_power: odd, resume without suspend.\n");
+ sc->sc_suspend = why;
+ return;
+ }
+ sc->sc_suspend = why;
+ cs4281_init(sc);
+ cs4281_reset_codec(sc);
+
+ /* restore ac97 registers */
+ for (i = 1; i <= CS4281_SAVE_REG_MAX; i++) {
+ if (i == 0x04) /* AC97_REG_MASTER_TONE */
+ continue;
+ cs4281_write_codec(sc, 2*i, sc->ac97_reg[i>>1]);
+ }
+ }
+}
+
+void
+cs4281_reset_codec(void *addr)
+{
+ struct cs4281_softc *sc;
+ u_int16_t data;
+ u_int32_t dat32;
+ int n;
+
+ sc = addr;
+
+ DPRINTFN(3,("cs4281_reset_codec\n"));
+
+ /* Reset codec */
+ BA0WRITE4(sc, CS4281_ACCTL, 0);
+ delay(50); /* delay 50us */
+
+ BA0WRITE4(sc, CS4281_SPMC, 0);
+ delay(100); /* delay 100us */
+ BA0WRITE4(sc, CS4281_SPMC, SPMC_RSTN);
+#if defined(ENABLE_SECONDARY_CODEC)
+ BA0WRITE4(sc, CS4281_SPMC, SPMC_RSTN | SPCM_ASDIN2E);
+ BA0WRITE4(sc, CS4281_SERMC, SERMC_TCID);
+#endif
+ delay(50000); /* XXX: delay 50ms */
+
+ /* Enable ASYNC generation */
+ BA0WRITE4(sc, CS4281_ACCTL, ACCTL_ESYN);
+
+ /* Wait for Codec ready. Linux driver wait 50ms here */
+ n = 0;
+ while((BA0READ4(sc, CS4281_ACSTS) & ACSTS_CRDY) == 0) {
+ delay(100);
+ if (++n > 1000) {
+ printf("reset_codec: AC97 codec ready timeout\n");
+ return;
+ }
+ }
+#if defined(ENABLE_SECONDARY_CODEC)
+ /* secondary codec ready*/
+ n = 0;
+ while((BA0READ4(sc, CS4281_ACSTS2) & ACSTS2_CRDY2) == 0) {
+ delay(100);
+ if (++n > 1000)
+ return;
+ }
+#endif
+ /* Set the serial timing configuration */
+ /* XXX: undocumented but the Linux driver do this */
+ BA0WRITE4(sc, CS4281_SERMC, SERMC_PTCAC97);
+
+ /* Wait for Codec ready signal */
+ n = 0;
+ do {
+ delay(1000);
+ if (++n > 1000) {
+ printf("%s: Timeout waiting for Codec ready\n",
+ sc->sc_dev.dv_xname);
+ return;
+ }
+ dat32 = BA0READ4(sc, CS4281_ACSTS) & ACSTS_CRDY;
+ } while (dat32 == 0);
+
+ /* Enable Valid Frame output on ASDOUT */
+ BA0WRITE4(sc, CS4281_ACCTL, ACCTL_ESYN | ACCTL_VFRM);
+
+ /* Wait until Codec Calibration is finished. Codec register 26h */
+ n = 0;
+ do {
+ delay(1);
+ if (++n > 1000) {
+ printf("%s: Timeout waiting for Codec calibration\n",
+ sc->sc_dev.dv_xname);
+ return ;
+ }
+ cs4281_read_codec(sc, AC97_REG_POWER, &data);
+ } while ((data & 0x0f) != 0x0f);
+
+ /* Set the serial timing configuration again */
+ /* XXX: undocumented but the Linux driver do this */
+ BA0WRITE4(sc, CS4281_SERMC, SERMC_PTCAC97);
+
+ /* Wait until we've sampled input slots 3 & 4 as valid */
+ n = 0;
+ do {
+ delay(1000);
+ if (++n > 1000) {
+ printf("%s: Timeout waiting for sampled input slots as valid\n",
+ sc->sc_dev.dv_xname);
+ return;
+ }
+ dat32 = BA0READ4(sc, CS4281_ACISV) & (ACISV_ISV3 | ACISV_ISV4) ;
+ } while (dat32 != (ACISV_ISV3 | ACISV_ISV4));
+
+ /* Start digital data transfer of audio data to the codec */
+ BA0WRITE4(sc, CS4281_ACOSV, (ACOSV_SLV3 | ACOSV_SLV4));
+}
+
+int
+cs4281_open(void *addr, int flags)
+{
+ return (0);
+}
+
+void
+cs4281_close(void *addr)
+{
+ struct cs4281_softc *sc;
+
+ sc = addr;
+
+ (*sc->halt_output)(sc);
+ (*sc->halt_input)(sc);
+
+ sc->sc_pintr = 0;
+ sc->sc_rintr = 0;
+}
+
+int
+cs4281_round_blocksize(void *addr, int blk)
+{
+ struct cs4281_softc *sc;
+ int retval;
+
+ DPRINTFN(5,("cs4281_round_blocksize blk=%d -> ", blk));
+
+ sc=addr;
+ if (blk < sc->hw_blocksize)
+ retval = sc->hw_blocksize;
+ else
+ retval = blk & -(sc->hw_blocksize);
+
+ DPRINTFN(5,("%d\n", retval));
+
+ return (retval);
+}
+
+int
+cs4281_mixer_set_port(void *addr, mixer_ctrl_t *cp)
+{
+ struct cs4281_softc *sc;
+ int val;
+
+ sc = addr;
+ val = sc->codec_if->vtbl->mixer_set_port(sc->codec_if, cp);
+ DPRINTFN(3,("mixer_set_port: val=%d\n", val));
+ return (val);
+}
+
+int
+cs4281_mixer_get_port(void *addr, mixer_ctrl_t *cp)
+{
+ struct cs4281_softc *sc;
+
+ sc = addr;
+ return (sc->codec_if->vtbl->mixer_get_port(sc->codec_if, cp));
+}
+
+
+int
+cs4281_query_devinfo(void *addr, mixer_devinfo_t *dip)
+{
+ struct cs4281_softc *sc;
+
+ sc = addr;
+ return (sc->codec_if->vtbl->query_devinfo(sc->codec_if, dip));
+}
+
+void *
+cs4281_malloc(void *addr, u_long size, int pool, int flags)
+{
+ struct cs4281_softc *sc;
+ struct cs4281_dma *p;
+ int error;
+
+ sc = addr;
+
+ p = malloc(sizeof(*p), pool, flags);
+ if (!p)
+ return (0);
+
+ error = cs4281_allocmem(sc, size, pool, flags, p);
+
+ if (error) {
+ free(p, pool);
+ return (0);
+ }
+
+ p->next = sc->sc_dmas;
+ sc->sc_dmas = p;
+ return (BUFADDR(p));
+}
+
+
+
+void
+cs4281_free(void *addr, void *ptr, int pool)
+{
+ struct cs4281_softc *sc;
+ struct cs4281_dma **pp, *p;
+
+ sc = addr;
+ for (pp = &sc->sc_dmas; (p = *pp) != NULL; pp = &p->next) {
+ if (BUFADDR(p) == ptr) {
+ bus_dmamap_unload(sc->sc_dmatag, p->map);
+ bus_dmamap_destroy(sc->sc_dmatag, p->map);
+ bus_dmamem_unmap(sc->sc_dmatag, p->addr, p->size);
+ bus_dmamem_free(sc->sc_dmatag, p->segs, p->nsegs);
+ free(p->dum, pool);
+ *pp = p->next;
+ free(p, pool);
+ return;
+ }
+ }
+}
+
+u_long
+cs4281_round_buffersize(void *addr, u_long size)
+{
+ /* The real dma buffersize are 4KB for CS4280
+ * and 64kB/MAX_CHANNELS for CS4281.
+ * But they are too small for high quality audio,
+ * let the upper layer(audio) use a larger buffer.
+ * (originally suggested by Lennart Augustsson.)
+ */
+ return (size);
+}
+
+int
+cs4281_mappage(void *addr, void *mem, int off, int prot)
+{
+ struct cs4281_softc *sc;
+ struct cs4281_dma *p;
+
+ sc = addr;
+ if (off < 0)
+ return -1;
+
+ for (p = sc->sc_dmas; p && BUFADDR(p) != mem; p = p->next)
+ ;
+
+ if (!p) {
+ DPRINTF(("cs4281_mappage: bad buffer address\n"));
+ return (-1);
+ }
+
+ return (bus_dmamem_mmap(sc->sc_dmatag, p->segs, p->nsegs, off, prot,
+ BUS_DMA_WAITOK));
+}
+
+
+int
+cs4281_get_props(void *addr)
+{
+ int retval;
+
+ retval = AUDIO_PROP_INDEPENDENT | AUDIO_PROP_FULLDUPLEX;
+#ifdef MMAP_READY
+ retval |= AUDIO_PROP_MMAP;
+#endif
+ return (retval);
+}
+
+/* AC97 */
+int
+cs4281_attach_codec(void *addr, struct ac97_codec_if *codec_if)
+{
+ struct cs4281_softc *sc;
+
+ DPRINTF(("cs4281_attach_codec:\n"));
+ sc = addr;
+ sc->codec_if = codec_if;
+ return (0);
+}
+
+
+int
+cs4281_read_codec(void *addr, u_int8_t ac97_addr, u_int16_t *ac97_data)
+{
+ struct cs4281_softc *sc;
+ u_int32_t acctl;
+ int n;
+
+ sc = addr;
+
+ DPRINTFN(5,("read_codec: add=0x%02x ", ac97_addr));
+ /*
+ * Make sure that there is not data sitting around from a preivous
+ * uncompleted access.
+ */
+ BA0READ4(sc, CS4281_ACSDA);
+
+ /* Set up AC97 control registers. */
+ BA0WRITE4(sc, CS4281_ACCAD, ac97_addr);
+ BA0WRITE4(sc, CS4281_ACCDA, 0);
+
+ acctl = ACCTL_ESYN | ACCTL_VFRM | ACCTL_CRW | ACCTL_DCV;
+ BA0WRITE4(sc, CS4281_ACCTL, acctl);
+
+ if (cs4281_src_wait(sc) < 0) {
+ printf("%s: AC97 read prob. (DCV!=0) for add=0x%0x\n",
+ sc->sc_dev.dv_xname, ac97_addr);
+ return 1;
+ }
+
+ /* wait for valid status bit is active */
+ n = 0;
+ while ((BA0READ4(sc, CS4281_ACSTS) & ACSTS_VSTS) == 0) {
+ delay(1);
+ while (++n > 1000) {
+ printf("%s: AC97 read fail (VSTS==0) for add=0x%0x\n",
+ sc->sc_dev.dv_xname, ac97_addr);
+ return 1;
+ }
+ }
+ *ac97_data = BA0READ4(sc, CS4281_ACSDA);
+ DPRINTFN(5,("data=0x%04x\n", *ac97_data));
+ return (0);
+}
+
+int
+cs4281_write_codec(void *addr, u_int8_t ac97_addr, u_int16_t ac97_data)
+{
+ struct cs4281_softc *sc;
+ u_int32_t acctl;
+
+ sc = addr;
+
+ DPRINTFN(5,("write_codec: add=0x%02x data=0x%04x\n", ac97_addr, ac97_data));
+ BA0WRITE4(sc, CS4281_ACCAD, ac97_addr);
+ BA0WRITE4(sc, CS4281_ACCDA, ac97_data);
+
+ acctl = ACCTL_ESYN | ACCTL_VFRM | ACCTL_DCV;
+ BA0WRITE4(sc, CS4281_ACCTL, acctl);
+
+ if (cs4281_src_wait(sc) < 0) {
+ printf("%s: AC97 write fail (DCV!=0) for add=0x%02x data="
+ "0x%04x\n", sc->sc_dev.dv_xname, ac97_addr, ac97_data);
+ return (1);
+ }
+ return (0);
+}
+
+int
+cs4281_allocmem(struct cs4281_softc *sc, size_t size, int pool, int flags,
+ struct cs4281_dma *p)
+{
+ int error;
+ size_t align;
+
+ align = sc->dma_align;
+ p->size = sc->dma_size;
+ /* allocate memory for upper audio driver */
+ p->dum = malloc(size, pool, flags);
+ if (!p->dum)
+ return (1);
+ error = bus_dmamem_alloc(sc->sc_dmatag, p->size, align, 0,
+ p->segs, sizeof(p->segs)/sizeof(p->segs[0]),
+ &p->nsegs, BUS_DMA_NOWAIT);
+ if (error) {
+ printf("%s: unable to allocate dma. error=%d\n",
+ sc->sc_dev.dv_xname, error);
+ return (error);
+ }
+
+ error = bus_dmamem_map(sc->sc_dmatag, p->segs, p->nsegs, p->size,
+ &p->addr, BUS_DMA_NOWAIT|BUS_DMA_COHERENT);
+ if (error) {
+ printf("%s: unable to map dma, error=%d\n",
+ sc->sc_dev.dv_xname, error);
+ goto free;
+ }
+
+ error = bus_dmamap_create(sc->sc_dmatag, p->size, 1, p->size,
+ 0, BUS_DMA_NOWAIT, &p->map);
+ if (error) {
+ printf("%s: unable to create dma map, error=%d\n",
+ sc->sc_dev.dv_xname, error);
+ goto unmap;
+ }
+
+ error = bus_dmamap_load(sc->sc_dmatag, p->map, p->addr, p->size, NULL,
+ BUS_DMA_NOWAIT);
+ if (error) {
+ printf("%s: unable to load dma map, error=%d\n",
+ sc->sc_dev.dv_xname, error);
+ goto destroy;
+ }
+ return (0);
+
+destroy:
+ bus_dmamap_destroy(sc->sc_dmatag, p->map);
+unmap:
+ bus_dmamem_unmap(sc->sc_dmatag, p->addr, p->size);
+free:
+ bus_dmamem_free(sc->sc_dmatag, p->segs, p->nsegs);
+ return (error);
+}
+
+
+int
+cs4281_src_wait(sc)
+ struct cs4281_softc *sc;
+{
+ int n;
+
+ n = 0;
+ while ((BA0READ4(sc, CS4281_ACCTL) & ACCTL_DCV)) {
+ delay(1000);
+ while (++n > 1000)
+ return (-1);
+ }
+ return (0);
+}
diff --git a/sys/dev/pci/cs4281reg.h b/sys/dev/pci/cs4281reg.h
new file mode 100644
index 00000000000..73f2c39118e
--- /dev/null
+++ b/sys/dev/pci/cs4281reg.h
@@ -0,0 +1,323 @@
+/* $OpenBSD: cs4281reg.h,v 1.1 2001/01/13 19:53:50 aaron Exp $ */
+/* $Tera: cs4281reg.h,v 1.9 2000/12/31 10:52:25 tacha Exp $ */
+
+/*
+ * Copyright (c) 2000 Tatoku Ogaito. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Tatoku Ogaito
+ * for the NetBSD Project.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
+ */
+
+
+#define CS4281_BA0_SIZE
+#define CS4281_BA1_SIZE 0x10000
+#define CS4281_BUFFER_SIZE 0x10000
+
+/* Base Address 0 */
+
+/* Interrupt Reporting Registers */
+#define CS4281_HISR 0x000 /* Host Interrupt Status Register*/
+#define HISR_INTENA 0x80000000
+#define HISR_MIDI 0x00400000
+#define HISR_FIFOI 0x00100000
+#define HISR_DMAI 0x00040000
+#define HISR_FIFO3 0x00008000
+#define HISR_FIFO2 0x00004000
+#define HISR_FIFO1 0x00002000
+#define HISR_FIFO0 0x00001000
+#define HISR_DMA3 0x00000800
+#define HISR_DMA2 0x00000400
+#define HISR_DMA1 0x00000200
+#define HISR_DMA0 0x00000100
+#define HISR_GPPI 0x00000020
+#define HISR_GPSI 0x00000010
+#define HISR_GP3I 0x00000008
+#define HISR_GP1I 0x00000004
+#define HISR_VUPI 0x00000002
+#define HISR_VDNI 0x00000001
+
+#define CS4281_HICR 0x008 /* Host Interrupt Control Register */
+#define HICR_CHGM 0x00000002
+#define HICR_IEV 0x00000001
+
+#define CS4281_HIMR 0x00C /* Host Interrupt Mask Register */
+#define HIMR_MIDIM 0x00400000
+#define HIMR_FIFOIM 0x00100000
+#define HIMR_DMAIM 0x00040000
+#define HIMR_F3IM 0x00008000
+#define HIMR_F2IM 0x00004000
+#define HIMR_F1IM 0x00002000
+#define HIMR_F0IM 0x00001000
+#define HIMR_D3IM 0x00000800
+#define HIMR_D2IM 0x00000400
+#define HIMR_D1IM 0x00000200
+#define HIMR_D0IM 0x00000100
+#define HIMR_GPPIM 0x00000020
+#define HIMR_GPSIM 0x00000010
+#define HIMR_GP3IM 0x00000008
+#define HIMR_GP1IM 0x00000004
+#define HIMR_VUPIM 0x00000002
+#define HIMR_VDNIM 0x00000001
+
+#define CS4281_IIER 0x010 /* ISA Interrupt Enable Register */
+
+#define CS4281_HDSR0 0x0F0 /* Host DMA Engine 0 Status Register */
+#define CS4281_HDSR1 0x0F4 /* Host DMA Engine 1 Status Register */
+#define CS4281_HDSR2 0x0F8 /* Host DMA Engine 2 Status Register */
+#define CS4281_HDSR3 0x0FC /* Host DMA Engine 3 Status Register */
+#define CS4281_DCA0 0x110 /* DMA Engine 0 Current Address Register */
+#define CS4281_DCC0 0x114 /* DMA Engine 0 Current Count Register */
+#define CS4281_DBA0 0x118 /* DMA Engine 0 Base Address Register */
+#define CS4281_DBC0 0x11C /* DMA Engine 0 Base Count Register */
+#define CS4281_DCA1 0x120 /* DMA Engine 1 Current Address Register */
+#define CS4281_DCC1 0x124 /* DMA Engine 1 Current Count Register */
+#define CS4281_DBA1 0x128 /* DMA Engine 1 Base Address Register */
+#define CS4281_DBC1 0x12C /* DMA Engine 1 Base Count Register */
+#define CS4281_DCA2 0x130 /* DMA Engine 2 Current Address Register */
+#define CS4281_DCC2 0x134 /* DMA Engine 2 Current Count Register */
+#define CS4281_DBA2 0x138 /* DMA Engine 2 Base Address Register */
+#define CS4281_DBC2 0x13C /* DMA Engine 2 Base Count Register */
+#define CS4281_DCA3 0x140 /* DMA Engine 3 Current Address Register */
+#define CS4281_DCC3 0x144 /* DMA Engine 3 Current Count Register */
+#define CS4281_DBA3 0x148 /* DMA Engine 3 Base Address Register */
+#define CS4281_DBC3 0x14C /* DMA Engine 3 Base Count Register */
+#define CS4281_DMR0 0x150 /* DMA Engine 0 Mode Register */
+#define CS4281_DCR0 0x154 /* DMA Engine 0 Command Register */
+#define CS4281_DMR1 0x158 /* DMA Engine 1 Mode Register */
+#define CS4281_DCR1 0x15C /* DMA Engine 1 Command Register */
+#define CS4281_DMR2 0x160 /* DMA Engine 2 Mode Register */
+#define CS4281_DCR2 0x164 /* DMA Engine 2 Command Register */
+#define CS4281_DMR3 0x168 /* DMA Engine 3 Mode Register */
+#define CS4281_DCR3 0x16C /* DMA Engine 3 Command Register */
+/* DMRn common bit description*/
+#define DMRn_DMA 0x20000000
+#define DMRn_POLL 0x10000000
+#define DMRn_TBC 0x02000000
+#define DMRn_CBC 0x01000000
+#define DMRn_SWAPC 0x00400000
+#define DMRn_SIZE20 0x00100000
+#define DMRn_USIGN 0x00080000
+#define DMRn_BEND 0x00040000
+#define DMRn_MONO 0x00020000
+#define DMRn_SIZE8 0x00010000
+#define DMRn_FMTMSK ( DMRn_SWAPC | DMRn_SIZE20 | DMRn_USIGN | DMRn_BEND | DMRn_MONO | DMRn_SIZE8 )
+#define DMRn_TYPE1 0x00000080
+#define DMRn_TYPE0 0x00000040
+#define DMRn_DEC 0x00000020
+#define DMRn_AUTO 0x00000010
+#define DMRn_TR_MASK 0x0000000c
+#define DMRn_TR_READ 0x00000008
+#define DMRn_TR_WRITE 0x00000004
+/* DCRn common bit description*/
+#define DCRn_HTCIE 0x00020000 /* Half Terminal Count Interrupt Enable */
+#define DCRn_TCIE 0x00010000 /* Terminal Count Interrupt Enable */
+#define DCRn_MSK 0x00000001
+
+#define CS4281_FCR0 0x180 /* FIFO Control Register 0 */
+#define CS4281_FCR1 0x184 /* FIFO Control Register 1 */
+#define CS4281_FCR2 0x188 /* FIFO Control Register 2 */
+#define CS4281_FCR3 0x18C /* FIFO Control Register 3 */
+#define FCRn_FEN 0x80000000
+#define FCRn_DACZ 0x40000000
+#define FCRn_PSH 0x20000000
+
+#define CS4281_FPDR0 0x190 /* FIFO Polled Data Register 0 */
+#define CS4281_FPDR1 0x194 /* FIFO Polled Data Register 1 */
+#define CS4281_FPDR2 0x198 /* FIFO Polled Data Register 2 */
+#define CS4281_FPDR3 0x19C /* FIFO Polled Data Register 3 */
+#define CS4281_FCHS 0x20C /* FIFO Channel Status */
+#define CS4281_FSIC0 0x210 /* FIFO Status and Interrupt Control Register 0 */
+#define CS4281_FSIC1 0x214 /* FIFO Status and Interrupt Control Register 1 */
+#define CS4281_FSIC2 0x218 /* FIFO Status and Interrupt Control Register 2 */
+#define CS4281_FSIC3 0x21C /* FIFO Status and Interrupt Control Register 3 */
+
+#if 0
+300h - 340h /* PCI Configuration Space Echo, offsets 00h - 42h, RO */
+#endif
+
+#define CS4281_PMCS 0x344 /* Power Management Control/Status */
+#define CS4281_CWPR 0x3E0 /* Configuration Write Protect Register */
+#define CS4281_EPPMC 0x3E4 /* Extended PCI Power Management Control */
+#define CS4281_GPIOR 0x3E8 /* GPIO Pin Interface Register */
+#define CS4281_SPMC 0x3EC /* Serial Port Power Management Control (& ASDIN2 enable) */
+#define SPMC_RSTN 0x00000001
+#define SPMC_ASYN 0x00000002
+#define SPMC_WUP1 0x00000004
+#define SPMC_WUP2 0x00000008
+#define SPMC_ASDO 0x00000080
+#define SPMC_ASDI2E 0x00000100
+#define SPMC_EESPD 0x00000200
+#define SPMC_GISPEN 0x00000400
+#define SPMC_GIPPEN 0x00008000
+#define CS4281_CFLR 0x3F0 /* Configuration Load Register (EEPROM or BIOS) */
+#define CS4281_IISR 0x3F4 /* ISA Interrupt Select Register */
+#define CS4281_TMS 0x3F8 /* Test Register - Reserved */
+
+#define CS4281_SSVID 0x3FC /* Subsystem ID register (read-only version at 32Ch) */
+
+#define CS4281_CLKCR1 0x400 /* Clock Control Register 1 */
+#define CLKCR1_DLLSS0 0x00000004
+#define CLKCR1_DLLSS1 0x00000008
+#define CLKCR1_DLLP 0x00000010
+#define CLKCR1_SWCE 0x00000020
+#define CLKCR1_DLLOS 0x00000040
+#define CLKCR1_CKRA 0x00010000
+#define CLKCR1_CKRN 0x00020000
+#define CLKCR1_DLLRDY 0x01000000
+#define CLKCR1_CLKON 0x02000000
+
+#define CS4281_FRR 0x410 /* Feature Reporting Register */
+#define CS4281_SLT12O 0x41C /* Slot 12 GPIO Output Register for AC Link */
+
+#define CS4281_SERMC 0x420 /* Serial Port Master Control Register */
+#define SERMC_MSPE 0x00000001
+#define SERMC_PTCMASK 0x0000000E
+#define SERMC_PTCAC97 0x00000002
+#define SERMC_PLB 0x00000100
+#define SERMC_PXLB 0x00000200
+#define SERMC_TCID0 0x00010000
+#define SERMC_TICD1 0x00020000
+#define SERMC_LOVF 0x00080000
+#define SERMC_SLB 0x00100000
+#define SERMC_SXLB 0x00200000
+#define SERMC_ODSEN1 0x01000000
+#define SERMC_ODSEN2 0x02000000
+#define SERMC_FCRN 0x08000000
+#define CS4281_SERC1 0x428 /* Serial Port Configuration Register 1 - RO */
+#define CS4281_SERC2 0x42C /* Serial Port Configuration Register 2 - RO */
+
+#define CS4281_SLT12M 0x45C /* Slot 12 Monitor Register for Primary AC Link */
+
+/*
+ * AC97 Registers are moved to cs428xreg.h since
+ * they are common for CS4280 and CS4281
+ */
+
+#define CS4281_JSPT 0x480 /* Joystick Poll/Trigger Register */
+#define CS4281_JSCTL 0x484 /* Joystick Control Register */
+#define CS4281_MIDCR 0x490 /* MIDI Control Register */
+#define CS4281_MIDCMD 0x494 /* MIDI Command Register - WO */
+#define CS4281_MIDSR 0x494 /* MIDI Status Register - RO */
+#define CS4281_MIDWP 0x498 /* MIDI Write Port */
+#define CS4281_MIDRP 0x49C /* MIDI Read Port - RO */
+#define CS4281_AODSD1 0x4A8 /* AC `97 On-Demand Slot Disable for primary link - RO */
+#define CS4281_AODSD2 0x4AC /* AC `97 On-Demand Slot Disable for secondary link - RO */
+#define CS4281_CFGI 0x4B0 /* Configuration Interface Register (EEPROM interface) */
+#define CS4281_SLT12M2 0x4DC /* Slot 12 Monitor Register 2 for Secondary AC Link */
+#define CS4281_ACSTS2 0x4E4 /* AC 97 Status Register 2 */
+#define ACSTS2_CRDY2 0x00000001
+#define ACSTS2_BSYS2 0x00000002
+#define CS4281_ACISV2 0x4F4 /* AC 97 Input Slot Valid Register 2 */
+#define CS4281_ACSAD2 0x4F8 /* AC 97 Status Address Register 2 */
+#define CS4281_ACSDA2 0x4FC /* AC 97 Status Data Register 2 */
+#define CS4281_FMSR 0x730 /* FM Synthesis Status Register - RO */
+#define CS4281_B0AP 0x730 /* FM Bank 0 Address Port - WO */
+#define CS4281_FMDP 0x734 /* FM Data Port */
+#define CS4281_B1AP 0x738 /* FM Bank 1 Address Port */
+#define CS4281_B1DP 0x73C /* FM Bank 1 Data Port */
+#define CS4281_SSPM 0x740 /* Sound System Power Management */
+#define SSPM_ALL 0x0000007E
+#define SSPM_MIXEN 0x00000040 /* p167 */
+#define SSPM_CSRCEN 0x00000020
+#define SSPM_PSRCEN 0x00000010
+#define SSPM_JSEN 0x00000008
+#define SSPM_ACLEN 0x00000004
+#define SSPM_FMEN 0x00000002
+
+#define CS4281_DACSR 0x744 /* DAC Sample Rate - Playback SRC */
+#define CS4281_ADCSR 0x748 /* ADC Sample Rate - Capture SRC */
+#define CS4281_SSCR 0x74C /* Sound System Control Register */
+#define SSCR_HVS1 0x00800000 /* Hardware Volume step by 1 */
+#define SSCR_MVCS 0x00080000 /* Master Volume Codec Select */
+#define SSCR_MVLD 0x00040000 /* Master Volume Line Out Disable */
+#define SSCR_MVAD 0x00020000 /* Master Volume Alternate Out Disable */
+#define SSCR_MVMD 0x00010000 /* Master Volume Mono Out Disable */
+#define SSCR_XLPSRC 0x00000100 /* External SRC loopback mode */
+#define SSCR_LPSRC 0x00000080 /* SRC loopback mode */
+#define SSCR_CDTX 0x00000020 /* CD Transfer Data */
+#define SSCR_HVC 0x00000008 /* Hardware Volume Control Enable */
+#define CS4281_FMLVC 0x754 /* FM Synthesis Left Volume Control */
+#define CS4281_FMRVC 0x758 /* FM Synthesis Right Volume Control */
+#define CS4281_SRCSA 0x75C /* SRC Slot Assignments */
+#define CS4281_PPLVC 0x760 /* PCM Playback Left Volume Control */
+#define CS4281_PPRVC 0x764 /* PCM Playback Right Volume Control */
+
+/* Base Address 1 Direct Memory Map */
+
+#if 0
+0000h - 03FFh FIFO RAM Audio Sample RAM Memory Block - FIFOs
+ Logical Size: 256 x 32 bits (1 kbytes stereo double words)
+0400h - D51Fh Reserved Reserved internal memory
+D600h - FFFFh Reserved Reserved for future use
+#endif
+
+#define CS4281_ACCTL 0x460 /* AC97 Control Register */
+#define ACCTL_RSTN 0x00000001 /* Only for CS4280 */
+#define ACCTL_ESYN 0x00000002
+#define ACCTL_VFRM 0x00000004
+#define ACCTL_DCV 0x00000008
+#define ACCTL_CRW 0x00000010
+#define ACCTL_ASYN 0x00000020 /* Only for CS4280 */
+#define ACCTL_TC 0x00000040
+
+#define CS4281_ACSTS 0x464 /* AC97 Status Register */
+#define ACSTS_CRDY 0x00000001
+#define ACSTS_VSTS 0x00000002
+
+#define CS4281_ACOSV 0x468 /* AC97 Output Slot Valid Register */
+#define ACOSV_SLV3 0x00000001
+#define ACOSV_SLV4 0x00000002
+#define ACOSV_SLV5 0x00000004
+#define ACOSV_SLV6 0x00000008
+#define ACOSV_SLV7 0x00000010
+#define ACOSV_SLV8 0x00000020
+#define ACOSV_SLV9 0x00000040
+#define ACOSV_SLV10 0x00000080
+#define ACOSV_SLV11 0x00000100
+#define ACOSV_SLV12 0x00000200
+
+#define CS4281_ACCAD 0x46c /* AC97 Command Address Register */
+#define CS4281_ACCDA 0x470 /* AC97 Command Data Register */
+
+#define CS4281_ACISV 0x474 /* AC97 Input Slot Valid Register */
+#define ACISV_ISV3 0x00000001
+#define ACISV_ISV4 0x00000002
+#define ACISV_ISV5 0x00000004
+#define ACISV_ISV6 0x00000008
+#define ACISV_ISV7 0x00000010
+#define ACISV_ISV8 0x00000020
+#define ACISV_ISV9 0x00000040
+#define ACISV_ISV10 0x00000080
+#define ACISV_ISV11 0x00000100
+#define ACISV_ISV12 0x00000200
+#define CS4281_ACSAD 0x478 /* AC97 Status Address Register */
+#define CS4281_ACSDA 0x47c /* AC97 Status Data Register */
+
+/* AC97 Registers */
+#define CS4281_SAVE_REG_MAX 0x10
+
+/* for AC97_REG_POWER */
+#define CS4281_POWER_DOWN_ALL 0x7f0f
diff --git a/sys/dev/pci/files.pci b/sys/dev/pci/files.pci
index bad03323099..ea40d9b9230 100644
--- a/sys/dev/pci/files.pci
+++ b/sys/dev/pci/files.pci
@@ -1,4 +1,4 @@
-# $OpenBSD: files.pci,v 1.89 2001/01/11 23:36:38 espie Exp $
+# $OpenBSD: files.pci,v 1.90 2001/01/13 19:53:50 aaron 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.
@@ -90,6 +90,11 @@ device clcs: audio, auconv, mulaw, ac97
attach clcs at pci
file dev/pci/cs4280.c clcs
+# CS4281 CrystalClear Audio
+device clct: audio, auconv, mulaw, ac97
+attach clct at pci
+file dev/pci/cs4281.c clct
+
# ESS Maestro
device maestro: audio, auconv, mulaw, ac97
attach maestro at pci