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