Date: Thu, 1 Jul 2004 18:32:25 +0900 From: Pyun YongHyeon <yongari@kt-is.co.kr> To: sparc64@freebsd.org Subject: pcm sound driver for SBus Ultra1/Ultra2 Message-ID: <20040701093225.GA12517@kt-is.co.kr>
next in thread | raw e-mail | index | archive | help
--/9DWx/yDrRhgMJTb Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Hello All, I don't know how may users want to hear sound on Ultra1/Ultra2. But I wanted to hear some music while 'build world' is in progress. <Driver summary> 1. The driver is based on OpenBSD's driver and ISA mss driver on FreeBSD. 2. Supports onboard CS4231A chip on SBus Ultra1/Ultra2. 3. Capture(recodring) was not tested at all. - I don't have microphone. 4. Due to lack of programming information for APC DMA, I used existing interfaces of OpenBSD driver. So it may have some bugs on FreeBSD. 5. Full-duplex mode doesn't work. 6. You may notice some noise when you work on ofw console. I don't know what is the reason, atm. 7. Due to lack of X supports, I could't test with xmms or GUI based audio tools. 8. If you have PCI/EBus based sparcs, they are not supported. Yes, they use the same chip but have different DMA interface. In addition, I don't have PCI/EBus based sparcs. Installation 1. get the driver patch file and fetch your system 2. build kernel and kernel modules 3. load snd_cs4231.ko and play Note, you may want to mute speaker output with mixer(8). The attached patch is for -CURRENT, and is also available at: http://www.kr.freebsd.org/~yongari/cs4231.freebsd.diff Corrections, suggestions welcome. Thanks. Regards, Pyun YongHyeon -- Pyun YongHyeon <http://www.kr.freebsd.org/~yongari> --/9DWx/yDrRhgMJTb Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="cs4231.freebsd.diff" --- sys/dev/sound/isa/sndbuf_dma.c.orig Mon Sep 8 01:28:02 2003 +++ sys/dev/sound/isa/sndbuf_dma.c Tue Jun 29 12:47:14 2004 @@ -30,6 +30,44 @@ SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/isa/sndbuf_dma.c,v 1.2 2003/09/07 16:28:02 cg Exp $"); +#ifdef __sparc64__ +/* + * XXX + * Sparc64 don't have ISA bus. Temp. glue code to load pcm module. + * pcm(4) should be architecture independent. + */ +int +sndbuf_dmasetup(struct snd_dbuf *b, struct resource *drq) +{ + return (0); +} + +int +sndbuf_dmasetdir(struct snd_dbuf *b, int dir) +{ + return (0); +} + +void +sndbuf_dma(struct snd_dbuf *b, int go) +{ + return; +} + +int +sndbuf_dmaptr(struct snd_dbuf *b) +{ + return (0); +} + +void +sndbuf_dmabounce(struct snd_dbuf *b) +{ + return; +} + +#else + int sndbuf_dmasetup(struct snd_dbuf *b, struct resource *drq) { @@ -101,3 +139,4 @@ /* tell isa_dma to bounce data in/out */ } +#endif --- /dev/null Thu Jul 1 18:00:00 2004 +++ sys/dev/sound/sbus/apcdmareg.h Wed Jun 30 18:26:26 2004 @@ -0,0 +1,113 @@ +/* $OpenBSD: apcdmareg.h,v 1.2 2003/06/02 18:53:18 jason Exp $ */ + +/* + * Copyright (c) 2001 Jason L. Wright (jason@thought.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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. + */ + +/* + * Definitions for Sun APC DMA controller. + */ + +/* APC DMA registers */ +#define APC_CSR 0x0010 /* control/status */ +#define APC_CVA 0x0020 /* capture virtual address */ +#define APC_CC 0x0024 /* capture count */ +#define APC_CNVA 0x0028 /* capture next virtual address */ +#define APC_CNC 0x002c /* capture next count */ +#define APC_PVA 0x0030 /* playback virtual address */ +#define APC_PC 0x0034 /* playback count */ +#define APC_PNVA 0x0038 /* playback next virtual address */ +#define APC_PNC 0x003c /* playback next count */ + +/* + * APC DMA Register definitions + */ +#define APC_CSR_RESET 0x00000001 /* reset */ +#define APC_CSR_CDMA_GO 0x00000004 /* capture dma go */ +#define APC_CSR_PDMA_GO 0x00000008 /* playback dma go */ +#define APC_CSR_CODEC_RESET 0x00000020 /* codec reset */ +#define APC_CSR_CPAUSE 0x00000040 /* capture dma pause */ +#define APC_CSR_PPAUSE 0x00000080 /* playback dma pause */ +#define APC_CSR_CMIE 0x00000100 /* capture pipe empty enb */ +#define APC_CSR_CMI 0x00000200 /* capture pipe empty intr */ +#define APC_CSR_CD 0x00000400 /* capture nva dirty */ +#define APC_CSR_CM 0x00000800 /* capture data lost */ +#define APC_CSR_PMIE 0x00001000 /* pb pipe empty intr enable */ +#define APC_CSR_PD 0x00002000 /* pb nva dirty */ +#define APC_CSR_PM 0x00004000 /* pb pipe empty */ +#define APC_CSR_PMI 0x00008000 /* pb pipe empty interrupt */ +#define APC_CSR_EIE 0x00010000 /* error interrupt enable */ +#define APC_CSR_CIE 0x00020000 /* capture intr enable */ +#define APC_CSR_PIE 0x00040000 /* playback intr enable */ +#define APC_CSR_GIE 0x00080000 /* general intr enable */ +#define APC_CSR_EI 0x00100000 /* error interrupt */ +#define APC_CSR_CI 0x00200000 /* capture interrupt */ +#define APC_CSR_PI 0x00400000 /* playback interrupt */ +#define APC_CSR_GI 0x00800000 /* general interrupt */ + +#define APC_CSR_PLAY ( \ + APC_CSR_EI | \ + APC_CSR_GIE | \ + APC_CSR_PIE | \ + APC_CSR_EIE | \ + APC_CSR_PDMA_GO | \ + APC_CSR_PMIE ) + +#define APC_CSR_CAPTURE ( \ + APC_CSR_EI | \ + APC_CSR_GIE | \ + APC_CSR_CIE | \ + APC_CSR_EIE | \ + APC_CSR_CDMA_GO ) + +#define APC_CSR_PLAY_PAUSE (~( \ + APC_CSR_PPAUSE | \ + APC_CSR_GI | \ + APC_CSR_PI | \ + APC_CSR_CI | \ + APC_CSR_EI | \ + APC_CSR_PMI | \ + APC_CSR_PMIE | \ + APC_CSR_CMI | \ + APC_CSR_CMIE ) ) + +#define APC_CSR_CAPTURE_PAUSE (~( \ + APC_CSR_PPAUSE | \ + APC_CSR_GI | \ + APC_CSR_PI | \ + APC_CSR_CI | \ + APC_CSR_EI | \ + APC_CSR_PMI | \ + APC_CSR_PMIE | \ + APC_CSR_CMI | \ + APC_CSR_CMIE ) ) + +#define APC_CSR_INTR_MASK ( \ + APC_CSR_GI | \ + APC_CSR_PI | \ + APC_CSR_CI | \ + APC_CSR_EI | \ + APC_CSR_PMI | \ + APC_CSR_CMI ) --- /dev/null Thu Jul 1 18:00:00 2004 +++ sys/dev/sound/sbus/cs4231.c Thu Jul 1 18:02:22 2004 @@ -0,0 +1,1030 @@ +/* + * Copyright (c) 1999 Jason L. Wright (jason@thought.net) + * Copyright (c) 2004 Pyun YongHyeon <yongari@kt-is.co.kr> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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. + * + * Effort sponsored in part by the Defense Advanced Research Projects + * Agency (DARPA) and Air Force Research Laboratory, Air Force + * Materiel Command, USAF, under agreement number F30602-01-2-0537. + * + * from: OpenBSD: cs4231.c,v 1.21 2003/07/03 20:36:07 jason Exp + */ + +/* + * Driver for CS4231 based audio found in some sun4m systems (cs4231) + * based on ideas from the S/Linux project and the NetBSD project. + */ +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/kernel.h> +#include <sys/resource.h> + +#include <machine/bus.h> +#include <dev/ofw/openfirm.h> +#include <machine/ofw_machdep.h> + +#include <dev/sound/pcm/sound.h> +#include <dev/sound/isa/mss.h> +#include <dev/sound/sbus/apcdmareg.h> +#include <dev/sound/sbus/cs4231.h> + +#include <sparc64/sbus/sbusvar.h> + +#include "mixer_if.h" + +/* + * This driver is for Sbus based sparcs(Ultra1 and Ultra2). If you have + * PCI/EBus based sparcs it is not supported by this driver. + * (We need another driver for those systems. Yes, they use the same chip + * cs4231A, but has different DMA engine and I don't have PCI/EBus based + * sparcs. + * Mixer routines were copied from ISA mss driver and modified. + * + * Though, cs4231 says it supports full-duplex mode, I doubt it due to + * lack of independent sampling frequency register. In addition, I can't + * find any documentation of Sun APC DMA programming information. I guessed + * the usage of APC DMA from existing OpenBSD's driver. + * + * Audio capture(recording) was not tested at all and may have bugs. + * Sorry, I don't have microphone. Don't try to use full-duplex mode. + * It wouldn't work. + */ +#define CS_TIMEOUT 90000 + +#define CS4231_MIN_BUF_SZ (8*1024) +#define CS4231_DEFAULT_BUF_SZ (32*1024) +#define CS4231_MAX_BUF_SZ (64*1024) +/* It seems that 4KB is the best value. */ +#define CS4231_MAX_DMA_SZ (4*1024) + + +#undef CS4231_DEBUG +#ifdef CS4231_DEBUG +#define DPRINTF(x) printf x +#else +#define DPRINTF(x) +#endif + +struct cs4231_softc; + +struct cs4231_channel { + struct cs4231_softc *parent; + struct pcm_channel *channel; + struct snd_dbuf *buffer; + int dir; + int locked; +}; + +struct cs4231_softc { + struct device *sc_dev; + int sc_rid; + struct resource *sc_res; + bus_space_handle_t sc_regh; + bus_space_tag_t sc_regt; + + int sc_irqrid; + struct resource *sc_irqres; + void *sc_ih; + bus_dma_tag_t sc_dmat; + u_int32_t sc_bufsz; + struct cs4231_channel sc_pch; + struct cs4231_channel sc_rch; + int sc_enabled; + struct mtx *sc_lock; +}; + +static int cs4231_sbus_probe(device_t); +static int cs4231_sbus_attach(device_t); +static int cs4231_sbus_detach(device_t); +static int cs4231_sbus_suspend(device_t); +static int cs4231_sbus_resume(device_t); +static void cs4231_free_resource(struct cs4231_softc *); +static void cs4231_power_reset(struct cs4231_softc *); +static int cs4231_enable(struct cs4231_softc *); +static void cs4231_disable(struct cs4231_softc *); +static void cs4231_write(struct cs4231_softc *, u_int8_t, u_int8_t); +static u_int8_t cs4231_read(struct cs4231_softc *, u_int8_t); +static void cs4231_intr(void *); +static int cs4231_mixer_init(struct snd_mixer *); +static void change_bits(mixer_tab *, u_int8_t *, u_int32_t, u_int32_t, + u_int32_t); +static int cs4231_mixer_set(struct snd_mixer *, u_int32_t, u_int32_t, + u_int32_t); +static int cs4231_mixer_setrecsrc(struct snd_mixer *, u_int32_t); +static void *cs4231_chan_init(kobj_t, void *, struct snd_dbuf *, + struct pcm_channel *, int); +static int cs4231_chan_setformat(kobj_t, void *, u_int32_t); +static int cs4231_chan_setspeed(kobj_t, void *, u_int32_t); +static void cs4231_chan_fs(struct cs4231_softc *, u_int8_t); +static int cs4231_chan_setblocksize(kobj_t, void *, u_int32_t); +static int cs4231_chan_trigger(kobj_t, void *, int); +static int cs4231_chan_getptr(kobj_t, void *); +static struct pcmchan_caps * + cs4231_chan_getcaps(kobj_t, void *); +static void cs4231_trigger(struct cs4231_channel *); +static void cs4231_halt(struct cs4231_channel *); + +#define CS4231_LOCK(sc) snd_mtxlock(sc->sc_lock) +#define CS4231_UNLOCK(sc) snd_mtxunlock(sc->sc_lock) +#define CS4231_LOCK_ASSERT(sc) snd_mtxassert(sc->sc_lock) + +#define CS_WRITE(sc,r,v) \ + bus_space_write_1((sc)->sc_regt, (sc)->sc_regh, (r) << 2, (v)) +#define CS_READ(sc,r) \ + bus_space_read_1((sc)->sc_regt, (sc)->sc_regh, (r) << 2) + +#define APC_WRITE(sc,r,v) \ + bus_space_write_4(sc->sc_regt, sc->sc_regh, r, v) +#define APC_READ(sc,r) \ + bus_space_read_4(sc->sc_regt, sc->sc_regh, r) + +static device_method_t cs4231_sbus_methods[] = { + DEVMETHOD(device_probe, cs4231_sbus_probe), + DEVMETHOD(device_attach, cs4231_sbus_attach), + DEVMETHOD(device_detach, cs4231_sbus_detach), + DEVMETHOD(device_suspend, cs4231_sbus_suspend), + DEVMETHOD(device_resume, cs4231_sbus_resume), + {0, 0} +}; + +static driver_t cs4231_sbus_driver = { + "pcm", + cs4231_sbus_methods, + PCM_SOFTC_SIZE +}; + +DRIVER_MODULE(snd_cs4231, sbus, cs4231_sbus_driver, pcm_devclass, 0, 0); +MODULE_DEPEND(snd_cs4231, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER); +MODULE_VERSION(snd_cs4231, 1); + + +static u_int32_t cs4231_fmt[] = { + AFMT_U8, + AFMT_STEREO | AFMT_U8, + AFMT_S16_LE, + AFMT_STEREO | AFMT_S16_LE, + AFMT_MU_LAW, + AFMT_STEREO | AFMT_MU_LAW, + AFMT_A_LAW, + AFMT_STEREO | AFMT_A_LAW, + 0 +}; + +static struct pcmchan_caps cs4231_caps = {5510, 48000, cs4231_fmt, 0}; + +/* + * pcm(4) channel interface + */ +static kobj_method_t cs4231_chan_methods[] = { + KOBJMETHOD(channel_init, cs4231_chan_init), + KOBJMETHOD(channel_setformat, cs4231_chan_setformat), + KOBJMETHOD(channel_setspeed, cs4231_chan_setspeed), + KOBJMETHOD(channel_setblocksize, cs4231_chan_setblocksize), + KOBJMETHOD(channel_trigger, cs4231_chan_trigger), + KOBJMETHOD(channel_getptr, cs4231_chan_getptr), + KOBJMETHOD(channel_getcaps, cs4231_chan_getcaps), + { 0, 0 } +}; +CHANNEL_DECLARE(cs4231_chan); + +/* + * pcm(4) mixer interface + */ +static mixer_ent cs4231_mix_devices[32][2] = { + MIX_NONE(SOUND_MIXER_VOLUME), + MIX_NONE(SOUND_MIXER_BASS), + MIX_NONE(SOUND_MIXER_TREBLE), + /* AUX1 */ + MIX_ENT(SOUND_MIXER_SYNTH, 2, 1, 0, 5, 3, 1, 0, 5), + MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 6, 7, 1, 0, 6), + MIX_ENT(SOUND_MIXER_SPEAKER, 26, 1, 0, 4, 0, 0, 0, 0), + MIX_ENT(SOUND_MIXER_LINE, 18, 1, 0, 5, 19, 1, 0, 5), + MIX_ENT(SOUND_MIXER_MIC, 0, 0, 5, 1, 1, 0, 5, 1), + /* + * XXX + * AUX2 : Ultra1/Ultra2 has no internal CD-ROM audio in + */ + MIX_ENT(SOUND_MIXER_CD, 4, 1, 0, 5, 5, 1, 0, 5), + MIX_ENT(SOUND_MIXER_IMIX, 13, 1, 2, 6, 0, 0, 0, 0), + MIX_NONE(SOUND_MIXER_ALTPCM), + MIX_NONE(SOUND_MIXER_RECLEV), + MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4), + MIX_NONE(SOUND_MIXER_OGAIN), + MIX_NONE(SOUND_MIXER_LINE1), + MIX_NONE(SOUND_MIXER_LINE2), + MIX_NONE(SOUND_MIXER_LINE3), +}; + +static kobj_method_t cs4231_mixer_methods[] = { + KOBJMETHOD(mixer_init, cs4231_mixer_init), + KOBJMETHOD(mixer_set, cs4231_mixer_set), + KOBJMETHOD(mixer_setrecsrc, cs4231_mixer_setrecsrc), + { 0, 0 } +}; +MIXER_DECLARE(cs4231_mixer); + +static int +cs4231_sbus_probe(device_t dev) +{ + char *name; + + name = sbus_get_name(dev); + if (strcmp("SUNW,CS4231", name) == 0) { + device_set_desc(dev, "Sun Audiocs CS4231A"); + return (0); + } + + return (ENXIO); +} + +static int +cs4231_sbus_attach(device_t dev) +{ + struct snddev_info *d; + struct cs4231_softc *sc; + char status[SND_STATUSLEN]; + + d = device_get_softc(dev); + sc = malloc(sizeof(struct cs4231_softc), M_DEVBUF, M_NOWAIT | M_ZERO); + if (sc == NULL) { + device_printf(dev, "cannot allocate softc\n"); + } + sc->sc_dev = dev; + sc->sc_lock = snd_mtxcreate(device_get_nameunit(sc->sc_dev), + "sound softc"); + + sc->sc_rid = 0; + if ((sc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, + &sc->sc_rid, RF_ACTIVE)) == NULL) { + device_printf(dev, "cannot map registers\n"); + return (ENXIO); + } + sc->sc_regt = rman_get_bustag(sc->sc_res); + sc->sc_regh = rman_get_bushandle(sc->sc_res); + sc->sc_irqrid = 0; + if ((sc->sc_irqres = bus_alloc_resource_any(sc->sc_dev, SYS_RES_IRQ, + &sc->sc_irqrid, RF_SHAREABLE | RF_ACTIVE)) == NULL) { + device_printf(sc->sc_dev, "cannot allocate interrupt\n"); + goto fail; + } + if (snd_setup_intr(sc->sc_dev, sc->sc_irqres, + INTR_MPSAFE, cs4231_intr, sc, &sc->sc_ih)) { + device_printf(sc->sc_dev, "cannot set up interrupt\n"); + goto fail; + } + + sc->sc_bufsz = pcm_getbuffersize(sc->sc_dev, CS4231_MIN_BUF_SZ, + CS4231_DEFAULT_BUF_SZ, CS4231_MAX_BUF_SZ); + if (bus_dma_tag_create( + NULL, /* parent */ + 64*1024, 0, /* alignment, boundary */ + BUS_SPACE_MAXADDR_32BIT,/* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filtfunc, filtfuncarg */ + sc->sc_bufsz, /* maxsize */ + 1, /* nsegments */ + sc->sc_bufsz, /* maxsegsz */ + BUS_DMA_ALLOCNOW, /* flags */ + NULL, /* lockfunc */ + NULL, /* lockfuncarg */ + &sc->sc_dmat)) { + device_printf(sc->sc_dev, "cannot allocate parent DMA tag\n"); + goto fail; + } + cs4231_enable(sc); + mixer_init(sc->sc_dev, &cs4231_mixer_class, sc); + if (pcm_register(sc->sc_dev, sc, 1, 1)) { + device_printf(sc->sc_dev, "cannot register to pcm\n"); + goto fail; + } + pcm_addchan(sc->sc_dev, PCMDIR_REC, &cs4231_chan_class, sc); + pcm_addchan(sc->sc_dev, PCMDIR_PLAY, &cs4231_chan_class, sc); + snprintf(status, SND_STATUSLEN, "at mem 0x%lx irq %ld bufsz %u", + rman_get_start(sc->sc_res), rman_get_start(sc->sc_irqres), + sc->sc_bufsz); + pcm_setstatus(sc->sc_dev, status); + + return (0); + +fail: + cs4231_free_resource(sc); + return (ENXIO); +} + +static int +cs4231_sbus_detach(device_t dev) +{ + struct cs4231_softc *sc; + int error; + + error = pcm_unregister(dev); + if (error) + return (error); + + sc = pcm_getdevinfo(dev); + cs4231_free_resource(sc); + + return (0); +} + +static int +cs4231_sbus_suspend(device_t dev) +{ + + return (ENXIO); +} + +static int +cs4231_sbus_resume(device_t dev) +{ + + return (ENXIO); +} + +static void +cs4231_power_reset(struct cs4231_softc *sc) +{ + + int i; + + APC_WRITE(sc, APC_CSR, APC_CSR_RESET); + DELAY(10); + APC_WRITE(sc, APC_CSR, 0); + DELAY(10); + APC_WRITE(sc, APC_CSR, APC_READ(sc, APC_CSR) | APC_CSR_CODEC_RESET); + DELAY(20); + APC_WRITE(sc, APC_CSR, APC_READ(sc, APC_CSR) & (~APC_CSR_CODEC_RESET)); + + for (i = CS_TIMEOUT; + i && CS_READ(sc, CS4231_IADDR) == CS_IN_INIT; i--) + DELAY(10); + if (i == 0) + device_printf(sc->sc_dev, "timeout waiting for reset\n"); + + /* Turn on cs4231 mode */ + cs4231_write(sc, CS_MISC_INFO, + cs4231_read(sc, CS_MISC_INFO) | CS_MODE2); + cs4231_write(sc, CS_PIN_CONTROL, + cs4231_read(sc, CS_PIN_CONTROL) | INTERRUPT_ENABLE); +} + +static int +cs4231_enable(struct cs4231_softc *sc) +{ + cs4231_power_reset(sc); + sc->sc_enabled = 1; + return (0); + +} + +static void +cs4231_disable(struct cs4231_softc *sc) +{ + u_int8_t v; + + if (sc->sc_enabled == 0) + return; + + cs4231_halt(&sc->sc_pch); + cs4231_halt(&sc->sc_rch); + v = cs4231_read(sc, CS_PIN_CONTROL) & ~INTERRUPT_ENABLE; + cs4231_write(sc, CS_PIN_CONTROL, v); + + /* reset APC */ + APC_WRITE(sc, APC_CSR, APC_CSR_RESET); + DELAY(10); + APC_WRITE(sc, APC_CSR, 0); + DELAY(10); + sc->sc_enabled = 0; +} + +static void +cs4231_free_resource(struct cs4231_softc *sc) +{ + cs4231_disable(sc); + if (sc->sc_irqres) { + if (sc->sc_ih) { + bus_teardown_intr(sc->sc_dev, sc->sc_irqres, + sc->sc_ih); + sc->sc_ih = NULL; + } + bus_release_resource(sc->sc_dev, SYS_RES_IRQ, sc->sc_irqrid, + sc->sc_irqres); + sc->sc_irqres = NULL; + } + if (sc->sc_dmat) + bus_dma_tag_destroy(sc->sc_dmat); + if (sc->sc_res) + bus_release_resource(sc->sc_dev, SYS_RES_MEMORY, sc->sc_rid, + sc->sc_res); + if (sc->sc_lock) + snd_mtxfree(sc->sc_lock); + free(sc, M_DEVBUF); +} + +static void +cs4231_write(struct cs4231_softc *sc, u_int8_t r, u_int8_t v) +{ + CS_WRITE(sc, CS4231_IADDR, r); + CS_WRITE(sc, CS4231_IDATA, v); +} + +static u_int8_t +cs4231_read(struct cs4231_softc *sc, u_int8_t r) +{ + CS_WRITE(sc, CS4231_IADDR, r); + return (CS_READ(sc, CS4231_IDATA)); +} + +static void +cs4231_intr(void *arg) +{ + struct cs4231_softc *sc; + struct cs4231_channel *ch; + u_int32_t csr; + u_int8_t reg, status; + + sc = arg; + CS4231_LOCK(sc); + + csr = APC_READ(sc, APC_CSR); + if ((csr & APC_CSR_GI) == 0) { + CS4231_UNLOCK(sc); + return; + } + APC_WRITE(sc, APC_CSR, csr); + + if ((csr & APC_CSR_EIE) && (csr & APC_CSR_EI)) { + status = cs4231_read(sc, CS_TEST_AND_INIT); + device_printf(sc->sc_dev, + "apc error interrupt : stat = 0x%x\n", status); + } + + if ((csr & APC_CSR_GIE) && (csr & APC_CSR_GI)) { + /* general interrupt */ + status = CS_READ(sc, CS4231_STATUS); + if (status & (INTERRUPT_STATUS | SAMPLE_ERROR)) { + reg = cs4231_read(sc, CS_IRQ_STATUS); + if (reg & CS_AFS_PI) { + cs4231_write(sc, CS_LOWER_BASE_COUNT, 0xff); + cs4231_write(sc, CS_UPPER_BASE_COUNT, 0xff); + } + if (reg & CS_AFS_CI) { + cs4231_write(sc, CS_LOWER_REC_CNT, 0xff); + cs4231_write(sc, CS_UPPER_REC_CNT, 0xff); + } + CS_WRITE(sc, CS4231_STATUS, 0); + } + } + + if ((csr & APC_CSR_PMIE) && (csr & APC_CSR_PMI)) { + u_long nextaddr, saddr; + u_int32_t togo; + + ch = &sc->sc_pch; + + CS4231_UNLOCK(sc); + chn_intr(ch->channel); + CS4231_LOCK(sc); + + togo = sndbuf_getblksz(ch->buffer); + saddr = sndbuf_getbufaddr(ch->buffer); + nextaddr = APC_READ(sc, APC_PNVA) + togo; + if (nextaddr >= saddr + sc->sc_bufsz) + nextaddr = saddr; + APC_WRITE(sc, APC_PNVA, nextaddr); + APC_WRITE(sc, APC_PNC, togo); + } + + if ((csr & APC_CSR_CIE) && (csr & APC_CSR_CI) && (csr & APC_CSR_CD)) { + u_long nextaddr, saddr; + u_int32_t togo; + + ch = &sc->sc_rch; + + CS4231_UNLOCK(sc); + chn_intr(ch->channel); + CS4231_LOCK(sc); + + togo = sndbuf_getblksz(ch->buffer); + saddr = sndbuf_getbufaddr(ch->buffer); + nextaddr = APC_READ(sc, APC_CNVA) + togo; + if (nextaddr >= saddr + sc->sc_bufsz) + nextaddr = saddr; + APC_WRITE(sc, APC_CNVA, nextaddr); + APC_WRITE(sc, APC_CNC, togo); + } + CS4231_UNLOCK(sc); +} + +static int +cs4231_mixer_init(struct snd_mixer *m) +{ + mix_setdevs(m, MODE2_MIXER_DEVICES); + mix_setrecdevs(m, MSS_REC_DEVICES); + + return (0); +} + +/* + * copied from sys/dev/sound/isa/mss.c + */ +static void +change_bits(mixer_tab *t, u_int8_t *regval, u_int32_t dev, u_int32_t chn, + u_int32_t newval) +{ + u_int8_t mask; + u_int32_t shift; + + if ((*t)[dev][chn].polarity == 1) /* reverse */ + newval = 100 - newval ; + + mask = (1 << (*t)[dev][chn].nbits) - 1; + newval = ((newval * mask) + 50) / 100; /* Scale it */ + shift = (*t)[dev][chn].bitoffs /*- (*t)[dev][LEFT_CHN].nbits + 1*/; + + *regval &= ~(mask << shift); /* Filter out the previous value */ + *regval |= (newval & mask) << shift; /* Set the new value */ +} + +/* + * copied from sys/dev/sound/isa/mss.c + */ +static int +cs4231_mixer_set(struct snd_mixer *m, u_int32_t dev, u_int32_t left, + u_int32_t right) +{ + struct cs4231_softc *sc; + mixer_tab *mix_d; + u_int8_t old, val, reg; + + sc = mix_getdevinfo(m); + CS4231_LOCK(sc); + mix_d = &cs4231_mix_devices; + if ((*mix_d)[dev][LEFT_CHN].nbits == 0) { + DEB(printf("nbits = 0 for dev %d\n", dev)); + return -1; + } + + if ((*mix_d)[dev][RIGHT_CHN].nbits == 0) + right = left; /* mono */ + + /* Set the left channel */ + reg = (*mix_d)[dev][LEFT_CHN].regno; + old = val = cs4231_read(sc, reg); + /* if volume is 0, mute chan. Otherwise, unmute. */ + if (reg != 0) { + if (reg == CS_MONO_IO_CONTROL) + val = (left == 0) ? old | MONO_OUTPUT_MUTE : old & 0xef; + else if (reg == CS_DIGITAL_MIX) + val = (left == 0) ? 0 : old & 0xfd; + else + val = (left == 0) ? old | 0x80 : old & 0x7f; + } + change_bits(mix_d, &val, dev, LEFT_CHN, left); + cs4231_write(sc, reg, val); + + if ((*mix_d)[dev][RIGHT_CHN].nbits != 0) { /* have stereo */ + /* Set the right channel */ + reg = (*mix_d)[dev][RIGHT_CHN].regno; + old = val = cs4231_read(sc, reg); + if (reg != 1) + val = (right == 0) ? old | 0x80 : old & 0x7f; + change_bits(mix_d, &val, dev, RIGHT_CHN, right); + cs4231_write(sc, reg, val); + } + CS4231_UNLOCK(sc); + + return (left | (right << 8)); +} + +static int +cs4231_mixer_setrecsrc(struct snd_mixer *m, u_int32_t src) +{ + struct cs4231_softc *sc; + u_int8_t v; + + sc = mix_getdevinfo(m); + switch(src) { + case SOUND_MASK_LINE: + case SOUND_MASK_LINE3: + v = CS_IN_LINE; + break; + + case SOUND_MASK_CD: + case SOUND_MASK_LINE1: + v = CS_IN_AUX1; + break; + + case SOUND_MASK_IMIX: + v = CS_IN_DAC; + break; + + case SOUND_MASK_MIC: + default: + v = CS_IN_MIC; + break; + } + CS4231_LOCK(sc); + cs4231_write(sc, CS_LEFT_INPUT_CONTROL, + (cs4231_read(sc, CS_LEFT_INPUT_CONTROL) & CS_IN_MASK) | v); + cs4231_write(sc, CS_RIGHT_INPUT_CONTROL, + (cs4231_read(sc, CS_RIGHT_INPUT_CONTROL) & CS_IN_MASK) | v); + CS4231_UNLOCK(sc); + + return (src); +} + +static void * +cs4231_chan_init(kobj_t obj, void *dev, struct snd_dbuf *b, + struct pcm_channel *c, int dir) +{ + struct cs4231_softc *sc; + struct cs4231_channel *ch; + + sc = dev; + ch = (dir == PCMDIR_PLAY) ? &sc->sc_pch : &sc->sc_rch; + ch->parent = sc; + ch->channel = c; + ch->dir = dir; + ch->buffer = b; + if (sndbuf_alloc(ch->buffer, sc->sc_dmat, sc->sc_bufsz) != 0) + return (NULL); + DPRINTF(("%s channel addr: %lx\n", dir == PCMDIR_PLAY ? "PLAY" : "REC", + sndbuf_getbufaddr(ch->buffer))); + return (ch); +} + +static int +cs4231_chan_setformat(kobj_t obj, void *data, u_int32_t format) +{ + struct cs4231_softc *sc; + struct cs4231_channel *ch; + u_int32_t encoding; + u_int8_t fs, v; + + ch = data; + sc = ch->parent; + + encoding = format & ~(AFMT_STEREO | AFMT_FULLDUPLEX); + fs = 0; + switch(encoding) { + case AFMT_U8: + fs = CS_AFMT_U8; + break; + case AFMT_MU_LAW: + fs = CS_AFMT_MU_LAW; + break; + case AFMT_S16_LE: + fs = CS_AFMT_S16_LE; + break; + case AFMT_A_LAW: + fs = CS_AFMT_A_LAW; + break; + case AFMT_IMA_ADPCM: + fs = CS_AFMT_IMA_ADPCM; + break; + case AFMT_U16_BE: + fs = CS_AFMT_U16_BE; + break; + default: + fs = CS_AFMT_U8; + break; + } + + if (format & AFMT_STEREO) + fs |= CS_AFMT_STEREO; + + DPRINTF(("FORMAT: %s : 0x%x\n", ch->dir == PCMDIR_PLAY ? "playback" : + "capture", format)); + CS4231_LOCK(sc); + v = cs4231_read(sc, CS_CLOCK_DATA_FORMAT); + v &= CS_CLOCK_DATA_FORMAT_MASK; + fs |= v; + cs4231_chan_fs(sc, fs); + CS4231_UNLOCK(sc); + return (0); +} + +static int +cs4231_chan_setspeed(kobj_t obj, void *data, u_int32_t speed) +{ + typedef struct { + u_int32_t speed; + u_int8_t bits; + } speed_struct; + + const static speed_struct speed_table[] = { + {5510, (0 << 1) | CLOCK_XTAL2}, + {5510, (0 << 1) | CLOCK_XTAL2}, + {6620, (7 << 1) | CLOCK_XTAL2}, + {8000, (0 << 1) | CLOCK_XTAL1}, + {9600, (7 << 1) | CLOCK_XTAL1}, + {11025, (1 << 1) | CLOCK_XTAL2}, + {16000, (1 << 1) | CLOCK_XTAL1}, + {18900, (2 << 1) | CLOCK_XTAL2}, + {22050, (3 << 1) | CLOCK_XTAL2}, + {27420, (2 << 1) | CLOCK_XTAL1}, + {32000, (3 << 1) | CLOCK_XTAL1}, + {33075, (6 << 1) | CLOCK_XTAL2}, + {33075, (4 << 1) | CLOCK_XTAL2}, + {44100, (5 << 1) | CLOCK_XTAL2}, + {48000, (6 << 1) | CLOCK_XTAL1}, + }; + + struct cs4231_softc *sc; + struct cs4231_channel *ch; + int i, n, sel; + u_int8_t fs; + + ch = data; + sc = ch->parent; + n = sizeof(speed_table) / sizeof(speed_struct); + + for (i = 1, sel =0; i < n - 1; i++) + if (abs(speed - speed_table[i].speed) < + abs(speed - speed_table[sel].speed)) + sel = i; + DPRINTF(("SPEED: %s : %dHz -> %dHz\n", ch->dir == PCMDIR_PLAY ? + "playback" : "capture", speed, speed_table[sel].speed)); + speed = speed_table[sel].speed; + + CS4231_LOCK(sc); + fs = cs4231_read(sc, CS_CLOCK_DATA_FORMAT); + fs &= ~CS_CLOCK_DATA_FORMAT_MASK; + fs |= speed_table[sel].bits; + cs4231_chan_fs(sc, fs); + CS4231_UNLOCK(sc); + return (speed); +} + +static void +cs4231_chan_fs(struct cs4231_softc *sc, u_int8_t fs) +{ + int i, doreset; + u_int8_t v; + + CS4231_LOCK_ASSERT(sc); + + /* set autocalibration */ + doreset = 0; + v = cs4231_read(sc, CS_INTERFACE_CONFIG) | AUTO_CAL_ENABLE; + CS_WRITE(sc, CS4231_IADDR, MODE_CHANGE_ENABLE); + CS_WRITE(sc, CS4231_IADDR, MODE_CHANGE_ENABLE | CS_INTERFACE_CONFIG); + CS_WRITE(sc, CS4231_IDATA, v); + + /* playback channel */ + CS_WRITE(sc, CS4231_IADDR, MODE_CHANGE_ENABLE | CS_CLOCK_DATA_FORMAT); + CS_WRITE(sc, CS4231_IDATA, fs); + CS_READ(sc, CS4231_IDATA); + CS_READ(sc, CS4231_IDATA); + for (i = CS_TIMEOUT; + i && CS_READ(sc, CS4231_IADDR) == CS_IN_INIT; i--) + DELAY(10); + if (i == 0) { + device_printf(sc->sc_dev, "timeout setting playback speed\n"); + doreset++; + } + + /* + * capture channel + * cs4231 doesn't allow sperate fs setup for playback/capture. + * I believe this will break full-duplex operation. + */ + CS_WRITE(sc, CS4231_IADDR, MODE_CHANGE_ENABLE | CS_REC_FORMAT); + CS_WRITE(sc, CS4231_IDATA, fs); + CS_READ(sc, CS4231_IDATA); + CS_READ(sc, CS4231_IDATA); + for (i = CS_TIMEOUT; + i && CS_READ(sc, CS4231_IADDR) == CS_IN_INIT; i--) + DELAY(10); + if (i == 0) { + device_printf(sc->sc_dev, "timeout setting capture format\n"); + doreset++; + } + + CS_WRITE(sc, CS4231_IADDR, 0); + for (i = CS_TIMEOUT; + i && CS_READ(sc, CS4231_IADDR) == CS_IN_INIT; i--) + DELAY(10); + if (i == 0) { + device_printf(sc->sc_dev, "timeout waiting for !MCE\n"); + doreset++; + } + CS_WRITE(sc, CS4231_IADDR, CS_TEST_AND_INIT); + + for (i = CS_TIMEOUT; + i && CS_READ(sc, CS4231_IDATA) & AUTO_CAL_IN_PROG; i--) + DELAY(10); + if (i == 0) { + device_printf(sc->sc_dev, + "timeout waiting for autocalibration\n"); + doreset++; + } + if (doreset) { + /* + * Maybe the last resort to avoid a dreadful message like + * "pcm0:play:0: play interrupt timeout, channel dead" would + * be hardware reset. + */ + device_printf(sc->sc_dev, "trying to hardware reset\n"); + cs4231_power_reset(sc); + CS4231_UNLOCK(sc); /* XXX */ + if (mixer_reinit(sc->sc_dev) == -1) + device_printf(sc->sc_dev, + "unable to reinitialize the mixer\n"); + CS4231_LOCK(sc); + } +} + +static int +cs4231_chan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) +{ + struct cs4231_softc *sc; + struct cs4231_channel *ch; + int nblks; + + ch = data; + sc = ch->parent; + + if (blocksize > CS4231_MAX_DMA_SZ) + blocksize = CS4231_MAX_DMA_SZ; + nblks = sc->sc_bufsz / blocksize; + sndbuf_resize(ch->buffer, nblks, blocksize); + + return blocksize; +} + +static int +cs4231_chan_trigger(kobj_t obj, void *data, int go) +{ + struct cs4231_channel *ch; + + ch = data; + switch(go) { + case PCMTRIG_EMLDMAWR: + case PCMTRIG_EMLDMARD: + break; + case PCMTRIG_START: + cs4231_trigger(ch); + break; + case PCMTRIG_ABORT: + case PCMTRIG_STOP: + cs4231_halt(ch); + break; + default: + break; + } + + return (0); +} + +static int +cs4231_chan_getptr(kobj_t obj, void *data) +{ + struct cs4231_softc *sc; + struct cs4231_channel *ch; + u_int32_t naddr; + int ptr, sz; + + ch = data; + sc = ch->parent; + + CS4231_LOCK(sc); + if (ch->dir == PCMDIR_PLAY) + naddr = APC_READ(sc, APC_PNVA); + else + naddr = APC_READ(sc, APC_CNVA); + sz = sndbuf_getsize(ch->buffer); + ptr = naddr + sndbuf_getblksz(ch->buffer) - + sndbuf_getbufaddr(ch->buffer); + CS4231_UNLOCK(sc); + + ptr %= sz; + return (ptr); +} + +static struct pcmchan_caps * +cs4231_chan_getcaps(kobj_t obj, void *data) +{ + return (&cs4231_caps); +} + +static void +cs4231_trigger(struct cs4231_channel *ch) +{ + struct cs4231_softc *sc; + u_int32_t csr, togo; + u_int32_t nextaddr, saddr; + + sc = ch->parent; + + CS4231_LOCK(sc); + if (ch->locked) { + device_printf(sc->sc_dev, "already triggered\n"); + CS4231_UNLOCK(sc); + return; + } + + nextaddr = sndbuf_getbufaddr(ch->buffer); + togo = sndbuf_getblksz(ch->buffer); + if (ch->dir == PCMDIR_PLAY) { + DPRINTF(("TRG: PNVA = %x, togo = %x\n", nextaddr, togo)); + + cs4231_read(sc, CS_TEST_AND_INIT); /* clear pending error */ + csr = APC_READ(sc, APC_CSR); + APC_WRITE(sc, APC_PNVA, nextaddr); + APC_WRITE(sc, APC_PNC, togo); + + if ((csr & APC_CSR_PDMA_GO) == 0 || + (csr & APC_CSR_PPAUSE) != 0) { + APC_WRITE(sc, APC_CSR, APC_READ(sc, APC_CSR) & + ~(APC_CSR_PIE | APC_CSR_PPAUSE)); + APC_WRITE(sc, APC_CSR, APC_READ(sc, APC_CSR) | + APC_CSR_GIE | APC_CSR_PIE | APC_CSR_EIE | + APC_CSR_EI | APC_CSR_PMIE | APC_CSR_PDMA_GO); + cs4231_write(sc, CS_LOWER_BASE_COUNT, 0xff); + cs4231_write(sc, CS_UPPER_BASE_COUNT, 0xff); + cs4231_write(sc, CS_INTERFACE_CONFIG, + cs4231_read(sc, CS_INTERFACE_CONFIG) | + PLAYBACK_ENABLE); + } + } else { + DPRINTF(("TRG: CNVA = %x, togo = %x\n", nextaddr, togo)); + + cs4231_read(sc, CS_TEST_AND_INIT); /* clear pending error */ + APC_WRITE(sc, APC_CNVA, nextaddr); + APC_WRITE(sc, APC_CNC, togo); + csr = APC_READ(sc, APC_CSR); + if ((csr & APC_CSR_CDMA_GO) == 0 || + (csr & APC_CSR_CPAUSE) != 0) { + csr &= APC_CSR_CPAUSE; + csr |= APC_CSR_GIE | APC_CSR_CMIE | APC_CSR_CIE | + APC_CSR_EI | APC_CSR_CDMA_GO; + APC_WRITE(sc, APC_CSR, csr); + cs4231_write(sc, CS_LOWER_REC_CNT, 0xff); + cs4231_write(sc, CS_UPPER_REC_CNT, 0xff); + cs4231_write(sc, CS_INTERFACE_CONFIG, + cs4231_read(sc, CS_INTERFACE_CONFIG) | + CAPTURE_ENABLE); + } + /* try to update next samples */ + if (APC_READ(sc, APC_CSR) & APC_CSR_CD) { + saddr = sndbuf_getbufaddr(ch->buffer); + nextaddr = APC_READ(sc, APC_CNVA) + togo; + if (nextaddr >= saddr + sc->sc_bufsz) + nextaddr = saddr; + APC_WRITE(sc, APC_CNVA, nextaddr); + APC_WRITE(sc, APC_CNC, togo); + } + } + ch->locked = 1; + CS4231_UNLOCK(sc); +} + +static void +cs4231_halt(struct cs4231_channel *ch) +{ + struct cs4231_softc *sc; + + sc = ch->parent; + CS4231_LOCK(sc); + + if (ch->dir == PCMDIR_PLAY ) { + /* XXX Kills some capture bits */ + APC_WRITE(sc, APC_CSR, APC_READ(sc, APC_CSR) & + ~(APC_CSR_EI | APC_CSR_GIE | APC_CSR_PIE | + APC_CSR_EIE | APC_CSR_PDMA_GO | APC_CSR_PMIE)); + cs4231_write(sc, CS_INTERFACE_CONFIG, + cs4231_read(sc, CS_INTERFACE_CONFIG) & (~PLAYBACK_ENABLE)); + } else { + /* XXX Kills some playback bits */ + APC_WRITE(sc, APC_CSR, APC_CSR_CAPTURE_PAUSE); + cs4231_write(sc, CS_INTERFACE_CONFIG, + cs4231_read(sc, CS_INTERFACE_CONFIG) & (~CAPTURE_ENABLE)); + } + ch->locked = 0; + CS4231_UNLOCK(sc); +} --- /dev/null Thu Jul 1 18:00:00 2004 +++ sys/dev/sound/sbus/cs4231.h Wed Jun 30 18:26:20 2004 @@ -0,0 +1,228 @@ +/*- + * Copyright (c) 1996 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Ken Hornstein and John Kohl. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Register defs for Crystal Semiconductor CS4231 Audio Codec/mixer + * chip, used on Gravis UltraSound MAX cards. + * + * Block diagram: + * +----------------------------------------------------+ + * | | + * | +----------------------------------------------+ | + * | |mixed in +-+ | | + * | +------------>--| | | | + * | mic in | | | | + * Mic --+-->| --------- GAIN ->-| | | | + * | | AUX 1 in |M| | | + * GF1 --)-->| -------------+-->-|U| | | + * | | Line in | |X|---- GAIN ----------+ | | + * Line --)-->| ---------+---)-->-| | | | | + * | | | | | | | | | + * | | | | +-+ ADC | | + * | | | | | | | + * | | | | | | | + * | | | +--- L/M --\ | | | AMP--> + * | | | \ | | | | + * | | | \ | | | | + * | | +---- L/M -------O-->--+--------)-------+-|--+-> line + * | | mono in /| | | | + * +---|-->------------ L/M -----/ | | | | + * | AUX 2 in | | | | + * CD --------|-->------------ L/M -------+ L/M | | + * | | v | + * | | | | + * | DAC | | + * | | | | + * +----------------------------------------------------+ + * | | + * | | + * v v + * Pc BUS (DISK) ??? + * + * Documentation for this chip can be found at: + * http://www.cirrus.com/products/overviews/cs4231.html + */ + +/* + * This file was merged from two header files.(ad1848reg.h and cs4231reg.h) + * And the suffix AD1848 and SP was changed to CS4231 and CS respectively. + */ +/* CS4231 direct registers */ +#define CS4231_IADDR 0x00 +#define CS4231_IDATA 0x01 +#define CS4231_STATUS 0x02 +#define CS4231_PIO 0x03 + +/* Index address register */ +#define CS_IN_INIT 0x80 +#define MODE_CHANGE_ENABLE 0x40 +#define TRANSFER_DISABLE 0x20 +#define ADDRESS_MASK 0xe0 + +/* Status bits */ +#define INTERRUPT_STATUS 0x01 +#define PLAYBACK_READY 0x02 +#define PLAYBACK_LEFT 0x04 +/* pbright is not left */ +#define PLAYBACK_UPPER 0x08 +/* bplower is not upper */ +#define SAMPLE_ERROR 0x10 +#define CAPTURE_READY 0x20 +#define CAPTURE_LEFT 0x40 +/* cpright is not left */ +#define CAPTURE_UPPER 0x80 +/* cplower is not upper */ + +/* CS4231 indirect mapped registers */ +#define CS_LEFT_INPUT_CONTROL 0x00 +#define CS_RIGHT_INPUT_CONTROL 0x01 +#define CS_LEFT_AUX1_CONTROL 0x02 +#define CS_RIGHT_AUX1_CONTROL 0x03 +#define CS_LEFT_AUX2_CONTROL 0x04 +#define CS_RIGHT_AUX2_CONTROL 0x05 +#define CS_LEFT_OUTPUT_CONTROL 0x06 +#define CS_RIGHT_OUTPUT_CONTROL 0x07 +#define CS_CLOCK_DATA_FORMAT 0x08 +#define CS_INTERFACE_CONFIG 0x09 +#define CS_PIN_CONTROL 0x0a +#define CS_TEST_AND_INIT 0x0b +#define CS_MISC_INFO 0x0c +#define CS_DIGITAL_MIX 0x0d +#define CS_UPPER_BASE_COUNT 0x0e +#define CS_LOWER_BASE_COUNT 0x0f +/* CS4231/AD1845 mode2 registers; added to AD1848 registers */ +#define CS_ALT_FEATURE1 0x10 +#define CS_ALT_FEATURE2 0x11 +#define CS_LEFT_LINE_CONTROL 0x12 +#define CS_RIGHT_LINE_CONTROL 0x13 +#define CS_TIMER_LOW 0x14 +#define CS_TIMER_HIGH 0x15 +#define CS_UPPER_FREQUENCY_SEL 0x16 +#define CS_LOWER_FREQUENCY_SEL 0x17 +#define CS_IRQ_STATUS 0x18 +#define CS_VERSION_ID 0x19 +#define CS_MONO_IO_CONTROL 0x1a +#define CS_POWERDOWN_CONTROL 0x1b +#define CS_REC_FORMAT 0x1c +#define CS_XTAL_SELECT 0x1d +#define CS_UPPER_REC_CNT 0x1e +#define CS_LOWER_REC_CNT 0x1f + +#define CS_IN_MASK 0x2f +#define CS_IN_LINE 0x00 +#define CS_IN_AUX1 0x40 +#define CS_IN_MIC 0x80 +#define CS_IN_DAC 0xc0 +#define CS_MIC_GAIN_ENABLE 0x20 +#define CS_IN_GAIN_MASK 0xf0 + +/* Aux input control - registers I2 (channel 1,left); I3 (channel 1,right) + I4 (channel 2,left); I5 (channel 2,right) */ +#define AUX_INPUT_ATTEN_BITS 0x1f +#define AUX_INPUT_ATTEN_MASK 0xe0 +#define AUX_INPUT_MUTE 0x80 + +/* Output bits - registers I6,I7*/ +#define OUTPUT_MUTE 0x80 +#define OUTPUT_ATTEN_BITS 0x3f +#define OUTPUT_ATTEN_MASK (~OUTPUT_ATTEN_BITS & 0xff) + +/* Clock and Data format reg bits (some also Capture Data format) - reg I8 */ +#define CS_CLOCK_DATA_FORMAT_MASK 0x0f +#define CLOCK_XTAL1 0x00 +#define CLOCK_XTAL2 0x01 +#define CLOCK_FREQ_MASK 0xf1 +#define CS_AFMT_STEREO 0x10 +#define CS_AFMT_U8 0x00 +#define CS_AFMT_MU_LAW 0x20 +#define CS_AFMT_S16_LE 0x40 +#define CS_AFMT_A_LAW 0x60 +#define CS_AFMT_IMA_ADPCM 0xa0 +#define CS_AFMT_U16_BE 0xc0 + +/* Interface Configuration reg bits - register I9 */ +#define PLAYBACK_ENABLE 0x01 +#define CAPTURE_ENABLE 0x02 +#define DUAL_DMA 0x00 +#define SINGLE_DMA 0x04 +#define AUTO_CAL_ENABLE 0x08 +#define PLAYBACK_PIO_ENABLE 0x40 +#define CAPTURE_PIO_ENABLE 0x80 + +/* Pin control bits - register I10 */ +#define INTERRUPT_ENABLE 0x02 +#define XCTL0_ENABLE 0x40 +#define XCTL1_ENABLE 0x80 + +/* Test and init reg bits - register I11 (read-only) */ +#define OVERRANGE_LEFT_MASK 0xfc +#define OVERRANGE_RIGHT_MASK 0xf3 +#define DATA_REQUEST_STATUS 0x10 +#define AUTO_CAL_IN_PROG 0x20 +#define PLAYBACK_UNDERRUN 0x40 +#define CAPTURE_OVERRUN 0x80 + +/* Miscellaneous Control reg bits - register I12 */ +#define CS_ID_MASK 0x70 +#define CS_MODE2 0x40 + +/* Digital Mix Control reg bits - register I13 */ +#define DIGITAL_MIX1_ENABLE 0x01 +#define MIX_ATTEN_MASK 0x03 + +/* alternate feature status(I24) */ +#define CS_AFS_TI 0x40 /* timer interrupt */ +#define CS_AFS_CI 0x20 /* capture interrupt */ +#define CS_AFS_PI 0x10 /* playback interrupt */ +#define CS_AFS_CU 0x08 /* capture underrun */ +#define CS_AFS_CO 0x04 /* capture overrun */ +#define CS_AFS_PO 0x02 /* playback overrun */ +#define CS_AFS_PU 0x01 /* playback underrun */ + + +/* Miscellaneous Control reg bits */ +#define CS_MODE2 0x40 + +#define MONO_INPUT_ATTEN_BITS 0x0f +#define MONO_INPUT_ATTEN_MASK 0xf0 +#define MONO_OUTPUT_MUTE 0x40 +#define MONO_INPUT_MUTE 0x80 +#define MONO_INPUT_MUTE_MASK 0x7f + +#define LINE_INPUT_ATTEN_BITS 0x1f +#define LINE_INPUT_ATTEN_MASK 0xe0 +#define LINE_INPUT_MUTE 0x80 +#define LINE_INPUT_MUTE_MASK 0x7f --- sys/modules/Makefile.orig Mon Jun 28 18:03:48 2004 +++ sys/modules/Makefile Wed Jun 30 20:03:47 2004 @@ -417,6 +417,7 @@ .if ${MACHINE_ARCH} == "sparc64" _gem= gem _hme= hme +_sound= sound .endif .if defined(MODULES_OVERRIDE) && !defined(ALL_MODULES) --- sys/modules/sound/driver/Makefile.orig Sun Aug 18 01:23:44 2002 +++ sys/modules/sound/driver/Makefile Wed Jun 30 18:33:09 2004 @@ -1,6 +1,6 @@ # $FreeBSD: src/sys/modules/sound/driver/Makefile,v 1.15 2002/08/17 16:23:44 orion Exp $ -SUBDIR = als4000 ad1816 cmi cs4281 csa ds1 emu10k1 es137x ess +SUBDIR = als4000 ad1816 cmi cs4231 cs4281 csa ds1 emu10k1 es137x ess SUBDIR += fm801 ich maestro maestro3 mss neomagic sb16 sb8 sbc solo SUBDIR += t4dwave via8233 via82c686 vibes SUBDIR += driver uaudio --- /dev/null Thu Jul 1 18:00:00 2004 +++ sys/modules/sound/driver/cs4231/Makefile Wed Jun 30 18:32:25 2004 @@ -0,0 +1,9 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/../../../../dev/sound/sbus + +KMOD= snd_cs4231 +SRCS= device_if.h bus_if.h +SRCS+= cs4231.c + +.include <bsd.kmod.mk> --/9DWx/yDrRhgMJTb--
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20040701093225.GA12517>