summaryrefslogtreecommitdiff
path: root/sys/arch/sparc/dev/audioamd.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/arch/sparc/dev/audioamd.c')
-rw-r--r--sys/arch/sparc/dev/audioamd.c450
1 files changed, 450 insertions, 0 deletions
diff --git a/sys/arch/sparc/dev/audioamd.c b/sys/arch/sparc/dev/audioamd.c
new file mode 100644
index 00000000000..90915cc19f9
--- /dev/null
+++ b/sys/arch/sparc/dev/audioamd.c
@@ -0,0 +1,450 @@
+/* $OpenBSD: audioamd.c,v 1.1 2011/09/03 20:04:02 miod Exp $ */
+/* $NetBSD: audioamd.c,v 1.26 2011/06/04 01:27:57 tsutsui Exp $ */
+
+/*
+ * Copyright (c) 1995 Rolf Grossmann
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Rolf Grossmann.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/device.h>
+#include <sys/proc.h>
+
+#include <machine/autoconf.h>
+#include <machine/cpu.h>
+
+#include <sys/audioio.h>
+#include <dev/audio_if.h>
+
+#include <dev/ic/am7930reg.h>
+#include <dev/ic/am7930var.h>
+#include <sparc/dev/audioamdvar.h>
+
+#define AUDIO_ROM_NAME "audio"
+
+#ifdef AUDIO_DEBUG
+#define DPRINTF(x) if (am7930debug) printf x
+#define DPRINTFN(n,x) if (am7930debug>(n)) printf x
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif /* AUDIO_DEBUG */
+
+/*
+ * Define AUDIO_C_HANDLER to force using non-fast trap routines.
+ */
+/* #define AUDIO_C_HANDLER */
+
+struct audioamd_softc {
+ struct am7930_softc sc_am7930; /* glue to MI code */
+
+ void (*sc_rintr)(void*); /* input completion intr handler */
+ void *sc_rarg; /* arg for sc_rintr() */
+ void (*sc_pintr)(void*); /* output completion intr handler */
+ void *sc_parg; /* arg for sc_pintr() */
+
+ /* sc_au is special in that the hardware interrupt handler uses it */
+ struct auio sc_au; /* recv and xmit buffers, etc */
+#define sc_hwih sc_au.au_ih /* hardware interrupt vector */
+#define sc_swih sc_au.au_swih /* software interrupt cookie */
+};
+
+void audioamd_attach(struct device *, struct device *, void *);
+int audioamd_match(struct device *, void *, void *);
+
+struct cfdriver audioamd_cd = {
+ NULL, "audioamd", DV_DULL
+};
+
+const struct cfattach audioamd_ca = {
+ sizeof(struct audioamd_softc), audioamd_match, audioamd_attach
+};
+
+/*
+ * Define our interface into the am7930 MI driver.
+ */
+
+uint8_t audioamd_codec_iread(struct am7930_softc *, int);
+uint16_t audioamd_codec_iread16(struct am7930_softc *, int);
+uint8_t audioamd_codec_dread(struct audioamd_softc *, int);
+void audioamd_codec_iwrite(struct am7930_softc *, int, uint8_t);
+void audioamd_codec_iwrite16(struct am7930_softc *, int, uint16_t);
+void audioamd_codec_dwrite(struct audioamd_softc *, int, uint8_t);
+void audioamd_onopen(struct am7930_softc *);
+void audioamd_onclose(struct am7930_softc *);
+
+struct am7930_glue audioamd_glue = {
+ audioamd_codec_iread,
+ audioamd_codec_iwrite,
+ audioamd_codec_iread16,
+ audioamd_codec_iwrite16,
+ audioamd_onopen,
+ audioamd_onclose
+};
+
+/*
+ * Define our interface to the higher level audio driver.
+ */
+int audioamd_start_output(void *, void *, int, void (*)(void *), void *);
+int audioamd_start_input(void *, void *, int, void (*)(void *), void *);
+int audioamd_getdev(void *, struct audio_device *);
+
+struct audio_hw_if sa_hw_if = {
+ am7930_open,
+ am7930_close,
+ NULL,
+ am7930_query_encoding,
+ am7930_set_params,
+ am7930_round_blocksize,
+ am7930_commit_settings,
+ NULL,
+ NULL,
+ audioamd_start_output,
+ audioamd_start_input,
+ am7930_halt_output,
+ am7930_halt_input,
+ NULL,
+ audioamd_getdev,
+ NULL,
+ am7930_set_port,
+ am7930_get_port,
+ am7930_query_devinfo,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ am7930_get_props,
+ NULL,
+ NULL,
+ NULL
+};
+
+struct audio_device audioamd_device = {
+ "am7930",
+ "x",
+ "audioamd"
+};
+
+/* forward declarations */
+int amd7930_shareintr(void *);
+#ifndef AUDIO_C_HANDLER
+struct auio *auiop;
+#endif /* AUDIO_C_HANDLER */
+int am7930hwintr(void *);
+void am7930swintr(void *);
+
+int
+audioamd_match(struct device *parent, void *vcf, void *aux)
+{
+ struct confargs *ca = aux;
+ struct romaux *ra = &ca->ca_ra;
+
+ if (CPU_ISSUN4)
+ return 0;
+ return strcmp(AUDIO_ROM_NAME, ra->ra_name) == 0;
+}
+
+void
+audioamd_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct audioamd_softc *sc = (struct audioamd_softc *)self;
+ struct confargs *ca = aux;
+ struct romaux *ra = &ca->ca_ra;
+ int pri;
+
+ if (ra->ra_nintr != 1) {
+ printf(": expected 1 interrupt, got %d\n", ra->ra_nintr);
+ return;
+ }
+ pri = ra->ra_intr[0].int_pri;
+ printf(" pri %d, softpri %d\n", pri, IPL_AUSOFT);
+ sc->sc_au.au_sc = sc;
+ sc->sc_au.au_amd = (volatile uint8_t *)(ra->ra_vaddr ?
+ ra->ra_vaddr : mapiodev(ra->ra_reg, 0, AM7930_DREG_SIZE));
+
+ /*
+ * Set up glue for MI code early; we use some of it here.
+ */
+ sc->sc_am7930.sc_glue = &audioamd_glue;
+ am7930_init(&sc->sc_am7930, AUDIOAMD_POLL_MODE);
+
+ /*
+ * Register interrupt handlers. We'll prefer a fast trap (unless
+ * AUDIO_C_HANDLER is defined), with a sharing callback so that we
+ * can revert into a regular trap vector if necessary.
+ */
+#ifndef AUDIO_C_HANDLER
+ sc->sc_hwih.ih_vec = pri;
+ if (intr_fasttrap(pri, amd7930_trap, amd7930_shareintr, sc) == 0) {
+ auiop = &sc->sc_au;
+ evcount_attach(&sc->sc_hwih.ih_count, self->dv_xname,
+ &sc->sc_hwih.ih_vec);
+ } else {
+#ifdef AUDIO_DEBUG
+ printf("%s: unable to register fast trap handler\n",
+ self->dv_xname);
+#endif
+#else
+ {
+#endif
+ sc->sc_hwih.ih_fun = am7930hwintr;
+ sc->sc_hwih.ih_arg = &sc->sc_au;
+ intr_establish(pri, &sc->sc_hwih, IPL_AUHARD, self->dv_xname);
+ }
+
+ sc->sc_swih = softintr_establish(IPL_AUSOFT, am7930swintr, sc);
+
+ audio_attach_mi(&sa_hw_if, sc, self);
+}
+
+void
+audioamd_onopen(struct am7930_softc *sc)
+{
+ struct audioamd_softc *ausc = (struct audioamd_softc *)sc;
+
+ /* reset pdma state */
+ ausc->sc_rintr = NULL;
+ ausc->sc_rarg = 0;
+ ausc->sc_pintr = NULL;
+ ausc->sc_parg = 0;
+
+ ausc->sc_au.au_rdata = NULL;
+ ausc->sc_au.au_pdata = NULL;
+}
+
+
+void
+audioamd_onclose(struct am7930_softc *sc)
+{
+ /* On sparc, just do the chipset-level halt. */
+ am7930_halt_input(sc);
+ am7930_halt_output(sc);
+}
+
+int
+audioamd_start_output(void *addr, void *p, int cc,
+ void (*intr)(void *), void *arg)
+{
+ struct audioamd_softc *sc = addr;
+
+ DPRINTFN(1, ("sa_start_output: cc=%d %p (%p)\n", cc, intr, arg));
+
+ if (!sc->sc_am7930.sc_locked) {
+ audioamd_codec_iwrite(&sc->sc_am7930,
+ AM7930_IREG_INIT, AM7930_INIT_PMS_ACTIVE);
+ sc->sc_am7930.sc_locked = 1;
+ DPRINTF(("sa_start_output: started intrs.\n"));
+ }
+ sc->sc_pintr = intr;
+ sc->sc_parg = arg;
+ sc->sc_au.au_pdata = p;
+ sc->sc_au.au_pend = (char *)p + cc - 1;
+ return 0;
+}
+
+int
+audioamd_start_input(void *addr, void *p, int cc,
+ void (*intr)(void *), void *arg)
+{
+ struct audioamd_softc *sc = addr;
+
+ DPRINTFN(1, ("sa_start_input: cc=%d %p (%p)\n", cc, intr, arg));
+
+ if (!sc->sc_am7930.sc_locked) {
+ audioamd_codec_iwrite(&sc->sc_am7930,
+ AM7930_IREG_INIT, AM7930_INIT_PMS_ACTIVE);
+ sc->sc_am7930.sc_locked = 1;
+ DPRINTF(("sa_start_input: started intrs.\n"));
+ }
+ sc->sc_rintr = intr;
+ sc->sc_rarg = arg;
+ sc->sc_au.au_rdata = p;
+ sc->sc_au.au_rend = (char *)p + cc -1;
+ return 0;
+}
+
+/*
+ * Pseudo-DMA support: either C or locore assember.
+ */
+
+int
+am7930hwintr(void *v)
+{
+ struct auio *au = v;
+ struct audioamd_softc *sc = au->au_sc;
+ uint8_t *d, *e;
+ int k;
+
+ /* clear interrupt */
+ k = audioamd_codec_dread(sc, AM7930_DREG_IR);
+ if ((k & (AM7930_IR_DTTHRSH | AM7930_IR_DRTHRSH | AM7930_IR_DSRI |
+ AM7930_IR_DERI | AM7930_IR_BBUFF)) == 0)
+ return 0;
+
+ /* receive incoming data */
+ d = au->au_rdata;
+ e = au->au_rend;
+ if (d != NULL && d <= e) {
+ *d = audioamd_codec_dread(sc, AM7930_DREG_BBRB);
+ au->au_rdata++;
+ if (d == e) {
+ DPRINTFN(1, ("am7930hwintr: swintr(r) requested"));
+ softintr_schedule(au->au_swih);
+ }
+ }
+
+ /* send outgoing data */
+ d = au->au_pdata;
+ e = au->au_pend;
+ if (d != NULL && d <= e) {
+ audioamd_codec_dwrite(sc, AM7930_DREG_BBTB, *d);
+ au->au_pdata++;
+ if (d == e) {
+ DPRINTFN(1, ("am7930hwintr: swintr(p) requested"));
+ softintr_schedule(au->au_swih);
+ }
+ }
+
+ return 1;
+}
+
+void
+am7930swintr(void *v)
+{
+ struct audioamd_softc *sc = v;
+ struct auio *au;
+ int s, dor, dow;
+
+ DPRINTFN(1, ("audiointr: sc=%p\n", sc););
+
+ au = &sc->sc_au;
+ dor = dow = 0;
+ s = splaudio();
+ if (au->au_rdata > au->au_rend && sc->sc_rintr != NULL)
+ dor = 1;
+ if (au->au_pdata > au->au_pend && sc->sc_pintr != NULL)
+ dow = 1;
+ splx(s);
+
+ if (dor != 0)
+ (*sc->sc_rintr)(sc->sc_rarg);
+ if (dow != 0)
+ (*sc->sc_pintr)(sc->sc_parg);
+}
+
+#ifndef AUDIO_C_HANDLER
+int
+amd7930_shareintr(void *arg)
+{
+ struct audioamd_softc *sc = arg;
+
+ /*
+ * We are invoked at splhigh(), so there is no need to prevent the chip
+ * from interrupting while we are messing with the handlers. We
+ * however need to properly untie the event counter from the chain,
+ * since it will be reused immediately by intr_establish()...
+ */
+
+ intr_fastuntrap(sc->sc_hwih.ih_vec);
+ evcount_detach(&sc->sc_hwih.ih_count);
+
+ sc->sc_hwih.ih_fun = am7930hwintr;
+ sc->sc_hwih.ih_arg = &sc->sc_au;
+ intr_establish(sc->sc_hwih.ih_vec, &sc->sc_hwih, IPL_AUHARD,
+ sc->sc_am7930.sc_dev.dv_xname);
+
+ return 0;
+}
+#endif
+
+/* indirect write */
+void
+audioamd_codec_iwrite(struct am7930_softc *sc, int reg, uint8_t val)
+{
+ struct audioamd_softc *ausc = (struct audioamd_softc *)sc;
+
+ audioamd_codec_dwrite(ausc, AM7930_DREG_CR, reg);
+ audioamd_codec_dwrite(ausc, AM7930_DREG_DR, val);
+}
+
+void
+audioamd_codec_iwrite16(struct am7930_softc *sc, int reg, uint16_t val)
+{
+ struct audioamd_softc *ausc = (struct audioamd_softc *)sc;
+
+ audioamd_codec_dwrite(ausc, AM7930_DREG_CR, reg);
+ audioamd_codec_dwrite(ausc, AM7930_DREG_DR, val);
+ audioamd_codec_dwrite(ausc, AM7930_DREG_DR, val >> 8);
+}
+
+
+/* indirect read */
+uint8_t
+audioamd_codec_iread(struct am7930_softc *sc, int reg)
+{
+ struct audioamd_softc *ausc = (struct audioamd_softc *)sc;
+
+ audioamd_codec_dwrite(ausc, AM7930_DREG_CR, reg);
+ return audioamd_codec_dread(ausc, AM7930_DREG_DR);
+}
+
+uint16_t
+audioamd_codec_iread16(struct am7930_softc *sc, int reg)
+{
+ struct audioamd_softc *ausc = (struct audioamd_softc *)sc;
+ uint lo, hi;
+
+ audioamd_codec_dwrite(ausc, AM7930_DREG_CR, reg);
+ lo = audioamd_codec_dread(ausc, AM7930_DREG_DR);
+ hi = audioamd_codec_dread(ausc, AM7930_DREG_DR);
+ return (hi << 8) | lo;
+}
+
+/* direct read */
+uint8_t
+audioamd_codec_dread(struct audioamd_softc *sc, int reg)
+{
+ return sc->sc_au.au_amd[reg];
+}
+
+/* direct write */
+void
+audioamd_codec_dwrite(struct audioamd_softc *sc, int reg, uint8_t val)
+{
+ sc->sc_au.au_amd[reg] = val;
+}
+
+int
+audioamd_getdev(void *addr, struct audio_device *retp)
+{
+ *retp = audioamd_device;
+ return 0;
+}