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