pcm sound driver for SBus Ultra1/Ultra2
Pyun YongHyeon
yongari at kt-is.co.kr
Thu Jul 1 02:33:13 PDT 2004
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>
-------------- next part --------------
--- 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 at 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 at thought.net)
+ * Copyright (c) 2004 Pyun YongHyeon <yongari at 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>
More information about the freebsd-sparc64
mailing list