xref: /openbsd-src/sys/dev/sbus/cs4231.c (revision cc5bdd413dc6810be27950ffc92a3e2dad4ebdcb)
1*cc5bdd41Skn /*	$OpenBSD: cs4231.c,v 1.44 2022/10/26 20:19:09 kn Exp $	*/
27f529f01Sjason 
37f529f01Sjason /*
47f529f01Sjason  * Copyright (c) 1999 Jason L. Wright (jason@thought.net)
57f529f01Sjason  * All rights reserved.
67f529f01Sjason  *
77f529f01Sjason  * Redistribution and use in source and binary forms, with or without
87f529f01Sjason  * modification, are permitted provided that the following conditions
97f529f01Sjason  * are met:
107f529f01Sjason  * 1. Redistributions of source code must retain the above copyright
117f529f01Sjason  *    notice, this list of conditions and the following disclaimer.
127f529f01Sjason  * 2. Redistributions in binary form must reproduce the above copyright
137f529f01Sjason  *    notice, this list of conditions and the following disclaimer in the
147f529f01Sjason  *    documentation and/or other materials provided with the distribution.
157f529f01Sjason  *
167f529f01Sjason  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
177f529f01Sjason  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
187f529f01Sjason  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
197f529f01Sjason  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
207f529f01Sjason  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
217f529f01Sjason  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
227f529f01Sjason  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
237f529f01Sjason  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
247f529f01Sjason  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
257f529f01Sjason  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
267f529f01Sjason  * POSSIBILITY OF SUCH DAMAGE.
275248d82bSjason  *
285248d82bSjason  * Effort sponsored in part by the Defense Advanced Research Projects
295248d82bSjason  * Agency (DARPA) and Air Force Research Laboratory, Air Force
305248d82bSjason  * Materiel Command, USAF, under agreement number F30602-01-2-0537.
315248d82bSjason  *
327f529f01Sjason  */
337f529f01Sjason 
347f529f01Sjason /*
357f529f01Sjason  * Driver for CS4231 based audio found in some sun4m systems (cs4231)
367f529f01Sjason  * based on ideas from the S/Linux project and the NetBSD project.
377f529f01Sjason  */
387f529f01Sjason 
397f529f01Sjason #include <sys/param.h>
407f529f01Sjason #include <sys/systm.h>
417f529f01Sjason #include <sys/errno.h>
427f529f01Sjason #include <sys/ioctl.h>
437f529f01Sjason #include <sys/device.h>
447f529f01Sjason #include <sys/proc.h>
457f529f01Sjason #include <sys/malloc.h>
467f529f01Sjason 
477f529f01Sjason #include <machine/bus.h>
487f529f01Sjason #include <machine/intr.h>
497f529f01Sjason #include <machine/autoconf.h>
507f529f01Sjason 
517f529f01Sjason #include <sys/audioio.h>
527f529f01Sjason #include <dev/audio_if.h>
537f529f01Sjason 
547e0aa3e6Sjason #include <dev/ic/ad1848reg.h>
557e0aa3e6Sjason #include <dev/ic/cs4231reg.h>
56a1f7da73Sjason #include <dev/ic/apcdmareg.h>
577f529f01Sjason #include <dev/sbus/sbusvar.h>
587f529f01Sjason #include <dev/sbus/cs4231var.h>
597f529f01Sjason 
607f529f01Sjason #define	CSAUDIO_DAC_LVL		0
617f529f01Sjason #define	CSAUDIO_LINE_IN_LVL	1
627f529f01Sjason #define	CSAUDIO_MIC_LVL		2
637f529f01Sjason #define	CSAUDIO_CD_LVL		3
647f529f01Sjason #define	CSAUDIO_MONITOR_LVL	4
657f529f01Sjason #define	CSAUDIO_OUTPUT_LVL	5
667f529f01Sjason #define	CSAUDIO_LINE_IN_MUTE	6
677f529f01Sjason #define	CSAUDIO_DAC_MUTE	7
687f529f01Sjason #define	CSAUDIO_CD_MUTE		8
697f529f01Sjason #define	CSAUDIO_MIC_MUTE	9
707f529f01Sjason #define	CSAUDIO_MONITOR_MUTE	10
717f529f01Sjason #define	CSAUDIO_OUTPUT_MUTE	11
727f529f01Sjason #define	CSAUDIO_REC_LVL		12
737f529f01Sjason #define	CSAUDIO_RECORD_SOURCE	13
747f529f01Sjason #define	CSAUDIO_OUTPUT		14
757f529f01Sjason #define	CSAUDIO_INPUT_CLASS	15
767f529f01Sjason #define	CSAUDIO_OUTPUT_CLASS	16
777f529f01Sjason #define	CSAUDIO_RECORD_CLASS	17
787f529f01Sjason #define	CSAUDIO_MONITOR_CLASS	18
797f529f01Sjason 
807f529f01Sjason #define	CSPORT_AUX2		0
817f529f01Sjason #define	CSPORT_AUX1		1
827f529f01Sjason #define	CSPORT_DAC		2
837f529f01Sjason #define	CSPORT_LINEIN		3
847f529f01Sjason #define	CSPORT_MONO		4
857f529f01Sjason #define	CSPORT_MONITOR		5
867f529f01Sjason #define	CSPORT_SPEAKER		6
877f529f01Sjason #define	CSPORT_LINEOUT		7
887f529f01Sjason #define	CSPORT_HEADPHONE	8
899820c8cbSjason #define	CSPORT_MICROPHONE	9
907f529f01Sjason 
917f529f01Sjason #define MIC_IN_PORT	0
927f529f01Sjason #define LINE_IN_PORT	1
937f529f01Sjason #define AUX1_IN_PORT	2
947f529f01Sjason #define DAC_IN_PORT	3
957f529f01Sjason 
967f529f01Sjason #ifdef AUDIO_DEBUG
977f529f01Sjason #define	DPRINTF(x)	printf x
987f529f01Sjason #else
997f529f01Sjason #define	DPRINTF(x)
1007f529f01Sjason #endif
1017f529f01Sjason 
102a1f7da73Sjason #define	CS_TIMEOUT	90000
103a1f7da73Sjason 
104a1f7da73Sjason #define	CS_PC_LINEMUTE	XCTL0_ENABLE
105a1f7da73Sjason #define	CS_PC_HDPHMUTE	XCTL1_ENABLE
1069820c8cbSjason #define	CS_AFS_TI	0x40		/* timer interrupt */
1079820c8cbSjason #define	CS_AFS_CI	0x20		/* capture interrupt */
1089820c8cbSjason #define	CS_AFS_PI	0x10		/* playback interrupt */
1099820c8cbSjason #define	CS_AFS_CU	0x08		/* capture underrun */
1109820c8cbSjason #define	CS_AFS_CO	0x04		/* capture overrun */
1119820c8cbSjason #define	CS_AFS_PO	0x02		/* playback overrun */
1129820c8cbSjason #define	CS_AFS_PU	0x01		/* playback underrun */
113a1f7da73Sjason 
1147f529f01Sjason #define CS_WRITE(sc,r,v)	\
115a1f7da73Sjason     bus_space_write_1((sc)->sc_bustag, (sc)->sc_regs, (r) << 2, (v))
1167f529f01Sjason #define	CS_READ(sc,r)		\
117a1f7da73Sjason     bus_space_read_1((sc)->sc_bustag, (sc)->sc_regs, (r) << 2)
1187f529f01Sjason 
1197f529f01Sjason #define	APC_WRITE(sc,r,v)	\
1207f529f01Sjason     bus_space_write_4(sc->sc_bustag, sc->sc_regs, r, v)
1217f529f01Sjason #define	APC_READ(sc,r)		\
1227f529f01Sjason     bus_space_read_4(sc->sc_bustag, sc->sc_regs, r)
1237f529f01Sjason 
124c4071fd1Smillert int	cs4231_match(struct device *, void *, void *);
125c4071fd1Smillert void	cs4231_attach(struct device *, struct device *, void *);
126c4071fd1Smillert int	cs4231_intr(void *);
1277f529f01Sjason 
128c4071fd1Smillert int	cs4231_set_speed(struct cs4231_softc *, u_long *);
129c4071fd1Smillert void	cs4231_setup_output(struct cs4231_softc *sc);
1307f529f01Sjason 
131c4071fd1Smillert void		cs4231_write(struct cs4231_softc *, u_int8_t, u_int8_t);
132c4071fd1Smillert u_int8_t	cs4231_read(struct cs4231_softc *, u_int8_t);
1337f529f01Sjason 
1347f529f01Sjason /* Audio interface */
135c4071fd1Smillert int	cs4231_open(void *, int);
136c4071fd1Smillert void	cs4231_close(void *);
137c4071fd1Smillert int	cs4231_set_params(void *, int, int, struct audio_params *,
138c4071fd1Smillert     struct audio_params *);
139c4071fd1Smillert int	cs4231_round_blocksize(void *, int);
140c4071fd1Smillert int	cs4231_commit_settings(void *);
141c4071fd1Smillert int	cs4231_halt_output(void *);
142c4071fd1Smillert int	cs4231_halt_input(void *);
143c4071fd1Smillert int	cs4231_set_port(void *, mixer_ctrl_t *);
144c4071fd1Smillert int	cs4231_get_port(void *, mixer_ctrl_t *);
1456e4fde28Sjason int	cs4231_query_devinfo(void *, mixer_devinfo_t *);
146c4071fd1Smillert void *	cs4231_alloc(void *, int, size_t, int, int);
147c4071fd1Smillert void	cs4231_free(void *, void *, int);
1484f9e30d0Smillert int	cs4231_trigger_output(void *, void *, void *, int,
1496e4fde28Sjason     void (*)(void *), void *, struct audio_params *);
1504f9e30d0Smillert int	cs4231_trigger_input(void *, void *, void *, int,
1516e4fde28Sjason     void (*)(void *), void *, struct audio_params *);
1527f529f01Sjason 
1530d6a2fdeSmiod const struct audio_hw_if cs4231_sa_hw_if = {
1545a8d990aSkn 	.open = cs4231_open,
1555a8d990aSkn 	.close = cs4231_close,
1565a8d990aSkn 	.set_params = cs4231_set_params,
1575a8d990aSkn 	.round_blocksize = cs4231_round_blocksize,
1585a8d990aSkn 	.commit_settings = cs4231_commit_settings,
1595a8d990aSkn 	.halt_output = cs4231_halt_output,
1605a8d990aSkn 	.halt_input = cs4231_halt_input,
1615a8d990aSkn 	.set_port = cs4231_set_port,
1625a8d990aSkn 	.get_port = cs4231_get_port,
1635a8d990aSkn 	.query_devinfo = cs4231_query_devinfo,
1645a8d990aSkn 	.allocm = cs4231_alloc,
1655a8d990aSkn 	.freem = cs4231_free,
1665a8d990aSkn 	.trigger_output = cs4231_trigger_output,
1675a8d990aSkn 	.trigger_input = cs4231_trigger_input,
1687f529f01Sjason };
1697f529f01Sjason 
17022e452dfSmpi const struct cfattach audiocs_ca = {
1717f529f01Sjason 	sizeof (struct cs4231_softc), cs4231_match, cs4231_attach
1727f529f01Sjason };
1737f529f01Sjason 
1747f529f01Sjason struct cfdriver audiocs_cd = {
1757f529f01Sjason 	NULL, "audiocs", DV_DULL
1767f529f01Sjason };
1777f529f01Sjason 
1787f529f01Sjason int
cs4231_match(struct device * parent,void * vcf,void * aux)1796e4fde28Sjason cs4231_match(struct device *parent, void *vcf, void *aux)
1807f529f01Sjason {
1817f529f01Sjason 	struct sbus_attach_args *sa = aux;
1827f529f01Sjason 
1837f529f01Sjason 	return (strcmp("SUNW,CS4231", sa->sa_name) == 0);
1847f529f01Sjason }
1857f529f01Sjason 
1867f529f01Sjason void
cs4231_attach(struct device * parent,struct device * self,void * aux)1876e4fde28Sjason cs4231_attach(struct device *parent, struct device *self, void *aux)
1887f529f01Sjason {
1897f529f01Sjason 	struct sbus_attach_args *sa = aux;
1907f529f01Sjason 	struct cs4231_softc *sc = (struct cs4231_softc *)self;
1917f529f01Sjason 	int node;
1927f529f01Sjason 	u_int32_t sbusburst, burst;
1937f529f01Sjason 
1947f529f01Sjason 	node = sa->sa_node;
1957f529f01Sjason 
1967f529f01Sjason 	/* Pass on the bus tags */
1977f529f01Sjason 	sc->sc_bustag = sa->sa_bustag;
1987f529f01Sjason 	sc->sc_dmatag = sa->sa_dmatag;
1997f529f01Sjason 
2007f529f01Sjason 	/* Make sure things are sane. */
2017f529f01Sjason 	if (sa->sa_nintr != 1) {
2027f529f01Sjason 		printf(": expected 1 interrupt, got %d\n", sa->sa_nintr);
2037f529f01Sjason 		return;
2047f529f01Sjason 	}
2057f529f01Sjason 	if (sa->sa_nreg != 1) {
2067f529f01Sjason 		printf(": expected 1 register set, got %d\n",
2077f529f01Sjason 		    sa->sa_nreg);
2087f529f01Sjason 		return;
2097f529f01Sjason 	}
2107f529f01Sjason 
2117f529f01Sjason 	if (bus_intr_establish(sa->sa_bustag, sa->sa_pri, IPL_AUDIO, 0,
21285729938Shenric 	    cs4231_intr, sc, self->dv_xname) == NULL) {
21349f9bd99Smiod 		printf(": couldn't establish interrupt, pri %d\n",
21449f9bd99Smiod 		    INTLEV(sa->sa_pri));
2157f529f01Sjason 		return;
2167f529f01Sjason 	}
2177f529f01Sjason 
2187f529f01Sjason 	if (sbus_bus_map(sa->sa_bustag,
219eb79e960Shenric 	    sa->sa_reg[0].sbr_slot,
2207f529f01Sjason 	    (bus_addr_t)sa->sa_reg[0].sbr_offset,
2217f529f01Sjason 	    (bus_size_t)sa->sa_reg[0].sbr_size,
2227f529f01Sjason 	    BUS_SPACE_MAP_LINEAR, 0, &sc->sc_regs) != 0) {
223eb79e960Shenric 		printf(": couldn't map registers\n");
2247f529f01Sjason 		return;
2257f529f01Sjason 	}
2267f529f01Sjason 
2277f529f01Sjason 	sbusburst = ((struct sbus_softc *)parent)->sc_burst;
2287f529f01Sjason 	if (sbusburst == 0)
2297f529f01Sjason 		sbusburst = SBUS_BURST_32 - 1;	/* 1->16 */
2307f529f01Sjason 	burst = getpropint(node, "burst-sizes", -1);
2317f529f01Sjason 	if (burst == -1)
2327f529f01Sjason 		burst = sbusburst;
2337f529f01Sjason 	sc->sc_burst = burst & sbusburst;
2347f529f01Sjason 
235145b857fSjason 	printf("\n");
2367f529f01Sjason 
2372baa08e2Santon 	audio_attach_mi(&cs4231_sa_hw_if, sc, NULL, &sc->sc_dev);
2387f529f01Sjason 
2397f529f01Sjason 	/* Default to speaker, unmuted, reasonable volume */
2407f529f01Sjason 	sc->sc_out_port = CSPORT_SPEAKER;
2419820c8cbSjason 	sc->sc_in_port = CSPORT_MICROPHONE;
2427f529f01Sjason 	sc->sc_mute[CSPORT_SPEAKER] = 1;
2433562e709Sjason 	sc->sc_mute[CSPORT_MONITOR] = 1;
2447f529f01Sjason 	sc->sc_volume[CSPORT_SPEAKER].left = 192;
2457f529f01Sjason 	sc->sc_volume[CSPORT_SPEAKER].right = 192;
2467f529f01Sjason }
2477f529f01Sjason 
2487f529f01Sjason /*
2493f51034aSjason  * Write to one of the indexed registers of cs4231.
2507f529f01Sjason  */
2517f529f01Sjason void
cs4231_write(struct cs4231_softc * sc,u_int8_t r,u_int8_t v)2526e4fde28Sjason cs4231_write(struct cs4231_softc *sc, u_int8_t r, u_int8_t v)
2537f529f01Sjason {
254a1f7da73Sjason 	CS_WRITE(sc, AD1848_IADDR, r);
255a1f7da73Sjason 	CS_WRITE(sc, AD1848_IDATA, v);
2567f529f01Sjason }
2577f529f01Sjason 
2587f529f01Sjason /*
2593f51034aSjason  * Read from one of the indexed registers of cs4231.
2607f529f01Sjason  */
2617f529f01Sjason u_int8_t
cs4231_read(struct cs4231_softc * sc,u_int8_t r)2626e4fde28Sjason cs4231_read(struct cs4231_softc *sc, u_int8_t r)
2637f529f01Sjason {
264a1f7da73Sjason 	CS_WRITE(sc, AD1848_IADDR, r);
265a1f7da73Sjason 	return (CS_READ(sc, AD1848_IDATA));
2667f529f01Sjason }
2677f529f01Sjason 
2687f529f01Sjason int
cs4231_set_speed(struct cs4231_softc * sc,u_long * argp)2696e4fde28Sjason cs4231_set_speed(struct cs4231_softc *sc, u_long *argp)
2707f529f01Sjason {
2717f529f01Sjason 	/*
2727f529f01Sjason 	 * The available speeds are in the following table. Keep the speeds in
2737f529f01Sjason 	 * the increasing order.
2747f529f01Sjason 	 */
2757f529f01Sjason 	typedef struct {
2767f529f01Sjason 		int speed;
2777f529f01Sjason 		u_char bits;
2787f529f01Sjason 	} speed_struct;
2797f529f01Sjason 	u_long arg = *argp;
2807f529f01Sjason 
2818f47c760Sjsg 	static const speed_struct speed_table[] = {
282a1f7da73Sjason 		{5510,	(0 << 1) | CLOCK_XTAL2},
283a1f7da73Sjason 		{5510,	(0 << 1) | CLOCK_XTAL2},
284a1f7da73Sjason 		{6620,	(7 << 1) | CLOCK_XTAL2},
285a1f7da73Sjason 		{8000,	(0 << 1) | CLOCK_XTAL1},
286a1f7da73Sjason 		{9600,	(7 << 1) | CLOCK_XTAL1},
287a1f7da73Sjason 		{11025,	(1 << 1) | CLOCK_XTAL2},
288a1f7da73Sjason 		{16000,	(1 << 1) | CLOCK_XTAL1},
289a1f7da73Sjason 		{18900,	(2 << 1) | CLOCK_XTAL2},
290a1f7da73Sjason 		{22050,	(3 << 1) | CLOCK_XTAL2},
291a1f7da73Sjason 		{27420,	(2 << 1) | CLOCK_XTAL1},
292a1f7da73Sjason 		{32000,	(3 << 1) | CLOCK_XTAL1},
293a1f7da73Sjason 		{33075,	(6 << 1) | CLOCK_XTAL2},
294a1f7da73Sjason 		{33075,	(4 << 1) | CLOCK_XTAL2},
295a1f7da73Sjason 		{44100,	(5 << 1) | CLOCK_XTAL2},
296a1f7da73Sjason 		{48000,	(6 << 1) | CLOCK_XTAL1},
2977f529f01Sjason 	};
2987f529f01Sjason 
2997f529f01Sjason 	int i, n, selected = -1;
3007f529f01Sjason 
3017f529f01Sjason 	n = sizeof(speed_table) / sizeof(speed_struct);
3027f529f01Sjason 
3037f529f01Sjason 	if (arg < speed_table[0].speed)
3047f529f01Sjason 		selected = 0;
3057f529f01Sjason 	if (arg > speed_table[n - 1].speed)
3067f529f01Sjason 		selected = n - 1;
3077f529f01Sjason 
3087f529f01Sjason 	for (i = 1; selected == -1 && i < n; i++) {
3097f529f01Sjason 		if (speed_table[i].speed == arg)
3107f529f01Sjason 			selected = i;
3117f529f01Sjason 		else if (speed_table[i].speed > arg) {
3127f529f01Sjason 			int diff1, diff2;
3137f529f01Sjason 
3147f529f01Sjason 			diff1 = arg - speed_table[i - 1].speed;
3157f529f01Sjason 			diff2 = speed_table[i].speed - arg;
3167f529f01Sjason 			if (diff1 < diff2)
3177f529f01Sjason 				selected = i - 1;
3187f529f01Sjason 			else
3197f529f01Sjason 				selected = i;
3207f529f01Sjason 		}
3217f529f01Sjason 	}
3227f529f01Sjason 
3239820c8cbSjason 	if (selected == -1)
3247f529f01Sjason 		selected = 3;
3257f529f01Sjason 
3267f529f01Sjason 	sc->sc_speed_bits = speed_table[selected].bits;
3277f529f01Sjason 	sc->sc_need_commit = 1;
3287f529f01Sjason 	*argp = speed_table[selected].speed;
3297f529f01Sjason 
3307f529f01Sjason 	return (0);
3317f529f01Sjason }
3327f529f01Sjason 
3337f529f01Sjason /*
3347f529f01Sjason  * Audio interface functions
3357f529f01Sjason  */
3367f529f01Sjason int
cs4231_open(void * vsc,int flags)3376e4fde28Sjason cs4231_open(void *vsc, int flags)
3387f529f01Sjason {
3396e4fde28Sjason 	struct cs4231_softc *sc = vsc;
3403f51034aSjason 	int tries;
3417f529f01Sjason 
3427f529f01Sjason 	if (sc->sc_open)
3437f529f01Sjason 		return (EBUSY);
3447f529f01Sjason 	sc->sc_open = 1;
3459820c8cbSjason 
3469820c8cbSjason 	sc->sc_capture.cs_intr = NULL;
3479820c8cbSjason 	sc->sc_capture.cs_arg = NULL;
3489820c8cbSjason 	sc->sc_capture.cs_locked = 0;
3499820c8cbSjason 
3509820c8cbSjason 	sc->sc_playback.cs_intr = NULL;
3519820c8cbSjason 	sc->sc_playback.cs_arg = NULL;
3529820c8cbSjason 	sc->sc_playback.cs_locked = 0;
3537f529f01Sjason 
3547f529f01Sjason 	APC_WRITE(sc, APC_CSR, APC_CSR_RESET);
3557f529f01Sjason 	DELAY(10);
3567f529f01Sjason 	APC_WRITE(sc, APC_CSR, 0);
3577f529f01Sjason 	DELAY(10);
3587f529f01Sjason 	APC_WRITE(sc, APC_CSR, APC_READ(sc, APC_CSR) | APC_CSR_CODEC_RESET);
3597f529f01Sjason 
3607f529f01Sjason 	DELAY(20);
3617f529f01Sjason 
3627f529f01Sjason 	APC_WRITE(sc, APC_CSR, APC_READ(sc, APC_CSR) & (~APC_CSR_CODEC_RESET));
3637f529f01Sjason 
3643f51034aSjason 	for (tries = CS_TIMEOUT;
365a1f7da73Sjason 	     tries && CS_READ(sc, AD1848_IADDR) == SP_IN_INIT; tries--)
3663f51034aSjason 		DELAY(10);
3673f51034aSjason 	if (tries == 0)
3683f51034aSjason 		printf("%s: timeout waiting for reset\n", sc->sc_dev.dv_xname);
3697f529f01Sjason 
3703f51034aSjason 	/* Turn on cs4231 mode */
3717e0aa3e6Sjason 	cs4231_write(sc, SP_MISC_INFO,
372a1f7da73Sjason 	    cs4231_read(sc, SP_MISC_INFO) | MODE2);
3737f529f01Sjason 
3747f529f01Sjason 	cs4231_setup_output(sc);
375bf383a97Sjason 
376bf383a97Sjason 	cs4231_write(sc, SP_PIN_CONTROL,
377bf383a97Sjason 	    cs4231_read(sc, SP_PIN_CONTROL) | INTERRUPT_ENABLE);
378bf383a97Sjason 
3797f529f01Sjason 	return (0);
3807f529f01Sjason }
3817f529f01Sjason 
3827f529f01Sjason void
cs4231_setup_output(struct cs4231_softc * sc)3836e4fde28Sjason cs4231_setup_output(struct cs4231_softc *sc)
3847f529f01Sjason {
3853562e709Sjason 	u_int8_t pc, mi, rm, lm;
3867f529f01Sjason 
3873562e709Sjason 	pc = cs4231_read(sc, SP_PIN_CONTROL) | CS_PC_HDPHMUTE | CS_PC_LINEMUTE;
3887f529f01Sjason 
3893562e709Sjason 	mi = cs4231_read(sc, CS_MONO_IO_CONTROL) | MONO_OUTPUT_MUTE;
3903562e709Sjason 
3913562e709Sjason 	lm = cs4231_read(sc, SP_LEFT_OUTPUT_CONTROL);
3923562e709Sjason 	lm &= ~OUTPUT_ATTEN_BITS;
3933562e709Sjason 	lm |= ((~(sc->sc_volume[CSPORT_SPEAKER].left >> 2)) &
3943562e709Sjason 	    OUTPUT_ATTEN_BITS) | OUTPUT_MUTE;
3953562e709Sjason 
3963562e709Sjason 	rm = cs4231_read(sc, SP_RIGHT_OUTPUT_CONTROL);
3973562e709Sjason 	rm &= ~OUTPUT_ATTEN_BITS;
3983562e709Sjason 	rm |= ((~(sc->sc_volume[CSPORT_SPEAKER].right >> 2)) &
3993562e709Sjason 	    OUTPUT_ATTEN_BITS) | OUTPUT_MUTE;
4003562e709Sjason 
4013562e709Sjason 	if (sc->sc_mute[CSPORT_MONITOR]) {
4023562e709Sjason 		lm &= ~OUTPUT_MUTE;
4033562e709Sjason 		rm &= ~OUTPUT_MUTE;
4043562e709Sjason 	}
4057f529f01Sjason 
4067f529f01Sjason 	switch (sc->sc_out_port) {
4077f529f01Sjason 	case CSPORT_HEADPHONE:
4083562e709Sjason 		if (sc->sc_mute[CSPORT_SPEAKER])
4093562e709Sjason 			pc &= ~CS_PC_HDPHMUTE;
4107f529f01Sjason 		break;
4117f529f01Sjason 	case CSPORT_SPEAKER:
4123562e709Sjason 		if (sc->sc_mute[CSPORT_SPEAKER])
4133562e709Sjason 			mi &= ~MONO_OUTPUT_MUTE;
4147f529f01Sjason 		break;
4157f529f01Sjason 	case CSPORT_LINEOUT:
4163562e709Sjason 		if (sc->sc_mute[CSPORT_SPEAKER])
4173562e709Sjason 			pc &= ~CS_PC_LINEMUTE;
4187f529f01Sjason 		break;
4197f529f01Sjason 	}
4207f529f01Sjason 
4213562e709Sjason 	cs4231_write(sc, SP_LEFT_OUTPUT_CONTROL, lm);
4223562e709Sjason 	cs4231_write(sc, SP_RIGHT_OUTPUT_CONTROL, rm);
4233562e709Sjason 	cs4231_write(sc, SP_PIN_CONTROL, pc);
4243562e709Sjason 	cs4231_write(sc, CS_MONO_IO_CONTROL, mi);
4259820c8cbSjason 
4269820c8cbSjason 	/* XXX doesn't really belong here... */
4279820c8cbSjason 	switch (sc->sc_in_port) {
4289820c8cbSjason 	case CSPORT_LINEIN:
4299820c8cbSjason 		pc = LINE_INPUT;
4309820c8cbSjason 		break;
4319820c8cbSjason 	case CSPORT_AUX1:
4329820c8cbSjason 		pc = AUX_INPUT;
4339820c8cbSjason 		break;
4349820c8cbSjason 	case CSPORT_DAC:
4359820c8cbSjason 		pc = MIXED_DAC_INPUT;
4369820c8cbSjason 		break;
4379820c8cbSjason 	case CSPORT_MICROPHONE:
4389820c8cbSjason 	default:
4399820c8cbSjason 		pc = MIC_INPUT;
4409820c8cbSjason 		break;
4419820c8cbSjason 	}
4429820c8cbSjason 	lm = cs4231_read(sc, SP_LEFT_INPUT_CONTROL);
4439820c8cbSjason 	rm = cs4231_read(sc, SP_RIGHT_INPUT_CONTROL);
4449820c8cbSjason 	lm &= ~(MIXED_DAC_INPUT | ATTEN_22_5);
4459820c8cbSjason 	rm &= ~(MIXED_DAC_INPUT | ATTEN_22_5);
4469820c8cbSjason 	lm |= pc | (sc->sc_adc.left >> 4);
4479820c8cbSjason 	rm |= pc | (sc->sc_adc.right >> 4);
4489820c8cbSjason 	cs4231_write(sc, SP_LEFT_INPUT_CONTROL, lm);
4499820c8cbSjason 	cs4231_write(sc, SP_RIGHT_INPUT_CONTROL, rm);
4507f529f01Sjason }
4517f529f01Sjason 
4527f529f01Sjason void
cs4231_close(void * vsc)4536e4fde28Sjason cs4231_close(void *vsc)
4547f529f01Sjason {
4556e4fde28Sjason 	struct cs4231_softc *sc = vsc;
4567f529f01Sjason 
4577f529f01Sjason 	cs4231_halt_input(sc);
4587f529f01Sjason 	cs4231_halt_output(sc);
459bf383a97Sjason 	cs4231_write(sc, SP_PIN_CONTROL,
460bf383a97Sjason 	    cs4231_read(sc, SP_PIN_CONTROL) & (~INTERRUPT_ENABLE));
4617f529f01Sjason 	sc->sc_open = 0;
4627f529f01Sjason }
4637f529f01Sjason 
4647f529f01Sjason int
cs4231_set_params(void * vsc,int setmode,int usemode,struct audio_params * p,struct audio_params * r)4656e4fde28Sjason cs4231_set_params(void *vsc, int setmode, int usemode,
4666e4fde28Sjason     struct audio_params *p, struct audio_params *r)
4677f529f01Sjason {
4686e4fde28Sjason 	struct cs4231_softc *sc = (struct cs4231_softc *)vsc;
4699820c8cbSjason 	int err, bits, enc = p->encoding;
4707f529f01Sjason 
4717f529f01Sjason 	switch (enc) {
4727f529f01Sjason 	case AUDIO_ENCODING_ULAW:
4739820c8cbSjason 		if (p->precision != 8)
4749820c8cbSjason 			return (EINVAL);
475a1f7da73Sjason 		bits = FMT_ULAW >> 5;
4767f529f01Sjason 		break;
4777f529f01Sjason 	case AUDIO_ENCODING_ALAW:
4789820c8cbSjason 		if (p->precision != 8)
4799820c8cbSjason 			return (EINVAL);
480a1f7da73Sjason 		bits = FMT_ALAW >> 5;
4817f529f01Sjason 		break;
4827f529f01Sjason 	case AUDIO_ENCODING_SLINEAR_LE:
483ac2bb4f3Sratchov 		if (p->precision == 16)
4849820c8cbSjason 			bits = FMT_TWOS_COMP >> 5;
485b1765456Sjason 		else
486b1765456Sjason 			return (EINVAL);
4879820c8cbSjason 		break;
4887f529f01Sjason 	case AUDIO_ENCODING_SLINEAR_BE:
489ac2bb4f3Sratchov 		if (p->precision == 16)
4909820c8cbSjason 			bits = FMT_TWOS_COMP_BE >> 5;
491b1765456Sjason 		else
492b1765456Sjason 			return (EINVAL);
4939820c8cbSjason 		break;
4947f529f01Sjason 	case AUDIO_ENCODING_ULINEAR_LE:
4959820c8cbSjason 	case AUDIO_ENCODING_ULINEAR_BE:
496b1765456Sjason 		if (p->precision == 8)
497b1765456Sjason 			bits = FMT_PCM8 >> 5;
498ac2bb4f3Sratchov 		else
499b1765456Sjason 			return (EINVAL);
5009820c8cbSjason 		break;
5017f529f01Sjason 	default:
5027f529f01Sjason 		return (EINVAL);
5037f529f01Sjason 	}
5047f529f01Sjason 
5057f529f01Sjason 	if (p->channels != 1 && p->channels != 2)
5067f529f01Sjason 		return (EINVAL);
5077f529f01Sjason 
5087f529f01Sjason 	err = cs4231_set_speed(sc, &p->sample_rate);
5097f529f01Sjason 	if (err)
5107f529f01Sjason 		return (err);
5117f529f01Sjason 
51216579317Sjakemsr 	p->bps = AUDIO_BPS(p->precision);
51316579317Sjakemsr 	r->bps = AUDIO_BPS(r->precision);
51416579317Sjakemsr 	p->msb = r->msb = 1;
5157f529f01Sjason 
5167f529f01Sjason 	sc->sc_format_bits = bits;
5177f529f01Sjason 	sc->sc_channels = p->channels;
5187f529f01Sjason 	sc->sc_precision = p->precision;
5197f529f01Sjason 	sc->sc_need_commit = 1;
5207f529f01Sjason 	return (0);
5217f529f01Sjason }
5227f529f01Sjason 
5237f529f01Sjason int
cs4231_round_blocksize(void * vsc,int blk)5246e4fde28Sjason cs4231_round_blocksize(void *vsc, int blk)
5257f529f01Sjason {
526e7e79399Smickey 	return ((blk + 3) & (-4));
5277f529f01Sjason }
5287f529f01Sjason 
5297f529f01Sjason int
cs4231_commit_settings(void * vsc)5306e4fde28Sjason cs4231_commit_settings(void *vsc)
5317f529f01Sjason {
5326e4fde28Sjason 	struct cs4231_softc *sc = (struct cs4231_softc *)vsc;
533886882aaSratchov 	int tries;
5343562e709Sjason 	u_int8_t r, fs;
5357f529f01Sjason 
5367f529f01Sjason 	if (sc->sc_need_commit == 0)
5377f529f01Sjason 		return (0);
5387f529f01Sjason 
5393562e709Sjason 	fs = sc->sc_speed_bits | (sc->sc_format_bits << 5);
5403562e709Sjason 	if (sc->sc_channels == 2)
5413562e709Sjason 		fs |= FMT_STEREO;
5427f529f01Sjason 
543886882aaSratchov 	/* XXX: this is called before DMA is setup, useful ? */
544886882aaSratchov 	mtx_enter(&audio_lock);
5457f529f01Sjason 
546a1f7da73Sjason 	r = cs4231_read(sc, SP_INTERFACE_CONFIG) | AUTO_CAL_ENABLE;
547a1f7da73Sjason 	CS_WRITE(sc, AD1848_IADDR, MODE_CHANGE_ENABLE);
548a1f7da73Sjason 	CS_WRITE(sc, AD1848_IADDR, MODE_CHANGE_ENABLE | SP_INTERFACE_CONFIG);
549a1f7da73Sjason 	CS_WRITE(sc, AD1848_IDATA, r);
5503f51034aSjason 
551a1f7da73Sjason 	CS_WRITE(sc, AD1848_IADDR, MODE_CHANGE_ENABLE | SP_CLOCK_DATA_FORMAT);
5523562e709Sjason 	CS_WRITE(sc, AD1848_IDATA, fs);
553a1f7da73Sjason 	CS_READ(sc, AD1848_IDATA);
554a1f7da73Sjason 	CS_READ(sc, AD1848_IDATA);
5553f51034aSjason 	tries = CS_TIMEOUT;
5563f51034aSjason 	for (tries = CS_TIMEOUT;
557a1f7da73Sjason 	     tries && CS_READ(sc, AD1848_IADDR) == SP_IN_INIT; tries--)
5587f529f01Sjason 		DELAY(10);
5593f51034aSjason 	if (tries == 0)
5607f529f01Sjason 		printf("%s: timeout committing fspb\n", sc->sc_dev.dv_xname);
5617f529f01Sjason 
562a1f7da73Sjason 	CS_WRITE(sc, AD1848_IADDR, MODE_CHANGE_ENABLE | CS_REC_FORMAT);
5633562e709Sjason 	CS_WRITE(sc, AD1848_IDATA, fs);
564a1f7da73Sjason 	CS_READ(sc, AD1848_IDATA);
565a1f7da73Sjason 	CS_READ(sc, AD1848_IDATA);
5663f51034aSjason 	for (tries = CS_TIMEOUT;
567a1f7da73Sjason 	     tries && CS_READ(sc, AD1848_IADDR) == SP_IN_INIT; tries--)
5687f529f01Sjason 		DELAY(10);
5693f51034aSjason 	if (tries == 0)
5707f529f01Sjason 		printf("%s: timeout committing cdf\n", sc->sc_dev.dv_xname);
5717f529f01Sjason 
572a1f7da73Sjason 	CS_WRITE(sc, AD1848_IADDR, 0);
5733f51034aSjason 	for (tries = CS_TIMEOUT;
574a1f7da73Sjason 	     tries && CS_READ(sc, AD1848_IADDR) == SP_IN_INIT; tries--)
5753f51034aSjason 		DELAY(10);
5763f51034aSjason 	if (tries == 0)
5773f51034aSjason 		printf("%s: timeout waiting for !mce\n", sc->sc_dev.dv_xname);
5783f51034aSjason 
579a1f7da73Sjason 	CS_WRITE(sc, AD1848_IADDR, SP_TEST_AND_INIT);
5803f51034aSjason 	for (tries = CS_TIMEOUT;
581a1f7da73Sjason 	     tries && CS_READ(sc, AD1848_IDATA) & AUTO_CAL_IN_PROG; tries--)
5823f51034aSjason 		DELAY(10);
5833f51034aSjason 	if (tries == 0)
5843f51034aSjason 		printf("%s: timeout waiting for autocalibration\n",
5853f51034aSjason 		    sc->sc_dev.dv_xname);
5867f529f01Sjason 
587886882aaSratchov 	mtx_leave(&audio_lock);
5887f529f01Sjason 
5897f529f01Sjason 	sc->sc_need_commit = 0;
5907f529f01Sjason 	return (0);
5917f529f01Sjason }
5927f529f01Sjason 
5937f529f01Sjason int
cs4231_halt_output(void * vsc)5946e4fde28Sjason cs4231_halt_output(void *vsc)
5957f529f01Sjason {
5966e4fde28Sjason 	struct cs4231_softc *sc = (struct cs4231_softc *)vsc;
5977f529f01Sjason 
5989820c8cbSjason 	/* XXX Kills some capture bits */
599886882aaSratchov 	mtx_enter(&audio_lock);
6007f529f01Sjason 	APC_WRITE(sc, APC_CSR, APC_READ(sc, APC_CSR) &
6017f529f01Sjason 	    ~(APC_CSR_EI | APC_CSR_GIE | APC_CSR_PIE |
6027f529f01Sjason 	      APC_CSR_EIE | APC_CSR_PDMA_GO | APC_CSR_PMIE));
6037e0aa3e6Sjason 	cs4231_write(sc, SP_INTERFACE_CONFIG,
604a1f7da73Sjason 	    cs4231_read(sc, SP_INTERFACE_CONFIG) & (~PLAYBACK_ENABLE));
6059820c8cbSjason 	sc->sc_playback.cs_locked = 0;
606886882aaSratchov 	mtx_leave(&audio_lock);
6077f529f01Sjason 	return (0);
6087f529f01Sjason }
6097f529f01Sjason 
6107f529f01Sjason int
cs4231_halt_input(void * vsc)6116e4fde28Sjason cs4231_halt_input(void *vsc)
6127f529f01Sjason {
6136e4fde28Sjason 	struct cs4231_softc *sc = (struct cs4231_softc *)vsc;
6147f529f01Sjason 
6159820c8cbSjason 	/* XXX Kills some playback bits */
616886882aaSratchov 	mtx_enter(&audio_lock);
6177f529f01Sjason 	APC_WRITE(sc, APC_CSR, APC_CSR_CAPTURE_PAUSE);
6187e0aa3e6Sjason 	cs4231_write(sc, SP_INTERFACE_CONFIG,
619a1f7da73Sjason 	    cs4231_read(sc, SP_INTERFACE_CONFIG) & (~CAPTURE_ENABLE));
6209820c8cbSjason 	sc->sc_capture.cs_locked = 0;
621886882aaSratchov 	mtx_leave(&audio_lock);
6227f529f01Sjason 	return (0);
6237f529f01Sjason }
6247f529f01Sjason 
6257f529f01Sjason int
cs4231_set_port(void * vsc,mixer_ctrl_t * cp)6266e4fde28Sjason cs4231_set_port(void *vsc, mixer_ctrl_t *cp)
6277f529f01Sjason {
6286e4fde28Sjason 	struct cs4231_softc *sc = (struct cs4231_softc *)vsc;
6297f529f01Sjason 	int error = EINVAL;
6307f529f01Sjason 
6317f529f01Sjason 	DPRINTF(("cs4231_set_port: port=%d type=%d\n", cp->dev, cp->type));
6327f529f01Sjason 
6337f529f01Sjason 	switch (cp->dev) {
6347f529f01Sjason 	case CSAUDIO_DAC_LVL:
6357f529f01Sjason 		if (cp->type != AUDIO_MIXER_VALUE)
6367f529f01Sjason 			break;
6377f529f01Sjason 		if (cp->un.value.num_channels == 1)
6387e0aa3e6Sjason 			cs4231_write(sc, SP_LEFT_AUX1_CONTROL,
6397f529f01Sjason 			    cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] &
640a1f7da73Sjason 			    LINE_INPUT_ATTEN_BITS);
6417f529f01Sjason 		else if (cp->un.value.num_channels == 2) {
6427e0aa3e6Sjason 			cs4231_write(sc, SP_LEFT_AUX1_CONTROL,
6437f529f01Sjason 			    cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] &
644a1f7da73Sjason 			    LINE_INPUT_ATTEN_BITS);
6457e0aa3e6Sjason 			cs4231_write(sc, SP_RIGHT_AUX1_CONTROL,
6467f529f01Sjason 			    cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] &
647a1f7da73Sjason 			    LINE_INPUT_ATTEN_BITS);
6487f529f01Sjason 		} else
6497f529f01Sjason 			break;
6507f529f01Sjason 		error = 0;
6517f529f01Sjason 		break;
6527f529f01Sjason 	case CSAUDIO_LINE_IN_LVL:
6537f529f01Sjason 		if (cp->type != AUDIO_MIXER_VALUE)
6547f529f01Sjason 			break;
6557f529f01Sjason 		if (cp->un.value.num_channels == 1)
6567e0aa3e6Sjason 			cs4231_write(sc, CS_LEFT_LINE_CONTROL,
6577f529f01Sjason 			    cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] &
658a1f7da73Sjason 			    AUX_INPUT_ATTEN_BITS);
6597f529f01Sjason 		else if (cp->un.value.num_channels == 2) {
6607e0aa3e6Sjason 			cs4231_write(sc, CS_LEFT_LINE_CONTROL,
6617f529f01Sjason 			    cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] &
662a1f7da73Sjason 			    AUX_INPUT_ATTEN_BITS);
6637e0aa3e6Sjason 			cs4231_write(sc, CS_RIGHT_LINE_CONTROL,
6647f529f01Sjason 			    cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] &
665a1f7da73Sjason 			    AUX_INPUT_ATTEN_BITS);
6667f529f01Sjason 		} else
6677f529f01Sjason 			break;
6687f529f01Sjason 		error = 0;
6697f529f01Sjason 		break;
6707f529f01Sjason 	case CSAUDIO_MIC_LVL:
6717f529f01Sjason 		if (cp->type != AUDIO_MIXER_VALUE)
6727f529f01Sjason 			break;
6737f529f01Sjason 		if (cp->un.value.num_channels == 1) {
6747f529f01Sjason #if 0
6757e0aa3e6Sjason 			cs4231_write(sc, CS_MONO_IO_CONTROL,
6767f529f01Sjason 			    cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] &
677a1f7da73Sjason 			    MONO_INPUT_ATTEN_BITS);
6787f529f01Sjason #endif
6797f529f01Sjason 		} else
6807f529f01Sjason 			break;
6817f529f01Sjason 		error = 0;
6827f529f01Sjason 		break;
6837f529f01Sjason 	case CSAUDIO_CD_LVL:
6847f529f01Sjason 		if (cp->type != AUDIO_MIXER_VALUE)
6857f529f01Sjason 			break;
6867f529f01Sjason 		if (cp->un.value.num_channels == 1) {
6877e0aa3e6Sjason 			cs4231_write(sc, SP_LEFT_AUX2_CONTROL,
6887f529f01Sjason 			    cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] &
689a1f7da73Sjason 			    LINE_INPUT_ATTEN_BITS);
6907f529f01Sjason 		} else if (cp->un.value.num_channels == 2) {
6917e0aa3e6Sjason 			cs4231_write(sc, SP_LEFT_AUX2_CONTROL,
6927f529f01Sjason 			    cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] &
693a1f7da73Sjason 			    LINE_INPUT_ATTEN_BITS);
6947e0aa3e6Sjason 			cs4231_write(sc, SP_RIGHT_AUX2_CONTROL,
6957f529f01Sjason 			    cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] &
696a1f7da73Sjason 			    LINE_INPUT_ATTEN_BITS);
6977f529f01Sjason 		} else
6987f529f01Sjason 			break;
6997f529f01Sjason 		error = 0;
7007f529f01Sjason 		break;
7017f529f01Sjason 	case CSAUDIO_MONITOR_LVL:
7027f529f01Sjason 		if (cp->type != AUDIO_MIXER_VALUE)
7037f529f01Sjason 			break;
7047f529f01Sjason 		if (cp->un.value.num_channels == 1)
7057e0aa3e6Sjason 			cs4231_write(sc, SP_DIGITAL_MIX,
7067f529f01Sjason 			    cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] << 2);
7077f529f01Sjason 		else
7087f529f01Sjason 			break;
7097f529f01Sjason 		error = 0;
7107f529f01Sjason 		break;
7117f529f01Sjason 	case CSAUDIO_OUTPUT_LVL:
7127f529f01Sjason 		if (cp->type != AUDIO_MIXER_VALUE)
7137f529f01Sjason 			break;
7147f529f01Sjason 		if (cp->un.value.num_channels == 1) {
7157f529f01Sjason 			sc->sc_volume[CSPORT_SPEAKER].left =
7167f529f01Sjason 			    cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
7177f529f01Sjason 			sc->sc_volume[CSPORT_SPEAKER].right =
7187f529f01Sjason 			    cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
7197f529f01Sjason 		}
7207f529f01Sjason 		else if (cp->un.value.num_channels == 2) {
7217f529f01Sjason 			sc->sc_volume[CSPORT_SPEAKER].left =
7227f529f01Sjason 			    cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
7237f529f01Sjason 			sc->sc_volume[CSPORT_SPEAKER].right =
7247f529f01Sjason 			    cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
7257f529f01Sjason 		}
7267f529f01Sjason 		else
7277f529f01Sjason 			break;
7287f529f01Sjason 
7297f529f01Sjason 		cs4231_setup_output(sc);
7307f529f01Sjason 		error = 0;
7317f529f01Sjason 		break;
7327f529f01Sjason 	case CSAUDIO_OUTPUT:
7339820c8cbSjason 		if (cp->type != AUDIO_MIXER_ENUM)
7349820c8cbSjason 			break;
7357f529f01Sjason 		if (cp->un.ord != CSPORT_LINEOUT &&
7367f529f01Sjason 		    cp->un.ord != CSPORT_SPEAKER &&
7377f529f01Sjason 		    cp->un.ord != CSPORT_HEADPHONE)
7387f529f01Sjason 			return (EINVAL);
7397f529f01Sjason 		sc->sc_out_port = cp->un.ord;
7407f529f01Sjason 		cs4231_setup_output(sc);
7417f529f01Sjason 		error = 0;
7427f529f01Sjason 		break;
7437f529f01Sjason 	case CSAUDIO_LINE_IN_MUTE:
7447f529f01Sjason 		if (cp->type != AUDIO_MIXER_ENUM)
7457f529f01Sjason 			break;
7467f529f01Sjason 		sc->sc_mute[CSPORT_LINEIN] = cp->un.ord ? 1 : 0;
7477f529f01Sjason 		error = 0;
7487f529f01Sjason 		break;
7497f529f01Sjason 	case CSAUDIO_DAC_MUTE:
7507f529f01Sjason 		if (cp->type != AUDIO_MIXER_ENUM)
7517f529f01Sjason 			break;
7527f529f01Sjason 		sc->sc_mute[CSPORT_AUX1] = cp->un.ord ? 1 : 0;
7537f529f01Sjason 		error = 0;
7547f529f01Sjason 		break;
7557f529f01Sjason 	case CSAUDIO_CD_MUTE:
7567f529f01Sjason 		if (cp->type != AUDIO_MIXER_ENUM)
7577f529f01Sjason 			break;
7587f529f01Sjason 		sc->sc_mute[CSPORT_AUX2] = cp->un.ord ? 1 : 0;
7597f529f01Sjason 		error = 0;
7607f529f01Sjason 		break;
7617f529f01Sjason 	case CSAUDIO_MIC_MUTE:
7627f529f01Sjason 		if (cp->type != AUDIO_MIXER_ENUM)
7637f529f01Sjason 			break;
7647f529f01Sjason 		sc->sc_mute[CSPORT_MONO] = cp->un.ord ? 1 : 0;
7657f529f01Sjason 		error = 0;
7667f529f01Sjason 		break;
7677f529f01Sjason 	case CSAUDIO_MONITOR_MUTE:
7687f529f01Sjason 		if (cp->type != AUDIO_MIXER_ENUM)
7697f529f01Sjason 			break;
7707f529f01Sjason 		sc->sc_mute[CSPORT_MONITOR] = cp->un.ord ? 1 : 0;
7717f529f01Sjason 		error = 0;
7727f529f01Sjason 		break;
7737f529f01Sjason 	case CSAUDIO_OUTPUT_MUTE:
7747f529f01Sjason 		if (cp->type != AUDIO_MIXER_ENUM)
7757f529f01Sjason 			break;
7767f529f01Sjason 		sc->sc_mute[CSPORT_SPEAKER] = cp->un.ord ? 1 : 0;
7777f529f01Sjason 		cs4231_setup_output(sc);
7787f529f01Sjason 		error = 0;
7797f529f01Sjason 		break;
7807f529f01Sjason 	case CSAUDIO_REC_LVL:
7817f529f01Sjason 		if (cp->type != AUDIO_MIXER_VALUE)
7827f529f01Sjason 			break;
7839820c8cbSjason 		if (cp->un.value.num_channels == 1) {
7849820c8cbSjason 			sc->sc_adc.left =
7859820c8cbSjason 			    cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
7869820c8cbSjason 			sc->sc_adc.right =
7879820c8cbSjason 			    cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
7889820c8cbSjason 		} else if (cp->un.value.num_channels == 2) {
7899820c8cbSjason 			sc->sc_adc.left =
7909820c8cbSjason 			    cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
7919820c8cbSjason 			sc->sc_adc.right =
7929820c8cbSjason 			    cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
7939820c8cbSjason 		} else
7949820c8cbSjason 			break;
7959820c8cbSjason 		cs4231_setup_output(sc);
7969820c8cbSjason 		error = 0;
7977f529f01Sjason 		break;
7987f529f01Sjason 	case CSAUDIO_RECORD_SOURCE:
7997f529f01Sjason 		if (cp->type != AUDIO_MIXER_ENUM)
8007f529f01Sjason 			break;
8019820c8cbSjason 		if (cp->un.ord == CSPORT_MICROPHONE ||
8029820c8cbSjason 		    cp->un.ord == CSPORT_LINEIN ||
8039820c8cbSjason 		    cp->un.ord == CSPORT_AUX1 ||
8049820c8cbSjason 		    cp->un.ord == CSPORT_DAC) {
8059820c8cbSjason 			sc->sc_in_port  = cp->un.ord;
8069820c8cbSjason 			error = 0;
8079820c8cbSjason 			cs4231_setup_output(sc);
8089820c8cbSjason 		}
8097f529f01Sjason 		break;
8107f529f01Sjason 	}
8117f529f01Sjason 
8127f529f01Sjason 	return (error);
8137f529f01Sjason }
8147f529f01Sjason 
8157f529f01Sjason int
cs4231_get_port(void * vsc,mixer_ctrl_t * cp)8166e4fde28Sjason cs4231_get_port(void *vsc, mixer_ctrl_t *cp)
8177f529f01Sjason {
8186e4fde28Sjason 	struct cs4231_softc *sc = (struct cs4231_softc *)vsc;
8197f529f01Sjason 	int error = EINVAL;
8207f529f01Sjason 
8217f529f01Sjason 	DPRINTF(("cs4231_get_port: port=%d type=%d\n", cp->dev, cp->type));
8227f529f01Sjason 
8237f529f01Sjason 	switch (cp->dev) {
8247f529f01Sjason 	case CSAUDIO_DAC_LVL:
8257f529f01Sjason 		if (cp->type != AUDIO_MIXER_VALUE)
8267f529f01Sjason 			break;
8277f529f01Sjason 		if (cp->un.value.num_channels == 1)
8287f529f01Sjason 			cp->un.value.level[AUDIO_MIXER_LEVEL_MONO]=
8297e0aa3e6Sjason 			    cs4231_read(sc, SP_LEFT_AUX1_CONTROL) &
830a1f7da73Sjason 			    LINE_INPUT_ATTEN_BITS;
8317f529f01Sjason 		else if (cp->un.value.num_channels == 2) {
8327f529f01Sjason 			cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] =
8337e0aa3e6Sjason 			    cs4231_read(sc, SP_LEFT_AUX1_CONTROL) &
834a1f7da73Sjason 			    LINE_INPUT_ATTEN_BITS;
8357f529f01Sjason 			cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] =
8367e0aa3e6Sjason 			    cs4231_read(sc, SP_RIGHT_AUX1_CONTROL) &
837a1f7da73Sjason 			    LINE_INPUT_ATTEN_BITS;
8387f529f01Sjason 		} else
8397f529f01Sjason 			break;
8407f529f01Sjason 		error = 0;
8417f529f01Sjason 		break;
8427f529f01Sjason 	case CSAUDIO_LINE_IN_LVL:
8437f529f01Sjason 		if (cp->type != AUDIO_MIXER_VALUE)
8447f529f01Sjason 			break;
8457f529f01Sjason 		if (cp->un.value.num_channels == 1)
8467f529f01Sjason 			cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] =
847a1f7da73Sjason 			    cs4231_read(sc, CS_LEFT_LINE_CONTROL) & AUX_INPUT_ATTEN_BITS;
8487f529f01Sjason 		else if (cp->un.value.num_channels == 2) {
8497f529f01Sjason 			cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] =
850a1f7da73Sjason 			    cs4231_read(sc, CS_LEFT_LINE_CONTROL) & AUX_INPUT_ATTEN_BITS;
8517f529f01Sjason 			cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] =
852a1f7da73Sjason 			    cs4231_read(sc, CS_RIGHT_LINE_CONTROL) & AUX_INPUT_ATTEN_BITS;
8537f529f01Sjason 		} else
8547f529f01Sjason 			break;
8557f529f01Sjason 		error = 0;
8567f529f01Sjason 		break;
8577f529f01Sjason 	case CSAUDIO_MIC_LVL:
8587f529f01Sjason 		if (cp->type != AUDIO_MIXER_VALUE)
8597f529f01Sjason 			break;
8607f529f01Sjason 		if (cp->un.value.num_channels == 1) {
8617f529f01Sjason #if 0
8627f529f01Sjason 			cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] =
8637e0aa3e6Sjason 			    cs4231_read(sc, CS_MONO_IO_CONTROL) &
864a1f7da73Sjason 			    MONO_INPUT_ATTEN_BITS;
8657f529f01Sjason #endif
8667f529f01Sjason 		} else
8677f529f01Sjason 			break;
8687f529f01Sjason 		error = 0;
8697f529f01Sjason 		break;
8707f529f01Sjason 	case CSAUDIO_CD_LVL:
8717f529f01Sjason 		if (cp->type != AUDIO_MIXER_VALUE)
8727f529f01Sjason 			break;
8737f529f01Sjason 		if (cp->un.value.num_channels == 1)
8747f529f01Sjason 			cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] =
8757e0aa3e6Sjason 			    cs4231_read(sc, SP_LEFT_AUX2_CONTROL) &
876a1f7da73Sjason 			    LINE_INPUT_ATTEN_BITS;
8777f529f01Sjason 		else if (cp->un.value.num_channels == 2) {
8787f529f01Sjason 			cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] =
8797e0aa3e6Sjason 			    cs4231_read(sc, SP_LEFT_AUX2_CONTROL) &
880a1f7da73Sjason 			    LINE_INPUT_ATTEN_BITS;
8817f529f01Sjason 			cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] =
8827e0aa3e6Sjason 			    cs4231_read(sc, SP_RIGHT_AUX2_CONTROL) &
883a1f7da73Sjason 			    LINE_INPUT_ATTEN_BITS;
8847f529f01Sjason 		}
8857f529f01Sjason 		else
8867f529f01Sjason 			break;
8877f529f01Sjason 		error = 0;
8887f529f01Sjason 		break;
8897f529f01Sjason 	case CSAUDIO_MONITOR_LVL:
8907f529f01Sjason 		if (cp->type != AUDIO_MIXER_VALUE)
8917f529f01Sjason 			break;
8927f529f01Sjason 		if (cp->un.value.num_channels != 1)
8937f529f01Sjason 			break;
8947f529f01Sjason 		cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] =
8957e0aa3e6Sjason 		    cs4231_read(sc, SP_DIGITAL_MIX) >> 2;
8967f529f01Sjason 		error = 0;
8977f529f01Sjason 		break;
8987f529f01Sjason 	case CSAUDIO_OUTPUT_LVL:
8997f529f01Sjason 		if (cp->type != AUDIO_MIXER_VALUE)
9007f529f01Sjason 			break;
9017f529f01Sjason 		if (cp->un.value.num_channels == 1)
9027f529f01Sjason 			cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] =
9037f529f01Sjason 			    sc->sc_volume[CSPORT_SPEAKER].left;
9047f529f01Sjason 		else if (cp->un.value.num_channels == 2) {
9057f529f01Sjason 			cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] =
9067f529f01Sjason 			    sc->sc_volume[CSPORT_SPEAKER].left;
9077f529f01Sjason 			cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] =
9087f529f01Sjason 			    sc->sc_volume[CSPORT_SPEAKER].right;
9097f529f01Sjason 		}
9107f529f01Sjason 		else
9117f529f01Sjason 			break;
9127f529f01Sjason 		error = 0;
9137f529f01Sjason 		break;
9147f529f01Sjason 	case CSAUDIO_LINE_IN_MUTE:
9157f529f01Sjason 		if (cp->type != AUDIO_MIXER_ENUM)
9167f529f01Sjason 			break;
9177f529f01Sjason 		cp->un.ord = sc->sc_mute[CSPORT_LINEIN] ? 1 : 0;
9187f529f01Sjason 		error = 0;
9197f529f01Sjason 		break;
9207f529f01Sjason 	case CSAUDIO_DAC_MUTE:
9217f529f01Sjason 		if (cp->type != AUDIO_MIXER_ENUM)
9227f529f01Sjason 			break;
9237f529f01Sjason 		cp->un.ord = sc->sc_mute[CSPORT_AUX1] ? 1 : 0;
9247f529f01Sjason 		error = 0;
9257f529f01Sjason 		break;
9267f529f01Sjason 	case CSAUDIO_CD_MUTE:
9277f529f01Sjason 		if (cp->type != AUDIO_MIXER_ENUM)
9287f529f01Sjason 			break;
9297f529f01Sjason 		cp->un.ord = sc->sc_mute[CSPORT_AUX2] ? 1 : 0;
9307f529f01Sjason 		error = 0;
9317f529f01Sjason 		break;
9327f529f01Sjason 	case CSAUDIO_MIC_MUTE:
9337f529f01Sjason 		if (cp->type != AUDIO_MIXER_ENUM)
9347f529f01Sjason 			break;
9357f529f01Sjason 		cp->un.ord = sc->sc_mute[CSPORT_MONO] ? 1 : 0;
9367f529f01Sjason 		error = 0;
9377f529f01Sjason 		break;
9387f529f01Sjason 	case CSAUDIO_MONITOR_MUTE:
9397f529f01Sjason 		if (cp->type != AUDIO_MIXER_ENUM)
9407f529f01Sjason 			break;
9417f529f01Sjason 		cp->un.ord = sc->sc_mute[CSPORT_MONITOR] ? 1 : 0;
9427f529f01Sjason 		error = 0;
9437f529f01Sjason 		break;
9447f529f01Sjason 	case CSAUDIO_OUTPUT_MUTE:
9457f529f01Sjason 		if (cp->type != AUDIO_MIXER_ENUM)
9467f529f01Sjason 			break;
9477f529f01Sjason 		cp->un.ord = sc->sc_mute[CSPORT_SPEAKER] ? 1 : 0;
9487f529f01Sjason 		error = 0;
9497f529f01Sjason 		break;
9507f529f01Sjason 	case CSAUDIO_REC_LVL:
9517f529f01Sjason 		if (cp->type != AUDIO_MIXER_VALUE)
9527f529f01Sjason 			break;
9537f529f01Sjason 		if (cp->un.value.num_channels == 1) {
9547f529f01Sjason 			cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] =
9559820c8cbSjason 			    sc->sc_adc.left;
9567f529f01Sjason 		} else if (cp->un.value.num_channels == 2) {
9577f529f01Sjason 			cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] =
9589820c8cbSjason 			    sc->sc_adc.left;
9597f529f01Sjason 			cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] =
9609820c8cbSjason 			    sc->sc_adc.right;
9617f529f01Sjason 		} else
9627f529f01Sjason 			break;
9637f529f01Sjason 		error = 0;
9647f529f01Sjason 		break;
9657f529f01Sjason 	case CSAUDIO_RECORD_SOURCE:
9669820c8cbSjason 		if (cp->type != AUDIO_MIXER_ENUM)
9679820c8cbSjason 			break;
9689820c8cbSjason 		cp->un.ord = sc->sc_in_port;
9697f529f01Sjason 		error = 0;
9707f529f01Sjason 		break;
9717f529f01Sjason 	case CSAUDIO_OUTPUT:
9729820c8cbSjason 		if (cp->type != AUDIO_MIXER_ENUM)
9739820c8cbSjason 			break;
9747f529f01Sjason 		cp->un.ord = sc->sc_out_port;
9757f529f01Sjason 		error = 0;
9767f529f01Sjason 		break;
9777f529f01Sjason 	}
9787f529f01Sjason 	return (error);
9797f529f01Sjason }
9807f529f01Sjason 
9817f529f01Sjason int
cs4231_query_devinfo(void * vsc,mixer_devinfo_t * dip)9826e4fde28Sjason cs4231_query_devinfo(void *vsc, mixer_devinfo_t *dip)
9837f529f01Sjason {
9847f529f01Sjason 	int err = 0;
9857f529f01Sjason 
9867f529f01Sjason 	switch (dip->index) {
9877f529f01Sjason 	case CSAUDIO_MIC_LVL:		/* mono/microphone mixer */
9887f529f01Sjason 		dip->type = AUDIO_MIXER_VALUE;
9897f529f01Sjason 		dip->mixer_class = CSAUDIO_INPUT_CLASS;
9907f529f01Sjason 		dip->prev = AUDIO_MIXER_LAST;
9917f529f01Sjason 		dip->next = CSAUDIO_MIC_MUTE;
9922627362dSho 		strlcpy(dip->label.name, AudioNmicrophone,
9932627362dSho 		    sizeof dip->label.name);
9947f529f01Sjason 		dip->un.v.num_channels = 1;
9952627362dSho 		strlcpy(dip->un.v.units.name, AudioNvolume,
9962627362dSho 		    sizeof dip->un.v.units.name);
9977f529f01Sjason 		break;
9987f529f01Sjason 	case CSAUDIO_DAC_LVL:		/* dacout */
9997f529f01Sjason 		dip->type = AUDIO_MIXER_VALUE;
10007f529f01Sjason 		dip->mixer_class = CSAUDIO_INPUT_CLASS;
10017f529f01Sjason 		dip->prev = AUDIO_MIXER_LAST;
10027f529f01Sjason 		dip->next = CSAUDIO_DAC_MUTE;
10032627362dSho 		strlcpy(dip->label.name, AudioNdac,
10042627362dSho 		    sizeof dip->label.name);
10057f529f01Sjason 		dip->un.v.num_channels = 2;
10062627362dSho 		strlcpy(dip->un.v.units.name, AudioNvolume,
10072627362dSho 		    sizeof dip->un.v.units.name);
10087f529f01Sjason 		break;
10097f529f01Sjason 	case CSAUDIO_LINE_IN_LVL:	/* line */
10107f529f01Sjason 		dip->type = AUDIO_MIXER_VALUE;
10117f529f01Sjason 		dip->mixer_class = CSAUDIO_INPUT_CLASS;
10127f529f01Sjason 		dip->prev = AUDIO_MIXER_LAST;
10137f529f01Sjason 		dip->next = CSAUDIO_LINE_IN_MUTE;
10142627362dSho 		strlcpy(dip->label.name, AudioNline, sizeof dip->label.name);
10157f529f01Sjason 		dip->un.v.num_channels = 2;
10162627362dSho 		strlcpy(dip->un.v.units.name, AudioNvolume,
10172627362dSho 		    sizeof dip->un.v.units.name);
10187f529f01Sjason 		break;
10197f529f01Sjason 	case CSAUDIO_CD_LVL:		/* cd */
10207f529f01Sjason 		dip->type = AUDIO_MIXER_VALUE;
10217f529f01Sjason 		dip->mixer_class = CSAUDIO_INPUT_CLASS;
10227f529f01Sjason 		dip->prev = AUDIO_MIXER_LAST;
10237f529f01Sjason 		dip->next = CSAUDIO_CD_MUTE;
10242627362dSho 		strlcpy(dip->label.name, AudioNcd, sizeof dip->label.name);
10257f529f01Sjason 		dip->un.v.num_channels = 2;
10262627362dSho 		strlcpy(dip->un.v.units.name, AudioNvolume,
10272627362dSho 		    sizeof dip->un.v.units.name);
10287f529f01Sjason 		break;
10297f529f01Sjason 	case CSAUDIO_MONITOR_LVL:	/* monitor level */
10307f529f01Sjason 		dip->type = AUDIO_MIXER_VALUE;
10317f529f01Sjason 		dip->mixer_class = CSAUDIO_MONITOR_CLASS;
10327f529f01Sjason 		dip->prev = AUDIO_MIXER_LAST;
10337f529f01Sjason 		dip->next = CSAUDIO_MONITOR_MUTE;
10342627362dSho 		strlcpy(dip->label.name, AudioNmonitor,
10352627362dSho 		    sizeof dip->label.name);
10367f529f01Sjason 		dip->un.v.num_channels = 1;
10372627362dSho 		strlcpy(dip->un.v.units.name, AudioNvolume,
10382627362dSho 		    sizeof dip->un.v.units.name);
10397f529f01Sjason 		break;
10407f529f01Sjason 	case CSAUDIO_OUTPUT_LVL:
10417f529f01Sjason 		dip->type = AUDIO_MIXER_VALUE;
10427f529f01Sjason 		dip->mixer_class = CSAUDIO_OUTPUT_CLASS;
10437f529f01Sjason 		dip->prev = AUDIO_MIXER_LAST;
10447f529f01Sjason 		dip->next = CSAUDIO_OUTPUT_MUTE;
10452627362dSho 		strlcpy(dip->label.name, AudioNoutput, sizeof dip->label.name);
10467f529f01Sjason 		dip->un.v.num_channels = 2;
10472627362dSho 		strlcpy(dip->un.v.units.name, AudioNvolume,
10482627362dSho 		    sizeof dip->un.v.units.name);
10497f529f01Sjason 		break;
10507f529f01Sjason 	case CSAUDIO_LINE_IN_MUTE:
10517f529f01Sjason 		dip->type = AUDIO_MIXER_ENUM;
10527f529f01Sjason 		dip->mixer_class = CSAUDIO_INPUT_CLASS;
10537f529f01Sjason 		dip->prev = CSAUDIO_LINE_IN_LVL;
10547f529f01Sjason 		dip->next = AUDIO_MIXER_LAST;
10557f529f01Sjason 		goto mute;
10567f529f01Sjason 	case CSAUDIO_DAC_MUTE:
10577f529f01Sjason 		dip->type = AUDIO_MIXER_ENUM;
10587f529f01Sjason 		dip->mixer_class = CSAUDIO_INPUT_CLASS;
10597f529f01Sjason 		dip->prev = CSAUDIO_DAC_LVL;
10607f529f01Sjason 		dip->next = AUDIO_MIXER_LAST;
10617f529f01Sjason 		goto mute;
10627f529f01Sjason 	case CSAUDIO_CD_MUTE:
10637f529f01Sjason 		dip->type = AUDIO_MIXER_ENUM;
10647f529f01Sjason 		dip->mixer_class = CSAUDIO_INPUT_CLASS;
10657f529f01Sjason 		dip->prev = CSAUDIO_CD_LVL;
10667f529f01Sjason 		dip->next = AUDIO_MIXER_LAST;
10677f529f01Sjason 		goto mute;
10687f529f01Sjason 	case CSAUDIO_MIC_MUTE:
10697f529f01Sjason 		dip->type = AUDIO_MIXER_ENUM;
10707f529f01Sjason 		dip->mixer_class = CSAUDIO_INPUT_CLASS;
10717f529f01Sjason 		dip->prev = CSAUDIO_MIC_LVL;
10727f529f01Sjason 		dip->next = AUDIO_MIXER_LAST;
10737f529f01Sjason 		goto mute;
10747f529f01Sjason 	case CSAUDIO_MONITOR_MUTE:
10757f529f01Sjason 		dip->type = AUDIO_MIXER_ENUM;
10767f529f01Sjason 		dip->mixer_class = CSAUDIO_OUTPUT_CLASS;
10777f529f01Sjason 		dip->prev = CSAUDIO_MONITOR_LVL;
10787f529f01Sjason 		dip->next = AUDIO_MIXER_LAST;
10797f529f01Sjason 		goto mute;
10807f529f01Sjason 	case CSAUDIO_OUTPUT_MUTE:
10817f529f01Sjason 		dip->type = AUDIO_MIXER_ENUM;
10827f529f01Sjason 		dip->mixer_class = CSAUDIO_OUTPUT_CLASS;
10837f529f01Sjason 		dip->prev = CSAUDIO_OUTPUT_LVL;
10847f529f01Sjason 		dip->next = AUDIO_MIXER_LAST;
10857f529f01Sjason 		goto mute;
10867f529f01Sjason 
10877f529f01Sjason 	mute:
10882627362dSho 		strlcpy(dip->label.name, AudioNmute, sizeof dip->label.name);
10897f529f01Sjason 		dip->un.e.num_mem = 2;
10902627362dSho 		strlcpy(dip->un.e.member[0].label.name, AudioNon,
10912627362dSho 		    sizeof dip->un.e.member[0].label.name);
10927f529f01Sjason 		dip->un.e.member[0].ord = 0;
10932627362dSho 		strlcpy(dip->un.e.member[1].label.name, AudioNoff,
10942627362dSho 		    sizeof dip->un.e.member[1].label.name);
10957f529f01Sjason 		dip->un.e.member[1].ord = 1;
10967f529f01Sjason 		break;
10977f529f01Sjason 	case CSAUDIO_REC_LVL:		/* record level */
10987f529f01Sjason 		dip->type = AUDIO_MIXER_VALUE;
10997f529f01Sjason 		dip->mixer_class = CSAUDIO_RECORD_CLASS;
11007f529f01Sjason 		dip->prev = AUDIO_MIXER_LAST;
11017f529f01Sjason 		dip->next = CSAUDIO_RECORD_SOURCE;
11022627362dSho 		strlcpy(dip->label.name, AudioNrecord, sizeof dip->label.name);
11037f529f01Sjason 		dip->un.v.num_channels = 2;
11042627362dSho 		strlcpy(dip->un.v.units.name, AudioNvolume,
11052627362dSho 		    sizeof dip->un.v.units.name);
11067f529f01Sjason 		break;
11077f529f01Sjason 	case CSAUDIO_RECORD_SOURCE:
11087f529f01Sjason 		dip->type = AUDIO_MIXER_ENUM;
11097f529f01Sjason 		dip->mixer_class = CSAUDIO_RECORD_CLASS;
11107f529f01Sjason 		dip->prev = CSAUDIO_REC_LVL;
11117f529f01Sjason 		dip->next = AUDIO_MIXER_LAST;
11122627362dSho 		strlcpy(dip->label.name, AudioNsource, sizeof dip->label.name);
11139820c8cbSjason 		dip->un.e.num_mem = 4;
11142627362dSho 		strlcpy(dip->un.e.member[0].label.name, AudioNmicrophone,
11152627362dSho 		    sizeof dip->un.e.member[0].label.name);
11169820c8cbSjason 		dip->un.e.member[0].ord = CSPORT_MICROPHONE;
11172627362dSho 		strlcpy(dip->un.e.member[1].label.name, AudioNline,
11182627362dSho 		    sizeof dip->un.e.member[1].label.name);
11199820c8cbSjason 		dip->un.e.member[1].ord = CSPORT_LINEIN;
11202627362dSho 		strlcpy(dip->un.e.member[2].label.name, AudioNcd,
11212627362dSho 		    sizeof dip->un.e.member[2].label.name);
11229820c8cbSjason 		dip->un.e.member[2].ord = CSPORT_AUX1;
11232627362dSho 		strlcpy(dip->un.e.member[3].label.name, AudioNdac,
11242627362dSho 		    sizeof dip->un.e.member[3].label.name);
11259820c8cbSjason 		dip->un.e.member[3].ord = CSPORT_DAC;
11267f529f01Sjason 		break;
11277f529f01Sjason 	case CSAUDIO_OUTPUT:
11287f529f01Sjason 		dip->type = AUDIO_MIXER_ENUM;
11297f529f01Sjason 		dip->mixer_class = CSAUDIO_MONITOR_CLASS;
11307f529f01Sjason 		dip->prev = dip->next = AUDIO_MIXER_LAST;
11312627362dSho 		strlcpy(dip->label.name, AudioNoutput, sizeof dip->label.name);
11327f529f01Sjason 		dip->un.e.num_mem = 3;
11332627362dSho 		strlcpy(dip->un.e.member[0].label.name, AudioNspeaker,
11342627362dSho 		    sizeof dip->un.e.member[0].label.name);
11357f529f01Sjason 		dip->un.e.member[0].ord = CSPORT_SPEAKER;
11362627362dSho 		strlcpy(dip->un.e.member[1].label.name, AudioNline,
11372627362dSho 		    sizeof dip->un.e.member[1].label.name);
11387f529f01Sjason 		dip->un.e.member[1].ord = CSPORT_LINEOUT;
11392627362dSho 		strlcpy(dip->un.e.member[2].label.name, AudioNheadphone,
11402627362dSho 		    sizeof dip->un.e.member[2].label.name);
11417f529f01Sjason 		dip->un.e.member[2].ord = CSPORT_HEADPHONE;
11427f529f01Sjason 		break;
11437f529f01Sjason 	case CSAUDIO_INPUT_CLASS:	/* input class descriptor */
11447f529f01Sjason 		dip->type = AUDIO_MIXER_CLASS;
11457f529f01Sjason 		dip->mixer_class = CSAUDIO_INPUT_CLASS;
11467f529f01Sjason 		dip->prev = AUDIO_MIXER_LAST;
11477f529f01Sjason 		dip->next = AUDIO_MIXER_LAST;
11482627362dSho 		strlcpy(dip->label.name, AudioCinputs, sizeof dip->label.name);
11497f529f01Sjason 		break;
11507f529f01Sjason 	case CSAUDIO_OUTPUT_CLASS:	/* output class descriptor */
11517f529f01Sjason 		dip->type = AUDIO_MIXER_CLASS;
11527f529f01Sjason 		dip->mixer_class = CSAUDIO_OUTPUT_CLASS;
11537f529f01Sjason 		dip->prev = AUDIO_MIXER_LAST;
11547f529f01Sjason 		dip->next = AUDIO_MIXER_LAST;
11552627362dSho 		strlcpy(dip->label.name, AudioCoutputs,
11562627362dSho 		    sizeof dip->label.name);
11577f529f01Sjason 		break;
11587f529f01Sjason 	case CSAUDIO_MONITOR_CLASS:	/* monitor class descriptor */
11597f529f01Sjason 		dip->type = AUDIO_MIXER_CLASS;
11607f529f01Sjason 		dip->mixer_class = CSAUDIO_MONITOR_CLASS;
11617f529f01Sjason 		dip->prev = AUDIO_MIXER_LAST;
11627f529f01Sjason 		dip->next = AUDIO_MIXER_LAST;
11632627362dSho 		strlcpy(dip->label.name, AudioCmonitor,
11642627362dSho 		    sizeof dip->label.name);
11657f529f01Sjason 		break;
11667f529f01Sjason 	case CSAUDIO_RECORD_CLASS:	/* record class descriptor */
11677f529f01Sjason 		dip->type = AUDIO_MIXER_CLASS;
11687f529f01Sjason 		dip->mixer_class = CSAUDIO_RECORD_CLASS;
11697f529f01Sjason 		dip->prev = AUDIO_MIXER_LAST;
11707f529f01Sjason 		dip->next = AUDIO_MIXER_LAST;
11712627362dSho 		strlcpy(dip->label.name, AudioCrecord, sizeof dip->label.name);
11727f529f01Sjason 		break;
11737f529f01Sjason 	default:
11747f529f01Sjason 		err = ENXIO;
11757f529f01Sjason 	}
11767f529f01Sjason 
11777f529f01Sjason 	return (err);
11787f529f01Sjason }
11797f529f01Sjason 
11807f529f01Sjason /*
11817f529f01Sjason  * Hardware interrupt handler
11827f529f01Sjason  */
11837f529f01Sjason int
cs4231_intr(void * vsc)11846e4fde28Sjason cs4231_intr(void *vsc)
11857f529f01Sjason {
11866e4fde28Sjason 	struct cs4231_softc *sc = (struct cs4231_softc *)vsc;
11877f529f01Sjason 	u_int32_t csr;
11887f529f01Sjason 	u_int8_t reg, status;
11897f529f01Sjason 	struct cs_dma *p;
11907f529f01Sjason 	int r = 0;
11917f529f01Sjason 
1192886882aaSratchov 	mtx_enter(&audio_lock);
11937f529f01Sjason 	csr = APC_READ(sc, APC_CSR);
1194bf383a97Sjason 	APC_WRITE(sc, APC_CSR, csr);
1195bf383a97Sjason 
1196bf383a97Sjason 	if ((csr & APC_CSR_EIE) && (csr & APC_CSR_EI)) {
1197bf383a97Sjason 		printf("%s: error interrupt\n", sc->sc_dev.dv_xname);
1198bf383a97Sjason 		r = 1;
1199bf383a97Sjason 	}
1200bf383a97Sjason 
1201bf383a97Sjason 	if ((csr & APC_CSR_PIE) && (csr & APC_CSR_PI)) {
1202bf383a97Sjason 		/* playback interrupt */
1203bf383a97Sjason 		r = 1;
1204bf383a97Sjason 	}
1205bf383a97Sjason 
1206bf383a97Sjason 	if ((csr & APC_CSR_GIE) && (csr & APC_CSR_GI)) {
1207bf383a97Sjason 		/* general interrupt */
1208a1f7da73Sjason 		status = CS_READ(sc, AD1848_STATUS);
1209a1f7da73Sjason 		if (status & (INTERRUPT_STATUS | SAMPLE_ERROR)) {
12107e0aa3e6Sjason 			reg = cs4231_read(sc, CS_IRQ_STATUS);
12117f529f01Sjason 			if (reg & CS_AFS_PI) {
12127e0aa3e6Sjason 				cs4231_write(sc, SP_LOWER_BASE_COUNT, 0xff);
12137e0aa3e6Sjason 				cs4231_write(sc, SP_UPPER_BASE_COUNT, 0xff);
12147f529f01Sjason 			}
12159820c8cbSjason 			if (reg & CS_AFS_CI) {
12169820c8cbSjason 				cs4231_write(sc, CS_LOWER_REC_CNT, 0xff);
12179820c8cbSjason 				cs4231_write(sc, CS_UPPER_REC_CNT, 0xff);
12189820c8cbSjason 			}
1219a1f7da73Sjason 			CS_WRITE(sc, AD1848_STATUS, 0);
12207f529f01Sjason 		}
1221bf383a97Sjason 		r = 1;
1222bf383a97Sjason 	}
12237f529f01Sjason 
12247f529f01Sjason 
12257f529f01Sjason 	if (csr & (APC_CSR_PI|APC_CSR_PMI|APC_CSR_PIE|APC_CSR_PD))
12267f529f01Sjason 		r = 1;
12277f529f01Sjason 
1228bf383a97Sjason 	if ((csr & APC_CSR_PMIE) && (csr & APC_CSR_PMI)) {
12299820c8cbSjason 		struct cs_channel *chan = &sc->sc_playback;
12307f529f01Sjason 		u_long nextaddr, togo;
12317f529f01Sjason 
12329820c8cbSjason 		p = chan->cs_curdma;
12339820c8cbSjason 		togo = chan->cs_segsz - chan->cs_cnt;
12347f529f01Sjason 		if (togo == 0) {
12357f529f01Sjason 			nextaddr = (u_int32_t)p->dmamap->dm_segs[0].ds_addr;
12369820c8cbSjason 			chan->cs_cnt = togo = chan->cs_blksz;
12377f529f01Sjason 		} else {
12389820c8cbSjason 			nextaddr = APC_READ(sc, APC_PNVA) + chan->cs_blksz;
12399820c8cbSjason 			if (togo > chan->cs_blksz)
12409820c8cbSjason 				togo = chan->cs_blksz;
12419820c8cbSjason 			chan->cs_cnt += togo;
12427f529f01Sjason 		}
12437f529f01Sjason 
12447f529f01Sjason 		APC_WRITE(sc, APC_PNVA, nextaddr);
12457f529f01Sjason 		APC_WRITE(sc, APC_PNC, togo);
12467f529f01Sjason 
12479820c8cbSjason 		if (chan->cs_intr != NULL)
12489820c8cbSjason 			(*chan->cs_intr)(chan->cs_arg);
12497f529f01Sjason 		r = 1;
12507f529f01Sjason 	}
12517f529f01Sjason 
12529820c8cbSjason 	if ((csr & APC_CSR_CIE) && (csr & APC_CSR_CI)) {
12539820c8cbSjason 		if (csr & APC_CSR_CD) {
12549820c8cbSjason 			struct cs_channel *chan = &sc->sc_capture;
12559820c8cbSjason 			u_long nextaddr, togo;
12569820c8cbSjason 
12579820c8cbSjason 			p = chan->cs_curdma;
12589820c8cbSjason 			togo = chan->cs_segsz - chan->cs_cnt;
12599820c8cbSjason 			if (togo == 0) {
12609820c8cbSjason 				nextaddr =
12619820c8cbSjason 				    (u_int32_t)p->dmamap->dm_segs[0].ds_addr;
12629820c8cbSjason 				chan->cs_cnt = togo = chan->cs_blksz;
12639820c8cbSjason 			} else {
12649820c8cbSjason 				nextaddr = APC_READ(sc, APC_CNVA) +
12659820c8cbSjason 				    chan->cs_blksz;
12669820c8cbSjason 				if (togo > chan->cs_blksz)
12679820c8cbSjason 					togo = chan->cs_blksz;
12689820c8cbSjason 				chan->cs_cnt += togo;
12699820c8cbSjason 			}
12709820c8cbSjason 
12719820c8cbSjason 			APC_WRITE(sc, APC_CNVA, nextaddr);
12729820c8cbSjason 			APC_WRITE(sc, APC_CNC, togo);
12739820c8cbSjason 
12749820c8cbSjason 			if (chan->cs_intr != NULL)
12759820c8cbSjason 				(*chan->cs_intr)(chan->cs_arg);
12769820c8cbSjason 		}
12777f529f01Sjason 		r = 1;
12787f529f01Sjason 	}
12799820c8cbSjason 
12809820c8cbSjason 	if ((csr & APC_CSR_CMIE) && (csr & APC_CSR_CMI)) {
12819820c8cbSjason 		/* capture empty */
12829820c8cbSjason 		r = 1;
12837f529f01Sjason 	}
12847f529f01Sjason 
1285886882aaSratchov 	mtx_leave(&audio_lock);
12867f529f01Sjason 	return (r);
12877f529f01Sjason }
12887f529f01Sjason 
12897f529f01Sjason void *
cs4231_alloc(void * vsc,int direction,size_t size,int pool,int flags)12906e4fde28Sjason cs4231_alloc(void *vsc, int direction, size_t size, int pool, int flags)
12917f529f01Sjason {
12926e4fde28Sjason 	struct cs4231_softc *sc = (struct cs4231_softc *)vsc;
12937f529f01Sjason 	bus_dma_tag_t dmat = sc->sc_dmatag;
12947f529f01Sjason 	struct cs_dma *p;
12957f529f01Sjason 
12967f529f01Sjason 	p = (struct cs_dma *)malloc(sizeof(struct cs_dma), pool, flags);
12977f529f01Sjason 	if (p == NULL)
12984223a637Smiod 		return (NULL);
12997f529f01Sjason 
13007f529f01Sjason 	if (bus_dmamap_create(dmat, size, 1, size, 0,
13017f529f01Sjason 	    BUS_DMA_NOWAIT, &p->dmamap) != 0)
13027f529f01Sjason 		goto fail;
13037f529f01Sjason 
13047f529f01Sjason 	p->size = size;
13057f529f01Sjason 
13067f529f01Sjason 	if (bus_dmamem_alloc(dmat, size, 64*1024, 0, p->segs,
1307d9c3e9c1Sjasper 	    nitems(p->segs), &p->nsegs,
13087f529f01Sjason 	    BUS_DMA_NOWAIT) != 0)
13097f529f01Sjason 		goto fail1;
13107f529f01Sjason 
13117f529f01Sjason 	if (bus_dmamem_map(dmat, p->segs, p->nsegs, p->size,
13127f529f01Sjason 	    &p->addr, BUS_DMA_NOWAIT | BUS_DMA_COHERENT) != 0)
13137f529f01Sjason 		goto fail2;
13147f529f01Sjason 
13157f529f01Sjason 	if (bus_dmamap_load(dmat, p->dmamap, p->addr, size, NULL,
13167f529f01Sjason 	    BUS_DMA_NOWAIT) != 0)
13177f529f01Sjason 		goto fail3;
13187f529f01Sjason 
13197f529f01Sjason 	p->next = sc->sc_dmas;
13207f529f01Sjason 	sc->sc_dmas = p;
13217f529f01Sjason 	return (p->addr);
13227f529f01Sjason 
13237f529f01Sjason fail3:
13247f529f01Sjason 	bus_dmamem_unmap(dmat, p->addr, p->size);
13257f529f01Sjason fail2:
13267f529f01Sjason 	bus_dmamem_free(dmat, p->segs, p->nsegs);
13277f529f01Sjason fail1:
13287f529f01Sjason 	bus_dmamap_destroy(dmat, p->dmamap);
13297f529f01Sjason fail:
1330df2ac69fStedu 	free(p, pool, 0);
13317f529f01Sjason 	return (NULL);
13327f529f01Sjason }
13337f529f01Sjason 
13347f529f01Sjason void
cs4231_free(void * vsc,void * ptr,int pool)13356e4fde28Sjason cs4231_free(void *vsc, void *ptr, int pool)
13367f529f01Sjason {
13376e4fde28Sjason 	struct cs4231_softc *sc = vsc;
13387f529f01Sjason 	bus_dma_tag_t dmat = sc->sc_dmatag;
13397f529f01Sjason 	struct cs_dma *p, **pp;
13407f529f01Sjason 
13417f529f01Sjason 	for (pp = &sc->sc_dmas; (p = *pp) != NULL; pp = &(*pp)->next) {
13427f529f01Sjason 		if (p->addr != ptr)
13437f529f01Sjason 			continue;
13447f529f01Sjason 		bus_dmamap_unload(dmat, p->dmamap);
13457f529f01Sjason 		bus_dmamem_unmap(dmat, p->addr, p->size);
13467f529f01Sjason 		bus_dmamem_free(dmat, p->segs, p->nsegs);
13477f529f01Sjason 		bus_dmamap_destroy(dmat, p->dmamap);
13487f529f01Sjason 		*pp = p->next;
1349df2ac69fStedu 		free(p, pool, 0);
13507f529f01Sjason 		return;
13517f529f01Sjason 	}
13527f529f01Sjason 	printf("%s: attempt to free rogue pointer\n", sc->sc_dev.dv_xname);
13537f529f01Sjason }
13547f529f01Sjason 
13557f529f01Sjason int
cs4231_trigger_output(void * vsc,void * start,void * end,int blksize,void (* intr)(void *),void * arg,struct audio_params * param)13566e4fde28Sjason cs4231_trigger_output(void *vsc, void *start, void *end, int blksize,
13576e4fde28Sjason     void (*intr)(void *), void *arg, struct audio_params *param)
13587f529f01Sjason {
13596e4fde28Sjason 	struct cs4231_softc *sc = vsc;
13609820c8cbSjason 	struct cs_channel *chan = &sc->sc_playback;
13617f529f01Sjason 	struct cs_dma *p;
13627f529f01Sjason 	u_int32_t csr;
13639820c8cbSjason 	u_long n;
13647f529f01Sjason 
13659820c8cbSjason 	if (chan->cs_locked != 0) {
13667f529f01Sjason 		printf("%s: trigger_output: already running\n",
13677f529f01Sjason 		    sc->sc_dev.dv_xname);
13687f529f01Sjason 		return (EINVAL);
13697f529f01Sjason 	}
13707f529f01Sjason 
13719820c8cbSjason 	chan->cs_locked = 1;
13729820c8cbSjason 	chan->cs_intr = intr;
13739820c8cbSjason 	chan->cs_arg = arg;
13747f529f01Sjason 
13757f529f01Sjason 	for (p = sc->sc_dmas; p->addr != start; p = p->next)
13767f529f01Sjason 		/*EMPTY*/;
13777f529f01Sjason 	if (p == NULL) {
1378eb79e960Shenric 		printf("%s: trigger_output: bad addr: %p\n",
13797f529f01Sjason 		    sc->sc_dev.dv_xname, start);
13807f529f01Sjason 		return (EINVAL);
13817f529f01Sjason 	}
13827f529f01Sjason 
13837f529f01Sjason 	n = (char *)end - (char *)start;
13847f529f01Sjason 
13857f529f01Sjason 	/*
13867f529f01Sjason 	 * Do only `blksize' at a time, so audio_pint() is kept
13877f529f01Sjason 	 * synchronous with us...
13887f529f01Sjason 	 */
13899820c8cbSjason 	chan->cs_blksz = blksize;
13909820c8cbSjason 	chan->cs_curdma = p;
13919820c8cbSjason 	chan->cs_segsz = n;
13927f529f01Sjason 
13939820c8cbSjason 	if (n > chan->cs_blksz)
13949820c8cbSjason 		n = chan->cs_blksz;
13957f529f01Sjason 
13969820c8cbSjason 	chan->cs_cnt = n;
13977f529f01Sjason 
1398886882aaSratchov 	mtx_enter(&audio_lock);
13997f529f01Sjason 	csr = APC_READ(sc, APC_CSR);
14007f529f01Sjason 
14017f529f01Sjason 	APC_WRITE(sc, APC_PNVA, (u_long)p->dmamap->dm_segs[0].ds_addr);
14027f529f01Sjason 	APC_WRITE(sc, APC_PNC, (u_long)n);
14037f529f01Sjason 
14047f529f01Sjason 	if ((csr & APC_CSR_PDMA_GO) == 0 || (csr & APC_CSR_PPAUSE) != 0) {
14057f529f01Sjason 		APC_WRITE(sc, APC_CSR,
14067f529f01Sjason 		    APC_READ(sc, APC_CSR) & ~(APC_CSR_PIE | APC_CSR_PPAUSE));
14077f529f01Sjason 		APC_WRITE(sc, APC_CSR, APC_READ(sc, APC_CSR) |
14087f529f01Sjason 		    APC_CSR_EI | APC_CSR_GIE | APC_CSR_PIE | APC_CSR_EIE |
14097f529f01Sjason 		    APC_CSR_PMIE | APC_CSR_PDMA_GO);
14107e0aa3e6Sjason 		cs4231_write(sc, SP_LOWER_BASE_COUNT, 0xff);
14117e0aa3e6Sjason 		cs4231_write(sc, SP_UPPER_BASE_COUNT, 0xff);
14127e0aa3e6Sjason 		cs4231_write(sc, SP_INTERFACE_CONFIG,
1413a1f7da73Sjason 		    cs4231_read(sc, SP_INTERFACE_CONFIG) | PLAYBACK_ENABLE);
14147f529f01Sjason 	}
1415886882aaSratchov 	mtx_leave(&audio_lock);
14167f529f01Sjason 	return (0);
14177f529f01Sjason }
14187f529f01Sjason 
14197f529f01Sjason int
cs4231_trigger_input(void * vsc,void * start,void * end,int blksize,void (* intr)(void *),void * arg,struct audio_params * param)14206e4fde28Sjason cs4231_trigger_input(void *vsc, void *start, void *end, int blksize,
14216e4fde28Sjason     void (*intr)(void *), void *arg, struct audio_params *param)
14227f529f01Sjason {
14236e4fde28Sjason 	struct cs4231_softc *sc = vsc;
14249820c8cbSjason 	struct cs_channel *chan = &sc->sc_capture;
14259820c8cbSjason 	struct cs_dma *p;
14269820c8cbSjason 	u_int32_t csr;
14279820c8cbSjason 	u_long n;
14289820c8cbSjason 
14299820c8cbSjason 	if (chan->cs_locked != 0) {
14309820c8cbSjason 		printf("%s: trigger_input: already running\n",
14319820c8cbSjason 		    sc->sc_dev.dv_xname);
14329820c8cbSjason 		return (EINVAL);
14339820c8cbSjason 	}
14349820c8cbSjason 	chan->cs_locked = 1;
14359820c8cbSjason 	chan->cs_intr = intr;
14369820c8cbSjason 	chan->cs_arg = arg;
14379820c8cbSjason 
14389820c8cbSjason 	for (p = sc->sc_dmas; p->addr != start; p = p->next)
14399820c8cbSjason 		/*EMPTY*/;
14409820c8cbSjason 	if (p == NULL) {
1441eb79e960Shenric 		printf("%s: trigger_input: bad addr: %p\n",
14429820c8cbSjason 		    sc->sc_dev.dv_xname, start);
14439820c8cbSjason 		return (EINVAL);
14449820c8cbSjason 	}
14459820c8cbSjason 
14469820c8cbSjason 	n = (char *)end - (char *)start;
14479820c8cbSjason 
14489820c8cbSjason 	/*
14499820c8cbSjason 	 * Do only `blksize' at a time, so audio_cint() is kept
14509820c8cbSjason 	 * synchronous with us...
14519820c8cbSjason 	 */
14529820c8cbSjason 	chan->cs_blksz = blksize;
14539820c8cbSjason 	chan->cs_curdma = p;
14549820c8cbSjason 	chan->cs_segsz = n;
14559820c8cbSjason 
14569820c8cbSjason 	if (n > chan->cs_blksz)
14579820c8cbSjason 		n = chan->cs_blksz;
14589820c8cbSjason 	chan->cs_cnt = n;
14599820c8cbSjason 
1460886882aaSratchov 	mtx_enter(&audio_lock);
14619820c8cbSjason 	APC_WRITE(sc, APC_CNVA, p->dmamap->dm_segs[0].ds_addr);
14629820c8cbSjason 	APC_WRITE(sc, APC_CNC, (u_long)n);
14639820c8cbSjason 
14649820c8cbSjason 	csr = APC_READ(sc, APC_CSR);
14659820c8cbSjason 	if ((csr & APC_CSR_CDMA_GO) == 0 || (csr & APC_CSR_CPAUSE) != 0) {
14669820c8cbSjason 		csr &= APC_CSR_CPAUSE;
14679820c8cbSjason 		csr |= APC_CSR_GIE | APC_CSR_CMIE | APC_CSR_CIE | APC_CSR_EI |
14689820c8cbSjason 		    APC_CSR_CDMA_GO;
14699820c8cbSjason 		APC_WRITE(sc, APC_CSR, csr);
14709820c8cbSjason 		cs4231_write(sc, CS_LOWER_REC_CNT, 0xff);
14719820c8cbSjason 		cs4231_write(sc, CS_UPPER_REC_CNT, 0xff);
14729820c8cbSjason 		cs4231_write(sc, SP_INTERFACE_CONFIG,
14739820c8cbSjason 		    cs4231_read(sc, SP_INTERFACE_CONFIG) | CAPTURE_ENABLE);
14749820c8cbSjason 	}
14759820c8cbSjason 
14769820c8cbSjason 	if (APC_READ(sc, APC_CSR) & APC_CSR_CD) {
14779820c8cbSjason 		u_long nextaddr, togo;
14789820c8cbSjason 
14799820c8cbSjason 		p = chan->cs_curdma;
14809820c8cbSjason 		togo = chan->cs_segsz - chan->cs_cnt;
14819820c8cbSjason 		if (togo == 0) {
14829820c8cbSjason 			nextaddr = (u_int32_t)p->dmamap->dm_segs[0].ds_addr;
14839820c8cbSjason 			chan->cs_cnt = togo = chan->cs_blksz;
14849820c8cbSjason 		} else {
14859820c8cbSjason 			nextaddr = APC_READ(sc, APC_CNVA) + chan->cs_blksz;
14869820c8cbSjason 			if (togo > chan->cs_blksz)
14879820c8cbSjason 				togo = chan->cs_blksz;
14889820c8cbSjason 			chan->cs_cnt += togo;
14899820c8cbSjason 		}
14909820c8cbSjason 
14919820c8cbSjason 		APC_WRITE(sc, APC_CNVA, nextaddr);
14929820c8cbSjason 		APC_WRITE(sc, APC_CNC, togo);
14939820c8cbSjason 	}
14949820c8cbSjason 
1495886882aaSratchov 	mtx_leave(&audio_lock);
14969820c8cbSjason 	return (0);
14977f529f01Sjason }
1498