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