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