xref: /netbsd-src/sys/arch/macppc/dev/snapper.c (revision e5fbc36ada28f9b9a5836ecffaf4a06aa1ebb687)
1*e5fbc36aSthorpej /*	$NetBSD: snapper.c,v 1.67 2023/12/20 15:29:04 thorpej Exp $	*/
29427a0c7Sgrant /*	Id: snapper.c,v 1.11 2002/10/31 17:42:13 tsubai Exp	*/
335a2b63bSmacallan /*	Id: i2s.c,v 1.12 2005/01/15 14:32:35 tsubai Exp		*/
4a741bccaSmacallan 
59427a0c7Sgrant /*-
635a2b63bSmacallan  * Copyright (c) 2002, 2003 Tsubai Masanari.  All rights reserved.
79427a0c7Sgrant  *
89427a0c7Sgrant  * Redistribution and use in source and binary forms, with or without
99427a0c7Sgrant  * modification, are permitted provided that the following conditions
109427a0c7Sgrant  * are met:
119427a0c7Sgrant  * 1. Redistributions of source code must retain the above copyright
129427a0c7Sgrant  *    notice, this list of conditions and the following disclaimer.
139427a0c7Sgrant  * 2. Redistributions in binary form must reproduce the above copyright
149427a0c7Sgrant  *    notice, this list of conditions and the following disclaimer in the
159427a0c7Sgrant  *    documentation and/or other materials provided with the distribution.
169427a0c7Sgrant  * 3. The name of the author may not be used to endorse or promote products
179427a0c7Sgrant  *    derived from this software without specific prior written permission.
189427a0c7Sgrant  *
199427a0c7Sgrant  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
209427a0c7Sgrant  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
219427a0c7Sgrant  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
229427a0c7Sgrant  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
239427a0c7Sgrant  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
249427a0c7Sgrant  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
259427a0c7Sgrant  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
269427a0c7Sgrant  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
279427a0c7Sgrant  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
289427a0c7Sgrant  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
299427a0c7Sgrant  */
309427a0c7Sgrant 
319427a0c7Sgrant /*
329427a0c7Sgrant  * Datasheet is available from
339427a0c7Sgrant  * http://www.ti.com/sc/docs/products/analog/tas3004.html
34a741bccaSmacallan  * http://www.ti.com/sc/docs/products/analog/tas3001.html
359427a0c7Sgrant  */
369427a0c7Sgrant 
37a741bccaSmacallan #include <sys/cdefs.h>
38*e5fbc36aSthorpej __KERNEL_RCSID(0, "$NetBSD: snapper.c,v 1.67 2023/12/20 15:29:04 thorpej Exp $");
39a741bccaSmacallan 
409427a0c7Sgrant #include <sys/param.h>
419427a0c7Sgrant #include <sys/audioio.h>
429427a0c7Sgrant #include <sys/device.h>
439427a0c7Sgrant #include <sys/systm.h>
449427a0c7Sgrant 
45e622eac4Sisaki #include <dev/audio/audio_if.h>
469427a0c7Sgrant #include <dev/ofw/openfirm.h>
479427a0c7Sgrant #include <macppc/dev/dbdma.h>
489427a0c7Sgrant 
499427a0c7Sgrant #include <uvm/uvm_extern.h>
50d9f9caa0Smacallan #include <dev/i2c/i2cvar.h>
51275e3107Smacallan #include <dev/onewire/onewirevar.h>
529427a0c7Sgrant 
539427a0c7Sgrant #include <machine/autoconf.h>
549427a0c7Sgrant #include <machine/pio.h>
559427a0c7Sgrant 
56d9f9caa0Smacallan #include <macppc/dev/deqvar.h>
57b8ae2bc3Smacallan #include <macppc/dev/obiovar.h>
58275e3107Smacallan 
59275e3107Smacallan #include "opt_snapper.h"
60275e3107Smacallan 
619427a0c7Sgrant #ifdef SNAPPER_DEBUG
629427a0c7Sgrant # define DPRINTF printf
639427a0c7Sgrant #else
649427a0c7Sgrant # define DPRINTF while (0) printf
659427a0c7Sgrant #endif
669427a0c7Sgrant 
67a741bccaSmacallan #define SNAPPER_MAXPAGES	16
68a741bccaSmacallan 
699427a0c7Sgrant struct snapper_softc {
707d61890bSmacallan 	device_t sc_dev;
7199276969Smacallan 	int sc_mode;
7299276969Smacallan #define SNAPPER_IS_TAS3004	0 // codec is TAS3004
73a741bccaSmacallan #define SNAPPER_IS_TAS3001	1 // codec is TAS3001
7499276969Smacallan #define SNAPPER_IS_PCM3052	2 // codec is PCM3052
7599276969Smacallan #define SNAPPER_IS_CS8416	3 // codec is CS8416
7699276969Smacallan #define SNAPPER_SWVOL		4 // software codec
77a741bccaSmacallan 
789427a0c7Sgrant 	int sc_node;
799427a0c7Sgrant 
809427a0c7Sgrant 	void (*sc_ointr)(void *);	/* dma completion intr handler */
819427a0c7Sgrant 	void *sc_oarg;			/* arg for sc_ointr() */
829427a0c7Sgrant 	int sc_opages;			/* # of output pages */
839427a0c7Sgrant 
849427a0c7Sgrant 	void (*sc_iintr)(void *);	/* dma completion intr handler */
859427a0c7Sgrant 	void *sc_iarg;			/* arg for sc_iintr() */
865f55aaa2Smacallan 	int sc_ipages;			/* # of input pages */
879427a0c7Sgrant 
889427a0c7Sgrant 	u_int sc_record_source;		/* recording source mask */
899427a0c7Sgrant 	u_int sc_output_mask;		/* output source mask */
909427a0c7Sgrant 
91d974db0aSgarbled 	bus_space_tag_t sc_tag;
92d974db0aSgarbled 	bus_space_handle_t sc_bsh;
93d9f9caa0Smacallan 	i2c_addr_t sc_deqaddr;
94d9f9caa0Smacallan 	i2c_tag_t sc_i2c;
95a741bccaSmacallan 	uint32_t sc_baseaddr;
969427a0c7Sgrant 
9735a2b63bSmacallan 	int sc_rate;                    /* current sampling rate */
9835a2b63bSmacallan 	int sc_bitspersample;
99a741bccaSmacallan 
100e622eac4Sisaki 	/* for SNAPPER_SWVOL */
101e622eac4Sisaki 	u_int sc_swvol_l;
102e622eac4Sisaki 	u_int sc_swvol_r;
10335a2b63bSmacallan 
1049427a0c7Sgrant 	u_int sc_vol_l;
1059427a0c7Sgrant 	u_int sc_vol_r;
106d9f9caa0Smacallan 	u_int sc_treble;
107d9f9caa0Smacallan 	u_int sc_bass;
108d9f9caa0Smacallan 	u_int mixer[6]; /* s1_l, s2_l, an_l, s1_r, s2_r, an_r */
109e622eac4Sisaki 	uint16_t sc_rval;
1109427a0c7Sgrant 
111d974db0aSgarbled 	bus_space_handle_t sc_odmah;
112d974db0aSgarbled 	bus_space_handle_t sc_idmah;
1139427a0c7Sgrant 	dbdma_regmap_t *sc_odma;
1149427a0c7Sgrant 	dbdma_regmap_t *sc_idma;
115d9b67662Sbriggs 	unsigned char	dbdma_cmdspace[sizeof(struct dbdma_command) * 40 + 15];
116d9b67662Sbriggs 	struct dbdma_command *sc_odmacmd;
117d9b67662Sbriggs 	struct dbdma_command *sc_idmacmd;
1188a962f23Sjmcneill 
1198a962f23Sjmcneill 	kmutex_t sc_lock;
1208a962f23Sjmcneill 	kmutex_t sc_intr_lock;
121275e3107Smacallan 
122275e3107Smacallan 	struct onewire_bus	sc_ow_bus;
123275e3107Smacallan 	device_t		sc_ow_dev;
124275e3107Smacallan 	int			sc_ow_data;
1259427a0c7Sgrant };
1269427a0c7Sgrant 
127e3693941Smacallan static int snapper_match(device_t, struct cfdata *, void *);
128e3693941Smacallan static void snapper_attach(device_t, device_t, void *);
129e3693941Smacallan static void snapper_defer(device_t);
130e3693941Smacallan static int snapper_intr(void *);
131e622eac4Sisaki static int snapper_query_format(void *, audio_format_query_t *);
132e622eac4Sisaki static int snapper_set_format(void *, int,
133e622eac4Sisaki     const audio_params_t *, const audio_params_t *,
134e622eac4Sisaki     audio_filter_reg_t *, audio_filter_reg_t *);
135abea73c7Sjmcneill static int snapper_commit_settings(void *);
136e3693941Smacallan static int snapper_round_blocksize(void *, int, int, const audio_params_t *);
137e3693941Smacallan static int snapper_halt_output(void *);
138e3693941Smacallan static int snapper_halt_input(void *);
139e3693941Smacallan static int snapper_getdev(void *, struct audio_device *);
140e3693941Smacallan static int snapper_set_port(void *, mixer_ctrl_t *);
141e3693941Smacallan static int snapper_get_port(void *, mixer_ctrl_t *);
142e3693941Smacallan static int snapper_query_devinfo(void *, mixer_devinfo_t *);
143e3693941Smacallan static size_t snapper_round_buffersize(void *, int, size_t);
144e3693941Smacallan static int snapper_get_props(void *);
145e3693941Smacallan static int snapper_trigger_output(void *, void *, void *, int, void (*)(void *),
14623b5d914Skent     void *, const audio_params_t *);
147e3693941Smacallan static int snapper_trigger_input(void *, void *, void *, int, void (*)(void *),
14823b5d914Skent     void *, const audio_params_t *);
1498a962f23Sjmcneill static void snapper_get_locks(void *, kmutex_t **, kmutex_t **);
150e3693941Smacallan static void snapper_set_volume(struct snapper_softc *, u_int, u_int);
151e3693941Smacallan static int snapper_set_rate(struct snapper_softc *);
152e3693941Smacallan static void snapper_set_treble(struct snapper_softc *, u_int);
153e3693941Smacallan static void snapper_set_bass(struct snapper_softc *, u_int);
154e3693941Smacallan static void snapper_write_mixers(struct snapper_softc *);
1559427a0c7Sgrant 
156e3693941Smacallan static int tas3004_write(struct snapper_softc *, u_int, const void *);
157bea393bbSmacallan static int gpio_read(bus_size_t);
158bea393bbSmacallan static void gpio_write(bus_size_t, int);
159e3693941Smacallan static void snapper_mute_speaker(struct snapper_softc *, int);
160e3693941Smacallan static void snapper_mute_headphone(struct snapper_softc *, int);
161202e08cdSmacallan static void snapper_mute_lineout(struct snapper_softc *, int);
162e3693941Smacallan static int snapper_cint(void *);
163e3693941Smacallan static int tas3004_init(struct snapper_softc *);
164e3693941Smacallan static void snapper_init(struct snapper_softc *, int);
1659427a0c7Sgrant 
166275e3107Smacallan static void snapper_setup_ow(struct snapper_softc *);
167275e3107Smacallan static int snapper_ow_reset(void *);
168275e3107Smacallan static int snapper_ow_read_bit(void *);
169275e3107Smacallan static void snapper_ow_write_bit(void *, int);
170275e3107Smacallan 
171275e3107Smacallan static void snapper_bb_rx(void *);
172275e3107Smacallan static void snapper_bb_tx(void *);
173275e3107Smacallan static int snapper_bb_get(void *);
174275e3107Smacallan static void snapper_bb_set(void *, int);
175275e3107Smacallan 
176275e3107Smacallan static const struct onewire_bbops snapper_bbops = {
177275e3107Smacallan 	snapper_bb_rx,
178275e3107Smacallan 	snapper_bb_tx,
179275e3107Smacallan 	snapper_bb_get,
180275e3107Smacallan 	snapper_bb_set
181275e3107Smacallan };
182275e3107Smacallan 
183275e3107Smacallan 
184e622eac4Sisaki static void
snapper_volume(audio_filter_arg_t * arg)185e622eac4Sisaki snapper_volume(audio_filter_arg_t *arg)
18671195759Sjmcneill {
187e622eac4Sisaki 	struct snapper_softc *sc;
188e622eac4Sisaki 	const aint_t *src;
189017edd80Smlelstv 	int16_t *dst;
190e622eac4Sisaki 	u_int sample_count;
191e622eac4Sisaki 	u_int i;
19271195759Sjmcneill 
193e622eac4Sisaki 	sc = arg->context;
194e622eac4Sisaki 	src = arg->src;
195e622eac4Sisaki 	dst = arg->dst;
196e622eac4Sisaki 	sample_count = arg->count * arg->srcfmt->channels;
197e622eac4Sisaki 	for (i = 0; i < sample_count; i++) {
198e622eac4Sisaki 		aint2_t l = (aint2_t)(*src++);
199e622eac4Sisaki 		l = l * sc->sc_swvol_l / 255;
200e622eac4Sisaki 		*dst++ = (aint_t)l;
201e622eac4Sisaki 	}
20271195759Sjmcneill }
20371195759Sjmcneill 
204a741bccaSmacallan /*
205a741bccaSmacallan  * A hardware bug in the TAS3004 I2S transport
206a741bccaSmacallan  * produces phase differences between channels
207a741bccaSmacallan  * (left channel appears delayed by one sample).
208a741bccaSmacallan  * Fix the phase difference by delaying the right channel
209a741bccaSmacallan  * by one sample.
210a741bccaSmacallan  */
21171195759Sjmcneill static void
snapper_fixphase(audio_filter_arg_t * arg)212e622eac4Sisaki snapper_fixphase(audio_filter_arg_t *arg)
21371195759Sjmcneill {
214e622eac4Sisaki 	struct snapper_softc *sc;
215e622eac4Sisaki 	const aint_t *src;
216e622eac4Sisaki 	aint_t *dst;
217e622eac4Sisaki 	u_int i;
218e622eac4Sisaki 
219e622eac4Sisaki 	sc = arg->context;
220e622eac4Sisaki 	src = arg->src;
221e622eac4Sisaki 	dst = arg->dst;
222e622eac4Sisaki 	for (i = 0; i < arg->count; i++) {
223e622eac4Sisaki 		*dst++ = *src++;
224e622eac4Sisaki 		*dst++ = sc->sc_rval;
225e622eac4Sisaki 		sc->sc_rval = *src++;
226e622eac4Sisaki 	}
22771195759Sjmcneill }
22871195759Sjmcneill 
2297d61890bSmacallan CFATTACH_DECL_NEW(snapper, sizeof(struct snapper_softc), snapper_match,
2300477b152Sdogcow 	snapper_attach, NULL, NULL);
2319427a0c7Sgrant 
23218f717bbSyamt const struct audio_hw_if snapper_hw_if = {
233e622eac4Sisaki 	.query_format		= snapper_query_format,
234e622eac4Sisaki 	.set_format		= snapper_set_format,
235abea73c7Sjmcneill 	.commit_settings	= snapper_commit_settings,
2366291b134Sisaki 	.round_blocksize	= snapper_round_blocksize,
2376291b134Sisaki 	.halt_output		= snapper_halt_output,
2386291b134Sisaki 	.halt_input		= snapper_halt_input,
2396291b134Sisaki 	.getdev			= snapper_getdev,
2406291b134Sisaki 	.set_port		= snapper_set_port,
2416291b134Sisaki 	.get_port		= snapper_get_port,
2426291b134Sisaki 	.query_devinfo		= snapper_query_devinfo,
2436291b134Sisaki 	.round_buffersize	= snapper_round_buffersize,
2446291b134Sisaki 	.get_props		= snapper_get_props,
2456291b134Sisaki 	.trigger_output		= snapper_trigger_output,
2466291b134Sisaki 	.trigger_input		= snapper_trigger_input,
2476291b134Sisaki 	.get_locks		= snapper_get_locks,
2489427a0c7Sgrant };
2499427a0c7Sgrant 
2509427a0c7Sgrant struct audio_device snapper_device = {
2519427a0c7Sgrant 	"SNAPPER",
2529427a0c7Sgrant 	"",
2539427a0c7Sgrant 	"snapper"
2549427a0c7Sgrant };
2559427a0c7Sgrant 
256a741bccaSmacallan #define SNAPPER_BASSTAB_0DB	18
257d9f9caa0Smacallan const uint8_t snapper_basstab[] = {
258d9f9caa0Smacallan 	0x96,	/* -18dB */
259d9f9caa0Smacallan 	0x94,	/* -17dB */
260d9f9caa0Smacallan 	0x92,	/* -16dB */
261d9f9caa0Smacallan 	0x90,	/* -15dB */
262d9f9caa0Smacallan 	0x8e,	/* -14dB */
263d9f9caa0Smacallan 	0x8c,	/* -13dB */
264d9f9caa0Smacallan 	0x8a,	/* -12dB */
265d9f9caa0Smacallan 	0x88,	/* -11dB */
266d9f9caa0Smacallan 	0x86,	/* -10dB */
267d9f9caa0Smacallan 	0x84,	/* -9dB */
268d9f9caa0Smacallan 	0x82,	/* -8dB */
269d9f9caa0Smacallan 	0x80,	/* -7dB */
270d9f9caa0Smacallan 	0x7e,	/* -6dB */
271d9f9caa0Smacallan 	0x7c,	/* -5dB */
272d9f9caa0Smacallan 	0x7a,	/* -4dB */
273d9f9caa0Smacallan 	0x78,	/* -3dB */
274d9f9caa0Smacallan 	0x76,	/* -2dB */
275d9f9caa0Smacallan 	0x74,	/* -1dB */
276d9f9caa0Smacallan 	0x72,	/* 0dB */
277d9f9caa0Smacallan 	0x6f,	/* 1dB */
278d9f9caa0Smacallan 	0x6d,	/* 2dB */
279d9f9caa0Smacallan 	0x6a,	/* 3dB */
280d9f9caa0Smacallan 	0x67,	/* 4dB */
281d9f9caa0Smacallan 	0x65,	/* 5dB */
282d9f9caa0Smacallan 	0x62,	/* 6dB */
283d9f9caa0Smacallan 	0x5f,	/* 7dB */
284d9f9caa0Smacallan 	0x5b,	/* 8dB */
285d9f9caa0Smacallan 	0x55,	/* 9dB */
286d9f9caa0Smacallan 	0x4f,	/* 10dB */
287d9f9caa0Smacallan 	0x49,	/* 11dB */
288d9f9caa0Smacallan 	0x43,	/* 12dB */
289d9f9caa0Smacallan 	0x3b,	/* 13dB */
290d9f9caa0Smacallan 	0x33,	/* 14dB */
291d9f9caa0Smacallan 	0x29,	/* 15dB */
292d9f9caa0Smacallan 	0x1e,	/* 16dB */
293d9f9caa0Smacallan 	0x11,	/* 17dB */
294d9f9caa0Smacallan 	0x01,	/* 18dB */
295d9f9caa0Smacallan };
296d9f9caa0Smacallan 
297a741bccaSmacallan #define SNAPPER_MIXER_GAIN_0DB		36
2985f55aaa2Smacallan const uint8_t snapper_mixer_gain[178][3] = {
2995f55aaa2Smacallan 	{ 0x7f, 0x17, 0xaf }, /* 18.0 dB */
3005f55aaa2Smacallan 	{ 0x77, 0xfb, 0xaa }, /* 17.5 dB */
3015f55aaa2Smacallan 	{ 0x71, 0x45, 0x75 }, /* 17.0 dB */
3025f55aaa2Smacallan 	{ 0x6a, 0xef, 0x5d }, /* 16.5 dB */
3035f55aaa2Smacallan 	{ 0x64, 0xf4, 0x03 }, /* 16.0 dB */
3045f55aaa2Smacallan 	{ 0x5f, 0x4e, 0x52 }, /* 15.5 dB */
3055f55aaa2Smacallan 	{ 0x59, 0xf9, 0x80 }, /* 15.0 dB */
3065f55aaa2Smacallan 	{ 0x54, 0xf1, 0x06 }, /* 14.5 dB */
3075f55aaa2Smacallan 	{ 0x50, 0x30, 0xa1 }, /* 14.0 dB */
3085f55aaa2Smacallan 	{ 0x4b, 0xb4, 0x46 }, /* 13.5 dB */
3095f55aaa2Smacallan 	{ 0x47, 0x78, 0x28 }, /* 13.0 dB */
3105f55aaa2Smacallan 	{ 0x43, 0x78, 0xb0 }, /* 12.5 dB */
3115f55aaa2Smacallan 	{ 0x3f, 0xb2, 0x78 }, /* 12.0 dB */
3125f55aaa2Smacallan 	{ 0x3c, 0x22, 0x4c }, /* 11.5 dB */
3135f55aaa2Smacallan 	{ 0x38, 0xc5, 0x28 }, /* 11.0 dB */
3145f55aaa2Smacallan 	{ 0x35, 0x98, 0x2f }, /* 10.5 dB */
3155f55aaa2Smacallan 	{ 0x32, 0x98, 0xb0 }, /* 10.0 dB */
3165f55aaa2Smacallan 	{ 0x2f, 0xc4, 0x20 }, /* 9.5 dB */
3175f55aaa2Smacallan 	{ 0x2d, 0x18, 0x18 }, /* 9.0 dB */
3185f55aaa2Smacallan 	{ 0x2a, 0x92, 0x54 }, /* 8.5 dB */
3195f55aaa2Smacallan 	{ 0x28, 0x30, 0xaf }, /* 8.0 dB */
3205f55aaa2Smacallan 	{ 0x25, 0xf1, 0x25 }, /* 7.5 dB */
3215f55aaa2Smacallan 	{ 0x23, 0xd1, 0xcd }, /* 7.0 dB */
3225f55aaa2Smacallan 	{ 0x21, 0xd0, 0xd9 }, /* 6.5 dB */
3235f55aaa2Smacallan 	{ 0x1f, 0xec, 0x98 }, /* 6.0 dB */
3245f55aaa2Smacallan 	{ 0x1e, 0x23, 0x6d }, /* 5.5 dB */
3255f55aaa2Smacallan 	{ 0x1c, 0x73, 0xd5 }, /* 5.0 dB */
3265f55aaa2Smacallan 	{ 0x1a, 0xdc, 0x61 }, /* 4.5 dB */
3275f55aaa2Smacallan 	{ 0x19, 0x5b, 0xb8 }, /* 4.0 dB */
3285f55aaa2Smacallan 	{ 0x17, 0xf0, 0x94 }, /* 3.5 dB */
3295f55aaa2Smacallan 	{ 0x16, 0x99, 0xc0 }, /* 3.0 dB */
3305f55aaa2Smacallan 	{ 0x15, 0x56, 0x1a }, /* 2.5 dB */
3315f55aaa2Smacallan 	{ 0x14, 0x24, 0x8e }, /* 2.0 dB */
3325f55aaa2Smacallan 	{ 0x13, 0x04, 0x1a }, /* 1.5 dB */
3335f55aaa2Smacallan 	{ 0x11, 0xf3, 0xc9 }, /* 1.0 dB */
3345f55aaa2Smacallan 	{ 0x10, 0xf2, 0xb4 }, /* 0.5 dB */
3355f55aaa2Smacallan 	{ 0x10, 0x00, 0x00 }, /* 0.0 dB */
3365f55aaa2Smacallan 	{ 0x0f, 0x1a, 0xdf }, /* -0.5 dB */
3375f55aaa2Smacallan 	{ 0x0e, 0x42, 0x90 }, /* -1.0 dB */
3385f55aaa2Smacallan 	{ 0x0d, 0x76, 0x5a }, /* -1.5 dB */
3395f55aaa2Smacallan 	{ 0x0c, 0xb5, 0x91 }, /* -2.0 dB */
3405f55aaa2Smacallan 	{ 0x0b, 0xff, 0x91 }, /* -2.5 dB */
3415f55aaa2Smacallan 	{ 0x0b, 0x53, 0xbe }, /* -3.0 dB */
3425f55aaa2Smacallan 	{ 0x0a, 0xb1, 0x89 }, /* -3.5 dB */
3435f55aaa2Smacallan 	{ 0x0a, 0x18, 0x66 }, /* -4.0 dB */
3445f55aaa2Smacallan 	{ 0x09, 0x87, 0xd5 }, /* -4.5 dB */
3455f55aaa2Smacallan 	{ 0x08, 0xff, 0x59 }, /* -5.0 dB */
3465f55aaa2Smacallan 	{ 0x08, 0x7e, 0x80 }, /* -5.5 dB */
3475f55aaa2Smacallan 	{ 0x08, 0x04, 0xdc }, /* -6.0 dB */
3485f55aaa2Smacallan 	{ 0x07, 0x92, 0x07 }, /* -6.5 dB */
3495f55aaa2Smacallan 	{ 0x07, 0x25, 0x9d }, /* -7.0 dB */
3505f55aaa2Smacallan 	{ 0x06, 0xbf, 0x44 }, /* -7.5 dB */
3515f55aaa2Smacallan 	{ 0x06, 0x5e, 0xa5 }, /* -8.0 dB */
3525f55aaa2Smacallan 	{ 0x06, 0x03, 0x6e }, /* -8.5 dB */
3535f55aaa2Smacallan 	{ 0x05, 0xad, 0x50 }, /* -9.0 dB */
3545f55aaa2Smacallan 	{ 0x05, 0x5c, 0x04 }, /* -9.5 dB */
3555f55aaa2Smacallan 	{ 0x05, 0x0f, 0x44 }, /* -10.0 dB */
3565f55aaa2Smacallan 	{ 0x04, 0xc6, 0xd0 }, /* -10.5 dB */
3575f55aaa2Smacallan 	{ 0x04, 0x82, 0x68 }, /* -11.0 dB */
3585f55aaa2Smacallan 	{ 0x04, 0x41, 0xd5 }, /* -11.5 dB */
3595f55aaa2Smacallan 	{ 0x04, 0x04, 0xde }, /* -12.0 dB */
3605f55aaa2Smacallan 	{ 0x03, 0xcb, 0x50 }, /* -12.5 dB */
3615f55aaa2Smacallan 	{ 0x03, 0x94, 0xfa }, /* -13.0 dB */
3625f55aaa2Smacallan 	{ 0x03, 0x61, 0xaf }, /* -13.5 dB */
3635f55aaa2Smacallan 	{ 0x03, 0x31, 0x42 }, /* -14.0 dB */
3645f55aaa2Smacallan 	{ 0x03, 0x03, 0x8a }, /* -14.5 dB */
3655f55aaa2Smacallan 	{ 0x02, 0xd8, 0x62 }, /* -15.0 dB */
3665f55aaa2Smacallan 	{ 0x02, 0xaf, 0xa3 }, /* -15.5 dB */
3675f55aaa2Smacallan 	{ 0x02, 0x89, 0x2c }, /* -16.0 dB */
3685f55aaa2Smacallan 	{ 0x02, 0x64, 0xdb }, /* -16.5 dB */
3695f55aaa2Smacallan 	{ 0x02, 0x42, 0x93 }, /* -17.0 dB */
3705f55aaa2Smacallan 	{ 0x02, 0x22, 0x35 }, /* -17.5 dB */
3715f55aaa2Smacallan 	{ 0x02, 0x03, 0xa7 }, /* -18.0 dB */
3725f55aaa2Smacallan 	{ 0x01, 0xe6, 0xcf }, /* -18.5 dB */
3735f55aaa2Smacallan 	{ 0x01, 0xcb, 0x94 }, /* -19.0 dB */
3745f55aaa2Smacallan 	{ 0x01, 0xb1, 0xde }, /* -19.5 dB */
3755f55aaa2Smacallan 	{ 0x01, 0x99, 0x99 }, /* -20.0 dB */
3765f55aaa2Smacallan 	{ 0x01, 0x82, 0xaf }, /* -20.5 dB */
3775f55aaa2Smacallan 	{ 0x01, 0x6d, 0x0e }, /* -21.0 dB */
3785f55aaa2Smacallan 	{ 0x01, 0x58, 0xa2 }, /* -21.5 dB */
3795f55aaa2Smacallan 	{ 0x01, 0x45, 0x5b }, /* -22.0 dB */
3805f55aaa2Smacallan 	{ 0x01, 0x33, 0x28 }, /* -22.5 dB */
3815f55aaa2Smacallan 	{ 0x01, 0x21, 0xf9 }, /* -23.0 dB */
3825f55aaa2Smacallan 	{ 0x01, 0x11, 0xc0 }, /* -23.5 dB */
3835f55aaa2Smacallan 	{ 0x01, 0x02, 0x70 }, /* -24.0 dB */
3845f55aaa2Smacallan 	{ 0x00, 0xf3, 0xfb }, /* -24.5 dB */
3855f55aaa2Smacallan 	{ 0x00, 0xe6, 0x55 }, /* -25.0 dB */
3865f55aaa2Smacallan 	{ 0x00, 0xd9, 0x73 }, /* -25.5 dB */
3875f55aaa2Smacallan 	{ 0x00, 0xcd, 0x49 }, /* -26.0 dB */
3885f55aaa2Smacallan 	{ 0x00, 0xc1, 0xcd }, /* -26.5 dB */
3895f55aaa2Smacallan 	{ 0x00, 0xb6, 0xf6 }, /* -27.0 dB */
3905f55aaa2Smacallan 	{ 0x00, 0xac, 0xba }, /* -27.5 dB */
3915f55aaa2Smacallan 	{ 0x00, 0xa3, 0x10 }, /* -28.0 dB */
3925f55aaa2Smacallan 	{ 0x00, 0x99, 0xf1 }, /* -28.5 dB */
3935f55aaa2Smacallan 	{ 0x00, 0x91, 0x54 }, /* -29.0 dB */
3945f55aaa2Smacallan 	{ 0x00, 0x89, 0x33 }, /* -29.5 dB */
3955f55aaa2Smacallan 	{ 0x00, 0x81, 0x86 }, /* -30.0 dB */
3965f55aaa2Smacallan 	{ 0x00, 0x7a, 0x48 }, /* -30.5 dB */
3975f55aaa2Smacallan 	{ 0x00, 0x73, 0x70 }, /* -31.0 dB */
3985f55aaa2Smacallan 	{ 0x00, 0x6c, 0xfb }, /* -31.5 dB */
3995f55aaa2Smacallan 	{ 0x00, 0x66, 0xe3 }, /* -32.0 dB */
4005f55aaa2Smacallan 	{ 0x00, 0x61, 0x21 }, /* -32.5 dB */
4015f55aaa2Smacallan 	{ 0x00, 0x5b, 0xb2 }, /* -33.0 dB */
4025f55aaa2Smacallan 	{ 0x00, 0x56, 0x91 }, /* -33.5 dB */
4035f55aaa2Smacallan 	{ 0x00, 0x51, 0xb9 }, /* -34.0 dB */
4045f55aaa2Smacallan 	{ 0x00, 0x4d, 0x27 }, /* -34.5 dB */
4055f55aaa2Smacallan 	{ 0x00, 0x48, 0xd6 }, /* -35.0 dB */
4065f55aaa2Smacallan 	{ 0x00, 0x44, 0xc3 }, /* -35.5 dB */
4075f55aaa2Smacallan 	{ 0x00, 0x40, 0xea }, /* -36.0 dB */
4085f55aaa2Smacallan 	{ 0x00, 0x3d, 0x49 }, /* -36.5 dB */
4095f55aaa2Smacallan 	{ 0x00, 0x39, 0xdb }, /* -37.0 dB */
4105f55aaa2Smacallan 	{ 0x00, 0x36, 0x9e }, /* -37.5 dB */
4115f55aaa2Smacallan 	{ 0x00, 0x33, 0x90 }, /* -38.0 dB */
4125f55aaa2Smacallan 	{ 0x00, 0x30, 0xae }, /* -38.5 dB */
4135f55aaa2Smacallan 	{ 0x00, 0x2d, 0xf5 }, /* -39.0 dB */
4145f55aaa2Smacallan 	{ 0x00, 0x2b, 0x63 }, /* -39.5 dB */
4155f55aaa2Smacallan 	{ 0x00, 0x28, 0xf5 }, /* -40.0 dB */
4165f55aaa2Smacallan 	{ 0x00, 0x26, 0xab }, /* -40.5 dB */
4175f55aaa2Smacallan 	{ 0x00, 0x24, 0x81 }, /* -41.0 dB */
4185f55aaa2Smacallan 	{ 0x00, 0x22, 0x76 }, /* -41.5 dB */
4195f55aaa2Smacallan 	{ 0x00, 0x20, 0x89 }, /* -42.0 dB */
4205f55aaa2Smacallan 	{ 0x00, 0x1e, 0xb7 }, /* -42.5 dB */
4215f55aaa2Smacallan 	{ 0x00, 0x1c, 0xff }, /* -43.0 dB */
4225f55aaa2Smacallan 	{ 0x00, 0x1b, 0x60 }, /* -43.5 dB */
4235f55aaa2Smacallan 	{ 0x00, 0x19, 0xd8 }, /* -44.0 dB */
4245f55aaa2Smacallan 	{ 0x00, 0x18, 0x65 }, /* -44.5 dB */
4255f55aaa2Smacallan 	{ 0x00, 0x17, 0x08 }, /* -45.0 dB */
4265f55aaa2Smacallan 	{ 0x00, 0x15, 0xbe }, /* -45.5 dB */
4275f55aaa2Smacallan 	{ 0x00, 0x14, 0x87 }, /* -46.0 dB */
4285f55aaa2Smacallan 	{ 0x00, 0x13, 0x61 }, /* -46.5 dB */
4295f55aaa2Smacallan 	{ 0x00, 0x12, 0x4b }, /* -47.0 dB */
4305f55aaa2Smacallan 	{ 0x00, 0x11, 0x45 }, /* -47.5 dB */
4315f55aaa2Smacallan 	{ 0x00, 0x10, 0x4e }, /* -48.0 dB */
4325f55aaa2Smacallan 	{ 0x00, 0x0f, 0x64 }, /* -48.5 dB */
4335f55aaa2Smacallan 	{ 0x00, 0x0e, 0x88 }, /* -49.0 dB */
4345f55aaa2Smacallan 	{ 0x00, 0x0d, 0xb8 }, /* -49.5 dB */
4355f55aaa2Smacallan 	{ 0x00, 0x0c, 0xf3 }, /* -50.0 dB */
4365f55aaa2Smacallan 	{ 0x00, 0x0c, 0x3a }, /* -50.5 dB */
4375f55aaa2Smacallan 	{ 0x00, 0x0b, 0x8b }, /* -51.0 dB */
4385f55aaa2Smacallan 	{ 0x00, 0x0a, 0xe5 }, /* -51.5 dB */
4395f55aaa2Smacallan 	{ 0x00, 0x0a, 0x49 }, /* -52.0 dB */
4405f55aaa2Smacallan 	{ 0x00, 0x09, 0xb6 }, /* -52.5 dB */
4415f55aaa2Smacallan 	{ 0x00, 0x09, 0x2b }, /* -53.0 dB */
4425f55aaa2Smacallan 	{ 0x00, 0x08, 0xa8 }, /* -53.5 dB */
4435f55aaa2Smacallan 	{ 0x00, 0x08, 0x2c }, /* -54.0 dB */
4445f55aaa2Smacallan 	{ 0x00, 0x07, 0xb7 }, /* -54.5 dB */
4455f55aaa2Smacallan 	{ 0x00, 0x07, 0x48 }, /* -55.0 dB */
4465f55aaa2Smacallan 	{ 0x00, 0x06, 0xe0 }, /* -55.5 dB */
4475f55aaa2Smacallan 	{ 0x00, 0x06, 0x7d }, /* -56.0 dB */
4485f55aaa2Smacallan 	{ 0x00, 0x06, 0x20 }, /* -56.5 dB */
4495f55aaa2Smacallan 	{ 0x00, 0x05, 0xc9 }, /* -57.0 dB */
4505f55aaa2Smacallan 	{ 0x00, 0x05, 0x76 }, /* -57.5 dB */
4515f55aaa2Smacallan 	{ 0x00, 0x05, 0x28 }, /* -58.0 dB */
4525f55aaa2Smacallan 	{ 0x00, 0x04, 0xde }, /* -58.5 dB */
4535f55aaa2Smacallan 	{ 0x00, 0x04, 0x98 }, /* -59.0 dB */
4545f55aaa2Smacallan 	{ 0x00, 0x04, 0x56 }, /* -59.5 dB */
4555f55aaa2Smacallan 	{ 0x00, 0x04, 0x18 }, /* -60.0 dB */
4565f55aaa2Smacallan 	{ 0x00, 0x03, 0xdd }, /* -60.5 dB */
4575f55aaa2Smacallan 	{ 0x00, 0x03, 0xa6 }, /* -61.0 dB */
4585f55aaa2Smacallan 	{ 0x00, 0x03, 0x72 }, /* -61.5 dB */
4595f55aaa2Smacallan 	{ 0x00, 0x03, 0x40 }, /* -62.0 dB */
4605f55aaa2Smacallan 	{ 0x00, 0x03, 0x12 }, /* -62.5 dB */
4615f55aaa2Smacallan 	{ 0x00, 0x02, 0xe6 }, /* -63.0 dB */
4625f55aaa2Smacallan 	{ 0x00, 0x02, 0xbc }, /* -63.5 dB */
4635f55aaa2Smacallan 	{ 0x00, 0x02, 0x95 }, /* -64.0 dB */
4645f55aaa2Smacallan 	{ 0x00, 0x02, 0x70 }, /* -64.5 dB */
4655f55aaa2Smacallan 	{ 0x00, 0x02, 0x4d }, /* -65.0 dB */
4665f55aaa2Smacallan 	{ 0x00, 0x02, 0x2c }, /* -65.5 dB */
4675f55aaa2Smacallan 	{ 0x00, 0x02, 0x0d }, /* -66.0 dB */
4685f55aaa2Smacallan 	{ 0x00, 0x01, 0xf0 }, /* -66.5 dB */
4695f55aaa2Smacallan 	{ 0x00, 0x01, 0xd4 }, /* -67.0 dB */
4705f55aaa2Smacallan 	{ 0x00, 0x01, 0xba }, /* -67.5 dB */
4715f55aaa2Smacallan 	{ 0x00, 0x01, 0xa1 }, /* -68.0 dB */
4725f55aaa2Smacallan 	{ 0x00, 0x01, 0x8a }, /* -68.5 dB */
4735f55aaa2Smacallan 	{ 0x00, 0x01, 0x74 }, /* -69.0 dB */
4745f55aaa2Smacallan 	{ 0x00, 0x01, 0x5f }, /* -69.5 dB */
4755f55aaa2Smacallan 	{ 0x00, 0x01, 0x4b }, /* -70.0 dB */
4765f55aaa2Smacallan 	{ 0x00, 0x00, 0x00 }  /* Mute */
4775f55aaa2Smacallan };
4785f55aaa2Smacallan 
479e622eac4Sisaki /* The HW actually supports precisions more than 16bit, but 16bit is enough. */
480e622eac4Sisaki static const struct audio_format snapper_formats[] = {
481e622eac4Sisaki 	{
482e622eac4Sisaki 		.mode		= AUMODE_PLAY | AUMODE_RECORD,
483e622eac4Sisaki 		.encoding	= AUDIO_ENCODING_SLINEAR_BE,
484e622eac4Sisaki 		.validbits	= 16,
485e622eac4Sisaki 		.precision	= 16,
486e622eac4Sisaki 		.channels	= 2,
487e622eac4Sisaki 		.channel_mask	= AUFMT_STEREO,
488e622eac4Sisaki 		.frequency_type	= 3,
489e622eac4Sisaki 		.frequency	= { 32000, 44100, 48000 },
490e622eac4Sisaki 	}
49123b5d914Skent };
492e622eac4Sisaki #define SNAPPER_NFORMATS	__arraycount(snapper_formats)
49323b5d914Skent 
494e622eac4Sisaki static const struct audio_format tumbler_formats[] = {
495e622eac4Sisaki 	{
496e622eac4Sisaki 		.mode		= AUMODE_PLAY | AUMODE_RECORD,
497e622eac4Sisaki 		.encoding	= AUDIO_ENCODING_SLINEAR_BE,
498e622eac4Sisaki 		.validbits	= 16,
499e622eac4Sisaki 		.precision	= 16,
500e622eac4Sisaki 		.channels	= 2,
501e622eac4Sisaki 		.channel_mask	= AUFMT_STEREO,
502e622eac4Sisaki 		.frequency_type	= 4,
503e622eac4Sisaki 		.frequency	= { 32000, 44100, 48000, 96000 },
504e622eac4Sisaki 	},
5058b9fcfffSaymeric };
506e622eac4Sisaki #define TUMBLER_NFORMATS	__arraycount(tumbler_formats)
5078b9fcfffSaymeric 
50899276969Smacallan /* OF hands us the codec in 16bit mode, run with it for now */
50999276969Smacallan static const struct audio_format onyx_formats[] = {
51099276969Smacallan 	{
51199276969Smacallan 		.mode		= AUMODE_PLAY | AUMODE_RECORD,
51299276969Smacallan 		.encoding	= AUDIO_ENCODING_SLINEAR_BE,
51399276969Smacallan 		.validbits	= 16,
51499276969Smacallan 		.precision	= 16,
51599276969Smacallan 		.channels	= 2,
51699276969Smacallan 		.channel_mask	= AUFMT_STEREO,
51799276969Smacallan 		.frequency_type	= 3,
51899276969Smacallan 		.frequency	= { 44100, 48000, 96000 },
51999276969Smacallan 	},
52099276969Smacallan };
52199276969Smacallan #define ONYX_NFORMATS	__arraycount(onyx_formats)
52299276969Smacallan 
523bea393bbSmacallan static bus_size_t amp_mute;
524202e08cdSmacallan static bus_size_t headphone_mute = 0;
525bea393bbSmacallan static bus_size_t audio_hw_reset;
526202e08cdSmacallan static bus_size_t headphone_detect = 0;
527202e08cdSmacallan static bus_size_t lineout_detect = 0;
528202e08cdSmacallan static bus_size_t lineout_mute= 0;
529275e3107Smacallan static bus_size_t owaddr = -1;
530202e08cdSmacallan static uint8_t headphone_detect_active = 0;
531202e08cdSmacallan static uint8_t lineout_detect_active = 0;
5329427a0c7Sgrant 
5339427a0c7Sgrant 
5349427a0c7Sgrant /* I2S registers */
5359427a0c7Sgrant #define I2S_INT		0x00
5369427a0c7Sgrant #define I2S_FORMAT	0x10
5379427a0c7Sgrant #define I2S_FRAMECOUNT	0x40
5389427a0c7Sgrant #define I2S_FRAMEMATCH	0x50
5399427a0c7Sgrant #define I2S_WORDSIZE	0x60
5409427a0c7Sgrant 
54135a2b63bSmacallan /* I2S_INT register definitions */
54235a2b63bSmacallan #define I2S_INT_CLKSTOPPEND 0x01000000  /* clock-stop interrupt pending */
54335a2b63bSmacallan 
54499276969Smacallan /* I2S_WORDSIZE register definitions */
54599276969Smacallan #define INPUT_STEREO            (2 << 24)
54699276969Smacallan #define INPUT_MONO              (1 << 24)
54799276969Smacallan #define INPUT_16BIT             (0 << 16)
54899276969Smacallan #define INPUT_24BIT             (3 << 16)
54999276969Smacallan #define OUTPUT_STEREO           (2 << 8)
55099276969Smacallan #define OUTPUT_MONO             (1 << 8)
55199276969Smacallan #define OUTPUT_16BIT            (0 << 0)
55299276969Smacallan #define OUTPUT_24BIT            (3 << 0)
55399276969Smacallan 
55435a2b63bSmacallan /* FCR(0x3c) bits */
555a741bccaSmacallan #define KEYLARGO_FCR1   0x3c
55635a2b63bSmacallan #define  I2S0CLKEN      0x1000
55735a2b63bSmacallan #define  I2S0EN         0x2000
55835a2b63bSmacallan #define  I2S1CLKEN      0x080000
55935a2b63bSmacallan #define  I2S1EN         0x100000
56035a2b63bSmacallan #define FCR3C_BITMASK "\020\25I2S1EN\24I2S1CLKEN\16I2S0EN\15I2S0CLKEN"
56135a2b63bSmacallan 
562a741bccaSmacallan /* TAS3004/TAS3001 registers */
5639427a0c7Sgrant #define DEQ_MCR1	0x01	/* Main control register 1 (1byte) */
5648b9fcfffSaymeric #define DEQ_DRC		0x02	/* Dynamic range compression (6bytes?)
565a741bccaSmacallan                             	   2 bytes (reserved) on the TAS 3001 */
5669427a0c7Sgrant #define DEQ_VOLUME	0x04	/* Volume (6bytes) */
5679427a0c7Sgrant #define DEQ_TREBLE	0x05	/* Treble control (1byte) */
5689427a0c7Sgrant #define DEQ_BASS	0x06	/* Bass control (1byte) */
5698b9fcfffSaymeric #define DEQ_MIXER_L	0x07	/* Mixer left gain (9bytes; 3 on TAS3001) */
5708b9fcfffSaymeric #define DEQ_MIXER_R	0x08	/* Mixer right gain (9bytes; 3 on TAS3001) */
5719427a0c7Sgrant #define DEQ_LB0		0x0a	/* Left biquad 0 (15bytes) */
5729427a0c7Sgrant #define DEQ_LB1		0x0b	/* Left biquad 1 (15bytes) */
5739427a0c7Sgrant #define DEQ_LB2		0x0c	/* Left biquad 2 (15bytes) */
5749427a0c7Sgrant #define DEQ_LB3		0x0d	/* Left biquad 3 (15bytes) */
5759427a0c7Sgrant #define DEQ_LB4		0x0e	/* Left biquad 4 (15bytes) */
5769427a0c7Sgrant #define DEQ_LB5		0x0f	/* Left biquad 5 (15bytes) */
5779427a0c7Sgrant #define DEQ_LB6		0x10	/* Left biquad 6 (15bytes) */
5789427a0c7Sgrant #define DEQ_RB0		0x13	/* Right biquad 0 (15bytes) */
5799427a0c7Sgrant #define DEQ_RB1		0x14	/* Right biquad 1 (15bytes) */
5809427a0c7Sgrant #define DEQ_RB2		0x15	/* Right biquad 2 (15bytes) */
5819427a0c7Sgrant #define DEQ_RB3		0x16	/* Right biquad 3 (15bytes) */
5829427a0c7Sgrant #define DEQ_RB4		0x17	/* Right biquad 4 (15bytes) */
5839427a0c7Sgrant #define DEQ_RB5		0x18	/* Right biquad 5 (15bytes) */
5849427a0c7Sgrant #define DEQ_RB6		0x19	/* Right biquad 6 (15bytes) */
5859427a0c7Sgrant #define DEQ_LLB		0x21	/* Left loudness biquad (15bytes) */
5869427a0c7Sgrant #define DEQ_RLB		0x22	/* Right loudness biquad (15bytes) */
5879427a0c7Sgrant #define DEQ_LLB_GAIN	0x23	/* Left loudness biquad gain (3bytes) */
5889427a0c7Sgrant #define DEQ_RLB_GAIN	0x24	/* Right loudness biquad gain (3bytes) */
589a741bccaSmacallan #define DEQ_ACR		0x40	/* [TAS3004] Analog control register (1byte) */
590a741bccaSmacallan #define DEQ_MCR2	0x43	/* [TAS3004] Main control register 2 (1byte) */
5919427a0c7Sgrant #define DEQ_MCR1_FL	0x80	/* Fast load */
5929427a0c7Sgrant #define DEQ_MCR1_SC	0x40	/* SCLK frequency */
5939427a0c7Sgrant #define  DEQ_MCR1_SC_32	0x00	/*  32fs */
5949427a0c7Sgrant #define  DEQ_MCR1_SC_64	0x40	/*  64fs */
5959427a0c7Sgrant #define DEQ_MCR1_SM	0x30	/* Output serial port mode */
5969427a0c7Sgrant #define  DEQ_MCR1_SM_L	0x00	/*  Left justified */
5979427a0c7Sgrant #define  DEQ_MCR1_SM_R	0x10	/*  Right justified */
5989427a0c7Sgrant #define  DEQ_MCR1_SM_I2S 0x20	/*  I2S */
599a741bccaSmacallan #define DEQ_MCR1_ISM	0x0c	/* [TAS3001] Input serial port mode */
6008b9fcfffSaymeric #define  DEQ_MCR1_ISM_L	0x00	/*           Left justified */
6018b9fcfffSaymeric #define  DEQ_MCR1_ISM_R	0x04	/*           Right justified */
6028b9fcfffSaymeric #define  DEQ_MCR1_ISM_I2S 0x08	/*           I2S */
6039427a0c7Sgrant #define DEQ_MCR1_W	0x03	/* Serial port word length */
6049427a0c7Sgrant #define  DEQ_MCR1_W_16	0x00	/*  16 bit */
6059427a0c7Sgrant #define  DEQ_MCR1_W_18	0x01	/*  18 bit */
6069427a0c7Sgrant #define  DEQ_MCR1_W_20	0x02	/*  20 bit */
607210f669bSmacallan #define  DEQ_MCR1_W_24	0x03	/*  24 bit */
6089427a0c7Sgrant 
6099427a0c7Sgrant #define DEQ_MCR2_DL	0x80	/* Download */
6109427a0c7Sgrant #define DEQ_MCR2_AP	0x02	/* All pass mode */
6119427a0c7Sgrant 
6129427a0c7Sgrant #define DEQ_ACR_ADM	0x80	/* ADC output mode */
6139427a0c7Sgrant #define DEQ_ACR_LRB	0x40	/* Select B input */
6149427a0c7Sgrant #define DEQ_ACR_DM	0x0c	/* De-emphasis control */
6159427a0c7Sgrant #define  DEQ_ACR_DM_OFF	0x00	/*  off */
6169427a0c7Sgrant #define  DEQ_ACR_DM_48	0x04	/*  fs = 48kHz */
6179427a0c7Sgrant #define  DEQ_ACR_DM_44	0x08	/*  fs = 44.1kHz */
6189427a0c7Sgrant #define DEQ_ACR_INP	0x02	/* Analog input select */
6199427a0c7Sgrant #define  DEQ_ACR_INP_A	0x00	/*  A */
6209427a0c7Sgrant #define  DEQ_ACR_INP_B	0x02	/*  B */
6219427a0c7Sgrant #define DEQ_ACR_APD	0x01	/* Analog power down */
6229427a0c7Sgrant 
6239427a0c7Sgrant struct tas3004_reg {
6249427a0c7Sgrant 	u_char MCR1[1];
6259427a0c7Sgrant 	u_char DRC[6];
6269427a0c7Sgrant 	u_char VOLUME[6];
6279427a0c7Sgrant 	u_char TREBLE[1];
6289427a0c7Sgrant 	u_char BASS[1];
6299427a0c7Sgrant 	u_char MIXER_L[9];
6309427a0c7Sgrant 	u_char MIXER_R[9];
6319427a0c7Sgrant 	u_char LB0[15];
6329427a0c7Sgrant 	u_char LB1[15];
6339427a0c7Sgrant 	u_char LB2[15];
6349427a0c7Sgrant 	u_char LB3[15];
6359427a0c7Sgrant 	u_char LB4[15];
6369427a0c7Sgrant 	u_char LB5[15];
6379427a0c7Sgrant 	u_char LB6[15];
6389427a0c7Sgrant 	u_char RB0[15];
6399427a0c7Sgrant 	u_char RB1[15];
6409427a0c7Sgrant 	u_char RB2[15];
6419427a0c7Sgrant 	u_char RB3[15];
6429427a0c7Sgrant 	u_char RB4[15];
6439427a0c7Sgrant 	u_char RB5[15];
6449427a0c7Sgrant 	u_char RB6[15];
6459427a0c7Sgrant 	u_char LLB[15];
6469427a0c7Sgrant 	u_char RLB[15];
6479427a0c7Sgrant 	u_char LLB_GAIN[3];
6489427a0c7Sgrant 	u_char RLB_GAIN[3];
6499427a0c7Sgrant 	u_char ACR[1];
6509427a0c7Sgrant 	u_char MCR2[1];
6519427a0c7Sgrant };
6529427a0c7Sgrant 
6539427a0c7Sgrant #define GPIO_OUTSEL	0xf0	/* Output select */
6549427a0c7Sgrant 		/*	0x00	GPIO bit0 is output
6559427a0c7Sgrant 			0x10	media-bay power
6569427a0c7Sgrant 			0x20	reserved
6579427a0c7Sgrant 			0x30	MPIC */
6589427a0c7Sgrant 
6599427a0c7Sgrant #define GPIO_ALTOE	0x08	/* Alternate output enable */
6609427a0c7Sgrant 		/*	0x00	Use DDR
6619427a0c7Sgrant 			0x08	Use output select */
6629427a0c7Sgrant 
6639427a0c7Sgrant #define GPIO_DDR	0x04	/* Data direction */
6649427a0c7Sgrant #define GPIO_DDR_OUTPUT	0x04	/* Output */
6659427a0c7Sgrant #define GPIO_DDR_INPUT	0x00	/* Input */
6669427a0c7Sgrant 
6679427a0c7Sgrant #define GPIO_LEVEL	0x02	/* Pin level (RO) */
6689427a0c7Sgrant 
6699427a0c7Sgrant #define	GPIO_DATA	0x01	/* Data */
6709427a0c7Sgrant 
671e3693941Smacallan static int
snapper_match(device_t parent,struct cfdata * match,void * aux)6727d61890bSmacallan snapper_match(device_t parent, struct cfdata *match, void *aux)
6739427a0c7Sgrant {
67493293b9eSkent 	struct confargs *ca;
6755f55aaa2Smacallan 	int soundbus, soundchip, soundcodec;
6769427a0c7Sgrant 	char compat[32];
6779427a0c7Sgrant 
67893293b9eSkent 	ca = aux;
6799427a0c7Sgrant 	if (strcmp(ca->ca_name, "i2s") != 0)
6809427a0c7Sgrant 		return 0;
6819427a0c7Sgrant 
6829427a0c7Sgrant 	if ((soundbus = OF_child(ca->ca_node)) == 0 ||
6839427a0c7Sgrant 	    (soundchip = OF_child(soundbus)) == 0)
6849427a0c7Sgrant 		return 0;
6859427a0c7Sgrant 
686c363a9cbScegger 	memset(compat, 0, sizeof compat);
6879427a0c7Sgrant 	OF_getprop(soundchip, "compatible", compat, sizeof compat);
6889427a0c7Sgrant 
6895f55aaa2Smacallan 	if (strcmp(compat, "snapper") == 0)
6909427a0c7Sgrant 		return 1;
6915f55aaa2Smacallan 
6928b9fcfffSaymeric 	if (strcmp(compat, "tumbler") == 0)
6938b9fcfffSaymeric 		return 1;
6948b9fcfffSaymeric 
695c38a78caSjmcneill 	if (strcmp(compat, "AOAKeylargo") == 0)
696c38a78caSjmcneill 		return 1;
697c38a78caSjmcneill 
698f972216eSjmcneill 	if (strcmp(compat, "AOAK2") == 0)
699f972216eSjmcneill 		return 1;
700f972216eSjmcneill 
701202e08cdSmacallan 	if (strcmp(compat, "AOAShasta") == 0)
702202e08cdSmacallan 		return 1;
703202e08cdSmacallan 
70499276969Smacallan 	if (strcmp(compat, "AOAbase") == 0)
70599276969Smacallan 		return 1;
70699276969Smacallan 
7075f55aaa2Smacallan 	if (OF_getprop(soundchip, "platform-tas-codec-ref",
7085f55aaa2Smacallan 	    &soundcodec, sizeof soundcodec) == sizeof soundcodec)
7095f55aaa2Smacallan 		return 1;
7105f55aaa2Smacallan 
7115f55aaa2Smacallan 	return 0;
7129427a0c7Sgrant }
7139427a0c7Sgrant 
714e3693941Smacallan static void
snapper_attach(device_t parent,device_t self,void * aux)7157d61890bSmacallan snapper_attach(device_t parent, device_t self, void *aux)
7169427a0c7Sgrant {
71793293b9eSkent 	struct snapper_softc *sc;
71893293b9eSkent 	struct confargs *ca;
719f6e33f02Smrg 	int cirq, oirq, iirq, /*cirq_type,*/ oirq_type, iirq_type, soundbus;
7204f6a6a0cSphx 	uint32_t intr[6], reg[6];
721cfe2093dSrin 	char compat[32], intr_xname[INTRDEVNAMEBUF];
7229427a0c7Sgrant 
723a741bccaSmacallan 	sc = device_private(self);
7247d61890bSmacallan 	sc->sc_dev = self;
7257d61890bSmacallan 
72693293b9eSkent 	ca = aux;
727d9b67662Sbriggs 
7288b9fcfffSaymeric 	soundbus = OF_child(ca->ca_node);
729c363a9cbScegger 	memset(compat, 0, sizeof compat);
7308b9fcfffSaymeric 	OF_getprop(OF_child(soundbus), "compatible", compat, sizeof compat);
7318b9fcfffSaymeric 
73299276969Smacallan 	sc->sc_mode = SNAPPER_IS_TAS3004;
73399276969Smacallan 
7348b9fcfffSaymeric 	if (strcmp(compat, "tumbler") == 0)
735a741bccaSmacallan 		sc->sc_mode = SNAPPER_IS_TAS3001;
736e622eac4Sisaki 	sc->sc_swvol_l = 255;
737e622eac4Sisaki 	sc->sc_swvol_r = 255;
738e622eac4Sisaki 	sc->sc_vol_l = 128;
739e622eac4Sisaki 	sc->sc_vol_r = 128;
740e622eac4Sisaki 	sc->sc_rval = 0;
7418b9fcfffSaymeric 
742a741bccaSmacallan 	sc->sc_odmacmd = dbdma_alloc((SNAPPER_MAXPAGES + 4) *
743c9a4f92cSmacallan 				     sizeof(struct dbdma_command), NULL);
744a741bccaSmacallan 	sc->sc_idmacmd = dbdma_alloc((SNAPPER_MAXPAGES + 4) *
745c9a4f92cSmacallan 				     sizeof(struct dbdma_command), NULL);
746d9b67662Sbriggs 
747a741bccaSmacallan 	sc->sc_baseaddr = ca->ca_baseaddr;
748bea393bbSmacallan 
7494f6a6a0cSphx 	OF_getprop(soundbus, "reg", reg, sizeof reg);
7501a0d0c16Smacallan 	/* deal with messed up properties on PowerMac7,3 and friends */
751b285859fSmacallan 	if (reg[0] == 0) {
752b285859fSmacallan 		reg[0] += ca->ca_reg[0];
753b285859fSmacallan 		reg[2] += ca->ca_reg[2];
754b285859fSmacallan 		reg[4] += ca->ca_reg[2];
755b285859fSmacallan 	}
7564f6a6a0cSphx 	reg[0] += ca->ca_baseaddr;
7574f6a6a0cSphx 	reg[2] += ca->ca_baseaddr;
7584f6a6a0cSphx 	reg[4] += ca->ca_baseaddr;
7599427a0c7Sgrant 
7609427a0c7Sgrant 	sc->sc_node = ca->ca_node;
761d974db0aSgarbled 	sc->sc_tag = ca->ca_tag;
7624f6a6a0cSphx 
763b285859fSmacallan #ifdef SNAPPER_DEBUG
764b285859fSmacallan 	{
765b285859fSmacallan 		int i;
766b285859fSmacallan 		printf("\n");
767b285859fSmacallan 		for (i = 0; i < 6; i++) {
768b285859fSmacallan 			printf(" %08x", reg[i]);
769b285859fSmacallan 		}
770b285859fSmacallan 		printf("\n");
771b285859fSmacallan 	}
772b285859fSmacallan #endif
773b285859fSmacallan 
7744f6a6a0cSphx 	bus_space_map(sc->sc_tag, reg[0], reg[1], 0, &sc->sc_bsh);
775b25dbf73Smacallan 	obio_space_map(reg[2], reg[3], &sc->sc_odmah);
776b25dbf73Smacallan 	obio_space_map(reg[4], reg[5], &sc->sc_idmah);
7774f6a6a0cSphx 
778d974db0aSgarbled 	sc->sc_odma = bus_space_vaddr(sc->sc_tag, sc->sc_odmah);
779d974db0aSgarbled 	sc->sc_idma = bus_space_vaddr(sc->sc_tag, sc->sc_idmah);
7809427a0c7Sgrant 
781b285859fSmacallan 	DPRINTF("reg %08x odma %08x\n", (uint32_t)sc->sc_bsh, (uint32_t)sc->sc_odmah);
782b285859fSmacallan 
7839427a0c7Sgrant 	OF_getprop(soundbus, "interrupts", intr, sizeof intr);
7849427a0c7Sgrant 	cirq = intr[0];
7859427a0c7Sgrant 	oirq = intr[2];
7869427a0c7Sgrant 	iirq = intr[4];
787f6e33f02Smrg 	/* cirq_type = intr[1] ? IST_LEVEL : IST_EDGE; */
78899276969Smacallan 	oirq_type = (intr[3] & 1) ? IST_LEVEL : IST_EDGE;
78999276969Smacallan 	iirq_type = (intr[5] & 1) ? IST_LEVEL : IST_EDGE;
7909427a0c7Sgrant 
7918de08fa9Smrg 	/* intr_establish(cirq, cirq_type, IPL_AUDIO, snapper_intr, sc); */
792cfe2093dSrin 
793cfe2093dSrin 	snprintf(intr_xname, sizeof(intr_xname), "%s out", device_xname(self));
794cfe2093dSrin 	intr_establish_xname(oirq, oirq_type, IPL_AUDIO, snapper_intr, sc,
795cfe2093dSrin 	    intr_xname);
796cfe2093dSrin 
797cfe2093dSrin 	snprintf(intr_xname, sizeof(intr_xname), "%s in", device_xname(self));
798cfe2093dSrin 	intr_establish_xname(iirq, iirq_type, IPL_AUDIO, snapper_intr, sc,
799cfe2093dSrin 	    intr_xname);
8009427a0c7Sgrant 
8018b9fcfffSaymeric 	aprint_normal(": irq %d,%d,%d\n", cirq, oirq, iirq);
8029427a0c7Sgrant 
8038a962f23Sjmcneill 	mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE);
8048de08fa9Smrg 	mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_AUDIO);
8058a962f23Sjmcneill 
806f147f68fSmacallan 	/* PMF event handler */
807210f669bSmacallan 	pmf_device_register(sc->sc_dev, NULL, NULL);
808210f669bSmacallan 
8098b9fcfffSaymeric 	config_defer(self, snapper_defer);
8109427a0c7Sgrant }
8119427a0c7Sgrant 
812e3693941Smacallan static void
snapper_defer(device_t dev)8137d61890bSmacallan snapper_defer(device_t dev)
8149427a0c7Sgrant {
81593293b9eSkent 	struct snapper_softc *sc;
8167d61890bSmacallan 	device_t dv;
8177507fa68Sdyoung 	deviter_t di;
818d9f9caa0Smacallan 	struct deq_softc *deq;
81999276969Smacallan 	char prop[64], next[64], codec[64], *cref;
82099276969Smacallan 	int codec_node, soundbus, sound, ok, deqnode = 0;
8219427a0c7Sgrant 
822a741bccaSmacallan 	sc = device_private(dev);
82399276969Smacallan 
82499276969Smacallan 	/* look for platform-*-codec-ref node */
82599276969Smacallan 
82699276969Smacallan 	/*
82799276969Smacallan 	 * XXX
82899276969Smacallan 	 * there can be more than one i2sbus, the one we want just so happens
82999276969Smacallan 	 * to be the first we see
83099276969Smacallan 	 */
83199276969Smacallan 	soundbus = OF_child(sc->sc_node);
83299276969Smacallan 	sound = OF_child(soundbus);
83399276969Smacallan 	ok = OF_nextprop(sound, NULL, next);
83499276969Smacallan 	codec_node = 0;
83599276969Smacallan 	while (ok && (codec_node == 0)) {
83699276969Smacallan 		DPRINTF("prop %d %s\n", ok, next);
83799276969Smacallan 		strncpy(prop, next, 64);
83899276969Smacallan 		if ((cref = strstr(next, "-codec-ref")) != NULL) {
83999276969Smacallan 			OF_getprop(sound, next, &codec_node, 4);
84099276969Smacallan 			if (codec_node != 0) {
84199276969Smacallan 				OF_getprop(codec_node, "compatible", codec, 64);
84299276969Smacallan 				DPRINTF("%08x %s\n", codec_node, codec);
84399276969Smacallan 			}
84499276969Smacallan 		}
84599276969Smacallan 		ok = OF_nextprop(sound, prop, next);
84699276969Smacallan 	}
84799276969Smacallan 
8487507fa68Sdyoung 	for (dv = deviter_first(&di, DEVITER_F_ROOT_FIRST);
8497507fa68Sdyoung 	     dv != NULL;
8507507fa68Sdyoung 	     dv = deviter_next(&di)) {
851a741bccaSmacallan 		if (device_is_a(dv, "deq")) {
852a741bccaSmacallan 			deq = device_private(dv);
85399276969Smacallan 			if (codec_node != 0) {
85499276969Smacallan 				if (codec_node != deq->sc_node)
85599276969Smacallan 					continue;
85699276969Smacallan 			}
857d9f9caa0Smacallan 			sc->sc_i2c = deq->sc_i2c;
858d9f9caa0Smacallan 			sc->sc_deqaddr = deq->sc_address;
85999276969Smacallan 			deqnode = deq->sc_node;
860d9f9caa0Smacallan 		}
861a741bccaSmacallan 	}
8627507fa68Sdyoung 	deviter_release(&di);
863d9f9caa0Smacallan 
86499276969Smacallan 	DPRINTF("deqnode: %08x\n", deqnode);
86599276969Smacallan 
866f972216eSjmcneill 	/* If we don't find a codec, it's not the end of the world;
867f972216eSjmcneill 	 * we can control the volume in software in this case.
868f972216eSjmcneill 	 */
86999276969Smacallan 	if (sc->sc_i2c == NULL) {
870a741bccaSmacallan 		sc->sc_mode = SNAPPER_SWVOL;
87199276969Smacallan 	} else if (deqnode != 0) {
87299276969Smacallan 		int ret;
87399276969Smacallan 		codec[0] = 0;
87499276969Smacallan 		ret = OF_getprop(deqnode, "compatible", codec, 64);
875a741bccaSmacallan 
87699276969Smacallan 		DPRINTF("codec <%s> %d\n", codec, ret);
87799276969Smacallan 
87899276969Smacallan 		if (codec[0] == 0) {
87999276969Smacallan 			if (sc->sc_deqaddr == 0x34) {
88099276969Smacallan 				sc->sc_mode = SNAPPER_IS_TAS3001;
881202e08cdSmacallan 			} else {
882202e08cdSmacallan 				int root = OF_finddevice("/");
883202e08cdSmacallan 				char model[32];
88499276969Smacallan 				sc->sc_mode = SNAPPER_IS_TAS3004;
885202e08cdSmacallan 				if (OF_getprop(root, "model", model, 32) > 0) {
886202e08cdSmacallan 					printf("model %s\n", model);
887202e08cdSmacallan 					if (strcmp(model, "PowerMac8,1") == 0) {
888202e08cdSmacallan 						sc->sc_mode = SNAPPER_IS_PCM3052;
889202e08cdSmacallan 					}
890202e08cdSmacallan 				}
891202e08cdSmacallan 			}
89299276969Smacallan 		} else if (strcmp(codec, "tas3004") == 0) {
89399276969Smacallan 			sc->sc_mode = SNAPPER_IS_TAS3004;
89499276969Smacallan 		} else if (strcmp(codec, "pcm3052") == 0) {
89599276969Smacallan 			sc->sc_mode = SNAPPER_IS_PCM3052;
89699276969Smacallan 		} else if (strcmp(codec, "cs8416") == 0) {
89799276969Smacallan 			sc->sc_mode = SNAPPER_IS_CS8416;
89899276969Smacallan 		}
89999276969Smacallan 	}
90099276969Smacallan 	DPRINTF("mode %d\n", sc->sc_mode);
901a741bccaSmacallan 	switch (sc->sc_mode) {
902a741bccaSmacallan 	case SNAPPER_SWVOL:
903a741bccaSmacallan 		aprint_verbose("%s: software codec\n", device_xname(dev));
904a741bccaSmacallan 		break;
905a741bccaSmacallan 	case SNAPPER_IS_TAS3001:
906a741bccaSmacallan 		aprint_verbose("%s: codec: TAS3001\n", device_xname(dev));
907a741bccaSmacallan 		break;
90899276969Smacallan 	case SNAPPER_IS_TAS3004:
909a741bccaSmacallan 		aprint_verbose("%s: codec: TAS3004\n", device_xname(dev));
910a741bccaSmacallan 		break;
91199276969Smacallan 	case SNAPPER_IS_PCM3052:
91299276969Smacallan 		aprint_verbose("%s: codec: PCM3052 / ONYX\n", device_xname(dev));
91399276969Smacallan 		break;
91499276969Smacallan 	default:
91599276969Smacallan 		aprint_error_dev(sc->sc_dev, "unsupported codec\n");
91699276969Smacallan 		sc->sc_mode = SNAPPER_SWVOL;
917a741bccaSmacallan 	}
9189427a0c7Sgrant 
9199427a0c7Sgrant 	snapper_init(sc, sc->sc_node);
9209b65f16dSsevan 
9219b65f16dSsevan 	audio_attach_mi(&snapper_hw_if, sc, sc->sc_dev);
9229427a0c7Sgrant }
9239427a0c7Sgrant 
924e3693941Smacallan static int
snapper_intr(void * v)92593293b9eSkent snapper_intr(void *v)
9269427a0c7Sgrant {
92793293b9eSkent 	struct snapper_softc *sc;
92893293b9eSkent 	struct dbdma_command *cmd;
92993293b9eSkent 	int count;
9309427a0c7Sgrant 	int status;
9319427a0c7Sgrant 
93293293b9eSkent 	sc = v;
9338a962f23Sjmcneill 	mutex_spin_enter(&sc->sc_intr_lock);
93493293b9eSkent 	cmd = sc->sc_odmacmd;
93593293b9eSkent 	count = sc->sc_opages;
9369427a0c7Sgrant 	/* Fill used buffer(s). */
9379427a0c7Sgrant 	while (count-- > 0) {
938d974db0aSgarbled 		if ((in16rb(&cmd->d_command) & 0x30) == 0x30) {
939d974db0aSgarbled 			status = in16rb(&cmd->d_status);
9409427a0c7Sgrant 			cmd->d_status = 0;
9419427a0c7Sgrant 			if (status)	/* status == 0x8400 */
9429427a0c7Sgrant 				if (sc->sc_ointr)
9439427a0c7Sgrant 					(*sc->sc_ointr)(sc->sc_oarg);
9449427a0c7Sgrant 		}
9459427a0c7Sgrant 		cmd++;
9469427a0c7Sgrant 	}
9479427a0c7Sgrant 
9485f55aaa2Smacallan 	cmd = sc->sc_idmacmd;
9495f55aaa2Smacallan 	count = sc->sc_ipages;
9505f55aaa2Smacallan 	while (count-- > 0) {
951d974db0aSgarbled 		if ((in16rb(&cmd->d_command) & 0x30) == 0x30) {
952d974db0aSgarbled 			status = in16rb(&cmd->d_status);
9535f55aaa2Smacallan 			cmd->d_status = 0;
9545f55aaa2Smacallan 			if (status)	/* status == 0x8400 */
9555f55aaa2Smacallan 				if (sc->sc_iintr)
9565f55aaa2Smacallan 					(*sc->sc_iintr)(sc->sc_iarg);
9575f55aaa2Smacallan 		}
9585f55aaa2Smacallan 		cmd++;
9595f55aaa2Smacallan 	}
9608a962f23Sjmcneill 	mutex_spin_exit(&sc->sc_intr_lock);
9615f55aaa2Smacallan 
9629427a0c7Sgrant 	return 1;
9639427a0c7Sgrant }
9649427a0c7Sgrant 
9659427a0c7Sgrant 
966e3693941Smacallan static int
snapper_query_format(void * h,audio_format_query_t * afp)967e622eac4Sisaki snapper_query_format(void *h, audio_format_query_t *afp)
9689427a0c7Sgrant {
9698b9fcfffSaymeric 	struct snapper_softc *sc = h;
9708b9fcfffSaymeric 
97199276969Smacallan 	switch (sc->sc_mode) {
97299276969Smacallan 		case SNAPPER_IS_TAS3001:
97399276969Smacallan 			return audio_query_format(tumbler_formats,
97499276969Smacallan 			    TUMBLER_NFORMATS, afp);
97599276969Smacallan 		case SNAPPER_SWVOL:
97699276969Smacallan 		case SNAPPER_IS_TAS3004:
97799276969Smacallan 			return audio_query_format(snapper_formats,
97899276969Smacallan 			    SNAPPER_NFORMATS, afp);
97999276969Smacallan 		case SNAPPER_IS_PCM3052:
98099276969Smacallan 			return audio_query_format(onyx_formats,
98199276969Smacallan 			    ONYX_NFORMATS, afp);
982e622eac4Sisaki 	}
98399276969Smacallan 	return -1;
9849427a0c7Sgrant }
9859427a0c7Sgrant 
986e3693941Smacallan static int
snapper_set_format(void * h,int setmode,const audio_params_t * play,const audio_params_t * rec,audio_filter_reg_t * pfil,audio_filter_reg_t * rfil)987e622eac4Sisaki snapper_set_format(void *h, int setmode,
988e622eac4Sisaki 		   const audio_params_t *play, const audio_params_t *rec,
989e622eac4Sisaki 		   audio_filter_reg_t *pfil, audio_filter_reg_t *rfil)
9909427a0c7Sgrant {
99193293b9eSkent 	struct snapper_softc *sc;
9929427a0c7Sgrant 
99393293b9eSkent 	sc = h;
9949427a0c7Sgrant 
995e622eac4Sisaki 	/* *play and *rec are the identical because !AUDIO_PROP_INDEPENDENT. */
996e622eac4Sisaki 
997e622eac4Sisaki 	if (sc->sc_mode == SNAPPER_SWVOL) {
998e622eac4Sisaki 		pfil->codec = snapper_volume;
999e622eac4Sisaki 		pfil->context = sc;
1000e622eac4Sisaki 		rfil->codec = snapper_volume;
1001e622eac4Sisaki 		rfil->context = sc;
1002e622eac4Sisaki 	} else if (sc->sc_mode == 0 && play->channels == 2) {
1003e622eac4Sisaki 		/* Fix phase problems on TAS3004.  */
1004e622eac4Sisaki 		pfil->codec = snapper_fixphase;
1005e622eac4Sisaki 		pfil->context = sc;
1006e622eac4Sisaki 		rfil->codec = snapper_fixphase;
1007e622eac4Sisaki 		rfil->context = sc;
10089427a0c7Sgrant 	}
10099427a0c7Sgrant 
1010e622eac4Sisaki 	/* Set the speed. */
1011e622eac4Sisaki 	sc->sc_rate = play->sample_rate;
1012e622eac4Sisaki 	sc->sc_bitspersample = play->precision;
10139427a0c7Sgrant 	return 0;
10149427a0c7Sgrant }
10159427a0c7Sgrant 
1016e3693941Smacallan static int
snapper_commit_settings(void * h)1017abea73c7Sjmcneill snapper_commit_settings(void *h)
1018abea73c7Sjmcneill {
1019abea73c7Sjmcneill 	struct snapper_softc *sc;
1020abea73c7Sjmcneill 
1021abea73c7Sjmcneill 	DPRINTF("commit_settings\n");
1022abea73c7Sjmcneill 	sc = h;
1023abea73c7Sjmcneill 
1024abea73c7Sjmcneill 	return snapper_set_rate(sc);
1025abea73c7Sjmcneill }
1026abea73c7Sjmcneill 
1027abea73c7Sjmcneill static int
snapper_round_blocksize(void * h,int size,int mode,const audio_params_t * param)102893293b9eSkent snapper_round_blocksize(void *h, int size, int mode,
102993293b9eSkent 			const audio_params_t *param)
10309427a0c7Sgrant {
103193293b9eSkent 
103299276969Smacallan 	if (size < (3 * NBPG))
103399276969Smacallan 		size = (3 * NBPG);
10349427a0c7Sgrant 	return size & ~PGOFSET;
10359427a0c7Sgrant }
10369427a0c7Sgrant 
1037e3693941Smacallan static int
snapper_halt_output(void * h)103893293b9eSkent snapper_halt_output(void *h)
10399427a0c7Sgrant {
104093293b9eSkent 	struct snapper_softc *sc;
10419427a0c7Sgrant 
104293293b9eSkent 	sc = h;
10439427a0c7Sgrant 	dbdma_stop(sc->sc_odma);
10449427a0c7Sgrant 	dbdma_reset(sc->sc_odma);
10458b9fcfffSaymeric 	sc->sc_ointr = NULL;
1046e622eac4Sisaki 	sc->sc_rval = 0;
10479427a0c7Sgrant 	return 0;
10489427a0c7Sgrant }
10499427a0c7Sgrant 
1050e3693941Smacallan static int
snapper_halt_input(void * h)105193293b9eSkent snapper_halt_input(void *h)
10529427a0c7Sgrant {
105393293b9eSkent 	struct snapper_softc *sc;
10549427a0c7Sgrant 
105593293b9eSkent 	sc = h;
10569427a0c7Sgrant 	dbdma_stop(sc->sc_idma);
10579427a0c7Sgrant 	dbdma_reset(sc->sc_idma);
10588b9fcfffSaymeric 	sc->sc_iintr = NULL;
1059e622eac4Sisaki 	sc->sc_rval = 0;
10609427a0c7Sgrant 	return 0;
10619427a0c7Sgrant }
10629427a0c7Sgrant 
1063e3693941Smacallan static int
snapper_getdev(void * h,struct audio_device * retp)106493293b9eSkent snapper_getdev(void *h, struct audio_device *retp)
10659427a0c7Sgrant {
106693293b9eSkent 
10679427a0c7Sgrant 	*retp = snapper_device;
10689427a0c7Sgrant 	return 0;
10699427a0c7Sgrant }
10709427a0c7Sgrant 
10719427a0c7Sgrant enum {
10729427a0c7Sgrant 	SNAPPER_MONITOR_CLASS,
10739427a0c7Sgrant 	SNAPPER_OUTPUT_CLASS,
10749427a0c7Sgrant 	SNAPPER_RECORD_CLASS,
10759427a0c7Sgrant 	SNAPPER_OUTPUT_SELECT,
10769427a0c7Sgrant 	SNAPPER_VOL_OUTPUT,
1077d9f9caa0Smacallan 	SNAPPER_DIGI1,
1078d9f9caa0Smacallan 	SNAPPER_DIGI2,
10799427a0c7Sgrant 	SNAPPER_VOL_INPUT,
1080d9f9caa0Smacallan 	SNAPPER_TREBLE,
1081d9f9caa0Smacallan 	SNAPPER_BASS,
10828b9fcfffSaymeric 	/* From this point, unsupported by the TAS 3001 */
10838b9fcfffSaymeric 	SNAPPER_ANALOG,
10848b9fcfffSaymeric 	SNAPPER_INPUT_SELECT,
10859427a0c7Sgrant 	SNAPPER_ENUM_LAST
10869427a0c7Sgrant };
10879427a0c7Sgrant 
1088e3693941Smacallan static int
snapper_set_port(void * h,mixer_ctrl_t * mc)108993293b9eSkent snapper_set_port(void *h, mixer_ctrl_t *mc)
10909427a0c7Sgrant {
109193293b9eSkent 	struct snapper_softc *sc;
10929427a0c7Sgrant 	int l, r;
10935f55aaa2Smacallan 	u_char data;
10949427a0c7Sgrant 
10959427a0c7Sgrant 	DPRINTF("snapper_set_port dev = %d, type = %d\n", mc->dev, mc->type);
109693293b9eSkent 	sc = h;
10979427a0c7Sgrant 	l = mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
10989427a0c7Sgrant 	r = mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
10999427a0c7Sgrant 
11009427a0c7Sgrant 	switch (mc->dev) {
11019427a0c7Sgrant 	case SNAPPER_OUTPUT_SELECT:
11029427a0c7Sgrant 		/* No change necessary? */
11039427a0c7Sgrant 		if (mc->un.mask == sc->sc_output_mask)
11049427a0c7Sgrant 			return 0;
11059427a0c7Sgrant 
11069427a0c7Sgrant 		snapper_mute_speaker(sc, 1);
11079427a0c7Sgrant 		snapper_mute_headphone(sc, 1);
1108202e08cdSmacallan 		snapper_mute_lineout(sc, 1);
11099427a0c7Sgrant 		if (mc->un.mask & 1 << 0)
11109427a0c7Sgrant 			snapper_mute_speaker(sc, 0);
11119427a0c7Sgrant 		if (mc->un.mask & 1 << 1)
11129427a0c7Sgrant 			snapper_mute_headphone(sc, 0);
1113202e08cdSmacallan 		if (mc->un.mask & 1 << 2)
1114202e08cdSmacallan 			snapper_mute_lineout(sc, 0);
11159427a0c7Sgrant 
11169427a0c7Sgrant 		sc->sc_output_mask = mc->un.mask;
11179427a0c7Sgrant 		return 0;
11189427a0c7Sgrant 
11199427a0c7Sgrant 	case SNAPPER_VOL_OUTPUT:
11209427a0c7Sgrant 		snapper_set_volume(sc, l, r);
11219427a0c7Sgrant 		return 0;
11229427a0c7Sgrant 
11239427a0c7Sgrant 	case SNAPPER_INPUT_SELECT:
1124a741bccaSmacallan 		if (sc->sc_mode != 0)
11258b9fcfffSaymeric 			return ENXIO;
11268b9fcfffSaymeric 
11279427a0c7Sgrant 		/* no change necessary? */
11289427a0c7Sgrant 		if (mc->un.mask == sc->sc_record_source)
11299427a0c7Sgrant 			return 0;
11309427a0c7Sgrant 		switch (mc->un.mask) {
11315f55aaa2Smacallan 		case 1 << 0: /* microphone */
11325f55aaa2Smacallan 			/* Select right channel of B input */
11335f55aaa2Smacallan 			data = DEQ_ACR_ADM | DEQ_ACR_LRB | DEQ_ACR_INP_B;
11345f55aaa2Smacallan 			tas3004_write(sc, DEQ_ACR, &data);
11355f55aaa2Smacallan 			break;
11365f55aaa2Smacallan 		case 1 << 1: /* line in */
11375f55aaa2Smacallan 			/* Select both channels of A input */
11385f55aaa2Smacallan 			data = 0;
11395f55aaa2Smacallan 			tas3004_write(sc, DEQ_ACR, &data);
11409427a0c7Sgrant 			break;
11419427a0c7Sgrant 		default: /* invalid argument */
11429427a0c7Sgrant 			return EINVAL;
11439427a0c7Sgrant 		}
11449427a0c7Sgrant 		sc->sc_record_source = mc->un.mask;
11459427a0c7Sgrant 		return 0;
11469427a0c7Sgrant 
11479427a0c7Sgrant 	case SNAPPER_VOL_INPUT:
11489427a0c7Sgrant 		/* XXX TO BE DONE */
11499427a0c7Sgrant 		return 0;
11509427a0c7Sgrant 
1151d9f9caa0Smacallan 	case SNAPPER_BASS:
1152a741bccaSmacallan 		if (sc->sc_mode == SNAPPER_SWVOL)
1153a741bccaSmacallan 			return ENXIO;
1154d9f9caa0Smacallan 		snapper_set_bass(sc, l);
1155d9f9caa0Smacallan 		return 0;
1156d9f9caa0Smacallan 	case SNAPPER_TREBLE:
1157a741bccaSmacallan 		if (sc->sc_mode == SNAPPER_SWVOL)
1158a741bccaSmacallan 			return ENXIO;
1159d9f9caa0Smacallan 		snapper_set_treble(sc, l);
1160d9f9caa0Smacallan 		return 0;
1161d9f9caa0Smacallan 	case SNAPPER_DIGI1:
1162a741bccaSmacallan 		if (sc->sc_mode == SNAPPER_SWVOL)
1163a741bccaSmacallan 			return ENXIO;
1164a741bccaSmacallan 
1165d9f9caa0Smacallan 		sc->mixer[0] = l;
1166d9f9caa0Smacallan 		sc->mixer[3] = r;
1167d9f9caa0Smacallan 		snapper_write_mixers(sc);
1168d9f9caa0Smacallan 		return 0;
1169d9f9caa0Smacallan 	case SNAPPER_DIGI2:
1170a741bccaSmacallan 		if (sc->sc_mode == SNAPPER_SWVOL)
1171a741bccaSmacallan 			return ENXIO;
1172a741bccaSmacallan 
1173a741bccaSmacallan 		if (sc->sc_mode == SNAPPER_IS_TAS3001)
11748b9fcfffSaymeric 			sc->mixer[3] = l;
11758b9fcfffSaymeric 		else {
1176d9f9caa0Smacallan 			sc->mixer[1] = l;
1177d9f9caa0Smacallan 			sc->mixer[4] = r;
11788b9fcfffSaymeric 		}
1179d9f9caa0Smacallan 		snapper_write_mixers(sc);
1180d9f9caa0Smacallan 		return 0;
1181d9f9caa0Smacallan 	case SNAPPER_ANALOG:
1182a741bccaSmacallan 		if (sc->sc_mode != 0)
11838b9fcfffSaymeric 			return ENXIO;
11848b9fcfffSaymeric 
1185d9f9caa0Smacallan 		sc->mixer[2] = l;
1186d9f9caa0Smacallan 		sc->mixer[5] = r;
1187d9f9caa0Smacallan 		snapper_write_mixers(sc);
1188d9f9caa0Smacallan 		return 0;
1189d9f9caa0Smacallan 	}
11909427a0c7Sgrant 	return ENXIO;
11919427a0c7Sgrant }
11929427a0c7Sgrant 
1193e3693941Smacallan static int
snapper_get_port(void * h,mixer_ctrl_t * mc)119493293b9eSkent snapper_get_port(void *h, mixer_ctrl_t *mc)
11959427a0c7Sgrant {
119693293b9eSkent 	struct snapper_softc *sc;
11979427a0c7Sgrant 
11989427a0c7Sgrant 	DPRINTF("snapper_get_port dev = %d, type = %d\n", mc->dev, mc->type);
119993293b9eSkent 	sc = h;
12009427a0c7Sgrant 	switch (mc->dev) {
12019427a0c7Sgrant 	case SNAPPER_OUTPUT_SELECT:
12029427a0c7Sgrant 		mc->un.mask = sc->sc_output_mask;
12039427a0c7Sgrant 		return 0;
12049427a0c7Sgrant 
12059427a0c7Sgrant 	case SNAPPER_VOL_OUTPUT:
12069427a0c7Sgrant 		mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = sc->sc_vol_l;
12079427a0c7Sgrant 		mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = sc->sc_vol_r;
12089427a0c7Sgrant 		return 0;
12099427a0c7Sgrant 
12109427a0c7Sgrant 	case SNAPPER_INPUT_SELECT:
1211a741bccaSmacallan 		if (sc->sc_mode != 0)
12128b9fcfffSaymeric 			return ENXIO;
12138b9fcfffSaymeric 
12149427a0c7Sgrant 		mc->un.mask = sc->sc_record_source;
12159427a0c7Sgrant 		return 0;
12169427a0c7Sgrant 
12179427a0c7Sgrant 	case SNAPPER_VOL_INPUT:
12189427a0c7Sgrant 		/* XXX TO BE DONE */
12199427a0c7Sgrant 		mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = 0;
12209427a0c7Sgrant 		mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = 0;
12219427a0c7Sgrant 		return 0;
1222a741bccaSmacallan 
1223d9f9caa0Smacallan 	case SNAPPER_TREBLE:
1224a741bccaSmacallan 		if (sc->sc_mode == SNAPPER_SWVOL)
1225a741bccaSmacallan 			return ENXIO;
1226d9f9caa0Smacallan 		mc->un.value.level[AUDIO_MIXER_LEVEL_MONO] = sc->sc_treble;
1227d9f9caa0Smacallan 		return 0;
1228d9f9caa0Smacallan 	case SNAPPER_BASS:
1229a741bccaSmacallan 		if (sc->sc_mode == SNAPPER_SWVOL)
1230a741bccaSmacallan 			return ENXIO;
1231d9f9caa0Smacallan 		mc->un.value.level[AUDIO_MIXER_LEVEL_MONO] = sc->sc_bass;
1232d9f9caa0Smacallan 		return 0;
1233a741bccaSmacallan 
1234d9f9caa0Smacallan 	case SNAPPER_DIGI1:
1235a741bccaSmacallan 		if (sc->sc_mode == SNAPPER_SWVOL)
1236a741bccaSmacallan 			return ENXIO;
1237a741bccaSmacallan 
1238d9f9caa0Smacallan 		mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = sc->mixer[0];
1239d9f9caa0Smacallan 		mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = sc->mixer[3];
1240d9f9caa0Smacallan 		return 0;
1241d9f9caa0Smacallan 	case SNAPPER_DIGI2:
1242a741bccaSmacallan 		if (sc->sc_mode == SNAPPER_SWVOL)
1243a741bccaSmacallan 			return ENXIO;
1244a741bccaSmacallan 
1245d9f9caa0Smacallan 		mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = sc->mixer[1];
1246d9f9caa0Smacallan 		mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = sc->mixer[4];
1247d9f9caa0Smacallan 		return 0;
1248d9f9caa0Smacallan 	case SNAPPER_ANALOG:
1249a741bccaSmacallan 		if (sc->sc_mode != 0)
12508b9fcfffSaymeric 			return ENXIO;
12518b9fcfffSaymeric 
1252d9f9caa0Smacallan 		mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = sc->mixer[2];
1253d9f9caa0Smacallan 		mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = sc->mixer[5];
1254d9f9caa0Smacallan 		return 0;
12559427a0c7Sgrant 	default:
12569427a0c7Sgrant 		return ENXIO;
12579427a0c7Sgrant 	}
12589427a0c7Sgrant 
12599427a0c7Sgrant 	return 0;
12609427a0c7Sgrant }
12619427a0c7Sgrant 
1262e3693941Smacallan static int
snapper_query_devinfo(void * h,mixer_devinfo_t * dip)126393293b9eSkent snapper_query_devinfo(void *h, mixer_devinfo_t *dip)
12649427a0c7Sgrant {
12658b9fcfffSaymeric 	struct snapper_softc *sc = h;
12668b9fcfffSaymeric 
12679427a0c7Sgrant 	switch (dip->index) {
12689427a0c7Sgrant 
12699427a0c7Sgrant 	case SNAPPER_OUTPUT_SELECT:
1270210f669bSmacallan 		dip->mixer_class = SNAPPER_OUTPUT_CLASS;
12719427a0c7Sgrant 		strcpy(dip->label.name, AudioNoutput);
12729427a0c7Sgrant 		dip->type = AUDIO_MIXER_SET;
12739427a0c7Sgrant 		dip->prev = dip->next = AUDIO_MIXER_LAST;
1274202e08cdSmacallan 		dip->un.s.num_mem = 3;
12759427a0c7Sgrant 		strcpy(dip->un.s.member[0].label.name, AudioNspeaker);
12769ade61f4Smartin 		dip->un.s.member[0].mask = 1 << 0;
12779427a0c7Sgrant 		strcpy(dip->un.s.member[1].label.name, AudioNheadphone);
12789ade61f4Smartin 		dip->un.s.member[1].mask = 1 << 1;
1279202e08cdSmacallan 		strcpy(dip->un.s.member[2].label.name, AudioNline);
12809ade61f4Smartin 		dip->un.s.member[2].mask = 1 << 2;
12819427a0c7Sgrant 		return 0;
12829427a0c7Sgrant 
12839427a0c7Sgrant 	case SNAPPER_VOL_OUTPUT:
1284210f669bSmacallan 		dip->mixer_class = SNAPPER_OUTPUT_CLASS;
12859427a0c7Sgrant 		strcpy(dip->label.name, AudioNmaster);
12869427a0c7Sgrant 		dip->type = AUDIO_MIXER_VALUE;
12879427a0c7Sgrant 		dip->prev = dip->next = AUDIO_MIXER_LAST;
12889427a0c7Sgrant 		dip->un.v.num_channels = 2;
1289210f669bSmacallan 		dip->un.v.delta = 16;
12909427a0c7Sgrant 		strcpy(dip->un.v.units.name, AudioNvolume);
12919427a0c7Sgrant 		return 0;
12929427a0c7Sgrant 
12939427a0c7Sgrant 	case SNAPPER_INPUT_SELECT:
1294a741bccaSmacallan 		if (sc->sc_mode != 0)
12958b9fcfffSaymeric 			return ENXIO;
12968b9fcfffSaymeric 
12979427a0c7Sgrant 		dip->mixer_class = SNAPPER_RECORD_CLASS;
12989427a0c7Sgrant 		strcpy(dip->label.name, AudioNsource);
12999427a0c7Sgrant 		dip->type = AUDIO_MIXER_SET;
13009427a0c7Sgrant 		dip->prev = dip->next = AUDIO_MIXER_LAST;
13015f55aaa2Smacallan 		dip->un.s.num_mem = 2;
13025f55aaa2Smacallan 		strcpy(dip->un.s.member[0].label.name, AudioNmicrophone);
13039427a0c7Sgrant 		dip->un.s.member[0].mask = 1 << 0;
13045f55aaa2Smacallan 		strcpy(dip->un.s.member[1].label.name, AudioNline);
13059427a0c7Sgrant 		dip->un.s.member[1].mask = 1 << 1;
13069427a0c7Sgrant 		return 0;
13079427a0c7Sgrant 
13089427a0c7Sgrant 	case SNAPPER_VOL_INPUT:
13099427a0c7Sgrant 		dip->mixer_class = SNAPPER_RECORD_CLASS;
13109427a0c7Sgrant 		strcpy(dip->label.name, AudioNrecord);
13119427a0c7Sgrant 		dip->type = AUDIO_MIXER_VALUE;
13129427a0c7Sgrant 		dip->prev = dip->next = AUDIO_MIXER_LAST;
13139427a0c7Sgrant 		dip->un.v.num_channels = 2;
13149427a0c7Sgrant 		strcpy(dip->un.v.units.name, AudioNvolume);
13159427a0c7Sgrant 		return 0;
13169427a0c7Sgrant 
13179427a0c7Sgrant 	case SNAPPER_MONITOR_CLASS:
13189427a0c7Sgrant 		dip->mixer_class = SNAPPER_MONITOR_CLASS;
13199427a0c7Sgrant 		strcpy(dip->label.name, AudioCmonitor);
13209427a0c7Sgrant 		dip->type = AUDIO_MIXER_CLASS;
13219427a0c7Sgrant 		dip->next = dip->prev = AUDIO_MIXER_LAST;
13229427a0c7Sgrant 		return 0;
13239427a0c7Sgrant 
13249427a0c7Sgrant 	case SNAPPER_OUTPUT_CLASS:
13259427a0c7Sgrant 		dip->mixer_class = SNAPPER_OUTPUT_CLASS;
13269427a0c7Sgrant 		strcpy(dip->label.name, AudioCoutputs);
13279427a0c7Sgrant 		dip->type = AUDIO_MIXER_CLASS;
13289427a0c7Sgrant 		dip->next = dip->prev = AUDIO_MIXER_LAST;
13299427a0c7Sgrant 		return 0;
13309427a0c7Sgrant 
13319427a0c7Sgrant 	case SNAPPER_RECORD_CLASS:
13329427a0c7Sgrant 		dip->mixer_class = SNAPPER_RECORD_CLASS;
13339427a0c7Sgrant 		strcpy(dip->label.name, AudioCrecord);
13349427a0c7Sgrant 		dip->type = AUDIO_MIXER_CLASS;
13359427a0c7Sgrant 		dip->next = dip->prev = AUDIO_MIXER_LAST;
13369427a0c7Sgrant 		return 0;
1337d9f9caa0Smacallan 
1338d9f9caa0Smacallan 	case SNAPPER_TREBLE:
1339a741bccaSmacallan 		if (sc->sc_mode == SNAPPER_SWVOL)
1340a741bccaSmacallan 			return ENXIO;
1341a741bccaSmacallan 
1342210f669bSmacallan 		dip->mixer_class = SNAPPER_OUTPUT_CLASS;
1343d9f9caa0Smacallan 		strcpy(dip->label.name, AudioNtreble);
1344d9f9caa0Smacallan 		dip->type = AUDIO_MIXER_VALUE;
1345d9f9caa0Smacallan 		dip->prev = dip->next = AUDIO_MIXER_LAST;
1346d9f9caa0Smacallan 		dip->un.v.num_channels = 1;
1347d9f9caa0Smacallan 		return 0;
1348d9f9caa0Smacallan 
1349d9f9caa0Smacallan 	case SNAPPER_BASS:
1350a741bccaSmacallan 		if (sc->sc_mode == SNAPPER_SWVOL)
1351a741bccaSmacallan 			return ENXIO;
1352a741bccaSmacallan 
1353210f669bSmacallan 		dip->mixer_class = SNAPPER_OUTPUT_CLASS;
1354d9f9caa0Smacallan 		strcpy(dip->label.name, AudioNbass);
1355d9f9caa0Smacallan 		dip->type = AUDIO_MIXER_VALUE;
1356d9f9caa0Smacallan 		dip->prev = dip->next = AUDIO_MIXER_LAST;
1357d9f9caa0Smacallan 		dip->un.v.num_channels = 1;
1358d9f9caa0Smacallan 		return 0;
1359d9f9caa0Smacallan 
1360d9f9caa0Smacallan 	case SNAPPER_DIGI1:
1361a741bccaSmacallan 		if (sc->sc_mode == SNAPPER_SWVOL)
1362a741bccaSmacallan 			return ENXIO;
1363a741bccaSmacallan 
1364210f669bSmacallan 		dip->mixer_class = SNAPPER_OUTPUT_CLASS;
1365d9f9caa0Smacallan 		strcpy(dip->label.name, AudioNdac);
1366d9f9caa0Smacallan 		dip->type = AUDIO_MIXER_VALUE;
1367d9f9caa0Smacallan 		dip->prev = dip->next = AUDIO_MIXER_LAST;
13688b9fcfffSaymeric 		dip->un.v.num_channels =
1369a741bccaSmacallan 			sc->sc_mode == SNAPPER_IS_TAS3001? 1 : 2;
1370d9f9caa0Smacallan 		return 0;
1371d9f9caa0Smacallan 	case SNAPPER_DIGI2:
1372a741bccaSmacallan 		if (sc->sc_mode == SNAPPER_SWVOL)
1373a741bccaSmacallan 			return ENXIO;
1374a741bccaSmacallan 
1375210f669bSmacallan 		dip->mixer_class = SNAPPER_OUTPUT_CLASS;
13765f55aaa2Smacallan 		strcpy(dip->label.name, AudioNline);
1377d9f9caa0Smacallan 		dip->type = AUDIO_MIXER_VALUE;
1378d9f9caa0Smacallan 		dip->prev = dip->next = AUDIO_MIXER_LAST;
13798b9fcfffSaymeric 		dip->un.v.num_channels =
1380a741bccaSmacallan 			sc->sc_mode == SNAPPER_IS_TAS3001? 1 : 2;
1381d9f9caa0Smacallan 		return 0;
1382d9f9caa0Smacallan 	case SNAPPER_ANALOG:
1383a741bccaSmacallan 		if (sc->sc_mode != 0)
13848b9fcfffSaymeric 			return ENXIO;
13858b9fcfffSaymeric 
1386d9f9caa0Smacallan 		dip->mixer_class = SNAPPER_MONITOR_CLASS;
13875f55aaa2Smacallan 		strcpy(dip->label.name, AudioNmicrophone);
1388d9f9caa0Smacallan 		dip->type = AUDIO_MIXER_VALUE;
1389d9f9caa0Smacallan 		dip->prev = dip->next = AUDIO_MIXER_LAST;
1390d9f9caa0Smacallan 		dip->un.v.num_channels = 2;
1391d9f9caa0Smacallan 		return 0;
13929427a0c7Sgrant 	}
13939427a0c7Sgrant 
13949427a0c7Sgrant 	return ENXIO;
13959427a0c7Sgrant }
13969427a0c7Sgrant 
1397e3693941Smacallan static size_t
snapper_round_buffersize(void * h,int dir,size_t size)139893293b9eSkent snapper_round_buffersize(void *h, int dir, size_t size)
13999427a0c7Sgrant {
140093293b9eSkent 
14019427a0c7Sgrant 	if (size > 65536)
14029427a0c7Sgrant 		size = 65536;
14039427a0c7Sgrant 	return size;
14049427a0c7Sgrant }
14059427a0c7Sgrant 
1406e3693941Smacallan static int
snapper_get_props(void * h)140793293b9eSkent snapper_get_props(void *h)
14089427a0c7Sgrant {
1409ede47d01Sisaki 
1410ede47d01Sisaki 	return AUDIO_PROP_PLAYBACK | AUDIO_PROP_CAPTURE |
1411ede47d01Sisaki 	    AUDIO_PROP_FULLDUPLEX;
14129427a0c7Sgrant }
14139427a0c7Sgrant 
1414e3693941Smacallan static int
snapper_trigger_output(void * h,void * start,void * end,int bsize,void (* intr)(void *),void * arg,const audio_params_t * param)141593293b9eSkent snapper_trigger_output(void *h, void *start, void *end, int bsize,
141693293b9eSkent 		       void (*intr)(void *), void *arg,
141793293b9eSkent 		       const audio_params_t *param)
14189427a0c7Sgrant {
141993293b9eSkent 	struct snapper_softc *sc;
142093293b9eSkent 	struct dbdma_command *cmd;
14219427a0c7Sgrant 	vaddr_t va;
14229427a0c7Sgrant 	int i, len, intmode;
14239427a0c7Sgrant 
14249427a0c7Sgrant 	DPRINTF("trigger_output %p %p 0x%x\n", start, end, bsize);
142593293b9eSkent 	sc = h;
142635a2b63bSmacallan 
142793293b9eSkent 	cmd = sc->sc_odmacmd;
14289427a0c7Sgrant 	sc->sc_ointr = intr;
14299427a0c7Sgrant 	sc->sc_oarg = arg;
14309427a0c7Sgrant 	sc->sc_opages = ((char *)end - (char *)start) / NBPG;
14319427a0c7Sgrant 
14329427a0c7Sgrant #ifdef DIAGNOSTIC
1433a741bccaSmacallan 	if (sc->sc_opages > SNAPPER_MAXPAGES)
14349427a0c7Sgrant 		panic("snapper_trigger_output");
14359427a0c7Sgrant #endif
14369427a0c7Sgrant 
14379427a0c7Sgrant 	va = (vaddr_t)start;
14389427a0c7Sgrant 	len = 0;
14399427a0c7Sgrant 	for (i = sc->sc_opages; i > 0; i--) {
14409427a0c7Sgrant 		len += NBPG;
14419427a0c7Sgrant 		if (len < bsize)
14429427a0c7Sgrant 			intmode = 0;
14439427a0c7Sgrant 		else {
14449427a0c7Sgrant 			len = 0;
14459427a0c7Sgrant 			intmode = DBDMA_INT_ALWAYS;
14469427a0c7Sgrant 		}
14479427a0c7Sgrant 
144893293b9eSkent 		DBDMA_BUILD(cmd, DBDMA_CMD_OUT_MORE, 0, NBPG, vtophys(va),
144993293b9eSkent 		    intmode, DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER);
14509427a0c7Sgrant 		cmd++;
14519427a0c7Sgrant 		va += NBPG;
14529427a0c7Sgrant 	}
14539427a0c7Sgrant 
14549427a0c7Sgrant 	DBDMA_BUILD(cmd, DBDMA_CMD_NOP, 0, 0,
145593293b9eSkent 	    0/*vtophys((vaddr_t)sc->sc_odmacmd)*/, 0, DBDMA_WAIT_NEVER,
145693293b9eSkent 	    DBDMA_BRANCH_ALWAYS);
14579427a0c7Sgrant 
1458d974db0aSgarbled 	out32rb(&cmd->d_cmddep, vtophys((vaddr_t)sc->sc_odmacmd));
14599427a0c7Sgrant 
14609427a0c7Sgrant 	dbdma_start(sc->sc_odma, sc->sc_odmacmd);
14619427a0c7Sgrant 
14629427a0c7Sgrant 	return 0;
14639427a0c7Sgrant }
14649427a0c7Sgrant 
1465e3693941Smacallan static int
snapper_trigger_input(void * h,void * start,void * end,int bsize,void (* intr)(void *),void * arg,const audio_params_t * param)146693293b9eSkent snapper_trigger_input(void *h, void *start, void *end, int bsize,
146793293b9eSkent 		      void (*intr)(void *), void *arg,
146893293b9eSkent 		      const audio_params_t *param)
14699427a0c7Sgrant {
14705f55aaa2Smacallan 	struct snapper_softc *sc;
14715f55aaa2Smacallan 	struct dbdma_command *cmd;
14725f55aaa2Smacallan 	vaddr_t va;
14735f55aaa2Smacallan 	int i, len, intmode;
14749427a0c7Sgrant 
14755f55aaa2Smacallan 	DPRINTF("trigger_input %p %p 0x%x\n", start, end, bsize);
14765f55aaa2Smacallan 	sc = h;
147735a2b63bSmacallan 
14785f55aaa2Smacallan 	cmd = sc->sc_idmacmd;
14795f55aaa2Smacallan 	sc->sc_iintr = intr;
14805f55aaa2Smacallan 	sc->sc_iarg = arg;
14815f55aaa2Smacallan 	sc->sc_ipages = ((char *)end - (char *)start) / NBPG;
14825f55aaa2Smacallan 
14835f55aaa2Smacallan #ifdef DIAGNOSTIC
1484a741bccaSmacallan 	if (sc->sc_ipages > SNAPPER_MAXPAGES)
14855f55aaa2Smacallan 		panic("snapper_trigger_input");
14865f55aaa2Smacallan #endif
14875f55aaa2Smacallan 
14885f55aaa2Smacallan 	va = (vaddr_t)start;
14895f55aaa2Smacallan 	len = 0;
14905f55aaa2Smacallan 	for (i = sc->sc_ipages; i > 0; i--) {
14915f55aaa2Smacallan 		len += NBPG;
14925f55aaa2Smacallan 		if (len < bsize)
14935f55aaa2Smacallan 			intmode = 0;
14945f55aaa2Smacallan 		else {
14955f55aaa2Smacallan 			len = 0;
14965f55aaa2Smacallan 			intmode = DBDMA_INT_ALWAYS;
14975f55aaa2Smacallan 		}
14985f55aaa2Smacallan 
14995f55aaa2Smacallan 		DBDMA_BUILD(cmd, DBDMA_CMD_IN_MORE, 0, NBPG, vtophys(va),
15005f55aaa2Smacallan 		    intmode, DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER);
15015f55aaa2Smacallan 		cmd++;
15025f55aaa2Smacallan 		va += NBPG;
15035f55aaa2Smacallan 	}
15045f55aaa2Smacallan 
15055f55aaa2Smacallan 	DBDMA_BUILD(cmd, DBDMA_CMD_NOP, 0, 0,
15065f55aaa2Smacallan 	    0/*vtophys((vaddr_t)sc->sc_odmacmd)*/, 0, DBDMA_WAIT_NEVER,
15075f55aaa2Smacallan 	    DBDMA_BRANCH_ALWAYS);
15085f55aaa2Smacallan 
1509d974db0aSgarbled 	out32rb(&cmd->d_cmddep, vtophys((vaddr_t)sc->sc_idmacmd));
15105f55aaa2Smacallan 
15115f55aaa2Smacallan 	dbdma_start(sc->sc_idma, sc->sc_idmacmd);
15125f55aaa2Smacallan 
15135f55aaa2Smacallan 	return 0;
15149427a0c7Sgrant }
15159427a0c7Sgrant 
1516e3693941Smacallan static void
snapper_get_locks(void * opaque,kmutex_t ** intr,kmutex_t ** thread)15178a962f23Sjmcneill snapper_get_locks(void *opaque, kmutex_t **intr, kmutex_t **thread)
15188a962f23Sjmcneill {
15198a962f23Sjmcneill        struct snapper_softc *sc = opaque;
15208a962f23Sjmcneill 
15218a962f23Sjmcneill        *intr = &sc->sc_intr_lock;
15228a962f23Sjmcneill        *thread = &sc->sc_lock;
15238a962f23Sjmcneill }
15248a962f23Sjmcneill 
15258a962f23Sjmcneill static void
snapper_set_volume(struct snapper_softc * sc,u_int left,u_int right)1526a741bccaSmacallan snapper_set_volume(struct snapper_softc *sc, u_int left, u_int right)
15279427a0c7Sgrant {
15283887e678Smacallan 	u_char regs[6];
15293887e678Smacallan 	int l, r;
15303887e678Smacallan 
1531d1579b2dSriastradh 	left = uimin(255, left);
1532d1579b2dSriastradh 	right = uimin(255, right);
1533a741bccaSmacallan 
1534a741bccaSmacallan 	if (sc->sc_mode == SNAPPER_SWVOL) {
1535e622eac4Sisaki 		sc->sc_swvol_l = left;
1536e622eac4Sisaki 		sc->sc_swvol_r = right;
1537a741bccaSmacallan 	} else {
15383887e678Smacallan 		/*
15393887e678Smacallan 		 * for some insane reason the gain table for master volume and the
15403887e678Smacallan 		 * mixer channels is almost identical - just shifted by 4 bits
15413887e678Smacallan 		 * so we use the mixer_gain table and bit-twiddle it...
15423887e678Smacallan 		 */
1543a741bccaSmacallan 		l = 177 - (left * 178 / 256);
15443887e678Smacallan 		regs[0] =  (snapper_mixer_gain[l][0] >> 4);
15453887e678Smacallan 		regs[1] = ((snapper_mixer_gain[l][0] & 0x0f) << 4) |
15463887e678Smacallan 			   (snapper_mixer_gain[l][1] >> 4);
15473887e678Smacallan 		regs[2] = ((snapper_mixer_gain[l][1] & 0x0f) << 4) |
15483887e678Smacallan 			   (snapper_mixer_gain[l][2] >> 4);
15493887e678Smacallan 
1550a741bccaSmacallan 		r = 177 - (right * 178 / 256);
15513887e678Smacallan 		regs[3] =  (snapper_mixer_gain[r][0] >> 4);
15523887e678Smacallan 		regs[4] = ((snapper_mixer_gain[r][0] & 0x0f) << 4) |
15533887e678Smacallan 			   (snapper_mixer_gain[r][1] >> 4);
15543887e678Smacallan 		regs[5] = ((snapper_mixer_gain[r][1] & 0x0f) << 4) |
15553887e678Smacallan 			   (snapper_mixer_gain[r][2] >> 4);
15563887e678Smacallan 
15573887e678Smacallan 		tas3004_write(sc, DEQ_VOLUME, regs);
15589427a0c7Sgrant 
15593887e678Smacallan 		DPRINTF("%d %02x %02x %02x : %d %02x %02x %02x\n", l, regs[0],
15603887e678Smacallan 		    regs[1], regs[2], r, regs[3], regs[4], regs[5]);
15613887e678Smacallan 	}
1562a741bccaSmacallan 
1563a741bccaSmacallan 	sc->sc_vol_l = left;
1564a741bccaSmacallan 	sc->sc_vol_r = right;
15659427a0c7Sgrant }
15669427a0c7Sgrant 
1567a741bccaSmacallan static void
snapper_set_basstreble(struct snapper_softc * sc,u_int val,u_int mode)1568a741bccaSmacallan snapper_set_basstreble(struct snapper_softc *sc, u_int val, u_int mode)
1569d9f9caa0Smacallan {
1570a741bccaSmacallan 	int i = val & 0xFF;
1571d9f9caa0Smacallan 	uint8_t reg;
1572a741bccaSmacallan 
1573a741bccaSmacallan 	/*
1574a741bccaSmacallan 	 * Make 128 match the 0 dB point
1575a741bccaSmacallan 	 */
1576a741bccaSmacallan 	i = (i - (128 - (SNAPPER_BASSTAB_0DB << 2))) >> 2;
1577a741bccaSmacallan 	if (i < 0)
1578a741bccaSmacallan 		i = 0;
1579a741bccaSmacallan 	else if (i >= sizeof(snapper_basstab))
1580a741bccaSmacallan 		i = sizeof(snapper_basstab) - 1;
1581a741bccaSmacallan 	reg = snapper_basstab[i];
1582a741bccaSmacallan 
1583a741bccaSmacallan 	if (sc->sc_mode == SNAPPER_IS_TAS3001 &&
1584a741bccaSmacallan 	    mode == DEQ_BASS) {
1585a741bccaSmacallan 	    /*
1586a741bccaSmacallan 	     * XXX -- The TAS3001 bass table is different
1587a741bccaSmacallan 	     *        than the other tables.
1588a741bccaSmacallan 	     */
1589a741bccaSmacallan 	    reg = (reg >> 1) + 5; // map 0x72 -> 0x3E (0 dB)
1590d9f9caa0Smacallan 	}
1591d9f9caa0Smacallan 
1592a741bccaSmacallan 	tas3004_write(sc, mode, &reg);
1593a741bccaSmacallan }
1594a741bccaSmacallan 
1595e3693941Smacallan static void
snapper_set_treble(struct snapper_softc * sc,u_int val)1596a741bccaSmacallan snapper_set_treble(struct snapper_softc *sc, u_int val)
1597d9f9caa0Smacallan {
1598a741bccaSmacallan 	if (sc->sc_treble != (u_char)val) {
1599a741bccaSmacallan 		sc->sc_treble = val;
1600a741bccaSmacallan 		snapper_set_basstreble(sc, val, DEQ_TREBLE);
1601d9f9caa0Smacallan 	}
1602d9f9caa0Smacallan }
1603d9f9caa0Smacallan 
1604e3693941Smacallan static void
snapper_set_bass(struct snapper_softc * sc,u_int val)1605a741bccaSmacallan snapper_set_bass(struct snapper_softc *sc, u_int val)
1606a741bccaSmacallan {
1607a741bccaSmacallan 	if (sc->sc_bass != (u_char)val) {
1608a741bccaSmacallan 		sc->sc_bass = val;
1609a741bccaSmacallan 		snapper_set_basstreble(sc, val, DEQ_BASS);
1610a741bccaSmacallan 	}
1611a741bccaSmacallan }
1612a741bccaSmacallan 
1613a741bccaSmacallan 
1614a741bccaSmacallan /*
1615a741bccaSmacallan  * In the mixer gain setting, make 128 correspond to
1616a741bccaSmacallan  * the 0dB value from the table.
1617a741bccaSmacallan  * Note that the table values are complemented.
1618a741bccaSmacallan  */
1619a741bccaSmacallan #define SNAPPER_MIXER_GAIN_SIZE	(sizeof(snapper_mixer_gain) / \
1620a741bccaSmacallan                                	 sizeof(snapper_mixer_gain[0]))
1621a741bccaSmacallan #define NORMALIZE(i)	((~(i) & 0xff) - ((~128 & 0xff) - SNAPPER_MIXER_GAIN_0DB))
1622a741bccaSmacallan #define ADJUST(v, i)	do { \
1623a741bccaSmacallan                 		(v) = NORMALIZE(i);\
1624a741bccaSmacallan 				if ((v) < 0) \
1625a741bccaSmacallan 					(v) = 0; \
1626a741bccaSmacallan 				else if ((v) >= SNAPPER_MIXER_GAIN_SIZE) \
1627a741bccaSmacallan 					(v) = SNAPPER_MIXER_GAIN_SIZE - 1; \
1628a741bccaSmacallan 				\
1629a741bccaSmacallan 			} while (0)
1630e3693941Smacallan static void
snapper_write_mixers(struct snapper_softc * sc)1631a741bccaSmacallan snapper_write_mixers(struct snapper_softc *sc)
1632d9f9caa0Smacallan {
1633d9f9caa0Smacallan 	uint8_t regs[9] = {0, 0, 0, 0, 0, 0, 0, 0, 0};
16345f55aaa2Smacallan 	int i;
163599276969Smacallan if (sc->sc_mode > 1) return;
16365f55aaa2Smacallan 	/* Left channel of SDIN1 */
1637a741bccaSmacallan 	ADJUST(i, sc->mixer[0]);
16385f55aaa2Smacallan 	regs[0] = snapper_mixer_gain[i][0];
16395f55aaa2Smacallan 	regs[1] = snapper_mixer_gain[i][1];
16405f55aaa2Smacallan 	regs[2] = snapper_mixer_gain[i][2];
16415f55aaa2Smacallan 
16425f55aaa2Smacallan 	/* Left channel of SDIN2 */
1643a741bccaSmacallan 	ADJUST(i, sc->mixer[1]);
16445f55aaa2Smacallan 	regs[3] = snapper_mixer_gain[i][0];
16455f55aaa2Smacallan 	regs[4] = snapper_mixer_gain[i][1];
16465f55aaa2Smacallan 	regs[5] = snapper_mixer_gain[i][2];
16475f55aaa2Smacallan 
16485f55aaa2Smacallan 	/* Left channel of analog input */
1649a741bccaSmacallan 	ADJUST(i, sc->mixer[2]);
16505f55aaa2Smacallan 	regs[6] = snapper_mixer_gain[i][0];
16515f55aaa2Smacallan 	regs[7] = snapper_mixer_gain[i][1];
16525f55aaa2Smacallan 	regs[8] = snapper_mixer_gain[i][2];
16535f55aaa2Smacallan 
1654d9f9caa0Smacallan 	tas3004_write(sc, DEQ_MIXER_L, regs);
16555f55aaa2Smacallan 
16565f55aaa2Smacallan 	/* Right channel of SDIN1 */
1657a741bccaSmacallan 	ADJUST(i, sc->mixer[3]);
16585f55aaa2Smacallan 	regs[0] = snapper_mixer_gain[i][0];
16595f55aaa2Smacallan 	regs[1] = snapper_mixer_gain[i][1];
16605f55aaa2Smacallan 	regs[2] = snapper_mixer_gain[i][2];
16615f55aaa2Smacallan 
16625f55aaa2Smacallan 	/* Right channel of SDIN2 */
1663a741bccaSmacallan 	ADJUST(i, sc->mixer[4]);
16645f55aaa2Smacallan 	regs[3] = snapper_mixer_gain[i][0];
16655f55aaa2Smacallan 	regs[4] = snapper_mixer_gain[i][1];
16665f55aaa2Smacallan 	regs[5] = snapper_mixer_gain[i][2];
16675f55aaa2Smacallan 
16685f55aaa2Smacallan 	/* Right channel of analog input */
1669a741bccaSmacallan 	ADJUST(i, sc->mixer[5]);
16705f55aaa2Smacallan 	regs[6] = snapper_mixer_gain[i][0];
16715f55aaa2Smacallan 	regs[7] = snapper_mixer_gain[i][1];
16725f55aaa2Smacallan 	regs[8] = snapper_mixer_gain[i][2];
16735f55aaa2Smacallan 
1674d9f9caa0Smacallan 	tas3004_write(sc, DEQ_MIXER_R, regs);
1675d9f9caa0Smacallan }
1676d9f9caa0Smacallan 
16779427a0c7Sgrant #define CLKSRC_49MHz	0x80000000	/* Use 49152000Hz Osc. */
16789427a0c7Sgrant #define CLKSRC_45MHz	0x40000000	/* Use 45158400Hz Osc. */
16799427a0c7Sgrant #define CLKSRC_18MHz	0x00000000	/* Use 18432000Hz Osc. */
16809427a0c7Sgrant #define MCLK_DIV	0x1f000000	/* MCLK = SRC / DIV */
16819427a0c7Sgrant #define  MCLK_DIV1	0x14000000	/*  MCLK = SRC */
16829427a0c7Sgrant #define  MCLK_DIV3	0x13000000	/*  MCLK = SRC / 3 */
16839427a0c7Sgrant #define  MCLK_DIV5	0x12000000	/*  MCLK = SRC / 5 */
16849427a0c7Sgrant #define SCLK_DIV	0x00f00000	/* SCLK = MCLK / DIV */
16859427a0c7Sgrant #define  SCLK_DIV1	0x00800000
16869427a0c7Sgrant #define  SCLK_DIV3	0x00900000
16879427a0c7Sgrant #define SCLK_MASTER	0x00080000	/* Master mode */
16889427a0c7Sgrant #define SCLK_SLAVE	0x00000000	/* Slave mode */
16899427a0c7Sgrant #define SERIAL_FORMAT	0x00070000
16909427a0c7Sgrant #define  SERIAL_SONY	0x00000000
16919427a0c7Sgrant #define  SERIAL_64x	0x00010000
16929427a0c7Sgrant #define  SERIAL_32x	0x00020000
16939427a0c7Sgrant #define  SERIAL_DAV	0x00040000
16949427a0c7Sgrant #define  SERIAL_SILICON	0x00050000
16959427a0c7Sgrant 
16963150bfdeSjmcneill /*
16973150bfdeSjmcneill  * rate = fs = LRCLK
16983150bfdeSjmcneill  * SCLK = 64*LRCLK (I2S)
16993150bfdeSjmcneill  * MCLK = 256fs (typ. -- changeable)
17003150bfdeSjmcneill  *
17013150bfdeSjmcneill  * MCLK = clksrc / mdiv
17023150bfdeSjmcneill  * SCLK = MCLK / sdiv
17033150bfdeSjmcneill  * rate = SCLK / 64    ( = LRCLK = fs)
17043150bfdeSjmcneill  */
17059427a0c7Sgrant 
17069427a0c7Sgrant int
snapper_set_rate(struct snapper_softc * sc)170735a2b63bSmacallan snapper_set_rate(struct snapper_softc *sc)
17089427a0c7Sgrant {
170935a2b63bSmacallan 	u_int reg = 0, x;
171035a2b63bSmacallan 	u_int rate = sc->sc_rate;
171135a2b63bSmacallan 	uint32_t wordsize, ows;
17129427a0c7Sgrant 	int MCLK;
17139427a0c7Sgrant 	int clksrc, mdiv, sdiv;
17149427a0c7Sgrant 	int mclk_fs;
171535a2b63bSmacallan 	int timo;
171635a2b63bSmacallan 	uint8_t mcr1;
17179427a0c7Sgrant 
17189427a0c7Sgrant 	switch (rate) {
17199427a0c7Sgrant 	case 44100:
17209427a0c7Sgrant 		clksrc = 45158400;		/* 45MHz */
17219427a0c7Sgrant 		reg = CLKSRC_45MHz;
17229427a0c7Sgrant 		mclk_fs = 256;
17239427a0c7Sgrant 		break;
17249427a0c7Sgrant 
172535a2b63bSmacallan 	case 32000:
17269427a0c7Sgrant 	case 48000:
1727a741bccaSmacallan 	case 96000:
17289427a0c7Sgrant 		clksrc = 49152000;		/* 49MHz */
17299427a0c7Sgrant 		reg = CLKSRC_49MHz;
17309427a0c7Sgrant 		mclk_fs = 256;
17319427a0c7Sgrant 		break;
17329427a0c7Sgrant 
17339427a0c7Sgrant 	default:
17345f55aaa2Smacallan 		DPRINTF("snapper_set_rate: invalid rate %u\n", rate);
17359427a0c7Sgrant 		return EINVAL;
17369427a0c7Sgrant 	}
17379427a0c7Sgrant 
17389427a0c7Sgrant 	MCLK = rate * mclk_fs;
17393150bfdeSjmcneill 	mdiv = clksrc / MCLK;			/* 4 */
17403150bfdeSjmcneill 	sdiv = mclk_fs / 64;			/* 4 */
17419427a0c7Sgrant 
17429427a0c7Sgrant 	switch (mdiv) {
17439427a0c7Sgrant 	case 1:
17449427a0c7Sgrant 		reg |= MCLK_DIV1;
17459427a0c7Sgrant 		break;
17469427a0c7Sgrant 	case 3:
17479427a0c7Sgrant 		reg |= MCLK_DIV3;
17489427a0c7Sgrant 		break;
17499427a0c7Sgrant 	case 5:
17509427a0c7Sgrant 		reg |= MCLK_DIV5;
17519427a0c7Sgrant 		break;
17529427a0c7Sgrant 	default:
17539427a0c7Sgrant 		reg |= ((mdiv / 2 - 1) << 24) & 0x1f000000;
17549427a0c7Sgrant 		break;
17559427a0c7Sgrant 	}
17569427a0c7Sgrant 
17579427a0c7Sgrant 	switch (sdiv) {
17589427a0c7Sgrant 	case 1:
17599427a0c7Sgrant 		reg |= SCLK_DIV1;
17609427a0c7Sgrant 		break;
17619427a0c7Sgrant 	case 3:
17629427a0c7Sgrant 		reg |= SCLK_DIV3;
17639427a0c7Sgrant 		break;
17649427a0c7Sgrant 	default:
17659427a0c7Sgrant 		reg |= ((sdiv / 2 - 1) << 20) & 0x00f00000;
17669427a0c7Sgrant 		break;
17679427a0c7Sgrant 	}
17689427a0c7Sgrant 
17699427a0c7Sgrant 	reg |= SCLK_MASTER;	/* XXX master mode */
17709427a0c7Sgrant 
17719427a0c7Sgrant 	reg |= SERIAL_64x;
17729427a0c7Sgrant 
17739427a0c7Sgrant 	/* stereo input and output */
177435a2b63bSmacallan 
177535a2b63bSmacallan 	DPRINTF("precision: %d\n", sc->sc_bitspersample);
177635a2b63bSmacallan 	switch(sc->sc_bitspersample) {
177735a2b63bSmacallan 		case 16:
177899276969Smacallan 			wordsize = INPUT_STEREO | INPUT_16BIT |
177999276969Smacallan 			           OUTPUT_STEREO | OUTPUT_16BIT;
178035a2b63bSmacallan 			mcr1 = DEQ_MCR1_SC_64 | DEQ_MCR1_SM_I2S | DEQ_MCR1_W_16;
178135a2b63bSmacallan 			break;
178235a2b63bSmacallan 		case 24:
178399276969Smacallan 			wordsize = INPUT_STEREO | INPUT_24BIT |
178499276969Smacallan 			           OUTPUT_STEREO | OUTPUT_24BIT;
178535a2b63bSmacallan 			mcr1 = DEQ_MCR1_SC_64 | DEQ_MCR1_SM_I2S | DEQ_MCR1_W_24;
178635a2b63bSmacallan 			break;
178735a2b63bSmacallan 		default:
178835a2b63bSmacallan 			printf("%s: unsupported sample size %d\n",
17897d61890bSmacallan 			    device_xname(sc->sc_dev), sc->sc_bitspersample);
179035a2b63bSmacallan 			return EINVAL;
179135a2b63bSmacallan 	}
179235a2b63bSmacallan 
1793a741bccaSmacallan 	if (sc->sc_mode == SNAPPER_IS_TAS3001)
17948b9fcfffSaymeric 		mcr1 |= DEQ_MCR1_ISM_I2S;
17958b9fcfffSaymeric 
1796d974db0aSgarbled 	ows = bus_space_read_4(sc->sc_tag, sc->sc_bsh, I2S_WORDSIZE);
1797d974db0aSgarbled 
17989427a0c7Sgrant 	DPRINTF("I2SSetDataWordSizeReg 0x%08x -> 0x%08x\n",
179935a2b63bSmacallan 	    ows, wordsize);
180035a2b63bSmacallan 	if (ows != wordsize) {
1801a741bccaSmacallan 		bus_space_write_4(sc->sc_tag, sc->sc_bsh, I2S_WORDSIZE,
1802a741bccaSmacallan 		    wordsize);
1803a741bccaSmacallan 		if (sc->sc_mode != SNAPPER_SWVOL)
180435a2b63bSmacallan 			tas3004_write(sc, DEQ_MCR1, &mcr1);
180535a2b63bSmacallan 	}
180635a2b63bSmacallan 
1807d974db0aSgarbled 	x = bus_space_read_4(sc->sc_tag, sc->sc_bsh, I2S_FORMAT);
180835a2b63bSmacallan 	if (x == reg)
180935a2b63bSmacallan 		return 0;        /* No change; do nothing. */
18109427a0c7Sgrant 
18119427a0c7Sgrant 	DPRINTF("I2SSetSerialFormatReg 0x%x -> 0x%x\n",
1812d974db0aSgarbled 	    bus_space_read_4(sc->sc_tag, sc->sc_bsh, + I2S_FORMAT), reg);
181335a2b63bSmacallan 
181435a2b63bSmacallan 	/* Clear CLKSTOPPEND. */
1815d974db0aSgarbled 	bus_space_write_4(sc->sc_tag, sc->sc_bsh, I2S_INT, I2S_INT_CLKSTOPPEND);
181635a2b63bSmacallan 
1817b8ae2bc3Smacallan 	x = obio_read_4(KEYLARGO_FCR1);                /* FCR */
181835a2b63bSmacallan 	x &= ~I2S0CLKEN;                /* XXX I2S0 */
1819b8ae2bc3Smacallan 	obio_write_4(KEYLARGO_FCR1, x);
182035a2b63bSmacallan 
182135a2b63bSmacallan 	/* Wait until clock is stopped. */
182235a2b63bSmacallan 	for (timo = 1000; timo > 0; timo--) {
1823a741bccaSmacallan 		if (bus_space_read_4(sc->sc_tag, sc->sc_bsh, I2S_INT) &
1824a741bccaSmacallan 		    I2S_INT_CLKSTOPPEND)
182535a2b63bSmacallan 			goto done;
182635a2b63bSmacallan 		delay(1);
182735a2b63bSmacallan 	}
182835a2b63bSmacallan 	DPRINTF("snapper_set_rate: timeout\n");
182935a2b63bSmacallan done:
1830d974db0aSgarbled 	bus_space_write_4(sc->sc_tag, sc->sc_bsh, I2S_FORMAT, reg);
18319427a0c7Sgrant 
1832b8ae2bc3Smacallan 	x = obio_read_4(KEYLARGO_FCR1);
183335a2b63bSmacallan 	x |= I2S0CLKEN;
1834b8ae2bc3Smacallan 	obio_write_4(KEYLARGO_FCR1, x);
183535a2b63bSmacallan 
18369427a0c7Sgrant 	return 0;
18379427a0c7Sgrant }
18389427a0c7Sgrant 
18399427a0c7Sgrant const struct tas3004_reg tas3004_initdata = {
184035a2b63bSmacallan 	{ DEQ_MCR1_SC_64 | DEQ_MCR1_SM_I2S | DEQ_MCR1_W_16 },	/* MCR1 */
18419427a0c7Sgrant 	{ 1, 0, 0, 0, 0, 0 },					/* DRC */
18429427a0c7Sgrant 	{ 0, 0, 0, 0, 0, 0 },					/* VOLUME */
18439427a0c7Sgrant 	{ 0x72 },						/* TREBLE */
18449427a0c7Sgrant 	{ 0x72 },						/* BASS */
18459427a0c7Sgrant 	{ 0x10, 0x00, 0x00, 0, 0, 0, 0, 0, 0 },			/* MIXER_L */
18469427a0c7Sgrant 	{ 0x10, 0x00, 0x00, 0, 0, 0, 0, 0, 0 },			/* MIXER_R */
18479427a0c7Sgrant 	{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },	/* BIQUAD */
18489427a0c7Sgrant 	{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },	/* BIQUAD */
18499427a0c7Sgrant 	{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },	/* BIQUAD */
18509427a0c7Sgrant 	{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },	/* BIQUAD */
18519427a0c7Sgrant 	{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },	/* BIQUAD */
18529427a0c7Sgrant 	{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },	/* BIQUAD */
18539427a0c7Sgrant 	{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },	/* BIQUAD */
18549427a0c7Sgrant 	{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },	/* BIQUAD */
18559427a0c7Sgrant 	{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },	/* BIQUAD */
18569427a0c7Sgrant 	{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },	/* BIQUAD */
18579427a0c7Sgrant 	{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },	/* BIQUAD */
18589427a0c7Sgrant 	{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },	/* BIQUAD */
18599427a0c7Sgrant 	{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },	/* BIQUAD */
18609427a0c7Sgrant 	{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },	/* BIQUAD */
18619427a0c7Sgrant 	{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },	/* BIQUAD */
18629427a0c7Sgrant 	{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },	/* BIQUAD */
18639427a0c7Sgrant 	{ 0, 0, 0 },						/* LLB_GAIN */
18649427a0c7Sgrant 	{ 0, 0, 0 },						/* RLB_GAIN */
18651a0d0c16Smacallan 	{ 0 },							/* ACR - line in */
1866d9f9caa0Smacallan 	{ 2 }							/* MCR2 - AllPass mode since we don't use the equalizer anyway */
18679427a0c7Sgrant };
18689427a0c7Sgrant 
18699427a0c7Sgrant const char tas3004_regsize[] = {
18709427a0c7Sgrant 	0,					/* 0x00 */
18719427a0c7Sgrant 	sizeof tas3004_initdata.MCR1,		/* 0x01 */
18729427a0c7Sgrant 	sizeof tas3004_initdata.DRC,		/* 0x02 */
18739427a0c7Sgrant 	0,					/* 0x03 */
18749427a0c7Sgrant 	sizeof tas3004_initdata.VOLUME,		/* 0x04 */
18759427a0c7Sgrant 	sizeof tas3004_initdata.TREBLE,		/* 0x05 */
18769427a0c7Sgrant 	sizeof tas3004_initdata.BASS,		/* 0x06 */
18779427a0c7Sgrant 	sizeof tas3004_initdata.MIXER_L,	/* 0x07 */
18789427a0c7Sgrant 	sizeof tas3004_initdata.MIXER_R,	/* 0x08 */
18799427a0c7Sgrant 	0,					/* 0x09 */
18809427a0c7Sgrant 	sizeof tas3004_initdata.LB0,		/* 0x0a */
18819427a0c7Sgrant 	sizeof tas3004_initdata.LB1,		/* 0x0b */
18829427a0c7Sgrant 	sizeof tas3004_initdata.LB2,		/* 0x0c */
18839427a0c7Sgrant 	sizeof tas3004_initdata.LB3,		/* 0x0d */
18849427a0c7Sgrant 	sizeof tas3004_initdata.LB4,		/* 0x0e */
18859427a0c7Sgrant 	sizeof tas3004_initdata.LB5,		/* 0x0f */
18869427a0c7Sgrant 	sizeof tas3004_initdata.LB6,		/* 0x10 */
18879427a0c7Sgrant 	0,					/* 0x11 */
18889427a0c7Sgrant 	0,					/* 0x12 */
18899427a0c7Sgrant 	sizeof tas3004_initdata.RB0,		/* 0x13 */
18909427a0c7Sgrant 	sizeof tas3004_initdata.RB1,		/* 0x14 */
18919427a0c7Sgrant 	sizeof tas3004_initdata.RB2,		/* 0x15 */
18929427a0c7Sgrant 	sizeof tas3004_initdata.RB3,		/* 0x16 */
18939427a0c7Sgrant 	sizeof tas3004_initdata.RB4,		/* 0x17 */
18949427a0c7Sgrant 	sizeof tas3004_initdata.RB5,		/* 0x18 */
18959427a0c7Sgrant 	sizeof tas3004_initdata.RB6,		/* 0x19 */
18969427a0c7Sgrant 	0,0,0,0, 0,0,
18979427a0c7Sgrant 	0,					/* 0x20 */
18989427a0c7Sgrant 	sizeof tas3004_initdata.LLB,		/* 0x21 */
18999427a0c7Sgrant 	sizeof tas3004_initdata.RLB,		/* 0x22 */
19009427a0c7Sgrant 	sizeof tas3004_initdata.LLB_GAIN,	/* 0x23 */
19019427a0c7Sgrant 	sizeof tas3004_initdata.RLB_GAIN,	/* 0x24 */
19029427a0c7Sgrant 	0,0,0,0, 0,0,0,0, 0,0,0,
19039427a0c7Sgrant 	0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
19049427a0c7Sgrant 	sizeof tas3004_initdata.ACR,		/* 0x40 */
19059427a0c7Sgrant 	0,					/* 0x41 */
19069427a0c7Sgrant 	0,					/* 0x42 */
19079427a0c7Sgrant 	sizeof tas3004_initdata.MCR2		/* 0x43 */
19089427a0c7Sgrant };
19099427a0c7Sgrant 
1910e3693941Smacallan static int
tas3004_write(struct snapper_softc * sc,u_int reg,const void * data)191193293b9eSkent tas3004_write(struct snapper_softc *sc, u_int reg, const void *data)
19129427a0c7Sgrant {
19139427a0c7Sgrant 	int size;
1914d9f9caa0Smacallan 	static char regblock[sizeof(struct tas3004_reg)+1];
19159427a0c7Sgrant 
1916c38a78caSjmcneill 	if (sc->sc_i2c == NULL)
1917c38a78caSjmcneill 		return 0;
1918c38a78caSjmcneill 
19199427a0c7Sgrant 	KASSERT(reg < sizeof tas3004_regsize);
19209427a0c7Sgrant 	size = tas3004_regsize[reg];
19219427a0c7Sgrant 	KASSERT(size > 0);
19229427a0c7Sgrant 
192335a2b63bSmacallan 	DPRINTF("reg: %x, %d %d\n", reg, size, ((const char*)data)[0]);
1924d9f9caa0Smacallan 
1925d9f9caa0Smacallan 	regblock[0] = reg;
1926d9f9caa0Smacallan 	memcpy(&regblock[1], data, size);
1927a741bccaSmacallan 	if (sc->sc_mode == SNAPPER_IS_TAS3001) {
19288b9fcfffSaymeric 		if (reg == DEQ_MIXER_L || reg == DEQ_MIXER_R)
19298b9fcfffSaymeric 			size = 3;
1930a741bccaSmacallan 		else if (reg == DEQ_DRC || reg == DEQ_ACR ||
1931a741bccaSmacallan 			 reg == DEQ_MCR2) {
1932a741bccaSmacallan 			/* these registers are not available on TAS3001 */
1933a741bccaSmacallan 			return 0;
19348b9fcfffSaymeric 		}
19358b9fcfffSaymeric 	}
1936d9f9caa0Smacallan 	iic_acquire_bus(sc->sc_i2c, 0);
19375f55aaa2Smacallan 	iic_exec(sc->sc_i2c, I2C_OP_WRITE, sc->sc_deqaddr, regblock, size + 1,
1938d9f9caa0Smacallan 	    NULL, 0, 0);
1939d9f9caa0Smacallan 	iic_release_bus(sc->sc_i2c, 0);
19409427a0c7Sgrant 
19419427a0c7Sgrant 	return 0;
19429427a0c7Sgrant }
19439427a0c7Sgrant 
1944e3693941Smacallan static int
gpio_read(bus_size_t addr)1945bea393bbSmacallan gpio_read(bus_size_t addr)
19469427a0c7Sgrant {
194793293b9eSkent 
1948bea393bbSmacallan 	if (obio_read_1(addr) & GPIO_DATA)
19499427a0c7Sgrant 		return 1;
19509427a0c7Sgrant 	return 0;
19519427a0c7Sgrant }
19529427a0c7Sgrant 
1953e3693941Smacallan static void
gpio_write(bus_size_t addr,int val)1954bea393bbSmacallan gpio_write(bus_size_t addr, int val)
19559427a0c7Sgrant {
1956bea393bbSmacallan 	uint8_t data;
19579427a0c7Sgrant 
195893293b9eSkent 	data = GPIO_DDR_OUTPUT;
19599427a0c7Sgrant 	if (val)
19609427a0c7Sgrant 		data |= GPIO_DATA;
1961bea393bbSmacallan 	obio_write_1(addr, data);
19629427a0c7Sgrant }
19639427a0c7Sgrant 
19644245aa3cSmacallan int headphone_active = 0;
19654245aa3cSmacallan int lineout_active = 0;
19664245aa3cSmacallan int amp_active = 0;
19679427a0c7Sgrant 
1968e3693941Smacallan static void
snapper_mute_speaker(struct snapper_softc * sc,int mute)196993293b9eSkent snapper_mute_speaker(struct snapper_softc *sc, int mute)
19709427a0c7Sgrant {
1971bea393bbSmacallan 	int x;
19729427a0c7Sgrant 
1973ffe1973eSphx 	if (amp_mute) {
19749427a0c7Sgrant 		DPRINTF("ampmute %d --> ", gpio_read(amp_mute));
19759427a0c7Sgrant 
19769427a0c7Sgrant 		if (mute)
19779427a0c7Sgrant 			x = amp_active;		/* mute */
19789427a0c7Sgrant 		else
19794245aa3cSmacallan 			x = amp_active ^ 1;	/* unmute */
19804245aa3cSmacallan 
19819427a0c7Sgrant 		gpio_write(amp_mute, x);
19829427a0c7Sgrant 
19839427a0c7Sgrant 		DPRINTF("%d\n", gpio_read(amp_mute));
19849427a0c7Sgrant 	}
1985ffe1973eSphx }
19869427a0c7Sgrant 
1987e3693941Smacallan static void
snapper_mute_headphone(struct snapper_softc * sc,int mute)198893293b9eSkent snapper_mute_headphone(struct snapper_softc *sc, int mute)
19899427a0c7Sgrant {
19909427a0c7Sgrant 	u_int x;
19919427a0c7Sgrant 
1992bea393bbSmacallan 	if (headphone_mute != 0) {
19939427a0c7Sgrant 		DPRINTF("headphonemute %d --> ", gpio_read(headphone_mute));
19949427a0c7Sgrant 
19959427a0c7Sgrant 		if (mute)
19969427a0c7Sgrant 			x = headphone_active;	/* mute */
19979427a0c7Sgrant 		else
19984245aa3cSmacallan 			x = headphone_active ^ 1;	/* unmute */
19994245aa3cSmacallan 
20009427a0c7Sgrant 		gpio_write(headphone_mute, x);
20019427a0c7Sgrant 
20029427a0c7Sgrant 		DPRINTF("%d\n", gpio_read(headphone_mute));
20039427a0c7Sgrant 	}
2004ffe1973eSphx }
20059427a0c7Sgrant 
2006202e08cdSmacallan static void
snapper_mute_lineout(struct snapper_softc * sc,int mute)2007202e08cdSmacallan snapper_mute_lineout(struct snapper_softc *sc, int mute)
2008202e08cdSmacallan {
2009202e08cdSmacallan 	u_int x;
2010202e08cdSmacallan 
2011202e08cdSmacallan 	if (lineout_mute != 0) {
2012202e08cdSmacallan 		DPRINTF("lineoutmute %d --> ", gpio_read(lineout_mute));
2013202e08cdSmacallan 
2014202e08cdSmacallan 		if (mute)
2015202e08cdSmacallan 			x = lineout_active;	/* mute */
2016202e08cdSmacallan 		else
20174245aa3cSmacallan 			x = lineout_active ^ 1;	/* unmute */
20184245aa3cSmacallan 
2019202e08cdSmacallan 		gpio_write(lineout_mute, x);
2020202e08cdSmacallan 
2021202e08cdSmacallan 		DPRINTF("%d\n", gpio_read(lineout_mute));
2022202e08cdSmacallan 	}
2023202e08cdSmacallan }
2024202e08cdSmacallan 
2025e3693941Smacallan static int
snapper_cint(void * v)202693293b9eSkent snapper_cint(void *v)
20279427a0c7Sgrant {
2028202e08cdSmacallan 	struct snapper_softc *sc = v;
20299427a0c7Sgrant 	u_int sense;
2030202e08cdSmacallan 	int mask = 1 << 0;
20319427a0c7Sgrant 
2032bea393bbSmacallan 	if (headphone_detect != 0) {
2033bea393bbSmacallan 		sense = obio_read_1(headphone_detect);
20349427a0c7Sgrant 		DPRINTF("headphone detect = 0x%x\n", sense);
20359427a0c7Sgrant 
20369427a0c7Sgrant 		if (((sense & 0x02) >> 1) == headphone_detect_active) {
20379427a0c7Sgrant 			DPRINTF("headphone is inserted\n");
2038202e08cdSmacallan 			mask |= 1 << 1;
2039202e08cdSmacallan 			mask &= ~(1 << 0);
20409427a0c7Sgrant 		} else {
20419427a0c7Sgrant 			DPRINTF("headphone is NOT inserted\n");
20429427a0c7Sgrant 		}
2043ffe1973eSphx 	}
2044202e08cdSmacallan 	if (lineout_detect != 0) {
2045202e08cdSmacallan 		sense = obio_read_1(lineout_detect);
2046202e08cdSmacallan 		DPRINTF("lineout detect = 0x%x\n", sense);
20479427a0c7Sgrant 
2048202e08cdSmacallan 		if (((sense & 0x02) >> 1) == lineout_detect_active) {
2049202e08cdSmacallan 			DPRINTF("lineout is inserted\n");
2050202e08cdSmacallan 			mask |= 1 << 2;
2051202e08cdSmacallan 			mask &= ~(1 << 0);
2052202e08cdSmacallan 		} else {
2053202e08cdSmacallan 			DPRINTF("lineout is NOT inserted\n");
2054202e08cdSmacallan 		}
2055202e08cdSmacallan 	}
2056202e08cdSmacallan 	if (mask != sc->sc_output_mask) {
2057202e08cdSmacallan 		sc->sc_output_mask = mask;
2058202e08cdSmacallan 		if (mask & (1 << 0)) {
2059202e08cdSmacallan 			snapper_mute_speaker(sc, 0);
2060202e08cdSmacallan 		} else snapper_mute_speaker(sc, 1);
2061202e08cdSmacallan 		if (mask & (1 << 1)) {
2062202e08cdSmacallan 			snapper_mute_headphone(sc, 0);
2063202e08cdSmacallan 		} else snapper_mute_headphone(sc, 1);
2064202e08cdSmacallan 		if (mask & (1 << 2)) {
2065202e08cdSmacallan 			snapper_mute_lineout(sc, 0);
2066202e08cdSmacallan 		} else snapper_mute_lineout(sc, 1);
2067202e08cdSmacallan 	}
20689427a0c7Sgrant 	return 1;
20699427a0c7Sgrant }
20709427a0c7Sgrant 
20719427a0c7Sgrant #define reset_active 0	/* XXX OF */
20729427a0c7Sgrant 
20739427a0c7Sgrant #define DEQ_WRITE(sc, reg, addr) \
20749427a0c7Sgrant 	if (tas3004_write(sc, reg, addr)) goto err
20759427a0c7Sgrant 
2076e3693941Smacallan static int
tas3004_init(struct snapper_softc * sc)207793293b9eSkent tas3004_init(struct snapper_softc *sc)
20789427a0c7Sgrant {
20799427a0c7Sgrant 
20809427a0c7Sgrant 	/* No reset port.  Nothing to do. */
2081bea393bbSmacallan 	if (audio_hw_reset == 0)
20829427a0c7Sgrant 		goto noreset;
20839427a0c7Sgrant 
20849427a0c7Sgrant 	/* Reset TAS3004. */
20859427a0c7Sgrant 	gpio_write(audio_hw_reset, !reset_active);	/* Negate RESET */
20869427a0c7Sgrant 	delay(100000);				/* XXX Really needed? */
20879427a0c7Sgrant 
20889427a0c7Sgrant 	gpio_write(audio_hw_reset, reset_active);	/* Assert RESET */
20899427a0c7Sgrant 	delay(1);
20909427a0c7Sgrant 
20919427a0c7Sgrant 	gpio_write(audio_hw_reset, !reset_active);	/* Negate RESET */
20929427a0c7Sgrant 	delay(10000);
20939427a0c7Sgrant 
20949427a0c7Sgrant noreset:
209599276969Smacallan 	if ((sc->sc_mode == SNAPPER_IS_TAS3004) ||
209699276969Smacallan 	    (sc->sc_mode == SNAPPER_IS_TAS3001)) {
20979427a0c7Sgrant 		DEQ_WRITE(sc, DEQ_LB0, tas3004_initdata.LB0);
20989427a0c7Sgrant 		DEQ_WRITE(sc, DEQ_LB1, tas3004_initdata.LB1);
20999427a0c7Sgrant 		DEQ_WRITE(sc, DEQ_LB2, tas3004_initdata.LB2);
21009427a0c7Sgrant 		DEQ_WRITE(sc, DEQ_LB3, tas3004_initdata.LB3);
21019427a0c7Sgrant 		DEQ_WRITE(sc, DEQ_LB4, tas3004_initdata.LB4);
21029427a0c7Sgrant 		DEQ_WRITE(sc, DEQ_LB5, tas3004_initdata.LB5);
21039427a0c7Sgrant 		DEQ_WRITE(sc, DEQ_LB6, tas3004_initdata.LB6);
21049427a0c7Sgrant 		DEQ_WRITE(sc, DEQ_RB0, tas3004_initdata.RB0);
21059427a0c7Sgrant 		DEQ_WRITE(sc, DEQ_RB1, tas3004_initdata.RB1);
21069427a0c7Sgrant 		DEQ_WRITE(sc, DEQ_RB1, tas3004_initdata.RB1);
21079427a0c7Sgrant 		DEQ_WRITE(sc, DEQ_RB2, tas3004_initdata.RB2);
21089427a0c7Sgrant 		DEQ_WRITE(sc, DEQ_RB3, tas3004_initdata.RB3);
21099427a0c7Sgrant 		DEQ_WRITE(sc, DEQ_RB4, tas3004_initdata.RB4);
21109427a0c7Sgrant 		DEQ_WRITE(sc, DEQ_RB5, tas3004_initdata.RB5);
21119427a0c7Sgrant 		DEQ_WRITE(sc, DEQ_MCR1, tas3004_initdata.MCR1);
21129427a0c7Sgrant 		DEQ_WRITE(sc, DEQ_MCR2, tas3004_initdata.MCR2);
21139427a0c7Sgrant 		DEQ_WRITE(sc, DEQ_DRC, tas3004_initdata.DRC);
21149427a0c7Sgrant 		DEQ_WRITE(sc, DEQ_VOLUME, tas3004_initdata.VOLUME);
21159427a0c7Sgrant 		DEQ_WRITE(sc, DEQ_TREBLE, tas3004_initdata.TREBLE);
21169427a0c7Sgrant 		DEQ_WRITE(sc, DEQ_BASS, tas3004_initdata.BASS);
21179427a0c7Sgrant 		DEQ_WRITE(sc, DEQ_MIXER_L, tas3004_initdata.MIXER_L);
21189427a0c7Sgrant 		DEQ_WRITE(sc, DEQ_MIXER_R, tas3004_initdata.MIXER_R);
21199427a0c7Sgrant 		DEQ_WRITE(sc, DEQ_LLB, tas3004_initdata.LLB);
21209427a0c7Sgrant 		DEQ_WRITE(sc, DEQ_RLB, tas3004_initdata.RLB);
21219427a0c7Sgrant 		DEQ_WRITE(sc, DEQ_LLB_GAIN, tas3004_initdata.LLB_GAIN);
21229427a0c7Sgrant 		DEQ_WRITE(sc, DEQ_RLB_GAIN, tas3004_initdata.RLB_GAIN);
21239427a0c7Sgrant 		DEQ_WRITE(sc, DEQ_ACR, tas3004_initdata.ACR);
212499276969Smacallan 	}
21259427a0c7Sgrant 	return 0;
21269427a0c7Sgrant err:
21279427a0c7Sgrant 	printf("tas3004_init: error\n");
21289427a0c7Sgrant 	return -1;
21299427a0c7Sgrant }
21309427a0c7Sgrant 
2131e3693941Smacallan static void
snapper_init(struct snapper_softc * sc,int node)213293293b9eSkent snapper_init(struct snapper_softc *sc, int node)
21339427a0c7Sgrant {
21349427a0c7Sgrant 	int gpio;
2135202e08cdSmacallan 	int headphone_detect_intr, lineout_detect_intr;
21364245aa3cSmacallan 	uint32_t gpio_base, reg[1], fcreg, buf[8];
2137cfe2093dSrin 	char intr_xname[INTRDEVNAMEBUF];
21389427a0c7Sgrant #ifdef SNAPPER_DEBUG
21399427a0c7Sgrant 	char fcr[32];
21409427a0c7Sgrant 
21419a5d3f28Schristos 	snprintb(fcr, sizeof(fcr),  FCR3C_BITMASK, obio_read_4(KEYLARGO_FCR1));
2142eabd5e1dSmsaitoh 	printf("FCR(0x3c) %s\n", fcr);
21439427a0c7Sgrant #endif
21446bde8e48Smacallan 	fcreg = obio_read_4(KEYLARGO_FCR1);
21456bde8e48Smacallan 	fcreg |= I2S0CLKEN | I2S0EN;
21466bde8e48Smacallan 	obio_write_4(KEYLARGO_FCR1, fcreg);
21476bde8e48Smacallan 
214893293b9eSkent 	headphone_detect_intr = -1;
2149202e08cdSmacallan 	lineout_detect_intr = -1;
21509427a0c7Sgrant 
215123a7584dSgarbled 	gpio = of_getnode_byname(OF_parent(node), "gpio");
2152ffe1973eSphx 	if (OF_getprop(gpio, "reg", reg, sizeof(reg)) == sizeof(reg))
2153ffe1973eSphx 		gpio_base = reg[0];
2154ffe1973eSphx 	else
2155ffe1973eSphx 		gpio_base = 0;
2156ffe1973eSphx 	DPRINTF(" /gpio 0x%x@0x%x\n", (unsigned)gpio, gpio_base);
2157ffe1973eSphx 
21589427a0c7Sgrant 	gpio = OF_child(gpio);
21599427a0c7Sgrant 	while (gpio) {
2160275e3107Smacallan 		char name[64], audio_gpio[64], sid[64];
21619427a0c7Sgrant 		int intr[2];
2162bea393bbSmacallan 		bus_size_t addr;
21639427a0c7Sgrant 
2164c363a9cbScegger 		memset(name, 0, sizeof name);
2165c363a9cbScegger 		memset(audio_gpio, 0, sizeof audio_gpio);
21669427a0c7Sgrant 		addr = 0;
21679427a0c7Sgrant 		OF_getprop(gpio, "name", name, sizeof name);
21689427a0c7Sgrant 		OF_getprop(gpio, "audio-gpio", audio_gpio, sizeof audio_gpio);
2169275e3107Smacallan 		OF_getprop(gpio, "one-wire-bus", sid, sizeof sid);
2170ffe1973eSphx 		if (OF_getprop(gpio, "AAPL,address", &addr, sizeof addr) == -1)
2171ffe1973eSphx 			if (OF_getprop(gpio, "reg", reg, sizeof reg)
2172ffe1973eSphx 			    == sizeof reg)
2173bea393bbSmacallan 				addr = gpio_base + reg[0];
2174bea393bbSmacallan 		/*
2175bea393bbSmacallan 		 * XXX
2176bea393bbSmacallan 		 * APL,address contains the absolute address, we only want the
2177bea393bbSmacallan 		 * offset from mac-io's base address
2178bea393bbSmacallan 		 */
2179bea393bbSmacallan 		addr &= 0x7fff;
2180bea393bbSmacallan 		DPRINTF(" 0x%x %s %s %08x\n", gpio, name, audio_gpio, addr);
21819427a0c7Sgrant 
21829427a0c7Sgrant 		/* gpio5 */
2183ffe1973eSphx 		if (strcmp(audio_gpio, "headphone-mute") == 0 ||
21844245aa3cSmacallan 		    strcmp(name, "headphone-mute") == 0) {
21859427a0c7Sgrant 			headphone_mute = addr;
21864245aa3cSmacallan 			if (OF_getprop(gpio,
21874245aa3cSmacallan 			    "platform-do-headphone-mute", buf, 20) == 20) {
21884245aa3cSmacallan 				headphone_active = buf[3] & 1;
21894245aa3cSmacallan 				DPRINTF("platform-do-headphone-mute %d\n",
21904245aa3cSmacallan 				    headphone_active);
21914245aa3cSmacallan 			}
21924245aa3cSmacallan 		}
21939427a0c7Sgrant 		/* gpio6 */
2194ffe1973eSphx 		if (strcmp(audio_gpio, "amp-mute") == 0 ||
21954245aa3cSmacallan 		    strcmp(name, "amp-mute") == 0) {
21969427a0c7Sgrant 			amp_mute = addr;
21974245aa3cSmacallan 			if (OF_getprop(gpio,
21984245aa3cSmacallan 			    "platform-do-amp-mute", buf, 20) == 20) {
21994245aa3cSmacallan 				amp_active = buf[3] & 1;
22004245aa3cSmacallan 				DPRINTF("platform-do-amp-mute %d\n",
22014245aa3cSmacallan 				    amp_active);
22024245aa3cSmacallan 			}
22034245aa3cSmacallan 		}
22049427a0c7Sgrant 		/* extint-gpio15 */
2205ffe1973eSphx 		if (strcmp(audio_gpio, "headphone-detect") == 0 ||
2206ffe1973eSphx 		    strcmp(name, "headphone-detect") == 0) {
2207275e3107Smacallan 		    	uint32_t act = 0;
22089427a0c7Sgrant 			headphone_detect = addr;
22094ac14a66Smacallan 			OF_getprop(gpio, "audio-gpio-active-state", &act, 4);
22104ac14a66Smacallan 			headphone_detect_active = act;
2211ffe1973eSphx 			if (OF_getprop(gpio, "interrupts", intr, 8) == 8) {
22129427a0c7Sgrant 				headphone_detect_intr = intr[0];
22139427a0c7Sgrant 			}
2214ffe1973eSphx 		}
2215202e08cdSmacallan 		if (strcmp(audio_gpio, "lineout-mute") == 0 ||
221628979bf0Smartin 		    strcmp(name, "lineout-mute") == 0 ||
22174245aa3cSmacallan 		    strcmp(name, "line-output-mute") == 0) {
2218202e08cdSmacallan 			lineout_mute = addr;
22194245aa3cSmacallan 			if (OF_getprop(gpio,
22204245aa3cSmacallan 			    "platform-do-lineout-mute", buf, 20) == 20) {
22214245aa3cSmacallan 				lineout_active = buf[3] & 1;
22224245aa3cSmacallan 				DPRINTF("platform-do-lineout-mute %d\n",
22234245aa3cSmacallan 				    lineout_active);
22244245aa3cSmacallan 			}
22254245aa3cSmacallan 		}
2226202e08cdSmacallan 		if (strcmp(audio_gpio, "lineout-detect") == 0 ||
222728979bf0Smartin 		    strcmp(name, "lineout-detect") == 0 ||
222828979bf0Smartin 		    strcmp(name, "line-output-detect") == 0) {
2229202e08cdSmacallan 		    	uint32_t act = 0;
2230202e08cdSmacallan 			lineout_detect = addr;
2231202e08cdSmacallan 			OF_getprop(gpio, "audio-gpio-active-state", &act, 4);
2232202e08cdSmacallan 			lineout_detect_active = act;
2233202e08cdSmacallan 			if (OF_getprop(gpio, "interrupts", intr, 8) == 8) {
2234202e08cdSmacallan 				lineout_detect_intr = intr[0];
2235202e08cdSmacallan 			}
2236202e08cdSmacallan 		}
2237275e3107Smacallan 		/* extint-gpio16 on Quicksilver */
2238275e3107Smacallan 		if (strcmp(sid, "speaker-id") == 0) {
2239275e3107Smacallan 			owaddr = addr;
2240275e3107Smacallan 		}
22419427a0c7Sgrant 		/* gpio11 (keywest-11) */
2242ffe1973eSphx 		if (strcmp(audio_gpio, "audio-hw-reset") == 0 ||
2243ffe1973eSphx 		    strcmp(name, "hw-reset") == 0)
22449427a0c7Sgrant 			audio_hw_reset = addr;
2245ffe1973eSphx 
22469427a0c7Sgrant 		gpio = OF_peer(gpio);
22479427a0c7Sgrant 	}
2248ffe1973eSphx 
2249275e3107Smacallan 	if (owaddr != -1) snapper_setup_ow(sc);
2250275e3107Smacallan 
2251bea393bbSmacallan 	DPRINTF(" headphone-mute %x\n", headphone_mute);
2252202e08cdSmacallan 	DPRINTF(" lineout-mute %x\n", lineout_mute);
2253bea393bbSmacallan 	DPRINTF(" amp-mute %x\n", amp_mute);
2254bea393bbSmacallan 	DPRINTF(" headphone-detect %x\n", headphone_detect);
22559427a0c7Sgrant 	DPRINTF(" headphone-detect active %x\n", headphone_detect_active);
22569427a0c7Sgrant 	DPRINTF(" headphone-detect intr %x\n", headphone_detect_intr);
2257202e08cdSmacallan 	DPRINTF(" lineout-detect %x\n", lineout_detect);
2258202e08cdSmacallan 	DPRINTF(" lineout-detect active %x\n", lineout_detect_active);
2259202e08cdSmacallan 	DPRINTF(" lineout-detect intr %x\n", lineout_detect_intr);
2260bea393bbSmacallan 	DPRINTF(" audio-hw-reset %x\n", audio_hw_reset);
22619427a0c7Sgrant 
2262cfe2093dSrin 	if (headphone_detect_intr != -1) {
2263cfe2093dSrin 		snprintf(intr_xname, sizeof(intr_xname), "%s headphone",
2264cfe2093dSrin 		    device_xname(sc->sc_dev));
22654245aa3cSmacallan 		intr_establish_xname(headphone_detect_intr, IST_EDGE,
22664245aa3cSmacallan 		    IPL_AUDIO, snapper_cint, sc, intr_xname);
2267cfe2093dSrin 	}
22689427a0c7Sgrant 
2269202e08cdSmacallan 	if (lineout_detect_intr != -1) {
2270202e08cdSmacallan 		snprintf(intr_xname, sizeof(intr_xname), "%s line out",
2271202e08cdSmacallan 		    device_xname(sc->sc_dev));
2272202e08cdSmacallan 		intr_establish_xname(lineout_detect_intr, IST_EDGE, IPL_AUDIO,
2273202e08cdSmacallan 		    snapper_cint, sc, intr_xname);
2274202e08cdSmacallan 	}
2275202e08cdSmacallan 
227635a2b63bSmacallan 	sc->sc_rate = 44100;	/* default rate */
227735a2b63bSmacallan 	sc->sc_bitspersample = 16;
22789427a0c7Sgrant 
22799427a0c7Sgrant 	/* Enable headphone interrupt? */
2280bea393bbSmacallan 	if (headphone_detect != 0) {
2281bea393bbSmacallan 		obio_write_1(headphone_detect,
2282bea393bbSmacallan 		    obio_read_1(headphone_detect) | 0x80);
2283ffe1973eSphx 	}
2284202e08cdSmacallan 	if (lineout_detect != 0) {
2285202e08cdSmacallan 		obio_write_1(lineout_detect,
2286202e08cdSmacallan 		    obio_read_1(lineout_detect) | 0x80);
2287202e08cdSmacallan 	}
22889427a0c7Sgrant 
22899427a0c7Sgrant 	if (tas3004_init(sc))
22909427a0c7Sgrant 		return;
22919427a0c7Sgrant 
22929427a0c7Sgrant 	/* Update headphone status. */
22939427a0c7Sgrant 	snapper_cint(sc);
22949427a0c7Sgrant 
229535a2b63bSmacallan 	snapper_set_volume(sc, 128, 128);
2296a741bccaSmacallan 	snapper_set_bass(sc, 128);
2297a741bccaSmacallan 	snapper_set_treble(sc, 128);
2298d9f9caa0Smacallan 
22991a0d0c16Smacallan 	/* Record source defaults to line in.  This reflects the
23005f55aaa2Smacallan 	 * default value for the ACR (see tas3004_initdata).
23015f55aaa2Smacallan 	 */
23021a0d0c16Smacallan 	sc->sc_record_source = 1 << 1;
23035f55aaa2Smacallan 
2304d9f9caa0Smacallan 	/* We mute the analog input for now */
2305a741bccaSmacallan 	sc->mixer[0] = 128;
2306a741bccaSmacallan 	sc->mixer[1] = 128;
2307d9f9caa0Smacallan 	sc->mixer[2] = 0;
230899276969Smacallan 	if (sc->sc_mode == SNAPPER_IS_TAS3001) {
230999276969Smacallan 		sc->mixer[3] = 0;
231099276969Smacallan 	} else
2311a741bccaSmacallan 		sc->mixer[3] = 128;
2312a741bccaSmacallan 	sc->mixer[4] = 128;
2313d9f9caa0Smacallan 	sc->mixer[5] = 0;
2314d9f9caa0Smacallan 	snapper_write_mixers(sc);
23159427a0c7Sgrant }
2316275e3107Smacallan 
2317275e3107Smacallan static void
snapper_setup_ow(struct snapper_softc * sc)2318275e3107Smacallan snapper_setup_ow(struct snapper_softc *sc)
2319275e3107Smacallan {
2320275e3107Smacallan 	struct onewirebus_attach_args oba;
2321275e3107Smacallan 
2322275e3107Smacallan 	/* Attach 1-Wire bus */
2323275e3107Smacallan 	sc->sc_ow_bus.bus_cookie = sc;
2324275e3107Smacallan 	sc->sc_ow_bus.bus_reset = snapper_ow_reset;
2325275e3107Smacallan 	sc->sc_ow_bus.bus_read_bit = snapper_ow_read_bit;
2326275e3107Smacallan 	sc->sc_ow_bus.bus_write_bit = snapper_ow_write_bit;
2327275e3107Smacallan 
2328275e3107Smacallan 	memset(&oba, 0, sizeof(oba));
2329275e3107Smacallan 	oba.oba_bus = &sc->sc_ow_bus;
23302685996bSthorpej 	sc->sc_ow_dev = config_found(sc->sc_dev, &oba, onewirebus_print,
2331c7fb772bSthorpej 	    CFARGS(.iattr = "onewirebus"));
2332275e3107Smacallan }
2333275e3107Smacallan 
2334275e3107Smacallan static int
snapper_ow_reset(void * cookie)2335275e3107Smacallan snapper_ow_reset(void *cookie)
2336275e3107Smacallan {
2337275e3107Smacallan 	return (onewire_bb_reset(&snapper_bbops, cookie));
2338275e3107Smacallan }
2339275e3107Smacallan 
2340275e3107Smacallan static int
snapper_ow_read_bit(void * cookie)2341275e3107Smacallan snapper_ow_read_bit(void *cookie)
2342275e3107Smacallan {
2343275e3107Smacallan 	return (onewire_bb_read_bit(&snapper_bbops, cookie));
2344275e3107Smacallan }
2345275e3107Smacallan 
2346275e3107Smacallan static void
snapper_ow_write_bit(void * cookie,int bit)2347275e3107Smacallan snapper_ow_write_bit(void *cookie, int bit)
2348275e3107Smacallan {
2349275e3107Smacallan 	onewire_bb_write_bit(&snapper_bbops, cookie, bit);
2350275e3107Smacallan }
2351275e3107Smacallan 
2352275e3107Smacallan static void
snapper_bb_rx(void * cookie)2353275e3107Smacallan snapper_bb_rx(void *cookie)
2354275e3107Smacallan {
2355275e3107Smacallan 	struct snapper_softc *sc = cookie;
2356275e3107Smacallan 
2357275e3107Smacallan 	sc->sc_ow_data &= ~GPIO_DDR_OUTPUT;
2358275e3107Smacallan 	obio_write_1(owaddr, sc->sc_ow_data);
2359275e3107Smacallan }
2360275e3107Smacallan 
2361275e3107Smacallan static void
snapper_bb_tx(void * cookie)2362275e3107Smacallan snapper_bb_tx(void *cookie)
2363275e3107Smacallan {
2364275e3107Smacallan 	struct snapper_softc *sc = cookie;
2365275e3107Smacallan 
2366275e3107Smacallan 	sc->sc_ow_data |= GPIO_DDR_OUTPUT;
2367275e3107Smacallan 	obio_write_1(owaddr, sc->sc_ow_data);
2368275e3107Smacallan }
2369275e3107Smacallan 
snapper_bb_get(void * cookie)2370275e3107Smacallan static int snapper_bb_get(void *cookie)
2371275e3107Smacallan {
2372275e3107Smacallan 	int data = (obio_read_1(owaddr) & GPIO_LEVEL) ? 1 : 0;
2373275e3107Smacallan 	return data;
2374275e3107Smacallan }
2375275e3107Smacallan 
snapper_bb_set(void * cookie,int bit)2376275e3107Smacallan static void snapper_bb_set(void *cookie, int bit)
2377275e3107Smacallan {
2378275e3107Smacallan 	struct snapper_softc *sc = cookie;
2379275e3107Smacallan 
2380275e3107Smacallan 	if (bit) {
2381275e3107Smacallan 		sc->sc_ow_data |= GPIO_DATA;
2382275e3107Smacallan 	} else
2383275e3107Smacallan 		sc->sc_ow_data &= ~GPIO_DATA;
2384275e3107Smacallan 
2385275e3107Smacallan 	obio_write_1(owaddr, sc->sc_ow_data);
2386275e3107Smacallan }
2387