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, ®);
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(®block[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