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