diff options
-rw-r--r-- | share/man/man4/Makefile | 8 | ||||
-rw-r--r-- | share/man/man4/audio.4 | 4 | ||||
-rw-r--r-- | share/man/man4/clct.4 | 23 | ||||
-rw-r--r-- | share/man/man4/pci.4 | 4 | ||||
-rw-r--r-- | sys/arch/i386/conf/GENERIC | 4 | ||||
-rw-r--r-- | sys/dev/pci/cs4281.c | 1579 | ||||
-rw-r--r-- | sys/dev/pci/cs4281reg.h | 323 | ||||
-rw-r--r-- | sys/dev/pci/files.pci | 7 |
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 |