Add an audio driver, necsb(4), for NEC PC-9801-86 sound board on
authoraoyama <aoyama@openbsd.org>
Sun, 28 Dec 2014 13:03:18 +0000 (13:03 +0000)
committeraoyama <aoyama@openbsd.org>
Sun, 28 Dec 2014 13:03:18 +0000 (13:03 +0000)
cbus(4).

This work is based on the source code of NetBSD/pc98, an unofficial
NetBSD port for NEC PC-9801 series, written about 16 years ago.
Thanks to NetBSD/pc98 porting staff for their work, especially NAGAO
Tadaaki and N. Honda who wrote the original driver.

ok miod@

15 files changed:
share/man/man4/man4.luna88k/Makefile
share/man/man4/man4.luna88k/cbus.4
share/man/man4/man4.luna88k/necsb.4 [new file with mode: 0644]
sys/arch/luna88k/cbus/cbus.c
sys/arch/luna88k/cbus/cbusvar.h
sys/arch/luna88k/cbus/nec86.c [new file with mode: 0644]
sys/arch/luna88k/cbus/nec86hw.c [new file with mode: 0644]
sys/arch/luna88k/cbus/nec86hwvar.h [new file with mode: 0644]
sys/arch/luna88k/cbus/nec86reg.h [new file with mode: 0644]
sys/arch/luna88k/cbus/nec86var.h [new file with mode: 0644]
sys/arch/luna88k/cbus/necsb.c [new file with mode: 0644]
sys/arch/luna88k/cbus/pcex.c
sys/arch/luna88k/conf/files.luna88k
sys/arch/luna88k/include/intr.h
sys/arch/luna88k/luna88k/conf.c

index 19444b0..d8daa88 100644 (file)
@@ -1,7 +1,8 @@
-#      $OpenBSD: Makefile,v 1.5 2014/12/08 13:24:04 aoyama Exp $
+#      $OpenBSD: Makefile,v 1.6 2014/12/28 13:03:18 aoyama Exp $
 
 # TODO: clock fb sio/siotty ws
-MAN=   autoconf.4 cbus.4 intro.4 lcd.4 le.4 mem.4 pcexmem.4 spc.4
+MAN=   autoconf.4 cbus.4 intro.4 lcd.4 le.4 mem.4 necsb.4 pcexmem.4 \
+       spc.4
 MANSUBDIR=luna88k
 
 MLINKS+= mem.4 kmem.4 \
index a285755..70cfe28 100644 (file)
@@ -1,4 +1,4 @@
-.\"    $OpenBSD: cbus.4,v 1.2 2014/12/08 13:59:28 jmc Exp $
+.\"    $OpenBSD: cbus.4,v 1.3 2014/12/28 13:03:18 aoyama Exp $
 .\"
 .\" Copyright (c) 2014 Kenji Aoyama.
 .\"
@@ -13,7 +13,7 @@
 .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-.Dd $Mdocdate: December 8 2014 $
+.Dd $Mdocdate: December 28 2014 $
 .Dt CBUS 4 luna88k
 .Os
 .Sh NAME
@@ -32,6 +32,8 @@ The devices currently supported on
 are:
 .Pp
 .Bl -tag -width 10n -offset 3n -compact
+.It Xr necsb 4
+NEC PC-9801-86 sound board
 .It Xr pcexmem 4
 NEC PC-9801 extension board slot
 .El
diff --git a/share/man/man4/man4.luna88k/necsb.4 b/share/man/man4/man4.luna88k/necsb.4
new file mode 100644 (file)
index 0000000..4b9dfe3
--- /dev/null
@@ -0,0 +1,41 @@
+.\"    $OpenBSD: necsb.4,v 1.1 2014/12/28 13:03:18 aoyama Exp $
+.\"
+.\" Copyright (c) 2014 Kenji Aoyama.
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.Dd $Mdocdate: December 28 2014 $
+.Dt NECSB 4 luna88k
+.Os
+.Sh NAME
+.Nm necsb
+.Nd NEC PC-9801-86 sound board
+.Sh SYNOPSIS
+.Cd "necsb0 at cbus?"
+.Cd "audio* at necsb?"
+.Sh DESCRIPTION
+The
+.Nm
+driver supports NEC PC-9801-86 sound board on
+.Xr cbus 4 .
+This board is a half-duplex device.
+.Sh SEE ALSO
+.Xr audio 4 ,
+.Xr cbus 4 ,
+.Xr intro 4
+.Sh HISTORY
+The
+.Nm
+driver first appeared in
+.Ox 5.7 .
+.Sh BUGS
+The FM synthesizer is not supported.
index 571296b..87f918d 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: cbus.c,v 1.1 2014/12/19 13:17:35 aoyama Exp $ */
+/*     $OpenBSD: cbus.c,v 1.2 2014/12/28 13:03:18 aoyama Exp $ */
 
 /*
  * Copyright (c) 2014 Kenji Aoyama.
 #define CBUS_DEBUG
 #endif
 
+#include "necsb.h"
+
+static struct cbus_attach_args cbus_devs[] = {
+#if NNECSB > 0
+       { "necsb",      -1 },   /* PC-9801-86 sound board */
+#endif
+       { "pcex",       -1 }    /* C-bus "generic" driver */
+};
+
 /*
  * C-bus interrupt status register
  */
