xref: /openbsd-src/sys/arch/hppa/gsc/harmony.c (revision 32ffafad193fd9d5bc1743017cf37d821be47b78)
1*32ffafadSjsg /*	$OpenBSD: harmony.c,v 1.41 2024/05/22 14:25:47 jsg Exp $	*/
242aaa9baSjason 
342aaa9baSjason /*
442aaa9baSjason  * Copyright (c) 2003 Jason L. Wright (jason@thought.net)
542aaa9baSjason  * All rights reserved.
642aaa9baSjason  *
742aaa9baSjason  * Redistribution and use in source and binary forms, with or without
842aaa9baSjason  * modification, are permitted provided that the following conditions
942aaa9baSjason  * are met:
1042aaa9baSjason  * 1. Redistributions of source code must retain the above copyright
1142aaa9baSjason  *    notice, this list of conditions and the following disclaimer.
1242aaa9baSjason  * 2. Redistributions in binary form must reproduce the above copyright
1342aaa9baSjason  *    notice, this list of conditions and the following disclaimer in the
1442aaa9baSjason  *    documentation and/or other materials provided with the distribution.
1542aaa9baSjason  *
1642aaa9baSjason  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1742aaa9baSjason  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
1842aaa9baSjason  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
1942aaa9baSjason  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
2042aaa9baSjason  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
2142aaa9baSjason  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
2242aaa9baSjason  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2342aaa9baSjason  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
2442aaa9baSjason  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
2542aaa9baSjason  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2642aaa9baSjason  * POSSIBILITY OF SUCH DAMAGE.
2742aaa9baSjason  */
2842aaa9baSjason 
29b88c9c3fSjason /*
30b88c9c3fSjason  * Harmony (CS4215/AD1849 LASI) audio interface.
31b88c9c3fSjason  */
32b88c9c3fSjason 
3342aaa9baSjason #include <sys/param.h>
347a877ae9Sjason #include <sys/kernel.h>
3542aaa9baSjason #include <sys/systm.h>
3642aaa9baSjason #include <sys/errno.h>
3742aaa9baSjason #include <sys/ioctl.h>
3842aaa9baSjason #include <sys/device.h>
3942aaa9baSjason #include <sys/proc.h>
4042aaa9baSjason #include <sys/malloc.h>
4142aaa9baSjason 
4242aaa9baSjason #include <sys/audioio.h>
4342aaa9baSjason #include <dev/audio_if.h>
4442aaa9baSjason 
4542aaa9baSjason #include <machine/cpu.h>
4642aaa9baSjason #include <machine/intr.h>
4742aaa9baSjason #include <machine/iomod.h>
4842aaa9baSjason #include <machine/autoconf.h>
4942aaa9baSjason #include <machine/bus.h>
5042aaa9baSjason 
5142aaa9baSjason #include <hppa/dev/cpudevs.h>
5242aaa9baSjason #include <hppa/gsc/gscbusvar.h>
53b88c9c3fSjason #include <hppa/gsc/harmonyreg.h>
54591687a2Sjason #include <hppa/gsc/harmonyvar.h>
557282f66cSjason 
5642aaa9baSjason int     harmony_open(void *, int);
5742aaa9baSjason void    harmony_close(void *);
5842aaa9baSjason int     harmony_set_params(void *, int, int, struct audio_params *,
5942aaa9baSjason     struct audio_params *);
6042aaa9baSjason int     harmony_round_blocksize(void *, int);
6142aaa9baSjason int     harmony_commit_settings(void *);
6242aaa9baSjason int     harmony_halt_output(void *);
6342aaa9baSjason int     harmony_halt_input(void *);
6442aaa9baSjason int     harmony_set_port(void *, mixer_ctrl_t *);
6542aaa9baSjason int     harmony_get_port(void *, mixer_ctrl_t *);
6642aaa9baSjason int     harmony_query_devinfo(void *addr, mixer_devinfo_t *);
67673bd31fSjason void *  harmony_allocm(void *, int, size_t, int, int);
68673bd31fSjason void    harmony_freem(void *, void *, int);
6942aaa9baSjason size_t  harmony_round_buffersize(void *, int, size_t);
7042aaa9baSjason int     harmony_trigger_output(void *, void *, void *, int,
717a877ae9Sjason     void (*intr)(void *), void *, struct audio_params *);
7242aaa9baSjason int     harmony_trigger_input(void *, void *, void *, int,
737a877ae9Sjason     void (*intr)(void *), void *, struct audio_params *);
7442aaa9baSjason 
750d6a2fdeSmiod const struct audio_hw_if harmony_sa_hw_if = {
765a8d990aSkn 	.open = harmony_open,
775a8d990aSkn 	.close = harmony_close,
785a8d990aSkn 	.set_params = harmony_set_params,
795a8d990aSkn 	.round_blocksize = harmony_round_blocksize,
805a8d990aSkn 	.commit_settings = harmony_commit_settings,
815a8d990aSkn 	.halt_output = harmony_halt_output,
825a8d990aSkn 	.halt_input = harmony_halt_input,
835a8d990aSkn 	.set_port = harmony_set_port,
845a8d990aSkn 	.get_port = harmony_get_port,
855a8d990aSkn 	.query_devinfo = harmony_query_devinfo,
865a8d990aSkn 	.allocm = harmony_allocm,
875a8d990aSkn 	.freem = harmony_freem,
885a8d990aSkn 	.round_buffersize = harmony_round_buffersize,
895a8d990aSkn 	.trigger_output = harmony_trigger_output,
905a8d990aSkn 	.trigger_input = harmony_trigger_input,
9142aaa9baSjason };
9242aaa9baSjason 
9342aaa9baSjason int harmony_match(struct device *, void *, void *);
9442aaa9baSjason void harmony_attach(struct device *, struct device *, void *);
9542aaa9baSjason int harmony_intr(void *);
9642aaa9baSjason void harmony_intr_enable(struct harmony_softc *);
9742aaa9baSjason void harmony_intr_disable(struct harmony_softc *);
98673bd31fSjason u_int32_t harmony_speed_bits(struct harmony_softc *, u_long *);
99fe6bde3cSjason int harmony_set_gainctl(struct harmony_softc *);
1007282f66cSjason void harmony_reset_codec(struct harmony_softc *);
101167728ffSjason void harmony_start_cp(struct harmony_softc *);
1027a877ae9Sjason void harmony_try_more(struct harmony_softc *);
10342aaa9baSjason 
1047180d4afSmickey void harmony_acc_tmo(void *);
1057180d4afSmickey #define	ADD_CLKALLICA(sc) do {						\
1067180d4afSmickey 	(sc)->sc_acc <<= 1;						\
1077180d4afSmickey 	(sc)->sc_acc |= READ_REG((sc), HARMONY_DIAG) & DIAG_CO;		\
1087180d4afSmickey 	if ((sc)->sc_acc_cnt++ && !((sc)->sc_acc_cnt % 32))		\
1099e9abf5bSjasper 		enqueue_randomness((sc)->sc_acc_num ^= (sc)->sc_acc);	\
1107180d4afSmickey } while(0)
1117180d4afSmickey 
11242aaa9baSjason int
harmony_match(parent,match,aux)11342aaa9baSjason harmony_match(parent, match, aux)
11442aaa9baSjason 	struct device *parent;
11542aaa9baSjason 	void *match, *aux;
11642aaa9baSjason {
11742aaa9baSjason 	struct gsc_attach_args *ga = aux;
11814fe4800Smickey 	bus_space_handle_t bh;
11914fe4800Smickey 	u_int32_t cntl;
12042aaa9baSjason 
12142aaa9baSjason 	if (ga->ga_type.iodc_type == HPPA_TYPE_FIO) {
12242aaa9baSjason 		if (ga->ga_type.iodc_sv_model == HPPA_FIO_A1 ||
12342aaa9baSjason 		    ga->ga_type.iodc_sv_model == HPPA_FIO_A2NB ||
12442aaa9baSjason 		    ga->ga_type.iodc_sv_model == HPPA_FIO_A1NB ||
12514fe4800Smickey 		    ga->ga_type.iodc_sv_model == HPPA_FIO_A2) {
12614fe4800Smickey 			if (bus_space_map(ga->ga_iot, ga->ga_hpa,
12714fe4800Smickey 			    HARMONY_NREGS, 0, &bh) != 0)
12814fe4800Smickey 				return (0);
12914fe4800Smickey 			cntl = bus_space_read_4(ga->ga_iot, bh, HARMONY_ID) &
13014fe4800Smickey 			    ID_REV_MASK;
13114fe4800Smickey 			bus_space_unmap(ga->ga_iot, bh, HARMONY_NREGS);
13214fe4800Smickey 			if (cntl == ID_REV_TS || cntl == ID_REV_NOTS)
13342aaa9baSjason 				return (1);
13442aaa9baSjason 		}
13514fe4800Smickey 	}
13642aaa9baSjason 	return (0);
13742aaa9baSjason }
13842aaa9baSjason 
13942aaa9baSjason void
harmony_attach(parent,self,aux)14042aaa9baSjason harmony_attach(parent, self, aux)
14142aaa9baSjason 	struct device *parent, *self;
14242aaa9baSjason 	void *aux;
14342aaa9baSjason {
14442aaa9baSjason 	struct harmony_softc *sc = (struct harmony_softc *)self;
14542aaa9baSjason 	struct gsc_attach_args *ga = aux;
1464de9670dSjason 	u_int8_t rev;
147f182043cSmickey 	u_int32_t cntl;
14842aaa9baSjason 	int i;
14942aaa9baSjason 
15042aaa9baSjason 	sc->sc_bt = ga->ga_iot;
15142aaa9baSjason 	sc->sc_dmat = ga->ga_dmatag;
15242aaa9baSjason 
15342aaa9baSjason 	if (bus_space_map(sc->sc_bt, ga->ga_hpa, HARMONY_NREGS, 0,
15442aaa9baSjason 	    &sc->sc_bh) != 0) {
15542aaa9baSjason 		printf(": couldn't map registers\n");
15642aaa9baSjason 		return;
15742aaa9baSjason 	}
15842aaa9baSjason 
15927b54b8bSmickey 	cntl = READ_REG(sc, HARMONY_ID);
16014fe4800Smickey 	sc->sc_teleshare = (cntl & ID_REV_MASK) == ID_REV_TS;
16127b54b8bSmickey 
16242aaa9baSjason 	if (bus_dmamem_alloc(sc->sc_dmat, sizeof(struct harmony_empty),
16342aaa9baSjason 	    PAGE_SIZE, 0, &sc->sc_empty_seg, 1, &sc->sc_empty_rseg,
16442aaa9baSjason 	    BUS_DMA_NOWAIT) != 0) {
16527b54b8bSmickey 		printf(": couldn't alloc DMA memory\n");
16627b54b8bSmickey 		bus_space_unmap(sc->sc_bt, sc->sc_bh, HARMONY_NREGS);
16742aaa9baSjason 		return;
16842aaa9baSjason 	}
16942aaa9baSjason 	if (bus_dmamem_map(sc->sc_dmat, &sc->sc_empty_seg, 1,
17042aaa9baSjason 	    sizeof(struct harmony_empty), (caddr_t *)&sc->sc_empty_kva,
17142aaa9baSjason 	    BUS_DMA_NOWAIT) != 0) {
17227b54b8bSmickey 		printf(": couldn't map DMA memory\n");
17342aaa9baSjason 		bus_dmamem_free(sc->sc_dmat, &sc->sc_empty_seg,
17442aaa9baSjason 		    sc->sc_empty_rseg);
17527b54b8bSmickey 		bus_space_unmap(sc->sc_bt, sc->sc_bh, HARMONY_NREGS);
17642aaa9baSjason 		return;
17742aaa9baSjason 	}
17842aaa9baSjason 	if (bus_dmamap_create(sc->sc_dmat, sizeof(struct harmony_empty), 1,
17942aaa9baSjason 	    sizeof(struct harmony_empty), 0, BUS_DMA_NOWAIT,
18042aaa9baSjason 	    &sc->sc_empty_map) != 0) {
18127b54b8bSmickey 		printf(": can't create DMA map\n");
18242aaa9baSjason 		bus_dmamem_unmap(sc->sc_dmat, (caddr_t)sc->sc_empty_kva,
18342aaa9baSjason 		    sizeof(struct harmony_empty));
18442aaa9baSjason 		bus_dmamem_free(sc->sc_dmat, &sc->sc_empty_seg,
18542aaa9baSjason 		    sc->sc_empty_rseg);
18627b54b8bSmickey 		bus_space_unmap(sc->sc_bt, sc->sc_bh, HARMONY_NREGS);
18742aaa9baSjason 		return;
18842aaa9baSjason 	}
18942aaa9baSjason 	if (bus_dmamap_load(sc->sc_dmat, sc->sc_empty_map, sc->sc_empty_kva,
19042aaa9baSjason 	    sizeof(struct harmony_empty), NULL, BUS_DMA_NOWAIT) != 0) {
19127b54b8bSmickey 		printf(": can't load DMA map\n");
19242aaa9baSjason 		bus_dmamap_destroy(sc->sc_dmat, sc->sc_empty_map);
19342aaa9baSjason 		bus_dmamem_unmap(sc->sc_dmat, (caddr_t)sc->sc_empty_kva,
19442aaa9baSjason 		    sizeof(struct harmony_empty));
19542aaa9baSjason 		bus_dmamem_free(sc->sc_dmat, &sc->sc_empty_seg,
19642aaa9baSjason 		    sc->sc_empty_rseg);
19727b54b8bSmickey 		bus_space_unmap(sc->sc_bt, sc->sc_bh, HARMONY_NREGS);
19842aaa9baSjason 		return;
19942aaa9baSjason 	}
20042aaa9baSjason 
20142aaa9baSjason 	sc->sc_playback_empty = 0;
20242aaa9baSjason 	for (i = 0; i < PLAYBACK_EMPTYS; i++)
20342aaa9baSjason 		sc->sc_playback_paddrs[i] =
20442aaa9baSjason 		    sc->sc_empty_map->dm_segs[0].ds_addr +
20542aaa9baSjason 		    offsetof(struct harmony_empty, playback[i][0]);
20642aaa9baSjason 
20742aaa9baSjason 	sc->sc_capture_empty = 0;
20842aaa9baSjason 	for (i = 0; i < CAPTURE_EMPTYS; i++)
20942aaa9baSjason 		sc->sc_capture_paddrs[i] =
21042aaa9baSjason 		    sc->sc_empty_map->dm_segs[0].ds_addr +
21142aaa9baSjason 		    offsetof(struct harmony_empty, playback[i][0]);
21242aaa9baSjason 
21342aaa9baSjason 	bus_dmamap_sync(sc->sc_dmat, sc->sc_empty_map,
21442aaa9baSjason 	    offsetof(struct harmony_empty, playback[0][0]),
21542aaa9baSjason 	    PLAYBACK_EMPTYS * HARMONY_BUFSIZE, BUS_DMASYNC_PREWRITE);
21642aaa9baSjason 
2177c4844bbSmickey 	(void)gsc_intr_establish((struct gsc_softc *)parent, ga->ga_irq,
2187c4844bbSmickey 	    IPL_AUDIO, harmony_intr, sc, sc->sc_dv.dv_xname);
21942aaa9baSjason 
2207282f66cSjason 	/* set defaults */
2217282f66cSjason 	sc->sc_in_port = HARMONY_IN_LINE;
222591687a2Sjason 	sc->sc_out_port = HARMONY_OUT_SPEAKER;
223b88c9c3fSjason 	sc->sc_input_lvl.left = sc->sc_input_lvl.right = 240;
224b88c9c3fSjason 	sc->sc_output_lvl.left = sc->sc_output_lvl.right = 244;
225b88c9c3fSjason 	sc->sc_monitor_lvl.left = sc->sc_monitor_lvl.right = 208;
2267c84e6fbSjason 	sc->sc_outputgain = 0;
22742aaa9baSjason 
2287282f66cSjason 	/* reset chip, and push default gain controls */
2297282f66cSjason 	harmony_reset_codec(sc);
230532b98aeSjason 
231f182043cSmickey 	cntl = READ_REG(sc, HARMONY_CNTL);
2324de9670dSjason 	rev = (cntl & CNTL_CODEC_REV_MASK) >> CNTL_CODEC_REV_SHIFT;
23327b54b8bSmickey 	printf(": rev %u", rev);
23427b54b8bSmickey 
23527b54b8bSmickey 	if (sc->sc_teleshare)
23627b54b8bSmickey 		printf(", teleshare");
23727b54b8bSmickey 	printf("\n");
2384de9670dSjason 
2394de9670dSjason 	if ((rev & CS4215_REV_VER) >= CS4215_REV_VER_E)
2404de9670dSjason 		sc->sc_hasulinear8 = 1;
24142aaa9baSjason 
2422baa08e2Santon 	audio_attach_mi(&harmony_sa_hw_if, sc, NULL, &sc->sc_dv);
2437180d4afSmickey 
2447180d4afSmickey 	timeout_set(&sc->sc_acc_tmo, harmony_acc_tmo, sc);
2457180d4afSmickey 	sc->sc_acc_num = 0xa5a5a5a5;
24642aaa9baSjason }
24742aaa9baSjason 
2487282f66cSjason void
harmony_reset_codec(struct harmony_softc * sc)2497282f66cSjason harmony_reset_codec(struct harmony_softc *sc)
2507282f66cSjason {
2517282f66cSjason 	/* silence */
2527282f66cSjason 	WRITE_REG(sc, HARMONY_GAINCTL, GAINCTL_OUTPUT_LEFT_M |
2537282f66cSjason 	    GAINCTL_OUTPUT_RIGHT_M | GAINCTL_MONITOR_M);
2547282f66cSjason 
2557282f66cSjason 	/* start reset */
2567282f66cSjason 	WRITE_REG(sc, HARMONY_RESET, RESET_RST);
2577282f66cSjason 
2587282f66cSjason 	DELAY(100000);		/* wait at least 0.05 sec */
2597282f66cSjason 
2607282f66cSjason 	harmony_set_gainctl(sc);
2617282f66cSjason 	WRITE_REG(sc, HARMONY_RESET, 0);
2627282f66cSjason }
2637282f66cSjason 
2647180d4afSmickey void
harmony_acc_tmo(void * v)2657180d4afSmickey harmony_acc_tmo(void *v)
2667180d4afSmickey {
2677180d4afSmickey 	struct harmony_softc *sc = v;
2687180d4afSmickey 
2697180d4afSmickey 	ADD_CLKALLICA(sc);
2707180d4afSmickey 	timeout_add(&sc->sc_acc_tmo, 1);
2717180d4afSmickey }
2727180d4afSmickey 
27342aaa9baSjason /*
27442aaa9baSjason  * interrupt handler
27542aaa9baSjason  */
27642aaa9baSjason int
harmony_intr(vsc)27742aaa9baSjason harmony_intr(vsc)
27842aaa9baSjason 	void *vsc;
27942aaa9baSjason {
28042aaa9baSjason 	struct harmony_softc *sc = vsc;
281167728ffSjason 	struct harmony_channel *c;
28242aaa9baSjason 	u_int32_t dstatus;
283673bd31fSjason 	int r = 0;
284673bd31fSjason 
285886882aaSratchov 	mtx_enter(&audio_lock);
2867180d4afSmickey 	ADD_CLKALLICA(sc);
2877180d4afSmickey 
28842aaa9baSjason 	harmony_intr_disable(sc);
289532b98aeSjason 
2907282f66cSjason 	dstatus = READ_REG(sc, HARMONY_DSTATUS);
29142aaa9baSjason 
292b88c9c3fSjason 	if (dstatus & DSTATUS_PN) {
2937a877ae9Sjason 		struct harmony_dma *d;
2947a877ae9Sjason 		bus_addr_t nextaddr;
2957a877ae9Sjason 		bus_size_t togo;
2967a877ae9Sjason 
297673bd31fSjason 		r = 1;
2987a877ae9Sjason 		c = &sc->sc_playback;
2997a877ae9Sjason 		d = c->c_current;
3007a877ae9Sjason 		togo = c->c_segsz - c->c_cnt;
3017a877ae9Sjason 		if (togo == 0) {
3027a877ae9Sjason 			nextaddr = d->d_map->dm_segs[0].ds_addr;
3037a877ae9Sjason 			c->c_cnt = togo = c->c_blksz;
3047a877ae9Sjason 		} else {
3057a877ae9Sjason 			nextaddr = c->c_lastaddr;
3067a877ae9Sjason 			if (togo > c->c_blksz)
3077a877ae9Sjason 				togo = c->c_blksz;
3087a877ae9Sjason 			c->c_cnt += togo;
3097a877ae9Sjason 		}
3107a877ae9Sjason 
3117a877ae9Sjason 		bus_dmamap_sync(sc->sc_dmat, d->d_map,
3127a877ae9Sjason 		    nextaddr - d->d_map->dm_segs[0].ds_addr,
3137a877ae9Sjason 		    c->c_blksz, BUS_DMASYNC_PREWRITE);
3147a877ae9Sjason 
3157a877ae9Sjason 		WRITE_REG(sc, HARMONY_PNXTADD, nextaddr);
3167a877ae9Sjason 		SYNC_REG(sc, HARMONY_PNXTADD, BUS_SPACE_BARRIER_WRITE);
3177a877ae9Sjason 		c->c_lastaddr = nextaddr + togo;
3187a877ae9Sjason 		harmony_try_more(sc);
319673bd31fSjason 	}
32042aaa9baSjason 
321c20c6401Smickey 	dstatus = READ_REG(sc, HARMONY_DSTATUS);
322c20c6401Smickey 
323b88c9c3fSjason 	if (dstatus & DSTATUS_RN) {
324167728ffSjason 		c = &sc->sc_capture;
325673bd31fSjason 		r = 1;
326167728ffSjason 		harmony_start_cp(sc);
327167728ffSjason 		if (sc->sc_capturing && c->c_intr != NULL)
328b88c9c3fSjason 			(*c->c_intr)(c->c_intrarg);
329b88c9c3fSjason 	}
33042aaa9baSjason 
33127b54b8bSmickey 	if (READ_REG(sc, HARMONY_OV) & OV_OV) {
33227b54b8bSmickey 		sc->sc_ov = 1;
33327b54b8bSmickey 		WRITE_REG(sc, HARMONY_OV, 0);
33427b54b8bSmickey 	} else
33527b54b8bSmickey 		sc->sc_ov = 0;
33627b54b8bSmickey 
33742aaa9baSjason 	harmony_intr_enable(sc);
338886882aaSratchov 	mtx_leave(&audio_lock);
339673bd31fSjason 	return (r);
34042aaa9baSjason }
34142aaa9baSjason 
34242aaa9baSjason void
harmony_intr_enable(struct harmony_softc * sc)34342aaa9baSjason harmony_intr_enable(struct harmony_softc *sc)
34442aaa9baSjason {
3457282f66cSjason 	WRITE_REG(sc, HARMONY_DSTATUS, DSTATUS_IE);
3467282f66cSjason 	SYNC_REG(sc, HARMONY_DSTATUS, BUS_SPACE_BARRIER_WRITE);
34742aaa9baSjason }
34842aaa9baSjason 
34942aaa9baSjason void
harmony_intr_disable(struct harmony_softc * sc)35042aaa9baSjason harmony_intr_disable(struct harmony_softc *sc)
35142aaa9baSjason {
3527282f66cSjason 	WRITE_REG(sc, HARMONY_DSTATUS, 0);
3537282f66cSjason 	SYNC_REG(sc, HARMONY_DSTATUS, BUS_SPACE_BARRIER_WRITE);
35442aaa9baSjason }
35542aaa9baSjason 
35642aaa9baSjason int
harmony_open(void * vsc,int flags)35742aaa9baSjason harmony_open(void *vsc, int flags)
35842aaa9baSjason {
35942aaa9baSjason 	struct harmony_softc *sc = vsc;
36042aaa9baSjason 
36142aaa9baSjason 	if (sc->sc_open)
36242aaa9baSjason 		return (EBUSY);
36342aaa9baSjason 	sc->sc_open = 1;
36442aaa9baSjason 	return (0);
36542aaa9baSjason }
36642aaa9baSjason 
36742aaa9baSjason void
harmony_close(void * vsc)36842aaa9baSjason harmony_close(void *vsc)
36942aaa9baSjason {
37042aaa9baSjason 	struct harmony_softc *sc = vsc;
37142aaa9baSjason 
372886882aaSratchov 	/* XXX: not useful, halt_*() already called */
37342aaa9baSjason 	harmony_halt_input(sc);
37442aaa9baSjason 	harmony_halt_output(sc);
3757282f66cSjason 	harmony_intr_disable(sc);
37642aaa9baSjason 	sc->sc_open = 0;
37742aaa9baSjason }
37842aaa9baSjason 
37942aaa9baSjason int
harmony_set_params(void * vsc,int setmode,int usemode,struct audio_params * p,struct audio_params * r)38042aaa9baSjason harmony_set_params(void *vsc, int setmode, int usemode,
38142aaa9baSjason     struct audio_params *p, struct audio_params *r)
38242aaa9baSjason {
38342aaa9baSjason 	struct harmony_softc *sc = vsc;
38442aaa9baSjason 	u_int32_t bits;
38542aaa9baSjason 
38642aaa9baSjason 	switch (p->encoding) {
38742aaa9baSjason 	case AUDIO_ENCODING_ULAW:
38842aaa9baSjason 		bits = CNTL_FORMAT_ULAW;
389ac2bb4f3Sratchov 		p->precision = 8;
39042aaa9baSjason 		break;
39142aaa9baSjason 	case AUDIO_ENCODING_ALAW:
39242aaa9baSjason 		bits = CNTL_FORMAT_ALAW;
393ac2bb4f3Sratchov 		p->precision = 8;
39442aaa9baSjason 		break;
39542aaa9baSjason 	case AUDIO_ENCODING_SLINEAR_BE:
3964de9670dSjason 		if (p->precision == 16) {
39742aaa9baSjason 			bits = CNTL_FORMAT_SLINEAR16BE;
39842aaa9baSjason 			break;
3994de9670dSjason 		}
40042aaa9baSjason 		return (EINVAL);
4014de9670dSjason 	case AUDIO_ENCODING_ULINEAR_LE:
402ac2bb4f3Sratchov 	case AUDIO_ENCODING_ULINEAR_BE:
4034de9670dSjason 		if (p->precision == 8) {
4044de9670dSjason 			bits = CNTL_FORMAT_ULINEAR8;
4054de9670dSjason 			break;
4064de9670dSjason 		}
4074de9670dSjason 		return (EINVAL);
40842aaa9baSjason 	default:
40942aaa9baSjason 		return (EINVAL);
41042aaa9baSjason 	}
41142aaa9baSjason 
4127c84e6fbSjason 	if (sc->sc_outputgain)
4137c84e6fbSjason 		bits |= CNTL_OLB;
4147c84e6fbSjason 
41542aaa9baSjason 	if (p->channels == 1)
41642aaa9baSjason 		bits |= CNTL_CHANS_MONO;
41742aaa9baSjason 	else if (p->channels == 2)
41842aaa9baSjason 		bits |= CNTL_CHANS_STEREO;
41942aaa9baSjason 	else
42042aaa9baSjason 		return (EINVAL);
42142aaa9baSjason 
422ac2bb4f3Sratchov 	r->sample_rate = p->sample_rate;
423ac2bb4f3Sratchov 	r->encoding = p->encoding;
424ac2bb4f3Sratchov 	r->precision = p->precision;
42516579317Sjakemsr 	p->bps = AUDIO_BPS(p->precision);
42616579317Sjakemsr 	r->bps = AUDIO_BPS(r->precision);
42716579317Sjakemsr 	p->msb = r->msb = 1;
42816579317Sjakemsr 
42916579317Sjakemsr 	bits |= harmony_speed_bits(sc, &p->sample_rate);
43042aaa9baSjason 	sc->sc_cntlbits = bits;
43142aaa9baSjason 	sc->sc_need_commit = 1;
43242aaa9baSjason 
43342aaa9baSjason 	return (0);
43442aaa9baSjason }
43542aaa9baSjason 
43642aaa9baSjason int
harmony_round_blocksize(void * vsc,int blk)43742aaa9baSjason harmony_round_blocksize(void *vsc, int blk)
43842aaa9baSjason {
439673bd31fSjason 	return (HARMONY_BUFSIZE);
44042aaa9baSjason }
44142aaa9baSjason 
44242aaa9baSjason int
harmony_commit_settings(void * vsc)44342aaa9baSjason harmony_commit_settings(void *vsc)
44442aaa9baSjason {
44542aaa9baSjason 	struct harmony_softc *sc = vsc;
4467282f66cSjason 	u_int32_t reg;
44742aaa9baSjason 	u_int8_t quietchar;
44842aaa9baSjason 	int i;
44942aaa9baSjason 
45042aaa9baSjason 	if (sc->sc_need_commit == 0)
45142aaa9baSjason 		return (0);
45242aaa9baSjason 
4537282f66cSjason 	harmony_intr_disable(sc);
4547282f66cSjason 
4557282f66cSjason 	for (;;) {
4567282f66cSjason 		reg = READ_REG(sc, HARMONY_DSTATUS);
4577282f66cSjason 		if ((reg & (DSTATUS_PC | DSTATUS_RC)) == 0)
4587282f66cSjason 			break;
4597282f66cSjason 	}
4607282f66cSjason 
461fe6bde3cSjason 	/* Setting some bits in gainctl requires a reset */
462fe6bde3cSjason 	harmony_reset_codec(sc);
46342aaa9baSjason 
46442aaa9baSjason 	/* set the silence character based on the encoding type */
46542aaa9baSjason 	bus_dmamap_sync(sc->sc_dmat, sc->sc_empty_map,
46642aaa9baSjason 	    offsetof(struct harmony_empty, playback[0][0]),
46742aaa9baSjason 	    PLAYBACK_EMPTYS * HARMONY_BUFSIZE, BUS_DMASYNC_POSTWRITE);
46842aaa9baSjason 	switch (sc->sc_cntlbits & CNTL_FORMAT_MASK) {
46942aaa9baSjason 	case CNTL_FORMAT_ULAW:
47042aaa9baSjason 		quietchar = 0x7f;
47142aaa9baSjason 		break;
47242aaa9baSjason 	case CNTL_FORMAT_ALAW:
47342aaa9baSjason 		quietchar = 0x55;
47442aaa9baSjason 		break;
47542aaa9baSjason 	case CNTL_FORMAT_SLINEAR16BE:
4764de9670dSjason 	case CNTL_FORMAT_ULINEAR8:
47742aaa9baSjason 	default:
47842aaa9baSjason 		quietchar = 0;
47942aaa9baSjason 		break;
48042aaa9baSjason 	}
48142aaa9baSjason 	for (i = 0; i < PLAYBACK_EMPTYS; i++)
48242aaa9baSjason 		memset(&sc->sc_empty_kva->playback[i][0],
48342aaa9baSjason 		    quietchar, HARMONY_BUFSIZE);
48442aaa9baSjason 	bus_dmamap_sync(sc->sc_dmat, sc->sc_empty_map,
48542aaa9baSjason 	    offsetof(struct harmony_empty, playback[0][0]),
48642aaa9baSjason 	    PLAYBACK_EMPTYS * HARMONY_BUFSIZE, BUS_DMASYNC_PREWRITE);
487b88c9c3fSjason 
4887282f66cSjason 	for (;;) {
4897282f66cSjason 		/* Wait for it to come out of control mode */
4907282f66cSjason 		reg = READ_REG(sc, HARMONY_CNTL);
4917282f66cSjason 		if ((reg & CNTL_C) == 0)
4927282f66cSjason 			break;
4937282f66cSjason 	}
4947282f66cSjason 
4957282f66cSjason 	bus_space_write_4(sc->sc_bt, sc->sc_bh, HARMONY_CNTL,
4967282f66cSjason 	    sc->sc_cntlbits | CNTL_C);
4977282f66cSjason 
4987282f66cSjason 	for (;;) {
4997282f66cSjason 		/* Wait for it to come out of control mode */
5007282f66cSjason 		reg = READ_REG(sc, HARMONY_CNTL);
5017282f66cSjason 		if ((reg & CNTL_C) == 0)
5027282f66cSjason 			break;
5037282f66cSjason 	}
5047282f66cSjason 
50542aaa9baSjason 	sc->sc_need_commit = 0;
50642aaa9baSjason 
5077282f66cSjason 	if (sc->sc_playing || sc->sc_capturing)
5087282f66cSjason 		harmony_intr_enable(sc);
5097282f66cSjason 
51042aaa9baSjason 	return (0);
51142aaa9baSjason }
51242aaa9baSjason 
51342aaa9baSjason int
harmony_halt_output(void * vsc)51442aaa9baSjason harmony_halt_output(void *vsc)
51542aaa9baSjason {
51642aaa9baSjason 	struct harmony_softc *sc = vsc;
51742aaa9baSjason 
518886882aaSratchov 	/* XXX: disable interrupts */
519673bd31fSjason 	sc->sc_playing = 0;
52042aaa9baSjason 	return (0);
52142aaa9baSjason }
52242aaa9baSjason 
52342aaa9baSjason int
harmony_halt_input(void * vsc)52442aaa9baSjason harmony_halt_input(void *vsc)
52542aaa9baSjason {
52642aaa9baSjason 	struct harmony_softc *sc = vsc;
52742aaa9baSjason 
528886882aaSratchov 	/* XXX: disable interrupts */
529673bd31fSjason 	sc->sc_capturing = 0;
53042aaa9baSjason 	return (0);
53142aaa9baSjason }
53242aaa9baSjason 
53342aaa9baSjason int
harmony_set_port(void * vsc,mixer_ctrl_t * cp)53442aaa9baSjason harmony_set_port(void *vsc, mixer_ctrl_t *cp)
53542aaa9baSjason {
53642aaa9baSjason 	struct harmony_softc *sc = vsc;
53742aaa9baSjason 	int err = EINVAL;
53842aaa9baSjason 
53942aaa9baSjason 	switch (cp->dev) {
54042aaa9baSjason 	case HARMONY_PORT_INPUT_LVL:
54142aaa9baSjason 		if (cp->type != AUDIO_MIXER_VALUE)
54242aaa9baSjason 			break;
543b88c9c3fSjason 		if (cp->un.value.num_channels == 1)
544b88c9c3fSjason 			sc->sc_input_lvl.left = sc->sc_input_lvl.right =
545b88c9c3fSjason 			    cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
546b88c9c3fSjason 		else if (cp->un.value.num_channels == 2) {
547b88c9c3fSjason 			sc->sc_input_lvl.left =
548b88c9c3fSjason 			    cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
549b88c9c3fSjason 			sc->sc_input_lvl.right =
550b88c9c3fSjason 			    cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
55142aaa9baSjason 		} else
55242aaa9baSjason 			break;
553b88c9c3fSjason 		sc->sc_need_commit = 1;
55442aaa9baSjason 		err = 0;
55542aaa9baSjason 		break;
55642aaa9baSjason 	case HARMONY_PORT_OUTPUT_LVL:
55742aaa9baSjason 		if (cp->type != AUDIO_MIXER_VALUE)
55842aaa9baSjason 			break;
559b88c9c3fSjason 		if (cp->un.value.num_channels == 1)
560b88c9c3fSjason 			sc->sc_output_lvl.left = sc->sc_output_lvl.right =
561b88c9c3fSjason 			    cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
562b88c9c3fSjason 		else if (cp->un.value.num_channels == 2) {
563b88c9c3fSjason 			sc->sc_output_lvl.left =
564b88c9c3fSjason 			    cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
565b88c9c3fSjason 			sc->sc_output_lvl.right =
566b88c9c3fSjason 			    cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
56742aaa9baSjason 		} else
56842aaa9baSjason 			break;
569b88c9c3fSjason 		sc->sc_need_commit = 1;
57042aaa9baSjason 		err = 0;
57142aaa9baSjason 		break;
5727c84e6fbSjason 	case HARMONY_PORT_OUTPUT_GAIN:
5737c84e6fbSjason 		if (cp->type != AUDIO_MIXER_ENUM)
5747c84e6fbSjason 			break;
5757c84e6fbSjason 		sc->sc_outputgain = cp->un.ord ? 1 : 0;
5767c84e6fbSjason 		err = 0;
5777c84e6fbSjason 		break;
57842aaa9baSjason 	case HARMONY_PORT_MONITOR_LVL:
57942aaa9baSjason 		if (cp->type != AUDIO_MIXER_VALUE)
58042aaa9baSjason 			break;
58142aaa9baSjason 		if (cp->un.value.num_channels != 1)
58242aaa9baSjason 			break;
583b88c9c3fSjason 		sc->sc_monitor_lvl.left = sc->sc_input_lvl.right =
584b88c9c3fSjason 		    cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
585b88c9c3fSjason 		sc->sc_need_commit = 1;
58642aaa9baSjason 		err = 0;
58742aaa9baSjason 		break;
5887282f66cSjason 	case HARMONY_PORT_RECORD_SOURCE:
5897282f66cSjason 		if (cp->type != AUDIO_MIXER_ENUM)
5907282f66cSjason 			break;
5917282f66cSjason 		if (cp->un.ord != HARMONY_IN_LINE &&
5927282f66cSjason 		    cp->un.ord != HARMONY_IN_MIC)
5937282f66cSjason 			break;
5947282f66cSjason 		sc->sc_in_port = cp->un.ord;
5957282f66cSjason 		err = 0;
5967282f66cSjason 		sc->sc_need_commit = 1;
5977282f66cSjason 		break;
598591687a2Sjason 	case HARMONY_PORT_OUTPUT_SOURCE:
599591687a2Sjason 		if (cp->type != AUDIO_MIXER_ENUM)
600591687a2Sjason 			break;
601591687a2Sjason 		if (cp->un.ord != HARMONY_OUT_LINE &&
602591687a2Sjason 		    cp->un.ord != HARMONY_OUT_SPEAKER &&
603591687a2Sjason 		    cp->un.ord != HARMONY_OUT_HEADPHONE)
604591687a2Sjason 			break;
605591687a2Sjason 		sc->sc_out_port = cp->un.ord;
606591687a2Sjason 		err = 0;
607591687a2Sjason 		sc->sc_need_commit = 1;
608591687a2Sjason 		break;
60942aaa9baSjason 	}
61042aaa9baSjason 
61142aaa9baSjason 	return (err);
61242aaa9baSjason }
61342aaa9baSjason 
61442aaa9baSjason int
harmony_get_port(void * vsc,mixer_ctrl_t * cp)61542aaa9baSjason harmony_get_port(void *vsc, mixer_ctrl_t *cp)
61642aaa9baSjason {
61742aaa9baSjason 	struct harmony_softc *sc = vsc;
61842aaa9baSjason 	int err = EINVAL;
61942aaa9baSjason 
62042aaa9baSjason 	switch (cp->dev) {
62142aaa9baSjason 	case HARMONY_PORT_INPUT_LVL:
62242aaa9baSjason 		if (cp->type != AUDIO_MIXER_VALUE)
62342aaa9baSjason 			break;
62442aaa9baSjason 		if (cp->un.value.num_channels == 1) {
62542aaa9baSjason 			cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] =
626b88c9c3fSjason 			    sc->sc_input_lvl.left;
62742aaa9baSjason 		} else if (cp->un.value.num_channels == 2) {
62842aaa9baSjason 			cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] =
629b88c9c3fSjason 			    sc->sc_input_lvl.left;
63042aaa9baSjason 			cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] =
631b88c9c3fSjason 			    sc->sc_input_lvl.right;
63242aaa9baSjason 		} else
63342aaa9baSjason 			break;
63442aaa9baSjason 		err = 0;
63542aaa9baSjason 		break;
63627b54b8bSmickey 	case HARMONY_PORT_INPUT_OV:
63727b54b8bSmickey 		if (cp->type != AUDIO_MIXER_ENUM)
63827b54b8bSmickey 			break;
63927b54b8bSmickey 		cp->un.ord = sc->sc_ov ? 1 : 0;
64027b54b8bSmickey 		err = 0;
64127b54b8bSmickey 		break;
64242aaa9baSjason 	case HARMONY_PORT_OUTPUT_LVL:
64342aaa9baSjason 		if (cp->type != AUDIO_MIXER_VALUE)
64442aaa9baSjason 			break;
64542aaa9baSjason 		if (cp->un.value.num_channels == 1) {
64642aaa9baSjason 			cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] =
647b88c9c3fSjason 			    sc->sc_output_lvl.left;
64842aaa9baSjason 		} else if (cp->un.value.num_channels == 2) {
64942aaa9baSjason 			cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] =
650b88c9c3fSjason 			    sc->sc_output_lvl.left;
65142aaa9baSjason 			cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] =
652b88c9c3fSjason 			    sc->sc_output_lvl.right;
65342aaa9baSjason 		} else
65442aaa9baSjason 			break;
65542aaa9baSjason 		err = 0;
65642aaa9baSjason 		break;
6577c84e6fbSjason 	case HARMONY_PORT_OUTPUT_GAIN:
6587c84e6fbSjason 		if (cp->type != AUDIO_MIXER_ENUM)
6597c84e6fbSjason 			break;
6607c84e6fbSjason 		cp->un.ord = sc->sc_outputgain ? 1 : 0;
6617c84e6fbSjason 		err = 0;
6627c84e6fbSjason 		break;
66342aaa9baSjason 	case HARMONY_PORT_MONITOR_LVL:
66442aaa9baSjason 		if (cp->type != AUDIO_MIXER_VALUE)
66542aaa9baSjason 			break;
66642aaa9baSjason 		if (cp->un.value.num_channels != 1)
66742aaa9baSjason 			break;
66842aaa9baSjason 		cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] =
669b88c9c3fSjason 		    sc->sc_monitor_lvl.left;
67042aaa9baSjason 		err = 0;
67142aaa9baSjason 		break;
6727282f66cSjason 	case HARMONY_PORT_RECORD_SOURCE:
6737282f66cSjason 		if (cp->type != AUDIO_MIXER_ENUM)
6747282f66cSjason 			break;
6757282f66cSjason 		cp->un.ord = sc->sc_in_port;
6767282f66cSjason 		err = 0;
6777282f66cSjason 		break;
678591687a2Sjason 	case HARMONY_PORT_OUTPUT_SOURCE:
679591687a2Sjason 		if (cp->type != AUDIO_MIXER_ENUM)
680591687a2Sjason 			break;
681591687a2Sjason 		cp->un.ord = sc->sc_out_port;
682591687a2Sjason 		err = 0;
683591687a2Sjason 		break;
68442aaa9baSjason 	}
68542aaa9baSjason 	return (0);
68642aaa9baSjason }
68742aaa9baSjason 
68842aaa9baSjason int
harmony_query_devinfo(void * vsc,mixer_devinfo_t * dip)68942aaa9baSjason harmony_query_devinfo(void *vsc, mixer_devinfo_t *dip)
69042aaa9baSjason {
69142aaa9baSjason 	int err = 0;
69242aaa9baSjason 
69342aaa9baSjason 	switch (dip->index) {
69442aaa9baSjason 	case HARMONY_PORT_INPUT_LVL:
69542aaa9baSjason 		dip->type = AUDIO_MIXER_VALUE;
69642aaa9baSjason 		dip->mixer_class = HARMONY_PORT_INPUT_CLASS;
69742aaa9baSjason 		dip->prev = dip->next = AUDIO_MIXER_LAST;
698094fa01fSderaadt 		strlcpy(dip->label.name, AudioNinput, sizeof dip->label.name);
69942aaa9baSjason 		dip->un.v.num_channels = 2;
700094fa01fSderaadt 		strlcpy(dip->un.v.units.name, AudioNvolume,
701094fa01fSderaadt 		    sizeof dip->un.v.units.name);
70242aaa9baSjason 		break;
70327b54b8bSmickey 	case HARMONY_PORT_INPUT_OV:
70427b54b8bSmickey 		dip->type = AUDIO_MIXER_ENUM;
70527b54b8bSmickey 		dip->mixer_class = HARMONY_PORT_INPUT_CLASS;
70627b54b8bSmickey 		dip->prev = dip->next = AUDIO_MIXER_LAST;
707094fa01fSderaadt 		strlcpy(dip->label.name, "overrange", sizeof dip->label.name);
70827b54b8bSmickey 		dip->un.e.num_mem = 2;
709094fa01fSderaadt 		strlcpy(dip->un.e.member[0].label.name, AudioNoff,
710094fa01fSderaadt 		    sizeof dip->un.e.member[0].label.name);
71127b54b8bSmickey 		dip->un.e.member[0].ord = 0;
712094fa01fSderaadt 		strlcpy(dip->un.e.member[1].label.name, AudioNon,
713094fa01fSderaadt 		    sizeof dip->un.e.member[1].label.name);
71427b54b8bSmickey 		dip->un.e.member[1].ord = 1;
71527b54b8bSmickey 		break;
71642aaa9baSjason 	case HARMONY_PORT_OUTPUT_LVL:
71742aaa9baSjason 		dip->type = AUDIO_MIXER_VALUE;
71842aaa9baSjason 		dip->mixer_class = HARMONY_PORT_OUTPUT_CLASS;
71942aaa9baSjason 		dip->prev = dip->next = AUDIO_MIXER_LAST;
720094fa01fSderaadt 		strlcpy(dip->label.name, AudioNoutput, sizeof dip->label.name);
72142aaa9baSjason 		dip->un.v.num_channels = 2;
722094fa01fSderaadt 		strlcpy(dip->un.v.units.name, AudioNvolume,
723094fa01fSderaadt 		    sizeof dip->un.v.units.name);
72442aaa9baSjason 		break;
7257c84e6fbSjason 	case HARMONY_PORT_OUTPUT_GAIN:
7267c84e6fbSjason 		dip->type = AUDIO_MIXER_ENUM;
7277c84e6fbSjason 		dip->mixer_class = HARMONY_PORT_OUTPUT_CLASS;
7287c84e6fbSjason 		dip->prev = dip->next = AUDIO_MIXER_LAST;
729094fa01fSderaadt 		strlcpy(dip->label.name, "gain", sizeof dip->label.name);
7307c84e6fbSjason 		dip->un.e.num_mem = 2;
731094fa01fSderaadt 		strlcpy(dip->un.e.member[0].label.name, AudioNoff,
732094fa01fSderaadt 		    sizeof dip->un.e.member[0].label.name);
7337c84e6fbSjason 		dip->un.e.member[0].ord = 0;
734094fa01fSderaadt 		strlcpy(dip->un.e.member[1].label.name, AudioNon,
735094fa01fSderaadt 		    sizeof dip->un.e.member[1].label.name);
7367c84e6fbSjason 		dip->un.e.member[1].ord = 1;
7377c84e6fbSjason 		break;
73842aaa9baSjason 	case HARMONY_PORT_MONITOR_LVL:
73942aaa9baSjason 		dip->type = AUDIO_MIXER_VALUE;
74042aaa9baSjason 		dip->mixer_class = HARMONY_PORT_MONITOR_CLASS;
74142aaa9baSjason 		dip->prev = dip->next = AUDIO_MIXER_LAST;
742094fa01fSderaadt 		strlcpy(dip->label.name, AudioNmonitor, sizeof dip->label.name);
74342aaa9baSjason 		dip->un.v.num_channels = 1;
744094fa01fSderaadt 		strlcpy(dip->un.v.units.name, AudioNvolume,
745094fa01fSderaadt 		    sizeof dip->un.v.units.name);
74642aaa9baSjason 		break;
7477282f66cSjason 	case HARMONY_PORT_RECORD_SOURCE:
7487282f66cSjason 		dip->type = AUDIO_MIXER_ENUM;
7497282f66cSjason 		dip->mixer_class = HARMONY_PORT_RECORD_CLASS;
7507282f66cSjason 		dip->prev = dip->next = AUDIO_MIXER_LAST;
751094fa01fSderaadt 		strlcpy(dip->label.name, AudioNsource, sizeof dip->label.name);
7527282f66cSjason 		dip->un.e.num_mem = 2;
753094fa01fSderaadt 		strlcpy(dip->un.e.member[0].label.name, AudioNmicrophone,
754094fa01fSderaadt 		    sizeof dip->un.e.member[0].label.name);
7557282f66cSjason 		dip->un.e.member[0].ord = HARMONY_IN_MIC;
756094fa01fSderaadt 		strlcpy(dip->un.e.member[1].label.name, AudioNline,
757094fa01fSderaadt 		    sizeof dip->un.e.member[1].label.name);
7587282f66cSjason 		dip->un.e.member[1].ord = HARMONY_IN_LINE;
7597282f66cSjason 		break;
760591687a2Sjason 	case HARMONY_PORT_OUTPUT_SOURCE:
761591687a2Sjason 		dip->type = AUDIO_MIXER_ENUM;
762591687a2Sjason 		dip->mixer_class = HARMONY_PORT_MONITOR_CLASS;
763591687a2Sjason 		dip->prev = dip->next = AUDIO_MIXER_LAST;
764094fa01fSderaadt 		strlcpy(dip->label.name, AudioNoutput, sizeof dip->label.name);
765591687a2Sjason 		dip->un.e.num_mem = 3;
766094fa01fSderaadt 		strlcpy(dip->un.e.member[0].label.name, AudioNline,
767094fa01fSderaadt 		    sizeof dip->un.e.member[0].label.name);
768591687a2Sjason 		dip->un.e.member[0].ord = HARMONY_OUT_LINE;
769094fa01fSderaadt 		strlcpy(dip->un.e.member[1].label.name, AudioNspeaker,
770094fa01fSderaadt 		    sizeof dip->un.e.member[1].label.name);
771591687a2Sjason 		dip->un.e.member[1].ord = HARMONY_OUT_SPEAKER;
772094fa01fSderaadt 		strlcpy(dip->un.e.member[2].label.name, AudioNheadphone,
773094fa01fSderaadt 		    sizeof dip->un.e.member[2].label.name);
774591687a2Sjason 		dip->un.e.member[2].ord = HARMONY_OUT_HEADPHONE;
775591687a2Sjason 		break;
77642aaa9baSjason 	case HARMONY_PORT_INPUT_CLASS:
77742aaa9baSjason 		dip->type = AUDIO_MIXER_CLASS;
77842aaa9baSjason 		dip->mixer_class = HARMONY_PORT_INPUT_CLASS;
77942aaa9baSjason 		dip->prev = dip->next = AUDIO_MIXER_LAST;
780094fa01fSderaadt 		strlcpy(dip->label.name, AudioCinputs, sizeof dip->label.name);
78142aaa9baSjason 		break;
78242aaa9baSjason 	case HARMONY_PORT_OUTPUT_CLASS:
78342aaa9baSjason 		dip->type = AUDIO_MIXER_CLASS;
78442aaa9baSjason 		dip->mixer_class = HARMONY_PORT_INPUT_CLASS;
78542aaa9baSjason 		dip->prev = dip->next = AUDIO_MIXER_LAST;
786094fa01fSderaadt 		strlcpy(dip->label.name, AudioCoutputs, sizeof dip->label.name);
78742aaa9baSjason 		break;
78842aaa9baSjason 	case HARMONY_PORT_MONITOR_CLASS:
78942aaa9baSjason 		dip->type = AUDIO_MIXER_CLASS;
79042aaa9baSjason 		dip->mixer_class = HARMONY_PORT_INPUT_CLASS;
79142aaa9baSjason 		dip->prev = dip->next = AUDIO_MIXER_LAST;
792094fa01fSderaadt 		strlcpy(dip->label.name, AudioCmonitor, sizeof dip->label.name);
79342aaa9baSjason 		break;
7947282f66cSjason 	case HARMONY_PORT_RECORD_CLASS:
7957282f66cSjason 		dip->type = AUDIO_MIXER_CLASS;
7967282f66cSjason 		dip->mixer_class = HARMONY_PORT_RECORD_CLASS;
7977282f66cSjason 		dip->prev = dip->next = AUDIO_MIXER_LAST;
798094fa01fSderaadt 		strlcpy(dip->label.name, AudioCrecord, sizeof dip->label.name);
7997282f66cSjason 		break;
80042aaa9baSjason 	default:
80142aaa9baSjason 		err = ENXIO;
80242aaa9baSjason 		break;
80342aaa9baSjason 	}
80442aaa9baSjason 
80542aaa9baSjason 	return (err);
80642aaa9baSjason }
80742aaa9baSjason 
808673bd31fSjason void *
harmony_allocm(void * vsc,int dir,size_t size,int pool,int flags)809673bd31fSjason harmony_allocm(void *vsc, int dir, size_t size, int pool, int flags)
810673bd31fSjason {
811673bd31fSjason 	struct harmony_softc *sc = vsc;
812673bd31fSjason 	struct harmony_dma *d;
813673bd31fSjason 	int rseg;
814673bd31fSjason 
815673bd31fSjason 	d = (struct harmony_dma *)malloc(sizeof(struct harmony_dma), pool, flags);
816673bd31fSjason 	if (d == NULL)
817673bd31fSjason 		goto fail;
818673bd31fSjason 
819673bd31fSjason 	if (bus_dmamap_create(sc->sc_dmat, size, 1, size, 0, BUS_DMA_NOWAIT,
820673bd31fSjason 	    &d->d_map) != 0)
821673bd31fSjason 		goto fail1;
822673bd31fSjason 
823673bd31fSjason 	if (bus_dmamem_alloc(sc->sc_dmat, size, PAGE_SIZE, 0, &d->d_seg, 1,
824673bd31fSjason 	    &rseg, BUS_DMA_NOWAIT) != 0)
825673bd31fSjason 		goto fail2;
826673bd31fSjason 
827673bd31fSjason 	if (bus_dmamem_map(sc->sc_dmat, &d->d_seg, 1, size, &d->d_kva,
828673bd31fSjason 	    BUS_DMA_NOWAIT) != 0)
829673bd31fSjason 		goto fail3;
830673bd31fSjason 
831673bd31fSjason 	if (bus_dmamap_load(sc->sc_dmat, d->d_map, d->d_kva, size, NULL,
832673bd31fSjason 	    BUS_DMA_NOWAIT) != 0)
833673bd31fSjason 		goto fail4;
834673bd31fSjason 
835673bd31fSjason 	d->d_next = sc->sc_dmas;
836673bd31fSjason 	sc->sc_dmas = d;
837673bd31fSjason 	d->d_size = size;
838673bd31fSjason 	return (d->d_kva);
839673bd31fSjason 
840673bd31fSjason fail4:
841673bd31fSjason 	bus_dmamem_unmap(sc->sc_dmat, d->d_kva, size);
842673bd31fSjason fail3:
843673bd31fSjason 	bus_dmamem_free(sc->sc_dmat, &d->d_seg, 1);
844673bd31fSjason fail2:
845673bd31fSjason 	bus_dmamap_destroy(sc->sc_dmat, d->d_map);
846673bd31fSjason fail1:
847367b04a3Sderaadt 	free(d, pool, sizeof *d);
848673bd31fSjason fail:
849673bd31fSjason 	return (NULL);
850673bd31fSjason }
851673bd31fSjason 
852673bd31fSjason void
harmony_freem(void * vsc,void * ptr,int pool)853673bd31fSjason harmony_freem(void *vsc, void *ptr, int pool)
854673bd31fSjason {
855673bd31fSjason 	struct harmony_softc *sc = vsc;
856673bd31fSjason 	struct harmony_dma *d, **dd;
857673bd31fSjason 
858673bd31fSjason 	for (dd = &sc->sc_dmas; (d = *dd) != NULL; dd = &(*dd)->d_next) {
859673bd31fSjason 		if (d->d_kva != ptr)
860673bd31fSjason 			continue;
861673bd31fSjason 		bus_dmamap_unload(sc->sc_dmat, d->d_map);
862673bd31fSjason 		bus_dmamem_unmap(sc->sc_dmat, d->d_kva, d->d_size);
863673bd31fSjason 		bus_dmamem_free(sc->sc_dmat, &d->d_seg, 1);
864673bd31fSjason 		bus_dmamap_destroy(sc->sc_dmat, d->d_map);
865367b04a3Sderaadt 		free(d, pool, sizeof *d);
866673bd31fSjason 		return;
867673bd31fSjason 	}
868673bd31fSjason 	printf("%s: free rogue pointer\n", sc->sc_dv.dv_xname);
869673bd31fSjason }
870673bd31fSjason 
87142aaa9baSjason size_t
harmony_round_buffersize(void * vsc,int direction,size_t size)87242aaa9baSjason harmony_round_buffersize(void *vsc, int direction, size_t size)
87342aaa9baSjason {
874b0d03aaaSmickey 	return ((size + HARMONY_BUFSIZE - 1) & (size_t)(-HARMONY_BUFSIZE));
87542aaa9baSjason }
87642aaa9baSjason 
87742aaa9baSjason int
harmony_trigger_output(void * vsc,void * start,void * end,int blksize,void (* intr)(void *),void * intrarg,struct audio_params * param)87842aaa9baSjason harmony_trigger_output(void *vsc, void *start, void *end, int blksize,
879c9f617c0Sjason     void (*intr)(void *), void *intrarg, struct audio_params *param)
88042aaa9baSjason {
88142aaa9baSjason 	struct harmony_softc *sc = vsc;
882673bd31fSjason 	struct harmony_channel *c = &sc->sc_playback;
883673bd31fSjason 	struct harmony_dma *d;
8847a877ae9Sjason 	bus_addr_t nextaddr;
8857a877ae9Sjason 	bus_size_t togo;
88642aaa9baSjason 
887673bd31fSjason 	for (d = sc->sc_dmas; d->d_kva != start; d = d->d_next)
888673bd31fSjason 		/*EMPTY*/;
889673bd31fSjason 	if (d == NULL) {
890673bd31fSjason 		printf("%s: trigger_output: bad addr: %p\n",
891673bd31fSjason 		    sc->sc_dv.dv_xname, start);
892673bd31fSjason 		return (EINVAL);
893673bd31fSjason 	}
894673bd31fSjason 
895c9f617c0Sjason 	c->c_intr = intr;
896c9f617c0Sjason 	c->c_intrarg = intrarg;
897673bd31fSjason 	c->c_blksz = blksize;
898673bd31fSjason 	c->c_current = d;
8997282f66cSjason 	c->c_segsz = (caddr_t)end - (caddr_t)start;
9007282f66cSjason 	c->c_cnt = 0;
9017282f66cSjason 	c->c_lastaddr = d->d_map->dm_segs[0].ds_addr;
902673bd31fSjason 
903532b98aeSjason 	sc->sc_playing = 1;
9047282f66cSjason 
905167728ffSjason 	togo = c->c_segsz - c->c_cnt;
906167728ffSjason 	if (togo == 0) {
907167728ffSjason 		nextaddr = d->d_map->dm_segs[0].ds_addr;
908167728ffSjason 		c->c_cnt = togo = c->c_blksz;
909167728ffSjason 	} else {
910167728ffSjason 		nextaddr = c->c_lastaddr;
911167728ffSjason 		if (togo > c->c_blksz)
912167728ffSjason 			togo = c->c_blksz;
913167728ffSjason 		c->c_cnt += togo;
914167728ffSjason 	}
915167728ffSjason 
916167728ffSjason 	bus_dmamap_sync(sc->sc_dmat, d->d_map,
917167728ffSjason 	    nextaddr - d->d_map->dm_segs[0].ds_addr,
918167728ffSjason 	    c->c_blksz, BUS_DMASYNC_PREWRITE);
919167728ffSjason 
920886882aaSratchov 	mtx_enter(&audio_lock);
921167728ffSjason 	WRITE_REG(sc, HARMONY_PNXTADD, nextaddr);
9227a877ae9Sjason 	c->c_theaddr = nextaddr;
923167728ffSjason 	SYNC_REG(sc, HARMONY_PNXTADD, BUS_SPACE_BARRIER_WRITE);
924167728ffSjason 	c->c_lastaddr = nextaddr + togo;
9257a877ae9Sjason 
9267a877ae9Sjason 	harmony_start_cp(sc);
9277a877ae9Sjason 	harmony_intr_enable(sc);
928886882aaSratchov 	mtx_leave(&audio_lock);
9297a877ae9Sjason 	return (0);
930167728ffSjason }
931167728ffSjason 
932167728ffSjason void
harmony_start_cp(struct harmony_softc * sc)933167728ffSjason harmony_start_cp(struct harmony_softc *sc)
934167728ffSjason {
935167728ffSjason 	struct harmony_channel *c = &sc->sc_capture;
936167728ffSjason 	struct harmony_dma *d;
937167728ffSjason 	bus_addr_t nextaddr;
938167728ffSjason 	bus_size_t togo;
939167728ffSjason 
940167728ffSjason 	if (sc->sc_capturing == 0) {
941167728ffSjason 		WRITE_REG(sc, HARMONY_RNXTADD,
942167728ffSjason 		    sc->sc_capture_paddrs[sc->sc_capture_empty]);
943167728ffSjason 		if (++sc->sc_capture_empty == CAPTURE_EMPTYS)
944167728ffSjason 			sc->sc_capture_empty = 0;
945167728ffSjason 	} else {
946167728ffSjason 		d = c->c_current;
947167728ffSjason 		togo = c->c_segsz - c->c_cnt;
948167728ffSjason 		if (togo == 0) {
949167728ffSjason 			nextaddr = d->d_map->dm_segs[0].ds_addr;
950167728ffSjason 			c->c_cnt = togo = c->c_blksz;
951167728ffSjason 		} else {
952167728ffSjason 			nextaddr = c->c_lastaddr;
953167728ffSjason 			if (togo > c->c_blksz)
954167728ffSjason 				togo = c->c_blksz;
955167728ffSjason 			c->c_cnt += togo;
956167728ffSjason 		}
957167728ffSjason 
958167728ffSjason 		bus_dmamap_sync(sc->sc_dmat, d->d_map,
959167728ffSjason 		    nextaddr - d->d_map->dm_segs[0].ds_addr,
960167728ffSjason 		    c->c_blksz, BUS_DMASYNC_PREWRITE);
961167728ffSjason 
962167728ffSjason 		WRITE_REG(sc, HARMONY_RNXTADD, nextaddr);
963591687a2Sjason 		SYNC_REG(sc, HARMONY_RNXTADD, BUS_SPACE_BARRIER_WRITE);
964167728ffSjason 		c->c_lastaddr = nextaddr + togo;
965167728ffSjason 	}
9667180d4afSmickey 
9677180d4afSmickey 	timeout_add(&sc->sc_acc_tmo, 1);
968167728ffSjason }
969167728ffSjason 
97042aaa9baSjason int
harmony_trigger_input(void * vsc,void * start,void * end,int blksize,void (* intr)(void *),void * intrarg,struct audio_params * param)97142aaa9baSjason harmony_trigger_input(void *vsc, void *start, void *end, int blksize,
972b88c9c3fSjason     void (*intr)(void *), void *intrarg, struct audio_params *param)
97342aaa9baSjason {
97442aaa9baSjason 	struct harmony_softc *sc = vsc;
975b88c9c3fSjason 	struct harmony_channel *c = &sc->sc_capture;
976b88c9c3fSjason 	struct harmony_dma *d;
97742aaa9baSjason 
978b88c9c3fSjason 	for (d = sc->sc_dmas; d->d_kva != start; d = d->d_next)
979b88c9c3fSjason 		/*EMPTY*/;
980b88c9c3fSjason 	if (d == NULL) {
981b88c9c3fSjason 		printf("%s: trigger_input: bad addr: %p\n",
982b88c9c3fSjason 		    sc->sc_dv.dv_xname, start);
983b88c9c3fSjason 		return (EINVAL);
984b88c9c3fSjason 	}
985b88c9c3fSjason 
986b88c9c3fSjason 	c->c_intr = intr;
987b88c9c3fSjason 	c->c_intrarg = intrarg;
988b88c9c3fSjason 	c->c_blksz = blksize;
989b88c9c3fSjason 	c->c_current = d;
9907282f66cSjason 	c->c_segsz = (caddr_t)end - (caddr_t)start;
9917282f66cSjason 	c->c_cnt = 0;
9927282f66cSjason 	c->c_lastaddr = d->d_map->dm_segs[0].ds_addr;
993886882aaSratchov 	mtx_enter(&audio_lock);
994673bd31fSjason 	sc->sc_capturing = 1;
995167728ffSjason 	harmony_start_cp(sc);
99642aaa9baSjason 	harmony_intr_enable(sc);
997886882aaSratchov 	mtx_leave(&audio_lock);
99842aaa9baSjason 	return (0);
99942aaa9baSjason }
100042aaa9baSjason 
100142aaa9baSjason static const struct speed_struct {
100242aaa9baSjason 	u_int32_t speed;
100342aaa9baSjason 	u_int32_t bits;
100442aaa9baSjason } harmony_speeds[] = {
100542aaa9baSjason 	{ 5125, CNTL_RATE_5125 },
100642aaa9baSjason 	{ 6615, CNTL_RATE_6615 },
100742aaa9baSjason 	{ 8000, CNTL_RATE_8000 },
100842aaa9baSjason 	{ 9600, CNTL_RATE_9600 },
100942aaa9baSjason 	{ 11025, CNTL_RATE_11025 },
101042aaa9baSjason 	{ 16000, CNTL_RATE_16000 },
101142aaa9baSjason 	{ 18900, CNTL_RATE_18900 },
101242aaa9baSjason 	{ 22050, CNTL_RATE_22050 },
101342aaa9baSjason 	{ 27428, CNTL_RATE_27428 },
101442aaa9baSjason 	{ 32000, CNTL_RATE_32000 },
101542aaa9baSjason 	{ 33075, CNTL_RATE_33075 },
101642aaa9baSjason 	{ 37800, CNTL_RATE_37800 },
101742aaa9baSjason 	{ 44100, CNTL_RATE_44100 },
101842aaa9baSjason 	{ 48000, CNTL_RATE_48000 },
101942aaa9baSjason };
102042aaa9baSjason 
102142aaa9baSjason u_int32_t
harmony_speed_bits(struct harmony_softc * sc,u_long * speedp)102242aaa9baSjason harmony_speed_bits(struct harmony_softc *sc, u_long *speedp)
102342aaa9baSjason {
102442aaa9baSjason 	int i, n, selected = -1;
102542aaa9baSjason 
102642aaa9baSjason 	n = sizeof(harmony_speeds) / sizeof(harmony_speeds[0]);
102742aaa9baSjason 
102842aaa9baSjason 	if ((*speedp) <= harmony_speeds[0].speed)
102942aaa9baSjason 		selected = 0;
103042aaa9baSjason 	else if ((*speedp) >= harmony_speeds[n - 1].speed)
103142aaa9baSjason 		selected = n - 1;
103242aaa9baSjason 	else {
103342aaa9baSjason 		for (i = 1; selected == -1 && i < n; i++) {
103442aaa9baSjason 			if ((*speedp) == harmony_speeds[i].speed)
103542aaa9baSjason 				selected = i;
103642aaa9baSjason 			else if ((*speedp) < harmony_speeds[i].speed) {
103742aaa9baSjason 				int diff1, diff2;
103842aaa9baSjason 
103942aaa9baSjason 				diff1 = (*speedp) - harmony_speeds[i - 1].speed;
104042aaa9baSjason 				diff2 = harmony_speeds[i].speed - (*speedp);
104142aaa9baSjason 				if (diff1 < diff2)
104242aaa9baSjason 					selected = i - 1;
104342aaa9baSjason 				else
104442aaa9baSjason 					selected = i;
104542aaa9baSjason 			}
104642aaa9baSjason 		}
104742aaa9baSjason 	}
104842aaa9baSjason 
104942aaa9baSjason 	if (selected == -1)
105042aaa9baSjason 		selected = 2;
105142aaa9baSjason 
105242aaa9baSjason 	*speedp = harmony_speeds[selected].speed;
105342aaa9baSjason 	return (harmony_speeds[selected].bits);
105442aaa9baSjason }
105542aaa9baSjason 
1056fe6bde3cSjason int
harmony_set_gainctl(struct harmony_softc * sc)10577282f66cSjason harmony_set_gainctl(struct harmony_softc *sc)
1058b88c9c3fSjason {
1059fe6bde3cSjason 	u_int32_t bits, mask, val, old;
1060b88c9c3fSjason 
10617282f66cSjason 	/* XXX leave these bits alone or the chip will not come out of CNTL */
1062591687a2Sjason 	bits = GAINCTL_LE | GAINCTL_HE | GAINCTL_SE | GAINCTL_IS_MASK;
1063b88c9c3fSjason 
1064b88c9c3fSjason 	/* input level */
1065b88c9c3fSjason 	bits |= ((sc->sc_input_lvl.left >> (8 - GAINCTL_INPUT_BITS)) <<
1066b88c9c3fSjason 	    GAINCTL_INPUT_LEFT_S) & GAINCTL_INPUT_LEFT_M;
1067b88c9c3fSjason 	bits |= ((sc->sc_input_lvl.right >> (8 - GAINCTL_INPUT_BITS)) <<
1068b88c9c3fSjason 	    GAINCTL_INPUT_RIGHT_S) & GAINCTL_INPUT_RIGHT_M;
1069b88c9c3fSjason 
1070b88c9c3fSjason 	/* output level (inverted) */
1071b88c9c3fSjason 	mask = (1 << GAINCTL_OUTPUT_BITS) - 1;
1072b88c9c3fSjason 	val = mask - (sc->sc_output_lvl.left >> (8 - GAINCTL_OUTPUT_BITS));
1073b88c9c3fSjason 	bits |= (val << GAINCTL_OUTPUT_LEFT_S) & GAINCTL_OUTPUT_LEFT_M;
1074b88c9c3fSjason 	val = mask - (sc->sc_output_lvl.right >> (8 - GAINCTL_OUTPUT_BITS));
1075b88c9c3fSjason 	bits |= (val << GAINCTL_OUTPUT_RIGHT_S) & GAINCTL_OUTPUT_RIGHT_M;
1076b88c9c3fSjason 
1077b88c9c3fSjason 	/* monitor level (inverted) */
1078b88c9c3fSjason 	mask = (1 << GAINCTL_MONITOR_BITS) - 1;
1079b88c9c3fSjason 	val = mask - (sc->sc_monitor_lvl.left >> (8 - GAINCTL_MONITOR_BITS));
1080b88c9c3fSjason 	bits |= (val << GAINCTL_MONITOR_S) & GAINCTL_MONITOR_M;
1081b88c9c3fSjason 
1082591687a2Sjason 	/* XXX messing with these causes CNTL_C to get stuck... grr. */
1083591687a2Sjason 	bits &= ~GAINCTL_IS_MASK;
1084591687a2Sjason 	if (sc->sc_in_port == HARMONY_IN_MIC)
1085591687a2Sjason 		bits |= GAINCTL_IS_LINE;
1086591687a2Sjason 	else
1087fe6bde3cSjason 		bits |= GAINCTL_IS_MICROPHONE;
1088591687a2Sjason 
1089591687a2Sjason 	/* XXX messing with these causes CNTL_C to get stuck... grr. */
1090fe6bde3cSjason 	bits &= ~(GAINCTL_LE | GAINCTL_HE | GAINCTL_SE);
1091591687a2Sjason 	if (sc->sc_out_port == HARMONY_OUT_LINE)
1092591687a2Sjason 		bits |= GAINCTL_LE;
1093591687a2Sjason 	else if (sc->sc_out_port == HARMONY_OUT_SPEAKER)
1094591687a2Sjason 		bits |= GAINCTL_SE;
1095591687a2Sjason 	else
1096591687a2Sjason 		bits |= GAINCTL_HE;
1097591687a2Sjason 
1098fe6bde3cSjason 	mask = GAINCTL_LE | GAINCTL_HE | GAINCTL_SE | GAINCTL_IS_MASK;
1099fe6bde3cSjason 	old = bus_space_read_4(sc->sc_bt, sc->sc_bh, HARMONY_GAINCTL);
11007282f66cSjason 	bus_space_write_4(sc->sc_bt, sc->sc_bh, HARMONY_GAINCTL, bits);
1101fe6bde3cSjason 	if ((old & mask) != (bits & mask))
1102fe6bde3cSjason 		return (1);
1103fe6bde3cSjason 	return (0);
1104b88c9c3fSjason }
1105b88c9c3fSjason 
11067a877ae9Sjason void
harmony_try_more(struct harmony_softc * sc)11077a877ae9Sjason harmony_try_more(struct harmony_softc *sc)
11087a877ae9Sjason {
11097a877ae9Sjason 	struct harmony_channel *c = &sc->sc_playback;
11107a877ae9Sjason 	struct harmony_dma *d = c->c_current;
11117a877ae9Sjason 	u_int32_t cur;
11127a877ae9Sjason 	int i, nsegs;
11137a877ae9Sjason 
11147a877ae9Sjason 	cur = bus_space_read_4(sc->sc_bt, sc->sc_bh, HARMONY_PCURADD);
11157a877ae9Sjason 	cur &= PCURADD_BUFMASK;
11167a877ae9Sjason 	nsegs = 0;
11177a877ae9Sjason 
11187a877ae9Sjason #ifdef DIAGNOSTIC
11197a877ae9Sjason 	if (cur < d->d_map->dm_segs[0].ds_addr ||
11207a877ae9Sjason 	    cur >= (d->d_map->dm_segs[0].ds_addr + c->c_segsz))
112156f69dffSmiod 		panic("%s: bad current %x < %lx || %x > %lx",
112256f69dffSmiod 		    sc->sc_dv.dv_xname, cur, d->d_map->dm_segs[0].ds_addr, cur,
11237a877ae9Sjason 		    d->d_map->dm_segs[0].ds_addr + c->c_segsz);
11247a877ae9Sjason #endif /* DIAGNOSTIC */
11257a877ae9Sjason 
11267a877ae9Sjason 	if (cur > c->c_theaddr) {
11277a877ae9Sjason 		nsegs = (cur - c->c_theaddr) / HARMONY_BUFSIZE;
11287a877ae9Sjason 	} else if (cur < c->c_theaddr) {
11297a877ae9Sjason 		nsegs = (d->d_map->dm_segs[0].ds_addr + c->c_segsz -
11307a877ae9Sjason 		    c->c_theaddr) / HARMONY_BUFSIZE;
11317a877ae9Sjason 		nsegs += (cur - d->d_map->dm_segs[0].ds_addr) /
11327a877ae9Sjason 		    HARMONY_BUFSIZE;
11337a877ae9Sjason 	}
11347a877ae9Sjason 
11357a877ae9Sjason 	if (nsegs != 0 && c->c_intr != NULL) {
11367a877ae9Sjason 		for (i = 0; i < nsegs; i++)
11377a877ae9Sjason 			(*c->c_intr)(c->c_intrarg);
11387a877ae9Sjason 		c->c_theaddr = cur;
11397a877ae9Sjason 	}
11407a877ae9Sjason }
11417a877ae9Sjason 
114242aaa9baSjason struct cfdriver harmony_cd = {
114342aaa9baSjason 	NULL, "harmony", DV_DULL
114442aaa9baSjason };
114542aaa9baSjason 
114678d5ff0eSmpi const struct cfattach harmony_ca = {
114742aaa9baSjason 	sizeof(struct harmony_softc), harmony_match, harmony_attach
114842aaa9baSjason };
1149