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