Home | History | Annotate | Line # | Download | only in obio
ascaudio.c revision 1.4
      1 /* $NetBSD: ascaudio.c,v 1.4 2025/01/13 16:23:48 riastradh 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.4 2025/01/13 16:23:48 riastradh 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		0x2000
     64 
     65 #define BUFSIZE 			32768
     66 #define PLAYBLKSIZE			8192
     67 #define RECBLKSIZE			8192
     68 
     69 #define ASC_VIA_CLR_INTR()     via_reg(VIA2, vIFR) = V2IF_ASC
     70 
     71 static int	ascaudiomatch(device_t, cfdata_t, void *);
     72 static void	ascaudioattach(device_t, device_t, void *);
     73 
     74 CFATTACH_DECL_NEW(ascaudio, sizeof(struct ascaudio_softc),
     75     ascaudiomatch, ascaudioattach, NULL, NULL);
     76 
     77 extern struct cfdriver ascaudio_cd;
     78 
     79 dev_type_open(ascaudioopen);
     80 dev_type_close(ascaudioclose);
     81 dev_type_read(ascaudioread);
     82 dev_type_write(ascaudiowrite);
     83 dev_type_ioctl(ascaudioioctl);
     84 
     85 const struct cdevsw ascaudio_cdevsw = {
     86 	.d_open = ascaudioopen,
     87 	.d_close = ascaudioclose,
     88 	.d_read = ascaudioread,
     89 	.d_write = ascaudiowrite,
     90 	.d_ioctl = ascaudioioctl,
     91 	.d_stop = nostop,
     92 	.d_tty = notty,
     93 	.d_poll = nopoll,
     94 	.d_mmap = nommap,
     95 	.d_kqfilter = nokqfilter,
     96 	.d_discard = nodiscard,
     97 	.d_flag = 0
     98 };
     99 
    100 static int	ascaudio_query_format(void *, struct audio_format_query *);
    101 static int	ascaudio_set_format(void *, int,
    102 		    const audio_params_t *, const audio_params_t *,
    103 		    audio_filter_reg_t *, audio_filter_reg_t *);
    104 static int	ascaudio_start_output(void *, void *, int,
    105 				    void (*)(void *), void *);
    106 static int	ascaudio_start_input(void *, void *, int,
    107 				   void (*)(void *), void *);
    108 static int	ascaudio_halt(void *);
    109 static int	ascaudio_set_port(void *, mixer_ctrl_t *);
    110 static int	ascaudio_get_port(void *, mixer_ctrl_t *);
    111 static int	ascaudio_getdev(void *, struct audio_device *);
    112 static int	ascaudio_query_devinfo(void *, mixer_devinfo_t *);
    113 static int	ascaudio_get_props(void *);
    114 static int
    115 	    ascaudio_round_blocksize(void *, int, int, const audio_params_t *);
    116 static void	ascaudio_get_locks(void *, kmutex_t **, kmutex_t **);
    117 static void 	ascaudio_intr(void *);
    118 static int 	ascaudio_intr_est(void *);
    119 static void	ascaudio_intr_enable(void);
    120 static void	ascaudio_done_output(void *);
    121 static void	ascaudio_done_input(void *);
    122 
    123 static const struct audio_hw_if ascaudio_hw_if = {
    124 	.query_format	 = ascaudio_query_format,
    125 	.set_format	 = ascaudio_set_format,
    126 	.start_output	 = ascaudio_start_output,
    127 	.start_input	 = ascaudio_start_input,
    128 	.halt_output	 = ascaudio_halt,
    129 	.halt_input	 = ascaudio_halt,
    130 	.set_port	 = ascaudio_set_port,
    131 	.get_port	 = ascaudio_get_port,
    132 	.getdev		 = ascaudio_getdev,
    133 	.query_devinfo	 = ascaudio_query_devinfo,
    134 	.get_props 	 = ascaudio_get_props,
    135 	.round_blocksize = ascaudio_round_blocksize,
    136 	.get_locks	 = ascaudio_get_locks,
    137 };
    138 
    139 #define EASC_VER	0xb0
    140 #define EASC_VER2	0xbb
    141 
    142 enum {
    143 	ASC_OUTPUT_CLASS,
    144 	ASC_INPUT_CLASS,
    145 	ASC_OUTPUT_MASTER_VOLUME,
    146 	ASC_INPUT_DAC_VOLUME,
    147 	ASC_ENUM_LAST,
    148 };
    149 
    150 static int
    151 ascaudiomatch(device_t parent, cfdata_t cf, void *aux)
    152 {
    153 	struct obio_attach_args *oa = (struct obio_attach_args *)aux;
    154 	bus_addr_t addr;
    155 	bus_space_handle_t bsh;
    156 	int rval = 0;
    157 
    158 	if (oa->oa_addr != (-1))
    159 		addr = (bus_addr_t)oa->oa_addr;
    160 	else if (current_mac_model->machineid == MACH_MACTV)
    161 		return 0;
    162 	else if (current_mac_model->machineid == MACH_MACIIFX)
    163 		addr = (bus_addr_t)MAC68K_IIFX_ASCAUDIO_BASE;
    164 	else
    165 		addr = (bus_addr_t)MAC68K_ASCAUDIO_BASE;
    166 
    167 	if (bus_space_map(oa->oa_tag, addr, MAC68K_ASCAUDIO_LEN, 0, &bsh))
    168 		return (0);
    169 
    170 	if (mac68k_bus_space_probe(oa->oa_tag, bsh, 0, 1)) {
    171 		rval = 1;
    172 	} else
    173 		rval = 0;
    174 
    175 	bus_space_unmap(oa->oa_tag, bsh, MAC68K_ASCAUDIO_LEN);
    176 
    177 	return rval;
    178 }
    179 
    180 static void
    181 ascaudioattach(device_t parent, device_t self, void *aux)
    182 {
    183 	struct ascaudio_softc *sc = device_private(self);
    184 	struct obio_attach_args *oa = (struct obio_attach_args *)aux;
    185 	bus_addr_t addr;
    186 	uint8_t tmp;
    187 
    188 	sc->sc_dev = self;
    189 	sc->sc_tag = oa->oa_tag;
    190 
    191 	if (oa->oa_addr != (-1))
    192 		addr = (bus_addr_t)oa->oa_addr;
    193 	else if (current_mac_model->machineid == MACH_MACIIFX)
    194 		addr = (bus_addr_t)MAC68K_IIFX_ASCAUDIO_BASE;
    195 	else
    196 		addr = (bus_addr_t)MAC68K_ASCAUDIO_BASE;
    197 	if (bus_space_map(sc->sc_tag, addr, MAC68K_ASCAUDIO_LEN, 0,
    198 	    &sc->sc_handle)) {
    199 		printf(": can't map memory space\n");
    200 		return;
    201 	}
    202 
    203 	/* Pull in the options flags. */
    204 	sc->sc_options = ((device_cfdata(self)->cf_flags) &
    205 			    ASCAUDIO_OPTIONS_MASK);
    206 
    207 	sc->sc_playbuf = kmem_alloc(BUFSIZE, KM_SLEEP);
    208 	sc->sc_recbuf = kmem_alloc(BUFSIZE, KM_SLEEP);
    209 	sc->sc_rptr = sc->sc_recbuf;
    210 	sc->sc_getptr = sc->sc_recbuf;
    211 	sc->sc_wptr = sc->sc_playbuf;
    212 	sc->sc_putptr = sc->sc_playbuf;
    213 
    214 	bus_space_write_1(sc->sc_tag, sc->sc_handle, ASCMODE, MODESTOP);
    215 
    216 	sc->sc_ver = bus_space_read_1(oa->oa_tag, sc->sc_handle, 0x800);
    217 
    218 	if (sc->sc_options & HIGHQUALITY) {
    219 		tmp = bus_space_read_1(sc->sc_tag, sc->sc_handle, ASCRATE);
    220 		switch (tmp) {
    221 			case 2:
    222 				sc->sc_rate = 22050;
    223 				break;
    224 			case 3:
    225 				sc->sc_rate = 44100;
    226 				break;
    227 			default:
    228 				sc->sc_rate = 22254;
    229 				break;
    230 		}
    231 
    232 		tmp = bus_space_read_1(sc->sc_tag, sc->sc_handle, ASCTRL);
    233 		if (tmp & STEREO)
    234 			sc->sc_speakers = 2;
    235 		else
    236 			sc->sc_speakers = 1;
    237 
    238 	} else {
    239 		__USE(tmp);
    240 		sc->sc_rate = 22254;
    241 		sc->sc_speakers = 1;
    242 	}
    243 
    244 	if (sc->sc_options & LOWQUALITY) {
    245 		sc->sc_slowcpu = true;
    246 		if (sc->sc_slowcpu)
    247 			sc->sc_rate /= 2;
    248 	}
    249 
    250 	if (sc->sc_ver == EASC_VER || sc->sc_ver == EASC_VER2)
    251 		printf(": Enhanced Apple Sound Chip");
    252 	else
    253 		printf(": Apple Sound Chip");
    254 
    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_AUDIO);
    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 = 180;
    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	2
    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 	};
    379 
    380 	return audio_query_format(asc_formats, ASCAUDIO_NFORMATS, ae);
    381 }
    382 
    383 static int
    384 ascaudio_set_format(void *opaque, int setmode,
    385     const audio_params_t *play, const audio_params_t *rec,
    386     audio_filter_reg_t *pfil, audio_filter_reg_t *rfil)
    387 {
    388 	struct ascaudio_softc *sc = opaque;
    389 
    390 	KASSERT(mutex_owned(&sc->sc_lock));
    391 
    392 	return 0;
    393 }
    394 
    395 static int
    396 ascaudio_start_output(void *opaque, void *block, int blksize,
    397     void (*intr)(void *), void *intrarg)
    398 {
    399 	struct ascaudio_softc *sc;
    400 	uint8_t *loc, tmp;
    401 	int total;
    402 
    403 	sc = (struct ascaudio_softc *)opaque;
    404 	if (!sc)
    405 		return (ENODEV);
    406 
    407 	sc->sc_pintr = intr;
    408 	sc->sc_pintrarg = intrarg;
    409 
    410 	loc = block;
    411  	if (bus_space_read_1(sc->sc_tag, sc->sc_handle, ASCMODE) !=
    412 								 MODEFIFO) {
    413 		bus_space_write_1(sc->sc_tag, sc->sc_handle, ASCMODE, MODESTOP);
    414 
    415 		if (sc->sc_ver == EASC_VER || sc->sc_ver == EASC_VER2) {
    416 			/* disable half interrupts channel a */
    417 			bus_space_write_1(sc->sc_tag, sc->sc_handle, IRQA,
    418 			    DISABLEHALFIRQ);
    419 			/* Disable half interrupts channel b */
    420 			bus_space_write_1(sc->sc_tag, sc->sc_handle, IRQB,
    421 			    DISABLEHALFIRQ);
    422 		}
    423 
    424 		bus_space_write_1(sc->sc_tag, sc->sc_handle, ASCTEST, 0);
    425 		bus_space_write_1(sc->sc_tag, sc->sc_handle, FIFOPARAM,
    426 		    CLEARFIFO);
    427 		tmp = 0;
    428 		bus_space_write_1(sc->sc_tag, sc->sc_handle, APLAYREC, tmp);
    429 
    430 		if (sc->sc_ver == EASC_VER || sc->sc_ver == EASC_VER2) {
    431 			/* enable interrupts channel b */
    432 			bus_space_write_1(sc->sc_tag, sc->sc_handle, IRQB, 0);
    433 		}
    434 	}
    435 
    436 	/* set the volume. */
    437 	if (sc->sc_ver == EASC_VER || sc->sc_ver == EASC_VER2) {
    438 		/* DO NOT CHANGE THESE VALUES UNLESS TESTED.
    439 		   CAN BE VERY LOUD!!!! */
    440 		tmp = sc->sc_vol >> 5;
    441 		KASSERT(tmp <= MACOS_HIGH_VOL);
    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 	}
    447 	bus_space_write_1(sc->sc_tag, sc->sc_handle, INTVOL, sc->sc_vol);
    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 	if (total) {
    454 		memcpy(sc->sc_putptr, loc, total);
    455 		sc->sc_putptr += total;
    456 		loc += total;
    457 	}
    458 
    459 	total = blksize - total;
    460 	if (total) {
    461 		sc->sc_putptr = sc->sc_playbuf;
    462 		memcpy(sc->sc_playbuf, loc, total);
    463 		sc->sc_putptr += total;
    464 	}
    465 
    466 	sc->sc_avail += blksize;
    467 	if (sc->sc_avail > BUFSIZE)
    468 		sc->sc_avail = BUFSIZE;
    469 
    470 	/* start fifo playback */
    471 	bus_space_write_1(sc->sc_tag, sc->sc_handle, ASCMODE, MODEFIFO);
    472 
    473 	return 0;
    474 }
    475 
    476 static int
    477 ascaudio_start_input(void *opaque, void *block, int blksize,
    478     void (*intr)(void *), void *intrarg)
    479 {
    480 	struct ascaudio_softc *sc;
    481 	uint8_t tmp;
    482 	int total;
    483 
    484 	sc = (struct ascaudio_softc *)opaque;
    485 	if (!sc)
    486 		return (ENODEV);
    487 
    488 	uint8_t *loc;
    489 	loc = block;
    490 
    491 	sc->sc_rintr = intr;
    492 	sc->sc_rintrarg = intrarg;
    493 
    494  	if (bus_space_read_1(sc->sc_tag, sc->sc_handle, ASCMODE) !=
    495 								 MODEFIFO) {
    496 		bus_space_write_1(sc->sc_tag, sc->sc_handle, ASCMODE, MODESTOP);
    497 
    498 		if (sc->sc_ver == EASC_VER || sc->sc_ver == EASC_VER2) {
    499 			/* disable half interrupts channel a */
    500 			bus_space_write_1(sc->sc_tag, sc->sc_handle, IRQA,
    501 			    DISABLEHALFIRQ);
    502 			/* Disable half interrupts channel b */
    503 			bus_space_write_1(sc->sc_tag, sc->sc_handle, IRQB,
    504 			    DISABLEHALFIRQ);
    505 		}
    506 
    507 		bus_space_write_1(sc->sc_tag, sc->sc_handle, ASCTEST, 0);
    508 
    509 		/* start fifo playback */
    510 		bus_space_write_1(sc->sc_tag, sc->sc_handle, ASCMODE, MODEFIFO);
    511 
    512 		if (sc->sc_ver == EASC_VER || sc->sc_ver == EASC_VER2) {
    513 			/* enable interrupts channel a */
    514 			bus_space_write_1(sc->sc_tag, sc->sc_handle, IRQA, 0);
    515 		}
    516 
    517 		bus_space_write_1(sc->sc_tag, sc->sc_handle, FIFOPARAM,
    518 		    CLEARFIFO);
    519 
    520 #if 0
    521 		bus_space_write_4(sc->sc_tag, sc->sc_handle, FIFO_A_ALT, 0);
    522 		bus_space_write_4(sc->sc_tag, sc->sc_handle, FIFO_B_ALT, 0);
    523 #endif
    524 
    525 		bus_space_write_1(sc->sc_tag, sc->sc_handle, FIFOPARAM,
    526 		    CLEARFIFO);
    527 
    528 		tmp = RECORDA;
    529 		bus_space_write_1(sc->sc_tag, sc->sc_handle, APLAYREC, tmp);
    530 
    531 #if 0
    532 		int i;
    533 		for (i = 0; i < 0x400; i++) {
    534 			bus_space_read_1(sc->sc_tag, sc->sc_handle, FIFO_A);
    535 			bus_space_read_1(sc->sc_tag, sc->sc_handle, FIFO_B);
    536 		}
    537 #endif
    538 
    539 		return 0;
    540 	}
    541 
    542 	/* set the volume. */
    543 	if (sc->sc_ver == EASC_VER || sc->sc_ver == EASC_VER2) {
    544 		/* DO NOT CHANGE THESE VALUES UNLESS TESTED.
    545 		   CAN BE VERY LOUD!!!! */
    546 		tmp = sc->sc_vol >> 5;
    547 		KASSERT(tmp <= MACOS_HIGH_VOL);
    548  		bus_space_write_1(sc->sc_tag, sc->sc_handle, A_LEFT_VOL, tmp);
    549  		bus_space_write_1(sc->sc_tag, sc->sc_handle, B_LEFT_VOL, tmp);
    550  		bus_space_write_1(sc->sc_tag, sc->sc_handle, A_RIGHT_VOL, tmp);
    551  		bus_space_write_1(sc->sc_tag, sc->sc_handle, B_RIGHT_VOL, tmp);
    552 	}
    553 	bus_space_write_1(sc->sc_tag, sc->sc_handle, INTVOL, sc->sc_vol);
    554 
    555 	total = blksize;
    556 	if (sc->sc_getptr + blksize >= sc->sc_recbuf + BUFSIZE)
    557 		total = sc->sc_recbuf + BUFSIZE - sc->sc_getptr;
    558 
    559 	if (total) {
    560 		memcpy(loc, sc->sc_getptr, total);
    561 		sc->sc_getptr += total;
    562 		loc += total;
    563 	}
    564 
    565 	total = blksize - total;
    566 	if (total) {
    567 		sc->sc_getptr = sc->sc_recbuf;
    568 		memcpy(loc, sc->sc_getptr, total);
    569 		sc->sc_getptr += total;
    570 	}
    571 
    572 	sc->sc_recavail -= blksize;
    573 
    574 	return 0;
    575 }
    576 
    577 static int
    578 ascaudio_halt(void *opaque)
    579 {
    580 	ascaudio_softc_t *sc;
    581 
    582 	sc = (ascaudio_softc_t *)opaque;
    583 
    584 	KASSERT(mutex_owned(&sc->sc_lock));
    585 
    586 	callout_halt(&sc->sc_pcallout, &sc->sc_intr_lock);
    587 	callout_halt(&sc->sc_rcallout, &sc->sc_intr_lock);
    588 
    589 	bus_space_write_1(sc->sc_tag, sc->sc_handle, ASCMODE, MODESTOP);
    590 
    591 	sc->sc_pintr = NULL;
    592 	sc->sc_pintrarg = NULL;
    593 	sc->sc_rintr = NULL;
    594 	sc->sc_rintrarg = NULL;
    595 
    596 	sc->sc_avail = 0;
    597 	sc->sc_recavail = 0;
    598 
    599 	bus_space_write_1(sc->sc_tag, sc->sc_handle, FIFOPARAM, CLEARFIFO);
    600 
    601 	sc->sc_rptr = sc->sc_recbuf;
    602 	sc->sc_getptr = sc->sc_recbuf;
    603 	sc->sc_wptr = sc->sc_playbuf;
    604 	sc->sc_putptr = sc->sc_playbuf;
    605 
    606 	if (sc->sc_ver != EASC_VER && sc->sc_ver != EASC_VER2)
    607 		return 0;
    608 
    609 	/* disable half interrupts channel a */
    610 	bus_space_write_1(sc->sc_tag, sc->sc_handle, IRQA, DISABLEHALFIRQ);
    611 	/* disable half interrupts channel b */
    612 	bus_space_write_1(sc->sc_tag, sc->sc_handle, IRQB, DISABLEHALFIRQ);
    613 
    614 	return 0;
    615 }
    616 
    617 static int
    618 ascaudio_getdev(void *opaque, struct audio_device *ret)
    619 {
    620 	strlcpy(ret->name, "Apple ASC Audio", sizeof(ret->name));
    621 	strlcpy(ret->version, osrelease, sizeof(ret->version));
    622 	strlcpy(ret->config, "ascaudio", sizeof(ret->config));
    623 
    624 	return 0;
    625 }
    626 
    627 static int
    628 ascaudio_set_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 		sc->sc_vol = mc->un.value.level[AUDIO_MIXER_LEVEL_MONO];
    640 		return 0;
    641 	}
    642 
    643 	return ENXIO;
    644 }
    645 
    646 static int
    647 ascaudio_get_port(void *opaque, mixer_ctrl_t *mc)
    648 {
    649 	struct ascaudio_softc *sc = opaque;
    650 
    651 	KASSERT(mutex_owned(&sc->sc_lock));
    652 
    653 	switch (mc->dev) {
    654 	case ASC_OUTPUT_MASTER_VOLUME:
    655 	case ASC_INPUT_DAC_VOLUME:
    656 		if (mc->un.value.num_channels != 1)
    657 			return EINVAL;
    658 		mc->un.value.level[AUDIO_MIXER_LEVEL_MONO] = sc->sc_vol;
    659 		return 0;
    660 	}
    661 
    662 	return ENXIO;
    663 }
    664 
    665 static int
    666 ascaudio_query_devinfo(void *opaque, mixer_devinfo_t *di)
    667 {
    668 	ascaudio_softc_t *sc __diagused;
    669 
    670 	sc = (ascaudio_softc_t *)opaque;
    671 
    672 	KASSERT(mutex_owned(&sc->sc_lock));
    673 
    674 	switch (di->index) {
    675 	case ASC_OUTPUT_CLASS:
    676 		di->mixer_class = ASC_OUTPUT_CLASS;
    677 		strcpy(di->label.name, AudioCoutputs);
    678 		di->type = AUDIO_MIXER_CLASS;
    679 		di->next = di->prev = AUDIO_MIXER_LAST;
    680 		return 0;
    681 	case ASC_INPUT_CLASS:
    682 		di->mixer_class = ASC_INPUT_CLASS;
    683 		strcpy(di->label.name, AudioCinputs);
    684 		di->type = AUDIO_MIXER_CLASS;
    685 		di->next = di->prev = AUDIO_MIXER_LAST;
    686 		return 0;
    687 	case ASC_OUTPUT_MASTER_VOLUME:
    688 		di->mixer_class = ASC_OUTPUT_CLASS;
    689 		strcpy(di->label.name, AudioNmaster);
    690 		di->type = AUDIO_MIXER_VALUE;
    691 		di->next = di->prev = AUDIO_MIXER_LAST;
    692 		di->un.v.num_channels = 1;
    693 		strcpy(di->un.v.units.name, AudioNvolume);
    694 		return 0;
    695 	case ASC_INPUT_DAC_VOLUME:
    696 		di->mixer_class = ASC_INPUT_CLASS;
    697 		strcpy(di->label.name, AudioNdac);
    698 		di->type = AUDIO_MIXER_VALUE;
    699 		di->next = di->prev = AUDIO_MIXER_LAST;
    700 		di->un.v.num_channels = 1;
    701 		strcpy(di->un.v.units.name, AudioNvolume);
    702 		return 0;
    703 	}
    704 
    705 	return ENXIO;
    706 }
    707 
    708 static int
    709 ascaudio_get_props(void *opaque)
    710 {
    711 
    712 	return AUDIO_PROP_PLAYBACK | AUDIO_PROP_CAPTURE |
    713 	    AUDIO_PROP_INDEPENDENT;
    714 }
    715 
    716 static int
    717 ascaudio_round_blocksize(void *opaque, int blksize, int mode,
    718     const audio_params_t *p)
    719 {
    720 	ascaudio_softc_t *sc __diagused;
    721 
    722 	sc = (ascaudio_softc_t *)opaque;
    723 	KASSERT(mutex_owned(&sc->sc_lock));
    724 
    725 	if (mode == AUMODE_PLAY)
    726 		return PLAYBLKSIZE;
    727 	else
    728 		return RECBLKSIZE;
    729 }
    730 
    731 static void
    732 ascaudio_get_locks(void *opaque, kmutex_t **intr, kmutex_t **thread)
    733 {
    734 	ascaudio_softc_t *sc;
    735 
    736 	sc = (ascaudio_softc_t *)opaque;
    737 
    738 	*intr = &sc->sc_intr_lock;
    739 	*thread = &sc->sc_lock;
    740 }
    741 
    742 static int
    743 ascaudio_intr_est(void *arg)
    744 {
    745 	ascaudio_intr(arg);
    746 
    747 	return 0;
    748 }
    749 
    750 static void
    751 ascaudio_intr(void *arg)
    752 {
    753 	struct ascaudio_softc *sc = arg;
    754 	uint8_t val;
    755 	int loc_a, loc_b, total, count, i;
    756 
    757 	if (!sc)
    758 		return;
    759 
    760 	if (!sc->sc_pintr && !sc->sc_rintr)
    761 		return;
    762 
    763 	mutex_enter(&sc->sc_intr_lock);
    764 
    765 	count = 0x200;
    766 	if (sc->sc_rintr) {
    767 		if (sc->sc_ver == EASC_VER || sc->sc_ver == EASC_VER2)
    768 			bus_space_write_1(sc->sc_tag, sc->sc_handle,
    769 			    IRQA, DISABLEHALFIRQ);
    770 
    771 		total = count;
    772 		if (sc->sc_rptr + count >= sc->sc_recbuf + BUFSIZE)
    773 			count = sc->sc_recbuf + BUFSIZE - sc->sc_rptr;
    774 		while (total) {
    775 			if (sc->sc_ver == EASC_VER2) {
    776 				loc_a = FIFO_A_ALT;
    777 				loc_b = FIFO_B_ALT;
    778 			} else {
    779 				loc_a = FIFO_A;
    780 				loc_b = 0;
    781 			}
    782 			for (i = 0; i < count; i++) {
    783 				val = bus_space_read_1(sc->sc_tag,
    784 				    sc->sc_handle, loc_a);
    785 				val ^= 0x80;
    786 				*sc->sc_rptr++ = val;
    787 				if (loc_b) {
    788 					(void)bus_space_read_1
    789 					    (sc->sc_tag, sc->sc_handle, loc_b);
    790 				}
    791 			}
    792 			if (sc->sc_rptr >= sc->sc_recbuf + BUFSIZE)
    793 				sc->sc_rptr = sc->sc_recbuf;
    794 			total -= count;
    795 			sc->sc_recavail += count;
    796 			count = total;
    797 		}
    798 
    799 		if (sc->sc_recavail > BUFSIZE)
    800 			sc->sc_recavail = BUFSIZE;
    801 
    802 		if (sc->sc_ver == EASC_VER || sc->sc_ver == EASC_VER2)
    803 			bus_space_write_1(sc->sc_tag, sc->sc_handle, IRQA, 0);
    804 
    805 		goto more;
    806 	}
    807 
    808 	count = 0x200;
    809 	if (sc->sc_slowcpu)
    810 		count /= 2;
    811 
    812 	if (sc->sc_avail < count) {
    813 		if (sc->sc_avail) {
    814 			count = sc->sc_avail;
    815 			goto fill_fifo;
    816 		}
    817 		if (sc->sc_pintr) {
    818 			for (i = 0; i < 0x200; i++) {
    819 				bus_space_write_1(sc->sc_tag,
    820 				    sc->sc_handle, FIFO_A, 0x80);
    821 				bus_space_write_1(sc->sc_tag,
    822 				    sc->sc_handle, FIFO_B, 0x80);
    823 			}
    824 		} else {
    825 			if (sc->sc_slowcpu)
    826 				count *= 2;
    827 			for (i = 0; i < count; i++) {
    828 				bus_space_write_1(sc->sc_tag,
    829 				    sc->sc_handle, FIFO_B, 0x80);
    830 			}
    831 		}
    832 		goto more;
    833 	}
    834 
    835 fill_fifo:
    836 	if (sc->sc_ver == EASC_VER || sc->sc_ver == EASC_VER2)
    837 		bus_space_write_1(sc->sc_tag, sc->sc_handle, IRQB,
    838 		    DISABLEHALFIRQ);
    839 
    840 	total = count;
    841 	if (sc->sc_wptr + count >= sc->sc_playbuf + BUFSIZE)
    842 		count = sc->sc_playbuf + BUFSIZE - sc->sc_wptr;
    843 
    844 	while (total) {
    845 		if (sc->sc_slowcpu) {
    846 			for (i = 0; i < count; i++) {
    847 				val = *sc->sc_wptr++;
    848 				val ^= 0x80;
    849 				bus_space_write_1(sc->sc_tag,
    850 				    sc->sc_handle, FIFO_A, val);
    851 				bus_space_write_1(sc->sc_tag,
    852 				    sc->sc_handle, FIFO_B, val);
    853 				bus_space_write_1(sc->sc_tag,
    854 				    sc->sc_handle, FIFO_A, val);
    855 				bus_space_write_1(sc->sc_tag,
    856 				    sc->sc_handle, FIFO_B, val);
    857 			}
    858 		} else {
    859 			for (i = 0; i < count; i++) {
    860 				val = *sc->sc_wptr++;
    861 				val ^= 0x80;
    862 				bus_space_write_1(sc->sc_tag,
    863 				    sc->sc_handle, FIFO_A, val);
    864 				bus_space_write_1(sc->sc_tag,
    865 				    sc->sc_handle, FIFO_B, val);
    866 			}
    867 		}
    868 		if (sc->sc_wptr >= sc->sc_playbuf + BUFSIZE)
    869 			sc->sc_wptr = sc->sc_playbuf;
    870 		total -= count;
    871 		sc->sc_avail -= count;
    872 		count = total;
    873 	}
    874 	if (sc->sc_ver == EASC_VER || sc->sc_ver == EASC_VER2)
    875 		bus_space_write_1(sc->sc_tag, sc->sc_handle, IRQB, 0);
    876 
    877 more:
    878 	if (sc->sc_pintr && (sc->sc_avail <= PLAYBLKSIZE))
    879 		callout_schedule(&sc->sc_pcallout, 0);
    880 
    881 	if (sc->sc_rintr && (sc->sc_recavail >= RECBLKSIZE))
    882 		callout_schedule(&sc->sc_rcallout, 0);
    883 
    884 	mutex_exit(&sc->sc_intr_lock);
    885 }
    886 
    887 static void
    888 ascaudio_intr_enable(void)
    889 {
    890 	int s;
    891 
    892 	s = splhigh();
    893 	if (VIA2 == VIA2OFF)
    894 		via2_reg(vIER) = 0x80 | V2IF_ASC;
    895 	else
    896 		via2_reg(rIER) = 0x80 | V2IF_ASC;
    897 	splx(s);
    898 }
    899 
    900 static void
    901 ascaudio_done_output(void *arg)
    902 {
    903 	struct ascaudio_softc *sc = arg;
    904 
    905 	mutex_enter(&sc->sc_intr_lock);
    906 	if (sc->sc_pintr)
    907 		(*sc->sc_pintr)(sc->sc_pintrarg);
    908 	mutex_exit(&sc->sc_intr_lock);
    909 }
    910 
    911 static void
    912 ascaudio_done_input(void *arg)
    913 {
    914 	struct ascaudio_softc *sc = arg;
    915 
    916 	mutex_enter(&sc->sc_intr_lock);
    917 	if (sc->sc_rintr)
    918 		(*sc->sc_rintr)(sc->sc_rintrarg);
    919 	mutex_exit(&sc->sc_intr_lock);
    920 }
    921 
    922 #ifdef _MODULE
    923 
    924 MODULE(MODULE_CLASS_DRIVER, ascaudio, "audio");
    925 
    926 static const struct cfiattrdata audiobuscf_iattrdata = {
    927 	"audiobus", 0, { { NULL, NULL, 0 }, }
    928 };
    929 static const struct cfiattrdata * const ascaudio_attrs[] = {
    930 	&audiobuscf_iattrdata, NULL
    931 };
    932 
    933 CFDRIVER_DECL(ascaudio, DV_DULL, ascaud_attrs);
    934 extern struct cfattach ascaudio_ca;
    935 static int ascaudioloc[] = { -1, -1 };
    936 
    937 static struct cfdata ascaudio_cfdata[] = {
    938 	{
    939 		.cf_name = "ascaudio",
    940 		.cf_atname = "ascaudio",
    941 		.cf_unit = 0,
    942 		.cf_fstate = FSTATE_STAR,
    943 		.cf_loc = ascaudioloc,
    944 		.cf_flags = 0,
    945 		.cf_pspec = NULL,
    946 	},
    947 	{ NULL, NULL, 0, 0, NULL, 0, NULL }
    948 };
    949 
    950 static int
    951 ascaudio_modcmd(modcmd_t cmd, void *arg)
    952 {
    953 	devmajor_t cmajor = NODEVMAJOR, bmajor = NODEVMAJOR;
    954 	int error;
    955 
    956 	switch (cmd) {
    957 	case MODULE_CMD_INIT:
    958 		error = config_cfdriver_attach(&ascaudio_cd);
    959 		if (error) {
    960 			return error;
    961 		}
    962 
    963 		error = config_cfattach_attach(ascaudio_cd.cd_name, &ascaud_ca);
    964 		if (error) {
    965 			config_cfdriver_detach(&ascaudio_cd);
    966 			aprint_error("%s: unable to register cfattach\n",
    967 				ascaudio_cd.cd_name);
    968 
    969 			return error;
    970 		}
    971 
    972 		error = config_cfdata_attach(ascaudio_cfdata, 1);
    973 		if (error) {
    974 			config_cfattach_detach(ascaudio_cd.cd_name, &ascaud_ca);
    975 			config_cfdriver_detach(&ascaudio_cd);
    976 			aprint_error("%s: unable to register cfdata\n",
    977 				ascaudio_cd.cd_name);
    978 
    979 			return error;
    980 		}
    981 
    982 		error = devsw_attach(ascaudio_cd.cd_name, NULL, &bmajor,
    983 		    &ascaudio_cdevsw, &cmajor);
    984 		if (error) {
    985 			error = config_cfdata_detach(ascaudio_cfdata);
    986 			if (error) {
    987 				return error;
    988 			}
    989 			config_cfattach_detach(ascaudio_cd.cd_name, &ascaud_ca);
    990 			config_cfdriver_detach(&ascaudio_cd);
    991 			aprint_error("%s: unable to register devsw\n",
    992 				ascaudio_cd.cd_name);
    993 
    994 			return error;
    995 		}
    996 
    997 		(void)config_attach_pseudo(ascaudio_cfdata);
    998 
    999 		return 0;
   1000 	case MODULE_CMD_FINI:
   1001 		error = config_cfdata_detach(ascaudio_cfdata);
   1002 		if (error) {
   1003 			return error;
   1004 		}
   1005 
   1006 		config_cfattach_detach(ascaudio_cd.cd_name, &ascaud_ca);
   1007 		config_cfdriver_detach(&ascaudio_cd);
   1008 		devsw_detach(NULL, &ascaudio_cdevsw);
   1009 
   1010 		return 0;
   1011 	default:
   1012 		return ENOTTY;
   1013 	}
   1014 }
   1015 
   1016 #endif
   1017