Expand PMF_FN_* macros.
[netbsd-mini2440.git] / sys / dev / pci / azalia_codec.c
blobf200a8acd6eb61acc6ede552f7122a2329c7ff10
1 /* $NetBSD: azalia_codec.c,v 1.76 2009/02/28 17:12:13 jmcneill Exp $ */
3 /*-
4 * Copyright (c) 2005 The NetBSD Foundation, Inc.
5 * All rights reserved.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by TAMURA Kent
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: azalia_codec.c,v 1.76 2009/02/28 17:12:13 jmcneill Exp $");
35 #include <sys/param.h>
36 #include <sys/device.h>
37 #include <sys/malloc.h>
38 #include <sys/null.h>
39 #include <sys/systm.h>
40 #include <dev/pci/azalia.h>
42 #ifdef MAX_VOLUME_255
43 # define MIXER_DELTA(n) (AUDIO_MAX_GAIN / (n))
44 #else
45 # define MIXER_DELTA(n) (1)
46 #endif
47 #define AZ_CLASS_INPUT 0
48 #define AZ_CLASS_OUTPUT 1
49 #define AZ_CLASS_RECORD 2
50 #define AZ_CLASS_PLAYBACK 3
51 #define AZ_CLASS_MIXER 4
52 #define AzaliaCplayback "playback"
53 #define AzaliaCmixer "mix"
54 #define AzaliaNfront "front"
55 #define AzaliaNclfe "clfe"
56 #define AzaliaNside "side"
57 #define AzaliaNdigital "spdif"
58 #define ENUM_OFFON .un.e={2, {{{AudioNoff, 0}, 0}, {{AudioNon, 0}, 1}}}
59 #define ENUM_IO .un.e={2, {{{AudioNinput, 0}, 0}, {{AudioNoutput, 0}, 1}}}
60 #define ENUM_V .un.v={{ "", 0 }, 0, 0 }
61 #define AZ_MIXER_CLASSES \
62 {{AZ_CLASS_INPUT, {AudioCinputs, 0}, AUDIO_MIXER_CLASS, \
63 AZ_CLASS_INPUT, 0, 0, ENUM_V }, 0, 0, }, \
64 {{AZ_CLASS_OUTPUT, {AudioCoutputs, 0}, AUDIO_MIXER_CLASS, \
65 AZ_CLASS_OUTPUT, 0, 0, ENUM_V }, 0, 0, }, \
66 {{AZ_CLASS_RECORD, {AudioCrecord, 0}, AUDIO_MIXER_CLASS, \
67 AZ_CLASS_RECORD, 0, 0, ENUM_V }, 0, 0, }, \
68 {{AZ_CLASS_PLAYBACK, {AzaliaCplayback, 0}, AUDIO_MIXER_CLASS, \
69 AZ_CLASS_PLAYBACK, 0, 0, ENUM_V }, 0, 0, }, \
70 {{AZ_CLASS_MIXER, {AzaliaCmixer, 0}, AUDIO_MIXER_CLASS, \
71 AZ_CLASS_MIXER, 0, 0, ENUM_V }, 0, 0, }
72 #define AZ_MIXER_SPDIF(cl, nid) \
73 {{0, {AzaliaNdigital, 0}, AUDIO_MIXER_SET, cl, 0, 0, \
74 .un.s={6, {{{"v", 0}, CORB_DCC_V}, {{"vcfg", 0}, CORB_DCC_VCFG}, \
75 {{"pre", 0}, CORB_DCC_PRE}, {{"copy", 0}, CORB_DCC_COPY}, \
76 {{"pro", 0}, CORB_DCC_PRO}, {{"l", 0}, CORB_DCC_L}}}}, \
77 nid, MI_TARGET_SPDIF}, \
78 {{0, {AzaliaNdigital".cc", 0}, AUDIO_MIXER_VALUE, cl, 0, 0, \
79 .un.v={{"", 0}, 1, 1}}, nid, MI_TARGET_SPDIF_CC}
82 static int generic_codec_init_dacgroup(codec_t *);
83 static int generic_codec_add_dacgroup(codec_t *, int, uint32_t);
84 static int generic_codec_find_pin(const codec_t *, int, int, uint32_t);
85 static int generic_codec_find_dac(const codec_t *, int, int);
87 static int generic_mixer_init(codec_t *);
88 static int generic_mixer_autoinit(codec_t *);
89 static int generic_mixer_init_widget(const codec_t *, widget_t *, nid_t);
91 static int generic_mixer_fix_indexes(codec_t *);
92 static int generic_mixer_default(codec_t *);
93 static int generic_mixer_pin_sense(codec_t *);
94 static int generic_mixer_widget_name(const codec_t *, widget_t *);
95 static int generic_mixer_create_virtual(codec_t *);
96 static int generic_mixer_delete(codec_t *);
97 static void generic_mixer_cat_names
98 (char *, size_t, const char *, const char *, const char *);
99 static int generic_mixer_ensure_capacity(codec_t *, size_t);
100 static int generic_mixer_get(const codec_t *, nid_t, int, mixer_ctrl_t *);
101 static int generic_mixer_set(codec_t *, nid_t, int, const mixer_ctrl_t *);
102 static int generic_mixer_pinctrl(codec_t *, nid_t, uint32_t);
103 static u_char generic_mixer_from_device_value
104 (const codec_t *, nid_t, int, uint32_t );
105 static uint32_t generic_mixer_to_device_value
106 (const codec_t *, nid_t, int, u_char);
107 static uint32_t generic_mixer_max(const codec_t *, nid_t, int);
108 static bool generic_mixer_validate_value
109 (const codec_t *, nid_t, int, u_char);
110 static int generic_set_port(codec_t *, mixer_ctrl_t *);
111 static int generic_get_port(codec_t *, mixer_ctrl_t *);
113 static int alc260_init_dacgroup(codec_t *);
114 static int alc260_mixer_init(codec_t *);
115 static int alc260_set_port(codec_t *, mixer_ctrl_t *);
116 static int alc260_get_port(codec_t *, mixer_ctrl_t *);
117 static int alc260_unsol_event(codec_t *, int);
118 static int alc262_init_widget(const codec_t *, widget_t *, nid_t);
119 static int alc268_init_dacgroup(codec_t *);
120 static int alc662_init_dacgroup(codec_t *);
121 static int alc861_init_dacgroup(codec_t *);
122 static int alc861vdgr_init_dacgroup(codec_t *);
123 static int alc880_init_dacgroup(codec_t *);
124 static int alc880_mixer_init(codec_t *);
125 static int alc882_init_dacgroup(codec_t *);
126 static int alc882_mixer_init(codec_t *);
127 static int alc882_set_port(codec_t *, mixer_ctrl_t *);
128 static int alc882_get_port(codec_t *, mixer_ctrl_t *);
129 static int alc883_init_dacgroup(codec_t *);
130 static int alc883_mixer_init(codec_t *);
131 static int alc885_init_dacgroup(codec_t *);
132 static int alc888_init_dacgroup(codec_t *);
133 static int alc888_init_widget(const codec_t *, widget_t *, nid_t);
134 static int alc888_mixer_init(codec_t *);
135 static int ad1981hd_init_widget(const codec_t *, widget_t *, nid_t);
136 static int ad1981hd_mixer_init(codec_t *);
137 static int ad1983_mixer_init(codec_t *);
138 static int ad1983_unsol_event(codec_t *, int);
139 static int ad1984_init_dacgroup(codec_t *);
140 static int ad1984_init_widget(const codec_t *, widget_t *, nid_t);
141 static int ad1984_mixer_init(codec_t *);
142 static int ad1984_unsol_event(codec_t *, int);
143 static int ad1986a_init_dacgroup(codec_t *);
144 static int ad1986a_mixer_init(codec_t *);
145 static int ad1988_init_dacgroup(codec_t *);
146 static int cmi9880_init_dacgroup(codec_t *);
147 static int cmi9880_mixer_init(codec_t *);
148 static int stac9221_init_dacgroup(codec_t *);
149 static int stac9221_mixer_init(codec_t *);
150 static int stac9221_gpio_unmute(codec_t *, int);
151 static int stac9200_mixer_init(codec_t *);
152 static int stac9200_unsol_event(codec_t *, int);
153 static int atihdmi_init_dacgroup(codec_t *);
157 azalia_codec_init_vtbl(codec_t *this)
159 size_t extra_size;
162 * We can refer this->vid and this->subid.
164 DPRINTF(("%s: vid=%08x subid=%08x\n", __func__, this->vid, this->subid));
165 extra_size = 0;
166 this->name = NULL;
167 this->init_dacgroup = generic_codec_init_dacgroup;
168 this->mixer_init = generic_mixer_init;
169 this->mixer_delete = generic_mixer_delete;
170 this->set_port = generic_set_port;
171 this->get_port = generic_get_port;
172 this->unsol_event = NULL;
173 switch (this->vid) {
174 case 0x10027919:
175 case 0x1002793c:
176 this->name = "ATI RS600 HDMI";
177 this->init_dacgroup = atihdmi_init_dacgroup;
178 break;
179 case 0x1002791a:
180 this->name = "ATI RS690/780 HDMI";
181 this->init_dacgroup = atihdmi_init_dacgroup;
182 break;
183 case 0x1002aa01:
184 this->name = "ATI R600 HDMI";
185 this->init_dacgroup = atihdmi_init_dacgroup;
186 break;
187 case 0x10ec0260:
188 this->name = "Realtek ALC260";
189 this->mixer_init = alc260_mixer_init;
190 this->init_dacgroup = alc260_init_dacgroup;
191 this->set_port = alc260_set_port;
192 this->unsol_event = alc260_unsol_event;
193 extra_size = 1;
194 break;
195 case 0x10ec0262:
196 this->name = "Realtek ALC262";
197 this->init_widget = alc262_init_widget;
198 break;
199 case 0x10ec0268:
200 this->name = "Realtek ALC268";
201 this->init_dacgroup = alc268_init_dacgroup;
202 this->mixer_init = generic_mixer_autoinit;
203 this->init_widget = generic_mixer_init_widget;
204 break;
205 case 0x10ec0269:
206 this->name = "Realtek ALC269";
207 this->mixer_init = generic_mixer_autoinit;
208 this->init_widget = generic_mixer_init_widget;
209 break;
210 case 0x10ec0662:
211 this->name = "Realtek ALC662-GR";
212 this->init_dacgroup = alc662_init_dacgroup;
213 this->mixer_init = generic_mixer_autoinit;
214 this->init_widget = generic_mixer_init_widget;
215 break;
216 case 0x10ec0663:
217 this->name = "Realtek ALC663";
218 this->init_dacgroup = alc662_init_dacgroup;
219 this->mixer_init = generic_mixer_autoinit;
220 this->init_widget = generic_mixer_init_widget;
221 break;
222 case 0x10ec0861:
223 this->name = "Realtek ALC861";
224 this->init_dacgroup = alc861_init_dacgroup;
225 break;
226 case 0x10ec0862:
227 this->name = "Realtek ALC861-VD-GR";
228 this->init_dacgroup = alc861vdgr_init_dacgroup;
229 break;
230 case 0x10ec0880:
231 this->name = "Realtek ALC880";
232 this->init_dacgroup = alc880_init_dacgroup;
233 this->mixer_init = alc880_mixer_init;
234 break;
235 case 0x10ec0882:
236 this->name = "Realtek ALC882";
237 this->init_dacgroup = alc882_init_dacgroup;
238 this->mixer_init = alc882_mixer_init;
239 this->get_port = alc882_get_port;
240 this->set_port = alc882_set_port;
241 break;
242 case 0x10ec0883:
243 /* ftp://209.216.61.149/pc/audio/ALC883_DataSheet_1.3.pdf */
244 this->name = "Realtek ALC883";
245 this->init_dacgroup = alc883_init_dacgroup;
246 this->mixer_init = alc883_mixer_init;
247 this->get_port = alc882_get_port;
248 this->set_port = alc882_set_port;
249 break;
250 case 0x10ec0885:
251 this->name = "Realtek ALC885";
252 this->init_dacgroup = alc885_init_dacgroup;
253 this->mixer_init = generic_mixer_autoinit;
254 this->init_widget = generic_mixer_init_widget;
255 break;
256 case 0x10ec0888:
257 this->name = "Realtek ALC888";
258 this->init_dacgroup = alc888_init_dacgroup;
259 this->init_widget = alc888_init_widget;
260 this->mixer_init = alc888_mixer_init;
261 break;
262 case 0x11d41981:
263 /* http://www.analog.com/en/prod/0,2877,AD1981HD,00.html */
264 this->name = "Analog Devices AD1981HD";
265 this->init_widget = ad1981hd_init_widget;
266 this->mixer_init = ad1981hd_mixer_init;
267 break;
268 case 0x11d41983:
269 /* http://www.analog.com/en/prod/0,2877,AD1983,00.html */
270 this->name = "Analog Devices AD1983";
271 this->mixer_init = ad1983_mixer_init;
272 this->unsol_event = ad1983_unsol_event;
273 break;
274 case 0x11d41984:
275 /* http://www.analog.com/en/prod/0,2877,AD1984,00.html */
276 this->name = "Analog Devices AD1984";
277 this->init_dacgroup = ad1984_init_dacgroup;
278 this->init_widget = ad1984_init_widget;
279 this->mixer_init = ad1984_mixer_init;
280 this->unsol_event = ad1984_unsol_event;
281 break;
282 case 0x11d4194a:
283 /* http://www.analog.com/static/imported-files/data_sheets/AD1984A.pdf */
284 this->name = "Analog Devices AD1984A";
285 this->init_dacgroup = ad1984_init_dacgroup;
286 this->init_widget = ad1984_init_widget;
287 this->mixer_init = ad1984_mixer_init;
288 this->unsol_event = ad1984_unsol_event;
289 break;
290 case 0x11d41986:
291 /* http://www.analog.com/en/prod/0,2877,AD1986A,00.html */
292 this->name = "Analog Devices AD1986A";
293 this->init_dacgroup = ad1986a_init_dacgroup;
294 this->mixer_init = ad1986a_mixer_init;
295 break;
296 case 0x11d41988:
297 /* http://www.analog.com/en/prod/0,2877,AD1988A,00.html */
298 this->name = "Analog Devices AD1988A";
299 this->init_dacgroup = ad1988_init_dacgroup;
300 break;
301 case 0x11d4198b:
302 /* http://www.analog.com/en/prod/0,2877,AD1988B,00.html */
303 this->name = "Analog Devices AD1988B";
304 this->init_dacgroup = ad1988_init_dacgroup;
305 break;
306 case 0x434d4980:
307 this->name = "CMedia CMI9880";
308 this->init_dacgroup = cmi9880_init_dacgroup;
309 this->mixer_init = cmi9880_mixer_init;
310 break;
311 case 0x83847612:
312 /* http://www.idt.com/products/getDoc.cfm?docID=17122893 */
313 this->name = "Sigmatel STAC9230X";
314 break;
315 case 0x83847613:
316 /* http://www.idt.com/products/getDoc.cfm?docID=17122893 */
317 this->name = "Sigmatel STAC9230D";
318 break;
319 case 0x83847614:
320 /* http://www.idt.com/products/getDoc.cfm?docID=17122893 */
321 this->name = "Sigmatel STAC9229X";
322 break;
323 case 0x83847615:
324 /* http://www.idt.com/products/getDoc.cfm?docID=17122893 */
325 this->name = "Sigmatel STAC9229D";
326 break;
327 case 0x83847616:
328 /* http://www.idt.com/products/getDoc.cfm?docID=17122893 */
329 this->name = "Sigmatel STAC9228X";
330 break;
331 case 0x83847617:
332 /* http://www.idt.com/products/getDoc.cfm?docID=17122893 */
333 this->name = "Sigmatel STAC9228D";
334 break;
335 case 0x83847618:
336 /* http://www.idt.com/products/getDoc.cfm?docID=17122893 */
337 this->name = "Sigmatel STAC9227X";
338 break;
339 case 0x83847619:
340 /* http://www.idt.com/products/getDoc.cfm?docID=17122893 */
341 this->name = "Sigmatel STAC9227D";
342 break;
343 case 0x83847680:
344 this->name = "Sigmatel STAC9221";
345 this->init_dacgroup = stac9221_init_dacgroup;
346 this->mixer_init = stac9221_mixer_init;
347 break;
348 case 0x83847683:
349 this->name = "Sigmatel STAC9221D";
350 this->init_dacgroup = stac9221_init_dacgroup;
351 break;
352 case 0x83847690:
353 /* http://www.idt.com/products/getDoc.cfm?docID=17812077 */
354 this->name = "Sigmatel STAC9200";
355 this->mixer_init = stac9200_mixer_init;
356 this->unsol_event = stac9200_unsol_event;
357 break;
358 case 0x83847691:
359 /* http://www.idt.com/products/getDoc.cfm?docID=17812077 */
360 this->name = "Sigmatel STAC9200D";
361 this->mixer_init = stac9200_mixer_init;
362 this->unsol_event = stac9200_unsol_event;
363 break;
365 if (extra_size > 0) {
366 this->extra = malloc(sizeof(uint32_t) * extra_size,
367 M_DEVBUF, M_ZERO | M_NOWAIT);
368 if (this->extra == NULL) {
369 aprint_error_dev(this->dev, "Not enough memory\n");
370 return ENOMEM;
373 return 0;
376 /* ----------------------------------------------------------------
377 * functions for generic codecs
378 * ---------------------------------------------------------------- */
380 static int
381 generic_codec_init_dacgroup(codec_t *this)
383 int i, j, assoc, group;
386 * grouping DACs
387 * [0] the lowest assoc DACs
388 * [1] the lowest assoc digital outputs
389 * [2] the 2nd assoc DACs
392 this->dacs.ngroups = 0;
393 for (assoc = 0; assoc < CORB_CD_ASSOCIATION_MAX; assoc++) {
394 generic_codec_add_dacgroup(this, assoc, 0);
395 generic_codec_add_dacgroup(this, assoc, COP_AWCAP_DIGITAL);
398 /* find DACs which do not connect with any pins by default */
399 DPRINTF(("%s: find non-connected DACs\n", __func__));
400 FOR_EACH_WIDGET(this, i) {
401 bool found;
403 if (this->w[i].type != COP_AWTYPE_AUDIO_OUTPUT)
404 continue;
405 found = FALSE;
406 for (group = 0; group < this->dacs.ngroups; group++) {
407 for (j = 0; j < this->dacs.groups[group].nconv; j++) {
408 if (i == this->dacs.groups[group].conv[j]) {
409 found = TRUE;
410 group = this->dacs.ngroups;
411 break;
415 if (found)
416 continue;
417 if (this->dacs.ngroups >= 32)
418 break;
419 this->dacs.groups[this->dacs.ngroups].nconv = 1;
420 this->dacs.groups[this->dacs.ngroups].conv[0] = i;
421 this->dacs.ngroups++;
423 this->dacs.cur = 0;
425 /* enumerate ADCs */
426 this->adcs.ngroups = 0;
427 FOR_EACH_WIDGET(this, i) {
428 if (this->w[i].type != COP_AWTYPE_AUDIO_INPUT)
429 continue;
430 this->adcs.groups[this->adcs.ngroups].nconv = 1;
431 this->adcs.groups[this->adcs.ngroups].conv[0] = i;
432 this->adcs.ngroups++;
433 if (this->adcs.ngroups >= 32)
434 break;
436 this->adcs.cur = 0;
437 return 0;
440 static int
441 generic_codec_add_dacgroup(codec_t *this, int assoc, uint32_t digital)
443 int i, j, n, dac, seq;
445 n = 0;
446 for (seq = 0 ; seq < CORB_CD_SEQUENCE_MAX; seq++) {
447 i = generic_codec_find_pin(this, assoc, seq, digital);
448 if (i < 0)
449 continue;
450 dac = generic_codec_find_dac(this, i, 0);
451 if (dac < 0)
452 continue;
453 /* duplication check */
454 for (j = 0; j < n; j++) {
455 if (this->dacs.groups[this->dacs.ngroups].conv[j] == dac)
456 break;
458 if (j < n) /* this group already has <dac> */
459 continue;
460 this->dacs.groups[this->dacs.ngroups].conv[n++] = dac;
461 DPRINTF(("%s: assoc=%d seq=%d ==> g=%d n=%d\n",
462 __func__, assoc, seq, this->dacs.ngroups, n-1));
464 if (n <= 0) /* no such DACs */
465 return 0;
466 this->dacs.groups[this->dacs.ngroups].nconv = n;
468 /* check if the same combination is already registered */
469 for (i = 0; i < this->dacs.ngroups; i++) {
470 if (n != this->dacs.groups[i].nconv)
471 continue;
472 for (j = 0; j < n; j++) {
473 if (this->dacs.groups[this->dacs.ngroups].conv[j] !=
474 this->dacs.groups[i].conv[j])
475 break;
477 if (j >= n) /* matched */
478 return 0;
480 /* found no equivalent group */
481 this->dacs.ngroups++;
482 return 0;
485 static int
486 generic_codec_find_pin(const codec_t *this, int assoc, int seq, uint32_t digital)
488 int i;
490 FOR_EACH_WIDGET(this, i) {
491 if (this->w[i].type != COP_AWTYPE_PIN_COMPLEX)
492 continue;
493 if ((this->w[i].d.pin.cap & COP_PINCAP_OUTPUT) == 0)
494 continue;
495 if ((this->w[i].widgetcap & COP_AWCAP_DIGITAL) != digital)
496 continue;
497 if (this->w[i].d.pin.association != assoc)
498 continue;
499 if (this->w[i].d.pin.sequence == seq) {
500 return i;
503 return -1;
506 static int
507 generic_codec_find_dac(const codec_t *this, int index, int depth)
509 const widget_t *w;
510 int i, j, ret;
512 w = &this->w[index];
513 if (w->type == COP_AWTYPE_AUDIO_OUTPUT) {
514 DPRINTF(("%s: DAC: nid=0x%x index=%d\n",
515 __func__, w->nid, index));
516 return index;
518 if (++depth > 50) {
519 return -1;
521 if (w->selected >= 0) {
522 j = w->connections[w->selected];
523 if (VALID_WIDGET_NID(j, this)) {
524 ret = generic_codec_find_dac(this, j, depth);
525 if (ret >= 0) {
526 DPRINTF(("%s: DAC path: nid=0x%x index=%d\n",
527 __func__, w->nid, index));
528 return ret;
532 for (i = 0; i < w->nconnections; i++) {
533 j = w->connections[i];
534 if (!VALID_WIDGET_NID(j, this))
535 continue;
536 ret = generic_codec_find_dac(this, j, depth);
537 if (ret >= 0) {
538 DPRINTF(("%s: DAC path: nid=0x%x index=%d\n",
539 __func__, w->nid, index));
540 return ret;
543 return -1;
546 /* ----------------------------------------------------------------
547 * Generic mixer functions
548 * ---------------------------------------------------------------- */
550 #define GMIDPRINTF(x) do {} while (0/*CONSTCOND*/)
552 static int
553 generic_mixer_init(codec_t *this)
556 * pin "<color>%2.2x"
557 * audio output "dac%2.2x"
558 * audio input "adc%2.2x"
559 * mixer "mixer%2.2x"
560 * selector "sel%2.2x"
562 mixer_item_t *m;
563 int err, i, j, k;
565 this->maxmixers = 10;
566 this->nmixers = 0;
567 this->mixers = malloc(sizeof(mixer_item_t) * this->maxmixers,
568 M_DEVBUF, M_ZERO | M_NOWAIT);
569 if (this->mixers == NULL) {
570 aprint_error_dev(this->dev, "out of memory in %s\n", __func__);
571 return ENOMEM;
574 /* register classes */
575 DPRINTF(("%s: register classes\n", __func__));
576 m = &this->mixers[AZ_CLASS_INPUT];
577 m->devinfo.index = AZ_CLASS_INPUT;
578 strlcpy(m->devinfo.label.name, AudioCinputs,
579 sizeof(m->devinfo.label.name));
580 m->devinfo.type = AUDIO_MIXER_CLASS;
581 m->devinfo.mixer_class = AZ_CLASS_INPUT;
582 m->devinfo.next = AUDIO_MIXER_LAST;
583 m->devinfo.prev = AUDIO_MIXER_LAST;
584 m->nid = 0;
586 m = &this->mixers[AZ_CLASS_OUTPUT];
587 m->devinfo.index = AZ_CLASS_OUTPUT;
588 strlcpy(m->devinfo.label.name, AudioCoutputs,
589 sizeof(m->devinfo.label.name));
590 m->devinfo.type = AUDIO_MIXER_CLASS;
591 m->devinfo.mixer_class = AZ_CLASS_OUTPUT;
592 m->devinfo.next = AUDIO_MIXER_LAST;
593 m->devinfo.prev = AUDIO_MIXER_LAST;
594 m->nid = 0;
596 m = &this->mixers[AZ_CLASS_RECORD];
597 m->devinfo.index = AZ_CLASS_RECORD;
598 strlcpy(m->devinfo.label.name, AudioCrecord,
599 sizeof(m->devinfo.label.name));
600 m->devinfo.type = AUDIO_MIXER_CLASS;
601 m->devinfo.mixer_class = AZ_CLASS_RECORD;
602 m->devinfo.next = AUDIO_MIXER_LAST;
603 m->devinfo.prev = AUDIO_MIXER_LAST;
604 m->nid = 0;
606 m = &this->mixers[AZ_CLASS_PLAYBACK];
607 m->devinfo.index = AZ_CLASS_PLAYBACK;
608 strlcpy(m->devinfo.label.name, AzaliaCplayback,
609 sizeof(m->devinfo.label.name));
610 m->devinfo.type = AUDIO_MIXER_CLASS;
611 m->devinfo.mixer_class = AZ_CLASS_PLAYBACK;
612 m->devinfo.next = AUDIO_MIXER_LAST;
613 m->devinfo.prev = AUDIO_MIXER_LAST;
614 m->nid = 0;
616 m = &this->mixers[AZ_CLASS_MIXER];
617 m->devinfo.index = AZ_CLASS_MIXER;
618 strlcpy(m->devinfo.label.name, AzaliaCmixer,
619 sizeof(m->devinfo.label.name));
620 m->devinfo.type = AUDIO_MIXER_CLASS;
621 m->devinfo.mixer_class = AZ_CLASS_MIXER;
622 m->devinfo.next = AUDIO_MIXER_LAST;
623 m->devinfo.prev = AUDIO_MIXER_LAST;
624 m->nid = 0;
626 this->nmixers = AZ_CLASS_MIXER + 1;
628 #define MIXER_REG_PROLOG \
629 mixer_devinfo_t *d; \
630 err = generic_mixer_ensure_capacity(this, this->nmixers + 1); \
631 if (err) \
632 return err; \
633 m = &this->mixers[this->nmixers]; \
634 d = &m->devinfo; \
635 m->nid = i
637 FOR_EACH_WIDGET(this, i) {
638 const widget_t *w;
640 w = &this->w[i];
642 /* skip unconnected pins */
643 if (w->type == COP_AWTYPE_PIN_COMPLEX) {
644 uint8_t conn =
645 (w->d.pin.config & CORB_CD_PORT_MASK) >> 30;
646 if (conn == 1) /* no physical connection */
647 continue;
650 /* selector */
651 if (w->type != COP_AWTYPE_AUDIO_MIXER &&
652 w->type != COP_AWTYPE_POWER && w->nconnections >= 2) {
653 MIXER_REG_PROLOG;
654 GMIDPRINTF(("%s: selector %s\n", __func__, w->name));
655 snprintf(d->label.name, sizeof(d->label.name),
656 "%s.source", w->name);
657 d->type = AUDIO_MIXER_ENUM;
658 if (w->type == COP_AWTYPE_AUDIO_MIXER)
659 d->mixer_class = AZ_CLASS_RECORD;
660 else if (w->type == COP_AWTYPE_AUDIO_SELECTOR)
661 d->mixer_class = AZ_CLASS_INPUT;
662 else
663 d->mixer_class = AZ_CLASS_OUTPUT;
664 m->target = MI_TARGET_CONNLIST;
665 for (j = 0, k = 0; j < w->nconnections && k < 32; j++) {
666 uint8_t conn;
668 if (!VALID_WIDGET_NID(w->connections[j], this))
669 continue;
670 /* skip unconnected pins */
671 PIN_STATUS(&this->w[w->connections[j]],
672 conn);
673 if (conn == 1)
674 continue;
675 GMIDPRINTF(("%s: selector %d=%s\n", __func__, j,
676 this->w[w->connections[j]].name));
677 d->un.e.member[k].ord = j;
678 strlcpy(d->un.e.member[k].label.name,
679 this->w[w->connections[j]].name,
680 MAX_AUDIO_DEV_LEN);
681 k++;
683 d->un.e.num_mem = k;
684 this->nmixers++;
687 /* output mute */
688 if (w->widgetcap & COP_AWCAP_OUTAMP &&
689 w->outamp_cap & COP_AMPCAP_MUTE) {
690 MIXER_REG_PROLOG;
691 GMIDPRINTF(("%s: output mute %s\n", __func__, w->name));
692 snprintf(d->label.name, sizeof(d->label.name),
693 "%s.mute", w->name);
694 d->type = AUDIO_MIXER_ENUM;
695 if (w->type == COP_AWTYPE_AUDIO_MIXER)
696 d->mixer_class = AZ_CLASS_MIXER;
697 else if (w->type == COP_AWTYPE_AUDIO_SELECTOR)
698 d->mixer_class = AZ_CLASS_OUTPUT;
699 else if (w->type == COP_AWTYPE_PIN_COMPLEX)
700 d->mixer_class = AZ_CLASS_OUTPUT;
701 else
702 d->mixer_class = AZ_CLASS_INPUT;
703 m->target = MI_TARGET_OUTAMP;
704 d->un.e.num_mem = 2;
705 d->un.e.member[0].ord = 0;
706 strlcpy(d->un.e.member[0].label.name, AudioNoff,
707 MAX_AUDIO_DEV_LEN);
708 d->un.e.member[1].ord = 1;
709 strlcpy(d->un.e.member[1].label.name, AudioNon,
710 MAX_AUDIO_DEV_LEN);
711 this->nmixers++;
714 /* output gain */
715 if (w->widgetcap & COP_AWCAP_OUTAMP
716 && COP_AMPCAP_NUMSTEPS(w->outamp_cap)) {
717 MIXER_REG_PROLOG;
718 GMIDPRINTF(("%s: output gain %s\n", __func__, w->name));
719 snprintf(d->label.name, sizeof(d->label.name),
720 "%s", w->name);
721 d->type = AUDIO_MIXER_VALUE;
722 if (w->type == COP_AWTYPE_AUDIO_MIXER)
723 d->mixer_class = AZ_CLASS_MIXER;
724 else if (w->type == COP_AWTYPE_AUDIO_SELECTOR)
725 d->mixer_class = AZ_CLASS_OUTPUT;
726 else if (w->type == COP_AWTYPE_PIN_COMPLEX)
727 d->mixer_class = AZ_CLASS_OUTPUT;
728 else
729 d->mixer_class = AZ_CLASS_INPUT;
730 m->target = MI_TARGET_OUTAMP;
731 d->un.v.num_channels = WIDGET_CHANNELS(w);
732 #ifdef MAX_VOLUME_255
733 d->un.v.units.name[0] = 0;
734 #else
735 snprintf(d->un.v.units.name, sizeof(d->un.v.units.name),
736 "0.25x%ddB", COP_AMPCAP_STEPSIZE(w->outamp_cap)+1);
737 #endif
738 d->un.v.delta =
739 MIXER_DELTA(COP_AMPCAP_NUMSTEPS(w->outamp_cap));
740 this->nmixers++;
743 /* input mute */
744 if (w->widgetcap & COP_AWCAP_INAMP &&
745 w->inamp_cap & COP_AMPCAP_MUTE) {
746 GMIDPRINTF(("%s: input mute %s\n", __func__, w->name));
747 if (w->type != COP_AWTYPE_AUDIO_SELECTOR &&
748 w->type != COP_AWTYPE_AUDIO_MIXER) {
749 MIXER_REG_PROLOG;
750 snprintf(d->label.name, sizeof(d->label.name),
751 "%s.mute", w->name);
752 d->type = AUDIO_MIXER_ENUM;
753 if (w->type == COP_AWTYPE_PIN_COMPLEX)
754 d->mixer_class = AZ_CLASS_OUTPUT;
755 else if (w->type == COP_AWTYPE_AUDIO_INPUT)
756 d->mixer_class = AZ_CLASS_RECORD;
757 else
758 d->mixer_class = AZ_CLASS_INPUT;
759 m->target = 0;
760 d->un.e.num_mem = 2;
761 d->un.e.member[0].ord = 0;
762 strlcpy(d->un.e.member[0].label.name,
763 AudioNoff, MAX_AUDIO_DEV_LEN);
764 d->un.e.member[1].ord = 1;
765 strlcpy(d->un.e.member[1].label.name,
766 AudioNon, MAX_AUDIO_DEV_LEN);
767 this->nmixers++;
768 } else {
769 uint8_t conn;
771 for (j = 0; j < w->nconnections; j++) {
772 MIXER_REG_PROLOG;
773 if (!VALID_WIDGET_NID(w->connections[j], this))
774 continue;
776 /* skip unconnected pins */
777 PIN_STATUS(&this->w[w->connections[j]],
778 conn);
779 if (conn == 1)
780 continue;
782 GMIDPRINTF(("%s: input mute %s.%s\n", __func__,
783 w->name, this->w[w->connections[j]].name));
784 generic_mixer_cat_names(
785 d->label.name,
786 sizeof(d->label.name),
787 w->name, this->w[w->connections[j]].name,
788 "mute");
789 d->type = AUDIO_MIXER_ENUM;
790 if (w->type == COP_AWTYPE_PIN_COMPLEX)
791 d->mixer_class = AZ_CLASS_OUTPUT;
792 else if (w->type == COP_AWTYPE_AUDIO_INPUT)
793 d->mixer_class = AZ_CLASS_RECORD;
794 else if (w->type == COP_AWTYPE_AUDIO_MIXER)
795 d->mixer_class = AZ_CLASS_MIXER;
796 else
797 d->mixer_class = AZ_CLASS_INPUT;
798 m->target = j;
799 d->un.e.num_mem = 2;
800 d->un.e.member[0].ord = 0;
801 strlcpy(d->un.e.member[0].label.name,
802 AudioNoff, MAX_AUDIO_DEV_LEN);
803 d->un.e.member[1].ord = 1;
804 strlcpy(d->un.e.member[1].label.name,
805 AudioNon, MAX_AUDIO_DEV_LEN);
806 this->nmixers++;
811 /* input gain */
812 if (w->widgetcap & COP_AWCAP_INAMP
813 && COP_AMPCAP_NUMSTEPS(w->inamp_cap)) {
814 GMIDPRINTF(("%s: input gain %s\n", __func__, w->name));
815 if (w->type != COP_AWTYPE_AUDIO_SELECTOR &&
816 w->type != COP_AWTYPE_AUDIO_MIXER) {
817 MIXER_REG_PROLOG;
818 snprintf(d->label.name, sizeof(d->label.name),
819 "%s", w->name);
820 d->type = AUDIO_MIXER_VALUE;
821 if (w->type == COP_AWTYPE_PIN_COMPLEX)
822 d->mixer_class = AZ_CLASS_OUTPUT;
823 else if (w->type == COP_AWTYPE_AUDIO_INPUT)
824 d->mixer_class = AZ_CLASS_RECORD;
825 else
826 d->mixer_class = AZ_CLASS_INPUT;
827 m->target = 0;
828 d->un.v.num_channels = WIDGET_CHANNELS(w);
829 #ifdef MAX_VOLUME_255
830 d->un.v.units.name[0] = 0;
831 #else
832 snprintf(d->un.v.units.name,
833 sizeof(d->un.v.units.name), "0.25x%ddB",
834 COP_AMPCAP_STEPSIZE(w->inamp_cap)+1);
835 #endif
836 d->un.v.delta =
837 MIXER_DELTA(COP_AMPCAP_NUMSTEPS(w->inamp_cap));
838 this->nmixers++;
839 } else {
840 uint8_t conn;
842 for (j = 0; j < w->nconnections; j++) {
843 MIXER_REG_PROLOG;
844 if (!VALID_WIDGET_NID(w->connections[j], this))
845 continue;
846 /* skip unconnected pins */
847 PIN_STATUS(&this->w[w->connections[j]],
848 conn);
849 if (conn == 1)
850 continue;
851 GMIDPRINTF(("%s: input gain %s.%s\n", __func__,
852 w->name, this->w[w->connections[j]].name));
853 snprintf(d->label.name, sizeof(d->label.name),
854 "%s.%s", w->name,
855 this->w[w->connections[j]].name);
856 d->type = AUDIO_MIXER_VALUE;
857 if (w->type == COP_AWTYPE_PIN_COMPLEX)
858 d->mixer_class = AZ_CLASS_OUTPUT;
859 else if (w->type == COP_AWTYPE_AUDIO_INPUT)
860 d->mixer_class = AZ_CLASS_RECORD;
861 else if (w->type == COP_AWTYPE_AUDIO_MIXER)
862 d->mixer_class = AZ_CLASS_MIXER;
863 else
864 d->mixer_class = AZ_CLASS_INPUT;
865 m->target = j;
866 d->un.v.num_channels = WIDGET_CHANNELS(w);
867 #ifdef MAX_VOLUME_255
868 d->un.v.units.name[0] = 0;
869 #else
870 snprintf(d->un.v.units.name,
871 sizeof(d->un.v.units.name), "0.25x%ddB",
872 COP_AMPCAP_STEPSIZE(w->inamp_cap)+1);
873 #endif
874 d->un.v.delta =
875 MIXER_DELTA(COP_AMPCAP_NUMSTEPS(w->inamp_cap));
876 this->nmixers++;
881 /* pin direction */
882 if (w->type == COP_AWTYPE_PIN_COMPLEX &&
883 w->d.pin.cap & COP_PINCAP_OUTPUT &&
884 w->d.pin.cap & COP_PINCAP_INPUT) {
885 MIXER_REG_PROLOG;
886 GMIDPRINTF(("%s: pin dir %s\n", __func__, w->name));
887 snprintf(d->label.name, sizeof(d->label.name),
888 "%s.dir", w->name);
889 d->type = AUDIO_MIXER_ENUM;
890 d->mixer_class = AZ_CLASS_OUTPUT;
891 m->target = MI_TARGET_PINDIR;
892 d->un.e.num_mem = 2;
893 d->un.e.member[0].ord = 0;
894 strlcpy(d->un.e.member[0].label.name, AudioNinput,
895 MAX_AUDIO_DEV_LEN);
896 d->un.e.member[1].ord = 1;
897 strlcpy(d->un.e.member[1].label.name, AudioNoutput,
898 MAX_AUDIO_DEV_LEN);
899 this->nmixers++;
902 /* pin headphone-boost */
903 if (w->type == COP_AWTYPE_PIN_COMPLEX &&
904 w->d.pin.cap & COP_PINCAP_HEADPHONE) {
905 MIXER_REG_PROLOG;
906 GMIDPRINTF(("%s: hpboost %s\n", __func__, w->name));
907 snprintf(d->label.name, sizeof(d->label.name),
908 "%s.boost", w->name);
909 d->type = AUDIO_MIXER_ENUM;
910 d->mixer_class = AZ_CLASS_OUTPUT;
911 m->target = MI_TARGET_PINBOOST;
912 d->un.e.num_mem = 2;
913 d->un.e.member[0].ord = 0;
914 strlcpy(d->un.e.member[0].label.name, AudioNoff,
915 MAX_AUDIO_DEV_LEN);
916 d->un.e.member[1].ord = 1;
917 strlcpy(d->un.e.member[1].label.name, AudioNon,
918 MAX_AUDIO_DEV_LEN);
919 this->nmixers++;
922 if (w->type == COP_AWTYPE_PIN_COMPLEX &&
923 w->d.pin.cap & COP_PINCAP_EAPD) {
924 MIXER_REG_PROLOG;
925 GMIDPRINTF(("%s: eapd %s\n", __func__, w->name));
926 snprintf(d->label.name, sizeof(d->label.name),
927 "%s.eapd", w->name);
928 d->type = AUDIO_MIXER_ENUM;
929 d->mixer_class = AZ_CLASS_OUTPUT;
930 m->target = MI_TARGET_EAPD;
931 d->un.e.num_mem = 2;
932 d->un.e.member[0].ord = 0;
933 strlcpy(d->un.e.member[0].label.name, AudioNoff,
934 MAX_AUDIO_DEV_LEN);
935 d->un.e.member[1].ord = 1;
936 strlcpy(d->un.e.member[1].label.name, AudioNon,
937 MAX_AUDIO_DEV_LEN);
938 this->nmixers++;
941 if (w->type == COP_AWTYPE_PIN_COMPLEX &&
942 w->d.pin.cap & COP_PINCAP_BALANCE) {
943 MIXER_REG_PROLOG;
944 GMIDPRINTF(("%s: balance %s\n", __func__, w->name));
945 snprintf(d->label.name, sizeof(d->label.name),
946 "%s.balance", w->name);
947 d->type = AUDIO_MIXER_ENUM;
948 if (w->type == COP_AWTYPE_PIN_COMPLEX)
949 d->mixer_class = AZ_CLASS_OUTPUT;
950 else if (w->type == COP_AWTYPE_AUDIO_INPUT)
951 d->mixer_class = AZ_CLASS_RECORD;
952 else
953 d->mixer_class = AZ_CLASS_INPUT;
954 m->target = MI_TARGET_BALANCE;
955 d->un.e.num_mem = 2;
956 d->un.e.member[0].ord = 0;
957 strlcpy(d->un.e.member[0].label.name, AudioNoff,
958 MAX_AUDIO_DEV_LEN);
959 d->un.e.member[1].ord = 1;
960 strlcpy(d->un.e.member[1].label.name, AudioNon,
961 MAX_AUDIO_DEV_LEN);
962 this->nmixers++;
965 if (w->widgetcap & COP_AWCAP_LRSWAP) {
966 MIXER_REG_PROLOG;
967 GMIDPRINTF(("%s: lrswap %s\n", __func__, w->name));
968 snprintf(d->label.name, sizeof(d->label.name),
969 "%s.lrswap", w->name);
970 d->type = AUDIO_MIXER_ENUM;
971 if (w->type == COP_AWTYPE_PIN_COMPLEX)
972 d->mixer_class = AZ_CLASS_OUTPUT;
973 else if (w->type == COP_AWTYPE_AUDIO_INPUT)
974 d->mixer_class = AZ_CLASS_RECORD;
975 else
976 d->mixer_class = AZ_CLASS_INPUT;
977 m->target = MI_TARGET_LRSWAP;
978 d->un.e.num_mem = 2;
979 d->un.e.member[0].ord = 0;
980 strlcpy(d->un.e.member[0].label.name, AudioNoff,
981 MAX_AUDIO_DEV_LEN);
982 d->un.e.member[1].ord = 1;
983 strlcpy(d->un.e.member[1].label.name, AudioNon,
984 MAX_AUDIO_DEV_LEN);
985 this->nmixers++;
988 /* volume knob */
989 if (w->type == COP_AWTYPE_VOLUME_KNOB &&
990 w->d.volume.cap & COP_VKCAP_DELTA) {
991 MIXER_REG_PROLOG;
992 GMIDPRINTF(("%s: volume knob %s\n", __func__, w->name));
993 strlcpy(d->label.name, w->name, sizeof(d->label.name));
994 d->type = AUDIO_MIXER_VALUE;
995 d->mixer_class = AZ_CLASS_OUTPUT;
996 m->target = MI_TARGET_VOLUME;
997 d->un.v.num_channels = 1;
998 d->un.v.units.name[0] = 0;
999 d->un.v.delta =
1000 MIXER_DELTA(COP_VKCAP_NUMSTEPS(w->d.volume.cap));
1001 this->nmixers++;
1005 /* if the codec has multiple DAC groups, create "playback.mode" */
1006 if (this->dacs.ngroups > 1) {
1007 MIXER_REG_PROLOG;
1008 GMIDPRINTF(("%s: create playback.mode\n", __func__));
1009 strlcpy(d->label.name, AudioNmode, sizeof(d->label.name));
1010 d->type = AUDIO_MIXER_ENUM;
1011 d->mixer_class = AZ_CLASS_PLAYBACK;
1012 m->target = MI_TARGET_DAC;
1013 for (i = 0; i < this->dacs.ngroups && i < 32; i++) {
1014 d->un.e.member[i].ord = i;
1015 for (j = 0; j < this->dacs.groups[i].nconv; j++) {
1016 if (j * 2 >= MAX_AUDIO_DEV_LEN)
1017 break;
1018 snprintf(d->un.e.member[i].label.name + j*2,
1019 MAX_AUDIO_DEV_LEN - j*2, "%2.2x",
1020 this->dacs.groups[i].conv[j]);
1023 d->un.e.num_mem = i;
1024 this->nmixers++;
1027 /* if the codec has multiple ADC groups, create "record.mode" */
1028 if (this->adcs.ngroups > 1) {
1029 MIXER_REG_PROLOG;
1030 GMIDPRINTF(("%s: create record.mode\n", __func__));
1031 strlcpy(d->label.name, AudioNmode, sizeof(d->label.name));
1032 d->type = AUDIO_MIXER_ENUM;
1033 d->mixer_class = AZ_CLASS_RECORD;
1034 m->target = MI_TARGET_ADC;
1035 for (i = 0; i < this->adcs.ngroups && i < 32; i++) {
1036 d->un.e.member[i].ord = i;
1037 for (j = 0; j < this->adcs.groups[i].nconv; j++) {
1038 if (j * 2 >= MAX_AUDIO_DEV_LEN)
1039 break;
1040 snprintf(d->un.e.member[i].label.name + j*2,
1041 MAX_AUDIO_DEV_LEN - j*2, "%2.2x",
1042 this->adcs.groups[i].conv[j]);
1045 d->un.e.num_mem = i;
1046 this->nmixers++;
1049 generic_mixer_fix_indexes(this);
1050 generic_mixer_default(this);
1051 return 0;
1054 static void
1055 generic_mixer_cat_names(char *dst, size_t dstsize,
1056 const char *str1, const char *str2, const char *str3)
1058 const char *last2;
1059 size_t len1, len2, len3, total;
1061 len1 = strlen(str1);
1062 len2 = strlen(str2);
1063 len3 = strlen(str3);
1064 total = len1 + 1 + len2 + 1 + len3 + 1;
1065 if (total - (len3 - 1) <= dstsize) {
1066 snprintf(dst, dstsize, "%s.%s.%s", str1, str2, str3);
1067 return;
1069 last2 = len2 > 2 ? str2 + len2 - 2 : str2;
1070 if (len2 > 4) {
1071 snprintf(dst, dstsize, "%s.%.2s%s.%s", str1, str2, last2, str3);
1072 return;
1074 snprintf(dst, dstsize, "%s.%s.%s", str1, last2, str3);
1077 static int
1078 generic_mixer_ensure_capacity(codec_t *this, size_t newsize)
1080 size_t newmax;
1081 void *newbuf;
1083 if (this->maxmixers >= newsize)
1084 return 0;
1085 newmax = this->maxmixers + 10;
1086 if (newmax < newsize)
1087 newmax = newsize;
1088 newbuf = realloc(this->mixers, sizeof(mixer_item_t) * newmax, M_DEVBUF,
1089 M_ZERO | M_NOWAIT);
1090 if (newbuf == NULL) {
1091 aprint_error_dev(this->dev, "out of memory in %s\n", __func__);
1092 return ENOMEM;
1094 this->mixers = newbuf;
1095 /* realloc(9) doesn't clear expanded area even if M_ZERO. */
1096 memset(&this->mixers[this->maxmixers], 0,
1097 sizeof(mixer_item_t) * (newmax - this->maxmixers));
1098 this->maxmixers = newmax;
1099 return 0;
1102 static int
1103 generic_mixer_fix_indexes(codec_t *this)
1105 int i;
1106 mixer_devinfo_t *d;
1108 for (i = 0; i < this->nmixers; i++) {
1109 d = &this->mixers[i].devinfo;
1110 #ifdef DIAGNOSTIC
1111 if (d->index != 0 && d->index != i)
1112 aprint_error("%s: index mismatch %d %d\n", __func__,
1113 d->index, i);
1114 #endif
1115 d->index = i;
1116 if (d->prev == 0)
1117 d->prev = AUDIO_MIXER_LAST;
1118 if (d->next == 0)
1119 d->next = AUDIO_MIXER_LAST;
1121 return 0;
1124 static int
1125 generic_mixer_default(codec_t *this)
1127 int i;
1128 mixer_item_t *m;
1129 /* unmute all */
1130 DPRINTF(("%s: unmute\n", __func__));
1131 for (i = 0; i < this->nmixers; i++) {
1132 mixer_ctrl_t mc;
1134 m = &this->mixers[i];
1135 if (!IS_MI_TARGET_INAMP(m->target) &&
1136 m->target != MI_TARGET_OUTAMP)
1137 continue;
1138 if (m->devinfo.type != AUDIO_MIXER_ENUM)
1139 continue;
1140 mc.dev = i;
1141 mc.type = AUDIO_MIXER_ENUM;
1142 mc.un.ord = 0;
1143 generic_mixer_set(this, m->nid, m->target, &mc);
1147 * For bidirectional pins, make the default `output'
1149 DPRINTF(("%s: process bidirectional pins\n", __func__));
1150 for (i = 0; i < this->nmixers; i++) {
1151 mixer_ctrl_t mc;
1153 m = &this->mixers[i];
1154 if (m->target != MI_TARGET_PINDIR)
1155 continue;
1156 mc.dev = i;
1157 mc.type = AUDIO_MIXER_ENUM;
1158 mc.un.ord = 1; /* output */
1159 generic_mixer_set(this, m->nid, m->target, &mc);
1162 /* set unextreme volume */
1163 DPRINTF(("%s: set volume\n", __func__));
1164 for (i = 0; i < this->nmixers; i++) {
1165 mixer_ctrl_t mc;
1167 m = &this->mixers[i];
1168 if (!IS_MI_TARGET_INAMP(m->target) &&
1169 m->target != MI_TARGET_OUTAMP &&
1170 m->target != MI_TARGET_VOLUME)
1171 continue;
1172 if (m->devinfo.type != AUDIO_MIXER_VALUE)
1173 continue;
1174 mc.dev = i;
1175 mc.type = AUDIO_MIXER_VALUE;
1176 mc.un.value.num_channels = 1;
1177 mc.un.value.level[0] = AUDIO_MAX_GAIN / 2;
1178 if (m->target != MI_TARGET_VOLUME &&
1179 WIDGET_CHANNELS(&this->w[m->nid]) == 2) {
1180 mc.un.value.num_channels = 2;
1181 mc.un.value.level[1] = AUDIO_MAX_GAIN / 2;
1183 generic_mixer_set(this, m->nid, m->target, &mc);
1186 return 0;
1189 static int
1190 generic_mixer_pin_sense(codec_t *this)
1192 typedef enum {
1193 PIN_DIR_IN,
1194 PIN_DIR_OUT,
1195 PIN_DIR_MIC
1196 } pintype_t;
1197 const widget_t *w;
1198 int i;
1200 FOR_EACH_WIDGET(this, i) {
1201 pintype_t pintype = PIN_DIR_IN;
1203 w = &this->w[i];
1204 if (w->type != COP_AWTYPE_PIN_COMPLEX)
1205 continue;
1206 if (!(w->d.pin.cap & COP_PINCAP_INPUT))
1207 pintype = PIN_DIR_OUT;
1208 if (!(w->d.pin.cap & COP_PINCAP_OUTPUT))
1209 pintype = PIN_DIR_IN;
1211 switch (w->d.pin.device) {
1212 case CORB_CD_LINEOUT:
1213 case CORB_CD_SPEAKER:
1214 case CORB_CD_HEADPHONE:
1215 case CORB_CD_SPDIFOUT:
1216 case CORB_CD_DIGITALOUT:
1217 pintype = PIN_DIR_OUT;
1218 break;
1219 case CORB_CD_CD:
1220 case CORB_CD_LINEIN:
1221 pintype = PIN_DIR_IN;
1222 break;
1223 case CORB_CD_MICIN:
1224 pintype = PIN_DIR_MIC;
1225 break;
1228 switch (pintype) {
1229 case PIN_DIR_IN:
1230 this->comresp(this, w->nid,
1231 CORB_SET_PIN_WIDGET_CONTROL,
1232 CORB_PWC_INPUT, NULL);
1233 break;
1234 case PIN_DIR_OUT:
1235 this->comresp(this, w->nid,
1236 CORB_SET_PIN_WIDGET_CONTROL,
1237 CORB_PWC_OUTPUT, NULL);
1238 break;
1239 case PIN_DIR_MIC:
1240 this->comresp(this, w->nid,
1241 CORB_SET_PIN_WIDGET_CONTROL,
1242 CORB_PWC_INPUT|CORB_PWC_VREF_80, NULL);
1243 break;
1246 if (w->d.pin.cap & COP_PINCAP_EAPD) {
1247 uint32_t result;
1248 int err;
1250 err = this->comresp(this, w->nid,
1251 CORB_GET_EAPD_BTL_ENABLE, 0, &result);
1252 if (err)
1253 continue;
1254 result &= 0xff;
1255 result |= CORB_EAPD_EAPD;
1256 err = this->comresp(this, w->nid,
1257 CORB_SET_EAPD_BTL_ENABLE, result, &result);
1258 if (err)
1259 continue;
1263 return 0;
1266 static int
1267 generic_mixer_widget_name(const codec_t *this, widget_t *w)
1269 const char *name = NULL, *grossloc = "", *geoloc = "";
1270 uint8_t grosslocval, geolocval;
1272 if (w->type != COP_AWTYPE_PIN_COMPLEX)
1273 return 0;
1275 switch (w->d.pin.device) {
1276 case CORB_CD_LINEOUT: name = "lineout"; break;
1277 case CORB_CD_SPEAKER: name = "spkr"; break;
1278 case CORB_CD_HEADPHONE: name = "hp"; break;
1279 case CORB_CD_CD: name = AudioNcd; break;
1280 case CORB_CD_SPDIFOUT: name = "spdifout"; break;
1281 case CORB_CD_DIGITALOUT: name = "digout"; break;
1282 case CORB_CD_MODEMLINE: name = "modemline"; break;
1283 case CORB_CD_MODEMHANDSET: name = "modemhset"; break;
1284 case CORB_CD_LINEIN: name = "linein"; break;
1285 case CORB_CD_AUX: name = AudioNaux; break;
1286 case CORB_CD_MICIN: name = AudioNmicrophone; break;
1287 case CORB_CD_TELEPHONY: name = "telephony"; break;
1288 case CORB_CD_SPDIFIN: name = "spdifin"; break;
1289 case CORB_CD_DIGITALIN: name = "digin"; break;
1290 case CORB_CD_DEVICE_OTHER: name = "reserved"; break;
1291 default: name = "unused"; break;
1294 grosslocval = ((w->d.pin.config & CORB_CD_LOCATION_MASK) >> 24) & 0xf;
1295 geolocval = (w->d.pin.config & CORB_CD_LOCATION_MASK) >> 28;
1297 switch (geolocval) {
1298 case 0x00: /* external on primary chassis */
1299 case 0x10: /* external on separate chassis */
1300 geoloc = (geolocval == 0x00 ? "" : "d");
1301 switch (grosslocval) {
1302 case 0x00: grossloc = ""; break; /* N/A */
1303 case 0x01: grossloc = ""; break; /* rear */
1304 case 0x02: grossloc = ".front"; break; /* front */
1305 case 0x03: grossloc = ".left"; break; /* left */
1306 case 0x04: grossloc = ".right"; break; /* right */
1307 case 0x05: grossloc = ".top"; break; /* top */
1308 case 0x06: grossloc = ".bottom"; break; /* bottom */
1309 case 0x07: grossloc = ".rearpnl"; break; /* rear panel */
1310 case 0x08: grossloc = ".drivebay"; break; /* drive bay */
1311 default: grossloc = ""; break;
1313 break;
1314 case 0x01:
1315 geoloc = "i";
1316 switch (grosslocval) {
1317 case 0x00: grossloc = ""; break; /* N/A */
1318 case 0x07: grossloc = ".riser"; break; /* riser */
1319 case 0x08: grossloc = ".hdmi"; break; /* hdmi */
1320 default: grossloc = ""; break;
1322 break;
1323 default:
1324 geoloc = "o";
1325 switch (grosslocval) {
1326 case 0x00: grossloc = ""; break; /* N/A */
1327 case 0x06: grossloc = ".bottom"; break; /* bottom */
1328 case 0x07: grossloc = ".lidin"; break; /* lid inside */
1329 case 0x08: grossloc = ".lidout"; break; /* lid outside */
1330 default: grossloc = ""; break;
1332 break;
1335 snprintf(w->name, sizeof(w->name), "%s%s%s", geoloc, name, grossloc);
1336 return 0;
1339 static int
1340 generic_mixer_create_virtual(codec_t *this)
1342 mixer_item_t *m;
1343 mixer_devinfo_t *d;
1344 convgroup_t *cgdac = &this->dacs.groups[0];
1345 convgroup_t *cgadc = &this->adcs.groups[0];
1346 int i, err, mdac, madc, mmaster;
1348 /* Clear mixer indexes, to make generic_mixer_fix_index happy */
1349 for (i = 0; i < this->nmixers; i++) {
1350 d = &this->mixers[i].devinfo;
1351 d->index = d->prev = d->next = 0;
1354 mdac = madc = mmaster = -1;
1355 for (i = 0; i < this->nmixers; i++) {
1356 if (this->mixers[i].devinfo.type != AUDIO_MIXER_VALUE)
1357 continue;
1358 if (mdac < 0 && this->dacs.ngroups > 0 && cgdac->nconv > 0) {
1359 if (this->mixers[i].nid == cgdac->conv[0])
1360 mdac = mmaster = i;
1362 if (madc < 0 && this->adcs.ngroups > 0 && cgadc->nconv > 0) {
1363 if (this->mixers[i].nid == cgadc->conv[0])
1364 madc = i;
1368 if (mdac == -1) {
1370 * no volume mixer found on the DAC; enumerate peer widgets
1371 * and try to find a volume mixer on them
1373 widget_t *w;
1374 int j;
1375 FOR_EACH_WIDGET(this, i) {
1376 w = &this->w[i];
1377 for (j = 0; j < w->nconnections; j++)
1378 if (w->connections[j] == cgdac->conv[0])
1379 break;
1381 if (j == w->nconnections)
1382 continue;
1384 for (j = 0; j < this->nmixers; j++) {
1385 if (this->mixers[j].devinfo.type !=
1386 AUDIO_MIXER_VALUE)
1387 continue;
1388 if (this->mixers[j].nid == w->nid) {
1389 mdac = mmaster = j;
1390 break;
1394 if (mdac == -1)
1395 break;
1399 if (mdac >= 0) {
1400 err = generic_mixer_ensure_capacity(this, this->nmixers + 1);
1401 if (err)
1402 return err;
1403 m = &this->mixers[this->nmixers];
1404 d = &m->devinfo;
1405 memcpy(m, &this->mixers[mmaster], sizeof(*m));
1406 d->mixer_class = AZ_CLASS_OUTPUT;
1407 snprintf(d->label.name, sizeof(d->label.name), AudioNmaster);
1408 this->nmixers++;
1410 err = generic_mixer_ensure_capacity(this, this->nmixers + 1);
1411 if (err)
1412 return err;
1413 m = &this->mixers[this->nmixers];
1414 d = &m->devinfo;
1415 memcpy(m, &this->mixers[mdac], sizeof(*m));
1416 d->mixer_class = AZ_CLASS_INPUT;
1417 snprintf(d->label.name, sizeof(d->label.name), AudioNdac);
1418 this->nmixers++;
1421 if (madc >= 0) {
1422 err = generic_mixer_ensure_capacity(this, this->nmixers + 1);
1423 if (err)
1424 return err;
1425 m = &this->mixers[this->nmixers];
1426 d = &m->devinfo;
1427 memcpy(m, &this->mixers[madc], sizeof(*m));
1428 d->mixer_class = AZ_CLASS_RECORD;
1429 snprintf(d->label.name, sizeof(d->label.name), AudioNvolume);
1430 this->nmixers++;
1433 generic_mixer_fix_indexes(this);
1435 return 0;
1438 static int
1439 generic_mixer_autoinit(codec_t *this)
1441 generic_mixer_init(this);
1442 generic_mixer_create_virtual(this);
1443 generic_mixer_pin_sense(this);
1445 return 0;
1448 static int
1449 generic_mixer_init_widget(const codec_t *this, widget_t *w, nid_t nid)
1451 return generic_mixer_widget_name(this, w);
1454 static int
1455 generic_mixer_delete(codec_t *this)
1457 if (this->mixers == NULL)
1458 return 0;
1459 free(this->mixers, M_DEVBUF);
1460 this->mixers = NULL;
1461 return 0;
1465 * @param mc mc->type must be set by the caller before the call
1467 static int
1468 generic_mixer_get(const codec_t *this, nid_t nid, int target, mixer_ctrl_t *mc)
1470 uint32_t result;
1471 nid_t n;
1472 int err;
1474 /* inamp mute */
1475 if (IS_MI_TARGET_INAMP(target) && mc->type == AUDIO_MIXER_ENUM) {
1476 err = this->comresp(this, nid, CORB_GET_AMPLIFIER_GAIN_MUTE,
1477 CORB_GAGM_INPUT | CORB_GAGM_LEFT |
1478 MI_TARGET_INAMP(target), &result);
1479 if (err)
1480 return err;
1481 mc->un.ord = result & CORB_GAGM_MUTE ? 1 : 0;
1484 /* inamp gain */
1485 else if (IS_MI_TARGET_INAMP(target) && mc->type == AUDIO_MIXER_VALUE) {
1486 err = this->comresp(this, nid, CORB_GET_AMPLIFIER_GAIN_MUTE,
1487 CORB_GAGM_INPUT | CORB_GAGM_LEFT |
1488 MI_TARGET_INAMP(target), &result);
1489 if (err)
1490 return err;
1491 mc->un.value.level[0] = generic_mixer_from_device_value(this,
1492 nid, target, CORB_GAGM_GAIN(result));
1493 if (this->w[nid].type == COP_AWTYPE_AUDIO_SELECTOR ||
1494 this->w[nid].type == COP_AWTYPE_AUDIO_MIXER) {
1495 n = this->w[nid].connections[MI_TARGET_INAMP(target)];
1496 #ifdef AZALIA_DEBUG
1497 if (!VALID_WIDGET_NID(n, this)) {
1498 DPRINTF(("%s: invalid target: nid=%d nconn=%d index=%d\n",
1499 __func__, nid, this->w[nid].nconnections,
1500 MI_TARGET_INAMP(target)));
1501 return EINVAL;
1503 #endif
1504 } else {
1505 n = nid;
1507 mc->un.value.num_channels = WIDGET_CHANNELS(&this->w[n]);
1508 if (mc->un.value.num_channels == 2) {
1509 err = this->comresp(this, nid,
1510 CORB_GET_AMPLIFIER_GAIN_MUTE, CORB_GAGM_INPUT |
1511 CORB_GAGM_RIGHT | MI_TARGET_INAMP(target),
1512 &result);
1513 if (err)
1514 return err;
1515 mc->un.value.level[1] = generic_mixer_from_device_value
1516 (this, nid, target, CORB_GAGM_GAIN(result));
1520 /* outamp mute */
1521 else if (target == MI_TARGET_OUTAMP && mc->type == AUDIO_MIXER_ENUM) {
1522 err = this->comresp(this, nid, CORB_GET_AMPLIFIER_GAIN_MUTE,
1523 CORB_GAGM_OUTPUT | CORB_GAGM_LEFT | 0, &result);
1524 if (err)
1525 return err;
1526 mc->un.ord = result & CORB_GAGM_MUTE ? 1 : 0;
1529 /* outamp gain */
1530 else if (target == MI_TARGET_OUTAMP && mc->type == AUDIO_MIXER_VALUE) {
1531 err = this->comresp(this, nid, CORB_GET_AMPLIFIER_GAIN_MUTE,
1532 CORB_GAGM_OUTPUT | CORB_GAGM_LEFT | 0, &result);
1533 if (err)
1534 return err;
1535 mc->un.value.level[0] = generic_mixer_from_device_value(this,
1536 nid, target, CORB_GAGM_GAIN(result));
1537 mc->un.value.num_channels = WIDGET_CHANNELS(&this->w[nid]);
1538 if (mc->un.value.num_channels == 2) {
1539 err = this->comresp(this, nid,
1540 CORB_GET_AMPLIFIER_GAIN_MUTE,
1541 CORB_GAGM_OUTPUT | CORB_GAGM_RIGHT | 0, &result);
1542 if (err)
1543 return err;
1544 mc->un.value.level[1] = generic_mixer_from_device_value
1545 (this, nid, target, CORB_GAGM_GAIN(result));
1549 /* selection */
1550 else if (target == MI_TARGET_CONNLIST) {
1551 err = this->comresp(this, nid,
1552 CORB_GET_CONNECTION_SELECT_CONTROL, 0, &result);
1553 if (err)
1554 return err;
1555 result = CORB_CSC_INDEX(result);
1556 if (!VALID_WIDGET_NID(this->w[nid].connections[result], this))
1557 mc->un.ord = -1;
1558 else
1559 mc->un.ord = result;
1562 /* pin I/O */
1563 else if (target == MI_TARGET_PINDIR) {
1564 err = this->comresp(this, nid,
1565 CORB_GET_PIN_WIDGET_CONTROL, 0, &result);
1566 if (err)
1567 return err;
1568 mc->un.ord = result & CORB_PWC_OUTPUT ? 1 : 0;
1571 /* pin headphone-boost */
1572 else if (target == MI_TARGET_PINBOOST) {
1573 err = this->comresp(this, nid,
1574 CORB_GET_PIN_WIDGET_CONTROL, 0, &result);
1575 if (err)
1576 return err;
1577 mc->un.ord = result & CORB_PWC_HEADPHONE ? 1 : 0;
1580 /* DAC group selection */
1581 else if (target == MI_TARGET_DAC) {
1582 mc->un.ord = this->dacs.cur;
1585 /* ADC selection */
1586 else if (target == MI_TARGET_ADC) {
1587 mc->un.ord = this->adcs.cur;
1590 /* Volume knob */
1591 else if (target == MI_TARGET_VOLUME) {
1592 err = this->comresp(this, nid, CORB_GET_VOLUME_KNOB,
1593 0, &result);
1594 if (err)
1595 return err;
1596 mc->un.value.level[0] = generic_mixer_from_device_value(this,
1597 nid, target, CORB_VKNOB_VOLUME(result));
1598 mc->un.value.num_channels = 1;
1601 /* S/PDIF */
1602 else if (target == MI_TARGET_SPDIF) {
1603 err = this->comresp(this, nid, CORB_GET_DIGITAL_CONTROL,
1604 0, &result);
1605 if (err)
1606 return err;
1607 mc->un.mask = result & 0xff & ~(CORB_DCC_DIGEN | CORB_DCC_NAUDIO);
1608 } else if (target == MI_TARGET_SPDIF_CC) {
1609 err = this->comresp(this, nid, CORB_GET_DIGITAL_CONTROL,
1610 0, &result);
1611 if (err)
1612 return err;
1613 mc->un.value.num_channels = 1;
1614 mc->un.value.level[0] = CORB_DCC_CC(result);
1617 /* EAPD */
1618 else if (target == MI_TARGET_EAPD) {
1619 err = this->comresp(this, nid,
1620 CORB_GET_EAPD_BTL_ENABLE, 0, &result);
1621 if (err)
1622 return err;
1623 mc->un.ord = result & CORB_EAPD_EAPD ? 1 : 0;
1626 /* Balanced I/O */
1627 else if (target == MI_TARGET_BALANCE) {
1628 err = this->comresp(this, nid,
1629 CORB_GET_EAPD_BTL_ENABLE, 0, &result);
1630 if (err)
1631 return err;
1632 mc->un.ord = result & CORB_EAPD_BTL ? 1 : 0;
1635 /* LR-Swap */
1636 else if (target == MI_TARGET_LRSWAP) {
1637 err = this->comresp(this, nid,
1638 CORB_GET_EAPD_BTL_ENABLE, 0, &result);
1639 if (err)
1640 return err;
1641 mc->un.ord = result & CORB_EAPD_LRSWAP ? 1 : 0;
1644 else {
1645 aprint_error_dev(this->dev, "internal error in %s: target=%x\n",
1646 __func__, target);
1647 return -1;
1649 return 0;
1652 static int
1653 generic_mixer_set(codec_t *this, nid_t nid, int target, const mixer_ctrl_t *mc)
1655 uint32_t result, value;
1656 int err;
1658 /* inamp mute */
1659 if (IS_MI_TARGET_INAMP(target) && mc->type == AUDIO_MIXER_ENUM) {
1660 /* We have to set stereo mute separately to keep each gain value. */
1661 err = this->comresp(this, nid, CORB_GET_AMPLIFIER_GAIN_MUTE,
1662 CORB_GAGM_INPUT | CORB_GAGM_LEFT |
1663 MI_TARGET_INAMP(target), &result);
1664 if (err)
1665 return err;
1666 value = CORB_AGM_INPUT | CORB_AGM_LEFT |
1667 (target << CORB_AGM_INDEX_SHIFT) |
1668 CORB_GAGM_GAIN(result);
1669 if (mc->un.ord)
1670 value |= CORB_AGM_MUTE;
1671 err = this->comresp(this, nid, CORB_SET_AMPLIFIER_GAIN_MUTE,
1672 value, &result);
1673 if (err)
1674 return err;
1675 if (WIDGET_CHANNELS(&this->w[nid]) == 2) {
1676 err = this->comresp(this, nid,
1677 CORB_GET_AMPLIFIER_GAIN_MUTE, CORB_GAGM_INPUT |
1678 CORB_GAGM_RIGHT | MI_TARGET_INAMP(target),
1679 &result);
1680 if (err)
1681 return err;
1682 value = CORB_AGM_INPUT | CORB_AGM_RIGHT |
1683 (target << CORB_AGM_INDEX_SHIFT) |
1684 CORB_GAGM_GAIN(result);
1685 if (mc->un.ord)
1686 value |= CORB_AGM_MUTE;
1687 err = this->comresp(this, nid,
1688 CORB_SET_AMPLIFIER_GAIN_MUTE, value, &result);
1689 if (err)
1690 return err;
1694 /* inamp gain */
1695 else if (IS_MI_TARGET_INAMP(target) && mc->type == AUDIO_MIXER_VALUE) {
1696 if (mc->un.value.num_channels < 1)
1697 return EINVAL;
1698 if (!generic_mixer_validate_value(this, nid, target,
1699 mc->un.value.level[0]))
1700 return EINVAL;
1701 err = this->comresp(this, nid, CORB_GET_AMPLIFIER_GAIN_MUTE,
1702 CORB_GAGM_INPUT | CORB_GAGM_LEFT |
1703 MI_TARGET_INAMP(target), &result);
1704 if (err)
1705 return err;
1706 value = generic_mixer_to_device_value(this, nid, target,
1707 mc->un.value.level[0]);
1708 value = CORB_AGM_INPUT | CORB_AGM_LEFT |
1709 (target << CORB_AGM_INDEX_SHIFT) |
1710 (result & CORB_GAGM_MUTE ? CORB_AGM_MUTE : 0) |
1711 (value & CORB_AGM_GAIN_MASK);
1712 err = this->comresp(this, nid, CORB_SET_AMPLIFIER_GAIN_MUTE,
1713 value, &result);
1714 if (err)
1715 return err;
1716 if (mc->un.value.num_channels >= 2 &&
1717 WIDGET_CHANNELS(&this->w[nid]) == 2) {
1718 if (!generic_mixer_validate_value(this, nid, target,
1719 mc->un.value.level[1]))
1720 return EINVAL;
1721 err = this->comresp(this, nid,
1722 CORB_GET_AMPLIFIER_GAIN_MUTE, CORB_GAGM_INPUT |
1723 CORB_GAGM_RIGHT | MI_TARGET_INAMP(target),
1724 &result);
1725 if (err)
1726 return err;
1727 value = generic_mixer_to_device_value(this, nid, target,
1728 mc->un.value.level[1]);
1729 value = CORB_AGM_INPUT | CORB_AGM_RIGHT |
1730 (target << CORB_AGM_INDEX_SHIFT) |
1731 (result & CORB_GAGM_MUTE ? CORB_AGM_MUTE : 0) |
1732 (value & CORB_AGM_GAIN_MASK);
1733 err = this->comresp(this, nid,
1734 CORB_SET_AMPLIFIER_GAIN_MUTE, value, &result);
1735 if (err)
1736 return err;
1740 /* outamp mute */
1741 else if (target == MI_TARGET_OUTAMP && mc->type == AUDIO_MIXER_ENUM) {
1742 err = this->comresp(this, nid, CORB_GET_AMPLIFIER_GAIN_MUTE,
1743 CORB_GAGM_OUTPUT | CORB_GAGM_LEFT, &result);
1744 if (err)
1745 return err;
1746 value = CORB_AGM_OUTPUT | CORB_AGM_LEFT | CORB_GAGM_GAIN(result);
1747 if (mc->un.ord)
1748 value |= CORB_AGM_MUTE;
1749 err = this->comresp(this, nid, CORB_SET_AMPLIFIER_GAIN_MUTE,
1750 value, &result);
1751 if (err)
1752 return err;
1753 if (WIDGET_CHANNELS(&this->w[nid]) == 2) {
1754 err = this->comresp(this, nid,
1755 CORB_GET_AMPLIFIER_GAIN_MUTE,
1756 CORB_GAGM_OUTPUT | CORB_GAGM_RIGHT, &result);
1757 if (err)
1758 return err;
1759 value = CORB_AGM_OUTPUT | CORB_AGM_RIGHT |
1760 CORB_GAGM_GAIN(result);
1761 if (mc->un.ord)
1762 value |= CORB_AGM_MUTE;
1763 err = this->comresp(this, nid,
1764 CORB_SET_AMPLIFIER_GAIN_MUTE, value, &result);
1765 if (err)
1766 return err;
1770 /* outamp gain */
1771 else if (target == MI_TARGET_OUTAMP && mc->type == AUDIO_MIXER_VALUE) {
1772 if (mc->un.value.num_channels < 1)
1773 return EINVAL;
1774 if (!generic_mixer_validate_value(this, nid, target,
1775 mc->un.value.level[0]))
1776 return EINVAL;
1777 err = this->comresp(this, nid, CORB_GET_AMPLIFIER_GAIN_MUTE,
1778 CORB_GAGM_OUTPUT | CORB_GAGM_LEFT, &result);
1779 if (err)
1780 return err;
1781 value = generic_mixer_to_device_value(this, nid, target,
1782 mc->un.value.level[0]);
1783 value = CORB_AGM_OUTPUT | CORB_AGM_LEFT |
1784 (result & CORB_GAGM_MUTE ? CORB_AGM_MUTE : 0) |
1785 (value & CORB_AGM_GAIN_MASK);
1786 err = this->comresp(this, nid, CORB_SET_AMPLIFIER_GAIN_MUTE,
1787 value, &result);
1788 if (err)
1789 return err;
1790 if (mc->un.value.num_channels >= 2 &&
1791 WIDGET_CHANNELS(&this->w[nid]) == 2) {
1792 if (!generic_mixer_validate_value(this, nid, target,
1793 mc->un.value.level[1]))
1794 return EINVAL;
1795 err = this->comresp(this, nid,
1796 CORB_GET_AMPLIFIER_GAIN_MUTE, CORB_GAGM_OUTPUT |
1797 CORB_GAGM_RIGHT, &result);
1798 if (err)
1799 return err;
1800 value = generic_mixer_to_device_value(this, nid, target,
1801 mc->un.value.level[1]);
1802 value = CORB_AGM_OUTPUT | CORB_AGM_RIGHT |
1803 (result & CORB_GAGM_MUTE ? CORB_AGM_MUTE : 0) |
1804 (value & CORB_AGM_GAIN_MASK);
1805 err = this->comresp(this, nid,
1806 CORB_SET_AMPLIFIER_GAIN_MUTE, value, &result);
1807 if (err)
1808 return err;
1812 /* selection */
1813 else if (target == MI_TARGET_CONNLIST) {
1814 if (mc->un.ord < 0 ||
1815 mc->un.ord >= this->w[nid].nconnections ||
1816 !VALID_WIDGET_NID(this->w[nid].connections[mc->un.ord], this))
1817 return EINVAL;
1818 err = this->comresp(this, nid,
1819 CORB_SET_CONNECTION_SELECT_CONTROL, mc->un.ord, &result);
1820 if (err)
1821 return err;
1824 /* pin I/O */
1825 else if (target == MI_TARGET_PINDIR) {
1826 if (mc->un.ord >= 2)
1827 return EINVAL;
1828 if (mc->un.ord == 0) {
1829 return generic_mixer_pinctrl(this, nid, CORB_PWC_INPUT);
1830 } else {
1831 return generic_mixer_pinctrl(
1832 this, nid, CORB_PWC_OUTPUT);
1836 /* pin headphone-boost */
1837 else if (target == MI_TARGET_PINBOOST) {
1838 if (mc->un.ord >= 2)
1839 return EINVAL;
1840 err = this->comresp(this, nid,
1841 CORB_GET_PIN_WIDGET_CONTROL, 0, &result);
1842 if (err)
1843 return err;
1844 if (mc->un.ord == 0) {
1845 result &= ~CORB_PWC_HEADPHONE;
1846 } else {
1847 result |= CORB_PWC_HEADPHONE;
1849 err = this->comresp(this, nid,
1850 CORB_SET_PIN_WIDGET_CONTROL, result, &result);
1851 if (err)
1852 return err;
1855 /* DAC group selection */
1856 else if (target == MI_TARGET_DAC) {
1857 if (this->running)
1858 return EBUSY;
1859 if (mc->un.ord >= this->dacs.ngroups)
1860 return EINVAL;
1861 return azalia_codec_construct_format(this,
1862 mc->un.ord, this->adcs.cur);
1865 /* ADC selection */
1866 else if (target == MI_TARGET_ADC) {
1867 if (this->running)
1868 return EBUSY;
1869 if (mc->un.ord >= this->adcs.ngroups)
1870 return EINVAL;
1871 return azalia_codec_construct_format(this,
1872 this->dacs.cur, mc->un.ord);
1875 /* Volume knob */
1876 else if (target == MI_TARGET_VOLUME) {
1877 if (mc->un.value.num_channels != 1)
1878 return EINVAL;
1879 if (!generic_mixer_validate_value(this, nid,
1880 target, mc->un.value.level[0]))
1881 return EINVAL;
1882 value = generic_mixer_to_device_value(this, nid, target,
1883 mc->un.value.level[0]) | CORB_VKNOB_DIRECT;
1884 err = this->comresp(this, nid, CORB_SET_VOLUME_KNOB,
1885 value, &result);
1886 if (err)
1887 return err;
1890 /* S/PDIF */
1891 else if (target == MI_TARGET_SPDIF) {
1892 err = this->comresp(this, nid, CORB_GET_DIGITAL_CONTROL,
1893 0, &result);
1894 result &= CORB_DCC_DIGEN | CORB_DCC_NAUDIO;
1895 result |= mc->un.mask & 0xff & ~CORB_DCC_DIGEN;
1896 err = this->comresp(this, nid, CORB_SET_DIGITAL_CONTROL_L,
1897 result, NULL);
1898 if (err)
1899 return err;
1900 } else if (target == MI_TARGET_SPDIF_CC) {
1901 if (mc->un.value.num_channels != 1)
1902 return EINVAL;
1903 if (mc->un.value.level[0] > 127)
1904 return EINVAL;
1905 err = this->comresp(this, nid, CORB_SET_DIGITAL_CONTROL_H,
1906 mc->un.value.level[0], NULL);
1907 if (err)
1908 return err;
1911 /* EAPD */
1912 else if (target == MI_TARGET_EAPD) {
1913 if (mc->un.ord >= 2)
1914 return EINVAL;
1915 err = this->comresp(this, nid,
1916 CORB_GET_EAPD_BTL_ENABLE, 0, &result);
1917 if (err)
1918 return err;
1919 result &= 0xff;
1920 if (mc->un.ord == 0) {
1921 result &= ~CORB_EAPD_EAPD;
1922 } else {
1923 result |= CORB_EAPD_EAPD;
1925 err = this->comresp(this, nid,
1926 CORB_SET_EAPD_BTL_ENABLE, result, &result);
1927 if (err)
1928 return err;
1931 /* Balanced I/O */
1932 else if (target == MI_TARGET_BALANCE) {
1933 if (mc->un.ord >= 2)
1934 return EINVAL;
1935 err = this->comresp(this, nid,
1936 CORB_GET_EAPD_BTL_ENABLE, 0, &result);
1937 if (err)
1938 return err;
1939 result &= 0xff;
1940 if (mc->un.ord == 0) {
1941 result &= ~CORB_EAPD_BTL;
1942 } else {
1943 result |= CORB_EAPD_BTL;
1945 err = this->comresp(this, nid,
1946 CORB_SET_EAPD_BTL_ENABLE, result, &result);
1947 if (err)
1948 return err;
1951 /* LR-Swap */
1952 else if (target == MI_TARGET_LRSWAP) {
1953 if (mc->un.ord >= 2)
1954 return EINVAL;
1955 err = this->comresp(this, nid,
1956 CORB_GET_EAPD_BTL_ENABLE, 0, &result);
1957 if (err)
1958 return err;
1959 result &= 0xff;
1960 if (mc->un.ord == 0) {
1961 result &= ~CORB_EAPD_LRSWAP;
1962 } else {
1963 result |= CORB_EAPD_LRSWAP;
1965 err = this->comresp(this, nid,
1966 CORB_SET_EAPD_BTL_ENABLE, result, &result);
1967 if (err)
1968 return err;
1971 else {
1972 aprint_error_dev(this->dev, "internal error in %s: target=%x\n",
1973 __func__, target);
1974 return -1;
1976 return 0;
1979 static int
1980 generic_mixer_pinctrl(codec_t *this, nid_t nid, uint32_t value)
1982 int err;
1983 uint32_t result;
1985 err = this->comresp(this, nid, CORB_GET_PIN_WIDGET_CONTROL, 0, &result);
1986 if (err)
1987 return err;
1988 result &= ~(CORB_PWC_OUTPUT | CORB_PWC_INPUT);
1989 result |= value & (CORB_PWC_OUTPUT | CORB_PWC_INPUT);
1990 return this->comresp(this, nid,
1991 CORB_SET_PIN_WIDGET_CONTROL, result, NULL);
1994 static u_char
1995 generic_mixer_from_device_value(const codec_t *this, nid_t nid, int target,
1996 uint32_t dv)
1998 #ifdef MAX_VOLUME_255
1999 uint32_t dmax;
2001 if (IS_MI_TARGET_INAMP(target))
2002 dmax = COP_AMPCAP_NUMSTEPS(this->w[nid].inamp_cap);
2003 else if (target == MI_TARGET_OUTAMP)
2004 dmax = COP_AMPCAP_NUMSTEPS(this->w[nid].outamp_cap);
2005 else if (target == MI_TARGET_VOLUME)
2006 dmax = COP_VKCAP_NUMSTEPS(this->w[nid].d.volume.cap);
2007 else {
2008 printf("unknown target: %d\n", target);
2009 dmax = 127;
2011 return dv * MIXER_DELTA(dmax);
2012 #else
2013 return dv;
2014 #endif
2017 static uint32_t
2018 generic_mixer_to_device_value(const codec_t *this, nid_t nid, int target,
2019 u_char uv)
2021 #ifdef MAX_VOLUME_255
2022 uint32_t dmax;
2024 if (IS_MI_TARGET_INAMP(target))
2025 dmax = COP_AMPCAP_NUMSTEPS(this->w[nid].inamp_cap);
2026 else if (target == MI_TARGET_OUTAMP)
2027 dmax = COP_AMPCAP_NUMSTEPS(this->w[nid].outamp_cap);
2028 else if (target == MI_TARGET_VOLUME)
2029 dmax = COP_VKCAP_NUMSTEPS(this->w[nid].d.volume.cap);
2030 else {
2031 printf("unknown target: %d\n", target);
2032 dmax = 127;
2034 return uv / MIXER_DELTA(dmax);
2035 #else
2036 return uv;
2037 #endif
2040 static uint32_t
2041 generic_mixer_max(const codec_t *this, nid_t nid,
2042 int target)
2044 #ifdef MAX_VOLUME_255
2045 return AUDIO_MAX_GAIN;
2046 #else
2047 uint32_t dmax;
2049 if (IS_MI_TARGET_INAMP(target))
2050 dmax = COP_AMPCAP_NUMSTEPS(this->w[nid].inamp_cap);
2051 else if (target == MI_TARGET_OUTAMP)
2052 dmax = COP_AMPCAP_NUMSTEPS(this->w[nid].outamp_cap);
2053 else if (target == MI_TARGET_VOLUME)
2054 dmax = COP_VKCAP_NUMSTEPS(this->w[nid].d.volume.cap);
2055 return dmax;
2056 #endif
2059 static bool
2060 generic_mixer_validate_value(const codec_t *this, nid_t nid,
2061 int target, u_char uv)
2063 #ifdef MAX_VOLUME_255
2064 return TRUE;
2065 #else
2066 return uv <= generic_mixer_max(this, nid, target);
2067 #endif
2070 static int
2071 generic_set_port(codec_t *this, mixer_ctrl_t *mc)
2073 const mixer_item_t *m;
2075 if (mc->dev >= this->nmixers)
2076 return ENXIO;
2077 m = &this->mixers[mc->dev];
2078 if (mc->type != m->devinfo.type)
2079 return EINVAL;
2080 if (mc->type == AUDIO_MIXER_CLASS)
2081 return 0; /* nothing to do */
2082 return generic_mixer_set(this, m->nid, m->target, mc);
2085 static int
2086 generic_get_port(codec_t *this, mixer_ctrl_t *mc)
2088 const mixer_item_t *m;
2090 if (mc->dev >= this->nmixers)
2091 return ENXIO;
2092 m = &this->mixers[mc->dev];
2093 mc->type = m->devinfo.type;
2094 if (mc->type == AUDIO_MIXER_CLASS)
2095 return 0; /* nothing to do */
2096 return generic_mixer_get(this, m->nid, m->target, mc);
2100 /* ----------------------------------------------------------------
2101 * Realtek ALC260
2103 * Fujitsu LOOX T70M/T
2104 * Internal Speaker: 0x10
2105 * Front Headphone: 0x14
2106 * Front mic: 0x12
2107 * ---------------------------------------------------------------- */
2109 #define ALC260_FUJITSU_ID 0x132610cf
2110 #define ALC260_EVENT_HP 0
2111 #define ALC260_EXTRA_MASTER 0
2112 static const mixer_item_t alc260_mixer_items[] = {
2113 AZ_MIXER_CLASSES,
2114 {{0, {AudioNmaster, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
2115 0, 0, .un.v={{"", 0}, 2, 3}}, 0x08, MI_TARGET_OUTAMP}, /* and 0x09, 0x0a(mono) */
2116 {{0, {AudioNmaster".mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2117 0, 0, ENUM_OFFON}, 0x0f, MI_TARGET_OUTAMP},
2118 {{0, {AudioNheadphone".mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2119 0, 0, ENUM_OFFON}, 0x10, MI_TARGET_OUTAMP},
2120 {{0, {AudioNheadphone".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2121 0, 0, ENUM_OFFON}, 0x10, MI_TARGET_PINBOOST},
2122 {{0, {AudioNmono".mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2123 0, 0, ENUM_OFFON}, 0x11, MI_TARGET_OUTAMP},
2124 {{0, {"mic1.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2125 0, 0, ENUM_OFFON}, 0x12, MI_TARGET_OUTAMP},
2126 {{0, {"mic1", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2127 0, 0, ENUM_IO}, 0x12, MI_TARGET_PINDIR},
2128 {{0, {"mic2.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2129 0, 0, ENUM_OFFON}, 0x13, MI_TARGET_OUTAMP},
2130 {{0, {"mic2", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2131 0, 0, ENUM_IO}, 0x13, MI_TARGET_PINDIR},
2132 {{0, {"line1.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2133 0, 0, ENUM_OFFON}, 0x14, MI_TARGET_OUTAMP},
2134 {{0, {"line1", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2135 0, 0, ENUM_IO}, 0x14, MI_TARGET_PINDIR},
2136 {{0, {"line2.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2137 0, 0, ENUM_OFFON}, 0x15, MI_TARGET_OUTAMP},
2138 {{0, {"line2", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2139 0, 0, ENUM_IO}, 0x15, MI_TARGET_PINDIR},
2141 {{0, {AudioNdac".mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
2142 0, 0, ENUM_OFFON}, 0x08, MI_TARGET_INAMP(0)}, /* and 0x09, 0x0a(mono) */
2143 {{0, {"mic1.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
2144 0, 0, ENUM_OFFON}, 0x07, MI_TARGET_INAMP(0)},
2145 {{0, {"mic1", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
2146 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(65)}}, 0x07, MI_TARGET_INAMP(0)},
2147 {{0, {"mic2.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
2148 0, 0, ENUM_OFFON}, 0x07, MI_TARGET_INAMP(1)},
2149 {{0, {"mic2", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
2150 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(65)}}, 0x07, MI_TARGET_INAMP(1)},
2151 {{0, {"line1.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
2152 0, 0, ENUM_OFFON}, 0x07, MI_TARGET_INAMP(2)},
2153 {{0, {"line1", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
2154 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(65)}}, 0x07, MI_TARGET_INAMP(2)},
2155 {{0, {"line2.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
2156 0, 0, ENUM_OFFON}, 0x07, MI_TARGET_INAMP(3)},
2157 {{0, {"line2", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
2158 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(65)}}, 0x07, MI_TARGET_INAMP(3)},
2159 {{0, {AudioNcd".mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
2160 0, 0, ENUM_OFFON}, 0x07, MI_TARGET_INAMP(4)},
2161 {{0, {AudioNcd, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
2162 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(65)}}, 0x07, MI_TARGET_INAMP(4)},
2163 {{0, {AudioNspeaker".mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
2164 0, 0, ENUM_OFFON}, 0x07, MI_TARGET_INAMP(5)},
2165 {{0, {AudioNspeaker, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
2166 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(65)}}, 0x07, MI_TARGET_INAMP(5)},
2168 {{0, {"adc04.source", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, 0, 0,
2169 .un.e={5, {{{"mic1", 0}, 0}, {{"mic2", 0}, 1}, {{"line1", 0}, 2},
2170 {{"line2", 0}, 3}, {{AudioNcd, 0}, 4}}}},
2171 0x04, MI_TARGET_CONNLIST},
2172 {{0, {"adc04.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, 0, 0,
2173 ENUM_OFFON}, 0x04, MI_TARGET_INAMP(0)},
2174 {{0, {"adc04", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD, 0, 0,
2175 .un.v={{"", 0}, 2, MIXER_DELTA(35)}}, 0x04, MI_TARGET_INAMP(0)},
2176 {{0, {"adc05.source", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, 0, 0,
2177 .un.e={6, {{{"mic1", 0}, 0}, {{"mic2", 0}, 1}, {{"line1", 0}, 2},
2178 {{"line2", 0}, 3}, {{AudioNcd, 0}, 4}, {{AudioNmixerout, 0}, 5}}}},
2179 0x05, MI_TARGET_CONNLIST},
2180 {{0, {"adc05.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, 0, 0,
2181 ENUM_OFFON}, 0x05, MI_TARGET_INAMP(0)},
2182 {{0, {"adc05", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD, 0, 0,
2183 .un.v={{"", 0}, 2, MIXER_DELTA(35)}}, 0x05, MI_TARGET_INAMP(0)},
2185 {{0, {AudioNmode, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_PLAYBACK, 0, 0,
2186 .un.e={2, {{{"analog", 0}, 0}, {{AzaliaNdigital, 0}, 1}}}}, 0, MI_TARGET_DAC},
2187 {{0, {AudioNmode, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, 0, 0,
2188 .un.e={3, {{{"adc04", 0}, 0}, {{"adc05", 0}, 1}, {{AzaliaNdigital, 0}, 2}}}},
2189 0, MI_TARGET_ADC},
2192 static const mixer_item_t alc260_loox_mixer_items[] = {
2193 AZ_MIXER_CLASSES,
2195 {{0, {AudioNmaster, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
2196 0, 0, .un.v={{"", 0}, 2, 3}}, 0x08, MI_TARGET_OUTAMP}, /* and 0x09, 0x0a(mono) */
2197 {{0, {AudioNmaster".mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2198 0, 0, ENUM_OFFON}, 0x10, MI_TARGET_OUTAMP},
2199 {{0, {AudioNmaster".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2200 0, 0, ENUM_OFFON}, 0x10, MI_TARGET_PINBOOST},
2201 {{0, {AudioNheadphone".mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2202 0, 0, ENUM_OFFON}, 0x14, MI_TARGET_OUTAMP},
2203 {{0, {AudioNheadphone".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2204 0, 0, ENUM_OFFON}, 0x14, MI_TARGET_PINBOOST},
2206 {{0, {AudioNdac".mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
2207 0, 0, ENUM_OFFON}, 0x08, MI_TARGET_INAMP(0)}, /* and 0x09, 0x0a(mono) */
2208 {{0, {AudioNmicrophone".mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
2209 0, 0, ENUM_OFFON}, 0x07, MI_TARGET_INAMP(0)},
2210 {{0, {AudioNmicrophone, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
2211 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(65)}}, 0x07, MI_TARGET_INAMP(0)},
2212 {{0, {AudioNcd".mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
2213 0, 0, ENUM_OFFON}, 0x07, MI_TARGET_INAMP(4)},
2214 {{0, {AudioNcd, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
2215 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(65)}}, 0x07, MI_TARGET_INAMP(4)},
2216 {{0, {AudioNspeaker".mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
2217 0, 0, ENUM_OFFON}, 0x07, MI_TARGET_INAMP(5)},
2218 {{0, {AudioNspeaker, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
2219 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(65)}}, 0x07, MI_TARGET_INAMP(5)},
2221 {{0, {"adc04.source", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, 0, 0,
2222 .un.e={2, {{{AudioNmicrophone, 0}, 0}, {{AudioNcd, 0}, 4}}}}, 0x04, MI_TARGET_CONNLIST},
2223 {{0, {"adc04.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, 0, 0,
2224 ENUM_OFFON}, 0x04, MI_TARGET_INAMP(0)},
2225 {{0, {"adc04", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD, 0, 0,
2226 .un.v={{"", 0}, 2, MIXER_DELTA(35)}}, 0x04, MI_TARGET_INAMP(0)},
2227 {{0, {"adc05.source", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, 0, 0,
2228 .un.e={3, {{{AudioNmicrophone, 0}, 0}, {{AudioNcd, 0}, 4}, {{AudioNmixerout, 0}, 5}}}},
2229 0x05, MI_TARGET_CONNLIST},
2230 {{0, {"adc05.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, 0, 0,
2231 ENUM_OFFON}, 0x05, MI_TARGET_INAMP(0)},
2232 {{0, {"adc05", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD, 0, 0,
2233 .un.v={{"", 0}, 2, MIXER_DELTA(35)}}, 0x05, MI_TARGET_INAMP(0)},
2235 {{0, {AudioNmode, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_PLAYBACK, 0, 0,
2236 .un.e={2, {{{"analog", 0}, 0}, {{AzaliaNdigital, 0}, 1}}}}, 0, MI_TARGET_DAC},
2237 {{0, {AudioNmode, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, 0, 0,
2238 .un.e={3, {{{"adc04", 0}, 0}, {{"adc05", 0}, 1}, {{AzaliaNdigital, 0}, 2}}}},
2239 0, MI_TARGET_ADC},
2242 static int
2243 alc260_mixer_init(codec_t *this)
2245 const mixer_item_t *mi;
2246 mixer_ctrl_t mc;
2247 uint32_t value;
2249 switch (this->subid) {
2250 case ALC260_FUJITSU_ID:
2251 this->nmixers = __arraycount(alc260_loox_mixer_items);
2252 mi = alc260_loox_mixer_items;
2253 break;
2254 default:
2255 this->nmixers = __arraycount(alc260_mixer_items);
2256 mi = alc260_mixer_items;
2258 this->mixers = malloc(sizeof(mixer_item_t) * this->nmixers,
2259 M_DEVBUF, M_NOWAIT);
2260 if (this->mixers == NULL) {
2261 aprint_error_dev(this->dev, "out of memory in %s\n", __func__);
2262 return ENOMEM;
2264 memcpy(this->mixers, mi, sizeof(mixer_item_t) * this->nmixers);
2265 generic_mixer_fix_indexes(this);
2266 generic_mixer_default(this);
2268 mc.dev = -1; /* no need for generic_mixer_set() */
2269 mc.type = AUDIO_MIXER_ENUM;
2270 mc.un.ord = 1; /* pindir: output */
2271 generic_mixer_set(this, 0x0f, MI_TARGET_PINDIR, &mc); /* lineout */
2272 generic_mixer_set(this, 0x10, MI_TARGET_PINDIR, &mc); /* headphones */
2273 mc.un.ord = 0; /* pindir: input */
2274 generic_mixer_set(this, 0x12, MI_TARGET_PINDIR, &mc); /* mic1 */
2275 generic_mixer_set(this, 0x13, MI_TARGET_PINDIR, &mc); /* mic2 */
2276 generic_mixer_set(this, 0x14, MI_TARGET_PINDIR, &mc); /* line1 */
2277 generic_mixer_set(this, 0x15, MI_TARGET_PINDIR, &mc); /* line2 */
2278 mc.un.ord = 0; /* mute: off */
2279 generic_mixer_set(this, 0x08, MI_TARGET_INAMP(0), &mc);
2280 generic_mixer_set(this, 0x08, MI_TARGET_INAMP(1), &mc);
2281 generic_mixer_set(this, 0x09, MI_TARGET_INAMP(0), &mc);
2282 generic_mixer_set(this, 0x09, MI_TARGET_INAMP(1), &mc);
2283 generic_mixer_set(this, 0x0a, MI_TARGET_INAMP(0), &mc);
2284 generic_mixer_set(this, 0x0a, MI_TARGET_INAMP(1), &mc);
2285 if (this->subid == ALC260_FUJITSU_ID) {
2286 mc.un.ord = 1; /* pindir: output */
2287 generic_mixer_set(this, 0x14, MI_TARGET_PINDIR, &mc); /* line1 */
2288 mc.un.ord = 4; /* connlist: cd */
2289 generic_mixer_set(this, 0x05, MI_TARGET_CONNLIST, &mc);
2290 /* setup a unsolicited event for the headphones */
2291 this->comresp(this, 0x14, CORB_SET_UNSOLICITED_RESPONSE,
2292 CORB_UNSOL_ENABLE | ALC260_EVENT_HP, NULL);
2293 this->extra[ALC260_EXTRA_MASTER] = 0; /* unmute */
2294 /* If the headphone presents, mute the internal speaker */
2295 this->comresp(this, 0x14, CORB_GET_PIN_SENSE, 0, &value);
2296 mc.un.ord = value & CORB_PS_PRESENCE ? 1 : 0;
2297 generic_mixer_set(this, 0x10, MI_TARGET_OUTAMP, &mc);
2298 this->get_port = alc260_get_port;
2300 return 0;
2303 static int
2304 alc260_init_dacgroup(codec_t *this)
2306 static const convgroupset_t dacs = {
2307 -1, 2,
2308 {{1, {0x02}}, /* analog 2ch */
2309 {1, {0x03}}}}; /* digital */
2310 static const convgroupset_t adcs = {
2311 -1, 3,
2312 {{1, {0x04}}, /* analog 2ch */
2313 {1, {0x05}}, /* analog 2ch */
2314 {1, {0x06}}}}; /* digital */
2316 this->dacs = dacs;
2317 this->adcs = adcs;
2318 return 0;
2321 static int
2322 alc260_set_port(codec_t *this, mixer_ctrl_t *mc)
2324 const mixer_item_t *m;
2325 mixer_ctrl_t mc2;
2326 uint32_t value;
2327 int err;
2329 if (mc->dev >= this->nmixers)
2330 return ENXIO;
2331 m = &this->mixers[mc->dev];
2332 if (mc->type != m->devinfo.type)
2333 return EINVAL;
2334 if (mc->type == AUDIO_MIXER_CLASS)
2335 return 0;
2336 if (m->nid == 0x08 && m->target == MI_TARGET_OUTAMP) {
2337 DPRINTF(("%s: hook for outputs.master\n", __func__));
2338 err = generic_mixer_set(this, m->nid, m->target, mc);
2339 if (!err) {
2340 generic_mixer_set(this, 0x09, m->target, mc);
2341 mc2 = *mc;
2342 mc2.un.value.num_channels = 1;
2343 mc2.un.value.level[0] = (mc2.un.value.level[0]
2344 + mc2.un.value.level[1]) / 2;
2345 generic_mixer_set(this, 0x0a, m->target, &mc2);
2347 return err;
2348 } else if (m->nid == 0x08 && m->target == MI_TARGET_INAMP(0)) {
2349 DPRINTF(("%s: hook for inputs.dac.mute\n", __func__));
2350 err = generic_mixer_set(this, m->nid, m->target, mc);
2351 if (!err) {
2352 generic_mixer_set(this, 0x09, m->target, mc);
2353 generic_mixer_set(this, 0x0a, m->target, mc);
2355 return err;
2356 } else if (m->nid == 0x04 &&
2357 m->target == MI_TARGET_CONNLIST &&
2358 m->devinfo.un.e.num_mem == 2) {
2359 if (1 <= mc->un.ord && mc->un.ord <= 3)
2360 return EINVAL;
2361 } else if (m->nid == 0x05 &&
2362 m->target == MI_TARGET_CONNLIST &&
2363 m->devinfo.un.e.num_mem == 3) {
2364 if (1 <= mc->un.ord && mc->un.ord <= 3)
2365 return EINVAL;
2366 } else if (this->subid == ALC260_FUJITSU_ID && m->nid == 0x10 &&
2367 m->target == MI_TARGET_OUTAMP) {
2368 if (mc->un.ord != 0 && mc->un.ord != 1)
2369 return EINVAL;
2370 this->extra[ALC260_EXTRA_MASTER] = mc->un.ord;
2371 err = this->comresp(this, 0x14, CORB_GET_PIN_SENSE, 0, &value);
2372 if (err)
2373 return err;
2374 if (!(value & CORB_PS_PRESENCE)) {
2375 return generic_mixer_set(this, m->nid, m->target, mc);
2377 return 0;
2379 return generic_mixer_set(this, m->nid, m->target, mc);
2382 static int
2383 alc260_get_port(codec_t *this, mixer_ctrl_t *mc)
2385 const mixer_item_t *m;
2387 if (mc->dev >= this->nmixers)
2388 return ENXIO;
2389 m = &this->mixers[mc->dev];
2390 mc->type = m->devinfo.type;
2391 if (mc->type == AUDIO_MIXER_CLASS)
2392 return 0;
2393 if (this->subid == ALC260_FUJITSU_ID && m->nid == 0x10 &&
2394 m->target == MI_TARGET_OUTAMP) {
2395 mc->un.ord = this->extra[ALC260_EXTRA_MASTER];
2396 return 0;
2398 return generic_mixer_get(this, m->nid, m->target, mc);
2401 static int
2402 alc260_unsol_event(codec_t *this, int tag)
2404 int err;
2405 uint32_t value;
2406 mixer_ctrl_t mc;
2408 switch (tag) {
2409 case ALC260_EVENT_HP:
2410 err = this->comresp(this, 0x14, CORB_GET_PIN_SENSE, 0, &value);
2411 if (err)
2412 break;
2413 mc.dev = -1;
2414 mc.type = AUDIO_MIXER_ENUM;
2415 if (value & CORB_PS_PRESENCE) {
2416 DPRINTF(("%s: headphone has been inserted.\n", __func__));
2417 mc.un.ord = 1; /* mute */
2418 generic_mixer_set(this, 0x10, MI_TARGET_OUTAMP, &mc);
2419 } else {
2420 DPRINTF(("%s: headphone has been pulled out.\n", __func__));
2421 mc.un.ord = this->extra[ALC260_EXTRA_MASTER];
2422 generic_mixer_set(this, 0x10, MI_TARGET_OUTAMP, &mc);
2424 break;
2425 default:
2426 printf("%s: unknown tag: %d\n", __func__, tag);
2428 return 0;
2431 /* ----------------------------------------------------------------
2432 * Realtek ALC262
2433 * ---------------------------------------------------------------- */
2435 static int
2436 alc262_init_widget(const codec_t *this, widget_t *w, nid_t nid)
2438 switch (nid) {
2439 case 0x0c:
2440 strlcpy(w->name, AudioNmaster, sizeof(w->name));
2441 break;
2444 return 0;
2447 /* ----------------------------------------------------------------
2448 * Realtek ALC268
2449 * ---------------------------------------------------------------- */
2451 static int
2452 alc268_init_dacgroup(codec_t *this)
2454 static const convgroupset_t dacs = {
2455 -1, 1,
2456 {{2, {0x02, 0x03}}}}; /* analog 4ch */
2457 static const convgroupset_t adcs = {
2458 -1, 1,
2459 {{2, {0x08, 0x07}}}}; /* analog 4ch */
2461 this->dacs = dacs;
2462 this->adcs = adcs;
2463 return 0;
2466 /* ----------------------------------------------------------------
2467 * Realtek ALC662-GR
2468 * ---------------------------------------------------------------- */
2470 static int
2471 alc662_init_dacgroup(codec_t *this)
2473 static const convgroupset_t dacs = {
2474 -1, 1,
2475 {{3, {0x02, 0x03, 0x04}}}}; /* analog 6ch */
2476 static const convgroupset_t adcs = {
2477 -1, 1,
2478 {{2, {0x09, 0x08}}}}; /* analog 4ch */
2480 this->dacs = dacs;
2481 this->adcs = adcs;
2482 return 0;
2485 /* ----------------------------------------------------------------
2486 * Realtek ALC861
2487 * ---------------------------------------------------------------- */
2489 static int
2490 alc861_init_dacgroup(codec_t *this)
2492 static const convgroupset_t dacs = {
2493 -1, 2,
2494 {{4, {0x03, 0x04, 0x05, 0x06}}, /* analog 8ch */
2495 {1, {0x07}}}}; /* digital */
2496 static const convgroupset_t adcs = {
2497 -1, 1,
2498 {{1, {0x08}}}}; /* analog 2ch */
2500 this->dacs = dacs;
2501 this->adcs = adcs;
2502 return 0;
2505 /* ----------------------------------------------------------------
2506 * Realtek ALC861-VD-GR
2507 * ---------------------------------------------------------------- */
2509 static int
2510 alc861vdgr_init_dacgroup(codec_t *this)
2512 static const convgroupset_t dacs = {
2513 -1, 2,
2514 {{4, {0x02, 0x03, 0x04, 0x05}}, /* analog 8ch */
2515 {1, {0x06}}}}; /* digital */
2516 static const convgroupset_t adcs = {
2517 -1, 1,
2518 {{1, {0x09}}}}; /* analog 2ch */
2520 this->dacs = dacs;
2521 this->adcs = adcs;
2522 return 0;
2525 /* ----------------------------------------------------------------
2526 * Realtek ALC880
2527 * ---------------------------------------------------------------- */
2529 static const mixer_item_t alc880_mixer_items[] = {
2530 AZ_MIXER_CLASSES,
2532 {{0, {AudioNsurround"."AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD,
2533 0, 0, .un.e={5, {{{"mic1", 0}, 0}, {{"mic2", 0}, 1}, {{"line1", 0}, 2},
2534 {{"line2", 0}, 3}, {{AudioNcd, 0}, 4}}}}, 0x08, MI_TARGET_CONNLIST},
2535 {{0, {AudioNsurround"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD,
2536 0, 0, ENUM_OFFON}, 0x08, MI_TARGET_INAMP(0)},
2537 {{0, {AudioNsurround, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD,
2538 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(35)}}, 0x08, MI_TARGET_INAMP(0)},
2540 {{0, {AzaliaNfront"."AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD,
2541 0, 0, .un.e={5, {{{"mic1", 0}, 0}, {{"mic2", 0}, 1}, {{"line1", 0}, 2},
2542 {{"line2", 0}, 3}, {{AudioNcd, 0}, 4},
2543 {{AudioNmixerout, 0}, 5}}}}, 0x09, MI_TARGET_CONNLIST},
2544 {{0, {AzaliaNfront"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD,
2545 0, 0, ENUM_OFFON}, 0x08, MI_TARGET_INAMP(0)},
2546 {{0, {AzaliaNfront, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD,
2547 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(35)}}, 0x09, MI_TARGET_INAMP(0)},
2549 {{0, {"mic1."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
2550 0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(0)},
2551 {{0, {"mic1", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
2552 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(65)}}, 0x0b, MI_TARGET_INAMP(0)},
2553 {{0, {"mic2."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
2554 0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(1)},
2555 {{0, {"mic2", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
2556 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(65)}}, 0x0b, MI_TARGET_INAMP(1)},
2557 {{0, {"line1."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
2558 0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(2)},
2559 {{0, {"line1", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
2560 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(65)}}, 0x0b, MI_TARGET_INAMP(2)},
2561 {{0, {"line2."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
2562 0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(3)},
2563 {{0, {"line2", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
2564 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(65)}}, 0x0b, MI_TARGET_INAMP(3)},
2565 {{0, {AudioNcd"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
2566 0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(4)},
2567 {{0, {AudioNcd, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
2568 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(65)}}, 0x0b, MI_TARGET_INAMP(4)},
2569 {{0, {AudioNspeaker"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
2570 0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(5)},
2571 {{0, {AudioNspeaker, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
2572 0, 0, .un.v={{"", 0}, 1, MIXER_DELTA(65)}}, 0x0b, MI_TARGET_INAMP(5)},
2574 {{0, {AudioNmaster, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
2575 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(64)}}, 0x0c, MI_TARGET_OUTAMP},
2576 {{0, {AzaliaNfront".dac.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
2577 0, 0, ENUM_OFFON}, 0x0c, MI_TARGET_INAMP(0)},
2578 {{0, {AzaliaNfront".mixer.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
2579 0, 0, ENUM_OFFON}, 0x0c, MI_TARGET_INAMP(1)},
2580 {{0, {AudioNsurround, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
2581 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(64)}}, 0x0d, MI_TARGET_OUTAMP},
2582 {{0, {AzaliaNclfe, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
2583 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(64)}}, 0x0e, MI_TARGET_OUTAMP},
2584 {{0, {AzaliaNside, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
2585 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(64)}}, 0x0f, MI_TARGET_OUTAMP},
2586 #if 0 /* The followings are useless. */
2587 {{0, {AudioNsurround".dac.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
2588 0, 0, ENUM_OFFON}, 0x0d, MI_TARGET_INAMP(0)},
2589 {{0, {AudioNsurround".mixer.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
2590 0, 0, ENUM_OFFON}, 0x0d, MI_TARGET_INAMP(1)},
2591 {{0, {AzaliaNclfe".dac.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
2592 0, 0, ENUM_OFFON}, 0x0e, MI_TARGET_INAMP(0)},
2593 {{0, {AzaliaNclfe".mixer.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
2594 0, 0, ENUM_OFFON}, 0x0e, MI_TARGET_INAMP(1)},
2595 {{0, {AzaliaNside".dac.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
2596 0, 0, ENUM_OFFON}, 0x0f, MI_TARGET_INAMP(0)},
2597 {{0, {AzaliaNside".mixer.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
2598 0, 0, ENUM_OFFON}, 0x0f, MI_TARGET_INAMP(1)},
2599 #endif
2601 {{0, {AudioNmaster"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2602 0, 0, ENUM_OFFON}, 0x14, MI_TARGET_OUTAMP},
2603 {{0, {AudioNmaster".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2604 0, 0, ENUM_OFFON}, 0x14, MI_TARGET_PINBOOST},
2605 {{0, {AudioNsurround"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2606 0, 0, ENUM_OFFON}, 0x15, MI_TARGET_OUTAMP},
2607 {{0, {AudioNsurround".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2608 0, 0, ENUM_OFFON}, 0x15, MI_TARGET_PINBOOST},
2609 {{0, {AzaliaNclfe"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2610 0, 0, ENUM_OFFON}, 0x16, MI_TARGET_OUTAMP},
2611 {{0, {AzaliaNclfe".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2612 0, 0, ENUM_OFFON}, 0x16, MI_TARGET_PINBOOST},
2613 {{0, {AzaliaNside"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2614 0, 0, ENUM_OFFON}, 0x17, MI_TARGET_OUTAMP},
2615 {{0, {AzaliaNside".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2616 0, 0, ENUM_OFFON}, 0x17, MI_TARGET_PINBOOST},
2618 {{0, {"mic2."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2619 0, 0, ENUM_OFFON}, 0x19, MI_TARGET_OUTAMP},
2620 {{0, {"mic2.boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2621 0, 0, ENUM_OFFON}, 0x19, MI_TARGET_PINBOOST},
2622 {{0, {"mic2.dir", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2623 0, 0, ENUM_IO}, 0x19, MI_TARGET_PINDIR},
2624 {{0, {"line1."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2625 0, 0, ENUM_OFFON}, 0x1a, MI_TARGET_OUTAMP},
2626 {{0, {"line1.boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2627 0, 0, ENUM_OFFON}, 0x1a, MI_TARGET_PINBOOST},
2628 {{0, {"line1.dir", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2629 0, 0, ENUM_IO}, 0x1a, MI_TARGET_PINDIR},
2630 {{0, {"line2."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2631 0, 0, ENUM_OFFON}, 0x1b, MI_TARGET_OUTAMP},
2632 {{0, {"line2.boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2633 0, 0, ENUM_OFFON}, 0x1b, MI_TARGET_PINBOOST},
2634 {{0, {"line2.dir", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2635 0, 0, ENUM_IO}, 0x1b, MI_TARGET_PINDIR},
2637 {{0, {AudioNmode, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_PLAYBACK, 0, 0,
2638 .un.e={2, {{{"analog", 0}, 0}, {{AzaliaNdigital, 0}, 1}}}}, 0, MI_TARGET_DAC},
2639 {{0, {AudioNmode, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, 0, 0,
2640 .un.e={2, {{{"analog", 0}, 0}, {{AzaliaNdigital, 0}, 1}}}}, 0, MI_TARGET_ADC},
2643 static int
2644 alc880_mixer_init(codec_t *this)
2646 mixer_ctrl_t mc;
2648 this->nmixers = __arraycount(alc880_mixer_items);
2649 this->mixers = malloc(sizeof(alc880_mixer_items), M_DEVBUF, M_NOWAIT);
2650 if (this->mixers == NULL) {
2651 aprint_error_dev(this->dev, "out of memory in %s\n", __func__);
2652 return ENOMEM;
2654 memcpy(this->mixers, alc880_mixer_items, sizeof(alc880_mixer_items));
2655 generic_mixer_fix_indexes(this);
2656 generic_mixer_default(this);
2658 mc.dev = -1;
2659 mc.type = AUDIO_MIXER_ENUM;
2660 mc.un.ord = 0; /* mute: off */
2661 generic_mixer_set(this, 0x0c, MI_TARGET_INAMP(0), &mc); /* dac->front */
2662 generic_mixer_set(this, 0x0c, MI_TARGET_INAMP(1), &mc); /* mixer->front */
2663 generic_mixer_set(this, 0x0d, MI_TARGET_INAMP(0), &mc); /* dac->surround */
2664 generic_mixer_set(this, 0x0e, MI_TARGET_INAMP(0), &mc); /* dac->clfe */
2665 generic_mixer_set(this, 0x0f, MI_TARGET_INAMP(0), &mc); /* dac->side */
2666 mc.un.ord = 1; /* mute: on */
2667 generic_mixer_set(this, 0x0d, MI_TARGET_INAMP(1), &mc); /* mixer->surround */
2668 generic_mixer_set(this, 0x0e, MI_TARGET_INAMP(1), &mc); /* mixer->clfe */
2669 generic_mixer_set(this, 0x0f, MI_TARGET_INAMP(1), &mc); /* mixer->side */
2670 mc.un.ord = 1; /* pindir: output */
2671 generic_mixer_set(this, 0x14, MI_TARGET_PINDIR, &mc); /* front */
2672 generic_mixer_set(this, 0x15, MI_TARGET_PINDIR, &mc); /* surround */
2673 generic_mixer_set(this, 0x16, MI_TARGET_PINDIR, &mc); /* clfe */
2674 generic_mixer_set(this, 0x17, MI_TARGET_PINDIR, &mc); /* side */
2675 generic_mixer_set(this, 0x19, MI_TARGET_PINDIR, &mc); /* mic2 */
2676 generic_mixer_set(this, 0x1b, MI_TARGET_PINDIR, &mc); /* line2 */
2677 mc.un.ord = 0; /* pindir: input */
2678 generic_mixer_set(this, 0x18, MI_TARGET_PINDIR, &mc); /* mic1 */
2679 generic_mixer_set(this, 0x1a, MI_TARGET_PINDIR, &mc); /* line1 */
2680 return 0;
2683 static int
2684 alc880_init_dacgroup(codec_t *this)
2686 static const convgroupset_t dacs = {
2687 -1, 2,
2688 {{4, {0x02, 0x03, 0x04, 0x05}}, /* analog 8ch */
2689 {1, {0x06}}}}; /* digital */
2690 static const convgroupset_t adcs = {
2691 -1, 2,
2692 {{2, {0x08, 0x09}}, /* analog 4ch */
2693 {1, {0x0a}}}}; /* digital */
2695 this->dacs = dacs;
2696 this->adcs = adcs;
2697 return 0;
2700 /* ----------------------------------------------------------------
2701 * Realtek ALC882
2702 * ---------------------------------------------------------------- */
2704 static const mixer_item_t alc882_mixer_items[] = {
2705 AZ_MIXER_CLASSES,
2707 /* 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x14,0x15,0x16,0x17 */
2708 {{0, {"mic1."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
2709 0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(0)},
2710 {{0, {"mic1", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
2711 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x0b, MI_TARGET_INAMP(0)},
2712 {{0, {"mic2."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
2713 0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(1)},
2714 {{0, {"mic2", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
2715 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x0b, MI_TARGET_INAMP(1)},
2716 {{0, {AudioNline"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
2717 0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(2)},
2718 {{0, {AudioNline, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
2719 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x0b, MI_TARGET_INAMP(2)},
2720 {{0, {AudioNcd"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
2721 0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(4)},
2722 {{0, {AudioNcd, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
2723 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x0b, MI_TARGET_INAMP(4)},
2724 {{0, {AudioNspeaker"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
2725 0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(5)},
2726 {{0, {AudioNspeaker, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
2727 0, 0, .un.v={{"", 0}, 1, MIXER_DELTA(31)}}, 0x0b, MI_TARGET_INAMP(5)},
2729 {{0, {AudioNmaster, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
2730 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x0c, MI_TARGET_OUTAMP},
2731 {{0, {AudioNmaster"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2732 0, 0, ENUM_OFFON}, 0x14, MI_TARGET_OUTAMP},
2733 {{0, {AudioNmaster".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2734 0, 0, ENUM_OFFON}, 0x14, MI_TARGET_PINBOOST},
2735 {{0, {AudioNheadphone"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2736 0, 0, ENUM_OFFON}, 0x1b, MI_TARGET_OUTAMP},
2737 {{0, {AudioNheadphone".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2738 0, 0, ENUM_OFFON}, 0x1b, MI_TARGET_PINBOOST},
2739 {{0, {AzaliaNfront".dac.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
2740 0, 0, ENUM_OFFON}, 0x0c, MI_TARGET_INAMP(0)},
2741 {{0, {AzaliaNfront".mixer.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
2742 0, 0, ENUM_OFFON}, 0x0c, MI_TARGET_INAMP(1)},
2744 {{0, {AudioNsurround, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
2745 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x0d, MI_TARGET_OUTAMP},
2746 {{0, {AudioNsurround"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2747 0, 0, ENUM_OFFON}, 0x15, MI_TARGET_OUTAMP},
2748 {{0, {AudioNsurround".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2749 0, 0, ENUM_OFFON}, 0x15, MI_TARGET_PINBOOST},
2751 {{0, {AzaliaNclfe, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
2752 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x0e, MI_TARGET_OUTAMP},
2753 {{0, {AzaliaNclfe"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2754 0, 0, ENUM_OFFON}, 0x16, MI_TARGET_OUTAMP},
2755 {{0, {AzaliaNclfe".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2756 0, 0, ENUM_OFFON}, 0x16, MI_TARGET_PINBOOST},
2758 {{0, {AzaliaNside, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
2759 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x0f, MI_TARGET_OUTAMP},
2760 {{0, {AzaliaNside"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2761 0, 0, ENUM_OFFON}, 0x17, MI_TARGET_OUTAMP},
2762 {{0, {AzaliaNside".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2763 0, 0, ENUM_OFFON}, 0x17, MI_TARGET_PINBOOST},
2765 /* 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x14,0x15,0x16,0x17,0xb */
2766 #define ALC882_MIC1 0x001
2767 #define ALC882_MIC2 0x002
2768 #define ALC882_LINE 0x004
2769 #define ALC882_CD 0x010
2770 #define ALC882_BEEP 0x020
2771 #define ALC882_MIX 0x400
2772 #define ALC882_MASK 0x437
2773 {{0, {AzaliaNfront"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD,
2774 0, 0, ENUM_OFFON}, 0x07, MI_TARGET_INAMP(0)},
2775 {{0, {AzaliaNfront, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD,
2776 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x07, MI_TARGET_INAMP(0)},
2777 {{0, {AzaliaNfront"."AudioNsource, 0}, AUDIO_MIXER_SET, AZ_CLASS_RECORD,
2778 0, 0, .un.s={6, {{{"mic1", 0}, ALC882_MIC1}, {{"mic2", 0}, ALC882_MIC2},
2779 {{AudioNline, 0}, ALC882_LINE}, {{AudioNcd, 0}, ALC882_CD},
2780 {{AudioNspeaker, 0}, ALC882_BEEP},
2781 {{AudioNmixerout, 0}, ALC882_MIX}}}}, 0x24, -1},
2782 {{0, {AudioNsurround"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD,
2783 0, 0, ENUM_OFFON}, 0x08, MI_TARGET_INAMP(0)},
2784 {{0, {AudioNsurround, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD,
2785 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x08, MI_TARGET_INAMP(0)},
2786 {{0, {AudioNsurround"."AudioNsource, 0}, AUDIO_MIXER_SET, AZ_CLASS_RECORD,
2787 0, 0, .un.s={6, {{{"mic1", 0}, ALC882_MIC1}, {{"mic2", 0}, ALC882_MIC2},
2788 {{AudioNline, 0}, ALC882_LINE}, {{AudioNcd, 0}, ALC882_CD},
2789 {{AudioNspeaker, 0}, ALC882_BEEP},
2790 {{AudioNmixerout, 0}, ALC882_MIX}}}}, 0x23, -1},
2791 {{0, {AzaliaNclfe"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD,
2792 0, 0, ENUM_OFFON}, 0x09, MI_TARGET_INAMP(0)},
2793 {{0, {AzaliaNclfe, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD,
2794 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x09, MI_TARGET_INAMP(0)},
2795 {{0, {AzaliaNclfe"."AudioNsource, 0}, AUDIO_MIXER_SET, AZ_CLASS_RECORD,
2796 0, 0, .un.s={6, {{{"mic1", 0}, ALC882_MIC1}, {{"mic2", 0}, ALC882_MIC2},
2797 {{AudioNline, 0}, ALC882_LINE}, {{AudioNcd, 0}, ALC882_CD},
2798 {{AudioNspeaker, 0}, ALC882_BEEP},
2799 {{AudioNmixerout, 0}, ALC882_MIX}}}}, 0x22, -1},
2801 {{0, {AudioNmode, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_PLAYBACK, 0, 0,
2802 .un.e={2, {{{"analog", 0}, 0}, {{AzaliaNdigital, 0}, 1}}}}, 0, MI_TARGET_DAC},
2803 {{0, {AudioNmode, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, 0, 0,
2804 .un.e={2, {{{"analog", 0}, 0}, {{AzaliaNdigital, 0}, 1}}}}, 0, MI_TARGET_ADC},
2805 /* AZ_MIXER_SPDIF(AZ_CLASS_PLAYBACK, 0x06),*/
2808 static int
2809 alc882_mixer_init(codec_t *this)
2811 mixer_ctrl_t mc;
2813 this->nmixers = __arraycount(alc882_mixer_items);
2814 this->mixers = malloc(sizeof(alc882_mixer_items), M_DEVBUF, M_NOWAIT);
2815 if (this->mixers == NULL) {
2816 aprint_error_dev(this->dev, "out of memory in %s\n", __func__);
2817 return ENOMEM;
2819 memcpy(this->mixers, alc882_mixer_items, sizeof(alc882_mixer_items));
2820 generic_mixer_fix_indexes(this);
2821 generic_mixer_default(this);
2823 mc.dev = -1;
2824 mc.type = AUDIO_MIXER_ENUM;
2825 mc.un.ord = 1; /* pindir: output */
2826 generic_mixer_set(this, 0x14, MI_TARGET_PINDIR, &mc);
2827 generic_mixer_set(this, 0x1b, MI_TARGET_PINDIR, &mc);
2828 generic_mixer_set(this, 0x15, MI_TARGET_PINDIR, &mc);
2829 generic_mixer_set(this, 0x16, MI_TARGET_PINDIR, &mc);
2830 generic_mixer_set(this, 0x17, MI_TARGET_PINDIR, &mc);
2831 mc.un.ord = 0; /* [0] 0x0c */
2832 generic_mixer_set(this, 0x14, MI_TARGET_CONNLIST, &mc);
2833 generic_mixer_set(this, 0x1b, MI_TARGET_CONNLIST, &mc);
2834 mc.un.ord = 1; /* [1] 0x0d */
2835 generic_mixer_set(this, 0x15, MI_TARGET_CONNLIST, &mc);
2836 mc.un.ord = 2; /* [2] 0x0e */
2837 generic_mixer_set(this, 0x16, MI_TARGET_CONNLIST, &mc);
2838 mc.un.ord = 2; /* [3] 0x0fb */
2839 generic_mixer_set(this, 0x17, MI_TARGET_CONNLIST, &mc);
2841 mc.un.ord = 0; /* pindir: input */
2842 generic_mixer_set(this, 0x18, MI_TARGET_PINDIR, &mc);
2843 generic_mixer_set(this, 0x19, MI_TARGET_PINDIR, &mc);
2844 generic_mixer_set(this, 0x1a, MI_TARGET_PINDIR, &mc);
2845 /* XXX: inamp for 18/19/1a */
2847 mc.un.ord = 0; /* unmute */
2848 generic_mixer_set(this, 0x24, MI_TARGET_INAMP(0), &mc);
2849 generic_mixer_set(this, 0x23, MI_TARGET_INAMP(1), &mc);
2850 generic_mixer_set(this, 0x22, MI_TARGET_INAMP(2), &mc);
2851 generic_mixer_set(this, 0x0d, MI_TARGET_INAMP(0), &mc);
2852 generic_mixer_set(this, 0x0e, MI_TARGET_INAMP(0), &mc);
2853 generic_mixer_set(this, 0x0f, MI_TARGET_INAMP(0), &mc);
2854 mc.un.ord = 1; /* mute */
2855 generic_mixer_set(this, 0x0d, MI_TARGET_INAMP(1), &mc);
2856 generic_mixer_set(this, 0x0e, MI_TARGET_INAMP(1), &mc);
2857 generic_mixer_set(this, 0x0f, MI_TARGET_INAMP(1), &mc);
2858 return 0;
2861 static int
2862 alc882_init_dacgroup(codec_t *this)
2864 #if 0 /* makes no sense to support for 0x25 node */
2865 static const convgroupset_t dacs = {
2866 -1, 3,
2867 {{4, {0x02, 0x03, 0x04, 0x05}}, /* analog 8ch */
2868 {1, {0x06}}, /* digital */
2869 {1, {0x25}}}}; /* another analog */
2870 #else
2871 static const convgroupset_t dacs = {
2872 -1, 2,
2873 {{4, {0x02, 0x03, 0x04, 0x05}}, /* analog 8ch */
2874 {1, {0x06}}}}; /* digital */
2875 #endif
2876 static const convgroupset_t adcs = {
2877 -1, 2,
2878 {{3, {0x07, 0x08, 0x09}}, /* analog 6ch */
2879 {1, {0x0a}}}}; /* digital */
2881 this->dacs = dacs;
2882 this->adcs = adcs;
2883 return 0;
2886 static int
2887 alc882_set_port(codec_t *this, mixer_ctrl_t *mc)
2889 const mixer_item_t *m;
2890 mixer_ctrl_t mc2;
2891 uint32_t mask, bit;
2892 int i, err;
2894 if (mc->dev >= this->nmixers)
2895 return ENXIO;
2896 m = &this->mixers[mc->dev];
2897 if (mc->type != m->devinfo.type)
2898 return EINVAL;
2899 if (mc->type == AUDIO_MIXER_CLASS)
2900 return 0;
2901 if ((m->nid == 0x22 || m->nid == 0x23 || m->nid == 0x24)
2902 && m->target == -1) {
2903 DPRINTF(("%s: hook for record.*.source\n", __func__));
2904 mc2.dev = -1;
2905 mc2.type = AUDIO_MIXER_ENUM;
2906 bit = 1;
2907 mask = mc->un.mask & ALC882_MASK;
2908 for (i = 0; i < this->w[m->nid].nconnections && i < 32; i++) {
2909 mc2.un.ord = (mask & bit) ? 0 : 1;
2910 err = generic_mixer_set(this, m->nid,
2911 MI_TARGET_INAMP(i), &mc2);
2912 if (err)
2913 return err;
2914 bit = bit << 1;
2916 return 0;
2918 return generic_mixer_set(this, m->nid, m->target, mc);
2921 static int
2922 alc882_get_port(codec_t *this, mixer_ctrl_t *mc)
2924 const mixer_item_t *m;
2925 uint32_t mask, bit, result;
2926 int i, err;
2928 if (mc->dev >= this->nmixers)
2929 return ENXIO;
2930 m = &this->mixers[mc->dev];
2931 mc->type = m->devinfo.type;
2932 if (mc->type == AUDIO_MIXER_CLASS)
2933 return 0;
2934 if ((m->nid == 0x22 || m->nid == 0x23 || m->nid == 0x24)
2935 && m->target == -1) {
2936 DPRINTF(("%s: hook for record.*.source\n", __func__));
2937 mask = 0;
2938 bit = 1;
2939 for (i = 0; i < this->w[m->nid].nconnections && i < 32; i++) {
2940 err = this->comresp(this, m->nid, CORB_GET_AMPLIFIER_GAIN_MUTE,
2941 CORB_GAGM_INPUT | CORB_GAGM_LEFT |
2942 i, &result);
2943 if (err)
2944 return err;
2945 if ((result & CORB_GAGM_MUTE) == 0)
2946 mask |= bit;
2947 bit = bit << 1;
2949 mc->un.mask = mask & ALC882_MASK;
2950 return 0;
2952 return generic_mixer_get(this, m->nid, m->target, mc);
2955 /* ----------------------------------------------------------------
2956 * Realtek ALC883
2957 * ALC882 without adc07 and mix24.
2958 * ---------------------------------------------------------------- */
2960 static int
2961 alc883_init_dacgroup(codec_t *this)
2963 static const convgroupset_t dacs = {
2964 -1, 2,
2965 {{4, {0x02, 0x03, 0x04, 0x05}}, /* analog 8ch */
2966 {1, {0x06}}}}; /* digital */
2967 /* don't support for 0x25 dac */
2968 static const convgroupset_t adcs = {
2969 -1, 2,
2970 {{2, {0x08, 0x09}}, /* analog 4ch */
2971 {1, {0x0a}}}}; /* digital */
2973 this->dacs = dacs;
2974 this->adcs = adcs;
2975 return 0;
2978 static const mixer_item_t alc883_mixer_items[] = {
2979 AZ_MIXER_CLASSES,
2981 /* 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x14,0x15,0x16,0x17 */
2982 {{0, {"mic1."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
2983 0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(0)},
2984 {{0, {"mic1", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
2985 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x0b, MI_TARGET_INAMP(0)},
2986 {{0, {"mic2."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
2987 0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(1)},
2988 {{0, {"mic2", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
2989 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x0b, MI_TARGET_INAMP(1)},
2990 {{0, {AudioNline"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
2991 0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(2)},
2992 {{0, {AudioNline, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
2993 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x0b, MI_TARGET_INAMP(2)},
2994 {{0, {AudioNcd"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
2995 0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(4)},
2996 {{0, {AudioNcd, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
2997 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x0b, MI_TARGET_INAMP(4)},
2998 {{0, {AudioNspeaker"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
2999 0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(5)},
3000 {{0, {AudioNspeaker, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
3001 0, 0, .un.v={{"", 0}, 1, MIXER_DELTA(31)}}, 0x0b, MI_TARGET_INAMP(5)},
3003 {{0, {AudioNmaster, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
3004 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x0c, MI_TARGET_OUTAMP},
3005 {{0, {AudioNmaster"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
3006 0, 0, ENUM_OFFON}, 0x14, MI_TARGET_OUTAMP},
3007 {{0, {AudioNmaster".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
3008 0, 0, ENUM_OFFON}, 0x14, MI_TARGET_PINBOOST},
3009 {{0, {AudioNheadphone"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
3010 0, 0, ENUM_OFFON}, 0x1b, MI_TARGET_OUTAMP},
3011 {{0, {AudioNheadphone".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
3012 0, 0, ENUM_OFFON}, 0x1b, MI_TARGET_PINBOOST},
3013 {{0, {AzaliaNfront".dac.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
3014 0, 0, ENUM_OFFON}, 0x0c, MI_TARGET_INAMP(0)},
3015 {{0, {AzaliaNfront".mixer.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
3016 0, 0, ENUM_OFFON}, 0x0c, MI_TARGET_INAMP(1)},
3017 {{0, {AudioNsurround, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
3018 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x0d, MI_TARGET_OUTAMP},
3019 {{0, {AudioNsurround"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
3020 0, 0, ENUM_OFFON}, 0x15, MI_TARGET_OUTAMP},
3021 {{0, {AudioNsurround".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
3022 0, 0, ENUM_OFFON}, 0x15, MI_TARGET_PINBOOST},
3023 {{0, {AudioNsurround".dac.mut", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
3024 0, 0, ENUM_OFFON}, 0x0d, MI_TARGET_INAMP(0)},
3025 {{0, {AudioNsurround".mixer.m", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
3026 0, 0, ENUM_OFFON}, 0x0d, MI_TARGET_INAMP(1)},
3028 {{0, {AzaliaNclfe, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
3029 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x0e, MI_TARGET_OUTAMP},
3030 {{0, {AzaliaNclfe"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
3031 0, 0, ENUM_OFFON}, 0x16, MI_TARGET_OUTAMP},
3032 {{0, {AzaliaNclfe".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
3033 0, 0, ENUM_OFFON}, 0x16, MI_TARGET_PINBOOST},
3034 {{0, {AzaliaNclfe".dac.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
3035 0, 0, ENUM_OFFON}, 0x0e, MI_TARGET_INAMP(0)},
3036 {{0, {AzaliaNclfe".mixer.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
3037 0, 0, ENUM_OFFON}, 0x0e, MI_TARGET_INAMP(1)},
3039 {{0, {AzaliaNside, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
3040 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x0f, MI_TARGET_OUTAMP},
3041 {{0, {AzaliaNside"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
3042 0, 0, ENUM_OFFON}, 0x17, MI_TARGET_OUTAMP},
3043 {{0, {AzaliaNside".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
3044 0, 0, ENUM_OFFON}, 0x17, MI_TARGET_PINBOOST},
3045 {{0, {AzaliaNside".dac.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
3046 0, 0, ENUM_OFFON}, 0x0f, MI_TARGET_INAMP(0)},
3047 {{0, {AzaliaNside".mixer.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
3048 0, 0, ENUM_OFFON}, 0x0f, MI_TARGET_INAMP(1)},
3050 {{0, {AudioNsurround"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD,
3051 0, 0, ENUM_OFFON}, 0x08, MI_TARGET_INAMP(0)},
3052 {{0, {AudioNsurround, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD,
3053 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x08, MI_TARGET_INAMP(0)},
3054 {{0, {AudioNsurround"."AudioNsource, 0}, AUDIO_MIXER_SET, AZ_CLASS_RECORD,
3055 0, 0, .un.s={6, {{{"mic1", 0}, ALC882_MIC1}, {{"mic2", 0}, ALC882_MIC2},
3056 {{AudioNline, 0}, ALC882_LINE}, {{AudioNcd, 0}, ALC882_CD},
3057 {{AudioNspeaker, 0}, ALC882_BEEP},
3058 {{AudioNmixerout, 0}, ALC882_MIX}}}}, 0x23, -1},
3060 {{0, {AzaliaNclfe"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD,
3061 0, 0, ENUM_OFFON}, 0x09, MI_TARGET_INAMP(0)},
3062 {{0, {AzaliaNclfe, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD,
3063 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x09, MI_TARGET_INAMP(0)},
3064 {{0, {AzaliaNclfe"."AudioNsource, 0}, AUDIO_MIXER_SET, AZ_CLASS_RECORD,
3065 0, 0, .un.s={6, {{{"mic1", 0}, ALC882_MIC1}, {{"mic2", 0}, ALC882_MIC2},
3066 {{AudioNline, 0}, ALC882_LINE}, {{AudioNcd, 0}, ALC882_CD},
3067 {{AudioNspeaker, 0}, ALC882_BEEP},
3068 {{AudioNmixerout, 0}, ALC882_MIX}}}}, 0x22, -1},
3070 {{0, {"usingdac", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, 0, 0,
3071 .un.e={2, {{{"analog", 0}, 0}, {{"digital", 0}, 1}}}}, 0, MI_TARGET_DAC},
3072 {{0, {"usingadc", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, 0, 0,
3073 .un.e={2, {{{"analog", 0}, 0}, {{"digital", 0}, 1}}}}, 0, MI_TARGET_ADC},
3077 alc883_mixer_init(codec_t *this)
3079 mixer_ctrl_t mc;
3081 this->nmixers = __arraycount(alc883_mixer_items);
3082 this->mixers = malloc(sizeof(alc883_mixer_items), M_DEVBUF, M_NOWAIT);
3083 if (this->mixers == NULL) {
3084 aprint_error_dev(this->dev, "out of memory in %s\n", __func__);
3085 return ENOMEM;
3087 memcpy(this->mixers, alc883_mixer_items, sizeof(alc883_mixer_items));
3088 generic_mixer_fix_indexes(this);
3089 generic_mixer_default(this);
3091 mc.dev = -1;
3092 mc.type = AUDIO_MIXER_ENUM;
3093 mc.un.ord = 1; /* pindir: output */
3094 generic_mixer_set(this, 0x14, MI_TARGET_PINDIR, &mc);
3095 generic_mixer_set(this, 0x1b, MI_TARGET_PINDIR, &mc);
3096 generic_mixer_set(this, 0x15, MI_TARGET_PINDIR, &mc);
3097 generic_mixer_set(this, 0x16, MI_TARGET_PINDIR, &mc);
3098 generic_mixer_set(this, 0x17, MI_TARGET_PINDIR, &mc);
3099 mc.un.ord = 0; /* [0] 0x0c */
3100 generic_mixer_set(this, 0x14, MI_TARGET_CONNLIST, &mc);
3101 generic_mixer_set(this, 0x1b, MI_TARGET_CONNLIST, &mc);
3102 mc.un.ord = 1; /* [1] 0x0d */
3103 generic_mixer_set(this, 0x15, MI_TARGET_CONNLIST, &mc);
3104 mc.un.ord = 2; /* [2] 0x0e */
3105 generic_mixer_set(this, 0x16, MI_TARGET_CONNLIST, &mc);
3106 mc.un.ord = 2; /* [3] 0x0fb */
3107 generic_mixer_set(this, 0x17, MI_TARGET_CONNLIST, &mc);
3109 mc.un.ord = 0; /* pindir: input */
3110 generic_mixer_set(this, 0x18, MI_TARGET_PINDIR, &mc);
3111 generic_mixer_set(this, 0x19, MI_TARGET_PINDIR, &mc);
3112 generic_mixer_set(this, 0x1a, MI_TARGET_PINDIR, &mc);
3113 /* XXX: inamp for 18/19/1a */
3115 mc.un.ord = 0; /* unmute */
3116 generic_mixer_set(this, 0x23, MI_TARGET_INAMP(1), &mc);
3117 generic_mixer_set(this, 0x22, MI_TARGET_INAMP(2), &mc);
3118 return 0;
3120 /* ----------------------------------------------------------------
3121 * Realtek ALC885
3122 * ---------------------------------------------------------------- */
3124 static int
3125 alc885_init_dacgroup(codec_t *this)
3127 static const convgroupset_t dacs = {
3128 -1, 2,
3129 {{4, {0x02, 0x03, 0x04, 0x05}}, /* analog 8ch */
3130 {1, {0x06}}}}; /* digital */
3131 /* don't support for 0x25 dac */
3132 static const convgroupset_t adcs = {
3133 -1, 2,
3134 {{3, {0x07, 0x08, 0x09}}, /* analog 6ch */
3135 {1, {0x0a}}}}; /* digital */
3137 this->dacs = dacs;
3138 this->adcs = adcs;
3139 return 0;
3142 /* ----------------------------------------------------------------
3143 * Realtek ALC888
3144 * ---------------------------------------------------------------- */
3146 static int
3147 alc888_init_dacgroup(codec_t *this)
3149 static const convgroupset_t dacs = {
3150 -1, 2,
3151 {{4, {0x02, 0x03, 0x04, 0x05}}, /* analog 8ch */
3152 {1, {0x06}}}}; /* digital */
3153 /* don't support for 0x25 dac */
3154 /* ALC888S has another SPDIF-out 0x10 */
3155 static const convgroupset_t adcs = {
3156 -1, 2,
3157 {{2, {0x08, 0x09}}, /* analog 4ch */
3158 {1, {0x0a}}}}; /* digital */
3160 this->dacs = dacs;
3161 this->adcs = adcs;
3162 return 0;
3165 static int
3166 alc888_init_widget(const codec_t *this, widget_t *w, nid_t nid)
3168 switch (nid) {
3169 case 0x0c:
3170 strlcpy(w->name, AudioNmaster, sizeof(w->name));
3171 break;
3173 return 0;
3176 static int
3177 alc888_mixer_init(codec_t *this)
3179 mixer_item_t *m, *mdac = NULL;
3180 mixer_devinfo_t *d;
3181 int err, i;
3183 err = generic_mixer_init(this);
3184 if (err)
3185 return err;
3187 /* Clear mixer indexes, to make generic_mixer_fix_indexes happy */
3188 for (i = 0; i < this->nmixers; i++) {
3189 d = &this->mixers[i].devinfo;
3190 d->index = d->prev = d->next = 0;
3193 /* We're looking for front l/r mixer, which we know is nid 0x0c */
3194 for (i = 0; i < this->nmixers; i++)
3195 if (this->mixers[i].nid == 0x0c) {
3196 mdac = &this->mixers[i];
3197 break;
3199 if (mdac) {
3201 * ALC888 doesn't have a master mixer, so create a fake
3202 * inputs.dac that mirrors outputs.master
3204 err = generic_mixer_ensure_capacity(this, this->nmixers + 1);
3205 if (err)
3206 return err;
3208 m = &this->mixers[this->nmixers];
3209 d = &m->devinfo;
3210 memcpy(m, mdac, sizeof(*m));
3211 d->mixer_class = AZ_CLASS_INPUT;
3212 snprintf(d->label.name, sizeof(d->label.name), AudioNdac);
3213 this->nmixers++;
3216 /* Recreate mixer indexes and defaults after making a mess of things */
3217 generic_mixer_fix_indexes(this);
3218 generic_mixer_default(this);
3220 return 0;
3223 /* ----------------------------------------------------------------
3224 * Analog Devices AD1981HD
3225 * ---------------------------------------------------------------- */
3227 #define AD1981HD_THINKPAD 0x201017aa
3229 static const mixer_item_t ad1981hd_mixer_items[] = {
3230 AZ_MIXER_CLASSES,
3232 {{0, {AzaliaNdigital "." AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
3233 0, 0, .un.e={2, {{{"os", 0}, 0}, {{"adc", 0}, 1}}}}, 0x02, MI_TARGET_CONNLIST},
3235 {{0, {"lineout." AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
3236 0, 0, .un.e={2, {{{AudioNdac, 0}, 0}, {{AudioNmixerout, 0}, 1}}}},
3237 0x05, MI_TARGET_CONNLIST},
3238 {{0, {"lineout." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
3239 0, 0, ENUM_OFFON}, 0x05, MI_TARGET_OUTAMP},
3240 {{0, {"lineout", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
3241 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(63)}}, 0x05, MI_TARGET_OUTAMP},
3242 {{0, {"lineout", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD,
3243 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(3)}}, 0x05, MI_TARGET_INAMP(0)},
3244 {{0, {"lineout.dir", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
3245 0, 0, ENUM_IO}, 0x05, MI_TARGET_PINDIR},
3246 {{0, {"lineout.boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
3247 0, 0, ENUM_OFFON}, 0x05, MI_TARGET_PINBOOST},
3248 {{0, {"lineout.eapd", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
3249 0, 0, ENUM_OFFON}, 0x05, MI_TARGET_EAPD},
3251 {{0, {AudioNheadphone ".src", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
3252 0, 0, .un.e={2, {{{AudioNdac, 0}, 0}, {{AudioNmixerout, 0}, 1}}}},
3253 0x06, MI_TARGET_CONNLIST},
3254 {{0, {AudioNheadphone "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
3255 0, 0, ENUM_OFFON}, 0x06, MI_TARGET_OUTAMP},
3256 {{0, {AudioNheadphone, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
3257 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(63)}}, 0x06, MI_TARGET_OUTAMP},
3258 {{0, {AudioNheadphone ".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
3259 0, 0, ENUM_OFFON}, 0x06, MI_TARGET_PINBOOST},
3261 {{0, {AudioNmono "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
3262 0, 0, ENUM_OFFON}, 0x07, MI_TARGET_OUTAMP},
3263 {{0, {AudioNmono, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
3264 0, 0, .un.v={{"", 0}, 1, MIXER_DELTA(63)}}, 0x07, MI_TARGET_OUTAMP},
3266 {{0, {AudioNmicrophone, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD,
3267 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(3)}}, 0x08, MI_TARGET_INAMP(0)},
3269 {{0, {"linein." AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
3270 0, 0, .un.e={2, {{{AudioNdac, 0}, 0}, {{AudioNmixerout, 0}, 1}}}},
3271 0x09, MI_TARGET_CONNLIST},
3272 {{0, {"linein." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
3273 0, 0, ENUM_OFFON}, 0x09, MI_TARGET_OUTAMP},
3274 {{0, {"linein", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
3275 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(63)}}, 0x09, MI_TARGET_OUTAMP},
3276 {{0, {"linein", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD,
3277 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(3)}}, 0x09, MI_TARGET_INAMP(0)},
3278 {{0, {"linein.dir", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
3279 0, 0, ENUM_IO}, 0x09, MI_TARGET_PINDIR},
3281 {{0, {AudioNmono "." AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
3282 0, 0, .un.e={6, {{{AudioNdac, 0}, 0}, {{"mixedmic", 0}, 1},
3283 {{"linein", 0}, 2}, {{AudioNmixerout, 0}, 3},
3284 {{"lineout", 0}, 4}, {{"mic2", 0}, 5}}}},
3285 0x0b, MI_TARGET_CONNLIST},
3287 {{0, {"beep." AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
3288 0, 0, .un.e={2, {{{"digitalbeep", 0}, 0}, {{"beep", 0}, 1}}}},
3289 0x0d, MI_TARGET_CONNLIST},
3290 {{0, {"beep." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
3291 0, 0, ENUM_OFFON}, 0x0d, MI_TARGET_OUTAMP},
3292 {{0, {"beep", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
3293 0, 0, .un.v={{"", 0}, 1, MIXER_DELTA(15)}}, 0x0d, MI_TARGET_OUTAMP},
3295 {{0, {AudioNdac "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
3296 0, 0, ENUM_OFFON}, 0x11, MI_TARGET_OUTAMP},
3297 {{0, {AudioNdac, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
3298 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x11, MI_TARGET_OUTAMP},
3300 {{0, {AudioNmicrophone "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
3301 0, 0, ENUM_OFFON}, 0x12, MI_TARGET_OUTAMP},
3302 {{0, {AudioNmicrophone, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
3303 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x12, MI_TARGET_OUTAMP},
3305 {{0, {"linein." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
3306 0, 0, ENUM_OFFON}, 0x13, MI_TARGET_OUTAMP},
3307 {{0, {"linein", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
3308 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x13, MI_TARGET_OUTAMP},
3310 {{0, {AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD,
3311 0, 0, .un.e={8, {{{"mixedmic", 0}, 0}, {{"linein", 0}, 1},
3312 {{AudioNmixerout, 0}, 2}, {{AudioNmono, 0}, 3},
3313 {{AudioNcd, 0}, 4}, {{"lineout", 0}, 5},
3314 {{"mic2", 0}, 6}, {{AudioNaux, 0}, 7}}}},
3315 0x15, MI_TARGET_CONNLIST},
3316 {{0, {AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD,
3317 0, 0, ENUM_OFFON}, 0x15, MI_TARGET_OUTAMP},
3318 {{0, {AudioNmaster, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD,
3319 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(15)}}, 0x15, MI_TARGET_OUTAMP},
3321 {{0, {"mic2." AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
3322 0, 0, .un.e={2, {{{AudioNdac, 0}, 0}, {{AudioNmixerout, 0}, 1}}}},
3323 0x18, MI_TARGET_CONNLIST},
3324 {{0, {"mic2." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
3325 0, 0, ENUM_OFFON}, 0x18, MI_TARGET_OUTAMP},
3326 {{0, {"mic2", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
3327 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(63)}}, 0x18, MI_TARGET_OUTAMP},
3328 {{0, {"mic2", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD,
3329 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(3)}}, 0x18, MI_TARGET_INAMP(0)},
3330 {{0, {"mic2.dir", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
3331 0, 0, ENUM_IO}, 0x18, MI_TARGET_PINDIR},
3333 {{0, {"lineout." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
3334 0, 0, ENUM_OFFON}, 0x1a, MI_TARGET_OUTAMP},
3335 {{0, {"lineout", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
3336 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x1a, MI_TARGET_OUTAMP},
3338 {{0, {AudioNaux "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
3339 0, 0, ENUM_OFFON}, 0x1b, MI_TARGET_OUTAMP},
3340 {{0, {AudioNaux, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
3341 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x1b, MI_TARGET_OUTAMP},
3343 {{0, {"mic2." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
3344 0, 0, ENUM_OFFON}, 0x1c, MI_TARGET_OUTAMP},
3345 {{0, {"mic2", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
3346 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x1c, MI_TARGET_OUTAMP},
3348 {{0, {AudioNcd "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
3349 0, 0, ENUM_OFFON}, 0x1d, MI_TARGET_OUTAMP},
3350 {{0, {AudioNcd, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
3351 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x1d, MI_TARGET_OUTAMP},
3353 {{0, {"mixedmic.mute1", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD,
3354 0, 0, ENUM_OFFON}, 0x1e, MI_TARGET_OUTAMP},
3355 {{0, {"mixedmic.mute2", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD,
3356 0, 0, ENUM_OFFON}, 0x1f, MI_TARGET_OUTAMP},
3358 {{0, {AudioNmode, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_PLAYBACK, 0, 0,
3359 .un.e={2, {{{"analog", 0}, 0}, {{AzaliaNdigital, 0}, 1}}}}, 0, MI_TARGET_DAC},
3362 static int
3363 ad1981hd_init_widget(const codec_t *this, widget_t *w, nid_t nid)
3365 switch (nid) {
3366 case 0x05:
3367 strlcpy(w->name, AudioNline "out", sizeof(w->name));
3368 break;
3369 case 0x06:
3370 strlcpy(w->name, "hp", sizeof(w->name));
3371 break;
3372 case 0x07:
3373 strlcpy(w->name, AudioNmono, sizeof(w->name));
3374 break;
3375 case 0x08:
3376 strlcpy(w->name, AudioNmicrophone, sizeof(w->name));
3377 break;
3378 case 0x09:
3379 strlcpy(w->name, AudioNline "in", sizeof(w->name));
3380 break;
3381 case 0x0d:
3382 strlcpy(w->name, "beep", sizeof(w->name));
3383 break;
3384 case 0x17:
3385 strlcpy(w->name, AudioNaux, sizeof(w->name));
3386 break;
3387 case 0x18:
3388 strlcpy(w->name, AudioNmicrophone "2", sizeof(w->name));
3389 break;
3390 case 0x19:
3391 strlcpy(w->name, AudioNcd, sizeof(w->name));
3392 break;
3393 case 0x1d:
3394 strlcpy(w->name, AudioNcd, sizeof(w->name));
3395 break;
3397 return 0;
3400 static int
3401 ad1981hd_mixer_init(codec_t *this)
3403 mixer_ctrl_t mc;
3405 this->nmixers = __arraycount(ad1981hd_mixer_items);
3406 this->mixers = malloc(sizeof(ad1981hd_mixer_items), M_DEVBUF, M_NOWAIT);
3407 if (this->mixers == NULL) {
3408 aprint_error_dev(this->dev, "out of memory in %s\n", __func__);
3409 return ENOMEM;
3411 memcpy(this->mixers, ad1981hd_mixer_items, sizeof(ad1981hd_mixer_items));
3412 generic_mixer_fix_indexes(this);
3413 generic_mixer_default(this);
3415 if (this->subid == AD1981HD_THINKPAD) {
3416 mc.dev = -1;
3417 mc.type = AUDIO_MIXER_ENUM;
3418 mc.un.ord = 1;
3419 generic_mixer_set(this, 0x09, MI_TARGET_PINDIR, &mc);
3420 generic_mixer_set(this, 0x05, MI_TARGET_EAPD, &mc);
3421 mc.type = AUDIO_MIXER_VALUE;
3422 mc.un.value.num_channels = 2;
3423 mc.un.value.level[0] = AUDIO_MAX_GAIN;
3424 mc.un.value.level[1] = AUDIO_MAX_GAIN;
3425 generic_mixer_set(this, 0x1a, MI_TARGET_VOLUME, &mc);
3427 return 0;
3430 /* ----------------------------------------------------------------
3431 * Analog Devices AD1983
3432 * ---------------------------------------------------------------- */
3434 static const mixer_item_t ad1983_mixer_items[] = {
3435 AZ_MIXER_CLASSES,
3437 {{0, {AzaliaNdigital "." AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
3438 0, 0, .un.e={2, {{{"os", 0}, 0}, {{"adc", 0}, 1}}}}, 0x02, MI_TARGET_CONNLIST},
3440 {{0, {AudioNspeaker "." AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
3441 0, 0, .un.e={2, {{{AudioNdac, 0}, 0}, {{AudioNmixerout, 0}, 1}}}},
3442 0x05, MI_TARGET_CONNLIST},
3443 {{0, {AudioNspeaker "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
3444 0, 0, ENUM_OFFON}, 0x05, MI_TARGET_OUTAMP},
3445 {{0, {AudioNspeaker, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
3446 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(64)}}, 0x05, MI_TARGET_OUTAMP},
3448 {{0, {AudioNheadphone ".src", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
3449 0, 0, .un.e={2, {{{AudioNdac, 0}, 0}, {{AudioNmixerout, 0}, 1}}}},
3450 0x06, MI_TARGET_CONNLIST},
3451 {{0, {AudioNheadphone "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
3452 0, 0, ENUM_OFFON}, 0x06, MI_TARGET_OUTAMP},
3453 {{0, {AudioNheadphone, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
3454 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(64)}}, 0x06, MI_TARGET_OUTAMP},
3455 {{0, {AudioNheadphone ".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
3456 0, 0, ENUM_OFFON}, 0x06, MI_TARGET_PINBOOST},
3458 {{0, {AudioNmono "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
3459 0, 0, ENUM_OFFON}, 0x07, MI_TARGET_OUTAMP},
3460 {{0, {AudioNmono, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
3461 0, 0, .un.v={{"", 0}, 1, MIXER_DELTA(64)}}, 0x07, MI_TARGET_OUTAMP},
3463 {{0, {AudioNmono "." AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
3464 0, 0, .un.e={4, {{{AudioNdac, 0}, 0}, {{AudioNmicrophone, 0}, 1},
3465 {{AudioNline, 0}, 2}, {{AudioNmixerout, 0}, 3}}}},
3466 0x0b, MI_TARGET_CONNLIST},
3468 {{0, {AudioNmicrophone "." AudioNpreamp "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
3469 0, 0, ENUM_OFFON}, 0x0c, MI_TARGET_OUTAMP},
3470 {{0, {AudioNmicrophone "." AudioNpreamp, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
3471 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(4)}}, 0x0c, MI_TARGET_OUTAMP},
3472 {{0, {AudioNmicrophone "." AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
3473 0, 0, .un.e={2, {{{AudioNmicrophone, 0}, 0}, {{AudioNline, 0}, 1}}}},
3474 0x0c, MI_TARGET_CONNLIST},
3476 {{0, {AudioNline "." AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
3477 0, 0, .un.e={2, {{{AudioNline, 0}, 0}, {{AudioNmicrophone, 0}, 1}}}},
3478 0x0d, MI_TARGET_CONNLIST},
3480 {{0, {"beep." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
3481 0, 0, ENUM_OFFON}, 0x10, MI_TARGET_OUTAMP},
3482 {{0, {"beep", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
3483 0, 0, .un.v={{"", 0}, 1, MIXER_DELTA(16)}}, 0x10, MI_TARGET_OUTAMP},
3485 {{0, {AudioNdac "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
3486 0, 0, ENUM_OFFON}, 0x11, MI_TARGET_OUTAMP},
3487 {{0, {AudioNdac, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
3488 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(32)}}, 0x11, MI_TARGET_OUTAMP},
3490 {{0, {AudioNmicrophone "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
3491 0, 0, ENUM_OFFON}, 0x12, MI_TARGET_OUTAMP},
3492 {{0, {AudioNmicrophone, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
3493 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(32)}}, 0x12, MI_TARGET_OUTAMP},
3495 {{0, {AudioNline "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
3496 0, 0, ENUM_OFFON}, 0x13, MI_TARGET_OUTAMP},
3497 {{0, {AudioNline, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
3498 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(32)}}, 0x13, MI_TARGET_OUTAMP},
3500 {{0, {AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD,
3501 0, 0, .un.e={4, {{{AudioNmicrophone, 0}, 0}, {{AudioNline, 0}, 1},
3502 {{AudioNmixerout, 0}, 2}, {{AudioNmono, 0}, 3}}}},
3503 0x14, MI_TARGET_CONNLIST},
3504 {{0, {AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD,
3505 0, 0, ENUM_OFFON}, 0x14, MI_TARGET_OUTAMP},
3506 {{0, {AudioNvolume, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD,
3507 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(16)}}, 0x14, MI_TARGET_OUTAMP},
3509 {{0, {AudioNmode, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_PLAYBACK, 0, 0,
3510 .un.e={2, {{{"analog", 0}, 0}, {{AzaliaNdigital, 0}, 1}}}}, 0, MI_TARGET_DAC},
3513 static int
3514 ad1983_mixer_init(codec_t *this)
3516 mixer_ctrl_t mc;
3518 this->nmixers = __arraycount(ad1983_mixer_items);
3519 this->mixers = malloc(sizeof(ad1983_mixer_items), M_DEVBUF, M_NOWAIT);
3520 if (this->mixers == NULL) {
3521 aprint_error_dev(this->dev, "out of memory in %s\n", __func__);
3522 return ENOMEM;
3524 memcpy(this->mixers, ad1983_mixer_items, sizeof(ad1983_mixer_items));
3525 generic_mixer_fix_indexes(this);
3526 generic_mixer_default(this);
3528 #define AD198X_EVENT_HP 1
3529 #define AD198X_EVENT_SPEAKER 2
3531 mc.dev = -1; /* no need for generic_mixer_set() */
3532 mc.type = AUDIO_MIXER_ENUM;
3533 mc.un.ord = 1; /* connlist: mixerout */
3534 generic_mixer_set(this, 0x05, MI_TARGET_CONNLIST, &mc);
3535 generic_mixer_set(this, 0x06, MI_TARGET_CONNLIST, &mc);
3536 mc.un.ord = 3; /* connlist: mixerout */
3537 generic_mixer_set(this, 0x0b, MI_TARGET_CONNLIST, &mc);
3539 /* setup a unsolicited event for the headphones and speaker */
3540 this->comresp(this, 0x05, CORB_SET_UNSOLICITED_RESPONSE,
3541 CORB_UNSOL_ENABLE | AD198X_EVENT_SPEAKER, NULL);
3542 this->comresp(this, 0x06, CORB_SET_UNSOLICITED_RESPONSE,
3543 CORB_UNSOL_ENABLE | AD198X_EVENT_HP, NULL);
3544 ad1983_unsol_event(this, AD198X_EVENT_SPEAKER);
3545 ad1983_unsol_event(this, AD198X_EVENT_HP);
3546 return 0;
3549 static int
3550 ad1983_unsol_event(codec_t *this, int tag)
3552 int err;
3553 uint32_t value;
3554 mixer_ctrl_t mc;
3556 mc.dev = -1;
3557 mc.type = AUDIO_MIXER_ENUM;
3559 switch (tag) {
3560 case AD198X_EVENT_HP:
3561 err = this->comresp(this, 0x06, CORB_GET_PIN_SENSE, 0, &value);
3562 if (err)
3563 break;
3564 if (value & CORB_PS_PRESENCE) {
3565 DPRINTF(("%s: headphone has been inserted.\n", __func__));
3566 mc.un.ord = 1; /* mute */
3567 generic_mixer_set(this, 0x05, MI_TARGET_OUTAMP, &mc);
3568 generic_mixer_set(this, 0x07, MI_TARGET_OUTAMP, &mc);
3569 } else {
3570 DPRINTF(("%s: headphone has been pulled out.\n", __func__));
3571 mc.un.ord = 0; /* unmute */
3572 generic_mixer_set(this, 0x05, MI_TARGET_OUTAMP, &mc);
3573 /* if no speaker unmute internal mono */
3574 err = this->comresp(this, 0x05, CORB_GET_PIN_SENSE, 0, &value);
3575 if (err)
3576 break;
3577 if (!(value & CORB_PS_PRESENCE))
3578 generic_mixer_set(this, 0x07, MI_TARGET_OUTAMP, &mc);
3580 break;
3581 case AD198X_EVENT_SPEAKER:
3582 err = this->comresp(this, 0x05, CORB_GET_PIN_SENSE, 0, &value);
3583 if (err)
3584 break;
3585 if (value & CORB_PS_PRESENCE) {
3586 DPRINTF(("%s: speaker has been inserted.\n", __func__));
3587 mc.un.ord = 1; /* mute */
3588 generic_mixer_set(this, 0x07, MI_TARGET_OUTAMP, &mc);
3589 } else {
3590 DPRINTF(("%s: speaker has been pulled out.\n", __func__));
3591 /* if no headphones unmute internal mono */
3592 err = this->comresp(this, 0x06, CORB_GET_PIN_SENSE, 0, &value);
3593 if (err)
3594 break;
3595 if (!(value & CORB_PS_PRESENCE)) {
3596 mc.un.ord = 0; /* unmute */
3597 generic_mixer_set(this, 0x07, MI_TARGET_OUTAMP, &mc);
3600 break;
3601 default:
3602 printf("%s: unknown tag: %d\n", __func__, tag);
3604 return 0;
3607 /* ----------------------------------------------------------------
3608 * Analog Devices AD1984
3609 * ---------------------------------------------------------------- */
3611 #define AD1984_THINKPAD 0x20ac17aa
3612 #define AD1984_DELL_OPTIPLEX_755 0x02111028
3613 #define AD1984A_DELL_OPTIPLEX_760 0x027f1028
3615 static int
3616 ad1984_init_dacgroup(codec_t *this)
3618 static const convgroupset_t dacs = {
3619 -1, 2,
3620 {{2, {0x04, 0x03}}, /* analog 4ch */
3621 {1, {0x02}}}}; /* digital */
3622 static const convgroupset_t adcs = {
3623 -1, 3,
3624 {{2, {0x08, 0x09}}, /* analog 4ch */
3625 {1, {0x06}}, /* digital */
3626 {1, {0x05}}}}; /* digital */
3627 this->dacs = dacs;
3628 this->adcs = adcs;
3629 return 0;
3632 static int
3633 ad1984_mixer_init(codec_t *this)
3635 int err;
3637 err = generic_mixer_autoinit(this);
3638 if (err)
3639 return err;
3641 if (this->subid == AD1984_DELL_OPTIPLEX_755 ||
3642 this->subid == AD1984A_DELL_OPTIPLEX_760) {
3643 /* setup a unsolicited event for the headphones and speaker */
3644 this->comresp(this, 0x12, CORB_SET_UNSOLICITED_RESPONSE,
3645 CORB_UNSOL_ENABLE | AD198X_EVENT_SPEAKER, NULL);
3646 this->comresp(this, 0x11, CORB_SET_UNSOLICITED_RESPONSE,
3647 CORB_UNSOL_ENABLE | AD198X_EVENT_HP, NULL);
3648 ad1984_unsol_event(this, AD198X_EVENT_SPEAKER);
3649 ad1984_unsol_event(this, AD198X_EVENT_HP);
3652 return 0;
3655 static int
3656 ad1984_init_widget(const codec_t *this, widget_t *w, nid_t nid)
3658 switch (nid) {
3659 case 0x07:
3660 strlcpy(w->name, "hp", sizeof(w->name));
3661 break;
3662 case 0x0a:
3663 strlcpy(w->name, "spkr", sizeof(w->name));
3664 break;
3665 case 0x0b:
3666 strlcpy(w->name, AudioNaux, sizeof(w->name));
3667 break;
3668 case 0x0c:
3669 strlcpy(w->name, "adc08", sizeof(w->name));
3670 break;
3671 case 0x0d:
3672 strlcpy(w->name, "adc09", sizeof(w->name));
3673 break;
3674 case 0x0e:
3675 strlcpy(w->name, AudioNmono "sel", sizeof(w->name));
3676 break;
3677 case 0x0f:
3678 strlcpy(w->name, AudioNaux "sel", sizeof(w->name));
3679 break;
3680 case 0x10:
3681 strlcpy(w->name, "beep", sizeof(w->name));
3682 break;
3683 case 0x1e:
3684 strlcpy(w->name, AudioNmono, sizeof(w->name));
3685 break;
3686 case 0x22:
3687 strlcpy(w->name, "hp" "sel", sizeof(w->name));
3688 break;
3689 case 0x23:
3690 strlcpy(w->name, "dock" "sel", sizeof(w->name));
3691 break;
3692 case 0x24:
3693 strlcpy(w->name, "dock", sizeof(w->name));
3694 break;
3695 case 0x25:
3696 strlcpy(w->name, "dock.pre", sizeof(w->name));
3697 break;
3698 default:
3699 return generic_mixer_init_widget(this, w, nid);
3702 return 0;
3705 static int
3706 ad1984_unsol_event(codec_t *this, int tag)
3708 int err;
3709 uint32_t value;
3710 mixer_ctrl_t mc;
3712 mc.dev = -1;
3713 mc.type = AUDIO_MIXER_ENUM;
3715 switch (tag) {
3716 case AD198X_EVENT_HP:
3717 err = this->comresp(this, 0x11, CORB_GET_PIN_SENSE, 0, &value);
3718 if (err)
3719 break;
3720 if (value & CORB_PS_PRESENCE) {
3721 DPRINTF(("%s: headphone has been inserted.\n", __func__));
3722 mc.un.ord = 1; /* mute */
3723 generic_mixer_set(this, 0x12, MI_TARGET_OUTAMP, &mc);
3724 generic_mixer_set(this, 0x13, MI_TARGET_OUTAMP, &mc);
3725 } else {
3726 DPRINTF(("%s: headphone has been pulled out.\n", __func__));
3727 mc.un.ord = 0; /* unmute */
3728 generic_mixer_set(this, 0x12, MI_TARGET_OUTAMP, &mc);
3729 /* if no speaker unmute internal mono */
3730 err = this->comresp(this, 0x12, CORB_GET_PIN_SENSE, 0, &value);
3731 if (err)
3732 break;
3733 if (!(value & CORB_PS_PRESENCE))
3734 generic_mixer_set(this, 0x13, MI_TARGET_OUTAMP, &mc);
3736 break;
3737 case AD198X_EVENT_SPEAKER:
3738 err = this->comresp(this, 0x12, CORB_GET_PIN_SENSE, 0, &value);
3739 if (err)
3740 break;
3741 if (value & CORB_PS_PRESENCE) {
3742 DPRINTF(("%s: speaker has been inserted.\n", __func__));
3743 mc.un.ord = 1; /* mute */
3744 generic_mixer_set(this, 0x13, MI_TARGET_OUTAMP, &mc);
3745 } else {
3746 DPRINTF(("%s: speaker has been pulled out.\n", __func__));
3747 /* if no headphones unmute internal mono */
3748 err = this->comresp(this, 0x11, CORB_GET_PIN_SENSE, 0, &value);
3749 if (err)
3750 break;
3751 if (!(value & CORB_PS_PRESENCE)) {
3752 mc.un.ord = 0; /* unmute */
3753 generic_mixer_set(this, 0x13, MI_TARGET_OUTAMP, &mc);
3756 break;
3757 default:
3758 printf("%s: unknown tag: %d\n", __func__, tag);
3760 return 0;
3763 /* ----------------------------------------------------------------
3764 * Analog Devices AD1986A
3765 * ---------------------------------------------------------------- */
3767 static const mixer_item_t ad1986a_mixer_items[] = {
3768 AZ_MIXER_CLASSES,
3770 {{0, {AzaliaNdigital "." AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
3771 0, 0, .un.e={2, {{{"hdaudio", 0}, 0}, {{"recordout", 0}, 1}}}}, 0x02, MI_TARGET_CONNLIST},
3773 #if 0
3774 /* fix to mixerout (default) */
3775 {{0, {AudioNheadphone ".src", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
3776 0, 0, .un.e={3, {{{AudioNmixerout, 0}, 0}, {{"surrounddac", 0}, 1},
3777 {{"clfedac", 0}, 2}}}}, 0x0a, MI_TARGET_CONNLIST},
3778 #endif
3779 {{0, {AudioNheadphone "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
3780 0, 0, ENUM_OFFON}, 0x1a, MI_TARGET_OUTAMP},
3781 {{0, {AudioNheadphone "." "boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
3782 0, 0, ENUM_OFFON}, 0x1a, MI_TARGET_PINBOOST},
3783 {{0, {AudioNheadphone, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
3784 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x1a, MI_TARGET_OUTAMP},
3786 #if 0
3787 /* fix to mixerout (default) */
3788 {{0, {AudioNline "." AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
3789 0, 0, .un.e={2, {{{AudioNmixerout, 0}, 0}, {{"surrounddac", 0}, 1}}}},
3790 0x0b, MI_TARGET_CONNLIST},
3791 #endif
3792 {{0, {AudioNline "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
3793 0, 0, ENUM_OFFON}, 0x1b, MI_TARGET_OUTAMP},
3794 {{0, {AudioNline "." "boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
3795 0, 0, ENUM_OFFON}, 0x1b, MI_TARGET_PINBOOST},
3796 {{0, {AudioNline "." "eapd", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
3797 0, 0, ENUM_OFFON}, 0x1b, MI_TARGET_EAPD},
3798 {{0, {AudioNline, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
3799 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x1b, MI_TARGET_OUTAMP},
3801 #if 0
3802 /* fix to the surround DAC (default) */
3803 {{0, {AudioNsurround "." AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
3804 0, 0, .un.e={2, {{{"surrounddac", 0}, 0}, {{AudioNmixerout, 0}, 1}}}},
3805 0x0c, MI_TARGET_CONNLIST},
3806 /* unmute */
3807 {{0, {AudioNsurround "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
3808 0, 0, ENUM_OFFON}, 0x1c, MI_TARGET_OUTAMP},
3809 /* fix to output */
3810 {{0, {AudioNsurround "." "dir", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
3811 0, 0, ENUM_IO}, 0x1c, MI_TARGET_PINDIR},
3812 /* fix to maximum */
3813 {{0, {AudioNsurround, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
3814 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x1c, MI_TARGET_OUTAMP},
3815 #endif
3816 {{0, {AudioNsurround "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
3817 0, 0, ENUM_OFFON}, 0x04, MI_TARGET_OUTAMP},
3818 {{0, {AudioNsurround, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
3819 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x04, MI_TARGET_OUTAMP},
3821 #if 0
3822 /* fix to the clfe DAC (default) */
3823 {{0, {AzaliaNclfe "." AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
3824 0, 0, .un.e={2, {{{"clfedac", 0}, 0}, {{"mixeroutmono", 0}, 1}}}},
3825 0x0d, MI_TARGET_CONNLIST},
3826 /* unmute */
3827 {{0, {AzaliaNclfe "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
3828 0, 0, ENUM_OFFON}, 0x1d, MI_TARGET_OUTAMP},
3829 /* fix to output */
3830 {{0, {AzaliaNclfe "." "dir", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
3831 0, 0, ENUM_IO}, 0x1d, MI_TARGET_PINDIR},
3832 /* fix to maximum */
3833 {{0, {AzaliaNclfe, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
3834 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x1d, MI_TARGET_OUTAMP},
3835 #endif
3836 {{0, {AzaliaNclfe "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
3837 0, 0, ENUM_OFFON}, 0x05, MI_TARGET_OUTAMP},
3838 {{0, {AzaliaNclfe, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
3839 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x05, MI_TARGET_OUTAMP},
3840 {{0, {AzaliaNclfe "." "lrswap", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
3841 0, 0, ENUM_OFFON}, 0x1d, MI_TARGET_LRSWAP},
3843 {{0, {AudioNmono "." AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
3844 0, 0, .un.e={2, {{{AudioNmixerout, 0}, 0}, {{AudioNmicrophone, 0}, 1}}}},
3845 0x0e, MI_TARGET_CONNLIST},
3846 {{0, {AudioNmono "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
3847 0, 0, ENUM_OFFON}, 0x1e, MI_TARGET_OUTAMP},
3848 {{0, {AudioNmono, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
3849 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x1e, MI_TARGET_OUTAMP},
3851 /* Front DAC */
3852 {{0, {AudioNdac "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
3853 0, 0, ENUM_OFFON}, 0x03, MI_TARGET_OUTAMP},
3854 {{0, {AudioNdac, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
3855 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x03, MI_TARGET_OUTAMP},
3857 #if 0
3858 /* 0x09: 5.1 -> Stereo Downmix */
3859 {{0, {"downmix" "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
3860 0, 0, ENUM_OFFON}, 0x09, MI_TARGET_OUTAMP},
3861 #endif
3863 #if 0
3864 /* mic source is mic jack (default) */
3865 {{0, {AudioNmicrophone "." AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
3866 0, 0, .un.e={8, {{{AudioNmicrophone, 0}, 0}, {{AudioNline, 0}, 1},
3867 {{AzaliaNclfe, 0}, 2}, {{AzaliaNclfe "2", 0}, 3},
3868 {{"micclfe", 0}, 4}, {{"micline", 0}, 5},
3869 {{"clfeline", 0}, 6}, {{"miclineclfe", 0}, 7}}}},
3870 0x0f, MI_TARGET_CONNLIST},
3871 #endif
3872 {{0, {AudioNmicrophone "." "gain", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD,
3873 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(3)}}, 0x0f, MI_TARGET_OUTAMP},
3874 {{0, {AudioNmicrophone "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD,
3875 0, 0, ENUM_OFFON}, 0x13, MI_TARGET_OUTAMP},
3876 {{0, {AudioNmicrophone, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD,
3877 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x13, MI_TARGET_OUTAMP},
3879 #if 0
3880 /* line source is line jack (default) */
3881 {{0, {AudioNline "." AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
3882 0, 0, .un.e={3, {{{AudioNline, 0}, 0}, {{AudioNsurround, 0}, 1},
3883 {{AudioNmicrophone, 0}, 2}}}}, 0x10, MI_TARGET_CONNLIST},
3884 #endif
3885 {{0, {AudioNline "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
3886 0, 0, ENUM_OFFON}, 0x17, MI_TARGET_OUTAMP},
3887 {{0, {AudioNline, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
3888 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x17, MI_TARGET_OUTAMP},
3890 {{0, {"phone" "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
3891 0, 0, ENUM_OFFON}, 0x14, MI_TARGET_OUTAMP},
3892 {{0, {"phone", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
3893 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x14, MI_TARGET_OUTAMP},
3895 {{0, {AudioNcd "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
3896 0, 0, ENUM_OFFON}, 0x15, MI_TARGET_OUTAMP},
3897 {{0, {AudioNcd, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
3898 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x15, MI_TARGET_OUTAMP},
3900 {{0, {AudioNaux "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
3901 0, 0, ENUM_OFFON}, 0x16, MI_TARGET_OUTAMP},
3902 {{0, {AudioNaux, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
3903 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x16, MI_TARGET_OUTAMP},
3905 {{0, {AudioNspeaker "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
3906 0, 0, ENUM_OFFON}, 0x18, MI_TARGET_OUTAMP},
3907 {{0, {AudioNspeaker, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
3908 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(15)}}, 0x18, MI_TARGET_OUTAMP},
3911 /* 0x11: inputs.sel11.source=sel0f [ sel0f mix2b ] XXXX
3912 inputs.sel11.lrswap=off [ off on ] XXXX */
3914 {{0, {AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD,
3915 0, 0, .un.e={7, {{{AudioNmicrophone, 0}, 0}, {{AudioNcd, 0}, 1},
3916 {{AudioNaux, 0}, 2}, {{AudioNline, 0}, 3},
3917 {{AudioNmixerout, 0}, 4}, {{"mixeroutmono", 0}, 5},
3918 {{"phone", 0}, 6}}}},
3919 0x12, MI_TARGET_CONNLIST},
3920 {{0, {AudioNvolume "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD,
3921 0, 0, ENUM_OFFON}, 0x12, MI_TARGET_OUTAMP},
3922 {{0, {AudioNvolume, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD,
3923 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(15)}}, 0x12, MI_TARGET_OUTAMP},
3925 {{0, {AudioNmode, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_PLAYBACK, 0, 0,
3926 .un.e={2, {{{"analog", 0}, 0}, {{AzaliaNdigital, 0}, 1}}}}, 0, MI_TARGET_DAC},
3929 static int
3930 ad1986a_mixer_init(codec_t *this)
3932 mixer_ctrl_t mc;
3934 this->nmixers = __arraycount(ad1986a_mixer_items);
3935 this->mixers = malloc(sizeof(ad1986a_mixer_items), M_DEVBUF, M_NOWAIT);
3936 if (this->mixers == NULL) {
3937 aprint_error_dev(this->dev, "out of memory in %s\n", __func__);
3938 return ENOMEM;
3940 memcpy(this->mixers, ad1986a_mixer_items, sizeof(ad1986a_mixer_items));
3941 generic_mixer_fix_indexes(this);
3942 generic_mixer_default(this);
3944 mc.dev = -1;
3945 mc.type = AUDIO_MIXER_ENUM;
3946 mc.un.ord = 0; /* unmute */
3947 generic_mixer_set(this, 0x1c, MI_TARGET_OUTAMP, &mc);
3948 mc.un.ord = 1; /* dir: output */
3949 generic_mixer_set(this, 0x1c, MI_TARGET_PINDIR, &mc);
3950 mc.type = AUDIO_MIXER_VALUE;
3951 mc.un.value.num_channels = 2;
3952 mc.un.value.level[0] = AUDIO_MAX_GAIN;
3953 mc.un.value.level[1] = AUDIO_MAX_GAIN;
3954 generic_mixer_set(this, 0x1c, MI_TARGET_VOLUME, &mc);
3955 mc.type = AUDIO_MIXER_ENUM;
3956 mc.un.ord = 0; /* unmute */
3957 generic_mixer_set(this, 0x1d, MI_TARGET_OUTAMP, &mc);
3958 mc.un.ord = 1; /* dir: output */
3959 generic_mixer_set(this, 0x1d, MI_TARGET_PINDIR, &mc);
3960 mc.type = AUDIO_MIXER_VALUE;
3961 mc.un.value.num_channels = 2;
3962 mc.un.value.level[0] = AUDIO_MAX_GAIN;
3963 mc.un.value.level[1] = AUDIO_MAX_GAIN;
3964 generic_mixer_set(this, 0x1d, MI_TARGET_VOLUME, &mc);
3965 return 0;
3968 static int
3969 ad1986a_init_dacgroup(codec_t *this)
3971 static const convgroupset_t dacs = {
3972 -1, 2,
3973 {{6, {0x03, 0x04, 0x05}}, /* analog 6ch */
3974 {1, {0x02}}}}; /* digital */
3975 static const convgroupset_t adcs = {
3976 -1, 1,
3977 {{1, {0x06}}}}; /* analog 2ch */
3979 this->dacs = dacs;
3980 this->adcs = adcs;
3981 return 0;
3984 /* ----------------------------------------------------------------
3985 * Analog Devices AD1988A/AD1988B
3986 * ---------------------------------------------------------------- */
3988 static int
3989 ad1988_init_dacgroup(codec_t *this)
3991 static const convgroupset_t dacs = {
3992 -1, 3,
3993 {{4, {0x04, 0x05, 0x06, 0x0a}}, /* analog 8ch */
3994 {1, {0x02}}, /* digital */
3995 {1, {0x03}}}}; /* another analog */
3996 static const convgroupset_t adcs = {
3997 -1, 2,
3998 {{2, {0x08, 0x09, 0x0f}}, /* analog 6ch */
3999 {1, {0x07}}}}; /* digital */
4001 this->dacs = dacs;
4002 this->adcs = adcs;
4003 return 0;
4006 /* ----------------------------------------------------------------
4007 * CMedia CMI9880
4008 * ---------------------------------------------------------------- */
4010 static const mixer_item_t cmi9880_mixer_items[] = {
4011 AZ_MIXER_CLASSES,
4013 {{0, {AudioNmaster"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
4014 0, 0, ENUM_OFFON}, 0x03, MI_TARGET_OUTAMP},
4015 {{0, {AudioNsurround"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
4016 0, 0, ENUM_OFFON}, 0x04, MI_TARGET_OUTAMP},
4017 {{0, {AzaliaNclfe"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
4018 0, 0, ENUM_OFFON}, 0x05, MI_TARGET_OUTAMP},
4019 {{0, {AzaliaNside"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
4020 0, 0, ENUM_OFFON}, 0x06, MI_TARGET_OUTAMP},
4021 {{0, {AzaliaNdigital"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
4022 0, 0, ENUM_OFFON}, 0x07, MI_TARGET_OUTAMP},
4024 {{0, {AzaliaNfront"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD,
4025 0, 0, ENUM_OFFON}, 0x08, MI_TARGET_INAMP(0)},
4026 {{0, {AzaliaNfront, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD,
4027 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(30)}}, 0x08, MI_TARGET_INAMP(0)},
4028 {{0, {AzaliaNfront"."AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD,
4029 0, 0, .un.e={4, {{{AudioNmicrophone, 0}, 5}, {{AudioNcd, 0}, 6},
4030 {{"line1", 0}, 7}, {{"line2", 0}, 8}}}},
4031 0x08, MI_TARGET_CONNLIST},
4032 {{0, {AudioNsurround"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD,
4033 0, 0, ENUM_OFFON}, 0x09, MI_TARGET_INAMP(0)},
4034 {{0, {AudioNsurround, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD,
4035 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(30)}}, 0x09, MI_TARGET_INAMP(0)},
4036 {{0, {AudioNsurround"."AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD,
4037 0, 0, .un.e={4, {{{AudioNmicrophone, 0}, 5}, {{AudioNcd, 0}, 6},
4038 {{"line1", 0}, 7}, {{"line2", 0}, 8}}}},
4039 0x09, MI_TARGET_CONNLIST},
4041 {{0, {AudioNspeaker"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
4042 0, 0, ENUM_OFFON}, 0x23, MI_TARGET_OUTAMP},
4043 {{0, {AudioNspeaker, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
4044 0, 0, .un.v={{"", 0}, 1, MIXER_DELTA(15)}}, 0x23, MI_TARGET_OUTAMP}
4047 static int
4048 cmi9880_mixer_init(codec_t *this)
4050 mixer_ctrl_t mc;
4052 this->nmixers = __arraycount(cmi9880_mixer_items);
4053 this->mixers = malloc(sizeof(cmi9880_mixer_items), M_DEVBUF, M_NOWAIT);
4054 if (this->mixers == NULL) {
4055 aprint_error_dev(this->dev, "out of memory in %s\n", __func__);
4056 return ENOMEM;
4058 memcpy(this->mixers, cmi9880_mixer_items, sizeof(cmi9880_mixer_items));
4059 generic_mixer_fix_indexes(this);
4060 generic_mixer_default(this);
4062 mc.dev = -1;
4063 mc.type = AUDIO_MIXER_ENUM;
4064 mc.un.ord = 5; /* record.front.source=mic */
4065 generic_mixer_set(this, 0x08, MI_TARGET_CONNLIST, &mc);
4066 mc.un.ord = 7; /* record.surround.source=line1 */
4067 generic_mixer_set(this, 0x09, MI_TARGET_CONNLIST, &mc);
4068 mc.un.ord = 1; /* pindir: output */
4069 generic_mixer_set(this, 0x0b, MI_TARGET_PINDIR, &mc);
4070 generic_mixer_set(this, 0x0c, MI_TARGET_PINDIR, &mc);
4071 generic_mixer_set(this, 0x0d, MI_TARGET_PINDIR, &mc);
4072 generic_mixer_set(this, 0x0e, MI_TARGET_PINDIR, &mc);
4073 generic_mixer_set(this, 0x0f, MI_TARGET_PINDIR, &mc);
4074 mc.un.ord = 0; /* front DAC -> headphones */
4075 generic_mixer_set(this, 0x0f, MI_TARGET_CONNLIST, &mc);
4076 mc.un.ord = 0; /* pindir: input */
4077 generic_mixer_set(this, 0x10, MI_TARGET_PINDIR, &mc); /* mic */
4078 generic_mixer_set(this, 0x13, MI_TARGET_PINDIR, &mc); /* SPDIF-in */
4079 generic_mixer_set(this, 0x1f, MI_TARGET_PINDIR, &mc); /* line1 */
4080 generic_mixer_set(this, 0x20, MI_TARGET_PINDIR, &mc); /* line2 */
4081 return 0;
4084 static int
4085 cmi9880_init_dacgroup(codec_t *this)
4087 static const convgroupset_t dacs = {
4088 -1, 2,
4089 {{4, {0x03, 0x04, 0x05, 0x06}}, /* analog 8ch */
4090 {1, {0x07}}}}; /* digital */
4091 static const convgroupset_t adcs = {
4092 -1, 2,
4093 {{2, {0x08, 0x09}}, /* analog 4ch */
4094 {1, {0x0a}}}}; /* digital */
4096 this->dacs = dacs;
4097 this->adcs = adcs;
4098 return 0;
4101 /* ----------------------------------------------------------------
4102 * Sigmatel STAC9221 and STAC9221D
4103 * ---------------------------------------------------------------- */
4105 #define STAC9221_MAC 0x76808384
4107 static int
4108 stac9221_init_dacgroup(codec_t *this)
4110 static const convgroupset_t dacs = {
4111 -1, 3,
4112 {{4, {0x02, 0x03, 0x04, 0x05}}, /* analog 8ch */
4113 {1, {0x08}}, /* digital */
4114 {1, {0x1a}}}}; /* another digital? */
4115 static const convgroupset_t adcs = {
4116 -1, 2,
4117 {{2, {0x06, 0x07}}, /* analog 4ch */
4118 {1, {0x09}}}}; /* digital */
4120 this->dacs = dacs;
4121 this->adcs = adcs;
4122 return 0;
4125 static int
4126 stac9221_mixer_init(codec_t *this)
4128 int err;
4130 err = generic_mixer_init(this);
4131 if (err)
4132 return err;
4133 if (this->subid == STAC9221_MAC) {
4134 stac9221_gpio_unmute(this, 0);
4135 stac9221_gpio_unmute(this, 1);
4137 return 0;
4140 static int
4141 stac9221_gpio_unmute(codec_t *this, int pin)
4143 uint32_t data, mask, dir;
4145 this->comresp(this, this->audiofunc, CORB_GET_GPIO_DATA, 0, &data);
4146 this->comresp(this, this->audiofunc,
4147 CORB_GET_GPIO_ENABLE_MASK, 0, &mask);
4148 this->comresp(this, this->audiofunc, CORB_GET_GPIO_DIRECTION, 0, &dir);
4149 data &= ~(1 << pin);
4150 mask |= 1 << pin;
4151 dir |= 1 << pin;
4152 this->comresp(this, this->audiofunc, 0x7e7, 0, NULL);
4153 this->comresp(this, this->audiofunc,
4154 CORB_SET_GPIO_ENABLE_MASK, mask, NULL);
4155 this->comresp(this, this->audiofunc,
4156 CORB_SET_GPIO_DIRECTION, dir, NULL);
4157 DELAY(1000);
4158 this->comresp(this, this->audiofunc, CORB_SET_GPIO_DATA, data, NULL);
4159 return 0;
4162 /* ----------------------------------------------------------------
4163 * Sigmatel STAC9200 and STAC9200D
4164 * ---------------------------------------------------------------- */
4166 static const mixer_item_t stac9200_mixer_items[] = {
4167 AZ_MIXER_CLASSES,
4169 {{0, {AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
4170 0, 0, .un.e={3, {{{AudioNdac, 0}, 0}, {{AzaliaNdigital"-in", 0}, 1},
4171 {{"selector", 0}, 2}}}}, 0x07, MI_TARGET_CONNLIST},
4172 {{0, {AzaliaNdigital"."AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
4173 0, 0, .un.e={2, {{{AudioNdac, 0}, 0}, {{"selector", 0}, 1}}}},
4174 0x09, MI_TARGET_CONNLIST}, /* AudioNdac is not accurate name */
4175 {{0, {"selector."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
4176 0, 0, ENUM_OFFON}, 0x0a, MI_TARGET_OUTAMP},
4177 {{0, {"selector", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
4178 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(15)}}, 0x0a, MI_TARGET_OUTAMP},
4179 {{0, {AudioNmaster"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
4180 0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_OUTAMP},
4181 {{0, {AudioNmaster, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
4182 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x0b, MI_TARGET_OUTAMP},
4183 {{0, {"selector."AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
4184 0, 0, .un.e={3, {{{"mic1", 0}, 0}, {{"mic2", 0}, 1}, {{AudioNcd, 0}, 4}}}},
4185 0x0c, MI_TARGET_CONNLIST},
4186 {{0, {AudioNheadphone".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
4187 0, 0, ENUM_OFFON}, 0x0d, MI_TARGET_PINBOOST},
4188 {{0, {AudioNspeaker".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
4189 0, 0, ENUM_OFFON}, 0x0e, MI_TARGET_PINBOOST},
4190 {{0, {AudioNmono"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
4191 0, 0, ENUM_OFFON}, 0x11, MI_TARGET_OUTAMP},
4192 {{0, {AudioNmono, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
4193 0, 0, .un.v={{"", 0}, 1, MIXER_DELTA(31)}}, 0x11, MI_TARGET_OUTAMP},
4194 {{0, {"beep."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
4195 0, 0, ENUM_OFFON}, 0x14, MI_TARGET_OUTAMP},
4196 {{0, {"beep", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
4197 0, 0, .un.v={{"", 0}, 1, MIXER_DELTA(3)}}, 0x14, MI_TARGET_OUTAMP},
4198 {{0, {AudioNmode, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_PLAYBACK,
4199 0, 0, .un.e={2, {{{"analog", 0}, 0}, {{AzaliaNdigital, 0}, 1}}}},
4200 0, MI_TARGET_DAC},
4201 {{0, {AudioNmode, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD,
4202 0, 0, .un.e={2, {{{"analog", 0}, 0}, {{AzaliaNdigital, 0}, 1}}}},
4203 0, MI_TARGET_ADC},
4206 static int
4207 stac9200_mixer_init(codec_t *this)
4209 mixer_ctrl_t mc;
4210 uint32_t value;
4212 this->nmixers = __arraycount(stac9200_mixer_items);
4213 this->mixers = malloc(sizeof(stac9200_mixer_items), M_DEVBUF, M_NOWAIT);
4214 if (this->mixers == NULL) {
4215 aprint_error_dev(this->dev, "out of memory in %s\n", __func__);
4216 return ENOMEM;
4218 memcpy(this->mixers, stac9200_mixer_items, sizeof(stac9200_mixer_items));
4219 generic_mixer_fix_indexes(this);
4220 generic_mixer_default(this);
4222 mc.dev = -1; /* no need for generic_mixer_set() */
4223 mc.type = AUDIO_MIXER_ENUM;
4224 mc.un.ord = 1; /* pindir: output */
4225 generic_mixer_set(this, 0x0d, MI_TARGET_PINDIR, &mc); /* headphones */
4226 generic_mixer_set(this, 0x0e, MI_TARGET_PINDIR, &mc); /* speaker */
4227 mc.un.ord = 0; /* pindir: input */
4228 generic_mixer_set(this, 0x0f, MI_TARGET_PINDIR, &mc); /* mic2 */
4229 generic_mixer_set(this, 0x10, MI_TARGET_PINDIR, &mc); /* mic1 */
4230 mc.type = AUDIO_MIXER_VALUE;
4231 mc.un.value.num_channels = 2;
4232 mc.un.value.level[0] = generic_mixer_max(this, 0x0c, MI_TARGET_OUTAMP);
4233 mc.un.value.level[1] = mc.un.value.level[0];
4234 generic_mixer_set(this, 0x0c, MI_TARGET_OUTAMP, &mc);
4236 #define STAC9200_DELL_INSPIRON6400_ID 0x01bd1028
4237 #define STAC9200_DELL_INSPIRON9400_ID 0x01cd1028
4238 #define STAC9200_DELL_640M_ID 0x01d81028
4239 #define STAC9200_DELL_LATITUDE_D420_ID 0x01d61028
4240 #define STAC9200_DELL_LATITUDE_D430_ID 0x02011028
4242 #define STAC9200_EVENT_HP 0
4243 #define STAC9200_NID_HP 0x0d
4244 #define STAC9200_NID_SPEAKER 0x0e
4246 switch (this->subid) {
4247 case STAC9200_DELL_INSPIRON6400_ID:
4248 case STAC9200_DELL_INSPIRON9400_ID:
4249 case STAC9200_DELL_640M_ID:
4250 case STAC9200_DELL_LATITUDE_D420_ID:
4251 case STAC9200_DELL_LATITUDE_D430_ID:
4252 /* Does every DELL model have the same pin configuration?
4253 * I'm not sure. */
4255 /* setup a unsolicited event for the headphones */
4256 this->comresp(this, STAC9200_NID_HP, CORB_SET_UNSOLICITED_RESPONSE,
4257 CORB_UNSOL_ENABLE | STAC9200_EVENT_HP, NULL);
4258 /* If the headphone presents, mute the internal speaker */
4259 this->comresp(this, STAC9200_NID_HP, CORB_GET_PIN_SENSE, 0, &value);
4260 if (value & CORB_PS_PRESENCE) {
4261 generic_mixer_pinctrl(this, STAC9200_NID_SPEAKER, 0);
4262 } else {
4263 generic_mixer_pinctrl(this,
4264 STAC9200_NID_SPEAKER, CORB_PWC_OUTPUT);
4266 break;
4267 default:
4268 break;
4270 return 0;
4273 static int
4274 stac9200_unsol_event(codec_t *this, int tag)
4276 int err;
4277 uint32_t value;
4279 switch (tag) {
4280 case STAC9200_EVENT_HP:
4281 err = this->comresp(this, STAC9200_NID_HP,
4282 CORB_GET_PIN_SENSE, 0, &value);
4283 if (err)
4284 break;
4285 if (value & CORB_PS_PRESENCE) {
4286 DPRINTF(("%s: headphone has been inserted.\n", __func__));
4287 generic_mixer_pinctrl(this, STAC9200_NID_SPEAKER, 0);
4288 } else {
4289 DPRINTF(("%s: headphone has been pulled out.\n", __func__));
4290 generic_mixer_pinctrl(this, STAC9200_NID_SPEAKER, CORB_PWC_OUTPUT);
4292 break;
4293 default:
4294 printf("%s: unknown tag: %d\n", __func__, tag);
4296 return 0;
4299 /* ----------------------------------------------------------------
4300 * ATI HDMI
4301 * ---------------------------------------------------------------- */
4303 static int
4304 atihdmi_init_dacgroup(codec_t *this)
4306 static const convgroupset_t dacs = {
4307 -1, 1,
4308 {{1, {0x02}}}}; /* digital */
4309 static const convgroupset_t adcs = {
4310 -1, 0,
4311 {{0, {0x00}}}}; /* no recording */
4313 this->dacs = dacs;
4314 this->adcs = adcs;
4315 return 0;