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