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