1*bef84f10Smacallan /* $NetBSD: awacs.c,v 1.52 2023/08/30 08:38:51 macallan Exp $ */
2308d7eb3Stsubai
3308d7eb3Stsubai /*-
4308d7eb3Stsubai * Copyright (c) 2000 Tsubai Masanari. All rights reserved.
5308d7eb3Stsubai *
6308d7eb3Stsubai * Redistribution and use in source and binary forms, with or without
7308d7eb3Stsubai * modification, are permitted provided that the following conditions
8308d7eb3Stsubai * are met:
9308d7eb3Stsubai * 1. Redistributions of source code must retain the above copyright
10308d7eb3Stsubai * notice, this list of conditions and the following disclaimer.
11308d7eb3Stsubai * 2. Redistributions in binary form must reproduce the above copyright
12308d7eb3Stsubai * notice, this list of conditions and the following disclaimer in the
13308d7eb3Stsubai * documentation and/or other materials provided with the distribution.
14308d7eb3Stsubai * 3. The name of the author may not be used to endorse or promote products
15308d7eb3Stsubai * derived from this software without specific prior written permission.
16308d7eb3Stsubai *
17308d7eb3Stsubai * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18308d7eb3Stsubai * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19308d7eb3Stsubai * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20308d7eb3Stsubai * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21308d7eb3Stsubai * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22308d7eb3Stsubai * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23308d7eb3Stsubai * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24308d7eb3Stsubai * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25308d7eb3Stsubai * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26308d7eb3Stsubai * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27308d7eb3Stsubai */
28308d7eb3Stsubai
294b2744bfSlukem #include <sys/cdefs.h>
30*bef84f10Smacallan __KERNEL_RCSID(0, "$NetBSD: awacs.c,v 1.52 2023/08/30 08:38:51 macallan Exp $");
314b2744bfSlukem
32308d7eb3Stsubai #include <sys/param.h>
33308d7eb3Stsubai #include <sys/audioio.h>
34308d7eb3Stsubai #include <sys/device.h>
35308d7eb3Stsubai #include <sys/systm.h>
363fff0522Smacallan #include <sys/kthread.h>
373fff0522Smacallan #include <sys/kernel.h>
388a962f23Sjmcneill #include <sys/mutex.h>
398a962f23Sjmcneill #include <sys/condvar.h>
40308d7eb3Stsubai
41e622eac4Sisaki #include <dev/audio/audio_if.h>
42308d7eb3Stsubai
43fbcd8328Smacallan #include <uvm/uvm_extern.h>
44308d7eb3Stsubai #include <machine/autoconf.h>
45308d7eb3Stsubai #include <machine/pio.h>
46d2f0c68dSmatt
47d2f0c68dSmatt #include <dev/ofw/openfirm.h>
48308d7eb3Stsubai #include <macppc/dev/dbdma.h>
49308d7eb3Stsubai
500990226cSmacallan #include <dev/i2c/sgsmixvar.h>
510990226cSmacallan #include "sgsmix.h"
523fff0522Smacallan #include "opt_awacs.h"
530990226cSmacallan
54308d7eb3Stsubai #ifdef AWACS_DEBUG
55308d7eb3Stsubai # define DPRINTF printf
56308d7eb3Stsubai #else
57308d7eb3Stsubai # define DPRINTF while (0) printf
58308d7eb3Stsubai #endif
59308d7eb3Stsubai
60308d7eb3Stsubai struct awacs_softc {
61b6c6870cSmacallan device_t sc_dev;
62e3b706f1Smacallan bus_space_tag_t sc_tag;
63e3b706f1Smacallan bus_space_handle_t sc_regh;
64e3b706f1Smacallan bus_space_handle_t sc_idmah;
65e3b706f1Smacallan bus_space_handle_t sc_odmah;
661ffa7b76Swiz void (*sc_ointr)(void *); /* DMA completion intr handler */
67308d7eb3Stsubai void *sc_oarg; /* arg for sc_ointr() */
68308d7eb3Stsubai int sc_opages; /* # of output pages */
69308d7eb3Stsubai
701ffa7b76Swiz void (*sc_iintr)(void *); /* DMA completion intr handler */
71308d7eb3Stsubai void *sc_iarg; /* arg for sc_iintr() */
72308d7eb3Stsubai
733fff0522Smacallan uint32_t sc_record_source; /* recording source mask */
743fff0522Smacallan uint32_t sc_output_mask; /* output mask */
753fff0522Smacallan uint32_t sc_headphones_mask; /* which reading of the gpio means */
763fff0522Smacallan uint32_t sc_headphones_in; /* headphones are present */
77308d7eb3Stsubai
780990226cSmacallan int sc_screamer;
790990226cSmacallan int sc_have_perch;
803fff0522Smacallan int vol_l, vol_r;
810990226cSmacallan int sc_bass, sc_treble;
8288ab7da9Sad lwp_t *sc_thread;
838a962f23Sjmcneill kcondvar_t sc_event;
843fff0522Smacallan int sc_output_wanted;
855297b0caSjmcneill int sc_need_parallel_output;
860990226cSmacallan #if NSGSMIX > 0
87b6c6870cSmacallan device_t sc_sgsmix;
880990226cSmacallan #endif
890990226cSmacallan
90308d7eb3Stsubai u_int sc_codecctl0;
91308d7eb3Stsubai u_int sc_codecctl1;
92308d7eb3Stsubai u_int sc_codecctl2;
93308d7eb3Stsubai u_int sc_codecctl4;
940990226cSmacallan u_int sc_codecctl5;
950990226cSmacallan u_int sc_codecctl6;
960990226cSmacallan u_int sc_codecctl7;
97308d7eb3Stsubai u_int sc_soundctl;
98308d7eb3Stsubai
99308d7eb3Stsubai struct dbdma_regmap *sc_odma;
100308d7eb3Stsubai struct dbdma_regmap *sc_idma;
101308d7eb3Stsubai struct dbdma_command *sc_odmacmd;
102308d7eb3Stsubai struct dbdma_command *sc_idmacmd;
10323b5d914Skent
1048a962f23Sjmcneill kmutex_t sc_lock;
105*bef84f10Smacallan kmutex_t sc_event_lock;
1068a962f23Sjmcneill kmutex_t sc_intr_lock;
107308d7eb3Stsubai };
108308d7eb3Stsubai
109b6c6870cSmacallan static int awacs_match(device_t, struct cfdata *, void *);
110b6c6870cSmacallan static void awacs_attach(device_t, device_t, void *);
1113fff0522Smacallan static int awacs_intr(void *);
1123fff0522Smacallan static int awacs_status_intr(void *);
113308d7eb3Stsubai
114e622eac4Sisaki static int awacs_query_format(void *, audio_format_query_t *);
115e622eac4Sisaki static int awacs_set_format(void *, int,
116e622eac4Sisaki const audio_params_t *, const audio_params_t *,
117e622eac4Sisaki audio_filter_reg_t *, audio_filter_reg_t *);
1183fff0522Smacallan
1193fff0522Smacallan static int awacs_round_blocksize(void *, int, int, const audio_params_t *);
1203fff0522Smacallan static int awacs_trigger_output(void *, void *, void *, int, void (*)(void *),
12123b5d914Skent void *, const audio_params_t *);
1223fff0522Smacallan static int awacs_trigger_input(void *, void *, void *, int, void (*)(void *),
12323b5d914Skent void *, const audio_params_t *);
1243fff0522Smacallan static int awacs_halt_output(void *);
1253fff0522Smacallan static int awacs_halt_input(void *);
1263fff0522Smacallan static int awacs_getdev(void *, struct audio_device *);
1273fff0522Smacallan static int awacs_set_port(void *, mixer_ctrl_t *);
1283fff0522Smacallan static int awacs_get_port(void *, mixer_ctrl_t *);
1293fff0522Smacallan static int awacs_query_devinfo(void *, mixer_devinfo_t *);
1303fff0522Smacallan static size_t awacs_round_buffersize(void *, int, size_t);
1313fff0522Smacallan static int awacs_get_props(void *);
1328a962f23Sjmcneill static void awacs_get_locks(void *, kmutex_t **, kmutex_t **);
133308d7eb3Stsubai
134308d7eb3Stsubai static inline u_int awacs_read_reg(struct awacs_softc *, int);
135308d7eb3Stsubai static inline void awacs_write_reg(struct awacs_softc *, int, int);
1363fff0522Smacallan static void awacs_write_codec(struct awacs_softc *, int);
1373fff0522Smacallan
138b6c6870cSmacallan void awacs_set_volume(struct awacs_softc *, int, int);
1393fff0522Smacallan static void awacs_set_speaker_volume(struct awacs_softc *, int, int);
1403fff0522Smacallan static void awacs_set_ext_volume(struct awacs_softc *, int, int);
1413fff0522Smacallan static void awacs_set_loopthrough_volume(struct awacs_softc *, int, int);
1423fff0522Smacallan static int awacs_set_rate(struct awacs_softc *, const audio_params_t *);
1433fff0522Smacallan static void awacs_select_output(struct awacs_softc *, int);
1443fff0522Smacallan static int awacs_check_headphones(struct awacs_softc *);
1453fff0522Smacallan static void awacs_thread(void *);
1463fff0522Smacallan
1470990226cSmacallan #if NSGSMIX > 0
1480990226cSmacallan static void awacs_set_bass(struct awacs_softc *, int);
1490990226cSmacallan static void awacs_set_treble(struct awacs_softc *, int);
1500990226cSmacallan #endif
151b6c6870cSmacallan static int awacs_setup_sgsmix(device_t);
152308d7eb3Stsubai
153b6c6870cSmacallan CFATTACH_DECL_NEW(awacs, sizeof(struct awacs_softc),
154c5e91d44Sthorpej awacs_match, awacs_attach, NULL, NULL);
155308d7eb3Stsubai
15618f717bbSyamt const struct audio_hw_if awacs_hw_if = {
157e622eac4Sisaki .query_format = awacs_query_format,
158e622eac4Sisaki .set_format = awacs_set_format,
1596291b134Sisaki .round_blocksize = awacs_round_blocksize,
1606291b134Sisaki .halt_output = awacs_halt_output,
1616291b134Sisaki .halt_input = awacs_halt_input,
1626291b134Sisaki .getdev = awacs_getdev,
1636291b134Sisaki .set_port = awacs_set_port,
1646291b134Sisaki .get_port = awacs_get_port,
1656291b134Sisaki .query_devinfo = awacs_query_devinfo,
1666291b134Sisaki .round_buffersize = awacs_round_buffersize,
1676291b134Sisaki .get_props = awacs_get_props,
1686291b134Sisaki .trigger_output = awacs_trigger_output,
1696291b134Sisaki .trigger_input = awacs_trigger_input,
1706291b134Sisaki .get_locks = awacs_get_locks,
171308d7eb3Stsubai };
172308d7eb3Stsubai
173308d7eb3Stsubai struct audio_device awacs_device = {
174308d7eb3Stsubai "AWACS",
175308d7eb3Stsubai "",
176308d7eb3Stsubai "awacs"
177308d7eb3Stsubai };
178308d7eb3Stsubai
179e622eac4Sisaki static const struct audio_format awacs_formats[] = {
180e622eac4Sisaki {
181e622eac4Sisaki .mode = AUMODE_PLAY | AUMODE_RECORD,
182e622eac4Sisaki .encoding = AUDIO_ENCODING_SLINEAR_BE,
183e622eac4Sisaki .validbits = 16,
184e622eac4Sisaki .precision = 16,
185e622eac4Sisaki .channels = 2,
186e622eac4Sisaki .channel_mask = AUFMT_STEREO,
187e622eac4Sisaki .frequency_type = 8,
188e622eac4Sisaki .frequency =
189e622eac4Sisaki { 7350, 8820, 11025, 14700, 17640, 22050, 29400, 44100 },
190e622eac4Sisaki }
19123b5d914Skent };
192e622eac4Sisaki #define AWACS_NFORMATS __arraycount(awacs_formats)
19323b5d914Skent
194308d7eb3Stsubai /* register offset */
195308d7eb3Stsubai #define AWACS_SOUND_CTRL 0x00
196308d7eb3Stsubai #define AWACS_CODEC_CTRL 0x10
197308d7eb3Stsubai #define AWACS_CODEC_STATUS 0x20
198308d7eb3Stsubai #define AWACS_CLIP_COUNT 0x30
199308d7eb3Stsubai #define AWACS_BYTE_SWAP 0x40
200308d7eb3Stsubai
201308d7eb3Stsubai /* sound control */
202308d7eb3Stsubai #define AWACS_INPUT_SUBFRAME0 0x00000001
203308d7eb3Stsubai #define AWACS_INPUT_SUBFRAME1 0x00000002
204308d7eb3Stsubai #define AWACS_INPUT_SUBFRAME2 0x00000004
205308d7eb3Stsubai #define AWACS_INPUT_SUBFRAME3 0x00000008
206308d7eb3Stsubai
207308d7eb3Stsubai #define AWACS_OUTPUT_SUBFRAME0 0x00000010
208308d7eb3Stsubai #define AWACS_OUTPUT_SUBFRAME1 0x00000020
209308d7eb3Stsubai #define AWACS_OUTPUT_SUBFRAME2 0x00000040
210308d7eb3Stsubai #define AWACS_OUTPUT_SUBFRAME3 0x00000080
211308d7eb3Stsubai
212308d7eb3Stsubai #define AWACS_RATE_44100 0x00000000
213308d7eb3Stsubai #define AWACS_RATE_29400 0x00000100
214308d7eb3Stsubai #define AWACS_RATE_22050 0x00000200
215308d7eb3Stsubai #define AWACS_RATE_17640 0x00000300
216308d7eb3Stsubai #define AWACS_RATE_14700 0x00000400
217308d7eb3Stsubai #define AWACS_RATE_11025 0x00000500
218308d7eb3Stsubai #define AWACS_RATE_8820 0x00000600
219308d7eb3Stsubai #define AWACS_RATE_7350 0x00000700
220308d7eb3Stsubai #define AWACS_RATE_MASK 0x00000700
221308d7eb3Stsubai
2223fff0522Smacallan #define AWACS_ERROR 0x00000800
2233fff0522Smacallan #define AWACS_PORTCHG 0x00001000
2243fff0522Smacallan #define AWACS_INTR_ERROR 0x00002000 /* interrupt on error */
2253fff0522Smacallan #define AWACS_INTR_PORTCHG 0x00004000 /* interrupt on port change */
2263fff0522Smacallan
2273fff0522Smacallan #define AWACS_STATUS_SUBFRAME 0x00018000 /* mask */
2286a9cc7b7Sjmcneill
229308d7eb3Stsubai /* codec control */
230308d7eb3Stsubai #define AWACS_CODEC_ADDR0 0x00000000
231308d7eb3Stsubai #define AWACS_CODEC_ADDR1 0x00001000
232308d7eb3Stsubai #define AWACS_CODEC_ADDR2 0x00002000
233308d7eb3Stsubai #define AWACS_CODEC_ADDR4 0x00004000
2340990226cSmacallan #define AWACS_CODEC_ADDR5 0x00005000
2350990226cSmacallan #define AWACS_CODEC_ADDR6 0x00006000
2360990226cSmacallan #define AWACS_CODEC_ADDR7 0x00007000
237308d7eb3Stsubai #define AWACS_CODEC_EMSEL0 0x00000000
238308d7eb3Stsubai #define AWACS_CODEC_EMSEL1 0x00400000
239308d7eb3Stsubai #define AWACS_CODEC_EMSEL2 0x00800000
240308d7eb3Stsubai #define AWACS_CODEC_EMSEL4 0x00c00000
241308d7eb3Stsubai #define AWACS_CODEC_BUSY 0x01000000
242308d7eb3Stsubai
243308d7eb3Stsubai /* cc0 */
244308d7eb3Stsubai #define AWACS_DEFAULT_CD_GAIN 0x000000bb
245308d7eb3Stsubai #define AWACS_INPUT_CD 0x00000200
2460a4ef18aSwiz #define AWACS_INPUT_LINE 0x00000400
2470a4ef18aSwiz #define AWACS_INPUT_MICROPHONE 0x00000800
248308d7eb3Stsubai #define AWACS_INPUT_MASK 0x00000e00
249308d7eb3Stsubai
250308d7eb3Stsubai /* cc1 */
2510990226cSmacallan #define AWACS_LOOP_THROUGH 0x00000040
252308d7eb3Stsubai #define AWACS_MUTE_SPEAKER 0x00000080
253308d7eb3Stsubai #define AWACS_MUTE_HEADPHONE 0x00000200
2540990226cSmacallan #define AWACS_PARALLEL_OUTPUT 0x00000c00
255308d7eb3Stsubai
2560990226cSmacallan /* output */
2570990226cSmacallan #define OUTPUT_SPEAKER 1
2580990226cSmacallan #define OUTPUT_HEADPHONES 2
2593fff0522Smacallan
2603fff0522Smacallan /* codec status */
2613fff0522Smacallan
2623fff0522Smacallan static const char *screamer[] = {"screamer", NULL};
2633fff0522Smacallan
2643fff0522Smacallan /*
2653fff0522Smacallan * list machines that have the headphone detect GPIO reversed here.
2663fff0522Smacallan * so far the only known case is the PowerBook 3400c and similar machines
2673fff0522Smacallan */
2683fff0522Smacallan static const char *detect_reversed[] = {"AAPL,3400/2400",
2693fff0522Smacallan "AAPL,3500",
2703fff0522Smacallan NULL};
2713fff0522Smacallan
27210db3a4bSphx static const char *use_gpio4[] = { "PowerMac3,1",
27310db3a4bSphx "PowerMac3,2",
27410db3a4bSphx "PowerMac3,3",
27571e51863Smacallan NULL};
27671e51863Smacallan
2775297b0caSjmcneill /*
2785297b0caSjmcneill * list of machines that do not require AWACS_PARALLEL_OUTPUT
2795297b0caSjmcneill */
2805297b0caSjmcneill static const char *no_parallel_output[] = { "PowerBook3,1",
2815297b0caSjmcneill NULL};
2825297b0caSjmcneill
2833fff0522Smacallan static int
awacs_match(device_t parent,struct cfdata * match,void * aux)284b6c6870cSmacallan awacs_match(device_t parent, struct cfdata *match, void *aux)
285308d7eb3Stsubai {
28693293b9eSkent struct confargs *ca;
287308d7eb3Stsubai
28893293b9eSkent ca = aux;
289889bccccSsoren
29071e51863Smacallan if (strcmp(ca->ca_name, "awacs") == 0 ||
29171e51863Smacallan strcmp(ca->ca_name, "davbus") == 0)
29271e51863Smacallan return 100;
293308d7eb3Stsubai
294308d7eb3Stsubai if (ca->ca_nreg < 24 || ca->ca_nintr < 12)
295308d7eb3Stsubai return 0;
296308d7eb3Stsubai
29771e51863Smacallan if (strcmp(ca->ca_name, "i2s") == 0)
298308d7eb3Stsubai return 1;
29971e51863Smacallan
30071e51863Smacallan return 0;
301308d7eb3Stsubai }
302308d7eb3Stsubai
3033fff0522Smacallan static void
awacs_attach(device_t parent,device_t self,void * aux)304b6c6870cSmacallan awacs_attach(device_t parent, device_t self, void *aux)
305308d7eb3Stsubai {
30693293b9eSkent struct awacs_softc *sc;
307e3b706f1Smacallan struct confargs *ca = aux;
308b672929bStsubai int cirq, oirq, iirq, cirq_type, oirq_type, iirq_type;
3090990226cSmacallan int len = -1, perch;
3103fff0522Smacallan int root_node;
311cfe2093dSrin char compat[256], intr_xname[INTRDEVNAMEBUF];
312308d7eb3Stsubai
313b6c6870cSmacallan sc = device_private(self);
314b6c6870cSmacallan sc->sc_dev = self;
315e3b706f1Smacallan sc->sc_tag = ca->ca_tag;
316308d7eb3Stsubai
317e3b706f1Smacallan if (bus_space_map(sc->sc_tag, ca->ca_baseaddr + ca->ca_reg[0],
318e3b706f1Smacallan ca->ca_reg[1], 0, &sc->sc_regh) != 0)
319e3b706f1Smacallan printf("couldn't map codec registers\n");
320e3b706f1Smacallan if (bus_space_map(sc->sc_tag, ca->ca_baseaddr + ca->ca_reg[2],
321e3b706f1Smacallan ca->ca_reg[3], BUS_SPACE_MAP_LINEAR, &sc->sc_odmah) != 0)
322e3b706f1Smacallan printf("couldn't map DMA out registers\n");
323e3b706f1Smacallan if (bus_space_map(sc->sc_tag, ca->ca_baseaddr + ca->ca_reg[4],
324e3b706f1Smacallan ca->ca_reg[5], BUS_SPACE_MAP_LINEAR, &sc->sc_idmah) != 0)
325e3b706f1Smacallan printf("couldn't map DMA in registers\n");
326308d7eb3Stsubai
327e3b706f1Smacallan sc->sc_odma = bus_space_vaddr(sc->sc_tag, sc->sc_odmah);
328e3b706f1Smacallan sc->sc_idma = bus_space_vaddr(sc->sc_tag, sc->sc_idmah);
329c9a4f92cSmacallan sc->sc_odmacmd = dbdma_alloc(20 * sizeof(struct dbdma_command), NULL);
330c9a4f92cSmacallan sc->sc_idmacmd = dbdma_alloc(20 * sizeof(struct dbdma_command), NULL);
331308d7eb3Stsubai
332889bccccSsoren if (strcmp(ca->ca_name, "i2s") == 0) {
333889bccccSsoren int node, intr[6];
334889bccccSsoren
335889bccccSsoren node = OF_child(ca->ca_node);
336dec320ebSthorpej if (node == 0) {
337889bccccSsoren printf("no i2s-a child\n");
338889bccccSsoren return;
339889bccccSsoren }
340889bccccSsoren if (OF_getprop(node, "interrupts", intr, sizeof(intr)) == -1) {
341889bccccSsoren printf("no interrupt property\n");
342889bccccSsoren return;
343889bccccSsoren }
344889bccccSsoren
345889bccccSsoren cirq = intr[0];
346889bccccSsoren oirq = intr[2];
347889bccccSsoren iirq = intr[4];
348889bccccSsoren cirq_type = intr[1] ? IST_LEVEL : IST_EDGE;
349889bccccSsoren oirq_type = intr[3] ? IST_LEVEL : IST_EDGE;
350889bccccSsoren iirq_type = intr[5] ? IST_LEVEL : IST_EDGE;
351889bccccSsoren } else if (ca->ca_nintr == 24) {
352b672929bStsubai cirq = ca->ca_intr[0];
353b672929bStsubai oirq = ca->ca_intr[2];
354b672929bStsubai iirq = ca->ca_intr[4];
355b672929bStsubai cirq_type = ca->ca_intr[1] ? IST_LEVEL : IST_EDGE;
356b672929bStsubai oirq_type = ca->ca_intr[3] ? IST_LEVEL : IST_EDGE;
357b672929bStsubai iirq_type = ca->ca_intr[5] ? IST_LEVEL : IST_EDGE;
358b672929bStsubai } else {
359b672929bStsubai cirq = ca->ca_intr[0];
360b672929bStsubai oirq = ca->ca_intr[1];
361b672929bStsubai iirq = ca->ca_intr[2];
362d974db0aSgarbled cirq_type = oirq_type = iirq_type = IST_EDGE;
363b672929bStsubai }
364889bccccSsoren
365cfe2093dSrin snprintf(intr_xname, sizeof(intr_xname), "%s status",
366cfe2093dSrin device_xname(self));
367cfe2093dSrin intr_establish_xname(cirq, cirq_type, IPL_BIO, awacs_status_intr, sc,
368cfe2093dSrin intr_xname);
369cfe2093dSrin
370cfe2093dSrin snprintf(intr_xname, sizeof(intr_xname), "%s out", device_xname(self));
371cfe2093dSrin intr_establish_xname(oirq, oirq_type, IPL_AUDIO, awacs_intr, sc,
372cfe2093dSrin intr_xname);
373cfe2093dSrin
374cfe2093dSrin snprintf(intr_xname, sizeof(intr_xname), "%s in", device_xname(self));
375cfe2093dSrin intr_establish_xname(iirq, iirq_type, IPL_AUDIO, awacs_intr, sc,
376cfe2093dSrin intr_xname);
3778a962f23Sjmcneill
3788a962f23Sjmcneill mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE);
379*bef84f10Smacallan mutex_init(&sc->sc_event_lock, MUTEX_DEFAULT, IPL_NONE);
3808de08fa9Smrg mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_AUDIO);
3818a962f23Sjmcneill
3828a962f23Sjmcneill cv_init(&sc->sc_event, "awacs_wait");
3830990226cSmacallan
3840990226cSmacallan /* check if the chip is a screamer */
38522c149c8Sthorpej sc->sc_screamer = of_compatible(ca->ca_node, screamer);
3863fff0522Smacallan if (!sc->sc_screamer) {
3873fff0522Smacallan /* look for 'sound' child node */
3883fff0522Smacallan int sound_node;
3890990226cSmacallan
3903fff0522Smacallan sound_node = OF_child(ca->ca_node);
3913fff0522Smacallan while ((sound_node != 0) && (!sc->sc_screamer)) {
3920990226cSmacallan
39322c149c8Sthorpej sc->sc_screamer = of_compatible(sound_node, screamer);
3943fff0522Smacallan sound_node = OF_peer(sound_node);
3950990226cSmacallan }
3960990226cSmacallan }
3973fff0522Smacallan
3983fff0522Smacallan if (sc->sc_screamer) {
3990990226cSmacallan printf(" Screamer");
4003fff0522Smacallan }
4010990226cSmacallan
4023fff0522Smacallan printf(": irq %d,%d,%d\n", cirq, oirq, iirq);
4033fff0522Smacallan
4043fff0522Smacallan sc->vol_l = 0;
4053fff0522Smacallan sc->vol_r = 0;
4060990226cSmacallan
407308d7eb3Stsubai sc->sc_soundctl = AWACS_INPUT_SUBFRAME0 | AWACS_OUTPUT_SUBFRAME0 |
4083fff0522Smacallan AWACS_RATE_44100 | AWACS_INTR_PORTCHG;
409308d7eb3Stsubai awacs_write_reg(sc, AWACS_SOUND_CTRL, sc->sc_soundctl);
410308d7eb3Stsubai
411308d7eb3Stsubai sc->sc_codecctl0 = AWACS_CODEC_ADDR0 | AWACS_CODEC_EMSEL0;
412308d7eb3Stsubai sc->sc_codecctl1 = AWACS_CODEC_ADDR1 | AWACS_CODEC_EMSEL0;
413308d7eb3Stsubai sc->sc_codecctl2 = AWACS_CODEC_ADDR2 | AWACS_CODEC_EMSEL0;
414308d7eb3Stsubai sc->sc_codecctl4 = AWACS_CODEC_ADDR4 | AWACS_CODEC_EMSEL0;
4150990226cSmacallan sc->sc_codecctl5 = AWACS_CODEC_ADDR5 | AWACS_CODEC_EMSEL0;
4160990226cSmacallan sc->sc_codecctl6 = AWACS_CODEC_ADDR6 | AWACS_CODEC_EMSEL0;
4170990226cSmacallan sc->sc_codecctl7 = AWACS_CODEC_ADDR7 | AWACS_CODEC_EMSEL0;
418308d7eb3Stsubai
419308d7eb3Stsubai sc->sc_codecctl0 |= AWACS_INPUT_CD | AWACS_DEFAULT_CD_GAIN;
420308d7eb3Stsubai awacs_write_codec(sc, sc->sc_codecctl0);
421308d7eb3Stsubai
4223fff0522Smacallan /* Set loopthrough for external mixer on beige G3 */
4235297b0caSjmcneill sc->sc_codecctl1 |= AWACS_LOOP_THROUGH;
424308d7eb3Stsubai
425b6c6870cSmacallan printf("%s: ", device_xname(sc->sc_dev));
4263fff0522Smacallan
427b5dc0007Smacallan /*
428b5dc0007Smacallan * all(?) awacs have GPIOs to detect if there's something plugged into
429b5dc0007Smacallan * the headphone jack. The other GPIOs are either used for other jacks
430b5dc0007Smacallan * ( the PB3400c's microphone jack for instance ) or unused.
431b5dc0007Smacallan * The problem is that there are at least three different ways how
432b5dc0007Smacallan * those GPIOs are wired to the actual jacks.
433b5dc0007Smacallan * For now we bother only with headphone detection
434b5dc0007Smacallan */
435b5dc0007Smacallan perch = OF_finddevice("/perch");
4363fff0522Smacallan root_node = OF_finddevice("/");
43722c149c8Sthorpej if (of_compatible(root_node, detect_reversed)) {
4383fff0522Smacallan
4393fff0522Smacallan /* 0x02 is for the microphone jack, high active */
4403fff0522Smacallan /*
4413fff0522Smacallan * for some reason the gpio for the headphones jack is low
4423fff0522Smacallan * active on the PB3400 and similar machines
4433fff0522Smacallan */
4443fff0522Smacallan sc->sc_headphones_mask = 0x8;
4453fff0522Smacallan sc->sc_headphones_in = 0x0;
44622c149c8Sthorpej } else if ((perch != -1) || of_compatible(root_node, use_gpio4)) {
4473fff0522Smacallan /*
448b5dc0007Smacallan * this is for the beige G3's 'personality card' which uses
449b5dc0007Smacallan * yet another wiring of the headphone detect GPIOs
45071e51863Smacallan * some G4s use it as well
4513fff0522Smacallan */
4523fff0522Smacallan sc->sc_headphones_mask = 0x04;
4533fff0522Smacallan sc->sc_headphones_in = 0x04;
4540990226cSmacallan } else {
455b5dc0007Smacallan /* while on most machines it's high active as well */
4563fff0522Smacallan sc->sc_headphones_mask = 0x8;
4573fff0522Smacallan sc->sc_headphones_in = 0x8;
4580990226cSmacallan }
4595297b0caSjmcneill
46022c149c8Sthorpej if (of_compatible(root_node, no_parallel_output))
4615297b0caSjmcneill sc->sc_need_parallel_output = 0;
4625297b0caSjmcneill else {
4635297b0caSjmcneill sc->sc_need_parallel_output = 1;
4645297b0caSjmcneill sc->sc_codecctl1 |= AWACS_PARALLEL_OUTPUT;
4655297b0caSjmcneill }
4665297b0caSjmcneill
4673fff0522Smacallan if (awacs_check_headphones(sc)) {
4683fff0522Smacallan
4693fff0522Smacallan /* default output to headphones */
4703fff0522Smacallan printf("headphones\n");
4713fff0522Smacallan sc->sc_output_mask = OUTPUT_HEADPHONES;
4723fff0522Smacallan } else {
4733fff0522Smacallan
4743fff0522Smacallan /* default output to speakers */
4753fff0522Smacallan printf("speaker\n");
4763fff0522Smacallan sc->sc_output_mask = OUTPUT_SPEAKER;
4773fff0522Smacallan }
4783fff0522Smacallan sc->sc_output_wanted = sc->sc_output_mask;
4793fff0522Smacallan awacs_select_output(sc, sc->sc_output_mask);
480308d7eb3Stsubai
4810990226cSmacallan delay(100);
4820990226cSmacallan if (sc->sc_screamer) {
4830990226cSmacallan awacs_write_codec(sc, sc->sc_codecctl6);
4840990226cSmacallan awacs_write_codec(sc, sc->sc_codecctl5);
4850990226cSmacallan delay(2);
4860990226cSmacallan awacs_write_codec(sc, sc->sc_codecctl1);
4870990226cSmacallan awacs_write_codec(sc, sc->sc_codecctl7);
4880990226cSmacallan }
4890990226cSmacallan
4900a4ef18aSwiz /* default input from CD */
4910a4ef18aSwiz sc->sc_record_source = 1 << 0;
4920a4ef18aSwiz sc->sc_codecctl0 &= ~AWACS_INPUT_MASK;
4930a4ef18aSwiz sc->sc_codecctl0 |= AWACS_INPUT_CD;
4940a4ef18aSwiz awacs_write_codec(sc, sc->sc_codecctl0);
4950a4ef18aSwiz
496308d7eb3Stsubai /* Enable interrupts and looping mode. */
497308d7eb3Stsubai /* XXX ... */
498308d7eb3Stsubai
4995297b0caSjmcneill sc->sc_codecctl1 |= AWACS_LOOP_THROUGH;
5005297b0caSjmcneill if (sc->sc_need_parallel_output)
5015297b0caSjmcneill sc->sc_codecctl1 |= AWACS_PARALLEL_OUTPUT;
5020990226cSmacallan awacs_write_codec(sc, sc->sc_codecctl1);
5030990226cSmacallan
5040990226cSmacallan #if NSGSMIX > 0
5050990226cSmacallan sc->sc_sgsmix = NULL;
5060990226cSmacallan #endif
5070990226cSmacallan sc->sc_have_perch = 0;
508b5dc0007Smacallan if (perch != -1) {
5090990226cSmacallan
5100990226cSmacallan len = OF_getprop(perch, "compatible", compat, 255);
5110990226cSmacallan if (len > 0) {
5120990226cSmacallan printf("%s: found '%s' personality card\n",
513b6c6870cSmacallan device_xname(sc->sc_dev), compat);
5140990226cSmacallan sc->sc_have_perch = 1;
515b6c6870cSmacallan config_finalize_register(sc->sc_dev,
5161f070ed9Smacallan awacs_setup_sgsmix);
5170990226cSmacallan }
5180990226cSmacallan }
5190990226cSmacallan
5200990226cSmacallan /* Set initial volume[s] */
5213fff0522Smacallan awacs_set_volume(sc, 144, 144);
5221f070ed9Smacallan awacs_set_loopthrough_volume(sc, 0, 0);
5230990226cSmacallan
524b6c6870cSmacallan audio_attach_mi(&awacs_hw_if, sc, sc->sc_dev);
5253fff0522Smacallan
52688ab7da9Sad if (kthread_create(PRI_NONE, 0, NULL, awacs_thread, sc,
52788ab7da9Sad &sc->sc_thread, "%s", "awacs") != 0) {
52888ab7da9Sad printf("awacs: unable to create event kthread");
52988ab7da9Sad }
53048d31d1eSmacallan pmf_device_register(sc->sc_dev, NULL, NULL);
531308d7eb3Stsubai }
532308d7eb3Stsubai
5330990226cSmacallan static int
awacs_setup_sgsmix(device_t cookie)534b6c6870cSmacallan awacs_setup_sgsmix(device_t cookie)
5350990226cSmacallan {
536b6c6870cSmacallan struct awacs_softc *sc = device_private(cookie);
5370990226cSmacallan #if NSGSMIX > 0
538b6c6870cSmacallan device_t dv;
5397507fa68Sdyoung deviter_t di;
5400990226cSmacallan #endif
5410990226cSmacallan
5420990226cSmacallan if (!sc->sc_have_perch)
5430990226cSmacallan return 0;
5440990226cSmacallan #if NSGSMIX > 0
5450990226cSmacallan /* look for sgsmix */
5467507fa68Sdyoung for (dv = deviter_first(&di, DEVITER_F_ROOT_FIRST);
5477507fa68Sdyoung dv != NULL;
5487507fa68Sdyoung dv = deviter_next(&di)) {
5490990226cSmacallan if (device_is_a(dv, "sgsmix")) {
5500990226cSmacallan sc->sc_sgsmix = dv;
5510990226cSmacallan break;
5520990226cSmacallan }
5530990226cSmacallan }
5547507fa68Sdyoung deviter_release(&di);
5550990226cSmacallan if (sc->sc_sgsmix == NULL)
5560990226cSmacallan return 0;
5570990226cSmacallan
558b6c6870cSmacallan printf("%s: using %s\n", device_xname(sc->sc_dev),
559b6c6870cSmacallan device_xname(sc->sc_sgsmix));
5600990226cSmacallan
561e3b706f1Smacallan sc->sc_codecctl1 &= ~AWACS_MUTE_HEADPHONE;
562e3b706f1Smacallan awacs_write_codec(sc, sc->sc_codecctl1);
563e3b706f1Smacallan
5640990226cSmacallan awacs_select_output(sc, sc->sc_output_mask);
5653fff0522Smacallan awacs_set_volume(sc, sc->vol_l, sc->vol_r);
5660990226cSmacallan awacs_set_bass(sc, 128);
5670990226cSmacallan awacs_set_treble(sc, 128);
5688a962f23Sjmcneill cv_signal(&sc->sc_event);
5690990226cSmacallan #endif
5700990226cSmacallan return 0;
5710990226cSmacallan }
5720990226cSmacallan
5730990226cSmacallan
574d4bcd9c7Ssimonb static inline u_int
awacs_read_reg(struct awacs_softc * sc,int reg)57593293b9eSkent awacs_read_reg(struct awacs_softc *sc, int reg)
576308d7eb3Stsubai {
577308d7eb3Stsubai
578e3b706f1Smacallan return bus_space_read_4(sc->sc_tag, sc->sc_regh, reg);
579308d7eb3Stsubai }
580308d7eb3Stsubai
581d4bcd9c7Ssimonb static inline void
awacs_write_reg(struct awacs_softc * sc,int reg,int val)58293293b9eSkent awacs_write_reg(struct awacs_softc *sc, int reg, int val)
583308d7eb3Stsubai {
584308d7eb3Stsubai
585e3b706f1Smacallan bus_space_write_4(sc->sc_tag, sc->sc_regh, reg, val);
586308d7eb3Stsubai }
587308d7eb3Stsubai
5883fff0522Smacallan static void
awacs_write_codec(struct awacs_softc * sc,int value)58993293b9eSkent awacs_write_codec(struct awacs_softc *sc, int value)
590308d7eb3Stsubai {
59193293b9eSkent
5920990226cSmacallan do {
5930990226cSmacallan delay(100);
5940990226cSmacallan } while (awacs_read_reg(sc, AWACS_CODEC_CTRL) & AWACS_CODEC_BUSY);
5950990226cSmacallan
596308d7eb3Stsubai awacs_write_reg(sc, AWACS_CODEC_CTRL, value);
5970990226cSmacallan
5980990226cSmacallan do {
5990990226cSmacallan delay(100);
6000990226cSmacallan } while (awacs_read_reg(sc, AWACS_CODEC_CTRL) & AWACS_CODEC_BUSY);
601308d7eb3Stsubai }
602308d7eb3Stsubai
6033fff0522Smacallan static int
awacs_intr(void * v)60493293b9eSkent awacs_intr(void *v)
605308d7eb3Stsubai {
60693293b9eSkent struct awacs_softc *sc;
60793293b9eSkent struct dbdma_command *cmd;
60893293b9eSkent int count;
609308d7eb3Stsubai int status;
610308d7eb3Stsubai
61193293b9eSkent sc = v;
6128a962f23Sjmcneill mutex_spin_enter(&sc->sc_intr_lock);
61393293b9eSkent cmd = sc->sc_odmacmd;
61493293b9eSkent count = sc->sc_opages;
615308d7eb3Stsubai /* Fill used buffer(s). */
616308d7eb3Stsubai while (count-- > 0) {
617308d7eb3Stsubai /* if DBDMA_INT_ALWAYS */
618308d7eb3Stsubai if (in16rb(&cmd->d_command) & 0x30) { /* XXX */
619308d7eb3Stsubai status = in16rb(&cmd->d_status);
620308d7eb3Stsubai cmd->d_status = 0;
621308d7eb3Stsubai if (status) /* status == 0x8400 */
622308d7eb3Stsubai if (sc->sc_ointr)
623308d7eb3Stsubai (*sc->sc_ointr)(sc->sc_oarg);
624308d7eb3Stsubai }
625308d7eb3Stsubai cmd++;
626308d7eb3Stsubai }
6278a962f23Sjmcneill mutex_spin_exit(&sc->sc_intr_lock);
628308d7eb3Stsubai
629308d7eb3Stsubai return 1;
630308d7eb3Stsubai }
631308d7eb3Stsubai
6323fff0522Smacallan static int
awacs_query_format(void * h,audio_format_query_t * afp)633e622eac4Sisaki awacs_query_format(void *h, audio_format_query_t *afp)
634308d7eb3Stsubai {
635b672929bStsubai
636e622eac4Sisaki return audio_query_format(awacs_formats, AWACS_NFORMATS, afp);
637308d7eb3Stsubai }
638308d7eb3Stsubai
6393fff0522Smacallan static int
awacs_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)640e622eac4Sisaki awacs_set_format(void *h, int setmode,
641e622eac4Sisaki const audio_params_t *play, const audio_params_t *rec,
642e622eac4Sisaki audio_filter_reg_t *pfil, audio_filter_reg_t *rfil)
643308d7eb3Stsubai {
64493293b9eSkent struct awacs_softc *sc;
645308d7eb3Stsubai
64693293b9eSkent sc = h;
647308d7eb3Stsubai
648e622eac4Sisaki /* *play and *rec are the identical because !AUDIO_PROP_INDEPENDENT. */
649308d7eb3Stsubai
65023b5d914Skent awacs_write_reg(sc, AWACS_BYTE_SWAP, 0);
651e622eac4Sisaki
652e622eac4Sisaki if (awacs_set_rate(sc, play))
6534382a59dStsubai return EINVAL;
654308d7eb3Stsubai return 0;
655308d7eb3Stsubai }
656308d7eb3Stsubai
6573fff0522Smacallan static int
awacs_round_blocksize(void * h,int size,int mode,const audio_params_t * param)65893293b9eSkent awacs_round_blocksize(void *h, int size, int mode, const audio_params_t *param)
659308d7eb3Stsubai {
66093293b9eSkent
661d470cda5Sthorpej if (size < PAGE_SIZE)
662d470cda5Sthorpej size = PAGE_SIZE;
663308d7eb3Stsubai return size & ~PGOFSET;
664308d7eb3Stsubai }
665308d7eb3Stsubai
6663fff0522Smacallan static int
awacs_halt_output(void * h)66793293b9eSkent awacs_halt_output(void *h)
668308d7eb3Stsubai {
66993293b9eSkent struct awacs_softc *sc;
670308d7eb3Stsubai
67193293b9eSkent sc = h;
672308d7eb3Stsubai dbdma_stop(sc->sc_odma);
673308d7eb3Stsubai dbdma_reset(sc->sc_odma);
6747b911d03Sisaki sc->sc_ointr = NULL;
675308d7eb3Stsubai return 0;
676308d7eb3Stsubai }
677308d7eb3Stsubai
6783fff0522Smacallan static int
awacs_halt_input(void * h)67993293b9eSkent awacs_halt_input(void *h)
680308d7eb3Stsubai {
68193293b9eSkent struct awacs_softc *sc;
682308d7eb3Stsubai
68393293b9eSkent sc = h;
684308d7eb3Stsubai dbdma_stop(sc->sc_idma);
685308d7eb3Stsubai dbdma_reset(sc->sc_idma);
6867b911d03Sisaki sc->sc_iintr = NULL;
687308d7eb3Stsubai return 0;
688308d7eb3Stsubai }
689308d7eb3Stsubai
6903fff0522Smacallan static int
awacs_getdev(void * h,struct audio_device * retp)69193293b9eSkent awacs_getdev(void *h, struct audio_device *retp)
692308d7eb3Stsubai {
69393293b9eSkent
694308d7eb3Stsubai *retp = awacs_device;
695308d7eb3Stsubai return 0;
696308d7eb3Stsubai }
697308d7eb3Stsubai
698308d7eb3Stsubai enum {
699a9e0aabfStsubai AWACS_MONITOR_CLASS,
700a9e0aabfStsubai AWACS_OUTPUT_CLASS,
701a9e0aabfStsubai AWACS_RECORD_CLASS,
702308d7eb3Stsubai AWACS_OUTPUT_SELECT,
7033fff0522Smacallan AWACS_VOL_MASTER,
7040a4ef18aSwiz AWACS_INPUT_SELECT,
7050a4ef18aSwiz AWACS_VOL_INPUT,
7061f070ed9Smacallan AWACS_VOL_MONITOR,
7070990226cSmacallan AWACS_BASS,
7080990226cSmacallan AWACS_TREBLE,
709308d7eb3Stsubai AWACS_ENUM_LAST
710308d7eb3Stsubai };
711308d7eb3Stsubai
7123fff0522Smacallan static int
awacs_set_port(void * h,mixer_ctrl_t * mc)71393293b9eSkent awacs_set_port(void *h, mixer_ctrl_t *mc)
714308d7eb3Stsubai {
71593293b9eSkent struct awacs_softc *sc;
716308d7eb3Stsubai int l, r;
717308d7eb3Stsubai
718308d7eb3Stsubai DPRINTF("awacs_set_port dev = %d, type = %d\n", mc->dev, mc->type);
71993293b9eSkent sc = h;
720308d7eb3Stsubai l = mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
721308d7eb3Stsubai r = mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
722308d7eb3Stsubai
723308d7eb3Stsubai switch (mc->dev) {
724308d7eb3Stsubai case AWACS_OUTPUT_SELECT:
725a9e0aabfStsubai /* No change necessary? */
726ec35b8d0Swiz if (mc->un.mask == sc->sc_output_mask)
727ec35b8d0Swiz return 0;
7280990226cSmacallan awacs_select_output(sc, mc->un.mask);
729308d7eb3Stsubai return 0;
730308d7eb3Stsubai
7313fff0522Smacallan case AWACS_VOL_MASTER:
7323fff0522Smacallan awacs_set_volume(sc, l, r);
733308d7eb3Stsubai return 0;
7340a4ef18aSwiz
7350a4ef18aSwiz case AWACS_INPUT_SELECT:
7360a4ef18aSwiz /* no change necessary? */
7370a4ef18aSwiz if (mc->un.mask == sc->sc_record_source)
7380a4ef18aSwiz return 0;
7390a4ef18aSwiz switch (mc->un.mask) {
7400a4ef18aSwiz case 1 << 0: /* CD */
7410a4ef18aSwiz sc->sc_codecctl0 &= ~AWACS_INPUT_MASK;
7420a4ef18aSwiz sc->sc_codecctl0 |= AWACS_INPUT_CD;
7430a4ef18aSwiz awacs_write_codec(sc, sc->sc_codecctl0);
7440a4ef18aSwiz break;
7450a4ef18aSwiz case 1 << 1: /* microphone */
7460a4ef18aSwiz sc->sc_codecctl0 &= ~AWACS_INPUT_MASK;
7470a4ef18aSwiz sc->sc_codecctl0 |= AWACS_INPUT_MICROPHONE;
7480a4ef18aSwiz awacs_write_codec(sc, sc->sc_codecctl0);
7490a4ef18aSwiz break;
7500a4ef18aSwiz case 1 << 2: /* line in */
7510a4ef18aSwiz sc->sc_codecctl0 &= ~AWACS_INPUT_MASK;
7520a4ef18aSwiz sc->sc_codecctl0 |= AWACS_INPUT_LINE;
7530a4ef18aSwiz awacs_write_codec(sc, sc->sc_codecctl0);
7540a4ef18aSwiz break;
7550a4ef18aSwiz default: /* invalid argument */
756a9e0aabfStsubai return EINVAL;
7570a4ef18aSwiz }
7580a4ef18aSwiz sc->sc_record_source = mc->un.mask;
7590a4ef18aSwiz return 0;
760a9e0aabfStsubai
761a9e0aabfStsubai case AWACS_VOL_INPUT:
762a9e0aabfStsubai sc->sc_codecctl0 &= ~0xff;
763a9e0aabfStsubai sc->sc_codecctl0 |= (l & 0xf0) | (r >> 4);
764a9e0aabfStsubai awacs_write_codec(sc, sc->sc_codecctl0);
765a9e0aabfStsubai return 0;
7660990226cSmacallan
7671f070ed9Smacallan case AWACS_VOL_MONITOR:
7681f070ed9Smacallan awacs_set_loopthrough_volume(sc, l, r);
7691f070ed9Smacallan return 0;
7701f070ed9Smacallan
7710990226cSmacallan #if NSGSMIX > 0
7720990226cSmacallan case AWACS_BASS:
7730990226cSmacallan awacs_set_bass(sc, l);
7740990226cSmacallan return 0;
7750990226cSmacallan
7760990226cSmacallan case AWACS_TREBLE:
7770990226cSmacallan awacs_set_treble(sc, l);
7780990226cSmacallan return 0;
7790990226cSmacallan #endif
780308d7eb3Stsubai }
781308d7eb3Stsubai
782308d7eb3Stsubai return ENXIO;
783308d7eb3Stsubai }
784308d7eb3Stsubai
7853fff0522Smacallan static int
awacs_get_port(void * h,mixer_ctrl_t * mc)78693293b9eSkent awacs_get_port(void *h, mixer_ctrl_t *mc)
787308d7eb3Stsubai {
78893293b9eSkent struct awacs_softc *sc;
7890990226cSmacallan int l, r, vol;
790308d7eb3Stsubai
79193293b9eSkent sc = h;
792308d7eb3Stsubai switch (mc->dev) {
793308d7eb3Stsubai case AWACS_OUTPUT_SELECT:
794308d7eb3Stsubai mc->un.mask = sc->sc_output_mask;
795308d7eb3Stsubai return 0;
796308d7eb3Stsubai
7973fff0522Smacallan case AWACS_VOL_MASTER:
7983fff0522Smacallan mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = sc->vol_l;
7993fff0522Smacallan mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = sc->vol_r;
8000a4ef18aSwiz return 0;
8010a4ef18aSwiz
8020a4ef18aSwiz case AWACS_INPUT_SELECT:
8030a4ef18aSwiz mc->un.mask = sc->sc_record_source;
8040a4ef18aSwiz return 0;
8050a4ef18aSwiz
8060a4ef18aSwiz case AWACS_VOL_INPUT:
8070a4ef18aSwiz vol = sc->sc_codecctl0 & 0xff;
8080a4ef18aSwiz l = (vol & 0xf0);
8090a4ef18aSwiz r = (vol & 0x0f) << 4;
810308d7eb3Stsubai mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = l;
811308d7eb3Stsubai mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = r;
812308d7eb3Stsubai return 0;
8131f070ed9Smacallan
8141f070ed9Smacallan case AWACS_VOL_MONITOR:
8151f070ed9Smacallan vol = sc->sc_codecctl5 & 0x3cf;
8161f070ed9Smacallan l = (vol & 0x3c0) >> 6;
8171f070ed9Smacallan r = vol & 0xf;
8181f070ed9Smacallan mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = (15 - l) << 4;
8191f070ed9Smacallan mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = (15 - r) << 4;
8201f070ed9Smacallan return 0;
8211f070ed9Smacallan
8220990226cSmacallan #if NSGSMIX > 0
8230990226cSmacallan case AWACS_BASS:
8240990226cSmacallan mc->un.value.level[AUDIO_MIXER_LEVEL_MONO] = sc->sc_bass;
8250990226cSmacallan return 0;
8260990226cSmacallan
8270990226cSmacallan case AWACS_TREBLE:
8280990226cSmacallan mc->un.value.level[AUDIO_MIXER_LEVEL_MONO] = sc->sc_treble;
8290990226cSmacallan return 0;
8300990226cSmacallan #endif
831308d7eb3Stsubai
832308d7eb3Stsubai default:
833308d7eb3Stsubai return ENXIO;
834308d7eb3Stsubai }
835308d7eb3Stsubai
836308d7eb3Stsubai return 0;
837308d7eb3Stsubai }
838308d7eb3Stsubai
8393fff0522Smacallan static int
awacs_query_devinfo(void * h,mixer_devinfo_t * dip)84093293b9eSkent awacs_query_devinfo(void *h, mixer_devinfo_t *dip)
841308d7eb3Stsubai {
8420990226cSmacallan #if NSGSMIX > 0
8430990226cSmacallan struct awacs_softc *sc = h;
8440990226cSmacallan #endif
845308d7eb3Stsubai
846308d7eb3Stsubai switch (dip->index) {
847308d7eb3Stsubai
848308d7eb3Stsubai case AWACS_OUTPUT_SELECT:
84948d31d1eSmacallan dip->mixer_class = AWACS_OUTPUT_CLASS;
850308d7eb3Stsubai strcpy(dip->label.name, AudioNoutput);
851308d7eb3Stsubai dip->type = AUDIO_MIXER_SET;
852308d7eb3Stsubai dip->prev = dip->next = AUDIO_MIXER_LAST;
853308d7eb3Stsubai dip->un.s.num_mem = 2;
854308d7eb3Stsubai strcpy(dip->un.s.member[0].label.name, AudioNspeaker);
855308d7eb3Stsubai dip->un.s.member[0].mask = 1 << 0;
856308d7eb3Stsubai strcpy(dip->un.s.member[1].label.name, AudioNheadphone);
857308d7eb3Stsubai dip->un.s.member[1].mask = 1 << 1;
858308d7eb3Stsubai return 0;
859308d7eb3Stsubai
8603fff0522Smacallan case AWACS_VOL_MASTER:
86148d31d1eSmacallan dip->mixer_class = AWACS_OUTPUT_CLASS;
8623fff0522Smacallan strcpy(dip->label.name, AudioNmaster);
863308d7eb3Stsubai dip->type = AUDIO_MIXER_VALUE;
864308d7eb3Stsubai dip->prev = dip->next = AUDIO_MIXER_LAST;
865308d7eb3Stsubai dip->un.v.num_channels = 2;
86648d31d1eSmacallan dip->un.v.delta = 16;
867308d7eb3Stsubai strcpy(dip->un.v.units.name, AudioNvolume);
868308d7eb3Stsubai return 0;
869308d7eb3Stsubai
8701f070ed9Smacallan case AWACS_VOL_MONITOR:
8711f070ed9Smacallan dip->mixer_class = AWACS_MONITOR_CLASS;
8721f070ed9Smacallan strcpy(dip->label.name, AudioNmonitor);
8731f070ed9Smacallan dip->type = AUDIO_MIXER_VALUE;
8741f070ed9Smacallan dip->prev = dip->next = AUDIO_MIXER_LAST;
8751f070ed9Smacallan dip->un.v.num_channels = 2;
8761f070ed9Smacallan strcpy(dip->un.v.units.name, AudioNvolume);
8771f070ed9Smacallan return 0;
8781f070ed9Smacallan
8790990226cSmacallan #if NSGSMIX > 0
8800990226cSmacallan case AWACS_BASS:
8810990226cSmacallan if (sc->sc_sgsmix == NULL)
8820990226cSmacallan return ENXIO;
88348d31d1eSmacallan dip->mixer_class = AWACS_OUTPUT_CLASS;
8840990226cSmacallan strcpy(dip->label.name, AudioNbass);
8850990226cSmacallan dip->type = AUDIO_MIXER_VALUE;
8860990226cSmacallan dip->prev = dip->next = AUDIO_MIXER_LAST;
8870990226cSmacallan dip->un.v.num_channels = 1;
8880990226cSmacallan strcpy(dip->un.v.units.name, AudioNbass);
8890990226cSmacallan return 0;
8900990226cSmacallan
8910990226cSmacallan case AWACS_TREBLE:
8920990226cSmacallan if (sc->sc_sgsmix == NULL)
8930990226cSmacallan return ENXIO;
89448d31d1eSmacallan dip->mixer_class = AWACS_OUTPUT_CLASS;
8950990226cSmacallan strcpy(dip->label.name, AudioNtreble);
8960990226cSmacallan dip->type = AUDIO_MIXER_VALUE;
8970990226cSmacallan dip->prev = dip->next = AUDIO_MIXER_LAST;
8980990226cSmacallan dip->un.v.num_channels = 1;
8990990226cSmacallan strcpy(dip->un.v.units.name, AudioNtreble);
9000990226cSmacallan return 0;
9010990226cSmacallan #endif
9020990226cSmacallan
9030a4ef18aSwiz case AWACS_INPUT_SELECT:
904a9e0aabfStsubai dip->mixer_class = AWACS_RECORD_CLASS;
905a9e0aabfStsubai strcpy(dip->label.name, AudioNsource);
9060a4ef18aSwiz dip->type = AUDIO_MIXER_SET;
9070a4ef18aSwiz dip->prev = dip->next = AUDIO_MIXER_LAST;
9080a4ef18aSwiz dip->un.s.num_mem = 3;
9090a4ef18aSwiz strcpy(dip->un.s.member[0].label.name, AudioNcd);
9100a4ef18aSwiz dip->un.s.member[0].mask = 1 << 0;
9110a4ef18aSwiz strcpy(dip->un.s.member[1].label.name, AudioNmicrophone);
9120a4ef18aSwiz dip->un.s.member[1].mask = 1 << 1;
9130a4ef18aSwiz strcpy(dip->un.s.member[2].label.name, AudioNline);
9140a4ef18aSwiz dip->un.s.member[2].mask = 1 << 2;
9150a4ef18aSwiz return 0;
9160a4ef18aSwiz
9170a4ef18aSwiz case AWACS_VOL_INPUT:
918a9e0aabfStsubai dip->mixer_class = AWACS_RECORD_CLASS;
919a9e0aabfStsubai strcpy(dip->label.name, AudioNrecord);
9200a4ef18aSwiz dip->type = AUDIO_MIXER_VALUE;
9210a4ef18aSwiz dip->prev = dip->next = AUDIO_MIXER_LAST;
9220a4ef18aSwiz dip->un.v.num_channels = 2;
9230a4ef18aSwiz strcpy(dip->un.v.units.name, AudioNvolume);
9240a4ef18aSwiz return 0;
9250a4ef18aSwiz
926308d7eb3Stsubai case AWACS_MONITOR_CLASS:
927308d7eb3Stsubai dip->mixer_class = AWACS_MONITOR_CLASS;
928308d7eb3Stsubai strcpy(dip->label.name, AudioCmonitor);
929308d7eb3Stsubai dip->type = AUDIO_MIXER_CLASS;
930308d7eb3Stsubai dip->next = dip->prev = AUDIO_MIXER_LAST;
931308d7eb3Stsubai return 0;
932308d7eb3Stsubai
933308d7eb3Stsubai case AWACS_OUTPUT_CLASS:
934308d7eb3Stsubai dip->mixer_class = AWACS_OUTPUT_CLASS;
935308d7eb3Stsubai strcpy(dip->label.name, AudioCoutputs);
936308d7eb3Stsubai dip->type = AUDIO_MIXER_CLASS;
937308d7eb3Stsubai dip->next = dip->prev = AUDIO_MIXER_LAST;
938308d7eb3Stsubai return 0;
9390a4ef18aSwiz
9400a4ef18aSwiz case AWACS_RECORD_CLASS:
941a9e0aabfStsubai dip->mixer_class = AWACS_RECORD_CLASS;
9420a4ef18aSwiz strcpy(dip->label.name, AudioCrecord);
9430a4ef18aSwiz dip->type = AUDIO_MIXER_CLASS;
9440a4ef18aSwiz dip->next = dip->prev = AUDIO_MIXER_LAST;
9450a4ef18aSwiz return 0;
946308d7eb3Stsubai }
947308d7eb3Stsubai
948308d7eb3Stsubai return ENXIO;
949308d7eb3Stsubai }
950308d7eb3Stsubai
9513fff0522Smacallan static size_t
awacs_round_buffersize(void * h,int dir,size_t size)95293293b9eSkent awacs_round_buffersize(void *h, int dir, size_t size)
953308d7eb3Stsubai {
95493293b9eSkent
955308d7eb3Stsubai if (size > 65536)
956308d7eb3Stsubai size = 65536;
957308d7eb3Stsubai return size;
958308d7eb3Stsubai }
959308d7eb3Stsubai
9603fff0522Smacallan static int
awacs_get_props(void * h)96193293b9eSkent awacs_get_props(void *h)
962308d7eb3Stsubai {
963ede47d01Sisaki
964ede47d01Sisaki return AUDIO_PROP_PLAYBACK | AUDIO_PROP_CAPTURE |
965ede47d01Sisaki AUDIO_PROP_FULLDUPLEX;
966308d7eb3Stsubai }
967308d7eb3Stsubai
9683fff0522Smacallan static int
awacs_trigger_output(void * h,void * start,void * end,int bsize,void (* intr)(void *),void * arg,const audio_params_t * param)96993293b9eSkent awacs_trigger_output(void *h, void *start, void *end, int bsize,
97093293b9eSkent void (*intr)(void *), void *arg,
97193293b9eSkent const audio_params_t *param)
972308d7eb3Stsubai {
97393293b9eSkent struct awacs_softc *sc;
97493293b9eSkent struct dbdma_command *cmd;
975308d7eb3Stsubai vaddr_t va;
976308d7eb3Stsubai int i, len, intmode;
977308d7eb3Stsubai
978308d7eb3Stsubai DPRINTF("trigger_output %p %p 0x%x\n", start, end, bsize);
97993293b9eSkent sc = h;
98093293b9eSkent cmd = sc->sc_odmacmd;
981308d7eb3Stsubai sc->sc_ointr = intr;
982308d7eb3Stsubai sc->sc_oarg = arg;
983d470cda5Sthorpej sc->sc_opages = ((char *)end - (char *)start) / PAGE_SIZE;
984308d7eb3Stsubai
985308d7eb3Stsubai #ifdef DIAGNOSTIC
986308d7eb3Stsubai if (sc->sc_opages > 16)
987308d7eb3Stsubai panic("awacs_trigger_output");
988308d7eb3Stsubai #endif
989308d7eb3Stsubai
990308d7eb3Stsubai va = (vaddr_t)start;
991308d7eb3Stsubai len = 0;
992308d7eb3Stsubai for (i = sc->sc_opages; i > 0; i--) {
993d470cda5Sthorpej len += PAGE_SIZE;
994308d7eb3Stsubai if (len < bsize)
995308d7eb3Stsubai intmode = DBDMA_INT_NEVER;
996308d7eb3Stsubai else {
997308d7eb3Stsubai len = 0;
998308d7eb3Stsubai intmode = DBDMA_INT_ALWAYS;
999308d7eb3Stsubai }
1000308d7eb3Stsubai
1001d470cda5Sthorpej DBDMA_BUILD(cmd, DBDMA_CMD_OUT_MORE, 0, PAGE_SIZE, vtophys(va),
1002308d7eb3Stsubai intmode, DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER);
1003d470cda5Sthorpej va += PAGE_SIZE;
1004308d7eb3Stsubai cmd++;
1005308d7eb3Stsubai }
1006308d7eb3Stsubai
1007308d7eb3Stsubai DBDMA_BUILD(cmd, DBDMA_CMD_NOP, 0, 0, 0,
1008308d7eb3Stsubai DBDMA_INT_NEVER, DBDMA_WAIT_NEVER, DBDMA_BRANCH_ALWAYS);
1009d974db0aSgarbled out32rb(&cmd->d_cmddep, vtophys((vaddr_t)sc->sc_odmacmd));
1010308d7eb3Stsubai
1011308d7eb3Stsubai dbdma_start(sc->sc_odma, sc->sc_odmacmd);
1012308d7eb3Stsubai
1013308d7eb3Stsubai return 0;
1014308d7eb3Stsubai }
1015308d7eb3Stsubai
10163fff0522Smacallan static int
awacs_trigger_input(void * h,void * start,void * end,int bsize,void (* intr)(void *),void * arg,const audio_params_t * param)101793293b9eSkent awacs_trigger_input(void *h, void *start, void *end, int bsize,
101893293b9eSkent void (*intr)(void *), void *arg,
101993293b9eSkent const audio_params_t *param)
1020308d7eb3Stsubai {
1021308d7eb3Stsubai
10223fff0522Smacallan DPRINTF("awacs_trigger_input called\n");
1023308d7eb3Stsubai return 1;
1024308d7eb3Stsubai }
1025308d7eb3Stsubai
10263fff0522Smacallan static void
awacs_get_locks(void * opaque,kmutex_t ** intr,kmutex_t ** thread)10278a962f23Sjmcneill awacs_get_locks(void *opaque, kmutex_t **intr, kmutex_t **thread)
10288a962f23Sjmcneill {
10298a962f23Sjmcneill struct awacs_softc *sc = opaque;
10308a962f23Sjmcneill
10318a962f23Sjmcneill *intr = &sc->sc_intr_lock;
10328a962f23Sjmcneill *thread = &sc->sc_lock;
10338a962f23Sjmcneill }
10348a962f23Sjmcneill
10358a962f23Sjmcneill static void
awacs_select_output(struct awacs_softc * sc,int mask)10360990226cSmacallan awacs_select_output(struct awacs_softc *sc, int mask)
10370990226cSmacallan {
10380990226cSmacallan
10390990226cSmacallan #if NSGSMIX > 0
10400990226cSmacallan if (sc->sc_sgsmix) {
10410990226cSmacallan if (mask & OUTPUT_HEADPHONES) {
10420990226cSmacallan /* mute speakers */
10430990226cSmacallan sgsmix_set_speaker_vol(sc->sc_sgsmix, 0, 0);
10440990226cSmacallan sgsmix_set_headphone_vol(sc->sc_sgsmix,
10453fff0522Smacallan sc->vol_l, sc->vol_r);
10460990226cSmacallan }
10470990226cSmacallan if (mask & OUTPUT_SPEAKER) {
10480990226cSmacallan /* mute headphones */
10490990226cSmacallan sgsmix_set_speaker_vol(sc->sc_sgsmix,
10503fff0522Smacallan sc->vol_l, sc->vol_r);
10510990226cSmacallan sgsmix_set_headphone_vol(sc->sc_sgsmix, 0, 0);
10520990226cSmacallan }
10530990226cSmacallan } else {
10540990226cSmacallan #endif
10550990226cSmacallan sc->sc_codecctl1 |= AWACS_MUTE_SPEAKER | AWACS_MUTE_HEADPHONE;
10563fff0522Smacallan if ((sc->vol_l > 0) || (sc->vol_r > 0)) {
10573fff0522Smacallan if (mask & OUTPUT_SPEAKER)
10580990226cSmacallan sc->sc_codecctl1 &= ~AWACS_MUTE_SPEAKER;
10593fff0522Smacallan if (mask & OUTPUT_HEADPHONES)
10600990226cSmacallan sc->sc_codecctl1 &= ~AWACS_MUTE_HEADPHONE;
10613fff0522Smacallan }
10620990226cSmacallan awacs_write_codec(sc, sc->sc_codecctl1);
10630990226cSmacallan #if NSGSMIX > 0
10640990226cSmacallan }
10650990226cSmacallan #endif
10660990226cSmacallan sc->sc_output_mask = mask;
10670990226cSmacallan }
10680990226cSmacallan
10693fff0522Smacallan static void
awacs_set_speaker_volume(struct awacs_softc * sc,int left,int right)107093293b9eSkent awacs_set_speaker_volume(struct awacs_softc *sc, int left, int right)
1071308d7eb3Stsubai {
10720990226cSmacallan
10730990226cSmacallan #if NSGSMIX > 0
10740990226cSmacallan if (sc->sc_sgsmix) {
10750990226cSmacallan if (sc->sc_output_mask & OUTPUT_SPEAKER)
10760990226cSmacallan sgsmix_set_speaker_vol(sc->sc_sgsmix, left, right);
10770990226cSmacallan } else
10780990226cSmacallan #endif
10790990226cSmacallan {
108093293b9eSkent int lval;
108193293b9eSkent int rval;
10823fff0522Smacallan uint32_t codecctl = sc->sc_codecctl1;
1083308d7eb3Stsubai
10843fff0522Smacallan lval = 15 - ((left & 0xf0) >> 4);
10853fff0522Smacallan rval = 15 - ((right & 0xf0) >> 4);
1086308d7eb3Stsubai DPRINTF("speaker_volume %d %d\n", lval, rval);
1087308d7eb3Stsubai
1088308d7eb3Stsubai sc->sc_codecctl4 &= ~0x3cf;
1089308d7eb3Stsubai sc->sc_codecctl4 |= (lval << 6) | rval;
1090308d7eb3Stsubai awacs_write_codec(sc, sc->sc_codecctl4);
10913fff0522Smacallan if ((left == 0) && (right == 0)) {
10923fff0522Smacallan /*
10933fff0522Smacallan * max. attenuation doesn't mean silence so we need to
10943fff0522Smacallan * mute the output channel here
10953fff0522Smacallan */
10963fff0522Smacallan codecctl |= AWACS_MUTE_SPEAKER;
10973fff0522Smacallan } else if (sc->sc_output_mask & OUTPUT_SPEAKER) {
10983fff0522Smacallan codecctl &= ~AWACS_MUTE_SPEAKER;
10990990226cSmacallan }
1100308d7eb3Stsubai
11013fff0522Smacallan if (codecctl != sc->sc_codecctl1) {
11023fff0522Smacallan
11033fff0522Smacallan sc->sc_codecctl1 = codecctl;
11043fff0522Smacallan awacs_write_codec(sc, sc->sc_codecctl1);
11053fff0522Smacallan }
11063fff0522Smacallan }
11073fff0522Smacallan }
11083fff0522Smacallan
11093fff0522Smacallan static void
awacs_set_ext_volume(struct awacs_softc * sc,int left,int right)111093293b9eSkent awacs_set_ext_volume(struct awacs_softc *sc, int left, int right)
1111308d7eb3Stsubai {
11120990226cSmacallan
11130990226cSmacallan #if NSGSMIX > 0
11140990226cSmacallan if (sc->sc_sgsmix) {
11150990226cSmacallan if (sc->sc_output_mask & OUTPUT_HEADPHONES)
11160990226cSmacallan sgsmix_set_headphone_vol(sc->sc_sgsmix, left, right);
11170990226cSmacallan } else
11180990226cSmacallan #endif
11190990226cSmacallan {
112093293b9eSkent int lval;
112193293b9eSkent int rval;
11223fff0522Smacallan uint32_t codecctl = sc->sc_codecctl1;
1123308d7eb3Stsubai
11243fff0522Smacallan lval = 15 - ((left & 0xf0) >> 4);
11253fff0522Smacallan rval = 15 - ((right & 0xf0) >> 4);
1126308d7eb3Stsubai DPRINTF("ext_volume %d %d\n", lval, rval);
1127308d7eb3Stsubai
1128308d7eb3Stsubai sc->sc_codecctl2 &= ~0x3cf;
1129308d7eb3Stsubai sc->sc_codecctl2 |= (lval << 6) | rval;
1130308d7eb3Stsubai awacs_write_codec(sc, sc->sc_codecctl2);
11313fff0522Smacallan
11323fff0522Smacallan if ((left == 0) && (right == 0)) {
11333fff0522Smacallan /*
11343fff0522Smacallan * max. attenuation doesn't mean silence so we need to
11353fff0522Smacallan * mute the output channel here
11363fff0522Smacallan */
11373fff0522Smacallan codecctl |= AWACS_MUTE_HEADPHONE;
11383fff0522Smacallan } else if (sc->sc_output_mask & OUTPUT_HEADPHONES) {
11393fff0522Smacallan
11403fff0522Smacallan codecctl &= ~AWACS_MUTE_HEADPHONE;
1141308d7eb3Stsubai }
11423fff0522Smacallan
11433fff0522Smacallan if (codecctl != sc->sc_codecctl1) {
11443fff0522Smacallan
11453fff0522Smacallan sc->sc_codecctl1 = codecctl;
11463fff0522Smacallan awacs_write_codec(sc, sc->sc_codecctl1);
11473fff0522Smacallan }
11483fff0522Smacallan }
11493fff0522Smacallan }
11503fff0522Smacallan
1151b6c6870cSmacallan void
awacs_set_volume(struct awacs_softc * sc,int left,int right)11523fff0522Smacallan awacs_set_volume(struct awacs_softc *sc, int left, int right)
11533fff0522Smacallan {
11543fff0522Smacallan
11553fff0522Smacallan awacs_set_ext_volume(sc, left, right);
11563fff0522Smacallan awacs_set_speaker_volume(sc, left, right);
11573fff0522Smacallan
11583fff0522Smacallan sc->vol_l = left;
11593fff0522Smacallan sc->vol_r = right;
11600990226cSmacallan }
11610990226cSmacallan
11620990226cSmacallan #if NSGSMIX > 0
11630990226cSmacallan static void
awacs_set_bass(struct awacs_softc * sc,int bass)11640990226cSmacallan awacs_set_bass(struct awacs_softc *sc, int bass)
11650990226cSmacallan {
11660990226cSmacallan
11670990226cSmacallan if (sc->sc_bass == bass)
11680990226cSmacallan return;
11690990226cSmacallan
11700990226cSmacallan sc->sc_bass = bass;
11710990226cSmacallan if (sc->sc_sgsmix)
1172b6c6870cSmacallan sgsmix_set_bass_treble(sc->sc_sgsmix, sc->sc_bass,
1173b6c6870cSmacallan sc->sc_treble);
11740990226cSmacallan }
11750990226cSmacallan
11760990226cSmacallan static void
awacs_set_treble(struct awacs_softc * sc,int treble)11770990226cSmacallan awacs_set_treble(struct awacs_softc *sc, int treble)
11780990226cSmacallan {
11790990226cSmacallan
11800990226cSmacallan if (sc->sc_treble == treble)
11810990226cSmacallan return;
11820990226cSmacallan
11830990226cSmacallan sc->sc_treble = treble;
11840990226cSmacallan if (sc->sc_sgsmix)
1185b6c6870cSmacallan sgsmix_set_bass_treble(sc->sc_sgsmix, sc->sc_bass,
1186b6c6870cSmacallan sc->sc_treble);
11870990226cSmacallan }
11880990226cSmacallan #endif
11890990226cSmacallan
11900990226cSmacallan void
awacs_set_loopthrough_volume(struct awacs_softc * sc,int left,int right)11910990226cSmacallan awacs_set_loopthrough_volume(struct awacs_softc *sc, int left, int right)
11920990226cSmacallan {
11930990226cSmacallan int lval;
11940990226cSmacallan int rval;
11950990226cSmacallan
11963fff0522Smacallan lval = 15 - ((left & 0xff) >> 4);
11973fff0522Smacallan rval = 15 - ((right & 0xff) >> 4);
11980990226cSmacallan DPRINTF("loopthrough_volume %d %d\n", lval, rval);
11990990226cSmacallan
12000990226cSmacallan sc->sc_codecctl5 &= ~0x3cf;
12010990226cSmacallan sc->sc_codecctl5 |= (lval << 6) | rval;
12020990226cSmacallan awacs_write_codec(sc, sc->sc_codecctl5);
12030990226cSmacallan }
1204308d7eb3Stsubai
1205308d7eb3Stsubai int
awacs_set_rate(struct awacs_softc * sc,const audio_params_t * p)120693293b9eSkent awacs_set_rate(struct awacs_softc *sc, const audio_params_t *p)
1207308d7eb3Stsubai {
1208308d7eb3Stsubai int c;
1209308d7eb3Stsubai
121065cd27e4Swiz switch (p->sample_rate) {
1211308d7eb3Stsubai case 44100:
1212308d7eb3Stsubai c = AWACS_RATE_44100;
1213308d7eb3Stsubai break;
1214308d7eb3Stsubai case 29400:
1215308d7eb3Stsubai c = AWACS_RATE_29400;
1216308d7eb3Stsubai break;
1217308d7eb3Stsubai case 22050:
1218308d7eb3Stsubai c = AWACS_RATE_22050;
1219308d7eb3Stsubai break;
1220308d7eb3Stsubai case 17640:
1221308d7eb3Stsubai c = AWACS_RATE_17640;
1222308d7eb3Stsubai break;
1223308d7eb3Stsubai case 14700:
1224308d7eb3Stsubai c = AWACS_RATE_14700;
1225308d7eb3Stsubai break;
1226308d7eb3Stsubai case 11025:
1227308d7eb3Stsubai c = AWACS_RATE_11025;
1228308d7eb3Stsubai break;
1229308d7eb3Stsubai case 8820:
1230308d7eb3Stsubai c = AWACS_RATE_8820;
1231308d7eb3Stsubai break;
1232308d7eb3Stsubai case 7350:
1233308d7eb3Stsubai c = AWACS_RATE_7350;
1234308d7eb3Stsubai break;
1235308d7eb3Stsubai default:
1236308d7eb3Stsubai return -1;
1237308d7eb3Stsubai }
1238308d7eb3Stsubai
1239308d7eb3Stsubai sc->sc_soundctl &= ~AWACS_RATE_MASK;
1240308d7eb3Stsubai sc->sc_soundctl |= c;
1241308d7eb3Stsubai awacs_write_reg(sc, AWACS_SOUND_CTRL, sc->sc_soundctl);
1242308d7eb3Stsubai
1243308d7eb3Stsubai return 0;
1244308d7eb3Stsubai }
12453fff0522Smacallan
12463fff0522Smacallan static int
awacs_check_headphones(struct awacs_softc * sc)12473fff0522Smacallan awacs_check_headphones(struct awacs_softc *sc)
12483fff0522Smacallan {
12493fff0522Smacallan uint32_t reg;
12503fff0522Smacallan reg = awacs_read_reg(sc, AWACS_CODEC_STATUS);
1251b6c6870cSmacallan DPRINTF("%s: codec status reg %08x\n", device_xname(sc->sc_dev), reg);
12523fff0522Smacallan return ((reg & sc->sc_headphones_mask) == sc->sc_headphones_in);
12533fff0522Smacallan }
12543fff0522Smacallan
12553fff0522Smacallan static int
awacs_status_intr(void * cookie)12563fff0522Smacallan awacs_status_intr(void *cookie)
12573fff0522Smacallan {
12583fff0522Smacallan struct awacs_softc *sc = cookie;
12593fff0522Smacallan int mask;
12603fff0522Smacallan
12618a962f23Sjmcneill mutex_spin_enter(&sc->sc_intr_lock);
12623fff0522Smacallan mask = awacs_check_headphones(sc) ? OUTPUT_HEADPHONES : OUTPUT_SPEAKER;
12633fff0522Smacallan if (mask != sc->sc_output_mask) {
12643fff0522Smacallan
12653fff0522Smacallan sc->sc_output_wanted = mask;
12668a962f23Sjmcneill cv_signal(&sc->sc_event);
12673fff0522Smacallan }
12683fff0522Smacallan /* clear the interrupt */
12693fff0522Smacallan awacs_write_reg(sc, AWACS_SOUND_CTRL, sc->sc_soundctl | AWACS_PORTCHG);
12708a962f23Sjmcneill mutex_spin_exit(&sc->sc_intr_lock);
12713fff0522Smacallan return 1;
12723fff0522Smacallan }
12733fff0522Smacallan
12743fff0522Smacallan static void
awacs_thread(void * cookie)12753fff0522Smacallan awacs_thread(void *cookie)
12763fff0522Smacallan {
12773fff0522Smacallan struct awacs_softc *sc = cookie;
12783fff0522Smacallan
12793fff0522Smacallan while (1) {
1280*bef84f10Smacallan mutex_enter(&sc->sc_event_lock);
1281*bef84f10Smacallan cv_timedwait(&sc->sc_event, &sc->sc_event_lock, hz);
1282*bef84f10Smacallan mutex_exit(&sc->sc_event_lock);
12833fff0522Smacallan if (sc->sc_output_wanted == sc->sc_output_mask)
12843fff0522Smacallan continue;
12853fff0522Smacallan
12863fff0522Smacallan awacs_select_output(sc, sc->sc_output_wanted);
1287b6c6870cSmacallan DPRINTF("%s: switching to %s\n", device_xname(sc->sc_dev),
12883fff0522Smacallan (sc->sc_output_wanted & OUTPUT_SPEAKER) ?
12893fff0522Smacallan "speaker" : "headphones");
12903fff0522Smacallan }
12913fff0522Smacallan }
1292