1*36dba039Sjsg /* $OpenBSD: awacs.c,v 1.42 2024/04/14 03:26:25 jsg Exp $ */
2d9a5f17fSdrahn /* $NetBSD: awacs.c,v 1.4 2001/02/26 21:07:51 wiz Exp $ */
3d9a5f17fSdrahn
4d9a5f17fSdrahn /*-
5d9a5f17fSdrahn * Copyright (c) 2000 Tsubai Masanari. All rights reserved.
6d9a5f17fSdrahn *
7d9a5f17fSdrahn * Redistribution and use in source and binary forms, with or without
8d9a5f17fSdrahn * modification, are permitted provided that the following conditions
9d9a5f17fSdrahn * are met:
10d9a5f17fSdrahn * 1. Redistributions of source code must retain the above copyright
11d9a5f17fSdrahn * notice, this list of conditions and the following disclaimer.
12d9a5f17fSdrahn * 2. Redistributions in binary form must reproduce the above copyright
13d9a5f17fSdrahn * notice, this list of conditions and the following disclaimer in the
14d9a5f17fSdrahn * documentation and/or other materials provided with the distribution.
15d9a5f17fSdrahn * 3. The name of the author may not be used to endorse or promote products
16d9a5f17fSdrahn * derived from this software without specific prior written permission.
17d9a5f17fSdrahn *
18d9a5f17fSdrahn * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19d9a5f17fSdrahn * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20d9a5f17fSdrahn * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21d9a5f17fSdrahn * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22d9a5f17fSdrahn * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23d9a5f17fSdrahn * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24d9a5f17fSdrahn * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25d9a5f17fSdrahn * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26d9a5f17fSdrahn * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27d9a5f17fSdrahn * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28d9a5f17fSdrahn */
29d9a5f17fSdrahn
30d9a5f17fSdrahn #include <sys/param.h>
31d9a5f17fSdrahn #include <sys/audioio.h>
32d9a5f17fSdrahn #include <sys/device.h>
33d9a5f17fSdrahn #include <sys/malloc.h>
34d9a5f17fSdrahn #include <sys/systm.h>
35d9a5f17fSdrahn
36d9a5f17fSdrahn #include <dev/audio_if.h>
37d9a5f17fSdrahn
38e6d856e8Smickey #include <machine/bus.h>
39d9a5f17fSdrahn #include <machine/autoconf.h>
4078a7e9fcSdrahn #include <macppc/dev/dbdma.h>
41d9a5f17fSdrahn
42d9a5f17fSdrahn #ifdef AWACS_DEBUG
43d9a5f17fSdrahn # define DPRINTF printf
44d9a5f17fSdrahn #else
45d9a5f17fSdrahn # define DPRINTF while (0) printf
46d9a5f17fSdrahn #endif
47d9a5f17fSdrahn
48e6d856e8Smickey #define AWACS_DMALIST_MAX 32
49e6d856e8Smickey #define AWACS_DMASEG_MAX NBPG
50e6d856e8Smickey
51e6d856e8Smickey struct awacs_dma {
52e6d856e8Smickey bus_dmamap_t map;
53e6d856e8Smickey caddr_t addr;
54e6d856e8Smickey bus_dma_segment_t segs[AWACS_DMALIST_MAX];
55e6d856e8Smickey int nsegs;
56e6d856e8Smickey size_t size;
57e6d856e8Smickey struct awacs_dma *next;
58e6d856e8Smickey };
59e6d856e8Smickey
60e6d856e8Smickey
61d9a5f17fSdrahn struct awacs_softc {
62d9a5f17fSdrahn struct device sc_dev;
63d9a5f17fSdrahn
64d9a5f17fSdrahn void (*sc_ointr)(void *); /* dma completion intr handler */
65d9a5f17fSdrahn void *sc_oarg; /* arg for sc_ointr() */
66d9a5f17fSdrahn
67d9a5f17fSdrahn void (*sc_iintr)(void *); /* dma completion intr handler */
68d9a5f17fSdrahn void *sc_iarg; /* arg for sc_iintr() */
69d9a5f17fSdrahn
70d9a5f17fSdrahn u_int sc_record_source; /* recording source mask */
71d9a5f17fSdrahn u_int sc_output_mask; /* output source mask */
72d9a5f17fSdrahn
73aadf1369Sgkoehler int sc_awacs_hardware;
74aadf1369Sgkoehler int sc_awacs_headphone_mask;
75aadf1369Sgkoehler
76d9a5f17fSdrahn char *sc_reg;
77d9a5f17fSdrahn u_int sc_codecctl0;
78d9a5f17fSdrahn u_int sc_codecctl1;
79d9a5f17fSdrahn u_int sc_codecctl2;
80d9a5f17fSdrahn u_int sc_codecctl4;
81d9a5f17fSdrahn u_int sc_soundctl;
82d9a5f17fSdrahn
83e6d856e8Smickey bus_dma_tag_t sc_dmat;
84d9a5f17fSdrahn struct dbdma_regmap *sc_odma;
85d9a5f17fSdrahn struct dbdma_regmap *sc_idma;
86e6d856e8Smickey struct dbdma_command *sc_odmacmd, *sc_odmap;
87e6d856e8Smickey struct dbdma_command *sc_idmacmd, *sc_idmap;
88e6d856e8Smickey dbdma_t sc_odbdma, sc_idbdma;
89e6d856e8Smickey
90e6d856e8Smickey struct awacs_dma *sc_dmas;
91d9a5f17fSdrahn };
92d9a5f17fSdrahn
93d9a5f17fSdrahn int awacs_match(struct device *, void *, void *);
94d9a5f17fSdrahn void awacs_attach(struct device *, struct device *, void *);
95d9a5f17fSdrahn int awacs_intr(void *);
96d9a5f17fSdrahn int awacs_tx_intr(void *);
97d9a5f17fSdrahn int awacs_rx_intr(void *);
98d9a5f17fSdrahn
99d9a5f17fSdrahn int awacs_open(void *, int);
100d9a5f17fSdrahn void awacs_close(void *);
101d9a5f17fSdrahn int awacs_set_params(void *, int, int, struct audio_params *,
102d9a5f17fSdrahn struct audio_params *);
103d9a5f17fSdrahn int awacs_round_blocksize(void *, int);
104d9a5f17fSdrahn int awacs_trigger_output(void *, void *, void *, int, void (*)(void *),
105d9a5f17fSdrahn void *, struct audio_params *);
106d9a5f17fSdrahn int awacs_trigger_input(void *, void *, void *, int, void (*)(void *),
107d9a5f17fSdrahn void *, struct audio_params *);
108d9a5f17fSdrahn int awacs_halt_output(void *);
109d9a5f17fSdrahn int awacs_halt_input(void *);
110d9a5f17fSdrahn int awacs_set_port(void *, mixer_ctrl_t *);
111d9a5f17fSdrahn int awacs_get_port(void *, mixer_ctrl_t *);
112d9a5f17fSdrahn int awacs_query_devinfo(void *, mixer_devinfo_t *);
113d9a5f17fSdrahn size_t awacs_round_buffersize(void *, int, size_t);
114c4071fd1Smillert void *awacs_allocm(void *, int, size_t, int, int);
115d9a5f17fSdrahn
116d9a5f17fSdrahn static inline u_int awacs_read_reg(struct awacs_softc *, int);
117d9a5f17fSdrahn static inline void awacs_write_reg(struct awacs_softc *, int, int);
118d9a5f17fSdrahn void awacs_write_codec(struct awacs_softc *, int);
119d9a5f17fSdrahn void awacs_set_speaker_volume(struct awacs_softc *, int, int);
120d9a5f17fSdrahn void awacs_set_ext_volume(struct awacs_softc *, int, int);
121d7ee1e40Sjason void awacs_set_rate(struct awacs_softc *, struct audio_params *);
122d9a5f17fSdrahn
12389ed722cSmpi const struct cfattach awacs_ca = {
124d9a5f17fSdrahn sizeof(struct awacs_softc), awacs_match, awacs_attach
125d9a5f17fSdrahn };
126d9a5f17fSdrahn
127d9a5f17fSdrahn struct cfdriver awacs_cd = {
128d9a5f17fSdrahn NULL, "awacs", DV_DULL
129d9a5f17fSdrahn };
130d9a5f17fSdrahn
1310d6a2fdeSmiod const struct audio_hw_if awacs_hw_if = {
1325a8d990aSkn .open = awacs_open,
1335a8d990aSkn .close = awacs_close,
1345a8d990aSkn .set_params = awacs_set_params,
1355a8d990aSkn .round_blocksize = awacs_round_blocksize,
1365a8d990aSkn .halt_output = awacs_halt_output,
1375a8d990aSkn .halt_input = awacs_halt_input,
1385a8d990aSkn .set_port = awacs_set_port,
1395a8d990aSkn .get_port = awacs_get_port,
1405a8d990aSkn .query_devinfo = awacs_query_devinfo,
1415a8d990aSkn .allocm = awacs_allocm,
1425a8d990aSkn .round_buffersize = awacs_round_buffersize,
1435a8d990aSkn .trigger_output = awacs_trigger_output,
1445a8d990aSkn .trigger_input = awacs_trigger_input,
145d9a5f17fSdrahn };
146d9a5f17fSdrahn
147d9a5f17fSdrahn /* register offset */
148d9a5f17fSdrahn #define AWACS_SOUND_CTRL 0x00
149d9a5f17fSdrahn #define AWACS_CODEC_CTRL 0x10
150d9a5f17fSdrahn #define AWACS_CODEC_STATUS 0x20
151d9a5f17fSdrahn #define AWACS_CLIP_COUNT 0x30
152d9a5f17fSdrahn #define AWACS_BYTE_SWAP 0x40
153d9a5f17fSdrahn
154d9a5f17fSdrahn /* sound control */
155d9a5f17fSdrahn #define AWACS_INPUT_SUBFRAME0 0x00000001
156d9a5f17fSdrahn #define AWACS_INPUT_SUBFRAME1 0x00000002
157d9a5f17fSdrahn #define AWACS_INPUT_SUBFRAME2 0x00000004
158d9a5f17fSdrahn #define AWACS_INPUT_SUBFRAME3 0x00000008
159d9a5f17fSdrahn
160d9a5f17fSdrahn #define AWACS_OUTPUT_SUBFRAME0 0x00000010
161d9a5f17fSdrahn #define AWACS_OUTPUT_SUBFRAME1 0x00000020
162d9a5f17fSdrahn #define AWACS_OUTPUT_SUBFRAME2 0x00000040
163d9a5f17fSdrahn #define AWACS_OUTPUT_SUBFRAME3 0x00000080
164d9a5f17fSdrahn
165d9a5f17fSdrahn #define AWACS_RATE_44100 0x00000000
166d9a5f17fSdrahn #define AWACS_RATE_29400 0x00000100
167d9a5f17fSdrahn #define AWACS_RATE_22050 0x00000200
168d9a5f17fSdrahn #define AWACS_RATE_17640 0x00000300
169d9a5f17fSdrahn #define AWACS_RATE_14700 0x00000400
170d9a5f17fSdrahn #define AWACS_RATE_11025 0x00000500
171d9a5f17fSdrahn #define AWACS_RATE_8820 0x00000600
172d9a5f17fSdrahn #define AWACS_RATE_7350 0x00000700
173d9a5f17fSdrahn #define AWACS_RATE_MASK 0x00000700
174d9a5f17fSdrahn
175d9a5f17fSdrahn #define AWACS_CTL_CNTRLERR (1 << 11)
176d9a5f17fSdrahn #define AWACS_CTL_PORTCHG (1 << 12)
177d9a5f17fSdrahn #define AWACS_INT_CNTRLERR (1 << 13)
178d9a5f17fSdrahn #define AWACS_INT_PORTCHG (1 << 14)
179d9a5f17fSdrahn
180d9a5f17fSdrahn /* codec control */
181d9a5f17fSdrahn #define AWACS_CODEC_ADDR0 0x00000000
182d9a5f17fSdrahn #define AWACS_CODEC_ADDR1 0x00001000
183d9a5f17fSdrahn #define AWACS_CODEC_ADDR2 0x00002000
184d9a5f17fSdrahn #define AWACS_CODEC_ADDR4 0x00004000
185d9a5f17fSdrahn #define AWACS_CODEC_EMSEL0 0x00000000
186d9a5f17fSdrahn #define AWACS_CODEC_EMSEL1 0x00400000
187d9a5f17fSdrahn #define AWACS_CODEC_EMSEL2 0x00800000
188d9a5f17fSdrahn #define AWACS_CODEC_EMSEL4 0x00c00000
189d9a5f17fSdrahn #define AWACS_CODEC_BUSY 0x01000000
190d9a5f17fSdrahn
191d9a5f17fSdrahn /* cc0 */
192d9a5f17fSdrahn #define AWACS_DEFAULT_CD_GAIN 0x000000bb
193d9a5f17fSdrahn #define AWACS_INPUT_CD 0x00000200
194d9a5f17fSdrahn #define AWACS_INPUT_LINE 0x00000400
195d9a5f17fSdrahn #define AWACS_INPUT_MICROPHONE 0x00000800
196d9a5f17fSdrahn #define AWACS_INPUT_MASK 0x00000e00
197d9a5f17fSdrahn
198d9a5f17fSdrahn /* cc1 */
199d9a5f17fSdrahn #define AWACS_MUTE_SPEAKER 0x00000080
200d9a5f17fSdrahn #define AWACS_MUTE_HEADPHONE 0x00000200
201aadf1369Sgkoehler /*
202aadf1369Sgkoehler * iMacs that use this driver have a speaker amp power down feature,
203aadf1369Sgkoehler * triggered by this bit in cc1.
204aadf1369Sgkoehler */
205aadf1369Sgkoehler #define AWACS_MUTE_SPEAKER_IMAC 0x00000800
206d9a5f17fSdrahn
207d7ee1e40Sjason const struct awacs_speed_tab {
208d7ee1e40Sjason int rate;
209d7ee1e40Sjason u_int32_t bits;
210d7ee1e40Sjason } awacs_speeds[] = {
211d7ee1e40Sjason { 7350, AWACS_RATE_7350 },
212d7ee1e40Sjason { 8820, AWACS_RATE_8820 },
213d7ee1e40Sjason { 11025, AWACS_RATE_11025 },
214d7ee1e40Sjason { 14700, AWACS_RATE_14700 },
215d7ee1e40Sjason { 17640, AWACS_RATE_17640 },
216d7ee1e40Sjason { 22050, AWACS_RATE_22050 },
217d7ee1e40Sjason { 29400, AWACS_RATE_29400 },
218d7ee1e40Sjason { 44100, AWACS_RATE_44100 },
219d7ee1e40Sjason };
220d7ee1e40Sjason
221aadf1369Sgkoehler /* add hardware as needed */
222aadf1369Sgkoehler #define HW_IS_OTHER 0
223aadf1369Sgkoehler /* iMac (Late 1999) */
224aadf1369Sgkoehler #define HW_IS_IMAC1 1
225aadf1369Sgkoehler /* iMac (Summer 2000) and iMac (Early 2001, Summer 2001) */
226aadf1369Sgkoehler #define HW_IS_IMAC2 2
227aadf1369Sgkoehler
228aadf1369Sgkoehler /* Codecs status headphones mask */
229aadf1369Sgkoehler
230aadf1369Sgkoehler /* headphone connector on back */
231aadf1369Sgkoehler #define AWACS_STATUS_HP_CONN 8
232aadf1369Sgkoehler
233aadf1369Sgkoehler /* right connector on front */
234aadf1369Sgkoehler #define AWACS_STATUS_HP_RCONN_IMAC 4
235aadf1369Sgkoehler /* left connector on front */
236aadf1369Sgkoehler #define AWACS_STATUS_HP_LCONN_IMAC 2
237aadf1369Sgkoehler /* connector on the right side */
238aadf1369Sgkoehler #define AWACS_STATUS_HP_SCONN_IMAC 1
239aadf1369Sgkoehler
240d9a5f17fSdrahn int
awacs_match(struct device * parent,void * match,void * aux)241093da1aaSdrahn awacs_match(struct device *parent, void *match, void *aux)
242d9a5f17fSdrahn {
243d9a5f17fSdrahn struct confargs *ca = aux;
244d9a5f17fSdrahn
245d9a5f17fSdrahn if (strcmp(ca->ca_name, "awacs") != 0 &&
246d9a5f17fSdrahn strcmp(ca->ca_name, "davbus") != 0)
247d9a5f17fSdrahn return 0;
248d9a5f17fSdrahn
249cbeddb02Smiod #ifdef DEBUG
250d9a5f17fSdrahn printf("awacs: matched %s nreg %d nintr %d\n",
251d9a5f17fSdrahn ca->ca_name, ca->ca_nreg, ca->ca_nintr);
252cbeddb02Smiod #endif
253d9a5f17fSdrahn
254d9a5f17fSdrahn if (ca->ca_nreg < 24 || ca->ca_nintr < 12)
255d9a5f17fSdrahn return 0;
256d9a5f17fSdrahn
257d9a5f17fSdrahn /* XXX for now
258d9a5f17fSdrahn if (ca->ca_nintr > 12)
259d9a5f17fSdrahn return 0;
260d9a5f17fSdrahn */
261d9a5f17fSdrahn
262d9a5f17fSdrahn return 1;
263d9a5f17fSdrahn }
264d9a5f17fSdrahn
265d9a5f17fSdrahn void
awacs_attach(struct device * parent,struct device * self,void * aux)266093da1aaSdrahn awacs_attach(struct device *parent, struct device *self, void *aux)
267d9a5f17fSdrahn {
268d9a5f17fSdrahn struct awacs_softc *sc = (struct awacs_softc *)self;
269d9a5f17fSdrahn struct confargs *ca = aux;
270d9a5f17fSdrahn int cirq, oirq, iirq;
271d9a5f17fSdrahn int cirq_type, oirq_type, iirq_type;
272d9a5f17fSdrahn
273aadf1369Sgkoehler if (!strcmp(hw_prod, "PowerMac2,1"))
274aadf1369Sgkoehler sc->sc_awacs_hardware = HW_IS_IMAC1;
275aadf1369Sgkoehler else if (!strcmp(hw_prod, "PowerMac2,2") ||
276aadf1369Sgkoehler !strcmp(hw_prod, "PowerMac4,1"))
277aadf1369Sgkoehler sc->sc_awacs_hardware = HW_IS_IMAC2;
278aadf1369Sgkoehler else
279aadf1369Sgkoehler sc->sc_awacs_hardware = HW_IS_OTHER;
280aadf1369Sgkoehler /* set headphone mask */
281aadf1369Sgkoehler if (sc->sc_awacs_hardware == HW_IS_IMAC1 ||
282aadf1369Sgkoehler sc->sc_awacs_hardware == HW_IS_IMAC2)
283aadf1369Sgkoehler sc->sc_awacs_headphone_mask = AWACS_STATUS_HP_LCONN_IMAC |
284aadf1369Sgkoehler AWACS_STATUS_HP_RCONN_IMAC | AWACS_STATUS_HP_SCONN_IMAC;
285aadf1369Sgkoehler else
286aadf1369Sgkoehler sc->sc_awacs_headphone_mask = AWACS_STATUS_HP_CONN;
287aadf1369Sgkoehler
288d9a5f17fSdrahn ca->ca_reg[0] += ca->ca_baseaddr;
289d9a5f17fSdrahn ca->ca_reg[2] += ca->ca_baseaddr;
290d9a5f17fSdrahn ca->ca_reg[4] += ca->ca_baseaddr;
291d9a5f17fSdrahn
292d9a5f17fSdrahn sc->sc_reg = mapiodev(ca->ca_reg[0], ca->ca_reg[1]);
293d9a5f17fSdrahn
294e6d856e8Smickey sc->sc_dmat = ca->ca_dmat;
295d9a5f17fSdrahn sc->sc_odma = mapiodev(ca->ca_reg[2], ca->ca_reg[3]); /* out */
296d9a5f17fSdrahn sc->sc_idma = mapiodev(ca->ca_reg[4], ca->ca_reg[5]); /* in */
297e6d856e8Smickey sc->sc_odbdma = dbdma_alloc(sc->sc_dmat, AWACS_DMALIST_MAX);
298e6d856e8Smickey sc->sc_odmacmd = sc->sc_odbdma->d_addr;
299e6d856e8Smickey sc->sc_idbdma = dbdma_alloc(sc->sc_dmat, AWACS_DMALIST_MAX);
300e6d856e8Smickey sc->sc_idmacmd = sc->sc_idbdma->d_addr;
301d9a5f17fSdrahn
302d9a5f17fSdrahn if (ca->ca_nintr == 24) {
303d9a5f17fSdrahn cirq = ca->ca_intr[0];
304d9a5f17fSdrahn oirq = ca->ca_intr[2];
305d9a5f17fSdrahn iirq = ca->ca_intr[4];
306d9a5f17fSdrahn cirq_type = ca->ca_intr[1] ? IST_LEVEL : IST_EDGE;
307d9a5f17fSdrahn oirq_type = ca->ca_intr[3] ? IST_LEVEL : IST_EDGE;
308d9a5f17fSdrahn iirq_type = ca->ca_intr[5] ? IST_LEVEL : IST_EDGE;
309d9a5f17fSdrahn } else {
310d9a5f17fSdrahn cirq = ca->ca_intr[0];
311d9a5f17fSdrahn oirq = ca->ca_intr[1];
312d9a5f17fSdrahn iirq = ca->ca_intr[2];
313d9a5f17fSdrahn cirq_type = oirq_type = iirq_type = IST_LEVEL;
314d9a5f17fSdrahn }
3153b53f463Smpi mac_intr_establish(parent, cirq, cirq_type, IPL_AUDIO | IPL_MPSAFE,
3163b53f463Smpi awacs_intr, sc, sc->sc_dev.dv_xname);
3173b53f463Smpi mac_intr_establish(parent, oirq, oirq_type, IPL_AUDIO | IPL_MPSAFE,
3183b53f463Smpi awacs_tx_intr, sc, sc->sc_dev.dv_xname);
3193b53f463Smpi mac_intr_establish(parent, iirq, iirq_type, IPL_AUDIO | IPL_MPSAFE,
3203b53f463Smpi awacs_rx_intr, sc, sc->sc_dev.dv_xname);
321d9a5f17fSdrahn
322d9a5f17fSdrahn printf(": irq %d,%d,%d",
323d9a5f17fSdrahn cirq, oirq, iirq);
324d9a5f17fSdrahn
325d9a5f17fSdrahn sc->sc_soundctl = AWACS_INPUT_SUBFRAME0 | AWACS_OUTPUT_SUBFRAME0 |
326d9a5f17fSdrahn AWACS_RATE_44100 | AWACS_INT_PORTCHG;
327d9a5f17fSdrahn awacs_write_reg(sc, AWACS_SOUND_CTRL, sc->sc_soundctl);
328d9a5f17fSdrahn
329d9a5f17fSdrahn sc->sc_codecctl0 = AWACS_CODEC_ADDR0 | AWACS_CODEC_EMSEL0;
330d9a5f17fSdrahn sc->sc_codecctl1 = AWACS_CODEC_ADDR1 | AWACS_CODEC_EMSEL0;
331d9a5f17fSdrahn sc->sc_codecctl2 = AWACS_CODEC_ADDR2 | AWACS_CODEC_EMSEL0;
332d9a5f17fSdrahn sc->sc_codecctl4 = AWACS_CODEC_ADDR4 | AWACS_CODEC_EMSEL0;
333d9a5f17fSdrahn
334aadf1369Sgkoehler /* don't set CD in on iMacs, or else the outputs will be very noisy */
335aadf1369Sgkoehler if (sc->sc_awacs_hardware != HW_IS_IMAC1 &&
336aadf1369Sgkoehler sc->sc_awacs_hardware != HW_IS_IMAC2)
337d9a5f17fSdrahn sc->sc_codecctl0 |= AWACS_INPUT_CD | AWACS_DEFAULT_CD_GAIN;
338d9a5f17fSdrahn awacs_write_codec(sc, sc->sc_codecctl0);
339d9a5f17fSdrahn
340d9a5f17fSdrahn /* Set initial volume[s] */
341d9a5f17fSdrahn awacs_set_speaker_volume(sc, 80, 80);
342d9a5f17fSdrahn awacs_set_ext_volume(sc, 80, 80);
343d9a5f17fSdrahn
344d9a5f17fSdrahn /* Set loopback (for CD?) */
345d9a5f17fSdrahn /* sc->sc_codecctl1 |= 0x440; */
346d9a5f17fSdrahn sc->sc_codecctl1 |= 0x40;
347d9a5f17fSdrahn awacs_write_codec(sc, sc->sc_codecctl1);
348d9a5f17fSdrahn
349d9a5f17fSdrahn /* check for headphone present */
350aadf1369Sgkoehler if (awacs_read_reg(sc, AWACS_CODEC_STATUS) &
351aadf1369Sgkoehler sc->sc_awacs_headphone_mask) {
352d9a5f17fSdrahn /* default output to speakers */
353d9a5f17fSdrahn printf(" headphones");
354d9a5f17fSdrahn sc->sc_output_mask = 1 << 1;
355aadf1369Sgkoehler /* front headphones on iMacs are wired to the speakers output */
356aadf1369Sgkoehler if ((sc->sc_awacs_hardware != HW_IS_IMAC2 &&
357aadf1369Sgkoehler sc->sc_awacs_hardware != HW_IS_IMAC1) ||
358aadf1369Sgkoehler ((sc->sc_awacs_hardware == HW_IS_IMAC2 ||
359aadf1369Sgkoehler sc->sc_awacs_hardware == HW_IS_IMAC1) &&
360aadf1369Sgkoehler (awacs_read_reg(sc, AWACS_CODEC_STATUS) &
361aadf1369Sgkoehler AWACS_STATUS_HP_SCONN_IMAC))) {
362d9a5f17fSdrahn sc->sc_codecctl1 &= ~AWACS_MUTE_HEADPHONE;
363d9a5f17fSdrahn sc->sc_codecctl1 |= AWACS_MUTE_SPEAKER;
364aadf1369Sgkoehler }
365aadf1369Sgkoehler if (sc->sc_awacs_hardware == HW_IS_IMAC1)
366aadf1369Sgkoehler sc->sc_codecctl1 |= AWACS_MUTE_SPEAKER_IMAC;
367aadf1369Sgkoehler else if (sc->sc_awacs_hardware == HW_IS_IMAC2)
368aadf1369Sgkoehler sc->sc_codecctl1 &= ~AWACS_MUTE_SPEAKER_IMAC;
369d9a5f17fSdrahn awacs_write_codec(sc, sc->sc_codecctl1);
370d9a5f17fSdrahn } else {
371d9a5f17fSdrahn /* default output to speakers */
372d9a5f17fSdrahn printf(" speaker");
373d9a5f17fSdrahn sc->sc_output_mask = 1 << 0;
374d9a5f17fSdrahn sc->sc_codecctl1 &= ~AWACS_MUTE_SPEAKER;
375d9a5f17fSdrahn sc->sc_codecctl1 |= AWACS_MUTE_HEADPHONE;
376aadf1369Sgkoehler if (sc->sc_awacs_hardware == HW_IS_IMAC1)
377aadf1369Sgkoehler sc->sc_codecctl1 &= ~AWACS_MUTE_SPEAKER_IMAC;
378aadf1369Sgkoehler else if (sc->sc_awacs_hardware == HW_IS_IMAC2)
379aadf1369Sgkoehler sc->sc_codecctl1 |= AWACS_MUTE_SPEAKER_IMAC;
380d9a5f17fSdrahn awacs_write_codec(sc, sc->sc_codecctl1);
381d9a5f17fSdrahn }
382d9a5f17fSdrahn
383d9a5f17fSdrahn /* default input from CD */
384d9a5f17fSdrahn sc->sc_record_source = 1 << 0;
385d9a5f17fSdrahn sc->sc_codecctl0 &= ~AWACS_INPUT_MASK;
386aadf1369Sgkoehler if (sc->sc_awacs_hardware != HW_IS_IMAC1 &&
387aadf1369Sgkoehler sc->sc_awacs_hardware != HW_IS_IMAC2)
388d9a5f17fSdrahn sc->sc_codecctl0 |= AWACS_INPUT_CD;
389d9a5f17fSdrahn awacs_write_codec(sc, sc->sc_codecctl0);
390d9a5f17fSdrahn
391d9a5f17fSdrahn /* Enable interrupts and looping mode. */
392d9a5f17fSdrahn /* XXX ... */
393d9a5f17fSdrahn awacs_halt_output(sc);
394d9a5f17fSdrahn awacs_halt_input(sc);
395d9a5f17fSdrahn printf("\n");
396d9a5f17fSdrahn
3972baa08e2Santon audio_attach_mi(&awacs_hw_if, sc, NULL, &sc->sc_dev);
398d9a5f17fSdrahn }
399d9a5f17fSdrahn
400d9a5f17fSdrahn u_int
awacs_read_reg(struct awacs_softc * sc,int reg)401093da1aaSdrahn awacs_read_reg(struct awacs_softc *sc, int reg)
402d9a5f17fSdrahn {
403d9a5f17fSdrahn char *addr = sc->sc_reg;
404d9a5f17fSdrahn
405d9a5f17fSdrahn return in32rb(addr + reg);
406d9a5f17fSdrahn }
407d9a5f17fSdrahn
408d9a5f17fSdrahn void
awacs_write_reg(struct awacs_softc * sc,int reg,int val)409093da1aaSdrahn awacs_write_reg(struct awacs_softc *sc, int reg, int val)
410d9a5f17fSdrahn {
411d9a5f17fSdrahn char *addr = sc->sc_reg;
412d9a5f17fSdrahn
413d9a5f17fSdrahn out32rb(addr + reg, val);
414d9a5f17fSdrahn }
415d9a5f17fSdrahn
416d9a5f17fSdrahn void
awacs_write_codec(struct awacs_softc * sc,int value)417093da1aaSdrahn awacs_write_codec(struct awacs_softc *sc, int value)
418d9a5f17fSdrahn {
419d9a5f17fSdrahn awacs_write_reg(sc, AWACS_CODEC_CTRL, value);
420d9a5f17fSdrahn while (awacs_read_reg(sc, AWACS_CODEC_CTRL) & AWACS_CODEC_BUSY);
421d9a5f17fSdrahn }
422d9a5f17fSdrahn
423d9a5f17fSdrahn int
awacs_intr(void * v)424093da1aaSdrahn awacs_intr(void *v)
425d9a5f17fSdrahn {
426d9a5f17fSdrahn int reason;
427d9a5f17fSdrahn struct awacs_softc *sc = v;
428d9a5f17fSdrahn
429886882aaSratchov mtx_enter(&audio_lock);
430886882aaSratchov reason = awacs_read_reg(sc, AWACS_SOUND_CTRL);
431d9a5f17fSdrahn if (reason & AWACS_CTL_CNTRLERR) {
432d9a5f17fSdrahn /* change outputs ?? */
433d9a5f17fSdrahn }
434d9a5f17fSdrahn if (reason & AWACS_CTL_PORTCHG) {
435d9a5f17fSdrahn #ifdef DEBUG
436d9a5f17fSdrahn printf("status = %x\n", awacs_read_reg(sc, AWACS_CODEC_STATUS));
437d9a5f17fSdrahn #endif
438d9a5f17fSdrahn
439aadf1369Sgkoehler if (awacs_read_reg(sc, AWACS_CODEC_STATUS) &
440aadf1369Sgkoehler sc->sc_awacs_headphone_mask) {
441d9a5f17fSdrahn /* default output to speakers */
442d9a5f17fSdrahn sc->sc_output_mask = 1 << 1;
443aadf1369Sgkoehler /*
444aadf1369Sgkoehler * Front headphones on iMacs are wired to the
445aadf1369Sgkoehler * speakers output.
446aadf1369Sgkoehler */
447aadf1369Sgkoehler if ((sc->sc_awacs_hardware != HW_IS_IMAC2 &&
448aadf1369Sgkoehler sc->sc_awacs_hardware != HW_IS_IMAC1) ||
449aadf1369Sgkoehler ((sc->sc_awacs_hardware == HW_IS_IMAC2 ||
450aadf1369Sgkoehler sc->sc_awacs_hardware == HW_IS_IMAC1) &&
451aadf1369Sgkoehler (awacs_read_reg(sc, AWACS_CODEC_STATUS) &
452aadf1369Sgkoehler AWACS_STATUS_HP_SCONN_IMAC))) {
453d9a5f17fSdrahn sc->sc_codecctl1 &= ~AWACS_MUTE_HEADPHONE;
454d9a5f17fSdrahn sc->sc_codecctl1 |= AWACS_MUTE_SPEAKER;
455aadf1369Sgkoehler }
456aadf1369Sgkoehler if (sc->sc_awacs_hardware == HW_IS_IMAC1)
457aadf1369Sgkoehler sc->sc_codecctl1 |= AWACS_MUTE_SPEAKER_IMAC;
458aadf1369Sgkoehler else if (sc->sc_awacs_hardware == HW_IS_IMAC2)
459aadf1369Sgkoehler sc->sc_codecctl1 &= ~AWACS_MUTE_SPEAKER_IMAC;
460d9a5f17fSdrahn awacs_write_codec(sc, sc->sc_codecctl1);
461d9a5f17fSdrahn } else {
462d9a5f17fSdrahn /* default output to speakers */
463d9a5f17fSdrahn sc->sc_output_mask = 1 << 0;
464d9a5f17fSdrahn sc->sc_codecctl1 &= ~AWACS_MUTE_SPEAKER;
465d9a5f17fSdrahn sc->sc_codecctl1 |= AWACS_MUTE_HEADPHONE;
466aadf1369Sgkoehler if (sc->sc_awacs_hardware == HW_IS_IMAC1)
467aadf1369Sgkoehler sc->sc_codecctl1 &= ~AWACS_MUTE_SPEAKER_IMAC;
468aadf1369Sgkoehler else if (sc->sc_awacs_hardware == HW_IS_IMAC2)
469aadf1369Sgkoehler sc->sc_codecctl1 |= AWACS_MUTE_SPEAKER_IMAC;
470d9a5f17fSdrahn awacs_write_codec(sc, sc->sc_codecctl1);
471d9a5f17fSdrahn }
472d9a5f17fSdrahn }
473d9a5f17fSdrahn
474d9a5f17fSdrahn awacs_write_reg(sc, AWACS_SOUND_CTRL, reason); /* clear interrupt */
475886882aaSratchov mtx_leave(&audio_lock);
476d9a5f17fSdrahn return 1;
477d9a5f17fSdrahn }
478e6d856e8Smickey
479d9a5f17fSdrahn int
awacs_tx_intr(void * v)480093da1aaSdrahn awacs_tx_intr(void *v)
481d9a5f17fSdrahn {
482d9a5f17fSdrahn struct awacs_softc *sc = v;
483e6d856e8Smickey struct dbdma_command *cmd = sc->sc_odmap;
484e6d856e8Smickey u_int16_t c, status;
485d9a5f17fSdrahn
486e6d856e8Smickey /* if not set we are not running */
487e6d856e8Smickey if (!cmd)
488e6d856e8Smickey return (0);
489886882aaSratchov mtx_enter(&audio_lock);
490e6d856e8Smickey c = in16rb(&cmd->d_command);
491d9a5f17fSdrahn status = in16rb(&cmd->d_status);
492e6d856e8Smickey
493e6d856e8Smickey if (c >> 12 == DBDMA_CMD_OUT_LAST)
494e6d856e8Smickey sc->sc_odmap = sc->sc_odmacmd;
495e6d856e8Smickey else
496e6d856e8Smickey sc->sc_odmap++;
497e6d856e8Smickey
498e6d856e8Smickey if (c & (DBDMA_INT_ALWAYS << 4)) {
499d9a5f17fSdrahn cmd->d_status = 0;
500d9a5f17fSdrahn if (status) /* status == 0x8400 */
501d9a5f17fSdrahn if (sc->sc_ointr)
502d9a5f17fSdrahn (*sc->sc_ointr)(sc->sc_oarg);
503d9a5f17fSdrahn }
504886882aaSratchov mtx_leave(&audio_lock);
505e6d856e8Smickey return (1);
506d9a5f17fSdrahn }
507514ba8d6Sdrahn int
awacs_rx_intr(void * v)508093da1aaSdrahn awacs_rx_intr(void *v)
509514ba8d6Sdrahn {
510514ba8d6Sdrahn struct awacs_softc *sc = v;
511514ba8d6Sdrahn struct dbdma_command *cmd = sc->sc_idmap;
512514ba8d6Sdrahn u_int16_t c, status;
513514ba8d6Sdrahn
514514ba8d6Sdrahn /* if not set we are not running */
515514ba8d6Sdrahn if (!cmd)
516514ba8d6Sdrahn return (0);
517514ba8d6Sdrahn
518886882aaSratchov mtx_enter(&audio_lock);
519514ba8d6Sdrahn c = in16rb(&cmd->d_command);
520514ba8d6Sdrahn status = in16rb(&cmd->d_status);
521514ba8d6Sdrahn
522514ba8d6Sdrahn if (c >> 12 == DBDMA_CMD_IN_LAST)
523514ba8d6Sdrahn sc->sc_idmap = sc->sc_idmacmd;
524514ba8d6Sdrahn else
525514ba8d6Sdrahn sc->sc_idmap++;
526514ba8d6Sdrahn
527514ba8d6Sdrahn if (c & (DBDMA_INT_ALWAYS << 4)) {
528514ba8d6Sdrahn cmd->d_status = 0;
529514ba8d6Sdrahn if (status) /* status == 0x8400 */
530514ba8d6Sdrahn if (sc->sc_iintr)
531514ba8d6Sdrahn (*sc->sc_iintr)(sc->sc_iarg);
532514ba8d6Sdrahn }
533886882aaSratchov mtx_leave(&audio_lock);
534514ba8d6Sdrahn return (1);
535514ba8d6Sdrahn }
536d9a5f17fSdrahn
537d9a5f17fSdrahn int
awacs_open(void * h,int flags)538093da1aaSdrahn awacs_open(void *h, int flags)
539d9a5f17fSdrahn {
540d9a5f17fSdrahn return 0;
541d9a5f17fSdrahn }
542d9a5f17fSdrahn
543d9a5f17fSdrahn /*
544d9a5f17fSdrahn * Close function is called at splaudio().
545d9a5f17fSdrahn */
546d9a5f17fSdrahn void
awacs_close(void * h)547093da1aaSdrahn awacs_close(void *h)
548d9a5f17fSdrahn {
549d9a5f17fSdrahn struct awacs_softc *sc = h;
550d9a5f17fSdrahn
551886882aaSratchov /* XXX: halt_xxx() already called by upper layer */
552d9a5f17fSdrahn awacs_halt_output(sc);
553d9a5f17fSdrahn awacs_halt_input(sc);
554d9a5f17fSdrahn
555d9a5f17fSdrahn sc->sc_ointr = 0;
556d9a5f17fSdrahn sc->sc_iintr = 0;
557d9a5f17fSdrahn }
558d9a5f17fSdrahn
559d9a5f17fSdrahn int
awacs_set_params(void * h,int setmode,int usemode,struct audio_params * play,struct audio_params * rec)560093da1aaSdrahn awacs_set_params(void *h, int setmode, int usemode, struct audio_params *play,
561093da1aaSdrahn struct audio_params *rec)
562d9a5f17fSdrahn {
563d9a5f17fSdrahn struct awacs_softc *sc = h;
564d9a5f17fSdrahn struct audio_params *p;
565d7ee1e40Sjason int mode;
566d9a5f17fSdrahn
567d9a5f17fSdrahn /*
568d9a5f17fSdrahn * This device only has one clock, so make the sample rates match.
569d9a5f17fSdrahn */
570d9a5f17fSdrahn if (play->sample_rate != rec->sample_rate &&
571d9a5f17fSdrahn usemode == (AUMODE_PLAY | AUMODE_RECORD)) {
572d9a5f17fSdrahn if (setmode == AUMODE_PLAY) {
573d9a5f17fSdrahn rec->sample_rate = play->sample_rate;
574d9a5f17fSdrahn setmode |= AUMODE_RECORD;
575d9a5f17fSdrahn } else if (setmode == AUMODE_RECORD) {
576d9a5f17fSdrahn play->sample_rate = rec->sample_rate;
577d9a5f17fSdrahn setmode |= AUMODE_PLAY;
578d9a5f17fSdrahn } else
579d9a5f17fSdrahn return EINVAL;
580d9a5f17fSdrahn }
581d9a5f17fSdrahn
582d9a5f17fSdrahn for (mode = AUMODE_RECORD; mode != -1;
583d9a5f17fSdrahn mode = mode == AUMODE_RECORD ? AUMODE_PLAY : -1) {
584d9a5f17fSdrahn if ((setmode & mode) == 0)
585d9a5f17fSdrahn continue;
586d9a5f17fSdrahn
587d9a5f17fSdrahn p = mode == AUMODE_PLAY ? play : rec;
588d9a5f17fSdrahn
5899f8ca1f5Stodd if (p->sample_rate < 4000)
5909f8ca1f5Stodd p->sample_rate = 4000;
5919f8ca1f5Stodd if (p->sample_rate > 50000)
5929f8ca1f5Stodd p->sample_rate = 50000;
593d9a5f17fSdrahn
594d9a5f17fSdrahn awacs_write_reg(sc, AWACS_BYTE_SWAP, 0);
595d9a5f17fSdrahn
596ac2bb4f3Sratchov p->encoding = AUDIO_ENCODING_SLINEAR_BE;
5979f8ca1f5Stodd p->precision = 16;
598ac2bb4f3Sratchov p->channels = 2;
59916579317Sjakemsr p->bps = AUDIO_BPS(p->precision);
60016579317Sjakemsr p->msb = 1;
601d9a5f17fSdrahn }
602d9a5f17fSdrahn
603d9a5f17fSdrahn /* Set the speed */
604d7ee1e40Sjason awacs_set_rate(sc, p);
605d9a5f17fSdrahn
606d7ee1e40Sjason return (0);
607d9a5f17fSdrahn }
608d9a5f17fSdrahn
609d9a5f17fSdrahn int
awacs_round_blocksize(void * h,int size)610093da1aaSdrahn awacs_round_blocksize(void *h, int size)
611d9a5f17fSdrahn {
612e6d856e8Smickey if (size < PAGE_SIZE)
613e6d856e8Smickey size = PAGE_SIZE;
614e6d856e8Smickey return (size + PAGE_SIZE / 2) & ~(PGOFSET);
615d9a5f17fSdrahn }
616d9a5f17fSdrahn
617d9a5f17fSdrahn int
awacs_halt_output(void * h)618093da1aaSdrahn awacs_halt_output(void *h)
619d9a5f17fSdrahn {
620d9a5f17fSdrahn struct awacs_softc *sc = h;
621d9a5f17fSdrahn
622886882aaSratchov mtx_enter(&audio_lock);
623d9a5f17fSdrahn dbdma_stop(sc->sc_odma);
624d9a5f17fSdrahn dbdma_reset(sc->sc_odma);
625d9a5f17fSdrahn dbdma_stop(sc->sc_odma);
626e6d856e8Smickey sc->sc_odmap = NULL;
627886882aaSratchov mtx_leave(&audio_lock);
628d9a5f17fSdrahn return 0;
629d9a5f17fSdrahn }
630d9a5f17fSdrahn
631d9a5f17fSdrahn int
awacs_halt_input(void * h)632093da1aaSdrahn awacs_halt_input(void *h)
633d9a5f17fSdrahn {
634d9a5f17fSdrahn struct awacs_softc *sc = h;
635d9a5f17fSdrahn
636886882aaSratchov mtx_enter(&audio_lock);
637d9a5f17fSdrahn dbdma_stop(sc->sc_idma);
638d9a5f17fSdrahn dbdma_reset(sc->sc_idma);
639886882aaSratchov mtx_leave(&audio_lock);
640d9a5f17fSdrahn return 0;
641d9a5f17fSdrahn }
642d9a5f17fSdrahn
643d9a5f17fSdrahn enum {
644d9a5f17fSdrahn AWACS_OUTPUT_SELECT,
645d9a5f17fSdrahn AWACS_VOL_SPEAKER,
646d9a5f17fSdrahn AWACS_VOL_HEADPHONE,
647d9a5f17fSdrahn AWACS_OUTPUT_CLASS,
648d9a5f17fSdrahn AWACS_MONITOR_CLASS,
649d9a5f17fSdrahn AWACS_INPUT_SELECT,
650d9a5f17fSdrahn AWACS_VOL_INPUT,
651d9a5f17fSdrahn AWACS_INPUT_CLASS,
652d9a5f17fSdrahn AWACS_RECORD_CLASS,
653d9a5f17fSdrahn AWACS_ENUM_LAST
654d9a5f17fSdrahn };
655d9a5f17fSdrahn
656d9a5f17fSdrahn int
awacs_set_port(void * h,mixer_ctrl_t * mc)657093da1aaSdrahn awacs_set_port(void *h, mixer_ctrl_t *mc)
658d9a5f17fSdrahn {
659d9a5f17fSdrahn struct awacs_softc *sc = h;
660d9a5f17fSdrahn int l, r;
661d9a5f17fSdrahn
662d9a5f17fSdrahn DPRINTF("awacs_set_port dev = %d, type = %d\n", mc->dev, mc->type);
663d9a5f17fSdrahn
664d9a5f17fSdrahn l = mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
665d9a5f17fSdrahn r = mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
666d9a5f17fSdrahn
667d9a5f17fSdrahn switch (mc->dev) {
668d9a5f17fSdrahn case AWACS_OUTPUT_SELECT:
669d9a5f17fSdrahn /* no change necessary? */
670d9a5f17fSdrahn if (mc->un.mask == sc->sc_output_mask)
671d9a5f17fSdrahn return 0;
672514ba8d6Sdrahn sc->sc_codecctl1 |= AWACS_MUTE_SPEAKER | AWACS_MUTE_HEADPHONE;
673514ba8d6Sdrahn if (mc->un.mask & 1 << 0)
674d9a5f17fSdrahn sc->sc_codecctl1 &= ~AWACS_MUTE_SPEAKER;
675514ba8d6Sdrahn if (mc->un.mask & 1 << 1)
676d9a5f17fSdrahn sc->sc_codecctl1 &= ~AWACS_MUTE_HEADPHONE;
677514ba8d6Sdrahn
678d9a5f17fSdrahn awacs_write_codec(sc, sc->sc_codecctl1);
679d9a5f17fSdrahn sc->sc_output_mask = mc->un.mask;
680d9a5f17fSdrahn return 0;
681d9a5f17fSdrahn
682d9a5f17fSdrahn case AWACS_VOL_SPEAKER:
683d9a5f17fSdrahn awacs_set_speaker_volume(sc, l, r);
684d9a5f17fSdrahn return 0;
685d9a5f17fSdrahn
686d9a5f17fSdrahn case AWACS_VOL_HEADPHONE:
687d9a5f17fSdrahn awacs_set_ext_volume(sc, l, r);
688d9a5f17fSdrahn return 0;
689d9a5f17fSdrahn
690d9a5f17fSdrahn case AWACS_VOL_INPUT:
691d9a5f17fSdrahn sc->sc_codecctl0 &= ~0xff;
692d9a5f17fSdrahn sc->sc_codecctl0 |= (l & 0xf0) | (r >> 4);
693d9a5f17fSdrahn awacs_write_codec(sc, sc->sc_codecctl0);
694d9a5f17fSdrahn return 0;
695d9a5f17fSdrahn
696d9a5f17fSdrahn case AWACS_INPUT_SELECT:
697d9a5f17fSdrahn /* no change necessary? */
698d9a5f17fSdrahn if (mc->un.mask == sc->sc_record_source)
699d9a5f17fSdrahn return 0;
700d9a5f17fSdrahn switch(mc->un.mask) {
701d9a5f17fSdrahn case 1<<0: /* CD */
702d9a5f17fSdrahn sc->sc_codecctl0 &= ~AWACS_INPUT_MASK;
703d9a5f17fSdrahn sc->sc_codecctl0 |= AWACS_INPUT_CD;
704d9a5f17fSdrahn awacs_write_codec(sc, sc->sc_codecctl0);
705d9a5f17fSdrahn break;
706d9a5f17fSdrahn case 1<<1: /* microphone */
707d9a5f17fSdrahn sc->sc_codecctl0 &= ~AWACS_INPUT_MASK;
708d9a5f17fSdrahn sc->sc_codecctl0 |= AWACS_INPUT_MICROPHONE;
709d9a5f17fSdrahn awacs_write_codec(sc, sc->sc_codecctl0);
710d9a5f17fSdrahn break;
711d9a5f17fSdrahn case 1<<2: /* line in */
712d9a5f17fSdrahn sc->sc_codecctl0 &= ~AWACS_INPUT_MASK;
713d9a5f17fSdrahn sc->sc_codecctl0 |= AWACS_INPUT_LINE;
714d9a5f17fSdrahn awacs_write_codec(sc, sc->sc_codecctl0);
715d9a5f17fSdrahn break;
716d9a5f17fSdrahn default: /* invalid argument */
717d9a5f17fSdrahn return -1;
718d9a5f17fSdrahn }
719d9a5f17fSdrahn sc->sc_record_source = mc->un.mask;
720d9a5f17fSdrahn return 0;
721d9a5f17fSdrahn }
722d9a5f17fSdrahn
723d9a5f17fSdrahn return ENXIO;
724d9a5f17fSdrahn }
725d9a5f17fSdrahn
726d9a5f17fSdrahn int
awacs_get_port(void * h,mixer_ctrl_t * mc)727093da1aaSdrahn awacs_get_port(void *h, mixer_ctrl_t *mc)
728d9a5f17fSdrahn {
729d9a5f17fSdrahn struct awacs_softc *sc = h;
730d9a5f17fSdrahn int vol, l, r;
731d9a5f17fSdrahn
732d9a5f17fSdrahn DPRINTF("awacs_get_port dev = %d, type = %d\n", mc->dev, mc->type);
733d9a5f17fSdrahn
734d9a5f17fSdrahn switch (mc->dev) {
735d9a5f17fSdrahn case AWACS_OUTPUT_SELECT:
736d9a5f17fSdrahn mc->un.mask = sc->sc_output_mask;
737d9a5f17fSdrahn return 0;
738d9a5f17fSdrahn
739d9a5f17fSdrahn case AWACS_VOL_SPEAKER:
740d9a5f17fSdrahn vol = sc->sc_codecctl4;
741d9a5f17fSdrahn l = (15 - ((vol & 0x3c0) >> 6)) * 16;
742d9a5f17fSdrahn r = (15 - (vol & 0x0f)) * 16;
743d9a5f17fSdrahn mc->un.mask = 1 << 0;
744d9a5f17fSdrahn mc->un.value.num_channels = 2;
745d9a5f17fSdrahn mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = l;
746d9a5f17fSdrahn mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = r;
747d9a5f17fSdrahn return 0;
748d9a5f17fSdrahn
749d9a5f17fSdrahn case AWACS_VOL_HEADPHONE:
750d9a5f17fSdrahn vol = sc->sc_codecctl2;
751d9a5f17fSdrahn l = (15 - ((vol & 0x3c0) >> 6)) * 16;
752d9a5f17fSdrahn r = (15 - (vol & 0x0f)) * 16;
753d9a5f17fSdrahn mc->un.mask = 1 << 1;
754d9a5f17fSdrahn mc->un.value.num_channels = 2;
755d9a5f17fSdrahn mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = l;
756d9a5f17fSdrahn mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = r;
757d9a5f17fSdrahn return 0;
758d9a5f17fSdrahn
759d9a5f17fSdrahn case AWACS_INPUT_SELECT:
760d9a5f17fSdrahn mc->un.mask = sc->sc_record_source;
761d9a5f17fSdrahn return 0;
762d9a5f17fSdrahn
763d9a5f17fSdrahn case AWACS_VOL_INPUT:
764d9a5f17fSdrahn vol = sc->sc_codecctl0 & 0xff;
765d9a5f17fSdrahn l = (vol & 0xf0);
766d9a5f17fSdrahn r = (vol & 0x0f) << 4;
767d9a5f17fSdrahn mc->un.mask = sc->sc_record_source;
768d9a5f17fSdrahn mc->un.value.num_channels = 2;
769d9a5f17fSdrahn mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = l;
770d9a5f17fSdrahn mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = r;
771d9a5f17fSdrahn return 0;
772d9a5f17fSdrahn
773d9a5f17fSdrahn default:
774d9a5f17fSdrahn return ENXIO;
775d9a5f17fSdrahn }
776d9a5f17fSdrahn
777d9a5f17fSdrahn return 0;
778d9a5f17fSdrahn }
779d9a5f17fSdrahn
780d9a5f17fSdrahn int
awacs_query_devinfo(void * h,mixer_devinfo_t * dip)781093da1aaSdrahn awacs_query_devinfo(void *h, mixer_devinfo_t *dip)
782d9a5f17fSdrahn {
783d9a5f17fSdrahn DPRINTF("query_devinfo %d\n", dip->index);
784d9a5f17fSdrahn
785d9a5f17fSdrahn switch (dip->index) {
786d9a5f17fSdrahn
787d9a5f17fSdrahn case AWACS_OUTPUT_SELECT:
788a7f41e16Sbrad dip->mixer_class = AWACS_OUTPUT_CLASS;
789a7f41e16Sbrad strlcpy(dip->label.name, AudioNselect, sizeof dip->label.name);
790d9a5f17fSdrahn dip->type = AUDIO_MIXER_SET;
791d9a5f17fSdrahn dip->prev = dip->next = AUDIO_MIXER_LAST;
792d9a5f17fSdrahn dip->un.s.num_mem = 2;
793094fa01fSderaadt strlcpy(dip->un.s.member[0].label.name, AudioNspeaker,
794094fa01fSderaadt sizeof dip->un.s.member[0].label.name);
795d9a5f17fSdrahn dip->un.s.member[0].mask = 1 << 0;
796094fa01fSderaadt strlcpy(dip->un.s.member[1].label.name, AudioNheadphone,
797094fa01fSderaadt sizeof dip->un.s.member[0].label.name);
798d9a5f17fSdrahn dip->un.s.member[1].mask = 1 << 1;
799d9a5f17fSdrahn return 0;
800d9a5f17fSdrahn
801d9a5f17fSdrahn case AWACS_VOL_SPEAKER:
802d9a5f17fSdrahn dip->mixer_class = AWACS_OUTPUT_CLASS;
803094fa01fSderaadt strlcpy(dip->label.name, AudioNspeaker,
804094fa01fSderaadt sizeof dip->label.name);
805d9a5f17fSdrahn dip->type = AUDIO_MIXER_VALUE;
806d9a5f17fSdrahn dip->prev = dip->next = AUDIO_MIXER_LAST;
807d9a5f17fSdrahn dip->un.v.num_channels = 2;
808094fa01fSderaadt strlcpy(dip->un.v.units.name, AudioNvolume,
809094fa01fSderaadt sizeof dip->un.v.units.name);
810d9a5f17fSdrahn return 0;
811d9a5f17fSdrahn
812d9a5f17fSdrahn case AWACS_VOL_HEADPHONE:
813d9a5f17fSdrahn dip->mixer_class = AWACS_OUTPUT_CLASS;
814094fa01fSderaadt strlcpy(dip->label.name, AudioNheadphone,
815094fa01fSderaadt sizeof dip->label.name);
816d9a5f17fSdrahn dip->type = AUDIO_MIXER_VALUE;
817d9a5f17fSdrahn dip->prev = dip->next = AUDIO_MIXER_LAST;
818d9a5f17fSdrahn dip->un.v.num_channels = 2;
819094fa01fSderaadt strlcpy(dip->un.v.units.name, AudioNvolume,
820094fa01fSderaadt sizeof dip->un.v.units.name);
821d9a5f17fSdrahn return 0;
822d9a5f17fSdrahn
823d9a5f17fSdrahn case AWACS_INPUT_SELECT:
824514ba8d6Sdrahn dip->mixer_class = AWACS_RECORD_CLASS;
825094fa01fSderaadt strlcpy(dip->label.name, AudioNsource, sizeof dip->label.name);
826d9a5f17fSdrahn dip->type = AUDIO_MIXER_SET;
827d9a5f17fSdrahn dip->prev = dip->next = AUDIO_MIXER_LAST;
828d9a5f17fSdrahn dip->un.s.num_mem = 3;
829094fa01fSderaadt strlcpy(dip->un.s.member[0].label.name, AudioNcd,
830094fa01fSderaadt sizeof dip->un.s.member[0].label.name);
831d9a5f17fSdrahn dip->un.s.member[0].mask = 1 << 0;
832094fa01fSderaadt strlcpy(dip->un.s.member[1].label.name, AudioNmicrophone,
833094fa01fSderaadt sizeof dip->un.s.member[1].label.name);
834d9a5f17fSdrahn dip->un.s.member[1].mask = 1 << 1;
835094fa01fSderaadt strlcpy(dip->un.s.member[2].label.name, AudioNline,
836094fa01fSderaadt sizeof dip->un.s.member[2].label.name);
837d9a5f17fSdrahn dip->un.s.member[2].mask = 1 << 2;
838d9a5f17fSdrahn return 0;
839d9a5f17fSdrahn
840d9a5f17fSdrahn case AWACS_VOL_INPUT:
841514ba8d6Sdrahn dip->mixer_class = AWACS_RECORD_CLASS;
842094fa01fSderaadt strlcpy(dip->label.name, AudioNmaster, sizeof dip->label.name);
843d9a5f17fSdrahn dip->type = AUDIO_MIXER_VALUE;
844d9a5f17fSdrahn dip->prev = dip->next = AUDIO_MIXER_LAST;
845d9a5f17fSdrahn dip->un.v.num_channels = 2;
846094fa01fSderaadt strlcpy(dip->un.v.units.name, AudioNvolume,
847094fa01fSderaadt sizeof dip->un.v.units.name);
848d9a5f17fSdrahn return 0;
849d9a5f17fSdrahn
850d9a5f17fSdrahn case AWACS_MONITOR_CLASS:
851d9a5f17fSdrahn dip->mixer_class = AWACS_MONITOR_CLASS;
852094fa01fSderaadt strlcpy(dip->label.name, AudioCmonitor, sizeof dip->label.name);
853d9a5f17fSdrahn dip->type = AUDIO_MIXER_CLASS;
854d9a5f17fSdrahn dip->next = dip->prev = AUDIO_MIXER_LAST;
855d9a5f17fSdrahn return 0;
856d9a5f17fSdrahn
857d9a5f17fSdrahn case AWACS_OUTPUT_CLASS:
858d9a5f17fSdrahn dip->mixer_class = AWACS_OUTPUT_CLASS;
859094fa01fSderaadt strlcpy(dip->label.name, AudioCoutputs, sizeof dip->label.name);
860d9a5f17fSdrahn dip->type = AUDIO_MIXER_CLASS;
861d9a5f17fSdrahn dip->next = dip->prev = AUDIO_MIXER_LAST;
862d9a5f17fSdrahn return 0;
863d9a5f17fSdrahn
864d9a5f17fSdrahn case AWACS_RECORD_CLASS:
865d9a5f17fSdrahn dip->mixer_class = AWACS_MONITOR_CLASS;
866094fa01fSderaadt strlcpy(dip->label.name, AudioCrecord, sizeof dip->label.name);
867d9a5f17fSdrahn dip->type = AUDIO_MIXER_CLASS;
868d9a5f17fSdrahn dip->next = dip->prev = AUDIO_MIXER_LAST;
869d9a5f17fSdrahn return 0;
870d9a5f17fSdrahn }
871d9a5f17fSdrahn
872d9a5f17fSdrahn return ENXIO;
873d9a5f17fSdrahn }
874d9a5f17fSdrahn
875d9a5f17fSdrahn size_t
awacs_round_buffersize(void * h,int dir,size_t size)876093da1aaSdrahn awacs_round_buffersize(void *h, int dir, size_t size)
877d9a5f17fSdrahn {
878e6d856e8Smickey size = (size + PGOFSET) & ~(PGOFSET);
879e6d856e8Smickey if (size > AWACS_DMALIST_MAX * AWACS_DMASEG_MAX)
880e6d856e8Smickey size = AWACS_DMALIST_MAX * AWACS_DMASEG_MAX;
881e6d856e8Smickey return (size);
882e6d856e8Smickey }
883e6d856e8Smickey
884e6d856e8Smickey void *
awacs_allocm(void * h,int dir,size_t size,int type,int flags)885093da1aaSdrahn awacs_allocm(void *h, int dir, size_t size, int type, int flags)
886e6d856e8Smickey {
887e6d856e8Smickey struct awacs_softc *sc = h;
888e6d856e8Smickey struct awacs_dma *p;
889e6d856e8Smickey int error;
890e6d856e8Smickey
891e6d856e8Smickey if (size > AWACS_DMALIST_MAX * AWACS_DMASEG_MAX)
892e6d856e8Smickey return (NULL);
893e6d856e8Smickey
894bc9397c2Skrw p = malloc(sizeof(*p), type, flags | M_ZERO);
895e6d856e8Smickey if (!p)
896e6d856e8Smickey return (NULL);
897e6d856e8Smickey
898e6d856e8Smickey /* convert to the bus.h style, not used otherwise */
899e6d856e8Smickey if (flags & M_NOWAIT)
900e6d856e8Smickey flags = BUS_DMA_NOWAIT;
901e6d856e8Smickey
902e6d856e8Smickey p->size = size;
903e6d856e8Smickey if ((error = bus_dmamem_alloc(sc->sc_dmat, p->size, NBPG, 0, p->segs,
904e6d856e8Smickey 1, &p->nsegs, flags)) != 0) {
905e6d856e8Smickey printf("%s: unable to allocate dma, error = %d\n",
906e6d856e8Smickey sc->sc_dev.dv_xname, error);
90758d5cf8aSderaadt free(p, type, sizeof *p);
908e6d856e8Smickey return NULL;
909e6d856e8Smickey }
910e6d856e8Smickey
911e6d856e8Smickey if ((error = bus_dmamem_map(sc->sc_dmat, p->segs, p->nsegs, p->size,
912e6d856e8Smickey &p->addr, flags | BUS_DMA_COHERENT)) != 0) {
913e6d856e8Smickey printf("%s: unable to map dma, error = %d\n",
914e6d856e8Smickey sc->sc_dev.dv_xname, error);
915e6d856e8Smickey bus_dmamem_free(sc->sc_dmat, p->segs, p->nsegs);
91658d5cf8aSderaadt free(p, type, sizeof *p);
917e6d856e8Smickey return NULL;
918e6d856e8Smickey }
919e6d856e8Smickey
920e6d856e8Smickey if ((error = bus_dmamap_create(sc->sc_dmat, p->size, 1,
921e6d856e8Smickey p->size, 0, flags, &p->map)) != 0) {
922e6d856e8Smickey printf("%s: unable to create dma map, error = %d\n",
923e6d856e8Smickey sc->sc_dev.dv_xname, error);
924e6d856e8Smickey bus_dmamem_unmap(sc->sc_dmat, p->addr, size);
925e6d856e8Smickey bus_dmamem_free(sc->sc_dmat, p->segs, p->nsegs);
92658d5cf8aSderaadt free(p, type, sizeof *p);
927e6d856e8Smickey return NULL;
928e6d856e8Smickey }
929e6d856e8Smickey
930e6d856e8Smickey if ((error = bus_dmamap_load(sc->sc_dmat, p->map, p->addr, p->size,
931e6d856e8Smickey NULL, flags)) != 0) {
932e6d856e8Smickey printf("%s: unable to load dma map, error = %d\n",
933e6d856e8Smickey sc->sc_dev.dv_xname, error);
934e6d856e8Smickey bus_dmamap_destroy(sc->sc_dmat, p->map);
935e6d856e8Smickey bus_dmamem_unmap(sc->sc_dmat, p->addr, size);
936e6d856e8Smickey bus_dmamem_free(sc->sc_dmat, p->segs, p->nsegs);
93758d5cf8aSderaadt free(p, type, sizeof *p);
938e6d856e8Smickey return NULL;
939e6d856e8Smickey }
940e6d856e8Smickey
941e6d856e8Smickey p->next = sc->sc_dmas;
942e6d856e8Smickey sc->sc_dmas = p;
943e6d856e8Smickey
944e6d856e8Smickey return p->addr;
945d9a5f17fSdrahn }
946d9a5f17fSdrahn
947d9a5f17fSdrahn int
awacs_trigger_output(void * h,void * start,void * end,int bsize,void (* intr)(void *),void * arg,struct audio_params * param)948093da1aaSdrahn awacs_trigger_output(void *h, void *start, void *end, int bsize,
949093da1aaSdrahn void (*intr)(void *), void *arg, struct audio_params *param)
950d9a5f17fSdrahn {
951d9a5f17fSdrahn struct awacs_softc *sc = h;
952e6d856e8Smickey struct awacs_dma *p;
953d9a5f17fSdrahn struct dbdma_command *cmd = sc->sc_odmacmd;
954e6d856e8Smickey vaddr_t spa, pa, epa;
955e6d856e8Smickey int c;
956d9a5f17fSdrahn
957d9a5f17fSdrahn DPRINTF("trigger_output %p %p 0x%x\n", start, end, bsize);
958d9a5f17fSdrahn
959*36dba039Sjsg for (p = sc->sc_dmas; p && p->addr != start; p = p->next)
960*36dba039Sjsg ;
961e6d856e8Smickey if (!p)
962e6d856e8Smickey return -1;
963e6d856e8Smickey
964d9a5f17fSdrahn sc->sc_ointr = intr;
965d9a5f17fSdrahn sc->sc_oarg = arg;
966e6d856e8Smickey sc->sc_odmap = sc->sc_odmacmd;
967d9a5f17fSdrahn
968886882aaSratchov mtx_enter(&audio_lock);
969e6d856e8Smickey spa = p->segs[0].ds_addr;
970e6d856e8Smickey c = DBDMA_CMD_OUT_MORE;
971e6d856e8Smickey for (pa = spa, epa = spa + (end - start);
972e6d856e8Smickey pa < epa; pa += bsize, cmd++) {
973d9a5f17fSdrahn
974e6d856e8Smickey if (pa + bsize == epa)
975e6d856e8Smickey c = DBDMA_CMD_OUT_LAST;
976d9a5f17fSdrahn
977e6d856e8Smickey DBDMA_BUILD(cmd, c, 0, bsize, pa, DBDMA_INT_ALWAYS,
978e6d856e8Smickey DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER);
979d9a5f17fSdrahn }
980d9a5f17fSdrahn
981d9a5f17fSdrahn DBDMA_BUILD(cmd, DBDMA_CMD_NOP, 0, 0, 0,
982d9a5f17fSdrahn DBDMA_INT_NEVER, DBDMA_WAIT_NEVER, DBDMA_BRANCH_ALWAYS);
983e6d856e8Smickey dbdma_st32(&cmd->d_cmddep, sc->sc_odbdma->d_paddr);
984d9a5f17fSdrahn
985e6d856e8Smickey dbdma_start(sc->sc_odma, sc->sc_odbdma);
986d9a5f17fSdrahn
987886882aaSratchov mtx_leave(&audio_lock);
988d9a5f17fSdrahn return 0;
989d9a5f17fSdrahn }
990d9a5f17fSdrahn
991d9a5f17fSdrahn int
awacs_trigger_input(void * h,void * start,void * end,int bsize,void (* intr)(void *),void * arg,struct audio_params * param)992093da1aaSdrahn awacs_trigger_input(void *h, void *start, void *end, int bsize,
993093da1aaSdrahn void (*intr)(void *), void *arg, struct audio_params *param)
994d9a5f17fSdrahn {
995514ba8d6Sdrahn struct awacs_softc *sc = h;
996514ba8d6Sdrahn struct awacs_dma *p;
997514ba8d6Sdrahn struct dbdma_command *cmd = sc->sc_idmacmd;
998514ba8d6Sdrahn vaddr_t spa, pa, epa;
999514ba8d6Sdrahn int c;
1000d9a5f17fSdrahn
1001f76df540Smiod DPRINTF("trigger_input %p %p 0x%x\n", start, end, bsize);
1002514ba8d6Sdrahn
1003*36dba039Sjsg for (p = sc->sc_dmas; p && p->addr != start; p = p->next)
1004*36dba039Sjsg ;
1005514ba8d6Sdrahn if (!p)
1006514ba8d6Sdrahn return -1;
1007514ba8d6Sdrahn
1008514ba8d6Sdrahn sc->sc_iintr = intr;
1009514ba8d6Sdrahn sc->sc_iarg = arg;
1010514ba8d6Sdrahn sc->sc_idmap = sc->sc_idmacmd;
1011514ba8d6Sdrahn
1012886882aaSratchov mtx_enter(&audio_lock);
1013514ba8d6Sdrahn spa = p->segs[0].ds_addr;
1014514ba8d6Sdrahn c = DBDMA_CMD_IN_MORE;
1015514ba8d6Sdrahn for (pa = spa, epa = spa + (end - start);
1016514ba8d6Sdrahn pa < epa; pa += bsize, cmd++) {
1017514ba8d6Sdrahn
1018514ba8d6Sdrahn if (pa + bsize == epa)
1019514ba8d6Sdrahn c = DBDMA_CMD_IN_LAST;
1020514ba8d6Sdrahn
1021514ba8d6Sdrahn DBDMA_BUILD(cmd, c, 0, bsize, pa, DBDMA_INT_ALWAYS,
1022514ba8d6Sdrahn DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER);
1023514ba8d6Sdrahn }
1024514ba8d6Sdrahn
1025514ba8d6Sdrahn DBDMA_BUILD(cmd, DBDMA_CMD_NOP, 0, 0, 0,
1026514ba8d6Sdrahn DBDMA_INT_NEVER, DBDMA_WAIT_NEVER, DBDMA_BRANCH_ALWAYS);
1027514ba8d6Sdrahn dbdma_st32(&cmd->d_cmddep, sc->sc_idbdma->d_paddr);
1028514ba8d6Sdrahn
1029514ba8d6Sdrahn dbdma_start(sc->sc_idma, sc->sc_idbdma);
1030514ba8d6Sdrahn
1031886882aaSratchov mtx_leave(&audio_lock);
1032514ba8d6Sdrahn return 0;
1033d9a5f17fSdrahn }
1034d9a5f17fSdrahn
1035d9a5f17fSdrahn void
awacs_set_speaker_volume(struct awacs_softc * sc,int left,int right)1036093da1aaSdrahn awacs_set_speaker_volume(struct awacs_softc *sc, int left, int right)
1037d9a5f17fSdrahn {
1038d9a5f17fSdrahn int lval = 15 - (left & 0xff) / 16;
1039d9a5f17fSdrahn int rval = 15 - (right & 0xff) / 16;
1040d9a5f17fSdrahn
1041d9a5f17fSdrahn DPRINTF("speaker_volume %d %d\n", lval, rval);
1042d9a5f17fSdrahn
1043d9a5f17fSdrahn sc->sc_codecctl4 &= ~0x3cf;
1044d9a5f17fSdrahn sc->sc_codecctl4 |= (lval << 6) | rval;
1045d9a5f17fSdrahn awacs_write_codec(sc, sc->sc_codecctl4);
1046d9a5f17fSdrahn }
1047d9a5f17fSdrahn
1048d9a5f17fSdrahn void
awacs_set_ext_volume(struct awacs_softc * sc,int left,int right)1049093da1aaSdrahn awacs_set_ext_volume(struct awacs_softc *sc, int left, int right)
1050d9a5f17fSdrahn {
1051d9a5f17fSdrahn int lval = 15 - (left & 0xff) / 16;
1052d9a5f17fSdrahn int rval = 15 - (right & 0xff) / 16;
1053d9a5f17fSdrahn
1054d9a5f17fSdrahn DPRINTF("ext_volume %d %d\n", lval, rval);
1055d9a5f17fSdrahn
1056d9a5f17fSdrahn sc->sc_codecctl2 &= ~0x3cf;
1057d9a5f17fSdrahn sc->sc_codecctl2 |= (lval << 6) | rval;
1058d9a5f17fSdrahn awacs_write_codec(sc, sc->sc_codecctl2);
1059d9a5f17fSdrahn }
1060d9a5f17fSdrahn
1061d7ee1e40Sjason void
awacs_set_rate(struct awacs_softc * sc,struct audio_params * p)1062d7ee1e40Sjason awacs_set_rate(struct awacs_softc *sc, struct audio_params *p)
1063d9a5f17fSdrahn {
1064d7ee1e40Sjason int selected = -1;
1065d7ee1e40Sjason size_t n, i;
1066d9a5f17fSdrahn
1067d7ee1e40Sjason n = sizeof(awacs_speeds)/sizeof(awacs_speeds[0]);
1068d9a5f17fSdrahn
1069d7ee1e40Sjason if (p->sample_rate < awacs_speeds[0].rate)
1070d7ee1e40Sjason selected = 0;
1071d7ee1e40Sjason if (p->sample_rate > awacs_speeds[n - 1].rate)
1072d7ee1e40Sjason selected = n - 1;
1073d7ee1e40Sjason
1074d7ee1e40Sjason for (i = 1; selected == -1 && i < n; i++) {
1075d7ee1e40Sjason if (p->sample_rate == awacs_speeds[i].rate)
1076d7ee1e40Sjason selected = i;
1077b768baf5Sjason else if (p->sample_rate < awacs_speeds[i].rate) {
1078d7ee1e40Sjason u_int diff1, diff2;
1079d7ee1e40Sjason
1080d7ee1e40Sjason diff1 = p->sample_rate - awacs_speeds[i - 1].rate;
1081d7ee1e40Sjason diff2 = awacs_speeds[i].rate - p->sample_rate;
1082d7ee1e40Sjason selected = (diff1 < diff2) ? i - 1 : i;
1083d7ee1e40Sjason }
1084d9a5f17fSdrahn }
1085d9a5f17fSdrahn
1086d7ee1e40Sjason if (selected == -1)
1087d7ee1e40Sjason selected = 0;
1088d9a5f17fSdrahn
1089d7ee1e40Sjason sc->sc_soundctl &= ~AWACS_RATE_MASK;
1090d7ee1e40Sjason sc->sc_soundctl |= awacs_speeds[selected].bits;
1091d7ee1e40Sjason p->sample_rate = awacs_speeds[selected].rate;
1092d7ee1e40Sjason awacs_write_reg(sc, AWACS_SOUND_CTRL, sc->sc_soundctl);
1093d9a5f17fSdrahn }
1094