Annotation of sys/dev/pci/azalia_codec.c, Revision 1.1.1.1
1.1 nbrk 1: /* $OpenBSD: azalia_codec.c,v 1.29 2007/07/31 17:06:25 deanna Exp $ */
2: /* $NetBSD: azalia_codec.c,v 1.8 2006/05/10 11:17:27 kent Exp $ */
3:
4: /*-
5: * Copyright (c) 2005 The NetBSD Foundation, Inc.
6: * All rights reserved.
7: *
8: * This code is derived from software contributed to The NetBSD Foundation
9: * by TAMURA Kent
10: *
11: * Redistribution and use in source and binary forms, with or without
12: * modification, are permitted provided that the following conditions
13: * are met:
14: * 1. Redistributions of source code must retain the above copyright
15: * notice, this list of conditions and the following disclaimer.
16: * 2. Redistributions in binary form must reproduce the above copyright
17: * notice, this list of conditions and the following disclaimer in the
18: * documentation and/or other materials provided with the distribution.
19: * 3. All advertising materials mentioning features or use of this software
20: * must display the following acknowledgement:
21: * This product includes software developed by the NetBSD
22: * Foundation, Inc. and its contributors.
23: * 4. Neither the name of The NetBSD Foundation nor the names of its
24: * contributors may be used to endorse or promote products derived
25: * from this software without specific prior written permission.
26: *
27: * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
28: * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
29: * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
31: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37: * POSSIBILITY OF SUCH DAMAGE.
38: */
39:
40: #include <sys/cdefs.h>
41: #ifdef NETBSD_GOOP
42: __KERNEL_RCSID(0, "$NetBSD: azalia_codec.c,v 1.3 2005/09/29 04:14:03 kent Exp $");
43: #endif
44:
45: #include <sys/param.h>
46: #include <sys/device.h>
47: #include <sys/malloc.h>
48: #include <sys/systm.h>
49: #include <uvm/uvm_param.h>
50: #include <dev/pci/azalia.h>
51:
52: #define XNAME(co) (((struct device *)co->az)->dv_xname)
53: #ifdef MAX_VOLUME_255
54: # define MIXER_DELTA(n) (AUDIO_MAX_GAIN / (n))
55: #else
56: # define MIXER_DELTA(n) (1)
57: #endif
58: #define AZ_CLASS_INPUT 0
59: #define AZ_CLASS_OUTPUT 1
60: #define AZ_CLASS_RECORD 2
61: #define ENUM_OFFON .un.e={2, {{{AudioNoff}, 0}, {{AudioNon}, 1}}}
62: #define ENUM_IO .un.e={2, {{{"input"}, 0}, {{"output"}, 1}}}
63: #define AzaliaNfront "front"
64: #define AzaliaNclfe "clfe"
65: #define AzaliaNside "side"
66:
67:
68: int azalia_generic_codec_init_dacgroup(codec_t *);
69: int azalia_generic_codec_add_dacgroup(codec_t *, int, uint32_t);
70: int azalia_generic_codec_find_pin(const codec_t *, int, int, uint32_t);
71: int azalia_generic_codec_find_dac(const codec_t *, int, int);
72:
73: int azalia_generic_mixer_init(codec_t *);
74: int azalia_generic_mixer_fix_indexes(codec_t *);
75: int azalia_generic_mixer_default(codec_t *);
76: int azalia_generic_mixer_delete(codec_t *);
77: int azalia_generic_mixer_ensure_capacity(codec_t *, size_t);
78: int azalia_generic_mixer_get(const codec_t *, nid_t, int, mixer_ctrl_t *);
79: int azalia_generic_mixer_set(codec_t *, nid_t, int, const mixer_ctrl_t *);
80: int azalia_generic_mixer_pinctrl(codec_t *, nid_t, uint32_t);
81: u_char azalia_generic_mixer_from_device_value
82: (const codec_t *, nid_t, int, uint32_t );
83: uint32_t azalia_generic_mixer_to_device_value
84: (const codec_t *, nid_t, int, u_char);
85: uint32_t azalia_generic_mixer_max(const codec_t *, nid_t, int);
86: boolean_t azalia_generic_mixer_validate_value
87: (const codec_t *, nid_t, int, u_char);
88: int azalia_generic_set_port(codec_t *, mixer_ctrl_t *);
89: int azalia_generic_get_port(codec_t *, mixer_ctrl_t *);
90:
91: int azalia_alc260_init_dacgroup(codec_t *);
92: int azalia_alc260_mixer_init(codec_t *);
93: int azalia_alc260_set_port(codec_t *, mixer_ctrl_t *);
94: int azalia_alc880_init_dacgroup(codec_t *);
95: int azalia_alc882_init_dacgroup(codec_t *);
96: int azalia_alc882_mixer_init(codec_t *);
97: int azalia_alc882_set_port(codec_t *, mixer_ctrl_t *);
98: int azalia_alc882_get_port(codec_t *, mixer_ctrl_t *);
99: int azalia_alc883_init_dacgroup(codec_t *);
100: int azalia_alc883_mixer_init(codec_t *);
101: int azalia_ad1981hd_init_widget(const codec_t *, widget_t *, nid_t);
102: int azalia_ad1981hd_mixer_init(codec_t *);
103: int azalia_cmi9880_init_dacgroup(codec_t *);
104: int azalia_cmi9880_mixer_init(codec_t *);
105: int azalia_stac9221_init_dacgroup(codec_t *);
106: int azalia_stac9200_mixer_init(codec_t *);
107: int azalia_stac9200_unsol_event(codec_t *, int);
108: int azalia_stac9221_apple_mixer_init(codec_t *);
109: int azalia_stac9221_apple_init_dacgroup(codec_t *);
110: int azalia_stac9221_gpio_unmute(codec_t *, int);
111: int azalia_stac7661_init_dacgroup(codec_t *);
112: int azalia_stac7661_mixer_init(codec_t *);
113: int azalia_stac7661_set_port(codec_t *, mixer_ctrl_t *);
114: int azalia_stac7661_get_port(codec_t *, mixer_ctrl_t *);
115:
116: int
117: azalia_codec_init_vtbl(codec_t *this)
118: {
119: /**
120: * We can refer this->vid and this->subid.
121: */
122: DPRINTF(("%s: vid=%08x subid=%08x\n", __func__, this->vid, this->subid));
123: this->name = NULL;
124: this->init_dacgroup = azalia_generic_codec_init_dacgroup;
125: this->mixer_init = azalia_generic_mixer_init;
126: this->mixer_delete = azalia_generic_mixer_delete;
127: this->set_port = azalia_generic_set_port;
128: this->get_port = azalia_generic_get_port;
129: switch (this->vid) {
130: case 0x10ec0260:
131: this->name = "Realtek ALC260";
132: this->mixer_init = azalia_alc260_mixer_init;
133: this->init_dacgroup = azalia_alc260_init_dacgroup;
134: this->set_port = azalia_alc260_set_port;
135: break;
136: case 0x10ec0880:
137: this->name = "Realtek ALC880";
138: this->init_dacgroup = azalia_alc880_init_dacgroup;
139: break;
140: case 0x10ec0882:
141: this->name = "Realtek ALC882";
142: this->init_dacgroup = azalia_alc882_init_dacgroup;
143: this->mixer_init = azalia_alc882_mixer_init;
144: this->get_port = azalia_alc882_get_port;
145: this->set_port = azalia_alc882_set_port;
146: break;
147: case 0x10ec0883:
148: /* ftp://209.216.61.149/pc/audio/ALC883_DataSheet_1.3.pdf */
149: this->name = "Realtek ALC883";
150: this->init_dacgroup = azalia_alc883_init_dacgroup;
151: this->mixer_init = azalia_alc883_mixer_init;
152: this->get_port = azalia_alc882_get_port;
153: this->set_port = azalia_alc882_set_port;
154: break;
155: case 0x11d41981:
156: /* http://www.analog.com/en/prod/0,2877,AD1981HD,00.html */
157: this->name = "Analog Devices AD1981HD";
158: this->init_widget = azalia_ad1981hd_init_widget;
159: this->mixer_init = azalia_ad1981hd_mixer_init;
160: break;
161: case 0x11d41983:
162: /* http://www.analog.com/en/prod/0,2877,AD1983,00.html */
163: this->name = "Analog Devices AD1983";
164: break;
165: case 0x434d4980:
166: this->name = "CMedia CMI9880";
167: this->init_dacgroup = azalia_cmi9880_init_dacgroup;
168: this->mixer_init = azalia_cmi9880_mixer_init;
169: break;
170: case 0x83847680:
171: this->name = "Sigmatel STAC9221";
172: this->init_dacgroup = azalia_stac9221_init_dacgroup;
173: if (this->subid == 0x76808384) {
174: this->init_dacgroup =
175: azalia_stac9221_apple_init_dacgroup;
176: this->mixer_init =
177: azalia_stac9221_apple_mixer_init;
178: break;
179: }
180: break;
181: case 0x83847683:
182: this->name = "Sigmatel STAC9221D";
183: this->init_dacgroup = azalia_stac9221_init_dacgroup;
184: break;
185: case 0x83847690:
186: /* http://www.idt.com/products/getDoc.cfm?docID=17812077 */
187: this->name = "Sigmatel STAC9200";
188: this->mixer_init = azalia_stac9200_mixer_init;
189: this->unsol_event = azalia_stac9200_unsol_event;
190: break;
191: case 0x83847691:
192: this->name = "Sigmatel STAC9200D";
193: break;
194: case 0x83847661:
195: this->name = "Sigmatel 83847661";
196: this->init_dacgroup = azalia_stac7661_init_dacgroup;
197: this->mixer_init = azalia_stac7661_mixer_init;
198: this->get_port = azalia_stac7661_get_port;
199: this->set_port = azalia_stac7661_set_port;
200: break;
201: }
202: return 0;
203: }
204:
205: /* ----------------------------------------------------------------
206: * functions for generic codecs
207: * ---------------------------------------------------------------- */
208:
209: int
210: azalia_generic_codec_init_dacgroup(codec_t *this)
211: {
212: int i, j, assoc, group;
213:
214: /*
215: * grouping DACs
216: * [0] the lowest assoc DACs
217: * [1] the lowest assoc digital outputs
218: * [2] the 2nd assoc DACs
219: * :
220: */
221: this->dacs.ngroups = 0;
222: for (assoc = 0; assoc < CORB_CD_ASSOCIATION_MAX; assoc++) {
223: azalia_generic_codec_add_dacgroup(this, assoc, 0);
224: azalia_generic_codec_add_dacgroup(this, assoc, COP_AWCAP_DIGITAL);
225: }
226:
227: /* find DACs which do not connect with any pins by default */
228: DPRINTF(("%s: find non-connected DACs\n", __func__));
229: FOR_EACH_WIDGET(this, i) {
230: boolean_t found;
231:
232: if (this->w[i].type != COP_AWTYPE_AUDIO_OUTPUT)
233: continue;
234: found = FALSE;
235: for (group = 0; group < this->dacs.ngroups; group++) {
236: for (j = 0; j < this->dacs.groups[group].nconv; j++) {
237: if (i == this->dacs.groups[group].conv[j]) {
238: found = TRUE;
239: group = this->dacs.ngroups;
240: break;
241: }
242: }
243: }
244: if (found)
245: continue;
246: if (this->dacs.ngroups >= 32)
247: break;
248: this->dacs.groups[this->dacs.ngroups].nconv = 1;
249: this->dacs.groups[this->dacs.ngroups].conv[0] = i;
250: this->dacs.ngroups++;
251: }
252: this->dacs.cur = 0;
253:
254: /* enumerate ADCs */
255: this->adcs.ngroups = 0;
256: FOR_EACH_WIDGET(this, i) {
257: if (this->w[i].type != COP_AWTYPE_AUDIO_INPUT)
258: continue;
259: this->adcs.groups[this->adcs.ngroups].nconv = 1;
260: this->adcs.groups[this->adcs.ngroups].conv[0] = i;
261: this->adcs.ngroups++;
262: if (this->adcs.ngroups >= 32)
263: break;
264: }
265: this->adcs.cur = 0;
266: return 0;
267: }
268:
269: int
270: azalia_generic_codec_add_dacgroup(codec_t *this, int assoc, uint32_t digital)
271: {
272: int i, j, n, dac, seq;
273:
274: n = 0;
275: for (seq = 0 ; seq < CORB_CD_SEQUENCE_MAX; seq++) {
276: i = azalia_generic_codec_find_pin(this, assoc, seq, digital);
277: if (i < 0)
278: continue;
279: dac = azalia_generic_codec_find_dac(this, i, 0);
280: if (dac < 0)
281: continue;
282: /* duplication check */
283: for (j = 0; j < n; j++) {
284: if (this->dacs.groups[this->dacs.ngroups].conv[j] == dac)
285: break;
286: }
287: if (j < n) /* this group already has <dac> */
288: continue;
289: this->dacs.groups[this->dacs.ngroups].conv[n++] = dac;
290: DPRINTF(("%s: assoc=%d seq=%d ==> g=%d n=%d\n",
291: __func__, assoc, seq, this->dacs.ngroups, n-1));
292: }
293: if (n <= 0) /* no such DACs */
294: return 0;
295: this->dacs.groups[this->dacs.ngroups].nconv = n;
296:
297: /* check if the same combination is already registered */
298: for (i = 0; i < this->dacs.ngroups; i++) {
299: if (n != this->dacs.groups[i].nconv)
300: continue;
301: for (j = 0; j < n; j++) {
302: if (this->dacs.groups[this->dacs.ngroups].conv[j] !=
303: this->dacs.groups[i].conv[j])
304: break;
305: }
306: if (j >= n) /* matched */
307: return 0;
308: }
309: /* found no equivalent group */
310: this->dacs.ngroups++;
311: return 0;
312: }
313:
314: int
315: azalia_generic_codec_find_pin(const codec_t *this, int assoc, int seq, uint32_t digital)
316: {
317: int i;
318:
319: FOR_EACH_WIDGET(this, i) {
320: if (this->w[i].type != COP_AWTYPE_PIN_COMPLEX)
321: continue;
322: if ((this->w[i].d.pin.cap & COP_PINCAP_OUTPUT) == 0)
323: continue;
324: if ((this->w[i].widgetcap & COP_AWCAP_DIGITAL) != digital)
325: continue;
326: if (this->w[i].d.pin.association != assoc)
327: continue;
328: if (this->w[i].d.pin.sequence == seq) {
329: return i;
330: }
331: }
332: return -1;
333: }
334:
335: int
336: azalia_generic_codec_find_dac(const codec_t *this, int index, int depth)
337: {
338: const widget_t *w;
339: int i, j, ret;
340:
341: w = &this->w[index];
342: if (w->type == COP_AWTYPE_AUDIO_OUTPUT) {
343: DPRINTF(("%s: DAC: nid=0x%x index=%d\n",
344: __func__, w->nid, index));
345: return index;
346: }
347: if (++depth > 50) {
348: return -1;
349: }
350: if (w->selected >= 0) {
351: j = w->connections[w->selected];
352: if (VALID_WIDGET_NID(j, this)) {
353: ret = azalia_generic_codec_find_dac(this, j, depth);
354: if (ret >= 0) {
355: DPRINTF(("%s: DAC path: nid=0x%x index=%d\n",
356: __func__, w->nid, index));
357: return ret;
358: }
359: }
360: }
361: for (i = 0; i < w->nconnections; i++) {
362: j = w->connections[i];
363: if (!VALID_WIDGET_NID(j, this))
364: continue;
365: ret = azalia_generic_codec_find_dac(this, j, depth);
366: if (ret >= 0) {
367: DPRINTF(("%s: DAC path: nid=0x%x index=%d\n",
368: __func__, w->nid, index));
369: return ret;
370: }
371: }
372: return -1;
373: }
374:
375: /* ----------------------------------------------------------------
376: * Generic mixer functions
377: * ---------------------------------------------------------------- */
378:
379: int
380: azalia_generic_mixer_init(codec_t *this)
381: {
382: /*
383: * pin "<color>%2.2x"
384: * audio output "dac%2.2x"
385: * audio input "adc%2.2x"
386: * mixer "mixer%2.2x"
387: * selector "sel%2.2x"
388: */
389: mixer_item_t *m;
390: int err, i, j, k;
391:
392: this->maxmixers = 10;
393: this->nmixers = 0;
394: this->mixers = malloc(sizeof(mixer_item_t) * this->maxmixers,
395: M_DEVBUF, M_NOWAIT);
396: if (this->mixers == NULL) {
397: printf("%s: out of memory in %s\n", XNAME(this), __func__);
398: return ENOMEM;
399: }
400: bzero(this->mixers, sizeof(mixer_item_t) * this->maxmixers);
401:
402: /* register classes */
403: DPRINTF(("%s: register classes\n", __func__));
404: m = &this->mixers[AZ_CLASS_INPUT];
405: m->devinfo.index = AZ_CLASS_INPUT;
406: strlcpy(m->devinfo.label.name, AudioCinputs,
407: sizeof(m->devinfo.label.name));
408: m->devinfo.type = AUDIO_MIXER_CLASS;
409: m->devinfo.mixer_class = AZ_CLASS_INPUT;
410: m->devinfo.next = AUDIO_MIXER_LAST;
411: m->devinfo.prev = AUDIO_MIXER_LAST;
412: m->nid = 0;
413:
414: m = &this->mixers[AZ_CLASS_OUTPUT];
415: m->devinfo.index = AZ_CLASS_OUTPUT;
416: strlcpy(m->devinfo.label.name, AudioCoutputs,
417: sizeof(m->devinfo.label.name));
418: m->devinfo.type = AUDIO_MIXER_CLASS;
419: m->devinfo.mixer_class = AZ_CLASS_OUTPUT;
420: m->devinfo.next = AUDIO_MIXER_LAST;
421: m->devinfo.prev = AUDIO_MIXER_LAST;
422: m->nid = 0;
423:
424: m = &this->mixers[AZ_CLASS_RECORD];
425: m->devinfo.index = AZ_CLASS_RECORD;
426: strlcpy(m->devinfo.label.name, AudioCrecord,
427: sizeof(m->devinfo.label.name));
428: m->devinfo.type = AUDIO_MIXER_CLASS;
429: m->devinfo.mixer_class = AZ_CLASS_RECORD;
430: m->devinfo.next = AUDIO_MIXER_LAST;
431: m->devinfo.prev = AUDIO_MIXER_LAST;
432: m->nid = 0;
433:
434: this->nmixers = AZ_CLASS_RECORD + 1;
435:
436: #define MIXER_REG_PROLOG \
437: mixer_devinfo_t *d; \
438: err = azalia_generic_mixer_ensure_capacity(this, this->nmixers + 1); \
439: if (err) \
440: return err; \
441: m = &this->mixers[this->nmixers]; \
442: d = &m->devinfo; \
443: m->nid = i
444:
445: FOR_EACH_WIDGET(this, i) {
446: const widget_t *w;
447:
448: w = &this->w[i];
449:
450: /* selector */
451: if (w->type != COP_AWTYPE_AUDIO_MIXER && w->nconnections >= 2) {
452: MIXER_REG_PROLOG;
453: DPRINTF(("%s: selector %s\n", __func__, w->name));
454: snprintf(d->label.name, sizeof(d->label.name),
455: "%s.source", w->name);
456: d->type = AUDIO_MIXER_ENUM;
457: if (w->type == COP_AWTYPE_AUDIO_MIXER)
458: d->mixer_class = AZ_CLASS_RECORD;
459: else if (w->type == COP_AWTYPE_AUDIO_SELECTOR)
460: d->mixer_class = AZ_CLASS_INPUT;
461: else
462: d->mixer_class = AZ_CLASS_OUTPUT;
463: m->target = MI_TARGET_CONNLIST;
464: for (j = 0, k = 0; j < w->nconnections && k < 32; j++) {
465: if (!VALID_WIDGET_NID(w->connections[j], this))
466: continue;
467: DPRINTF(("%s: selector %d=%s\n", __func__, j,
468: this->w[w->connections[j]].name));
469: d->un.e.member[k].ord = j;
470: strlcpy(d->un.e.member[k].label.name,
471: this->w[w->connections[j]].name,
472: MAX_AUDIO_DEV_LEN);
473: k++;
474: }
475: d->un.e.num_mem = k;
476: this->nmixers++;
477: }
478:
479: /* output mute */
480: if (w->widgetcap & COP_AWCAP_OUTAMP &&
481: w->outamp_cap & COP_AMPCAP_MUTE) {
482: MIXER_REG_PROLOG;
483: DPRINTF(("%s: output mute %s\n", __func__, w->name));
484: snprintf(d->label.name, sizeof(d->label.name),
485: "%s.mute", w->name);
486: d->type = AUDIO_MIXER_ENUM;
487: if (w->type == COP_AWTYPE_AUDIO_MIXER)
488: d->mixer_class = AZ_CLASS_OUTPUT;
489: else if (w->type == COP_AWTYPE_AUDIO_SELECTOR)
490: d->mixer_class = AZ_CLASS_OUTPUT;
491: else if (w->type == COP_AWTYPE_PIN_COMPLEX)
492: d->mixer_class = AZ_CLASS_OUTPUT;
493: else
494: d->mixer_class = AZ_CLASS_INPUT;
495: m->target = MI_TARGET_OUTAMP;
496: d->un.e.num_mem = 2;
497: d->un.e.member[0].ord = 0;
498: strlcpy(d->un.e.member[0].label.name, AudioNoff,
499: MAX_AUDIO_DEV_LEN);
500: d->un.e.member[1].ord = 1;
501: strlcpy(d->un.e.member[1].label.name, AudioNon,
502: MAX_AUDIO_DEV_LEN);
503: this->nmixers++;
504: }
505:
506: /* output gain */
507: if (w->widgetcap & COP_AWCAP_OUTAMP
508: && COP_AMPCAP_NUMSTEPS(w->outamp_cap)) {
509: MIXER_REG_PROLOG;
510: DPRINTF(("%s: output gain %s\n", __func__, w->name));
511: snprintf(d->label.name, sizeof(d->label.name),
512: "%s", w->name);
513: d->type = AUDIO_MIXER_VALUE;
514: if (w->type == COP_AWTYPE_AUDIO_MIXER)
515: d->mixer_class = AZ_CLASS_OUTPUT;
516: else if (w->type == COP_AWTYPE_AUDIO_SELECTOR)
517: d->mixer_class = AZ_CLASS_OUTPUT;
518: else if (w->type == COP_AWTYPE_PIN_COMPLEX)
519: d->mixer_class = AZ_CLASS_OUTPUT;
520: else
521: d->mixer_class = AZ_CLASS_INPUT;
522: m->target = MI_TARGET_OUTAMP;
523: d->un.v.num_channels = WIDGET_CHANNELS(w);
524: #ifdef MAX_VOLUME_255
525: d->un.v.units.name[0] = 0;
526: #else
527: snprintf(d->un.v.units.name, sizeof(d->un.v.units.name),
528: "0.25x%ddB", COP_AMPCAP_STEPSIZE(w->outamp_cap)+1);
529: #endif
530: d->un.v.delta =
531: MIXER_DELTA(COP_AMPCAP_NUMSTEPS(w->outamp_cap));
532: this->nmixers++;
533: }
534:
535: /* input mute */
536: if (w->widgetcap & COP_AWCAP_INAMP &&
537: w->inamp_cap & COP_AMPCAP_MUTE) {
538: DPRINTF(("%s: input mute %s\n", __func__, w->name));
539: if (w->type != COP_AWTYPE_AUDIO_SELECTOR &&
540: w->type != COP_AWTYPE_AUDIO_MIXER) {
541: MIXER_REG_PROLOG;
542: snprintf(d->label.name, sizeof(d->label.name),
543: "%s.mute", w->name);
544: d->type = AUDIO_MIXER_ENUM;
545: if (w->type == COP_AWTYPE_PIN_COMPLEX)
546: d->mixer_class = AZ_CLASS_OUTPUT;
547: else if (w->type == COP_AWTYPE_AUDIO_INPUT)
548: d->mixer_class = AZ_CLASS_RECORD;
549: else
550: d->mixer_class = AZ_CLASS_INPUT;
551: m->target = 0;
552: d->un.e.num_mem = 2;
553: d->un.e.member[0].ord = 0;
554: strlcpy(d->un.e.member[0].label.name,
555: AudioNoff, MAX_AUDIO_DEV_LEN);
556: d->un.e.member[1].ord = 1;
557: strlcpy(d->un.e.member[1].label.name,
558: AudioNon, MAX_AUDIO_DEV_LEN);
559: this->nmixers++;
560: } else {
561: for (j = 0; j < w->nconnections; j++) {
562: MIXER_REG_PROLOG;
563: if (!VALID_WIDGET_NID(w->connections[j], this))
564: continue;
565: DPRINTF(("%s: input mute %s.%s\n", __func__,
566: w->name, this->w[w->connections[j]].name));
567: snprintf(d->label.name, sizeof(d->label.name),
568: "%s.%s.mute", w->name,
569: this->w[w->connections[j]].name);
570: d->type = AUDIO_MIXER_ENUM;
571: if (w->type == COP_AWTYPE_PIN_COMPLEX)
572: d->mixer_class = AZ_CLASS_OUTPUT;
573: else if (w->type == COP_AWTYPE_AUDIO_INPUT)
574: d->mixer_class = AZ_CLASS_RECORD;
575: else
576: d->mixer_class = AZ_CLASS_INPUT;
577: m->target = j;
578: d->un.e.num_mem = 2;
579: d->un.e.member[0].ord = 0;
580: strlcpy(d->un.e.member[0].label.name,
581: AudioNoff, MAX_AUDIO_DEV_LEN);
582: d->un.e.member[1].ord = 1;
583: strlcpy(d->un.e.member[1].label.name,
584: AudioNon, MAX_AUDIO_DEV_LEN);
585: this->nmixers++;
586: }
587: }
588: }
589:
590: /* input gain */
591: if (w->widgetcap & COP_AWCAP_INAMP
592: && COP_AMPCAP_NUMSTEPS(w->inamp_cap)) {
593: DPRINTF(("%s: input gain %s\n", __func__, w->name));
594: if (w->type != COP_AWTYPE_AUDIO_SELECTOR &&
595: w->type != COP_AWTYPE_AUDIO_MIXER) {
596: MIXER_REG_PROLOG;
597: snprintf(d->label.name, sizeof(d->label.name),
598: "%s", w->name);
599: d->type = AUDIO_MIXER_VALUE;
600: if (w->type == COP_AWTYPE_PIN_COMPLEX)
601: d->mixer_class = AZ_CLASS_OUTPUT;
602: else if (w->type == COP_AWTYPE_AUDIO_INPUT)
603: d->mixer_class = AZ_CLASS_RECORD;
604: else
605: d->mixer_class = AZ_CLASS_INPUT;
606: m->target = 0;
607: d->un.v.num_channels = WIDGET_CHANNELS(w);
608: #ifdef MAX_VOLUME_255
609: d->un.v.units.name[0] = 0;
610: #else
611: snprintf(d->un.v.units.name,
612: sizeof(d->un.v.units.name), "0.25x%ddB",
613: COP_AMPCAP_STEPSIZE(w->inamp_cap)+1);
614: #endif
615: d->un.v.delta =
616: MIXER_DELTA(COP_AMPCAP_NUMSTEPS(w->inamp_cap));
617: this->nmixers++;
618: } else {
619: for (j = 0; j < w->nconnections; j++) {
620: MIXER_REG_PROLOG;
621: if (!VALID_WIDGET_NID(w->connections[j], this))
622: continue;
623: DPRINTF(("%s: input gain %s.%s\n", __func__,
624: w->name, this->w[w->connections[j]].name));
625: snprintf(d->label.name, sizeof(d->label.name),
626: "%s.%s", w->name,
627: this->w[w->connections[j]].name);
628: d->type = AUDIO_MIXER_VALUE;
629: if (w->type == COP_AWTYPE_PIN_COMPLEX)
630: d->mixer_class = AZ_CLASS_OUTPUT;
631: else if (w->type == COP_AWTYPE_AUDIO_INPUT)
632: d->mixer_class = AZ_CLASS_RECORD;
633: else
634: d->mixer_class = AZ_CLASS_INPUT;
635: m->target = j;
636: d->un.v.num_channels = WIDGET_CHANNELS(w);
637: #ifdef MAX_VOLUME_255
638: d->un.v.units.name[0] = 0;
639: #else
640: snprintf(d->un.v.units.name,
641: sizeof(d->un.v.units.name), "0.25x%ddB",
642: COP_AMPCAP_STEPSIZE(w->inamp_cap)+1);
643: #endif
644: d->un.v.delta =
645: MIXER_DELTA(COP_AMPCAP_NUMSTEPS(w->inamp_cap));
646: this->nmixers++;
647: }
648: }
649: }
650:
651: /* pin direction */
652: if (w->type == COP_AWTYPE_PIN_COMPLEX &&
653: w->d.pin.cap & COP_PINCAP_OUTPUT &&
654: w->d.pin.cap & COP_PINCAP_INPUT) {
655: MIXER_REG_PROLOG;
656: DPRINTF(("%s: pin dir %s\n", __func__, w->name));
657: snprintf(d->label.name, sizeof(d->label.name),
658: "%s.dir", w->name);
659: d->type = AUDIO_MIXER_ENUM;
660: d->mixer_class = AZ_CLASS_OUTPUT;
661: m->target = MI_TARGET_PINDIR;
662: d->un.e.num_mem = 2;
663: d->un.e.member[0].ord = 0;
664: strlcpy(d->un.e.member[0].label.name, AudioNinput,
665: MAX_AUDIO_DEV_LEN);
666: d->un.e.member[1].ord = 1;
667: strlcpy(d->un.e.member[1].label.name, AudioNoutput,
668: MAX_AUDIO_DEV_LEN);
669: this->nmixers++;
670: }
671:
672: /* pin headphone-boost */
673: if (w->type == COP_AWTYPE_PIN_COMPLEX &&
674: w->d.pin.cap & COP_PINCAP_HEADPHONE) {
675: MIXER_REG_PROLOG;
676: DPRINTF(("%s: hpboost %s\n", __func__, w->name));
677: snprintf(d->label.name, sizeof(d->label.name),
678: "%s.boost", w->name);
679: d->type = AUDIO_MIXER_ENUM;
680: d->mixer_class = AZ_CLASS_OUTPUT;
681: m->target = MI_TARGET_PINBOOST;
682: d->un.e.num_mem = 2;
683: d->un.e.member[0].ord = 0;
684: strlcpy(d->un.e.member[0].label.name, AudioNoff,
685: MAX_AUDIO_DEV_LEN);
686: d->un.e.member[1].ord = 1;
687: strlcpy(d->un.e.member[1].label.name, AudioNon,
688: MAX_AUDIO_DEV_LEN);
689: this->nmixers++;
690: }
691:
692: /* volume knob */
693: if (w->type == COP_AWTYPE_VOLUME_KNOB &&
694: w->d.volume.cap & COP_VKCAP_DELTA) {
695: MIXER_REG_PROLOG;
696: DPRINTF(("%s: volume knob %s\n", __func__, w->name));
697: strlcpy(d->label.name, w->name, sizeof(d->label.name));
698: d->type = AUDIO_MIXER_VALUE;
699: d->mixer_class = AZ_CLASS_OUTPUT;
700: m->target = MI_TARGET_VOLUME;
701: d->un.v.num_channels = 1;
702: d->un.v.units.name[0] = 0;
703: d->un.v.delta =
704: MIXER_DELTA(COP_VKCAP_NUMSTEPS(w->d.volume.cap));
705: this->nmixers++;
706: }
707: }
708:
709: /* if the codec has multiple DAC groups, create "inputs.usingdac" */
710: if (this->dacs.ngroups > 1) {
711: MIXER_REG_PROLOG;
712: DPRINTF(("%s: create inputs.usingdac\n", __func__));
713: strlcpy(d->label.name, "usingdac", sizeof(d->label.name));
714: d->type = AUDIO_MIXER_ENUM;
715: d->mixer_class = AZ_CLASS_INPUT;
716: m->target = MI_TARGET_DAC;
717: for (i = 0; i < this->dacs.ngroups && i < 32; i++) {
718: d->un.e.member[i].ord = i;
719: for (j = 0; j < this->dacs.groups[i].nconv; j++) {
720: if (j * 2 >= MAX_AUDIO_DEV_LEN)
721: break;
722: snprintf(d->un.e.member[i].label.name + j*2,
723: MAX_AUDIO_DEV_LEN - j*2, "%2.2x",
724: this->dacs.groups[i].conv[j]);
725: }
726: }
727: d->un.e.num_mem = i;
728: this->nmixers++;
729: }
730:
731: /* if the codec has multiple ADC groups, create "record.usingadc" */
732: if (this->adcs.ngroups > 1) {
733: MIXER_REG_PROLOG;
734: DPRINTF(("%s: create inputs.usingadc\n", __func__));
735: strlcpy(d->label.name, "usingadc", sizeof(d->label.name));
736: d->type = AUDIO_MIXER_ENUM;
737: d->mixer_class = AZ_CLASS_RECORD;
738: m->target = MI_TARGET_ADC;
739: for (i = 0; i < this->adcs.ngroups && i < 32; i++) {
740: d->un.e.member[i].ord = i;
741: for (j = 0; j < this->adcs.groups[i].nconv; j++) {
742: if (j * 2 >= MAX_AUDIO_DEV_LEN)
743: break;
744: snprintf(d->un.e.member[i].label.name + j*2,
745: MAX_AUDIO_DEV_LEN - j*2, "%2.2x",
746: this->adcs.groups[i].conv[j]);
747: }
748: }
749: d->un.e.num_mem = i;
750: this->nmixers++;
751: }
752:
753: azalia_generic_mixer_fix_indexes(this);
754: azalia_generic_mixer_default(this);
755: return 0;
756: }
757:
758: int
759: azalia_generic_mixer_ensure_capacity(codec_t *this, size_t newsize)
760: {
761: size_t newmax;
762: void *newbuf;
763:
764: if (this->maxmixers >= newsize)
765: return 0;
766: newmax = this->maxmixers + 10;
767: if (newmax < newsize)
768: newmax = newsize;
769: newbuf = malloc(sizeof(mixer_item_t) * newmax, M_DEVBUF, M_NOWAIT);
770: if (newbuf == NULL) {
771: printf("%s: out of memory in %s\n", XNAME(this), __func__);
772: return ENOMEM;
773: }
774: bzero(newbuf, sizeof(mixer_item_t) * newmax);
775: bcopy(this->mixers, newbuf, this->maxmixers * sizeof(mixer_item_t));
776: free(this->mixers, M_DEVBUF);
777: this->mixers = newbuf;
778: this->maxmixers = newmax;
779: return 0;
780: }
781:
782: int
783: azalia_generic_mixer_fix_indexes(codec_t *this)
784: {
785: int i;
786: mixer_devinfo_t *d;
787:
788: for (i = 0; i < this->nmixers; i++) {
789: d = &this->mixers[i].devinfo;
790: #ifdef DIAGNOSTIC
791: if (d->index != 0 && d->index != i)
792: printf("%s: index mismatch %d %d\n", __func__,
793: d->index, i);
794: #endif
795: d->index = i;
796: if (d->prev == 0)
797: d->prev = AUDIO_MIXER_LAST;
798: if (d->next == 0)
799: d->next = AUDIO_MIXER_LAST;
800: }
801: return 0;
802: }
803:
804: int
805: azalia_generic_mixer_default(codec_t *this)
806: {
807: int i;
808: mixer_item_t *m;
809: /* unmute all */
810: DPRINTF(("%s: unmute\n", __func__));
811: for (i = 0; i < this->nmixers; i++) {
812: mixer_ctrl_t mc;
813:
814: m = &this->mixers[i];
815: if (!IS_MI_TARGET_INAMP(m->target) &&
816: m->target != MI_TARGET_OUTAMP)
817: continue;
818: if (m->devinfo.type != AUDIO_MIXER_ENUM)
819: continue;
820: mc.dev = i;
821: mc.type = AUDIO_MIXER_ENUM;
822: mc.un.ord = 0;
823: azalia_generic_mixer_set(this, m->nid, m->target, &mc);
824: }
825:
826: /*
827: * For bidirectional pins,
828: * Output: lineout, speaker, headphone, spdifout, digitalout, other
829: * Input: others
830: */
831: DPRINTF(("%s: process bidirectional pins\n", __func__));
832: for (i = 0; i < this->nmixers; i++) {
833: mixer_ctrl_t mc;
834:
835: m = &this->mixers[i];
836: if (m->target != MI_TARGET_PINDIR)
837: continue;
838: mc.dev = i;
839: mc.type = AUDIO_MIXER_ENUM;
840: switch (this->w[m->nid].d.pin.device) {
841: case CORB_CD_LINEOUT:
842: case CORB_CD_SPEAKER:
843: case CORB_CD_HEADPHONE:
844: case CORB_CD_SPDIFOUT:
845: case CORB_CD_DIGITALOUT:
846: case CORB_CD_DEVICE_OTHER:
847: mc.un.ord = 1;
848: break;
849: default:
850: mc.un.ord = 0;
851: }
852: azalia_generic_mixer_set(this, m->nid, m->target, &mc);
853: }
854:
855: /* set unextreme volume */
856: DPRINTF(("%s: set volume\n", __func__));
857: for (i = 0; i < this->nmixers; i++) {
858: mixer_ctrl_t mc;
859:
860: m = &this->mixers[i];
861: if (!IS_MI_TARGET_INAMP(m->target) &&
862: m->target != MI_TARGET_OUTAMP &&
863: m->target != MI_TARGET_VOLUME)
864: continue;
865: if (m->devinfo.type != AUDIO_MIXER_VALUE)
866: continue;
867: mc.dev = i;
868: mc.type = AUDIO_MIXER_VALUE;
869: mc.un.value.num_channels = 1;
870: mc.un.value.level[0] = AUDIO_MAX_GAIN / 2;
871: if (m->target != MI_TARGET_VOLUME &&
872: WIDGET_CHANNELS(&this->w[m->nid]) == 2) {
873: mc.un.value.num_channels = 2;
874: mc.un.value.level[1] = AUDIO_MAX_GAIN / 2;
875: }
876: azalia_generic_mixer_set(this, m->nid, m->target, &mc);
877: }
878:
879: return 0;
880: }
881:
882: int
883: azalia_generic_mixer_delete(codec_t *this)
884: {
885: if (this->mixers == NULL)
886: return 0;
887: free(this->mixers, M_DEVBUF);
888: this->mixers = NULL;
889: return 0;
890: }
891:
892: /**
893: * @param mc mc->type must be set by the caller before the call
894: */
895: int
896: azalia_generic_mixer_get(const codec_t *this, nid_t nid, int target, mixer_ctrl_t *mc)
897: {
898: uint32_t result;
899: nid_t n;
900: int err;
901:
902: /* inamp mute */
903: if (IS_MI_TARGET_INAMP(target) && mc->type == AUDIO_MIXER_ENUM) {
904: err = this->comresp(this, nid, CORB_GET_AMPLIFIER_GAIN_MUTE,
905: CORB_GAGM_INPUT | CORB_GAGM_LEFT |
906: MI_TARGET_INAMP(target), &result);
907: if (err)
908: return err;
909: mc->un.ord = result & CORB_GAGM_MUTE ? 1 : 0;
910: }
911:
912: /* inamp gain */
913: else if (IS_MI_TARGET_INAMP(target) && mc->type == AUDIO_MIXER_VALUE) {
914: err = this->comresp(this, nid, CORB_GET_AMPLIFIER_GAIN_MUTE,
915: CORB_GAGM_INPUT | CORB_GAGM_LEFT |
916: MI_TARGET_INAMP(target), &result);
917: if (err)
918: return err;
919: mc->un.value.level[0] = azalia_generic_mixer_from_device_value(this,
920: nid, target, CORB_GAGM_GAIN(result));
921: if (this->w[nid].type == COP_AWTYPE_AUDIO_SELECTOR ||
922: this->w[nid].type == COP_AWTYPE_AUDIO_MIXER) {
923: n = this->w[nid].connections[MI_TARGET_INAMP(target)];
924: #ifdef AZALIA_DEBUG
925: if (!VALID_WIDGET_NID(n, this)) {
926: DPRINTF(("%s: invalid target: nid=%d nconn=%d index=%d\n",
927: __func__, nid, this->w[nid].nconnections,
928: MI_TARGET_INAMP(target)));
929: return EINVAL;
930: }
931: #endif
932: } else
933: n = nid;
934: mc->un.value.num_channels = WIDGET_CHANNELS(&this->w[n]);
935: if (mc->un.value.num_channels == 2) {
936: err = this->comresp(this, nid,
937: CORB_GET_AMPLIFIER_GAIN_MUTE, CORB_GAGM_INPUT |
938: CORB_GAGM_RIGHT | MI_TARGET_INAMP(target),
939: &result);
940: if (err)
941: return err;
942: mc->un.value.level[1] = azalia_generic_mixer_from_device_value
943: (this, nid, target, CORB_GAGM_GAIN(result));
944: }
945: }
946:
947: /* outamp mute */
948: else if (target == MI_TARGET_OUTAMP && mc->type == AUDIO_MIXER_ENUM) {
949: err = this->comresp(this, nid, CORB_GET_AMPLIFIER_GAIN_MUTE,
950: CORB_GAGM_OUTPUT | CORB_GAGM_LEFT | 0, &result);
951: if (err)
952: return err;
953: mc->un.ord = result & CORB_GAGM_MUTE ? 1 : 0;
954: }
955:
956: /* outamp gain */
957: else if (target == MI_TARGET_OUTAMP && mc->type == AUDIO_MIXER_VALUE) {
958: err = this->comresp(this, nid, CORB_GET_AMPLIFIER_GAIN_MUTE,
959: CORB_GAGM_OUTPUT | CORB_GAGM_LEFT | 0, &result);
960: if (err)
961: return err;
962: mc->un.value.level[0] = azalia_generic_mixer_from_device_value(this,
963: nid, target, CORB_GAGM_GAIN(result));
964: mc->un.value.num_channels = WIDGET_CHANNELS(&this->w[nid]);
965: if (mc->un.value.num_channels == 2) {
966: err = this->comresp(this, nid,
967: CORB_GET_AMPLIFIER_GAIN_MUTE,
968: CORB_GAGM_OUTPUT | CORB_GAGM_RIGHT | 0, &result);
969: if (err)
970: return err;
971: mc->un.value.level[1] = azalia_generic_mixer_from_device_value
972: (this, nid, target, CORB_GAGM_GAIN(result));
973: }
974: }
975:
976: /* selection */
977: else if (target == MI_TARGET_CONNLIST) {
978: err = this->comresp(this, nid,
979: CORB_GET_CONNECTION_SELECT_CONTROL, 0, &result);
980: if (err)
981: return err;
982: result = CORB_CSC_INDEX(result);
983: if (!VALID_WIDGET_NID(this->w[nid].connections[result], this))
984: mc->un.ord = -1;
985: else
986: mc->un.ord = result;
987: }
988:
989: /* pin I/O */
990: else if (target == MI_TARGET_PINDIR) {
991: err = this->comresp(this, nid,
992: CORB_GET_PIN_WIDGET_CONTROL, 0, &result);
993: if (err)
994: return err;
995: mc->un.ord = result & CORB_PWC_OUTPUT ? 1 : 0;
996: }
997:
998: /* pin headphone-boost */
999: else if (target == MI_TARGET_PINBOOST) {
1000: err = this->comresp(this, nid,
1001: CORB_GET_PIN_WIDGET_CONTROL, 0, &result);
1002: if (err)
1003: return err;
1004: mc->un.ord = result & CORB_PWC_HEADPHONE ? 1 : 0;
1005: }
1006:
1007: /* DAC group selection */
1008: else if (target == MI_TARGET_DAC) {
1009: mc->un.ord = this->dacs.cur;
1010: }
1011:
1012: /* ADC selection */
1013: else if (target == MI_TARGET_ADC) {
1014: mc->un.ord = this->adcs.cur;
1015: }
1016:
1017: /* Volume knob */
1018: else if (target == MI_TARGET_VOLUME) {
1019: err = this->comresp(this, nid, CORB_GET_VOLUME_KNOB,
1020: 0, &result);
1021: if (err)
1022: return err;
1023: mc->un.value.level[0] = azalia_generic_mixer_from_device_value(this,
1024: nid, target, CORB_VKNOB_VOLUME(result));
1025: mc->un.value.num_channels = 1;
1026: }
1027:
1028: else {
1029: printf("%s: internal error in %s: target=%x\n",
1030: XNAME(this), __func__, target);
1031: return -1;
1032: }
1033: return 0;
1034: }
1035:
1036: int
1037: azalia_generic_mixer_set(codec_t *this, nid_t nid, int target, const mixer_ctrl_t *mc)
1038: {
1039: uint32_t result, value;
1040: int err;
1041:
1042: /* inamp mute */
1043: if (IS_MI_TARGET_INAMP(target) && mc->type == AUDIO_MIXER_ENUM) {
1044: /* We have to set stereo mute separately to keep each gain value. */
1045: err = this->comresp(this, nid, CORB_GET_AMPLIFIER_GAIN_MUTE,
1046: CORB_GAGM_INPUT | CORB_GAGM_LEFT |
1047: MI_TARGET_INAMP(target), &result);
1048: if (err)
1049: return err;
1050: value = CORB_AGM_INPUT | CORB_AGM_LEFT |
1051: (target << CORB_AGM_INDEX_SHIFT) |
1052: CORB_GAGM_GAIN(result);
1053: if (mc->un.ord)
1054: value |= CORB_AGM_MUTE;
1055: err = this->comresp(this, nid, CORB_SET_AMPLIFIER_GAIN_MUTE,
1056: value, &result);
1057: if (err)
1058: return err;
1059: if (WIDGET_CHANNELS(&this->w[nid]) == 2) {
1060: err = this->comresp(this, nid,
1061: CORB_GET_AMPLIFIER_GAIN_MUTE, CORB_GAGM_INPUT |
1062: CORB_GAGM_RIGHT | MI_TARGET_INAMP(target),
1063: &result);
1064: if (err)
1065: return err;
1066: value = CORB_AGM_INPUT | CORB_AGM_RIGHT |
1067: (target << CORB_AGM_INDEX_SHIFT) |
1068: CORB_GAGM_GAIN(result);
1069: if (mc->un.ord)
1070: value |= CORB_AGM_MUTE;
1071: err = this->comresp(this, nid,
1072: CORB_SET_AMPLIFIER_GAIN_MUTE, value, &result);
1073: if (err)
1074: return err;
1075: }
1076: }
1077:
1078: /* inamp gain */
1079: else if (IS_MI_TARGET_INAMP(target) && mc->type == AUDIO_MIXER_VALUE) {
1080: if (mc->un.value.num_channels < 1)
1081: return EINVAL;
1082: if (!azalia_generic_mixer_validate_value(this, nid, target,
1083: mc->un.value.level[0]))
1084: return EINVAL;
1085: err = this->comresp(this, nid, CORB_GET_AMPLIFIER_GAIN_MUTE,
1086: CORB_GAGM_INPUT | CORB_GAGM_LEFT |
1087: MI_TARGET_INAMP(target), &result);
1088: if (err)
1089: return err;
1090: value = azalia_generic_mixer_to_device_value(this, nid, target,
1091: mc->un.value.level[0]);
1092: value = CORB_AGM_INPUT | CORB_AGM_LEFT |
1093: (target << CORB_AGM_INDEX_SHIFT) |
1094: (result & CORB_GAGM_MUTE ? CORB_AGM_MUTE : 0) |
1095: (value & CORB_AGM_GAIN_MASK);
1096: err = this->comresp(this, nid, CORB_SET_AMPLIFIER_GAIN_MUTE,
1097: value, &result);
1098: if (err)
1099: return err;
1100: if (mc->un.value.num_channels >= 2 &&
1101: WIDGET_CHANNELS(&this->w[nid]) == 2) {
1102: if (!azalia_generic_mixer_validate_value(this, nid, target,
1103: mc->un.value.level[1]))
1104: return EINVAL;
1105: err = this->comresp(this, nid,
1106: CORB_GET_AMPLIFIER_GAIN_MUTE, CORB_GAGM_INPUT |
1107: CORB_GAGM_RIGHT | MI_TARGET_INAMP(target),
1108: &result);
1109: if (err)
1110: return err;
1111: value = azalia_generic_mixer_to_device_value(this, nid, target,
1112: mc->un.value.level[1]);
1113: value = CORB_AGM_INPUT | CORB_AGM_RIGHT |
1114: (target << CORB_AGM_INDEX_SHIFT) |
1115: (result & CORB_GAGM_MUTE ? CORB_AGM_MUTE : 0) |
1116: (value & CORB_AGM_GAIN_MASK);
1117: err = this->comresp(this, nid,
1118: CORB_SET_AMPLIFIER_GAIN_MUTE, value, &result);
1119: if (err)
1120: return err;
1121: }
1122: }
1123:
1124: /* outamp mute */
1125: else if (target == MI_TARGET_OUTAMP && mc->type == AUDIO_MIXER_ENUM) {
1126: err = this->comresp(this, nid, CORB_GET_AMPLIFIER_GAIN_MUTE,
1127: CORB_GAGM_OUTPUT | CORB_GAGM_LEFT, &result);
1128: if (err)
1129: return err;
1130: value = CORB_AGM_OUTPUT | CORB_AGM_LEFT | CORB_GAGM_GAIN(result);
1131: if (mc->un.ord)
1132: value |= CORB_AGM_MUTE;
1133: err = this->comresp(this, nid, CORB_SET_AMPLIFIER_GAIN_MUTE,
1134: value, &result);
1135: if (err)
1136: return err;
1137: if (WIDGET_CHANNELS(&this->w[nid]) == 2) {
1138: err = this->comresp(this, nid,
1139: CORB_GET_AMPLIFIER_GAIN_MUTE,
1140: CORB_GAGM_OUTPUT | CORB_GAGM_RIGHT, &result);
1141: if (err)
1142: return err;
1143: value = CORB_AGM_OUTPUT | CORB_AGM_RIGHT |
1144: CORB_GAGM_GAIN(result);
1145: if (mc->un.ord)
1146: value |= CORB_AGM_MUTE;
1147: err = this->comresp(this, nid,
1148: CORB_SET_AMPLIFIER_GAIN_MUTE, value, &result);
1149: if (err)
1150: return err;
1151: }
1152: }
1153:
1154: /* outamp gain */
1155: else if (target == MI_TARGET_OUTAMP && mc->type == AUDIO_MIXER_VALUE) {
1156: if (mc->un.value.num_channels < 1)
1157: return EINVAL;
1158: if (!azalia_generic_mixer_validate_value(this, nid, target,
1159: mc->un.value.level[0]))
1160: return EINVAL;
1161: err = this->comresp(this, nid, CORB_GET_AMPLIFIER_GAIN_MUTE,
1162: CORB_GAGM_OUTPUT | CORB_GAGM_LEFT, &result);
1163: if (err)
1164: return err;
1165: value = azalia_generic_mixer_to_device_value(this, nid, target,
1166: mc->un.value.level[0]);
1167: value = CORB_AGM_OUTPUT | CORB_AGM_LEFT |
1168: (result & CORB_GAGM_MUTE ? CORB_AGM_MUTE : 0) |
1169: (value & CORB_AGM_GAIN_MASK);
1170: err = this->comresp(this, nid, CORB_SET_AMPLIFIER_GAIN_MUTE,
1171: value, &result);
1172: if (err)
1173: return err;
1174: if (mc->un.value.num_channels >= 2 &&
1175: WIDGET_CHANNELS(&this->w[nid]) == 2) {
1176: if (!azalia_generic_mixer_validate_value(this, nid, target,
1177: mc->un.value.level[1]))
1178: return EINVAL;
1179: err = this->comresp(this, nid,
1180: CORB_GET_AMPLIFIER_GAIN_MUTE, CORB_GAGM_OUTPUT |
1181: CORB_GAGM_RIGHT, &result);
1182: if (err)
1183: return err;
1184: value = azalia_generic_mixer_to_device_value(this, nid, target,
1185: mc->un.value.level[1]);
1186: value = CORB_AGM_OUTPUT | CORB_AGM_RIGHT |
1187: (result & CORB_GAGM_MUTE ? CORB_AGM_MUTE : 0) |
1188: (value & CORB_AGM_GAIN_MASK);
1189: err = this->comresp(this, nid,
1190: CORB_SET_AMPLIFIER_GAIN_MUTE, value, &result);
1191: if (err)
1192: return err;
1193: }
1194: }
1195:
1196: /* selection */
1197: else if (target == MI_TARGET_CONNLIST) {
1198: if (mc->un.ord < 0 ||
1199: mc->un.ord >= this->w[nid].nconnections ||
1200: !VALID_WIDGET_NID(this->w[nid].connections[mc->un.ord], this))
1201: return EINVAL;
1202: err = this->comresp(this, nid,
1203: CORB_SET_CONNECTION_SELECT_CONTROL, mc->un.ord, &result);
1204: if (err)
1205: return err;
1206: }
1207:
1208: /* pin I/O */
1209: else if (target == MI_TARGET_PINDIR) {
1210: if (mc->un.ord >= 2)
1211: return EINVAL;
1212: err = this->comresp(this, nid,
1213: CORB_GET_PIN_WIDGET_CONTROL, 0, &result);
1214: if (err)
1215: return err;
1216: if (mc->un.ord == 0) {
1217: result &= ~CORB_PWC_OUTPUT;
1218: result |= CORB_PWC_INPUT;
1219: } else {
1220: result &= ~CORB_PWC_INPUT;
1221: result |= CORB_PWC_OUTPUT;
1222: }
1223: err = this->comresp(this, nid,
1224: CORB_SET_PIN_WIDGET_CONTROL, result, &result);
1225: if (err)
1226: return err;
1227: }
1228:
1229: /* pin headphone-boost */
1230: else if (target == MI_TARGET_PINBOOST) {
1231: if (mc->un.ord >= 2)
1232: return EINVAL;
1233: err = this->comresp(this, nid,
1234: CORB_GET_PIN_WIDGET_CONTROL, 0, &result);
1235: if (err)
1236: return err;
1237: if (mc->un.ord == 0) {
1238: result &= ~CORB_PWC_HEADPHONE;
1239: } else {
1240: result |= CORB_PWC_HEADPHONE;
1241: }
1242: err = this->comresp(this, nid,
1243: CORB_SET_PIN_WIDGET_CONTROL, result, &result);
1244: if (err)
1245: return err;
1246: }
1247:
1248: /* DAC group selection */
1249: else if (target == MI_TARGET_DAC) {
1250: if (this->running)
1251: return EBUSY;
1252: if (mc->un.ord >= this->dacs.ngroups)
1253: return EINVAL;
1254: return azalia_codec_construct_format(this,
1255: mc->un.ord, this->adcs.cur);
1256: }
1257:
1258: /* ADC selection */
1259: else if (target == MI_TARGET_ADC) {
1260: if (this->running)
1261: return EBUSY;
1262: if (mc->un.ord >= this->adcs.ngroups)
1263: return EINVAL;
1264: return azalia_codec_construct_format(this,
1265: this->dacs.cur, mc->un.ord);
1266: }
1267:
1268: /* Volume knob */
1269: else if (target == MI_TARGET_VOLUME) {
1270: if (mc->un.value.num_channels != 1)
1271: return EINVAL;
1272: if (!azalia_generic_mixer_validate_value(this, nid,
1273: target, mc->un.value.level[0]))
1274: return EINVAL;
1275: value = azalia_generic_mixer_to_device_value(this, nid, target,
1276: mc->un.value.level[0]) | CORB_VKNOB_DIRECT;
1277: err = this->comresp(this, nid, CORB_SET_VOLUME_KNOB,
1278: value, &result);
1279: if (err)
1280: return err;
1281: }
1282:
1283: else {
1284: printf("%s: internal error in %s: target=%x\n",
1285: XNAME(this), __func__, target);
1286: return -1;
1287: }
1288: return 0;
1289: }
1290:
1291: int
1292: azalia_generic_mixer_pinctrl(codec_t *this, nid_t nid, uint32_t value)
1293: {
1294: int err;
1295: uint32_t result;
1296:
1297: err = this->comresp(this, nid, CORB_GET_PIN_WIDGET_CONTROL, 0, &result);
1298: if (err)
1299: return err;
1300: result &= ~(CORB_PWC_OUTPUT | CORB_PWC_INPUT);
1301: result |= value & (CORB_PWC_OUTPUT | CORB_PWC_INPUT);
1302: return this->comresp(this, nid,
1303: CORB_SET_PIN_WIDGET_CONTROL, result, NULL);
1304: }
1305:
1306: u_char
1307: azalia_generic_mixer_from_device_value(const codec_t *this, nid_t nid, int target,
1308: uint32_t dv)
1309: {
1310: #ifdef MAX_VOLUME_255
1311: uint32_t dmax;
1312:
1313: if (IS_MI_TARGET_INAMP(target))
1314: dmax = COP_AMPCAP_NUMSTEPS(this->w[nid].inamp_cap);
1315: else if (target == MI_TARGET_OUTAMP)
1316: dmax = COP_AMPCAP_NUMSTEPS(this->w[nid].outamp_cap);
1317: else if (target == MI_TARGET_VOLUME)
1318: dmax = COP_VKCAP_NUMSTEPS(this->w[nid].d.volume.cap);
1319: else {
1320: printf("unknown target: %d\n", target);
1321: dmax = 255;
1322: }
1323: if (dv <= 0 || dmax == 0)
1324: return AUDIO_MIN_GAIN;
1325: if (dv >= dmax)
1326: return AUDIO_MAX_GAIN - AUDIO_MAX_GAIN % dmax;
1327: return dv * (AUDIO_MAX_GAIN - AUDIO_MAX_GAIN % dmax) / dmax;
1328: #else
1329: return dv;
1330: #endif
1331: }
1332:
1333: uint32_t
1334: azalia_generic_mixer_to_device_value(const codec_t *this, nid_t nid, int target,
1335: u_char uv)
1336: {
1337: #ifdef MAX_VOLUME_255
1338: uint32_t dmax;
1339:
1340: if (IS_MI_TARGET_INAMP(target))
1341: dmax = COP_AMPCAP_NUMSTEPS(this->w[nid].inamp_cap);
1342: else if (target == MI_TARGET_OUTAMP)
1343: dmax = COP_AMPCAP_NUMSTEPS(this->w[nid].outamp_cap);
1344: else if (target == MI_TARGET_VOLUME)
1345: dmax = COP_VKCAP_NUMSTEPS(this->w[nid].d.volume.cap);
1346: else {
1347: printf("unknown target: %d\n", target);
1348: dmax = 255;
1349: }
1350: if (uv <= AUDIO_MIN_GAIN || dmax == 0)
1351: return 0;
1352: if (uv >= AUDIO_MAX_GAIN - AUDIO_MAX_GAIN % dmax)
1353: return dmax;
1354: return uv * dmax / (AUDIO_MAX_GAIN - AUDIO_MAX_GAIN % dmax);
1355: #else
1356: return uv;
1357: #endif
1358: }
1359:
1360: uint32_t
1361: azalia_generic_mixer_max(const codec_t *this, nid_t nid, int target)
1362: {
1363: #ifdef MAX_VOLUME_255
1364: return AUDIO_MAX_GAIN;
1365: #else
1366: uint32_t dmax;
1367:
1368: if (IS_MI_TARGET_INAMP(target))
1369: dmax = COP_AMPCAP_NUMSTEPS(this->w[nid].inamp_cap);
1370: else if (target == MI_TARGET_OUTAMP)
1371: dmax = COP_AMPCAP_NUMSTEPS(this->w[nid].outamp_cap);
1372: else if (target == MI_TARGET_VOLUME)
1373: dmax = COP_VKCAP_NUMSTEPS(this->w[nid].d.volume.cap);
1374: return dmax;
1375: #endif
1376: }
1377:
1378: boolean_t
1379: azalia_generic_mixer_validate_value(const codec_t *this, nid_t nid, int target,
1380: u_char uv)
1381: {
1382: #ifdef MAX_VOLUME_255
1383: return TRUE;
1384: #else
1385: return uv <= generic_mixer_max(this, nid, target);
1386: #endif
1387: }
1388:
1389: int
1390: azalia_generic_set_port(codec_t *this, mixer_ctrl_t *mc)
1391: {
1392: const mixer_item_t *m;
1393:
1394: if (mc->dev >= this->nmixers)
1395: return ENXIO;
1396: m = &this->mixers[mc->dev];
1397: if (mc->type != m->devinfo.type)
1398: return EINVAL;
1399: if (mc->type == AUDIO_MIXER_CLASS)
1400: return 0; /* nothing to do */
1401: return azalia_generic_mixer_set(this, m->nid, m->target, mc);
1402: }
1403:
1404: int
1405: azalia_generic_get_port(codec_t *this, mixer_ctrl_t *mc)
1406: {
1407: const mixer_item_t *m;
1408:
1409: if (mc->dev >= this->nmixers)
1410: return ENXIO;
1411: m = &this->mixers[mc->dev];
1412: mc->type = m->devinfo.type;
1413: if (mc->type == AUDIO_MIXER_CLASS)
1414: return 0; /* nothing to do */
1415: return azalia_generic_mixer_get(this, m->nid, m->target, mc);
1416: }
1417:
1418:
1419: /* ----------------------------------------------------------------
1420: * Realtek ALC260
1421: *
1422: * Fujitsu LOOX T70M/T
1423: * Internal Speaker: 0x10
1424: * Front Headphone: 0x14
1425: * Front mic: 0x12
1426: * ---------------------------------------------------------------- */
1427:
1428: #define ALC260_FUJITSU_ID 0x132610cf
1429: static const mixer_item_t alc260_mixer_items[] = {
1430: {{AZ_CLASS_INPUT, {AudioCinputs}, AUDIO_MIXER_CLASS, AZ_CLASS_INPUT, 0, 0}, 0},
1431: {{AZ_CLASS_OUTPUT, {AudioCoutputs}, AUDIO_MIXER_CLASS, AZ_CLASS_OUTPUT, 0, 0}, 0},
1432: {{AZ_CLASS_RECORD, {AudioCrecord}, AUDIO_MIXER_CLASS, AZ_CLASS_RECORD, 0, 0}, 0},
1433:
1434: {{0, {AudioNmaster}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
1435: 0, 0, .un.v={{""}, 2, 3}}, 0x08, MI_TARGET_OUTAMP}, /* and 0x09, 0x0a(mono) */
1436: {{0, {AudioNmaster".mute"}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
1437: 0, 0, ENUM_OFFON}, 0x0f, MI_TARGET_OUTAMP},
1438: {{0, {AudioNheadphone".mute"}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
1439: 0, 0, ENUM_OFFON}, 0x10, MI_TARGET_OUTAMP},
1440: {{0, {AudioNheadphone".boost"}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
1441: 0, 0, ENUM_OFFON}, 0x10, MI_TARGET_PINBOOST},
1442: {{0, {AudioNmono".mute"}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
1443: 0, 0, ENUM_OFFON}, 0x11, MI_TARGET_OUTAMP},
1444: {{0, {"mic1.mute"}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
1445: 0, 0, ENUM_OFFON}, 0x12, MI_TARGET_OUTAMP},
1446: {{0, {"mic1"}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
1447: 0, 0, ENUM_IO}, 0x12, MI_TARGET_PINDIR},
1448: {{0, {"mic2.mute"}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
1449: 0, 0, ENUM_OFFON}, 0x13, MI_TARGET_OUTAMP},
1450: {{0, {"mic2"}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
1451: 0, 0, ENUM_IO}, 0x13, MI_TARGET_PINDIR},
1452: {{0, {"line1.mute"}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
1453: 0, 0, ENUM_OFFON}, 0x14, MI_TARGET_OUTAMP},
1454: {{0, {"line1"}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
1455: 0, 0, ENUM_IO}, 0x14, MI_TARGET_PINDIR},
1456: {{0, {"line2.mute"}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
1457: 0, 0, ENUM_OFFON}, 0x15, MI_TARGET_OUTAMP},
1458: {{0, {"line2"}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
1459: 0, 0, ENUM_IO}, 0x15, MI_TARGET_PINDIR},
1460:
1461: {{0, {AudioNdac".mute"}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
1462: 0, 0, ENUM_OFFON}, 0x08, MI_TARGET_INAMP(0)}, /* and 0x09, 0x0a(mono) */
1463: {{0, {"mic1.mute"}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
1464: 0, 0, ENUM_OFFON}, 0x07, MI_TARGET_INAMP(0)},
1465: {{0, {"mic1"}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
1466: 0, 0, .un.v={{""}, 2, MIXER_DELTA(65)}}, 0x07, MI_TARGET_INAMP(0)},
1467: {{0, {"mic2.mute"}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
1468: 0, 0, ENUM_OFFON}, 0x07, MI_TARGET_INAMP(1)},
1469: {{0, {"mic2"}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
1470: 0, 0, .un.v={{""}, 2, MIXER_DELTA(65)}}, 0x07, MI_TARGET_INAMP(1)},
1471: {{0, {"line1.mute"}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
1472: 0, 0, ENUM_OFFON}, 0x07, MI_TARGET_INAMP(2)},
1473: {{0, {"line1"}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
1474: 0, 0, .un.v={{""}, 2, 3}}, 0x07, MI_TARGET_INAMP(2)},
1475: {{0, {"line2.mute"}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
1476: 0, 0, ENUM_OFFON}, 0x07, MI_TARGET_INAMP(3)},
1477: {{0, {"line2"}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
1478: 0, 0, .un.v={{""}, 2, MIXER_DELTA(65)}}, 0x07, MI_TARGET_INAMP(3)},
1479: {{0, {AudioNcd".mute"}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
1480: 0, 0, ENUM_OFFON}, 0x07, MI_TARGET_INAMP(4)},
1481: {{0, {AudioNcd}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
1482: 0, 0, .un.v={{""}, 2, MIXER_DELTA(65)}}, 0x07, MI_TARGET_INAMP(4)},
1483: {{0, {AudioNspeaker".mute"}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
1484: 0, 0, ENUM_OFFON}, 0x07, MI_TARGET_INAMP(5)},
1485: {{0, {AudioNspeaker}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
1486: 0, 0, .un.v={{""}, 2, MIXER_DELTA(65)}}, 0x07, MI_TARGET_INAMP(5)},
1487:
1488: {{0, {"adc04.source"}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, 0, 0,
1489: .un.e={5, {{{"mic1"}, 0}, {{"mic2"}, 1}, {{"line1"}, 2},
1490: {{"line2"}, 3}, {{AudioNcd}, 4}}}},
1491: 0x04, MI_TARGET_CONNLIST},
1492: {{0, {"adc04.mute"}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, 0, 0,
1493: ENUM_OFFON}, 0x04, MI_TARGET_INAMP(0)},
1494: {{0, {"adc04"}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD, 0, 0,
1495: .un.v={{""}, 2, MIXER_DELTA(35)}}, 0x04, MI_TARGET_INAMP(0)},
1496: {{0, {"adc05.source"}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, 0, 0,
1497: .un.e={6, {{{"mic1"}, 0}, {{"mic2"}, 1}, {{"line1"}, 2},
1498: {{"line2"}, 3}, {{AudioNcd}, 4}, {{AudioNmixerout}, 5}}}},
1499: 0x05, MI_TARGET_CONNLIST},
1500: {{0, {"adc05.mute"}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, 0, 0,
1501: ENUM_OFFON}, 0x05, MI_TARGET_INAMP(0)},
1502: {{0, {"adc05"}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD, 0, 0,
1503: .un.v={{""}, 2, MIXER_DELTA(35)}}, 0x05, MI_TARGET_INAMP(0)},
1504:
1505: {{0, {"usingdac"}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, 0, 0,
1506: .un.e={2, {{{"analog"}, 0}, {{"digital"}, 1}}}}, 0, MI_TARGET_DAC},
1507: {{0, {"usingadc"}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, 0, 0,
1508: .un.e={3, {{{"adc04"}, 0}, {{"adc05"}, 1}, {{"digital"}, 2}}}}, 0, MI_TARGET_ADC},
1509: };
1510:
1511: static const mixer_item_t alc260_loox_mixer_items[] = {
1512: {{AZ_CLASS_INPUT, {AudioCinputs}, AUDIO_MIXER_CLASS, AZ_CLASS_INPUT, 0, 0}, 0},
1513: {{AZ_CLASS_OUTPUT, {AudioCoutputs}, AUDIO_MIXER_CLASS, AZ_CLASS_OUTPUT, 0, 0}, 0},
1514: {{AZ_CLASS_RECORD, {AudioCrecord}, AUDIO_MIXER_CLASS, AZ_CLASS_RECORD, 0, 0}, 0},
1515:
1516: {{0, {AudioNmaster}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
1517: 0, 0, .un.v={{""}, 2, 3}}, 0x08, MI_TARGET_OUTAMP}, /* and 0x09, 0x0a(mono) */
1518: {{0, {AudioNmaster".mute"}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
1519: 0, 0, ENUM_OFFON}, 0x10, MI_TARGET_OUTAMP},
1520: {{0, {AudioNmaster".boost"}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
1521: 0, 0, ENUM_OFFON}, 0x10, MI_TARGET_PINBOOST},
1522: {{0, {AudioNheadphone".mute"}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
1523: 0, 0, ENUM_OFFON}, 0x14, MI_TARGET_OUTAMP},
1524: {{0, {AudioNheadphone".boost"}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
1525: 0, 0, ENUM_OFFON}, 0x14, MI_TARGET_PINBOOST},
1526:
1527: {{0, {AudioNdac".mute"}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
1528: 0, 0, ENUM_OFFON}, 0x08, MI_TARGET_INAMP(0)}, /* and 0x09, 0x0a(mono) */
1529: {{0, {AudioNmicrophone".mute"}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
1530: 0, 0, ENUM_OFFON}, 0x07, MI_TARGET_INAMP(0)},
1531: {{0, {AudioNmicrophone}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
1532: 0, 0, .un.v={{""}, 2, MIXER_DELTA(65)}}, 0x07, MI_TARGET_INAMP(0)},
1533: {{0, {AudioNcd".mute"}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
1534: 0, 0, ENUM_OFFON}, 0x07, MI_TARGET_INAMP(4)},
1535: {{0, {AudioNcd}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
1536: 0, 0, .un.v={{""}, 2, MIXER_DELTA(65)}}, 0x07, MI_TARGET_INAMP(4)},
1537: {{0, {AudioNspeaker".mute"}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
1538: 0, 0, ENUM_OFFON}, 0x07, MI_TARGET_INAMP(5)},
1539: {{0, {AudioNspeaker}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
1540: 0, 0, .un.v={{""}, 2, MIXER_DELTA(65)}}, 0x07, MI_TARGET_INAMP(5)},
1541:
1542: {{0, {"adc04.source"}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, 0, 0,
1543: .un.e={2, {{{AudioNmicrophone}, 0}, {{AudioNcd}, 4}}}}, 0x04, MI_TARGET_CONNLIST},
1544: {{0, {"adc04.mute"}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, 0, 0,
1545: ENUM_OFFON}, 0x04, MI_TARGET_INAMP(0)},
1546: {{0, {"adc04"}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD, 0, 0,
1547: .un.v={{""}, 2, MIXER_DELTA(35)}}, 0x04, MI_TARGET_INAMP(0)},
1548: {{0, {"adc05.source"}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, 0, 0,
1549: .un.e={3, {{{AudioNmicrophone}, 0}, {{AudioNcd}, 4}, {{AudioNmixerout}, 5}}}},
1550: 0x05, MI_TARGET_CONNLIST},
1551: {{0, {"adc05.mute"}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, 0, 0,
1552: ENUM_OFFON}, 0x05, MI_TARGET_INAMP(0)},
1553: {{0, {"adc05"}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD, 0, 0,
1554: .un.v={{""}, 2, MIXER_DELTA(35)}}, 0x05, MI_TARGET_INAMP(0)},
1555:
1556: {{0, {"usingdac"}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, 0, 0,
1557: .un.e={2, {{{"analog"}, 0}, {{"digital"}, 1}}}}, 0, MI_TARGET_DAC},
1558: {{0, {"usingadc"}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, 0, 0,
1559: .un.e={3, {{{"adc04"}, 0}, {{"adc05"}, 1}, {{"digital"}, 2}}}}, 0, MI_TARGET_ADC},
1560: };
1561:
1562: int
1563: azalia_alc260_mixer_init(codec_t *this)
1564: {
1565: const mixer_item_t *mi;
1566: mixer_ctrl_t mc;
1567:
1568: switch (this->subid) {
1569: case ALC260_FUJITSU_ID:
1570: this->nmixers = sizeof(alc260_loox_mixer_items) / sizeof(mixer_item_t);
1571: mi = alc260_loox_mixer_items;
1572: break;
1573: default:
1574: this->nmixers = sizeof(alc260_mixer_items) / sizeof(mixer_item_t);
1575: mi = alc260_mixer_items;
1576: }
1577: this->mixers = malloc(sizeof(mixer_item_t) * this->nmixers,
1578: M_DEVBUF, M_NOWAIT);
1579: if (this->mixers == NULL) {
1580: printf("%s: out of memory in %s\n", XNAME(this), __func__);
1581: return ENOMEM;
1582: }
1583: bzero(this->mixers, sizeof(mixer_item_t) * this->nmixers);
1584: memcpy(this->mixers, mi, sizeof(mixer_item_t) * this->nmixers);
1585: azalia_generic_mixer_fix_indexes(this);
1586: azalia_generic_mixer_default(this);
1587:
1588: mc.dev = -1; /* no need for generic_mixer_set() */
1589: mc.type = AUDIO_MIXER_ENUM;
1590: mc.un.ord = 1; /* pindir: output */
1591: azalia_generic_mixer_set(this, 0x0f, MI_TARGET_PINDIR, &mc); /* lineout */
1592: azalia_generic_mixer_set(this, 0x10, MI_TARGET_PINDIR, &mc); /* headphones */
1593: mc.un.ord = 0; /* pindir: input */
1594: azalia_generic_mixer_set(this, 0x12, MI_TARGET_PINDIR, &mc); /* mic1 */
1595: azalia_generic_mixer_set(this, 0x13, MI_TARGET_PINDIR, &mc); /* mic2 */
1596: azalia_generic_mixer_set(this, 0x14, MI_TARGET_PINDIR, &mc); /* line1 */
1597: azalia_generic_mixer_set(this, 0x15, MI_TARGET_PINDIR, &mc); /* line2 */
1598: mc.un.ord = 0; /* mute: off */
1599: azalia_generic_mixer_set(this, 0x08, MI_TARGET_INAMP(0), &mc);
1600: azalia_generic_mixer_set(this, 0x08, MI_TARGET_INAMP(1), &mc);
1601: azalia_generic_mixer_set(this, 0x09, MI_TARGET_INAMP(0), &mc);
1602: azalia_generic_mixer_set(this, 0x09, MI_TARGET_INAMP(1), &mc);
1603: azalia_generic_mixer_set(this, 0x0a, MI_TARGET_INAMP(0), &mc);
1604: azalia_generic_mixer_set(this, 0x0a, MI_TARGET_INAMP(1), &mc);
1605: if (this->subid == ALC260_FUJITSU_ID) {
1606: mc.un.ord = 1; /* pindir: output */
1607: azalia_generic_mixer_set(this, 0x14, MI_TARGET_PINDIR, &mc); /* line1 */
1608: mc.un.ord = 4; /* connlist: cd */
1609: azalia_generic_mixer_set(this, 0x05, MI_TARGET_CONNLIST, &mc);
1610: }
1611: return 0;
1612: }
1613:
1614: int
1615: azalia_alc260_init_dacgroup(codec_t *this)
1616: {
1617: static const convgroupset_t dacs = {
1618: -1, 2,
1619: {{1, {0x02}}, /* analog 2ch */
1620: {1, {0x03}}}}; /* digital */
1621: static const convgroupset_t adcs = {
1622: -1, 3,
1623: {{1, {0x04}}, /* analog 2ch */
1624: {1, {0x05}}, /* analog 2ch */
1625: {1, {0x06}}}}; /* digital */
1626:
1627: this->dacs = dacs;
1628: this->adcs = adcs;
1629: return 0;
1630: }
1631:
1632: int
1633: azalia_alc260_set_port(codec_t *this, mixer_ctrl_t *mc)
1634: {
1635: const mixer_item_t *m;
1636: mixer_ctrl_t mc2;
1637: int err;
1638:
1639: if (mc->dev >= this->nmixers)
1640: return ENXIO;
1641: m = &this->mixers[mc->dev];
1642: if (mc->type != m->devinfo.type)
1643: return EINVAL;
1644: if (mc->type == AUDIO_MIXER_CLASS)
1645: return 0;
1646: if (m->nid == 0x08 && m->target == MI_TARGET_OUTAMP) {
1647: DPRINTF(("%s: hook for outputs.master\n", __func__));
1648: err = azalia_generic_mixer_set(this, m->nid, m->target, mc);
1649: if (!err) {
1650: azalia_generic_mixer_set(this, 0x09, m->target, mc);
1651: mc2 = *mc;
1652: mc2.un.value.num_channels = 1;
1653: mc2.un.value.level[0] = (mc2.un.value.level[0]
1654: + mc2.un.value.level[1]) / 2;
1655: azalia_generic_mixer_set(this, 0x0a, m->target, &mc2);
1656: }
1657: return err;
1658: } else if (m->nid == 0x08 && m->target == MI_TARGET_INAMP(0)) {
1659: DPRINTF(("%s: hook for inputs.dac.mute\n", __func__));
1660: err = azalia_generic_mixer_set(this, m->nid, m->target, mc);
1661: if (!err) {
1662: azalia_generic_mixer_set(this, 0x09, m->target, mc);
1663: azalia_generic_mixer_set(this, 0x0a, m->target, mc);
1664: }
1665: return err;
1666: } else if (m->nid == 0x04 &&
1667: m->target == MI_TARGET_CONNLIST &&
1668: m->devinfo.un.e.num_mem == 2) {
1669: if (1 <= mc->un.ord && mc->un.ord <= 3)
1670: return EINVAL;
1671: } else if (m->nid == 0x05 &&
1672: m->target == MI_TARGET_CONNLIST &&
1673: m->devinfo.un.e.num_mem == 3) {
1674: if (1 <= mc->un.ord && mc->un.ord <= 3)
1675: return EINVAL;
1676: }
1677: return azalia_generic_mixer_set(this, m->nid, m->target, mc);
1678: }
1679:
1680: /* ----------------------------------------------------------------
1681: * Realtek ALC880
1682: * ---------------------------------------------------------------- */
1683:
1684: int
1685: azalia_alc880_init_dacgroup(codec_t *this)
1686: {
1687: static const convgroupset_t dacs = {
1688: -1, 2,
1689: {{4, {0x02, 0x03, 0x04, 0x05}}, /* analog 8ch */
1690: {1, {0x06}}}}; /* digital */
1691: static const convgroupset_t adcs = {
1692: -1, 2,
1693: {{2, {0x08, 0x09}}, /* analog 4ch */
1694: {1, {0x0a}}}}; /* digital */
1695:
1696: this->dacs = dacs;
1697: this->adcs = adcs;
1698: return 0;
1699: }
1700:
1701: /* ----------------------------------------------------------------
1702: * Realtek ALC882
1703: * ---------------------------------------------------------------- */
1704:
1705: static const mixer_item_t alc882_mixer_items[] = {
1706: {{AZ_CLASS_INPUT, {AudioCinputs}, AUDIO_MIXER_CLASS, AZ_CLASS_INPUT, 0, 0}, 0},
1707: {{AZ_CLASS_OUTPUT, {AudioCoutputs}, AUDIO_MIXER_CLASS, AZ_CLASS_OUTPUT, 0, 0}, 0},
1708: {{AZ_CLASS_RECORD, {AudioCrecord}, AUDIO_MIXER_CLASS, AZ_CLASS_RECORD, 0, 0}, 0},
1709:
1710: /* 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x14,0x15,0x16,0x17 */
1711: {{0, {"mic1."AudioNmute}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
1712: 0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(0)},
1713: {{0, {"mic1"}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
1714: 0, 0, .un.v={{""}, 2, MIXER_DELTA(31)}}, 0x0b, MI_TARGET_INAMP(0)},
1715: {{0, {"mic2."AudioNmute}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
1716: 0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(1)},
1717: {{0, {"mic2"}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
1718: 0, 0, .un.v={{""}, 2, MIXER_DELTA(31)}}, 0x0b, MI_TARGET_INAMP(1)},
1719: {{0, {AudioNline"."AudioNmute}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
1720: 0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(2)},
1721: {{0, {AudioNline}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
1722: 0, 0, .un.v={{""}, 2, MIXER_DELTA(31)}}, 0x0b, MI_TARGET_INAMP(2)},
1723: {{0, {AudioNcd"."AudioNmute}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
1724: 0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(4)},
1725: {{0, {AudioNcd}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
1726: 0, 0, .un.v={{""}, 2, MIXER_DELTA(31)}}, 0x0b, MI_TARGET_INAMP(4)},
1727: {{0, {AudioNspeaker"."AudioNmute}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
1728: 0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(5)},
1729: {{0, {AudioNspeaker}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
1730: 0, 0, .un.v={{""}, 1, MIXER_DELTA(31)}}, 0x0b, MI_TARGET_INAMP(5)},
1731:
1732: {{0, {AudioNmaster}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
1733: 0, 0, .un.v={{""}, 2, MIXER_DELTA(31)}}, 0x0c, MI_TARGET_OUTAMP},
1734: {{0, {AudioNmaster"."AudioNmute}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
1735: 0, 0, ENUM_OFFON}, 0x14, MI_TARGET_OUTAMP},
1736: {{0, {AudioNmaster".boost"}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
1737: 0, 0, ENUM_OFFON}, 0x14, MI_TARGET_PINBOOST},
1738: {{0, {AudioNheadphone"."AudioNmute}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
1739: 0, 0, ENUM_OFFON}, 0x1b, MI_TARGET_OUTAMP},
1740: {{0, {AudioNheadphone".boost"}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
1741: 0, 0, ENUM_OFFON}, 0x1b, MI_TARGET_PINBOOST},
1742: {{0, {AzaliaNfront".dac.mute"}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
1743: 0, 0, ENUM_OFFON}, 0x0c, MI_TARGET_INAMP(0)},
1744: {{0, {AzaliaNfront".mixer.mute"}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
1745: 0, 0, ENUM_OFFON}, 0x0c, MI_TARGET_INAMP(1)},
1746:
1747: {{0, {AudioNsurround}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
1748: 0, 0, .un.v={{""}, 2, MIXER_DELTA(31)}}, 0x0d, MI_TARGET_OUTAMP},
1749: {{0, {AudioNsurround"."AudioNmute}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
1750: 0, 0, ENUM_OFFON}, 0x15, MI_TARGET_OUTAMP},
1751: {{0, {AudioNsurround".boost"}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
1752: 0, 0, ENUM_OFFON}, 0x15, MI_TARGET_PINBOOST},
1753: {{0, {AudioNsurround".dac.mut"}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
1754: 0, 0, ENUM_OFFON}, 0x0d, MI_TARGET_INAMP(0)},
1755: {{0, {AudioNsurround".mixer.m"}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
1756: 0, 0, ENUM_OFFON}, 0x0d, MI_TARGET_INAMP(1)},
1757:
1758: {{0, {AzaliaNclfe}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
1759: 0, 0, .un.v={{""}, 2, MIXER_DELTA(31)}}, 0x0e, MI_TARGET_OUTAMP},
1760: {{0, {AzaliaNclfe"."AudioNmute}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
1761: 0, 0, ENUM_OFFON}, 0x16, MI_TARGET_OUTAMP},
1762: {{0, {AzaliaNclfe".boost"}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
1763: 0, 0, ENUM_OFFON}, 0x16, MI_TARGET_PINBOOST},
1764: {{0, {AzaliaNclfe".dac.mute"}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
1765: 0, 0, ENUM_OFFON}, 0x0e, MI_TARGET_INAMP(0)},
1766: {{0, {AzaliaNclfe".mixer.mute"}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
1767: 0, 0, ENUM_OFFON}, 0x0e, MI_TARGET_INAMP(1)},
1768:
1769: {{0, {AzaliaNside}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
1770: 0, 0, .un.v={{""}, 2, MIXER_DELTA(31)}}, 0x0f, MI_TARGET_OUTAMP},
1771: {{0, {AzaliaNside"."AudioNmute}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
1772: 0, 0, ENUM_OFFON}, 0x17, MI_TARGET_OUTAMP},
1773: {{0, {AzaliaNside".boost"}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
1774: 0, 0, ENUM_OFFON}, 0x17, MI_TARGET_PINBOOST},
1775: {{0, {AzaliaNside".dac.mute"}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
1776: 0, 0, ENUM_OFFON}, 0x0f, MI_TARGET_INAMP(0)},
1777: {{0, {AzaliaNside".mixer.mute"}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
1778: 0, 0, ENUM_OFFON}, 0x0f, MI_TARGET_INAMP(1)},
1779:
1780: /* 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x14,0x15,0x16,0x17,0xb */
1781: #define ALC882_MIC1 0x001
1782: #define ALC882_MIC2 0x002
1783: #define ALC882_LINE 0x004
1784: #define ALC882_CD 0x010
1785: #define ALC882_BEEP 0x020
1786: #define ALC882_MIX 0x400
1787: #define ALC882_MASK 0x437
1788: {{0, {AzaliaNfront"."AudioNmute}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD,
1789: 0, 0, ENUM_OFFON}, 0x07, MI_TARGET_INAMP(0)},
1790: {{0, {AzaliaNfront}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD,
1791: 0, 0, .un.v={{""}, 2, MIXER_DELTA(31)}}, 0x07, MI_TARGET_INAMP(0)},
1792: {{0, {AzaliaNfront"."AudioNsource}, AUDIO_MIXER_SET, AZ_CLASS_RECORD,
1793: 0, 0, .un.s={6, {{{"mic1"}, ALC882_MIC1}, {{"mic2"}, ALC882_MIC2},
1794: {{AudioNline}, ALC882_LINE}, {{AudioNcd}, ALC882_CD},
1795: {{AudioNspeaker}, ALC882_BEEP},
1796: {{AudioNmixerout}, ALC882_MIX}}}}, 0x24, -1},
1797: {{0, {AudioNsurround"."AudioNmute}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD,
1798: 0, 0, ENUM_OFFON}, 0x08, MI_TARGET_INAMP(0)},
1799: {{0, {AudioNsurround}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD,
1800: 0, 0, .un.v={{""}, 2, MIXER_DELTA(31)}}, 0x08, MI_TARGET_INAMP(0)},
1801: {{0, {AudioNsurround"."AudioNsource}, AUDIO_MIXER_SET, AZ_CLASS_RECORD,
1802: 0, 0, .un.s={6, {{{"mic1"}, ALC882_MIC1}, {{"mic2"}, ALC882_MIC2},
1803: {{AudioNline}, ALC882_LINE}, {{AudioNcd}, ALC882_CD},
1804: {{AudioNspeaker}, ALC882_BEEP},
1805: {{AudioNmixerout}, ALC882_MIX}}}}, 0x23, -1},
1806: {{0, {AzaliaNclfe"."AudioNmute}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD,
1807: 0, 0, ENUM_OFFON}, 0x09, MI_TARGET_INAMP(0)},
1808: {{0, {AzaliaNclfe}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD,
1809: 0, 0, .un.v={{""}, 2, MIXER_DELTA(31)}}, 0x09, MI_TARGET_INAMP(0)},
1810: {{0, {AzaliaNclfe"."AudioNsource}, AUDIO_MIXER_SET, AZ_CLASS_RECORD,
1811: 0, 0, .un.s={6, {{{"mic1"}, ALC882_MIC1}, {{"mic2"}, ALC882_MIC2},
1812: {{AudioNline}, ALC882_LINE}, {{AudioNcd}, ALC882_CD},
1813: {{AudioNspeaker}, ALC882_BEEP},
1814: {{AudioNmixerout}, ALC882_MIX}}}}, 0x22, -1},
1815:
1816: {{0, {"usingdac"}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, 0, 0,
1817: .un.e={2, {{{"analog"}, 0}, {{"digital"}, 1}}}}, 0, MI_TARGET_DAC},
1818: {{0, {"usingadc"}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, 0, 0,
1819: .un.e={2, {{{"analog"}, 0}, {{"digital"}, 1}}}}, 0, MI_TARGET_ADC},
1820: };
1821:
1822: int
1823: azalia_alc882_mixer_init(codec_t *this)
1824: {
1825: mixer_ctrl_t mc;
1826:
1827: this->nmixers = sizeof(alc882_mixer_items) / sizeof(mixer_item_t);
1828: this->mixers = malloc(sizeof(mixer_item_t) * this->nmixers,
1829: M_DEVBUF, M_NOWAIT);
1830: if (this->mixers == NULL) {
1831: printf("%s: out of memory in %s\n", XNAME(this), __func__);
1832: return ENOMEM;
1833: }
1834: bzero(this->mixers, sizeof(mixer_item_t) * this->maxmixers);
1835: memcpy(this->mixers, alc882_mixer_items,
1836: sizeof(mixer_item_t) * this->nmixers);
1837: azalia_generic_mixer_fix_indexes(this);
1838: azalia_generic_mixer_default(this);
1839:
1840: mc.dev = -1;
1841: mc.type = AUDIO_MIXER_ENUM;
1842: mc.un.ord = 1; /* pindir: output */
1843: azalia_generic_mixer_set(this, 0x14, MI_TARGET_PINDIR, &mc);
1844: azalia_generic_mixer_set(this, 0x1b, MI_TARGET_PINDIR, &mc);
1845: azalia_generic_mixer_set(this, 0x15, MI_TARGET_PINDIR, &mc);
1846: azalia_generic_mixer_set(this, 0x16, MI_TARGET_PINDIR, &mc);
1847: azalia_generic_mixer_set(this, 0x17, MI_TARGET_PINDIR, &mc);
1848: mc.un.ord = 0; /* [0] 0x0c */
1849: azalia_generic_mixer_set(this, 0x14, MI_TARGET_CONNLIST, &mc);
1850: azalia_generic_mixer_set(this, 0x1b, MI_TARGET_CONNLIST, &mc);
1851: mc.un.ord = 1; /* [1] 0x0d */
1852: azalia_generic_mixer_set(this, 0x15, MI_TARGET_CONNLIST, &mc);
1853: mc.un.ord = 2; /* [2] 0x0e */
1854: azalia_generic_mixer_set(this, 0x16, MI_TARGET_CONNLIST, &mc);
1855: mc.un.ord = 2; /* [3] 0x0fb */
1856: azalia_generic_mixer_set(this, 0x17, MI_TARGET_CONNLIST, &mc);
1857:
1858: mc.un.ord = 0; /* pindir: input */
1859: azalia_generic_mixer_set(this, 0x18, MI_TARGET_PINDIR, &mc);
1860: azalia_generic_mixer_set(this, 0x19, MI_TARGET_PINDIR, &mc);
1861: azalia_generic_mixer_set(this, 0x1a, MI_TARGET_PINDIR, &mc);
1862: /* XXX: inamp for 18/19/1a */
1863:
1864: mc.un.ord = 0; /* unmute */
1865: azalia_generic_mixer_set(this, 0x24, MI_TARGET_INAMP(0), &mc);
1866: azalia_generic_mixer_set(this, 0x23, MI_TARGET_INAMP(1), &mc);
1867: azalia_generic_mixer_set(this, 0x22, MI_TARGET_INAMP(2), &mc);
1868: return 0;
1869: }
1870:
1871: int
1872: azalia_alc882_init_dacgroup(codec_t *this)
1873: {
1874: #if 0
1875: static const convgroupset_t dacs = {
1876: -1, 3,
1877: {{4, {0x02, 0x03, 0x04, 0x05}}, /* analog 8ch */
1878: {1, {0x06}}, /* digital */
1879: {1, {0x25}}}}; /* another analog */
1880: #else
1881: static const convgroupset_t dacs = {
1882: -1, 2,
1883: {{4, {0x02, 0x03, 0x04, 0x05}}, /* analog 8ch */
1884: {1, {0x06}}}}; /* digital */
1885: #endif
1886: static const convgroupset_t adcs = {
1887: -1, 2,
1888: {{3, {0x07, 0x08, 0x09}}, /* analog 6ch */
1889: {1, {0x0a}}}}; /* digital */
1890:
1891: this->dacs = dacs;
1892: this->adcs = adcs;
1893: return 0;
1894: }
1895:
1896: int
1897: azalia_alc882_set_port(codec_t *this, mixer_ctrl_t *mc)
1898: {
1899: const mixer_item_t *m;
1900: mixer_ctrl_t mc2;
1901: uint32_t mask, bit;
1902: int i, err;
1903:
1904: if (mc->dev >= this->nmixers)
1905: return ENXIO;
1906: m = &this->mixers[mc->dev];
1907: if (mc->type != m->devinfo.type)
1908: return EINVAL;
1909: if (mc->type == AUDIO_MIXER_CLASS)
1910: return 0;
1911: if ((m->nid == 0x22 || m->nid == 0x23 || m->nid == 0x24)
1912: && m->target == -1) {
1913: DPRINTF(("%s: hook for record.*.source\n", __func__));
1914: mc2.dev = -1;
1915: mc2.type = AUDIO_MIXER_ENUM;
1916: bit = 1;
1917: mask = mc->un.mask & ALC882_MASK;
1918: for (i = 0; i < this->w[m->nid].nconnections && i < 32; i++) {
1919: mc2.un.ord = (mask & bit) ? 0 : 1;
1920: err = azalia_generic_mixer_set(this, m->nid,
1921: MI_TARGET_INAMP(i), &mc2);
1922: if (err)
1923: return err;
1924: bit = bit << 1;
1925: }
1926: return 0;
1927: }
1928: return azalia_generic_mixer_set(this, m->nid, m->target, mc);
1929: }
1930:
1931: int
1932: azalia_alc882_get_port(codec_t *this, mixer_ctrl_t *mc)
1933: {
1934: const mixer_item_t *m;
1935: uint32_t mask, bit, result;
1936: int i, err;
1937:
1938: if (mc->dev >= this->nmixers)
1939: return ENXIO;
1940: m = &this->mixers[mc->dev];
1941: mc->type = m->devinfo.type;
1942: if (mc->type == AUDIO_MIXER_CLASS)
1943: return 0;
1944: if ((m->nid == 0x22 || m->nid == 0x23 || m->nid == 0x24)
1945: && m->target == -1) {
1946: DPRINTF(("%s: hook for record.*.source\n", __func__));
1947: mask = 0;
1948: bit = 1;
1949: for (i = 0; i < this->w[m->nid].nconnections && i < 32; i++) {
1950: err = this->comresp(this, m->nid, CORB_GET_AMPLIFIER_GAIN_MUTE,
1951: CORB_GAGM_INPUT | CORB_GAGM_LEFT |
1952: i, &result);
1953: if (err)
1954: return err;
1955: if ((result & CORB_GAGM_MUTE) == 0)
1956: mask |= bit;
1957: bit = bit << 1;
1958: }
1959: mc->un.mask = mask & ALC882_MASK;
1960: return 0;
1961: }
1962: return azalia_generic_mixer_get(this, m->nid, m->target, mc);
1963: }
1964:
1965: /* ----------------------------------------------------------------
1966: * Realtek ALC883
1967: * ALC882 without adc07 and mix24.
1968: * ---------------------------------------------------------------- */
1969:
1970: int
1971: azalia_alc883_init_dacgroup(codec_t *this)
1972: {
1973: static const convgroupset_t dacs = {
1974: -1, 2,
1975: {{4, {0x02, 0x03, 0x04, 0x05}}, /* analog 8ch */
1976: {1, {0x06}}}}; /* digital */
1977:
1978: static const convgroupset_t adcs = {
1979: -1, 2,
1980: {{2, {0x08, 0x09}}, /* analog 4ch */
1981: {1, {0x0a}}}}; /* digital */
1982:
1983: this->dacs = dacs;
1984: this->adcs = adcs;
1985: return 0;
1986: }
1987:
1988: static const mixer_item_t alc883_mixer_items[] = {
1989: {{AZ_CLASS_INPUT, {AudioCinputs}, AUDIO_MIXER_CLASS, AZ_CLASS_INPUT, 0, 0}, 0},
1990: {{AZ_CLASS_OUTPUT, {AudioCoutputs}, AUDIO_MIXER_CLASS, AZ_CLASS_OUTPUT, 0, 0}, 0},
1991: {{AZ_CLASS_RECORD, {AudioCrecord}, AUDIO_MIXER_CLASS, AZ_CLASS_RECORD, 0, 0}, 0},
1992:
1993: /* 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x14,0x15,0x16,0x17 */
1994: {{0, {"mic1."AudioNmute}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
1995: 0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(0)},
1996: {{0, {"mic1"}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
1997: 0, 0, .un.v={{""}, 2, MIXER_DELTA(31)}}, 0x0b, MI_TARGET_INAMP(0)},
1998: {{0, {"mic2."AudioNmute}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
1999: 0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(1)},
2000: {{0, {"mic2"}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
2001: 0, 0, .un.v={{""}, 2, MIXER_DELTA(31)}}, 0x0b, MI_TARGET_INAMP(1)},
2002: {{0, {AudioNline"."AudioNmute}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
2003: 0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(2)},
2004: {{0, {AudioNline}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
2005: 0, 0, .un.v={{""}, 2, MIXER_DELTA(31)}}, 0x0b, MI_TARGET_INAMP(2)},
2006: {{0, {AudioNcd"."AudioNmute}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
2007: 0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(4)},
2008: {{0, {AudioNcd}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
2009: 0, 0, .un.v={{""}, 2, MIXER_DELTA(31)}}, 0x0b, MI_TARGET_INAMP(4)},
2010: {{0, {AudioNspeaker"."AudioNmute}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
2011: 0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(5)},
2012: {{0, {AudioNspeaker}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
2013: 0, 0, .un.v={{""}, 1, MIXER_DELTA(31)}}, 0x0b, MI_TARGET_INAMP(5)},
2014:
2015: {{0, {AudioNmaster}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
2016: 0, 0, .un.v={{""}, 2, MIXER_DELTA(31)}}, 0x0c, MI_TARGET_OUTAMP},
2017: {{0, {AudioNmaster"."AudioNmute}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2018: 0, 0, ENUM_OFFON}, 0x14, MI_TARGET_OUTAMP},
2019: {{0, {AudioNmaster".boost"}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2020: 0, 0, ENUM_OFFON}, 0x14, MI_TARGET_PINBOOST},
2021: {{0, {AudioNheadphone"."AudioNmute}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2022: 0, 0, ENUM_OFFON}, 0x1b, MI_TARGET_OUTAMP},
2023: {{0, {AudioNheadphone".boost"}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2024: 0, 0, ENUM_OFFON}, 0x1b, MI_TARGET_PINBOOST},
2025: {{0, {AzaliaNfront".dac.mute"}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
2026: 0, 0, ENUM_OFFON}, 0x0c, MI_TARGET_INAMP(0)},
2027: {{0, {AzaliaNfront".mixer.mute"}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
2028: 0, 0, ENUM_OFFON}, 0x0c, MI_TARGET_INAMP(1)},
2029: {{0, {AudioNsurround}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
2030: 0, 0, .un.v={{""}, 2, MIXER_DELTA(31)}}, 0x0d, MI_TARGET_OUTAMP},
2031: {{0, {AudioNsurround"."AudioNmute}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2032: 0, 0, ENUM_OFFON}, 0x15, MI_TARGET_OUTAMP},
2033: {{0, {AudioNsurround".boost"}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2034: 0, 0, ENUM_OFFON}, 0x15, MI_TARGET_PINBOOST},
2035: {{0, {AudioNsurround".dac.mut"}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
2036: 0, 0, ENUM_OFFON}, 0x0d, MI_TARGET_INAMP(0)},
2037: {{0, {AudioNsurround".mixer.m"}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
2038: 0, 0, ENUM_OFFON}, 0x0d, MI_TARGET_INAMP(1)},
2039:
2040: {{0, {AzaliaNclfe}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
2041: 0, 0, .un.v={{""}, 2, MIXER_DELTA(31)}}, 0x0e, MI_TARGET_OUTAMP},
2042: {{0, {AzaliaNclfe"."AudioNmute}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2043: 0, 0, ENUM_OFFON}, 0x16, MI_TARGET_OUTAMP},
2044: {{0, {AzaliaNclfe".boost"}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2045: 0, 0, ENUM_OFFON}, 0x16, MI_TARGET_PINBOOST},
2046: {{0, {AzaliaNclfe".dac.mute"}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
2047: 0, 0, ENUM_OFFON}, 0x0e, MI_TARGET_INAMP(0)},
2048: {{0, {AzaliaNclfe".mixer.mute"}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
2049: 0, 0, ENUM_OFFON}, 0x0e, MI_TARGET_INAMP(1)},
2050:
2051: {{0, {AzaliaNside}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
2052: 0, 0, .un.v={{""}, 2, MIXER_DELTA(31)}}, 0x0f, MI_TARGET_OUTAMP},
2053: {{0, {AzaliaNside"."AudioNmute}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2054: 0, 0, ENUM_OFFON}, 0x17, MI_TARGET_OUTAMP},
2055: {{0, {AzaliaNside".boost"}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2056: 0, 0, ENUM_OFFON}, 0x17, MI_TARGET_PINBOOST},
2057: {{0, {AzaliaNside".dac.mute"}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
2058: 0, 0, ENUM_OFFON}, 0x0f, MI_TARGET_INAMP(0)},
2059: {{0, {AzaliaNside".mixer.mute"}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
2060: 0, 0, ENUM_OFFON}, 0x0f, MI_TARGET_INAMP(1)},
2061:
2062: {{0, {AudioNsurround"."AudioNmute}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD,
2063: 0, 0, ENUM_OFFON}, 0x08, MI_TARGET_INAMP(0)},
2064: {{0, {AudioNsurround}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD,
2065: 0, 0, .un.v={{""}, 2, MIXER_DELTA(31)}}, 0x08, MI_TARGET_INAMP(0)},
2066: {{0, {AudioNsurround"."AudioNsource}, AUDIO_MIXER_SET, AZ_CLASS_RECORD,
2067: 0, 0, .un.s={6, {{{"mic1"}, ALC882_MIC1}, {{"mic2"}, ALC882_MIC2},
2068: {{AudioNline}, ALC882_LINE}, {{AudioNcd}, ALC882_CD},
2069: {{AudioNspeaker}, ALC882_BEEP},
2070: {{AudioNmixerout}, ALC882_MIX}}}}, 0x23, -1},
2071:
2072: {{0, {AzaliaNclfe"."AudioNmute}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD,
2073: 0, 0, ENUM_OFFON}, 0x09, MI_TARGET_INAMP(0)},
2074: {{0, {AzaliaNclfe}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD,
2075: 0, 0, .un.v={{""}, 2, MIXER_DELTA(31)}}, 0x09, MI_TARGET_INAMP(0)},
2076: {{0, {AzaliaNclfe"."AudioNsource}, AUDIO_MIXER_SET, AZ_CLASS_RECORD,
2077: 0, 0, .un.s={6, {{{"mic1"}, ALC882_MIC1}, {{"mic2"}, ALC882_MIC2},
2078: {{AudioNline}, ALC882_LINE}, {{AudioNcd}, ALC882_CD},
2079: {{AudioNspeaker}, ALC882_BEEP},
2080: {{AudioNmixerout}, ALC882_MIX}}}}, 0x22, -1},
2081:
2082: {{0, {"usingdac"}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, 0, 0,
2083: .un.e={2, {{{"analog"}, 0}, {{"digital"}, 1}}}}, 0, MI_TARGET_DAC},
2084: {{0, {"usingadc"}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, 0, 0,
2085: .un.e={2, {{{"analog"}, 0}, {{"digital"}, 1}}}}, 0, MI_TARGET_ADC},
2086: };
2087:
2088: int
2089: azalia_alc883_mixer_init(codec_t *this)
2090: {
2091: mixer_ctrl_t mc;
2092:
2093: this->nmixers = sizeof(alc883_mixer_items) / sizeof(mixer_item_t);
2094: this->mixers = malloc(sizeof(mixer_item_t) * this->nmixers,
2095: M_DEVBUF, M_NOWAIT);
2096: if (this->mixers == NULL) {
2097: printf("%s: out of memory in %s\n", XNAME(this), __func__);
2098: return ENOMEM;
2099: }
2100: bzero(this->mixers, sizeof(mixer_item_t) * this->maxmixers);
2101: memcpy(this->mixers, alc883_mixer_items,
2102: sizeof(mixer_item_t) * this->nmixers);
2103: azalia_generic_mixer_fix_indexes(this);
2104: azalia_generic_mixer_default(this);
2105:
2106: mc.dev = -1;
2107: mc.type = AUDIO_MIXER_ENUM;
2108: mc.un.ord = 1; /* pindir: output */
2109: azalia_generic_mixer_set(this, 0x14, MI_TARGET_PINDIR, &mc);
2110: azalia_generic_mixer_set(this, 0x1b, MI_TARGET_PINDIR, &mc);
2111: azalia_generic_mixer_set(this, 0x15, MI_TARGET_PINDIR, &mc);
2112: azalia_generic_mixer_set(this, 0x16, MI_TARGET_PINDIR, &mc);
2113: azalia_generic_mixer_set(this, 0x17, MI_TARGET_PINDIR, &mc);
2114: mc.un.ord = 0; /* [0] 0x0c */
2115: azalia_generic_mixer_set(this, 0x14, MI_TARGET_CONNLIST, &mc);
2116: azalia_generic_mixer_set(this, 0x1b, MI_TARGET_CONNLIST, &mc);
2117: mc.un.ord = 1; /* [1] 0x0d */
2118: azalia_generic_mixer_set(this, 0x15, MI_TARGET_CONNLIST, &mc);
2119: mc.un.ord = 2; /* [2] 0x0e */
2120: azalia_generic_mixer_set(this, 0x16, MI_TARGET_CONNLIST, &mc);
2121: mc.un.ord = 2; /* [3] 0x0fb */
2122: azalia_generic_mixer_set(this, 0x17, MI_TARGET_CONNLIST, &mc);
2123:
2124: mc.un.ord = 0; /* pindir: input */
2125: azalia_generic_mixer_set(this, 0x18, MI_TARGET_PINDIR, &mc);
2126: azalia_generic_mixer_set(this, 0x19, MI_TARGET_PINDIR, &mc);
2127: azalia_generic_mixer_set(this, 0x1a, MI_TARGET_PINDIR, &mc);
2128: /* XXX: inamp for 18/19/1a */
2129:
2130: mc.un.ord = 0; /* unmute */
2131: azalia_generic_mixer_set(this, 0x23, MI_TARGET_INAMP(1), &mc);
2132: azalia_generic_mixer_set(this, 0x22, MI_TARGET_INAMP(2), &mc);
2133: return 0;
2134: }
2135:
2136:
2137: /* ----------------------------------------------------------------
2138: * Analog Devices AD1981HD
2139: * ---------------------------------------------------------------- */
2140:
2141: #define AD1981HD_THINKPAD 0x201017aa
2142:
2143: int
2144: azalia_ad1981hd_init_widget(const codec_t *this, widget_t *w, nid_t nid)
2145: {
2146: switch (nid) {
2147: case 0x05:
2148: strlcpy(w->name, AudioNline "out", sizeof(w->name));
2149: break;
2150: case 0x06:
2151: strlcpy(w->name, "hp", sizeof(w->name));
2152: break;
2153: case 0x07:
2154: strlcpy(w->name, AudioNmono, sizeof(w->name));
2155: break;
2156: case 0x08:
2157: strlcpy(w->name, AudioNmicrophone, sizeof(w->name));
2158: break;
2159: case 0x09:
2160: strlcpy(w->name, AudioNline "in", sizeof(w->name));
2161: break;
2162: case 0x0d:
2163: strlcpy(w->name, "beep", sizeof(w->name));
2164: break;
2165: case 0x17:
2166: strlcpy(w->name, AudioNaux, sizeof(w->name));
2167: break;
2168: case 0x18:
2169: strlcpy(w->name, AudioNmicrophone "2", sizeof(w->name));
2170: break;
2171: case 0x19:
2172: strlcpy(w->name, AudioNcd, sizeof(w->name));
2173: break;
2174: case 0x1d:
2175: strlcpy(w->name, AudioNspeaker, sizeof(w->name));
2176: break;
2177: }
2178: return 0;
2179: }
2180:
2181: int
2182: azalia_ad1981hd_mixer_init(codec_t *this)
2183: {
2184: mixer_ctrl_t mc;
2185: int err;
2186:
2187: err = azalia_generic_mixer_init(this);
2188: if (err)
2189: return err;
2190: if (this->subid == AD1981HD_THINKPAD) {
2191: mc.dev = -1;
2192: mc.type = AUDIO_MIXER_ENUM;
2193: mc.un.ord = 1;
2194: azalia_generic_mixer_set(this, 0x09, MI_TARGET_PINDIR, &mc);
2195: }
2196: return 0;
2197: }
2198:
2199: /* ----------------------------------------------------------------
2200: * CMedia CMI9880
2201: * ---------------------------------------------------------------- */
2202:
2203: static const mixer_item_t cmi9880_mixer_items[] = {
2204: {{AZ_CLASS_INPUT, {AudioCinputs}, AUDIO_MIXER_CLASS, AZ_CLASS_INPUT, 0, 0}, 0},
2205: {{AZ_CLASS_OUTPUT, {AudioCoutputs}, AUDIO_MIXER_CLASS, AZ_CLASS_OUTPUT, 0, 0}, 0},
2206: {{AZ_CLASS_RECORD, {AudioCrecord}, AUDIO_MIXER_CLASS, AZ_CLASS_RECORD, 0, 0}, 0},
2207:
2208: {{0, {AudioNmaster"."AudioNmute}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2209: 0, 0, ENUM_OFFON}, 0x03, MI_TARGET_OUTAMP},
2210: {{0, {AudioNsurround"."AudioNmute}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2211: 0, 0, ENUM_OFFON}, 0x04, MI_TARGET_OUTAMP},
2212: {{0, {AzaliaNclfe"."AudioNmute}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2213: 0, 0, ENUM_OFFON}, 0x05, MI_TARGET_OUTAMP},
2214: {{0, {AzaliaNside"."AudioNmute}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2215: 0, 0, ENUM_OFFON}, 0x06, MI_TARGET_OUTAMP},
2216: {{0, {"digital."AudioNmute}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2217: 0, 0, ENUM_OFFON}, 0x07, MI_TARGET_OUTAMP},
2218:
2219: {{0, {AzaliaNfront"."AudioNmute}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD,
2220: 0, 0, ENUM_OFFON}, 0x08, MI_TARGET_INAMP(0)},
2221: {{0, {AzaliaNfront}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD,
2222: 0, 0, .un.v={{""}, 2, MIXER_DELTA(30)}}, 0x08, MI_TARGET_INAMP(0)},
2223: {{0, {AzaliaNfront"."AudioNsource}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD,
2224: 0, 0, .un.e={4, {{{AudioNmicrophone}, 5}, {{AudioNcd}, 6},
2225: {{"line1"}, 7}, {{"line2"}, 8}}}},
2226: 0x08, MI_TARGET_CONNLIST},
2227: {{0, {AudioNsurround"."AudioNmute}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD,
2228: 0, 0, ENUM_OFFON}, 0x09, MI_TARGET_INAMP(0)},
2229: {{0, {AudioNsurround}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD,
2230: 0, 0, .un.v={{""}, 2, MIXER_DELTA(30)}}, 0x09, MI_TARGET_INAMP(0)},
2231: {{0, {AudioNsurround"."AudioNsource}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD,
2232: 0, 0, .un.e={4, {{{AudioNmicrophone}, 5}, {{AudioNcd}, 6},
2233: {{"line1"}, 7}, {{"line2"}, 8}}}},
2234: 0x09, MI_TARGET_CONNLIST},
2235:
2236: {{0, {AudioNspeaker"."AudioNmute}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
2237: 0, 0, ENUM_OFFON}, 0x23, MI_TARGET_OUTAMP},
2238: {{0, {AudioNspeaker}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
2239: 0, 0, .un.v={{""}, 1, MIXER_DELTA(15)}}, 0x23, MI_TARGET_OUTAMP}
2240: };
2241:
2242: int
2243: azalia_cmi9880_mixer_init(codec_t *this)
2244: {
2245: mixer_ctrl_t mc;
2246:
2247: this->nmixers = sizeof(cmi9880_mixer_items) / sizeof(mixer_item_t);
2248: this->mixers = malloc(sizeof(mixer_item_t) * this->nmixers,
2249: M_DEVBUF, M_NOWAIT);
2250: if (this->mixers == NULL) {
2251: printf("%s: out of memory in %s\n", XNAME(this), __func__);
2252: return ENOMEM;
2253: }
2254: bzero(this->mixers, sizeof(mixer_item_t) * this->maxmixers);
2255: memcpy(this->mixers, cmi9880_mixer_items,
2256: sizeof(mixer_item_t) * this->nmixers);
2257: azalia_generic_mixer_fix_indexes(this);
2258: azalia_generic_mixer_default(this);
2259:
2260: mc.dev = -1;
2261: mc.type = AUDIO_MIXER_ENUM;
2262: mc.un.ord = 5; /* record.front.source=mic */
2263: azalia_generic_mixer_set(this, 0x08, MI_TARGET_CONNLIST, &mc);
2264: mc.un.ord = 7; /* record.surround.source=line1 */
2265: azalia_generic_mixer_set(this, 0x09, MI_TARGET_CONNLIST, &mc);
2266: mc.un.ord = 1; /* pindir: output */
2267: azalia_generic_mixer_set(this, 0x0b, MI_TARGET_PINDIR, &mc);
2268: azalia_generic_mixer_set(this, 0x0c, MI_TARGET_PINDIR, &mc);
2269: azalia_generic_mixer_set(this, 0x0d, MI_TARGET_PINDIR, &mc);
2270: azalia_generic_mixer_set(this, 0x0e, MI_TARGET_PINDIR, &mc);
2271: azalia_generic_mixer_set(this, 0x0f, MI_TARGET_PINDIR, &mc);
2272: mc.un.ord = 0; /* front DAC -> headphones */
2273: azalia_generic_mixer_set(this, 0x0f, MI_TARGET_CONNLIST, &mc);
2274: mc.un.ord = 0; /* pindir: input */
2275: azalia_generic_mixer_set(this, 0x10, MI_TARGET_PINDIR, &mc); /* mic */
2276: azalia_generic_mixer_set(this, 0x13, MI_TARGET_PINDIR, &mc); /* SPDIF-in */
2277: azalia_generic_mixer_set(this, 0x1f, MI_TARGET_PINDIR, &mc); /* line1 */
2278: azalia_generic_mixer_set(this, 0x20, MI_TARGET_PINDIR, &mc); /* line2 */
2279: return 0;
2280: }
2281:
2282: int
2283: azalia_cmi9880_init_dacgroup(codec_t *this)
2284: {
2285: static const convgroupset_t dacs = {
2286: -1, 2,
2287: {{4, {0x03, 0x04, 0x05, 0x06}}, /* analog 8ch */
2288: {1, {0x07}}}}; /* digital */
2289: static const convgroupset_t adcs = {
2290: -1, 2,
2291: {{2, {0x08, 0x09}}, /* analog 4ch */
2292: {1, {0x0a}}}}; /* digital */
2293:
2294: this->dacs = dacs;
2295: this->adcs = adcs;
2296: return 0;
2297: }
2298:
2299: /* ----------------------------------------------------------------
2300: * Sigmatel STAC9221 and STAC9221D
2301: * ---------------------------------------------------------------- */
2302:
2303: int
2304: azalia_stac9221_init_dacgroup(codec_t *this)
2305: {
2306: static const convgroupset_t dacs = {
2307: -1, 3,
2308: {{4, {0x02, 0x03, 0x04, 0x05}}, /* analog 8ch */
2309: {1, {0x08}}, /* digital */
2310: {1, {0x1a}}}}; /* another digital? */
2311: static const convgroupset_t adcs = {
2312: -1, 2,
2313: {{2, {0x06, 0x07}}, /* analog 4ch */
2314: {1, {0x09}}}}; /* digital */
2315:
2316: this->dacs = dacs;
2317: this->adcs = adcs;
2318: return 0;
2319: }
2320:
2321: /* ----------------------------------------------------------------
2322: * Sigmatel STAC9200
2323: * ---------------------------------------------------------------- */
2324:
2325: static const mixer_item_t stac9200_mixer_items[] = {
2326: {{AZ_CLASS_INPUT, {AudioCinputs}, AUDIO_MIXER_CLASS, AZ_CLASS_INPUT, 0, 0}, 0},
2327: {{AZ_CLASS_OUTPUT, {AudioCoutputs}, AUDIO_MIXER_CLASS, AZ_CLASS_OUTPUT, 0, 0}, 0},
2328: {{AZ_CLASS_RECORD, {AudioCrecord}, AUDIO_MIXER_CLASS, AZ_CLASS_RECORD, 0, 0}, 0},
2329:
2330: {{0, {AudioNmaster}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
2331: 4, 0, .un.v={{""}, 2, MIXER_DELTA(31)}}, 0x0b, MI_TARGET_OUTAMP},
2332: {{0, {AudioNmute}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2333: 0, 3, ENUM_OFFON}, 0x0b, MI_TARGET_OUTAMP},
2334: {{0, {AudioNvolume}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD,
2335: 0, 0, .un.v={{""}, 2, MIXER_DELTA(15)}}, 0x0a, MI_TARGET_OUTAMP},
2336: {{0, {AudioNvolume"."AudioNmute}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD,
2337: 0, 0, ENUM_OFFON}, 0x0a, MI_TARGET_OUTAMP},
2338: {{0, {AudioNsource}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD,
2339: 0, 0, .un.e={5, {{{AudioNline}, 0}, {{AudioNmicrophone}, 1},
2340: {{AudioNline"2"}, 2}, {{AudioNline"3"}, 3},
2341: {{AudioNcd}, 4}}}},
2342: 0x0c, MI_TARGET_CONNLIST},
2343: {{0, {AudioNmicrophone}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
2344: 0, 0, .un.v={{""}, 2, MIXER_DELTA(4)}}, 0x0c, MI_TARGET_OUTAMP},
2345: {{0, {AudioNmicrophone"."AudioNmute}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
2346: 0, 0, ENUM_OFFON}, 0x0c, MI_TARGET_OUTAMP},
2347: {{0, {AudioNsource}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2348: 0, 0, .un.e={3, {{{AudioNdac}, 0}, {{"digital-in"}, 1}, {{"selector"}, 2}}}},
2349: 0x07, MI_TARGET_CONNLIST},
2350: {{0, {"digital."AudioNsource}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2351: 0, 0, .un.e={2, {{{AudioNdac}, 0}, {{"selector"}, 1}}}},
2352: 0x09, MI_TARGET_CONNLIST}, /* AudioNdac is not accurate name */
2353: {{0, {AudioNheadphone".boost"}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2354: 0, 0, ENUM_OFFON}, 0x0d, MI_TARGET_PINBOOST},
2355: {{0, {AudioNspeaker".boost"}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2356: 0, 0, ENUM_OFFON}, 0x0e, MI_TARGET_PINBOOST},
2357: {{0, {AudioNmono"."AudioNmute}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2358: 0, 0, ENUM_OFFON}, 0x11, MI_TARGET_OUTAMP},
2359: {{0, {AudioNmono}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
2360: 0, 0, .un.v={{""}, 1, MIXER_DELTA(31)}}, 0x11, MI_TARGET_OUTAMP},
2361: {{0, {"beep."AudioNmute}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2362: 0, 0, ENUM_OFFON}, 0x14, MI_TARGET_OUTAMP},
2363: {{0, {"beep"}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
2364: 0, 0, .un.v={{""}, 1, MIXER_DELTA(3)}}, 0x14, MI_TARGET_OUTAMP},
2365: {{0, {"usingdac"}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
2366: 0, 0, .un.e={2, {{{"analog"}, 0}, {{"digital"}, 1}}}},
2367: 0, MI_TARGET_DAC},
2368: {{0, {"usingadc"}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD,
2369: 0, 0, .un.e={2, {{{"analog"}, 0}, {{"digital"}, 1}}}},
2370: 0, MI_TARGET_ADC},
2371: };
2372:
2373: int
2374: azalia_stac9200_mixer_init(codec_t *this)
2375: {
2376: mixer_ctrl_t mc;
2377:
2378: this->nmixers = sizeof(stac9200_mixer_items) / sizeof(mixer_item_t);
2379: this->mixers = malloc(sizeof(mixer_item_t) * this->nmixers,
2380: M_DEVBUF, M_NOWAIT);
2381: if (this->mixers == NULL) {
2382: printf("%s: out of memory in %s\n", XNAME(this), __func__);
2383: return ENOMEM;
2384: }
2385: bzero(this->mixers, sizeof(mixer_item_t) * this->maxmixers);
2386: memcpy(this->mixers, stac9200_mixer_items,
2387: sizeof(mixer_item_t) * this->nmixers);
2388: azalia_generic_mixer_fix_indexes(this);
2389: azalia_generic_mixer_default(this);
2390:
2391: mc.dev = -1; /* no need for generic_mixer_set() */
2392: mc.type = AUDIO_MIXER_ENUM;
2393: mc.un.ord = 1; /* pindir: output */
2394: azalia_generic_mixer_set(this, 0x0d, MI_TARGET_PINDIR, &mc); /* headphones */
2395: azalia_generic_mixer_set(this, 0x0e, MI_TARGET_PINDIR, &mc); /* speaker */
2396: mc.un.ord = 0; /* pindir: input */
2397: azalia_generic_mixer_set(this, 0x0f, MI_TARGET_PINDIR, &mc); /* mic2 */
2398: azalia_generic_mixer_set(this, 0x10, MI_TARGET_PINDIR, &mc); /* mic1 */
2399: mc.type = AUDIO_MIXER_VALUE;
2400: mc.un.value.num_channels = 2;
2401: mc.un.value.level[0] = azalia_generic_mixer_max(this, 0x0c, MI_TARGET_OUTAMP);
2402: mc.un.value.level[1] = mc.un.value.level[0];
2403: azalia_generic_mixer_set(this, 0x0c, MI_TARGET_OUTAMP, &mc);
2404:
2405: #define STAC9200_EVENT_HP 0
2406: #define STAC9200_NID_HP 0x0d
2407: #define STAC9200_NID_SPEAKER 0x0e
2408:
2409: /* register hp unsolicited event */
2410: this->comresp(this, STAC9200_NID_HP,
2411: CORB_SET_UNSOLICITED_RESPONSE,
2412: CORB_UNSOL_ENABLE | STAC9200_EVENT_HP, NULL);
2413:
2414: azalia_stac9200_unsol_event(this, STAC9200_EVENT_HP);
2415:
2416: return 0;
2417: }
2418: int
2419: azalia_stac9200_unsol_event(codec_t *this, int tag)
2420: {
2421: int err;
2422: uint32_t value;
2423:
2424: switch (tag) {
2425: case STAC9200_EVENT_HP:
2426: err = this->comresp(this, STAC9200_NID_HP,
2427: CORB_GET_PIN_SENSE, 0, &value);
2428: if (err)
2429: break;
2430: if (value & CORB_PS_PRESENCE) {
2431: DPRINTF(("%s: headphone inserted\n", __func__));
2432: azalia_generic_mixer_pinctrl(this,
2433: STAC9200_NID_SPEAKER, 0);
2434: } else {
2435: DPRINTF(("%s: headphone pulled\n", __func__));
2436: azalia_generic_mixer_pinctrl(this,
2437: STAC9200_NID_SPEAKER, CORB_PWC_OUTPUT);
2438: }
2439: break;
2440: default:
2441: DPRINTF(("%s: unknown tag: %d\n", __func__, tag));
2442: }
2443: return 0;
2444: }
2445:
2446: int
2447: azalia_stac9221_apple_init_dacgroup(codec_t *this)
2448: {
2449: static const convgroupset_t dacs = {
2450: -1, 1,
2451: {{4, {0x02, 0x03, 0x04, 0x05}}}};
2452:
2453: static const convgroupset_t adcs = {
2454: -1, 2,
2455: {{2, {0x06, 0x07}},
2456: {1, {0x09}}}};
2457:
2458: this->dacs = dacs;
2459: this->adcs = adcs;
2460: return 0;
2461: }
2462:
2463: static const mixer_item_t stac9221_apple_mixer_items[] = {
2464: {{AZ_CLASS_INPUT, {AudioCinputs}, AUDIO_MIXER_CLASS, AZ_CLASS_INPUT, 0, 0}, 0},
2465: {{AZ_CLASS_OUTPUT, {AudioCoutputs}, AUDIO_MIXER_CLASS, AZ_CLASS_OUTPUT, 0, 0}, 0},
2466: {{AZ_CLASS_RECORD, {AudioCrecord}, AUDIO_MIXER_CLASS, AZ_CLASS_RECORD, 0, 0}, 0},
2467:
2468: {{0, {AudioNheadphone}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
2469: 0, 0, .un.v={{""}, 2, MIXER_DELTA(15)}}, 0x02, MI_TARGET_OUTAMP},
2470: {{0, {AudioNheadphone".mute"}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2471: 0, 0, ENUM_OFFON}, 0x02, MI_TARGET_OUTAMP},
2472:
2473: {{0, {AudioNspeaker}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
2474: 0, 0, .un.v={{""}, 2, MIXER_DELTA(15)}}, 0x03, MI_TARGET_OUTAMP},
2475: {{0, {AudioNspeaker".mute"}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2476: 0, 0, ENUM_OFFON}, 0x03, MI_TARGET_OUTAMP},
2477:
2478: {{0, {"line"}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
2479: 0, 0, .un.v={{""}, 2, MIXER_DELTA(15)}}, 0x04, MI_TARGET_OUTAMP},
2480: {{0, {"line.mute"}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2481: 0, 0, ENUM_OFFON}, 0x04, MI_TARGET_OUTAMP},
2482:
2483: {{0, {"line2"}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
2484: 0, 0, .un.v={{""}, 2, MIXER_DELTA(15)}}, 0x05, MI_TARGET_OUTAMP},
2485: {{0, {"line2.mute"}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2486: 0, 0, ENUM_OFFON}, 0x05, MI_TARGET_OUTAMP},
2487:
2488: {{0, {AudioNmaster}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
2489: 0, 0, .un.v={{""}, 1, MIXER_DELTA(15)}}, 0x16, MI_TARGET_VOLUME},
2490: };
2491:
2492: int
2493: azalia_stac9221_apple_mixer_init(codec_t *this)
2494: {
2495: mixer_ctrl_t mc;
2496:
2497: this->nmixers = sizeof(stac9221_apple_mixer_items) / sizeof(mixer_item_t);
2498: this->mixers = malloc(sizeof(mixer_item_t) * this->nmixers,
2499: M_DEVBUF, M_NOWAIT);
2500: if (this->mixers == NULL) {
2501: printf("%s: out of memory in %s\n", XNAME(this), __func__);
2502: return ENOMEM;
2503: }
2504: bzero(this->mixers, sizeof(mixer_item_t) * this->maxmixers);
2505: memcpy(this->mixers, stac9221_apple_mixer_items,
2506: sizeof(mixer_item_t) * this->nmixers);
2507: azalia_generic_mixer_fix_indexes(this);
2508: azalia_generic_mixer_default(this);
2509:
2510: mc.dev = -1;
2511: mc.type = AUDIO_MIXER_ENUM;
2512: mc.un.ord = 1; /* pindir: output */
2513: azalia_generic_mixer_set(this, 0x0a, MI_TARGET_PINDIR, &mc); /* headphones */
2514: azalia_generic_mixer_set(this, 0x0b, MI_TARGET_PINDIR, &mc); /* mic, set to output */
2515: azalia_generic_mixer_set(this, 0x0c, MI_TARGET_PINDIR, &mc); /* speaker */
2516: azalia_generic_mixer_set(this, 0x0d, MI_TARGET_PINDIR, &mc); /* line out */
2517: azalia_generic_mixer_set(this, 0x0f, MI_TARGET_PINDIR, &mc); /* another line out */
2518:
2519: /* max all volumes except master */
2520: mc.type = AUDIO_MIXER_VALUE;
2521: mc.un.value.num_channels = 2;
2522: mc.un.value.level[0] = azalia_generic_mixer_max(this, 0x02, MI_TARGET_OUTAMP);
2523: mc.un.value.level[1] = mc.un.value.level[0];
2524: azalia_generic_mixer_set(this, 0x02, MI_TARGET_OUTAMP, &mc);
2525:
2526: mc.un.value.level[0] = azalia_generic_mixer_max(this, 0x03, MI_TARGET_OUTAMP);
2527: mc.un.value.level[1] = mc.un.value.level[0];
2528: azalia_generic_mixer_set(this, 0x03, MI_TARGET_OUTAMP, &mc);
2529:
2530: mc.un.value.level[0] = azalia_generic_mixer_max(this, 0x04, MI_TARGET_OUTAMP);
2531: mc.un.value.level[1] = mc.un.value.level[0];
2532: azalia_generic_mixer_set(this, 0x04, MI_TARGET_OUTAMP, &mc);
2533:
2534: mc.un.value.level[0] = azalia_generic_mixer_max(this, 0x05, MI_TARGET_OUTAMP);
2535: mc.un.value.level[1] = mc.un.value.level[0];
2536: azalia_generic_mixer_set(this, 0x05, MI_TARGET_OUTAMP, &mc);
2537:
2538: azalia_stac9221_gpio_unmute(this, 0);
2539: azalia_stac9221_gpio_unmute(this, 1);
2540:
2541: return 0;
2542: }
2543:
2544: int
2545: azalia_stac9221_gpio_unmute(codec_t *this, int pin)
2546: {
2547: uint32_t data, mask, dir;
2548:
2549: this->comresp(this, this->audiofunc, CORB_GET_GPIO_DATA, 0, &data);
2550: this->comresp(this, this->audiofunc, CORB_GET_GPIO_ENABLE_MASK, 0, &mask);
2551: this->comresp(this, this->audiofunc, CORB_GET_GPIO_DIRECTION, 0, &dir);
2552:
2553: data |= 1 << pin;
2554: mask |= 1 << pin;
2555: dir |= 1 << pin;
2556:
2557: this->comresp(this, this->audiofunc, 0x7e7, 0, NULL);
2558: this->comresp(this, this->audiofunc, CORB_SET_GPIO_ENABLE_MASK, mask, NULL);
2559: this->comresp(this, this->audiofunc, CORB_SET_GPIO_DIRECTION, dir, NULL);
2560: DELAY(1000);
2561: this->comresp(this, this->audiofunc, CORB_SET_GPIO_DATA, data, NULL);
2562:
2563: return 0;
2564: }
2565:
2566: /* ----------------------------------------------------------------
2567: * Sony VAIO FE and SZ
2568: * ---------------------------------------------------------------- */
2569:
2570: int
2571: azalia_stac7661_init_dacgroup(codec_t *this)
2572: {
2573: static const convgroupset_t dacs = {
2574: -1, 1,
2575: {{2, {0x02, 0x05}}}};
2576:
2577: static const convgroupset_t adcs = {
2578: -1, 1,
2579: {{1, {0x08}}}};
2580:
2581: this->dacs = dacs;
2582: this->adcs = adcs;
2583:
2584: return 0;
2585: }
2586:
2587: static const mixer_item_t stac7661_mixer_items[] = {
2588: {{AZ_CLASS_INPUT, {AudioCinputs}, AUDIO_MIXER_CLASS, AZ_CLASS_INPUT, 0, 0}, 0},
2589: {{AZ_CLASS_OUTPUT, {AudioCoutputs}, AUDIO_MIXER_CLASS, AZ_CLASS_OUTPUT, 0, 0}, 0},
2590: {{AZ_CLASS_RECORD, {AudioCrecord}, AUDIO_MIXER_CLASS, AZ_CLASS_RECORD, 0, 0}, 0},
2591:
2592: #define STAC7661_DAC_HP 0x02
2593: #define STAC7661_DAC_SPEAKER 0x05
2594: #define STAC7661_TARGET_MASTER -1
2595:
2596: {{0, {AudioNvolume"."AudioNmute}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, 0, 0,
2597: ENUM_OFFON}, 0x09, MI_TARGET_INAMP(0)},
2598: {{0, {AudioNvolume}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD, 0, 0,
2599: .un.v={{""}, 2, MIXER_DELTA(15)}}, 0x09, MI_TARGET_INAMP(0)},
2600: {{0, {AudioNsource}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD,
2601: 0, 0, .un.e={3, {{{AudioNmicrophone}, 1}, {{AudioNmicrophone"2"}, 2},
2602: {{AudioNdac}, 3}}}},
2603: 0x15, MI_TARGET_CONNLIST},
2604: {{0, {AudioNmaster}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
2605: 0, 0, .un.v={{""}, 2, MIXER_DELTA(127)}}, 0x02, STAC7661_TARGET_MASTER},
2606: {{0, {AudioNmaster"."AudioNmute}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2607: 0, 0, ENUM_OFFON}, 0x02, STAC7661_TARGET_MASTER},
2608: {{0, {AudioNvolume".knob"}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
2609: 0, 0, .un.v={{""}, 1, MIXER_DELTA(15)}}, 0x17, MI_TARGET_VOLUME},
2610: {{0, {AudioNheadphone".mute"}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2611: 0, 0, ENUM_OFFON}, 0x02, MI_TARGET_OUTAMP},
2612: {{0, {AudioNheadphone}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
2613: 0, 0, .un.v={{""}, 2, MIXER_DELTA(127)}}, 0x02, MI_TARGET_OUTAMP},
2614: {{0, {AudioNspeaker".mute"}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
2615: 0, 0, ENUM_OFFON}, 0x05, MI_TARGET_OUTAMP},
2616: {{0, {AudioNspeaker}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
2617: 0, 0, .un.v={{""}, 2, MIXER_DELTA(127)}}, 0x05, MI_TARGET_OUTAMP}
2618: };
2619:
2620: int
2621: azalia_stac7661_mixer_init(codec_t *this)
2622: {
2623: mixer_ctrl_t mc;
2624:
2625: this->nmixers = sizeof(stac7661_mixer_items) / sizeof(mixer_item_t);
2626: this->mixers = malloc(sizeof(mixer_item_t) * this->nmixers,
2627: M_DEVBUF, M_NOWAIT);
2628: if (this->mixers == NULL) {
2629: printf("%s: out of memory in %s\n", XNAME(this), __func__);
2630: return ENOMEM;
2631: }
2632: bzero(this->mixers, sizeof(mixer_item_t) * this->maxmixers);
2633: memcpy(this->mixers, stac7661_mixer_items,
2634: sizeof(mixer_item_t) * this->nmixers);
2635: azalia_generic_mixer_fix_indexes(this);
2636: azalia_generic_mixer_default(this);
2637: mc.dev = -1;
2638: mc.type = AUDIO_MIXER_ENUM;
2639: mc.un.ord = 1;
2640: azalia_generic_mixer_set(this, 0x0a, MI_TARGET_PINDIR, &mc); /* headphones */
2641: azalia_generic_mixer_set(this, 0x0f, MI_TARGET_PINDIR, &mc); /* speaker */
2642: azalia_generic_mixer_set(this, 0x09, MI_TARGET_INAMP(0), &mc); /* mute input */
2643: mc.un.ord = 0;
2644: azalia_generic_mixer_set(this, 0x0d, MI_TARGET_PINDIR, &mc); /* mic */
2645: azalia_generic_mixer_set(this, 0x14, MI_TARGET_PINDIR, &mc); /* internal mic */
2646: mc.un.ord = 2; /* select internal mic for recording */
2647: azalia_generic_mixer_set(this, 0x15, MI_TARGET_CONNLIST, &mc);
2648: mc.type = AUDIO_MIXER_VALUE;
2649: mc.un.value.num_channels = 1;
2650: mc.un.value.level[0] = azalia_generic_mixer_max(this, 0x17, MI_TARGET_VOLUME);
2651: azalia_generic_mixer_set(this, 0x17, MI_TARGET_VOLUME, &mc);
2652:
2653: return 0;
2654: }
2655:
2656: int
2657: azalia_stac7661_set_port(codec_t *this, mixer_ctrl_t *mc)
2658: {
2659: const mixer_item_t *m;
2660: int err;
2661:
2662: if (mc->dev >= this->nmixers)
2663: return ENXIO;
2664: m = &this->mixers[mc->dev];
2665: if (mc->type != m->devinfo.type)
2666: return EINVAL;
2667: if (mc->type == AUDIO_MIXER_CLASS)
2668: return 0;
2669: if (m->target == STAC7661_TARGET_MASTER) {
2670: err = azalia_generic_mixer_set(this, STAC7661_DAC_HP,
2671: MI_TARGET_OUTAMP, mc);
2672: err = azalia_generic_mixer_set(this, STAC7661_DAC_SPEAKER,
2673: MI_TARGET_OUTAMP, mc);
2674: return err;
2675: }
2676: return azalia_generic_mixer_set(this, m->nid, m->target, mc);
2677: }
2678: int
2679: azalia_stac7661_get_port(codec_t *this, mixer_ctrl_t *mc)
2680: {
2681: const mixer_item_t *m;
2682:
2683: if (mc->dev >= this->nmixers)
2684: return ENXIO;
2685: m = &this->mixers[mc->dev];
2686: mc->type = m->devinfo.type;
2687: if (mc->type == AUDIO_MIXER_CLASS)
2688: return 0;
2689: if (m->target == STAC7661_TARGET_MASTER)
2690: return azalia_generic_mixer_get(this, m->nid,
2691: MI_TARGET_OUTAMP, mc);
2692: return azalia_generic_mixer_get(this, m->nid, m->target, mc);
2693: }
CVSweb