diff -r 8529122615db sys/arch/mac68k/conf/GENERIC --- a/sys/arch/mac68k/conf/GENERIC Tue Oct 17 12:07:42 2023 +0000 +++ b/sys/arch/mac68k/conf/GENERIC Fri Oct 20 14:18:45 2023 +1100 @@ -287,7 +287,12 @@ # Audio Devices # On-board audio hardware -asc0 at obio? # ASC/EASC audio +# Only one of asc or ascaudio/audio/spkr/wsbell +#asc0 at obio? # ASC/EASC audio beeper +ascaudio* at obio? # ASC/EASC audio +audio* at audiobus? +spkr* at audio? # PC speaker (synthesized) +wsbell* at spkr? # Console beep # Pseudo-Devices diff -r 8529122615db sys/arch/mac68k/conf/files.mac68k --- a/sys/arch/mac68k/conf/files.mac68k Tue Oct 17 12:07:42 2023 +0000 +++ b/sys/arch/mac68k/conf/files.mac68k Fri Oct 20 14:18:45 2023 +1100 @@ -43,10 +43,16 @@ attach ams at adb file arch/mac68k/dev/ams.c ams needs-flag +# ASC (beeper) device asc attach asc at obio file arch/mac68k/obio/asc.c asc needs-flag +#ASC audio +device ascaudio: audiobus, auconv, mulaw, aurateconv, auvolconv +attach ascaudio at obio +file arch/mac68k/obio/ascaudio.c ascaudio needs-flag + device nubus { } attach nubus at mainbus file arch/mac68k/nubus/nubus.c nubus diff -r 8529122615db sys/arch/mac68k/conf/majors.mac68k --- a/sys/arch/mac68k/conf/majors.mac68k Tue Oct 17 12:07:42 2023 +0000 +++ b/sys/arch/mac68k/conf/majors.mac68k Fri Oct 20 14:18:45 2023 +1100 @@ -52,6 +52,7 @@ device-major wsfont char 53 wsfont device-major cpi char 54 cpi device-major sysmon char 55 sysmon +device-major audio char 56 audio #device-major obsolete char 98 obsolete (nsmb) --- /dev/null 2023-10-20 14:19:03.236463444 +1100 +++ src/sys/arch/mac68k/obio/ascaudiovar.h 2023-10-20 13:44:35.147397330 +1100 @@ -0,0 +1,134 @@ +/* $NetBSD$ */ + +/*- + * Copyright (c) 2017, 2023 Nathanial Sloss + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _SYS_ARCH_MAC68K_OBIO_ASCAUDIOVAR_H +#define _SYS_ARCH_MAC68K_OBIO_ASCAUDIOVAR_H + +#define ASCAUDIOUNIT(d) ((d) & 0x7) + +#define ASCIRQ 5 + +#define FIFO_A 0 +#define FIFO_B 0x400 +#define FIFO_LEN 0x400 + +#define VERLOC 0x800 + +#define ASCMODE 0x801 +#define MODESTOP 0 +#define MODEFIFO 1 +#define MODEWAVE 2 /* not in easc */ + +#define ASCTRL 0x807 +#define UNDERRUN __BIT(7) +#define STEREO __BIT(1) +#define ANAPWM __BIT(0) + +#define FIFOPARAM 0x803 +#define CLEARFIFO __BIT(7) +#define NONCOMP __BIT(1) +#define ROMCOMP __BIT(0) + +#define FIFOSTATUS 0x804 +#define A_HALF __BIT(0) +#define A_FULL __BIT(1) +#define B_HALF __BIT(2) +#define B_FULL __BIT(3) + +#define INTVOL 0x806 /* b2-b4 Int volume. b5-b7 ext. */ + +#define ASCRATE 0x802 +#define MACFREQ 0 /* 22254 Hz */ +#define F22KHZ 2 /* 22050 Hz */ +#define F44KHZ 3 /* 44100 Hz */ + +#define APLAYREC 0x80a +#define RECORDA __BIT(0) +#define REC22KHZ __BIT(1) + +#define ASCTEST 0x80f + +#define A_WRITEPTRHI 0xf00 +#define A_WRITEPTRLO 0xf01 +#define A_READPTRHI 0xf02 +#define A_READPTRLO 0xf03 +#define B_WRITEPTRHI 0xf20 +#define B_WRITEPTRLO 0xf21 +#define B_READPTRHI 0xf22 +#define B_READPTRLO 0xf23 + +#define A_LEFT_VOL 0xf06 +#define A_RIGHT_VOL 0xf07 +#define B_LEFT_VOL 0xf26 +#define B_RIGHT_VOL 0xf27 + +#define FIFOCTRLA 0xf08 +#define FIFOCTRLB 0xf28 +#define ENBCDXA __BIT(7) + +#define IRQA 0xf09 +#define IRQB 0xf29 +#define DISABLEIRQ __BIT(0) + +typedef struct ascaudio_softc { + device_t sc_dev; + bus_space_tag_t sc_tag; + bus_space_handle_t sc_handle; + int sc_open; + + device_t sc_audiodev; + struct audio_encoding_set *sc_encodings; + void *sc_intr; + void (*sc_pintr)(void *); + void *sc_pintrarg; + void (*sc_rintr)(void *); + void *sc_rintrarg; + + kmutex_t sc_lock; + kmutex_t sc_intr_lock; + + callout_t sc_pcallout; + callout_t sc_rcallout; + + uint8_t *sc_playbuf; + uint8_t *sc_wptr; + uint8_t *sc_putptr; + int sc_avail; + + uint8_t *sc_recbuf; + uint8_t *sc_rptr; + uint8_t *sc_getptr; + int sc_recavail; + + uint8_t sc_vol; + uint8_t sc_ver; + uint8_t sc_speakers; + uint sc_rate; +} ascaudio_softc_t; + +#endif /* !_SYS_ARCH_MAC68K_OBIO_ASCAUDIOVAR_H */ --- /dev/null 2023-10-20 14:19:12.368402712 +1100 +++ src/sys/arch/mac68k/obio/ascaudio.c 2023-10-20 13:50:06.782554056 +1100 @@ -0,0 +1,927 @@ +/* $NetBSD$ */ + +/*- + * Copyright (c) 2017, 2023 Nathanial Sloss + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* Based on pad(4) and asc(4) */ + +#include +__KERNEL_RCSID(0, "$NetBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include + +#define MAC68K_ASCAUDIO_BASE 0x50f14000 +#define MAC68K_IIFX_ASCAUDIO_BASE 0x50f10000 +#define MAC68K_ASCAUDIO_LEN 0x1000 + +#define BUFSIZE 32768 +#define PLAYBLKSIZE 8192 +#define RECBLKSIZE 8192 + +static int ascaudiomatch(device_t, cfdata_t, void *); +static void ascaudioattach(device_t, device_t, void *); + +CFATTACH_DECL_NEW(ascaudio, sizeof(struct ascaudio_softc), + ascaudiomatch, ascaudioattach, NULL, NULL); + +extern struct cfdriver ascaudio_cd; + +dev_type_open(ascaudioopen); +dev_type_close(ascaudioclose); +dev_type_read(ascaudioread); +dev_type_write(ascaudiowrite); +dev_type_ioctl(ascaudioioctl); + +const struct cdevsw ascaudio_cdevsw = { + .d_open = ascaudioopen, + .d_close = ascaudioclose, + .d_read = ascaudioread, + .d_write = ascaudiowrite, + .d_ioctl = ascaudioioctl, + .d_stop = nostop, + .d_tty = notty, + .d_poll = nopoll, + .d_mmap = nommap, + .d_kqfilter = nokqfilter, + .d_discard = nodiscard, + .d_flag = 0 +}; + +static int ascaudio_query_format(void *, struct audio_format_query *); +static int ascaudio_set_format(void *, int, + const audio_params_t *, const audio_params_t *, + audio_filter_reg_t *, audio_filter_reg_t *); +static int ascaudio_start_output(void *, void *, int, + void (*)(void *), void *); +static int ascaudio_start_input(void *, void *, int, + void (*)(void *), void *); +static int ascaudio_halt(void *); +static int ascaudio_set_port(void *, mixer_ctrl_t *); +static int ascaudio_get_port(void *, mixer_ctrl_t *); +static int ascaudio_getdev(void *, struct audio_device *); +static int ascaudio_query_devinfo(void *, mixer_devinfo_t *); +static int ascaudio_get_props(void *); +static int + ascaudio_round_blocksize(void *, int, int, const audio_params_t *); +static void ascaudio_get_locks(void *, kmutex_t **, kmutex_t **); +static void ascaudio_intr(void *); +static int ascaudio_intr_est(void *); +static void ascaudio_intr_enable(void); +static void ascaudio_done_output(void *); +static void ascaudio_done_input(void *); + +static const struct audio_hw_if ascaudio_hw_if = { + .query_format = ascaudio_query_format, + .set_format = ascaudio_set_format, + .start_output = ascaudio_start_output, + .start_input = ascaudio_start_input, + .halt_output = ascaudio_halt, + .halt_input = ascaudio_halt, + .set_port = ascaudio_set_port, + .get_port = ascaudio_get_port, + .getdev = ascaudio_getdev, + .query_devinfo = ascaudio_query_devinfo, + .get_props = ascaudio_get_props, + .round_blocksize = ascaudio_round_blocksize, + .get_locks = ascaudio_get_locks, +}; + +#define EASC_VER 0xb0 + +enum { + ASC_OUTPUT_CLASS, + ASC_INPUT_CLASS, + ASC_OUTPUT_MASTER_VOLUME, + ASC_INPUT_DAC_VOLUME, + ASC_ENUM_LAST, +}; + +static int +ascaudiomatch(device_t parent, cfdata_t cf, void *aux) +{ + struct obio_attach_args *oa = (struct obio_attach_args *)aux; + bus_addr_t addr; + bus_space_handle_t bsh; + int rval = 0; + + if (oa->oa_addr != (-1)) + addr = (bus_addr_t)oa->oa_addr; + else if (current_mac_model->machineid == MACH_MACTV) + return 0; + else if (current_mac_model->machineid == MACH_MACIIFX) + addr = (bus_addr_t)MAC68K_IIFX_ASCAUDIO_BASE; + else + addr = (bus_addr_t)MAC68K_ASCAUDIO_BASE; + + if (bus_space_map(oa->oa_tag, addr, MAC68K_ASCAUDIO_LEN, 0, &bsh)) + return (0); + + if (mac68k_bus_space_probe(oa->oa_tag, bsh, 0, 1)) { + rval = 1; + } else + rval = 0; + + bus_space_unmap(oa->oa_tag, bsh, MAC68K_ASCAUDIO_LEN); + + return rval; +} + +static void +ascaudioattach(device_t parent, device_t self, void *aux) +{ + struct ascaudio_softc *sc = device_private(self); + struct obio_attach_args *oa = (struct obio_attach_args *)aux; + bus_addr_t addr; + uint8_t tmp; + + sc->sc_dev = self; + sc->sc_tag = oa->oa_tag; + + if (oa->oa_addr != (-1)) + addr = (bus_addr_t)oa->oa_addr; + else if (current_mac_model->machineid == MACH_MACIIFX) + addr = (bus_addr_t)MAC68K_IIFX_ASCAUDIO_BASE; + else + addr = (bus_addr_t)MAC68K_ASCAUDIO_BASE; + if (bus_space_map(sc->sc_tag, addr, MAC68K_ASCAUDIO_LEN, 0, + &sc->sc_handle)) { + printf(": can't map memory space\n"); + return; + } + + sc->sc_playbuf = kmem_alloc(BUFSIZE, KM_SLEEP); + sc->sc_recbuf = kmem_alloc(BUFSIZE, KM_SLEEP); + sc->sc_rptr = sc->sc_recbuf; + sc->sc_getptr = sc->sc_recbuf; + sc->sc_wptr = sc->sc_playbuf; + sc->sc_putptr = sc->sc_playbuf; + + sc->sc_ver = bus_space_read_1(oa->oa_tag, sc->sc_handle, 0x800); + + tmp = bus_space_read_1(sc->sc_tag, sc->sc_handle, ASCRATE); + switch (tmp) { + case 2: + sc->sc_rate = 22050; + break; + case 3: + sc->sc_rate = 44100; + break; + default: + sc->sc_rate = 22254; + break; + } + + tmp = bus_space_read_1(sc->sc_tag, sc->sc_handle, ASCTRL); + if (tmp & STEREO) + sc->sc_speakers = 2; + else + sc->sc_speakers = 1; + + if (sc->sc_ver != EASC_VER) + printf(": Apple Sound Chip"); + else + printf(": Enhanced Apple Sound Chip"); + if (oa->oa_addr != (-1)) + printf(" at %x", oa->oa_addr); + printf("\n"); + + if (mac68k_machine.aux_interrupts) { + intr_establish(ascaudio_intr_est, sc, ASCIRQ); + } else { + via2_register_irq(VIA2_ASC, ascaudio_intr, sc); + } + ascaudio_intr_enable(); + + mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE); + mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_HIGH); + callout_init(&sc->sc_pcallout, CALLOUT_MPSAFE); + callout_setfunc(&sc->sc_pcallout, ascaudio_done_output, sc); + callout_init(&sc->sc_rcallout, CALLOUT_MPSAFE); + callout_setfunc(&sc->sc_rcallout, ascaudio_done_input, sc); + + sc->sc_vol = 255; + + sc->sc_audiodev = audio_attach_mi(&ascaudio_hw_if, sc, sc->sc_dev); + + if (!pmf_device_register(sc->sc_dev, NULL, NULL)) + aprint_error_dev(sc->sc_dev, + "couldn't establish power handler\n"); + + bus_space_write_1(sc->sc_tag, sc->sc_handle, ASCMODE, MODESTOP); + + if (sc->sc_ver != EASC_VER) + return; + + /* Disable CD-XA decompression for channel a */ + tmp = bus_space_read_1(sc->sc_tag, sc->sc_handle, FIFOCTRLA); + tmp &= ~ENBCDXA; + bus_space_write_1(sc->sc_tag, sc->sc_handle, FIFOCTRLA, tmp); + + /* Disable CD-XA decompression for channel b */ + tmp = bus_space_read_1(sc->sc_tag, sc->sc_handle, FIFOCTRLB); + tmp &= ~ENBCDXA; + bus_space_write_1(sc->sc_tag, sc->sc_handle, FIFOCTRLB, tmp); +} + +int +ascaudioopen(dev_t dev, int flag, int mode, struct lwp *l) +{ + struct ascaudio_softc *sc; + + sc = device_lookup_private(&ascaudio_cd, ASCAUDIOUNIT(dev)); + if (sc == NULL) + return (ENXIO); + if (sc->sc_open) + return (EBUSY); + sc->sc_open = 1; + + return (0); +} + +int +ascaudioclose(dev_t dev, int flag, int mode, struct lwp *l) +{ + struct ascaudio_softc *sc; + + sc = device_lookup_private(&ascaudio_cd, ASCAUDIOUNIT(dev)); + sc->sc_open = 0; + + return (0); +} + +int +ascaudioread(dev_t dev, struct uio *uio, int ioflag) +{ + return (ENXIO); +} + +int +ascaudiowrite(dev_t dev, struct uio *uio, int ioflag) +{ + return (ENXIO); +} + +int +ascaudioioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l) +{ + int error; +#ifdef not_yet + struct ascaudio_softc *sc; + int unit = ASCAUDIOUNIT(dev); + + sc = device_lookup_private(&ascaudio_cd, unit); +#endif + error = 0; + + switch (cmd) { + default: + error = EINVAL; + break; + } + return (error); +} + +#define ASCAUDIO_NFORMATS 2 +static int +ascaudio_query_format(void *opaque, struct audio_format_query *ae) +{ + struct ascaudio_softc *sc = opaque; + + const struct audio_format asc_formats[ASCAUDIO_NFORMATS] = { + { .mode = AUMODE_PLAY, + .encoding = AUDIO_ENCODING_SLINEAR_LE, + .validbits = 8, + .precision = 8, + .channels = sc->sc_speakers, + .channel_mask = sc->sc_speakers == 2 ? AUFMT_STEREO : + AUFMT_MONAURAL, + .frequency_type = 1, + .frequency = { sc->sc_rate }, }, + { .mode = AUMODE_RECORD, + .encoding = AUDIO_ENCODING_SLINEAR_LE, + .validbits = 8, + .precision = 8, + .channels = 1, + .channel_mask = AUFMT_MONAURAL, + .frequency_type = 1, + .frequency = { 11025 }, } + }; + + return audio_query_format(asc_formats, ASCAUDIO_NFORMATS, ae); +} + +static int +ascaudio_set_format(void *opaque, int setmode, + const audio_params_t *play, const audio_params_t *rec, + audio_filter_reg_t *pfil, audio_filter_reg_t *rfil) +{ + struct ascaudio_softc *sc = opaque; + + KASSERT(mutex_owned(&sc->sc_lock)); + + return 0; +} + +static int +ascaudio_start_output(void *opaque, void *block, int blksize, + void (*intr)(void *), void *intrarg) +{ + struct ascaudio_softc *sc; + uint8_t *loc, tmp; + int total; + + sc = (struct ascaudio_softc *)opaque; + if (!sc) + return (ENODEV); + + sc->sc_pintr = intr; + sc->sc_pintrarg = intrarg; + + + loc = block; + if (bus_space_read_1(sc->sc_tag, sc->sc_handle, ASCMODE) != + MODEFIFO) { + bus_space_write_1(sc->sc_tag, sc->sc_handle, ASCMODE, MODESTOP); + + if (sc->sc_ver == EASC_VER) { + /* disable interrupts channel a */ + bus_space_write_1(sc->sc_tag, sc->sc_handle, IRQA, + DISABLEIRQ); + /* Disable interrupts channel b */ + bus_space_write_1(sc->sc_tag, sc->sc_handle, IRQB, + DISABLEIRQ); + } + + bus_space_write_1(sc->sc_tag, sc->sc_handle, ASCTEST, 0); + bus_space_write_1(sc->sc_tag, sc->sc_handle, FIFOPARAM, + CLEARFIFO); + tmp = 0; + bus_space_write_1(sc->sc_tag, sc->sc_handle, APLAYREC, tmp); + + if (sc->sc_ver == EASC_VER) { + /* enable interrupts channel a */ + bus_space_write_1(sc->sc_tag, sc->sc_handle, IRQA, 0); + + /* enable interrupts channel b */ + bus_space_write_1(sc->sc_tag, sc->sc_handle, IRQB, 0); + } + } + + /* set the volume */ + tmp = sc->sc_vol >> 5; + /* set volume for channel b left and right speakers */ + if (sc->sc_ver == EASC_VER) { + bus_space_write_1(sc->sc_tag, sc->sc_handle, A_LEFT_VOL, tmp); + bus_space_write_1(sc->sc_tag, sc->sc_handle, B_LEFT_VOL, tmp); + bus_space_write_1(sc->sc_tag, sc->sc_handle, A_RIGHT_VOL, tmp); + bus_space_write_1(sc->sc_tag, sc->sc_handle, B_RIGHT_VOL, tmp); + } else + bus_space_write_1(sc->sc_tag, sc->sc_handle, INTVOL, tmp << 5); + + total = blksize; + if (sc->sc_putptr + blksize > sc->sc_playbuf + BUFSIZE) + total = sc->sc_playbuf + BUFSIZE - sc->sc_putptr; + + memcpy(sc->sc_putptr, loc, total); + sc->sc_putptr += total; + loc += total; + + total = PLAYBLKSIZE - total; + if (total) { + sc->sc_putptr = sc->sc_playbuf; + memcpy(sc->sc_playbuf, loc, total); + sc->sc_putptr += total; + } + + mutex_enter(&sc->sc_intr_lock); + sc->sc_avail += blksize; + if (sc->sc_avail > BUFSIZE) + sc->sc_avail = BUFSIZE; + mutex_exit(&sc->sc_intr_lock); + + /* start fifo playback */ + bus_space_write_1(sc->sc_tag, sc->sc_handle, ASCMODE, MODEFIFO); + + return 0; +} + +static int +ascaudio_start_input(void *opaque, void *block, int blksize, + void (*intr)(void *), void *intrarg) +{ + struct ascaudio_softc *sc; + uint8_t tmp; + int total; + + sc = (struct ascaudio_softc *)opaque; + if (!sc) + return (ENODEV); + + KASSERT(mutex_owned(&sc->sc_lock)); + + + uint8_t *loc; + loc = block; + + sc->sc_rintr = intr; + sc->sc_rintrarg = intrarg; + + if (bus_space_read_1(sc->sc_tag, sc->sc_handle, ASCMODE) != + MODEFIFO) { + bus_space_write_1(sc->sc_tag, sc->sc_handle, ASCMODE, MODESTOP); + + if (sc->sc_ver == EASC_VER) { + /* disable interrupts channel a */ + bus_space_write_1(sc->sc_tag, sc->sc_handle, IRQA, + DISABLEIRQ); + /* Disable interrupts channel b */ + bus_space_write_1(sc->sc_tag, sc->sc_handle, IRQB, + DISABLEIRQ); + } + + bus_space_write_1(sc->sc_tag, sc->sc_handle, ASCTEST, 0); + bus_space_write_1(sc->sc_tag, sc->sc_handle, FIFOPARAM, + CLEARFIFO); + tmp = RECORDA; + bus_space_write_1(sc->sc_tag, sc->sc_handle, APLAYREC, tmp); + + if (sc->sc_ver == EASC_VER) { + /* enable interrupts channel a */ + bus_space_write_1(sc->sc_tag, sc->sc_handle, IRQA, 0); + + /* enable interrupts channel b */ + bus_space_write_1(sc->sc_tag, sc->sc_handle, IRQB, 0); + } + + /* start fifo playback */ + bus_space_write_1(sc->sc_tag, sc->sc_handle, ASCMODE, MODEFIFO); + + return 0; + } + + /* set the volume */ + tmp = sc->sc_vol >> 5; + /* set volume for channel b left and right speakers */ + if (sc->sc_ver == EASC_VER) { + bus_space_write_1(sc->sc_tag, sc->sc_handle, A_LEFT_VOL, tmp); + bus_space_write_1(sc->sc_tag, sc->sc_handle, B_LEFT_VOL, tmp); + bus_space_write_1(sc->sc_tag, sc->sc_handle, A_RIGHT_VOL, tmp); + bus_space_write_1(sc->sc_tag, sc->sc_handle, B_RIGHT_VOL, tmp); + } else + bus_space_write_1(sc->sc_tag, sc->sc_handle, INTVOL, tmp << 5); + + total = RECBLKSIZE; + if (sc->sc_getptr + blksize > sc->sc_recbuf + BUFSIZE) + total = sc->sc_recbuf + BUFSIZE - sc->sc_getptr; + + memcpy(loc, sc->sc_getptr, total); + sc->sc_getptr += total; + loc += total; + + if (sc->sc_getptr >= sc->sc_recbuf + BUFSIZE) + sc->sc_getptr = sc->sc_recbuf; + + total = blksize - total; + if (total) { + memcpy(loc, sc->sc_getptr, total); + sc->sc_getptr += total; + } + + mutex_enter(&sc->sc_intr_lock); + sc->sc_recavail -= blksize; + mutex_exit(&sc->sc_intr_lock); + + return 0; +} + +static int +ascaudio_halt(void *opaque) +{ + ascaudio_softc_t *sc; + + sc = (ascaudio_softc_t *)opaque; + + KASSERT(mutex_owned(&sc->sc_lock)); + + + sc->sc_pintr = NULL; + sc->sc_pintrarg = NULL; + sc->sc_rintr = NULL; + sc->sc_rintrarg = NULL; + + sc->sc_avail = 0; + sc->sc_recavail = 0; + + callout_halt(&sc->sc_pcallout, &sc->sc_lock); + callout_halt(&sc->sc_rcallout, &sc->sc_lock); + + bus_space_write_1(sc->sc_tag, sc->sc_handle, ASCMODE, MODESTOP); + + bus_space_write_1(sc->sc_tag, sc->sc_handle, FIFOPARAM, CLEARFIFO); + + sc->sc_rptr = sc->sc_recbuf; + sc->sc_getptr = sc->sc_recbuf; + sc->sc_wptr = sc->sc_playbuf; + sc->sc_putptr = sc->sc_playbuf; + + if (sc->sc_ver != EASC_VER) + return 0; + + /* disable interrupts channel a */ + bus_space_write_1(sc->sc_tag, sc->sc_handle, IRQA, DISABLEIRQ); + /* disable interrupts channel b */ + bus_space_write_1(sc->sc_tag, sc->sc_handle, IRQB, DISABLEIRQ); + + return 0; +} + +static int +ascaudio_getdev(void *opaque, struct audio_device *ret) +{ + strlcpy(ret->name, "Apple ASC Audio", sizeof(ret->name)); + strlcpy(ret->version, osrelease, sizeof(ret->version)); + strlcpy(ret->config, "ascaudio", sizeof(ret->config)); + + return 0; +} + +static int +ascaudio_set_port(void *opaque, mixer_ctrl_t *mc) +{ + struct ascaudio_softc *sc = opaque; + + KASSERT(mutex_owned(&sc->sc_lock)); + + switch (mc->dev) { + case ASC_OUTPUT_MASTER_VOLUME: + case ASC_INPUT_DAC_VOLUME: + if (mc->un.value.num_channels != 1) + return EINVAL; + sc->sc_vol = mc->un.value.level[AUDIO_MIXER_LEVEL_MONO]; + return 0; + } + + return ENXIO; +} + +static int +ascaudio_get_port(void *opaque, mixer_ctrl_t *mc) +{ + struct ascaudio_softc *sc = opaque; + + KASSERT(mutex_owned(&sc->sc_lock)); + + switch (mc->dev) { + case ASC_OUTPUT_MASTER_VOLUME: + case ASC_INPUT_DAC_VOLUME: + if (mc->un.value.num_channels != 1) + return EINVAL; + mc->un.value.level[AUDIO_MIXER_LEVEL_MONO] = sc->sc_vol; + return 0; + } + + return ENXIO; +} + +static int +ascaudio_query_devinfo(void *opaque, mixer_devinfo_t *di) +{ + ascaudio_softc_t *sc __diagused; + + sc = (ascaudio_softc_t *)opaque; + + KASSERT(mutex_owned(&sc->sc_lock)); + + switch (di->index) { + case ASC_OUTPUT_CLASS: + di->mixer_class = ASC_OUTPUT_CLASS; + strcpy(di->label.name, AudioCoutputs); + di->type = AUDIO_MIXER_CLASS; + di->next = di->prev = AUDIO_MIXER_LAST; + return 0; + case ASC_INPUT_CLASS: + di->mixer_class = ASC_INPUT_CLASS; + strcpy(di->label.name, AudioCinputs); + di->type = AUDIO_MIXER_CLASS; + di->next = di->prev = AUDIO_MIXER_LAST; + return 0; + case ASC_OUTPUT_MASTER_VOLUME: + di->mixer_class = ASC_OUTPUT_CLASS; + strcpy(di->label.name, AudioNmaster); + di->type = AUDIO_MIXER_VALUE; + di->next = di->prev = AUDIO_MIXER_LAST; + di->un.v.num_channels = 1; + strcpy(di->un.v.units.name, AudioNvolume); + return 0; + case ASC_INPUT_DAC_VOLUME: + di->mixer_class = ASC_INPUT_CLASS; + strcpy(di->label.name, AudioNdac); + di->type = AUDIO_MIXER_VALUE; + di->next = di->prev = AUDIO_MIXER_LAST; + di->un.v.num_channels = 1; + strcpy(di->un.v.units.name, AudioNvolume); + return 0; + } + + return ENXIO; +} + +static int +ascaudio_get_props(void *opaque) +{ +#if 0 + ascaudio_softc_t *sc __diagused; + + sc = (ascaudio_softc_t *)opaque; +#endif + + return AUDIO_PROP_PLAYBACK | AUDIO_PROP_CAPTURE | + AUDIO_PROP_INDEPENDENT; +} + +static int +ascaudio_round_blocksize(void *opaque, int blksize, int mode, + const audio_params_t *p) +{ + ascaudio_softc_t *sc __diagused; + + sc = (ascaudio_softc_t *)opaque; + KASSERT(mutex_owned(&sc->sc_lock)); + + if (mode == AUMODE_PLAY) + return PLAYBLKSIZE; + else + return RECBLKSIZE; +} + +static void +ascaudio_get_locks(void *opaque, kmutex_t **intr, kmutex_t **thread) +{ + ascaudio_softc_t *sc; + + sc = (ascaudio_softc_t *)opaque; + + *intr = &sc->sc_intr_lock; + *thread = &sc->sc_lock; +} + +static int +ascaudio_intr_est(void *arg) +{ + ascaudio_intr(arg); + + return 0; +} + +static void +ascaudio_intr(void *arg) +{ + struct ascaudio_softc *sc = arg; + uint8_t status, val; + int i; + + if (!sc) + return; + + if (!sc->sc_pintr && !sc->sc_rintr) + return; + + mutex_enter(&sc->sc_intr_lock); + status = bus_space_read_1(sc->sc_tag, sc->sc_handle, FIFOSTATUS); + while (sc->sc_rintr && ((status & (A_FULL | A_HALF)) == 0)) { + for (i = 0; i < 0x200; i++) { + val = bus_space_read_1(sc->sc_tag, sc->sc_handle, + FIFO_A); +#if 0 + val = bus_space_read_1(sc->sc_tag, sc->sc_handle, + FIFO_B); +#endif + val ^= 0x80; + *sc->sc_rptr++ = val; + } + if (sc->sc_rptr >= sc->sc_recbuf + BUFSIZE) + sc->sc_rptr = sc->sc_recbuf; + + sc->sc_recavail += 0x200; + if (sc->sc_recavail > BUFSIZE) + sc->sc_recavail = BUFSIZE; + + status = bus_space_read_1(sc->sc_tag, sc->sc_handle, + FIFOSTATUS); + } + + status = bus_space_read_1(sc->sc_tag, sc->sc_handle, FIFOSTATUS); + while (status & (B_FULL | B_HALF)) { + if (sc->sc_avail < 0x200) { + for (i = 0; i < 0x200; i++) { + if (sc->sc_pintr) { + bus_space_write_1(sc->sc_tag, + sc->sc_handle, FIFO_A, 0x80); + } + bus_space_write_1(sc->sc_tag, + sc->sc_handle, FIFO_B, 0x80); + } + } else { + for (i = 0; i < 0x200; i++) { + val = *sc->sc_wptr++; + val ^= 0x80; + bus_space_write_1(sc->sc_tag, + sc->sc_handle, FIFO_A, val); + bus_space_write_1(sc->sc_tag, + sc->sc_handle, FIFO_B, val); + } + sc->sc_avail -= 0x200; + if (sc->sc_wptr >= sc->sc_playbuf + BUFSIZE) + sc->sc_wptr = sc->sc_playbuf; + } + status = bus_space_read_1(sc->sc_tag, sc->sc_handle, + FIFOSTATUS); + } + mutex_exit(&sc->sc_intr_lock); + +#if 1 + if (sc->sc_pintr && (sc->sc_avail <= BUFSIZE - PLAYBLKSIZE)) + callout_schedule(&sc->sc_pcallout, 0); + + if (sc->sc_rintr && (sc->sc_recavail >= RECBLKSIZE)) + callout_schedule(&sc->sc_rcallout, 0); +#endif +} + +static void +ascaudio_intr_enable(void) +{ + int s; + + s = splhigh(); + if (VIA2 == VIA2OFF) + via2_reg(vIER) = 0x80 | V2IF_ASC; + else + via2_reg(rIER) = 0x80 | V2IF_ASC; + splx(s); +} + +static void +ascaudio_done_output(void *arg) +{ + struct ascaudio_softc *sc = arg; + + mutex_enter(&sc->sc_lock); + if (sc->sc_pintr) + (*sc->sc_pintr)(sc->sc_pintrarg); + mutex_exit(&sc->sc_lock); +} + +static void +ascaudio_done_input(void *arg) +{ + struct ascaudio_softc *sc = arg; + + mutex_enter(&sc->sc_lock); + if (sc->sc_rintr) + (*sc->sc_rintr)(sc->sc_rintrarg); + mutex_exit(&sc->sc_lock); +} + +#ifdef _MODULE + +MODULE(MODULE_CLASS_DRIVER, ascaudio, "audio"); + +static const struct cfiattrdata audiobuscf_iattrdata = { + "audiobus", 0, { { NULL, NULL, 0 }, } +}; +static const struct cfiattrdata * const ascaudio_attrs[] = { + &audiobuscf_iattrdata, NULL +}; + +CFDRIVER_DECL(ascaudio, DV_DULL, ascaud_attrs); +extern struct cfattach ascaudio_ca; +static int ascaudioloc[] = { -1, -1 }; + +static struct cfdata ascaudio_cfdata[] = { + { + .cf_name = "ascaudio", + .cf_atname = "ascaudio", + .cf_unit = 0, + .cf_fstate = FSTATE_STAR, + .cf_loc = ascaudioloc, + .cf_flags = 0, + .cf_pspec = NULL, + }, + { NULL, NULL, 0, 0, NULL, 0, NULL } +}; + +static int +ascaudio_modcmd(modcmd_t cmd, void *arg) +{ + devmajor_t cmajor = NODEVMAJOR, bmajor = NODEVMAJOR; + int error; + + switch (cmd) { + case MODULE_CMD_INIT: + error = config_cfdriver_attach(&ascaudio_cd); + if (error) { + return error; + } + + error = config_cfattach_attach(ascaudio_cd.cd_name, &ascaud_ca); + if (error) { + config_cfdriver_detach(&ascaudio_cd); + aprint_error("%s: unable to register cfattach\n", + ascaudio_cd.cd_name); + + return error; + } + + error = config_cfdata_attach(ascaudio_cfdata, 1); + if (error) { + config_cfattach_detach(ascaudio_cd.cd_name, &ascaud_ca); + config_cfdriver_detach(&ascaudio_cd); + aprint_error("%s: unable to register cfdata\n", + ascaudio_cd.cd_name); + + return error; + } + + error = devsw_attach(ascaudio_cd.cd_name, NULL, &bmajor, + &ascaudio_cdevsw, &cmajor); + if (error) { + error = config_cfdata_detach(ascaudio_cfdata); + if (error) { + return error; + } + config_cfattach_detach(ascaudio_cd.cd_name, &ascaud_ca); + config_cfdriver_detach(&ascaudio_cd); + aprint_error("%s: unable to register devsw\n", + ascaudio_cd.cd_name); + + return error; + } + + (void)config_attach_pseudo(ascaudio_cfdata); + + return 0; + case MODULE_CMD_FINI: + error = config_cfdata_detach(ascaudio_cfdata); + if (error) { + return error; + } + + config_cfattach_detach(ascaudio_cd.cd_name, &ascaud_ca); + config_cfdriver_detach(&ascaudio_cd); + devsw_detach(NULL, &ascaudio_cdevsw); + + return 0; + default: + return ENOTTY; + } +} + +#endif