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