@@ -86,7 +95,6 @@ cbus_attach(struct device *parent, struct device *self, void *args)
 {
        struct cbus_softc *sc = (struct cbus_softc *)self;
        struct mainbus_attach_args *ma = args;
-       struct cbus_attach_args caa;
        int i;
 
        for (i = 0; i < NCBUSISR; i++) {
@@ -101,8 +109,9 @@ cbus_attach(struct device *parent, struct device *self, void *args)
 
        printf("\n");
 
-       caa.intlevel = -1;      /* not specified */
-       config_found(self, &caa, cbus_print);
+       for (i = 0; i < sizeof(cbus_devs)/sizeof(cbus_devs[0]); i++)
+               config_found(self, &cbus_devs[i], cbus_print);
+
        return;
 }
 
@@ -110,12 +119,11 @@ int
 cbus_print(void *aux, const char *pnp)
 {
        struct cbus_attach_args *caa = aux;
-#if 0  /* not yet */
+
        if (pnp)
-               printf("%s at %s", caa->name, pnp);     /* not configured */
-#endif
-       if (caa->intlevel != -1)
-               printf(" INT %d", caa->intlevel);
+               printf("%s at %s", caa->ca_name, pnp);  /* not configured */
+       if (caa->ca_intlevel != -1)
+               printf(" int %d", caa->ca_intlevel);
 
        return UNCONF;
 }
index 547a237..4103ffd 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: cbusvar.h,v 1.1 2014/12/19 13:17:35 aoyama Exp $      */
+/*     $OpenBSD: cbusvar.h,v 1.2 2014/12/28 13:03:18 aoyama Exp $      */
 
 /*
  * Copyright (c) 2014 Kenji Aoyama.
@@ -42,5 +42,6 @@ int   cbus_isrunlink(int (*)(void *), int);
 void   cbus_isrdispatch(int);
 
 struct cbus_attach_args {
-       int     intlevel;
+       char    *ca_name;
+       int     ca_intlevel;
 };
diff --git a/sys/arch/luna88k/cbus/nec86.c b/sys/arch/luna88k/cbus/nec86.c
new file mode 100644 (file)
index 0000000..45c450f
--- /dev/null
@@ -0,0 +1,265 @@
+/*     $OpenBSD: nec86.c,v 1.1 2014/12/28 13:03:18 aoyama Exp $        */
+/*     $NecBSD: nec86.c,v 1.11 1999/07/23 11:04:39 honda Exp $ */
+/*     $NetBSD$        */
+
+/*
+ * [NetBSD for NEC PC-98 series]
+ *  Copyright (c) 1996, 1997, 1998
+ *     NetBSD/pc98 porting staff. 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.
+ */
+
+/*
+ * nec86.c
+ *
+ * NEC PC-9801-86 SoundBoard PCM driver for NetBSD/pc98.
+ * Written by NAGAO Tadaaki, Feb 10, 1996.
+ *
+ * Modified by N. Honda, Mar 7, 1998
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/syslog.h>
+#include <sys/device.h>
+#include <sys/proc.h>
+
+#include <machine/board.h>     /* PC_BASE */
+#include <machine/bus.h>
+#include <machine/cpu.h>
+
+#include <sys/audioio.h>
+#include <dev/audio_if.h>
+
+#include <luna88k/cbus/nec86reg.h>
+#include <luna88k/cbus/nec86hwvar.h>
+#include <luna88k/cbus/nec86var.h>
+
+#define NEC_SCR_SIDMASK                0xf0
+#define NEC_SCR_MASK           0x0f
+#define NEC_SCR_EXT_ENABLE     0x01
+
+struct audio_device nec86_device = {
+       .name           = "PC-9801-86",
+       .version        = "",
+       .config         = "nec86"
+};
+
+/*
+ * Define our interface to the higher level audio driver.
+ */
+
+struct audio_hw_if nec86_hw_if = {
+       .open           = nec86hw_open,
+       .close          = nec86hw_close,
+       .drain          = NULL,
+       .query_encoding = nec86hw_query_encoding,
+       .set_params     = nec86hw_set_params,
+       .round_blocksize        = nec86hw_round_blocksize,
+       .commit_settings        = nec86hw_commit_settings,
+       .init_output    = nec86hw_pdma_init_output,
+       .init_input     = nec86hw_pdma_init_input,
+       .start_output   = nec86hw_pdma_output,
+       .start_input    = nec86hw_pdma_input,
+       .halt_output    = nec86hw_halt_pdma,
+       .halt_input     = nec86hw_halt_pdma,
+       .speaker_ctl    = nec86hw_speaker_ctl,
+       .getdev         = nec86getdev,
+       .setfd          = nec86hw_setfd,
+       .set_port       = nec86hw_mixer_set_port,
+       .get_port       = nec86hw_mixer_get_port,
+       .query_devinfo  = nec86hw_mixer_query_devinfo,
+       .allocm         = NULL,
+       .freem          = NULL,
+       .round_buffersize       = NULL,
+       .mappage        =  NULL,
+       .get_props      = nec86_get_props,
+       .trigger_output = NULL,
+       .trigger_input  = NULL,
+       .get_default_params     = NULL
+};
+
+/*
+ * YAMAHA YM2608(OPNA) register read/write functions
+ */
+#define YM_INDEX       0
+#define YM_DATA                2
+
+void nec86_ym_read(struct nec86_softc *, u_int8_t, u_int8_t *);
+void nec86_ym_write(struct nec86_softc *, u_int8_t, u_int8_t);
+
+void
+nec86_ym_read(struct nec86_softc *sc, u_int8_t index, u_int8_t *data) {
+       bus_space_write_1(sc->sc_ym_iot,
+           sc->sc_ym_iobase + sc->sc_ym_ioh, YM_INDEX, index);
+       delay(100);
+       *data = bus_space_read_1(sc->sc_ym_iot,
+           sc->sc_ym_iobase + sc->sc_ym_ioh, YM_DATA);
+       delay(100);
+}
+
+void
+nec86_ym_write(struct nec86_softc *sc, u_int8_t index, u_int8_t data) {
+       bus_space_write_1(sc->sc_ym_iot,
+           sc->sc_ym_iobase + sc->sc_ym_ioh, YM_INDEX, index);
+       delay(100);
+       bus_space_write_1(sc->sc_ym_iot,
+           sc->sc_ym_iobase + sc->sc_ym_ioh, YM_DATA, data);
+       delay(100);
+}
+
+/*
+ * Probe for NEC PC-9801-86 SoundBoard hardware.
+ */
+int
+nec86_probesubr(bus_space_tag_t iot, bus_space_handle_t ioh,
+    bus_space_handle_t n86ioh)
+{
+       u_int8_t data;
+
+#ifdef notyet
+       if (nec86hw_probesubr(iot, ioh) != 0)
+               return -1;
+#endif /* notyet */
+
+       if (n86ioh == 0)
+               return -1;
+
+       data = bus_space_read_1(iot, n86ioh, NEC86_SOUND_ID);
+
+       switch (data & NEC_SCR_SIDMASK) {
+#if 0  /* XXX -  PC-9801-73 not yet supported. */
+       case 0x20:
+       case 0x30:
+               break;
+#endif
+       case 0x40:
+       case 0x50:
+               break;
+       default:        /* No supported board found. */
+               return -1;
+               /*NOTREACHED*/
+       }
+
+       return ((data & NEC_SCR_SIDMASK) >> 4) - 2;
+}
+
+/*
+ * Attach hardware to driver, attach hardware driver to audio
+ * pseudo-device driver.
+ */
+#define MODEL0_NAME    "PC-9801-73 soundboard"
+#define MODEL1_NAME    "PC-9801-86 soundboard"
+
+void
+nec86_attachsubr(struct nec86_softc *sc)
+{
+       struct nec86hw_softc *ysc = &sc->sc_nec86hw;
+       bus_space_tag_t iot = sc->sc_n86iot;
+       bus_space_handle_t n86ioh = sc->sc_n86ioh;
+       char *boardname[] =
+           {MODEL0_NAME, MODEL0_NAME, MODEL1_NAME, MODEL1_NAME};
+       u_int8_t data;
+       int model;
+
+       if ((model = nec86_probesubr(iot, n86ioh, n86ioh)) < 0) {
+               printf("%s: missing hardware\n", ysc->sc_dev.dv_xname);
+               return;
+       }
+       ysc->model = model;
+
+       /* enable YM2608(ONPA) */
+       data = bus_space_read_1(iot, n86ioh, NEC86_SOUND_ID);
+       data &= ~NEC_SCR_MASK;
+       data |= NEC_SCR_EXT_ENABLE;
+       bus_space_write_1(iot, n86ioh, NEC86_SOUND_ID, data);
+
+       switch (ysc->model) {
+       case 2: /* base I/O port for YM2608(OPNA) is 0x188 */
+               sc->sc_ym_ioh = OPNA_IOBASE1;
+               break;
+       case 3: /* base I/O port for YM2608(OPNA) is 0x288 */
+               sc->sc_ym_ioh = OPNA_IOBASE2;
+               break;
+       default:
+               /* can not happen; set to factory default */
+               sc->sc_ym_ioh = OPNA_IOBASE1;
+               break;
+       }
+
+       /* YM2608 I/O port set (IOA:input IOB:output) */
+       nec86_ym_read(sc, 0x07, &data);
+       data &= 0x3f;
+       data |= 0x80;
+       nec86_ym_write(sc, 0x07, data);
+
+       /* YM2608 register 0x0e has C-bus interrupt level information */
+       nec86_ym_read(sc, 0x0e, &data);
+       switch (data & 0xc0) {
+       case 0x00:
+               sc->sc_intlevel = 0;
+               break;
+       case 0x40:
+               sc->sc_intlevel = 6;
+               break;
+       case 0x80:
+               sc->sc_intlevel = 4;
+               break;
+       case 0xc0:
+               sc->sc_intlevel = 5;    /* factory default setting */
+               break;
+       default:
+               /* can not happen; set to factory default */
+               sc->sc_intlevel = 5;
+               break;
+       }
+
+       /* reset YM2608 timer A and B: XXX need this? */
+       data = 0x30;
+       nec86_ym_write(sc, 0x27, data);
+
+       printf(" int %d", sc->sc_intlevel);
+
+       nec86hw_attach(ysc);
+
+       if (sc->sc_attached == 0) {
+               printf(": %s\n", boardname[ysc->model]);
+               audio_attach_mi(&nec86_hw_if, ysc, &ysc->sc_dev);
+               sc->sc_attached = 1;
+       }
+}
+
+/*
+ * Various routines to interface to higher level audio driver.
+ */
+int
+nec86getdev(void *addr, struct audio_device *retp)
+{
+       *retp = nec86_device;
+
+       return 0;
+}
diff --git a/sys/arch/luna88k/cbus/nec86hw.c b/sys/arch/luna88k/cbus/nec86hw.c
new file mode 100644 (file)
index 0000000..ee3fd07
--- /dev/null
@@ -0,0 +1,1833 @@
+/*     $OpenBSD: nec86hw.c,v 1.1 2014/12/28 13:03:18 aoyama Exp $      */
+/*     $NecBSD: nec86hw.c,v 1.13 1998/03/14 07:04:54 kmatsuda Exp $    */
+/*     $NetBSD$        */
+
+/*
+ * [NetBSD for NEC PC-98 series]
+ *  Copyright (c) 1996, 1997, 1998
+ *     NetBSD/pc98 porting staff. 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.
+ */
+
+/*
+ * nec86hw.c
+ *
+ * NEC PC-9801-86 SoundBoard PCM driver for NetBSD/pc98.
+ * Written by NAGAO Tadaaki, Feb 10, 1996.
+ *
+ * Modified by N. Honda, Mar 7, 1998
+ */
+/*
+ * TODO:
+ * - Add PC-9801-73 support.
+ * - Fake the mixer device with electric volumes.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/syslog.h>
+#include <sys/device.h>
+#include <sys/proc.h>
+
+#include <machine/bus.h>
+#include <machine/cpu.h>
+
+#include <sys/audioio.h>
+#include <dev/audio_if.h>
+#include <dev/mulaw.h>
+#include <dev/auconv.h>
+
+#if 0
+#include <dev/ic/ym2203reg.h>
+#endif
+#include <luna88k/cbus/nec86reg.h>
+#include <luna88k/cbus/nec86hwvar.h>
+#include <luna88k/cbus/nec86var.h>
+
+#ifdef AUDIO_DEBUG
+extern void Dprintf(const char *, ...);
+#define DPRINTF(x)     if (nec86hwdebug) printf x
+#define DPRINTF2(l, x) if (nec86hwdebug >= l) printf x
+int    nec86hwdebug = 3;
+#else  /* !AUDIO_DEBUG */
+#define DPRINTF(x)
+#define DPRINTF2(l, x)
+#endif /* AUDIO_DEBUG */
+
+#ifndef VOLUME_DELAY
+/*
+ * XXX -  Delaytime in microsecond after an access
+ *       to the volume I/O port.
+ */
+#define VOLUME_DELAY   10
+#endif /* !VOLUME_DELAY */
+
+/*
+ * Sampling rates supported by the hardware.
+ */
+static int nec86hw_rate_table[NEC86HW_NRATE_TYPE][NEC86_NRATE] = {
+       /* NEC PC-9801-86 or its full compatibles */
+       { 44100, 33075, 22050, 16538, 11025, 8269, 5513, 4134 },
+       /* Some earlier versions of Q-Vision's WaveMaster */
+       { 44100, 33075, 22050, 16000, 11025, 8000, 5513, 4000 },
+};
+
+int nec86hw_set_output_block(struct nec86hw_softc *, int);
+int nec86hw_set_input_block(struct nec86hw_softc *, int);
+
+/*
+ * Function tables.
+ */
+static struct nec86hw_functable_entry nec86hw_functable[] = {
+       /* precision, channels,
+          output function, input function (without resampling),
+          output function, input function (with resampling) */
+       { 8, 1, 
+           nec86fifo_output_mono_8_direct, nec86fifo_input_mono_8_direct,
+           nec86fifo_output_mono_8_resamp, nec86fifo_input_mono_8_resamp },
+       { 16, 1,
+           nec86fifo_output_mono_16_direct, nec86fifo_input_mono_16_direct,
+           nec86fifo_output_mono_16_resamp, nec86fifo_input_mono_16_resamp },
+       { 8, 2,
+           nec86fifo_output_stereo_8_direct, nec86fifo_input_stereo_8_direct,
+           nec86fifo_output_stereo_8_resamp, nec86fifo_input_stereo_8_resamp },
+       { 16, 2,
+           nec86fifo_output_stereo_16_direct, nec86fifo_input_stereo_16_direct,
+           nec86fifo_output_stereo_16_resamp, nec86fifo_input_stereo_16_resamp },
+};
+#define NFUNCTABLEENTRY        (sizeof(nec86hw_functable) / sizeof(nec86hw_functable[0]))
+
+/*
+ * Attach hardware to driver, attach hardware driver to audio
+ * pseudo-device driver.
+ */
+void
+nec86hw_attach(struct nec86hw_softc *sc)
+{
+       bus_space_tag_t iot = sc->sc_iot;
+       bus_space_handle_t ioh = sc->sc_ioh;
+       u_int8_t data;
+
+       /* Set default encoding. */
+       sc->func_fifo_output = nec86fifo_output_mono_8_resamp;
+       sc->func_fifo_input = nec86fifo_input_mono_8_resamp;
+       (void) nec86hw_set_params(sc, AUMODE_RECORD, 0,
+           &audio_default, &audio_default);
+       (void) nec86hw_set_params(sc, AUMODE_PLAY,   0,
+           &audio_default, &audio_default);
+
+       /* Set default ports. */
+       (void) nec86hw_set_in_port(sc, NEC86HW_INPUT_MIXER);
+       (void) nec86hw_set_out_port(sc, NEC86HW_OUTPUT_MIXER);
+
+       /* Set default gains. */
+       nec86hw_set_volume(sc, NEC86_VOLUME_PORT_OPNAD, NEC86_MAXVOL);
+       nec86hw_set_volume(sc, NEC86_VOLUME_PORT_OPNAI, NEC86_MAXVOL);
+       nec86hw_set_volume(sc, NEC86_VOLUME_PORT_LINED, NEC86_MAXVOL);
+       nec86hw_set_volume(sc, NEC86_VOLUME_PORT_LINEI, NEC86_MAXVOL);
+       nec86hw_set_volume(sc, NEC86_VOLUME_PORT_PCMD, NEC86_MAXVOL);
+
+       /* Internal Speaker ON */
+       nec86hw_speaker_ctl(sc, SPKR_ON);
+
+       /* Set miscellanous stuffs. */
+       data = bus_space_read_1(iot, ioh, NEC86_CTRL);
+       data &= NEC86_CTRL_MASK_PAN | NEC86_CTRL_MASK_PORT;
+       data |= NEC86_CTRL_PAN_L | NEC86_CTRL_PAN_R;
+       data |= NEC86_CTRL_PORT_STD;
+       bus_space_write_1(iot, ioh, NEC86_CTRL, data);
+}
+
+/*
+ * Various routines to interface to higher level audio driver.
+ */
+
+int
+nec86hw_open(void *arg, int flags)
+{
+       struct nec86hw_softc *sc = arg;
+       DPRINTF(("nec86hw_open: sc=%p\n", sc));
+
+       if (sc->sc_open != 0 || nec86hw_reset(sc) != 0)
+               return ENXIO;
+
+       sc->sc_open = 1;
+       sc->sc_intr = NULL;
+       sc->sc_arg = NULL;
+
+       sc->conv_acc = 0;
+       sc->conv_last0 = 0;
+       sc->conv_last0_l = 0;
+       sc->conv_last0_r = 0;
+       sc->conv_last1 = 0;
+       sc->conv_last1_l = 0;
+       sc->conv_last1_r = 0;
+
+       /*
+        * Leave most things including sampling format and rate as they were.
+        * (See audio_open() in audio.c)
+        */
+
+       DPRINTF(("nec86hw_open: opened\n"));
+
+       return 0;
+}
+
+void
+nec86hw_close(void *addr)
+{
+       register struct nec86hw_softc *sc = (struct nec86hw_softc *) addr;
+
+       DPRINTF(("nec86hw_close: sc=%p\n", sc));
+
+       sc->sc_open = 0;
+       sc->sc_intr = NULL;
+       (void) nec86hw_reset(sc);
+
+       DPRINTF(("nec86hw_close: closed\n"));
+}
+
+int
+nec86hw_set_params(void *addr, int mode, int usemode, struct audio_params *p,
+    struct audio_params *r)
+{
+       register struct nec86hw_softc *sc = (struct nec86hw_softc *) addr;
+       int rate_type = NEC86HW_RATE_TYPE(sc->sc_cfgflags);
+       u_int prec, enc;
+
+       if ((p->channels != 1) && (p->channels != 2))
+               return EINVAL;
+
+       enc = p->encoding;
+       prec = p->precision;
+       switch (enc) {
+       case AUDIO_ENCODING_SLINEAR_BE:
+       case AUDIO_ENCODING_SLINEAR_LE:
+               if (prec == 8)
+                       enc = AUDIO_ENCODING_SLINEAR;
+               break;
+       case AUDIO_ENCODING_ULINEAR_BE:
+       case AUDIO_ENCODING_ULINEAR_LE:
+               if (prec == 8)
+                       enc = AUDIO_ENCODING_ULINEAR;
+               break;
+       case AUDIO_ENCODING_SLINEAR:
+               if (prec == 16)
+                       enc = AUDIO_ENCODING_SLINEAR_LE;
+               break;
+       case AUDIO_ENCODING_ULINEAR:
+               if (prec == 16)
+                       enc = AUDIO_ENCODING_ULINEAR_LE;
+               break;
+       case AUDIO_ENCODING_ULAW:
+       case AUDIO_ENCODING_ALAW:
+               break;
+       default:
+               return EINVAL;
+       }
+
+       p->sw_code = NULL;
+       r->sw_code = NULL;
+       switch (enc) {
+       case AUDIO_ENCODING_ULAW:
+               p->sw_code = mulaw_to_ulinear8;
+               r->sw_code = ulinear8_to_mulaw;
+               enc = AUDIO_ENCODING_ULINEAR;
+               break;
+       case AUDIO_ENCODING_ALAW:
+               p->sw_code = alaw_to_ulinear8;
+               r->sw_code = ulinear8_to_alaw;
+               enc = AUDIO_ENCODING_ULINEAR;
+               break;
+       case AUDIO_ENCODING_ULINEAR_LE:
+               p->sw_code = r->sw_code = change_sign16;
+               enc = AUDIO_ENCODING_SLINEAR_LE;
+               break;
+       case AUDIO_ENCODING_ULINEAR_BE:
+               p->sw_code = r->sw_code = swap_bytes_change_sign16;
+               enc = AUDIO_ENCODING_SLINEAR_LE;
+               break;
+       case AUDIO_ENCODING_SLINEAR_BE:
+               p->sw_code = r->sw_code = swap_bytes;
+               enc = AUDIO_ENCODING_SLINEAR_LE;
+               break;
+       case AUDIO_ENCODING_SLINEAR:
+               p->sw_code = r->sw_code = change_sign8;
+               enc = AUDIO_ENCODING_ULINEAR;
+               break;
+       default:
+               break;
+       }
+
+       sc->channels = p->channels;
+       sc->precision = prec;
+       sc->encoding = enc;
+       sc->sc_orate = p->sample_rate;
+       sc->hw_orate_bits = nec86hw_rate_bits(sc, p->sample_rate);
+       sc->hw_orate = nec86hw_rate_table[rate_type][sc->hw_orate_bits];
+       sc->sc_irate = r->sample_rate;
+       sc->hw_irate_bits = nec86hw_rate_bits(sc, r->sample_rate);
+       sc->hw_irate = nec86hw_rate_table[rate_type][sc->hw_irate_bits];
+       return 0;
+}
+
+int
+nec86hw_query_encoding(void *addr, struct audio_encoding *fp)
+{
+
+       switch (fp->index) {
+       case 0:
+               strlcpy(fp->name, AudioEmulaw, sizeof(fp->name));
+               fp->encoding = AUDIO_ENCODING_ULAW;
+               fp->precision = 8;
+               fp->bps = 1;
+               fp->msb = 1;
+               fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
+               break;
+       case 1:
+               strlcpy(fp->name, AudioEalaw, sizeof(fp->name));
+               fp->encoding = AUDIO_ENCODING_ALAW;
+               fp->precision = 8;
+               fp->bps = 1;
+               fp->msb = 1;
+               fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
+               break;
+       case 2:
+               strlcpy(fp->name, AudioEulinear, sizeof(fp->name));
+               fp->encoding = AUDIO_ENCODING_ULINEAR;
+               fp->precision = 8;
+               fp->bps = 1;
+               fp->msb = 1;
+               fp->flags = 0;
+               break;
+       case 3:
+               strlcpy(fp->name, AudioEslinear, sizeof(fp->name));
+               fp->encoding = AUDIO_ENCODING_SLINEAR;
+               fp->precision = 8;
+               fp->bps = 1;
+               fp->msb = 1;
+               fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
+               break;
+       case 4:
+               strlcpy(fp->name, AudioEulinear_le, sizeof(fp->name));
+               fp->encoding = AUDIO_ENCODING_ULINEAR_LE;
+               fp->precision = 16;
+               fp->bps = 2;
+               fp->msb = 1;    /* is this OK? */
+               fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
+               break;
+       case 5:
+               strlcpy(fp->name, AudioEulinear_be, sizeof(fp->name));
+               fp->encoding = AUDIO_ENCODING_ULINEAR_BE;
+               fp->precision = 16;
+               fp->bps = 2;
+               fp->msb = 1;    /* is this OK? */
+               fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
+               break;
+       case 6:
+               strlcpy(fp->name, AudioEslinear_le, sizeof(fp->name));
+               fp->encoding = AUDIO_ENCODING_SLINEAR_LE;
+               fp->precision = 16;
+               fp->bps = 2;
+               fp->msb = 1;    /* is this OK? */
+               fp->flags = 0;
+               break;
+       case 7:
+               strlcpy(fp->name, AudioEslinear_be, sizeof(fp->name));
+               fp->encoding = AUDIO_ENCODING_SLINEAR_BE;
+               fp->precision = 16;
+               fp->bps = 2;
+               fp->msb = 1;    /* is this OK? */
+               fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
+               break;
+       default:
+               return EINVAL;
+               /*NOTREACHED*/
+       }
+
+       return 0;
+}
+
+int
+nec86hw_round_blocksize(void *addr, int blk)
+{
+       u_int base = NEC86_INTRBLK_UNIT;
+
+       if (blk < NEC86_INTRBLK_UNIT * 2)
+               return NEC86_INTRBLK_UNIT * 2;
+
+       for ( ; base <= blk; base *= 2)
+               ;
+       return base / 2;
+}
+
+int
+nec86hw_set_out_port(void *addr, int port)
+{
+       register struct nec86hw_softc *sc = (struct nec86hw_softc *) addr;
+
+       DPRINTF(("nec86hw_set_out_port:\n"));
+
+       if (port != NEC86HW_OUTPUT_MIXER)
+               return EINVAL;
+
+       sc->out_port = port;
+
+       return 0;
+}
+
+int
+nec86hw_get_out_port(void *addr)
+{
+       register struct nec86hw_softc *sc = (struct nec86hw_softc *) addr;
+
+       DPRINTF(("nec86hw_get_out_port:\n"));
+
+       return sc->out_port;
+}
+
+int
+nec86hw_set_in_port(void *addr, int port)
+{
+       register struct nec86hw_softc *sc = (struct nec86hw_softc *) addr;
+
+       DPRINTF(("nec86hw_set_in_port:\n"));
+
+       if (port != NEC86HW_INPUT_MIXER)
+               return EINVAL;
+
+       sc->in_port = port;
+
+       return 0;
+}
+
+int
+nec86hw_get_in_port(void *addr)
+{
+       register struct nec86hw_softc *sc = (struct nec86hw_softc *) addr;
+
+       DPRINTF(("nec86hw_get_in_port:\n"));
+
+       return sc->in_port;
+}
+
+int
+nec86hw_commit_settings(void *addr)
+{
+       register struct nec86hw_softc *sc = (struct nec86hw_softc *) addr;
+       int i;
+
+       /*
+        * Determine which function should be used to write to/read from
+        * the FIFO ring buffer.
+        */
+
+       for (i = 0; i < NFUNCTABLEENTRY; i++) {
+               if ((nec86hw_functable[i].precision == sc->precision)
+                   && (nec86hw_functable[i].channels == sc->channels))
+                       break;
+       }
+
+       if (i >= NFUNCTABLEENTRY) {
+               /* ??? -  This should never happen. */
+               return EINVAL;
+       }
+
+       if (sc->sc_orate == sc->hw_orate) {
+               /* No need to resample. */
+               sc->func_fifo_output =
+                   nec86hw_functable[i].func_fifo_output_direct;
+       } else {
+               /* Resampling needed. */
+               sc->func_fifo_output =
+                   nec86hw_functable[i].func_fifo_output_resamp;
+       }
+
+       if (sc->sc_irate == sc->hw_irate) {
+               /* No need to resample. */
+               sc->func_fifo_input =
+                   nec86hw_functable[i].func_fifo_input_direct;
+       } else {
+               /* Resampling needed. */
+               sc->func_fifo_input =
+                   nec86hw_functable[i].func_fifo_input_resamp;
+       }
+
+       return 0;
+}
+
+int
+nec86hw_setfd(void *addr, int flag)
+{
+       DPRINTF(("nec86hw_setfd:\n"));
+
+       /* Can't do full-duplex */
+       return ENOTTY;
+}
+
+int
+nec86hw_mixer_set_port(void *addr, mixer_ctrl_t *cp)
+{
+       DPRINTF(("nec86hw_mixer_set_port:\n"));
+
+       /* not yet implemented */
+       return ENXIO;
+}
+
+int
+nec86hw_mixer_get_port(void *addr, mixer_ctrl_t *cp)
+{
+       DPRINTF(("nec86hw_mixer_get_port:\n"));
+
+       /* not yet implemented */
+       return ENXIO;
+}
+
+int
+nec86hw_mixer_query_devinfo(void *addr, mixer_devinfo_t *dip)
+{
+       DPRINTF(("nec86hw_mixer_query_devinfo:\n"));
+
+       /* not yet implemented */
+       return ENXIO;
+}
+
+int
+nec86hw_set_output_block(struct nec86hw_softc *sc, int cc)
+{
+       int bpf, hw_blocksize, watermark;
+
+       bpf = (sc->channels * sc->precision) / NBBY;    /* bytes per frame */
+       sc->pdma_count = cc / bpf;
+
+       /* Size of the block after the rate-conversion. */
+       hw_blocksize =
+           (sc->pdma_count * sc->hw_orate) / sc->sc_orate
+           * (sc->precision / NBBY * 2);
+
+       /* How many chunks the block should be divided into. */
+       sc->pdma_nchunk =
+           ((hw_blocksize * (WATERMARK_MAX_RATIO + WATERMARK_RATIO_OUT))
+           + (NEC86_BUFFSIZE * WATERMARK_MAX_RATIO - 1))
+           / (NEC86_BUFFSIZE * WATERMARK_MAX_RATIO);
+
+       /* Calculate the watermark. */
+       watermark =
+           (hw_blocksize * WATERMARK_RATIO_OUT)
+           / (sc->pdma_nchunk * WATERMARK_MAX_RATIO);
+       sc->pdma_watermark = nec86hw_round_watermark(watermark);
+
+       /*
+        * If the formula (*1) does not hold, watermark may be less
+        * than the minimum watermark (NEC86_INTRBLK_UNIT) and then
+        * nec86hw_round_watermark() returns that minimum value.
+        * In this case, the ring buffer on the hardware will potentially
+        * overflow.
+        * To avoid such a case, here calculate pdma_nchunk again.
+        *
+        * (*1)  NEC86_BUFFSIZE / NEC86_INTRBLK_UNIT
+        *           >= WATERMARK_MAX_RATIO / WATERMARK_RATIO_OUT + 1
+        */
+       if (hw_blocksize + sc->pdma_watermark > NEC86_BUFFSIZE) {
+               sc->pdma_nchunk =
+                   (hw_blocksize + (NEC86_BUFFSIZE - sc->pdma_watermark - 1))
+                   / (NEC86_BUFFSIZE - sc->pdma_watermark);
+       }
+
+       DPRINTF2(2, ("nec86hw_pdma_output: cc=%d count=%d hw_blocksize=%d "
+           "watermark=%d nchunk=%d ptr=%p\n",
+           cc, sc->pdma_count, hw_blocksize, sc->pdma_watermark,
+           sc->pdma_nchunk, sc->pdma_ptr));
+       return 0;
+}
+
+int
+nec86hw_set_input_block(struct nec86hw_softc *sc, int cc)
+{
+       int bpf, hw_blocksize, watermark, maxwatermark;
+
+       bpf = (sc->channels * sc->precision) / NBBY;    /* bytes per frame */
+       sc->pdma_count = cc / bpf;
+
+       /* Maximum watermark. */
+       watermark =
+           (NEC86_BUFFSIZE * WATERMARK_RATIO_IN) / WATERMARK_MAX_RATIO;
+       maxwatermark = nec86hw_round_watermark(watermark);
+
+       /* Size of the block before the rate-conversion. */
+       hw_blocksize =
+           (sc->pdma_count * sc->hw_irate + (sc->sc_irate - 1))
+           / sc->sc_irate * (sc->precision / NBBY * 2);
+
+       /* How many chunks the block should be divided into. */
+       sc->pdma_nchunk = (hw_blocksize + (maxwatermark - 1)) / maxwatermark;
+
+       /* Calculate the watermark. */
+       watermark = (hw_blocksize / sc->pdma_nchunk) + (NEC86_INTRBLK_UNIT - 1);
+       sc->pdma_watermark = nec86hw_round_watermark(watermark);
+
+       DPRINTF2(2, ("nec86hw_pdma_input: cc=%d count=%d hw_blocksize=%d "
+           "watermark=%d nchunk=%d ptr=%p\n",
+           cc, sc->pdma_count, hw_blocksize, sc->pdma_watermark,
+           sc->pdma_nchunk, sc->pdma_ptr));
+       return 0;
+}
+
+int
+nec86hw_pdma_init_output(void *addr, void *buf, int cc)
+{
+       struct nec86hw_softc *sc = (struct nec86hw_softc *) addr;
+
+       nec86hw_halt_pdma(addr);
+       nec86hw_reset_fifo(sc);
+       nec86hw_set_mode_playing(sc);
+       nec86hw_set_rate_real(sc, sc->hw_orate_bits);
+       nec86hw_set_precision_real(sc, sc->precision);
+
+       nec86hw_set_output_block(sc, cc);
+       nec86hw_reset_fifo(sc);
+
+       nec86hw_enable_fifointr(sc);
+       nec86hw_set_watermark(sc, sc->pdma_watermark);
+       nec86hw_disable_fifointr(sc);
+
+       return 0;
+}
+
+int
+nec86hw_pdma_init_input(void *addr, void *buf, int cc)
+{
+       struct nec86hw_softc *sc = (struct nec86hw_softc *) addr;
+
+       nec86hw_halt_pdma(addr);
+       nec86hw_reset_fifo(sc);
+       nec86hw_set_mode_recording(sc);
+       nec86hw_set_rate_real(sc, sc->hw_irate_bits);
+       nec86hw_set_precision_real(sc, sc->precision);
+
+       nec86hw_set_input_block(sc, cc);
+       nec86hw_reset_fifo(sc);
+
+       nec86hw_enable_fifointr(sc);
+       nec86hw_set_watermark(sc, sc->pdma_watermark);
+       nec86hw_disable_fifointr(sc);
+
+       return 0;
+}
+
+int
+nec86hw_pdma_output(void *addr, void *p, int cc, void (*intr)(void *),
+    void *arg)
+{
+       struct nec86hw_softc *sc = (struct nec86hw_softc *) addr;
+       int bpf;
+
+       bpf = (sc->channels * sc->precision) / NBBY;    /* bytes per frame */
+       if ((cc % bpf) != 0) {
+               DPRINTF(("nec86hw_pdma_output: odd bytes\n"));
+               return EIO;
+       }
+
+       sc->sc_intr = intr;
+       sc->sc_arg = arg;
+
+       /*
+        * We divide the data block into some relatively small chunks if the
+        * block is so large that the ring buffer on the hardware would
+        * overflow.
+        * In this case, we send the first chunk now and the rest in the
+        * interrupt handler.
+        */
+
+       /* Set up for pseudo-DMA. */
+       sc->pdma_ptr = (u_char *) p;
+       sc->pdma_padded = 0;
+       sc->pdma_mode = PDMA_MODE_OUTPUT;
+       if (sc->pdma_count != cc / bpf)
+               nec86hw_set_output_block(sc, cc);
+
+       if (!sc->intr_busy) {
+               nec86hw_set_precision_real(sc, sc->precision);
+               nec86hw_set_rate_real(sc, sc->hw_orate_bits);
+               nec86hw_set_mode_playing(sc);
+               nec86hw_reset_fifo(sc);
+       }
+
+       /*
+        * Send the first chunk.  The rest will be sent in the interrupt
+        * handler nec86hw_intr(), if any.
+        */
+       nec86hw_disable_fifointr(sc);
+       nec86hw_output_chunk(sc);
+       nec86hw_enable_fifointr(sc);
+
+       if (!sc->intr_busy) {
+               /* Now start playing. */
+               nec86hw_start_fifo(sc);
+
+               sc->intr_busy = 1;
+       }
+
+       nec86hw_set_watermark(sc, sc->pdma_watermark);
+       return 0;
+}
+
+int
+nec86hw_pdma_input(void *addr, void *p, int cc, void (*intr)(void *),
+    void *arg)
+{
+       struct nec86hw_softc *sc = (struct nec86hw_softc *) addr;
+       int bpf;
+
+       bpf = (sc->channels * sc->precision) / NBBY;    /* bytes per frame */
+       if ((cc % bpf) != 0) {
+               DPRINTF(("nec86hw_pdma_input: odd bytes\n"));
+               return EIO;
+       }
+
+       sc->sc_intr = intr;
+       sc->sc_arg = arg;
+
+       /* Set up for pseudo-DMA. */
+       sc->pdma_ptr = (u_int8_t *) p;
+       sc->pdma_padded = 0;    /* Never padded in recording, though. */
+       sc->pdma_mode = PDMA_MODE_INPUT;
+
+       if (sc->pdma_count != cc / bpf)
+               nec86hw_set_input_block(sc, cc);
+
+       if (!sc->intr_busy) {
+               nec86hw_set_precision_real(sc, sc->precision);
+               nec86hw_set_rate_real(sc, sc->hw_irate_bits);
+               nec86hw_set_mode_recording(sc);
+               nec86hw_reset_fifo(sc);
+
+               /* Now start recording. */
+               nec86hw_enable_fifointr(sc);
+               nec86hw_start_fifo(sc);
+
+               sc->intr_busy = 1;
+       }
+
+       nec86hw_set_watermark(sc, sc->pdma_watermark);
+
+       return 0;
+}
+
+int
+nec86hw_halt_pdma(void *addr)
+{
+       register struct nec86hw_softc *sc = (struct nec86hw_softc *) addr;
+
+       DPRINTF(("nec86hw_halt_pdma: sc=%p\n", sc));
+
+       nec86hw_stop_fifo(sc);
+       nec86hw_disable_fifointr(sc);
+
+       sc->intr_busy = 0;
+
+       return 0;
+}
+
+int
+nec86hw_cont_pdma(void *addr)
+{
+       register struct nec86hw_softc *sc = (struct nec86hw_softc *) addr;
+
+       DPRINTF(("nec86hw_cont_pdma: sc=%p\n", sc));
+
+       nec86hw_enable_fifointr(sc);
+       nec86hw_start_fifo(sc);
+
+       sc->intr_busy = 1;
+
+       return 0;
+}
+
+int
+nec86hw_speaker_ctl(void *addr, int onoff)
+{
+       register struct nec86hw_softc *sc = (struct nec86hw_softc *) addr;
+       bus_space_tag_t iot = sc->sc_iot;
+       bus_space_handle_t ioh = sc->sc_ioh;
+
+       DPRINTF(("nec86hw_speaker_ctl:\n"));
+
+       switch (onoff) {
+       case SPKR_ON:
+               bus_space_write_1(iot, ioh, NEC86_VOLUME, 0x0d1);
+               delay(VOLUME_DELAY);
+       break;
+       case SPKR_OFF:
+               bus_space_write_1(iot, ioh, NEC86_VOLUME, 0x0d0);
+               delay(VOLUME_DELAY);
+       break;
+       default:
+               return EINVAL;
+       }
+
+       return 0;
+}
+
+u_int8_t
+nec86hw_rate_bits(struct nec86hw_softc *sc, u_long sr)
+{
+       int i;
+       u_long rval, hr, min;
+       int rate_type = NEC86HW_RATE_TYPE(sc->sc_cfgflags);
+
+       /* Look for the minimum hardware rate equal to or more than sr. */
+       min = 0;
+       rval = 0;
+       for (i = 0; i < NEC86_NRATE; i++) {
+               hr = nec86hw_rate_table[rate_type][i];
+               if ((hr >= sr) && ((min == 0) || (min > hr))) {
+                       min = hr;
+                       rval = (u_int8_t) i;
+               }
+       }
+
+       return rval;
+}
+
+int
+nec86hw_round_watermark(int wm)
+{
+       wm = (wm / NEC86_INTRBLK_UNIT) * NEC86_INTRBLK_UNIT;
+
+       if (wm < NEC86_INTRBLK_UNIT)
+               wm = NEC86_INTRBLK_UNIT;
+
+       return wm;
+}
+
+/*
+ * Lower-level routines.
+ */
+
+int
+nec86hw_reset(struct nec86hw_softc *sc)
+{
+       nec86hw_stop_fifo(sc);
+       nec86hw_disable_fifointr(sc);
+       nec86hw_clear_intrflg(sc);
+       nec86hw_reset_fifo(sc);
+
+       sc->intr_busy = 0;
+       sc->pdma_mode = PDMA_MODE_NONE;
+
+       if (nec86hw_seeif_intrflg(sc))
+               return -1;      /* The hardware vanished? */
+
+       return 0;
+}
+
+/*
+ * Set the mode for playing/recording.
+ */
+void
+nec86hw_set_mode_playing(struct nec86hw_softc *sc)
+{
+       bus_space_tag_t iot = sc->sc_iot;
+       bus_space_handle_t ioh = sc->sc_ioh;
+       u_int8_t data;
+
+       data = bus_space_read_1(iot, ioh, NEC86_FIFOCTL);
+       data &= ~NEC86_FIFOCTL_RECMODE;
+       bus_space_write_1(iot, ioh, NEC86_FIFOCTL, data);
+}
+
+void
+nec86hw_set_mode_recording(struct nec86hw_softc *sc)
+{
+       bus_space_tag_t iot = sc->sc_iot;
+       bus_space_handle_t ioh = sc->sc_ioh;
+       u_int8_t data;
+
+       data = bus_space_read_1(iot, ioh, NEC86_FIFOCTL);
+       data |= NEC86_FIFOCTL_RECMODE;
+       bus_space_write_1(iot, ioh, NEC86_FIFOCTL, data);
+}
+
+/*
+ * Set the electric volumes.
+ */
+void
+nec86hw_set_volume(struct nec86hw_softc *sc, int port, u_int8_t vol)
+{
+       bus_space_tag_t iot = sc->sc_iot;
+       bus_space_handle_t ioh = sc->sc_ioh;
+
+       bus_space_write_1(iot, ioh, NEC86_VOLUME,
+           NEC86_VOL_TO_BITS(port, vol));
+       delay(VOLUME_DELAY);
+}
+
+/*
+ * Control the FIFO ring buffer on the board.
+ */
+void
+nec86hw_start_fifo(struct nec86hw_softc *sc)
+{
+       bus_space_tag_t iot = sc->sc_iot;
+       bus_space_handle_t ioh = sc->sc_ioh;
+       u_int8_t data;
+
+       /* Start playing/recording. */
+       data = bus_space_read_1(iot, ioh, NEC86_FIFOCTL);
+       data |= NEC86_FIFOCTL_RUN;
+       bus_space_write_1(iot, ioh, NEC86_FIFOCTL, data);
+}
+
+void
+nec86hw_stop_fifo(struct nec86hw_softc *sc)
+{
+       bus_space_tag_t iot = sc->sc_iot;
+       bus_space_handle_t ioh = sc->sc_ioh;
+       u_int8_t data;
+
+       /* Stop playing/recording. */
+       data = bus_space_read_1(iot, ioh, NEC86_FIFOCTL);
+       data &= ~NEC86_FIFOCTL_RUN;
+       bus_space_write_1(iot, ioh, NEC86_FIFOCTL, data);
+}
+
+void
+nec86hw_enable_fifointr(struct nec86hw_softc *sc)
+{
+       bus_space_tag_t iot = sc->sc_iot;
+       bus_space_handle_t ioh = sc->sc_ioh;
+       u_int8_t data;
+
+       data = bus_space_read_1(iot, ioh, NEC86_FIFOCTL);
+       data |= NEC86_FIFOCTL_ENBLINTR;
+       bus_space_write_1(iot, ioh, NEC86_FIFOCTL, data);
+}
+
+void
+nec86hw_disable_fifointr(struct nec86hw_softc *sc)
+{
+       bus_space_tag_t iot = sc->sc_iot;
+       bus_space_handle_t ioh = sc->sc_ioh;
+       u_int8_t data;
+
+       data = bus_space_read_1(iot, ioh, NEC86_FIFOCTL);
+       data &= ~NEC86_FIFOCTL_ENBLINTR;
+       bus_space_write_1(iot, ioh, NEC86_FIFOCTL, data);
+}
+
+int
+nec86hw_seeif_intrflg(struct nec86hw_softc *sc)
+{
+       bus_space_tag_t iot = sc->sc_iot;
+       bus_space_handle_t ioh = sc->sc_ioh;
+       u_int8_t data;
+
+       data = bus_space_read_1(iot, ioh, NEC86_FIFOCTL);
+
+       return (data & NEC86_FIFOCTL_INTRFLG);
+}
+
+void
+nec86hw_clear_intrflg(struct nec86hw_softc *sc)
+{
+       bus_space_tag_t iot = sc->sc_iot;
+       bus_space_handle_t ioh = sc->sc_ioh;
+       u_int8_t data;
+
+       data = bus_space_read_1(iot, ioh, NEC86_FIFOCTL);
+       data &= ~NEC86_FIFOCTL_INTRFLG;
+       bus_space_write_1(iot, ioh, NEC86_FIFOCTL, data);
+       data |= NEC86_FIFOCTL_INTRFLG;
+       bus_space_write_1(iot, ioh, NEC86_FIFOCTL, data);
+}
+
+void
+nec86hw_reset_fifo(struct nec86hw_softc *sc)
+{
+       bus_space_tag_t iot = sc->sc_iot;
+       bus_space_handle_t ioh = sc->sc_ioh;
+       u_int8_t data;
+
+       data = bus_space_read_1(iot, ioh, NEC86_FIFOCTL);
+       data |= NEC86_FIFOCTL_INIT;
+       bus_space_write_1(iot, ioh, NEC86_FIFOCTL, data);
+       data &= ~NEC86_FIFOCTL_INIT;
+       bus_space_write_1(iot, ioh, NEC86_FIFOCTL, data);
+}
+
+void
+nec86hw_set_watermark(struct nec86hw_softc *sc, int wm)
+{
+       bus_space_tag_t iot = sc->sc_iot;
+       bus_space_handle_t ioh = sc->sc_ioh;
+       /*
+        * Must be called after nec86hw_start_fifo() and
+        * nec86hw_enable_fifointr() are both called.
+        */
+
+#ifdef DIAGNOSTIC
+       if ((wm < NEC86_INTRBLK_UNIT) || (wm > NEC86_BUFFSIZE)
+           || ((wm % NEC86_INTRBLK_UNIT) != 0))
+               printf("nec86hw_set_watermark: invalid watermark %d\n", wm);
+#endif /* DIAGNOSTIC */
+
+       /*
+        * The interrupt occurs when the number of bytes in the FIFO ring
+        * buffer exceeds this watarmark.
+        */
+       bus_space_write_1(iot, ioh, NEC86_FIFOINTRBLK,
+           (wm / NEC86_INTRBLK_UNIT) - 1);
+}
+
+void
+nec86hw_set_precision_real(struct nec86hw_softc *sc, u_int prec)
+{
+       bus_space_tag_t iot = sc->sc_iot;
+       bus_space_handle_t ioh = sc->sc_ioh;
+       u_int8_t data;
+
+       data = bus_space_read_1(iot, ioh, NEC86_CTRL);
+       data &= ~NEC86_CTRL_8BITS;
+       if (prec == 8)
+               data |= NEC86_CTRL_8BITS;
+       bus_space_write_1(iot, ioh, NEC86_CTRL, data);
+}
+
+void
+nec86hw_set_rate_real(struct nec86hw_softc *sc, u_int8_t bits)
+{
+       bus_space_tag_t iot = sc->sc_iot;
+       bus_space_handle_t ioh = sc->sc_ioh;
+       u_int8_t data;
+
+       data = bus_space_read_1(iot, ioh, NEC86_FIFOCTL);
+       data &= ~NEC86_FIFOCTL_MASK_RATE;
+       data |= bits & NEC86_FIFOCTL_MASK_RATE;
+       bus_space_write_1(iot, ioh, NEC86_FIFOCTL, data);
+}
+
+/*
+ * Write data to the FIFO ring buffer on the board.
+ */
+void
+nec86hw_output_chunk(struct nec86hw_softc *sc)
+{
+       int cc, nbyte;
+
+       if (sc->pdma_nchunk > 0) {
+               /* Update chunksize and then send the chunk to the board. */
+
+               /* chunksize in frames */
+               cc = sc->pdma_count / sc->pdma_nchunk;
+
+               nbyte = (*sc->func_fifo_output)(sc, cc);
+
+               DPRINTF2(3, ("nec86hw_output_chunk: sc->pdma_count=%d "
+                   "sc->pdma_nchunk=%d cc=%d nbyte=%d ptr=%p\n",
+                   sc->pdma_count, sc->pdma_nchunk, cc, nbyte, sc->pdma_ptr));
+
+               sc->pdma_nchunk--;
+               sc->pdma_count -= cc;
+               sc->pdma_ptr += nbyte; 
+       } else {
+               /* ??? -  This should never happen. */
+               nbyte = 0;
+               DPRINTF(("nec86hw_output_chunk: sc->pdma_nchunk=%d\n",
+                   sc->pdma_nchunk));
+       }
+
+       /*
+        * If size of the sent chunk is not enough, then pad out the buffer
+        * with zero's.
+        */
+       if (nbyte <= sc->pdma_watermark) {
+               nec86fifo_padding(sc, sc->pdma_watermark);
+               sc->pdma_padded = 1;
+
+               DPRINTF(("nec86hw_output_chunk: write padding zero's\n"));
+       }
+}
+
+/*
+ * Routines to write data directly.
+ */
+int
+nec86fifo_output_mono_8_direct(struct nec86hw_softc *sc, int cc)
+{
+       bus_space_tag_t iot = sc->sc_iot;
+       bus_space_handle_t ioh = sc->sc_ioh;
+       u_int8_t *p = sc->pdma_ptr;
+       int i;
+       register u_int8_t d;
+
+       for (i = 0; i < cc; i++) {
+               d = *p++;
+               d ^= 0x80;      /* unsigned -> signed */
+               /* Fake monoral playing by duplicating a sample. */
+               bus_space_write_1(iot, ioh, NEC86_FIFODATA, d);
+               bus_space_write_1(iot, ioh, NEC86_FIFODATA, d);
+       }
+
+       return cc * 2;
+}
+
+int
+nec86fifo_output_mono_16_direct(struct nec86hw_softc *sc, int cc)
+{
+       bus_space_tag_t iot = sc->sc_iot;
+       bus_space_handle_t ioh = sc->sc_ioh;
+       u_int8_t *p = sc->pdma_ptr;
+       int i;
+
+       for (i = 0; i < cc; i++) {
+               /* Fake monoral playing by duplicating a sample. */
+#if BYTE_ORDER == BIG_ENDIAN
+               bus_space_write_1(iot, ioh, NEC86_FIFODATA, *p);
+               bus_space_write_1(iot, ioh, NEC86_FIFODATA, *(p + 1));
+               bus_space_write_1(iot, ioh, NEC86_FIFODATA, *p);
+               bus_space_write_1(iot, ioh, NEC86_FIFODATA, *(p + 1));
+#else  /* little endian -> big endian */
+               bus_space_write_1(iot, ioh, NEC86_FIFODATA, *(p + 1));
+               bus_space_write_1(iot, ioh, NEC86_FIFODATA, *p);
+               bus_space_write_1(iot, ioh, NEC86_FIFODATA, *(p + 1));
+               bus_space_write_1(iot, ioh, NEC86_FIFODATA, *p);
+#endif
+               p += 2;
+       }
+
+       return cc * 4;
+}
+
+int
+nec86fifo_output_stereo_8_direct(struct nec86hw_softc *sc, int cc)
+{
+       bus_space_tag_t iot = sc->sc_iot;
+       bus_space_handle_t ioh = sc->sc_ioh;
+       u_int8_t *p = sc->pdma_ptr;
+       int i;
+
+       for (i = 0; i < cc; i++) {
+               /* unsigned -> signed (L) */
+               bus_space_write_1(iot, ioh, NEC86_FIFODATA, (*p++) ^ 0x80);
+               /* unsigned -> signed (R) */
+               bus_space_write_1(iot, ioh, NEC86_FIFODATA, (*p++) ^ 0x80);
+       }
+
+       return cc * 2;
+}
+
+int
+nec86fifo_output_stereo_16_direct(struct nec86hw_softc *sc, int cc)
+{
+       bus_space_tag_t iot = sc->sc_iot;
+       bus_space_handle_t ioh = sc->sc_ioh;
+       u_int8_t *p = sc->pdma_ptr;
+       int i;
+
+       for (i = 0; i < cc; i++) {
+#if BYTE_ORDER == BIG_ENDIAN
+               /* (L) */
+               bus_space_write_1(iot, ioh, NEC86_FIFODATA, *p);
+               bus_space_write_1(iot, ioh, NEC86_FIFODATA, *(p + 1));
+               p += 2;
+               /* (R) */
+               bus_space_write_1(iot, ioh, NEC86_FIFODATA, *p);
+               bus_space_write_1(iot, ioh, NEC86_FIFODATA, *(p + 1));
+               p += 2;
+#else
+               /* little endian -> big endian */
+               /* (L) */
+               bus_space_write_1(iot, ioh, NEC86_FIFODATA, *(p + 1));
+               bus_space_write_1(iot, ioh, NEC86_FIFODATA, *p);
+               p += 2;
+               /* (R) */
+               bus_space_write_1(iot, ioh, NEC86_FIFODATA, *(p + 1));
+               bus_space_write_1(iot, ioh, NEC86_FIFODATA, *p);
+               p += 2;
+#endif
+       }
+
+       return cc * 4;
+}
+
+/*
+ * Routines to write data with resampling. (linear interpolation)
+ */
+int
+nec86fifo_output_mono_8_resamp(struct nec86hw_softc *sc, int cc)
+{
+       bus_space_tag_t iot = sc->sc_iot;
+       bus_space_handle_t ioh = sc->sc_ioh;
+       u_int8_t *p = sc->pdma_ptr;
+       int i;
+       register int rval;
+       register u_int8_t d;
+       register u_int8_t d0, d1;
+       register u_long acc, orate, hw_orate;
+
+       rval = 0;
+
+       orate = sc->sc_orate;
+       hw_orate = sc->hw_orate;
+       acc = sc->conv_acc;
+       d0 = (u_int8_t) sc->conv_last0;
+       d1 = (u_int8_t) sc->conv_last1;
+
+       for (i = 0; i < cc; i++) {
+               d0 = d1;
+               d1 = *p++;
+
+               while (acc <= hw_orate) {
+                       /* Linear interpolation. */
+                       d = ((d0 * (hw_orate - acc)) + (d1 * acc)) / hw_orate;
+                       /* unsigned -> signed */
+                       d ^= 0x80;
+
+                       /* Fake monoral playing by duplicating a sample. */
+                       bus_space_write_1(iot, ioh, NEC86_FIFODATA, d);
+                       bus_space_write_1(iot, ioh, NEC86_FIFODATA, d);
+
+                       acc += orate;
+                       rval += 2;
+               }
+
+               acc -= hw_orate;
+       }
+
+       sc->conv_acc = acc;
+       sc->conv_last0 = (u_short) d0;
+       sc->conv_last1 = (u_short) d1;
+
+       return rval;
+}
+
+int
+nec86fifo_output_mono_16_resamp(struct nec86hw_softc *sc, int cc)
+{
+       bus_space_tag_t iot = sc->sc_iot;
+       bus_space_handle_t ioh = sc->sc_ioh;
+       u_int8_t *p = sc->pdma_ptr;
+       int i;
+       register int rval;
+       register u_short d, d0, d1;
+       register u_long acc, orate, hw_orate;
+
+       rval = 0;
+
+       orate = sc->sc_orate;
+       hw_orate = sc->hw_orate;
+       acc = sc->conv_acc;
+       d0 = sc->conv_last0;
+       d1 = sc->conv_last1;
+
+       for (i = 0; i < cc; i++) {
+               d0 = d1;
+               /* little endian signed -> unsigned */
+               d1 = (*p | (*(p + 1) << 8)) ^ 0x8000;
+               p += 2;
+
+               while (acc <= hw_orate) {
+                       /* Linear interpolation. */
+                       d = ((d0 * (hw_orate - acc)) + (d1 * acc)) / hw_orate;
+                       /* unsigned -> signed */
+                       d ^= 0x8000;
+
+                       /* Fake monoral playing by duplicating a sample. */
+                       bus_space_write_1(iot, ioh, NEC86_FIFODATA,
+                           (d >> 8) & 0xff); /* -> big endian */
+                       bus_space_write_1(iot, ioh, NEC86_FIFODATA, d & 0xff);
+                       bus_space_write_1(iot, ioh, NEC86_FIFODATA,
+                           (d >> 8) & 0xff);
+                       bus_space_write_1(iot, ioh, NEC86_FIFODATA, d & 0xff);
+
+                       acc += orate;
+                       rval += 4;
+               }
+
+               acc -= hw_orate;
+       }
+
+       sc->conv_acc = acc;
+       sc->conv_last0 = d0;
+       sc->conv_last1 = d1;
+
+       return rval;
+}
+
+int
+nec86fifo_output_stereo_8_resamp(struct nec86hw_softc *sc, int cc)
+{
+       bus_space_tag_t iot = sc->sc_iot;
+       bus_space_handle_t ioh = sc->sc_ioh;
+       u_int8_t *p = sc->pdma_ptr;
+       int i;
+       register int rval;
+       register u_int8_t d, d0_l, d0_r, d1_l, d1_r;
+       register u_long acc, orate, hw_orate;
+
+       rval = 0;
+
+       orate = sc->sc_orate;
+       hw_orate = sc->hw_orate;
+       acc = sc->conv_acc;
+       d0_l = sc->conv_last0_l;
+       d0_r = sc->conv_last0_r;
+       d1_l = sc->conv_last1_l;
+       d1_r = sc->conv_last1_r;
+
+       for (i = 0; i < cc; i++) {
+               d0_l = d1_l;
+               d0_r = d1_r;
+               d1_l = *p++;
+               d1_r = *p++;
+
+               while (acc <= hw_orate) {
+                       /* Linear interpolation. (L) */
+                       d = ((d0_l * (hw_orate - acc)) + (d1_l * acc))
+                           / hw_orate;
+                       /* unsigned -> signed (L) */
+                       d ^= 0x80;
+                       bus_space_write_1(iot, ioh, NEC86_FIFODATA, d);
+
+                       /* Linear interpolation. (R) */
+                       d = ((d0_r * (hw_orate - acc)) + (d1_r * acc))
+                           / hw_orate;
+                       /* unsigned -> signed (R) */
+                       d ^= 0x80;
+                       bus_space_write_1(iot, ioh, NEC86_FIFODATA, d);
+
+                       acc += orate;
+                       rval += 2;
+               }
+
+               acc -= hw_orate;
+       }
+
+       sc->conv_acc = acc;
+       sc->conv_last0_l = d0_l;
+       sc->conv_last0_r = d0_r;
+       sc->conv_last1_l = d1_l;
+       sc->conv_last1_r = d1_r;
+
+       return rval;
+}
+
+int
+nec86fifo_output_stereo_16_resamp(struct nec86hw_softc *sc, int cc)
+{
+       bus_space_tag_t iot = sc->sc_iot;
+       bus_space_handle_t ioh = sc->sc_ioh;
+       u_int8_t *p = sc->pdma_ptr;
+       int i;
+       register int rval;
+       register u_short d, d0_l, d0_r, d1_l, d1_r;
+       register u_long acc, orate, hw_orate;
+
+       rval = 0;
+
+       orate = sc->sc_orate;
+       hw_orate = sc->hw_orate;
+       acc = sc->conv_acc;
+
+       d0_l = sc->conv_last0_l;
+       d0_r = sc->conv_last0_r;
+       d1_l = sc->conv_last1_l;
+       d1_r = sc->conv_last1_r;
+
+       for (i = 0; i < cc; i++) {
+               d0_l = d1_l;
+               d0_r = d1_r;
+               /* little endian signed -> unsigned (L) */
+               d1_l = (*p | (*(p + 1) << 8)) ^ 0x8000;
+               p += 2;
+               /* little endian signed -> unsigned (R) */
+               d1_r = (*p | (*(p + 1) << 8)) ^ 0x8000;
+               p += 2;
+
+               while (acc <= hw_orate) {
+                       /* Linear interpolation. (L) */
+                       d = ((d0_l * (hw_orate - acc)) + (d1_l * acc))
+                           / hw_orate;
+                       /* unsigned -> signed (L) */
+                       d ^= 0x8000;
+
+                       bus_space_write_1(iot, ioh, NEC86_FIFODATA,
+                           (d >> 8) & 0xff); /* -> big endian (L) */
+                       bus_space_write_1(iot, ioh, NEC86_FIFODATA, d & 0xff);
+
+                       /* Linear interpolation. (R) */
+                       d = ((d0_r * (hw_orate - acc)) + (d1_r * acc))
+                           / hw_orate;
+                       /* unsigned -> signed (R) */
+                       d ^= 0x8000;
+
+                       bus_space_write_1(iot, ioh, NEC86_FIFODATA,
+                           (d >> 8) & 0xff); /* -> big endian (R) */
+                       bus_space_write_1(iot, ioh, NEC86_FIFODATA, d & 0xff);
+
+                       acc += orate;
+                       rval += 4;
+               }
+
+               acc -= hw_orate;
+       }
+
+       sc->conv_acc = acc;
+       sc->conv_last0_l = d0_l;
+       sc->conv_last0_r = d0_r;
+       sc->conv_last1_l = d1_l;
+       sc->conv_last1_r = d1_r;
+
+       return rval;
+}
+
+/*
+ * Read data from the FIFO ring buffer on the board.
+ */
+void
+nec86hw_input_chunk(struct nec86hw_softc *sc)
+{
+       int cc, bpf;
+
+       bpf = (sc->channels * sc->precision) / NBBY;
+       if (sc->pdma_nchunk > 0) {
+               /* Update chunksize and then receive the chunk from the board. */
+               /* chunksize in frames */
+               cc = sc->pdma_count / sc->pdma_nchunk;
+               (*sc->func_fifo_input)(sc, cc);
+
+               DPRINTF2(3, ("nec86hw_input_chunk: sc->pdma_count=%d "
+                   "sc->pdma_nchunk=%d cc=%d ptr=%p\n",
+                   sc->pdma_count, sc->pdma_nchunk, cc, sc->pdma_ptr));
+
+               sc->pdma_nchunk--;
+               sc->pdma_count -= cc;
+               sc->pdma_ptr += (cc * bpf);
+       } else {
+               /* ??? -  This should never happen. */
+               cc = 0;
+               DPRINTF(("nec86hw_input_chunk: ??? sc->pdma_nchunk=%d ???",
+                   sc->pdma_nchunk));
+       }
+}
+
+/*
+ * Routines to read data directly.
+ */
+void
+nec86fifo_input_mono_8_direct(struct nec86hw_softc *sc, int cc)
+{
+       bus_space_tag_t iot = sc->sc_iot;
+       bus_space_handle_t ioh = sc->sc_ioh;
+       u_int8_t *p = sc->pdma_ptr;
+       int i;
+       register u_int8_t d_l, d_r;
+
+       for (i = 0; i < cc; i++) {
+               /* signed -> unsigned (L) */
+               d_l = bus_space_read_1(iot, ioh, NEC86_FIFODATA) ^ 0x80;
+               /* signed -> unsigned (R) */
+               d_r = bus_space_read_1(iot, ioh, NEC86_FIFODATA) ^ 0x80;
+
+               /* Fake monoral recording by taking arithmetical mean. */
+               *p++ = (d_l + d_r) / 2;
+       }
+}
+
+void
+nec86fifo_input_mono_16_direct(struct nec86hw_softc *sc, int cc)
+{
+       bus_space_tag_t iot = sc->sc_iot;
+       bus_space_handle_t ioh = sc->sc_ioh;
+       u_int8_t *p = sc->pdma_ptr;
+       int i;
+       register u_short d, d_l, d_r;
+
+       for (i = 0; i < cc; i++) {
+               /* big endian signed -> unsigned (L) */
+               d_l = (bus_space_read_1(iot, ioh, NEC86_FIFODATA) ^ 0x80) << 8;
+               d_l |= bus_space_read_1(iot, ioh, NEC86_FIFODATA);
+               /* big endian signed -> unsigned (R) */
+               d_r = (bus_space_read_1(iot, ioh, NEC86_FIFODATA) ^ 0x80) << 8;
+               d_r |= bus_space_read_1(iot, ioh, NEC86_FIFODATA);
+
+               /* Fake monoral recording by taking arithmetical mean. */
+               d = (d_l + d_r) / 2;
+
+#if BYTE_ORDER == BIG_ENDIAN
+               /* -> big endian signed */
+               *p++ = ((d >> 8) & 0xff) ^ 0x80;
+               *p++ = d & 0xff;
+#else
+               /* -> little endian signed */
+               *p++ = d & 0xff;
+               *p++ = ((d >> 8) & 0xff) ^ 0x80;
+#endif
+       }
+}
+
+void
+nec86fifo_input_stereo_8_direct(struct nec86hw_softc *sc, int cc)
+{
+       bus_space_tag_t iot = sc->sc_iot;
+       bus_space_handle_t ioh = sc->sc_ioh;
+       u_int8_t *p = sc->pdma_ptr;
+       int i;
+
+       for (i = 0; i < cc; i++) {
+               /* signed -> unsigned (L) */
+               *p++ = bus_space_read_1(iot, ioh, NEC86_FIFODATA) ^ 0x80;
+               /* signed -> unsigned (R) */
+               *p++ = bus_space_read_1(iot, ioh, NEC86_FIFODATA) ^ 0x80;
+       }
+}
+
+void
+nec86fifo_input_stereo_16_direct(struct nec86hw_softc *sc, int cc)
+{
+       bus_space_tag_t iot = sc->sc_iot;
+       bus_space_handle_t ioh = sc->sc_ioh;
+       u_int8_t *p = sc->pdma_ptr;
+       int i;
+
+       for (i = 0; i < cc; i++) {
+#if BYTE_ORDER == BIG_ENDIAN
+               *p = bus_space_read_1(iot, ioh, NEC86_FIFODATA);/* (L) */
+               *(p + 1) = bus_space_read_1(iot, ioh, NEC86_FIFODATA);
+               p += 2;
+               *p = bus_space_read_1(iot, ioh, NEC86_FIFODATA);/* (R) */
+               *(p + 1) = bus_space_read_1(iot, ioh, NEC86_FIFODATA);
+               p += 2;
+#else  /* big endian -> little endian */
+               *(p + 1) = bus_space_read_1(iot, ioh, NEC86_FIFODATA);/* (L) */
+               *p = bus_space_read_1(iot, ioh, NEC86_FIFODATA);
+               p += 2;
+               *(p + 1) = bus_space_read_1(iot, ioh, NEC86_FIFODATA);/* (R) */
+               *p = bus_space_read_1(iot, ioh, NEC86_FIFODATA);
+               p += 2;
+#endif
+       }
+}
+
+/*
+ * Routines to read data with resampling. (linear interpolation)
+ */
+void
+nec86fifo_input_mono_8_resamp(struct nec86hw_softc *sc, int cc)
+{
+       bus_space_tag_t iot = sc->sc_iot;
+       bus_space_handle_t ioh = sc->sc_ioh;
+       u_int8_t *p = sc->pdma_ptr;
+       int i;
+       register u_int8_t d_l, d_r;
+       register u_int8_t d0, d1;
+       register u_long acc, irate, hw_irate;
+
+       irate = sc->sc_irate;
+       hw_irate = sc->hw_irate;
+       acc = sc->conv_acc;
+       d0 = sc->conv_last0;
+       d1 = sc->conv_last1;
+
+       for (i = 0; i < cc; i++) {
+               while (acc > irate) {
+                       d0 = d1;
+                       /* signed -> unsigned (L) */
+                       d_l = bus_space_read_1(iot, ioh, NEC86_FIFODATA) ^ 0x80;
+                       /* signed -> unsigned (R) */
+                       d_r = bus_space_read_1(iot, ioh, NEC86_FIFODATA) ^ 0x80;
+                       /* Fake monoral recording by taking arithmetical mean. */
+                       d1 = (d_l + d_r) / 2;
+
+                       acc -= irate;
+               }
+
+               /* Linear interpolation. */
+               *p++ = ((d0 * (irate - acc)) + (d1 * acc)) / irate;
+
+               acc += hw_irate;
+       }
+
+       sc->conv_acc = acc;
+       sc->conv_last0 = d0;
+       sc->conv_last1 = d1;
+}
+
+void
+nec86fifo_input_mono_16_resamp(struct nec86hw_softc *sc, int cc)
+{
+       bus_space_tag_t iot = sc->sc_iot;
+       bus_space_handle_t ioh = sc->sc_ioh;
+       u_int8_t *p = sc->pdma_ptr;
+       int i;
+       register u_short d, d_l, d_r;
+       register u_short d0, d1;
+       register u_long acc, irate, hw_irate;
+
+       irate = sc->sc_irate;
+       hw_irate = sc->hw_irate;
+       acc = sc->conv_acc;
+       d0 = sc->conv_last0;
+       d1 = sc->conv_last1;
+
+       for (i = 0; i < cc; i++) {
+               while (acc > irate) {
+                       d0 = d1;
+                       /* big endian signed -> unsigned (L) */
+                       d_l = (bus_space_read_1(iot, ioh, NEC86_FIFODATA)
+                           ^ 0x80) << 8;
+                       d_l |= bus_space_read_1(iot, ioh, NEC86_FIFODATA);
+                       /* big endian signed -> unsigned (R) */
+                       d_r = (bus_space_read_1(iot, ioh, NEC86_FIFODATA)
+                           ^ 0x80) << 8;
+                       d_r |= bus_space_read_1(iot, ioh, NEC86_FIFODATA);
+                       /* Fake monoral recording by taking arithmetical mean. */
+                       d1 = (d_l + d_r) / 2;
+
+                       acc -= irate;
+               }
+
+               /* Linear interpolation. */
+               d = ((d0 * (irate - acc)) + (d1 * acc)) / irate;
+
+#if BYTE_ORDER == BIG_ENDIAN
+               /* -> big endian signed */
+               *p++ = ((d >> 8) & 0xff) ^ 0x80;
+               *p++ = d & 0xff;
+#else
+               /* -> little endian signed */
+               *p++ = d & 0xff;
+               *p++ = ((d >> 8) & 0xff) ^ 0x80;
+#endif
+
+               acc += hw_irate;
+       }
+
+       sc->conv_acc = acc;
+       sc->conv_last0 = d0;
+       sc->conv_last1 = d1;
+}
+
+void
+nec86fifo_input_stereo_8_resamp(struct nec86hw_softc *sc, int cc)
+{
+       bus_space_tag_t iot = sc->sc_iot;
+       bus_space_handle_t ioh = sc->sc_ioh;
+       u_int8_t *p = sc->pdma_ptr;
+       int i;
+       register u_int8_t d0_l, d0_r, d1_l, d1_r;
+       register u_long acc, irate, hw_irate;
+
+       irate = sc->sc_irate;
+       hw_irate = sc->hw_irate;
+       acc = sc->conv_acc;
+       d0_l = sc->conv_last0_l;
+       d0_r = sc->conv_last0_r;
+       d1_l = sc->conv_last1_l;
+       d1_r = sc->conv_last1_r;
+
+       for (i = 0; i < cc; i++) {
+               while (acc > irate) {
+                       d0_l = d1_l;
+                       d0_r = d1_r;
+                       /* signed -> unsigned (L) */
+                       d1_l = bus_space_read_1(iot, ioh, NEC86_FIFODATA)
+                           ^ 0x80;
+                       /* signed -> unsigned (R) */
+                       d1_r = bus_space_read_1(iot, ioh, NEC86_FIFODATA)
+                           ^ 0x80;
+
+                       acc -= irate;
+               }
+
+               /* Linear interpolation. (L) */
+               *p++ = ((d0_l * (irate - acc)) + (d1_l * acc)) / irate;
+
+               /* Linear interpolation. (R) */
+               *p++ = ((d0_r * (irate - acc)) + (d1_r * acc)) / irate;
+
+               acc += hw_irate;
+       }
+
+       sc->conv_acc = acc;
+       sc->conv_last0_l = d0_l;
+       sc->conv_last0_r = d0_r;
+       sc->conv_last1_l = d1_l;
+       sc->conv_last1_r = d1_r;
+}
+
+void
+nec86fifo_input_stereo_16_resamp(struct nec86hw_softc *sc, int cc)
+{
+       bus_space_tag_t iot = sc->sc_iot;
+       bus_space_handle_t ioh = sc->sc_ioh;
+       u_int8_t *p = sc->pdma_ptr;
+       int i;
+       register u_short d, d0_l, d0_r, d1_l, d1_r;
+       register u_long acc, irate, hw_irate;
+
+       irate = sc->sc_irate;
+       hw_irate = sc->hw_irate;
+       acc = sc->conv_acc;
+       d0_l = sc->conv_last0_l;
+       d0_r = sc->conv_last0_r;
+       d1_l = sc->conv_last1_l;
+       d1_r = sc->conv_last1_r;
+
+       for (i = 0; i < cc; i++) {
+               while (acc > irate) {
+                       d0_l = d1_l;
+                       d0_r = d1_r;
+                       /* big endian signed -> unsigned (L) */
+                       d1_l = (bus_space_read_1(iot, ioh, NEC86_FIFODATA)
+                           ^ 0x80) << 8;
+                       d1_l |= bus_space_read_1(iot, ioh, NEC86_FIFODATA);
+                       /* big endian signed -> unsigned (R) */
+                       d1_r = (bus_space_read_1(iot, ioh, NEC86_FIFODATA)
+                           ^ 0x80) << 8;
+                       d1_r |= bus_space_read_1(iot, ioh, NEC86_FIFODATA);
+
+                       acc -= irate;
+               }
+
+               /* Linear interpolation. (L) */
+               d = ((d0_l * (irate - acc)) + (d1_l * acc)) / irate;
+
+#if BYTE_ORDER == BIG_ENDIAN
+               /* -> big endian signed (L) */
+               *p++ = ((d >> 8) & 0xff) ^ 0x80;
+               *p++ = d & 0xff;
+#else
+               /* -> little endian signed (L) */
+               *p++ = d & 0xff;
+               *p++ = ((d >> 8) & 0xff) ^ 0x80;
+#endif
+
+               /* Linear interpolation. (R) */
+               d = ((d0_r * (irate - acc)) + (d1_r * acc)) / irate;
+
+#if BYTE_ORDER == BIG_ENDIAN
+               /* -> big endian signed (R) */
+               *p++ = ((d >> 8) & 0xff) ^ 0x80;
+               *p++ = d & 0xff;
+#else
+               /* -> little endian signed (R) */
+               *p++ = d & 0xff;
+               *p++ = ((d >> 8) & 0xff) ^ 0x80;
+#endif
+
+               acc += hw_irate;
+       }
+
+       sc->conv_acc = acc;
+       sc->conv_last0_l = d0_l;
+       sc->conv_last0_r = d0_r;
+       sc->conv_last1_l = d1_l;
+       sc->conv_last1_r = d1_r;
+}
+
+/*
+ * Write padding zero's to the FIFO ring buffer on the board.
+ */
+void
+nec86fifo_padding(struct nec86hw_softc *sc, int cc)
+{
+       bus_space_tag_t iot = sc->sc_iot;
+       bus_space_handle_t ioh = sc->sc_ioh;
+       register int i;
+
+       DPRINTF2(2, ("nec86fifo_padding: %d\n", cc));
+
+       for (i = 0; i < cc; i++)
+               bus_space_write_1(iot, ioh, NEC86_FIFODATA, 0);
+}
+
+/*
+ * Interrupt handler.
+ */
+int
+nec86hw_intr(void *arg)
+{
+       struct nec86hw_softc *sc = (struct nec86hw_softc *) arg;
+
+       if (!nec86hw_seeif_intrflg(sc)) {
+               /* Seems to be an FM sound interrupt. */
+               DPRINTF(("nec86hw_intr: ??? FM sound interrupt ???\n"));
+               return 0;
+       }
+
+       mtx_enter(&audio_lock);
+       nec86hw_clear_intrflg(sc);
+
+       switch(sc->pdma_mode) {
+       case PDMA_MODE_OUTPUT:
+               if (sc->pdma_padded) {
+                       /* Clear the padding zero's. */
+                       nec86hw_reset_fifo(sc);
+                       sc->pdma_padded = 0;
+                       DPRINTF(("nec86hw_intr: clear padding zero's\n"));
+               }
+               if (sc->pdma_count > 0) {
+                       /* Send the next chunk. */
+                       nec86hw_disable_fifointr(sc);
+                       nec86hw_output_chunk(sc);
+                       nec86hw_enable_fifointr(sc);
+               } else
+                       (*sc->sc_intr)(sc->sc_arg);
+               break;
+       case PDMA_MODE_INPUT:
+               if (sc->pdma_count > 0) {
+                       /* Receive the next chunk. */
+                       nec86hw_disable_fifointr(sc);
+                       nec86hw_input_chunk(sc);
+                       nec86hw_enable_fifointr(sc);
+               }
+               if (sc->pdma_count <= 0)
+                       (*sc->sc_intr)(sc->sc_arg);
+               break;
+       default:
+               /* This should never happen. */
+               nec86hw_stop_fifo(sc);
+               nec86hw_disable_fifointr(sc);
+               sc->intr_busy = 0;
+
+               DPRINTF(("nec86hw_intr: ??? unexpected interrupt ???\n"));
+
+               mtx_leave(&audio_lock);
+               return 0;
+       }
+
+       mtx_leave(&audio_lock);
+       return 1;
+}
+
+int
+nec86_get_props(void *addr)
+{
+       return 0; 
+}
diff --git a/sys/arch/luna88k/cbus/nec86hwvar.h b/sys/arch/luna88k/cbus/nec86hwvar.h
new file mode 100644 (file)
index 0000000..a7e36d0
--- /dev/null
@@ -0,0 +1,221 @@
+/*     $OpenBSD: nec86hwvar.h,v 1.1 2014/12/28 13:03:18 aoyama Exp $   */
+/*     $NecBSD: nec86hwvar.h,v 1.10 1998/03/14 07:04:55 kmatsuda Exp $ */
+/*     $NetBSD$        */
+
+/*
+ * [NetBSD for NEC PC-98 series]
+ *  Copyright (c) 1996, 1997, 1998
+ *     NetBSD/pc98 porting staff. 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.
+ */
+
+/*
+ * nec86hwvar.h
+ *
+ * NEC PC-9801-86 SoundBoard PCM driver for NetBSD/pc98.
+ * Written by NAGAO Tadaaki, Feb 10, 1996.
+ */
+
+#ifndef        _NEC86HWVAR_H_
+#define        _NEC86HWVAR_H_
+
+/* types of function which writes to/reads from FIFO buffer */
+struct nec86hw_softc;  /* dummy declaration */
+typedef int (*func_fifo_output_t) (struct nec86hw_softc *, int);
+typedef void (*func_fifo_input_t) (struct nec86hw_softc *, int);
+
+
+struct nec86hw_softc {
+       struct  device sc_dev;          /* base device */
+
+       bus_space_tag_t sc_iot;         /* bus space tag */
+       bus_space_handle_t sc_ioh;      /* nec86 core chip space handle */
+
+       u_int   sc_cfgflags;            /* config flags */
+#define NEC86HW_RATE_TYPE(flags)       ((flags) & 1)
+#define NEC86HW_NRATE_TYPE             2
+
+       u_short sc_open;                /* reference count of open calls */
+
+       u_long  sc_irate;               /* sample rate for input */
+       u_long  sc_orate;               /* sample rate for output */
+
+       u_long  hw_irate;               /* hardware rate for input */
+       u_char  hw_irate_bits;
+       u_long  hw_orate;               /* hardware rate for output */
+       u_char  hw_orate_bits;
+
+       u_int   encoding;               /* ulaw / linear */
+       u_int   precision;              /* 8/16 bits */
+       int     channels;               /* monoral(1) / stereo(2) */
+
+       int     in_port;                /* Just keep track of it */
+#define NEC86HW_INPUT_MIXER    0
+       int     out_port;               /* Just keep track of it */
+#define NEC86HW_OUTPUT_MIXER   0
+
+       int     model;                  /* board identification number */
+
+       /* linear interpolation */
+       u_long  conv_acc;
+       u_short conv_last0;
+       u_short conv_last0_l;
+       u_short conv_last0_r;
+       u_short conv_last1;
+       u_short conv_last1_l;
+       u_short conv_last1_r;
+
+       void    (*sc_intr)(void *);     /* DMA completion intr handler */
+       void    *sc_arg;                /* arg for sc_intr() */
+
+       char    intr_busy;              /* whether the hardware running */
+
+       /* pseudo-DMA */
+       u_char  *pdma_ptr;              /* pointer to the data buffer */
+       int     pdma_count;             /* size of the data in frames */
+       int     pdma_nchunk;            /* number of chunks to do with */
+       int     pdma_watermark;         /* interrupt trigger level */
+       char    pdma_padded;            /* the buffer is padded out with
+                                          dummy zero's */
+       char    pdma_mode;              /* input/output indicator */
+#define PDMA_MODE_NONE         0
+#define PDMA_MODE_OUTPUT       1
+#define PDMA_MODE_INPUT                2
+
+       /* function which writes to/reads from FIFO buffer */
+       func_fifo_output_t      func_fifo_output;
+       func_fifo_input_t       func_fifo_input;
+};
+
+struct nec86hw_functable_entry {
+       int     precision;
+       int     channels;
+
+       func_fifo_output_t      func_fifo_output_direct;
+       func_fifo_input_t       func_fifo_input_direct;
+       func_fifo_output_t      func_fifo_output_resamp;
+       func_fifo_input_t       func_fifo_input_resamp;
+};
+
+/*
+ * Interrupt watermarks for the FIFO ring buffer on the hardware.
+ *
+ * These values must satisfy:
+ *     0 < WATERMARK_RATIO_OUT <= WATERMARK_MAX_RATIO
+ *     0 < WATERMARK_RATIO_IN  <= WATERMARK_MAX_RATIO.
+ * (For more details, see also nec86hw_pdma_output() and nec86hw_pdma_input()
+ *  in nec86hw.c)
+ */
+#define WATERMARK_MAX_RATIO    100
+#define WATERMARK_RATIO_OUT    50      /* 50% of the blocksize */
+#define WATERMARK_RATIO_IN     50      /* 50% of NEC86_BUFFSIZE */
+
+/*
+ * Declarations of prototypes.
+ */
+#ifdef _KERNEL
+void   nec86hw_attach(struct nec86hw_softc *);
+
+int    nec86hw_open(void *, int);
+void   nec86hw_close(void *);
+
+int    nec86hw_set_params(void *, int, int, struct audio_params *,
+           struct audio_params *);
+int    nec86hw_query_encoding(void *, struct audio_encoding *);
+
+int    nec86hw_round_blocksize(void *, int);
+
+int    nec86hw_set_out_port(void *, int);
+int    nec86hw_get_out_port(void *);
+int    nec86hw_set_in_port(void *, int);
+int    nec86hw_get_in_port(void *);
+
+int    nec86hw_commit_settings(void *);
+
+int    nec86hw_setfd(void *, int);
+
+int    nec86hw_mixer_set_port(void *, mixer_ctrl_t *);
+int    nec86hw_mixer_get_port(void *, mixer_ctrl_t *);
+int    nec86hw_mixer_query_devinfo(void *, mixer_devinfo_t *);
+
+int    nec86hw_pdma_init_output(void *, void *, int);
+int    nec86hw_pdma_init_input(void *, void *, int);
+int    nec86hw_pdma_output(void *, void *, int, void (*)(void *), void *);
+int    nec86hw_pdma_input(void *, void *, int, void (*)(void *), void *);
+
+int    nec86hw_speaker_ctl(void *, int);
+
+int    nec86hw_halt_pdma(void *);
+int    nec86hw_cont_pdma(void *);
+
+u_char nec86hw_rate_bits(struct nec86hw_softc *, u_long);
+int    nec86hw_round_watermark(int);
+
+int    nec86hw_reset(struct nec86hw_softc *);
+void   nec86hw_set_mode_playing(struct nec86hw_softc *);
+void   nec86hw_set_mode_recording(struct nec86hw_softc *);
+
+void   nec86hw_set_volume(struct nec86hw_softc *, int, u_char);
+
+void   nec86hw_start_fifo(struct nec86hw_softc *);
+void   nec86hw_stop_fifo(struct nec86hw_softc *);
+void   nec86hw_enable_fifointr(struct nec86hw_softc *);
+void   nec86hw_disable_fifointr(struct nec86hw_softc *);
+int    nec86hw_seeif_intrflg(struct nec86hw_softc *);
+void   nec86hw_clear_intrflg(struct nec86hw_softc *);
+void   nec86hw_reset_fifo(struct nec86hw_softc *);
+void   nec86hw_set_watermark(struct nec86hw_softc *, int);
+void   nec86hw_set_precision_real(struct nec86hw_softc *, u_int);
+void   nec86hw_set_rate_real(struct nec86hw_softc *, u_char);
+
+void   nec86hw_output_chunk(struct nec86hw_softc *);
+void   nec86hw_input_chunk(struct nec86hw_softc *);
+
+int    nec86fifo_output_mono_8_direct(struct nec86hw_softc *, int);
+int    nec86fifo_output_mono_16_direct(struct nec86hw_softc *, int);
+int    nec86fifo_output_stereo_8_direct(struct nec86hw_softc *, int);
+int    nec86fifo_output_stereo_16_direct(struct nec86hw_softc *, int);
+int    nec86fifo_output_mono_8_resamp(struct nec86hw_softc *, int);
+int    nec86fifo_output_mono_16_resamp(struct nec86hw_softc *, int);
+int    nec86fifo_output_stereo_8_resamp(struct nec86hw_softc *, int);
+int    nec86fifo_output_stereo_16_resamp(struct nec86hw_softc *, int);
+void   nec86fifo_input_mono_8_direct(struct nec86hw_softc *, int);
+void   nec86fifo_input_mono_16_direct(struct nec86hw_softc *, int);
+void   nec86fifo_input_stereo_8_direct(struct nec86hw_softc *, int);
+void   nec86fifo_input_stereo_16_direct(struct nec86hw_softc *, int);
+void   nec86fifo_input_mono_8_resamp(struct nec86hw_softc *, int);
+void   nec86fifo_input_mono_16_resamp(struct nec86hw_softc *, int);
+void   nec86fifo_input_stereo_8_resamp(struct nec86hw_softc *, int);
+void   nec86fifo_input_stereo_16_resamp(struct nec86hw_softc *, int);
+
+void   nec86fifo_padding(struct nec86hw_softc *, int);
+
+int    nec86hw_intr(void *);
+
+int    nec86getdev(void *, struct audio_device *);
+int    nec86_get_props(void *);
+
+#endif /* _KERNEL */
+#endif /* !_NEC86HWVAR_H_ */
diff --git a/sys/arch/luna88k/cbus/nec86reg.h b/sys/arch/luna88k/cbus/nec86reg.h
new file mode 100644 (file)
index 0000000..7ee5ad2
--- /dev/null
@@ -0,0 +1,139 @@
+/*     $OpenBSD: nec86reg.h,v 1.1 2014/12/28 13:03:18 aoyama Exp $     */
+/*     $NecBSD: nec86reg.h,v 1.5 1998/03/14 07:04:56 kmatsuda Exp $    */
+/*     $NetBSD$        */
+
+/*
+ * [NetBSD for NEC PC-98 series]
+ *  Copyright (c) 1996, 1997, 1998
+ *     NetBSD/pc98 porting staff. 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.
+ */
+
+/*
+ * nec86reg.h
+ *
+ * NEC PC-9801-86 SoundBoard PCM driver for NetBSD/pc98.
+ * Written by NAGAO Tadaaki, Feb 10, 1996.
+ */
+
+#ifndef        _NEC86REG_H_
+#define        _NEC86REG_H_
+
+#define NEC86_PORT             16
+
+/*
+ * Offsets from PCM I/O base address
+ */
+#define NEC86_SOUND_ID         0
+
+#define        NEC86_COREOFFSET        6       /* core chip base */
+#define        NEC86_CORESIZE          7
+#define NEC86_FIFOSTAT         0       /* readonly */
+#define NEC86_VOLUME           0       /* writeonly */
+#define NEC86_FIFOCTL          2
+#define NEC86_CTRL             4       /* when FIFO not running */
+#define NEC86_FIFOINTRBLK      4       /* when FIFO running, writeonly */
+#define NEC86_FIFODATA         6
+
+/*
+ * FIFO status bits
+ */
+#define NEC86_FIFOSTAT_FULL    0x80
+#define NEC86_FIFOSTAT_EMPTY   0x40
+#define NEC86_FIFOSTAT_OVERFLOW        0x20
+#define NEC86_FIFOSTAT_LRCLK   0x01
+
+/*
+ * FIFO control bits
+ */
+#define NEC86_FIFOCTL_RUN      0x80
+#define NEC86_FIFOCTL_RECMODE  0x40
+#define NEC86_FIFOCTL_ENBLINTR 0x20
+#define NEC86_FIFOCTL_INTRFLG  0x10
+#define NEC86_FIFOCTL_INIT     0x08
+
+#define NEC86_FIFOCTL_MASK_RATE 0x07
+#define NEC86_NRATE            8
+
+/*
+ * Card control bits
+ */
+#define NEC86_CTRL_8BITS       0x40
+#define NEC86_CTRL_PAN_L       0x20
+#define NEC86_CTRL_PAN_R       0x10
+
+#define NEC86_CTRL_MASK_PAN    0x30
+#define NEC86_CTRL_MASK_PORT   0x0f
+
+#define NEC86_CTRL_PORT_STD    0x02    /* PC-9801-86 supports only this */
+
+/*
+ * Volume
+ */
+#define NEC86_VOLUME_MASK_PORT 0xe0
+#define NEC86_VOLUME_MASK_VOL  0x0f
+
+#define NEC86_VOLUME_PORT_OPNAD        0       /* OPNA -> LINEOUT */
+#define NEC86_VOLUME_PORT_OPNAI        1       /* OPNA -> ADC */
+#define NEC86_VOLUME_PORT_LINED 2      /* LINEIN (& CDDA) -> LINEOUT */
+#define NEC86_VOLUME_PORT_LINEI 3      /* LINEIN (& CDDA) -> ADC */
+#define NEC86_VOLUME_PORT_CDDAD 4      /* CDDA -> LINEOUT (PC-98GS only) */
+#define NEC86_VOLUME_PORT_PCMD 5       /* DAC -> LINEOUT */
+
+#define NEC86_MINVOL           0
+#define NEC86_MAXVOL           15
+
+#define NEC86_VOL_TO_BITS(p, v)        (((p) << 5) | ((v) ^ 0xf))
+
+/*
+ * Unit size of the interrupt watermark (NEC86_FIFOINTRBLK).
+ */
+#define NEC86_INTRBLK_UNIT     128     /* 128 bytes */
+
+/*
+ * Size of the FIFO ring buffer on the board.
+ *
+ * XXX -  We define this value a little less than the actual size to work
+ *       around the bug at 'buffer full' in Q-Vision's cards.
+ *       ... Too paranoid?
+ */
+#define NEC86_BUFFSIZE (32768 - 2 * 2) /* 2(stereo) * 2(16bits sampling) */
+
+/*
+ * I/O base addresses for YAMAHA FM sound chip OPNA (YM2608/YMF288)
+ */
+#define OPNA_IOBASE0   0x088
+#define OPNA_IOBASE1   0x188
+#define OPNA_IOBASE2   0x288
+#define OPNA_IOBASE3   0x388
+
+/*
+ * Macros to detect valid hardware configuration data
+ */
+#define NEC86_IRQ_VALID(irq)   (((irq) == 3) || ((irq) == 10) || \
+                                ((irq) == 12) || ((irq) == 13))
+#define NEC86_BASE_VALID(base) (((base) == 0xa460) || ((base) == 0xa470) || \
+                                ((base) == 0xa480) || ((base) == 0xa490))
+#endif /* !_NEC86REG_H_ */
diff --git a/sys/arch/luna88k/cbus/nec86var.h b/sys/arch/luna88k/cbus/nec86var.h
new file mode 100644 (file)
index 0000000..921a3b9
--- /dev/null
@@ -0,0 +1,62 @@
+/*     $OpenBSD: nec86var.h,v 1.1 2014/12/28 13:03:18 aoyama Exp $     */
+/*     $NecBSD: nec86var.h,v 1.6 1998/03/14 07:04:57 kmatsuda Exp $    */
+/*     $NetBSD$        */
+
+/*
+ * [NetBSD for NEC PC-98 series]
+ *  Copyright (c) 1996, 1997, 1998
+ *     NetBSD/pc98 porting staff. 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.
+ */
+
+/*
+ * nec86var.h
+ *
+ * NEC PC-9801-86 SoundBoard PCM driver for NetBSD/pc98.
+ * Written by NAGAO Tadaaki, Feb 10, 1996.
+ */
+
+#ifndef        _NEC86VAR_H_
+#define        _NEC86VAR_H_
+
+struct nec86_softc {
+       struct nec86hw_softc sc_nec86hw;
+
+       bus_space_tag_t sc_n86iot;
+       bus_space_handle_t sc_n86ioh;
+
+       bus_space_tag_t sc_ym_iot;
+       bus_space_handle_t sc_ym_iobase;
+       bus_space_handle_t sc_ym_ioh;
+
+       int sc_attached;
+       int sc_intlevel;
+};
+
+extern  struct  audio_hw_if nec86_hw_if;
+int    nec86_probesubr(bus_space_tag_t, bus_space_handle_t,
+           bus_space_handle_t);
+void   nec86_attachsubr(struct nec86_softc *);
+#endif /* !_NEC86VAR_H_ */
diff --git a/sys/arch/luna88k/cbus/necsb.c b/sys/arch/luna88k/cbus/necsb.c
new file mode 100644 (file)
index 0000000..70c8936
--- /dev/null
@@ -0,0 +1,117 @@
+/*     $OpenBSD: necsb.c,v 1.1 2014/12/28 13:03:18 aoyama Exp $        */
+/*     $NecBSD: nec86_isa.c,v 1.9 1998/09/26 11:31:11 kmatsuda Exp $   */
+/*     $NetBSD$        */
+
+/*
+ * [NetBSD for NEC PC-98 series]
+ *  Copyright (c) 1995, 1996, 1997, 1998
+ *     NetBSD/pc98 porting staff. 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.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+
+#include <machine/board.h>             /* PC_BASE */
+#include <machine/bus.h>
+
+#include <sys/audioio.h>
+#include <dev/audio_if.h>
+
+#include <luna88k/cbus/nec86reg.h>
+#include <luna88k/cbus/nec86hwvar.h>
+#include <luna88k/cbus/nec86var.h>
+
+#include <luna88k/cbus/cbusvar.h>      /* cbus_isrlink() */
+
+#define        PCEXIO_BASE     (PC_BASE + 0x1000000)
+#define        NECSB_BASE      (PCEXIO_BASE + 0xa460)
+
+int    necsb_match(struct device *, void *, void *);
+void   necsb_attach(struct device *, struct device *, void *);
+
+struct cfattach necsb_ca = {
+       sizeof(struct nec86_softc), necsb_match, necsb_attach
+};
+
+struct cfdriver necsb_cd = {
+       NULL, "necsb", DV_DULL
+};
+
+/* bus space tag for necsb */
+struct luna88k_bus_space_tag necsb_bst = {
+       1,      /* when reading/writing 1 byte, the stride is 1. */
+       0,
+       0,
+       0,
+};
+
+int
+necsb_match(struct device *parent, void *cf, void *aux)
+{
+       struct cbus_attach_args *caa = aux;
+
+       if (strcmp(caa->ca_name, necsb_cd.cd_name))
+               return 0;
+
+       return 1;
+}
+
+void
+necsb_attach(struct device *parent, struct device *self, void *aux)
+{
+       struct nec86_softc *nsc = (struct nec86_softc *)self;
+       struct nec86hw_softc *ysc = &nsc->sc_nec86hw;
+#if 0
+       struct cbus_attach_args *caa = aux;
+#endif
+
+       bus_space_tag_t iot = &necsb_bst;
+
+       nsc->sc_n86iot = iot;
+       nsc->sc_n86ioh =
+           (bus_space_handle_t)(NECSB_BASE + NEC86_SOUND_ID);
+
+       ysc->sc_iot = iot;
+       ysc->sc_ioh =
+           (bus_space_handle_t)(NECSB_BASE + NEC86_COREOFFSET);
+       ysc->sc_cfgflags = 0;   /* original 'PC-9801-86' */
+
+       nsc->sc_ym_iot = iot;
+       nsc->sc_ym_iobase = (bus_space_handle_t)PCEXIO_BASE;
+       nsc->sc_ym_ioh = (bus_space_handle_t)0; /* default */
+
+       nec86_attachsubr(nsc);
+
+       if (nsc->sc_intlevel == -1)
+               return;
+
+       if ((nsc->sc_intlevel < 0) || (nsc->sc_intlevel >= NCBUSISR))
+               panic("necsb_attach: bad INT level");
+
+       /* XXX: check return value ? */
+       cbus_isrlink(nec86hw_intr, ysc, nsc->sc_intlevel, self->dv_xname);
+}
index d48962b..6063da3 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: pcex.c,v 1.1 2014/12/19 13:17:35 aoyama Exp $ */
+/*     $OpenBSD: pcex.c,v 1.2 2014/12/28 13:03:18 aoyama Exp $ */
 
 /*
  * Copyright (c) 2014 Kenji Aoyama.
@@ -67,7 +67,12 @@ int pcex_wait_int(struct pcex_softc *, u_int);
 int
 pcex_match(struct device *parent, void *cf, void *aux)
 {
-       return 1;       /* XXX: always matched */
+       struct cbus_attach_args *caa = aux;
+
+       if (strcmp(caa->ca_name, pcex_cd.cd_name))
+               return 0;
+
+       return 1;
 }
 
 void
