xref: /openbsd-src/sys/dev/ic/arcofi.c (revision 7eff0e4e0f4f6052a2649fb49f86fac46f57cd50)
1*7eff0e4eSkn /*	$OpenBSD: arcofi.c,v 1.21 2022/10/28 15:09:45 kn Exp $	*/
25538dba8Smiod 
35538dba8Smiod /*
45538dba8Smiod  * Copyright (c) 2011 Miodrag Vallat.
55538dba8Smiod  *
65538dba8Smiod  * Permission to use, copy, modify, and distribute this software for any
75538dba8Smiod  * purpose with or without fee is hereby granted, provided that the above
85538dba8Smiod  * copyright notice and this permission notice appear in all copies.
95538dba8Smiod  *
105538dba8Smiod  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
115538dba8Smiod  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
125538dba8Smiod  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
135538dba8Smiod  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
145538dba8Smiod  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
155538dba8Smiod  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
165538dba8Smiod  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
175538dba8Smiod  */
185538dba8Smiod 
195538dba8Smiod /*
205538dba8Smiod  * Driver for the HP ``Audio1'' device, which is a FIFO layer around a
215538dba8Smiod  * Siemens PSB 2160 ``ARCOFI'' phone quality audio chip.
225538dba8Smiod  *
235538dba8Smiod  * It is known to exist in two flavours: on-board the HP9000/425e as a DIO
245538dba8Smiod  * device, an on-board the HP9000/{705,710,745,747} as a GIO device.
255538dba8Smiod  *
265538dba8Smiod  * The FIFO logic buffers up to 128 bytes. When using 8 bit samples and
275538dba8Smiod  * the logic set to interrupt every half FIFO, the device will interrupt
285538dba8Smiod  * 125 times per second.
295538dba8Smiod  */
305538dba8Smiod 
315538dba8Smiod #include <sys/param.h>
325538dba8Smiod #include <sys/systm.h>
335538dba8Smiod #include <sys/conf.h>
345538dba8Smiod #include <sys/device.h>
355538dba8Smiod #include <sys/kernel.h>
365538dba8Smiod #include <sys/proc.h>
379b18ffb8Sguenther #include <sys/endian.h>
38*7eff0e4eSkn #include <sys/fcntl.h>
395538dba8Smiod 
405538dba8Smiod #include <sys/audioio.h>
415538dba8Smiod #include <dev/audio_if.h>
425538dba8Smiod 
435538dba8Smiod #include <machine/autoconf.h>
445538dba8Smiod #include <machine/bus.h>
455538dba8Smiod #include <machine/intr.h>
465538dba8Smiod 
475538dba8Smiod #include <dev/ic/arcofivar.h>
485538dba8Smiod 
495538dba8Smiod #if 0
505538dba8Smiod #define	ARCOFI_DEBUG
515538dba8Smiod #endif
525538dba8Smiod 
535538dba8Smiod /*
545538dba8Smiod  * Siemens PSB2160 registers
555538dba8Smiod  */
565538dba8Smiod 
575538dba8Smiod /* CMDR */
58e5128cdcSmiod #define	CMDR_AD		0x80	/* SP1/SP2 address convention */
595538dba8Smiod #define	CMDR_READ	0x40
605538dba8Smiod #define	CMDR_WRITE	0x00
615538dba8Smiod #define	CMDR_PU		0x20	/* Power Up */
625538dba8Smiod #define	CMDR_RCS	0x10	/* Receive and transmit in CH B2 */
635538dba8Smiod #define	CMDR_MASK	0x0f
645538dba8Smiod 
655538dba8Smiod 	/* command	     length	data */
665538dba8Smiod #define	SOP_0	0x00	/*	5	CR4 CR3 CR2 CR1 */
675538dba8Smiod #define	COP_1	0x01	/*	5	t1_hi t1_lo f1_hi f1_lo */
685538dba8Smiod #define	COP_2	0x02	/*	3	gr1 gr2 */
695538dba8Smiod #define	COP_3	0x03	/*	3	t2_hi t2_lo f2_hi f2_lo */
705538dba8Smiod #define	SOP_4	0x04	/*	2	CR1 */
715538dba8Smiod #define	SOP_5	0x05	/*	2	CR2 */
725538dba8Smiod #define	SOP_6	0x06	/*	2	CR3 */
735538dba8Smiod #define	SOP_7	0x07	/*	2	CR4 */
745538dba8Smiod #define	COP_8	0x08	/*	3	dtmf_hi dtmf_lo */
755538dba8Smiod #define	COP_9	0x09	/*	5	gz a3 a2 a1 */
765538dba8Smiod #define	COP_A	0x0a	/*	9	fx1 to fx8 */
775538dba8Smiod #define	COP_B	0x0b	/*	3	gx1 gx2 */
785538dba8Smiod #define	COP_C	0x0c	/*	9	fr1 to fr 8 */
795538dba8Smiod #define	COP_D	0x0d	/*	5	fr9 fr10 fx9 fx10 */
805538dba8Smiod #define	COP_E	0x0e	/*	5	t3_hi t3_lo f3_hi f3_lo */
815538dba8Smiod 
825538dba8Smiod /* CR1 */
835538dba8Smiod #define	CR1_GR		0x80	/* GR gain loaded from CRAM vs 0dB */
845538dba8Smiod #define	CR1_GZ		0x40	/* Z gain loaded from CRAM vs -18dB */
855538dba8Smiod #define	CR1_FX		0x20	/* X filter loaded from CRAM vs 0dB flat */
865538dba8Smiod #define	CR1_FR		0x10	/* R filter loaded from CRAM vs 0dB flat */
875538dba8Smiod #define	CR1_GX		0x08	/* GX gain loaded from CRAM vs 0dB */
885538dba8Smiod #define	CR1_T_MASK	0x07	/* test mode */
895538dba8Smiod #define	CR1_DLP		0x07	/* digital loopback via PCM registers */
905538dba8Smiod #define	CR1_DLM		0x06	/* D/A output looped back to A/D input */
915538dba8Smiod #define	CR1_DLS		0x05	/* digital loopback via converter registers */
925538dba8Smiod #define	CR1_IDR		0x04	/* data RAM initialization */
935538dba8Smiod #define	CR1_BYP		0x03	/* bypass analog frontend */
945538dba8Smiod #define	CR1_ALM		0x02	/* analog loopback via MUX */
955538dba8Smiod #define	CR1_ALS		0x01	/* analog loopback via converter registers */
965538dba8Smiod 
975538dba8Smiod /* CR2 */
985538dba8Smiod #define	CR2_SD		0x80	/* SD pin set to input vs output */
995538dba8Smiod #define	CR2_SC		0x40	/* SC pin set to input vs output */
1005538dba8Smiod #define	CR2_SB		0x20	/* SB pin set to input vs output */
1015538dba8Smiod #define	CR2_SA		0x10	/* SA pin set to input vs output */
1025538dba8Smiod #define	CR2_ELS		0x08	/* non-input S pins tristate SIP vs sending 0 */
1035538dba8Smiod #define	CR2_AM		0x04	/* only one device on the SLD bus */
1045538dba8Smiod #define	CR2_TR		0x02	/* three party conferencing */
1055538dba8Smiod #define	CR2_EFC		0x01	/* enable feature control */
1065538dba8Smiod 
1075538dba8Smiod /* CR3 */
1085538dba8Smiod #define	CR3_MIC_G_MASK	0xe0		/* MIC input analog gain  */
1095538dba8Smiod #define	CR3_MIC_X_INPUT		0xe0	/* MIC disabled, X input 15.1 dB */
1105538dba8Smiod #define	CR3_MIC_G_17		0xc0	/* 17 dB */
1115538dba8Smiod #define	CR3_MIC_G_22		0xa0	/* 22 dB */
1125538dba8Smiod #define	CR3_MIC_G_28		0x80	/* 28 dB */
1135538dba8Smiod #define	CR3_MIC_G_34		0x60	/* 34 dB */
1145538dba8Smiod #define	CR3_MIC_G_40		0x40	/* 40 dB */
1155538dba8Smiod #define	CR3_MIC_G_46		0x20	/* 46 dB */
1165538dba8Smiod #define	CR3_MIC_G_52		0x00	/* 52 dB (reset default) */
1175538dba8Smiod #define	CR3_AFEC_MASK	0x1c
1185538dba8Smiod #define	CR3_AFEC_MUTE		0x18	/* mute: Hout */
1195538dba8Smiod #define	CR3_AFEC_HFS		0x14	/* hands free: FHM, LS out */
1205538dba8Smiod #define	CR3_AFEC_LH3		0x10	/* loud hearing 3: MIC, H out, LS out */
1215538dba8Smiod #define	CR3_AFEC_LH2		0x0c	/* loud hearing 2: MIC, LS out */
1225538dba8Smiod #define	CR3_AFEC_LH1		0x08	/* loud hearing 1: LS out */
1235538dba8Smiod #define	CR3_AFEC_RDY		0x04	/* ready: MIC, H out */
1245538dba8Smiod #define	CR3_AFEC_POR		0x00	/* power on reset: all off */
1255538dba8Smiod #define	CR3_OPMODE_MASK	0x03
1265538dba8Smiod #define	CR3_OPMODE_LINEAR	0x02	/* linear (16 bit) */
1275538dba8Smiod #define	CR3_OPMODE_MIXED	0x01	/* mixed */
1285538dba8Smiod #define	CR3_OPMODE_NORMAL	0x00	/* normal (A/u-Law) */
1295538dba8Smiod 
1305538dba8Smiod /* CR4 */
1315538dba8Smiod #define	CR4_DHF		0x80	/* TX digital high frequency enable */
1325538dba8Smiod #define	CR4_DTMF	0x40	/* DTMF generator enable */
1335538dba8Smiod #define	CR4_TG		0x20	/* tone ring enable */
1345538dba8Smiod #define	CR4_BT		0x10	/* beat tone generator enable */
1355538dba8Smiod #define	CR4_TM		0x08	/* incoming voice enable */
1365538dba8Smiod #define	CR4_BM		0x04	/* beat mode (3 tone vs 2 tone) */
1375538dba8Smiod #define	CR4_PM		0x02	/* tone sent to piezo vs loudspeaker */
1385538dba8Smiod #define	CR4_ULAW	0x01	/* u-Law vs A-Law */
1395538dba8Smiod 
1405538dba8Smiod 
1415538dba8Smiod /*
1425538dba8Smiod  * Glue logic registers
1435538dba8Smiod  * Note the register values here are symbolic, as actual addresses
1445538dba8Smiod  * depend upon the particular bus the device is connected to.
1455538dba8Smiod  */
1465538dba8Smiod 
1475538dba8Smiod #define	ARCOFI_ID		0	/* id (r) and reset (w) register */
1485538dba8Smiod 
1495538dba8Smiod #define	ARCOFI_CSR		1	/* status and control register */
1505538dba8Smiod #define	CSR_INTR_ENABLE			0x80
1515538dba8Smiod #define	CSR_INTR_REQUEST		0x40	/* unacknowledged interrupt */
1525538dba8Smiod /* 0x20 and 0x10 used in DIO flavours, to provide IPL */
1535538dba8Smiod #define	CSR_WIDTH_16			0x08	/* 16-bit samples */
1545538dba8Smiod #define	CSR_CTRL_FIFO_ENABLE		0x04	/* connect FIFO to CMDR */
1555538dba8Smiod #define	CSR_DATA_FIFO_ENABLE		0x01	/* connect FIFO to DU/DD */
1565538dba8Smiod 
1575538dba8Smiod #define	ARCOFI_FIFO_IR		2	/* FIFO interrupt register */
1585538dba8Smiod #define	FIFO_IR_ENABLE(ev)		((ev) << 4)
1595538dba8Smiod #define	FIFO_IR_EVENT(ev)		(ev)
1605538dba8Smiod #define	FIFO_IR_OUT_EMPTY		0x08
1615538dba8Smiod #define	FIFO_IR_CTRL_EMPTY		0x04
1625538dba8Smiod #define	FIFO_IR_OUT_HALF_EMPTY		0x02
1635538dba8Smiod #define	FIFO_IR_IN_HALF_EMPTY		0x01
1645538dba8Smiod 
1655538dba8Smiod #define	ARCOFI_FIFO_SR		3	/* FIFO status register (ro) */
1665538dba8Smiod #define	FIFO_SR_CTRL_FULL		0x20
1675538dba8Smiod #define	FIFO_SR_CTRL_EMPTY		0x10
1685538dba8Smiod #define	FIFO_SR_OUT_FULL		0x08
1695538dba8Smiod #define	FIFO_SR_OUT_EMPTY		0x04
1705538dba8Smiod #define	FIFO_SR_IN_FULL			0x02
1715538dba8Smiod #define	FIFO_SR_IN_EMPTY		0x01
1725538dba8Smiod 
1735538dba8Smiod #define	ARCOFI_FIFO_DATA	4	/* data FIFO port */
1745538dba8Smiod 
1755538dba8Smiod #define	ARCOFI_FIFO_CTRL	5	/* control FIFO port (wo) */
1765538dba8Smiod 
1775538dba8Smiod #define	ARCOFI_FIFO_SIZE	128
1785538dba8Smiod 
1795538dba8Smiod 
1805538dba8Smiod struct cfdriver arcofi_cd = {
1815538dba8Smiod 	NULL, "arcofi", DV_DULL
1825538dba8Smiod };
1835538dba8Smiod 
1845538dba8Smiod #define	arcofi_read(sc, r) \
1855538dba8Smiod 	bus_space_read_1((sc)->sc_iot, (sc)->sc_ioh, (sc)->sc_reg[r])
1865538dba8Smiod #define	arcofi_write(sc, r, v) \
1875538dba8Smiod 	bus_space_write_1((sc)->sc_iot, (sc)->sc_ioh, (sc)->sc_reg[r], v)
1885538dba8Smiod 
1895538dba8Smiod int	arcofi_cmd(struct arcofi_softc *, uint8_t, const uint8_t *);
1901129e472Smiod int	arcofi_cr3_to_portmask(uint, int);
1915538dba8Smiod int	arcofi_gain_to_mi(uint);
1925538dba8Smiod uint	arcofi_mi_to_gain(int);
1935538dba8Smiod uint	arcofi_portmask_to_cr3(int);
1945538dba8Smiod int	arcofi_set_param(struct arcofi_softc *, int, int, int,
1955538dba8Smiod 	    struct audio_params *);
1965538dba8Smiod 
1975538dba8Smiod void	arcofi_close(void *);
1985538dba8Smiod int	arcofi_commit_settings(void *);
1995538dba8Smiod int	arcofi_get_port(void *, mixer_ctrl_t *);
2005538dba8Smiod int	arcofi_halt_input(void *);
2015538dba8Smiod int	arcofi_halt_output(void *);
2025538dba8Smiod int	arcofi_open(void *, int);
2035538dba8Smiod int	arcofi_query_devinfo(void *, mixer_devinfo_t *);
2045538dba8Smiod int	arcofi_round_blocksize(void *, int);
2055538dba8Smiod int	arcofi_set_params(void *, int, int, struct audio_params *,
2065538dba8Smiod 	    struct audio_params *);
2075538dba8Smiod int	arcofi_set_port(void *, mixer_ctrl_t *);
2085538dba8Smiod int	arcofi_start_input(void *, void *, int, void (*)(void *), void *);
2095538dba8Smiod int	arcofi_start_output(void *, void *, int, void (*)(void *), void *);
2105538dba8Smiod 
2110d6a2fdeSmiod const struct audio_hw_if arcofi_hw_if = {
2125538dba8Smiod 	.open = arcofi_open,
2135538dba8Smiod 	.close = arcofi_close,
2145538dba8Smiod 	.set_params = arcofi_set_params,
2155538dba8Smiod 	.round_blocksize = arcofi_round_blocksize,
2165538dba8Smiod 	.commit_settings = arcofi_commit_settings,
2175538dba8Smiod 	.start_output = arcofi_start_output,
2185538dba8Smiod 	.start_input = arcofi_start_input,
2195538dba8Smiod 	.halt_output = arcofi_halt_output,
2205538dba8Smiod 	.halt_input = arcofi_halt_input,
2215538dba8Smiod 	.set_port = arcofi_set_port,
2225538dba8Smiod 	.get_port = arcofi_get_port,
2235538dba8Smiod 	.query_devinfo = arcofi_query_devinfo,
2245538dba8Smiod };
2255538dba8Smiod 
2265538dba8Smiod /* mixer items */
2275538dba8Smiod #define	ARCOFI_PORT_AUDIO_IN_VOLUME	0	/* line in volume (GR) */
2285538dba8Smiod #define	ARCOFI_PORT_AUDIO_OUT_VOLUME	1	/* line out volume (GX) */
2295538dba8Smiod #define	ARCOFI_PORT_AUDIO_SPKR_VOLUME	2	/* speaker volume (GX) */
2305538dba8Smiod #define	ARCOFI_PORT_AUDIO_IN_MUTE	3	/* line in mute (MIC) */
2315538dba8Smiod #define	ARCOFI_PORT_AUDIO_OUT_MUTE	4	/* line out mute (H out) */
2325538dba8Smiod #define	ARCOFI_PORT_AUDIO_SPKR_MUTE	5	/* line in mute (LS out) */
2335538dba8Smiod /* mixer classes */
2345538dba8Smiod #define	ARCOFI_CLASS_INPUT		6
2355538dba8Smiod #define	ARCOFI_CLASS_OUTPUT		7
2365538dba8Smiod 
2375538dba8Smiod /*
2385538dba8Smiod  * Gain programming formulae are a complete mystery to me, and of course
2395538dba8Smiod  * no two chips are compatible - not even the PSB 2163 and PSB 2165
2405538dba8Smiod  * later ARCOFI chips, from the same manufacturer as the PSB 2160!
2415538dba8Smiod  *
2425538dba8Smiod  * Of course, the PSB 2160 datasheet does not give any set of values.
243184d26b4Smiod  * The following table is taken from the HP-UX audio driver (audio_shared.o
244184d26b4Smiod  * private_audio_gain_tab).
2455538dba8Smiod  */
2465538dba8Smiod 
247184d26b4Smiod #define	NEGATIVE_GAINS	60
248184d26b4Smiod #define	POSITIVE_GAINS	14
2495538dba8Smiod static const uint16_t arcofi_gains[1 + NEGATIVE_GAINS + 1 + POSITIVE_GAINS] = {
2505538dba8Smiod 	/* minus infinity */
251184d26b4Smiod 	0x0988,
252184d26b4Smiod 
253184d26b4Smiod 	0xf8b8, 0xf8b8, 0xf8b8, 0xf8b8, 0x099f, 0x099f, 0x099f, 0x099f,
254184d26b4Smiod 	0x09af, 0x09af, 0x09af, 0x09cf, 0x09cf, 0x09cf, 0xf8a9, 0xf83a,
255184d26b4Smiod 	0xf83a, 0xf82b, 0xf82d, 0xf8a3, 0xf8b2, 0xf8a1, 0xe8aa, 0xe84b,
256184d26b4Smiod 	0xe89e, 0xe8d3, 0xe891, 0xe8b1, 0xd8aa, 0xd8cb, 0xd8a6, 0xd8b3,
257184d26b4Smiod 	0xd842, 0xd8b1, 0xc8aa, 0xc8bb, 0xc888, 0xc853, 0xc852, 0xc8b1,
258184d26b4Smiod 	0xb8aa, 0xb8ab, 0xb896, 0xb892, 0xb842, 0xb8b1, 0xa8aa, 0xa8bb,
259184d26b4Smiod 	0x199f, 0x195b, 0x29c1, 0x2923, 0x29aa, 0x392b, 0xf998, 0xb988,
260184d26b4Smiod 	0x1aac, 0x3aa1, 0xbaa1, 0xbb88,
261184d26b4Smiod 
2625538dba8Smiod 	/* 0 */
263184d26b4Smiod 	0x8888,
264184d26b4Smiod 
265184d26b4Smiod 	0xd388, 0x5288, 0xb1a1, 0x31a1, 0x1192, 0x11d0, 0x30c0, 0x2050,
266184d26b4Smiod 	0x1021, 0x1020, 0x1000, 0x0001, 0x0010, 0x0000
2675538dba8Smiod };
2685538dba8Smiod 
2695538dba8Smiod int
arcofi_open(void * v,int flags)2705538dba8Smiod arcofi_open(void *v, int flags)
2715538dba8Smiod {
2725538dba8Smiod 	struct arcofi_softc *sc = (struct arcofi_softc *)v;
2735538dba8Smiod 
274*7eff0e4eSkn 	if ((flags & (FWRITE | FREAD)) == (FWRITE | FREAD))
275*7eff0e4eSkn 		return ENXIO;
2765538dba8Smiod 	if (sc->sc_open)
2775538dba8Smiod 		return EBUSY;
2785538dba8Smiod 	sc->sc_open = 1;
2795538dba8Smiod 	KASSERT(sc->sc_mode == 0);
2805538dba8Smiod 
2815538dba8Smiod 	return 0;
2825538dba8Smiod }
2835538dba8Smiod 
2845538dba8Smiod void
arcofi_close(void * v)2855538dba8Smiod arcofi_close(void *v)
2865538dba8Smiod {
2875538dba8Smiod 	struct arcofi_softc *sc = (struct arcofi_softc *)v;
2885538dba8Smiod 
2895538dba8Smiod 	arcofi_halt_input(v);
2905538dba8Smiod 	arcofi_halt_output(v);
2915538dba8Smiod 	sc->sc_open = 0;
2925538dba8Smiod }
2935538dba8Smiod 
2945538dba8Smiod /*
2955538dba8Smiod  * Compute proper sample and hardware settings. Invoked both for record
2965538dba8Smiod  * and playback, as we don't support independent settings.
2975538dba8Smiod  */
2985538dba8Smiod int
arcofi_set_param(struct arcofi_softc * sc,int set,int use,int mode,struct audio_params * ap)2995538dba8Smiod arcofi_set_param(struct arcofi_softc *sc, int set, int use, int mode,
3005538dba8Smiod     struct audio_params *ap)
3015538dba8Smiod {
3025538dba8Smiod 	if ((set & mode) == 0)
3035538dba8Smiod 		return 0;
3045538dba8Smiod 
3055538dba8Smiod #ifdef ARCOFI_DEBUG
306eff9a77cSmiod 	printf("%s: set_param, mode %d encoding %d precision %d\n",
307eff9a77cSmiod 	    sc->sc_dev.dv_xname, mode, ap->encoding, ap->precision);
3085538dba8Smiod #endif
309eff9a77cSmiod 	switch (ap->precision) {
310eff9a77cSmiod 	case 8:
3115538dba8Smiod 		switch (ap->encoding) {
3125538dba8Smiod 		case AUDIO_ENCODING_ULAW:
313eff9a77cSmiod 			sc->sc_shadow.cr4 |= CR4_ULAW;
3145538dba8Smiod 			break;
3155538dba8Smiod 		case AUDIO_ENCODING_ALAW:
3165538dba8Smiod 			sc->sc_shadow.cr4 &= ~CR4_ULAW;
3175538dba8Smiod 			break;
3185538dba8Smiod 		default:
3195538dba8Smiod 			return EINVAL;
3205538dba8Smiod 		}
321eff9a77cSmiod 		sc->sc_shadow.cr3 = (sc->sc_shadow.cr3 & ~CR3_OPMODE_MASK) |
322eff9a77cSmiod 		    CR3_OPMODE_NORMAL;
323eff9a77cSmiod 		break;
324eff9a77cSmiod 	case 16:
325eff9a77cSmiod 		switch (ap->encoding) {
326eff9a77cSmiod 		case AUDIO_ENCODING_SLINEAR_BE:
327eff9a77cSmiod 			break;
328eff9a77cSmiod 		default:
329eff9a77cSmiod 			return EINVAL;
330eff9a77cSmiod 		}
331eff9a77cSmiod 		sc->sc_shadow.cr3 = (sc->sc_shadow.cr3 & ~CR3_OPMODE_MASK) |
332eff9a77cSmiod 		    CR3_OPMODE_LINEAR;
333eff9a77cSmiod 		break;
334eff9a77cSmiod 	default:
335eff9a77cSmiod 		return EINVAL;
336eff9a77cSmiod 	}
3375538dba8Smiod 
338eff9a77cSmiod 	ap->bps = AUDIO_BPS(ap->precision);
3395538dba8Smiod 	ap->msb = 1;
3405538dba8Smiod 	ap->channels = 1;
3415538dba8Smiod 	ap->sample_rate = 8000;
3425538dba8Smiod 
3435538dba8Smiod 	return 0;
3445538dba8Smiod }
3455538dba8Smiod 
3465538dba8Smiod int
arcofi_set_params(void * v,int set,int use,struct audio_params * p,struct audio_params * r)3475538dba8Smiod arcofi_set_params(void *v, int set, int use, struct audio_params *p,
3485538dba8Smiod     struct audio_params *r)
3495538dba8Smiod {
3505538dba8Smiod 	struct arcofi_softc *sc = (struct arcofi_softc *)v;
3515538dba8Smiod 	int rc;
3525538dba8Smiod 
3535538dba8Smiod 	if (r != NULL) {
3545538dba8Smiod 		rc = arcofi_set_param(sc, set, use, AUMODE_RECORD, r);
3555538dba8Smiod 		if (rc != 0)
3565538dba8Smiod 			return rc;
3575538dba8Smiod 	}
3585538dba8Smiod 
3595538dba8Smiod 	if (p != NULL) {
3605538dba8Smiod 		rc = arcofi_set_param(sc, set, use, AUMODE_PLAY, p);
3615538dba8Smiod 		if (rc != 0)
3625538dba8Smiod 			return rc;
3635538dba8Smiod 	}
3645538dba8Smiod 
3655538dba8Smiod 	return 0;
3665538dba8Smiod }
3675538dba8Smiod 
3685538dba8Smiod int
arcofi_round_blocksize(void * v,int blksz)3695538dba8Smiod arcofi_round_blocksize(void *v, int blksz)
3705538dba8Smiod {
3715538dba8Smiod 	/*
3725538dba8Smiod 	 * Round the size up to a multiple of half the FIFO, to favour
3735538dba8Smiod 	 * smooth interrupt operation.
3745538dba8Smiod 	 */
3755538dba8Smiod 	return roundup(blksz, ARCOFI_FIFO_SIZE / 2);
3765538dba8Smiod }
3775538dba8Smiod 
3785538dba8Smiod int
arcofi_commit_settings(void * v)3795538dba8Smiod arcofi_commit_settings(void *v)
3805538dba8Smiod {
3815538dba8Smiod 	struct arcofi_softc *sc = (struct arcofi_softc *)v;
3825538dba8Smiod 	int rc;
383eff9a77cSmiod 	uint8_t cmd[2], csr, ocsr;
3845538dba8Smiod 
3855538dba8Smiod #ifdef ARCOFI_DEBUG
3861129e472Smiod 	printf("%s: commit_settings, gr %04x gx %04x cr3 %02x cr4 %02x mute %d\n",
3875538dba8Smiod 	    sc->sc_dev.dv_xname,
3881129e472Smiod 	    arcofi_gains[sc->sc_shadow.gr_idx],
3891129e472Smiod 	    arcofi_gains[sc->sc_shadow.gx_idx],
3901129e472Smiod 	    sc->sc_shadow.cr3, sc->sc_shadow.cr4, sc->sc_shadow.output_mute);
3915538dba8Smiod #endif
3925538dba8Smiod 
3935538dba8Smiod 	if (bcmp(&sc->sc_active, &sc->sc_shadow, sizeof(sc->sc_active)) == 0)
3945538dba8Smiod 		return 0;
3955538dba8Smiod 
396886882aaSratchov 	mtx_enter(&audio_lock);
3975538dba8Smiod 
3985538dba8Smiod 	if (sc->sc_active.gr_idx != sc->sc_shadow.gr_idx) {
3995538dba8Smiod 		cmd[0] = arcofi_gains[sc->sc_shadow.gr_idx] >> 8;
4005538dba8Smiod 		cmd[1] = arcofi_gains[sc->sc_shadow.gr_idx];
4015538dba8Smiod 		if ((rc = arcofi_cmd(sc, COP_2, cmd)) != 0)
4025538dba8Smiod 			goto error;
4035538dba8Smiod 		sc->sc_active.gr_idx = sc->sc_shadow.gr_idx;
4045538dba8Smiod 	}
4055538dba8Smiod 
4061129e472Smiod 	if (sc->sc_active.gx_idx != sc->sc_shadow.gx_idx ||
4071129e472Smiod 	    sc->sc_active.output_mute != sc->sc_shadow.output_mute) {
4081129e472Smiod 		if (sc->sc_shadow.output_mute) {
4091129e472Smiod 			cmd[0] = arcofi_gains[0] >> 8;
4101129e472Smiod 			cmd[1] = arcofi_gains[0];
4111129e472Smiod 		} else {
4125538dba8Smiod 			cmd[0] = arcofi_gains[sc->sc_shadow.gx_idx] >> 8;
4135538dba8Smiod 			cmd[1] = arcofi_gains[sc->sc_shadow.gx_idx];
4141129e472Smiod 		}
4155538dba8Smiod 		if ((rc = arcofi_cmd(sc, COP_B, cmd)) != 0)
4165538dba8Smiod 			goto error;
4175538dba8Smiod 		sc->sc_active.gx_idx = sc->sc_shadow.gx_idx;
4181129e472Smiod 		sc->sc_active.output_mute = sc->sc_shadow.output_mute;
4195538dba8Smiod 	}
4205538dba8Smiod 
4215538dba8Smiod 	if (sc->sc_active.cr3 != sc->sc_shadow.cr3) {
4225538dba8Smiod 		cmd[0] = sc->sc_shadow.cr3;
4235538dba8Smiod 		if ((rc = arcofi_cmd(sc, SOP_6, cmd)) != 0)
4245538dba8Smiod 			goto error;
4255538dba8Smiod 		sc->sc_active.cr3 = sc->sc_shadow.cr3;
426eff9a77cSmiod 
427eff9a77cSmiod 		ocsr = arcofi_read(sc, ARCOFI_CSR);
428eff9a77cSmiod 		if ((sc->sc_active.cr3 & CR3_OPMODE_MASK) != CR3_OPMODE_NORMAL)
429eff9a77cSmiod 			csr = ocsr | CSR_WIDTH_16;
430eff9a77cSmiod 		else
431eff9a77cSmiod 			csr = ocsr & ~CSR_WIDTH_16;
432eff9a77cSmiod 		if (csr != ocsr)
433eff9a77cSmiod 			arcofi_write(sc, ARCOFI_CSR, csr);
4345538dba8Smiod 	}
4355538dba8Smiod 
4365538dba8Smiod 	if (sc->sc_active.cr4 != sc->sc_shadow.cr4) {
4375538dba8Smiod 		cmd[0] = sc->sc_shadow.cr4;
4385538dba8Smiod 		if ((rc = arcofi_cmd(sc, SOP_7, cmd)) != 0)
4395538dba8Smiod 			goto error;
4405538dba8Smiod 		sc->sc_active.cr4 = sc->sc_shadow.cr4;
4415538dba8Smiod 	}
4425538dba8Smiod 
443eff9a77cSmiod 	rc = 0;
4445538dba8Smiod error:
445886882aaSratchov 	mtx_leave(&audio_lock);
4465538dba8Smiod 	return rc;
4475538dba8Smiod }
4485538dba8Smiod 
4495538dba8Smiod int
arcofi_start_input(void * v,void * rbuf,int rsz,void (* cb)(void *),void * cbarg)4505538dba8Smiod arcofi_start_input(void *v, void *rbuf, int rsz, void (*cb)(void *),
4515538dba8Smiod     void *cbarg)
4525538dba8Smiod {
4535538dba8Smiod 	struct arcofi_softc *sc = (struct arcofi_softc *)v;
4545538dba8Smiod 
4555538dba8Smiod #ifdef ARCOFI_DEBUG
4565538dba8Smiod 	printf("%s: start_input, mode %d\n",
4575538dba8Smiod 	    sc->sc_dev.dv_xname, sc->sc_mode);
4585538dba8Smiod #endif
4595538dba8Smiod 
4605538dba8Smiod 	/* enable data FIFO if becoming active */
4615538dba8Smiod 	if (sc->sc_mode == 0)
4625538dba8Smiod 		arcofi_write(sc, ARCOFI_CSR,
4635538dba8Smiod 		    arcofi_read(sc, ARCOFI_CSR) | CSR_DATA_FIFO_ENABLE);
4645538dba8Smiod 	sc->sc_mode |= AUMODE_RECORD;
4655538dba8Smiod 
4665538dba8Smiod 	sc->sc_recv.buf = (uint8_t *)rbuf;
4675538dba8Smiod 	sc->sc_recv.past = (uint8_t *)rbuf + rsz;
4685538dba8Smiod 	sc->sc_recv.cb = cb;
4695538dba8Smiod 	sc->sc_recv.cbarg = cbarg;
4705538dba8Smiod 
4715538dba8Smiod 	/* enable input FIFO interrupts */
472eff9a77cSmiod 	arcofi_write(sc, ARCOFI_FIFO_IR, arcofi_read(sc, ARCOFI_FIFO_IR) |
4735538dba8Smiod 	    FIFO_IR_ENABLE(FIFO_IR_IN_HALF_EMPTY));
4745538dba8Smiod 
4755538dba8Smiod 	return 0;
4765538dba8Smiod }
4775538dba8Smiod 
4785538dba8Smiod int
arcofi_start_output(void * v,void * wbuf,int wsz,void (* cb)(void *),void * cbarg)4795538dba8Smiod arcofi_start_output(void *v, void *wbuf, int wsz, void (*cb)(void *),
4805538dba8Smiod     void *cbarg)
4815538dba8Smiod {
4825538dba8Smiod 	struct arcofi_softc *sc = (struct arcofi_softc *)v;
4835538dba8Smiod 
4845538dba8Smiod #ifdef ARCOFI_DEBUG
4855538dba8Smiod 	printf("%s: start_output, mode %d\n",
4865538dba8Smiod 	    sc->sc_dev.dv_xname, sc->sc_mode);
4875538dba8Smiod #endif
4885538dba8Smiod 
4895538dba8Smiod 	/* enable data FIFO if becoming active */
4905538dba8Smiod 	if (sc->sc_mode == 0)
4915538dba8Smiod 		arcofi_write(sc, ARCOFI_CSR,
4925538dba8Smiod 		    arcofi_read(sc, ARCOFI_CSR) | CSR_DATA_FIFO_ENABLE);
4935538dba8Smiod 	sc->sc_mode |= AUMODE_PLAY;
4945538dba8Smiod 
4955538dba8Smiod 	sc->sc_xmit.buf = (uint8_t *)wbuf;
4965538dba8Smiod 	sc->sc_xmit.past = (uint8_t *)wbuf + wsz;
4975538dba8Smiod 	sc->sc_xmit.cb = cb;
4985538dba8Smiod 	sc->sc_xmit.cbarg = cbarg;
4995538dba8Smiod 
5005538dba8Smiod 	/* enable output FIFO interrupts */
501eff9a77cSmiod 	arcofi_write(sc, ARCOFI_FIFO_IR, arcofi_read(sc, ARCOFI_FIFO_IR) |
5025538dba8Smiod 	    FIFO_IR_ENABLE(FIFO_IR_OUT_HALF_EMPTY));
5035538dba8Smiod 
5045538dba8Smiod 	return 0;
5055538dba8Smiod }
5065538dba8Smiod 
5075538dba8Smiod int
arcofi_halt_input(void * v)5085538dba8Smiod arcofi_halt_input(void *v)
5095538dba8Smiod {
5105538dba8Smiod 	struct arcofi_softc *sc = (struct arcofi_softc *)v;
5115538dba8Smiod 
512886882aaSratchov 	mtx_enter(&audio_lock);
5135538dba8Smiod 
5145538dba8Smiod 	/* disable input FIFO interrupts */
515eff9a77cSmiod 	arcofi_write(sc, ARCOFI_FIFO_IR, arcofi_read(sc, ARCOFI_FIFO_IR) &
5165538dba8Smiod 	    ~FIFO_IR_ENABLE(FIFO_IR_IN_HALF_EMPTY));
5175538dba8Smiod 	/* disable data FIFO if becoming idle */
5185538dba8Smiod 	sc->sc_mode &= ~AUMODE_RECORD;
5195538dba8Smiod 	if (sc->sc_mode == 0)
5205538dba8Smiod 		arcofi_write(sc, ARCOFI_CSR,
5215538dba8Smiod 		    arcofi_read(sc, ARCOFI_CSR) & ~CSR_DATA_FIFO_ENABLE);
5225538dba8Smiod 
523886882aaSratchov 	mtx_leave(&audio_lock);
5245538dba8Smiod 	return 0;
5255538dba8Smiod }
5265538dba8Smiod 
5275538dba8Smiod int
arcofi_halt_output(void * v)5285538dba8Smiod arcofi_halt_output(void *v)
5295538dba8Smiod {
5305538dba8Smiod 	struct arcofi_softc *sc = (struct arcofi_softc *)v;
5315538dba8Smiod 
532886882aaSratchov 	mtx_enter(&audio_lock);
5335538dba8Smiod 
5345538dba8Smiod 	/* disable output FIFO interrupts */
535eff9a77cSmiod 	arcofi_write(sc, ARCOFI_FIFO_IR, arcofi_read(sc, ARCOFI_FIFO_IR) &
5365538dba8Smiod 	    ~FIFO_IR_ENABLE(FIFO_IR_OUT_HALF_EMPTY));
5375538dba8Smiod 	/* disable data FIFO if becoming idle */
5385538dba8Smiod 	sc->sc_mode &= ~AUMODE_PLAY;
5395538dba8Smiod 	if (sc->sc_mode == 0)
5405538dba8Smiod 		arcofi_write(sc, ARCOFI_CSR,
5415538dba8Smiod 		    arcofi_read(sc, ARCOFI_CSR) & ~CSR_DATA_FIFO_ENABLE);
5425538dba8Smiod 
543886882aaSratchov 	mtx_leave(&audio_lock);
5445538dba8Smiod 	return 0;
5455538dba8Smiod }
5465538dba8Smiod 
5475538dba8Smiod /*
5485538dba8Smiod  * Convert gain table index to AUDIO_MIN_GAIN..AUDIO_MAX_GAIN scale.
5495538dba8Smiod  */
5505538dba8Smiod int
arcofi_gain_to_mi(uint idx)5515538dba8Smiod arcofi_gain_to_mi(uint idx)
5525538dba8Smiod {
5535538dba8Smiod 	if (idx == 0)
5545538dba8Smiod 		return AUDIO_MIN_GAIN;
5555538dba8Smiod 	if (idx == nitems(arcofi_gains) - 1)
5565538dba8Smiod 		return AUDIO_MAX_GAIN;
5575538dba8Smiod 
5581129e472Smiod 	return ((idx - 1) * (AUDIO_MAX_GAIN - AUDIO_MIN_GAIN)) /
5591129e472Smiod 	    (nitems(arcofi_gains) - 1) + AUDIO_MIN_GAIN + 1;
5605538dba8Smiod }
5615538dba8Smiod 
5625538dba8Smiod /*
563eff9a77cSmiod  * Convert AUDIO_MIN_GAIN..AUDIO_MAX_GAIN scale to gain table index.
5645538dba8Smiod  */
5655538dba8Smiod uint
arcofi_mi_to_gain(int lvl)5665538dba8Smiod arcofi_mi_to_gain(int lvl)
5675538dba8Smiod {
5685538dba8Smiod 	if (lvl <= AUDIO_MIN_GAIN)
5695538dba8Smiod 		return 0;
5705538dba8Smiod 	if (lvl >= AUDIO_MAX_GAIN)
5715538dba8Smiod 		return nitems(arcofi_gains) - 1;
5725538dba8Smiod 
5731129e472Smiod 	return ((lvl - AUDIO_MIN_GAIN - 1) * (nitems(arcofi_gains) - 1)) /
5741129e472Smiod 	    (AUDIO_MAX_GAIN - AUDIO_MIN_GAIN);
5755538dba8Smiod }
5765538dba8Smiod 
5775538dba8Smiod /*
578b5445af2Smiod  * Input and output ports definition (used to be in <audioio.h>)
5795538dba8Smiod  */
58086757eeeSmiod #define	AUDIO_SPEAKER		0x01	/* built-in speaker */
58186757eeeSmiod #define	AUDIO_LINE_IN		0x02	/* line in	 */
58286757eeeSmiod #define	AUDIO_LINE_OUT		0x04	/* line out	 */
5835538dba8Smiod 
5845538dba8Smiod /*
5855538dba8Smiod  * The mapping between the available inputs and outputs, and CR3, is as
5865538dba8Smiod  * follows:
5875538dba8Smiod  * - the `line in' connector is the `MIC' input.
5885538dba8Smiod  * - the `line out' connector is the `H out' (heaphones) output.
5895538dba8Smiod  * - the internal `speaker' is the `LS out' (loudspeaker) output.
5905538dba8Smiod  *
591a349d0bbSmiod  * Each of these can be enabled or disabled independently, except for
5925538dba8Smiod  * MIC enabled with H out and LS out disabled, which is not allowed
5935538dba8Smiod  * by the chip (and makes no sense for a chip which was intended to
5941129e472Smiod  * be used in phones, not voice recorders); we cheat by keeping one
5951129e472Smiod  * output source enabled, but with the output gain forced to minus
5961129e472Smiod  * infinity to mute it.
5975538dba8Smiod  *
5985538dba8Smiod  * The truth table is thus:
5995538dba8Smiod  *
6005538dba8Smiod  *	MIC	LS out	H out	AFEC
6015538dba8Smiod  *	off	off	off	POR
6025538dba8Smiod  *	off	off	on	MUTE
6035538dba8Smiod  *	off	on	off	LH1
6045538dba8Smiod  *	off	on	on	LH3, X input enabled
6051129e472Smiod  *	on	off	off	RDY, GX forced to minus infinity
6065538dba8Smiod  *	on	off	on	RDY
6075538dba8Smiod  *	on	on	off	LH2
6085538dba8Smiod  *	on	on	on	LH3
6095538dba8Smiod  */
6105538dba8Smiod 
6115538dba8Smiod /*
6125538dba8Smiod  * Convert logical port enable settings to a valid CR3 value.
6135538dba8Smiod  */
6145538dba8Smiod uint
arcofi_portmask_to_cr3(int mask)6155538dba8Smiod arcofi_portmask_to_cr3(int mask)
6165538dba8Smiod {
6175538dba8Smiod 	switch (mask) {
6185538dba8Smiod 	default:
6195538dba8Smiod 	case 0:
620184d26b4Smiod 		return CR3_MIC_G_17 | CR3_AFEC_POR;
6215538dba8Smiod 	case AUDIO_LINE_OUT:
622184d26b4Smiod 		return CR3_MIC_G_17 | CR3_AFEC_MUTE;
6235538dba8Smiod 	case AUDIO_SPEAKER:
624184d26b4Smiod 		return CR3_MIC_G_17 | CR3_AFEC_LH1;
6255538dba8Smiod 	case AUDIO_SPEAKER | AUDIO_LINE_OUT:
6265538dba8Smiod 		return CR3_MIC_X_INPUT | CR3_AFEC_LH3;
6271129e472Smiod 	case AUDIO_LINE_IN:
6281129e472Smiod 		/* since we can't do this, just... */
6291129e472Smiod 		/* FALLTHROUGH */
6305538dba8Smiod 	case AUDIO_LINE_IN | AUDIO_LINE_OUT:
631184d26b4Smiod 		return CR3_MIC_G_17 | CR3_AFEC_RDY;
6325538dba8Smiod 	case AUDIO_LINE_IN | AUDIO_SPEAKER:
633184d26b4Smiod 		return CR3_MIC_G_17 | CR3_AFEC_LH2;
6345538dba8Smiod 	case AUDIO_LINE_IN | AUDIO_SPEAKER | AUDIO_LINE_OUT:
635184d26b4Smiod 		return CR3_MIC_G_17 | CR3_AFEC_LH3;
6365538dba8Smiod 	}
6375538dba8Smiod }
6385538dba8Smiod 
6395538dba8Smiod /*
6405538dba8Smiod  * Convert CR3 to an enabled ports mask.
6415538dba8Smiod  */
6425538dba8Smiod int
arcofi_cr3_to_portmask(uint cr3,int output_mute)6431129e472Smiod arcofi_cr3_to_portmask(uint cr3, int output_mute)
6445538dba8Smiod {
6455538dba8Smiod 	switch (cr3 & CR3_AFEC_MASK) {
6465538dba8Smiod 	default:
6475538dba8Smiod 	case CR3_AFEC_POR:
6485538dba8Smiod 		return 0;
6495538dba8Smiod 	case CR3_AFEC_RDY:
6501129e472Smiod 		return output_mute ?
6511129e472Smiod 		    AUDIO_LINE_IN : AUDIO_LINE_IN | AUDIO_LINE_OUT;
6525538dba8Smiod 	case CR3_AFEC_HFS:
6535538dba8Smiod 	case CR3_AFEC_LH1:
6545538dba8Smiod 		return AUDIO_SPEAKER;
6555538dba8Smiod 	case CR3_AFEC_LH2:
6565538dba8Smiod 		return AUDIO_LINE_IN | AUDIO_SPEAKER;
6575538dba8Smiod 	case CR3_AFEC_LH3:
6585538dba8Smiod 		if ((cr3 & CR3_MIC_G_MASK) == CR3_MIC_X_INPUT)
6595538dba8Smiod 			return AUDIO_SPEAKER | AUDIO_LINE_OUT;
6605538dba8Smiod 		else
6615538dba8Smiod 			return AUDIO_LINE_IN | AUDIO_SPEAKER | AUDIO_LINE_OUT;
6625538dba8Smiod 	case CR3_AFEC_MUTE:
6635538dba8Smiod 		return AUDIO_LINE_OUT;
6645538dba8Smiod 	}
6655538dba8Smiod }
6665538dba8Smiod 
6675538dba8Smiod int
arcofi_set_port(void * v,mixer_ctrl_t * mc)6685538dba8Smiod arcofi_set_port(void *v, mixer_ctrl_t *mc)
6695538dba8Smiod {
6705538dba8Smiod 	struct arcofi_softc *sc = (struct arcofi_softc *)v;
6715538dba8Smiod 	int portmask;
6725538dba8Smiod 
6735538dba8Smiod 	/* check for proper type */
6745538dba8Smiod 	switch (mc->dev) {
6755538dba8Smiod 	/* volume settings */
6765538dba8Smiod 	case ARCOFI_PORT_AUDIO_IN_VOLUME:
6775538dba8Smiod 	case ARCOFI_PORT_AUDIO_OUT_VOLUME:
6785538dba8Smiod 	case ARCOFI_PORT_AUDIO_SPKR_VOLUME:
6795538dba8Smiod 		if (mc->un.value.num_channels != 1)
6805538dba8Smiod 			return EINVAL;
6815538dba8Smiod 		break;
6825538dba8Smiod 	/* mute settings */
6835538dba8Smiod 	case ARCOFI_PORT_AUDIO_IN_MUTE:
6845538dba8Smiod 	case ARCOFI_PORT_AUDIO_OUT_MUTE:
6855538dba8Smiod 	case ARCOFI_PORT_AUDIO_SPKR_MUTE:
6865538dba8Smiod 		if (mc->type != AUDIO_MIXER_ENUM)
6875538dba8Smiod 			return EINVAL;
6881129e472Smiod 		portmask = arcofi_cr3_to_portmask(sc->sc_shadow.cr3,
6891129e472Smiod 		    sc->sc_shadow.output_mute);
6905538dba8Smiod #ifdef ARCOFI_DEBUG
6915538dba8Smiod 		printf("%s: set_port cr3 %02x -> mask %02x\n",
6925538dba8Smiod 		    sc->sc_dev.dv_xname, sc->sc_shadow.cr3, portmask);
6935538dba8Smiod #endif
6945538dba8Smiod 		break;
6955538dba8Smiod 	default:
6965538dba8Smiod 		return EINVAL;
6975538dba8Smiod 	}
6985538dba8Smiod 
6995538dba8Smiod 	switch (mc->dev) {
700eff9a77cSmiod 	/* volume settings */
7015538dba8Smiod 	case ARCOFI_PORT_AUDIO_IN_VOLUME:
7025538dba8Smiod 		sc->sc_shadow.gr_idx =
7035538dba8Smiod 		    arcofi_mi_to_gain(mc->un.value.level[AUDIO_MIXER_LEVEL_MONO]);
7045538dba8Smiod 		return 0;
7055538dba8Smiod 	case ARCOFI_PORT_AUDIO_OUT_VOLUME:
7065538dba8Smiod 	case ARCOFI_PORT_AUDIO_SPKR_VOLUME:
7075538dba8Smiod 		sc->sc_shadow.gx_idx =
7085538dba8Smiod 		    arcofi_mi_to_gain(mc->un.value.level[AUDIO_MIXER_LEVEL_MONO]);
7095538dba8Smiod 		return 0;
7105538dba8Smiod 
711eff9a77cSmiod 	/* mute settings */
7125538dba8Smiod 	case ARCOFI_PORT_AUDIO_IN_MUTE:
7135538dba8Smiod 		if (mc->un.ord)
7145538dba8Smiod 			portmask &= ~AUDIO_LINE_IN;
7155538dba8Smiod 		else
7165538dba8Smiod 			portmask |= AUDIO_LINE_IN;
7175538dba8Smiod 		break;
7185538dba8Smiod 	case ARCOFI_PORT_AUDIO_OUT_MUTE:
7195538dba8Smiod 		if (mc->un.ord)
7205538dba8Smiod 			portmask &= ~AUDIO_LINE_OUT;
7215538dba8Smiod 		else
7225538dba8Smiod 			portmask |= AUDIO_LINE_OUT;
7235538dba8Smiod 		break;
7245538dba8Smiod 	case ARCOFI_PORT_AUDIO_SPKR_MUTE:
7255538dba8Smiod 		if (mc->un.ord)
7265538dba8Smiod 			portmask &= ~AUDIO_SPEAKER;
7275538dba8Smiod 		else
7285538dba8Smiod 			portmask |= AUDIO_SPEAKER;
7295538dba8Smiod 		break;
7305538dba8Smiod 	}
7315538dba8Smiod 
7325538dba8Smiod 	sc->sc_shadow.cr3 = (sc->sc_shadow.cr3 & CR3_OPMODE_MASK) |
7335538dba8Smiod 	    arcofi_portmask_to_cr3(portmask);
7341129e472Smiod 	sc->sc_shadow.output_mute = (portmask == AUDIO_LINE_IN);
7355538dba8Smiod #ifdef ARCOFI_DEBUG
7361129e472Smiod 	printf("%s: set_port mask %02x -> cr3 %02x m %d\n",
7371129e472Smiod 	    sc->sc_dev.dv_xname, portmask,
7381129e472Smiod 	    sc->sc_shadow.cr3, sc->sc_shadow.output_mute);
7395538dba8Smiod #endif
7405538dba8Smiod 
7415538dba8Smiod 	return 0;
7425538dba8Smiod }
7435538dba8Smiod 
7445538dba8Smiod int
arcofi_get_port(void * v,mixer_ctrl_t * mc)7455538dba8Smiod arcofi_get_port(void *v, mixer_ctrl_t *mc)
7465538dba8Smiod {
7475538dba8Smiod 	struct arcofi_softc *sc = (struct arcofi_softc *)v;
7485538dba8Smiod 	int portmask;
7495538dba8Smiod 
7505538dba8Smiod 	/* check for proper type */
7515538dba8Smiod 	switch (mc->dev) {
7525538dba8Smiod 	/* volume settings */
7535538dba8Smiod 	case ARCOFI_PORT_AUDIO_IN_VOLUME:
7545538dba8Smiod 	case ARCOFI_PORT_AUDIO_OUT_VOLUME:
7555538dba8Smiod 	case ARCOFI_PORT_AUDIO_SPKR_VOLUME:
7565538dba8Smiod 		if (mc->un.value.num_channels != 1)
7575538dba8Smiod 			return EINVAL;
7585538dba8Smiod 		break;
759eff9a77cSmiod 
7605538dba8Smiod 	/* mute settings */
7615538dba8Smiod 	case ARCOFI_PORT_AUDIO_IN_MUTE:
7625538dba8Smiod 	case ARCOFI_PORT_AUDIO_OUT_MUTE:
7635538dba8Smiod 	case ARCOFI_PORT_AUDIO_SPKR_MUTE:
7645538dba8Smiod 		if (mc->type != AUDIO_MIXER_ENUM)
7655538dba8Smiod 			return EINVAL;
7661129e472Smiod 		portmask = arcofi_cr3_to_portmask(sc->sc_shadow.cr3,
7671129e472Smiod 		    sc->sc_shadow.output_mute);
7685538dba8Smiod #ifdef ARCOFI_DEBUG
7695538dba8Smiod 		printf("%s: get_port cr3 %02x -> mask %02x\n",
7705538dba8Smiod 		    sc->sc_dev.dv_xname, sc->sc_shadow.cr3, portmask);
7715538dba8Smiod #endif
7725538dba8Smiod 		break;
7735538dba8Smiod 	default:
7745538dba8Smiod 		return EINVAL;
7755538dba8Smiod 	}
7765538dba8Smiod 
7775538dba8Smiod 	switch (mc->dev) {
778eff9a77cSmiod 	/* volume settings */
7795538dba8Smiod 	case ARCOFI_PORT_AUDIO_IN_VOLUME:
7805538dba8Smiod 		mc->un.value.level[AUDIO_MIXER_LEVEL_MONO] =
7815538dba8Smiod 		    arcofi_gain_to_mi(sc->sc_shadow.gr_idx);
7825538dba8Smiod 		break;
7835538dba8Smiod 	case ARCOFI_PORT_AUDIO_OUT_VOLUME:
7845538dba8Smiod 	case ARCOFI_PORT_AUDIO_SPKR_VOLUME:
7855538dba8Smiod 		mc->un.value.level[AUDIO_MIXER_LEVEL_MONO] =
7865538dba8Smiod 		    arcofi_gain_to_mi(sc->sc_shadow.gx_idx);
7875538dba8Smiod 		break;
788eff9a77cSmiod 
789eff9a77cSmiod 	/* mute settings */
7905538dba8Smiod 	case ARCOFI_PORT_AUDIO_IN_MUTE:
7915538dba8Smiod 		mc->un.ord = portmask & AUDIO_LINE_IN ? 0 : 1;
7925538dba8Smiod 		break;
7935538dba8Smiod 	case ARCOFI_PORT_AUDIO_OUT_MUTE:
7945538dba8Smiod 		mc->un.ord = portmask & AUDIO_LINE_OUT ? 0 : 1;
7955538dba8Smiod 		break;
7965538dba8Smiod 	case ARCOFI_PORT_AUDIO_SPKR_MUTE:
7975538dba8Smiod 		mc->un.ord = portmask & AUDIO_SPEAKER ? 0 : 1;
7985538dba8Smiod 		break;
7995538dba8Smiod 	}
8005538dba8Smiod 
8015538dba8Smiod 	return 0;
8025538dba8Smiod }
8035538dba8Smiod 
8045538dba8Smiod int
arcofi_query_devinfo(void * v,mixer_devinfo_t * md)8055538dba8Smiod arcofi_query_devinfo(void *v, mixer_devinfo_t *md)
8065538dba8Smiod {
8075538dba8Smiod 	switch (md->index) {
8085538dba8Smiod 	default:
8095538dba8Smiod 		return ENXIO;
8105538dba8Smiod 
8115538dba8Smiod 	/* items */
8125538dba8Smiod 	case ARCOFI_PORT_AUDIO_IN_VOLUME:
8135538dba8Smiod 		md->type = AUDIO_MIXER_VALUE;
8145538dba8Smiod 		md->mixer_class = ARCOFI_CLASS_INPUT;
8155538dba8Smiod 		md->prev = AUDIO_MIXER_LAST;
8165538dba8Smiod 		md->next = ARCOFI_PORT_AUDIO_IN_MUTE;
8175538dba8Smiod 		strlcpy(md->label.name, AudioNline,
8185538dba8Smiod 		    sizeof md->label.name);
819eff9a77cSmiod 		goto mono_volume;
8205538dba8Smiod 	case ARCOFI_PORT_AUDIO_OUT_VOLUME:
8215538dba8Smiod 		md->type = AUDIO_MIXER_VALUE;
8225538dba8Smiod 		md->mixer_class = ARCOFI_CLASS_OUTPUT;
8235538dba8Smiod 		md->prev = AUDIO_MIXER_LAST;
8245538dba8Smiod 		md->next = ARCOFI_PORT_AUDIO_OUT_MUTE;
8255538dba8Smiod 		strlcpy(md->label.name, AudioNline,
8265538dba8Smiod 		    sizeof md->label.name);
827eff9a77cSmiod 		goto mono_volume;
8285538dba8Smiod 	case ARCOFI_PORT_AUDIO_SPKR_VOLUME:
8295538dba8Smiod 		md->type = AUDIO_MIXER_VALUE;
8305538dba8Smiod 		md->mixer_class = ARCOFI_CLASS_OUTPUT;
8315538dba8Smiod 		md->prev = AUDIO_MIXER_LAST;
8325538dba8Smiod 		md->next = ARCOFI_PORT_AUDIO_SPKR_MUTE;
8335538dba8Smiod 		strlcpy(md->label.name, AudioNspeaker,
8345538dba8Smiod 		    sizeof md->label.name);
835eff9a77cSmiod 		/* goto mono_volume; */
836eff9a77cSmiod mono_volume:
8375538dba8Smiod 		md->un.v.num_channels = 1;
8385538dba8Smiod 		strlcpy(md->un.v.units.name, AudioNvolume,
8395538dba8Smiod 		    sizeof md->un.v.units.name);
8405538dba8Smiod 		break;
8415538dba8Smiod 
8425538dba8Smiod 	case ARCOFI_PORT_AUDIO_IN_MUTE:
8435538dba8Smiod 		md->type = AUDIO_MIXER_ENUM;
8445538dba8Smiod 		md->mixer_class = ARCOFI_CLASS_INPUT;
8455538dba8Smiod 		md->prev = ARCOFI_PORT_AUDIO_IN_VOLUME;
8465538dba8Smiod 		md->next = AUDIO_MIXER_LAST;
8475538dba8Smiod 		goto mute;
8485538dba8Smiod 	case ARCOFI_PORT_AUDIO_OUT_MUTE:
8495538dba8Smiod 		md->type = AUDIO_MIXER_ENUM;
8505538dba8Smiod 		md->mixer_class = ARCOFI_CLASS_OUTPUT;
8515538dba8Smiod 		md->prev = ARCOFI_PORT_AUDIO_OUT_VOLUME;
8525538dba8Smiod 		md->next = AUDIO_MIXER_LAST;
8535538dba8Smiod 		goto mute;
8545538dba8Smiod 	case ARCOFI_PORT_AUDIO_SPKR_MUTE:
8555538dba8Smiod 		md->type = AUDIO_MIXER_ENUM;
8565538dba8Smiod 		md->mixer_class = ARCOFI_CLASS_OUTPUT;
8575538dba8Smiod 		md->prev = ARCOFI_PORT_AUDIO_SPKR_VOLUME;
8585538dba8Smiod 		md->next = AUDIO_MIXER_LAST;
8595538dba8Smiod 		/* goto mute; */
8605538dba8Smiod mute:
8615538dba8Smiod 		strlcpy(md->label.name, AudioNmute, sizeof md->label.name);
8625538dba8Smiod 		md->un.e.num_mem = 2;
8635538dba8Smiod 		strlcpy(md->un.e.member[0].label.name, AudioNoff,
8645538dba8Smiod 		    sizeof md->un.e.member[0].label.name);
8655538dba8Smiod 		md->un.e.member[0].ord = 0;
8665538dba8Smiod 		strlcpy(md->un.e.member[1].label.name, AudioNon,
8675538dba8Smiod 		    sizeof md->un.e.member[1].label.name);
8685538dba8Smiod 		md->un.e.member[1].ord = 1;
8695538dba8Smiod 		break;
8705538dba8Smiod 
8715538dba8Smiod 	/* classes */
8725538dba8Smiod 	case ARCOFI_CLASS_INPUT:
8735538dba8Smiod 		md->type = AUDIO_MIXER_CLASS;
8745538dba8Smiod 		md->mixer_class = ARCOFI_CLASS_INPUT;
8755538dba8Smiod 		md->prev = AUDIO_MIXER_LAST;
8765538dba8Smiod 		md->next = AUDIO_MIXER_LAST;
8775538dba8Smiod 		strlcpy(md->label.name, AudioCinputs,
8785538dba8Smiod 		    sizeof md->label.name);
8795538dba8Smiod 		break;
8805538dba8Smiod 	case ARCOFI_CLASS_OUTPUT:
8815538dba8Smiod 		md->type = AUDIO_MIXER_CLASS;
8825538dba8Smiod 		md->mixer_class = ARCOFI_CLASS_OUTPUT;
8835538dba8Smiod 		md->prev = AUDIO_MIXER_LAST;
8845538dba8Smiod 		md->next = AUDIO_MIXER_LAST;
8855538dba8Smiod 		strlcpy(md->label.name, AudioCoutputs,
8865538dba8Smiod 		    sizeof md->label.name);
8875538dba8Smiod 		break;
8885538dba8Smiod 	}
8895538dba8Smiod 
8905538dba8Smiod 	return 0;
8915538dba8Smiod }
8925538dba8Smiod 
8935538dba8Smiod int
arcofi_hwintr(void * v)8945538dba8Smiod arcofi_hwintr(void *v)
8955538dba8Smiod {
8965538dba8Smiod 	struct arcofi_softc *sc = (struct arcofi_softc *)v;
8975538dba8Smiod 	uint8_t *cur, *past;
8985538dba8Smiod 	uint8_t csr, fir, data;
8995538dba8Smiod 	int rc = 0;
9005538dba8Smiod 
9015538dba8Smiod 	csr = arcofi_read(sc, ARCOFI_CSR);
9025538dba8Smiod 	if ((csr & CSR_INTR_REQUEST) == 0)
9035538dba8Smiod 		return 0;
9045538dba8Smiod 
9055538dba8Smiod 	fir = arcofi_read(sc, ARCOFI_FIFO_IR);
9065538dba8Smiod 
9075538dba8Smiod 	/* receive */
9085538dba8Smiod 	if (fir & FIFO_IR_EVENT(FIFO_IR_IN_HALF_EMPTY)) {
9095538dba8Smiod 		rc = 1;
9105538dba8Smiod 		cur = sc->sc_recv.buf;
9115538dba8Smiod 		past = sc->sc_recv.past;
9125538dba8Smiod 
9135538dba8Smiod 		while ((arcofi_read(sc, ARCOFI_FIFO_SR) &
9145538dba8Smiod 		    FIFO_SR_IN_EMPTY) == 0) {
9155538dba8Smiod 			data = arcofi_read(sc, ARCOFI_FIFO_DATA);
9165538dba8Smiod 			if (cur != NULL && cur != past) {
9175538dba8Smiod 				*cur++ = data;
9185538dba8Smiod 				if (cur == past) {
9195538dba8Smiod 					softintr_schedule(sc->sc_sih);
9205538dba8Smiod 					break;
9215538dba8Smiod 				}
9225538dba8Smiod 			}
9235538dba8Smiod 		}
9245538dba8Smiod 		sc->sc_recv.buf = cur;
9255538dba8Smiod 
9265538dba8Smiod 		if (cur == NULL || cur == past) {
9275538dba8Smiod 			/* underrun, disable further interrupts */
9285538dba8Smiod 			arcofi_write(sc, ARCOFI_FIFO_IR,
9295538dba8Smiod 			    arcofi_read(sc, ARCOFI_FIFO_IR) &
9305538dba8Smiod 			    ~FIFO_IR_ENABLE(FIFO_IR_IN_HALF_EMPTY));
9315538dba8Smiod 		}
9325538dba8Smiod 	}
9335538dba8Smiod 
9345538dba8Smiod 	/* xmit */
9355538dba8Smiod 	if (fir & FIFO_IR_EVENT(FIFO_IR_OUT_HALF_EMPTY)) {
9365538dba8Smiod 		rc = 1;
9375538dba8Smiod 		cur = sc->sc_xmit.buf;
9385538dba8Smiod 		past = sc->sc_xmit.past;
9395538dba8Smiod 		if (cur != NULL) {
9405538dba8Smiod 			while ((arcofi_read(sc, ARCOFI_FIFO_SR) &
9415538dba8Smiod 			    FIFO_SR_OUT_FULL) == 0) {
9425538dba8Smiod 				if (cur != past)
9435538dba8Smiod 					arcofi_write(sc, ARCOFI_FIFO_DATA,
9445538dba8Smiod 					    *cur++);
9455538dba8Smiod 				if (cur == past) {
9465538dba8Smiod 					softintr_schedule(sc->sc_sih);
9475538dba8Smiod 					break;
9485538dba8Smiod 				}
9495538dba8Smiod 			}
9505538dba8Smiod 		}
9515538dba8Smiod 		if (cur == NULL || cur == past) {
9525538dba8Smiod 			/* disable further interrupts */
9535538dba8Smiod 			arcofi_write(sc, ARCOFI_FIFO_IR,
9545538dba8Smiod 			    arcofi_read(sc, ARCOFI_FIFO_IR) &
9555538dba8Smiod 			    ~FIFO_IR_ENABLE(FIFO_IR_OUT_HALF_EMPTY));
9565538dba8Smiod 		}
9575538dba8Smiod 		sc->sc_xmit.buf = cur;
9585538dba8Smiod 	}
9595538dba8Smiod 
9605538dba8Smiod 	/* drain */
9615538dba8Smiod 	if (fir & FIFO_IR_EVENT(FIFO_IR_OUT_EMPTY)) {
9625538dba8Smiod 		rc = 1;
9635538dba8Smiod 		arcofi_write(sc, ARCOFI_FIFO_IR,
9645538dba8Smiod 		    arcofi_read(sc, ARCOFI_FIFO_IR) &
9655538dba8Smiod 		    ~FIFO_IR_ENABLE(FIFO_IR_OUT_EMPTY));
9665538dba8Smiod 		wakeup(&sc->sc_xmit);
9675538dba8Smiod 	}
9685538dba8Smiod 
9695538dba8Smiod #ifdef ARCOFI_DEBUG
9705538dba8Smiod 	if (rc == 0)
9715538dba8Smiod 		printf("%s: unclaimed interrupt, csr %02x fir %02x fsr %02x\n",
9725538dba8Smiod 		    sc->sc_dev.dv_xname, csr, fir,
9735538dba8Smiod 		    arcofi_read(sc, ARCOFI_FIFO_SR));
9745538dba8Smiod #endif
9755538dba8Smiod 
9765538dba8Smiod 	return rc;
9775538dba8Smiod }
9785538dba8Smiod 
9795538dba8Smiod void
arcofi_swintr(void * v)9805538dba8Smiod arcofi_swintr(void *v)
9815538dba8Smiod {
9825538dba8Smiod 	struct arcofi_softc *sc = (struct arcofi_softc *)v;
983886882aaSratchov 	int action;
9845538dba8Smiod 
9855538dba8Smiod 	action = 0;
986886882aaSratchov 	mtx_enter(&audio_lock);
9875538dba8Smiod 	if (sc->sc_recv.buf != NULL && sc->sc_recv.buf == sc->sc_recv.past)
9885538dba8Smiod 		action |= AUMODE_RECORD;
9895538dba8Smiod 	if (sc->sc_xmit.buf != NULL && sc->sc_xmit.buf == sc->sc_xmit.past)
9905538dba8Smiod 		action |= AUMODE_PLAY;
9915538dba8Smiod 
9925538dba8Smiod 	if (action & AUMODE_RECORD) {
9935538dba8Smiod 		if (sc->sc_recv.cb)
9945538dba8Smiod 			sc->sc_recv.cb(sc->sc_recv.cbarg);
9955538dba8Smiod 	}
9965538dba8Smiod 	if (action & AUMODE_PLAY) {
9975538dba8Smiod 		if (sc->sc_xmit.cb)
9985538dba8Smiod 			sc->sc_xmit.cb(sc->sc_xmit.cbarg);
9995538dba8Smiod 	}
10000cf84469Sratchov 	mtx_leave(&audio_lock);
10015538dba8Smiod }
10025538dba8Smiod 
10035538dba8Smiod int
arcofi_cmd(struct arcofi_softc * sc,uint8_t cmd,const uint8_t * data)10045538dba8Smiod arcofi_cmd(struct arcofi_softc *sc, uint8_t cmd, const uint8_t *data)
10055538dba8Smiod {
10065538dba8Smiod 	size_t len;
10075538dba8Smiod 	uint8_t csr;
10085538dba8Smiod 	int cnt;
10095538dba8Smiod 	static const uint8_t cmdlen[] = {
10105538dba8Smiod 	    [SOP_0] = 4,
10115538dba8Smiod 	    [COP_1] = 4,
10125538dba8Smiod 	    [COP_2] = 2,
10135538dba8Smiod 	    [COP_3] = 2,
10145538dba8Smiod 	    [SOP_4] = 1,
10155538dba8Smiod 	    [SOP_5] = 1,
10165538dba8Smiod 	    [SOP_6] = 1,
10175538dba8Smiod 	    [SOP_7] = 1,
10185538dba8Smiod 	    [COP_8] = 2,
10195538dba8Smiod 	    [COP_9] = 4,
10205538dba8Smiod 	    [COP_A] = 8,
10215538dba8Smiod 	    [COP_B] = 2,
10225538dba8Smiod 	    [COP_C] = 8,
10235538dba8Smiod 	    [COP_D] = 4,
10245538dba8Smiod 	    [COP_E] = 4
10255538dba8Smiod 	};
10265538dba8Smiod 
10275538dba8Smiod 	/*
10285538dba8Smiod 	 * Compute command length.
10295538dba8Smiod 	 */
10305538dba8Smiod 	if (cmd >= nitems(cmdlen))
10315538dba8Smiod 		return EINVAL;
10325538dba8Smiod 	len = cmdlen[cmd];
10335538dba8Smiod 
1034886882aaSratchov 	mtx_enter(&audio_lock);
10355538dba8Smiod 
10365538dba8Smiod 	/*
10375538dba8Smiod 	 * Disable all FIFO processing.
10385538dba8Smiod 	 */
10395538dba8Smiod 	csr = arcofi_read(sc, ARCOFI_CSR);
10405538dba8Smiod 	arcofi_write(sc, ARCOFI_CSR,
10415538dba8Smiod 	    csr & ~(CSR_DATA_FIFO_ENABLE | CSR_CTRL_FIFO_ENABLE));
10425538dba8Smiod 
10435538dba8Smiod 	/*
10445538dba8Smiod 	 * Fill the FIFO with the command bytes.
10455538dba8Smiod 	 */
10465538dba8Smiod 	arcofi_write(sc, ARCOFI_FIFO_CTRL, CMDR_PU | CMDR_WRITE | cmd);
10475538dba8Smiod 	for (; len != 0; len--)
10485538dba8Smiod 		arcofi_write(sc, ARCOFI_FIFO_CTRL, *data++);
10495538dba8Smiod 
10505538dba8Smiod 	/*
10515538dba8Smiod 	 * Enable command processing.
10525538dba8Smiod 	 */
10535538dba8Smiod 	arcofi_write(sc, ARCOFI_CSR,
10545538dba8Smiod 	    (csr & ~CSR_DATA_FIFO_ENABLE) | CSR_CTRL_FIFO_ENABLE);
10555538dba8Smiod 
10565538dba8Smiod 	/*
10575538dba8Smiod 	 * Wait for the command FIFO to be empty.
10585538dba8Smiod 	 */
10595538dba8Smiod 	cnt = 100;
10605538dba8Smiod 	while ((arcofi_read(sc, ARCOFI_FIFO_SR) & FIFO_SR_CTRL_EMPTY) == 0) {
1061886882aaSratchov 		if (cnt-- == 0) {
1062886882aaSratchov 			mtx_leave(&audio_lock);
10635538dba8Smiod 			return EBUSY;
1064886882aaSratchov 		}
10655538dba8Smiod 		delay(10);
10665538dba8Smiod 	}
10675538dba8Smiod 
10685538dba8Smiod 	arcofi_write(sc, ARCOFI_CSR, csr);
10695538dba8Smiod 
1070886882aaSratchov 	mtx_leave(&audio_lock);
10715538dba8Smiod 	return 0;
10725538dba8Smiod }
10735538dba8Smiod 
10745538dba8Smiod void
arcofi_attach(struct arcofi_softc * sc,const char * version)10755538dba8Smiod arcofi_attach(struct arcofi_softc *sc, const char *version)
10765538dba8Smiod {
10775538dba8Smiod 	int rc;
10785538dba8Smiod 	uint8_t cmd[4];
10795538dba8Smiod 
10805538dba8Smiod 	/*
10815538dba8Smiod 	 * Reset logic.
10825538dba8Smiod 	 */
10835538dba8Smiod 	arcofi_write(sc, ARCOFI_ID, 0);
10845538dba8Smiod 	delay(100000);
10855538dba8Smiod 	arcofi_write(sc, ARCOFI_CSR, 0);
10865538dba8Smiod 
10875538dba8Smiod 	/*
10885538dba8Smiod 	 * Initialize the chip to default settings (8 bit, u-Law).
10895538dba8Smiod 	 */
10905538dba8Smiod 	sc->sc_active.cr3 =
10915538dba8Smiod 	    arcofi_portmask_to_cr3(AUDIO_SPEAKER) | CR3_OPMODE_NORMAL;
10925538dba8Smiod 	sc->sc_active.cr4 = CR4_TM | CR4_ULAW;
10931129e472Smiod 	sc->sc_active.gr_idx = sc->sc_active.gx_idx = 1 + NEGATIVE_GAINS;
10941129e472Smiod 	sc->sc_active.output_mute = 0;
10955538dba8Smiod 	bcopy(&sc->sc_active, &sc->sc_shadow, sizeof(sc->sc_active));
10965538dba8Smiod 
10975538dba8Smiod 	/* clear CRAM */
10985538dba8Smiod 	cmd[0] = CR1_IDR;
10995538dba8Smiod 	if ((rc = arcofi_cmd(sc, SOP_4, cmd)) != 0)
11005538dba8Smiod 		goto error;
11015538dba8Smiod 	delay(1000);
11025538dba8Smiod 
11035538dba8Smiod 	/* set gain values before enabling them in CR1 */
11045538dba8Smiod 	cmd[0] = arcofi_gains[sc->sc_active.gr_idx] >> 8;
11055538dba8Smiod 	cmd[1] = arcofi_gains[sc->sc_active.gr_idx];
11065538dba8Smiod 	if ((rc = arcofi_cmd(sc, COP_2, cmd)) != 0)
11075538dba8Smiod 		goto error;
11085538dba8Smiod 	/* same value for gx... */
11095538dba8Smiod 	if ((rc = arcofi_cmd(sc, COP_B, cmd)) != 0)
11105538dba8Smiod 		goto error;
11115538dba8Smiod 
11125538dba8Smiod 	/* set all CR registers at once */
11135538dba8Smiod 	cmd[0] = sc->sc_active.cr4;
11145538dba8Smiod 	cmd[1] = sc->sc_active.cr3;
11155538dba8Smiod 	cmd[2] = CR2_SD | CR2_SC | CR2_SB | CR2_SA | CR2_ELS | CR2_AM | CR2_EFC;
11165538dba8Smiod 	cmd[3] = CR1_GR | CR1_GX;
11175538dba8Smiod 	if ((rc = arcofi_cmd(sc, SOP_0, cmd)) != 0)
11185538dba8Smiod 		goto error;
11195538dba8Smiod 
11205538dba8Smiod 	arcofi_write(sc, ARCOFI_FIFO_IR, 0);
11215538dba8Smiod 	arcofi_write(sc, ARCOFI_CSR, CSR_INTR_ENABLE);
11225538dba8Smiod 
11232baa08e2Santon 	audio_attach_mi(&arcofi_hw_if, sc, NULL, &sc->sc_dev);
11245538dba8Smiod 	return;
11255538dba8Smiod 
11265538dba8Smiod error:
11275538dba8Smiod 	arcofi_write(sc, ARCOFI_ID, 0);
1128c8b758f8Smiod 	printf("%s: command failed, error %d\n", __func__, rc);
11295538dba8Smiod }
1130