diff --git a/sys/conf/NOTES b/sys/conf/NOTES index f8f3ca6bf63e..cd55ed0fd784 100644 --- a/sys/conf/NOTES +++ b/sys/conf/NOTES @@ -1834,6 +1834,7 @@ device sound # # snd_als4000: Avance Logic ALS4000 PCI. # snd_ad1816: Analog Devices AD1816 ISA PnP/non-PnP. +# snd_audiocs: Crystal Semiconductor CS4231 SBus/EBus. # snd_cmi: CMedia CMI8338/CMI8738 PCI. # snd_cs4281: Crystal Semiconductor CS4281 PCI. # snd_csa: Crystal Semiconductor CS461x/428x PCI. (except @@ -1867,6 +1868,7 @@ device sound device snd_ad1816 device snd_als4000 #device snd_au88x0 +#device snd_audiocs device snd_cmi device snd_cs4281 device snd_csa diff --git a/sys/conf/files.sparc64 b/sys/conf/files.sparc64 index 64572e35f57c..9dd52f9b67c4 100644 --- a/sys/conf/files.sparc64 +++ b/sys/conf/files.sparc64 @@ -25,6 +25,8 @@ dev/ofw/ofw_console.c optional ofw_console dev/ofw/openfirm.c standard dev/ofw/openfirmio.c standard dev/ofw/openpromio.c standard +dev/sound/sbus/cs4231.c optional snd_audiocs ebus +dev/sound/sbus/cs4231.c optional snd_audiocs sbus dev/syscons/scgfbrndr.c optional sc dev/syscons/schistory.c optional sc dev/syscons/scmouse.c optional sc @@ -53,6 +55,7 @@ sparc64/fhc/fhc.c optional fhc sparc64/fhc/fhc_central.c optional fhc central sparc64/fhc/fhc_nexus.c optional fhc sparc64/isa/isa.c optional isa +sparc64/isa/isa_dma.c optional isa #sparc64/isa/ofw_isa.c optional ebus #sparc64/isa/ofw_isa.c optional isa sparc64/isa/ofw_isa.c standard diff --git a/sys/dev/sound/sbus/apcdmareg.h b/sys/dev/sound/sbus/apcdmareg.h new file mode 100644 index 000000000000..28abd8b16dc6 --- /dev/null +++ b/sys/dev/sound/sbus/apcdmareg.h @@ -0,0 +1,114 @@ +/* $FreeBSD$ */ +/* $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 ) diff --git a/sys/dev/sound/sbus/cs4231.c b/sys/dev/sound/sbus/cs4231.c new file mode 100644 index 000000000000..5a236a6b5f0a --- /dev/null +++ b/sys/dev/sound/sbus/cs4231.c @@ -0,0 +1,1573 @@ +/* + * Copyright (c) 1999 Jason L. Wright (jason@thought.net) + * Copyright (c) 2004 Pyun YongHyeon + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "mixer_if.h" + +/* + * The driver supports CS4231A audio chips found on Sbus/Ebus based + * UltraSPARCs. Though, CS4231A says it supports full-duplex mode, I + * doubt it due to the lack of independent sampling frequency register + * for playback/capture. + * Since I couldn't find any documentation for APCDMA programming + * information, I guessed the usage of APCDMA from that of OpenBSD's + * driver. The EBDMA infomation of PCIO can be obtained from + * http://solutions.sun.com/embedded/databook/web/microprocessors/pcio.html + * And CS4231A datasheet can also be obtained from + * ftp://ftp.alsa-project.org/pub/manuals/cirrus/4231a.pdf + * + * 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 (16*1024) +#define CS4231_DEFAULT_BUF_SZ (32*1024) +#define CS4231_MAX_BUF_SZ (64*1024) +#define CS4231_MAX_BLK_SZ (8*1024) +#define CS4231_MAX_APC_DMA_SZ (8*1024) + + +#undef CS4231_DEBUG +#ifdef CS4231_DEBUG +#define DPRINTF(x) printf x +#else +#define DPRINTF(x) +#endif +#define CS4231_AUTO_CALIBRATION + +struct cs4231_softc; + +struct cs4231_channel { + struct cs4231_softc *parent; + struct pcm_channel *channel; + struct snd_dbuf *buffer; + u_int32_t format; + u_int32_t speed; + u_int32_t nextaddr; + u_int32_t togo; + int dir; + int locked; +}; + +#define CS4231_RES_MEM_MAX 4 +#define CS4231_RES_IRQ_MAX 2 +struct cs4231_softc { + struct device *sc_dev; + int sc_rid[CS4231_RES_MEM_MAX]; + struct resource *sc_res[CS4231_RES_MEM_MAX]; + bus_space_handle_t sc_regh[CS4231_RES_MEM_MAX]; + bus_space_tag_t sc_regt[CS4231_RES_MEM_MAX]; + + int sc_irqrid[CS4231_RES_IRQ_MAX]; + struct resource *sc_irqres[CS4231_RES_IRQ_MAX]; + void *sc_ih[CS4231_RES_IRQ_MAX]; + bus_dma_tag_t sc_dmat[CS4231_RES_IRQ_MAX]; + int sc_burst; + + u_int32_t sc_bufsz; + struct cs4231_channel sc_pch; + struct cs4231_channel sc_rch; + int sc_enabled; + int sc_rtype; + int sc_nmres; + int sc_nires; + int sc_codecv; + int sc_chipvid; + int sc_flags; +#define CS4231_SBUS 0x01 +#define CS4231_EBUS 0x02 + + struct mtx *sc_lock; +}; + +struct mix_table { + u_int32_t reg:8; + u_int32_t bits:8; + u_int32_t mute:8; + u_int32_t shift:4; + u_int32_t neg:1; + u_int32_t avail:1; + u_int32_t recdev:1; +}; + +static int cs4231_bus_probe(device_t); +static int cs4231_sbus_attach(device_t); +static int cs4231_ebus_attach(device_t); +static int cs4231_attach_common(struct cs4231_softc *); +static int cs4231_bus_detach(device_t); +static int cs4231_bus_suspend(device_t); +static int cs4231_bus_resume(device_t); +static void cs4231_getversion(struct cs4231_softc *); +static void cs4231_free_resource(struct cs4231_softc *); +static void cs4231_ebdma_reset(struct cs4231_softc *); +static void cs4231_power_reset(struct cs4231_softc *, int); +static int cs4231_enable(struct cs4231_softc *, int); +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_sbus_intr(void *); +static void cs4231_ebus_pintr(void *arg); +static void cs4231_ebus_cintr(void *arg); +static int cs4231_mixer_init(struct snd_mixer *); +static void cs4231_mixer_set_value(struct cs4231_softc *, + const struct mix_table *, u_int8_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 *, int, 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_apcdma_trigger(struct cs4231_softc *, + struct cs4231_channel *); +static void cs4231_ebdma_trigger(struct cs4231_softc *, + 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[0], (sc)->sc_regh[0], (r) << 2, (v)) +#define CS_READ(sc,r) \ + bus_space_read_1((sc)->sc_regt[0], (sc)->sc_regh[0], (r) << 2) + +#define APC_WRITE(sc,r,v) \ + bus_space_write_4(sc->sc_regt[0], sc->sc_regh[0], r, v) +#define APC_READ(sc,r) \ + bus_space_read_4(sc->sc_regt[0], sc->sc_regh[0], r) + +#define EBDMA_P_WRITE(sc,r,v) \ + bus_space_write_4((sc)->sc_regt[1], (sc)->sc_regh[1], (r), (v)) +#define EBDMA_P_READ(sc,r) \ + bus_space_read_4((sc)->sc_regt[1], (sc)->sc_regh[1], (r)) + +#define EBDMA_C_WRITE(sc,r,v) \ + bus_space_write_4((sc)->sc_regt[2], (sc)->sc_regh[2], (r), (v)) +#define EBDMA_C_READ(sc,r) \ + bus_space_read_4((sc)->sc_regt[2], (sc)->sc_regh[2], (r)) + +#define AUXIO_CODEC 0x00 +#define AUXIO_WRITE(sc,r,v) \ + bus_space_write_4((sc)->sc_regt[3], (sc)->sc_regh[3], (r), (v)) +#define AUXIO_READ(sc,r) \ + bus_space_read_4((sc)->sc_regt[3], (sc)->sc_regh[3], (r)) + +#define CODEC_WARM_RESET 0 +#define CODEC_COLD_RESET 1 + +/* SBus */ +static device_method_t cs4231_sbus_methods[] = { + DEVMETHOD(device_probe, cs4231_bus_probe), + DEVMETHOD(device_attach, cs4231_sbus_attach), + DEVMETHOD(device_detach, cs4231_bus_detach), + DEVMETHOD(device_suspend, cs4231_bus_suspend), + DEVMETHOD(device_resume, cs4231_bus_resume), + {0, 0} +}; + +static driver_t cs4231_sbus_driver = { + "pcm", + cs4231_sbus_methods, + PCM_SOFTC_SIZE +}; + +DRIVER_MODULE(snd_audiocs, sbus, cs4231_sbus_driver, pcm_devclass, 0, 0); + +/* EBus */ +static device_method_t cs4231_ebus_methods[] = { + DEVMETHOD(device_probe, cs4231_bus_probe), + DEVMETHOD(device_attach, cs4231_ebus_attach), + DEVMETHOD(device_detach, cs4231_bus_detach), + DEVMETHOD(device_suspend, cs4231_bus_suspend), + DEVMETHOD(device_resume, cs4231_bus_resume), + {0, 0} +}; + +static driver_t cs4231_ebus_driver = { + "pcm", + cs4231_ebus_methods, + PCM_SOFTC_SIZE +}; + +DRIVER_MODULE(snd_audiocs, ebus, cs4231_ebus_driver, pcm_devclass, 0, 0); +MODULE_DEPEND(snd_audiocs, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); +MODULE_VERSION(snd_audiocs, 1); + + +static u_int32_t cs4231_fmt[] = { + AFMT_U8, + AFMT_STEREO | AFMT_U8, + AFMT_MU_LAW, + AFMT_STEREO | AFMT_MU_LAW, + AFMT_A_LAW, + AFMT_STEREO | AFMT_A_LAW, + AFMT_IMA_ADPCM, + AFMT_STEREO | AFMT_IMA_ADPCM, + AFMT_S16_LE, + AFMT_STEREO | AFMT_S16_LE, + AFMT_S16_BE, + AFMT_STEREO | AFMT_S16_BE, + 0 +}; + +static struct pcmchan_caps cs4231_caps = {5510, 48000, cs4231_fmt, 0}; + +/* + * sound(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); + +/* + * sound(4) mixer interface + */ +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_bus_probe(device_t dev) +{ + const char *name; + + name = ofw_bus_get_name(dev); + if (strcmp("SUNW,CS4231", name) == 0) { + device_set_desc(dev, "Sun Audiocs"); + return (0); + } + return (ENXIO); +} + +static int +cs4231_sbus_attach(device_t dev) +{ + struct snddev_info *d; + struct cs4231_softc *sc; + int burst; + + 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"); + return (ENOMEM); + } + sc->sc_dev = dev; + /* + * XXX + * No public documentation exists on programming burst size of APCDMA. + */ + burst = sbus_get_burstsz(sc->sc_dev); + if ((burst & SBUS_BURST_64)) + sc->sc_burst = 64; + else if ((burst & SBUS_BURST_32)) + sc->sc_burst = 32; + else if ((burst & SBUS_BURST_16)) + sc->sc_burst = 16; + else + sc->sc_burst = 0; + sc->sc_flags = CS4231_SBUS; + sc->sc_rtype = SYS_RES_MEMORY; + sc->sc_nmres = 1; + sc->sc_nires = 1; + return cs4231_attach_common(sc); +} + +static int +cs4231_ebus_attach(device_t dev) +{ + struct snddev_info *d; + struct cs4231_softc *sc; + + 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"); + return (ENOMEM); + } + sc->sc_dev = dev; + sc->sc_burst = EBDCSR_BURST_1; + sc->sc_rtype = SYS_RES_IOPORT; + sc->sc_nmres = CS4231_RES_MEM_MAX; + sc->sc_nires = CS4231_RES_IRQ_MAX; + sc->sc_flags = CS4231_EBUS; + return cs4231_attach_common(sc); +} + +static int +cs4231_attach_common(struct cs4231_softc *sc) +{ + char status[SND_STATUSLEN]; + driver_intr_t *ihandler; + int i; + + sc->sc_lock = snd_mtxcreate(device_get_nameunit(sc->sc_dev), + "sound softc"); + if (sc->sc_lock == NULL) { + device_printf(sc->sc_dev, "cannot create mutex\n"); + free(sc, M_DEVBUF); + return (ENXIO); + } + + for (i = 0; i < sc->sc_nmres; i++) { + sc->sc_rid[i] = i; + if ((sc->sc_res[i] = bus_alloc_resource_any(sc->sc_dev, + sc->sc_rtype, &sc->sc_rid[i], RF_ACTIVE)) == NULL) { + device_printf(sc->sc_dev, + "cannot map register %d\n", i); + goto fail; + } + sc->sc_regt[i] = rman_get_bustag(sc->sc_res[i]); + sc->sc_regh[i] = rman_get_bushandle(sc->sc_res[i]); + } + for (i = 0; i < sc->sc_nires; i++) { + sc->sc_irqrid[i] = i; + if ((sc->sc_irqres[i] = bus_alloc_resource_any(sc->sc_dev, + SYS_RES_IRQ, &sc->sc_irqrid[i], RF_SHAREABLE | RF_ACTIVE)) + == NULL) { + if ((sc->sc_flags & CS4231_SBUS) != 0) + device_printf(sc->sc_dev, + "cannot allocate interrupt\n"); + else + device_printf(sc->sc_dev, "cannot allocate %s " + "interrupt\n", i == 0 ? "capture" : + "playback"); + goto fail; + } + } + + ihandler = cs4231_sbus_intr; + for (i = 0; i < sc->sc_nires; i++) { + if ((sc->sc_flags & CS4231_EBUS) != 0) { + if (i == 0) + ihandler = cs4231_ebus_cintr; + else + ihandler = cs4231_ebus_pintr; + } + if (snd_setup_intr(sc->sc_dev, sc->sc_irqres[i], INTR_MPSAFE, + ihandler, sc, &sc->sc_ih[i])) { + if ((sc->sc_flags & CS4231_SBUS) != 0) + device_printf(sc->sc_dev, + "cannot set up interrupt\n"); + else + device_printf(sc->sc_dev, "cannot set up %s " + " interrupt\n", i == 0 ? "capture" : + "playback"); + goto fail; + } + } + + sc->sc_bufsz = pcm_getbuffersize(sc->sc_dev, CS4231_MIN_BUF_SZ, + CS4231_DEFAULT_BUF_SZ, CS4231_MAX_BUF_SZ); + for (i = 0; i < sc->sc_nires; i++) { + if (bus_dma_tag_create( + NULL, /* parent */ + 64, 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[i])) { + if ((sc->sc_flags & CS4231_SBUS) != 0) + device_printf(sc->sc_dev, + "cannot allocate DMA tag\n"); + else + device_printf(sc->sc_dev, "cannot allocate %s " + "DMA tag\n", i == 0 ? "capture" : + "playback"); + goto fail; + } + } + cs4231_enable(sc, CODEC_WARM_RESET); + cs4231_getversion(sc); + if (mixer_init(sc->sc_dev, &cs4231_mixer_class, sc) != 0) + goto fail; + if (pcm_register(sc->sc_dev, sc, 1, 1)) { + device_printf(sc->sc_dev, "cannot register to pcm\n"); + goto fail; + } + if (pcm_addchan(sc->sc_dev, PCMDIR_REC, &cs4231_chan_class, sc) != 0) + goto chan_fail; + if (pcm_addchan(sc->sc_dev, PCMDIR_PLAY, &cs4231_chan_class, sc) != 0) + goto chan_fail; + if ((sc->sc_flags & CS4231_SBUS) != 0) + snprintf(status, SND_STATUSLEN, "at mem 0x%lx irq %ld bufsz %u", + rman_get_start(sc->sc_res[0]), + rman_get_start(sc->sc_irqres[0]), sc->sc_bufsz); + else + snprintf(status, SND_STATUSLEN, "at io 0x%lx 0x%lx 0x%lx 0x%lx " + "irq %ld %ld bufsz %u", rman_get_start(sc->sc_res[0]), + rman_get_start(sc->sc_res[1]), + rman_get_start(sc->sc_res[2]), + rman_get_start(sc->sc_res[3]), + rman_get_start(sc->sc_irqres[0]), + rman_get_start(sc->sc_irqres[1]), sc->sc_bufsz); + pcm_setstatus(sc->sc_dev, status); + return (0); + +chan_fail: + pcm_unregister(sc->sc_dev); +fail: + cs4231_free_resource(sc); + return (ENXIO); +} + +static int +cs4231_bus_detach(device_t dev) +{ + struct cs4231_softc *sc; + struct cs4231_channel *pch, *rch; + int error; + + sc = pcm_getdevinfo(dev); + CS4231_LOCK(sc); + pch = &sc->sc_pch; + rch = &sc->sc_pch; + if (pch->locked || rch->locked) { + CS4231_UNLOCK(sc); + return (EBUSY); + } + /* + * Since EBDMA requires valid DMA buffer to drain its FIFO, we need + * real DMA buffer for draining. + */ + if ((sc->sc_flags & CS4231_EBUS) != 0) + cs4231_ebdma_reset(sc); + CS4231_UNLOCK(sc); + error = pcm_unregister(dev); + if (error) + return (error); + cs4231_free_resource(sc); + return (0); +} + +static int +cs4231_bus_suspend(device_t dev) +{ + + return (ENXIO); +} + +static int +cs4231_bus_resume(device_t dev) +{ + + return (ENXIO); +} + +static void +cs4231_getversion(struct cs4231_softc *sc) +{ + u_int8_t v; + + v = cs4231_read(sc, CS_MISC_INFO); + sc->sc_codecv = v & CS_CODEC_ID_MASK; + v = cs4231_read(sc, CS_VERSION_ID); + v &= (CS_VERSION_NUMBER | CS_VERSION_CHIPID); + sc->sc_chipvid = v; + switch(v) { + case 0x80: + device_printf(sc->sc_dev, "\n", + sc->sc_codecv); + break; + case 0xa0: + device_printf(sc->sc_dev, "\n", + sc->sc_codecv); + break; + case 0x82: + device_printf(sc->sc_dev, "\n", + sc->sc_codecv); + break; + default: + device_printf(sc->sc_dev, + "sc_codecv); + break; + } +} + +static void +cs4231_ebdma_reset(struct cs4231_softc *sc) +{ + int i; + + /* playback */ + EBDMA_P_WRITE(sc, EBDMA_DCSR, + EBDMA_P_READ(sc, EBDMA_DCSR) & ~(EBDCSR_INTEN | EBDCSR_NEXTEN)); + EBDMA_P_WRITE(sc, EBDMA_DCSR, EBDCSR_RESET); + for (i = CS_TIMEOUT; + i && EBDMA_P_READ(sc, EBDMA_DCSR) & EBDCSR_DRAIN; i--) + DELAY(1); + if (i == 0) + device_printf(sc->sc_dev, + "timeout waiting for playback DMA reset\n"); + EBDMA_P_WRITE(sc, EBDMA_DCSR, sc->sc_burst); + /* capture */ + EBDMA_C_WRITE(sc, EBDMA_DCSR, + EBDMA_C_READ(sc, EBDMA_DCSR) & ~(EBDCSR_INTEN | EBDCSR_NEXTEN)); + EBDMA_C_WRITE(sc, EBDMA_DCSR, EBDCSR_RESET); + for (i = CS_TIMEOUT; + i && EBDMA_C_READ(sc, EBDMA_DCSR) & EBDCSR_DRAIN; i--) + DELAY(1); + if (i == 0) + device_printf(sc->sc_dev, + "timeout waiting for capture DMA reset\n"); + EBDMA_C_WRITE(sc, EBDMA_DCSR, sc->sc_burst); +} + +static void +cs4231_power_reset(struct cs4231_softc *sc, int how) +{ + u_int32_t v; + int i; + + if ((sc->sc_flags & CS4231_SBUS) != 0) { + 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)); + } else { + v = AUXIO_READ(sc, AUXIO_CODEC); + if (how == CODEC_WARM_RESET && v != 0) { + AUXIO_WRITE(sc, AUXIO_CODEC, 0); + DELAY(20); + } else if (how == CODEC_COLD_RESET){ + AUXIO_WRITE(sc, AUXIO_CODEC, 1); + DELAY(20); + AUXIO_WRITE(sc, AUXIO_CODEC, 0); + DELAY(20); + } + cs4231_ebdma_reset(sc); + } + + 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); + /* enable interupts & clear CSR */ + cs4231_write(sc, CS_PIN_CONTROL, + cs4231_read(sc, CS_PIN_CONTROL) | INTERRUPT_ENABLE); + CS_WRITE(sc, CS4231_STATUS, 0); + /* enable DAC output */ + cs4231_write(sc, CS_LEFT_OUTPUT_CONTROL, + cs4231_read(sc, CS_LEFT_OUTPUT_CONTROL) & ~OUTPUT_MUTE); + cs4231_write(sc, CS_RIGHT_OUTPUT_CONTROL, + cs4231_read(sc, CS_RIGHT_OUTPUT_CONTROL) & ~OUTPUT_MUTE); + /* mute AUX1 since it generates noises */ + cs4231_write(sc, CS_LEFT_AUX1_CONTROL, + cs4231_read(sc, CS_LEFT_AUX1_CONTROL) | AUX_INPUT_MUTE); + cs4231_write(sc, CS_RIGHT_AUX1_CONTROL, + cs4231_read(sc, CS_RIGHT_AUX1_CONTROL) | AUX_INPUT_MUTE); + /* protect buffer underrun and set output level to 0dB */ + cs4231_write(sc, CS_ALT_FEATURE1, + cs4231_read(sc, CS_ALT_FEATURE1) | CS_DAC_ZERO | CS_OUTPUT_LVL); + /* enable high pass filter, dual xtal was disabled due to noises */ + cs4231_write(sc, CS_ALT_FEATURE2, + cs4231_read(sc, CS_ALT_FEATURE2) | CS_HPF_ENABLE); +} + +static int +cs4231_enable(struct cs4231_softc *sc, int how) +{ + cs4231_power_reset(sc, how); + sc->sc_enabled = 1; + return (0); +} + +static void +cs4231_disable(struct cs4231_softc *sc) +{ + u_int8_t v; + + CS4231_LOCK_ASSERT(sc); + + if (sc->sc_enabled == 0) + return; + sc->sc_enabled = 0; + CS4231_UNLOCK(sc); + cs4231_halt(&sc->sc_pch); + cs4231_halt(&sc->sc_rch); + CS4231_LOCK(sc); + v = cs4231_read(sc, CS_PIN_CONTROL) & ~INTERRUPT_ENABLE; + cs4231_write(sc, CS_PIN_CONTROL, v); + + if ((sc->sc_flags & CS4231_SBUS) != 0) { + APC_WRITE(sc, APC_CSR, APC_CSR_RESET); + DELAY(10); + APC_WRITE(sc, APC_CSR, 0); + DELAY(10); + } else + cs4231_ebdma_reset(sc); +} + +static void +cs4231_free_resource(struct cs4231_softc *sc) +{ + int i; + + CS4231_LOCK(sc); + cs4231_disable(sc); + CS4231_UNLOCK(sc); + for (i = 0; i < sc->sc_nires; i++) { + if (sc->sc_irqres[i]) { + if (sc->sc_ih[i]) { + bus_teardown_intr(sc->sc_dev, sc->sc_irqres[i], + sc->sc_ih[i]); + sc->sc_ih[i] = NULL; + } + bus_release_resource(sc->sc_dev, SYS_RES_IRQ, + sc->sc_irqrid[i], sc->sc_irqres[i]); + sc->sc_irqres[i] = NULL; + } + } + for (i = 0; i < sc->sc_nires; i++) { + if (sc->sc_dmat[i]) + bus_dma_tag_destroy(sc->sc_dmat[i]); + } + for (i = 0; i < sc->sc_nmres; i++) { + if (sc->sc_res[i]) + bus_release_resource(sc->sc_dev, sc->sc_rtype, + sc->sc_rid[i], sc->sc_res[i]); + } + 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_sbus_intr(void *arg) +{ + struct cs4231_softc *sc; + struct cs4231_channel *pch, *rch; + u_int32_t csr; + u_int8_t 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); + } + + pch = rch = NULL; + if ((csr & APC_CSR_PMIE) && (csr & APC_CSR_PMI)) { + u_long nextaddr, saddr; + u_int32_t togo; + + pch = &sc->sc_pch; + togo = pch->togo; + saddr = sndbuf_getbufaddr(pch->buffer); + nextaddr = pch->nextaddr + togo; + if (nextaddr >= saddr + sndbuf_getsize(pch->buffer)) + nextaddr = saddr; + APC_WRITE(sc, APC_PNVA, nextaddr); + APC_WRITE(sc, APC_PNC, togo); + pch->nextaddr = nextaddr; + } + + if ((csr & APC_CSR_CIE) && (csr & APC_CSR_CI) && (csr & APC_CSR_CD)) { + u_long nextaddr, saddr; + u_int32_t togo; + + rch = &sc->sc_rch; + togo = rch->togo; + saddr = sndbuf_getbufaddr(rch->buffer); + nextaddr = rch->nextaddr + togo; + if (nextaddr >= saddr + sndbuf_getsize(rch->buffer)) + nextaddr = saddr; + APC_WRITE(sc, APC_CNVA, nextaddr); + APC_WRITE(sc, APC_CNC, togo); + rch->nextaddr = nextaddr; + } + CS4231_UNLOCK(sc); + if (pch) + chn_intr(pch->channel); + if (rch) + chn_intr(rch->channel); +} + +/* playback interrupt handler */ +static void +cs4231_ebus_pintr(void *arg) +{ + struct cs4231_softc *sc; + struct cs4231_channel *ch; + u_int32_t csr; + u_int8_t status; + + sc = arg; + CS4231_LOCK(sc); + + csr = EBDMA_P_READ(sc, EBDMA_DCSR); + if ((csr & EBDCSR_INT) == 0) { + CS4231_UNLOCK(sc); + return; + } + + if ((csr & EBDCSR_ERR)) { + status = cs4231_read(sc, CS_TEST_AND_INIT); + device_printf(sc->sc_dev, + "ebdma error interrupt : stat = 0x%x\n", status); + } + EBDMA_P_WRITE(sc, EBDMA_DCSR, csr | EBDCSR_TC); + + ch = NULL; + if (csr & EBDCSR_TC) { + u_long nextaddr, saddr; + u_int32_t togo; + + ch = &sc->sc_pch; + togo = ch->togo; + saddr = sndbuf_getbufaddr(ch->buffer); + nextaddr = ch->nextaddr + togo; + if (nextaddr >= saddr + sndbuf_getsize(ch->buffer)) + nextaddr = saddr; + /* + * EBDMA_DCNT is loaded automatically + * EBDMA_P_WRITE(sc, EBDMA_DCNT, togo); + */ + EBDMA_P_WRITE(sc, EBDMA_DADDR, nextaddr); + ch->nextaddr = nextaddr; + } + CS4231_UNLOCK(sc); + if (ch) + chn_intr(ch->channel); +} + +/* capture interrupt handler */ +static void +cs4231_ebus_cintr(void *arg) +{ + struct cs4231_softc *sc; + struct cs4231_channel *ch; + u_int32_t csr; + u_int8_t status; + + sc = arg; + CS4231_LOCK(sc); + + csr = EBDMA_C_READ(sc, EBDMA_DCSR); + if ((csr & EBDCSR_INT) == 0) { + CS4231_UNLOCK(sc); + return; + } + if ((csr & EBDCSR_ERR)) { + status = cs4231_read(sc, CS_TEST_AND_INIT); + device_printf(sc->sc_dev, + "dma error interrupt : stat = 0x%x\n", status); + } + EBDMA_C_WRITE(sc, EBDMA_DCSR, csr | EBDCSR_TC); + + ch = NULL; + if (csr & EBDCSR_TC) { + u_long nextaddr, saddr; + u_int32_t togo; + + ch = &sc->sc_rch; + togo = ch->togo; + saddr = sndbuf_getbufaddr(ch->buffer); + nextaddr = ch->nextaddr + togo; + if (nextaddr >= saddr + sndbuf_getblksz(ch->buffer)) + nextaddr = saddr; + /* + * EBDMA_DCNT is loaded automatically + * EBDMA_C_WRITE(sc, EBDMA_DCNT, togo); + */ + EBDMA_C_WRITE(sc, EBDMA_DADDR, nextaddr); + ch->nextaddr = nextaddr; + } + CS4231_UNLOCK(sc); + if (ch) + chn_intr(ch->channel); +} + +static const struct mix_table cs4231_mix_table[SOUND_MIXER_NRDEVICES][2] = { + [SOUND_MIXER_PCM] = { + { CS_LEFT_OUTPUT_CONTROL, 6, OUTPUT_MUTE, 0, 1, 1, 0 }, + { CS_RIGHT_OUTPUT_CONTROL, 6, OUTPUT_MUTE, 0, 1, 1, 0 } + }, + [SOUND_MIXER_SPEAKER] = { + { CS_MONO_IO_CONTROL, 4, MONO_OUTPUT_MUTE, 0, 1, 1, 0 }, + { CS_REG_NONE, 0, 0, 0, 0, 1, 0 } + }, + [SOUND_MIXER_LINE] = { + { CS_LEFT_LINE_CONTROL, 5, LINE_INPUT_MUTE, 0, 1, 1, 1 }, + { CS_RIGHT_LINE_CONTROL, 5, LINE_INPUT_MUTE, 0, 1, 1, 1 } + }, + /* + * AUX1 : removed intentionally since it generates noises + * AUX2 : Ultra1/Ultra2 has no internal CD-ROM audio in + */ + [SOUND_MIXER_CD] = { + { CS_LEFT_AUX2_CONTROL, 5, LINE_INPUT_MUTE, 0, 1, 1, 1 }, + { CS_RIGHT_AUX2_CONTROL, 5, LINE_INPUT_MUTE, 0, 1, 1, 1 } + }, + [SOUND_MIXER_MIC] = { + { CS_LEFT_INPUT_CONTROL, 4, 0, 0, 0, 1, 1 }, + { CS_RIGHT_INPUT_CONTROL, 4, 0, 0, 0, 1, 1 } + }, + [SOUND_MIXER_IGAIN] = { + { CS_LEFT_INPUT_CONTROL, 4, 0, 0, 1, 0 }, + { CS_RIGHT_INPUT_CONTROL, 4, 0, 0, 1, 0 } + } +}; + +static int +cs4231_mixer_init(struct snd_mixer *m) +{ + u_int32_t v; + int i; + + v = 0; + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) + if (cs4231_mix_table[i][0].avail != 0) + v |= (1 << i); + mix_setdevs(m, v); + v = 0; + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) + if (cs4231_mix_table[i][0].recdev != 0) + v |= (1 << i); + mix_setrecdevs(m, v); + return (0); +} + +static void +cs4231_mixer_set_value(struct cs4231_softc *sc, const struct mix_table *mt, + u_int8_t v) +{ + u_int8_t mask, reg; + u_int8_t old, shift, val; + + if (mt->avail == 0 || mt->reg == CS_REG_NONE) + return; + reg = mt->reg; + if (mt->neg != 0) + val = 100 - v; + else + val = v; + mask = (1 << mt->bits) - 1; + val = ((val * mask) + 50) / 100; + shift = mt->shift; + val <<= shift; + if (v == 0) + val |= mt->mute; + old = cs4231_read(sc, reg); + old &= ~(mt->mute | (mask << shift)); + val |= old; + if (reg == CS_LEFT_INPUT_CONTROL || reg == CS_RIGHT_INPUT_CONTROL) { + if ((val & (mask << shift)) != 0) + val |= ADC_INPUT_GAIN_ENABLE; + else + val &= ~ADC_INPUT_GAIN_ENABLE; + } + cs4231_write(sc, reg, val); +} + +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; + + sc = mix_getdevinfo(m); + CS4231_LOCK(sc); + cs4231_mixer_set_value(sc, &cs4231_mix_table[dev][0], left); + cs4231_mixer_set_value(sc, &cs4231_mix_table[dev][1], right); + 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: + v = CS_IN_LINE; + break; + + case SOUND_MASK_CD: + v = CS_IN_DAC; + break; + + case SOUND_MASK_MIC: + default: + v = CS_IN_MIC; + src = SOUND_MASK_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; + bus_dma_tag_t dmat; + + 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 ((sc->sc_flags & CS4231_SBUS) != 0) + dmat = sc->sc_dmat[0]; + else { + if (dir == PCMDIR_PLAY) + dmat = sc->sc_dmat[1]; + else + dmat = sc->sc_dmat[0]; + } + if (sndbuf_alloc(ch->buffer, dmat, sc->sc_bufsz) != 0) + return (NULL); + DPRINTF(("%s channel addr: 0x%lx\n", dir == PCMDIR_PLAY ? "playback" : + "capture", 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; + + CS4231_LOCK(sc); + if (ch->format == format) { + CS4231_UNLOCK(sc); + return (0); + } + + encoding = format & ~AFMT_STEREO; + 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_S16_BE: + fs = CS_AFMT_S16_BE; + break; + default: + fs = CS_AFMT_U8; + format = AFMT_U8; + break; + } + + if (format & AFMT_STEREO) + fs |= CS_AFMT_STEREO; + + DPRINTF(("FORMAT: %s : 0x%x\n", ch->dir == PCMDIR_PLAY ? "playback" : + "capture", format)); + v = cs4231_read(sc, CS_CLOCK_DATA_FORMAT); + v &= CS_CLOCK_DATA_FORMAT_MASK; + fs |= v; + cs4231_chan_fs(sc, ch->dir, fs); + ch->format = format; + 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; + CS4231_LOCK(sc); + if (ch->speed == speed) { + CS4231_UNLOCK(sc); + return (speed); + } + 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; + + fs = cs4231_read(sc, CS_CLOCK_DATA_FORMAT); + fs &= ~CS_CLOCK_DATA_FORMAT_MASK; + fs |= speed_table[sel].bits; + cs4231_chan_fs(sc, ch->dir, fs); + ch->speed = speed; + CS4231_UNLOCK(sc); + + return (speed); +} + +static void +cs4231_chan_fs(struct cs4231_softc *sc, int dir, u_int8_t fs) +{ + int i, doreset; +#ifdef CS4231_AUTO_CALIBRATION + u_int8_t v; +#endif + + CS4231_LOCK_ASSERT(sc); + + /* set autocalibration */ + doreset = 0; +#ifdef CS4231_AUTO_CALIBRATION + 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); +#endif + + /* + * We always need to write CS_CLOCK_DATA_FORMAT register since + * the clock frequency is shared with playback/capture. + */ + 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 seperate fs setup for playback/capture. + * I believe this will break full-duplex operation. + */ + if (dir == PCMDIR_REC) { + 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++; + } + +#ifdef CS4231_AUTO_CALIBRATION + 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++; + } +#endif + 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_disable(sc); + cs4231_enable(sc, CODEC_COLD_RESET); + CS4231_UNLOCK(sc); /* XXX */ + if (mixer_reinit(sc->sc_dev) != 0) + 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, error; + + ch = data; + sc = ch->parent; + + if (blocksize > CS4231_MAX_BLK_SZ) + blocksize = CS4231_MAX_BLK_SZ; + nblks = sc->sc_bufsz / blocksize; + error = sndbuf_resize(ch->buffer, nblks, blocksize); + if (error != 0) + device_printf(sc->sc_dev, + "unable to block size, blksz = %d, error = %d\n", + blocksize, error); + + 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 cur; + int ptr, sz; + + ch = data; + sc = ch->parent; + + CS4231_LOCK(sc); + if ((sc->sc_flags & CS4231_SBUS) != 0) + cur = (ch->dir == PCMDIR_PLAY) ? APC_READ(sc, APC_PVA) : + APC_READ(sc, APC_CVA); + else + cur = (ch->dir == PCMDIR_PLAY) ? EBDMA_P_READ(sc, EBDMA_DADDR) : + EBDMA_C_READ(sc, EBDMA_DADDR); + sz = sndbuf_getsize(ch->buffer); + ptr = cur - sndbuf_getbufaddr(ch->buffer) + sz; + 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; + + sc = ch->parent; + if ((sc->sc_flags & CS4231_SBUS) != 0) + cs4231_apcdma_trigger(sc, ch); + else + cs4231_ebdma_trigger(sc, ch); +} + +static void +cs4231_apcdma_trigger(struct cs4231_softc *sc, struct cs4231_channel *ch) +{ + u_int32_t csr, togo; + u_int32_t nextaddr; + + CS4231_LOCK(sc); + if (ch->locked) { + device_printf(sc->sc_dev, "%s channel already triggered\n", + ch->dir == PCMDIR_PLAY ? "playback" : "capture"); + CS4231_UNLOCK(sc); + return; + } + + nextaddr = sndbuf_getbufaddr(ch->buffer); + togo = sndbuf_getsize(ch->buffer) / 2; + if (togo > CS4231_MAX_APC_DMA_SZ) + togo = CS4231_MAX_APC_DMA_SZ; + ch->togo = togo; + if (ch->dir == PCMDIR_PLAY) { + DPRINTF(("TRG: PNVA = 0x%x, togo = 0x%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_INTERFACE_CONFIG, + cs4231_read(sc, CS_INTERFACE_CONFIG) | + PLAYBACK_ENABLE); + } + /* load next address */ + if (APC_READ(sc, APC_CSR) & APC_CSR_PD) { + nextaddr += togo; + APC_WRITE(sc, APC_PNVA, nextaddr); + APC_WRITE(sc, APC_PNC, togo); + } + } else { + DPRINTF(("TRG: CNVA = 0x%x, togo = 0x%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_INTERFACE_CONFIG, + cs4231_read(sc, CS_INTERFACE_CONFIG) | + CAPTURE_ENABLE); + } + /* load next address */ + if (APC_READ(sc, APC_CSR) & APC_CSR_CD) { + nextaddr += togo; + APC_WRITE(sc, APC_CNVA, nextaddr); + APC_WRITE(sc, APC_CNC, togo); + } + } + ch->nextaddr = nextaddr; + ch->locked = 1; + CS4231_UNLOCK(sc); +} + +static void +cs4231_ebdma_trigger(struct cs4231_softc *sc, struct cs4231_channel *ch) +{ + u_int32_t csr, togo; + u_int32_t nextaddr; + + CS4231_LOCK(sc); + if (ch->locked) { + device_printf(sc->sc_dev, "%s channel already triggered\n", + ch->dir == PCMDIR_PLAY ? "playback" : "capture"); + CS4231_UNLOCK(sc); + return; + } + + nextaddr = sndbuf_getbufaddr(ch->buffer); + togo = sndbuf_getsize(ch->buffer) / 2; + if (togo % 64 == 0) + sc->sc_burst = EBDCSR_BURST_16; + else if (togo % 32 == 0) + sc->sc_burst = EBDCSR_BURST_8; + else if (togo % 16 == 0) + sc->sc_burst = EBDCSR_BURST_4; + else + sc->sc_burst = EBDCSR_BURST_1; + ch->togo = togo; + DPRINTF(("TRG: DNAR = 0x%x, togo = 0x%x\n", nextaddr, togo)); + if (ch->dir == PCMDIR_PLAY) { + cs4231_read(sc, CS_TEST_AND_INIT); /* clear pending error */ + csr = EBDMA_P_READ(sc, EBDMA_DCSR); + + if (csr & EBDCSR_DMAEN) { + EBDMA_P_WRITE(sc, EBDMA_DCNT, togo); + EBDMA_P_WRITE(sc, EBDMA_DADDR, nextaddr); + } else { + EBDMA_P_WRITE(sc, EBDMA_DCSR, EBDCSR_RESET); + EBDMA_P_WRITE(sc, EBDMA_DCSR, sc->sc_burst); + EBDMA_P_WRITE(sc, EBDMA_DCNT, togo); + EBDMA_P_WRITE(sc, EBDMA_DADDR, nextaddr); + + EBDMA_P_WRITE(sc, EBDMA_DCSR, sc->sc_burst | + EBDCSR_DMAEN | EBDCSR_INTEN | EBDCSR_CNTEN | + EBDCSR_NEXTEN); + cs4231_write(sc, CS_INTERFACE_CONFIG, + cs4231_read(sc, CS_INTERFACE_CONFIG) | + PLAYBACK_ENABLE); + } + /* load next address */ + if (EBDMA_P_READ(sc, EBDMA_DCSR) & EBDCSR_A_LOADED) { + nextaddr += togo; + EBDMA_P_WRITE(sc, EBDMA_DCNT, togo); + EBDMA_P_WRITE(sc, EBDMA_DADDR, nextaddr); + } + } else { + cs4231_read(sc, CS_TEST_AND_INIT); /* clear pending error */ + csr = EBDMA_C_READ(sc, EBDMA_DCSR); + + if (csr & EBDCSR_DMAEN) { + EBDMA_C_WRITE(sc, EBDMA_DCNT, togo); + EBDMA_C_WRITE(sc, EBDMA_DADDR, nextaddr); + } else { + EBDMA_C_WRITE(sc, EBDMA_DCSR, EBDCSR_RESET); + EBDMA_C_WRITE(sc, EBDMA_DCSR, sc->sc_burst); + EBDMA_C_WRITE(sc, EBDMA_DCNT, togo); + EBDMA_C_WRITE(sc, EBDMA_DADDR, nextaddr); + + EBDMA_C_WRITE(sc, EBDMA_DCSR, sc->sc_burst | + EBDCSR_WRITE | EBDCSR_DMAEN | EBDCSR_INTEN | + EBDCSR_CNTEN | EBDCSR_NEXTEN); + cs4231_write(sc, CS_INTERFACE_CONFIG, + cs4231_read(sc, CS_INTERFACE_CONFIG) | + CAPTURE_ENABLE); + } + /* load next address */ + if (EBDMA_C_READ(sc, EBDMA_DCSR) & EBDCSR_A_LOADED) { + nextaddr += togo; + EBDMA_C_WRITE(sc, EBDMA_DCNT, togo); + EBDMA_C_WRITE(sc, EBDMA_DADDR, nextaddr); + } + } + ch->nextaddr = nextaddr; + ch->locked = 1; + CS4231_UNLOCK(sc); +} + +static void +cs4231_halt(struct cs4231_channel *ch) +{ + struct cs4231_softc *sc; + u_int8_t status; + int i; + + sc = ch->parent; + CS4231_LOCK(sc); + if (ch->locked == 0) { + CS4231_UNLOCK(sc); + return; + } + + if (ch->dir == PCMDIR_PLAY ) { + if ((sc->sc_flags & CS4231_SBUS) != 0) { + /* 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)); + } else { + EBDMA_P_WRITE(sc, EBDMA_DCSR, + EBDMA_P_READ(sc, EBDMA_DCSR) & ~EBDCSR_DMAEN); + } + /* Waiting for playback FIFO to empty */ + status = cs4231_read(sc, CS_TEST_AND_INIT); + for (i = CS_TIMEOUT; + i && (status & PLAYBACK_UNDERRUN) == 0; i--) { + DELAY(5); + status = cs4231_read(sc, CS_TEST_AND_INIT); + } + if (i == 0) + device_printf(sc->sc_dev, "timeout waiting for " + "playback FIFO drain\n"); + cs4231_write(sc, CS_INTERFACE_CONFIG, + cs4231_read(sc, CS_INTERFACE_CONFIG) & (~PLAYBACK_ENABLE)); + } else { + if ((sc->sc_flags & CS4231_SBUS) != 0) { + /* XXX Kills some playback bits */ + APC_WRITE(sc, APC_CSR, APC_CSR_CAPTURE_PAUSE); + } else { + EBDMA_C_WRITE(sc, EBDMA_DCSR, + EBDMA_C_READ(sc, EBDMA_DCSR) & ~EBDCSR_DMAEN); + } + /* Waiting for capture FIFO to empty */ + status = cs4231_read(sc, CS_TEST_AND_INIT); + for (i = CS_TIMEOUT; + i && (status & CAPTURE_OVERRUN) == 0; i--) { + DELAY(5); + status = cs4231_read(sc, CS_TEST_AND_INIT); + } + if (i == 0) + device_printf(sc->sc_dev, "timeout waiting for " + "capture FIFO drain\n"); + cs4231_write(sc, CS_INTERFACE_CONFIG, + cs4231_read(sc, CS_INTERFACE_CONFIG) & (~CAPTURE_ENABLE)); + } + ch->locked = 0; + CS4231_UNLOCK(sc); +} diff --git a/sys/dev/sound/sbus/cs4231.h b/sys/dev/sound/sbus/cs4231.h new file mode 100644 index 000000000000..2b160c178da6 --- /dev/null +++ b/sys/dev/sound/sbus/cs4231.h @@ -0,0 +1,248 @@ +/* $FreeBSD$ */ +/*- + * 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_REG_NONE 0xff + +#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 + +/* ADC input control - registers I0 (channel 1,left); I1 (channel 1,right) */ +#define ADC_INPUT_ATTEN_BITS 0x0f +#define ADC_INPUT_GAIN_ENABLE 0x20 + +/* 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_S16_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 +#define CS_CODEC_ID_MASK 0x0f + +/* Digital Mix Control reg bits - register I13 */ +#define DIGITAL_MIX1_ENABLE 0x01 +#define MIX_ATTEN_MASK 0x03 + +/* Alternate Feature Enable I - register I16 */ +#define CS_DAC_ZERO 0x01 +#define CS_PMC_ENABLE 0x10 +#define CS_CMC_ENABLE 0x20 +#define CS_OUTPUT_LVL 0x80 + +/* Alternate Feature Enable II - register I17 */ +#define CS_HPF_ENABLE 0x01 +#define DUAL_XTAL_ENABLE 0x02 + +/* 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 */ + +/* Version - register I25 */ +#define CS_VERSION_NUMBER 0xe0 +#define CS_VERSION_CHIPID 0x07 + +/* 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 diff --git a/sys/modules/Makefile b/sys/modules/Makefile index 45ba8cb88082..b9c23bbc9f4b 100644 --- a/sys/modules/Makefile +++ b/sys/modules/Makefile @@ -443,6 +443,7 @@ _gem= gem .if ${MACHINE_ARCH} == "sparc64" _auxio= auxio _gem= gem +_sound= sound .endif .if defined(MODULES_OVERRIDE) && !defined(ALL_MODULES) diff --git a/sys/modules/sound/driver/Makefile b/sys/modules/sound/driver/Makefile index 2994097d4676..c863d7cce20d 100644 --- a/sys/modules/sound/driver/Makefile +++ b/sys/modules/sound/driver/Makefile @@ -1,8 +1,12 @@ # $FreeBSD$ +.if ${MACHINE_ARCH} == "sparc64" +SUBDIR = audiocs +.else SUBDIR = als4000 ad1816 cmi 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 +.endif .include diff --git a/sys/modules/sound/driver/audiocs/Makefile b/sys/modules/sound/driver/audiocs/Makefile new file mode 100644 index 000000000000..ff67554f9e1f --- /dev/null +++ b/sys/modules/sound/driver/audiocs/Makefile @@ -0,0 +1,10 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/../../../../dev/sound/sbus + +KMOD= snd_audiocs +SRCS= device_if.h bus_if.h ofw_bus_if.h +SRCS+= channel_if.h feeder_if.h mixer_if.h +SRCS+= cs4231.c + +.include diff --git a/sys/sparc64/conf/NOTES b/sys/sparc64/conf/NOTES index 7fd6e2749eb4..c67c390a17e6 100644 --- a/sys/sparc64/conf/NOTES +++ b/sys/sparc64/conf/NOTES @@ -70,7 +70,6 @@ nodevice star_saver nodevice bktr nodevice fdc nodevice ppc -nodevice sound nodevice "snd_ad1816" nodevice "snd_als4000" nodevice "snd_au88x0" diff --git a/sys/sparc64/ebus/ebusreg.h b/sys/sparc64/ebus/ebusreg.h new file mode 100644 index 000000000000..84ece991cb05 --- /dev/null +++ b/sys/sparc64/ebus/ebusreg.h @@ -0,0 +1,91 @@ +/* $FreeBSD$ */ +/* $OpenBSD: ebusreg.h,v 1.4 2001/10/01 18:08:04 jason Exp $ */ +/* $NetBSD: ebusreg.h,v 1.1 1999/06/04 13:29:13 mrg Exp $ */ + +/* + * Copyright (c) 1999 Matthew R. Green + * 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. 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. + * + */ + +/* + * UltraSPARC `ebus' + * + * The `ebus' bus is designed to plug traditional PC-ISA devices into + * an SPARC system with as few costs as possible, without sacrificing + * to performance. Typically, it is implemented in the PCIO IC from + * SME, which also implements a `hme-compatible' PCI network device + * (`network'). The ebus has 4 DMA channels, similar to the DMA seen + * in the ESP SCSI DMA. + * + * Typical UltraSPARC systems have a NatSemi SuperIO IC to provide + * serial ports for the keyboard and mouse (`se'), floppy disk + * controller (`fdthree'), parallel port controller (`bpp') connected + * to the ebus, and a PCI-IDE controller (connected directly to the + * PCI bus, of course), as well as a Siemens Nixdorf SAB82532 dual + * channel serial controller (`su' providing ttya and ttyb), an MK48T59 + * EEPROM/clock controller (also where the idprom, including the + * ethernet address, is located), the audio system (`SUNW,CS4231', same + * as other UltraSPARC and some SPARC systems), and other various + * internal devices found on traditional SPARC systems such as the + * `power', `flashprom', etc., devices. + * + * The ebus uses an interrupt mapping scheme similar to PCI, though + * the actual structures are different. + */ + +/* EBUS dma registers */ +#define EBDMA_DCSR 0x0 /* control/status */ +#define EBDMA_DADDR 0x4 /* DMA address */ +#define EBDMA_DCNT 0x8 /* DMA count */ + +/* EBUS DMA control/status (EBDMA_DCSR) */ +#define EBDCSR_INT 0x00000001 /* interrupt pending */ +#define EBDCSR_ERR 0x00000002 /* error pending */ +#define EBDCSR_DRAIN 0x00000004 /* drain */ +#define EBDCSR_INTEN 0x00000010 /* interrupt enable */ +#define EBDCSR_RESET 0x00000080 /* reset */ +#define EBDCSR_WRITE 0x00000100 /* write */ +#define EBDCSR_DMAEN 0x00000200 /* dma enable */ +#define EBDCSR_CYC 0x00000400 /* cyc pending */ +#define EBDCSR_DIAGRD 0x00000800 /* diagnostic read done */ +#define EBDCSR_DIAGWR 0x00001000 /* diagnostic write done */ +#define EBDCSR_CNTEN 0x00002000 /* count enable */ +#define EBDCSR_TC 0x00004000 /* terminal count */ +#define EBDCSR_CSRDRNDIS 0x00010000 /* disable csr drain */ +#define EBDCSR_BURSTMASK 0x000c0000 /* burst size mask */ +#define EBDCSR_BURST_1 0x00080000 /* burst 1 */ +#define EBDCSR_BURST_4 0x00000000 /* burst 4 */ +#define EBDCSR_BURST_8 0x00040000 /* burst 8 */ +#define EBDCSR_BURST_16 0x000c0000 /* burst 16 */ +#define EBDCSR_DIAGEN 0x00100000 /* enable diagnostics */ +#define EBDCSR_ERRDIS 0x00400000 /* disable error pending */ +#define EBDCSR_TCIDIS 0x00800000 /* disable TCI */ +#define EBDCSR_NEXTEN 0x01000000 /* enable next */ +#define EBDCSR_DMAON 0x02000000 /* dma on */ +#define EBDCSR_A_LOADED 0x04000000 /* address loaded */ +#define EBDCSR_NA_LOADED 0x08000000 /* next address loaded */ +#define EBDCSR_DEVMASK 0xf0000000 /* device id mask */ diff --git a/sys/sparc64/isa/isa_dma.c b/sys/sparc64/isa/isa_dma.c new file mode 100644 index 000000000000..66d93fbee33f --- /dev/null +++ b/sys/sparc64/isa/isa_dma.c @@ -0,0 +1,90 @@ +/*- + * Copyright (c) 2004 Pyun YongHyeon. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include +#include + +/* + * Glue code to load sound(4). Though fdc(4), ppc(4) don't work on + * sparc64 yet, they may need this glue code too. + */ + +int +isa_dma_init(int chan, u_int bouncebufsize, int flag) +{ + + return (0); +} + +int +isa_dma_acquire(int chan) +{ + return (0); +} + +void +isa_dma_release(int chan) +{ + +} + +void +isa_dmacascade(int chan) +{ + +} + +void +isa_dmastart(int flags, caddr_t addr, u_int nbytes, int chan) +{ + +} + +void +isa_dmadone(int flags, caddr_t addr, int nbytes, int chan) +{ + +} + +int +isa_dmastatus(int chan) +{ + return (0); +} + +int +isa_dmastop(int chan) +{ + return (0); +}