index 0acf5f3..b83c9b2 100644 (file)
@@ -1,4 +1,4 @@
-#      $OpenBSD: files.luna88k,v 1.23 2014/12/19 13:17:35 aoyama Exp $
+#      $OpenBSD: files.luna88k,v 1.24 2014/12/28 13:03:18 aoyama Exp $
 #
 maxpartitions 16
 
@@ -58,13 +58,6 @@ attach spc at mainbus
 file arch/luna88k/dev/spc.c                            spc
 file arch/luna88k/dev/mb89352.c                                spc
 
-# XXX: now testing
-#device pcm: audio, auconv, mulaw
-#attach pcm at mainbus
-#file arch/luna88k/dev/nec86.c                         pcm needs-flag
-#file arch/luna88k/dev/nec86hw.c                       pcm needs-flag
-#file arch/luna88k/dev/nec86_luna88k.c                 pcm needs-flag
-
 device cbus {}
 attach cbus at mainbus
 file   arch/luna88k/cbus/cbus.c
@@ -73,6 +66,13 @@ device pcex
 attach pcex at cbus
 file   arch/luna88k/cbus/pcex.c                                pcex needs-flag
 
+# NEC PC-9801-86 sound board
+device necsb: audio, auconv, mulaw
+attach necsb at cbus
+file arch/luna88k/cbus/necsb.c                         necsb needs-flag
+file arch/luna88k/cbus/nec86.c                         necsb needs-flag
+file arch/luna88k/cbus/nec86hw.c                       necsb needs-flag
+
 # list of standard files
 file   arch/luna88k/luna88k/clock.c
 
