Home | History | Annotate | Line # | Download | only in obio
      1 /* $NetBSD: ascaudio.c,v 1.23 2026/09/16 02:55:04 nat Exp $ */
      2 
      3 /*-
      4  * Copyright (c) 2017, 2023, 2025 Nathanial Sloss <nathanialsloss (at) yahoo.com.au>
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     17  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     18  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     26  * POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 
     29 /* Based on pad(4) and asc(4) */
     30 
     31 #include <sys/cdefs.h>
     32 __KERNEL_RCSID(0, "$NetBSD: ascaudio.c,v 1.23 2026/09/16 02:55:04 nat Exp $");
     33 
     34 #include <sys/types.h>
     35 #include <sys/param.h>
     36 #include <sys/conf.h>
     37 #include <sys/buf.h>
     38 #include <sys/kauth.h>
     39 #include <sys/kmem.h>
     40 #include <sys/kernel.h>
     41 #include <sys/device.h>
     42 #include <sys/proc.h>
     43 #include <sys/audioio.h>
     44 #include <sys/module.h>
     45 #include <sys/atomic.h>
     46 
     47 #include <uvm/uvm_extern.h>
     48 
     49 #include <dev/audio/audio_if.h>
     50 #include <dev/audio/audiovar.h>
     51 
     52 #include <machine/autoconf.h>
     53 #include <machine/cpu.h>
     54 #include <machine/bus.h>
     55 #include <machine/viareg.h>
     56 
     57 #include <mac68k/dev/pm_direct.h>
     58 #include <mac68k/obio/ascaudiovar.h>
     59 #include <mac68k/obio/ascreg.h>
     60 #include <mac68k/obio/obiovar.h>
     61 
     62 #define	MAC68K_ASCAUDIO_BASE		0x50f14000
     63 #define	MAC68K_IIFX_ASCAUDIO_BASE	0x50f10000
     64 #define	MAC68K_ASCAUDIO_LEN		0x2000
     65 
     66 #define BUFSIZE 			32768
     67 #define PLAYBLKUNIT			1024	/* one FIFO half: 512 frames */
     68 #define PLAYBLKMIN			2048	/* 92 ms */
     69 #define PLAYBLKSIZE			8192	/* 368 ms, the cap */
     70 #define RECBLKSIZE			1024
     71 
     72 #define ASC_VIA_CLR_INTR()     via_reg(VIA2, vIFR) = V2IF_ASC
     73 
     74 static int	ascaudiomatch(device_t, cfdata_t, void *);
     75 static void	ascaudioattach(device_t, device_t, void *);
     76 
     77 CFATTACH_DECL_NEW(ascaudio, sizeof(struct ascaudio_softc),
     78     ascaudiomatch, ascaudioattach, NULL, NULL);
     79 
     80 extern struct cfdriver ascaudio_cd;
     81 
     82 dev_type_open(ascaudioopen);
     83 dev_type_close(ascaudioclose);
     84 dev_type_read(ascaudioread);
     85 dev_type_write(ascaudiowrite);
     86 dev_type_ioctl(ascaudioioctl);
     87 
     88 const struct cdevsw ascaudio_cdevsw = {
     89 	.d_open = ascaudioopen,
     90 	.d_close = ascaudioclose,
     91 	.d_read = ascaudioread,
     92 	.d_write = ascaudiowrite,
     93 	.d_ioctl = ascaudioioctl,
     94 	.d_stop = nostop,
     95 	.d_tty = notty,
     96 	.d_poll = nopoll,
     97 	.d_mmap = nommap,
     98 	.d_kqfilter = nokqfilter,
     99 	.d_discard = nodiscard,
    100 	.d_flag = 0
    101 };
    102 
    103 static int	ascaudio_query_format(void *, struct audio_format_query *);
    104 static int	ascaudio_set_format(void *, int,
    105 		    const audio_params_t *, const audio_params_t *,
    106 		    audio_filter_reg_t *, audio_filter_reg_t *);
    107 static int	ascaudio_start_output(void *, void *, int,
    108 				    void (*)(void *), void *);
    109 static int	ascaudio_start_input(void *, void *, int,
    110 				   void (*)(void *), void *);
    111 static int	ascaudio_halt_input(void *);
    112 static int	ascaudio_halt_output(void *);
    113 static int	ascaudio_set_port(void *, mixer_ctrl_t *);
    114 static int	ascaudio_get_port(void *, mixer_ctrl_t *);
    115 static int	ascaudio_getdev(void *, struct audio_device *);
    116 static int	ascaudio_query_devinfo(void *, mixer_devinfo_t *);
    117 static int	ascaudio_get_props(void *);
    118 static int
    119 	    ascaudio_round_blocksize(void *, int, int, const audio_params_t *);
    120 static void	ascaudio_get_locks(void *, kmutex_t **, kmutex_t **);
    121 static void 	ascaudio_intr(void *);
    122 static int 	ascaudio_intr_est(void *);
    123 static void	ascaudio_intr_enable(void);
    124 static void	ascaudio_done_output(void *);
    125 static void	ascaudio_done_input(void *);
    126 static void	configure_dfac(uint8_t);
    127 
    128 static const struct audio_hw_if ascaudio_hw_if = {
    129 	.query_format	 = ascaudio_query_format,
    130 	.set_format	 = ascaudio_set_format,
    131 	.start_output	 = ascaudio_start_output,
    132 	.start_input	 = ascaudio_start_input,
    133 	.halt_output	 = ascaudio_halt_output,
    134 	.halt_input	 = ascaudio_halt_input,
    135 	.set_port	 = ascaudio_set_port,
    136 	.get_port	 = ascaudio_get_port,
    137 	.getdev		 = ascaudio_getdev,
    138 	.query_devinfo	 = ascaudio_query_devinfo,
    139 	.get_props 	 = ascaudio_get_props,
    140 	.round_blocksize = ascaudio_round_blocksize,
    141 	.get_locks	 = ascaudio_get_locks,
    142 };
    143 
    144 enum {
    145 	ASC_OUTPUT_CLASS,
    146 	ASC_INPUT_CLASS,
    147 	ASC_OUTPUT_MASTER_VOLUME,
    148 	ASC_INPUT_DAC_VOLUME,
    149 	ASC_ENUM_LAST,
    150 };
    151 
    152 static int
    153 ascaudiomatch(device_t parent, cfdata_t cf, void *aux)
    154 {
    155 	struct obio_attach_args *oa = (struct obio_attach_args *)aux;
    156 	bus_addr_t addr;
    157 	bus_space_handle_t bsh;
    158 	int rval = 0;
    159 
    160 	if (oa->oa_addr != (-1))
    161 		addr = (bus_addr_t)oa->oa_addr;
    162 	else if (current_mac_model->machineid == MACH_MACTV)
    163 		return 0;
    164 	else if (current_mac_model->machineid == MACH_MACIIFX)
    165 		addr = (bus_addr_t)MAC68K_IIFX_ASCAUDIO_BASE;
    166 	else
    167 		addr = (bus_addr_t)MAC68K_ASCAUDIO_BASE;
    168 
    169 	if (bus_space_map(oa->oa_tag, addr, MAC68K_ASCAUDIO_LEN, 0, &bsh))
    170 		return (0);
    171 
    172 	if (mac68k_bus_space_probe(oa->oa_tag, bsh, 0, 1)) {
    173 		rval = 1;
    174 	} else
    175 		rval = 0;
    176 
    177 	bus_space_unmap(oa->oa_tag, bsh, MAC68K_ASCAUDIO_LEN);
    178 
    179 	return rval;
    180 }
    181 
    182 static void
    183 ascaudioattach(device_t parent, device_t self, void *aux)
    184 {
    185 	struct ascaudio_softc *sc = device_private(self);
    186 	struct obio_attach_args *oa = (struct obio_attach_args *)aux;
    187 	bus_addr_t addr;
    188 	uint8_t tmp;
    189 
    190 	sc->sc_dev = self;
    191 	sc->sc_tag = oa->oa_tag;
    192 
    193 	if (oa->oa_addr != (-1))
    194 		addr = (bus_addr_t)oa->oa_addr;
    195 	else if (current_mac_model->machineid == MACH_MACIIFX)
    196 		addr = (bus_addr_t)MAC68K_IIFX_ASCAUDIO_BASE;
    197 	else
    198 		addr = (bus_addr_t)MAC68K_ASCAUDIO_BASE;
    199 	if (bus_space_map(sc->sc_tag, addr, MAC68K_ASCAUDIO_LEN, 0,
    200 	    &sc->sc_handle)) {
    201 		printf(": can't map memory space\n");
    202 		return;
    203 	}
    204 
    205 	/* Pull in the options flags. */
    206 	sc->sc_options = ((device_cfdata(self)->cf_flags) &
    207 			    ASCAUDIO_OPTIONS_MASK);
    208 
    209 	sc->sc_playbuf = kmem_alloc(BUFSIZE, KM_SLEEP);
    210 	sc->sc_recbuf = kmem_alloc(BUFSIZE, KM_SLEEP);
    211 	sc->sc_rptr = sc->sc_recbuf;
    212 	sc->sc_getptr = sc->sc_recbuf;
    213 	sc->sc_wptr = sc->sc_playbuf;
    214 	sc->sc_putptr = sc->sc_playbuf;
    215 
    216 	bus_space_write_1(sc->sc_tag, sc->sc_handle, ASCMODE, MODESTOP);
    217 
    218 	sc->sc_ver = bus_space_read_1(oa->oa_tag, sc->sc_handle, 0x800);
    219 
    220 	if (sc->sc_options & HIGHQUALITY) {
    221 		tmp = bus_space_read_1(sc->sc_tag, sc->sc_handle, ASCRATE);
    222 		switch (tmp) {
    223 			case 2:
    224 				sc->sc_rate = 22050;
    225 				break;
    226 			case 3:
    227 				sc->sc_rate = 44100;
    228 				break;
    229 			default:
    230 				sc->sc_rate = 22254;
    231 				break;
    232 		}
    233 
    234 		tmp = bus_space_read_1(sc->sc_tag, sc->sc_handle, ASCTRL);
    235 		if (tmp & STEREO)
    236 			sc->sc_speakers = 2;
    237 		else
    238 			sc->sc_speakers = 1;
    239 
    240 	} else {
    241 		__USE(tmp);
    242 		sc->sc_rate = 22254;
    243 		sc->sc_speakers = 1;
    244 	}
    245 
    246 	if (sc->sc_options & LOWQUALITY) {
    247 		sc->sc_slowcpu = true;
    248 		if (sc->sc_slowcpu)
    249 			sc->sc_rate /= 2;
    250 	}
    251 
    252 	if (sc->sc_ver == EASC_VER || sc->sc_ver == EASC_VER2)
    253 		printf(": Enhanced Apple Sound Chip");
    254 	else
    255 		printf(": Apple Sound Chip");
    256 
    257 	if (oa->oa_addr != (-1))
    258 		printf(" at %x", oa->oa_addr);
    259 	printf("\n");
    260 
    261 	bus_space_write_1(sc->sc_tag, sc->sc_handle, ASCMODE, MODESTOP);
    262 
    263 	mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE);
    264 	mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_AUDIO);
    265 	callout_init(&sc->sc_pcallout, CALLOUT_MPSAFE);
    266 	callout_setfunc(&sc->sc_pcallout, ascaudio_done_output, sc);
    267 	callout_init(&sc->sc_rcallout, CALLOUT_MPSAFE);
    268 	callout_setfunc(&sc->sc_rcallout, ascaudio_done_input, sc);
    269 
    270 	/* The handler takes sc_intr_lock, so it goes live only now. */
    271 	if (mac68k_machine.aux_interrupts) {
    272 		intr_establish(ascaudio_intr_est, sc, ASCIRQ);
    273 	} else {
    274 		via2_register_irq(VIA2_ASC, ascaudio_intr, sc);
    275 	}
    276 	ascaudio_intr_enable();
    277 
    278 	sc->sc_vol = 180;
    279 	sc->sc_recvol = 255;
    280 
    281 	sc->sc_audiodev = audio_attach_mi(&ascaudio_hw_if, sc, sc->sc_dev);
    282 
    283 	if (!pmf_device_register(sc->sc_dev, NULL, NULL))
    284 		aprint_error_dev(sc->sc_dev,
    285 		    "couldn't establish power handler\n");
    286 
    287 	if (sc->sc_ver != EASC_VER && sc->sc_ver != EASC_VER2)
    288 		return;
    289 
    290 	if (sc->sc_options & HIGHQUALITY)
    291 		tmp = CDQUALITY;
    292 	else
    293 		tmp = MACDEFAULTS;
    294 
    295 	bus_space_write_1(sc->sc_tag, sc->sc_handle, FIFOCTRLA, tmp);
    296 	bus_space_write_1(sc->sc_tag, sc->sc_handle, FIFOCTRLB, tmp);
    297 
    298 }
    299 
    300 int
    301 ascaudioopen(dev_t dev, int flag, int mode, struct lwp *l)
    302 {
    303 	struct ascaudio_softc *sc;
    304 
    305 	sc = device_lookup_private(&ascaudio_cd, ASCAUDIOUNIT(dev));
    306 	if (sc == NULL)
    307 		return (ENXIO);
    308 	if (sc->sc_open)
    309 		return (EBUSY);
    310 	sc->sc_open = 1;
    311 
    312 	return (0);
    313 }
    314 
    315 int
    316 ascaudioclose(dev_t dev, int flag, int mode, struct lwp *l)
    317 {
    318 	struct ascaudio_softc *sc;
    319 
    320 	sc = device_lookup_private(&ascaudio_cd, ASCAUDIOUNIT(dev));
    321 	sc->sc_open = 0;
    322 
    323 	return (0);
    324 }
    325 
    326 int
    327 ascaudioread(dev_t dev, struct uio *uio, int ioflag)
    328 {
    329 	return (ENXIO);
    330 }
    331 
    332 int
    333 ascaudiowrite(dev_t dev, struct uio *uio, int ioflag)
    334 {
    335 	return (ENXIO);
    336 }
    337 
    338 int
    339 ascaudioioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
    340 {
    341 	int error;
    342 #ifdef notyet
    343 	struct ascaudio_softc *sc;
    344 	int unit = ASCAUDIOUNIT(dev);
    345 
    346 	sc = device_lookup_private(&ascaudio_cd, unit);
    347 #endif
    348 	error = 0;
    349 
    350 	switch (cmd) {
    351 	default:
    352 		error = EINVAL;
    353 		break;
    354 	}
    355 	return (error);
    356 }
    357 
    358 #define ASCAUDIO_NFORMATS	3
    359 static int
    360 ascaudio_query_format(void *opaque, struct audio_format_query *ae)
    361 {
    362 	struct ascaudio_softc *sc = opaque;
    363 
    364 	const struct audio_format asc_formats[ASCAUDIO_NFORMATS] = {
    365 	      { .mode		= AUMODE_PLAY,
    366 		.encoding	= AUDIO_ENCODING_SLINEAR_BE,
    367 		.validbits	= 8,
    368 		.precision	= 8,
    369 		.channels	= sc->sc_speakers,
    370 		.channel_mask	= sc->sc_speakers == 2 ? AUFMT_STEREO :
    371 		    AUFMT_MONAURAL,
    372 		.frequency_type	= 1,
    373 		.frequency	= { sc->sc_rate }, },
    374 	      { .mode		= AUMODE_RECORD,
    375 		.encoding	= AUDIO_ENCODING_SLINEAR_BE,
    376 		.validbits	= 8,
    377 		.precision	= 8,
    378 		.channels	= 1,
    379 		.channel_mask	= AUFMT_MONAURAL,
    380 		.frequency_type	= 1,
    381 		.frequency	= { 11025 }, },
    382 	      { .mode		= AUMODE_RECORD,
    383 		.encoding	= AUDIO_ENCODING_SLINEAR_BE,
    384 		.validbits	= 8,
    385 		.precision	= 8,
    386 		.channels	= 1,
    387 		.channel_mask	= AUFMT_MONAURAL,
    388 		.frequency_type	= 1,
    389 		.frequency	= { 22050 }, }
    390 	};
    391 
    392 	return audio_query_format(asc_formats, ASCAUDIO_NFORMATS, ae);
    393 }
    394 
    395 static int
    396 ascaudio_set_format(void *opaque, int setmode,
    397     const audio_params_t *play, const audio_params_t *rec,
    398     audio_filter_reg_t *pfil, audio_filter_reg_t *rfil)
    399 {
    400 	struct ascaudio_softc *sc = opaque;
    401 
    402 	KASSERT(mutex_owned(&sc->sc_lock));
    403 
    404 	sc->sc_recfreq = rec->sample_rate;
    405 
    406 	return 0;
    407 }
    408 
    409 static int
    410 ascaudio_start_output(void *opaque, void *block, int blksize,
    411     void (*intr)(void *), void *intrarg)
    412 {
    413 	struct ascaudio_softc *sc;
    414 	uint8_t *loc, tmp;
    415 	int total;
    416 
    417 	sc = (struct ascaudio_softc *)opaque;
    418 	if (!sc)
    419 		return (ENODEV);
    420 
    421 	sc->sc_pintr = intr;
    422 	sc->sc_pintrarg = intrarg;
    423 
    424 	loc = block;
    425  	if (bus_space_read_1(sc->sc_tag, sc->sc_handle, ASCMODE) !=
    426 								 MODEFIFO) {
    427 		bus_space_write_1(sc->sc_tag, sc->sc_handle, ASCMODE, MODESTOP);
    428 		bus_space_write_1(sc->sc_tag, sc->sc_handle, ASCTEST, 0);
    429 		bus_space_write_1(sc->sc_tag, sc->sc_handle, FIFOPARAM,
    430 		    NONCOMP);
    431 
    432 	}
    433 
    434 	/* set the volume. */
    435 	if (sc->sc_ver == EASC_VER || sc->sc_ver == EASC_VER2) {
    436 		/* DO NOT CHANGE THESE VALUES UNLESS TESTED.
    437 		   CAN BE VERY LOUD!!!! */
    438 		tmp = sc->sc_vol >> 5;
    439 		KASSERT(tmp <= MACOS_HIGH_VOL);
    440  		bus_space_write_1(sc->sc_tag, sc->sc_handle, B_LEFT_VOL, tmp);
    441  		bus_space_write_1(sc->sc_tag, sc->sc_handle, B_RIGHT_VOL, tmp);
    442 		if (sc->sc_rintr == NULL) {
    443  			bus_space_write_1(sc->sc_tag, sc->sc_handle,
    444 			    A_LEFT_VOL, tmp);
    445  			bus_space_write_1(sc->sc_tag, sc->sc_handle,
    446 			    A_RIGHT_VOL, tmp);
    447 		}
    448 	}
    449 	bus_space_write_1(sc->sc_tag, sc->sc_handle, INTVOL, sc->sc_vol);
    450 
    451 	total = blksize;
    452 	if (sc->sc_putptr + blksize >= sc->sc_playbuf + BUFSIZE)
    453 		total = sc->sc_playbuf + BUFSIZE - sc->sc_putptr;
    454 
    455 	if (total) {
    456 		memcpy(sc->sc_putptr, loc, total);
    457 		sc->sc_putptr += total;
    458 		loc += total;
    459 	}
    460 
    461 	total = blksize - total;
    462 	if (total) {
    463 		sc->sc_putptr = sc->sc_playbuf;
    464 		memcpy(sc->sc_playbuf, loc, total);
    465 		sc->sc_putptr += total;
    466 	}
    467 
    468 	sc->sc_avail += blksize;
    469 	if (sc->sc_avail > BUFSIZE)
    470 		sc->sc_avail = BUFSIZE;
    471 
    472 	/* start fifo playback */
    473 	if ((sc->sc_rintr == NULL) && bus_space_read_1(sc->sc_tag,
    474 	    sc->sc_handle, ASCMODE) != MODEFIFO)
    475 		bus_space_write_1(sc->sc_tag, sc->sc_handle, ASCMODE, MODEFIFO);
    476 
    477 	if (sc->sc_ver == EASC_VER || sc->sc_ver == EASC_VER2) {
    478 		/* enable interrupts channel b */
    479 		bus_space_write_1(sc->sc_tag, sc->sc_handle, IRQB, 0);
    480 	}
    481 
    482 	return 0;
    483 }
    484 
    485 static int
    486 ascaudio_start_input(void *opaque, void *block, int blksize,
    487     void (*intr)(void *), void *intrarg)
    488 {
    489 	struct ascaudio_softc *sc;
    490 	uint8_t tmp;
    491 	int total;
    492 
    493 	sc = (struct ascaudio_softc *)opaque;
    494 	if (!sc)
    495 		return (ENODEV);
    496 
    497 	uint8_t *loc;
    498 	loc = block;
    499 
    500 	sc->sc_rintr = intr;
    501 	sc->sc_rintrarg = intrarg;
    502 
    503  	if (bus_space_read_1(sc->sc_tag, sc->sc_handle, ASCMODE) !=
    504 								 MODEFIFO) {
    505 		bus_space_write_1(sc->sc_tag, sc->sc_handle, ASCMODE, MODESTOP);
    506 		bus_space_write_1(sc->sc_tag, sc->sc_handle, ASCTEST, 0);
    507 		bus_space_write_1(sc->sc_tag, sc->sc_handle, FIFOPARAM,
    508 		    NONCOMP);
    509 		memset(loc, 0x80, blksize);
    510 	}
    511 
    512 	if (sc->sc_ver == EASC_VER || sc->sc_ver == EASC_VER2) {
    513 		/*
    514 		 * Set up dfac for microphone.
    515 		 * DO NOT SET BITS 5-7 due to loud feedback squeal.
    516 		 */
    517 		configure_dfac(DFAC_GAIN_HIGH);
    518 	}
    519 
    520 	tmp = RECORDA;
    521 	if (sc->sc_recfreq == 22050)
    522 		tmp |= REC22KHZ;
    523 	bus_space_write_1(sc->sc_tag, sc->sc_handle, APLAYREC, tmp);
    524 
    525 	if (sc->sc_ver == EASC_VER || sc->sc_ver == EASC_VER2) {
    526 		/* enable interrupts channel a */
    527 		bus_space_write_1(sc->sc_tag, sc->sc_handle, IRQA, 0);
    528 	}
    529 
    530 	/* start fifo playback */
    531 	if ((sc->sc_pintr == NULL) && bus_space_read_1(sc->sc_tag,
    532 	    sc->sc_handle, ASCMODE) != MODEFIFO) {
    533 		bus_space_write_1(sc->sc_tag, sc->sc_handle, ASCMODE, MODEFIFO);
    534 
    535 		return 0;
    536 	}
    537 
    538 	/* set the volume. */
    539 	if (sc->sc_ver == EASC_VER || sc->sc_ver == EASC_VER2) {
    540 		/* DO NOT CHANGE THESE VALUES UNLESS TESTED.
    541 		   CAN BE VERY LOUD!!!! */
    542 		tmp = sc->sc_recvol >> 5;
    543 		KASSERT(tmp <= MACOS_HIGH_VOL);
    544  		bus_space_write_1(sc->sc_tag, sc->sc_handle, A_LEFT_VOL, tmp);
    545  		bus_space_write_1(sc->sc_tag, sc->sc_handle, A_RIGHT_VOL, tmp);
    546 		if (sc->sc_pintr == NULL) {
    547  			bus_space_write_1(sc->sc_tag, sc->sc_handle,
    548 			    B_LEFT_VOL, tmp);
    549  			bus_space_write_1(sc->sc_tag, sc->sc_handle,
    550 			    B_RIGHT_VOL, tmp);
    551 		}
    552 	}
    553 	if (sc->sc_pintr == NULL) {
    554 		bus_space_write_1(sc->sc_tag, sc->sc_handle, INTVOL,
    555 		    sc->sc_recvol);
    556 	}
    557 
    558 	total = blksize;
    559 	if (sc->sc_getptr + blksize >= sc->sc_recbuf + BUFSIZE)
    560 		total = sc->sc_recbuf + BUFSIZE - sc->sc_getptr;
    561 
    562 	if (total) {
    563 		memcpy(loc, sc->sc_getptr, total);
    564 		sc->sc_getptr += total;
    565 		loc += total;
    566 	}
    567 
    568 	total = blksize - total;
    569 	if (total) {
    570 		sc->sc_getptr = sc->sc_recbuf;
    571 		memcpy(loc, sc->sc_getptr, total);
    572 		sc->sc_getptr += total;
    573 	}
    574 
    575 	sc->sc_recavail -= blksize;
    576 
    577 	return 0;
    578 }
    579 
    580 static int
    581 ascaudio_halt_input(void *opaque)
    582 {
    583 	ascaudio_softc_t *sc;
    584 
    585 	sc = (ascaudio_softc_t *)opaque;
    586 
    587 	KASSERT(mutex_owned(&sc->sc_lock));
    588 
    589 	callout_halt(&sc->sc_rcallout, &sc->sc_intr_lock);
    590 
    591 	sc->sc_rintr = NULL;
    592 	sc->sc_rintrarg = NULL;
    593 	sc->sc_recavail = 0;
    594 
    595 	bus_space_write_1(sc->sc_tag, sc->sc_handle, APLAYREC, 0);
    596 
    597 	if (sc->sc_pintr == NULL) {
    598 		bus_space_write_1(sc->sc_tag, sc->sc_handle, ASCMODE, MODESTOP);
    599 		bus_space_write_1(sc->sc_tag, sc->sc_handle, FIFOPARAM,
    600 		    CLEARFIFO);
    601 	}
    602 
    603 	sc->sc_rptr = sc->sc_recbuf;
    604 	sc->sc_getptr = sc->sc_recbuf;
    605 
    606 	if (sc->sc_ver != EASC_VER && sc->sc_ver != EASC_VER2)
    607 		return 0;
    608 
    609 	configure_dfac(DFAC_DISABLE);
    610 
    611 	/* disable half interrupts channel a */
    612 	bus_space_write_1(sc->sc_tag, sc->sc_handle, IRQA, DISABLEHALFIRQ);
    613 
    614 	return 0;
    615 }
    616 
    617 static int
    618 ascaudio_halt_output(void *opaque)
    619 {
    620 	ascaudio_softc_t *sc;
    621 
    622 	sc = (ascaudio_softc_t *)opaque;
    623 
    624 	KASSERT(mutex_owned(&sc->sc_lock));
    625 
    626 	callout_halt(&sc->sc_pcallout, &sc->sc_intr_lock);
    627 
    628 	sc->sc_pintr = NULL;
    629 	sc->sc_pintrarg = NULL;
    630 	sc->sc_avail = 0;
    631 
    632 	if (sc->sc_rintr == NULL) {
    633 		bus_space_write_1(sc->sc_tag, sc->sc_handle, ASCMODE, MODESTOP);
    634 		bus_space_write_1(sc->sc_tag, sc->sc_handle, FIFOPARAM,
    635 		    CLEARFIFO);
    636 	}
    637 
    638 	sc->sc_wptr = sc->sc_playbuf;
    639 	sc->sc_putptr = sc->sc_playbuf;
    640 
    641 	if (sc->sc_ver != EASC_VER && sc->sc_ver != EASC_VER2)
    642 		return 0;
    643 
    644 	/* disable half interrupts channel b */
    645 	bus_space_write_1(sc->sc_tag, sc->sc_handle, IRQB, DISABLEHALFIRQ);
    646 
    647 	return 0;
    648 }
    649 
    650 static int
    651 ascaudio_getdev(void *opaque, struct audio_device *ret)
    652 {
    653 	strlcpy(ret->name, "Apple ASC Audio", sizeof(ret->name));
    654 	strlcpy(ret->version, osrelease, sizeof(ret->version));
    655 	strlcpy(ret->config, "ascaudio", sizeof(ret->config));
    656 
    657 	return 0;
    658 }
    659 
    660 static int
    661 ascaudio_set_port(void *opaque, mixer_ctrl_t *mc)
    662 {
    663 	struct ascaudio_softc *sc = opaque;
    664 
    665 	KASSERT(mutex_owned(&sc->sc_lock));
    666 
    667 	switch (mc->dev) {
    668 	case ASC_OUTPUT_MASTER_VOLUME:
    669 		if (mc->un.value.num_channels != 1)
    670 			return EINVAL;
    671 		sc->sc_vol = mc->un.value.level[AUDIO_MIXER_LEVEL_MONO];
    672 		return 0;
    673 	case ASC_INPUT_DAC_VOLUME:
    674 		if (mc->un.value.num_channels != 1)
    675 			return EINVAL;
    676 		sc->sc_recvol = mc->un.value.level[AUDIO_MIXER_LEVEL_MONO];
    677 		return 0;
    678 	}
    679 
    680 	return ENXIO;
    681 }
    682 
    683 static int
    684 ascaudio_get_port(void *opaque, mixer_ctrl_t *mc)
    685 {
    686 	struct ascaudio_softc *sc = opaque;
    687 
    688 	KASSERT(mutex_owned(&sc->sc_lock));
    689 
    690 	switch (mc->dev) {
    691 	case ASC_OUTPUT_MASTER_VOLUME:
    692 		if (mc->un.value.num_channels != 1)
    693 			return EINVAL;
    694 		mc->un.value.level[AUDIO_MIXER_LEVEL_MONO] = sc->sc_vol;
    695 		return 0;
    696 	case ASC_INPUT_DAC_VOLUME:
    697 		if (mc->un.value.num_channels != 1)
    698 			return EINVAL;
    699 		mc->un.value.level[AUDIO_MIXER_LEVEL_MONO] = sc->sc_recvol;
    700 		return 0;
    701 	}
    702 
    703 	return ENXIO;
    704 }
    705 
    706 static int
    707 ascaudio_query_devinfo(void *opaque, mixer_devinfo_t *di)
    708 {
    709 	ascaudio_softc_t *sc __diagused;
    710 
    711 	sc = (ascaudio_softc_t *)opaque;
    712 
    713 	KASSERT(mutex_owned(&sc->sc_lock));
    714 
    715 	switch (di->index) {
    716 	case ASC_OUTPUT_CLASS:
    717 		di->mixer_class = ASC_OUTPUT_CLASS;
    718 		strcpy(di->label.name, AudioCoutputs);
    719 		di->type = AUDIO_MIXER_CLASS;
    720 		di->next = di->prev = AUDIO_MIXER_LAST;
    721 		return 0;
    722 	case ASC_INPUT_CLASS:
    723 		di->mixer_class = ASC_INPUT_CLASS;
    724 		strcpy(di->label.name, AudioCinputs);
    725 		di->type = AUDIO_MIXER_CLASS;
    726 		di->next = di->prev = AUDIO_MIXER_LAST;
    727 		return 0;
    728 	case ASC_OUTPUT_MASTER_VOLUME:
    729 		di->mixer_class = ASC_OUTPUT_CLASS;
    730 		strcpy(di->label.name, AudioNmaster);
    731 		di->type = AUDIO_MIXER_VALUE;
    732 		di->next = di->prev = AUDIO_MIXER_LAST;
    733 		di->un.v.num_channels = 1;
    734 		strcpy(di->un.v.units.name, AudioNvolume);
    735 		return 0;
    736 	case ASC_INPUT_DAC_VOLUME:
    737 		di->mixer_class = ASC_INPUT_CLASS;
    738 		strcpy(di->label.name, AudioNdac);
    739 		di->type = AUDIO_MIXER_VALUE;
    740 		di->next = di->prev = AUDIO_MIXER_LAST;
    741 		di->un.v.num_channels = 1;
    742 		strcpy(di->un.v.units.name, AudioNvolume);
    743 		return 0;
    744 	}
    745 
    746 	return ENXIO;
    747 }
    748 
    749 static int
    750 ascaudio_get_props(void *opaque)
    751 {
    752 
    753 	return AUDIO_PROP_PLAYBACK | AUDIO_PROP_CAPTURE |
    754 	    AUDIO_PROP_INDEPENDENT | AUDIO_PROP_FULLDUPLEX;
    755 }
    756 
    757 static int
    758 ascaudio_round_blocksize(void *opaque, int blksize, int mode,
    759     const audio_params_t *p)
    760 {
    761 	ascaudio_softc_t *sc __diagused;
    762 
    763 	sc = (ascaudio_softc_t *)opaque;
    764 	KASSERT(mutex_owned(&sc->sc_lock));
    765 
    766 	if (mode == AUMODE_PLAY) {
    767 		blksize = roundup(blksize, PLAYBLKUNIT);
    768 		if (blksize < PLAYBLKMIN)
    769 			blksize = PLAYBLKMIN;
    770 		if (blksize > PLAYBLKSIZE)
    771 			blksize = PLAYBLKSIZE;
    772 		return blksize;
    773 	}
    774 	return RECBLKSIZE;
    775 }
    776 
    777 static void
    778 ascaudio_get_locks(void *opaque, kmutex_t **intr, kmutex_t **thread)
    779 {
    780 	ascaudio_softc_t *sc;
    781 
    782 	sc = (ascaudio_softc_t *)opaque;
    783 
    784 	*intr = &sc->sc_intr_lock;
    785 	*thread = &sc->sc_lock;
    786 }
    787 
    788 static int
    789 ascaudio_intr_est(void *arg)
    790 {
    791 	ascaudio_intr(arg);
    792 
    793 	return 0;
    794 }
    795 
    796 static void
    797 ascaudio_intr(void *arg)
    798 {
    799 	struct ascaudio_softc *sc = arg;
    800 	uint8_t status;
    801 	int8_t val;
    802 	int16_t scaled;
    803 	int loc_a, loc_b, total, count, i;
    804 
    805 	if (!sc)
    806 		return;
    807 
    808 	if (!sc->sc_pintr && !sc->sc_rintr)
    809 		return;
    810 
    811 	mutex_enter(&sc->sc_intr_lock);
    812 
    813 	status = bus_space_read_1(sc->sc_tag, sc->sc_handle, FIFOSTATUS);
    814 
    815 	if (sc->sc_ver == EASC_VER || sc->sc_ver == EASC_VER2) {
    816 		if (sc->sc_pintr) {
    817 			bus_space_write_1(sc->sc_tag, sc->sc_handle,
    818 			    IRQB, DISABLEHALFIRQ);
    819 		}
    820 		if (sc->sc_rintr) {
    821 			bus_space_write_1(sc->sc_tag, sc->sc_handle,
    822 			    IRQA, DISABLEHALFIRQ);
    823 		}
    824 	}
    825 
    826 	if (sc->sc_ver == EASC_VER2 && (status & A_HALF))
    827 		count = 0x200;
    828 	else if (sc->sc_ver != EASC_VER2 && !(status & A_HALF))
    829 		count = 0x200;
    830 	else
    831 		count = 0;
    832 
    833 	if (count && sc->sc_rintr) {
    834 		total = count;
    835 		if (sc->sc_rptr + count >= sc->sc_recbuf + BUFSIZE)
    836 			count = sc->sc_recbuf + BUFSIZE - sc->sc_rptr;
    837 		while (total) {
    838 			if (sc->sc_ver == EASC_VER2) {
    839 				loc_a = FIFO_A_ALT;
    840 				loc_b = FIFO_B_ALT;
    841 			} else {
    842 				loc_a = FIFO_A;
    843 				loc_b = 0;
    844 			}
    845 			for (i = 0; i < count; i++) {
    846 				val = bus_space_read_1(sc->sc_tag,
    847 				    sc->sc_handle, loc_a);
    848 				val ^= 0x80;
    849 				scaled = val * sc->sc_recvol / 64;
    850 				if (scaled > INT8_MAX)
    851 					scaled = INT8_MAX;
    852 				else if (scaled < INT8_MIN)
    853 					scaled = INT8_MIN;
    854 				*sc->sc_rptr++ = scaled;
    855 				if (loc_b) {
    856 					(void)bus_space_read_1
    857 					    (sc->sc_tag, sc->sc_handle, loc_b);
    858 				}
    859 			}
    860 			if (sc->sc_rptr >= sc->sc_recbuf + BUFSIZE)
    861 				sc->sc_rptr = sc->sc_recbuf;
    862 			total -= count;
    863 			sc->sc_recavail += count;
    864 			count = total;
    865 		}
    866 
    867 		if (sc->sc_recavail > BUFSIZE)
    868 			sc->sc_recavail = BUFSIZE;
    869 	}
    870 
    871 	if (sc->sc_pintr == NULL)
    872 		goto more;
    873 
    874 	if (status & B_HALF)
    875 		count = 0x200;
    876 	else
    877 		count = 0;
    878 
    879 	if (sc->sc_slowcpu)
    880 		count /= 2;
    881 
    882 	if (count && sc->sc_avail < count) {
    883 		if (sc->sc_avail) {
    884 			count = sc->sc_avail;
    885 			goto fill_fifo;
    886 		}
    887 		if (sc->sc_pintr) {
    888 			for (i = 0; i < 0x200; i++) {
    889 				if (sc->sc_rintr == NULL ||
    890 				    sc->sc_ver == EASC_VER2) {
    891 					bus_space_write_1(sc->sc_tag,
    892 					    sc->sc_handle, FIFO_A, 0x80);
    893 				}
    894 				bus_space_write_1(sc->sc_tag,
    895 				    sc->sc_handle, FIFO_B, 0x80);
    896 			}
    897 		} else {
    898 			if (sc->sc_slowcpu)
    899 				count *= 2;
    900 			for (i = 0; i < count; i++) {
    901 				bus_space_write_1(sc->sc_tag,
    902 				    sc->sc_handle, FIFO_B, 0x80);
    903 			}
    904 		}
    905 		goto more;
    906 	}
    907 
    908 fill_fifo:
    909 	total = count;
    910 	if (sc->sc_wptr + count >= sc->sc_playbuf + BUFSIZE)
    911 		count = sc->sc_playbuf + BUFSIZE - sc->sc_wptr;
    912 
    913 	while (total) {
    914 		if (sc->sc_slowcpu) {
    915 			for (i = 0; i < count; i++) {
    916 				val = *sc->sc_wptr++;
    917 				val ^= 0x80;
    918 				if (sc->sc_rintr == NULL ||
    919 				    sc->sc_ver == EASC_VER2) {
    920 					bus_space_write_1(sc->sc_tag,
    921 					    sc->sc_handle, FIFO_A, val);
    922 					bus_space_write_1(sc->sc_tag,
    923 					    sc->sc_handle, FIFO_A, val);
    924 				}
    925 				bus_space_write_1(sc->sc_tag,
    926 				    sc->sc_handle, FIFO_B, val);
    927 				bus_space_write_1(sc->sc_tag,
    928 				    sc->sc_handle, FIFO_B, val);
    929 			}
    930 		} else {
    931 			for (i = 0; i < count; i++) {
    932 				val = *sc->sc_wptr++;
    933 				val ^= 0x80;
    934 				if (sc->sc_rintr == NULL ||
    935 				    sc->sc_ver == EASC_VER2) {
    936 					bus_space_write_1(sc->sc_tag,
    937 					    sc->sc_handle, FIFO_A, val);
    938 				}
    939 				bus_space_write_1(sc->sc_tag,
    940 				    sc->sc_handle, FIFO_B, val);
    941 			}
    942 		}
    943 		if (sc->sc_wptr >= sc->sc_playbuf + BUFSIZE)
    944 			sc->sc_wptr = sc->sc_playbuf;
    945 		total -= count;
    946 		sc->sc_avail -= count;
    947 		count = total;
    948 	}
    949 
    950 more:
    951 	if (sc->sc_ver == EASC_VER || sc->sc_ver == EASC_VER2) {
    952 		if (sc->sc_rintr)
    953 			bus_space_write_1(sc->sc_tag, sc->sc_handle, IRQA, 0);
    954 		if (sc->sc_pintr)
    955 			bus_space_write_1(sc->sc_tag, sc->sc_handle, IRQB, 0);
    956 	}
    957 
    958 	if (sc->sc_pintr && (sc->sc_avail <= PLAYBLKSIZE))
    959 		callout_schedule(&sc->sc_pcallout, 0);
    960 
    961 	if (sc->sc_rintr && (sc->sc_recavail >= RECBLKSIZE))
    962 		callout_schedule(&sc->sc_rcallout, 0);
    963 
    964 	mutex_exit(&sc->sc_intr_lock);
    965 }
    966 
    967 static void
    968 ascaudio_intr_enable(void)
    969 {
    970 	int s;
    971 
    972 	if (mac68k_machine.aux_interrupts)
    973 		return;
    974 
    975 	s = splhigh();
    976 	if (VIA2 == VIA2OFF)
    977 		via2_reg(vIER) = 0x80 | V2IF_ASC;
    978 	else
    979 		via2_reg(rIER) = 0x80 | V2IF_ASC;
    980 	splx(s);
    981 }
    982 
    983 static void
    984 ascaudio_done_output(void *arg)
    985 {
    986 	struct ascaudio_softc *sc = arg;
    987 
    988 	mutex_enter(&sc->sc_intr_lock);
    989 	if (sc->sc_pintr)
    990 		(*sc->sc_pintr)(sc->sc_pintrarg);
    991 	mutex_exit(&sc->sc_intr_lock);
    992 }
    993 
    994 static void
    995 ascaudio_done_input(void *arg)
    996 {
    997 	struct ascaudio_softc *sc = arg;
    998 
    999 	mutex_enter(&sc->sc_intr_lock);
   1000 	if (sc->sc_rintr)
   1001 		(*sc->sc_rintr)(sc->sc_rintrarg);
   1002 	mutex_exit(&sc->sc_intr_lock);
   1003 }
   1004 
   1005 static void
   1006 configure_dfac(uint8_t config)
   1007 {
   1008 	int i;
   1009 
   1010 	if (pmHardware == PM_HW_PB5XX)
   1011 		return;		/* These macs use the pm to configure dfac */
   1012 
   1013 	for (i = 0; i < 8; i++) {
   1014 		via_reg(VIA2, vBufB) &= ~DFAC_CLOCK;
   1015 
   1016 		if (config & 0x1)
   1017 			via_reg(VIA2, vBufB) |= DFAC_DATA;
   1018 		else
   1019 			via_reg(VIA2, vBufB) &= ~DFAC_DATA;
   1020 
   1021 		via_reg(VIA2, vBufB) |= DFAC_CLOCK;
   1022 		config >>= 1;
   1023 	}
   1024 	via_reg(VIA2, vBufB) &= ~DFAC_CLOCK;
   1025 	via_reg(VIA2, vBufB) |= DFAC_LATCH;
   1026 	via_reg(VIA2, vBufB) &= ~DFAC_LATCH;
   1027 }
   1028 
   1029 #ifdef _MODULE
   1030 
   1031 MODULE(MODULE_CLASS_DRIVER, ascaudio, "audio");
   1032 
   1033 static const struct cfiattrdata audiobuscf_iattrdata = {
   1034 	"audiobus", 0, { { NULL, NULL, 0 }, }
   1035 };
   1036 static const struct cfiattrdata * const ascaudio_attrs[] = {
   1037 	&audiobuscf_iattrdata, NULL
   1038 };
   1039 
   1040 CFDRIVER_DECL(ascaudio, DV_DULL, ascaudio_attrs);
   1041 extern struct cfattach ascaudio_ca;
   1042 static int ascaudioloc[] = { -1, -1 };
   1043 
   1044 static struct cfdata ascaudio_cfdata[] = {
   1045 	{
   1046 		.cf_name = "ascaudio",
   1047 		.cf_atname = "ascaudio",
   1048 		.cf_unit = 0,
   1049 		.cf_fstate = FSTATE_STAR,
   1050 		.cf_loc = ascaudioloc,
   1051 		.cf_flags = 0,
   1052 		.cf_pspec = NULL,
   1053 	},
   1054 	{ NULL, NULL, 0, 0, NULL, 0, NULL }
   1055 };
   1056 
   1057 static int
   1058 ascaudio_modcmd(modcmd_t cmd, void *arg)
   1059 {
   1060 	devmajor_t cmajor = NODEVMAJOR, bmajor = NODEVMAJOR;
   1061 	int error;
   1062 
   1063 	switch (cmd) {
   1064 	case MODULE_CMD_INIT:
   1065 		error = config_cfdriver_attach(&ascaudio_cd);
   1066 		if (error) {
   1067 			return error;
   1068 		}
   1069 
   1070 		error = config_cfattach_attach(ascaudio_cd.cd_name, &ascaudio_ca);
   1071 		if (error) {
   1072 			config_cfdriver_detach(&ascaudio_cd);
   1073 			aprint_error("%s: unable to register cfattach\n",
   1074 				ascaudio_cd.cd_name);
   1075 
   1076 			return error;
   1077 		}
   1078 
   1079 		error = config_cfdata_attach(ascaudio_cfdata, 1);
   1080 		if (error) {
   1081 			config_cfattach_detach(ascaudio_cd.cd_name, &ascaudio_ca);
   1082 			config_cfdriver_detach(&ascaudio_cd);
   1083 			aprint_error("%s: unable to register cfdata\n",
   1084 				ascaudio_cd.cd_name);
   1085 
   1086 			return error;
   1087 		}
   1088 
   1089 		error = devsw_attach(ascaudio_cd.cd_name, NULL, &bmajor,
   1090 		    &ascaudio_cdevsw, &cmajor);
   1091 		if (error) {
   1092 			error = config_cfdata_detach(ascaudio_cfdata);
   1093 			if (error) {
   1094 				return error;
   1095 			}
   1096 			config_cfattach_detach(ascaudio_cd.cd_name, &ascaudio_ca);
   1097 			config_cfdriver_detach(&ascaudio_cd);
   1098 			aprint_error("%s: unable to register devsw\n",
   1099 				ascaudio_cd.cd_name);
   1100 
   1101 			return error;
   1102 		}
   1103 
   1104 		(void)config_attach_pseudo(ascaudio_cfdata);
   1105 
   1106 		return 0;
   1107 	case MODULE_CMD_FINI:
   1108 		error = config_cfdata_detach(ascaudio_cfdata);
   1109 		if (error) {
   1110 			return error;
   1111 		}
   1112 
   1113 		config_cfattach_detach(ascaudio_cd.cd_name, &ascaudio_ca);
   1114 		config_cfdriver_detach(&ascaudio_cd);
   1115 		devsw_detach(NULL, &ascaudio_cdevsw);
   1116 
   1117 		return 0;
   1118 	default:
   1119 		return ENOTTY;
   1120 	}
   1121 }
   1122 
   1123 #endif
   1124