xref: /openbsd-src/sys/dev/pci/azalia_codec.c (revision 824adb5411e4389b29bae28eba5c2c2bbd147f34)
1 /*	$OpenBSD: azalia_codec.c,v 1.186 2021/06/11 15:50:43 jsg Exp $	*/
2 /*	$NetBSD: azalia_codec.c,v 1.8 2006/05/10 11:17:27 kent Exp $	*/
3 
4 /*-
5  * Copyright (c) 2005 The NetBSD Foundation, Inc.
6  * All rights reserved.
7  *
8  * This code is derived from software contributed to The NetBSD Foundation
9  * by TAMURA Kent
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 #include <sys/param.h>
34 #include <sys/device.h>
35 #include <sys/malloc.h>
36 #include <sys/systm.h>
37 #include <dev/pci/azalia.h>
38 #include <dev/pci/pcireg.h>
39 #include <dev/pci/pcidevs.h>
40 
41 #define XNAME(co)	(((struct device *)co->az)->dv_xname)
42 #define MIXER_DELTA(n)	(AUDIO_MAX_GAIN / (n))
43 
44 int	azalia_add_convgroup(codec_t *, convgroupset_t *,
45     struct io_pin *, int, nid_t *, int, uint32_t, uint32_t);
46 
47 int	azalia_mixer_fix_indexes(codec_t *);
48 int	azalia_mixer_default(codec_t *);
49 int	azalia_mixer_ensure_capacity(codec_t *, size_t);
50 u_char	azalia_mixer_from_device_value(const codec_t *, nid_t, int, uint32_t );
51 uint32_t azalia_mixer_to_device_value(const codec_t *, nid_t, int, u_char);
52 
53 void	azalia_devinfo_offon(mixer_devinfo_t *);
54 void	azalia_pin_config_ov(widget_t *, int, int);
55 void	azalia_ampcap_ov(widget_t *, int, int, int, int, int, int);
56 int	azalia_gpio_unmute(codec_t *, int);
57 
58 
59 int
60 azalia_codec_init_vtbl(codec_t *this)
61 {
62 	/**
63 	 * We can refer this->vid and this->subid.
64 	 */
65 	this->name = NULL;
66 	this->qrks = AZ_QRK_NONE;
67 	switch (this->vid) {
68 	case 0x10134206:
69 		this->name = "Cirrus Logic CS4206";
70 		if (this->subid == 0xcb8910de ||	/* APPLE_MBA3_1 */
71 		    this->subid == 0x72708086 ||	/* APPLE_MBA4_1 */
72 		    this->subid == 0xcb7910de) {	/* APPLE_MBP5_5 */
73 			this->qrks |= AZ_QRK_GPIO_UNMUTE_1 |
74 			    AZ_QRK_GPIO_UNMUTE_3;
75 		}
76 		break;
77 	case 0x10134208:
78 		this->name = "Cirrus Logic CS4208";
79 		if (this->subid == 0x72708086) {	/* APPLE_MBA6_1 */
80 			this->qrks |= AZ_QRK_GPIO_UNMUTE_0 |
81 			    AZ_QRK_GPIO_UNMUTE_1;
82 		}
83 		break;
84 	case 0x10ec0221:
85 		this->name = "Realtek ALC221";
86 		this->qrks |= AZ_QRK_WID_CDIN_1C | AZ_QRK_WID_BEEP_1D;
87 		break;
88 	case 0x10ec0225:
89 		this->name = "Realtek ALC225";
90 		break;
91 	case 0x10ec0233:
92 	case 0x10ec0235:
93 		this->name = "Realtek ALC233";
94 		break;
95 	case 0x10ec0236:
96 		if (PCI_VENDOR(this->subid) == PCI_VENDOR_DELL)
97 			this->name = "Realtek ALC3204";
98 		else
99 			this->name = "Realtek ALC236";
100 		break;
101 	case 0x10ec0255:
102 		this->name = "Realtek ALC255";
103 		break;
104 	case 0x10ec0256:
105 		this->name = "Realtek ALC256";
106 		break;
107 	case 0x10ec0257:
108 		this->name = "Realtek ALC257";
109 		break;
110 	case 0x10ec0260:
111 		this->name = "Realtek ALC260";
112 		if (this->subid == 0x008f1025)
113 			this->qrks |= AZ_QRK_GPIO_UNMUTE_0;
114 		break;
115 	case 0x10ec0262:
116 		this->name = "Realtek ALC262";
117 		this->qrks |= AZ_QRK_WID_CDIN_1C | AZ_QRK_WID_BEEP_1D;
118 		break;
119 	case 0x10ec0268:
120 		this->name = "Realtek ALC268";
121 		this->qrks |= AZ_QRK_WID_CDIN_1C | AZ_QRK_WID_BEEP_1D;
122 		break;
123 	case 0x10ec0269:
124 		this->name = "Realtek ALC269";
125 		this->qrks |= AZ_QRK_WID_CDIN_1C | AZ_QRK_WID_BEEP_1D;
126 
127 		/*
128 		 * Enable dock audio on Thinkpad docks
129 		 * 0x17aa : 0x21f3 = Thinkpad T430
130 		 * 0x17aa : 0x21f6 = Thinkpad T530
131 		 * 0x17aa : 0x21fa = Thinkpad X230
132 		 * 0x17aa : 0x21fb = Thinkpad T430s
133 		 * 0x17aa : 0x2203 = Thinkpad X230t
134 		 * 0x17aa : 0x2208 = Thinkpad T431s
135 		 */
136 		if (this->subid == 0x21f317aa ||
137 		    this->subid == 0x21f617aa ||
138 		    this->subid == 0x21fa17aa ||
139 		    this->subid == 0x21fb17aa ||
140 		    this->subid == 0x220317aa ||
141 		    this->subid == 0x220817aa)
142 			this->qrks |= AZ_QRK_WID_TPDOCK1;
143 		break;
144 	case 0x10ec0270:
145 		this->name = "Realtek ALC270";
146 		break;
147 	case 0x10ec0272:
148 		this->name = "Realtek ALC272";
149 		break;
150 	case 0x10ec0275:
151 		this->name = "Realtek ALC275";
152 		break;
153 	case 0x10ec0280:
154 		this->name = "Realtek ALC280";
155 		break;
156 	case 0x10ec0282:
157 		this->name = "Realtek ALC282";
158 		this->qrks |= AZ_QRK_WID_CDIN_1C | AZ_QRK_WID_BEEP_1D;
159 		break;
160 	case 0x10ec0283:
161 		this->name = "Realtek ALC283";
162 		break;
163 	case 0x10ec0285:
164 		this->name = "Realtek ALC285";
165 		if (this->subid == 0x229217aa) {
166 			/* Thinkpad X1 Carbon 7 */
167 			this->qrks |= AZ_QRK_ROUTE_SPKR2_DAC |
168 			    AZ_QRK_WID_CLOSE_PCBEEP;
169 		 } else if (this->subid == 0x22c017aa) {
170 			/* Thinkpad X1 Extreme 3 */
171 			this->qrks |= AZ_QRK_DOLBY_ATMOS |
172 			    AZ_QRK_ROUTE_SPKR2_DAC;
173 		}
174 		break;
175 	case 0x10ec0287:
176 		this->name = "Realtek ALC287";
177 		break;
178 	case 0x10ec0292:
179 		this->name = "Realtek ALC292";
180 		this->qrks |= AZ_QRK_WID_CDIN_1C | AZ_QRK_WID_BEEP_1D;
181 
182 		/*
183 		 * Enable dock audio on Thinkpad docks
184 		 * 0x17aa : 0x220c = Thinkpad T440s
185 		 * 0x17aa : 0x220e = Thinkpad T440p
186 		 * 0x17aa : 0x2210 = Thinkpad T540p
187 		 * 0x17aa : 0x2212 = Thinkpad T440
188 		 * 0x17aa : 0x2214 = Thinkpad X240
189 		 * 0x17aa : 0x2226 = Thinkpad X250
190 		 * 0x17aa : 0x501e = Thinkpad L440
191 		 * 0x17aa : 0x5034 = Thinkpad T450
192 		 * 0x17aa : 0x5036 = Thinkpad T450s
193 		 * 0x17aa : 0x503c = Thinkpad L450
194 		 */
195 		if (this->subid == 0x220c17aa ||
196 		    this->subid == 0x220e17aa ||
197 		    this->subid == 0x221017aa ||
198 		    this->subid == 0x221217aa ||
199 		    this->subid == 0x221417aa ||
200 		    this->subid == 0x222617aa ||
201 		    this->subid == 0x501e17aa ||
202 		    this->subid == 0x503417aa ||
203 		    this->subid == 0x503617aa ||
204 		    this->subid == 0x503c17aa)
205 			this->qrks |= AZ_QRK_WID_TPDOCK2;
206 		break;
207 	case 0x10ec0293:
208 		if (PCI_VENDOR(this->subid) == PCI_VENDOR_DELL)
209 			this->name = "Realtek ALC3235";
210 		else
211 			this->name = "Realtek ALC293";
212 		break;
213 	case 0x10ec0294:
214 		this->name = "Realtek ALC294";
215 		break;
216 	case 0x10ec0295:
217 		if (PCI_VENDOR(this->subid) == PCI_VENDOR_DELL)
218 			this->name = "Realtek ALC3254";
219 		else
220 			this->name = "Realtek ALC295";
221 		break;
222 	case 0x10ec0298:
223 		this->name = "Realtek ALC298";
224 		if (this->subid == 0x320019e5 ||
225 		    this->subid == 0x320119e5)		/* Huawei Matebook X */
226 			this->qrks |= AZ_QRK_DOLBY_ATMOS;
227 		break;
228 	case 0x10ec0299:
229 		this->name = "Realtek ALC299";
230 		break;
231 	case 0x10ec0660:
232 		this->name = "Realtek ALC660";
233 		if (this->subid == 0x13391043) {	/* ASUS_G2K */
234 			this->qrks |= AZ_QRK_GPIO_UNMUTE_0;
235 		}
236 		break;
237 	case 0x10ec0662:
238 		this->name = "Realtek ALC662";
239 		this->qrks |= AZ_QRK_WID_CDIN_1C | AZ_QRK_WID_BEEP_1D;
240 		break;
241 	case 0x10ec0663:
242 		this->name = "Realtek ALC663";
243 		break;
244 	case 0x10ec0668:
245 		if (PCI_VENDOR(this->subid) == PCI_VENDOR_DELL)
246 			this->name = "Realtek ALC3661";
247 		else
248 			this->name = "Realtek ALC668";
249 		break;
250 	case 0x10ec0671:
251 		this->name = "Realtek ALC671";
252 		break;
253 	case 0x10ec0700:
254 		this->name = "Realtek ALC700";
255 		break;
256 	case 0x10ec0861:
257 		this->name = "Realtek ALC861";
258 		break;
259 	case 0x10ec0880:
260 		this->name = "Realtek ALC880";
261 		this->qrks |= AZ_QRK_WID_CDIN_1C | AZ_QRK_WID_BEEP_1D;
262 		if (this->subid == 0x19931043 ||	/* ASUS_M5200 */
263 		    this->subid == 0x13231043) {	/* ASUS_A7M */
264 			this->qrks |= AZ_QRK_GPIO_UNMUTE_0;
265 		}
266 		if (this->subid == 0x203d161f) {	/* MEDION_MD95257 */
267 			this->qrks |= AZ_QRK_GPIO_UNMUTE_1;
268 		}
269 		break;
270 	case 0x10ec0882:
271 		this->name = "Realtek ALC882";
272 		this->qrks |= AZ_QRK_WID_CDIN_1C | AZ_QRK_WID_BEEP_1D;
273 		if (this->subid == 0x13c21043 ||	/* ASUS_A7T */
274 		    this->subid == 0x19711043) {	/* ASUS_W2J */
275 			this->qrks |= AZ_QRK_GPIO_UNMUTE_0;
276 		}
277 		break;
278 	case 0x10ec0883:
279 		this->name = "Realtek ALC883";
280 		this->qrks |= AZ_QRK_WID_CDIN_1C | AZ_QRK_WID_BEEP_1D;
281 		if (this->subid == 0x00981025) {	/* ACER_ID */
282 			this->qrks |= AZ_QRK_GPIO_UNMUTE_0 |
283 			    AZ_QRK_GPIO_UNMUTE_1;
284 		}
285 		break;
286 	case 0x10ec0885:
287 		this->name = "Realtek ALC885";
288 		this->qrks |= AZ_QRK_WID_CDIN_1C | AZ_QRK_WID_BEEP_1D;
289 		if (this->subid == 0x00a1106b ||	/* APPLE_MB3 */
290 		    this->subid == 0xcb7910de ||	/* APPLE_MACMINI3_1 (line-in + hp) */
291 		    this->subid == 0x00a0106b ||	/* APPLE_MB3_1 */
292 		    this->subid == 0x00a3106b) {	/* APPLE_MB4 */
293 			this->qrks |= AZ_QRK_GPIO_UNMUTE_0;
294 		}
295 		if (this->subid == 0x00a1106b ||
296 		    this->subid == 0xcb7910de ||	/* APPLE_MACMINI3_1 (internal spkr) */
297 		    this->subid == 0x00a0106b)
298 			this->qrks |= AZ_QRK_WID_OVREF50;
299 		break;
300 	case 0x10ec0887:
301 		this->name = "Realtek ALC887";
302 		this->qrks |= AZ_QRK_WID_CDIN_1C | AZ_QRK_WID_BEEP_1D;
303 		break;
304 	case 0x10ec0888:
305 		this->name = "Realtek ALC888";
306 		this->qrks |= AZ_QRK_WID_CDIN_1C | AZ_QRK_WID_BEEP_1D;
307 		break;
308 	case 0x10ec0889:
309 		this->name = "Realtek ALC889";
310 		break;
311 	case 0x10ec0892:
312 		this->name = "Realtek ALC892";
313 		break;
314 	case 0x10ec0897:
315 		this->name = "Realtek ALC897";
316 		break;
317 	case 0x10ec0900:
318 		this->name = "Realtek ALC1150";
319 		break;
320 	case 0x10ec0b00:
321 		this->name = "Realtek ALC1200";
322 		break;
323 	case 0x10ec1168:
324 	case 0x10ec1220:
325 		this->name = "Realtek ALC1220";
326 		break;
327 	case 0x11060398:
328 	case 0x11061398:
329 	case 0x11062398:
330 	case 0x11063398:
331 	case 0x11064398:
332 	case 0x11065398:
333 	case 0x11066398:
334 	case 0x11067398:
335 		this->name = "VIA VT1702";
336 		break;
337 	case 0x111d7603:
338 		this->name = "IDT 92HD75B3/4";
339 		if (PCI_VENDOR(this->subid) == PCI_VENDOR_HP)
340 			this->qrks |= AZ_QRK_GPIO_UNMUTE_0;
341 		break;
342 	case 0x111d7604:
343 		this->name = "IDT 92HD83C1X";
344 		break;
345 	case 0x111d7605:
346 		this->name = "IDT 92HD81B1X";
347 		break;
348 	case 0x111d7608:
349 		this->name = "IDT 92HD75B1/2";
350 		if (PCI_VENDOR(this->subid) == PCI_VENDOR_HP)
351 			this->qrks |= AZ_QRK_GPIO_UNMUTE_0;
352 		break;
353 	case 0x111d7674:
354 		this->name = "IDT 92HD73D1";
355 		break;
356 	case 0x111d7675:
357 		this->name = "IDT 92HD73C1";	/* aka 92HDW74C1 */
358 		if (PCI_VENDOR(this->subid) == PCI_VENDOR_DELL)
359 			this->qrks |= AZ_QRK_GPIO_UNMUTE_0;
360 		break;
361 	case 0x111d7676:
362 		this->name = "IDT 92HD73E1";	/* aka 92HDW74E1 */
363 		break;
364 	case 0x111d76b0:
365 		this->name = "IDT 92HD71B8";
366 		break;
367 	case 0x111d76b2:
368 		this->name = "IDT 92HD71B7";
369 		if (PCI_VENDOR(this->subid) == PCI_VENDOR_DELL ||
370 		    PCI_VENDOR(this->subid) == PCI_VENDOR_HP)
371 			this->qrks |= AZ_QRK_GPIO_UNMUTE_0;
372 		break;
373 	case 0x111d76b6:
374 		this->name = "IDT 92HD71B5";
375 		break;
376 	case 0x111d76d4:
377 		this->name = "IDT 92HD83C1C";
378 		break;
379 	case 0x111d76d5:
380 		this->name = "IDT 92HD81B1C";
381 		break;
382 	case 0x11d4184a:
383 		this->name = "Analog Devices AD1884A";
384 		break;
385 	case 0x11d41882:
386 		this->name = "Analog Devices AD1882";
387 		break;
388 	case 0x11d41883:
389 		this->name = "Analog Devices AD1883";
390 		break;
391 	case 0x11d41884:
392 		this->name = "Analog Devices AD1884";
393 		break;
394 	case 0x11d4194a:
395 		this->name = "Analog Devices AD1984A";
396 		break;
397 	case 0x11d41981:
398 		this->name = "Analog Devices AD1981HD";
399 		this->qrks |= AZ_QRK_WID_AD1981_OAMP;
400 		break;
401 	case 0x11d41983:
402 		this->name = "Analog Devices AD1983";
403 		break;
404 	case 0x11d41984:
405 		this->name = "Analog Devices AD1984";
406 		break;
407 	case 0x11d41988:
408 		this->name = "Analog Devices AD1988A";
409 		break;
410 	case 0x11d4198b:
411 		this->name = "Analog Devices AD1988B";
412 		break;
413 	case 0x11d4882a:
414 		this->name = "Analog Devices AD1882A";
415 		break;
416 	case 0x11d4989a:
417 		this->name = "Analog Devices AD1989A";
418 		break;
419 	case 0x11d4989b:
420 		this->name = "Analog Devices AD1989B";
421 		break;
422 	case 0x14f15045:
423 		this->name = "Conexant CX20549";  /* Venice */
424 		break;
425 	case 0x14f15047:
426 		this->name = "Conexant CX20551";  /* Waikiki */
427 		break;
428 	case 0x14f15051:
429 		this->name = "Conexant CX20561";  /* Hermosa */
430 		break;
431 	case 0x14f1506e:
432 		this->name = "Conexant CX20590";
433 		/*
434 		 * Enable dock audio on Thinkpad docks
435 		 * 0x17aa : 0x20f2 = Thinkpad T400
436 		 * 0x17aa : 0x215e = Thinkpad T410
437 		 * 0x17aa : 0x215f = Thinkpad T510
438 		 * 0x17aa : 0x21ce = Thinkpad T420
439 		 * 0x17aa : 0x21cf = Thinkpad T520
440 		 * 0x17aa : 0x21da = Thinkpad X220
441 		 * 0x17aa : 0x21db = Thinkpad X220t
442 		 */
443 		if (this->subid == 0x20f217aa ||
444 		    this->subid == 0x215e17aa ||
445 		    this->subid == 0x215f17aa ||
446 		    this->subid == 0x21ce17aa ||
447 		    this->subid == 0x21cf17aa ||
448 		    this->subid == 0x21da17aa ||
449 		    this->subid == 0x21db17aa)
450 			this->qrks |= AZ_QRK_WID_TPDOCK3;
451 		break;
452 	case 0x434d4980:
453 		this->name = "CMedia CMI9880";
454 		break;
455 	case 0x83847612:
456 		this->name = "Sigmatel STAC9230X";
457 		break;
458 	case 0x83847613:
459 		this->name = "Sigmatel STAC9230D";
460 		break;
461 	case 0x83847614:
462 		this->name = "Sigmatel STAC9229X";
463 		break;
464 	case 0x83847615:
465 		this->name = "Sigmatel STAC9229D";
466 		break;
467 	case 0x83847616:
468 		this->name = "Sigmatel STAC9228X";
469 		if (this->subid == 0x02271028 ||	/* DELL_V1400 */
470 		    this->subid == 0x01f31028) {	/* DELL_I1400 */
471 			this->qrks |= AZ_QRK_GPIO_UNMUTE_2;
472 	 	}
473 		break;
474 	case 0x83847617:
475 		this->name = "Sigmatel STAC9228D";
476 		break;
477 	case 0x83847618:
478 		this->name = "Sigmatel STAC9227X";
479 		break;
480 	case 0x83847619:
481 		this->name = "Sigmatel STAC9227D";
482 		break;
483 	case 0x83847620:
484 		this->name = "Sigmatel STAC9274";
485 		break;
486 	case 0x83847621:
487 		this->name = "Sigmatel STAC9274D";
488 		break;
489 	case 0x83847626:
490 		this->name = "Sigmatel STAC9271X";
491 		break;
492 	case 0x83847627:
493 		this->name = "Sigmatel STAC9271D";
494 		break;
495 	case 0x83847632:
496 		this->name = "Sigmatel STAC9202";
497 		break;
498 	case 0x83847634:
499 		this->name = "Sigmatel STAC9250";
500 		break;
501 	case 0x83847636:
502 		this->name = "Sigmatel STAC9251";
503 		break;
504 	case 0x83847638:
505 		this->name = "IDT 92HD700X";
506 		break;
507 	case 0x83847639:
508 		this->name = "IDT 92HD700D";
509 		break;
510 	case 0x83847645:
511 		this->name = "IDT 92HD206X";
512 		break;
513 	case 0x83847646:
514 		this->name = "IDT 92HD206D";
515 		break;
516 	case 0x83847661:
517 		/* FALLTHROUGH */
518 	case 0x83847662:
519 		this->name = "Sigmatel STAC9225";
520 		break;
521 	case 0x83847680:
522 		this->name = "Sigmatel STAC9220/1";
523 		if (this->subid == 0x76808384) {	/* APPLE_ID */
524 			this->qrks |= AZ_QRK_GPIO_POL_0 | AZ_QRK_GPIO_UNMUTE_0 |
525 			     AZ_QRK_GPIO_UNMUTE_1;
526 		}
527 		break;
528 	case 0x83847682:
529 		/* FALLTHROUGH */
530 	case 0x83847683:
531 		this->name = "Sigmatel STAC9221D";	/* aka IDT 92HD202 */
532 		break;
533 	case 0x83847690:
534 		this->name = "Sigmatel STAC9200";	/* aka IDT 92HD001 */
535 		break;
536 	case 0x83847691:
537 		this->name = "Sigmatel STAC9200D";
538 		break;
539 	case 0x83847698:
540 		this->name = "IDT 92HD005";
541 		break;
542 	case 0x83847699:
543 		this->name = "IDT 92HD005D";
544 		break;
545 	case 0x838476a0:
546 		this->name = "Sigmatel STAC9205X";
547 		if (this->subid == 0x01f91028 ||	/* DELL_D630 */
548 		    this->subid == 0x02281028) {	/* DELL_V1500 */
549 			this->qrks |= AZ_QRK_GPIO_UNMUTE_0;
550 		}
551 		break;
552 	case 0x838476a1:
553 		this->name = "Sigmatel STAC9205D";
554 		break;
555 	case 0x838476a2:
556 		this->name = "Sigmatel STAC9204X";
557 		break;
558 	case 0x838476a3:
559 		this->name = "Sigmatel STAC9204D";
560 		break;
561 	}
562 	return 0;
563 }
564 
565 /* ----------------------------------------------------------------
566  * functions for generic codecs
567  * ---------------------------------------------------------------- */
568 
569 int
570 azalia_widget_enabled(const codec_t *this, nid_t nid)
571 {
572 	if (!VALID_WIDGET_NID(nid, this) || !this->w[nid].enable)
573 		return 0;
574 	return 1;
575 }
576 
577 int
578 azalia_init_dacgroup(codec_t *this)
579 {
580 	this->dacs.ngroups = 0;
581 	if (this->na_dacs > 0)
582 		azalia_add_convgroup(this, &this->dacs,
583 		    this->opins, this->nopins,
584 		    this->a_dacs, this->na_dacs,
585 		    COP_AWTYPE_AUDIO_OUTPUT, 0);
586 	if (this->na_dacs_d > 0)
587 		azalia_add_convgroup(this, &this->dacs,
588 		    this->opins_d, this->nopins_d,
589 		    this->a_dacs_d, this->na_dacs_d,
590 		    COP_AWTYPE_AUDIO_OUTPUT, COP_AWCAP_DIGITAL);
591 	this->dacs.cur = 0;
592 
593 	this->adcs.ngroups = 0;
594 	if (this->na_adcs > 0)
595 		azalia_add_convgroup(this, &this->adcs,
596 		    this->ipins, this->nipins,
597 		    this->a_adcs, this->na_adcs,
598 		    COP_AWTYPE_AUDIO_INPUT, 0);
599 	if (this->na_adcs_d > 0)
600 		azalia_add_convgroup(this, &this->adcs,
601 		    this->ipins_d, this->nipins_d,
602 		    this->a_adcs_d, this->na_adcs_d,
603 		    COP_AWTYPE_AUDIO_INPUT, COP_AWCAP_DIGITAL);
604 	this->adcs.cur = 0;
605 
606 	return 0;
607 }
608 
609 int
610 azalia_add_convgroup(codec_t *this, convgroupset_t *group,
611     struct io_pin *pins, int npins, nid_t *all_convs, int nall_convs,
612     uint32_t type, uint32_t digital)
613 {
614 	nid_t convs[HDA_MAX_CHANNELS];
615 	int nconvs;
616 	nid_t conv;
617 	int i, j, k;
618 
619 	nconvs = 0;
620 
621 	/* default pin connections */
622 	for (i = 0; i < npins; i++) {
623 		conv = pins[i].conv;
624 		if (conv < 0)
625 			continue;
626 		for (j = 0; j < nconvs; j++) {
627 			if (convs[j] == conv)
628 				break;
629 		}
630 		if (j < nconvs)
631 			continue;
632 		convs[nconvs++] = conv;
633 		if (nconvs >= nall_convs) {
634 			goto done;
635 		}
636 	}
637 	/* non-default connections */
638 	for (i = 0; i < npins; i++) {
639 		for (j = 0; j < nall_convs; j++) {
640 			conv = all_convs[j];
641 			for (k = 0; k < nconvs; k++) {
642 				if (convs[k] == conv)
643 					break;
644 			}
645 			if (k < nconvs)
646 				continue;
647 			if (type == COP_AWTYPE_AUDIO_OUTPUT) {
648 				k = azalia_codec_fnode(this, conv,
649 				    pins[i].nid, 0);
650 				if (k < 0)
651 					continue;
652 			} else {
653 				if (!azalia_widget_enabled(this, conv))
654 					continue;
655 				k = azalia_codec_fnode(this, pins[i].nid,
656 				    conv, 0);
657 				if (k < 0)
658 					continue;
659 			}
660 			convs[nconvs++] = conv;
661 			if (nconvs >= nall_convs) {
662 				goto done;
663 			}
664 		}
665 	}
666 	/* Make sure the speaker dac is part of the analog output convgroup
667 	 * or it won't get connected by azalia_codec_connect_stream().
668 	 */
669 	if (type == COP_AWTYPE_AUDIO_OUTPUT && !digital &&
670 	    nconvs < nall_convs && this->spkr_dac != -1) {
671 		for (i = 0; i < nconvs; i++)
672 			if (convs[i] == this->spkr_dac)
673 				break;
674 		if (i == nconvs)
675 			convs[nconvs++] = this->spkr_dac;
676 	}
677 done:
678 	for (i = 0; i < nconvs; i++)
679 		group->groups[group->ngroups].conv[i] = convs[i];
680 	if (nconvs > 0) {
681 		group->groups[group->ngroups].nconv = i;
682 		group->ngroups++;
683 	}
684 
685 	/* Disable converters that aren't in a convgroup. */
686 	for (i = 0; i < nall_convs; i++) {
687 		conv = all_convs[i];
688 		for (j = 0; j < nconvs; j++)
689 			if (convs[j] == conv)
690 				break;
691 		if (j == nconvs)
692 			this->w[conv].enable = 0;
693 	}
694 
695 	return 0;
696 }
697 
698 int
699 azalia_codec_fnode(codec_t *this, nid_t node, int index, int depth)
700 {
701 	const widget_t *w;
702 	int i, ret;
703 
704 	w = &this->w[index];
705 	if (w->nid == node) {
706 		return index;
707 	}
708 	/* back at the beginning or a bad end */
709 	if (depth > 0 &&
710 	    (w->type == COP_AWTYPE_PIN_COMPLEX ||
711 	    w->type == COP_AWTYPE_BEEP_GENERATOR ||
712 	    w->type == COP_AWTYPE_AUDIO_OUTPUT ||
713 	    w->type == COP_AWTYPE_AUDIO_INPUT))
714 		return -1;
715 	if (++depth >= 10)
716 		return -1;
717 	for (i = 0; i < w->nconnections; i++) {
718 		if (!azalia_widget_enabled(this, w->connections[i]))
719 			continue;
720 		ret = azalia_codec_fnode(this, node, w->connections[i], depth);
721 		if (ret >= 0)
722 			return ret;
723 	}
724 	return -1;
725 }
726 
727 int
728 azalia_unsol_event(codec_t *this, int tag)
729 {
730 	mixer_ctrl_t mc;
731 	uint32_t result;
732 	int i, err, vol, vol2;
733 
734 	err = 0;
735 	tag = CORB_UNSOL_TAG(tag);
736 	switch (tag) {
737 	case AZ_TAG_SPKR:
738 		mc.type = AUDIO_MIXER_ENUM;
739 		vol = 0;
740 		for (i = 0; !vol && !err && i < this->nsense_pins; i++) {
741 			if (!(this->spkr_muters & (1 << i)))
742 				continue;
743 			err = azalia_comresp(this, this->sense_pins[i],
744 			    CORB_GET_PIN_WIDGET_CONTROL, 0, &result);
745 			if (err || !(result & CORB_PWC_OUTPUT))
746 				continue;
747 			err = azalia_comresp(this, this->sense_pins[i],
748 			    CORB_GET_PIN_SENSE, 0, &result);
749 			if (!err && (result & CORB_PS_PRESENCE))
750 				vol = 1;
751 		}
752 		if (err)
753 			break;
754 		this->spkr_muted = vol;
755 		switch(this->spkr_mute_method) {
756 		case AZ_SPKR_MUTE_SPKR_MUTE:
757 			mc.un.ord = vol;
758 			err = azalia_mixer_set(this, this->speaker,
759 			    MI_TARGET_OUTAMP, &mc);
760 			if (!err && this->speaker2 != -1 &&
761 			    (this->w[this->speaker2].widgetcap & COP_AWCAP_OUTAMP) &&
762 			    (this->w[this->speaker2].outamp_cap & COP_AMPCAP_MUTE))
763 				err = azalia_mixer_set(this, this->speaker2,
764 				    MI_TARGET_OUTAMP, &mc);
765 			break;
766 		case AZ_SPKR_MUTE_SPKR_DIR:
767 			mc.un.ord = vol ? 0 : 1;
768 			err = azalia_mixer_set(this, this->speaker,
769 			    MI_TARGET_PINDIR, &mc);
770 			if (!err && this->speaker2 != -1 &&
771 			    (this->w[this->speaker2].d.pin.cap & COP_PINCAP_OUTPUT) &&
772 			    (this->w[this->speaker2].d.pin.cap & COP_PINCAP_INPUT))
773 				err = azalia_mixer_set(this, this->speaker2,
774 				    MI_TARGET_PINDIR, &mc);
775 			break;
776 		case AZ_SPKR_MUTE_DAC_MUTE:
777 			mc.un.ord = vol;
778 			err = azalia_mixer_set(this, this->spkr_dac,
779 			    MI_TARGET_OUTAMP, &mc);
780 			break;
781 		}
782 		break;
783 
784 	case AZ_TAG_PLAYVOL:
785 		if (this->playvols.master == this->audiofunc)
786 			return EINVAL;
787 		err = azalia_comresp(this, this->playvols.master,
788 		    CORB_GET_VOLUME_KNOB, 0, &result);
789 		if (err)
790 			return err;
791 
792 		vol = CORB_VKNOB_VOLUME(result) - this->playvols.hw_step;
793 		vol2 = vol * (AUDIO_MAX_GAIN / this->playvols.hw_nsteps);
794 		this->playvols.hw_step = CORB_VKNOB_VOLUME(result);
795 
796 		vol = vol2 + this->playvols.vol_l;
797 		if (vol < 0)
798 			vol = 0;
799 		else if (vol > AUDIO_MAX_GAIN)
800 			vol = AUDIO_MAX_GAIN;
801 		this->playvols.vol_l = vol;
802 
803 		vol = vol2 + this->playvols.vol_r;
804 		if (vol < 0)
805 			vol = 0;
806 		else if (vol > AUDIO_MAX_GAIN)
807 			vol = AUDIO_MAX_GAIN;
808 		this->playvols.vol_r = vol;
809 
810 		mc.type = AUDIO_MIXER_VALUE;
811 		mc.un.value.num_channels = 2;
812 		mc.un.value.level[0] = this->playvols.vol_l;
813 		mc.un.value.level[1] = this->playvols.vol_r;
814 		err = azalia_mixer_set(this, this->playvols.master,
815 		    MI_TARGET_PLAYVOL, &mc);
816 		break;
817 
818 	default:
819 		DPRINTF(("%s: unknown tag %d\n", __func__, tag));
820 		break;
821 	}
822 
823 	return err;
824 }
825 
826 
827 /* ----------------------------------------------------------------
828  * Generic mixer functions
829  * ---------------------------------------------------------------- */
830 
831 int
832 azalia_mixer_init(codec_t *this)
833 {
834 	/*
835 	 * pin		"<color>%2.2x"
836 	 * audio output	"dac%2.2x"
837 	 * audio input	"adc%2.2x"
838 	 * mixer	"mixer%2.2x"
839 	 * selector	"sel%2.2x"
840 	 */
841 	const widget_t *w, *ww;
842 	mixer_item_t *m;
843 	int err, i, j, k, bits;
844 
845 	this->maxmixers = 10;
846 	this->nmixers = 0;
847 	this->mixers = mallocarray(this->maxmixers, sizeof(mixer_item_t),
848 	    M_DEVBUF, M_NOWAIT | M_ZERO);
849 	if (this->mixers == NULL) {
850 		printf("%s: out of memory in %s\n", XNAME(this), __func__);
851 		return ENOMEM;
852 	}
853 
854 	/* register classes */
855 	m = &this->mixers[AZ_CLASS_INPUT];
856 	m->devinfo.index = AZ_CLASS_INPUT;
857 	strlcpy(m->devinfo.label.name, AudioCinputs,
858 	    sizeof(m->devinfo.label.name));
859 	m->devinfo.type = AUDIO_MIXER_CLASS;
860 	m->devinfo.mixer_class = AZ_CLASS_INPUT;
861 	m->devinfo.next = AUDIO_MIXER_LAST;
862 	m->devinfo.prev = AUDIO_MIXER_LAST;
863 	m->nid = 0;
864 
865 	m = &this->mixers[AZ_CLASS_OUTPUT];
866 	m->devinfo.index = AZ_CLASS_OUTPUT;
867 	strlcpy(m->devinfo.label.name, AudioCoutputs,
868 	    sizeof(m->devinfo.label.name));
869 	m->devinfo.type = AUDIO_MIXER_CLASS;
870 	m->devinfo.mixer_class = AZ_CLASS_OUTPUT;
871 	m->devinfo.next = AUDIO_MIXER_LAST;
872 	m->devinfo.prev = AUDIO_MIXER_LAST;
873 	m->nid = 0;
874 
875 	m = &this->mixers[AZ_CLASS_RECORD];
876 	m->devinfo.index = AZ_CLASS_RECORD;
877 	strlcpy(m->devinfo.label.name, AudioCrecord,
878 	    sizeof(m->devinfo.label.name));
879 	m->devinfo.type = AUDIO_MIXER_CLASS;
880 	m->devinfo.mixer_class = AZ_CLASS_RECORD;
881 	m->devinfo.next = AUDIO_MIXER_LAST;
882 	m->devinfo.prev = AUDIO_MIXER_LAST;
883 	m->nid = 0;
884 
885 	this->nmixers = AZ_CLASS_RECORD + 1;
886 
887 #define MIXER_REG_PROLOG	\
888 	mixer_devinfo_t *d; \
889 	err = azalia_mixer_ensure_capacity(this, this->nmixers + 1); \
890 	if (err) \
891 		return err; \
892 	m = &this->mixers[this->nmixers]; \
893 	d = &m->devinfo; \
894 	m->nid = i
895 
896 	FOR_EACH_WIDGET(this, i) {
897 
898 		w = &this->w[i];
899 		if (!w->enable)
900 			continue;
901 
902 		/* selector */
903 		if (w->nconnections > 0 && w->type != COP_AWTYPE_AUDIO_MIXER &&
904 		    !(w->nconnections == 1 &&
905 		    azalia_widget_enabled(this, w->connections[0]) &&
906 		    strcmp(w->name, this->w[w->connections[0]].name) == 0) &&
907 		    w->nid != this->mic) {
908 			MIXER_REG_PROLOG;
909 			snprintf(d->label.name, sizeof(d->label.name),
910 			    "%s_source", w->name);
911 			d->type = AUDIO_MIXER_ENUM;
912 			if (w->mixer_class >= 0)
913 				d->mixer_class = w->mixer_class;
914 			else {
915 				if (w->type == COP_AWTYPE_AUDIO_SELECTOR)
916 					d->mixer_class = AZ_CLASS_INPUT;
917 				else
918 					d->mixer_class = AZ_CLASS_OUTPUT;
919 			}
920 			m->target = MI_TARGET_CONNLIST;
921 			for (j = 0, k = 0; j < w->nconnections && k < 32; j++) {
922 				if (!azalia_widget_enabled(this,
923 				    w->connections[j]))
924 					continue;
925 				d->un.e.member[k].ord = j;
926 				strlcpy(d->un.e.member[k].label.name,
927 				    this->w[w->connections[j]].name,
928 				    MAX_AUDIO_DEV_LEN);
929 				k++;
930 			}
931 			d->un.e.num_mem = k;
932 			this->nmixers++;
933 		}
934 
935 		/* output mute */
936 		if (w->widgetcap & COP_AWCAP_OUTAMP &&
937 		    w->outamp_cap & COP_AMPCAP_MUTE &&
938 		    w->nid != this->mic) {
939 			MIXER_REG_PROLOG;
940 			snprintf(d->label.name, sizeof(d->label.name),
941 			    "%s_mute", w->name);
942 			if (w->mixer_class >= 0)
943 				d->mixer_class = w->mixer_class;
944 			else {
945 				if (w->type == COP_AWTYPE_AUDIO_MIXER ||
946 				    w->type == COP_AWTYPE_AUDIO_SELECTOR ||
947 				    w->type == COP_AWTYPE_PIN_COMPLEX)
948 					d->mixer_class = AZ_CLASS_OUTPUT;
949 				else
950 					d->mixer_class = AZ_CLASS_INPUT;
951 			}
952 			m->target = MI_TARGET_OUTAMP;
953 			azalia_devinfo_offon(d);
954 			this->nmixers++;
955 		}
956 
957 		/* output gain */
958 		if (w->widgetcap & COP_AWCAP_OUTAMP &&
959 		    COP_AMPCAP_NUMSTEPS(w->outamp_cap) &&
960 		    w->nid != this->mic) {
961 			MIXER_REG_PROLOG;
962 			snprintf(d->label.name, sizeof(d->label.name),
963 			    "%s", w->name);
964 			d->type = AUDIO_MIXER_VALUE;
965 			if (w->mixer_class >= 0)
966 				d->mixer_class = w->mixer_class;
967 			else {
968 				if (w->type == COP_AWTYPE_AUDIO_MIXER ||
969 				    w->type == COP_AWTYPE_AUDIO_SELECTOR ||
970 				    w->type == COP_AWTYPE_PIN_COMPLEX)
971 					d->mixer_class = AZ_CLASS_OUTPUT;
972 				else
973 					d->mixer_class = AZ_CLASS_INPUT;
974 			}
975 			m->target = MI_TARGET_OUTAMP;
976 			d->un.v.num_channels = WIDGET_CHANNELS(w);
977 			d->un.v.units.name[0] = 0;
978 			d->un.v.delta =
979 			    MIXER_DELTA(COP_AMPCAP_NUMSTEPS(w->outamp_cap));
980 			this->nmixers++;
981 		}
982 
983 		/* input mute */
984 		if (w->widgetcap & COP_AWCAP_INAMP &&
985 		    w->inamp_cap & COP_AMPCAP_MUTE &&
986 		    w->nid != this->speaker &&
987 		    w->nid != this->speaker2) {
988 			if (w->type != COP_AWTYPE_AUDIO_MIXER) {
989 				MIXER_REG_PROLOG;
990 				snprintf(d->label.name, sizeof(d->label.name),
991 				    "%s_mute", w->name);
992 				if (w->mixer_class >= 0)
993 					d->mixer_class = w->mixer_class;
994 				else
995 					d->mixer_class = AZ_CLASS_INPUT;
996 				m->target = 0;
997 				azalia_devinfo_offon(d);
998 				this->nmixers++;
999 			} else {
1000 				MIXER_REG_PROLOG;
1001 				snprintf(d->label.name, sizeof(d->label.name),
1002 				    "%s_source", w->name);
1003 				m->target = MI_TARGET_MUTESET;
1004 				d->type = AUDIO_MIXER_SET;
1005 				if (w->mixer_class >= 0)
1006 					d->mixer_class = w->mixer_class;
1007 				else
1008 					d->mixer_class = AZ_CLASS_INPUT;
1009 				for (j = 0, k = 0;
1010 				    j < w->nconnections && k < 32; j++) {
1011 					if (!azalia_widget_enabled(this,
1012 					    w->connections[j]))
1013 						continue;
1014 					if (w->connections[j] == this->speaker ||
1015 					    w->connections[j] == this->speaker2)
1016 						continue;
1017 					d->un.s.member[k].mask = 1 << j;
1018 					strlcpy(d->un.s.member[k].label.name,
1019 					    this->w[w->connections[j]].name,
1020 					    MAX_AUDIO_DEV_LEN);
1021 					k++;
1022 				}
1023 				d->un.s.num_mem = k;
1024 				if (k != 0)
1025 					this->nmixers++;
1026 			}
1027 		}
1028 
1029 		/* input gain */
1030 		if (w->widgetcap & COP_AWCAP_INAMP &&
1031 		    COP_AMPCAP_NUMSTEPS(w->inamp_cap) &&
1032 		    w->nid != this->speaker &&
1033 		    w->nid != this->speaker2) {
1034 			if (w->type != COP_AWTYPE_AUDIO_SELECTOR &&
1035 			    w->type != COP_AWTYPE_AUDIO_MIXER) {
1036 				MIXER_REG_PROLOG;
1037 				snprintf(d->label.name, sizeof(d->label.name),
1038 				    "%s", w->name);
1039 				d->type = AUDIO_MIXER_VALUE;
1040 				if (w->mixer_class >= 0)
1041 					d->mixer_class = w->mixer_class;
1042 				else
1043 					d->mixer_class = AZ_CLASS_INPUT;
1044 				m->target = 0;
1045 				d->un.v.num_channels = WIDGET_CHANNELS(w);
1046 				d->un.v.units.name[0] = 0;
1047 				d->un.v.delta =
1048 				    MIXER_DELTA(COP_AMPCAP_NUMSTEPS(w->inamp_cap));
1049 				this->nmixers++;
1050 			} else {
1051 				for (j = 0; j < w->nconnections; j++) {
1052 					if (!azalia_widget_enabled(this,
1053 					    w->connections[j]))
1054 						continue;
1055 					if (w->connections[j] == this->speaker ||
1056 					    w->connections[j] == this->speaker2)
1057 						continue;
1058 					MIXER_REG_PROLOG;
1059 					snprintf(d->label.name,
1060 					    sizeof(d->label.name), "%s_%s",
1061 					    w->name,
1062 					    this->w[w->connections[j]].name);
1063 					d->type = AUDIO_MIXER_VALUE;
1064 					if (w->mixer_class >= 0)
1065 						d->mixer_class = w->mixer_class;
1066 					else
1067 						d->mixer_class = AZ_CLASS_INPUT;
1068 					m->target = j;
1069 					d->un.v.num_channels = WIDGET_CHANNELS(w);
1070 					d->un.v.units.name[0] = 0;
1071 					d->un.v.delta =
1072 					    MIXER_DELTA(COP_AMPCAP_NUMSTEPS(w->inamp_cap));
1073 					this->nmixers++;
1074 				}
1075 			}
1076 		}
1077 
1078 		/* hardcoded mixer inputs */
1079 		if (w->type == COP_AWTYPE_AUDIO_MIXER &&
1080 		    !(w->widgetcap & COP_AWCAP_INAMP)) {
1081 			MIXER_REG_PROLOG;
1082 			snprintf(d->label.name, sizeof(d->label.name),
1083 			    "%s_source", w->name);
1084 			m->target = MI_TARGET_MIXERSET;
1085 			d->type = AUDIO_MIXER_SET;
1086 			if (w->mixer_class >= 0)
1087 				d->mixer_class = w->mixer_class;
1088 			else
1089 				d->mixer_class = AZ_CLASS_INPUT;
1090 			for (j = 0, k = 0;
1091 			    j < w->nconnections && k < 32; j++) {
1092 				if (!azalia_widget_enabled(this,
1093 				    w->connections[j]))
1094 					continue;
1095 				if (w->connections[j] == this->speaker ||
1096 				    w->connections[j] == this->speaker2)
1097 					continue;
1098 				d->un.s.member[k].mask = 1 << j;
1099 				strlcpy(d->un.s.member[k].label.name,
1100 				    this->w[w->connections[j]].name,
1101 				    MAX_AUDIO_DEV_LEN);
1102 				k++;
1103 			}
1104 			d->un.s.num_mem = k;
1105 			if (k != 0)
1106 				this->nmixers++;
1107 		}
1108 
1109 		/* pin direction */
1110 		if (w->type == COP_AWTYPE_PIN_COMPLEX &&
1111 		    ((w->d.pin.cap & COP_PINCAP_OUTPUT &&
1112 		    w->d.pin.cap & COP_PINCAP_INPUT) ||
1113 		    COP_PINCAP_VREF(w->d.pin.cap) > 1)) {
1114 
1115 			MIXER_REG_PROLOG;
1116 			snprintf(d->label.name, sizeof(d->label.name),
1117 			    "%s_dir", w->name);
1118 			d->type = AUDIO_MIXER_ENUM;
1119 			d->mixer_class = AZ_CLASS_OUTPUT;
1120 			m->target = MI_TARGET_PINDIR;
1121 
1122 			k = 0;
1123 			d->un.e.member[k].ord = 0;
1124 			strlcpy(d->un.e.member[k].label.name, "none",
1125 			    MAX_AUDIO_DEV_LEN);
1126 			k++;
1127 
1128 			if (w->d.pin.cap & COP_PINCAP_OUTPUT) {
1129 				d->un.e.member[k].ord = 1;
1130 				strlcpy(d->un.e.member[k].label.name,
1131 				    AudioNoutput, MAX_AUDIO_DEV_LEN);
1132 				k++;
1133 			}
1134 
1135 			if (w->d.pin.cap & COP_PINCAP_INPUT) {
1136 				d->un.e.member[k].ord = 2;
1137 				strlcpy(d->un.e.member[k].label.name,
1138 				    AudioNinput, MAX_AUDIO_DEV_LEN);
1139 				k++;
1140 
1141 				for (j = 0; j < 4; j++) {
1142 					if (j == 0) {
1143 						bits = (1 << CORB_PWC_VREF_GND);
1144 						strlcpy(d->un.e.member[k].label.name,
1145 						    AudioNinput "-vr0",
1146 						    MAX_AUDIO_DEV_LEN);
1147 					} else if (j == 1) {
1148 						bits = (1 << CORB_PWC_VREF_50);
1149 						strlcpy(d->un.e.member[k].label.name,
1150 						    AudioNinput "-vr50",
1151 						    MAX_AUDIO_DEV_LEN);
1152 					} else if (j == 2) {
1153 						bits = (1 << CORB_PWC_VREF_80);
1154 						strlcpy(d->un.e.member[k].label.name,
1155 						    AudioNinput "-vr80",
1156 						    MAX_AUDIO_DEV_LEN);
1157 					} else if (j == 3) {
1158 						bits = (1 << CORB_PWC_VREF_100);
1159 						strlcpy(d->un.e.member[k].label.name,
1160 						    AudioNinput "-vr100",
1161 						    MAX_AUDIO_DEV_LEN);
1162 					}
1163 					if ((COP_PINCAP_VREF(w->d.pin.cap) &
1164 					    bits) == bits) {
1165 						d->un.e.member[k].ord = j + 3;
1166 						k++;
1167 					}
1168 				}
1169 			}
1170 			d->un.e.num_mem = k;
1171 			this->nmixers++;
1172 		}
1173 
1174 		/* pin headphone-boost */
1175 		if (w->type == COP_AWTYPE_PIN_COMPLEX &&
1176 		    w->d.pin.cap & COP_PINCAP_HEADPHONE &&
1177 		    w->nid != this->mic) {
1178 			MIXER_REG_PROLOG;
1179 			snprintf(d->label.name, sizeof(d->label.name),
1180 			    "%s_boost", w->name);
1181 			d->mixer_class = AZ_CLASS_OUTPUT;
1182 			m->target = MI_TARGET_PINBOOST;
1183 			azalia_devinfo_offon(d);
1184 			this->nmixers++;
1185 		}
1186 
1187 		if (w->type == COP_AWTYPE_PIN_COMPLEX &&
1188 		    w->d.pin.cap & COP_PINCAP_EAPD) {
1189 			MIXER_REG_PROLOG;
1190 			snprintf(d->label.name, sizeof(d->label.name),
1191 			    "%s_eapd", w->name);
1192 			d->mixer_class = AZ_CLASS_OUTPUT;
1193 			m->target = MI_TARGET_EAPD;
1194 			azalia_devinfo_offon(d);
1195 			this->nmixers++;
1196 		}
1197 	}
1198 
1199 	/* sense pins */
1200 	for (i = 0; i < this->nsense_pins; i++) {
1201 		if (!azalia_widget_enabled(this, this->sense_pins[i])) {
1202 			DPRINTF(("%s: sense pin %2.2x not found\n",
1203 			    __func__, this->sense_pins[i]));
1204 			continue;
1205 		}
1206 
1207 		MIXER_REG_PROLOG;
1208 		m->nid = this->w[this->sense_pins[i]].nid;
1209 		snprintf(d->label.name, sizeof(d->label.name), "%s_sense",
1210 		    this->w[this->sense_pins[i]].name);
1211 		d->type = AUDIO_MIXER_ENUM;
1212 		d->mixer_class = AZ_CLASS_OUTPUT;
1213 		m->target = MI_TARGET_PINSENSE;
1214 		d->un.e.num_mem = 2;
1215 		d->un.e.member[0].ord = 0;
1216 		strlcpy(d->un.e.member[0].label.name, "unplugged",
1217 		    MAX_AUDIO_DEV_LEN);
1218 		d->un.e.member[1].ord = 1;
1219 		strlcpy(d->un.e.member[1].label.name, "plugged",
1220 		    MAX_AUDIO_DEV_LEN);
1221 		this->nmixers++;
1222 	}
1223 
1224 	/* spkr mute by jack sense */
1225 	this->spkr_mute_method = AZ_SPKR_MUTE_NONE;
1226 	if (this->speaker != -1 && this->spkr_dac != -1 && this->nsense_pins > 0) {
1227 		w = &this->w[this->speaker];
1228 		if ((w->widgetcap & COP_AWCAP_OUTAMP) &&
1229 		    (w->outamp_cap & COP_AMPCAP_MUTE))
1230 			this->spkr_mute_method = AZ_SPKR_MUTE_SPKR_MUTE;
1231 		else if ((w->d.pin.cap & COP_PINCAP_OUTPUT) &&
1232 		    (w->d.pin.cap & COP_PINCAP_INPUT))
1233 			this->spkr_mute_method = AZ_SPKR_MUTE_SPKR_DIR;
1234 		else {
1235 			w = &this->w[this->spkr_dac];
1236 			if (w->nid != this->dacs.groups[0].conv[0] &&
1237 			    (w->widgetcap & COP_AWCAP_OUTAMP) &&
1238 			    (w->outamp_cap & COP_AMPCAP_MUTE))
1239 				this->spkr_mute_method = AZ_SPKR_MUTE_DAC_MUTE;
1240 		}
1241 	}
1242 	if (this->spkr_mute_method != AZ_SPKR_MUTE_NONE) {
1243 		w = &this->w[this->speaker];
1244 		MIXER_REG_PROLOG;
1245 		m->nid = w->nid;
1246 		snprintf(d->label.name, sizeof(d->label.name),
1247 		    "%s_muters", w->name);
1248 		m->target = MI_TARGET_SENSESET;
1249 		d->type = AUDIO_MIXER_SET;
1250 		d->mixer_class = AZ_CLASS_OUTPUT;
1251 		this->spkr_muters = 0;
1252 		for (i = 0, j = 0; i < this->nsense_pins; i++) {
1253 			ww = &this->w[this->sense_pins[i]];
1254 			if (!(ww->d.pin.cap & COP_PINCAP_OUTPUT))
1255 				continue;
1256 			if (!(ww->widgetcap & COP_AWCAP_UNSOL))
1257 				continue;
1258 			d->un.s.member[j].mask = 1 << i;
1259 			this->spkr_muters |= (1 << i);
1260 			strlcpy(d->un.s.member[j++].label.name, ww->name,
1261 			    MAX_AUDIO_DEV_LEN);
1262 		}
1263 		d->un.s.num_mem = j;
1264 		if (j != 0)
1265 			this->nmixers++;
1266 	}
1267 
1268 	/* playback volume group */
1269 	if (this->playvols.nslaves > 0) {
1270 		mixer_devinfo_t *d;
1271 		err = azalia_mixer_ensure_capacity(this,
1272 		    this->nmixers + 3);
1273 
1274 		/* volume */
1275 		m = &this->mixers[this->nmixers];
1276 		m->nid = this->playvols.master;
1277 		m->target = MI_TARGET_PLAYVOL;
1278 		d = &m->devinfo;
1279 		d->mixer_class = AZ_CLASS_OUTPUT;
1280 		snprintf(d->label.name, sizeof(d->label.name),
1281 		    "%s", AudioNmaster);
1282 		d->type = AUDIO_MIXER_VALUE;
1283 		d->un.v.num_channels = 2;
1284 		d->un.v.delta = 8;
1285 		this->nmixers++;
1286 		d->next = this->nmixers;
1287 
1288 		/* mute */
1289 		m = &this->mixers[this->nmixers];
1290 		m->nid = this->playvols.master;
1291 		m->target = MI_TARGET_PLAYVOL;
1292 		d = &m->devinfo;
1293 		d->prev = this->nmixers - 1;
1294 		d->mixer_class = AZ_CLASS_OUTPUT;
1295 		snprintf(d->label.name, sizeof(d->label.name),
1296 		    "%s", AudioNmute);
1297 		azalia_devinfo_offon(d);
1298 		this->nmixers++;
1299 		d->next = this->nmixers;
1300 
1301 		/* slaves */
1302 		m = &this->mixers[this->nmixers];
1303 		m->nid = this->playvols.master;
1304 		m->target = MI_TARGET_PLAYVOL;
1305 		d = &m->devinfo;
1306 		d->prev = this->nmixers - 1;
1307 		d->mixer_class = AZ_CLASS_OUTPUT;
1308 		snprintf(d->label.name, sizeof(d->label.name),
1309 		    "%s", "slaves");
1310 		d->type = AUDIO_MIXER_SET;
1311 		for (i = 0, j = 0; i < this->playvols.nslaves; i++) {
1312 			ww = &this->w[this->playvols.slaves[i]];
1313 			d->un.s.member[j].mask = (1 << i);
1314 			strlcpy(d->un.s.member[j++].label.name, ww->name,
1315 			    MAX_AUDIO_DEV_LEN);
1316 		}
1317 		d->un.s.num_mem = j;
1318 		this->nmixers++;
1319 	}
1320 
1321 	/* recording volume group */
1322 	if (this->recvols.nslaves > 0) {
1323 		mixer_devinfo_t *d;
1324 		err = azalia_mixer_ensure_capacity(this,
1325 		    this->nmixers + 3);
1326 
1327 		/* volume */
1328 		m = &this->mixers[this->nmixers];
1329 		m->nid = this->recvols.master;
1330 		m->target = MI_TARGET_RECVOL;
1331 		d = &m->devinfo;
1332 		d->mixer_class = AZ_CLASS_RECORD;
1333 		snprintf(d->label.name, sizeof(d->label.name),
1334 		    "%s", AudioNvolume);
1335 		d->type = AUDIO_MIXER_VALUE;
1336 		d->un.v.num_channels = 2;
1337 		d->un.v.delta = 8;
1338 		this->nmixers++;
1339 		d->next = this->nmixers;
1340 
1341 		/* mute */
1342 		m = &this->mixers[this->nmixers];
1343 		m->nid = this->recvols.master;
1344 		m->target = MI_TARGET_RECVOL;
1345 		d = &m->devinfo;
1346 		d->prev = this->nmixers - 1;
1347 		d->mixer_class = AZ_CLASS_RECORD;
1348 		snprintf(d->label.name, sizeof(d->label.name),
1349 		    "%s", AudioNmute);
1350 		azalia_devinfo_offon(d);
1351 		this->nmixers++;
1352 		d->next = this->nmixers;
1353 
1354 		/* slaves */
1355 		m = &this->mixers[this->nmixers];
1356 		m->nid = this->recvols.master;
1357 		m->target = MI_TARGET_RECVOL;
1358 		d = &m->devinfo;
1359 		d->prev = this->nmixers - 1;
1360 		d->mixer_class = AZ_CLASS_RECORD;
1361 		snprintf(d->label.name, sizeof(d->label.name),
1362 		    "%s", "slaves");
1363 		d->type = AUDIO_MIXER_SET;
1364 		for (i = 0, j = 0; i < this->recvols.nslaves; i++) {
1365 			ww = &this->w[this->recvols.slaves[i]];
1366 			d->un.s.member[j].mask = (1 << i);
1367 			strlcpy(d->un.s.member[j++].label.name, ww->name,
1368 			    MAX_AUDIO_DEV_LEN);
1369 		}
1370 		d->un.s.num_mem = j;
1371 		this->nmixers++;
1372 	}
1373 
1374 	/* if the codec has more than one DAC group, the first is analog
1375 	 * and the second is digital.
1376 	 */
1377 	if (this->dacs.ngroups > 1) {
1378 		MIXER_REG_PROLOG;
1379 		strlcpy(d->label.name, AudioNmode, sizeof(d->label.name));
1380 		d->type = AUDIO_MIXER_ENUM;
1381 		d->mixer_class = AZ_CLASS_OUTPUT;
1382 		m->target = MI_TARGET_DAC;
1383 		m->nid = this->audiofunc;
1384 		d->un.e.member[0].ord = 0;
1385 		strlcpy(d->un.e.member[0].label.name, "analog",
1386 		    MAX_AUDIO_DEV_LEN);
1387 		d->un.e.member[1].ord = 1;
1388 		strlcpy(d->un.e.member[1].label.name, "digital",
1389 		    MAX_AUDIO_DEV_LEN);
1390 		d->un.e.num_mem = 2;
1391 		this->nmixers++;
1392 	}
1393 
1394 	/* if the codec has more than one ADC group, the first is analog
1395 	 * and the second is digital.
1396 	 */
1397 	if (this->adcs.ngroups > 1) {
1398 		MIXER_REG_PROLOG;
1399 		strlcpy(d->label.name, AudioNmode, sizeof(d->label.name));
1400 		d->type = AUDIO_MIXER_ENUM;
1401 		d->mixer_class = AZ_CLASS_RECORD;
1402 		m->target = MI_TARGET_ADC;
1403 		m->nid = this->audiofunc;
1404 		d->un.e.member[0].ord = 0;
1405 		strlcpy(d->un.e.member[0].label.name, "analog",
1406 		    MAX_AUDIO_DEV_LEN);
1407 		d->un.e.member[1].ord = 1;
1408 		strlcpy(d->un.e.member[1].label.name, "digital",
1409 		    MAX_AUDIO_DEV_LEN);
1410 		d->un.e.num_mem = 2;
1411 		this->nmixers++;
1412 	}
1413 
1414 	azalia_mixer_fix_indexes(this);
1415 	azalia_mixer_default(this);
1416 	return 0;
1417 }
1418 
1419 void
1420 azalia_devinfo_offon(mixer_devinfo_t *d)
1421 {
1422 	d->type = AUDIO_MIXER_ENUM;
1423 	d->un.e.num_mem = 2;
1424 	d->un.e.member[0].ord = 0;
1425 	strlcpy(d->un.e.member[0].label.name, AudioNoff, MAX_AUDIO_DEV_LEN);
1426 	d->un.e.member[1].ord = 1;
1427 	strlcpy(d->un.e.member[1].label.name, AudioNon, MAX_AUDIO_DEV_LEN);
1428 }
1429 
1430 int
1431 azalia_mixer_ensure_capacity(codec_t *this, size_t newsize)
1432 {
1433 	size_t newmax;
1434 	void *newbuf;
1435 
1436 	if (this->maxmixers >= newsize)
1437 		return 0;
1438 	newmax = this->maxmixers + 10;
1439 	if (newmax < newsize)
1440 		newmax = newsize;
1441 	newbuf = mallocarray(newmax, sizeof(mixer_item_t), M_DEVBUF,
1442 	    M_NOWAIT | M_ZERO);
1443 	if (newbuf == NULL) {
1444 		printf("%s: out of memory in %s\n", XNAME(this), __func__);
1445 		return ENOMEM;
1446 	}
1447 	bcopy(this->mixers, newbuf, this->maxmixers * sizeof(mixer_item_t));
1448 	free(this->mixers, M_DEVBUF, this->maxmixers * sizeof(mixer_item_t));
1449 	this->mixers = newbuf;
1450 	this->maxmixers = newmax;
1451 	return 0;
1452 }
1453 
1454 int
1455 azalia_mixer_fix_indexes(codec_t *this)
1456 {
1457 	int i;
1458 	mixer_devinfo_t *d;
1459 
1460 	for (i = 0; i < this->nmixers; i++) {
1461 		d = &this->mixers[i].devinfo;
1462 #ifdef DIAGNOSTIC
1463 		if (d->index != 0 && d->index != i)
1464 			printf("%s: index mismatch %d %d\n", __func__,
1465 			    d->index, i);
1466 #endif
1467 		d->index = i;
1468 		if (d->prev == 0)
1469 			d->prev = AUDIO_MIXER_LAST;
1470 		if (d->next == 0)
1471 			d->next = AUDIO_MIXER_LAST;
1472 	}
1473 	return 0;
1474 }
1475 
1476 int
1477 azalia_mixer_default(codec_t *this)
1478 {
1479 	widget_t *w;
1480 	mixer_item_t *m;
1481 	mixer_ctrl_t mc;
1482 	int i, j, tgt, cap, err;
1483 
1484 	/* unmute all */
1485 	for (i = 0; i < this->nmixers; i++) {
1486 		m = &this->mixers[i];
1487 		if (!IS_MI_TARGET_INAMP(m->target) &&
1488 		    m->target != MI_TARGET_OUTAMP)
1489 			continue;
1490 		if (m->devinfo.type != AUDIO_MIXER_ENUM)
1491 			continue;
1492 		bzero(&mc, sizeof(mc));
1493 		mc.dev = i;
1494 		mc.type = AUDIO_MIXER_ENUM;
1495 		azalia_mixer_set(this, m->nid, m->target, &mc);
1496 	}
1497 
1498 	/* set unextreme volume */
1499 	for (i = 0; i < this->nmixers; i++) {
1500 		m = &this->mixers[i];
1501 		if (!IS_MI_TARGET_INAMP(m->target) &&
1502 		    m->target != MI_TARGET_OUTAMP)
1503 			continue;
1504 		if (m->devinfo.type != AUDIO_MIXER_VALUE)
1505 			continue;
1506 		bzero(&mc, sizeof(mc));
1507 		mc.dev = i;
1508 		mc.type = AUDIO_MIXER_VALUE;
1509 		mc.un.value.num_channels = 1;
1510 		mc.un.value.level[0] = AUDIO_MAX_GAIN / 2;
1511 		if (WIDGET_CHANNELS(&this->w[m->nid]) == 2) {
1512 			mc.un.value.num_channels = 2;
1513 			mc.un.value.level[1] = mc.un.value.level[0];
1514 		}
1515 		azalia_mixer_set(this, m->nid, m->target, &mc);
1516 	}
1517 
1518 	/* unmute all */
1519 	for (i = 0; i < this->nmixers; i++) {
1520 		m = &this->mixers[i];
1521 		if (m->target != MI_TARGET_MUTESET)
1522 			continue;
1523 		if (m->devinfo.type != AUDIO_MIXER_SET)
1524 			continue;
1525 		bzero(&mc, sizeof(mc));
1526 		mc.dev = i;
1527 		mc.type = AUDIO_MIXER_SET;
1528 		if (!azalia_widget_enabled(this, m->nid)) {
1529 			DPRINTF(("%s: invalid set nid\n", __func__));
1530 			return EINVAL;
1531 		}
1532 		w = &this->w[m->nid];
1533 		for (j = 0; j < w->nconnections; j++) {
1534 			if (!azalia_widget_enabled(this, w->connections[j]))
1535 				continue;
1536 			if (w->nid == this->input_mixer &&
1537 			    w->connections[j] == this->mic)
1538 				continue;
1539 			mc.un.mask |= 1 << j;
1540 		}
1541 		azalia_mixer_set(this, m->nid, m->target, &mc);
1542 	}
1543 
1544 	/* make sure default connection is valid */
1545 	for (i = 0; i < this->nmixers; i++) {
1546 		m = &this->mixers[i];
1547 		if (m->target != MI_TARGET_CONNLIST)
1548 			continue;
1549 
1550 		azalia_mixer_get(this, m->nid, m->target, &mc);
1551 		for (j = 0; j < m->devinfo.un.e.num_mem; j++) {
1552 			if (mc.un.ord == m->devinfo.un.e.member[j].ord)
1553 				break;
1554 		}
1555 		if (j >= m->devinfo.un.e.num_mem) {
1556 			bzero(&mc, sizeof(mc));
1557 			mc.dev = i;
1558 			mc.type = AUDIO_MIXER_ENUM;
1559 			mc.un.ord = m->devinfo.un.e.member[0].ord;
1560 		}
1561 		azalia_mixer_set(this, m->nid, m->target, &mc);
1562 	}
1563 
1564 	/* get default value for play group master */
1565 	for (i = 0; i < this->playvols.nslaves; i++) {
1566 		if (!(this->playvols.cur & (1 << i)))
1567  			continue;
1568 		w = &this->w[this->playvols.slaves[i]];
1569 		if (!(COP_AMPCAP_NUMSTEPS(w->outamp_cap)))
1570 			continue;
1571 		mc.type = AUDIO_MIXER_VALUE;
1572 		tgt = MI_TARGET_OUTAMP;
1573 		azalia_mixer_get(this, w->nid, tgt, &mc);
1574 		this->playvols.vol_l = mc.un.value.level[0];
1575 		this->playvols.vol_r = mc.un.value.level[0];
1576 		break;
1577  	}
1578 	this->playvols.mute = 0;
1579 
1580 	/* get default value for record group master */
1581 	for (i = 0; i < this->recvols.nslaves; i++) {
1582 		if (!(this->recvols.cur & (1 << i)))
1583 			continue;
1584 		w = &this->w[this->recvols.slaves[i]];
1585 		mc.type = AUDIO_MIXER_VALUE;
1586 		tgt = MI_TARGET_OUTAMP;
1587 		cap = w->outamp_cap;
1588 		if (w->type == COP_AWTYPE_PIN_COMPLEX ||
1589 		    w->type == COP_AWTYPE_AUDIO_INPUT) {
1590 			tgt = 0;
1591 			cap = w->inamp_cap;
1592  		}
1593 		if (!(COP_AMPCAP_NUMSTEPS(cap)))
1594 			continue;
1595 		azalia_mixer_get(this, w->nid, tgt, &mc);
1596 		this->recvols.vol_l = mc.un.value.level[0];
1597 		this->recvols.vol_r = mc.un.value.level[0];
1598 		break;
1599  	}
1600 	this->recvols.mute = 0;
1601 
1602 	err = azalia_codec_enable_unsol(this);
1603 	if (err)
1604 		return(err);
1605 
1606 	return 0;
1607 }
1608 
1609 int
1610 azalia_codec_enable_unsol(codec_t *this)
1611 {
1612 	widget_t *w;
1613 	uint32_t result;
1614 	int i, err;
1615 
1616 	/* jack sense */
1617 	for (i = 0; i < this->nsense_pins; i++) {
1618 		if (this->spkr_muters & (1 << i)) {
1619 			azalia_comresp(this, this->sense_pins[i],
1620 			    CORB_SET_UNSOLICITED_RESPONSE,
1621 			    CORB_UNSOL_ENABLE | AZ_TAG_SPKR, NULL);
1622 		}
1623 	}
1624 	if (this->spkr_muters != 0)
1625 		azalia_unsol_event(this, AZ_TAG_SPKR);
1626 
1627 	/* volume knob */
1628 	if (this->playvols.master != this->audiofunc) {
1629 
1630 		w = &this->w[this->playvols.master];
1631 		err = azalia_comresp(this, w->nid, CORB_GET_VOLUME_KNOB,
1632 		    0, &result);
1633 		if (err) {
1634 			DPRINTF(("%s: get volume knob error\n", __func__));
1635 			return err;
1636 		}
1637 
1638 		/* current level */
1639 		this->playvols.hw_step = CORB_VKNOB_VOLUME(result);
1640 		this->playvols.hw_nsteps = COP_VKCAP_NUMSTEPS(w->d.volume.cap);
1641 
1642 		/* indirect mode */
1643 		result &= ~(CORB_VKNOB_DIRECT);
1644 		err = azalia_comresp(this, w->nid, CORB_SET_VOLUME_KNOB,
1645 		    result, NULL);
1646 		if (err) {
1647 			DPRINTF(("%s: set volume knob error\n", __func__));
1648 			/* XXX If there was an error setting indirect
1649 			 * mode, do not return an error.  However, do not
1650 			 * enable unsolicited responses either.  Most
1651 			 * likely the volume knob doesn't work right.
1652 			 * Perhaps it's simply not wired/enabled.
1653 			 */
1654 			return 0;
1655 		}
1656 
1657 		/* enable unsolicited responses */
1658 		result = CORB_UNSOL_ENABLE | AZ_TAG_PLAYVOL;
1659 		err = azalia_comresp(this, w->nid,
1660 		    CORB_SET_UNSOLICITED_RESPONSE, result, NULL);
1661 		if (err) {
1662 			DPRINTF(("%s: set vknob unsol resp error\n", __func__));
1663 			return err;
1664 		}
1665 	}
1666 
1667 	return 0;
1668 }
1669 
1670 int
1671 azalia_mixer_delete(codec_t *this)
1672 {
1673 	if (this->mixers != NULL) {
1674 		free(this->mixers, M_DEVBUF, 0);
1675 		this->mixers = NULL;
1676 	}
1677 	return 0;
1678 }
1679 
1680 /**
1681  * @param mc	mc->type must be set by the caller before the call
1682  */
1683 int
1684 azalia_mixer_get(const codec_t *this, nid_t nid, int target,
1685     mixer_ctrl_t *mc)
1686 {
1687 	uint32_t result, cap, value;
1688 	nid_t n;
1689 	int i, err;
1690 
1691 	if (mc->type == AUDIO_MIXER_CLASS) {
1692 		return(0);
1693 	}
1694 
1695 	/* inamp mute */
1696 	else if (IS_MI_TARGET_INAMP(target) && mc->type == AUDIO_MIXER_ENUM) {
1697 		err = azalia_comresp(this, nid, CORB_GET_AMPLIFIER_GAIN_MUTE,
1698 		    CORB_GAGM_INPUT | CORB_GAGM_LEFT |
1699 		    MI_TARGET_INAMP(target), &result);
1700 		if (err)
1701 			return err;
1702 		mc->un.ord = result & CORB_GAGM_MUTE ? 1 : 0;
1703 	}
1704 
1705 	/* inamp gain */
1706 	else if (IS_MI_TARGET_INAMP(target) && mc->type == AUDIO_MIXER_VALUE) {
1707 		err = azalia_comresp(this, nid, CORB_GET_AMPLIFIER_GAIN_MUTE,
1708 		      CORB_GAGM_INPUT | CORB_GAGM_LEFT |
1709 		      MI_TARGET_INAMP(target), &result);
1710 		if (err)
1711 			return err;
1712 		mc->un.value.level[0] = azalia_mixer_from_device_value(this,
1713 		    nid, target, CORB_GAGM_GAIN(result));
1714 		if (this->w[nid].type == COP_AWTYPE_AUDIO_SELECTOR ||
1715 		    this->w[nid].type == COP_AWTYPE_AUDIO_MIXER) {
1716 			n = this->w[nid].connections[MI_TARGET_INAMP(target)];
1717 			if (!azalia_widget_enabled(this, n)) {
1718 				DPRINTF(("%s: nid %2.2x invalid index %d\n",
1719 				   __func__, nid,  MI_TARGET_INAMP(target)));
1720 				n = nid;
1721 			}
1722 		} else
1723 			n = nid;
1724 		mc->un.value.num_channels = WIDGET_CHANNELS(&this->w[n]);
1725 		if (mc->un.value.num_channels == 2) {
1726 			err = azalia_comresp(this, nid,
1727 			    CORB_GET_AMPLIFIER_GAIN_MUTE, CORB_GAGM_INPUT |
1728 			    CORB_GAGM_RIGHT | MI_TARGET_INAMP(target),
1729 			    &result);
1730 			if (err)
1731 				return err;
1732 			mc->un.value.level[1] = azalia_mixer_from_device_value
1733 			    (this, nid, target, CORB_GAGM_GAIN(result));
1734 		}
1735 	}
1736 
1737 	/* outamp mute */
1738 	else if (target == MI_TARGET_OUTAMP && mc->type == AUDIO_MIXER_ENUM) {
1739 		err = azalia_comresp(this, nid, CORB_GET_AMPLIFIER_GAIN_MUTE,
1740 		    CORB_GAGM_OUTPUT | CORB_GAGM_LEFT | 0, &result);
1741 		if (err)
1742 			return err;
1743 		mc->un.ord = result & CORB_GAGM_MUTE ? 1 : 0;
1744 	}
1745 
1746 	/* outamp gain */
1747 	else if (target == MI_TARGET_OUTAMP && mc->type == AUDIO_MIXER_VALUE) {
1748 		err = azalia_comresp(this, nid, CORB_GET_AMPLIFIER_GAIN_MUTE,
1749 		      CORB_GAGM_OUTPUT | CORB_GAGM_LEFT | 0, &result);
1750 		if (err)
1751 			return err;
1752 		mc->un.value.level[0] = azalia_mixer_from_device_value(this,
1753 		    nid, target, CORB_GAGM_GAIN(result));
1754 		mc->un.value.num_channels = WIDGET_CHANNELS(&this->w[nid]);
1755 		if (mc->un.value.num_channels == 2) {
1756 			err = azalia_comresp(this, nid,
1757 			    CORB_GET_AMPLIFIER_GAIN_MUTE,
1758 			    CORB_GAGM_OUTPUT | CORB_GAGM_RIGHT | 0, &result);
1759 			if (err)
1760 				return err;
1761 			mc->un.value.level[1] = azalia_mixer_from_device_value
1762 			    (this, nid, target, CORB_GAGM_GAIN(result));
1763 		}
1764 	}
1765 
1766 	/* selection */
1767 	else if (target == MI_TARGET_CONNLIST) {
1768 		err = azalia_comresp(this, nid,
1769 		    CORB_GET_CONNECTION_SELECT_CONTROL, 0, &result);
1770 		if (err)
1771 			return err;
1772 		result = CORB_CSC_INDEX(result);
1773 		if (!azalia_widget_enabled(this,
1774 		    this->w[nid].connections[result]))
1775 			mc->un.ord = -1;
1776 		else
1777 			mc->un.ord = result;
1778 	}
1779 
1780 	/* pin I/O */
1781 	else if (target == MI_TARGET_PINDIR) {
1782 		err = azalia_comresp(this, nid,
1783 		    CORB_GET_PIN_WIDGET_CONTROL, 0, &result);
1784 		if (err)
1785 			return err;
1786 
1787 		value = result;
1788 		if (!(result & (CORB_PWC_INPUT | CORB_PWC_OUTPUT)))
1789 			mc->un.ord = 0;
1790 		else if (result & CORB_PWC_OUTPUT)
1791 			mc->un.ord = 1;
1792 		else {
1793 			cap = COP_PINCAP_VREF(this->w[nid].d.pin.cap);
1794 			result &= CORB_PWC_VREF_MASK;
1795 			if (result == CORB_PWC_VREF_GND)
1796 				mc->un.ord = 3;
1797 			else if (result == CORB_PWC_VREF_50)
1798 				mc->un.ord = 4;
1799 			else if (result == CORB_PWC_VREF_80)
1800 				mc->un.ord = 5;
1801 			else if (result == CORB_PWC_VREF_100)
1802 				mc->un.ord = 6;
1803 			else
1804 				mc->un.ord = 2;
1805 		}
1806 	}
1807 
1808 	/* pin headphone-boost */
1809 	else if (target == MI_TARGET_PINBOOST) {
1810 		err = azalia_comresp(this, nid,
1811 		    CORB_GET_PIN_WIDGET_CONTROL, 0, &result);
1812 		if (err)
1813 			return err;
1814 		mc->un.ord = result & CORB_PWC_HEADPHONE ? 1 : 0;
1815 	}
1816 
1817 	/* DAC group selection */
1818 	else if (target == MI_TARGET_DAC) {
1819 		mc->un.ord = this->dacs.cur;
1820 	}
1821 
1822 	/* ADC selection */
1823 	else if (target == MI_TARGET_ADC) {
1824 		mc->un.ord = this->adcs.cur;
1825 	}
1826 
1827 	/* S/PDIF */
1828 	else if (target == MI_TARGET_SPDIF) {
1829 		err = azalia_comresp(this, nid, CORB_GET_DIGITAL_CONTROL,
1830 		    0, &result);
1831 		if (err)
1832 			return err;
1833 		mc->un.mask = result & 0xff & ~(CORB_DCC_DIGEN | CORB_DCC_NAUDIO);
1834 	} else if (target == MI_TARGET_SPDIF_CC) {
1835 		err = azalia_comresp(this, nid, CORB_GET_DIGITAL_CONTROL,
1836 		    0, &result);
1837 		if (err)
1838 			return err;
1839 		mc->un.value.num_channels = 1;
1840 		mc->un.value.level[0] = CORB_DCC_CC(result);
1841 	}
1842 
1843 	/* EAPD */
1844 	else if (target == MI_TARGET_EAPD) {
1845 		err = azalia_comresp(this, nid, CORB_GET_EAPD_BTL_ENABLE,
1846 		    0, &result);
1847 		if (err)
1848 			return err;
1849 		mc->un.ord = result & CORB_EAPD_EAPD ? 1 : 0;
1850 	}
1851 
1852 	/* sense pin */
1853 	else if (target == MI_TARGET_PINSENSE) {
1854 		err = azalia_comresp(this, nid, CORB_GET_PIN_SENSE,
1855 		    0, &result);
1856 		if (err)
1857 			return err;
1858 		mc->un.ord = result & CORB_PS_PRESENCE ? 1 : 0;
1859 	}
1860 
1861 	/* mute set */
1862 	else if (target == MI_TARGET_MUTESET && mc->type == AUDIO_MIXER_SET) {
1863 		const widget_t *w;
1864 
1865 		if (!azalia_widget_enabled(this, nid)) {
1866 			DPRINTF(("%s: invalid muteset nid\n", XNAME(this)));
1867 			return EINVAL;
1868 		}
1869 		w = &this->w[nid];
1870 		mc->un.mask = 0;
1871 		for (i = 0; i < w->nconnections; i++) {
1872 			if (!azalia_widget_enabled(this, w->connections[i]))
1873 				continue;
1874 			err = azalia_comresp(this, nid,
1875 			    CORB_GET_AMPLIFIER_GAIN_MUTE,
1876 			    CORB_GAGM_INPUT | CORB_GAGM_LEFT |
1877 			    MI_TARGET_INAMP(i), &result);
1878 			if (err)
1879 				return err;
1880 			mc->un.mask |= (result & CORB_GAGM_MUTE) ? 0 : (1 << i);
1881 		}
1882 	}
1883 
1884 	/* mixer set - show all connections */
1885 	else if (target == MI_TARGET_MIXERSET && mc->type == AUDIO_MIXER_SET) {
1886 		const widget_t *w;
1887 
1888 		if (!azalia_widget_enabled(this, nid)) {
1889 			DPRINTF(("%s: invalid mixerset nid\n", XNAME(this)));
1890 			return EINVAL;
1891 		}
1892 		w = &this->w[nid];
1893 		mc->un.mask = 0;
1894 		for (i = 0; i < w->nconnections; i++) {
1895 			if (!azalia_widget_enabled(this, w->connections[i]))
1896 				continue;
1897 			mc->un.mask |= (1 << i);
1898 		}
1899 	}
1900 
1901 	else if (target == MI_TARGET_SENSESET && mc->type == AUDIO_MIXER_SET) {
1902 
1903 		if (nid == this->speaker) {
1904 			mc->un.mask = this->spkr_muters;
1905 		} else {
1906 			DPRINTF(("%s: invalid senseset nid\n", XNAME(this)));
1907 			return EINVAL;
1908 		}
1909 	}
1910 
1911 	else if (target == MI_TARGET_PLAYVOL) {
1912 
1913 		if (mc->type == AUDIO_MIXER_VALUE) {
1914 			mc->un.value.num_channels = 2;
1915 			mc->un.value.level[0] = this->playvols.vol_l;
1916 			mc->un.value.level[1] = this->playvols.vol_r;
1917 
1918 		} else if (mc->type == AUDIO_MIXER_ENUM) {
1919 			mc->un.ord = this->playvols.mute;
1920 
1921 		} else if (mc->type == AUDIO_MIXER_SET) {
1922 			mc->un.mask = this->playvols.cur;
1923 
1924 		} else {
1925 			DPRINTF(("%s: invalid outmaster mixer type\n",
1926 				XNAME(this)));
1927 			return EINVAL;
1928 		}
1929 	}
1930 
1931 	else if (target == MI_TARGET_RECVOL) {
1932 
1933 		if (mc->type == AUDIO_MIXER_VALUE) {
1934 			mc->un.value.num_channels = 2;
1935 			mc->un.value.level[0] = this->recvols.vol_l;
1936 			mc->un.value.level[1] = this->recvols.vol_r;
1937 
1938 		} else if (mc->type == AUDIO_MIXER_ENUM) {
1939 			mc->un.ord = this->recvols.mute;
1940 
1941 		} else if (mc->type == AUDIO_MIXER_SET) {
1942 			mc->un.mask = this->recvols.cur;
1943 
1944 		} else {
1945 			DPRINTF(("%s: invalid inmaster mixer type\n",
1946 				XNAME(this)));
1947 			return EINVAL;
1948 		}
1949 	}
1950 
1951 	else {
1952 		DPRINTF(("%s: internal error in %s: target=%x\n",
1953 		    XNAME(this), __func__, target));
1954 		return -1;
1955 	}
1956 	return 0;
1957 }
1958 
1959 int
1960 azalia_mixer_set(codec_t *this, nid_t nid, int target, const mixer_ctrl_t *mc)
1961 {
1962 	uint32_t result, value;
1963 	int i, err;
1964 
1965 	if (mc->type == AUDIO_MIXER_CLASS) {
1966 		return(0);
1967 	}
1968 
1969 	/* inamp mute */
1970 	else if (IS_MI_TARGET_INAMP(target) && mc->type == AUDIO_MIXER_ENUM) {
1971 		/* set stereo mute separately to keep each gain value */
1972 		err = azalia_comresp(this, nid, CORB_GET_AMPLIFIER_GAIN_MUTE,
1973 		    CORB_GAGM_INPUT | CORB_GAGM_LEFT |
1974 		    MI_TARGET_INAMP(target), &result);
1975 		if (err)
1976 			return err;
1977 		value = CORB_AGM_INPUT | CORB_AGM_LEFT |
1978 		    (target << CORB_AGM_INDEX_SHIFT) |
1979 		    CORB_GAGM_GAIN(result);
1980 		if (mc->un.ord)
1981 			value |= CORB_AGM_MUTE;
1982 		err = azalia_comresp(this, nid, CORB_SET_AMPLIFIER_GAIN_MUTE,
1983 		    value, &result);
1984 		if (err)
1985 			return err;
1986 		if (WIDGET_CHANNELS(&this->w[nid]) == 2) {
1987 			err = azalia_comresp(this, nid,
1988 			    CORB_GET_AMPLIFIER_GAIN_MUTE, CORB_GAGM_INPUT |
1989 			    CORB_GAGM_RIGHT | MI_TARGET_INAMP(target),
1990 			    &result);
1991 			if (err)
1992 				return err;
1993 			value = CORB_AGM_INPUT | CORB_AGM_RIGHT |
1994 			    (target << CORB_AGM_INDEX_SHIFT) |
1995 			    CORB_GAGM_GAIN(result);
1996 			if (mc->un.ord)
1997 				value |= CORB_AGM_MUTE;
1998 			err = azalia_comresp(this, nid,
1999 			    CORB_SET_AMPLIFIER_GAIN_MUTE, value, &result);
2000 			if (err)
2001 				return err;
2002 		}
2003 	}
2004 
2005 	/* inamp gain */
2006 	else if (IS_MI_TARGET_INAMP(target) && mc->type == AUDIO_MIXER_VALUE) {
2007 		if (mc->un.value.num_channels < 1)
2008 			return EINVAL;
2009 		err = azalia_comresp(this, nid, CORB_GET_AMPLIFIER_GAIN_MUTE,
2010 		      CORB_GAGM_INPUT | CORB_GAGM_LEFT |
2011 		      MI_TARGET_INAMP(target), &result);
2012 		if (err)
2013 			return err;
2014 		value = azalia_mixer_to_device_value(this, nid, target,
2015 		    mc->un.value.level[0]);
2016 		value = CORB_AGM_INPUT | CORB_AGM_LEFT |
2017 		    (target << CORB_AGM_INDEX_SHIFT) |
2018 		    (result & CORB_GAGM_MUTE ? CORB_AGM_MUTE : 0) |
2019 		    (value & CORB_AGM_GAIN_MASK);
2020 		err = azalia_comresp(this, nid, CORB_SET_AMPLIFIER_GAIN_MUTE,
2021 		    value, &result);
2022 		if (err)
2023 			return err;
2024 		if (mc->un.value.num_channels >= 2 &&
2025 		    WIDGET_CHANNELS(&this->w[nid]) == 2) {
2026 			err = azalia_comresp(this, nid,
2027 			      CORB_GET_AMPLIFIER_GAIN_MUTE, CORB_GAGM_INPUT |
2028 			      CORB_GAGM_RIGHT | MI_TARGET_INAMP(target),
2029 			      &result);
2030 			if (err)
2031 				return err;
2032 			value = azalia_mixer_to_device_value(this, nid, target,
2033 			    mc->un.value.level[1]);
2034 			value = CORB_AGM_INPUT | CORB_AGM_RIGHT |
2035 			    (target << CORB_AGM_INDEX_SHIFT) |
2036 			    (result & CORB_GAGM_MUTE ? CORB_AGM_MUTE : 0) |
2037 			    (value & CORB_AGM_GAIN_MASK);
2038 			err = azalia_comresp(this, nid,
2039 			    CORB_SET_AMPLIFIER_GAIN_MUTE, value, &result);
2040 			if (err)
2041 				return err;
2042 		}
2043 	}
2044 
2045 	/* outamp mute */
2046 	else if (target == MI_TARGET_OUTAMP && mc->type == AUDIO_MIXER_ENUM) {
2047 		err = azalia_comresp(this, nid, CORB_GET_AMPLIFIER_GAIN_MUTE,
2048 		    CORB_GAGM_OUTPUT | CORB_GAGM_LEFT, &result);
2049 		if (err)
2050 			return err;
2051 		value = CORB_AGM_OUTPUT | CORB_AGM_LEFT | CORB_GAGM_GAIN(result);
2052 		if (mc->un.ord)
2053 			value |= CORB_AGM_MUTE;
2054 		err = azalia_comresp(this, nid, CORB_SET_AMPLIFIER_GAIN_MUTE,
2055 		    value, &result);
2056 		if (err)
2057 			return err;
2058 		if (WIDGET_CHANNELS(&this->w[nid]) == 2) {
2059 			err = azalia_comresp(this, nid,
2060 			    CORB_GET_AMPLIFIER_GAIN_MUTE,
2061 			    CORB_GAGM_OUTPUT | CORB_GAGM_RIGHT, &result);
2062 			if (err)
2063 				return err;
2064 			value = CORB_AGM_OUTPUT | CORB_AGM_RIGHT |
2065 			    CORB_GAGM_GAIN(result);
2066 			if (mc->un.ord)
2067 				value |= CORB_AGM_MUTE;
2068 			err = azalia_comresp(this, nid,
2069 			    CORB_SET_AMPLIFIER_GAIN_MUTE, value, &result);
2070 			if (err)
2071 				return err;
2072 		}
2073 	}
2074 
2075 	/* outamp gain */
2076 	else if (target == MI_TARGET_OUTAMP && mc->type == AUDIO_MIXER_VALUE) {
2077 		if (mc->un.value.num_channels < 1)
2078 			return EINVAL;
2079 		err = azalia_comresp(this, nid, CORB_GET_AMPLIFIER_GAIN_MUTE,
2080 		      CORB_GAGM_OUTPUT | CORB_GAGM_LEFT, &result);
2081 		if (err)
2082 			return err;
2083 		value = azalia_mixer_to_device_value(this, nid, target,
2084 		    mc->un.value.level[0]);
2085 		value = CORB_AGM_OUTPUT | CORB_AGM_LEFT |
2086 		    (result & CORB_GAGM_MUTE ? CORB_AGM_MUTE : 0) |
2087 		    (value & CORB_AGM_GAIN_MASK);
2088 		err = azalia_comresp(this, nid, CORB_SET_AMPLIFIER_GAIN_MUTE,
2089 		    value, &result);
2090 		if (err)
2091 			return err;
2092 		if (mc->un.value.num_channels >= 2 &&
2093 		    WIDGET_CHANNELS(&this->w[nid]) == 2) {
2094 			err = azalia_comresp(this, nid,
2095 			      CORB_GET_AMPLIFIER_GAIN_MUTE, CORB_GAGM_OUTPUT |
2096 			      CORB_GAGM_RIGHT, &result);
2097 			if (err)
2098 				return err;
2099 			value = azalia_mixer_to_device_value(this, nid, target,
2100 			    mc->un.value.level[1]);
2101 			value = CORB_AGM_OUTPUT | CORB_AGM_RIGHT |
2102 			    (result & CORB_GAGM_MUTE ? CORB_AGM_MUTE : 0) |
2103 			    (value & CORB_AGM_GAIN_MASK);
2104 			err = azalia_comresp(this, nid,
2105 			    CORB_SET_AMPLIFIER_GAIN_MUTE, value, &result);
2106 			if (err)
2107 				return err;
2108 		}
2109 	}
2110 
2111 	/* selection */
2112 	else if (target == MI_TARGET_CONNLIST) {
2113 		if (mc->un.ord < 0 ||
2114 		    mc->un.ord >= this->w[nid].nconnections ||
2115 		    !azalia_widget_enabled(this,
2116 		    this->w[nid].connections[mc->un.ord]))
2117 			return EINVAL;
2118 		err = azalia_comresp(this, nid,
2119 		    CORB_SET_CONNECTION_SELECT_CONTROL, mc->un.ord, &result);
2120 		if (err)
2121 			return err;
2122 	}
2123 
2124 	/* pin I/O */
2125 	else if (target == MI_TARGET_PINDIR) {
2126 
2127 		err = azalia_comresp(this, nid,
2128 		    CORB_GET_PIN_WIDGET_CONTROL, 0, &result);
2129 		if (err)
2130 			return err;
2131 
2132 		value = result;
2133 		value &= ~(CORB_PWC_VREF_MASK);
2134 		if (mc->un.ord == 0) {
2135 			value &= ~(CORB_PWC_OUTPUT | CORB_PWC_INPUT);
2136 		} else if (mc->un.ord == 1) {
2137 			value &= ~CORB_PWC_INPUT;
2138 			value |= CORB_PWC_OUTPUT;
2139 			if (this->qrks & AZ_QRK_WID_OVREF50)
2140 				value |= CORB_PWC_VREF_50;
2141 		} else {
2142 			value &= ~CORB_PWC_OUTPUT;
2143 			value |= CORB_PWC_INPUT;
2144 
2145 			if (mc->un.ord == 3)
2146 				value |= CORB_PWC_VREF_GND;
2147 			if (mc->un.ord == 4)
2148 				value |= CORB_PWC_VREF_50;
2149 			if (mc->un.ord == 5)
2150 				value |= CORB_PWC_VREF_80;
2151 			if (mc->un.ord == 6)
2152 				value |= CORB_PWC_VREF_100;
2153 		}
2154 		err = azalia_comresp(this, nid,
2155 		    CORB_SET_PIN_WIDGET_CONTROL, value, &result);
2156 		if (err)
2157 			return err;
2158 
2159 		/* Run the unsolicited response handler for speaker mute
2160 		 * since it depends on pin direction.
2161 		 */
2162 		for (i = 0; i < this->nsense_pins; i++) {
2163 			if (this->sense_pins[i] == nid)
2164 				break;
2165 		}
2166 		if (i < this->nsense_pins) {
2167 			azalia_unsol_event(this, AZ_TAG_SPKR);
2168 		}
2169 	}
2170 
2171 	/* pin headphone-boost */
2172 	else if (target == MI_TARGET_PINBOOST) {
2173 		if (mc->un.ord >= 2)
2174 			return EINVAL;
2175 		err = azalia_comresp(this, nid,
2176 		    CORB_GET_PIN_WIDGET_CONTROL, 0, &result);
2177 		if (err)
2178 			return err;
2179 		if (mc->un.ord == 0) {
2180 			result &= ~CORB_PWC_HEADPHONE;
2181 		} else {
2182 			result |= CORB_PWC_HEADPHONE;
2183 		}
2184 		err = azalia_comresp(this, nid,
2185 		    CORB_SET_PIN_WIDGET_CONTROL, result, &result);
2186 		if (err)
2187 			return err;
2188 	}
2189 
2190 	/* DAC group selection */
2191 	else if (target == MI_TARGET_DAC) {
2192 		if (this->running)
2193 			return EBUSY;
2194 		if (mc->un.ord >= this->dacs.ngroups)
2195 			return EINVAL;
2196 		if (mc->un.ord != this->dacs.cur)
2197 			return azalia_codec_construct_format(this,
2198 			    mc->un.ord, this->adcs.cur);
2199 		else
2200 			return 0;
2201 	}
2202 
2203 	/* ADC selection */
2204 	else if (target == MI_TARGET_ADC) {
2205 		if (this->running)
2206 			return EBUSY;
2207 		if (mc->un.ord >= this->adcs.ngroups)
2208 			return EINVAL;
2209 		if (mc->un.ord != this->adcs.cur)
2210 			return azalia_codec_construct_format(this,
2211 			    this->dacs.cur, mc->un.ord);
2212 		else
2213 			return 0;
2214 	}
2215 
2216 	/* S/PDIF */
2217 	else if (target == MI_TARGET_SPDIF) {
2218 		err = azalia_comresp(this, nid, CORB_GET_DIGITAL_CONTROL,
2219 		    0, &result);
2220 		result &= CORB_DCC_DIGEN | CORB_DCC_NAUDIO;
2221 		result |= mc->un.mask & 0xff & ~CORB_DCC_DIGEN;
2222 		err = azalia_comresp(this, nid, CORB_SET_DIGITAL_CONTROL_L,
2223 		    result, NULL);
2224 		if (err)
2225 			return err;
2226 	} else if (target == MI_TARGET_SPDIF_CC) {
2227 		if (mc->un.value.num_channels != 1)
2228 			return EINVAL;
2229 		if (mc->un.value.level[0] > 127)
2230 			return EINVAL;
2231 		err = azalia_comresp(this, nid, CORB_SET_DIGITAL_CONTROL_H,
2232 		    mc->un.value.level[0], NULL);
2233 		if (err)
2234 			return err;
2235 	}
2236 
2237 	/* EAPD */
2238 	else if (target == MI_TARGET_EAPD) {
2239 		if (mc->un.ord >= 2)
2240 			return EINVAL;
2241 		err = azalia_comresp(this, nid,
2242 		    CORB_GET_EAPD_BTL_ENABLE, 0, &result);
2243 		if (err)
2244 			return err;
2245 		result &= 0xff;
2246 		if (mc->un.ord == 0) {
2247 			result &= ~CORB_EAPD_EAPD;
2248 		} else {
2249 			result |= CORB_EAPD_EAPD;
2250 		}
2251 		err = azalia_comresp(this, nid,
2252 		    CORB_SET_EAPD_BTL_ENABLE, result, &result);
2253 		if (err)
2254 			return err;
2255 	}
2256 
2257 	else if (target == MI_TARGET_PINSENSE) {
2258 		/* do nothing, control is read only */
2259 	}
2260 
2261 	else if (target == MI_TARGET_MUTESET && mc->type == AUDIO_MIXER_SET) {
2262 		const widget_t *w;
2263 
2264 		if (!azalia_widget_enabled(this, nid)) {
2265 			DPRINTF(("%s: invalid muteset nid\n", XNAME(this)));
2266 			return EINVAL;
2267 		}
2268 		w = &this->w[nid];
2269 		for (i = 0; i < w->nconnections; i++) {
2270 			if (!azalia_widget_enabled(this, w->connections[i]))
2271 				continue;
2272 
2273 			/* We have to set stereo mute separately
2274 			 * to keep each gain value.
2275 			 */
2276 			err = azalia_comresp(this, nid,
2277 			    CORB_GET_AMPLIFIER_GAIN_MUTE,
2278 			    CORB_GAGM_INPUT | CORB_GAGM_LEFT |
2279 			    MI_TARGET_INAMP(i), &result);
2280 			if (err)
2281 				return err;
2282 			value = CORB_AGM_INPUT | CORB_AGM_LEFT |
2283 			    (i << CORB_AGM_INDEX_SHIFT) |
2284 			    CORB_GAGM_GAIN(result);
2285 			if ((mc->un.mask & (1 << i)) == 0)
2286 				value |= CORB_AGM_MUTE;
2287 			err = azalia_comresp(this, nid,
2288 			    CORB_SET_AMPLIFIER_GAIN_MUTE, value, &result);
2289 			if (err)
2290 				return err;
2291 
2292 			if (WIDGET_CHANNELS(w) == 2) {
2293 				err = azalia_comresp(this, nid,
2294 				    CORB_GET_AMPLIFIER_GAIN_MUTE,
2295 				    CORB_GAGM_INPUT | CORB_GAGM_RIGHT |
2296 				    MI_TARGET_INAMP(i), &result);
2297 				if (err)
2298 					return err;
2299 				value = CORB_AGM_INPUT | CORB_AGM_RIGHT |
2300 				    (i << CORB_AGM_INDEX_SHIFT) |
2301 				    CORB_GAGM_GAIN(result);
2302 				if ((mc->un.mask & (1 << i)) == 0)
2303 					value |= CORB_AGM_MUTE;
2304 				err = azalia_comresp(this, nid,
2305 				    CORB_SET_AMPLIFIER_GAIN_MUTE,
2306 				    value, &result);
2307 				if (err)
2308 					return err;
2309 			}
2310 		}
2311 	}
2312 
2313 	else if (target == MI_TARGET_MIXERSET && mc->type == AUDIO_MIXER_SET) {
2314 		/* do nothing, control is read only */
2315 	}
2316 
2317 	else if (target == MI_TARGET_SENSESET && mc->type == AUDIO_MIXER_SET) {
2318 
2319 		if (nid == this->speaker) {
2320 			this->spkr_muters = mc->un.mask;
2321 			azalia_unsol_event(this, AZ_TAG_SPKR);
2322 		} else {
2323 			DPRINTF(("%s: invalid senseset nid\n", XNAME(this)));
2324 			return EINVAL;
2325 		}
2326 	}
2327 
2328 	else if (target == MI_TARGET_PLAYVOL) {
2329 
2330 		const widget_t *w;
2331 		mixer_ctrl_t mc2;
2332 
2333 		if (mc->type == AUDIO_MIXER_VALUE) {
2334 			if (mc->un.value.num_channels != 2)
2335 				return EINVAL;
2336 			this->playvols.vol_l = mc->un.value.level[0];
2337 			this->playvols.vol_r = mc->un.value.level[1];
2338 			for (i = 0; i < this->playvols.nslaves; i++) {
2339 				if (!(this->playvols.cur & (1 << i)))
2340 					continue;
2341 				w = &this->w[this->playvols.slaves[i]];
2342 				if (!(COP_AMPCAP_NUMSTEPS(w->outamp_cap)))
2343 					continue;
2344 
2345 				/* don't change volume if muted */
2346 				if (w->outamp_cap & COP_AMPCAP_MUTE) {
2347 					mc2.type = AUDIO_MIXER_ENUM;
2348 					azalia_mixer_get(this, w->nid,
2349 					    MI_TARGET_OUTAMP, &mc2);
2350 					if (mc2.un.ord)
2351 						continue;
2352 				}
2353 				mc2.type = AUDIO_MIXER_VALUE;
2354 				mc2.un.value.num_channels = WIDGET_CHANNELS(w);
2355 				mc2.un.value.level[0] = this->playvols.vol_l;
2356 				mc2.un.value.level[1] = this->playvols.vol_r;
2357 				err = azalia_mixer_set(this, w->nid,
2358 				    MI_TARGET_OUTAMP, &mc2);
2359 				if (err) {
2360 					DPRINTF(("%s: out slave %2.2x vol\n",
2361 					    __func__, w->nid));
2362 					return err;
2363 				}
2364 			}
2365 		} else if (mc->type == AUDIO_MIXER_ENUM) {
2366 			if (mc->un.ord != 0 && mc->un.ord != 1)
2367 				return EINVAL;
2368 			this->playvols.mute = mc->un.ord;
2369 			for (i = 0; i < this->playvols.nslaves; i++) {
2370 				if (!(this->playvols.cur & (1 << i)))
2371 					continue;
2372 				w = &this->w[this->playvols.slaves[i]];
2373 				if (!(w->outamp_cap & COP_AMPCAP_MUTE))
2374 					continue;
2375 				if (this->spkr_muted == 1 &&
2376 				    ((this->spkr_mute_method ==
2377 				    AZ_SPKR_MUTE_SPKR_MUTE &&
2378 				    (w->nid == this->speaker ||
2379 				    w->nid == this->speaker2)) ||
2380 				    (this->spkr_mute_method ==
2381 				    AZ_SPKR_MUTE_DAC_MUTE &&
2382 				    w->nid == this->spkr_dac))) {
2383 					continue;
2384 				}
2385 				mc2.type = AUDIO_MIXER_ENUM;
2386 				mc2.un.ord = this->playvols.mute;
2387 				err = azalia_mixer_set(this, w->nid,
2388 				    MI_TARGET_OUTAMP, &mc2);
2389 				if (err) {
2390 					DPRINTF(("%s: out slave %2.2x mute\n",
2391 					    __func__, w->nid));
2392 					return err;
2393 				}
2394 			}
2395 
2396 		} else if (mc->type == AUDIO_MIXER_SET) {
2397 			this->playvols.cur =
2398 			    (mc->un.mask & this->playvols.mask);
2399 
2400 		} else {
2401 			DPRINTF(("%s: invalid output master mixer type\n",
2402 				XNAME(this)));
2403 			return EINVAL;
2404 		}
2405 	}
2406 
2407 	else if (target == MI_TARGET_RECVOL) {
2408 
2409 		const widget_t *w;
2410 		mixer_ctrl_t mc2;
2411 		uint32_t cap;
2412 		int tgt;
2413 
2414 		if (mc->type == AUDIO_MIXER_VALUE) {
2415 			if (mc->un.value.num_channels != 2)
2416 				return EINVAL;
2417 			this->recvols.vol_l = mc->un.value.level[0];
2418 			this->recvols.vol_r = mc->un.value.level[1];
2419 			for (i = 0; i < this->recvols.nslaves; i++) {
2420 				if (!(this->recvols.cur & (1 << i)))
2421 					continue;
2422 				w = &this->w[this->recvols.slaves[i]];
2423 				tgt = MI_TARGET_OUTAMP;
2424 				cap = w->outamp_cap;
2425 				if (w->type == COP_AWTYPE_AUDIO_INPUT ||
2426 				    w->type == COP_AWTYPE_PIN_COMPLEX) {
2427 					tgt = 0;
2428 					cap = w->inamp_cap;
2429 				}
2430 				if (!(COP_AMPCAP_NUMSTEPS(cap)))
2431 					continue;
2432 				mc2.type = AUDIO_MIXER_VALUE;
2433 				mc2.un.value.num_channels = WIDGET_CHANNELS(w);
2434 				mc2.un.value.level[0] = this->recvols.vol_l;
2435 				mc2.un.value.level[1] = this->recvols.vol_r;
2436 				err = azalia_mixer_set(this, w->nid,
2437 				    tgt, &mc2);
2438 				if (err) {
2439 					DPRINTF(("%s: in slave %2.2x vol\n",
2440 					    __func__, w->nid));
2441 					return err;
2442 				}
2443 			}
2444 		} else if (mc->type == AUDIO_MIXER_ENUM) {
2445 			if (mc->un.ord != 0 && mc->un.ord != 1)
2446 				return EINVAL;
2447 			this->recvols.mute = mc->un.ord;
2448 			for (i = 0; i < this->recvols.nslaves; i++) {
2449 				if (!(this->recvols.cur & (1 << i)))
2450 					continue;
2451 				w = &this->w[this->recvols.slaves[i]];
2452 				tgt = MI_TARGET_OUTAMP;
2453 				cap = w->outamp_cap;
2454 				if (w->type == COP_AWTYPE_AUDIO_INPUT ||
2455 				    w->type == COP_AWTYPE_PIN_COMPLEX) {
2456 					tgt = 0;
2457 					cap = w->inamp_cap;
2458 				}
2459 				if (!(cap & COP_AMPCAP_MUTE))
2460 					continue;
2461 				mc2.type = AUDIO_MIXER_ENUM;
2462 				mc2.un.ord = this->recvols.mute;
2463 				err = azalia_mixer_set(this, w->nid,
2464 				    tgt, &mc2);
2465 				if (err) {
2466 					DPRINTF(("%s: out slave %2.2x mute\n",
2467 					    __func__, w->nid));
2468 					return err;
2469 				}
2470 			}
2471 
2472 		} else if (mc->type == AUDIO_MIXER_SET) {
2473 			this->recvols.cur = (mc->un.mask & this->recvols.mask);
2474 
2475 		} else {
2476 			DPRINTF(("%s: invalid input master mixer type\n",
2477 				XNAME(this)));
2478 			return EINVAL;
2479 		}
2480 	}
2481 
2482 	else {
2483 		DPRINTF(("%s: internal error in %s: target=%x\n",
2484 		    XNAME(this), __func__, target));
2485 		return -1;
2486 	}
2487 	return 0;
2488 }
2489 
2490 u_char
2491 azalia_mixer_from_device_value(const codec_t *this, nid_t nid, int target,
2492     uint32_t dv)
2493 {
2494 	uint32_t steps;
2495 	int max_gain, ctloff;
2496 
2497 	if (IS_MI_TARGET_INAMP(target)) {
2498 		steps = COP_AMPCAP_NUMSTEPS(this->w[nid].inamp_cap);
2499 		ctloff = COP_AMPCAP_CTLOFF(this->w[nid].inamp_cap);
2500 	} else if (target == MI_TARGET_OUTAMP) {
2501 		steps = COP_AMPCAP_NUMSTEPS(this->w[nid].outamp_cap);
2502 		ctloff = COP_AMPCAP_CTLOFF(this->w[nid].outamp_cap);
2503 	} else {
2504 		DPRINTF(("%s: unknown target: %d\n", __func__, target));
2505 		steps = 255;
2506 		ctloff = 0;
2507 	}
2508 	dv -= ctloff;
2509 	if (dv <= 0 || steps == 0)
2510 		return(AUDIO_MIN_GAIN);
2511 	max_gain = AUDIO_MAX_GAIN - AUDIO_MAX_GAIN % steps;
2512 	if (dv >= steps)
2513 		return(max_gain);
2514 	return(dv * max_gain / steps);
2515 }
2516 
2517 uint32_t
2518 azalia_mixer_to_device_value(const codec_t *this, nid_t nid, int target,
2519     u_char uv)
2520 {
2521 	uint32_t steps;
2522 	int max_gain, ctloff;
2523 
2524 	if (IS_MI_TARGET_INAMP(target)) {
2525 		steps = COP_AMPCAP_NUMSTEPS(this->w[nid].inamp_cap);
2526 		ctloff = COP_AMPCAP_CTLOFF(this->w[nid].inamp_cap);
2527 	} else if (target == MI_TARGET_OUTAMP) {
2528 		steps = COP_AMPCAP_NUMSTEPS(this->w[nid].outamp_cap);
2529 		ctloff = COP_AMPCAP_CTLOFF(this->w[nid].outamp_cap);
2530 	} else {
2531 		DPRINTF(("%s: unknown target: %d\n", __func__, target));
2532 		steps = 255;
2533 		ctloff = 0;
2534 	}
2535 	if (uv <= AUDIO_MIN_GAIN || steps == 0)
2536 		return(ctloff);
2537 	max_gain = AUDIO_MAX_GAIN - AUDIO_MAX_GAIN % steps;
2538 	if (uv >= max_gain)
2539 		return(steps + ctloff);
2540 	return(uv * steps / max_gain + ctloff);
2541 }
2542 
2543 int
2544 azalia_gpio_unmute(codec_t *this, int pin)
2545 {
2546 	uint32_t data, mask, dir;
2547 
2548 	azalia_comresp(this, this->audiofunc, CORB_GET_GPIO_DATA, 0, &data);
2549 	azalia_comresp(this, this->audiofunc, CORB_GET_GPIO_ENABLE_MASK, 0, &mask);
2550 	azalia_comresp(this, this->audiofunc, CORB_GET_GPIO_DIRECTION, 0, &dir);
2551 
2552 	data |= 1 << pin;
2553 	mask |= 1 << pin;
2554 	dir |= 1 << pin;
2555 
2556 	azalia_comresp(this, this->audiofunc, CORB_SET_GPIO_ENABLE_MASK, mask, NULL);
2557 	azalia_comresp(this, this->audiofunc, CORB_SET_GPIO_DIRECTION, dir, NULL);
2558 	DELAY(1000);
2559 	azalia_comresp(this, this->audiofunc, CORB_SET_GPIO_DATA, data, NULL);
2560 
2561 	return 0;
2562 }
2563 
2564 void
2565 azalia_ampcap_ov(widget_t *w, int type, int offset, int steps, int size,
2566    int ctloff, int mute)
2567 {
2568 	uint32_t cap;
2569 
2570 	cap = (offset & 0x7f) | ((steps & 0x7f) << 8) |
2571 	    ((size & 0x7f) << 16) | ((ctloff & 0x7f) << 24) |
2572 	    (mute ? COP_AMPCAP_MUTE : 0);
2573 
2574 	if (type == COP_OUTPUT_AMPCAP) {
2575 		w->outamp_cap = cap;
2576 	} else if (type == COP_INPUT_AMPCAP) {
2577 		w->inamp_cap = cap;
2578 	}
2579 }
2580 
2581 void
2582 azalia_pin_config_ov(widget_t *w, int mask, int val)
2583 {
2584 	int bits, offset;
2585 
2586 	switch (mask) {
2587 	case CORB_CD_DEVICE_MASK:
2588 		bits = CORB_CD_DEVICE_BITS;
2589 		offset = CORB_CD_DEVICE_OFFSET;
2590 		break;
2591 	case CORB_CD_PORT_MASK:
2592 		bits = CORB_CD_PORT_BITS;
2593 		offset = CORB_CD_PORT_OFFSET;
2594 		break;
2595 	default:
2596 		return;
2597 	}
2598 	val &= bits;
2599 	w->d.pin.config &= ~(mask);
2600 	w->d.pin.config |= val << offset;
2601 	if (mask == CORB_CD_DEVICE_MASK)
2602 		w->d.pin.device = val;
2603 }
2604 
2605 int
2606 azalia_codec_gpio_quirks(codec_t *this)
2607 {
2608 	if (this->qrks & AZ_QRK_GPIO_POL_0) {
2609 		azalia_comresp(this, this->audiofunc,
2610 		    CORB_SET_GPIO_POLARITY, 0, NULL);
2611 	}
2612 	if (this->qrks & AZ_QRK_GPIO_UNMUTE_0) {
2613 		azalia_gpio_unmute(this, 0);
2614 	}
2615 	if (this->qrks & AZ_QRK_GPIO_UNMUTE_1) {
2616 		azalia_gpio_unmute(this, 1);
2617 	}
2618 	if (this->qrks & AZ_QRK_GPIO_UNMUTE_2) {
2619 		azalia_gpio_unmute(this, 2);
2620 	}
2621 	if (this->qrks & AZ_QRK_GPIO_UNMUTE_3) {
2622 		azalia_gpio_unmute(this, 3);
2623 	}
2624 
2625 	return(0);
2626 }
2627 
2628 int
2629 azalia_codec_widget_quirks(codec_t *this, nid_t nid)
2630 {
2631 	widget_t *w;
2632 
2633 	w = &this->w[nid];
2634 
2635 	if (this->qrks & AZ_QRK_WID_BEEP_1D &&
2636 	    nid == 0x1d && w->enable == 0) {
2637 		azalia_pin_config_ov(w, CORB_CD_DEVICE_MASK, CORB_CD_BEEP);
2638 		azalia_pin_config_ov(w, CORB_CD_PORT_MASK, CORB_CD_FIXED);
2639 		w->widgetcap |= COP_AWCAP_STEREO;
2640 		w->enable = 1;
2641 	}
2642 
2643 	if (this->qrks & AZ_QRK_WID_TPDOCK1 &&
2644 	    nid == 0x19) {
2645 		/* Thinkpad x230/t430 style dock microphone */
2646 		w->d.pin.config = 0x23a11040;
2647 		w->enable = 1;
2648 	}
2649 
2650 	if (this->qrks & AZ_QRK_WID_TPDOCK1 &&
2651 	    nid == 0x1b) {
2652 		/* Thinkpad x230/t430 style dock headphone */
2653 		w->d.pin.config = 0x2121103f;
2654 		w->enable = 1;
2655 	}
2656 
2657 	if (this->qrks & AZ_QRK_WID_TPDOCK2 &&
2658 	    nid == 0x16) {
2659 		/* Thinkpad x240/t440 style dock headphone */
2660 		w->d.pin.config = 0x21211010;
2661 		w->enable = 1;
2662 	}
2663 
2664 	if (this->qrks & AZ_QRK_WID_TPDOCK2 &&
2665 	    nid == 0x19) {
2666 		/* Thinkpad x240/t440 style dock microphone */
2667 		w->d.pin.config = 0x21a11010;
2668 		w->enable = 1;
2669 	}
2670 
2671 	if (this->qrks & AZ_QRK_WID_TPDOCK3 &&
2672 	    nid == 0x1a) {
2673 		/* Thinkpad x220/t420 style dock microphone */
2674 		w->d.pin.config = 0x21a190f0;
2675 		w->enable = 1;
2676 	}
2677 
2678 	if (this->qrks & AZ_QRK_WID_TPDOCK3 &&
2679 	    nid == 0x1c) {
2680 		/* Thinkpad x220/t420 style dock headphone */
2681 		w->d.pin.config = 0x212140ff;
2682 		w->enable = 1;
2683 	}
2684 
2685 	if (this->qrks & AZ_QRK_WID_CDIN_1C &&
2686 	    nid == 0x1c && w->enable == 0 && w->d.pin.device == CORB_CD_CD) {
2687 		azalia_pin_config_ov(w, CORB_CD_PORT_MASK, CORB_CD_FIXED);
2688 		w->widgetcap |= COP_AWCAP_STEREO;
2689 		w->enable = 1;
2690 	}
2691 
2692 	if ((this->qrks & AZ_QRK_WID_AD1981_OAMP) &&
2693 	    ((nid == 0x05) || (nid == 0x06) || (nid == 0x07) ||
2694 	    (nid == 0x09) || (nid == 0x18))) {
2695 		azalia_ampcap_ov(w, COP_OUTPUT_AMPCAP, 31, 33, 6, 30, 1);
2696 	}
2697 
2698 	if ((this->qrks & AZ_QRK_WID_CLOSE_PCBEEP) && (nid == 0x20))  {
2699 		/* Close PC beep passthrough to avoid headphone noise */
2700 		azalia_comresp(this, nid, CORB_SET_COEFFICIENT_INDEX, 0x36,
2701 		    NULL);
2702 		azalia_comresp(this, nid, CORB_SET_PROCESSING_COEFFICIENT,
2703 		    0x57d7, NULL);
2704 	}
2705 
2706 	return(0);
2707 }
2708 
2709 /* Magic init sequence to make the right speaker work (reverse-engineered) */
2710 void
2711 azalia_codec_init_dolby_atmos(codec_t *this)
2712 {
2713 	static uint16_t atmos_init[] = {
2714 		0x06, 0x73e, 0x00, 0x06, 0x73e, 0x80,
2715 		0x20, 0x500, 0x26, 0x20, 0x4f0, 0x00,
2716 		0x20, 0x500, 0x22, 0x20, 0x400, 0x31,
2717 		0x20, 0x500, 0x23, 0x20, 0x400, 0x0b,
2718 		0x20, 0x500, 0x25, 0x20, 0x400, 0x00,
2719 		0x20, 0x500, 0x26, 0x20, 0x4b0, 0x10,
2720 	};
2721 	static struct {
2722 		unsigned char v23;
2723 		unsigned char v25;
2724 	} atmos_v23_v25[] = {
2725 		{ 0x0c, 0x00 }, { 0x0d, 0x00 }, { 0x0e, 0x00 }, { 0x0f, 0x00 },
2726 		{ 0x10, 0x00 }, { 0x1a, 0x40 }, { 0x1b, 0x82 }, { 0x1c, 0x00 },
2727 		{ 0x1d, 0x00 }, { 0x1e, 0x00 }, { 0x1f, 0x00 }, { 0x20, 0xc2 },
2728 		{ 0x21, 0xc8 }, { 0x22, 0x26 }, { 0x23, 0x24 }, { 0x27, 0xff },
2729 		{ 0x28, 0xff }, { 0x29, 0xff }, { 0x2a, 0x8f }, { 0x2b, 0x02 },
2730 		{ 0x2c, 0x48 }, { 0x2d, 0x34 }, { 0x2e, 0x00 }, { 0x2f, 0x00 },
2731 		{ 0x30, 0x00 }, { 0x31, 0x00 }, { 0x32, 0x00 }, { 0x33, 0x00 },
2732 		{ 0x34, 0x00 }, { 0x35, 0x01 }, { 0x36, 0x93 }, { 0x37, 0x0c },
2733 		{ 0x38, 0x00 }, { 0x39, 0x00 }, { 0x3a, 0xf8 }, { 0x38, 0x80 },
2734 	};
2735 	int i;
2736 
2737 	for (i = 0; i < nitems(atmos_init) / 3; i++) {
2738 		if (azalia_comresp(this, atmos_init[i * 3],
2739 		    atmos_init[(i * 3) + 1], atmos_init[(i * 3) + 2], NULL))
2740 			return;
2741 	}
2742 
2743 	for (i = 0; i < nitems(atmos_v23_v25); i++) {
2744 		if (azalia_comresp(this, 0x06, 0x73e, 0x00, NULL))
2745 			return;
2746 		if (azalia_comresp(this, 0x20, 0x500, 0x26, NULL))
2747 			return;
2748 		if (azalia_comresp(this, 0x20, 0x4b0, 0x00, NULL))
2749 			return;
2750 		if (i == 0) {
2751 			if (azalia_comresp(this, 0x21, 0xf09, 0x00, NULL))
2752 				return;
2753 		}
2754 		if (i != 20) {
2755 			if (azalia_comresp(this, 0x06, 0x73e, 0x80, NULL))
2756 				return;
2757 		}
2758 
2759 		if (azalia_comresp(this, 0x20, 0x500, 0x26, NULL))
2760 			return;
2761 		if (azalia_comresp(this, 0x20, 0x4f0, 0x00, NULL))
2762 			return;
2763 		if (azalia_comresp(this, 0x20, 0x500, 0x23, NULL))
2764 			return;
2765 
2766 		if (azalia_comresp(this, 0x20, 0x400,
2767 		    atmos_v23_v25[i].v23, NULL))
2768 			return;
2769 
2770 		if (atmos_v23_v25[i].v23 != 0x1e) {
2771 			if (azalia_comresp(this, 0x20, 0x500, 0x25, NULL))
2772 				return;
2773 			if (azalia_comresp(this, 0x20, 0x400,
2774 			    atmos_v23_v25[i].v25, NULL))
2775 				return;
2776 		}
2777 
2778 		if (azalia_comresp(this, 0x20, 0x500, 0x26, NULL))
2779 			return;
2780 		if (azalia_comresp(this, 0x20, 0x4b0, 0x10, NULL))
2781 			return;
2782 	}
2783 }
2784