index df290e0..4d80aae 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: intr.h,v 1.8 2013/12/25 10:41:55 aoyama Exp $ */
+/*     $OpenBSD: intr.h,v 1.9 2014/12/28 13:03:18 aoyama Exp $ */
 /*
  * Copyright (C) 2000 Steve Murphree, Jr.
  * All rights reserved.
@@ -36,6 +36,7 @@
 #define IPL_NONE       0
 #define IPL_SOFTINT    1
 #define IPL_BIO                3
+#define IPL_AUDIO      4
 #define IPL_NET                4
 #define IPL_TTY                5
 #define IPL_VM         5
index 0a78721..5132b86 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: conf.c,v 1.24 2014/11/20 13:48:56 aoyama Exp $        */
+/*     $OpenBSD: conf.c,v 1.25 2014/12/28 13:03:18 aoyama Exp $        */
 
 /*-
  * Copyright (c) 1991 The Regents of the University of California.
@@ -54,6 +54,7 @@
 
 #include "ksyms.h"
 
+#include "audio.h"
 #include "lcd.h"
 #include "pcex.h"
 #include "siotty.h"
@@ -123,7 +124,7 @@ struct cdevsw       cdevsw[] =
        cdev_tun_init(NTUN,tun),        /* 23: network tunnel */
        cdev_notdef(),                  /* 24 was LKM */
        cdev_pcex_init(NPCEX, pcex),    /* 25: PC-9801 extension board slot */
-       cdev_notdef(),                  /* 26 */
+       cdev_audio_init(NAUDIO, audio), /* 26: generic audio I/O */
        cdev_notdef(),                  /* 27 */
        cdev_notdef(),                  /* 28 */
        cdev_notdef(),                  /* 29 */