xref: /dragonfly/sys/dev/sound/pci/hda/hdaa.c (revision e9d1c8d1)
12a1ad637SFrançois Tigeot /*-
22a1ad637SFrançois Tigeot  * Copyright (c) 2006 Stephane E. Potvin <sepotvin@videotron.ca>
32a1ad637SFrançois Tigeot  * Copyright (c) 2006 Ariff Abdullah <ariff@FreeBSD.org>
42a1ad637SFrançois Tigeot  * Copyright (c) 2008-2012 Alexander Motin <mav@FreeBSD.org>
52a1ad637SFrançois Tigeot  * All rights reserved.
62a1ad637SFrançois Tigeot  *
72a1ad637SFrançois Tigeot  * Redistribution and use in source and binary forms, with or without
82a1ad637SFrançois Tigeot  * modification, are permitted provided that the following conditions
92a1ad637SFrançois Tigeot  * are met:
102a1ad637SFrançois Tigeot  * 1. Redistributions of source code must retain the above copyright
112a1ad637SFrançois Tigeot  *    notice, this list of conditions and the following disclaimer.
122a1ad637SFrançois Tigeot  * 2. Redistributions in binary form must reproduce the above copyright
132a1ad637SFrançois Tigeot  *    notice, this list of conditions and the following disclaimer in the
142a1ad637SFrançois Tigeot  *    documentation and/or other materials provided with the distribution.
152a1ad637SFrançois Tigeot  *
162a1ad637SFrançois Tigeot  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
172a1ad637SFrançois Tigeot  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
182a1ad637SFrançois Tigeot  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
192a1ad637SFrançois Tigeot  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
202a1ad637SFrançois Tigeot  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
212a1ad637SFrançois Tigeot  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
222a1ad637SFrançois Tigeot  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
232a1ad637SFrançois Tigeot  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
242a1ad637SFrançois Tigeot  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
252a1ad637SFrançois Tigeot  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
262a1ad637SFrançois Tigeot  * SUCH DAMAGE.
272a1ad637SFrançois Tigeot  */
282a1ad637SFrançois Tigeot 
292a1ad637SFrançois Tigeot /*
302a1ad637SFrançois Tigeot  * Intel High Definition Audio (Audio function) driver for FreeBSD.
312a1ad637SFrançois Tigeot  */
322a1ad637SFrançois Tigeot 
332a1ad637SFrançois Tigeot #ifdef HAVE_KERNEL_OPTION_HEADERS
342a1ad637SFrançois Tigeot #include "opt_snd.h"
352a1ad637SFrançois Tigeot #endif
362a1ad637SFrançois Tigeot 
372a1ad637SFrançois Tigeot #include <dev/sound/pcm/sound.h>
382a1ad637SFrançois Tigeot 
392a1ad637SFrançois Tigeot #include <sys/ctype.h>
402a1ad637SFrançois Tigeot #include <sys/taskqueue.h>
412a1ad637SFrançois Tigeot 
422a1ad637SFrançois Tigeot #include <dev/sound/pci/hda/hdac.h>
432a1ad637SFrançois Tigeot #include <dev/sound/pci/hda/hdaa.h>
442a1ad637SFrançois Tigeot #include <dev/sound/pci/hda/hda_reg.h>
452a1ad637SFrançois Tigeot 
462a1ad637SFrançois Tigeot #include "mixer_if.h"
472a1ad637SFrançois Tigeot 
482a1ad637SFrançois Tigeot SND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pci/hda/hdaa.c 273377 2014-10-21 07:31:21Z hselasky $");
492a1ad637SFrançois Tigeot 
502a1ad637SFrançois Tigeot #define hdaa_lock(devinfo)	snd_mtxlock((devinfo)->lock)
512a1ad637SFrançois Tigeot #define hdaa_unlock(devinfo)	snd_mtxunlock((devinfo)->lock)
522a1ad637SFrançois Tigeot #define hdaa_lockassert(devinfo) snd_mtxassert((devinfo)->lock)
532a1ad637SFrançois Tigeot #define hdaa_lockowned(devinfo)	mtx_owned((devinfo)->lock)
542a1ad637SFrançois Tigeot 
552a1ad637SFrançois Tigeot static const struct {
562a1ad637SFrançois Tigeot 	const char *key;
572a1ad637SFrançois Tigeot 	uint32_t value;
582a1ad637SFrançois Tigeot } hdaa_quirks_tab[] = {
592a1ad637SFrançois Tigeot 	{ "softpcmvol", HDAA_QUIRK_SOFTPCMVOL },
602a1ad637SFrançois Tigeot 	{ "fixedrate", HDAA_QUIRK_FIXEDRATE },
612a1ad637SFrançois Tigeot 	{ "forcestereo", HDAA_QUIRK_FORCESTEREO },
622a1ad637SFrançois Tigeot 	{ "eapdinv", HDAA_QUIRK_EAPDINV },
632a1ad637SFrançois Tigeot 	{ "senseinv", HDAA_QUIRK_SENSEINV },
642a1ad637SFrançois Tigeot 	{ "ivref50", HDAA_QUIRK_IVREF50 },
652a1ad637SFrançois Tigeot 	{ "ivref80", HDAA_QUIRK_IVREF80 },
662a1ad637SFrançois Tigeot 	{ "ivref100", HDAA_QUIRK_IVREF100 },
672a1ad637SFrançois Tigeot 	{ "ovref50", HDAA_QUIRK_OVREF50 },
682a1ad637SFrançois Tigeot 	{ "ovref80", HDAA_QUIRK_OVREF80 },
692a1ad637SFrançois Tigeot 	{ "ovref100", HDAA_QUIRK_OVREF100 },
702a1ad637SFrançois Tigeot 	{ "ivref", HDAA_QUIRK_IVREF },
712a1ad637SFrançois Tigeot 	{ "ovref", HDAA_QUIRK_OVREF },
722a1ad637SFrançois Tigeot 	{ "vref", HDAA_QUIRK_VREF },
732a1ad637SFrançois Tigeot };
742a1ad637SFrançois Tigeot 
752a1ad637SFrançois Tigeot #define HDA_PARSE_MAXDEPTH	10
762a1ad637SFrançois Tigeot 
772a1ad637SFrançois Tigeot MALLOC_DEFINE(M_HDAA, "hdaa", "HDA Audio");
782a1ad637SFrançois Tigeot 
792a1ad637SFrançois Tigeot static const char *HDA_COLORS[16] = {"Unknown", "Black", "Grey", "Blue",
802a1ad637SFrançois Tigeot     "Green", "Red", "Orange", "Yellow", "Purple", "Pink", "Res.A", "Res.B",
812a1ad637SFrançois Tigeot     "Res.C", "Res.D", "White", "Other"};
822a1ad637SFrançois Tigeot 
832a1ad637SFrançois Tigeot static const char *HDA_DEVS[16] = {"Line-out", "Speaker", "Headphones", "CD",
842a1ad637SFrançois Tigeot     "SPDIF-out", "Digital-out", "Modem-line", "Modem-handset", "Line-in",
852a1ad637SFrançois Tigeot     "AUX", "Mic", "Telephony", "SPDIF-in", "Digital-in", "Res.E", "Other"};
862a1ad637SFrançois Tigeot 
872a1ad637SFrançois Tigeot static const char *HDA_CONNS[4] = {"Jack", "None", "Fixed", "Both"};
882a1ad637SFrançois Tigeot 
892a1ad637SFrançois Tigeot static const char *HDA_CONNECTORS[16] = {
902a1ad637SFrançois Tigeot     "Unknown", "1/8", "1/4", "ATAPI", "RCA", "Optical", "Digital", "Analog",
912a1ad637SFrançois Tigeot     "DIN", "XLR", "RJ-11", "Combo", "0xc", "0xd", "0xe", "Other" };
922a1ad637SFrançois Tigeot 
932a1ad637SFrançois Tigeot static const char *HDA_LOCS[64] = {
942a1ad637SFrançois Tigeot     "0x00", "Rear", "Front", "Left", "Right", "Top", "Bottom", "Rear-panel",
952a1ad637SFrançois Tigeot 	"Drive-bay", "0x09", "0x0a", "0x0b", "0x0c", "0x0d", "0x0e", "0x0f",
962a1ad637SFrançois Tigeot     "Internal", "0x11", "0x12", "0x13", "0x14", "0x15", "0x16", "Riser",
972a1ad637SFrançois Tigeot 	"0x18", "Onboard", "0x1a", "0x1b", "0x1c", "0x1d", "0x1e", "0x1f",
982a1ad637SFrançois Tigeot     "External", "Ext-Rear", "Ext-Front", "Ext-Left", "Ext-Right", "Ext-Top", "Ext-Bottom", "0x07",
992a1ad637SFrançois Tigeot 	"0x28", "0x29", "0x2a", "0x2b", "0x2c", "0x2d", "0x2e", "0x2f",
1002a1ad637SFrançois Tigeot     "Other", "0x31", "0x32", "0x33", "0x34", "0x35", "Other-Bott", "Lid-In",
1012a1ad637SFrançois Tigeot 	"Lid-Out", "0x39", "0x3a", "0x3b", "0x3c", "0x3d", "0x3e", "0x3f" };
1022a1ad637SFrançois Tigeot 
1032a1ad637SFrançois Tigeot static const char *HDA_GPIO_ACTIONS[8] = {
1042a1ad637SFrançois Tigeot     "keep", "set", "clear", "disable", "input", "0x05", "0x06", "0x07"};
1052a1ad637SFrançois Tigeot 
1062a1ad637SFrançois Tigeot static const char *HDA_HDMI_CODING_TYPES[18] = {
1072a1ad637SFrançois Tigeot     "undefined", "LPCM", "AC-3", "MPEG1", "MP3", "MPEG2", "AAC-LC", "DTS",
1082a1ad637SFrançois Tigeot     "ATRAC", "DSD", "E-AC-3", "DTS-HD", "MLP", "DST", "WMAPro", "HE-AAC",
1092a1ad637SFrançois Tigeot     "HE-AACv2", "MPEG-Surround"
1102a1ad637SFrançois Tigeot };
1112a1ad637SFrançois Tigeot 
1122a1ad637SFrançois Tigeot /* Default */
1132a1ad637SFrançois Tigeot static uint32_t hdaa_fmt[] = {
1142a1ad637SFrançois Tigeot 	SND_FORMAT(AFMT_S16_LE, 2, 0),
1152a1ad637SFrançois Tigeot 	0
1162a1ad637SFrançois Tigeot };
1172a1ad637SFrançois Tigeot 
1182a1ad637SFrançois Tigeot static struct pcmchan_caps hdaa_caps = {48000, 48000, hdaa_fmt, 0};
1192a1ad637SFrançois Tigeot 
1202a1ad637SFrançois Tigeot static const struct {
1212a1ad637SFrançois Tigeot 	uint32_t	rate;
1222a1ad637SFrançois Tigeot 	int		valid;
1232a1ad637SFrançois Tigeot 	uint16_t	base;
1242a1ad637SFrançois Tigeot 	uint16_t	mul;
1252a1ad637SFrançois Tigeot 	uint16_t	div;
1262a1ad637SFrançois Tigeot } hda_rate_tab[] = {
1272a1ad637SFrançois Tigeot 	{   8000, 1, 0x0000, 0x0000, 0x0500 },	/* (48000 * 1) / 6 */
1282a1ad637SFrançois Tigeot 	{   9600, 0, 0x0000, 0x0000, 0x0400 },	/* (48000 * 1) / 5 */
1292a1ad637SFrançois Tigeot 	{  12000, 0, 0x0000, 0x0000, 0x0300 },	/* (48000 * 1) / 4 */
1302a1ad637SFrançois Tigeot 	{  16000, 1, 0x0000, 0x0000, 0x0200 },	/* (48000 * 1) / 3 */
1312a1ad637SFrançois Tigeot 	{  18000, 0, 0x0000, 0x1000, 0x0700 },	/* (48000 * 3) / 8 */
1322a1ad637SFrançois Tigeot 	{  19200, 0, 0x0000, 0x0800, 0x0400 },	/* (48000 * 2) / 5 */
1332a1ad637SFrançois Tigeot 	{  24000, 0, 0x0000, 0x0000, 0x0100 },	/* (48000 * 1) / 2 */
1342a1ad637SFrançois Tigeot 	{  28800, 0, 0x0000, 0x1000, 0x0400 },	/* (48000 * 3) / 5 */
1352a1ad637SFrançois Tigeot 	{  32000, 1, 0x0000, 0x0800, 0x0200 },	/* (48000 * 2) / 3 */
1362a1ad637SFrançois Tigeot 	{  36000, 0, 0x0000, 0x1000, 0x0300 },	/* (48000 * 3) / 4 */
1372a1ad637SFrançois Tigeot 	{  38400, 0, 0x0000, 0x1800, 0x0400 },	/* (48000 * 4) / 5 */
1382a1ad637SFrançois Tigeot 	{  48000, 1, 0x0000, 0x0000, 0x0000 },	/* (48000 * 1) / 1 */
1392a1ad637SFrançois Tigeot 	{  64000, 0, 0x0000, 0x1800, 0x0200 },	/* (48000 * 4) / 3 */
1402a1ad637SFrançois Tigeot 	{  72000, 0, 0x0000, 0x1000, 0x0100 },	/* (48000 * 3) / 2 */
1412a1ad637SFrançois Tigeot 	{  96000, 1, 0x0000, 0x0800, 0x0000 },	/* (48000 * 2) / 1 */
1422a1ad637SFrançois Tigeot 	{ 144000, 0, 0x0000, 0x1000, 0x0000 },	/* (48000 * 3) / 1 */
1432a1ad637SFrançois Tigeot 	{ 192000, 1, 0x0000, 0x1800, 0x0000 },	/* (48000 * 4) / 1 */
1442a1ad637SFrançois Tigeot 	{   8820, 0, 0x4000, 0x0000, 0x0400 },	/* (44100 * 1) / 5 */
1452a1ad637SFrançois Tigeot 	{  11025, 1, 0x4000, 0x0000, 0x0300 },	/* (44100 * 1) / 4 */
1462a1ad637SFrançois Tigeot 	{  12600, 0, 0x4000, 0x0800, 0x0600 },	/* (44100 * 2) / 7 */
1472a1ad637SFrançois Tigeot 	{  14700, 0, 0x4000, 0x0000, 0x0200 },	/* (44100 * 1) / 3 */
1482a1ad637SFrançois Tigeot 	{  17640, 0, 0x4000, 0x0800, 0x0400 },	/* (44100 * 2) / 5 */
1492a1ad637SFrançois Tigeot 	{  18900, 0, 0x4000, 0x1000, 0x0600 },	/* (44100 * 3) / 7 */
1502a1ad637SFrançois Tigeot 	{  22050, 1, 0x4000, 0x0000, 0x0100 },	/* (44100 * 1) / 2 */
1512a1ad637SFrançois Tigeot 	{  25200, 0, 0x4000, 0x1800, 0x0600 },	/* (44100 * 4) / 7 */
1522a1ad637SFrançois Tigeot 	{  26460, 0, 0x4000, 0x1000, 0x0400 },	/* (44100 * 3) / 5 */
1532a1ad637SFrançois Tigeot 	{  29400, 0, 0x4000, 0x0800, 0x0200 },	/* (44100 * 2) / 3 */
1542a1ad637SFrançois Tigeot 	{  33075, 0, 0x4000, 0x1000, 0x0300 },	/* (44100 * 3) / 4 */
1552a1ad637SFrançois Tigeot 	{  35280, 0, 0x4000, 0x1800, 0x0400 },	/* (44100 * 4) / 5 */
1562a1ad637SFrançois Tigeot 	{  44100, 1, 0x4000, 0x0000, 0x0000 },	/* (44100 * 1) / 1 */
1572a1ad637SFrançois Tigeot 	{  58800, 0, 0x4000, 0x1800, 0x0200 },	/* (44100 * 4) / 3 */
1582a1ad637SFrançois Tigeot 	{  66150, 0, 0x4000, 0x1000, 0x0100 },	/* (44100 * 3) / 2 */
1592a1ad637SFrançois Tigeot 	{  88200, 1, 0x4000, 0x0800, 0x0000 },	/* (44100 * 2) / 1 */
1602a1ad637SFrançois Tigeot 	{ 132300, 0, 0x4000, 0x1000, 0x0000 },	/* (44100 * 3) / 1 */
1612a1ad637SFrançois Tigeot 	{ 176400, 1, 0x4000, 0x1800, 0x0000 },	/* (44100 * 4) / 1 */
1622a1ad637SFrançois Tigeot };
1632a1ad637SFrançois Tigeot #define HDA_RATE_TAB_LEN (sizeof(hda_rate_tab) / sizeof(hda_rate_tab[0]))
1642a1ad637SFrançois Tigeot 
16567931cc4SFrançois Tigeot static const char *ossnames[] = SOUND_DEVICE_NAMES;
1662a1ad637SFrançois Tigeot 
1672a1ad637SFrançois Tigeot /****************************************************************************
1682a1ad637SFrançois Tigeot  * Function prototypes
1692a1ad637SFrançois Tigeot  ****************************************************************************/
1702a1ad637SFrançois Tigeot static int	hdaa_pcmchannel_setup(struct hdaa_chan *);
1712a1ad637SFrançois Tigeot 
1722a1ad637SFrançois Tigeot static void	hdaa_widget_connection_select(struct hdaa_widget *, uint8_t);
1732a1ad637SFrançois Tigeot static void	hdaa_audio_ctl_amp_set(struct hdaa_audio_ctl *,
1742a1ad637SFrançois Tigeot 						uint32_t, int, int);
1752a1ad637SFrançois Tigeot static struct	hdaa_audio_ctl *hdaa_audio_ctl_amp_get(struct hdaa_devinfo *,
1762a1ad637SFrançois Tigeot 							nid_t, int, int, int);
1772a1ad637SFrançois Tigeot static void	hdaa_audio_ctl_amp_set_internal(struct hdaa_devinfo *,
1782a1ad637SFrançois Tigeot 				nid_t, int, int, int, int, int, int);
1792a1ad637SFrançois Tigeot 
1802a1ad637SFrançois Tigeot static void	hdaa_dump_pin_config(struct hdaa_widget *w, uint32_t conf);
1812a1ad637SFrançois Tigeot 
1822a1ad637SFrançois Tigeot static char *
hdaa_audio_ctl_ossmixer_mask2allname(uint32_t mask,char * buf,size_t len)1832a1ad637SFrançois Tigeot hdaa_audio_ctl_ossmixer_mask2allname(uint32_t mask, char *buf, size_t len)
1842a1ad637SFrançois Tigeot {
1852a1ad637SFrançois Tigeot 	int i, first = 1;
1862a1ad637SFrançois Tigeot 
1872a1ad637SFrançois Tigeot 	bzero(buf, len);
1882a1ad637SFrançois Tigeot 	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
1892a1ad637SFrançois Tigeot 		if (mask & (1 << i)) {
1902a1ad637SFrançois Tigeot 			if (first == 0)
1912a1ad637SFrançois Tigeot 				strlcat(buf, ", ", len);
1922a1ad637SFrançois Tigeot 			strlcat(buf, ossnames[i], len);
1932a1ad637SFrançois Tigeot 			first = 0;
1942a1ad637SFrançois Tigeot 		}
1952a1ad637SFrançois Tigeot 	}
1962a1ad637SFrançois Tigeot 	return (buf);
1972a1ad637SFrançois Tigeot }
1982a1ad637SFrançois Tigeot 
1992a1ad637SFrançois Tigeot static struct hdaa_audio_ctl *
hdaa_audio_ctl_each(struct hdaa_devinfo * devinfo,int * index)2002a1ad637SFrançois Tigeot hdaa_audio_ctl_each(struct hdaa_devinfo *devinfo, int *index)
2012a1ad637SFrançois Tigeot {
2022a1ad637SFrançois Tigeot 	if (devinfo == NULL ||
2032a1ad637SFrançois Tigeot 	    index == NULL || devinfo->ctl == NULL ||
2042a1ad637SFrançois Tigeot 	    devinfo->ctlcnt < 1 ||
2052a1ad637SFrançois Tigeot 	    *index < 0 || *index >= devinfo->ctlcnt)
2062a1ad637SFrançois Tigeot 		return (NULL);
2072a1ad637SFrançois Tigeot 	return (&devinfo->ctl[(*index)++]);
2082a1ad637SFrançois Tigeot }
2092a1ad637SFrançois Tigeot 
2102a1ad637SFrançois Tigeot static struct hdaa_audio_ctl *
hdaa_audio_ctl_amp_get(struct hdaa_devinfo * devinfo,nid_t nid,int dir,int index,int cnt)2112a1ad637SFrançois Tigeot hdaa_audio_ctl_amp_get(struct hdaa_devinfo *devinfo, nid_t nid, int dir,
2122a1ad637SFrançois Tigeot 						int index, int cnt)
2132a1ad637SFrançois Tigeot {
2142a1ad637SFrançois Tigeot 	struct hdaa_audio_ctl *ctl;
2152a1ad637SFrançois Tigeot 	int i, found = 0;
2162a1ad637SFrançois Tigeot 
2172a1ad637SFrançois Tigeot 	if (devinfo == NULL || devinfo->ctl == NULL)
2182a1ad637SFrançois Tigeot 		return (NULL);
2192a1ad637SFrançois Tigeot 
2202a1ad637SFrançois Tigeot 	i = 0;
2212a1ad637SFrançois Tigeot 	while ((ctl = hdaa_audio_ctl_each(devinfo, &i)) != NULL) {
2222a1ad637SFrançois Tigeot 		if (ctl->enable == 0)
2232a1ad637SFrançois Tigeot 			continue;
2242a1ad637SFrançois Tigeot 		if (ctl->widget->nid != nid)
2252a1ad637SFrançois Tigeot 			continue;
2262a1ad637SFrançois Tigeot 		if (dir && ctl->ndir != dir)
2272a1ad637SFrançois Tigeot 			continue;
2282a1ad637SFrançois Tigeot 		if (index >= 0 && ctl->ndir == HDAA_CTL_IN &&
2292a1ad637SFrançois Tigeot 		    ctl->dir == ctl->ndir && ctl->index != index)
2302a1ad637SFrançois Tigeot 			continue;
2312a1ad637SFrançois Tigeot 		found++;
2322a1ad637SFrançois Tigeot 		if (found == cnt || cnt <= 0)
2332a1ad637SFrançois Tigeot 			return (ctl);
2342a1ad637SFrançois Tigeot 	}
2352a1ad637SFrançois Tigeot 
2362a1ad637SFrançois Tigeot 	return (NULL);
2372a1ad637SFrançois Tigeot }
2382a1ad637SFrançois Tigeot 
2392a1ad637SFrançois Tigeot static const struct matrix {
2402a1ad637SFrançois Tigeot 	struct pcmchan_matrix	m;
2412a1ad637SFrançois Tigeot 	int			analog;
2422a1ad637SFrançois Tigeot } matrixes[]  = {
2432a1ad637SFrançois Tigeot     { SND_CHN_MATRIX_MAP_1_0,	1 },
2442a1ad637SFrançois Tigeot     { SND_CHN_MATRIX_MAP_2_0,	1 },
2452a1ad637SFrançois Tigeot     { SND_CHN_MATRIX_MAP_2_1,	0 },
2462a1ad637SFrançois Tigeot     { SND_CHN_MATRIX_MAP_3_0,	0 },
2472a1ad637SFrançois Tigeot     { SND_CHN_MATRIX_MAP_3_1,	0 },
2482a1ad637SFrançois Tigeot     { SND_CHN_MATRIX_MAP_4_0,	1 },
2492a1ad637SFrançois Tigeot     { SND_CHN_MATRIX_MAP_4_1,	0 },
2502a1ad637SFrançois Tigeot     { SND_CHN_MATRIX_MAP_5_0,	0 },
2512a1ad637SFrançois Tigeot     { SND_CHN_MATRIX_MAP_5_1,	1 },
2522a1ad637SFrançois Tigeot     { SND_CHN_MATRIX_MAP_6_0,	0 },
2532a1ad637SFrançois Tigeot     { SND_CHN_MATRIX_MAP_6_1,	0 },
2542a1ad637SFrançois Tigeot     { SND_CHN_MATRIX_MAP_7_0,	0 },
2552a1ad637SFrançois Tigeot     { SND_CHN_MATRIX_MAP_7_1,	1 },
2562a1ad637SFrançois Tigeot };
2572a1ad637SFrançois Tigeot 
2582a1ad637SFrançois Tigeot static const char *channel_names[] = SND_CHN_T_NAMES;
2592a1ad637SFrançois Tigeot 
2602a1ad637SFrançois Tigeot /*
2612a1ad637SFrançois Tigeot  * Connected channels change handler.
2622a1ad637SFrançois Tigeot  */
2632a1ad637SFrançois Tigeot static void
hdaa_channels_handler(struct hdaa_audio_as * as)2642a1ad637SFrançois Tigeot hdaa_channels_handler(struct hdaa_audio_as *as)
2652a1ad637SFrançois Tigeot {
2662a1ad637SFrançois Tigeot 	struct hdaa_pcm_devinfo *pdevinfo = as->pdevinfo;
2672a1ad637SFrançois Tigeot 	struct hdaa_devinfo *devinfo = pdevinfo->devinfo;
2682a1ad637SFrançois Tigeot 	struct hdaa_chan *ch = &devinfo->chans[as->chans[0]];
2692a1ad637SFrançois Tigeot 	struct hdaa_widget *w;
2702a1ad637SFrançois Tigeot 	uint8_t *eld;
2712a1ad637SFrançois Tigeot 	int i, total, sub, assume, channels;
2722a1ad637SFrançois Tigeot 	uint16_t cpins, upins, tpins;
2732a1ad637SFrançois Tigeot 
2742a1ad637SFrançois Tigeot 	cpins = upins = 0;
2752a1ad637SFrançois Tigeot 	eld = NULL;
2762a1ad637SFrançois Tigeot 	for (i = 0; i < 16; i++) {
2772a1ad637SFrançois Tigeot 		if (as->pins[i] <= 0)
2782a1ad637SFrançois Tigeot 			continue;
2792a1ad637SFrançois Tigeot 		w = hdaa_widget_get(devinfo, as->pins[i]);
2802a1ad637SFrançois Tigeot 		if (w == NULL)
2812a1ad637SFrançois Tigeot 			continue;
2822a1ad637SFrançois Tigeot 		if (w->wclass.pin.connected == 1)
2832a1ad637SFrançois Tigeot 			cpins |= (1 << i);
2842a1ad637SFrançois Tigeot 		else if (w->wclass.pin.connected != 0)
2852a1ad637SFrançois Tigeot 			upins |= (1 << i);
2862a1ad637SFrançois Tigeot 		if (w->eld != NULL && w->eld_len >= 8)
2872a1ad637SFrançois Tigeot 			eld = w->eld;
2882a1ad637SFrançois Tigeot 	}
2892a1ad637SFrançois Tigeot 	tpins = cpins | upins;
2902a1ad637SFrançois Tigeot 	if (as->hpredir >= 0)
2912a1ad637SFrançois Tigeot 		tpins &= 0x7fff;
2922a1ad637SFrançois Tigeot 	if (tpins == 0)
2932a1ad637SFrançois Tigeot 		tpins = as->pinset;
2942a1ad637SFrançois Tigeot 
2952a1ad637SFrançois Tigeot 	total = sub = assume = channels = 0;
2962a1ad637SFrançois Tigeot 	if (eld) {
2972a1ad637SFrançois Tigeot 		/* Map CEA speakers to sound(4) channels. */
2982a1ad637SFrançois Tigeot 		if (eld[7] & 0x01) /* Front Left/Right */
2992a1ad637SFrançois Tigeot 			channels |= SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR;
3002a1ad637SFrançois Tigeot 		if (eld[7] & 0x02) /* Low Frequency Effect */
3012a1ad637SFrançois Tigeot 			channels |= SND_CHN_T_MASK_LF;
3022a1ad637SFrançois Tigeot 		if (eld[7] & 0x04) /* Front Center */
3032a1ad637SFrançois Tigeot 			channels |= SND_CHN_T_MASK_FC;
3042a1ad637SFrançois Tigeot 		if (eld[7] & 0x08) { /* Rear Left/Right */
3052a1ad637SFrançois Tigeot 			/* If we have both RLR and RLRC, report RLR as side. */
3062a1ad637SFrançois Tigeot 			if (eld[7] & 0x40) /* Rear Left/Right Center */
3072a1ad637SFrançois Tigeot 			    channels |= SND_CHN_T_MASK_SL | SND_CHN_T_MASK_SR;
3082a1ad637SFrançois Tigeot 			else
3092a1ad637SFrançois Tigeot 			    channels |= SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BR;
3102a1ad637SFrançois Tigeot 		}
3112a1ad637SFrançois Tigeot 		if (eld[7] & 0x10) /* Rear center */
3122a1ad637SFrançois Tigeot 			channels |= SND_CHN_T_MASK_BC;
3132a1ad637SFrançois Tigeot 		if (eld[7] & 0x20) /* Front Left/Right Center */
3142a1ad637SFrançois Tigeot 			channels |= SND_CHN_T_MASK_FLC | SND_CHN_T_MASK_FRC;
3152a1ad637SFrançois Tigeot 		if (eld[7] & 0x40) /* Rear Left/Right Center */
3162a1ad637SFrançois Tigeot 			channels |= SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BR;
3172a1ad637SFrançois Tigeot 	} else if (as->pinset != 0 && (tpins & 0xffe0) == 0) {
3182a1ad637SFrançois Tigeot 		/* Map UAA speakers to sound(4) channels. */
3192a1ad637SFrançois Tigeot 		if (tpins & 0x0001)
3202a1ad637SFrançois Tigeot 			channels |= SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR;
3212a1ad637SFrançois Tigeot 		if (tpins & 0x0002)
3222a1ad637SFrançois Tigeot 			channels |= SND_CHN_T_MASK_FC | SND_CHN_T_MASK_LF;
3232a1ad637SFrançois Tigeot 		if (tpins & 0x0004)
3242a1ad637SFrançois Tigeot 			channels |= SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BR;
3252a1ad637SFrançois Tigeot 		if (tpins & 0x0008)
3262a1ad637SFrançois Tigeot 			channels |= SND_CHN_T_MASK_FLC | SND_CHN_T_MASK_FRC;
3272a1ad637SFrançois Tigeot 		if (tpins & 0x0010) {
3282a1ad637SFrançois Tigeot 			/* If there is no back pin, report side as back. */
3292a1ad637SFrançois Tigeot 			if ((as->pinset & 0x0004) == 0)
3302a1ad637SFrançois Tigeot 			    channels |= SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BR;
3312a1ad637SFrançois Tigeot 			else
3322a1ad637SFrançois Tigeot 			    channels |= SND_CHN_T_MASK_SL | SND_CHN_T_MASK_SR;
3332a1ad637SFrançois Tigeot 		}
3342a1ad637SFrançois Tigeot 	} else if (as->mixed) {
3352a1ad637SFrançois Tigeot 		/* Mixed assoc can be only stereo or theoretically mono. */
3362a1ad637SFrançois Tigeot 		if (ch->channels == 1)
3372a1ad637SFrançois Tigeot 			channels |= SND_CHN_T_MASK_FC;
3382a1ad637SFrançois Tigeot 		else
3392a1ad637SFrançois Tigeot 			channels |= SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR;
3402a1ad637SFrançois Tigeot 	}
3412a1ad637SFrançois Tigeot 	if (channels) {	/* We have some usable channels info. */
3422a1ad637SFrançois Tigeot 		HDA_BOOTVERBOSE(
3432a1ad637SFrançois Tigeot 			device_printf(pdevinfo->dev, "%s channel set is: ",
3442a1ad637SFrançois Tigeot 			    as->dir == HDAA_CTL_OUT ? "Playback" : "Recording");
3452a1ad637SFrançois Tigeot 			for (i = 0; i < SND_CHN_T_MAX; i++)
3462a1ad637SFrançois Tigeot 				if (channels & (1 << i))
34767931cc4SFrançois Tigeot 					kprintf("%s, ", channel_names[i]);
34867931cc4SFrançois Tigeot 			kprintf("\n");
3492a1ad637SFrançois Tigeot 		);
3502a1ad637SFrançois Tigeot 		/* Look for maximal fitting matrix. */
3512a1ad637SFrançois Tigeot 		for (i = 0; i < sizeof(matrixes) / sizeof(struct matrix); i++) {
3522a1ad637SFrançois Tigeot 			if (as->pinset != 0 && matrixes[i].analog == 0)
3532a1ad637SFrançois Tigeot 				continue;
3542a1ad637SFrançois Tigeot 			if ((matrixes[i].m.mask & ~channels) == 0) {
3552a1ad637SFrançois Tigeot 				total = matrixes[i].m.channels;
3562a1ad637SFrançois Tigeot 				sub = matrixes[i].m.ext;
3572a1ad637SFrançois Tigeot 			}
3582a1ad637SFrançois Tigeot 		}
3592a1ad637SFrançois Tigeot 	}
3602a1ad637SFrançois Tigeot 	if (total == 0) {
3612a1ad637SFrançois Tigeot 		assume = 1;
3622a1ad637SFrançois Tigeot 		total = ch->channels;
3632a1ad637SFrançois Tigeot 		sub = (total == 6 || total == 8) ? 1 : 0;
3642a1ad637SFrançois Tigeot 	}
3652a1ad637SFrançois Tigeot 	HDA_BOOTVERBOSE(
3662a1ad637SFrançois Tigeot 		device_printf(pdevinfo->dev,
3672a1ad637SFrançois Tigeot 		    "%s channel matrix is: %s%d.%d (%s)\n",
3682a1ad637SFrançois Tigeot 		    as->dir == HDAA_CTL_OUT ? "Playback" : "Recording",
3692a1ad637SFrançois Tigeot 		    assume ? "unknown, assuming " : "", total - sub, sub,
3702a1ad637SFrançois Tigeot 		    cpins != 0 ? "connected" :
3712a1ad637SFrançois Tigeot 		    (upins != 0 ? "unknown" : "disconnected"));
3722a1ad637SFrançois Tigeot 	);
3732a1ad637SFrançois Tigeot }
3742a1ad637SFrançois Tigeot 
3752a1ad637SFrançois Tigeot /*
3762a1ad637SFrançois Tigeot  * Headphones redirection change handler.
3772a1ad637SFrançois Tigeot  */
3782a1ad637SFrançois Tigeot static void
hdaa_hpredir_handler(struct hdaa_widget * w)3792a1ad637SFrançois Tigeot hdaa_hpredir_handler(struct hdaa_widget *w)
3802a1ad637SFrançois Tigeot {
3812a1ad637SFrançois Tigeot 	struct hdaa_devinfo *devinfo = w->devinfo;
3822a1ad637SFrançois Tigeot 	struct hdaa_audio_as *as = &devinfo->as[w->bindas];
3832a1ad637SFrançois Tigeot 	struct hdaa_widget *w1;
3842a1ad637SFrançois Tigeot 	struct hdaa_audio_ctl *ctl;
3852a1ad637SFrançois Tigeot 	uint32_t val;
3862a1ad637SFrançois Tigeot 	int j, connected = w->wclass.pin.connected;
3872a1ad637SFrançois Tigeot 
388536a8300SMatthew Dillon 	if (w->senseredir) {
389536a8300SMatthew Dillon 		for (j = 0; j < w->nconns; j++) {
390536a8300SMatthew Dillon 			if (w->conns[j] == w->senseredir && connected)
391536a8300SMatthew Dillon 				break;
392536a8300SMatthew Dillon 			else if (w->conns[j] != w->senseredir && !connected)
393536a8300SMatthew Dillon 				break;
394536a8300SMatthew Dillon 		}
395536a8300SMatthew Dillon 		if (j != w->nconns)
396536a8300SMatthew Dillon 			hdaa_widget_connection_select(w, j);
397536a8300SMatthew Dillon 	}
398536a8300SMatthew Dillon 
3992a1ad637SFrançois Tigeot 	HDA_BOOTVERBOSE(
4002a1ad637SFrançois Tigeot 		device_printf((as->pdevinfo && as->pdevinfo->dev) ?
4012a1ad637SFrançois Tigeot 		    as->pdevinfo->dev : devinfo->dev,
4022a1ad637SFrançois Tigeot 		    "Redirect output to: %s\n",
4032a1ad637SFrançois Tigeot 		    connected ? "headphones": "main");
4042a1ad637SFrançois Tigeot 	);
4052a1ad637SFrançois Tigeot 	/* (Un)Mute headphone pin. */
4062a1ad637SFrançois Tigeot 	ctl = hdaa_audio_ctl_amp_get(devinfo,
4072a1ad637SFrançois Tigeot 				     w->nid, HDAA_CTL_IN, -1, 1);
4082a1ad637SFrançois Tigeot 	if (ctl != NULL && ctl->mute) {
4092a1ad637SFrançois Tigeot 		/* If pin has muter - use it. */
4102a1ad637SFrançois Tigeot 		val = connected ? 0 : 1;
4112a1ad637SFrançois Tigeot 		if (val != ctl->forcemute) {
4122a1ad637SFrançois Tigeot 			ctl->forcemute = val;
4132a1ad637SFrançois Tigeot 			hdaa_audio_ctl_amp_set(ctl,
4142a1ad637SFrançois Tigeot 			    HDAA_AMP_MUTE_DEFAULT,
4152a1ad637SFrançois Tigeot 			    HDAA_AMP_VOL_DEFAULT, HDAA_AMP_VOL_DEFAULT);
4162a1ad637SFrançois Tigeot 		}
4172a1ad637SFrançois Tigeot 	} else {
4182a1ad637SFrançois Tigeot 		/* If there is no muter - disable pin output. */
4192a1ad637SFrançois Tigeot 		if (connected)
4202a1ad637SFrançois Tigeot 			val = w->wclass.pin.ctrl |
4212a1ad637SFrançois Tigeot 			    HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE;
4222a1ad637SFrançois Tigeot 		else
4232a1ad637SFrançois Tigeot 			val = w->wclass.pin.ctrl &
4242a1ad637SFrançois Tigeot 			    ~HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE;
4252a1ad637SFrançois Tigeot 		if (val != w->wclass.pin.ctrl) {
4262a1ad637SFrançois Tigeot 			w->wclass.pin.ctrl = val;
4272a1ad637SFrançois Tigeot 			hda_command(devinfo->dev,
4282a1ad637SFrançois Tigeot 			    HDA_CMD_SET_PIN_WIDGET_CTRL(0,
4292a1ad637SFrançois Tigeot 			    w->nid, w->wclass.pin.ctrl));
4302a1ad637SFrançois Tigeot 		}
4312a1ad637SFrançois Tigeot 	}
4322a1ad637SFrançois Tigeot 	/* (Un)Mute other pins. */
4332a1ad637SFrançois Tigeot 	for (j = 0; j < 15; j++) {
4342a1ad637SFrançois Tigeot 		if (as->pins[j] <= 0)
4352a1ad637SFrançois Tigeot 			continue;
436536a8300SMatthew Dillon 		w1 = hdaa_widget_get(devinfo, as->pins[j]);
437536a8300SMatthew Dillon 		if (w == w1)
438536a8300SMatthew Dillon 			continue;
439536a8300SMatthew Dillon 
440536a8300SMatthew Dillon 		/*
441536a8300SMatthew Dillon 		 * When senseredir is set (typically in hdaa_patches.c) in
442536a8300SMatthew Dillon 		 * a microphone nid it specifies which mux nid is being
443536a8300SMatthew Dillon 		 * routed.
444536a8300SMatthew Dillon 		 */
445536a8300SMatthew Dillon 		if (w->senseredir && w1) {
446536a8300SMatthew Dillon 			int k;
447536a8300SMatthew Dillon 
448536a8300SMatthew Dillon 			for (k = 0; k < w1->nconns; k++) {
449536a8300SMatthew Dillon 				if (w1->conns[k] == w->senseredir && connected)
450536a8300SMatthew Dillon 					break;
451536a8300SMatthew Dillon 				else if (w1->conns[k] != w->senseredir && !connected)
452536a8300SMatthew Dillon 					break;
453536a8300SMatthew Dillon 			}
454536a8300SMatthew Dillon 			if (k != w1->nconns)
455536a8300SMatthew Dillon 				hdaa_widget_connection_select(w1, k);
456536a8300SMatthew Dillon 		}
457536a8300SMatthew Dillon 
4582a1ad637SFrançois Tigeot 		ctl = hdaa_audio_ctl_amp_get(devinfo,
4592a1ad637SFrançois Tigeot 					     as->pins[j], HDAA_CTL_IN, -1, 1);
4602a1ad637SFrançois Tigeot 		if (ctl != NULL && ctl->mute) {
4612a1ad637SFrançois Tigeot 			/* If pin has muter - use it. */
4622a1ad637SFrançois Tigeot 			val = connected ? 1 : 0;
4632a1ad637SFrançois Tigeot 			if (val == ctl->forcemute)
4642a1ad637SFrançois Tigeot 				continue;
4652a1ad637SFrançois Tigeot 			ctl->forcemute = val;
4662a1ad637SFrançois Tigeot 			hdaa_audio_ctl_amp_set(ctl,
4672a1ad637SFrançois Tigeot 			    HDAA_AMP_MUTE_DEFAULT,
4682a1ad637SFrançois Tigeot 			    HDAA_AMP_VOL_DEFAULT, HDAA_AMP_VOL_DEFAULT);
4692a1ad637SFrançois Tigeot 			continue;
4702a1ad637SFrançois Tigeot 		}
4712a1ad637SFrançois Tigeot 		/* If there is no muter - disable pin output. */
4722a1ad637SFrançois Tigeot 		if (w1 != NULL) {
4732a1ad637SFrançois Tigeot 			if (connected)
4742a1ad637SFrançois Tigeot 				val = w1->wclass.pin.ctrl &
4752a1ad637SFrançois Tigeot 				    ~HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE;
4762a1ad637SFrançois Tigeot 			else
4772a1ad637SFrançois Tigeot 				val = w1->wclass.pin.ctrl |
4782a1ad637SFrançois Tigeot 				    HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE;
4792a1ad637SFrançois Tigeot 			if (val != w1->wclass.pin.ctrl) {
4802a1ad637SFrançois Tigeot 				w1->wclass.pin.ctrl = val;
4812a1ad637SFrançois Tigeot 				hda_command(devinfo->dev,
4822a1ad637SFrançois Tigeot 				    HDA_CMD_SET_PIN_WIDGET_CTRL(0,
4832a1ad637SFrançois Tigeot 				    w1->nid, w1->wclass.pin.ctrl));
4842a1ad637SFrançois Tigeot 			}
4852a1ad637SFrançois Tigeot 		}
4862a1ad637SFrançois Tigeot 	}
4872a1ad637SFrançois Tigeot }
4882a1ad637SFrançois Tigeot 
4892a1ad637SFrançois Tigeot /*
4902a1ad637SFrançois Tigeot  * Recording source change handler.
4912a1ad637SFrançois Tigeot  */
4922a1ad637SFrançois Tigeot static void
hdaa_autorecsrc_handler(struct hdaa_audio_as * as,struct hdaa_widget * w)4932a1ad637SFrançois Tigeot hdaa_autorecsrc_handler(struct hdaa_audio_as *as, struct hdaa_widget *w)
4942a1ad637SFrançois Tigeot {
4952a1ad637SFrançois Tigeot 	struct hdaa_pcm_devinfo *pdevinfo = as->pdevinfo;
4962a1ad637SFrançois Tigeot 	struct hdaa_devinfo *devinfo;
4972a1ad637SFrançois Tigeot 	struct hdaa_widget *w1;
4982a1ad637SFrançois Tigeot 	int i, mask, fullmask, prio, bestprio;
4992a1ad637SFrançois Tigeot 	char buf[128];
5002a1ad637SFrançois Tigeot 
5012a1ad637SFrançois Tigeot 	if (!as->mixed || pdevinfo == NULL || pdevinfo->mixer == NULL)
5022a1ad637SFrançois Tigeot 		return;
5032a1ad637SFrançois Tigeot 	/* Don't touch anything if we asked not to. */
5042a1ad637SFrançois Tigeot 	if (pdevinfo->autorecsrc == 0 ||
5052a1ad637SFrançois Tigeot 	    (pdevinfo->autorecsrc == 1 && w != NULL))
5062a1ad637SFrançois Tigeot 		return;
5072a1ad637SFrançois Tigeot 	/* Don't touch anything if "mix" or "speaker" selected. */
5082a1ad637SFrançois Tigeot 	if (pdevinfo->recsrc & (SOUND_MASK_IMIX | SOUND_MASK_SPEAKER))
5092a1ad637SFrançois Tigeot 		return;
5102a1ad637SFrançois Tigeot 	/* Don't touch anything if several selected. */
5112a1ad637SFrançois Tigeot 	if (ffs(pdevinfo->recsrc) != fls(pdevinfo->recsrc))
5122a1ad637SFrançois Tigeot 		return;
5132a1ad637SFrançois Tigeot 	devinfo = pdevinfo->devinfo;
5142a1ad637SFrançois Tigeot 	mask = fullmask = 0;
5152a1ad637SFrançois Tigeot 	bestprio = 0;
5162a1ad637SFrançois Tigeot 	for (i = 0; i < 16; i++) {
5172a1ad637SFrançois Tigeot 		if (as->pins[i] <= 0)
5182a1ad637SFrançois Tigeot 			continue;
5192a1ad637SFrançois Tigeot 		w1 = hdaa_widget_get(devinfo, as->pins[i]);
5202a1ad637SFrançois Tigeot 		if (w1 == NULL || w1->enable == 0)
5212a1ad637SFrançois Tigeot 			continue;
5222a1ad637SFrançois Tigeot 		if (w1->wclass.pin.connected == 0)
5232a1ad637SFrançois Tigeot 			continue;
5242a1ad637SFrançois Tigeot 		prio = (w1->wclass.pin.connected == 1) ? 2 : 1;
5252a1ad637SFrançois Tigeot 		if (prio < bestprio)
5262a1ad637SFrançois Tigeot 			continue;
5272a1ad637SFrançois Tigeot 		if (prio > bestprio) {
5282a1ad637SFrançois Tigeot 			mask = 0;
5292a1ad637SFrançois Tigeot 			bestprio = prio;
5302a1ad637SFrançois Tigeot 		}
5312a1ad637SFrançois Tigeot 		mask |= (1 << w1->ossdev);
5322a1ad637SFrançois Tigeot 		fullmask |= (1 << w1->ossdev);
5332a1ad637SFrançois Tigeot 	}
5342a1ad637SFrançois Tigeot 	if (mask == 0)
5352a1ad637SFrançois Tigeot 		return;
5362a1ad637SFrançois Tigeot 	/* Prefer newly connected input. */
5372a1ad637SFrançois Tigeot 	if (w != NULL && (mask & (1 << w->ossdev)))
5382a1ad637SFrançois Tigeot 		mask = (1 << w->ossdev);
5392a1ad637SFrançois Tigeot 	/* Prefer previously selected input */
5402a1ad637SFrançois Tigeot 	if (mask & pdevinfo->recsrc)
5412a1ad637SFrançois Tigeot 		mask &= pdevinfo->recsrc;
5422a1ad637SFrançois Tigeot 	/* Prefer mic. */
5432a1ad637SFrançois Tigeot 	if (mask & SOUND_MASK_MIC)
5442a1ad637SFrançois Tigeot 		mask = SOUND_MASK_MIC;
5452a1ad637SFrançois Tigeot 	/* Prefer monitor (2nd mic). */
5462a1ad637SFrançois Tigeot 	if (mask & SOUND_MASK_MONITOR)
5472a1ad637SFrançois Tigeot 		mask = SOUND_MASK_MONITOR;
5482a1ad637SFrançois Tigeot 	/* Just take first one. */
5492a1ad637SFrançois Tigeot 	mask = (1 << (ffs(mask) - 1));
5502a1ad637SFrançois Tigeot 	HDA_BOOTVERBOSE(
5512a1ad637SFrançois Tigeot 		hdaa_audio_ctl_ossmixer_mask2allname(mask, buf, sizeof(buf));
5522a1ad637SFrançois Tigeot 		device_printf(pdevinfo->dev,
5532a1ad637SFrançois Tigeot 		    "Automatically set rec source to: %s\n", buf);
5542a1ad637SFrançois Tigeot 	);
5552a1ad637SFrançois Tigeot 	hdaa_unlock(devinfo);
5562a1ad637SFrançois Tigeot 	mix_setrecsrc(pdevinfo->mixer, mask);
5572a1ad637SFrançois Tigeot 	hdaa_lock(devinfo);
5582a1ad637SFrançois Tigeot }
5592a1ad637SFrançois Tigeot 
5602a1ad637SFrançois Tigeot /*
5612a1ad637SFrançois Tigeot  * Jack presence detection event handler.
5622a1ad637SFrançois Tigeot  */
5632a1ad637SFrançois Tigeot static void
hdaa_presence_handler(struct hdaa_widget * w)5642a1ad637SFrançois Tigeot hdaa_presence_handler(struct hdaa_widget *w)
5652a1ad637SFrançois Tigeot {
5662a1ad637SFrançois Tigeot 	struct hdaa_devinfo *devinfo = w->devinfo;
5672a1ad637SFrançois Tigeot 	struct hdaa_audio_as *as;
5682a1ad637SFrançois Tigeot 	uint32_t res;
5692a1ad637SFrançois Tigeot 	int connected, old;
5702a1ad637SFrançois Tigeot 
5712a1ad637SFrançois Tigeot 	if (w->enable == 0 || w->type !=
5722a1ad637SFrançois Tigeot 	    HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX)
5732a1ad637SFrançois Tigeot 		return;
5742a1ad637SFrançois Tigeot 
5752a1ad637SFrançois Tigeot 	if (HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP(w->wclass.pin.cap) == 0 ||
5762a1ad637SFrançois Tigeot 	    (HDA_CONFIG_DEFAULTCONF_MISC(w->wclass.pin.config) & 1) != 0)
5772a1ad637SFrançois Tigeot 		return;
5782a1ad637SFrançois Tigeot 
5792a1ad637SFrançois Tigeot 	res = hda_command(devinfo->dev, HDA_CMD_GET_PIN_SENSE(0, w->nid));
5802a1ad637SFrançois Tigeot 	connected = (res & HDA_CMD_GET_PIN_SENSE_PRESENCE_DETECT) != 0;
5812a1ad637SFrançois Tigeot 	if (devinfo->quirks & HDAA_QUIRK_SENSEINV)
5822a1ad637SFrançois Tigeot 		connected = !connected;
5832a1ad637SFrançois Tigeot 	old = w->wclass.pin.connected;
5842a1ad637SFrançois Tigeot 	if (connected == old)
5852a1ad637SFrançois Tigeot 		return;
5862a1ad637SFrançois Tigeot 	w->wclass.pin.connected = connected;
5872a1ad637SFrançois Tigeot 	HDA_BOOTVERBOSE(
5882a1ad637SFrançois Tigeot 		if (connected || old != 2) {
5892a1ad637SFrançois Tigeot 			device_printf(devinfo->dev,
5902a1ad637SFrançois Tigeot 			    "Pin sense: nid=%d sense=0x%08x (%sconnected)\n",
5912a1ad637SFrançois Tigeot 			    w->nid, res, !connected ? "dis" : "");
5922a1ad637SFrançois Tigeot 		}
5932a1ad637SFrançois Tigeot 	);
5942a1ad637SFrançois Tigeot 
5952a1ad637SFrançois Tigeot 	as = &devinfo->as[w->bindas];
596536a8300SMatthew Dillon 	if (w->senseredir)
5972a1ad637SFrançois Tigeot 		hdaa_hpredir_handler(w);
598536a8300SMatthew Dillon 	else if (as->hpredir >= 0 && as->pins[15] == w->nid)
599536a8300SMatthew Dillon 		hdaa_hpredir_handler(w);
600536a8300SMatthew Dillon 
6012a1ad637SFrançois Tigeot 	if (as->dir == HDAA_CTL_IN && old != 2)
6022a1ad637SFrançois Tigeot 		hdaa_autorecsrc_handler(as, w);
6032a1ad637SFrançois Tigeot 	if (old != 2)
6042a1ad637SFrançois Tigeot 		hdaa_channels_handler(as);
6052a1ad637SFrançois Tigeot }
6062a1ad637SFrançois Tigeot 
6072a1ad637SFrançois Tigeot /*
6082a1ad637SFrançois Tigeot  * Callback for poll based presence detection.
6092a1ad637SFrançois Tigeot  */
6102a1ad637SFrançois Tigeot static void
hdaa_jack_poll_callback(void * arg)6112a1ad637SFrançois Tigeot hdaa_jack_poll_callback(void *arg)
6122a1ad637SFrançois Tigeot {
6132a1ad637SFrançois Tigeot 	struct hdaa_devinfo *devinfo = arg;
6142a1ad637SFrançois Tigeot 	struct hdaa_widget *w;
6152a1ad637SFrançois Tigeot 	int i;
6162a1ad637SFrançois Tigeot 
6172a1ad637SFrançois Tigeot 	hdaa_lock(devinfo);
6182a1ad637SFrançois Tigeot 	if (devinfo->poll_ival == 0) {
6192a1ad637SFrançois Tigeot 		hdaa_unlock(devinfo);
6202a1ad637SFrançois Tigeot 		return;
6212a1ad637SFrançois Tigeot 	}
6222a1ad637SFrançois Tigeot 	for (i = 0; i < devinfo->ascnt; i++) {
6232a1ad637SFrançois Tigeot 		if (devinfo->as[i].hpredir < 0)
6242a1ad637SFrançois Tigeot 			continue;
6252a1ad637SFrançois Tigeot 		w = hdaa_widget_get(devinfo, devinfo->as[i].pins[15]);
6262a1ad637SFrançois Tigeot 		if (w == NULL || w->enable == 0 || w->type !=
6272a1ad637SFrançois Tigeot 		    HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX)
6282a1ad637SFrançois Tigeot 			continue;
6292a1ad637SFrançois Tigeot 		hdaa_presence_handler(w);
6302a1ad637SFrançois Tigeot 	}
6312a1ad637SFrançois Tigeot 	callout_reset(&devinfo->poll_jack, devinfo->poll_ival,
6322a1ad637SFrançois Tigeot 	    hdaa_jack_poll_callback, devinfo);
6332a1ad637SFrançois Tigeot 	hdaa_unlock(devinfo);
6342a1ad637SFrançois Tigeot }
6352a1ad637SFrançois Tigeot 
6362a1ad637SFrançois Tigeot static void
hdaa_eld_dump(struct hdaa_widget * w)6372a1ad637SFrançois Tigeot hdaa_eld_dump(struct hdaa_widget *w)
6382a1ad637SFrançois Tigeot {
6392a1ad637SFrançois Tigeot 	struct hdaa_devinfo *devinfo = w->devinfo;
6402a1ad637SFrançois Tigeot 	device_t dev = devinfo->dev;
6412a1ad637SFrançois Tigeot 	uint8_t *sad;
6422a1ad637SFrançois Tigeot 	int len, mnl, i, sadc, fmt;
6432a1ad637SFrançois Tigeot 
6442a1ad637SFrançois Tigeot 	if (w->eld == NULL || w->eld_len < 4)
6452a1ad637SFrançois Tigeot 		return;
6462a1ad637SFrançois Tigeot 	device_printf(dev,
6472a1ad637SFrançois Tigeot 	    "ELD nid=%d: ELD_Ver=%u Baseline_ELD_Len=%u\n",
6482a1ad637SFrançois Tigeot 	    w->nid, w->eld[0] >> 3, w->eld[2]);
6492a1ad637SFrançois Tigeot 	if ((w->eld[0] >> 3) != 0x02)
6502a1ad637SFrançois Tigeot 		return;
6512a1ad637SFrançois Tigeot 	len = min(w->eld_len, (u_int)w->eld[2] * 4);
6522a1ad637SFrançois Tigeot 	mnl = w->eld[4] & 0x1f;
6532a1ad637SFrançois Tigeot 	device_printf(dev,
6542a1ad637SFrançois Tigeot 	    "ELD nid=%d: CEA_EDID_Ver=%u MNL=%u\n",
6552a1ad637SFrançois Tigeot 	    w->nid, w->eld[4] >> 5, mnl);
6562a1ad637SFrançois Tigeot 	sadc = w->eld[5] >> 4;
6572a1ad637SFrançois Tigeot 	device_printf(dev,
6582a1ad637SFrançois Tigeot 	    "ELD nid=%d: SAD_Count=%u Conn_Type=%u S_AI=%u HDCP=%u\n",
6592a1ad637SFrançois Tigeot 	    w->nid, sadc, (w->eld[5] >> 2) & 0x3,
6602a1ad637SFrançois Tigeot 	    (w->eld[5] >> 1) & 0x1, w->eld[5] & 0x1);
6612a1ad637SFrançois Tigeot 	device_printf(dev,
6622a1ad637SFrançois Tigeot 	    "ELD nid=%d: Aud_Synch_Delay=%ums\n",
6632a1ad637SFrançois Tigeot 	    w->nid, w->eld[6] * 2);
6642a1ad637SFrançois Tigeot 	device_printf(dev,
6652f8af9dbSzrj 	    "ELD nid=%d: Channels=0x%pb%i\n", w->nid,
6662f8af9dbSzrj 	    "\020\07RLRC\06FLRC\05RC\04RLR\03FC\02LFE\01FLR", w->eld[7]);
6672a1ad637SFrançois Tigeot 	device_printf(dev,
6682a1ad637SFrançois Tigeot 	    "ELD nid=%d: Port_ID=0x%02x%02x%02x%02x%02x%02x%02x%02x\n",
6692a1ad637SFrançois Tigeot 	    w->nid, w->eld[8], w->eld[9], w->eld[10], w->eld[11],
6702a1ad637SFrançois Tigeot 	    w->eld[12], w->eld[13], w->eld[14], w->eld[15]);
6712a1ad637SFrançois Tigeot 	device_printf(dev,
6722a1ad637SFrançois Tigeot 	    "ELD nid=%d: Manufacturer_Name=0x%02x%02x\n",
6732a1ad637SFrançois Tigeot 	    w->nid, w->eld[16], w->eld[17]);
6742a1ad637SFrançois Tigeot 	device_printf(dev,
6752a1ad637SFrançois Tigeot 	    "ELD nid=%d: Product_Code=0x%02x%02x\n",
6762a1ad637SFrançois Tigeot 	    w->nid, w->eld[18], w->eld[19]);
6772a1ad637SFrançois Tigeot 	device_printf(dev,
6782a1ad637SFrançois Tigeot 	    "ELD nid=%d: Monitor_Name_String='%.*s'\n",
6792a1ad637SFrançois Tigeot 	    w->nid, mnl, &w->eld[20]);
6802a1ad637SFrançois Tigeot 	for (i = 0; i < sadc; i++) {
6812a1ad637SFrançois Tigeot 		sad = &w->eld[20 + mnl + i * 3];
6822a1ad637SFrançois Tigeot 		fmt = (sad[0] >> 3) & 0x0f;
6832a1ad637SFrançois Tigeot 		if (fmt == HDA_HDMI_CODING_TYPE_REF_CTX) {
6842a1ad637SFrançois Tigeot 			fmt = (sad[2] >> 3) & 0x1f;
6852a1ad637SFrançois Tigeot 			if (fmt < 1 || fmt > 3)
6862a1ad637SFrançois Tigeot 				fmt = 0;
6872a1ad637SFrançois Tigeot 			else
6882a1ad637SFrançois Tigeot 				fmt += 14;
6892a1ad637SFrançois Tigeot 		}
6902a1ad637SFrançois Tigeot 		device_printf(dev,
6912f8af9dbSzrj 		    "ELD nid=%d: %s %dch freqs=0x%pb%i",
6922a1ad637SFrançois Tigeot 		    w->nid, HDA_HDMI_CODING_TYPES[fmt], (sad[0] & 0x07) + 1,
6932f8af9dbSzrj 		    "\020\007192\006176\00596\00488\00348\00244\00132", sad[1]);
6942a1ad637SFrançois Tigeot 		switch (fmt) {
6952a1ad637SFrançois Tigeot 		case HDA_HDMI_CODING_TYPE_LPCM:
6962f8af9dbSzrj 			kprintf(" sizes=0x%pb%i",
6972f8af9dbSzrj 			    "\020\00324\00220\00116", sad[2] & 0x07);
6982a1ad637SFrançois Tigeot 			break;
6992a1ad637SFrançois Tigeot 		case HDA_HDMI_CODING_TYPE_AC3:
7002a1ad637SFrançois Tigeot 		case HDA_HDMI_CODING_TYPE_MPEG1:
7012a1ad637SFrançois Tigeot 		case HDA_HDMI_CODING_TYPE_MP3:
7022a1ad637SFrançois Tigeot 		case HDA_HDMI_CODING_TYPE_MPEG2:
7032a1ad637SFrançois Tigeot 		case HDA_HDMI_CODING_TYPE_AACLC:
7042a1ad637SFrançois Tigeot 		case HDA_HDMI_CODING_TYPE_DTS:
7052a1ad637SFrançois Tigeot 		case HDA_HDMI_CODING_TYPE_ATRAC:
70667931cc4SFrançois Tigeot 			kprintf(" max_bitrate=%d", sad[2] * 8000);
7072a1ad637SFrançois Tigeot 			break;
7082a1ad637SFrançois Tigeot 		case HDA_HDMI_CODING_TYPE_WMAPRO:
70967931cc4SFrançois Tigeot 			kprintf(" profile=%d", sad[2] & 0x07);
7102a1ad637SFrançois Tigeot 			break;
7112a1ad637SFrançois Tigeot 		}
71267931cc4SFrançois Tigeot 		kprintf("\n");
7132a1ad637SFrançois Tigeot 	}
7142a1ad637SFrançois Tigeot }
7152a1ad637SFrançois Tigeot 
7162a1ad637SFrançois Tigeot static void
hdaa_eld_handler(struct hdaa_widget * w)7172a1ad637SFrançois Tigeot hdaa_eld_handler(struct hdaa_widget *w)
7182a1ad637SFrançois Tigeot {
7192a1ad637SFrançois Tigeot 	struct hdaa_devinfo *devinfo = w->devinfo;
7202a1ad637SFrançois Tigeot 	uint32_t res;
7212a1ad637SFrançois Tigeot 	int i;
7222a1ad637SFrançois Tigeot 
7232a1ad637SFrançois Tigeot 	if (w->enable == 0 || w->type !=
7242a1ad637SFrançois Tigeot 	    HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX)
7252a1ad637SFrançois Tigeot 		return;
7262a1ad637SFrançois Tigeot 
7272a1ad637SFrançois Tigeot 	if (HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP(w->wclass.pin.cap) == 0 ||
7282a1ad637SFrançois Tigeot 	    (HDA_CONFIG_DEFAULTCONF_MISC(w->wclass.pin.config) & 1) != 0)
7292a1ad637SFrançois Tigeot 		return;
7302a1ad637SFrançois Tigeot 
7312a1ad637SFrançois Tigeot 	res = hda_command(devinfo->dev, HDA_CMD_GET_PIN_SENSE(0, w->nid));
7322a1ad637SFrançois Tigeot 	if ((w->eld != 0) == ((res & HDA_CMD_GET_PIN_SENSE_ELD_VALID) != 0))
7332a1ad637SFrançois Tigeot 		return;
7342a1ad637SFrançois Tigeot 	if (w->eld != NULL) {
7352a1ad637SFrançois Tigeot 		w->eld_len = 0;
73667931cc4SFrançois Tigeot 		kfree(w->eld, M_HDAA);
7372a1ad637SFrançois Tigeot 		w->eld = NULL;
7382a1ad637SFrançois Tigeot 	}
7392a1ad637SFrançois Tigeot 	HDA_BOOTVERBOSE(
7402a1ad637SFrançois Tigeot 		device_printf(devinfo->dev,
7412a1ad637SFrançois Tigeot 		    "Pin sense: nid=%d sense=0x%08x "
7422a1ad637SFrançois Tigeot 		    "(%sconnected, ELD %svalid)\n",
7432a1ad637SFrançois Tigeot 		    w->nid, res,
7442a1ad637SFrançois Tigeot 		    (res & HDA_CMD_GET_PIN_SENSE_PRESENCE_DETECT) ? "" : "dis",
7452a1ad637SFrançois Tigeot 		    (res & HDA_CMD_GET_PIN_SENSE_ELD_VALID) ? "" : "in");
7462a1ad637SFrançois Tigeot 	);
7472a1ad637SFrançois Tigeot 	if ((res & HDA_CMD_GET_PIN_SENSE_ELD_VALID) == 0)
7482a1ad637SFrançois Tigeot 		return;
7492a1ad637SFrançois Tigeot 
7502a1ad637SFrançois Tigeot 	res = hda_command(devinfo->dev,
7512a1ad637SFrançois Tigeot 	    HDA_CMD_GET_HDMI_DIP_SIZE(0, w->nid, 0x08));
7522a1ad637SFrançois Tigeot 	if (res == HDA_INVALID)
7532a1ad637SFrançois Tigeot 		return;
7542a1ad637SFrançois Tigeot 	w->eld_len = res & 0xff;
7552a1ad637SFrançois Tigeot 	if (w->eld_len != 0)
7564e8e900cSMatthew Dillon 		w->eld = kmalloc(w->eld_len, M_HDAA, M_ZERO | M_WAITOK);
7572a1ad637SFrançois Tigeot 	if (w->eld == NULL) {
7582a1ad637SFrançois Tigeot 		w->eld_len = 0;
7592a1ad637SFrançois Tigeot 		return;
7602a1ad637SFrançois Tigeot 	}
7612a1ad637SFrançois Tigeot 
7622a1ad637SFrançois Tigeot 	for (i = 0; i < w->eld_len; i++) {
7632a1ad637SFrançois Tigeot 		res = hda_command(devinfo->dev,
7642a1ad637SFrançois Tigeot 		    HDA_CMD_GET_HDMI_ELDD(0, w->nid, i));
7652a1ad637SFrançois Tigeot 		if (res & 0x80000000)
7662a1ad637SFrançois Tigeot 			w->eld[i] = res & 0xff;
7672a1ad637SFrançois Tigeot 	}
7682a1ad637SFrançois Tigeot 	HDA_BOOTVERBOSE(
7692a1ad637SFrançois Tigeot 		hdaa_eld_dump(w);
7702a1ad637SFrançois Tigeot 	);
7712a1ad637SFrançois Tigeot 	hdaa_channels_handler(&devinfo->as[w->bindas]);
7722a1ad637SFrançois Tigeot }
7732a1ad637SFrançois Tigeot 
7742a1ad637SFrançois Tigeot /*
7752a1ad637SFrançois Tigeot  * Pin sense initializer.
7762a1ad637SFrançois Tigeot  */
7772a1ad637SFrançois Tigeot static void
hdaa_sense_init(struct hdaa_devinfo * devinfo)7782a1ad637SFrançois Tigeot hdaa_sense_init(struct hdaa_devinfo *devinfo)
7792a1ad637SFrançois Tigeot {
7802a1ad637SFrançois Tigeot 	struct hdaa_audio_as *as;
7812a1ad637SFrançois Tigeot 	struct hdaa_widget *w;
7822a1ad637SFrançois Tigeot 	int i, poll = 0;
7832a1ad637SFrançois Tigeot 
7842a1ad637SFrançois Tigeot 	for (i = devinfo->startnode; i < devinfo->endnode; i++) {
7852a1ad637SFrançois Tigeot 		w = hdaa_widget_get(devinfo, i);
7862a1ad637SFrançois Tigeot 		if (w == NULL || w->enable == 0 || w->type !=
7872a1ad637SFrançois Tigeot 		    HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX)
7882a1ad637SFrançois Tigeot 			continue;
7892a1ad637SFrançois Tigeot 		if (HDA_PARAM_AUDIO_WIDGET_CAP_UNSOL_CAP(w->param.widget_cap)) {
7902a1ad637SFrançois Tigeot 			if (w->unsol < 0)
7912a1ad637SFrançois Tigeot 				w->unsol = HDAC_UNSOL_ALLOC(
7922a1ad637SFrançois Tigeot 				    device_get_parent(devinfo->dev),
7932a1ad637SFrançois Tigeot 				    devinfo->dev, w->nid);
7942a1ad637SFrançois Tigeot 			hda_command(devinfo->dev,
7952a1ad637SFrançois Tigeot 			    HDA_CMD_SET_UNSOLICITED_RESPONSE(0, w->nid,
7962a1ad637SFrançois Tigeot 			    HDA_CMD_SET_UNSOLICITED_RESPONSE_ENABLE | w->unsol));
7972a1ad637SFrançois Tigeot 		}
7982a1ad637SFrançois Tigeot 		as = &devinfo->as[w->bindas];
7992a1ad637SFrançois Tigeot 		if (as->hpredir >= 0 && as->pins[15] == w->nid) {
8002a1ad637SFrançois Tigeot 			if (HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP(w->wclass.pin.cap) == 0 ||
8012a1ad637SFrançois Tigeot 			    (HDA_CONFIG_DEFAULTCONF_MISC(w->wclass.pin.config) & 1) != 0) {
8022a1ad637SFrançois Tigeot 				device_printf(devinfo->dev,
8032a1ad637SFrançois Tigeot 				    "No presence detection support at nid %d\n",
8042a1ad637SFrançois Tigeot 				    w->nid);
8052a1ad637SFrançois Tigeot 			} else {
8062a1ad637SFrançois Tigeot 				if (w->unsol < 0)
8072a1ad637SFrançois Tigeot 					poll = 1;
8082a1ad637SFrançois Tigeot 				HDA_BOOTVERBOSE(
8092a1ad637SFrançois Tigeot 					device_printf(devinfo->dev,
8102a1ad637SFrançois Tigeot 					    "Headphones redirection for "
8112a1ad637SFrançois Tigeot 					    "association %d nid=%d using %s.\n",
8122a1ad637SFrançois Tigeot 					    w->bindas, w->nid,
8132a1ad637SFrançois Tigeot 					    (w->unsol < 0) ? "polling" :
8142a1ad637SFrançois Tigeot 					    "unsolicited responses");
8152a1ad637SFrançois Tigeot 				);
8162a1ad637SFrançois Tigeot 			};
8172a1ad637SFrançois Tigeot 		}
8182a1ad637SFrançois Tigeot 		hdaa_presence_handler(w);
8192a1ad637SFrançois Tigeot 		if (!HDA_PARAM_PIN_CAP_DP(w->wclass.pin.cap) &&
8202a1ad637SFrançois Tigeot 		    !HDA_PARAM_PIN_CAP_HDMI(w->wclass.pin.cap))
8212a1ad637SFrançois Tigeot 			continue;
8222a1ad637SFrançois Tigeot 		hdaa_eld_handler(w);
8232a1ad637SFrançois Tigeot 	}
8242a1ad637SFrançois Tigeot 	if (poll) {
8252a1ad637SFrançois Tigeot 		callout_reset(&devinfo->poll_jack, 1,
8262a1ad637SFrançois Tigeot 		    hdaa_jack_poll_callback, devinfo);
8272a1ad637SFrançois Tigeot 	}
8282a1ad637SFrançois Tigeot }
8292a1ad637SFrançois Tigeot 
8302a1ad637SFrançois Tigeot static void
hdaa_sense_deinit(struct hdaa_devinfo * devinfo)8312a1ad637SFrançois Tigeot hdaa_sense_deinit(struct hdaa_devinfo *devinfo)
8322a1ad637SFrançois Tigeot {
8332a1ad637SFrançois Tigeot 	struct hdaa_widget *w;
8342a1ad637SFrançois Tigeot 	int i;
8352a1ad637SFrançois Tigeot 
8362a1ad637SFrançois Tigeot 	callout_stop(&devinfo->poll_jack);
8372a1ad637SFrançois Tigeot 	for (i = devinfo->startnode; i < devinfo->endnode; i++) {
8382a1ad637SFrançois Tigeot 		w = hdaa_widget_get(devinfo, i);
8392a1ad637SFrançois Tigeot 		if (w == NULL || w->enable == 0 || w->type !=
8402a1ad637SFrançois Tigeot 		    HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX)
8412a1ad637SFrançois Tigeot 			continue;
8422a1ad637SFrançois Tigeot 		if (w->unsol < 0)
8432a1ad637SFrançois Tigeot 			continue;
8442a1ad637SFrançois Tigeot 		hda_command(devinfo->dev,
8452a1ad637SFrançois Tigeot 		    HDA_CMD_SET_UNSOLICITED_RESPONSE(0, w->nid, 0));
8462a1ad637SFrançois Tigeot 		HDAC_UNSOL_FREE(
8472a1ad637SFrançois Tigeot 		    device_get_parent(devinfo->dev), devinfo->dev,
8482a1ad637SFrançois Tigeot 		    w->unsol);
8492a1ad637SFrançois Tigeot 		w->unsol = -1;
8502a1ad637SFrançois Tigeot 	}
8512a1ad637SFrançois Tigeot }
8522a1ad637SFrançois Tigeot 
8532a1ad637SFrançois Tigeot uint32_t
hdaa_widget_pin_patch(uint32_t config,const char * str)8542a1ad637SFrançois Tigeot hdaa_widget_pin_patch(uint32_t config, const char *str)
8552a1ad637SFrançois Tigeot {
8562a1ad637SFrançois Tigeot 	char buf[256];
8572a1ad637SFrançois Tigeot 	char *key, *value, *rest, *bad;
8582a1ad637SFrançois Tigeot 	int ival, i;
8592a1ad637SFrançois Tigeot 
8602a1ad637SFrançois Tigeot 	strlcpy(buf, str, sizeof(buf));
8612a1ad637SFrançois Tigeot 	rest = buf;
8622a1ad637SFrançois Tigeot 	while ((key = strsep(&rest, "=")) != NULL) {
8632a1ad637SFrançois Tigeot 		value = strsep(&rest, " \t");
8642a1ad637SFrançois Tigeot 		if (value == NULL)
8652a1ad637SFrançois Tigeot 			break;
8662a1ad637SFrançois Tigeot 		ival = strtol(value, &bad, 10);
8672a1ad637SFrançois Tigeot 		if (strcmp(key, "seq") == 0) {
8682a1ad637SFrançois Tigeot 			config &= ~HDA_CONFIG_DEFAULTCONF_SEQUENCE_MASK;
8692a1ad637SFrançois Tigeot 			config |= ((ival << HDA_CONFIG_DEFAULTCONF_SEQUENCE_SHIFT) &
8702a1ad637SFrançois Tigeot 			    HDA_CONFIG_DEFAULTCONF_SEQUENCE_MASK);
8712a1ad637SFrançois Tigeot 		} else if (strcmp(key, "as") == 0) {
8722a1ad637SFrançois Tigeot 			config &= ~HDA_CONFIG_DEFAULTCONF_ASSOCIATION_MASK;
8732a1ad637SFrançois Tigeot 			config |= ((ival << HDA_CONFIG_DEFAULTCONF_ASSOCIATION_SHIFT) &
8742a1ad637SFrançois Tigeot 			    HDA_CONFIG_DEFAULTCONF_ASSOCIATION_MASK);
8752a1ad637SFrançois Tigeot 		} else if (strcmp(key, "misc") == 0) {
8762a1ad637SFrançois Tigeot 			config &= ~HDA_CONFIG_DEFAULTCONF_MISC_MASK;
8772a1ad637SFrançois Tigeot 			config |= ((ival << HDA_CONFIG_DEFAULTCONF_MISC_SHIFT) &
8782a1ad637SFrançois Tigeot 			    HDA_CONFIG_DEFAULTCONF_MISC_MASK);
8792a1ad637SFrançois Tigeot 		} else if (strcmp(key, "color") == 0) {
8802a1ad637SFrançois Tigeot 			config &= ~HDA_CONFIG_DEFAULTCONF_COLOR_MASK;
8812a1ad637SFrançois Tigeot 			if (bad[0] == 0) {
8822a1ad637SFrançois Tigeot 				config |= ((ival << HDA_CONFIG_DEFAULTCONF_COLOR_SHIFT) &
8832a1ad637SFrançois Tigeot 				    HDA_CONFIG_DEFAULTCONF_COLOR_MASK);
8842a1ad637SFrançois Tigeot 			};
8852a1ad637SFrançois Tigeot 			for (i = 0; i < 16; i++) {
8862a1ad637SFrançois Tigeot 				if (strcasecmp(HDA_COLORS[i], value) == 0) {
8872a1ad637SFrançois Tigeot 					config |= (i << HDA_CONFIG_DEFAULTCONF_COLOR_SHIFT);
8882a1ad637SFrançois Tigeot 					break;
8892a1ad637SFrançois Tigeot 				}
8902a1ad637SFrançois Tigeot 			}
8912a1ad637SFrançois Tigeot 		} else if (strcmp(key, "ctype") == 0) {
8922a1ad637SFrançois Tigeot 			config &= ~HDA_CONFIG_DEFAULTCONF_CONNECTION_TYPE_MASK;
8932a1ad637SFrançois Tigeot 			if (bad[0] == 0) {
8942a1ad637SFrançois Tigeot 			config |= ((ival << HDA_CONFIG_DEFAULTCONF_CONNECTION_TYPE_SHIFT) &
8952a1ad637SFrançois Tigeot 			    HDA_CONFIG_DEFAULTCONF_CONNECTION_TYPE_MASK);
8962a1ad637SFrançois Tigeot 			}
8972a1ad637SFrançois Tigeot 			for (i = 0; i < 16; i++) {
8982a1ad637SFrançois Tigeot 				if (strcasecmp(HDA_CONNECTORS[i], value) == 0) {
8992a1ad637SFrançois Tigeot 					config |= (i << HDA_CONFIG_DEFAULTCONF_CONNECTION_TYPE_SHIFT);
9002a1ad637SFrançois Tigeot 					break;
9012a1ad637SFrançois Tigeot 				}
9022a1ad637SFrançois Tigeot 			}
9032a1ad637SFrançois Tigeot 		} else if (strcmp(key, "device") == 0) {
9042a1ad637SFrançois Tigeot 			config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK;
9052a1ad637SFrançois Tigeot 			if (bad[0] == 0) {
9062a1ad637SFrançois Tigeot 				config |= ((ival << HDA_CONFIG_DEFAULTCONF_DEVICE_SHIFT) &
9072a1ad637SFrançois Tigeot 				    HDA_CONFIG_DEFAULTCONF_DEVICE_MASK);
9082a1ad637SFrançois Tigeot 				continue;
9092a1ad637SFrançois Tigeot 			};
9102a1ad637SFrançois Tigeot 			for (i = 0; i < 16; i++) {
9112a1ad637SFrançois Tigeot 				if (strcasecmp(HDA_DEVS[i], value) == 0) {
9122a1ad637SFrançois Tigeot 					config |= (i << HDA_CONFIG_DEFAULTCONF_DEVICE_SHIFT);
9132a1ad637SFrançois Tigeot 					break;
9142a1ad637SFrançois Tigeot 				}
9152a1ad637SFrançois Tigeot 			}
9162a1ad637SFrançois Tigeot 		} else if (strcmp(key, "loc") == 0) {
9172a1ad637SFrançois Tigeot 			config &= ~HDA_CONFIG_DEFAULTCONF_LOCATION_MASK;
9182a1ad637SFrançois Tigeot 			if (bad[0] == 0) {
9192a1ad637SFrançois Tigeot 				config |= ((ival << HDA_CONFIG_DEFAULTCONF_LOCATION_SHIFT) &
9202a1ad637SFrançois Tigeot 				    HDA_CONFIG_DEFAULTCONF_LOCATION_MASK);
9212a1ad637SFrançois Tigeot 				continue;
9222a1ad637SFrançois Tigeot 			}
9232a1ad637SFrançois Tigeot 			for (i = 0; i < 64; i++) {
9242a1ad637SFrançois Tigeot 				if (strcasecmp(HDA_LOCS[i], value) == 0) {
9252a1ad637SFrançois Tigeot 					config |= (i << HDA_CONFIG_DEFAULTCONF_LOCATION_SHIFT);
9262a1ad637SFrançois Tigeot 					break;
9272a1ad637SFrançois Tigeot 				}
9282a1ad637SFrançois Tigeot 			}
9292a1ad637SFrançois Tigeot 		} else if (strcmp(key, "conn") == 0) {
9302a1ad637SFrançois Tigeot 			config &= ~HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK;
9312a1ad637SFrançois Tigeot 			if (bad[0] == 0) {
9322a1ad637SFrançois Tigeot 				config |= ((ival << HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_SHIFT) &
9332a1ad637SFrançois Tigeot 				    HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK);
9342a1ad637SFrançois Tigeot 				continue;
9352a1ad637SFrançois Tigeot 			};
9362a1ad637SFrançois Tigeot 			for (i = 0; i < 4; i++) {
9372a1ad637SFrançois Tigeot 				if (strcasecmp(HDA_CONNS[i], value) == 0) {
9382a1ad637SFrançois Tigeot 					config |= (i << HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_SHIFT);
9392a1ad637SFrançois Tigeot 					break;
9402a1ad637SFrançois Tigeot 				}
9412a1ad637SFrançois Tigeot 			}
9422a1ad637SFrançois Tigeot 		}
9432a1ad637SFrançois Tigeot 	}
9442a1ad637SFrançois Tigeot 	return (config);
9452a1ad637SFrançois Tigeot }
9462a1ad637SFrançois Tigeot 
9472a1ad637SFrançois Tigeot uint32_t
hdaa_gpio_patch(uint32_t gpio,const char * str)9482a1ad637SFrançois Tigeot hdaa_gpio_patch(uint32_t gpio, const char *str)
9492a1ad637SFrançois Tigeot {
9502a1ad637SFrançois Tigeot 	char buf[256];
9512a1ad637SFrançois Tigeot 	char *key, *value, *rest;
9522a1ad637SFrançois Tigeot 	int ikey, i;
9532a1ad637SFrançois Tigeot 
9542a1ad637SFrançois Tigeot 	strlcpy(buf, str, sizeof(buf));
9552a1ad637SFrançois Tigeot 	rest = buf;
9562a1ad637SFrançois Tigeot 	while ((key = strsep(&rest, "=")) != NULL) {
9572a1ad637SFrançois Tigeot 		value = strsep(&rest, " \t");
9582a1ad637SFrançois Tigeot 		if (value == NULL)
9592a1ad637SFrançois Tigeot 			break;
9602a1ad637SFrançois Tigeot 		ikey = strtol(key, NULL, 10);
9612a1ad637SFrançois Tigeot 		if (ikey < 0 || ikey > 7)
9622a1ad637SFrançois Tigeot 			continue;
9632a1ad637SFrançois Tigeot 		for (i = 0; i < 7; i++) {
9642a1ad637SFrançois Tigeot 			if (strcasecmp(HDA_GPIO_ACTIONS[i], value) == 0) {
9652a1ad637SFrançois Tigeot 				gpio &= ~HDAA_GPIO_MASK(ikey);
9662a1ad637SFrançois Tigeot 				gpio |= i << HDAA_GPIO_SHIFT(ikey);
9672a1ad637SFrançois Tigeot 				break;
9682a1ad637SFrançois Tigeot 			}
9692a1ad637SFrançois Tigeot 		}
9702a1ad637SFrançois Tigeot 	}
9712a1ad637SFrançois Tigeot 	return (gpio);
9722a1ad637SFrançois Tigeot }
9732a1ad637SFrançois Tigeot 
9742a1ad637SFrançois Tigeot static void
hdaa_local_patch_pin(struct hdaa_widget * w)9752a1ad637SFrançois Tigeot hdaa_local_patch_pin(struct hdaa_widget *w)
9762a1ad637SFrançois Tigeot {
9772a1ad637SFrançois Tigeot 	device_t dev = w->devinfo->dev;
9782a1ad637SFrançois Tigeot 	const char *res = NULL;
9792a1ad637SFrançois Tigeot 	uint32_t config, orig;
9802a1ad637SFrançois Tigeot 	char buf[32];
9812a1ad637SFrançois Tigeot 
9822a1ad637SFrançois Tigeot 	config = orig = w->wclass.pin.config;
98367931cc4SFrançois Tigeot 	ksnprintf(buf, sizeof(buf), "cad%u.nid%u.config",
9842a1ad637SFrançois Tigeot 	    hda_get_codec_id(dev), w->nid);
9852a1ad637SFrançois Tigeot 	if (resource_string_value(device_get_name(
9862a1ad637SFrançois Tigeot 	    device_get_parent(device_get_parent(dev))),
9872a1ad637SFrançois Tigeot 	    device_get_unit(device_get_parent(device_get_parent(dev))),
9882a1ad637SFrançois Tigeot 	    buf, &res) == 0) {
9892a1ad637SFrançois Tigeot 		if (strncmp(res, "0x", 2) == 0) {
9902a1ad637SFrançois Tigeot 			config = strtol(res + 2, NULL, 16);
9912a1ad637SFrançois Tigeot 		} else {
9922a1ad637SFrançois Tigeot 			config = hdaa_widget_pin_patch(config, res);
9932a1ad637SFrançois Tigeot 		}
9942a1ad637SFrançois Tigeot 	}
99567931cc4SFrançois Tigeot 	ksnprintf(buf, sizeof(buf), "nid%u.config", w->nid);
9962a1ad637SFrançois Tigeot 	if (resource_string_value(device_get_name(dev), device_get_unit(dev),
9972a1ad637SFrançois Tigeot 	    buf, &res) == 0) {
9982a1ad637SFrançois Tigeot 		if (strncmp(res, "0x", 2) == 0) {
9992a1ad637SFrançois Tigeot 			config = strtol(res + 2, NULL, 16);
10002a1ad637SFrançois Tigeot 		} else {
10012a1ad637SFrançois Tigeot 			config = hdaa_widget_pin_patch(config, res);
10022a1ad637SFrançois Tigeot 		}
10032a1ad637SFrançois Tigeot 	}
10042a1ad637SFrançois Tigeot 	HDA_BOOTVERBOSE(
10052a1ad637SFrançois Tigeot 		if (config != orig)
10062a1ad637SFrançois Tigeot 			device_printf(w->devinfo->dev,
10072a1ad637SFrançois Tigeot 			    "Patching pin config nid=%u 0x%08x -> 0x%08x\n",
10082a1ad637SFrançois Tigeot 			    w->nid, orig, config);
10092a1ad637SFrançois Tigeot 	);
10102a1ad637SFrançois Tigeot 	w->wclass.pin.newconf = w->wclass.pin.config = config;
10112a1ad637SFrançois Tigeot }
10122a1ad637SFrançois Tigeot 
10132a1ad637SFrançois Tigeot static void
hdaa_dump_audio_formats_sb(struct sbuf * sb,uint32_t fcap,uint32_t pcmcap)10142a1ad637SFrançois Tigeot hdaa_dump_audio_formats_sb(struct sbuf *sb, uint32_t fcap, uint32_t pcmcap)
10152a1ad637SFrançois Tigeot {
10162a1ad637SFrançois Tigeot 	uint32_t cap;
10172a1ad637SFrançois Tigeot 
10182a1ad637SFrançois Tigeot 	cap = fcap;
10192a1ad637SFrançois Tigeot 	if (cap != 0) {
10202a1ad637SFrançois Tigeot 		sbuf_printf(sb, "     Stream cap: 0x%08x", cap);
10212a1ad637SFrançois Tigeot 		if (HDA_PARAM_SUPP_STREAM_FORMATS_AC3(cap))
10222a1ad637SFrançois Tigeot 			sbuf_printf(sb, " AC3");
10232a1ad637SFrançois Tigeot 		if (HDA_PARAM_SUPP_STREAM_FORMATS_FLOAT32(cap))
10242a1ad637SFrançois Tigeot 			sbuf_printf(sb, " FLOAT32");
10252a1ad637SFrançois Tigeot 		if (HDA_PARAM_SUPP_STREAM_FORMATS_PCM(cap))
10262a1ad637SFrançois Tigeot 			sbuf_printf(sb, " PCM");
10272a1ad637SFrançois Tigeot 		sbuf_printf(sb, "\n");
10282a1ad637SFrançois Tigeot 	}
10292a1ad637SFrançois Tigeot 	cap = pcmcap;
10302a1ad637SFrançois Tigeot 	if (cap != 0) {
10312a1ad637SFrançois Tigeot 		sbuf_printf(sb, "        PCM cap: 0x%08x", cap);
10322a1ad637SFrançois Tigeot 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_8BIT(cap))
10332a1ad637SFrançois Tigeot 			sbuf_printf(sb, " 8");
10342a1ad637SFrançois Tigeot 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_16BIT(cap))
10352a1ad637SFrançois Tigeot 			sbuf_printf(sb, " 16");
10362a1ad637SFrançois Tigeot 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_20BIT(cap))
10372a1ad637SFrançois Tigeot 			sbuf_printf(sb, " 20");
10382a1ad637SFrançois Tigeot 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_24BIT(cap))
10392a1ad637SFrançois Tigeot 			sbuf_printf(sb, " 24");
10402a1ad637SFrançois Tigeot 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_32BIT(cap))
10412a1ad637SFrançois Tigeot 			sbuf_printf(sb, " 32");
10422a1ad637SFrançois Tigeot 		sbuf_printf(sb, " bits,");
10432a1ad637SFrançois Tigeot 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_8KHZ(cap))
10442a1ad637SFrançois Tigeot 			sbuf_printf(sb, " 8");
10452a1ad637SFrançois Tigeot 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_11KHZ(cap))
10462a1ad637SFrançois Tigeot 			sbuf_printf(sb, " 11");
10472a1ad637SFrançois Tigeot 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_16KHZ(cap))
10482a1ad637SFrançois Tigeot 			sbuf_printf(sb, " 16");
10492a1ad637SFrançois Tigeot 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_22KHZ(cap))
10502a1ad637SFrançois Tigeot 			sbuf_printf(sb, " 22");
10512a1ad637SFrançois Tigeot 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_32KHZ(cap))
10522a1ad637SFrançois Tigeot 			sbuf_printf(sb, " 32");
10532a1ad637SFrançois Tigeot 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_44KHZ(cap))
10542a1ad637SFrançois Tigeot 			sbuf_printf(sb, " 44");
10552a1ad637SFrançois Tigeot 		sbuf_printf(sb, " 48");
10562a1ad637SFrançois Tigeot 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_88KHZ(cap))
10572a1ad637SFrançois Tigeot 			sbuf_printf(sb, " 88");
10582a1ad637SFrançois Tigeot 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_96KHZ(cap))
10592a1ad637SFrançois Tigeot 			sbuf_printf(sb, " 96");
10602a1ad637SFrançois Tigeot 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_176KHZ(cap))
10612a1ad637SFrançois Tigeot 			sbuf_printf(sb, " 176");
10622a1ad637SFrançois Tigeot 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_192KHZ(cap))
10632a1ad637SFrançois Tigeot 			sbuf_printf(sb, " 192");
10642a1ad637SFrançois Tigeot 		sbuf_printf(sb, " KHz\n");
10652a1ad637SFrançois Tigeot 	}
10662a1ad637SFrançois Tigeot }
10672a1ad637SFrançois Tigeot 
10682a1ad637SFrançois Tigeot static void
hdaa_dump_pin_sb(struct sbuf * sb,struct hdaa_widget * w)10692a1ad637SFrançois Tigeot hdaa_dump_pin_sb(struct sbuf *sb, struct hdaa_widget *w)
10702a1ad637SFrançois Tigeot {
10712a1ad637SFrançois Tigeot 	uint32_t pincap, conf;
10722a1ad637SFrançois Tigeot 
10732a1ad637SFrançois Tigeot 	pincap = w->wclass.pin.cap;
10742a1ad637SFrançois Tigeot 
10752a1ad637SFrançois Tigeot 	sbuf_printf(sb, "        Pin cap: 0x%08x", pincap);
10762a1ad637SFrançois Tigeot 	if (HDA_PARAM_PIN_CAP_IMP_SENSE_CAP(pincap))
10772a1ad637SFrançois Tigeot 		sbuf_printf(sb, " ISC");
10782a1ad637SFrançois Tigeot 	if (HDA_PARAM_PIN_CAP_TRIGGER_REQD(pincap))
10792a1ad637SFrançois Tigeot 		sbuf_printf(sb, " TRQD");
10802a1ad637SFrançois Tigeot 	if (HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP(pincap))
10812a1ad637SFrançois Tigeot 		sbuf_printf(sb, " PDC");
10822a1ad637SFrançois Tigeot 	if (HDA_PARAM_PIN_CAP_HEADPHONE_CAP(pincap))
10832a1ad637SFrançois Tigeot 		sbuf_printf(sb, " HP");
10842a1ad637SFrançois Tigeot 	if (HDA_PARAM_PIN_CAP_OUTPUT_CAP(pincap))
10852a1ad637SFrançois Tigeot 		sbuf_printf(sb, " OUT");
10862a1ad637SFrançois Tigeot 	if (HDA_PARAM_PIN_CAP_INPUT_CAP(pincap))
10872a1ad637SFrançois Tigeot 		sbuf_printf(sb, " IN");
10882a1ad637SFrançois Tigeot 	if (HDA_PARAM_PIN_CAP_BALANCED_IO_PINS(pincap))
10892a1ad637SFrançois Tigeot 		sbuf_printf(sb, " BAL");
10902a1ad637SFrançois Tigeot 	if (HDA_PARAM_PIN_CAP_HDMI(pincap))
10912a1ad637SFrançois Tigeot 		sbuf_printf(sb, " HDMI");
10922a1ad637SFrançois Tigeot 	if (HDA_PARAM_PIN_CAP_VREF_CTRL(pincap)) {
10932a1ad637SFrançois Tigeot 		sbuf_printf(sb, " VREF[");
10942a1ad637SFrançois Tigeot 		if (HDA_PARAM_PIN_CAP_VREF_CTRL_50(pincap))
10952a1ad637SFrançois Tigeot 			sbuf_printf(sb, " 50");
10962a1ad637SFrançois Tigeot 		if (HDA_PARAM_PIN_CAP_VREF_CTRL_80(pincap))
10972a1ad637SFrançois Tigeot 			sbuf_printf(sb, " 80");
10982a1ad637SFrançois Tigeot 		if (HDA_PARAM_PIN_CAP_VREF_CTRL_100(pincap))
10992a1ad637SFrançois Tigeot 			sbuf_printf(sb, " 100");
11002a1ad637SFrançois Tigeot 		if (HDA_PARAM_PIN_CAP_VREF_CTRL_GROUND(pincap))
11012a1ad637SFrançois Tigeot 			sbuf_printf(sb, " GROUND");
11022a1ad637SFrançois Tigeot 		if (HDA_PARAM_PIN_CAP_VREF_CTRL_HIZ(pincap))
11032a1ad637SFrançois Tigeot 			sbuf_printf(sb, " HIZ");
11042a1ad637SFrançois Tigeot 		sbuf_printf(sb, " ]");
11052a1ad637SFrançois Tigeot 	}
11062a1ad637SFrançois Tigeot 	if (HDA_PARAM_PIN_CAP_EAPD_CAP(pincap))
11072a1ad637SFrançois Tigeot 		sbuf_printf(sb, " EAPD");
11082a1ad637SFrançois Tigeot 	if (HDA_PARAM_PIN_CAP_DP(pincap))
11092a1ad637SFrançois Tigeot 		sbuf_printf(sb, " DP");
11102a1ad637SFrançois Tigeot 	if (HDA_PARAM_PIN_CAP_HBR(pincap))
11112a1ad637SFrançois Tigeot 		sbuf_printf(sb, " HBR");
11122a1ad637SFrançois Tigeot 	sbuf_printf(sb, "\n");
11132a1ad637SFrançois Tigeot 	conf = w->wclass.pin.config;
11142a1ad637SFrançois Tigeot 	sbuf_printf(sb, "     Pin config: 0x%08x", conf);
11152a1ad637SFrançois Tigeot 	sbuf_printf(sb, " as=%d seq=%d "
11162a1ad637SFrançois Tigeot 	    "device=%s conn=%s ctype=%s loc=%s color=%s misc=%d\n",
11172a1ad637SFrançois Tigeot 	    HDA_CONFIG_DEFAULTCONF_ASSOCIATION(conf),
11182a1ad637SFrançois Tigeot 	    HDA_CONFIG_DEFAULTCONF_SEQUENCE(conf),
11192a1ad637SFrançois Tigeot 	    HDA_DEVS[HDA_CONFIG_DEFAULTCONF_DEVICE(conf)],
11202a1ad637SFrançois Tigeot 	    HDA_CONNS[HDA_CONFIG_DEFAULTCONF_CONNECTIVITY(conf)],
11212a1ad637SFrançois Tigeot 	    HDA_CONNECTORS[HDA_CONFIG_DEFAULTCONF_CONNECTION_TYPE(conf)],
11222a1ad637SFrançois Tigeot 	    HDA_LOCS[HDA_CONFIG_DEFAULTCONF_LOCATION(conf)],
11232a1ad637SFrançois Tigeot 	    HDA_COLORS[HDA_CONFIG_DEFAULTCONF_COLOR(conf)],
11242a1ad637SFrançois Tigeot 	    HDA_CONFIG_DEFAULTCONF_MISC(conf));
11252a1ad637SFrançois Tigeot 	sbuf_printf(sb, "    Pin control: 0x%08x", w->wclass.pin.ctrl);
11262a1ad637SFrançois Tigeot 	if (w->wclass.pin.ctrl & HDA_CMD_SET_PIN_WIDGET_CTRL_HPHN_ENABLE)
11272a1ad637SFrançois Tigeot 		sbuf_printf(sb, " HP");
11282a1ad637SFrançois Tigeot 	if (w->wclass.pin.ctrl & HDA_CMD_SET_PIN_WIDGET_CTRL_IN_ENABLE)
11292a1ad637SFrançois Tigeot 		sbuf_printf(sb, " IN");
11302a1ad637SFrançois Tigeot 	if (w->wclass.pin.ctrl & HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE)
11312a1ad637SFrançois Tigeot 		sbuf_printf(sb, " OUT");
11322a1ad637SFrançois Tigeot 	if (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap)) {
11332a1ad637SFrançois Tigeot 		if ((w->wclass.pin.ctrl &
11342a1ad637SFrançois Tigeot 		    HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE_MASK) == 0x03)
11352a1ad637SFrançois Tigeot 			sbuf_printf(sb, " HBR");
11362a1ad637SFrançois Tigeot 		else if ((w->wclass.pin.ctrl &
11372a1ad637SFrançois Tigeot 		    HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE_MASK) != 0)
11382a1ad637SFrançois Tigeot 			sbuf_printf(sb, " EPTs");
11392a1ad637SFrançois Tigeot 	} else {
11402a1ad637SFrançois Tigeot 		if ((w->wclass.pin.ctrl &
11412a1ad637SFrançois Tigeot 		    HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE_MASK) != 0)
11422a1ad637SFrançois Tigeot 			sbuf_printf(sb, " VREFs");
11432a1ad637SFrançois Tigeot 	}
11442a1ad637SFrançois Tigeot 	sbuf_printf(sb, "\n");
11452a1ad637SFrançois Tigeot }
11462a1ad637SFrançois Tigeot 
11472a1ad637SFrançois Tigeot static void
hdaa_dump_amp_sb(struct sbuf * sb,uint32_t cap,const char * banner)11482a1ad637SFrançois Tigeot hdaa_dump_amp_sb(struct sbuf *sb, uint32_t cap, const char *banner)
11492a1ad637SFrançois Tigeot {
11502a1ad637SFrançois Tigeot 	int offset, size, step;
11512a1ad637SFrançois Tigeot 
11522a1ad637SFrançois Tigeot 	offset = HDA_PARAM_OUTPUT_AMP_CAP_OFFSET(cap);
11532a1ad637SFrançois Tigeot 	size = HDA_PARAM_OUTPUT_AMP_CAP_STEPSIZE(cap);
11542a1ad637SFrançois Tigeot 	step = HDA_PARAM_OUTPUT_AMP_CAP_NUMSTEPS(cap);
11552a1ad637SFrançois Tigeot 	sbuf_printf(sb, "     %s amp: 0x%08x "
11562a1ad637SFrançois Tigeot 	    "mute=%d step=%d size=%d offset=%d (%+d/%+ddB)\n",
11572a1ad637SFrançois Tigeot 	    banner, cap,
11582a1ad637SFrançois Tigeot 	    HDA_PARAM_OUTPUT_AMP_CAP_MUTE_CAP(cap),
11592a1ad637SFrançois Tigeot 	    step, size, offset,
11602a1ad637SFrançois Tigeot 	    ((0 - offset) * (size + 1)) / 4,
11612a1ad637SFrançois Tigeot 	    ((step - offset) * (size + 1)) / 4);
11622a1ad637SFrançois Tigeot }
11632a1ad637SFrançois Tigeot 
11642a1ad637SFrançois Tigeot 
11652a1ad637SFrançois Tigeot static int
hdaa_sysctl_caps(SYSCTL_HANDLER_ARGS)11662a1ad637SFrançois Tigeot hdaa_sysctl_caps(SYSCTL_HANDLER_ARGS)
11672a1ad637SFrançois Tigeot {
11682a1ad637SFrançois Tigeot 	struct hdaa_devinfo *devinfo;
11692a1ad637SFrançois Tigeot 	struct hdaa_widget *w, *cw;
11702a1ad637SFrançois Tigeot 	struct sbuf sb;
11712a1ad637SFrançois Tigeot 	char buf[64];
11722a1ad637SFrançois Tigeot 	int error, j;
11732a1ad637SFrançois Tigeot 
11742a1ad637SFrançois Tigeot 	w = (struct hdaa_widget *)oidp->oid_arg1;
11752a1ad637SFrançois Tigeot 	devinfo = w->devinfo;
11762a1ad637SFrançois Tigeot 	sbuf_new_for_sysctl(&sb, NULL, 256, req);
11772a1ad637SFrançois Tigeot 
11782a1ad637SFrançois Tigeot 	sbuf_printf(&sb, "%s%s\n", w->name,
11792a1ad637SFrançois Tigeot 	    (w->enable == 0) ? " [DISABLED]" : "");
11802a1ad637SFrançois Tigeot 	sbuf_printf(&sb, "     Widget cap: 0x%08x",
11812a1ad637SFrançois Tigeot 	    w->param.widget_cap);
11822a1ad637SFrançois Tigeot 	if (w->param.widget_cap & 0x0ee1) {
11832a1ad637SFrançois Tigeot 		if (HDA_PARAM_AUDIO_WIDGET_CAP_LR_SWAP(w->param.widget_cap))
11842a1ad637SFrançois Tigeot 		    sbuf_printf(&sb, " LRSWAP");
11852a1ad637SFrançois Tigeot 		if (HDA_PARAM_AUDIO_WIDGET_CAP_POWER_CTRL(w->param.widget_cap))
11862a1ad637SFrançois Tigeot 		    sbuf_printf(&sb, " PWR");
11872a1ad637SFrançois Tigeot 		if (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap))
11882a1ad637SFrançois Tigeot 		    sbuf_printf(&sb, " DIGITAL");
11892a1ad637SFrançois Tigeot 		if (HDA_PARAM_AUDIO_WIDGET_CAP_UNSOL_CAP(w->param.widget_cap))
11902a1ad637SFrançois Tigeot 		    sbuf_printf(&sb, " UNSOL");
11912a1ad637SFrançois Tigeot 		if (HDA_PARAM_AUDIO_WIDGET_CAP_PROC_WIDGET(w->param.widget_cap))
11922a1ad637SFrançois Tigeot 		    sbuf_printf(&sb, " PROC");
11932a1ad637SFrançois Tigeot 		if (HDA_PARAM_AUDIO_WIDGET_CAP_STRIPE(w->param.widget_cap))
11942a1ad637SFrançois Tigeot 		    sbuf_printf(&sb, " STRIPE(x%d)",
11952a1ad637SFrançois Tigeot 			1 << (fls(w->wclass.conv.stripecap) - 1));
11962a1ad637SFrançois Tigeot 		j = HDA_PARAM_AUDIO_WIDGET_CAP_CC(w->param.widget_cap);
11972a1ad637SFrançois Tigeot 		if (j == 1)
11982a1ad637SFrançois Tigeot 		    sbuf_printf(&sb, " STEREO");
11992a1ad637SFrançois Tigeot 		else if (j > 1)
12002a1ad637SFrançois Tigeot 		    sbuf_printf(&sb, " %dCH", j + 1);
12012a1ad637SFrançois Tigeot 	}
12022a1ad637SFrançois Tigeot 	sbuf_printf(&sb, "\n");
12032a1ad637SFrançois Tigeot 	if (w->bindas != -1) {
12042a1ad637SFrançois Tigeot 		sbuf_printf(&sb, "    Association: %d (0x%04x)\n",
12052a1ad637SFrançois Tigeot 		    w->bindas, w->bindseqmask);
12062a1ad637SFrançois Tigeot 	}
12072a1ad637SFrançois Tigeot 	if (w->ossmask != 0 || w->ossdev >= 0) {
12082a1ad637SFrançois Tigeot 		sbuf_printf(&sb, "            OSS: %s",
12092a1ad637SFrançois Tigeot 		    hdaa_audio_ctl_ossmixer_mask2allname(w->ossmask, buf, sizeof(buf)));
12102a1ad637SFrançois Tigeot 		if (w->ossdev >= 0)
12112a1ad637SFrançois Tigeot 		    sbuf_printf(&sb, " (%s)", ossnames[w->ossdev]);
12122a1ad637SFrançois Tigeot 		sbuf_printf(&sb, "\n");
12132a1ad637SFrançois Tigeot 	}
12142a1ad637SFrançois Tigeot 	if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT ||
12152a1ad637SFrançois Tigeot 	    w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT) {
12162a1ad637SFrançois Tigeot 		hdaa_dump_audio_formats_sb(&sb,
12172a1ad637SFrançois Tigeot 		    w->param.supp_stream_formats,
12182a1ad637SFrançois Tigeot 		    w->param.supp_pcm_size_rate);
12192a1ad637SFrançois Tigeot 	} else if (w->type ==
12202a1ad637SFrançois Tigeot 	    HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX || w->waspin)
12212a1ad637SFrançois Tigeot 		hdaa_dump_pin_sb(&sb, w);
12222a1ad637SFrançois Tigeot 	if (w->param.eapdbtl != HDA_INVALID) {
12232a1ad637SFrançois Tigeot 		sbuf_printf(&sb, "           EAPD: 0x%08x%s%s%s\n",
12242a1ad637SFrançois Tigeot 		    w->param.eapdbtl,
12252a1ad637SFrançois Tigeot 		    (w->param.eapdbtl & HDA_CMD_SET_EAPD_BTL_ENABLE_LR_SWAP) ?
12262a1ad637SFrançois Tigeot 		     " LRSWAP" : "",
12272a1ad637SFrançois Tigeot 		    (w->param.eapdbtl & HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD) ?
12282a1ad637SFrançois Tigeot 		     " EAPD" : "",
12292a1ad637SFrançois Tigeot 		    (w->param.eapdbtl & HDA_CMD_SET_EAPD_BTL_ENABLE_BTL) ?
12302a1ad637SFrançois Tigeot 		     " BTL" : "");
12312a1ad637SFrançois Tigeot 	}
12322a1ad637SFrançois Tigeot 	if (HDA_PARAM_AUDIO_WIDGET_CAP_OUT_AMP(w->param.widget_cap) &&
12332a1ad637SFrançois Tigeot 	    w->param.outamp_cap != 0)
12342a1ad637SFrançois Tigeot 		hdaa_dump_amp_sb(&sb, w->param.outamp_cap, "Output");
12352a1ad637SFrançois Tigeot 	if (HDA_PARAM_AUDIO_WIDGET_CAP_IN_AMP(w->param.widget_cap) &&
12362a1ad637SFrançois Tigeot 	    w->param.inamp_cap != 0)
12372a1ad637SFrançois Tigeot 		hdaa_dump_amp_sb(&sb, w->param.inamp_cap, " Input");
12382a1ad637SFrançois Tigeot 	if (w->nconns > 0)
12392a1ad637SFrançois Tigeot 		sbuf_printf(&sb, "    Connections: %d\n", w->nconns);
12402a1ad637SFrançois Tigeot 	for (j = 0; j < w->nconns; j++) {
12412a1ad637SFrançois Tigeot 		cw = hdaa_widget_get(devinfo, w->conns[j]);
12422a1ad637SFrançois Tigeot 		sbuf_printf(&sb, "          + %s<- nid=%d [%s]",
12432a1ad637SFrançois Tigeot 		    (w->connsenable[j] == 0)?"[DISABLED] ":"",
12442a1ad637SFrançois Tigeot 		    w->conns[j], (cw == NULL) ? "GHOST!" : cw->name);
12452a1ad637SFrançois Tigeot 		if (cw == NULL)
12462a1ad637SFrançois Tigeot 			sbuf_printf(&sb, " [UNKNOWN]");
12472a1ad637SFrançois Tigeot 		else if (cw->enable == 0)
12482a1ad637SFrançois Tigeot 			sbuf_printf(&sb, " [DISABLED]");
12492a1ad637SFrançois Tigeot 		if (w->nconns > 1 && w->selconn == j && w->type !=
12502a1ad637SFrançois Tigeot 		    HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER)
12512a1ad637SFrançois Tigeot 			sbuf_printf(&sb, " (selected)");
12522a1ad637SFrançois Tigeot 		sbuf_printf(&sb, "\n");
12532a1ad637SFrançois Tigeot 	}
12542a1ad637SFrançois Tigeot 	error = sbuf_finish(&sb);
12552a1ad637SFrançois Tigeot 	sbuf_delete(&sb);
12562a1ad637SFrançois Tigeot 	return (error);
12572a1ad637SFrançois Tigeot }
12582a1ad637SFrançois Tigeot 
12592a1ad637SFrançois Tigeot static int
hdaa_sysctl_config(SYSCTL_HANDLER_ARGS)12602a1ad637SFrançois Tigeot hdaa_sysctl_config(SYSCTL_HANDLER_ARGS)
12612a1ad637SFrançois Tigeot {
12622a1ad637SFrançois Tigeot 	char buf[256];
12632a1ad637SFrançois Tigeot 	int error;
12642a1ad637SFrançois Tigeot 	uint32_t conf;
12652a1ad637SFrançois Tigeot 
12662a1ad637SFrançois Tigeot 	conf = *(uint32_t *)oidp->oid_arg1;
126767931cc4SFrançois Tigeot 	ksnprintf(buf, sizeof(buf), "0x%08x as=%d seq=%d "
12682a1ad637SFrançois Tigeot 	    "device=%s conn=%s ctype=%s loc=%s color=%s misc=%d",
12692a1ad637SFrançois Tigeot 	    conf,
12702a1ad637SFrançois Tigeot 	    HDA_CONFIG_DEFAULTCONF_ASSOCIATION(conf),
12712a1ad637SFrançois Tigeot 	    HDA_CONFIG_DEFAULTCONF_SEQUENCE(conf),
12722a1ad637SFrançois Tigeot 	    HDA_DEVS[HDA_CONFIG_DEFAULTCONF_DEVICE(conf)],
12732a1ad637SFrançois Tigeot 	    HDA_CONNS[HDA_CONFIG_DEFAULTCONF_CONNECTIVITY(conf)],
12742a1ad637SFrançois Tigeot 	    HDA_CONNECTORS[HDA_CONFIG_DEFAULTCONF_CONNECTION_TYPE(conf)],
12752a1ad637SFrançois Tigeot 	    HDA_LOCS[HDA_CONFIG_DEFAULTCONF_LOCATION(conf)],
12762a1ad637SFrançois Tigeot 	    HDA_COLORS[HDA_CONFIG_DEFAULTCONF_COLOR(conf)],
12772a1ad637SFrançois Tigeot 	    HDA_CONFIG_DEFAULTCONF_MISC(conf));
12782a1ad637SFrançois Tigeot 	error = sysctl_handle_string(oidp, buf, sizeof(buf), req);
12792a1ad637SFrançois Tigeot 	if (error != 0 || req->newptr == NULL)
12802a1ad637SFrançois Tigeot 		return (error);
12812a1ad637SFrançois Tigeot 	if (strncmp(buf, "0x", 2) == 0)
12822a1ad637SFrançois Tigeot 		conf = strtol(buf + 2, NULL, 16);
12832a1ad637SFrançois Tigeot 	else
12842a1ad637SFrançois Tigeot 		conf = hdaa_widget_pin_patch(conf, buf);
12852a1ad637SFrançois Tigeot 	*(uint32_t *)oidp->oid_arg1 = conf;
12862a1ad637SFrançois Tigeot 	return (0);
12872a1ad637SFrançois Tigeot }
12882a1ad637SFrançois Tigeot 
12892a1ad637SFrançois Tigeot static void
hdaa_config_fetch(const char * str,uint32_t * on,uint32_t * off)12902a1ad637SFrançois Tigeot hdaa_config_fetch(const char *str, uint32_t *on, uint32_t *off)
12912a1ad637SFrançois Tigeot {
12922a1ad637SFrançois Tigeot 	int i = 0, j, k, len, inv;
12932a1ad637SFrançois Tigeot 
12942a1ad637SFrançois Tigeot 	for (;;) {
12952a1ad637SFrançois Tigeot 		while (str[i] != '\0' &&
12962a1ad637SFrançois Tigeot 		    (str[i] == ',' || isspace(str[i]) != 0))
12972a1ad637SFrançois Tigeot 			i++;
12982a1ad637SFrançois Tigeot 		if (str[i] == '\0')
12992a1ad637SFrançois Tigeot 			return;
13002a1ad637SFrançois Tigeot 		j = i;
13012a1ad637SFrançois Tigeot 		while (str[j] != '\0' &&
13022a1ad637SFrançois Tigeot 		    !(str[j] == ',' || isspace(str[j]) != 0))
13032a1ad637SFrançois Tigeot 			j++;
13042a1ad637SFrançois Tigeot 		len = j - i;
13052a1ad637SFrançois Tigeot 		if (len > 2 && strncmp(str + i, "no", 2) == 0)
13062a1ad637SFrançois Tigeot 			inv = 2;
13072a1ad637SFrançois Tigeot 		else
13082a1ad637SFrançois Tigeot 			inv = 0;
13092a1ad637SFrançois Tigeot 		for (k = 0; len > inv && k < nitems(hdaa_quirks_tab); k++) {
13102a1ad637SFrançois Tigeot 			if (strncmp(str + i + inv,
13112a1ad637SFrançois Tigeot 			    hdaa_quirks_tab[k].key, len - inv) != 0)
13122a1ad637SFrançois Tigeot 				continue;
13132a1ad637SFrançois Tigeot 			if (len - inv != strlen(hdaa_quirks_tab[k].key))
13142a1ad637SFrançois Tigeot 				continue;
13152a1ad637SFrançois Tigeot 			if (inv == 0) {
13162a1ad637SFrançois Tigeot 				*on |= hdaa_quirks_tab[k].value;
13172a1ad637SFrançois Tigeot 				*off &= ~hdaa_quirks_tab[k].value;
13182a1ad637SFrançois Tigeot 			} else {
13192a1ad637SFrançois Tigeot 				*off |= hdaa_quirks_tab[k].value;
13202a1ad637SFrançois Tigeot 				*on &= ~hdaa_quirks_tab[k].value;
13212a1ad637SFrançois Tigeot 			}
13222a1ad637SFrançois Tigeot 			break;
13232a1ad637SFrançois Tigeot 		}
13242a1ad637SFrançois Tigeot 		i = j;
13252a1ad637SFrançois Tigeot 	}
13262a1ad637SFrançois Tigeot }
13272a1ad637SFrançois Tigeot 
13282a1ad637SFrançois Tigeot static int
hdaa_sysctl_quirks(SYSCTL_HANDLER_ARGS)13292a1ad637SFrançois Tigeot hdaa_sysctl_quirks(SYSCTL_HANDLER_ARGS)
13302a1ad637SFrançois Tigeot {
13312a1ad637SFrançois Tigeot 	char buf[256];
13322a1ad637SFrançois Tigeot 	int error, n = 0, i;
13332a1ad637SFrançois Tigeot 	uint32_t quirks, quirks_off;
13342a1ad637SFrançois Tigeot 
13352a1ad637SFrançois Tigeot 	quirks = *(uint32_t *)oidp->oid_arg1;
13362a1ad637SFrançois Tigeot 	buf[0] = 0;
13372a1ad637SFrançois Tigeot 	for (i = 0; i < nitems(hdaa_quirks_tab); i++) {
13382a1ad637SFrançois Tigeot 		if ((quirks & hdaa_quirks_tab[i].value) != 0)
133967931cc4SFrançois Tigeot 			n += ksnprintf(buf + n, sizeof(buf) - n, "%s%s",
13402a1ad637SFrançois Tigeot 			    n != 0 ? "," : "", hdaa_quirks_tab[i].key);
13412a1ad637SFrançois Tigeot 	}
13422a1ad637SFrançois Tigeot 	error = sysctl_handle_string(oidp, buf, sizeof(buf), req);
13432a1ad637SFrançois Tigeot 	if (error != 0 || req->newptr == NULL)
13442a1ad637SFrançois Tigeot 		return (error);
13452a1ad637SFrançois Tigeot 	if (strncmp(buf, "0x", 2) == 0)
13462a1ad637SFrançois Tigeot 		quirks = strtol(buf + 2, NULL, 16);
13472a1ad637SFrançois Tigeot 	else {
13482a1ad637SFrançois Tigeot 		quirks = 0;
13492a1ad637SFrançois Tigeot 		hdaa_config_fetch(buf, &quirks, &quirks_off);
13502a1ad637SFrançois Tigeot 	}
13512a1ad637SFrançois Tigeot 	*(uint32_t *)oidp->oid_arg1 = quirks;
13522a1ad637SFrançois Tigeot 	return (0);
13532a1ad637SFrançois Tigeot }
13542a1ad637SFrançois Tigeot 
13552a1ad637SFrançois Tigeot static void
hdaa_local_patch(struct hdaa_devinfo * devinfo)13562a1ad637SFrançois Tigeot hdaa_local_patch(struct hdaa_devinfo *devinfo)
13572a1ad637SFrançois Tigeot {
13582a1ad637SFrançois Tigeot 	struct hdaa_widget *w;
13592a1ad637SFrançois Tigeot 	const char *res = NULL;
13602a1ad637SFrançois Tigeot 	uint32_t quirks_on = 0, quirks_off = 0, x;
13612a1ad637SFrançois Tigeot 	int i;
13622a1ad637SFrançois Tigeot 
13632a1ad637SFrançois Tigeot 	for (i = devinfo->startnode; i < devinfo->endnode; i++) {
13642a1ad637SFrançois Tigeot 		w = hdaa_widget_get(devinfo, i);
13652a1ad637SFrançois Tigeot 		if (w == NULL)
13662a1ad637SFrançois Tigeot 			continue;
13672a1ad637SFrançois Tigeot 		if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX)
13682a1ad637SFrançois Tigeot 			hdaa_local_patch_pin(w);
13692a1ad637SFrançois Tigeot 	}
13702a1ad637SFrançois Tigeot 
13712a1ad637SFrançois Tigeot 	if (resource_string_value(device_get_name(devinfo->dev),
13722a1ad637SFrançois Tigeot 	    device_get_unit(devinfo->dev), "config", &res) == 0) {
13732a1ad637SFrançois Tigeot 		if (res != NULL && strlen(res) > 0)
13742a1ad637SFrançois Tigeot 			hdaa_config_fetch(res, &quirks_on, &quirks_off);
13752a1ad637SFrançois Tigeot 		devinfo->quirks |= quirks_on;
13762a1ad637SFrançois Tigeot 		devinfo->quirks &= ~quirks_off;
13772a1ad637SFrançois Tigeot 	}
13782a1ad637SFrançois Tigeot 	if (devinfo->newquirks == -1)
13792a1ad637SFrançois Tigeot 		devinfo->newquirks = devinfo->quirks;
13802a1ad637SFrançois Tigeot 	else
13812a1ad637SFrançois Tigeot 		devinfo->quirks = devinfo->newquirks;
13822a1ad637SFrançois Tigeot 	HDA_BOOTHVERBOSE(
13832a1ad637SFrançois Tigeot 		device_printf(devinfo->dev,
13842a1ad637SFrançois Tigeot 		    "Config options: 0x%08x\n", devinfo->quirks);
13852a1ad637SFrançois Tigeot 	);
13862a1ad637SFrançois Tigeot 
13872a1ad637SFrançois Tigeot 	if (resource_string_value(device_get_name(devinfo->dev),
13882a1ad637SFrançois Tigeot 	    device_get_unit(devinfo->dev), "gpio_config", &res) == 0) {
13892a1ad637SFrançois Tigeot 		if (strncmp(res, "0x", 2) == 0) {
13902a1ad637SFrançois Tigeot 			devinfo->gpio = strtol(res + 2, NULL, 16);
13912a1ad637SFrançois Tigeot 		} else {
13922a1ad637SFrançois Tigeot 			devinfo->gpio = hdaa_gpio_patch(devinfo->gpio, res);
13932a1ad637SFrançois Tigeot 		}
13942a1ad637SFrançois Tigeot 	}
13952a1ad637SFrançois Tigeot 	if (devinfo->newgpio == -1)
13962a1ad637SFrançois Tigeot 		devinfo->newgpio = devinfo->gpio;
13972a1ad637SFrançois Tigeot 	else
13982a1ad637SFrançois Tigeot 		devinfo->gpio = devinfo->newgpio;
13992a1ad637SFrançois Tigeot 	if (devinfo->newgpo == -1)
14002a1ad637SFrançois Tigeot 		devinfo->newgpo = devinfo->gpo;
14012a1ad637SFrançois Tigeot 	else
14022a1ad637SFrançois Tigeot 		devinfo->gpo = devinfo->newgpo;
14032a1ad637SFrançois Tigeot 	HDA_BOOTHVERBOSE(
14042a1ad637SFrançois Tigeot 		device_printf(devinfo->dev, "GPIO config options:");
14052a1ad637SFrançois Tigeot 		for (i = 0; i < 7; i++) {
14062a1ad637SFrançois Tigeot 			x = (devinfo->gpio & HDAA_GPIO_MASK(i)) >> HDAA_GPIO_SHIFT(i);
14072a1ad637SFrançois Tigeot 			if (x != 0)
140867931cc4SFrançois Tigeot 				kprintf(" %d=%s", i, HDA_GPIO_ACTIONS[x]);
14092a1ad637SFrançois Tigeot 		}
141067931cc4SFrançois Tigeot 		kprintf("\n");
14112a1ad637SFrançois Tigeot 	);
14122a1ad637SFrançois Tigeot }
14132a1ad637SFrançois Tigeot 
14142a1ad637SFrançois Tigeot static void
hdaa_widget_connection_parse(struct hdaa_widget * w)14152a1ad637SFrançois Tigeot hdaa_widget_connection_parse(struct hdaa_widget *w)
14162a1ad637SFrançois Tigeot {
14172a1ad637SFrançois Tigeot 	uint32_t res;
14182a1ad637SFrançois Tigeot 	int i, j, max, ents, entnum;
14192a1ad637SFrançois Tigeot 	nid_t nid = w->nid;
14202a1ad637SFrançois Tigeot 	nid_t cnid, addcnid, prevcnid;
14212a1ad637SFrançois Tigeot 
14222a1ad637SFrançois Tigeot 	w->nconns = 0;
14232a1ad637SFrançois Tigeot 
14242a1ad637SFrançois Tigeot 	res = hda_command(w->devinfo->dev,
14252a1ad637SFrançois Tigeot 	    HDA_CMD_GET_PARAMETER(0, nid, HDA_PARAM_CONN_LIST_LENGTH));
14262a1ad637SFrançois Tigeot 
14272a1ad637SFrançois Tigeot 	ents = HDA_PARAM_CONN_LIST_LENGTH_LIST_LENGTH(res);
14282a1ad637SFrançois Tigeot 
14292a1ad637SFrançois Tigeot 	if (ents < 1)
14302a1ad637SFrançois Tigeot 		return;
14312a1ad637SFrançois Tigeot 
14322a1ad637SFrançois Tigeot 	entnum = HDA_PARAM_CONN_LIST_LENGTH_LONG_FORM(res) ? 2 : 4;
14332a1ad637SFrançois Tigeot 	max = (sizeof(w->conns) / sizeof(w->conns[0])) - 1;
14342a1ad637SFrançois Tigeot 	prevcnid = 0;
14352a1ad637SFrançois Tigeot 
14362a1ad637SFrançois Tigeot #define CONN_RMASK(e)		(1 << ((32 / (e)) - 1))
14372a1ad637SFrançois Tigeot #define CONN_NMASK(e)		(CONN_RMASK(e) - 1)
14382a1ad637SFrançois Tigeot #define CONN_RESVAL(r, e, n)	((r) >> ((32 / (e)) * (n)))
14392a1ad637SFrançois Tigeot #define CONN_RANGE(r, e, n)	(CONN_RESVAL(r, e, n) & CONN_RMASK(e))
14402a1ad637SFrançois Tigeot #define CONN_CNID(r, e, n)	(CONN_RESVAL(r, e, n) & CONN_NMASK(e))
14412a1ad637SFrançois Tigeot 
14422a1ad637SFrançois Tigeot 	for (i = 0; i < ents; i += entnum) {
14432a1ad637SFrançois Tigeot 		res = hda_command(w->devinfo->dev,
14442a1ad637SFrançois Tigeot 		    HDA_CMD_GET_CONN_LIST_ENTRY(0, nid, i));
14452a1ad637SFrançois Tigeot 		for (j = 0; j < entnum; j++) {
14462a1ad637SFrançois Tigeot 			cnid = CONN_CNID(res, entnum, j);
14472a1ad637SFrançois Tigeot 			if (cnid == 0) {
14482a1ad637SFrançois Tigeot 				if (w->nconns < ents)
14492a1ad637SFrançois Tigeot 					device_printf(w->devinfo->dev,
14502a1ad637SFrançois Tigeot 					    "WARNING: nid=%d has zero cnid "
14512a1ad637SFrançois Tigeot 					    "entnum=%d j=%d index=%d "
14522a1ad637SFrançois Tigeot 					    "entries=%d found=%d res=0x%08x\n",
14532a1ad637SFrançois Tigeot 					    nid, entnum, j, i,
14542a1ad637SFrançois Tigeot 					    ents, w->nconns, res);
14552a1ad637SFrançois Tigeot 				else
14562a1ad637SFrançois Tigeot 					goto getconns_out;
14572a1ad637SFrançois Tigeot 			}
14582a1ad637SFrançois Tigeot 			if (cnid < w->devinfo->startnode ||
14592a1ad637SFrançois Tigeot 			    cnid >= w->devinfo->endnode) {
14602a1ad637SFrançois Tigeot 				HDA_BOOTVERBOSE(
14612a1ad637SFrançois Tigeot 					device_printf(w->devinfo->dev,
14622a1ad637SFrançois Tigeot 					    "WARNING: nid=%d has cnid outside "
14632a1ad637SFrançois Tigeot 					    "of the AFG range j=%d "
14642a1ad637SFrançois Tigeot 					    "entnum=%d index=%d res=0x%08x\n",
14652a1ad637SFrançois Tigeot 					    nid, j, entnum, i, res);
14662a1ad637SFrançois Tigeot 				);
14672a1ad637SFrançois Tigeot 			}
14682a1ad637SFrançois Tigeot 			if (CONN_RANGE(res, entnum, j) == 0)
14692a1ad637SFrançois Tigeot 				addcnid = cnid;
14702a1ad637SFrançois Tigeot 			else if (prevcnid == 0 || prevcnid >= cnid) {
14712a1ad637SFrançois Tigeot 				device_printf(w->devinfo->dev,
14722a1ad637SFrançois Tigeot 				    "WARNING: Invalid child range "
14732a1ad637SFrançois Tigeot 				    "nid=%d index=%d j=%d entnum=%d "
14742a1ad637SFrançois Tigeot 				    "prevcnid=%d cnid=%d res=0x%08x\n",
14752a1ad637SFrançois Tigeot 				    nid, i, j, entnum, prevcnid,
14762a1ad637SFrançois Tigeot 				    cnid, res);
14772a1ad637SFrançois Tigeot 				addcnid = cnid;
14782a1ad637SFrançois Tigeot 			} else
14792a1ad637SFrançois Tigeot 				addcnid = prevcnid + 1;
14802a1ad637SFrançois Tigeot 			while (addcnid <= cnid) {
14812a1ad637SFrançois Tigeot 				if (w->nconns > max) {
14822a1ad637SFrançois Tigeot 					device_printf(w->devinfo->dev,
14832a1ad637SFrançois Tigeot 					    "Adding %d (nid=%d): "
14842a1ad637SFrançois Tigeot 					    "Max connection reached! max=%d\n",
14852a1ad637SFrançois Tigeot 					    addcnid, nid, max + 1);
14862a1ad637SFrançois Tigeot 					goto getconns_out;
14872a1ad637SFrançois Tigeot 				}
14882a1ad637SFrançois Tigeot 				w->connsenable[w->nconns] = 1;
14892a1ad637SFrançois Tigeot 				w->conns[w->nconns++] = addcnid++;
14902a1ad637SFrançois Tigeot 			}
14912a1ad637SFrançois Tigeot 			prevcnid = cnid;
14922a1ad637SFrançois Tigeot 		}
14932a1ad637SFrançois Tigeot 	}
14942a1ad637SFrançois Tigeot 
14952a1ad637SFrançois Tigeot getconns_out:
14962a1ad637SFrançois Tigeot 	return;
14972a1ad637SFrançois Tigeot }
14982a1ad637SFrançois Tigeot 
14992a1ad637SFrançois Tigeot static void
hdaa_widget_parse(struct hdaa_widget * w)15002a1ad637SFrançois Tigeot hdaa_widget_parse(struct hdaa_widget *w)
15012a1ad637SFrançois Tigeot {
15022a1ad637SFrançois Tigeot 	device_t dev = w->devinfo->dev;
15032a1ad637SFrançois Tigeot 	uint32_t wcap, cap;
15042a1ad637SFrançois Tigeot 	nid_t nid = w->nid;
15052a1ad637SFrançois Tigeot 	char buf[64];
15062a1ad637SFrançois Tigeot 
15072a1ad637SFrançois Tigeot 	w->param.widget_cap = wcap = hda_command(dev,
15082a1ad637SFrançois Tigeot 	    HDA_CMD_GET_PARAMETER(0, nid, HDA_PARAM_AUDIO_WIDGET_CAP));
15092a1ad637SFrançois Tigeot 	w->type = HDA_PARAM_AUDIO_WIDGET_CAP_TYPE(wcap);
15102a1ad637SFrançois Tigeot 
15112a1ad637SFrançois Tigeot 	hdaa_widget_connection_parse(w);
15122a1ad637SFrançois Tigeot 
15132a1ad637SFrançois Tigeot 	if (HDA_PARAM_AUDIO_WIDGET_CAP_OUT_AMP(wcap)) {
15142a1ad637SFrançois Tigeot 		if (HDA_PARAM_AUDIO_WIDGET_CAP_AMP_OVR(wcap))
15152a1ad637SFrançois Tigeot 			w->param.outamp_cap =
15162a1ad637SFrançois Tigeot 			    hda_command(dev,
15172a1ad637SFrançois Tigeot 			    HDA_CMD_GET_PARAMETER(0, nid,
15182a1ad637SFrançois Tigeot 			    HDA_PARAM_OUTPUT_AMP_CAP));
15192a1ad637SFrançois Tigeot 		else
15202a1ad637SFrançois Tigeot 			w->param.outamp_cap =
15212a1ad637SFrançois Tigeot 			    w->devinfo->outamp_cap;
15222a1ad637SFrançois Tigeot 	} else
15232a1ad637SFrançois Tigeot 		w->param.outamp_cap = 0;
15242a1ad637SFrançois Tigeot 
15252a1ad637SFrançois Tigeot 	if (HDA_PARAM_AUDIO_WIDGET_CAP_IN_AMP(wcap)) {
15262a1ad637SFrançois Tigeot 		if (HDA_PARAM_AUDIO_WIDGET_CAP_AMP_OVR(wcap))
15272a1ad637SFrançois Tigeot 			w->param.inamp_cap =
15282a1ad637SFrançois Tigeot 			    hda_command(dev,
15292a1ad637SFrançois Tigeot 			    HDA_CMD_GET_PARAMETER(0, nid,
15302a1ad637SFrançois Tigeot 			    HDA_PARAM_INPUT_AMP_CAP));
15312a1ad637SFrançois Tigeot 		else
15322a1ad637SFrançois Tigeot 			w->param.inamp_cap =
15332a1ad637SFrançois Tigeot 			    w->devinfo->inamp_cap;
15342a1ad637SFrançois Tigeot 	} else
15352a1ad637SFrançois Tigeot 		w->param.inamp_cap = 0;
15362a1ad637SFrançois Tigeot 
15372a1ad637SFrançois Tigeot 	if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT ||
15382a1ad637SFrançois Tigeot 	    w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT) {
15392a1ad637SFrançois Tigeot 		if (HDA_PARAM_AUDIO_WIDGET_CAP_FORMAT_OVR(wcap)) {
15402a1ad637SFrançois Tigeot 			cap = hda_command(dev,
15412a1ad637SFrançois Tigeot 			    HDA_CMD_GET_PARAMETER(0, nid,
15422a1ad637SFrançois Tigeot 			    HDA_PARAM_SUPP_STREAM_FORMATS));
15432a1ad637SFrançois Tigeot 			w->param.supp_stream_formats = (cap != 0) ? cap :
15442a1ad637SFrançois Tigeot 			    w->devinfo->supp_stream_formats;
15452a1ad637SFrançois Tigeot 			cap = hda_command(dev,
15462a1ad637SFrançois Tigeot 			    HDA_CMD_GET_PARAMETER(0, nid,
15472a1ad637SFrançois Tigeot 			    HDA_PARAM_SUPP_PCM_SIZE_RATE));
15482a1ad637SFrançois Tigeot 			w->param.supp_pcm_size_rate = (cap != 0) ? cap :
15492a1ad637SFrançois Tigeot 			    w->devinfo->supp_pcm_size_rate;
15502a1ad637SFrançois Tigeot 		} else {
15512a1ad637SFrançois Tigeot 			w->param.supp_stream_formats =
15522a1ad637SFrançois Tigeot 			    w->devinfo->supp_stream_formats;
15532a1ad637SFrançois Tigeot 			w->param.supp_pcm_size_rate =
15542a1ad637SFrançois Tigeot 			    w->devinfo->supp_pcm_size_rate;
15552a1ad637SFrançois Tigeot 		}
15562a1ad637SFrançois Tigeot 		if (HDA_PARAM_AUDIO_WIDGET_CAP_STRIPE(w->param.widget_cap)) {
15572a1ad637SFrançois Tigeot 			w->wclass.conv.stripecap = hda_command(dev,
15582a1ad637SFrançois Tigeot 			    HDA_CMD_GET_STRIPE_CONTROL(0, w->nid)) >> 20;
15592a1ad637SFrançois Tigeot 		} else
15602a1ad637SFrançois Tigeot 			w->wclass.conv.stripecap = 1;
15612a1ad637SFrançois Tigeot 	} else {
15622a1ad637SFrançois Tigeot 		w->param.supp_stream_formats = 0;
15632a1ad637SFrançois Tigeot 		w->param.supp_pcm_size_rate = 0;
15642a1ad637SFrançois Tigeot 	}
15652a1ad637SFrançois Tigeot 
15662a1ad637SFrançois Tigeot 	if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) {
15672a1ad637SFrançois Tigeot 		w->wclass.pin.original = w->wclass.pin.newconf =
15682a1ad637SFrançois Tigeot 		    w->wclass.pin.config = hda_command(dev,
15692a1ad637SFrançois Tigeot 			HDA_CMD_GET_CONFIGURATION_DEFAULT(0, w->nid));
15702a1ad637SFrançois Tigeot 		w->wclass.pin.cap = hda_command(dev,
15712a1ad637SFrançois Tigeot 		    HDA_CMD_GET_PARAMETER(0, w->nid, HDA_PARAM_PIN_CAP));
15722a1ad637SFrançois Tigeot 		w->wclass.pin.ctrl = hda_command(dev,
15732a1ad637SFrançois Tigeot 		    HDA_CMD_GET_PIN_WIDGET_CTRL(0, nid));
15742a1ad637SFrançois Tigeot 		w->wclass.pin.connected = 2;
15752a1ad637SFrançois Tigeot 		if (HDA_PARAM_PIN_CAP_EAPD_CAP(w->wclass.pin.cap)) {
15762a1ad637SFrançois Tigeot 			w->param.eapdbtl = hda_command(dev,
15772a1ad637SFrançois Tigeot 			    HDA_CMD_GET_EAPD_BTL_ENABLE(0, nid));
15782a1ad637SFrançois Tigeot 			w->param.eapdbtl &= 0x7;
15792a1ad637SFrançois Tigeot 			w->param.eapdbtl |= HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD;
15802a1ad637SFrançois Tigeot 		} else
15812a1ad637SFrançois Tigeot 			w->param.eapdbtl = HDA_INVALID;
15822a1ad637SFrançois Tigeot 	}
15832a1ad637SFrançois Tigeot 	w->unsol = -1;
15842a1ad637SFrançois Tigeot 
15852a1ad637SFrançois Tigeot 	hdaa_unlock(w->devinfo);
158667931cc4SFrançois Tigeot 	ksnprintf(buf, sizeof(buf), "nid%d", w->nid);
15872a1ad637SFrançois Tigeot 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
15882a1ad637SFrançois Tigeot 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
158967931cc4SFrançois Tigeot 	    buf, CTLTYPE_STRING | CTLFLAG_RD,
15902a1ad637SFrançois Tigeot 	    w, sizeof(w), hdaa_sysctl_caps, "A", "Node capabilities");
15912a1ad637SFrançois Tigeot 	if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) {
159267931cc4SFrançois Tigeot 		ksnprintf(buf, sizeof(buf), "nid%d_config", w->nid);
15932a1ad637SFrançois Tigeot 		SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
15942a1ad637SFrançois Tigeot 		    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
159567931cc4SFrançois Tigeot 		    buf, CTLTYPE_STRING | CTLFLAG_RW,
15962a1ad637SFrançois Tigeot 		    &w->wclass.pin.newconf, sizeof(&w->wclass.pin.newconf),
15972a1ad637SFrançois Tigeot 		    hdaa_sysctl_config, "A", "Current pin configuration");
159867931cc4SFrançois Tigeot 		ksnprintf(buf, sizeof(buf), "nid%d_original", w->nid);
15992a1ad637SFrançois Tigeot 		SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
16002a1ad637SFrançois Tigeot 		    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
160167931cc4SFrançois Tigeot 		    buf, CTLTYPE_STRING | CTLFLAG_RD,
16022a1ad637SFrançois Tigeot 		    &w->wclass.pin.original, sizeof(&w->wclass.pin.original),
16032a1ad637SFrançois Tigeot 		    hdaa_sysctl_config, "A", "Original pin configuration");
16042a1ad637SFrançois Tigeot 	}
16052a1ad637SFrançois Tigeot 	hdaa_lock(w->devinfo);
16062a1ad637SFrançois Tigeot }
16072a1ad637SFrançois Tigeot 
16082a1ad637SFrançois Tigeot static void
hdaa_widget_postprocess(struct hdaa_widget * w)16092a1ad637SFrançois Tigeot hdaa_widget_postprocess(struct hdaa_widget *w)
16102a1ad637SFrançois Tigeot {
16112a1ad637SFrançois Tigeot 	const char *typestr;
16122a1ad637SFrançois Tigeot 
16132a1ad637SFrançois Tigeot 	w->type = HDA_PARAM_AUDIO_WIDGET_CAP_TYPE(w->param.widget_cap);
16142a1ad637SFrançois Tigeot 	switch (w->type) {
16152a1ad637SFrançois Tigeot 	case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT:
16162a1ad637SFrançois Tigeot 		typestr = "audio output";
16172a1ad637SFrançois Tigeot 		break;
16182a1ad637SFrançois Tigeot 	case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT:
16192a1ad637SFrançois Tigeot 		typestr = "audio input";
16202a1ad637SFrançois Tigeot 		break;
16212a1ad637SFrançois Tigeot 	case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER:
16222a1ad637SFrançois Tigeot 		typestr = "audio mixer";
16232a1ad637SFrançois Tigeot 		break;
16242a1ad637SFrançois Tigeot 	case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR:
16252a1ad637SFrançois Tigeot 		typestr = "audio selector";
16262a1ad637SFrançois Tigeot 		break;
16272a1ad637SFrançois Tigeot 	case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX:
16282a1ad637SFrançois Tigeot 		typestr = "pin";
16292a1ad637SFrançois Tigeot 		break;
16302a1ad637SFrançois Tigeot 	case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_POWER_WIDGET:
16312a1ad637SFrançois Tigeot 		typestr = "power widget";
16322a1ad637SFrançois Tigeot 		break;
16332a1ad637SFrançois Tigeot 	case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_VOLUME_WIDGET:
16342a1ad637SFrançois Tigeot 		typestr = "volume widget";
16352a1ad637SFrançois Tigeot 		break;
16362a1ad637SFrançois Tigeot 	case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET:
16372a1ad637SFrançois Tigeot 		typestr = "beep widget";
16382a1ad637SFrançois Tigeot 		break;
16392a1ad637SFrançois Tigeot 	case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_VENDOR_WIDGET:
16402a1ad637SFrançois Tigeot 		typestr = "vendor widget";
16412a1ad637SFrançois Tigeot 		break;
16422a1ad637SFrançois Tigeot 	default:
16432a1ad637SFrançois Tigeot 		typestr = "unknown type";
16442a1ad637SFrançois Tigeot 		break;
16452a1ad637SFrançois Tigeot 	}
16462a1ad637SFrançois Tigeot 	strlcpy(w->name, typestr, sizeof(w->name));
16472a1ad637SFrançois Tigeot 
16482a1ad637SFrançois Tigeot 	if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) {
16492a1ad637SFrançois Tigeot 		uint32_t config;
16502a1ad637SFrançois Tigeot 		const char *devstr;
16512a1ad637SFrançois Tigeot 		int conn, color;
16522a1ad637SFrançois Tigeot 
16532a1ad637SFrançois Tigeot 		config = w->wclass.pin.config;
16542a1ad637SFrançois Tigeot 		devstr = HDA_DEVS[(config & HDA_CONFIG_DEFAULTCONF_DEVICE_MASK) >>
16552a1ad637SFrançois Tigeot 		    HDA_CONFIG_DEFAULTCONF_DEVICE_SHIFT];
16562a1ad637SFrançois Tigeot 		conn = (config & HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK) >>
16572a1ad637SFrançois Tigeot 		    HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_SHIFT;
16582a1ad637SFrançois Tigeot 		color = (config & HDA_CONFIG_DEFAULTCONF_COLOR_MASK) >>
16592a1ad637SFrançois Tigeot 		    HDA_CONFIG_DEFAULTCONF_COLOR_SHIFT;
16602a1ad637SFrançois Tigeot 		strlcat(w->name, ": ", sizeof(w->name));
16612a1ad637SFrançois Tigeot 		strlcat(w->name, devstr, sizeof(w->name));
16622a1ad637SFrançois Tigeot 		strlcat(w->name, " (", sizeof(w->name));
16632a1ad637SFrançois Tigeot 		if (conn == 0 && color != 0 && color != 15) {
16642a1ad637SFrançois Tigeot 			strlcat(w->name, HDA_COLORS[color], sizeof(w->name));
16652a1ad637SFrançois Tigeot 			strlcat(w->name, " ", sizeof(w->name));
16662a1ad637SFrançois Tigeot 		}
16672a1ad637SFrançois Tigeot 		strlcat(w->name, HDA_CONNS[conn], sizeof(w->name));
16682a1ad637SFrançois Tigeot 		strlcat(w->name, ")", sizeof(w->name));
16692a1ad637SFrançois Tigeot 	}
16702a1ad637SFrançois Tigeot }
16712a1ad637SFrançois Tigeot 
16722a1ad637SFrançois Tigeot struct hdaa_widget *
hdaa_widget_get(struct hdaa_devinfo * devinfo,nid_t nid)16732a1ad637SFrançois Tigeot hdaa_widget_get(struct hdaa_devinfo *devinfo, nid_t nid)
16742a1ad637SFrançois Tigeot {
16752a1ad637SFrançois Tigeot 	if (devinfo == NULL || devinfo->widget == NULL ||
16762a1ad637SFrançois Tigeot 		    nid < devinfo->startnode || nid >= devinfo->endnode)
16772a1ad637SFrançois Tigeot 		return (NULL);
16782a1ad637SFrançois Tigeot 	return (&devinfo->widget[nid - devinfo->startnode]);
16792a1ad637SFrançois Tigeot }
16802a1ad637SFrançois Tigeot 
16812a1ad637SFrançois Tigeot static void
hdaa_audio_ctl_amp_set_internal(struct hdaa_devinfo * devinfo,nid_t nid,int index,int lmute,int rmute,int left,int right,int dir)16822a1ad637SFrançois Tigeot hdaa_audio_ctl_amp_set_internal(struct hdaa_devinfo *devinfo, nid_t nid,
16832a1ad637SFrançois Tigeot 					int index, int lmute, int rmute,
16842a1ad637SFrançois Tigeot 					int left, int right, int dir)
16852a1ad637SFrançois Tigeot {
16862a1ad637SFrançois Tigeot 	uint16_t v = 0;
16872a1ad637SFrançois Tigeot 
16882a1ad637SFrançois Tigeot 	HDA_BOOTHVERBOSE(
16892a1ad637SFrançois Tigeot 		device_printf(devinfo->dev,
16902a1ad637SFrançois Tigeot 		    "Setting amplifier nid=%d index=%d %s mute=%d/%d vol=%d/%d\n",
16912a1ad637SFrançois Tigeot 		    nid,index,dir ? "in" : "out",lmute,rmute,left,right);
16922a1ad637SFrançois Tigeot 	);
16932a1ad637SFrançois Tigeot 	if (left != right || lmute != rmute) {
16942a1ad637SFrançois Tigeot 		v = (1 << (15 - dir)) | (1 << 13) | (index << 8) |
16952a1ad637SFrançois Tigeot 		    (lmute << 7) | left;
16962a1ad637SFrançois Tigeot 		hda_command(devinfo->dev,
16972a1ad637SFrançois Tigeot 		    HDA_CMD_SET_AMP_GAIN_MUTE(0, nid, v));
16982a1ad637SFrançois Tigeot 		v = (1 << (15 - dir)) | (1 << 12) | (index << 8) |
16992a1ad637SFrançois Tigeot 		    (rmute << 7) | right;
17002a1ad637SFrançois Tigeot 	} else
17012a1ad637SFrançois Tigeot 		v = (1 << (15 - dir)) | (3 << 12) | (index << 8) |
17022a1ad637SFrançois Tigeot 		    (lmute << 7) | left;
17032a1ad637SFrançois Tigeot 
17042a1ad637SFrançois Tigeot 	hda_command(devinfo->dev,
17052a1ad637SFrançois Tigeot 	    HDA_CMD_SET_AMP_GAIN_MUTE(0, nid, v));
17062a1ad637SFrançois Tigeot }
17072a1ad637SFrançois Tigeot 
17082a1ad637SFrançois Tigeot static void
hdaa_audio_ctl_amp_set(struct hdaa_audio_ctl * ctl,uint32_t mute,int left,int right)17092a1ad637SFrançois Tigeot hdaa_audio_ctl_amp_set(struct hdaa_audio_ctl *ctl, uint32_t mute,
17102a1ad637SFrançois Tigeot 						int left, int right)
17112a1ad637SFrançois Tigeot {
17122a1ad637SFrançois Tigeot 	nid_t nid;
17132a1ad637SFrançois Tigeot 	int lmute, rmute;
17142a1ad637SFrançois Tigeot 
17152a1ad637SFrançois Tigeot 	nid = ctl->widget->nid;
17162a1ad637SFrançois Tigeot 
17172a1ad637SFrançois Tigeot 	/* Save new values if valid. */
17182a1ad637SFrançois Tigeot 	if (mute != HDAA_AMP_MUTE_DEFAULT)
17192a1ad637SFrançois Tigeot 		ctl->muted = mute;
17202a1ad637SFrançois Tigeot 	if (left != HDAA_AMP_VOL_DEFAULT)
17212a1ad637SFrançois Tigeot 		ctl->left = left;
17222a1ad637SFrançois Tigeot 	if (right != HDAA_AMP_VOL_DEFAULT)
17232a1ad637SFrançois Tigeot 		ctl->right = right;
17242a1ad637SFrançois Tigeot 	/* Prepare effective values */
17252a1ad637SFrançois Tigeot 	if (ctl->forcemute) {
17262a1ad637SFrançois Tigeot 		lmute = 1;
17272a1ad637SFrançois Tigeot 		rmute = 1;
17282a1ad637SFrançois Tigeot 		left = 0;
17292a1ad637SFrançois Tigeot 		right = 0;
17302a1ad637SFrançois Tigeot 	} else {
17312a1ad637SFrançois Tigeot 		lmute = HDAA_AMP_LEFT_MUTED(ctl->muted);
17322a1ad637SFrançois Tigeot 		rmute = HDAA_AMP_RIGHT_MUTED(ctl->muted);
17332a1ad637SFrançois Tigeot 		left = ctl->left;
17342a1ad637SFrançois Tigeot 		right = ctl->right;
17352a1ad637SFrançois Tigeot 	}
17362a1ad637SFrançois Tigeot 	/* Apply effective values */
17372a1ad637SFrançois Tigeot 	if (ctl->dir & HDAA_CTL_OUT)
17382a1ad637SFrançois Tigeot 		hdaa_audio_ctl_amp_set_internal(ctl->widget->devinfo, nid, ctl->index,
17392a1ad637SFrançois Tigeot 		    lmute, rmute, left, right, 0);
17402a1ad637SFrançois Tigeot 	if (ctl->dir & HDAA_CTL_IN)
17412a1ad637SFrançois Tigeot 		hdaa_audio_ctl_amp_set_internal(ctl->widget->devinfo, nid, ctl->index,
17422a1ad637SFrançois Tigeot 		    lmute, rmute, left, right, 1);
17432a1ad637SFrançois Tigeot }
17442a1ad637SFrançois Tigeot 
17452a1ad637SFrançois Tigeot static void
hdaa_widget_connection_select(struct hdaa_widget * w,uint8_t index)17462a1ad637SFrançois Tigeot hdaa_widget_connection_select(struct hdaa_widget *w, uint8_t index)
17472a1ad637SFrançois Tigeot {
17482a1ad637SFrançois Tigeot 	if (w == NULL || w->nconns < 1 || index > (w->nconns - 1))
17492a1ad637SFrançois Tigeot 		return;
17502a1ad637SFrançois Tigeot 	HDA_BOOTHVERBOSE(
17512a1ad637SFrançois Tigeot 		device_printf(w->devinfo->dev,
17522a1ad637SFrançois Tigeot 		    "Setting selector nid=%d index=%d\n", w->nid, index);
17532a1ad637SFrançois Tigeot 	);
17542a1ad637SFrançois Tigeot 	hda_command(w->devinfo->dev,
17552a1ad637SFrançois Tigeot 	    HDA_CMD_SET_CONNECTION_SELECT_CONTROL(0, w->nid, index));
17562a1ad637SFrançois Tigeot 	w->selconn = index;
17572a1ad637SFrançois Tigeot }
17582a1ad637SFrançois Tigeot 
17592a1ad637SFrançois Tigeot /****************************************************************************
17602a1ad637SFrançois Tigeot  * Device Methods
17612a1ad637SFrançois Tigeot  ****************************************************************************/
17622a1ad637SFrançois Tigeot 
17632a1ad637SFrançois Tigeot static void *
hdaa_channel_init(kobj_t obj,void * data,struct snd_dbuf * b,struct pcm_channel * c,int dir)17642a1ad637SFrançois Tigeot hdaa_channel_init(kobj_t obj, void *data, struct snd_dbuf *b,
17652a1ad637SFrançois Tigeot 					struct pcm_channel *c, int dir)
17662a1ad637SFrançois Tigeot {
17672a1ad637SFrançois Tigeot 	struct hdaa_chan *ch = data;
17682a1ad637SFrançois Tigeot 	struct hdaa_pcm_devinfo *pdevinfo = ch->pdevinfo;
17692a1ad637SFrançois Tigeot 	struct hdaa_devinfo *devinfo = pdevinfo->devinfo;
17702a1ad637SFrançois Tigeot 
17712a1ad637SFrançois Tigeot 	hdaa_lock(devinfo);
17722a1ad637SFrançois Tigeot 	if (devinfo->quirks & HDAA_QUIRK_FIXEDRATE) {
17732a1ad637SFrançois Tigeot 		ch->caps.minspeed = ch->caps.maxspeed = 48000;
17742a1ad637SFrançois Tigeot 		ch->pcmrates[0] = 48000;
17752a1ad637SFrançois Tigeot 		ch->pcmrates[1] = 0;
17762a1ad637SFrançois Tigeot 	}
17772a1ad637SFrançois Tigeot 	ch->dir = dir;
17782a1ad637SFrançois Tigeot 	ch->b = b;
17792a1ad637SFrançois Tigeot 	ch->c = c;
17802a1ad637SFrançois Tigeot 	ch->blksz = pdevinfo->chan_size / pdevinfo->chan_blkcnt;
17812a1ad637SFrançois Tigeot 	ch->blkcnt = pdevinfo->chan_blkcnt;
17822a1ad637SFrançois Tigeot 	hdaa_unlock(devinfo);
17832a1ad637SFrançois Tigeot 
17842a1ad637SFrançois Tigeot 	if (sndbuf_alloc(ch->b, bus_get_dma_tag(devinfo->dev),
17852a1ad637SFrançois Tigeot 	    hda_get_dma_nocache(devinfo->dev) ? BUS_DMA_NOCACHE : 0,
17862a1ad637SFrançois Tigeot 	    pdevinfo->chan_size) != 0)
17872a1ad637SFrançois Tigeot 		return (NULL);
17882a1ad637SFrançois Tigeot 
17892a1ad637SFrançois Tigeot 	return (ch);
17902a1ad637SFrançois Tigeot }
17912a1ad637SFrançois Tigeot 
17922a1ad637SFrançois Tigeot static int
hdaa_channel_setformat(kobj_t obj,void * data,uint32_t format)17932a1ad637SFrançois Tigeot hdaa_channel_setformat(kobj_t obj, void *data, uint32_t format)
17942a1ad637SFrançois Tigeot {
17952a1ad637SFrançois Tigeot 	struct hdaa_chan *ch = data;
17962a1ad637SFrançois Tigeot 	int i;
17972a1ad637SFrançois Tigeot 
17982a1ad637SFrançois Tigeot 	for (i = 0; ch->caps.fmtlist[i] != 0; i++) {
17992a1ad637SFrançois Tigeot 		if (format == ch->caps.fmtlist[i]) {
18002a1ad637SFrançois Tigeot 			ch->fmt = format;
18012a1ad637SFrançois Tigeot 			return (0);
18022a1ad637SFrançois Tigeot 		}
18032a1ad637SFrançois Tigeot 	}
18042a1ad637SFrançois Tigeot 
18052a1ad637SFrançois Tigeot 	return (EINVAL);
18062a1ad637SFrançois Tigeot }
18072a1ad637SFrançois Tigeot 
18082a1ad637SFrançois Tigeot static uint32_t
hdaa_channel_setspeed(kobj_t obj,void * data,uint32_t speed)18092a1ad637SFrançois Tigeot hdaa_channel_setspeed(kobj_t obj, void *data, uint32_t speed)
18102a1ad637SFrançois Tigeot {
18112a1ad637SFrançois Tigeot 	struct hdaa_chan *ch = data;
18122a1ad637SFrançois Tigeot 	uint32_t spd = 0, threshold;
18132a1ad637SFrançois Tigeot 	int i;
18142a1ad637SFrançois Tigeot 
18152a1ad637SFrançois Tigeot 	/* First look for equal or multiple frequency. */
18162a1ad637SFrançois Tigeot 	for (i = 0; ch->pcmrates[i] != 0; i++) {
18172a1ad637SFrançois Tigeot 		spd = ch->pcmrates[i];
1818ed183f8cSSascha Wildner 		if (speed != 0 && rounddown(spd, speed) == spd) {
18192a1ad637SFrançois Tigeot 			ch->spd = spd;
18202a1ad637SFrançois Tigeot 			return (spd);
18212a1ad637SFrançois Tigeot 		}
18222a1ad637SFrançois Tigeot 	}
18232a1ad637SFrançois Tigeot 	/* If no match, just find nearest. */
18242a1ad637SFrançois Tigeot 	for (i = 0; ch->pcmrates[i] != 0; i++) {
18252a1ad637SFrançois Tigeot 		spd = ch->pcmrates[i];
18262a1ad637SFrançois Tigeot 		threshold = spd + ((ch->pcmrates[i + 1] != 0) ?
18272a1ad637SFrançois Tigeot 		    ((ch->pcmrates[i + 1] - spd) >> 1) : 0);
18282a1ad637SFrançois Tigeot 		if (speed < threshold)
18292a1ad637SFrançois Tigeot 			break;
18302a1ad637SFrançois Tigeot 	}
18312a1ad637SFrançois Tigeot 	ch->spd = spd;
18322a1ad637SFrançois Tigeot 	return (spd);
18332a1ad637SFrançois Tigeot }
18342a1ad637SFrançois Tigeot 
18352a1ad637SFrançois Tigeot static uint16_t
hdaa_stream_format(struct hdaa_chan * ch)18362a1ad637SFrançois Tigeot hdaa_stream_format(struct hdaa_chan *ch)
18372a1ad637SFrançois Tigeot {
18382a1ad637SFrançois Tigeot 	int i;
18392a1ad637SFrançois Tigeot 	uint16_t fmt;
18402a1ad637SFrançois Tigeot 
18412a1ad637SFrançois Tigeot 	fmt = 0;
18422a1ad637SFrançois Tigeot 	if (ch->fmt & AFMT_S16_LE)
18432a1ad637SFrançois Tigeot 		fmt |= ch->bit16 << 4;
18442a1ad637SFrançois Tigeot 	else if (ch->fmt & AFMT_S32_LE)
18452a1ad637SFrançois Tigeot 		fmt |= ch->bit32 << 4;
18462a1ad637SFrançois Tigeot 	else
18472a1ad637SFrançois Tigeot 		fmt |= 1 << 4;
18482a1ad637SFrançois Tigeot 	for (i = 0; i < HDA_RATE_TAB_LEN; i++) {
18492a1ad637SFrançois Tigeot 		if (hda_rate_tab[i].valid && ch->spd == hda_rate_tab[i].rate) {
18502a1ad637SFrançois Tigeot 			fmt |= hda_rate_tab[i].base;
18512a1ad637SFrançois Tigeot 			fmt |= hda_rate_tab[i].mul;
18522a1ad637SFrançois Tigeot 			fmt |= hda_rate_tab[i].div;
18532a1ad637SFrançois Tigeot 			break;
18542a1ad637SFrançois Tigeot 		}
18552a1ad637SFrançois Tigeot 	}
18562a1ad637SFrançois Tigeot 	fmt |= (AFMT_CHANNEL(ch->fmt) - 1);
18572a1ad637SFrançois Tigeot 
18582a1ad637SFrançois Tigeot 	return (fmt);
18592a1ad637SFrançois Tigeot }
18602a1ad637SFrançois Tigeot 
18612a1ad637SFrançois Tigeot static int
hdaa_allowed_stripes(uint16_t fmt)18622a1ad637SFrançois Tigeot hdaa_allowed_stripes(uint16_t fmt)
18632a1ad637SFrançois Tigeot {
18642a1ad637SFrançois Tigeot 	static const int bits[8] = { 8, 16, 20, 24, 32, 32, 32, 32 };
18652a1ad637SFrançois Tigeot 	int size;
18662a1ad637SFrançois Tigeot 
18672a1ad637SFrançois Tigeot 	size = bits[(fmt >> 4) & 0x03];
18682a1ad637SFrançois Tigeot 	size *= (fmt & 0x0f) + 1;
18692a1ad637SFrançois Tigeot 	size *= ((fmt >> 11) & 0x07) + 1;
18702a1ad637SFrançois Tigeot 	return (0xffffffffU >> (32 - fls(size / 8)));
18712a1ad637SFrançois Tigeot }
18722a1ad637SFrançois Tigeot 
18732a1ad637SFrançois Tigeot static void
hdaa_audio_setup(struct hdaa_chan * ch)18742a1ad637SFrançois Tigeot hdaa_audio_setup(struct hdaa_chan *ch)
18752a1ad637SFrançois Tigeot {
18762a1ad637SFrançois Tigeot 	struct hdaa_audio_as *as = &ch->devinfo->as[ch->as];
18772a1ad637SFrançois Tigeot 	struct hdaa_widget *w, *wp;
18782a1ad637SFrançois Tigeot 	int i, j, k, chn, cchn, totalchn, totalextchn, c;
18792a1ad637SFrançois Tigeot 	uint16_t fmt, dfmt;
18802a1ad637SFrançois Tigeot 	/* Mapping channel pairs to codec pins/converters. */
188167931cc4SFrançois Tigeot 	static const uint16_t convmap[2][5] =
18822a1ad637SFrançois Tigeot 	    /*  1.0     2.0     4.0     5.1     7.1  */
18832a1ad637SFrançois Tigeot 	    {{ 0x0010, 0x0001, 0x0201, 0x0231, 0x4231 },	/* no dup. */
18842a1ad637SFrançois Tigeot 	     { 0x0010, 0x0001, 0x2201, 0x2231, 0x4231 }};	/* side dup. */
18852a1ad637SFrançois Tigeot 	/* Mapping formats to HDMI channel allocations. */
188667931cc4SFrançois Tigeot 	static const uint8_t hdmica[2][8] =
18872a1ad637SFrançois Tigeot 	    /*  1     2     3     4     5     6     7     8  */
18882a1ad637SFrançois Tigeot 	    {{ 0x02, 0x00, 0x04, 0x08, 0x0a, 0x0e, 0x12, 0x12 }, /* x.0 */
18892a1ad637SFrançois Tigeot 	     { 0x01, 0x03, 0x01, 0x03, 0x09, 0x0b, 0x0f, 0x13 }}; /* x.1 */
18902a1ad637SFrançois Tigeot 	/* Mapping formats to HDMI channels order. */
189167931cc4SFrançois Tigeot 	static const uint32_t hdmich[2][8] =
18922a1ad637SFrançois Tigeot 	    /*  1  /  5     2  /  6     3  /  7     4  /  8  */
18932a1ad637SFrançois Tigeot 	    {{ 0xFFFF0F00, 0xFFFFFF10, 0xFFF2FF10, 0xFF32FF10,
18942a1ad637SFrançois Tigeot 	       0xFF324F10, 0xF5324F10, 0x54326F10, 0x54326F10 }, /* x.0 */
18952a1ad637SFrançois Tigeot 	     { 0xFFFFF000, 0xFFFF0100, 0xFFFFF210, 0xFFFF2310,
18962a1ad637SFrançois Tigeot 	       0xFF32F410, 0xFF324510, 0xF6324510, 0x76325410 }}; /* x.1 */
18972a1ad637SFrançois Tigeot 	int convmapid = -1;
18982a1ad637SFrançois Tigeot 	nid_t nid;
18992a1ad637SFrançois Tigeot 	uint8_t csum;
19002a1ad637SFrançois Tigeot 
19012a1ad637SFrançois Tigeot 	totalchn = AFMT_CHANNEL(ch->fmt);
19022a1ad637SFrançois Tigeot 	totalextchn = AFMT_EXTCHANNEL(ch->fmt);
19032a1ad637SFrançois Tigeot 	HDA_BOOTHVERBOSE(
19042a1ad637SFrançois Tigeot 		device_printf(ch->pdevinfo->dev,
19052a1ad637SFrançois Tigeot 		    "PCMDIR_%s: Stream setup fmt=%08x (%d.%d) speed=%d\n",
19062a1ad637SFrançois Tigeot 		    (ch->dir == PCMDIR_PLAY) ? "PLAY" : "REC",
19072a1ad637SFrançois Tigeot 		    ch->fmt, totalchn - totalextchn, totalextchn, ch->spd);
19082a1ad637SFrançois Tigeot 	);
19092a1ad637SFrançois Tigeot 	fmt = hdaa_stream_format(ch);
19102a1ad637SFrançois Tigeot 
19112a1ad637SFrançois Tigeot 	/* Set channels to I/O converters mapping for known speaker setups. */
19122a1ad637SFrançois Tigeot 	if ((as->pinset == 0x0007 || as->pinset == 0x0013) || /* Standard 5.1 */
19132a1ad637SFrançois Tigeot 	    (as->pinset == 0x0017)) /* Standard 7.1 */
19142a1ad637SFrançois Tigeot 		convmapid = (ch->dir == PCMDIR_PLAY);
19152a1ad637SFrançois Tigeot 
19162a1ad637SFrançois Tigeot 	dfmt = HDA_CMD_SET_DIGITAL_CONV_FMT1_DIGEN;
19172a1ad637SFrançois Tigeot 	if (ch->fmt & AFMT_AC3)
19182a1ad637SFrançois Tigeot 		dfmt |= HDA_CMD_SET_DIGITAL_CONV_FMT1_NAUDIO;
19192a1ad637SFrançois Tigeot 
19202a1ad637SFrançois Tigeot 	chn = 0;
19212a1ad637SFrançois Tigeot 	for (i = 0; ch->io[i] != -1; i++) {
19222a1ad637SFrançois Tigeot 		w = hdaa_widget_get(ch->devinfo, ch->io[i]);
19232a1ad637SFrançois Tigeot 		if (w == NULL)
19242a1ad637SFrançois Tigeot 			continue;
19252a1ad637SFrançois Tigeot 
19262a1ad637SFrançois Tigeot 		/* If HP redirection is enabled, but failed to use same
19272a1ad637SFrançois Tigeot 		   DAC, make last DAC to duplicate first one. */
19282a1ad637SFrançois Tigeot 		if (as->fakeredir && i == (as->pincnt - 1)) {
19292a1ad637SFrançois Tigeot 			c = (ch->sid << 4);
19302a1ad637SFrançois Tigeot 		} else {
19312a1ad637SFrançois Tigeot 			/* Map channels to I/O converters, if set. */
19322a1ad637SFrançois Tigeot 			if (convmapid >= 0)
19332a1ad637SFrançois Tigeot 				chn = (((convmap[convmapid][totalchn / 2]
19342a1ad637SFrançois Tigeot 				    >> i * 4) & 0xf) - 1) * 2;
19352a1ad637SFrançois Tigeot 			if (chn < 0 || chn >= totalchn) {
19362a1ad637SFrançois Tigeot 				c = 0;
19372a1ad637SFrançois Tigeot 			} else {
19382a1ad637SFrançois Tigeot 				c = (ch->sid << 4) | chn;
19392a1ad637SFrançois Tigeot 			}
19402a1ad637SFrançois Tigeot 		}
19412a1ad637SFrançois Tigeot 		hda_command(ch->devinfo->dev,
19422a1ad637SFrançois Tigeot 		    HDA_CMD_SET_CONV_FMT(0, ch->io[i], fmt));
19432a1ad637SFrançois Tigeot 		if (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap)) {
19442a1ad637SFrançois Tigeot 			hda_command(ch->devinfo->dev,
19452a1ad637SFrançois Tigeot 			    HDA_CMD_SET_DIGITAL_CONV_FMT1(0, ch->io[i], dfmt));
19462a1ad637SFrançois Tigeot 		}
19472a1ad637SFrançois Tigeot 		hda_command(ch->devinfo->dev,
19482a1ad637SFrançois Tigeot 		    HDA_CMD_SET_CONV_STREAM_CHAN(0, ch->io[i], c));
19492a1ad637SFrançois Tigeot 		if (HDA_PARAM_AUDIO_WIDGET_CAP_STRIPE(w->param.widget_cap)) {
19502a1ad637SFrançois Tigeot 			hda_command(ch->devinfo->dev,
19512a1ad637SFrançois Tigeot 			    HDA_CMD_SET_STRIPE_CONTROL(0, w->nid, ch->stripectl));
19522a1ad637SFrançois Tigeot 		}
19532a1ad637SFrançois Tigeot 		cchn = HDA_PARAM_AUDIO_WIDGET_CAP_CC(w->param.widget_cap);
19542a1ad637SFrançois Tigeot 		if (cchn > 1 && chn < totalchn) {
19552a1ad637SFrançois Tigeot 			cchn = min(cchn, totalchn - chn - 1);
19562a1ad637SFrançois Tigeot 			hda_command(ch->devinfo->dev,
19572a1ad637SFrançois Tigeot 			    HDA_CMD_SET_CONV_CHAN_COUNT(0, ch->io[i], cchn));
19582a1ad637SFrançois Tigeot 		}
19592a1ad637SFrançois Tigeot 		HDA_BOOTHVERBOSE(
19602a1ad637SFrançois Tigeot 			device_printf(ch->pdevinfo->dev,
19612a1ad637SFrançois Tigeot 			    "PCMDIR_%s: Stream setup nid=%d: "
19622a1ad637SFrançois Tigeot 			    "fmt=0x%04x, dfmt=0x%04x, chan=0x%04x, "
19632a1ad637SFrançois Tigeot 			    "chan_count=0x%02x, stripe=%d\n",
19642a1ad637SFrançois Tigeot 			    (ch->dir == PCMDIR_PLAY) ? "PLAY" : "REC",
19652a1ad637SFrançois Tigeot 			    ch->io[i], fmt, dfmt, c, cchn, ch->stripectl);
19662a1ad637SFrançois Tigeot 		);
19672a1ad637SFrançois Tigeot 		for (j = 0; j < 16; j++) {
19682a1ad637SFrançois Tigeot 			if (as->dacs[ch->asindex][j] != ch->io[i])
19692a1ad637SFrançois Tigeot 				continue;
19702a1ad637SFrançois Tigeot 			nid = as->pins[j];
19712a1ad637SFrançois Tigeot 			wp = hdaa_widget_get(ch->devinfo, nid);
19722a1ad637SFrançois Tigeot 			if (wp == NULL)
19732a1ad637SFrançois Tigeot 				continue;
19742a1ad637SFrançois Tigeot 			if (!HDA_PARAM_PIN_CAP_DP(wp->wclass.pin.cap) &&
19752a1ad637SFrançois Tigeot 			    !HDA_PARAM_PIN_CAP_HDMI(wp->wclass.pin.cap))
19762a1ad637SFrançois Tigeot 				continue;
19772a1ad637SFrançois Tigeot 
19782a1ad637SFrançois Tigeot 			/* Set channel mapping. */
19792a1ad637SFrançois Tigeot 			for (k = 0; k < 8; k++) {
19802a1ad637SFrançois Tigeot 				hda_command(ch->devinfo->dev,
19812a1ad637SFrançois Tigeot 				    HDA_CMD_SET_HDMI_CHAN_SLOT(0, nid,
19822a1ad637SFrançois Tigeot 				    (((hdmich[totalextchn == 0 ? 0 : 1][totalchn - 1]
19832a1ad637SFrançois Tigeot 				     >> (k * 4)) & 0xf) << 4) | k));
19842a1ad637SFrançois Tigeot 			}
19852a1ad637SFrançois Tigeot 
19862a1ad637SFrançois Tigeot 			/*
19872a1ad637SFrançois Tigeot 			 * Enable High Bit Rate (HBR) Encoded Packet Type
19882a1ad637SFrançois Tigeot 			 * (EPT), if supported and needed (8ch data).
19892a1ad637SFrançois Tigeot 			 */
19902a1ad637SFrançois Tigeot 			if (HDA_PARAM_PIN_CAP_HDMI(wp->wclass.pin.cap) &&
19912a1ad637SFrançois Tigeot 			    HDA_PARAM_PIN_CAP_HBR(wp->wclass.pin.cap)) {
19922a1ad637SFrançois Tigeot 				wp->wclass.pin.ctrl &=
19932a1ad637SFrançois Tigeot 				    ~HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE_MASK;
19942a1ad637SFrançois Tigeot 				if ((ch->fmt & AFMT_AC3) && (cchn == 7))
19952a1ad637SFrançois Tigeot 					wp->wclass.pin.ctrl |= 0x03;
19962a1ad637SFrançois Tigeot 				hda_command(ch->devinfo->dev,
19972a1ad637SFrançois Tigeot 				    HDA_CMD_SET_PIN_WIDGET_CTRL(0, nid,
19982a1ad637SFrançois Tigeot 				    wp->wclass.pin.ctrl));
19992a1ad637SFrançois Tigeot 			}
20002a1ad637SFrançois Tigeot 
20012a1ad637SFrançois Tigeot 			/* Stop audio infoframe transmission. */
20022a1ad637SFrançois Tigeot 			hda_command(ch->devinfo->dev,
20032a1ad637SFrançois Tigeot 			    HDA_CMD_SET_HDMI_DIP_INDEX(0, nid, 0x00));
20042a1ad637SFrançois Tigeot 			hda_command(ch->devinfo->dev,
20052a1ad637SFrançois Tigeot 			    HDA_CMD_SET_HDMI_DIP_XMIT(0, nid, 0x00));
20062a1ad637SFrançois Tigeot 
20072a1ad637SFrançois Tigeot 			/* Clear audio infoframe buffer. */
20082a1ad637SFrançois Tigeot 			hda_command(ch->devinfo->dev,
20092a1ad637SFrançois Tigeot 			    HDA_CMD_SET_HDMI_DIP_INDEX(0, nid, 0x00));
20102a1ad637SFrançois Tigeot 			for (k = 0; k < 32; k++)
20112a1ad637SFrançois Tigeot 				hda_command(ch->devinfo->dev,
20122a1ad637SFrançois Tigeot 				    HDA_CMD_SET_HDMI_DIP_DATA(0, nid, 0x00));
20132a1ad637SFrançois Tigeot 
20142a1ad637SFrançois Tigeot 			/* Write HDMI/DisplayPort audio infoframe. */
20152a1ad637SFrançois Tigeot 			hda_command(ch->devinfo->dev,
20162a1ad637SFrançois Tigeot 			    HDA_CMD_SET_HDMI_DIP_INDEX(0, nid, 0x00));
20172a1ad637SFrançois Tigeot 			if (w->eld != NULL && w->eld_len >= 6 &&
20182a1ad637SFrançois Tigeot 			    ((w->eld[5] >> 2) & 0x3) == 1) { /* DisplayPort */
20192a1ad637SFrançois Tigeot 				hda_command(ch->devinfo->dev,
20202a1ad637SFrançois Tigeot 				    HDA_CMD_SET_HDMI_DIP_DATA(0, nid, 0x84));
20212a1ad637SFrançois Tigeot 				hda_command(ch->devinfo->dev,
20222a1ad637SFrançois Tigeot 				    HDA_CMD_SET_HDMI_DIP_DATA(0, nid, 0x1b));
20232a1ad637SFrançois Tigeot 				hda_command(ch->devinfo->dev,
20242a1ad637SFrançois Tigeot 				    HDA_CMD_SET_HDMI_DIP_DATA(0, nid, 0x44));
20252a1ad637SFrançois Tigeot 			} else {	/* HDMI */
20262a1ad637SFrançois Tigeot 				hda_command(ch->devinfo->dev,
20272a1ad637SFrançois Tigeot 				    HDA_CMD_SET_HDMI_DIP_DATA(0, nid, 0x84));
20282a1ad637SFrançois Tigeot 				hda_command(ch->devinfo->dev,
20292a1ad637SFrançois Tigeot 				    HDA_CMD_SET_HDMI_DIP_DATA(0, nid, 0x01));
20302a1ad637SFrançois Tigeot 				hda_command(ch->devinfo->dev,
20312a1ad637SFrançois Tigeot 				    HDA_CMD_SET_HDMI_DIP_DATA(0, nid, 0x0a));
20322a1ad637SFrançois Tigeot 				csum = 0;
20332a1ad637SFrançois Tigeot 				csum -= 0x84 + 0x01 + 0x0a + (totalchn - 1) +
20342a1ad637SFrançois Tigeot 				    hdmica[totalextchn == 0 ? 0 : 1][totalchn - 1];
20352a1ad637SFrançois Tigeot 				hda_command(ch->devinfo->dev,
20362a1ad637SFrançois Tigeot 				    HDA_CMD_SET_HDMI_DIP_DATA(0, nid, csum));
20372a1ad637SFrançois Tigeot 			}
20382a1ad637SFrançois Tigeot 			hda_command(ch->devinfo->dev,
20392a1ad637SFrançois Tigeot 			    HDA_CMD_SET_HDMI_DIP_DATA(0, nid, totalchn - 1));
20402a1ad637SFrançois Tigeot 			hda_command(ch->devinfo->dev,
20412a1ad637SFrançois Tigeot 			    HDA_CMD_SET_HDMI_DIP_DATA(0, nid, 0x00));
20422a1ad637SFrançois Tigeot 			hda_command(ch->devinfo->dev,
20432a1ad637SFrançois Tigeot 			    HDA_CMD_SET_HDMI_DIP_DATA(0, nid, 0x00));
20442a1ad637SFrançois Tigeot 			hda_command(ch->devinfo->dev,
20452a1ad637SFrançois Tigeot 			    HDA_CMD_SET_HDMI_DIP_DATA(0, nid,
20462a1ad637SFrançois Tigeot 			    hdmica[totalextchn == 0 ? 0 : 1][totalchn - 1]));
20472a1ad637SFrançois Tigeot 
20482a1ad637SFrançois Tigeot 			/* Start audio infoframe transmission. */
20492a1ad637SFrançois Tigeot 			hda_command(ch->devinfo->dev,
20502a1ad637SFrançois Tigeot 			    HDA_CMD_SET_HDMI_DIP_INDEX(0, nid, 0x00));
20512a1ad637SFrançois Tigeot 			hda_command(ch->devinfo->dev,
20522a1ad637SFrançois Tigeot 			    HDA_CMD_SET_HDMI_DIP_XMIT(0, nid, 0xc0));
20532a1ad637SFrançois Tigeot 		}
20542a1ad637SFrançois Tigeot 		chn += cchn + 1;
20552a1ad637SFrançois Tigeot 	}
20562a1ad637SFrançois Tigeot }
20572a1ad637SFrançois Tigeot 
20582a1ad637SFrançois Tigeot /*
20592a1ad637SFrançois Tigeot  * Greatest Common Divisor.
20602a1ad637SFrançois Tigeot  */
20612a1ad637SFrançois Tigeot static unsigned
gcd(unsigned a,unsigned b)20622a1ad637SFrançois Tigeot gcd(unsigned a, unsigned b)
20632a1ad637SFrançois Tigeot {
20642a1ad637SFrançois Tigeot 	u_int c;
20652a1ad637SFrançois Tigeot 
20662a1ad637SFrançois Tigeot 	while (b != 0) {
20672a1ad637SFrançois Tigeot 		c = a;
20682a1ad637SFrançois Tigeot 		a = b;
20692a1ad637SFrançois Tigeot 		b = (c % b);
20702a1ad637SFrançois Tigeot 	}
20712a1ad637SFrançois Tigeot 	return (a);
20722a1ad637SFrançois Tigeot }
20732a1ad637SFrançois Tigeot 
20742a1ad637SFrançois Tigeot /*
20752a1ad637SFrançois Tigeot  * Least Common Multiple.
20762a1ad637SFrançois Tigeot  */
20772a1ad637SFrançois Tigeot static unsigned
lcm(unsigned a,unsigned b)20782a1ad637SFrançois Tigeot lcm(unsigned a, unsigned b)
20792a1ad637SFrançois Tigeot {
20802a1ad637SFrançois Tigeot 
20812a1ad637SFrançois Tigeot 	return ((a * b) / gcd(a, b));
20822a1ad637SFrançois Tigeot }
20832a1ad637SFrançois Tigeot 
20842a1ad637SFrançois Tigeot static int
hdaa_channel_setfragments(kobj_t obj,void * data,uint32_t blksz,uint32_t blkcnt)20852a1ad637SFrançois Tigeot hdaa_channel_setfragments(kobj_t obj, void *data,
20862a1ad637SFrançois Tigeot 					uint32_t blksz, uint32_t blkcnt)
20872a1ad637SFrançois Tigeot {
20882a1ad637SFrançois Tigeot 	struct hdaa_chan *ch = data;
20892a1ad637SFrançois Tigeot 
20902a1ad637SFrançois Tigeot 	blksz -= blksz % lcm(HDA_DMA_ALIGNMENT, sndbuf_getalign(ch->b));
20912a1ad637SFrançois Tigeot 
20922a1ad637SFrançois Tigeot 	if (blksz > (sndbuf_getmaxsize(ch->b) / HDA_BDL_MIN))
20932a1ad637SFrançois Tigeot 		blksz = sndbuf_getmaxsize(ch->b) / HDA_BDL_MIN;
20942a1ad637SFrançois Tigeot 	if (blksz < HDA_BLK_MIN)
20952a1ad637SFrançois Tigeot 		blksz = HDA_BLK_MIN;
20962a1ad637SFrançois Tigeot 	if (blkcnt > HDA_BDL_MAX)
20972a1ad637SFrançois Tigeot 		blkcnt = HDA_BDL_MAX;
20982a1ad637SFrançois Tigeot 	if (blkcnt < HDA_BDL_MIN)
20992a1ad637SFrançois Tigeot 		blkcnt = HDA_BDL_MIN;
21002a1ad637SFrançois Tigeot 
21012a1ad637SFrançois Tigeot 	while ((blksz * blkcnt) > sndbuf_getmaxsize(ch->b)) {
21022a1ad637SFrançois Tigeot 		if ((blkcnt >> 1) >= HDA_BDL_MIN)
21032a1ad637SFrançois Tigeot 			blkcnt >>= 1;
21042a1ad637SFrançois Tigeot 		else if ((blksz >> 1) >= HDA_BLK_MIN)
21052a1ad637SFrançois Tigeot 			blksz >>= 1;
21062a1ad637SFrançois Tigeot 		else
21072a1ad637SFrançois Tigeot 			break;
21082a1ad637SFrançois Tigeot 	}
21092a1ad637SFrançois Tigeot 
21102a1ad637SFrançois Tigeot 	if ((sndbuf_getblksz(ch->b) != blksz ||
21112a1ad637SFrançois Tigeot 	    sndbuf_getblkcnt(ch->b) != blkcnt) &&
21122a1ad637SFrançois Tigeot 	    sndbuf_resize(ch->b, blkcnt, blksz) != 0)
21132a1ad637SFrançois Tigeot 		device_printf(ch->devinfo->dev, "%s: failed blksz=%u blkcnt=%u\n",
21142a1ad637SFrançois Tigeot 		    __func__, blksz, blkcnt);
21152a1ad637SFrançois Tigeot 
21162a1ad637SFrançois Tigeot 	ch->blksz = sndbuf_getblksz(ch->b);
21172a1ad637SFrançois Tigeot 	ch->blkcnt = sndbuf_getblkcnt(ch->b);
21182a1ad637SFrançois Tigeot 
21192a1ad637SFrançois Tigeot 	return (0);
21202a1ad637SFrançois Tigeot }
21212a1ad637SFrançois Tigeot 
21222a1ad637SFrançois Tigeot static uint32_t
hdaa_channel_setblocksize(kobj_t obj,void * data,uint32_t blksz)21232a1ad637SFrançois Tigeot hdaa_channel_setblocksize(kobj_t obj, void *data, uint32_t blksz)
21242a1ad637SFrançois Tigeot {
21252a1ad637SFrançois Tigeot 	struct hdaa_chan *ch = data;
21262a1ad637SFrançois Tigeot 
21272a1ad637SFrançois Tigeot 	hdaa_channel_setfragments(obj, data, blksz, ch->pdevinfo->chan_blkcnt);
21282a1ad637SFrançois Tigeot 
21292a1ad637SFrançois Tigeot 	return (ch->blksz);
21302a1ad637SFrançois Tigeot }
21312a1ad637SFrançois Tigeot 
21322a1ad637SFrançois Tigeot static void
hdaa_channel_stop(struct hdaa_chan * ch)21332a1ad637SFrançois Tigeot hdaa_channel_stop(struct hdaa_chan *ch)
21342a1ad637SFrançois Tigeot {
21352a1ad637SFrançois Tigeot 	struct hdaa_devinfo *devinfo = ch->devinfo;
21362a1ad637SFrançois Tigeot 	struct hdaa_widget *w;
21372a1ad637SFrançois Tigeot 	int i;
21382a1ad637SFrançois Tigeot 
21392a1ad637SFrançois Tigeot 	if ((ch->flags & HDAA_CHN_RUNNING) == 0)
21402a1ad637SFrançois Tigeot 		return;
21412a1ad637SFrançois Tigeot 	ch->flags &= ~HDAA_CHN_RUNNING;
21422a1ad637SFrançois Tigeot 	HDAC_STREAM_STOP(device_get_parent(devinfo->dev), devinfo->dev,
21432a1ad637SFrançois Tigeot 	    ch->dir == PCMDIR_PLAY ? 1 : 0, ch->sid);
21442a1ad637SFrançois Tigeot 	for (i = 0; ch->io[i] != -1; i++) {
21452a1ad637SFrançois Tigeot 		w = hdaa_widget_get(ch->devinfo, ch->io[i]);
21462a1ad637SFrançois Tigeot 		if (w == NULL)
21472a1ad637SFrançois Tigeot 			continue;
21482a1ad637SFrançois Tigeot 		if (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap)) {
21492a1ad637SFrançois Tigeot 			hda_command(devinfo->dev,
21502a1ad637SFrançois Tigeot 			    HDA_CMD_SET_DIGITAL_CONV_FMT1(0, ch->io[i], 0));
21512a1ad637SFrançois Tigeot 		}
21522a1ad637SFrançois Tigeot 		hda_command(devinfo->dev,
21532a1ad637SFrançois Tigeot 		    HDA_CMD_SET_CONV_STREAM_CHAN(0, ch->io[i],
21542a1ad637SFrançois Tigeot 		    0));
21552a1ad637SFrançois Tigeot 	}
21562a1ad637SFrançois Tigeot 	HDAC_STREAM_FREE(device_get_parent(devinfo->dev), devinfo->dev,
21572a1ad637SFrançois Tigeot 	    ch->dir == PCMDIR_PLAY ? 1 : 0, ch->sid);
21582a1ad637SFrançois Tigeot }
21592a1ad637SFrançois Tigeot 
21602a1ad637SFrançois Tigeot static int
hdaa_channel_start(struct hdaa_chan * ch)21612a1ad637SFrançois Tigeot hdaa_channel_start(struct hdaa_chan *ch)
21622a1ad637SFrançois Tigeot {
21632a1ad637SFrançois Tigeot 	struct hdaa_devinfo *devinfo = ch->devinfo;
21642a1ad637SFrançois Tigeot 	uint32_t fmt;
21652a1ad637SFrançois Tigeot 
21662a1ad637SFrançois Tigeot 	fmt = hdaa_stream_format(ch);
21672a1ad637SFrançois Tigeot 	ch->stripectl = fls(ch->stripecap & hdaa_allowed_stripes(fmt)) - 1;
21682a1ad637SFrançois Tigeot 	ch->sid = HDAC_STREAM_ALLOC(device_get_parent(devinfo->dev), devinfo->dev,
21692a1ad637SFrançois Tigeot 	    ch->dir == PCMDIR_PLAY ? 1 : 0, fmt, ch->stripectl, &ch->dmapos);
21702a1ad637SFrançois Tigeot 	if (ch->sid <= 0)
21712a1ad637SFrançois Tigeot 		return (EBUSY);
21722a1ad637SFrançois Tigeot 	hdaa_audio_setup(ch);
21732a1ad637SFrançois Tigeot 	HDAC_STREAM_RESET(device_get_parent(devinfo->dev), devinfo->dev,
21742a1ad637SFrançois Tigeot 	    ch->dir == PCMDIR_PLAY ? 1 : 0, ch->sid);
21752a1ad637SFrançois Tigeot 	HDAC_STREAM_START(device_get_parent(devinfo->dev), devinfo->dev,
21762a1ad637SFrançois Tigeot 	    ch->dir == PCMDIR_PLAY ? 1 : 0, ch->sid,
21772a1ad637SFrançois Tigeot 	    sndbuf_getbufaddr(ch->b), ch->blksz, ch->blkcnt);
21782a1ad637SFrançois Tigeot 	ch->flags |= HDAA_CHN_RUNNING;
21792a1ad637SFrançois Tigeot 	return (0);
21802a1ad637SFrançois Tigeot }
21812a1ad637SFrançois Tigeot 
21822a1ad637SFrançois Tigeot static int
hdaa_channel_trigger(kobj_t obj,void * data,int go)21832a1ad637SFrançois Tigeot hdaa_channel_trigger(kobj_t obj, void *data, int go)
21842a1ad637SFrançois Tigeot {
21852a1ad637SFrançois Tigeot 	struct hdaa_chan *ch = data;
21862a1ad637SFrançois Tigeot 	int error = 0;
21872a1ad637SFrançois Tigeot 
21882a1ad637SFrançois Tigeot 	if (!PCMTRIG_COMMON(go))
21892a1ad637SFrançois Tigeot 		return (0);
21902a1ad637SFrançois Tigeot 
21912a1ad637SFrançois Tigeot 	hdaa_lock(ch->devinfo);
21922a1ad637SFrançois Tigeot 	switch (go) {
21932a1ad637SFrançois Tigeot 	case PCMTRIG_START:
21942a1ad637SFrançois Tigeot 		error = hdaa_channel_start(ch);
21952a1ad637SFrançois Tigeot 		break;
21962a1ad637SFrançois Tigeot 	case PCMTRIG_STOP:
21972a1ad637SFrançois Tigeot 	case PCMTRIG_ABORT:
21982a1ad637SFrançois Tigeot 		hdaa_channel_stop(ch);
21992a1ad637SFrançois Tigeot 		break;
22002a1ad637SFrançois Tigeot 	default:
22012a1ad637SFrançois Tigeot 		break;
22022a1ad637SFrançois Tigeot 	}
22032a1ad637SFrançois Tigeot 	hdaa_unlock(ch->devinfo);
22042a1ad637SFrançois Tigeot 
22052a1ad637SFrançois Tigeot 	return (error);
22062a1ad637SFrançois Tigeot }
22072a1ad637SFrançois Tigeot 
22082a1ad637SFrançois Tigeot static uint32_t
hdaa_channel_getptr(kobj_t obj,void * data)22092a1ad637SFrançois Tigeot hdaa_channel_getptr(kobj_t obj, void *data)
22102a1ad637SFrançois Tigeot {
22112a1ad637SFrançois Tigeot 	struct hdaa_chan *ch = data;
22122a1ad637SFrançois Tigeot 	struct hdaa_devinfo *devinfo = ch->devinfo;
22132a1ad637SFrançois Tigeot 	uint32_t ptr;
22142a1ad637SFrançois Tigeot 
22152a1ad637SFrançois Tigeot 	hdaa_lock(devinfo);
22162a1ad637SFrançois Tigeot 	if (ch->dmapos != NULL) {
22172a1ad637SFrançois Tigeot 		ptr = *(ch->dmapos);
22182a1ad637SFrançois Tigeot 	} else {
22192a1ad637SFrançois Tigeot 		ptr = HDAC_STREAM_GETPTR(
22202a1ad637SFrançois Tigeot 		    device_get_parent(devinfo->dev), devinfo->dev,
22212a1ad637SFrançois Tigeot 		    ch->dir == PCMDIR_PLAY ? 1 : 0, ch->sid);
22222a1ad637SFrançois Tigeot 	}
22232a1ad637SFrançois Tigeot 	hdaa_unlock(devinfo);
22242a1ad637SFrançois Tigeot 
22252a1ad637SFrançois Tigeot 	/*
22262a1ad637SFrançois Tigeot 	 * Round to available space and force 128 bytes aligment.
22272a1ad637SFrançois Tigeot 	 */
22282a1ad637SFrançois Tigeot 	ptr %= ch->blksz * ch->blkcnt;
22292a1ad637SFrançois Tigeot 	ptr &= HDA_BLK_ALIGN;
22302a1ad637SFrançois Tigeot 
22312a1ad637SFrançois Tigeot 	return (ptr);
22322a1ad637SFrançois Tigeot }
22332a1ad637SFrançois Tigeot 
22342a1ad637SFrançois Tigeot static struct pcmchan_caps *
hdaa_channel_getcaps(kobj_t obj,void * data)22352a1ad637SFrançois Tigeot hdaa_channel_getcaps(kobj_t obj, void *data)
22362a1ad637SFrançois Tigeot {
22372a1ad637SFrançois Tigeot 	return (&((struct hdaa_chan *)data)->caps);
22382a1ad637SFrançois Tigeot }
22392a1ad637SFrançois Tigeot 
22402a1ad637SFrançois Tigeot static kobj_method_t hdaa_channel_methods[] = {
22412a1ad637SFrançois Tigeot 	KOBJMETHOD(channel_init,		hdaa_channel_init),
22422a1ad637SFrançois Tigeot 	KOBJMETHOD(channel_setformat,		hdaa_channel_setformat),
22432a1ad637SFrançois Tigeot 	KOBJMETHOD(channel_setspeed,		hdaa_channel_setspeed),
22442a1ad637SFrançois Tigeot 	KOBJMETHOD(channel_setblocksize,	hdaa_channel_setblocksize),
22452a1ad637SFrançois Tigeot 	KOBJMETHOD(channel_setfragments,	hdaa_channel_setfragments),
22462a1ad637SFrançois Tigeot 	KOBJMETHOD(channel_trigger,		hdaa_channel_trigger),
22472a1ad637SFrançois Tigeot 	KOBJMETHOD(channel_getptr,		hdaa_channel_getptr),
22482a1ad637SFrançois Tigeot 	KOBJMETHOD(channel_getcaps,		hdaa_channel_getcaps),
22492a1ad637SFrançois Tigeot 	KOBJMETHOD_END
22502a1ad637SFrançois Tigeot };
22512a1ad637SFrançois Tigeot CHANNEL_DECLARE(hdaa_channel);
22522a1ad637SFrançois Tigeot 
22532a1ad637SFrançois Tigeot static int
hdaa_audio_ctl_ossmixer_init(struct snd_mixer * m)22542a1ad637SFrançois Tigeot hdaa_audio_ctl_ossmixer_init(struct snd_mixer *m)
22552a1ad637SFrançois Tigeot {
22562a1ad637SFrançois Tigeot 	struct hdaa_pcm_devinfo *pdevinfo = mix_getdevinfo(m);
22572a1ad637SFrançois Tigeot 	struct hdaa_devinfo *devinfo = pdevinfo->devinfo;
22582a1ad637SFrançois Tigeot 	struct hdaa_widget *w, *cw;
22592a1ad637SFrançois Tigeot 	uint32_t mask, recmask;
22602a1ad637SFrançois Tigeot 	int i, j;
22612a1ad637SFrançois Tigeot 
22622a1ad637SFrançois Tigeot 	hdaa_lock(devinfo);
22632a1ad637SFrançois Tigeot 	pdevinfo->mixer = m;
22642a1ad637SFrançois Tigeot 
22652a1ad637SFrançois Tigeot 	/* Make sure that in case of soft volume it won't stay muted. */
22662a1ad637SFrançois Tigeot 	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
22672a1ad637SFrançois Tigeot 		pdevinfo->left[i] = 100;
22682a1ad637SFrançois Tigeot 		pdevinfo->right[i] = 100;
22692a1ad637SFrançois Tigeot 	}
22702a1ad637SFrançois Tigeot 
22712a1ad637SFrançois Tigeot 	/* Declare volume controls assigned to this association. */
22722a1ad637SFrançois Tigeot 	mask = pdevinfo->ossmask;
22732a1ad637SFrançois Tigeot 	if (pdevinfo->playas >= 0) {
22742a1ad637SFrançois Tigeot 		/* Declate EAPD as ogain control. */
22752a1ad637SFrançois Tigeot 		for (i = devinfo->startnode; i < devinfo->endnode; i++) {
22762a1ad637SFrançois Tigeot 			w = hdaa_widget_get(devinfo, i);
22772a1ad637SFrançois Tigeot 			if (w == NULL || w->enable == 0)
22782a1ad637SFrançois Tigeot 				continue;
22792a1ad637SFrançois Tigeot 			if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX ||
22802a1ad637SFrançois Tigeot 			    w->param.eapdbtl == HDA_INVALID ||
22812a1ad637SFrançois Tigeot 			    w->bindas != pdevinfo->playas)
22822a1ad637SFrançois Tigeot 				continue;
22832a1ad637SFrançois Tigeot 			mask |= SOUND_MASK_OGAIN;
22842a1ad637SFrançois Tigeot 			break;
22852a1ad637SFrançois Tigeot 		}
22862a1ad637SFrançois Tigeot 
22872a1ad637SFrançois Tigeot 		/* Declare soft PCM volume if needed. */
22882a1ad637SFrançois Tigeot 		if ((mask & SOUND_MASK_PCM) == 0 ||
22892a1ad637SFrançois Tigeot 		    (devinfo->quirks & HDAA_QUIRK_SOFTPCMVOL) ||
22902a1ad637SFrançois Tigeot 		    pdevinfo->minamp[SOUND_MIXER_PCM] ==
22912a1ad637SFrançois Tigeot 		     pdevinfo->maxamp[SOUND_MIXER_PCM]) {
22922a1ad637SFrançois Tigeot 			mask |= SOUND_MASK_PCM;
22932a1ad637SFrançois Tigeot 			pcm_setflags(pdevinfo->dev, pcm_getflags(pdevinfo->dev) | SD_F_SOFTPCMVOL);
22942a1ad637SFrançois Tigeot 			HDA_BOOTHVERBOSE(
22952a1ad637SFrançois Tigeot 				device_printf(pdevinfo->dev,
22962a1ad637SFrançois Tigeot 				    "Forcing Soft PCM volume\n");
22972a1ad637SFrançois Tigeot 			);
22982a1ad637SFrançois Tigeot 		}
22992a1ad637SFrançois Tigeot 
23002a1ad637SFrançois Tigeot 		/* Declare master volume if needed. */
23012a1ad637SFrançois Tigeot 		if ((mask & SOUND_MASK_VOLUME) == 0) {
23022a1ad637SFrançois Tigeot 			mask |= SOUND_MASK_VOLUME;
23032a1ad637SFrançois Tigeot 			mix_setparentchild(m, SOUND_MIXER_VOLUME,
23042a1ad637SFrançois Tigeot 			    SOUND_MASK_PCM);
23052a1ad637SFrançois Tigeot 			mix_setrealdev(m, SOUND_MIXER_VOLUME,
23062a1ad637SFrançois Tigeot 			    SOUND_MIXER_NONE);
23072a1ad637SFrançois Tigeot 			HDA_BOOTHVERBOSE(
23082a1ad637SFrançois Tigeot 				device_printf(pdevinfo->dev,
23092a1ad637SFrançois Tigeot 				    "Forcing master volume with PCM\n");
23102a1ad637SFrançois Tigeot 			);
23112a1ad637SFrançois Tigeot 		}
23122a1ad637SFrançois Tigeot 	}
23132a1ad637SFrançois Tigeot 
23142a1ad637SFrançois Tigeot 	/* Declare record sources available to this association. */
23152a1ad637SFrançois Tigeot 	recmask = 0;
23162a1ad637SFrançois Tigeot 	if (pdevinfo->recas >= 0) {
23172a1ad637SFrançois Tigeot 		for (i = 0; i < 16; i++) {
23182a1ad637SFrançois Tigeot 			if (devinfo->as[pdevinfo->recas].dacs[0][i] < 0)
23192a1ad637SFrançois Tigeot 				continue;
23202a1ad637SFrançois Tigeot 			w = hdaa_widget_get(devinfo,
23212a1ad637SFrançois Tigeot 			    devinfo->as[pdevinfo->recas].dacs[0][i]);
23222a1ad637SFrançois Tigeot 			if (w == NULL || w->enable == 0)
23232a1ad637SFrançois Tigeot 				continue;
23242a1ad637SFrançois Tigeot 			for (j = 0; j < w->nconns; j++) {
23252a1ad637SFrançois Tigeot 				if (w->connsenable[j] == 0)
23262a1ad637SFrançois Tigeot 					continue;
23272a1ad637SFrançois Tigeot 				cw = hdaa_widget_get(devinfo, w->conns[j]);
23282a1ad637SFrançois Tigeot 				if (cw == NULL || cw->enable == 0)
23292a1ad637SFrançois Tigeot 					continue;
23302a1ad637SFrançois Tigeot 				if (cw->bindas != pdevinfo->recas &&
23312a1ad637SFrançois Tigeot 				    cw->bindas != -2)
23322a1ad637SFrançois Tigeot 					continue;
23332a1ad637SFrançois Tigeot 				recmask |= cw->ossmask;
23342a1ad637SFrançois Tigeot 			}
23352a1ad637SFrançois Tigeot 		}
23362a1ad637SFrançois Tigeot 	}
23372a1ad637SFrançois Tigeot 
23382a1ad637SFrançois Tigeot 	recmask &= (1 << SOUND_MIXER_NRDEVICES) - 1;
23392a1ad637SFrançois Tigeot 	mask &= (1 << SOUND_MIXER_NRDEVICES) - 1;
23402a1ad637SFrançois Tigeot 	pdevinfo->ossmask = mask;
23412a1ad637SFrançois Tigeot 
23422a1ad637SFrançois Tigeot 	mix_setrecdevs(m, recmask);
23432a1ad637SFrançois Tigeot 	mix_setdevs(m, mask);
23442a1ad637SFrançois Tigeot 
23452a1ad637SFrançois Tigeot 	hdaa_unlock(devinfo);
23462a1ad637SFrançois Tigeot 
23472a1ad637SFrançois Tigeot 	return (0);
23482a1ad637SFrançois Tigeot }
23492a1ad637SFrançois Tigeot 
23502a1ad637SFrançois Tigeot /*
23512a1ad637SFrançois Tigeot  * Update amplification per pdevinfo per ossdev, calculate summary coefficient
23522a1ad637SFrançois Tigeot  * and write it to codec, update *left and *right to reflect remaining error.
23532a1ad637SFrançois Tigeot  */
23542a1ad637SFrançois Tigeot static void
hdaa_audio_ctl_dev_set(struct hdaa_audio_ctl * ctl,int ossdev,int mute,int * left,int * right)23552a1ad637SFrançois Tigeot hdaa_audio_ctl_dev_set(struct hdaa_audio_ctl *ctl, int ossdev,
23562a1ad637SFrançois Tigeot     int mute, int *left, int *right)
23572a1ad637SFrançois Tigeot {
23582a1ad637SFrançois Tigeot 	int i, zleft, zright, sleft, sright, smute, lval, rval;
23592a1ad637SFrançois Tigeot 
23602a1ad637SFrançois Tigeot 	ctl->devleft[ossdev] = *left;
23612a1ad637SFrançois Tigeot 	ctl->devright[ossdev] = *right;
23622a1ad637SFrançois Tigeot 	ctl->devmute[ossdev] = mute;
23632a1ad637SFrançois Tigeot 	smute = sleft = sright = zleft = zright = 0;
23642a1ad637SFrançois Tigeot 	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
23652a1ad637SFrançois Tigeot 		sleft += ctl->devleft[i];
23662a1ad637SFrançois Tigeot 		sright += ctl->devright[i];
23672a1ad637SFrançois Tigeot 		smute |= ctl->devmute[i];
23682a1ad637SFrançois Tigeot 		if (i == ossdev)
23692a1ad637SFrançois Tigeot 			continue;
23702a1ad637SFrançois Tigeot 		zleft += ctl->devleft[i];
23712a1ad637SFrançois Tigeot 		zright += ctl->devright[i];
23722a1ad637SFrançois Tigeot 	}
23732a1ad637SFrançois Tigeot 	lval = QDB2VAL(ctl, sleft);
23742a1ad637SFrançois Tigeot 	rval = QDB2VAL(ctl, sright);
23752a1ad637SFrançois Tigeot 	hdaa_audio_ctl_amp_set(ctl, smute, lval, rval);
23762a1ad637SFrançois Tigeot 	*left -= VAL2QDB(ctl, lval) - VAL2QDB(ctl, QDB2VAL(ctl, zleft));
23772a1ad637SFrançois Tigeot 	*right -= VAL2QDB(ctl, rval) - VAL2QDB(ctl, QDB2VAL(ctl, zright));
23782a1ad637SFrançois Tigeot }
23792a1ad637SFrançois Tigeot 
23802a1ad637SFrançois Tigeot /*
23812a1ad637SFrançois Tigeot  * Trace signal from source, setting volumes on the way.
23822a1ad637SFrançois Tigeot  */
23832a1ad637SFrançois Tigeot static void
hdaa_audio_ctl_source_volume(struct hdaa_pcm_devinfo * pdevinfo,int ossdev,nid_t nid,int index,int mute,int left,int right,int depth)23842a1ad637SFrançois Tigeot hdaa_audio_ctl_source_volume(struct hdaa_pcm_devinfo *pdevinfo,
23852a1ad637SFrançois Tigeot     int ossdev, nid_t nid, int index, int mute, int left, int right, int depth)
23862a1ad637SFrançois Tigeot {
23872a1ad637SFrançois Tigeot 	struct hdaa_devinfo *devinfo = pdevinfo->devinfo;
23882a1ad637SFrançois Tigeot 	struct hdaa_widget *w, *wc;
23892a1ad637SFrançois Tigeot 	struct hdaa_audio_ctl *ctl;
23902a1ad637SFrançois Tigeot 	int i, j, conns = 0;
23912a1ad637SFrançois Tigeot 
23922a1ad637SFrançois Tigeot 	if (depth > HDA_PARSE_MAXDEPTH)
23932a1ad637SFrançois Tigeot 		return;
23942a1ad637SFrançois Tigeot 
23952a1ad637SFrançois Tigeot 	w = hdaa_widget_get(devinfo, nid);
23962a1ad637SFrançois Tigeot 	if (w == NULL || w->enable == 0)
23972a1ad637SFrançois Tigeot 		return;
23982a1ad637SFrançois Tigeot 
23992a1ad637SFrançois Tigeot 	/* Count number of active inputs. */
24002a1ad637SFrançois Tigeot 	if (depth > 0) {
24012a1ad637SFrançois Tigeot 		for (j = 0; j < w->nconns; j++) {
24022a1ad637SFrançois Tigeot 			if (!w->connsenable[j])
24032a1ad637SFrançois Tigeot 				continue;
24042a1ad637SFrançois Tigeot 			conns++;
24052a1ad637SFrançois Tigeot 		}
24062a1ad637SFrançois Tigeot 	}
24072a1ad637SFrançois Tigeot 
24082a1ad637SFrançois Tigeot 	/* If this is not a first step - use input mixer.
24092a1ad637SFrançois Tigeot 	   Pins have common input ctl so care must be taken. */
24102a1ad637SFrançois Tigeot 	if (depth > 0 && (conns == 1 ||
24112a1ad637SFrançois Tigeot 	    w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX)) {
24122a1ad637SFrançois Tigeot 		ctl = hdaa_audio_ctl_amp_get(devinfo, w->nid, HDAA_CTL_IN,
24132a1ad637SFrançois Tigeot 		    index, 1);
24142a1ad637SFrançois Tigeot 		if (ctl)
24152a1ad637SFrançois Tigeot 			hdaa_audio_ctl_dev_set(ctl, ossdev, mute, &left, &right);
24162a1ad637SFrançois Tigeot 	}
24172a1ad637SFrançois Tigeot 
24182a1ad637SFrançois Tigeot 	/* If widget has own ossdev - not traverse it.
24192a1ad637SFrançois Tigeot 	   It will be traversed on it's own. */
24202a1ad637SFrançois Tigeot 	if (w->ossdev >= 0 && depth > 0)
24212a1ad637SFrançois Tigeot 		return;
24222a1ad637SFrançois Tigeot 
24232a1ad637SFrançois Tigeot 	/* We must not traverse pin */
24242a1ad637SFrançois Tigeot 	if ((w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT ||
24252a1ad637SFrançois Tigeot 	    w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) &&
24262a1ad637SFrançois Tigeot 	    depth > 0)
24272a1ad637SFrançois Tigeot 		return;
24282a1ad637SFrançois Tigeot 
24292a1ad637SFrançois Tigeot 	/*
24302a1ad637SFrançois Tigeot 	 * If signals mixed, we can't assign controls farther.
24312a1ad637SFrançois Tigeot 	 * Ignore this on depth zero. Caller must knows why.
24322a1ad637SFrançois Tigeot 	 */
24332a1ad637SFrançois Tigeot 	if (conns > 1 &&
24342a1ad637SFrançois Tigeot 	    (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER ||
24352a1ad637SFrançois Tigeot 	     w->selconn != index))
24362a1ad637SFrançois Tigeot 		return;
24372a1ad637SFrançois Tigeot 
24382a1ad637SFrançois Tigeot 	ctl = hdaa_audio_ctl_amp_get(devinfo, w->nid, HDAA_CTL_OUT, -1, 1);
24392a1ad637SFrançois Tigeot 	if (ctl)
24402a1ad637SFrançois Tigeot 		hdaa_audio_ctl_dev_set(ctl, ossdev, mute, &left, &right);
24412a1ad637SFrançois Tigeot 
24422a1ad637SFrançois Tigeot 	for (i = devinfo->startnode; i < devinfo->endnode; i++) {
24432a1ad637SFrançois Tigeot 		wc = hdaa_widget_get(devinfo, i);
24442a1ad637SFrançois Tigeot 		if (wc == NULL || wc->enable == 0)
24452a1ad637SFrançois Tigeot 			continue;
24462a1ad637SFrançois Tigeot 		for (j = 0; j < wc->nconns; j++) {
24472a1ad637SFrançois Tigeot 			if (wc->connsenable[j] && wc->conns[j] == nid) {
24482a1ad637SFrançois Tigeot 				hdaa_audio_ctl_source_volume(pdevinfo, ossdev,
24492a1ad637SFrançois Tigeot 				    wc->nid, j, mute, left, right, depth + 1);
24502a1ad637SFrançois Tigeot 			}
24512a1ad637SFrançois Tigeot 		}
24522a1ad637SFrançois Tigeot 	}
24532a1ad637SFrançois Tigeot 	return;
24542a1ad637SFrançois Tigeot }
24552a1ad637SFrançois Tigeot 
24562a1ad637SFrançois Tigeot /*
24572a1ad637SFrançois Tigeot  * Trace signal from destination, setting volumes on the way.
24582a1ad637SFrançois Tigeot  */
24592a1ad637SFrançois Tigeot static void
hdaa_audio_ctl_dest_volume(struct hdaa_pcm_devinfo * pdevinfo,int ossdev,nid_t nid,int index,int mute,int left,int right,int depth)24602a1ad637SFrançois Tigeot hdaa_audio_ctl_dest_volume(struct hdaa_pcm_devinfo *pdevinfo,
24612a1ad637SFrançois Tigeot     int ossdev, nid_t nid, int index, int mute, int left, int right, int depth)
24622a1ad637SFrançois Tigeot {
24632a1ad637SFrançois Tigeot 	struct hdaa_devinfo *devinfo = pdevinfo->devinfo;
24642a1ad637SFrançois Tigeot 	struct hdaa_audio_as *as = devinfo->as;
24652a1ad637SFrançois Tigeot 	struct hdaa_widget *w, *wc;
24662a1ad637SFrançois Tigeot 	struct hdaa_audio_ctl *ctl;
24672a1ad637SFrançois Tigeot 	int i, j, consumers, cleft, cright;
24682a1ad637SFrançois Tigeot 
24692a1ad637SFrançois Tigeot 	if (depth > HDA_PARSE_MAXDEPTH)
24702a1ad637SFrançois Tigeot 		return;
24712a1ad637SFrançois Tigeot 
24722a1ad637SFrançois Tigeot 	w = hdaa_widget_get(devinfo, nid);
24732a1ad637SFrançois Tigeot 	if (w == NULL || w->enable == 0)
24742a1ad637SFrançois Tigeot 		return;
24752a1ad637SFrançois Tigeot 
24762a1ad637SFrançois Tigeot 	if (depth > 0) {
24772a1ad637SFrançois Tigeot 		/* If this node produce output for several consumers,
24782a1ad637SFrançois Tigeot 		   we can't touch it. */
24792a1ad637SFrançois Tigeot 		consumers = 0;
24802a1ad637SFrançois Tigeot 		for (i = devinfo->startnode; i < devinfo->endnode; i++) {
24812a1ad637SFrançois Tigeot 			wc = hdaa_widget_get(devinfo, i);
24822a1ad637SFrançois Tigeot 			if (wc == NULL || wc->enable == 0)
24832a1ad637SFrançois Tigeot 				continue;
24842a1ad637SFrançois Tigeot 			for (j = 0; j < wc->nconns; j++) {
24852a1ad637SFrançois Tigeot 				if (wc->connsenable[j] && wc->conns[j] == nid)
24862a1ad637SFrançois Tigeot 					consumers++;
24872a1ad637SFrançois Tigeot 			}
24882a1ad637SFrançois Tigeot 		}
24892a1ad637SFrançois Tigeot 		/* The only exception is if real HP redirection is configured
24902a1ad637SFrançois Tigeot 		   and this is a duplication point.
24912a1ad637SFrançois Tigeot 		   XXX: Actually exception is not completely correct.
24922a1ad637SFrançois Tigeot 		   XXX: Duplication point check is not perfect. */
24932a1ad637SFrançois Tigeot 		if ((consumers == 2 && (w->bindas < 0 ||
24942a1ad637SFrançois Tigeot 		    as[w->bindas].hpredir < 0 || as[w->bindas].fakeredir ||
24952a1ad637SFrançois Tigeot 		    (w->bindseqmask & (1 << 15)) == 0)) ||
24962a1ad637SFrançois Tigeot 		    consumers > 2)
24972a1ad637SFrançois Tigeot 			return;
24982a1ad637SFrançois Tigeot 
24992a1ad637SFrançois Tigeot 		/* Else use it's output mixer. */
25002a1ad637SFrançois Tigeot 		ctl = hdaa_audio_ctl_amp_get(devinfo, w->nid,
25012a1ad637SFrançois Tigeot 		    HDAA_CTL_OUT, -1, 1);
25022a1ad637SFrançois Tigeot 		if (ctl)
25032a1ad637SFrançois Tigeot 			hdaa_audio_ctl_dev_set(ctl, ossdev, mute, &left, &right);
25042a1ad637SFrançois Tigeot 	}
25052a1ad637SFrançois Tigeot 
25062a1ad637SFrançois Tigeot 	/* We must not traverse pin */
25072a1ad637SFrançois Tigeot 	if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX &&
25082a1ad637SFrançois Tigeot 	    depth > 0)
25092a1ad637SFrançois Tigeot 		return;
25102a1ad637SFrançois Tigeot 
25112a1ad637SFrançois Tigeot 	for (i = 0; i < w->nconns; i++) {
25122a1ad637SFrançois Tigeot 		if (w->connsenable[i] == 0)
25132a1ad637SFrançois Tigeot 			continue;
25142a1ad637SFrançois Tigeot 		if (index >= 0 && i != index)
25152a1ad637SFrançois Tigeot 			continue;
25162a1ad637SFrançois Tigeot 		cleft = left;
25172a1ad637SFrançois Tigeot 		cright = right;
25182a1ad637SFrançois Tigeot 		ctl = hdaa_audio_ctl_amp_get(devinfo, w->nid,
25192a1ad637SFrançois Tigeot 		    HDAA_CTL_IN, i, 1);
25202a1ad637SFrançois Tigeot 		if (ctl)
25212a1ad637SFrançois Tigeot 			hdaa_audio_ctl_dev_set(ctl, ossdev, mute, &cleft, &cright);
25222a1ad637SFrançois Tigeot 		hdaa_audio_ctl_dest_volume(pdevinfo, ossdev, w->conns[i], -1,
25232a1ad637SFrançois Tigeot 		    mute, cleft, cright, depth + 1);
25242a1ad637SFrançois Tigeot 	}
25252a1ad637SFrançois Tigeot }
25262a1ad637SFrançois Tigeot 
25272a1ad637SFrançois Tigeot /*
25282a1ad637SFrançois Tigeot  * Set volumes for the specified pdevinfo and ossdev.
25292a1ad637SFrançois Tigeot  */
25302a1ad637SFrançois Tigeot static void
hdaa_audio_ctl_dev_volume(struct hdaa_pcm_devinfo * pdevinfo,unsigned dev)25312a1ad637SFrançois Tigeot hdaa_audio_ctl_dev_volume(struct hdaa_pcm_devinfo *pdevinfo, unsigned dev)
25322a1ad637SFrançois Tigeot {
25332a1ad637SFrançois Tigeot 	struct hdaa_devinfo *devinfo = pdevinfo->devinfo;
25342a1ad637SFrançois Tigeot 	struct hdaa_widget *w, *cw;
25352a1ad637SFrançois Tigeot 	uint32_t mute;
25362a1ad637SFrançois Tigeot 	int lvol, rvol;
25372a1ad637SFrançois Tigeot 	int i, j;
25382a1ad637SFrançois Tigeot 
25392a1ad637SFrançois Tigeot 	mute = 0;
25402a1ad637SFrançois Tigeot 	if (pdevinfo->left[dev] == 0) {
25412a1ad637SFrançois Tigeot 		mute |= HDAA_AMP_MUTE_LEFT;
25422a1ad637SFrançois Tigeot 		lvol = -4000;
25432a1ad637SFrançois Tigeot 	} else
25442a1ad637SFrançois Tigeot 		lvol = ((pdevinfo->maxamp[dev] - pdevinfo->minamp[dev]) *
25452a1ad637SFrançois Tigeot 		    pdevinfo->left[dev] + 50) / 100 + pdevinfo->minamp[dev];
25462a1ad637SFrançois Tigeot 	if (pdevinfo->right[dev] == 0) {
25472a1ad637SFrançois Tigeot 		mute |= HDAA_AMP_MUTE_RIGHT;
25482a1ad637SFrançois Tigeot 		rvol = -4000;
25492a1ad637SFrançois Tigeot 	} else
25502a1ad637SFrançois Tigeot 		rvol = ((pdevinfo->maxamp[dev] - pdevinfo->minamp[dev]) *
25512a1ad637SFrançois Tigeot 		    pdevinfo->right[dev] + 50) / 100 + pdevinfo->minamp[dev];
25522a1ad637SFrançois Tigeot 	for (i = devinfo->startnode; i < devinfo->endnode; i++) {
25532a1ad637SFrançois Tigeot 		w = hdaa_widget_get(devinfo, i);
25542a1ad637SFrançois Tigeot 		if (w == NULL || w->enable == 0)
25552a1ad637SFrançois Tigeot 			continue;
25562a1ad637SFrançois Tigeot 		if (w->bindas < 0) {
25572a1ad637SFrançois Tigeot 			if (pdevinfo->index != 0)
25582a1ad637SFrançois Tigeot 				continue;
25592a1ad637SFrançois Tigeot 		} else {
25602a1ad637SFrançois Tigeot 			if (w->bindas != pdevinfo->playas &&
25612a1ad637SFrançois Tigeot 			    w->bindas != pdevinfo->recas)
25622a1ad637SFrançois Tigeot 				continue;
25632a1ad637SFrançois Tigeot 		}
25642a1ad637SFrançois Tigeot 		if (dev == SOUND_MIXER_RECLEV &&
25652a1ad637SFrançois Tigeot 		    w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT) {
25662a1ad637SFrançois Tigeot 			hdaa_audio_ctl_dest_volume(pdevinfo, dev,
25672a1ad637SFrançois Tigeot 			    w->nid, -1, mute, lvol, rvol, 0);
25682a1ad637SFrançois Tigeot 			continue;
25692a1ad637SFrançois Tigeot 		}
25702a1ad637SFrançois Tigeot 		if (dev == SOUND_MIXER_VOLUME &&
25712a1ad637SFrançois Tigeot 		    w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX &&
25722a1ad637SFrançois Tigeot 		    devinfo->as[w->bindas].dir == HDAA_CTL_OUT) {
25732a1ad637SFrançois Tigeot 			hdaa_audio_ctl_dest_volume(pdevinfo, dev,
25742a1ad637SFrançois Tigeot 			    w->nid, -1, mute, lvol, rvol, 0);
25752a1ad637SFrançois Tigeot 			continue;
25762a1ad637SFrançois Tigeot 		}
25772a1ad637SFrançois Tigeot 		if (dev == SOUND_MIXER_IGAIN &&
25782a1ad637SFrançois Tigeot 		    w->pflags & HDAA_ADC_MONITOR) {
25792a1ad637SFrançois Tigeot 			for (j = 0; j < w->nconns; j++) {
25802a1ad637SFrançois Tigeot 				if (!w->connsenable[j])
25812a1ad637SFrançois Tigeot 				    continue;
25822a1ad637SFrançois Tigeot 				cw = hdaa_widget_get(devinfo, w->conns[j]);
25832a1ad637SFrançois Tigeot 				if (cw == NULL || cw->enable == 0)
25842a1ad637SFrançois Tigeot 				    continue;
25852a1ad637SFrançois Tigeot 				if (cw->bindas == -1)
25862a1ad637SFrançois Tigeot 				    continue;
25872a1ad637SFrançois Tigeot 				if (cw->bindas >= 0 &&
25882a1ad637SFrançois Tigeot 				    devinfo->as[cw->bindas].dir != HDAA_CTL_IN)
25892a1ad637SFrançois Tigeot 					continue;
25902a1ad637SFrançois Tigeot 				hdaa_audio_ctl_dest_volume(pdevinfo, dev,
25912a1ad637SFrançois Tigeot 				    w->nid, j, mute, lvol, rvol, 0);
25922a1ad637SFrançois Tigeot 			}
25932a1ad637SFrançois Tigeot 			continue;
25942a1ad637SFrançois Tigeot 		}
25952a1ad637SFrançois Tigeot 		if (w->ossdev != dev)
25962a1ad637SFrançois Tigeot 			continue;
25972a1ad637SFrançois Tigeot 		hdaa_audio_ctl_source_volume(pdevinfo, dev,
25982a1ad637SFrançois Tigeot 		    w->nid, -1, mute, lvol, rvol, 0);
25992a1ad637SFrançois Tigeot 		if (dev == SOUND_MIXER_IMIX && (w->pflags & HDAA_IMIX_AS_DST))
26002a1ad637SFrançois Tigeot 			hdaa_audio_ctl_dest_volume(pdevinfo, dev,
26012a1ad637SFrançois Tigeot 			    w->nid, -1, mute, lvol, rvol, 0);
26022a1ad637SFrançois Tigeot 	}
26032a1ad637SFrançois Tigeot }
26042a1ad637SFrançois Tigeot 
26052a1ad637SFrançois Tigeot /*
26062a1ad637SFrançois Tigeot  * OSS Mixer set method.
26072a1ad637SFrançois Tigeot  */
26082a1ad637SFrançois Tigeot static int
hdaa_audio_ctl_ossmixer_set(struct snd_mixer * m,unsigned dev,unsigned left,unsigned right)26092a1ad637SFrançois Tigeot hdaa_audio_ctl_ossmixer_set(struct snd_mixer *m, unsigned dev,
26102a1ad637SFrançois Tigeot 					unsigned left, unsigned right)
26112a1ad637SFrançois Tigeot {
26122a1ad637SFrançois Tigeot 	struct hdaa_pcm_devinfo *pdevinfo = mix_getdevinfo(m);
26132a1ad637SFrançois Tigeot 	struct hdaa_devinfo *devinfo = pdevinfo->devinfo;
26142a1ad637SFrançois Tigeot 	struct hdaa_widget *w;
26152a1ad637SFrançois Tigeot 	int i;
26162a1ad637SFrançois Tigeot 
26172a1ad637SFrançois Tigeot 	hdaa_lock(devinfo);
26182a1ad637SFrançois Tigeot 
26192a1ad637SFrançois Tigeot 	/* Save new values. */
26202a1ad637SFrançois Tigeot 	pdevinfo->left[dev] = left;
26212a1ad637SFrançois Tigeot 	pdevinfo->right[dev] = right;
26222a1ad637SFrançois Tigeot 
26232a1ad637SFrançois Tigeot 	/* 'ogain' is the special case implemented with EAPD. */
26242a1ad637SFrançois Tigeot 	if (dev == SOUND_MIXER_OGAIN) {
26252a1ad637SFrançois Tigeot 		uint32_t orig;
26262a1ad637SFrançois Tigeot 		w = NULL;
26272a1ad637SFrançois Tigeot 		for (i = devinfo->startnode; i < devinfo->endnode; i++) {
26282a1ad637SFrançois Tigeot 			w = hdaa_widget_get(devinfo, i);
26292a1ad637SFrançois Tigeot 			if (w == NULL || w->enable == 0)
26302a1ad637SFrançois Tigeot 				continue;
26312a1ad637SFrançois Tigeot 			if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX ||
26322a1ad637SFrançois Tigeot 			    w->param.eapdbtl == HDA_INVALID)
26332a1ad637SFrançois Tigeot 				continue;
26342a1ad637SFrançois Tigeot 			break;
26352a1ad637SFrançois Tigeot 		}
26362a1ad637SFrançois Tigeot 		if (i >= devinfo->endnode) {
26372a1ad637SFrançois Tigeot 			hdaa_unlock(devinfo);
26382a1ad637SFrançois Tigeot 			return (-1);
26392a1ad637SFrançois Tigeot 		}
26402a1ad637SFrançois Tigeot 		orig = w->param.eapdbtl;
26412a1ad637SFrançois Tigeot 		if (left == 0)
26422a1ad637SFrançois Tigeot 			w->param.eapdbtl &= ~HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD;
26432a1ad637SFrançois Tigeot 		else
26442a1ad637SFrançois Tigeot 			w->param.eapdbtl |= HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD;
26452a1ad637SFrançois Tigeot 		if (orig != w->param.eapdbtl) {
26462a1ad637SFrançois Tigeot 			uint32_t val;
26472a1ad637SFrançois Tigeot 
26482a1ad637SFrançois Tigeot 			val = w->param.eapdbtl;
26492a1ad637SFrançois Tigeot 			if (devinfo->quirks & HDAA_QUIRK_EAPDINV)
26502a1ad637SFrançois Tigeot 				val ^= HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD;
26512a1ad637SFrançois Tigeot 			hda_command(devinfo->dev,
26522a1ad637SFrançois Tigeot 			    HDA_CMD_SET_EAPD_BTL_ENABLE(0, w->nid, val));
26532a1ad637SFrançois Tigeot 		}
26542a1ad637SFrançois Tigeot 		hdaa_unlock(devinfo);
26552a1ad637SFrançois Tigeot 		return (left | (left << 8));
26562a1ad637SFrançois Tigeot 	}
26572a1ad637SFrançois Tigeot 
26582a1ad637SFrançois Tigeot 	/* Recalculate all controls related to this OSS device. */
26592a1ad637SFrançois Tigeot 	hdaa_audio_ctl_dev_volume(pdevinfo, dev);
26602a1ad637SFrançois Tigeot 
26612a1ad637SFrançois Tigeot 	hdaa_unlock(devinfo);
26622a1ad637SFrançois Tigeot 	return (left | (right << 8));
26632a1ad637SFrançois Tigeot }
26642a1ad637SFrançois Tigeot 
26652a1ad637SFrançois Tigeot /*
26662a1ad637SFrançois Tigeot  * Set mixer settings to our own default values:
26672a1ad637SFrançois Tigeot  * +20dB for mics, -10dB for analog vol, mute for igain, 0dB for others.
26682a1ad637SFrançois Tigeot  */
26692a1ad637SFrançois Tigeot static void
hdaa_audio_ctl_set_defaults(struct hdaa_pcm_devinfo * pdevinfo)26702a1ad637SFrançois Tigeot hdaa_audio_ctl_set_defaults(struct hdaa_pcm_devinfo *pdevinfo)
26712a1ad637SFrançois Tigeot {
26722a1ad637SFrançois Tigeot 	int amp, vol, dev;
26732a1ad637SFrançois Tigeot 
26742a1ad637SFrançois Tigeot 	for (dev = 0; dev < SOUND_MIXER_NRDEVICES; dev++) {
26752a1ad637SFrançois Tigeot 		if ((pdevinfo->ossmask & (1 << dev)) == 0)
26762a1ad637SFrançois Tigeot 			continue;
26772a1ad637SFrançois Tigeot 
26786a8bb22dSSascha Wildner 		/* If the value was overridden, leave it as is. */
26792a1ad637SFrançois Tigeot 		if (resource_int_value(device_get_name(pdevinfo->dev),
26802a1ad637SFrançois Tigeot 		    device_get_unit(pdevinfo->dev), ossnames[dev], &vol) == 0)
26812a1ad637SFrançois Tigeot 			continue;
26822a1ad637SFrançois Tigeot 
26832a1ad637SFrançois Tigeot 		vol = -1;
26842a1ad637SFrançois Tigeot 		if (dev == SOUND_MIXER_OGAIN)
26852a1ad637SFrançois Tigeot 			vol = 100;
26862a1ad637SFrançois Tigeot 		else if (dev == SOUND_MIXER_IGAIN)
26872a1ad637SFrançois Tigeot 			vol = 0;
26882a1ad637SFrançois Tigeot 		else if (dev == SOUND_MIXER_MIC ||
26892a1ad637SFrançois Tigeot 		    dev == SOUND_MIXER_MONITOR)
26902a1ad637SFrançois Tigeot 			amp = 20 * 4;	/* +20dB */
26912a1ad637SFrançois Tigeot 		else if (dev == SOUND_MIXER_VOLUME && !pdevinfo->digital)
26922a1ad637SFrançois Tigeot 			amp = -10 * 4;	/* -10dB */
26932a1ad637SFrançois Tigeot 		else
26942a1ad637SFrançois Tigeot 			amp = 0;
26952a1ad637SFrançois Tigeot 		if (vol < 0 &&
26962a1ad637SFrançois Tigeot 		    (pdevinfo->maxamp[dev] - pdevinfo->minamp[dev]) <= 0) {
26972a1ad637SFrançois Tigeot 			vol = 100;
26982a1ad637SFrançois Tigeot 		} else if (vol < 0) {
26992a1ad637SFrançois Tigeot 			vol = ((amp - pdevinfo->minamp[dev]) * 100 +
27002a1ad637SFrançois Tigeot 			    (pdevinfo->maxamp[dev] - pdevinfo->minamp[dev]) / 2) /
27012a1ad637SFrançois Tigeot 			    (pdevinfo->maxamp[dev] - pdevinfo->minamp[dev]);
27022a1ad637SFrançois Tigeot 			vol = imin(imax(vol, 1), 100);
27032a1ad637SFrançois Tigeot 		}
27042a1ad637SFrançois Tigeot 		mix_set(pdevinfo->mixer, dev, vol, vol);
27052a1ad637SFrançois Tigeot 	}
27062a1ad637SFrançois Tigeot }
27072a1ad637SFrançois Tigeot 
27082a1ad637SFrançois Tigeot /*
27092a1ad637SFrançois Tigeot  * Recursively commutate specified record source.
27102a1ad637SFrançois Tigeot  */
27112a1ad637SFrançois Tigeot static uint32_t
hdaa_audio_ctl_recsel_comm(struct hdaa_pcm_devinfo * pdevinfo,uint32_t src,nid_t nid,int depth)27122a1ad637SFrançois Tigeot hdaa_audio_ctl_recsel_comm(struct hdaa_pcm_devinfo *pdevinfo, uint32_t src, nid_t nid, int depth)
27132a1ad637SFrançois Tigeot {
27142a1ad637SFrançois Tigeot 	struct hdaa_devinfo *devinfo = pdevinfo->devinfo;
27152a1ad637SFrançois Tigeot 	struct hdaa_widget *w, *cw;
27162a1ad637SFrançois Tigeot 	struct hdaa_audio_ctl *ctl;
27172a1ad637SFrançois Tigeot 	char buf[64];
27182a1ad637SFrançois Tigeot 	int i, muted;
27192a1ad637SFrançois Tigeot 	uint32_t res = 0;
27202a1ad637SFrançois Tigeot 
27212a1ad637SFrançois Tigeot 	if (depth > HDA_PARSE_MAXDEPTH)
27222a1ad637SFrançois Tigeot 		return (0);
27232a1ad637SFrançois Tigeot 
27242a1ad637SFrançois Tigeot 	w = hdaa_widget_get(devinfo, nid);
27252a1ad637SFrançois Tigeot 	if (w == NULL || w->enable == 0)
27262a1ad637SFrançois Tigeot 		return (0);
27272a1ad637SFrançois Tigeot 
27282a1ad637SFrançois Tigeot 	for (i = 0; i < w->nconns; i++) {
27292a1ad637SFrançois Tigeot 		if (w->connsenable[i] == 0)
27302a1ad637SFrançois Tigeot 			continue;
27312a1ad637SFrançois Tigeot 		cw = hdaa_widget_get(devinfo, w->conns[i]);
27322a1ad637SFrançois Tigeot 		if (cw == NULL || cw->enable == 0 || cw->bindas == -1)
27332a1ad637SFrançois Tigeot 			continue;
27342a1ad637SFrançois Tigeot 		/* Call recursively to trace signal to it's source if needed. */
27352a1ad637SFrançois Tigeot 		if ((src & cw->ossmask) != 0) {
27362a1ad637SFrançois Tigeot 			if (cw->ossdev < 0) {
27372a1ad637SFrançois Tigeot 				res |= hdaa_audio_ctl_recsel_comm(pdevinfo, src,
27382a1ad637SFrançois Tigeot 				    w->conns[i], depth + 1);
27392a1ad637SFrançois Tigeot 			} else {
27402a1ad637SFrançois Tigeot 				res |= cw->ossmask;
27412a1ad637SFrançois Tigeot 			}
27422a1ad637SFrançois Tigeot 		}
27432a1ad637SFrançois Tigeot 		/* We have two special cases: mixers and others (selectors). */
27442a1ad637SFrançois Tigeot 		if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) {
27452a1ad637SFrançois Tigeot 			ctl = hdaa_audio_ctl_amp_get(devinfo,
27462a1ad637SFrançois Tigeot 			    w->nid, HDAA_CTL_IN, i, 1);
27472a1ad637SFrançois Tigeot 			if (ctl == NULL)
27482a1ad637SFrançois Tigeot 				continue;
27492a1ad637SFrançois Tigeot 			/* If we have input control on this node mute them
27502a1ad637SFrançois Tigeot 			 * according to requested sources. */
27512a1ad637SFrançois Tigeot 			muted = (src & cw->ossmask) ? 0 : 1;
27522a1ad637SFrançois Tigeot 			if (muted != ctl->forcemute) {
27532a1ad637SFrançois Tigeot 				ctl->forcemute = muted;
27542a1ad637SFrançois Tigeot 				hdaa_audio_ctl_amp_set(ctl,
27552a1ad637SFrançois Tigeot 				    HDAA_AMP_MUTE_DEFAULT,
27562a1ad637SFrançois Tigeot 				    HDAA_AMP_VOL_DEFAULT, HDAA_AMP_VOL_DEFAULT);
27572a1ad637SFrançois Tigeot 			}
27582a1ad637SFrançois Tigeot 			HDA_BOOTHVERBOSE(
27592a1ad637SFrançois Tigeot 				device_printf(pdevinfo->dev,
27602a1ad637SFrançois Tigeot 				    "Recsel (%s): nid %d source %d %s\n",
27612a1ad637SFrançois Tigeot 				    hdaa_audio_ctl_ossmixer_mask2allname(
27622a1ad637SFrançois Tigeot 				    src, buf, sizeof(buf)),
27632a1ad637SFrançois Tigeot 				    nid, i, muted?"mute":"unmute");
27642a1ad637SFrançois Tigeot 			);
27652a1ad637SFrançois Tigeot 		} else {
27662a1ad637SFrançois Tigeot 			if (w->nconns == 1)
27672a1ad637SFrançois Tigeot 				break;
27682a1ad637SFrançois Tigeot 			if ((src & cw->ossmask) == 0)
27692a1ad637SFrançois Tigeot 				continue;
27702a1ad637SFrançois Tigeot 			/* If we found requested source - select it and exit. */
27712a1ad637SFrançois Tigeot 			hdaa_widget_connection_select(w, i);
27722a1ad637SFrançois Tigeot 			HDA_BOOTHVERBOSE(
27732a1ad637SFrançois Tigeot 				device_printf(pdevinfo->dev,
27742a1ad637SFrançois Tigeot 				    "Recsel (%s): nid %d source %d select\n",
27752a1ad637SFrançois Tigeot 				    hdaa_audio_ctl_ossmixer_mask2allname(
27762a1ad637SFrançois Tigeot 				    src, buf, sizeof(buf)),
27772a1ad637SFrançois Tigeot 				    nid, i);
27782a1ad637SFrançois Tigeot 			);
27792a1ad637SFrançois Tigeot 			break;
27802a1ad637SFrançois Tigeot 		}
27812a1ad637SFrançois Tigeot 	}
27822a1ad637SFrançois Tigeot 	return (res);
27832a1ad637SFrançois Tigeot }
27842a1ad637SFrançois Tigeot 
27852a1ad637SFrançois Tigeot static uint32_t
hdaa_audio_ctl_ossmixer_setrecsrc(struct snd_mixer * m,uint32_t src)27862a1ad637SFrançois Tigeot hdaa_audio_ctl_ossmixer_setrecsrc(struct snd_mixer *m, uint32_t src)
27872a1ad637SFrançois Tigeot {
27882a1ad637SFrançois Tigeot 	struct hdaa_pcm_devinfo *pdevinfo = mix_getdevinfo(m);
27892a1ad637SFrançois Tigeot 	struct hdaa_devinfo *devinfo = pdevinfo->devinfo;
27902a1ad637SFrançois Tigeot 	struct hdaa_widget *w;
27912a1ad637SFrançois Tigeot 	struct hdaa_audio_as *as;
27922a1ad637SFrançois Tigeot 	struct hdaa_audio_ctl *ctl;
27932a1ad637SFrançois Tigeot 	struct hdaa_chan *ch;
27942a1ad637SFrançois Tigeot 	int i, j;
27952a1ad637SFrançois Tigeot 	uint32_t ret = 0xffffffff;
27962a1ad637SFrançois Tigeot 
27972a1ad637SFrançois Tigeot 	hdaa_lock(devinfo);
27982a1ad637SFrançois Tigeot 	if (pdevinfo->recas < 0) {
27992a1ad637SFrançois Tigeot 		hdaa_unlock(devinfo);
28002a1ad637SFrançois Tigeot 		return (0);
28012a1ad637SFrançois Tigeot 	}
28022a1ad637SFrançois Tigeot 	as = &devinfo->as[pdevinfo->recas];
28032a1ad637SFrançois Tigeot 
28042a1ad637SFrançois Tigeot 	/* For non-mixed associations we always recording everything. */
28052a1ad637SFrançois Tigeot 	if (!as->mixed) {
28062a1ad637SFrançois Tigeot 		hdaa_unlock(devinfo);
28072a1ad637SFrançois Tigeot 		return (mix_getrecdevs(m));
28082a1ad637SFrançois Tigeot 	}
28092a1ad637SFrançois Tigeot 
28102a1ad637SFrançois Tigeot 	/* Commutate requested recsrc for each ADC. */
28112a1ad637SFrançois Tigeot 	for (j = 0; j < as->num_chans; j++) {
28122a1ad637SFrançois Tigeot 		ch = &devinfo->chans[as->chans[j]];
28132a1ad637SFrançois Tigeot 		for (i = 0; ch->io[i] >= 0; i++) {
28142a1ad637SFrançois Tigeot 			w = hdaa_widget_get(devinfo, ch->io[i]);
28152a1ad637SFrançois Tigeot 			if (w == NULL || w->enable == 0)
28162a1ad637SFrançois Tigeot 				continue;
28172a1ad637SFrançois Tigeot 			ret &= hdaa_audio_ctl_recsel_comm(pdevinfo, src,
28182a1ad637SFrançois Tigeot 			    ch->io[i], 0);
28192a1ad637SFrançois Tigeot 		}
28202a1ad637SFrançois Tigeot 	}
28212a1ad637SFrançois Tigeot 	if (ret == 0xffffffff)
28222a1ad637SFrançois Tigeot 		ret = 0;
28232a1ad637SFrançois Tigeot 
28242a1ad637SFrançois Tigeot 	/*
28252a1ad637SFrançois Tigeot 	 * Some controls could be shared. Reset volumes for controls
28262a1ad637SFrançois Tigeot 	 * related to previously chosen devices, as they may no longer
28272a1ad637SFrançois Tigeot 	 * affect the signal.
28282a1ad637SFrançois Tigeot 	 */
28292a1ad637SFrançois Tigeot 	i = 0;
28302a1ad637SFrançois Tigeot 	while ((ctl = hdaa_audio_ctl_each(devinfo, &i)) != NULL) {
28312a1ad637SFrançois Tigeot 		if (ctl->enable == 0 ||
28322a1ad637SFrançois Tigeot 		    !(ctl->ossmask & pdevinfo->recsrc))
28332a1ad637SFrançois Tigeot 			continue;
28342a1ad637SFrançois Tigeot 		if (!((pdevinfo->playas >= 0 &&
28352a1ad637SFrançois Tigeot 		    ctl->widget->bindas == pdevinfo->playas) ||
28362a1ad637SFrançois Tigeot 		    (pdevinfo->recas >= 0 &&
28372a1ad637SFrançois Tigeot 		    ctl->widget->bindas == pdevinfo->recas) ||
28382a1ad637SFrançois Tigeot 		    (pdevinfo->index == 0 &&
28392a1ad637SFrançois Tigeot 		    ctl->widget->bindas == -2)))
28402a1ad637SFrançois Tigeot 			continue;
28412a1ad637SFrançois Tigeot 		for (j = 0; j < SOUND_MIXER_NRDEVICES; j++) {
28422a1ad637SFrançois Tigeot 			if (pdevinfo->recsrc & (1 << j)) {
28432a1ad637SFrançois Tigeot 				ctl->devleft[j] = 0;
28442a1ad637SFrançois Tigeot 				ctl->devright[j] = 0;
28452a1ad637SFrançois Tigeot 				ctl->devmute[j] = 0;
28462a1ad637SFrançois Tigeot 			}
28472a1ad637SFrançois Tigeot 		}
28482a1ad637SFrançois Tigeot 	}
28492a1ad637SFrançois Tigeot 
28502a1ad637SFrançois Tigeot 	/*
28512a1ad637SFrançois Tigeot 	 * Some controls could be shared. Set volumes for controls
28522a1ad637SFrançois Tigeot 	 * related to devices selected both previously and now.
28532a1ad637SFrançois Tigeot 	 */
28542a1ad637SFrançois Tigeot 	for (j = 0; j < SOUND_MIXER_NRDEVICES; j++) {
28552a1ad637SFrançois Tigeot 		if ((ret | pdevinfo->recsrc) & (1 << j))
28562a1ad637SFrançois Tigeot 			hdaa_audio_ctl_dev_volume(pdevinfo, j);
28572a1ad637SFrançois Tigeot 	}
28582a1ad637SFrançois Tigeot 
28592a1ad637SFrançois Tigeot 	pdevinfo->recsrc = ret;
28602a1ad637SFrançois Tigeot 	hdaa_unlock(devinfo);
28612a1ad637SFrançois Tigeot 	return (ret);
28622a1ad637SFrançois Tigeot }
28632a1ad637SFrançois Tigeot 
28642a1ad637SFrançois Tigeot static kobj_method_t hdaa_audio_ctl_ossmixer_methods[] = {
28652a1ad637SFrançois Tigeot 	KOBJMETHOD(mixer_init,		hdaa_audio_ctl_ossmixer_init),
28662a1ad637SFrançois Tigeot 	KOBJMETHOD(mixer_set,		hdaa_audio_ctl_ossmixer_set),
28672a1ad637SFrançois Tigeot 	KOBJMETHOD(mixer_setrecsrc,	hdaa_audio_ctl_ossmixer_setrecsrc),
28682a1ad637SFrançois Tigeot 	KOBJMETHOD_END
28692a1ad637SFrançois Tigeot };
28702a1ad637SFrançois Tigeot MIXER_DECLARE(hdaa_audio_ctl_ossmixer);
28712a1ad637SFrançois Tigeot 
28722a1ad637SFrançois Tigeot static void
hdaa_dump_gpi(struct hdaa_devinfo * devinfo)28732a1ad637SFrançois Tigeot hdaa_dump_gpi(struct hdaa_devinfo *devinfo)
28742a1ad637SFrançois Tigeot {
28752a1ad637SFrançois Tigeot 	device_t dev = devinfo->dev;
28762a1ad637SFrançois Tigeot 	int i;
28772a1ad637SFrançois Tigeot 	uint32_t data, wake, unsol, sticky;
28782a1ad637SFrançois Tigeot 
28792a1ad637SFrançois Tigeot 	if (HDA_PARAM_GPIO_COUNT_NUM_GPI(devinfo->gpio_cap) > 0) {
28802a1ad637SFrançois Tigeot 		data = hda_command(dev,
28812a1ad637SFrançois Tigeot 		    HDA_CMD_GET_GPI_DATA(0, devinfo->nid));
28822a1ad637SFrançois Tigeot 		wake = hda_command(dev,
28832a1ad637SFrançois Tigeot 		    HDA_CMD_GET_GPI_WAKE_ENABLE_MASK(0, devinfo->nid));
28842a1ad637SFrançois Tigeot 		unsol = hda_command(dev,
28852a1ad637SFrançois Tigeot 		    HDA_CMD_GET_GPI_UNSOLICITED_ENABLE_MASK(0, devinfo->nid));
28862a1ad637SFrançois Tigeot 		sticky = hda_command(dev,
28872a1ad637SFrançois Tigeot 		    HDA_CMD_GET_GPI_STICKY_MASK(0, devinfo->nid));
28882a1ad637SFrançois Tigeot 		for (i = 0; i < HDA_PARAM_GPIO_COUNT_NUM_GPI(devinfo->gpio_cap); i++) {
28892a1ad637SFrançois Tigeot 			device_printf(dev, " GPI%d:%s%s%s state=%d", i,
28902a1ad637SFrançois Tigeot 				    (sticky & (1 << i)) ? " sticky" : "",
28912a1ad637SFrançois Tigeot 				    (unsol & (1 << i)) ? " unsol" : "",
28922a1ad637SFrançois Tigeot 				    (wake & (1 << i)) ? " wake" : "",
28932a1ad637SFrançois Tigeot 				    (data >> i) & 1);
28942a1ad637SFrançois Tigeot 		}
28952a1ad637SFrançois Tigeot 	}
28962a1ad637SFrançois Tigeot }
28972a1ad637SFrançois Tigeot 
28982a1ad637SFrançois Tigeot static void
hdaa_dump_gpio(struct hdaa_devinfo * devinfo)28992a1ad637SFrançois Tigeot hdaa_dump_gpio(struct hdaa_devinfo *devinfo)
29002a1ad637SFrançois Tigeot {
29012a1ad637SFrançois Tigeot 	device_t dev = devinfo->dev;
29022a1ad637SFrançois Tigeot 	int i;
29032a1ad637SFrançois Tigeot 	uint32_t data, dir, enable, wake, unsol, sticky;
29042a1ad637SFrançois Tigeot 
29052a1ad637SFrançois Tigeot 	if (HDA_PARAM_GPIO_COUNT_NUM_GPIO(devinfo->gpio_cap) > 0) {
29062a1ad637SFrançois Tigeot 		data = hda_command(dev,
29072a1ad637SFrançois Tigeot 		    HDA_CMD_GET_GPIO_DATA(0, devinfo->nid));
29082a1ad637SFrançois Tigeot 		enable = hda_command(dev,
29092a1ad637SFrançois Tigeot 		    HDA_CMD_GET_GPIO_ENABLE_MASK(0, devinfo->nid));
29102a1ad637SFrançois Tigeot 		dir = hda_command(dev,
29112a1ad637SFrançois Tigeot 		    HDA_CMD_GET_GPIO_DIRECTION(0, devinfo->nid));
29122a1ad637SFrançois Tigeot 		wake = hda_command(dev,
29132a1ad637SFrançois Tigeot 		    HDA_CMD_GET_GPIO_WAKE_ENABLE_MASK(0, devinfo->nid));
29142a1ad637SFrançois Tigeot 		unsol = hda_command(dev,
29152a1ad637SFrançois Tigeot 		    HDA_CMD_GET_GPIO_UNSOLICITED_ENABLE_MASK(0, devinfo->nid));
29162a1ad637SFrançois Tigeot 		sticky = hda_command(dev,
29172a1ad637SFrançois Tigeot 		    HDA_CMD_GET_GPIO_STICKY_MASK(0, devinfo->nid));
29182a1ad637SFrançois Tigeot 		for (i = 0; i < HDA_PARAM_GPIO_COUNT_NUM_GPIO(devinfo->gpio_cap); i++) {
29192a1ad637SFrançois Tigeot 			device_printf(dev, " GPIO%d: ", i);
29202a1ad637SFrançois Tigeot 			if ((enable & (1 << i)) == 0) {
292167931cc4SFrançois Tigeot 				kprintf("disabled\n");
29222a1ad637SFrançois Tigeot 				continue;
29232a1ad637SFrançois Tigeot 			}
29242a1ad637SFrançois Tigeot 			if ((dir & (1 << i)) == 0) {
292567931cc4SFrançois Tigeot 				kprintf("input%s%s%s",
29262a1ad637SFrançois Tigeot 				    (sticky & (1 << i)) ? " sticky" : "",
29272a1ad637SFrançois Tigeot 				    (unsol & (1 << i)) ? " unsol" : "",
29282a1ad637SFrançois Tigeot 				    (wake & (1 << i)) ? " wake" : "");
29292a1ad637SFrançois Tigeot 			} else
293067931cc4SFrançois Tigeot 				kprintf("output");
293167931cc4SFrançois Tigeot 			kprintf(" state=%d\n", (data >> i) & 1);
29322a1ad637SFrançois Tigeot 		}
29332a1ad637SFrançois Tigeot 	}
29342a1ad637SFrançois Tigeot }
29352a1ad637SFrançois Tigeot 
29362a1ad637SFrançois Tigeot static void
hdaa_dump_gpo(struct hdaa_devinfo * devinfo)29372a1ad637SFrançois Tigeot hdaa_dump_gpo(struct hdaa_devinfo *devinfo)
29382a1ad637SFrançois Tigeot {
29392a1ad637SFrançois Tigeot 	device_t dev = devinfo->dev;
29402a1ad637SFrançois Tigeot 	int i;
29412a1ad637SFrançois Tigeot 	uint32_t data;
29422a1ad637SFrançois Tigeot 
29432a1ad637SFrançois Tigeot 	if (HDA_PARAM_GPIO_COUNT_NUM_GPO(devinfo->gpio_cap) > 0) {
29442a1ad637SFrançois Tigeot 		data = hda_command(dev,
29452a1ad637SFrançois Tigeot 		    HDA_CMD_GET_GPO_DATA(0, devinfo->nid));
29462a1ad637SFrançois Tigeot 		for (i = 0; i < HDA_PARAM_GPIO_COUNT_NUM_GPO(devinfo->gpio_cap); i++) {
29472a1ad637SFrançois Tigeot 			device_printf(dev, " GPO%d: state=%d", i,
29482a1ad637SFrançois Tigeot 				    (data >> i) & 1);
29492a1ad637SFrançois Tigeot 		}
29502a1ad637SFrançois Tigeot 	}
29512a1ad637SFrançois Tigeot }
29522a1ad637SFrançois Tigeot 
29532a1ad637SFrançois Tigeot static void
hdaa_audio_parse(struct hdaa_devinfo * devinfo)29542a1ad637SFrançois Tigeot hdaa_audio_parse(struct hdaa_devinfo *devinfo)
29552a1ad637SFrançois Tigeot {
29562a1ad637SFrançois Tigeot 	struct hdaa_widget *w;
29572a1ad637SFrançois Tigeot 	uint32_t res;
29582a1ad637SFrançois Tigeot 	int i;
29592a1ad637SFrançois Tigeot 	nid_t nid;
29602a1ad637SFrançois Tigeot 
29612a1ad637SFrançois Tigeot 	nid = devinfo->nid;
29622a1ad637SFrançois Tigeot 
29632a1ad637SFrançois Tigeot 	res = hda_command(devinfo->dev,
29642a1ad637SFrançois Tigeot 	    HDA_CMD_GET_PARAMETER(0, nid, HDA_PARAM_GPIO_COUNT));
29652a1ad637SFrançois Tigeot 	devinfo->gpio_cap = res;
29662a1ad637SFrançois Tigeot 
29672a1ad637SFrançois Tigeot 	HDA_BOOTVERBOSE(
29682a1ad637SFrançois Tigeot 		device_printf(devinfo->dev,
29692a1ad637SFrançois Tigeot 		    "NumGPIO=%d NumGPO=%d "
29702a1ad637SFrançois Tigeot 		    "NumGPI=%d GPIWake=%d GPIUnsol=%d\n",
29712a1ad637SFrançois Tigeot 		    HDA_PARAM_GPIO_COUNT_NUM_GPIO(devinfo->gpio_cap),
29722a1ad637SFrançois Tigeot 		    HDA_PARAM_GPIO_COUNT_NUM_GPO(devinfo->gpio_cap),
29732a1ad637SFrançois Tigeot 		    HDA_PARAM_GPIO_COUNT_NUM_GPI(devinfo->gpio_cap),
29742a1ad637SFrançois Tigeot 		    HDA_PARAM_GPIO_COUNT_GPI_WAKE(devinfo->gpio_cap),
29752a1ad637SFrançois Tigeot 		    HDA_PARAM_GPIO_COUNT_GPI_UNSOL(devinfo->gpio_cap));
29762a1ad637SFrançois Tigeot 		hdaa_dump_gpi(devinfo);
29772a1ad637SFrançois Tigeot 		hdaa_dump_gpio(devinfo);
29782a1ad637SFrançois Tigeot 		hdaa_dump_gpo(devinfo);
29792a1ad637SFrançois Tigeot 	);
29802a1ad637SFrançois Tigeot 
29812a1ad637SFrançois Tigeot 	res = hda_command(devinfo->dev,
29822a1ad637SFrançois Tigeot 	    HDA_CMD_GET_PARAMETER(0, nid, HDA_PARAM_SUPP_STREAM_FORMATS));
29832a1ad637SFrançois Tigeot 	devinfo->supp_stream_formats = res;
29842a1ad637SFrançois Tigeot 
29852a1ad637SFrançois Tigeot 	res = hda_command(devinfo->dev,
29862a1ad637SFrançois Tigeot 	    HDA_CMD_GET_PARAMETER(0, nid, HDA_PARAM_SUPP_PCM_SIZE_RATE));
29872a1ad637SFrançois Tigeot 	devinfo->supp_pcm_size_rate = res;
29882a1ad637SFrançois Tigeot 
29892a1ad637SFrançois Tigeot 	res = hda_command(devinfo->dev,
29902a1ad637SFrançois Tigeot 	    HDA_CMD_GET_PARAMETER(0, nid, HDA_PARAM_OUTPUT_AMP_CAP));
29912a1ad637SFrançois Tigeot 	devinfo->outamp_cap = res;
29922a1ad637SFrançois Tigeot 
29932a1ad637SFrançois Tigeot 	res = hda_command(devinfo->dev,
29942a1ad637SFrançois Tigeot 	    HDA_CMD_GET_PARAMETER(0, nid, HDA_PARAM_INPUT_AMP_CAP));
29952a1ad637SFrançois Tigeot 	devinfo->inamp_cap = res;
29962a1ad637SFrançois Tigeot 
29972a1ad637SFrançois Tigeot 	for (i = devinfo->startnode; i < devinfo->endnode; i++) {
29982a1ad637SFrançois Tigeot 		w = hdaa_widget_get(devinfo, i);
29992a1ad637SFrançois Tigeot 		if (w == NULL)
30002a1ad637SFrançois Tigeot 			device_printf(devinfo->dev, "Ghost widget! nid=%d!\n", i);
30012a1ad637SFrançois Tigeot 		else {
30022a1ad637SFrançois Tigeot 			w->devinfo = devinfo;
30032a1ad637SFrançois Tigeot 			w->nid = i;
30042a1ad637SFrançois Tigeot 			w->enable = 1;
30052a1ad637SFrançois Tigeot 			w->selconn = -1;
30062a1ad637SFrançois Tigeot 			w->pflags = 0;
30072a1ad637SFrançois Tigeot 			w->ossdev = -1;
30082a1ad637SFrançois Tigeot 			w->bindas = -1;
30092a1ad637SFrançois Tigeot 			w->param.eapdbtl = HDA_INVALID;
30102a1ad637SFrançois Tigeot 			hdaa_widget_parse(w);
30112a1ad637SFrançois Tigeot 		}
30122a1ad637SFrançois Tigeot 	}
30132a1ad637SFrançois Tigeot }
30142a1ad637SFrançois Tigeot 
30152a1ad637SFrançois Tigeot static void
hdaa_audio_postprocess(struct hdaa_devinfo * devinfo)30162a1ad637SFrançois Tigeot hdaa_audio_postprocess(struct hdaa_devinfo *devinfo)
30172a1ad637SFrançois Tigeot {
30182a1ad637SFrançois Tigeot 	struct hdaa_widget *w;
30192a1ad637SFrançois Tigeot 	int i;
30202a1ad637SFrançois Tigeot 
30212a1ad637SFrançois Tigeot 	for (i = devinfo->startnode; i < devinfo->endnode; i++) {
30222a1ad637SFrançois Tigeot 		w = hdaa_widget_get(devinfo, i);
30232a1ad637SFrançois Tigeot 		if (w == NULL)
30242a1ad637SFrançois Tigeot 			continue;
30252a1ad637SFrançois Tigeot 		hdaa_widget_postprocess(w);
30262a1ad637SFrançois Tigeot 	}
30272a1ad637SFrançois Tigeot }
30282a1ad637SFrançois Tigeot 
30292a1ad637SFrançois Tigeot static void
hdaa_audio_ctl_parse(struct hdaa_devinfo * devinfo)30302a1ad637SFrançois Tigeot hdaa_audio_ctl_parse(struct hdaa_devinfo *devinfo)
30312a1ad637SFrançois Tigeot {
30322a1ad637SFrançois Tigeot 	struct hdaa_audio_ctl *ctls;
30332a1ad637SFrançois Tigeot 	struct hdaa_widget *w, *cw;
30342a1ad637SFrançois Tigeot 	int i, j, cnt, max, ocap, icap;
30352a1ad637SFrançois Tigeot 	int mute, offset, step, size;
30362a1ad637SFrançois Tigeot 
30372a1ad637SFrançois Tigeot 	/* XXX This is redundant */
30382a1ad637SFrançois Tigeot 	max = 0;
30392a1ad637SFrançois Tigeot 	for (i = devinfo->startnode; i < devinfo->endnode; i++) {
30402a1ad637SFrançois Tigeot 		w = hdaa_widget_get(devinfo, i);
30412a1ad637SFrançois Tigeot 		if (w == NULL || w->enable == 0)
30422a1ad637SFrançois Tigeot 			continue;
30432a1ad637SFrançois Tigeot 		if (w->param.outamp_cap != 0)
30442a1ad637SFrançois Tigeot 			max++;
30452a1ad637SFrançois Tigeot 		if (w->param.inamp_cap != 0) {
30462a1ad637SFrançois Tigeot 			switch (w->type) {
30472a1ad637SFrançois Tigeot 			case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR:
30482a1ad637SFrançois Tigeot 			case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER:
30492a1ad637SFrançois Tigeot 				for (j = 0; j < w->nconns; j++) {
30502a1ad637SFrançois Tigeot 					cw = hdaa_widget_get(devinfo,
30512a1ad637SFrançois Tigeot 					    w->conns[j]);
30522a1ad637SFrançois Tigeot 					if (cw == NULL || cw->enable == 0)
30532a1ad637SFrançois Tigeot 						continue;
30542a1ad637SFrançois Tigeot 					max++;
30552a1ad637SFrançois Tigeot 				}
30562a1ad637SFrançois Tigeot 				break;
30572a1ad637SFrançois Tigeot 			default:
30582a1ad637SFrançois Tigeot 				max++;
30592a1ad637SFrançois Tigeot 				break;
30602a1ad637SFrançois Tigeot 			}
30612a1ad637SFrançois Tigeot 		}
30622a1ad637SFrançois Tigeot 	}
30632a1ad637SFrançois Tigeot 	devinfo->ctlcnt = max;
30642a1ad637SFrançois Tigeot 
30652a1ad637SFrançois Tigeot 	if (max < 1)
30662a1ad637SFrançois Tigeot 		return;
30672a1ad637SFrançois Tigeot 
306867931cc4SFrançois Tigeot 	ctls = (struct hdaa_audio_ctl *)kmalloc(
30694e8e900cSMatthew Dillon 	    sizeof(*ctls) * max, M_HDAA, M_ZERO | M_WAITOK);
30702a1ad637SFrançois Tigeot 
30712a1ad637SFrançois Tigeot 	if (ctls == NULL) {
30722a1ad637SFrançois Tigeot 		/* Blekh! */
30732a1ad637SFrançois Tigeot 		device_printf(devinfo->dev, "unable to allocate ctls!\n");
30742a1ad637SFrançois Tigeot 		devinfo->ctlcnt = 0;
30752a1ad637SFrançois Tigeot 		return;
30762a1ad637SFrançois Tigeot 	}
30772a1ad637SFrançois Tigeot 
30782a1ad637SFrançois Tigeot 	cnt = 0;
30792a1ad637SFrançois Tigeot 	for (i = devinfo->startnode; cnt < max && i < devinfo->endnode; i++) {
30802a1ad637SFrançois Tigeot 		w = hdaa_widget_get(devinfo, i);
30812a1ad637SFrançois Tigeot 		if (w == NULL || w->enable == 0)
30822a1ad637SFrançois Tigeot 			continue;
30832a1ad637SFrançois Tigeot 		ocap = w->param.outamp_cap;
30842a1ad637SFrançois Tigeot 		icap = w->param.inamp_cap;
30852a1ad637SFrançois Tigeot 		if (ocap != 0) {
30862a1ad637SFrançois Tigeot 			mute = HDA_PARAM_OUTPUT_AMP_CAP_MUTE_CAP(ocap);
30872a1ad637SFrançois Tigeot 			step = HDA_PARAM_OUTPUT_AMP_CAP_NUMSTEPS(ocap);
30882a1ad637SFrançois Tigeot 			size = HDA_PARAM_OUTPUT_AMP_CAP_STEPSIZE(ocap);
30892a1ad637SFrançois Tigeot 			offset = HDA_PARAM_OUTPUT_AMP_CAP_OFFSET(ocap);
30902a1ad637SFrançois Tigeot 			/*if (offset > step) {
30912a1ad637SFrançois Tigeot 				HDA_BOOTVERBOSE(
30922a1ad637SFrançois Tigeot 					device_printf(devinfo->dev,
30932a1ad637SFrançois Tigeot 					    "BUGGY outamp: nid=%d "
30942a1ad637SFrançois Tigeot 					    "[offset=%d > step=%d]\n",
30952a1ad637SFrançois Tigeot 					    w->nid, offset, step);
30962a1ad637SFrançois Tigeot 				);
30972a1ad637SFrançois Tigeot 				offset = step;
30982a1ad637SFrançois Tigeot 			}*/
30992a1ad637SFrançois Tigeot 			ctls[cnt].enable = 1;
31002a1ad637SFrançois Tigeot 			ctls[cnt].widget = w;
31012a1ad637SFrançois Tigeot 			ctls[cnt].mute = mute;
31022a1ad637SFrançois Tigeot 			ctls[cnt].step = step;
31032a1ad637SFrançois Tigeot 			ctls[cnt].size = size;
31042a1ad637SFrançois Tigeot 			ctls[cnt].offset = offset;
31052a1ad637SFrançois Tigeot 			ctls[cnt].left = offset;
31062a1ad637SFrançois Tigeot 			ctls[cnt].right = offset;
31072a1ad637SFrançois Tigeot 			if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX ||
31082a1ad637SFrançois Tigeot 			    w->waspin)
31092a1ad637SFrançois Tigeot 				ctls[cnt].ndir = HDAA_CTL_IN;
31102a1ad637SFrançois Tigeot 			else
31112a1ad637SFrançois Tigeot 				ctls[cnt].ndir = HDAA_CTL_OUT;
31122a1ad637SFrançois Tigeot 			ctls[cnt++].dir = HDAA_CTL_OUT;
31132a1ad637SFrançois Tigeot 		}
31142a1ad637SFrançois Tigeot 
31152a1ad637SFrançois Tigeot 		if (icap != 0) {
31162a1ad637SFrançois Tigeot 			mute = HDA_PARAM_OUTPUT_AMP_CAP_MUTE_CAP(icap);
31172a1ad637SFrançois Tigeot 			step = HDA_PARAM_OUTPUT_AMP_CAP_NUMSTEPS(icap);
31182a1ad637SFrançois Tigeot 			size = HDA_PARAM_OUTPUT_AMP_CAP_STEPSIZE(icap);
31192a1ad637SFrançois Tigeot 			offset = HDA_PARAM_OUTPUT_AMP_CAP_OFFSET(icap);
31202a1ad637SFrançois Tigeot 			/*if (offset > step) {
31212a1ad637SFrançois Tigeot 				HDA_BOOTVERBOSE(
31222a1ad637SFrançois Tigeot 					device_printf(devinfo->dev,
31232a1ad637SFrançois Tigeot 					    "BUGGY inamp: nid=%d "
31242a1ad637SFrançois Tigeot 					    "[offset=%d > step=%d]\n",
31252a1ad637SFrançois Tigeot 					    w->nid, offset, step);
31262a1ad637SFrançois Tigeot 				);
31272a1ad637SFrançois Tigeot 				offset = step;
31282a1ad637SFrançois Tigeot 			}*/
31292a1ad637SFrançois Tigeot 			switch (w->type) {
31302a1ad637SFrançois Tigeot 			case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR:
31312a1ad637SFrançois Tigeot 			case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER:
31322a1ad637SFrançois Tigeot 				for (j = 0; j < w->nconns; j++) {
31332a1ad637SFrançois Tigeot 					if (cnt >= max) {
31342a1ad637SFrançois Tigeot 						device_printf(devinfo->dev,
31352a1ad637SFrançois Tigeot 						    "%s: Ctl overflow!\n",
31362a1ad637SFrançois Tigeot 						    __func__);
31372a1ad637SFrançois Tigeot 						break;
31382a1ad637SFrançois Tigeot 					}
31392a1ad637SFrançois Tigeot 					cw = hdaa_widget_get(devinfo,
31402a1ad637SFrançois Tigeot 					    w->conns[j]);
31412a1ad637SFrançois Tigeot 					if (cw == NULL || cw->enable == 0)
31422a1ad637SFrançois Tigeot 						continue;
31432a1ad637SFrançois Tigeot 					ctls[cnt].enable = 1;
31442a1ad637SFrançois Tigeot 					ctls[cnt].widget = w;
31452a1ad637SFrançois Tigeot 					ctls[cnt].childwidget = cw;
31462a1ad637SFrançois Tigeot 					ctls[cnt].index = j;
31472a1ad637SFrançois Tigeot 					ctls[cnt].mute = mute;
31482a1ad637SFrançois Tigeot 					ctls[cnt].step = step;
31492a1ad637SFrançois Tigeot 					ctls[cnt].size = size;
31502a1ad637SFrançois Tigeot 					ctls[cnt].offset = offset;
31512a1ad637SFrançois Tigeot 					ctls[cnt].left = offset;
31522a1ad637SFrançois Tigeot 					ctls[cnt].right = offset;
31532a1ad637SFrançois Tigeot 				ctls[cnt].ndir = HDAA_CTL_IN;
31542a1ad637SFrançois Tigeot 					ctls[cnt++].dir = HDAA_CTL_IN;
31552a1ad637SFrançois Tigeot 				}
31562a1ad637SFrançois Tigeot 				break;
31572a1ad637SFrançois Tigeot 			default:
31582a1ad637SFrançois Tigeot 				if (cnt >= max) {
31592a1ad637SFrançois Tigeot 					device_printf(devinfo->dev,
31602a1ad637SFrançois Tigeot 					    "%s: Ctl overflow!\n",
31612a1ad637SFrançois Tigeot 					    __func__);
31622a1ad637SFrançois Tigeot 					break;
31632a1ad637SFrançois Tigeot 				}
31642a1ad637SFrançois Tigeot 				ctls[cnt].enable = 1;
31652a1ad637SFrançois Tigeot 				ctls[cnt].widget = w;
31662a1ad637SFrançois Tigeot 				ctls[cnt].mute = mute;
31672a1ad637SFrançois Tigeot 				ctls[cnt].step = step;
31682a1ad637SFrançois Tigeot 				ctls[cnt].size = size;
31692a1ad637SFrançois Tigeot 				ctls[cnt].offset = offset;
31702a1ad637SFrançois Tigeot 				ctls[cnt].left = offset;
31712a1ad637SFrançois Tigeot 				ctls[cnt].right = offset;
31722a1ad637SFrançois Tigeot 				if (w->type ==
31732a1ad637SFrançois Tigeot 				    HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX)
31742a1ad637SFrançois Tigeot 					ctls[cnt].ndir = HDAA_CTL_OUT;
31752a1ad637SFrançois Tigeot 				else
31762a1ad637SFrançois Tigeot 					ctls[cnt].ndir = HDAA_CTL_IN;
31772a1ad637SFrançois Tigeot 				ctls[cnt++].dir = HDAA_CTL_IN;
31782a1ad637SFrançois Tigeot 				break;
31792a1ad637SFrançois Tigeot 			}
31802a1ad637SFrançois Tigeot 		}
31812a1ad637SFrançois Tigeot 	}
31822a1ad637SFrançois Tigeot 
31832a1ad637SFrançois Tigeot 	devinfo->ctl = ctls;
31842a1ad637SFrançois Tigeot }
31852a1ad637SFrançois Tigeot 
31862a1ad637SFrançois Tigeot static void
hdaa_audio_as_parse(struct hdaa_devinfo * devinfo)31872a1ad637SFrançois Tigeot hdaa_audio_as_parse(struct hdaa_devinfo *devinfo)
31882a1ad637SFrançois Tigeot {
31892a1ad637SFrançois Tigeot 	struct hdaa_audio_as *as;
31902a1ad637SFrançois Tigeot 	struct hdaa_widget *w;
31912a1ad637SFrançois Tigeot 	int i, j, cnt, max, type, dir, assoc, seq, first, hpredir;
31922a1ad637SFrançois Tigeot 
31932a1ad637SFrançois Tigeot 	/* Count present associations */
31942a1ad637SFrançois Tigeot 	max = 0;
31952a1ad637SFrançois Tigeot 	for (j = 1; j < 16; j++) {
31962a1ad637SFrançois Tigeot 		for (i = devinfo->startnode; i < devinfo->endnode; i++) {
31972a1ad637SFrançois Tigeot 			w = hdaa_widget_get(devinfo, i);
31982a1ad637SFrançois Tigeot 			if (w == NULL || w->enable == 0)
31992a1ad637SFrançois Tigeot 				continue;
32002a1ad637SFrançois Tigeot 			if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX)
32012a1ad637SFrançois Tigeot 				continue;
32022a1ad637SFrançois Tigeot 			if (HDA_CONFIG_DEFAULTCONF_ASSOCIATION(w->wclass.pin.config)
32032a1ad637SFrançois Tigeot 			    != j)
32042a1ad637SFrançois Tigeot 				continue;
32052a1ad637SFrançois Tigeot 			max++;
32062a1ad637SFrançois Tigeot 			if (j != 15)  /* There could be many 1-pin assocs #15 */
32072a1ad637SFrançois Tigeot 				break;
32082a1ad637SFrançois Tigeot 		}
32092a1ad637SFrançois Tigeot 	}
32102a1ad637SFrançois Tigeot 
32112a1ad637SFrançois Tigeot 	devinfo->ascnt = max;
32122a1ad637SFrançois Tigeot 
32132a1ad637SFrançois Tigeot 	if (max < 1)
32142a1ad637SFrançois Tigeot 		return;
32152a1ad637SFrançois Tigeot 
321667931cc4SFrançois Tigeot 	as = (struct hdaa_audio_as *)kmalloc(
32174e8e900cSMatthew Dillon 	    sizeof(*as) * max, M_HDAA, M_ZERO | M_WAITOK);
32182a1ad637SFrançois Tigeot 
32192a1ad637SFrançois Tigeot 	if (as == NULL) {
32202a1ad637SFrançois Tigeot 		/* Blekh! */
32212a1ad637SFrançois Tigeot 		device_printf(devinfo->dev, "unable to allocate assocs!\n");
32222a1ad637SFrançois Tigeot 		devinfo->ascnt = 0;
32232a1ad637SFrançois Tigeot 		return;
32242a1ad637SFrançois Tigeot 	}
32252a1ad637SFrançois Tigeot 
32262a1ad637SFrançois Tigeot 	for (i = 0; i < max; i++) {
32272a1ad637SFrançois Tigeot 		as[i].hpredir = -1;
32282a1ad637SFrançois Tigeot 		as[i].digital = 0;
32292a1ad637SFrançois Tigeot 		as[i].num_chans = 1;
32302a1ad637SFrançois Tigeot 		as[i].location = -1;
32312a1ad637SFrançois Tigeot 	}
32322a1ad637SFrançois Tigeot 
32332a1ad637SFrançois Tigeot 	/* Scan associations skipping as=0. */
32342a1ad637SFrançois Tigeot 	cnt = 0;
32352a1ad637SFrançois Tigeot 	for (j = 1; j < 16; j++) {
32362a1ad637SFrançois Tigeot 		first = 16;
32372a1ad637SFrançois Tigeot 		hpredir = 0;
32382a1ad637SFrançois Tigeot 		for (i = devinfo->startnode; i < devinfo->endnode; i++) {
32392a1ad637SFrançois Tigeot 			w = hdaa_widget_get(devinfo, i);
32402a1ad637SFrançois Tigeot 			if (w == NULL || w->enable == 0)
32412a1ad637SFrançois Tigeot 				continue;
32422a1ad637SFrançois Tigeot 			if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX)
32432a1ad637SFrançois Tigeot 				continue;
32442a1ad637SFrançois Tigeot 			assoc = HDA_CONFIG_DEFAULTCONF_ASSOCIATION(w->wclass.pin.config);
32452a1ad637SFrançois Tigeot 			seq = HDA_CONFIG_DEFAULTCONF_SEQUENCE(w->wclass.pin.config);
32462a1ad637SFrançois Tigeot 			if (assoc != j) {
32472a1ad637SFrançois Tigeot 				continue;
32482a1ad637SFrançois Tigeot 			}
32492a1ad637SFrançois Tigeot 			KASSERT(cnt < max,
32502a1ad637SFrançois Tigeot 			    ("%s: Associations owerflow (%d of %d)",
32512a1ad637SFrançois Tigeot 			    __func__, cnt, max));
32522a1ad637SFrançois Tigeot 			type = w->wclass.pin.config &
32532a1ad637SFrançois Tigeot 			    HDA_CONFIG_DEFAULTCONF_DEVICE_MASK;
32542a1ad637SFrançois Tigeot 			/* Get pin direction. */
32552a1ad637SFrançois Tigeot 			if (type == HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_OUT ||
32562a1ad637SFrançois Tigeot 			    type == HDA_CONFIG_DEFAULTCONF_DEVICE_SPEAKER ||
32572a1ad637SFrançois Tigeot 			    type == HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT ||
32582a1ad637SFrançois Tigeot 			    type == HDA_CONFIG_DEFAULTCONF_DEVICE_SPDIF_OUT ||
32592a1ad637SFrançois Tigeot 			    type == HDA_CONFIG_DEFAULTCONF_DEVICE_DIGITAL_OTHER_OUT)
32602a1ad637SFrançois Tigeot 				dir = HDAA_CTL_OUT;
32612a1ad637SFrançois Tigeot 			else
32622a1ad637SFrançois Tigeot 				dir = HDAA_CTL_IN;
32632a1ad637SFrançois Tigeot 			/* If this is a first pin - create new association. */
32642a1ad637SFrançois Tigeot 			if (as[cnt].pincnt == 0) {
32652a1ad637SFrançois Tigeot 				as[cnt].enable = 1;
32662a1ad637SFrançois Tigeot 				as[cnt].index = j;
32672a1ad637SFrançois Tigeot 				as[cnt].dir = dir;
32682a1ad637SFrançois Tigeot 			}
32692a1ad637SFrançois Tigeot 			if (seq < first)
32702a1ad637SFrançois Tigeot 				first = seq;
32712a1ad637SFrançois Tigeot 			/* Check association correctness. */
32722a1ad637SFrançois Tigeot 			if (as[cnt].pins[seq] != 0) {
32732a1ad637SFrançois Tigeot 				device_printf(devinfo->dev, "%s: Duplicate pin %d (%d) "
32742a1ad637SFrançois Tigeot 				    "in association %d! Disabling association.\n",
32752a1ad637SFrançois Tigeot 				    __func__, seq, w->nid, j);
32762a1ad637SFrançois Tigeot 				as[cnt].enable = 0;
32772a1ad637SFrançois Tigeot 			}
32782a1ad637SFrançois Tigeot 			if (dir != as[cnt].dir) {
32792a1ad637SFrançois Tigeot 				device_printf(devinfo->dev, "%s: Pin %d has wrong "
32802a1ad637SFrançois Tigeot 				    "direction for association %d! Disabling "
32812a1ad637SFrançois Tigeot 				    "association.\n",
32822a1ad637SFrançois Tigeot 				    __func__, w->nid, j);
32832a1ad637SFrançois Tigeot 				as[cnt].enable = 0;
32842a1ad637SFrançois Tigeot 			}
32852a1ad637SFrançois Tigeot 			if (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap)) {
32862a1ad637SFrançois Tigeot 				as[cnt].digital |= 0x1;
32872a1ad637SFrançois Tigeot 				if (HDA_PARAM_PIN_CAP_HDMI(w->wclass.pin.cap))
32882a1ad637SFrançois Tigeot 					as[cnt].digital |= 0x2;
32892a1ad637SFrançois Tigeot 				if (HDA_PARAM_PIN_CAP_DP(w->wclass.pin.cap))
32902a1ad637SFrançois Tigeot 					as[cnt].digital |= 0x4;
32912a1ad637SFrançois Tigeot 			}
32922a1ad637SFrançois Tigeot 			if (as[cnt].location == -1) {
32932a1ad637SFrançois Tigeot 				as[cnt].location =
32942a1ad637SFrançois Tigeot 				    HDA_CONFIG_DEFAULTCONF_LOCATION(w->wclass.pin.config);
32952a1ad637SFrançois Tigeot 			} else if (as[cnt].location !=
32962a1ad637SFrançois Tigeot 			    HDA_CONFIG_DEFAULTCONF_LOCATION(w->wclass.pin.config)) {
32972a1ad637SFrançois Tigeot 				as[cnt].location = -2;
32982a1ad637SFrançois Tigeot 			}
32992a1ad637SFrançois Tigeot 			/* Headphones with seq=15 may mean redirection. */
33002a1ad637SFrançois Tigeot 			if (type == HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT &&
33012a1ad637SFrançois Tigeot 			    seq == 15)
33022a1ad637SFrançois Tigeot 				hpredir = 1;
33032a1ad637SFrançois Tigeot 			as[cnt].pins[seq] = w->nid;
33042a1ad637SFrançois Tigeot 			as[cnt].pincnt++;
33052a1ad637SFrançois Tigeot 			/* Association 15 is a multiple unassociated pins. */
33062a1ad637SFrançois Tigeot 			if (j == 15)
33072a1ad637SFrançois Tigeot 				cnt++;
33082a1ad637SFrançois Tigeot 		}
33092a1ad637SFrançois Tigeot 		if (j != 15 && as[cnt].pincnt > 0) {
33102a1ad637SFrançois Tigeot 			if (hpredir && as[cnt].pincnt > 1)
33112a1ad637SFrançois Tigeot 				as[cnt].hpredir = first;
33122a1ad637SFrançois Tigeot 			cnt++;
33132a1ad637SFrançois Tigeot 		}
33142a1ad637SFrançois Tigeot 	}
33152a1ad637SFrançois Tigeot 	for (i = 0; i < max; i++) {
33162a1ad637SFrançois Tigeot 		if (as[i].dir == HDAA_CTL_IN && (as[i].pincnt == 1 ||
33172a1ad637SFrançois Tigeot 		    as[i].pins[14] > 0 || as[i].pins[15] > 0))
33182a1ad637SFrançois Tigeot 			as[i].mixed = 1;
33192a1ad637SFrançois Tigeot 	}
33202a1ad637SFrançois Tigeot 	HDA_BOOTVERBOSE(
33212a1ad637SFrançois Tigeot 		device_printf(devinfo->dev,
33222a1ad637SFrançois Tigeot 		    "%d associations found:\n", max);
33232a1ad637SFrançois Tigeot 		for (i = 0; i < max; i++) {
33242a1ad637SFrançois Tigeot 			device_printf(devinfo->dev,
33252a1ad637SFrançois Tigeot 			    "Association %d (%d) %s%s:\n",
33262a1ad637SFrançois Tigeot 			    i, as[i].index, (as[i].dir == HDAA_CTL_IN)?"in":"out",
33272a1ad637SFrançois Tigeot 			    as[i].enable?"":" (disabled)");
33282a1ad637SFrançois Tigeot 			for (j = 0; j < 16; j++) {
33292a1ad637SFrançois Tigeot 				if (as[i].pins[j] == 0)
33302a1ad637SFrançois Tigeot 					continue;
33312a1ad637SFrançois Tigeot 				device_printf(devinfo->dev,
33322a1ad637SFrançois Tigeot 				    " Pin nid=%d seq=%d\n",
33332a1ad637SFrançois Tigeot 				    as[i].pins[j], j);
33342a1ad637SFrançois Tigeot 			}
33352a1ad637SFrançois Tigeot 		}
33362a1ad637SFrançois Tigeot 	);
33372a1ad637SFrançois Tigeot 
33382a1ad637SFrançois Tigeot 	devinfo->as = as;
33392a1ad637SFrançois Tigeot }
33402a1ad637SFrançois Tigeot 
33412a1ad637SFrançois Tigeot /*
33422a1ad637SFrançois Tigeot  * Trace path from DAC to pin.
33432a1ad637SFrançois Tigeot  */
33442a1ad637SFrançois Tigeot static nid_t
hdaa_audio_trace_dac(struct hdaa_devinfo * devinfo,int as,int seq,nid_t nid,int dupseq,int min,int only,int depth)33452a1ad637SFrançois Tigeot hdaa_audio_trace_dac(struct hdaa_devinfo *devinfo, int as, int seq, nid_t nid,
33462a1ad637SFrançois Tigeot     int dupseq, int min, int only, int depth)
33472a1ad637SFrançois Tigeot {
33482a1ad637SFrançois Tigeot 	struct hdaa_widget *w;
33492a1ad637SFrançois Tigeot 	int i, im = -1;
33502a1ad637SFrançois Tigeot 	nid_t m = 0, ret;
33512a1ad637SFrançois Tigeot 
33522a1ad637SFrançois Tigeot 	if (depth > HDA_PARSE_MAXDEPTH)
33532a1ad637SFrançois Tigeot 		return (0);
33542a1ad637SFrançois Tigeot 	w = hdaa_widget_get(devinfo, nid);
33552a1ad637SFrançois Tigeot 	if (w == NULL || w->enable == 0)
33562a1ad637SFrançois Tigeot 		return (0);
33572a1ad637SFrançois Tigeot 	HDA_BOOTHVERBOSE(
33582a1ad637SFrançois Tigeot 		if (!only) {
33592a1ad637SFrançois Tigeot 			device_printf(devinfo->dev,
33602a1ad637SFrançois Tigeot 			    " %*stracing via nid %d\n",
33612a1ad637SFrançois Tigeot 				depth + 1, "", w->nid);
33622a1ad637SFrançois Tigeot 		}
33632a1ad637SFrançois Tigeot 	);
33642a1ad637SFrançois Tigeot 	/* Use only unused widgets */
33652a1ad637SFrançois Tigeot 	if (w->bindas >= 0 && w->bindas != as) {
33662a1ad637SFrançois Tigeot 		HDA_BOOTHVERBOSE(
33672a1ad637SFrançois Tigeot 			if (!only) {
33682a1ad637SFrançois Tigeot 				device_printf(devinfo->dev,
33692a1ad637SFrançois Tigeot 				    " %*snid %d busy by association %d\n",
33702a1ad637SFrançois Tigeot 					depth + 1, "", w->nid, w->bindas);
33712a1ad637SFrançois Tigeot 			}
33722a1ad637SFrançois Tigeot 		);
33732a1ad637SFrançois Tigeot 		return (0);
33742a1ad637SFrançois Tigeot 	}
33752a1ad637SFrançois Tigeot 	if (dupseq < 0) {
33762a1ad637SFrançois Tigeot 		if (w->bindseqmask != 0) {
33772a1ad637SFrançois Tigeot 			HDA_BOOTHVERBOSE(
33782a1ad637SFrançois Tigeot 				if (!only) {
33792a1ad637SFrançois Tigeot 					device_printf(devinfo->dev,
33802a1ad637SFrançois Tigeot 					    " %*snid %d busy by seqmask %x\n",
33812a1ad637SFrançois Tigeot 						depth + 1, "", w->nid, w->bindseqmask);
33822a1ad637SFrançois Tigeot 				}
33832a1ad637SFrançois Tigeot 			);
33842a1ad637SFrançois Tigeot 			return (0);
33852a1ad637SFrançois Tigeot 		}
33862a1ad637SFrançois Tigeot 	} else {
33872a1ad637SFrançois Tigeot 		/* If this is headphones - allow duplicate first pin. */
33882a1ad637SFrançois Tigeot 		if (w->bindseqmask != 0 &&
33892a1ad637SFrançois Tigeot 		    (w->bindseqmask & (1 << dupseq)) == 0) {
33902a1ad637SFrançois Tigeot 			HDA_BOOTHVERBOSE(
33912a1ad637SFrançois Tigeot 				device_printf(devinfo->dev,
33922a1ad637SFrançois Tigeot 				    " %*snid %d busy by seqmask %x\n",
33932a1ad637SFrançois Tigeot 					depth + 1, "", w->nid, w->bindseqmask);
33942a1ad637SFrançois Tigeot 			);
33952a1ad637SFrançois Tigeot 			return (0);
33962a1ad637SFrançois Tigeot 		}
33972a1ad637SFrançois Tigeot 	}
33982a1ad637SFrançois Tigeot 
33992a1ad637SFrançois Tigeot 	switch (w->type) {
34002a1ad637SFrançois Tigeot 	case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT:
34012a1ad637SFrançois Tigeot 		/* Do not traverse input. AD1988 has digital monitor
34022a1ad637SFrançois Tigeot 		for which we are not ready. */
34032a1ad637SFrançois Tigeot 		break;
34042a1ad637SFrançois Tigeot 	case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT:
34052a1ad637SFrançois Tigeot 		/* If we are tracing HP take only dac of first pin. */
34062a1ad637SFrançois Tigeot 		if ((only == 0 || only == w->nid) &&
34072a1ad637SFrançois Tigeot 		    (w->nid >= min) && (dupseq < 0 || w->nid ==
34082a1ad637SFrançois Tigeot 		    devinfo->as[as].dacs[0][dupseq]))
34092a1ad637SFrançois Tigeot 			m = w->nid;
34102a1ad637SFrançois Tigeot 		break;
34112a1ad637SFrançois Tigeot 	case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX:
34122a1ad637SFrançois Tigeot 		if (depth > 0)
34132a1ad637SFrançois Tigeot 			break;
34142a1ad637SFrançois Tigeot 		/* Fall */
34152a1ad637SFrançois Tigeot 	default:
34162a1ad637SFrançois Tigeot 		/* Find reachable DACs with smallest nid respecting constraints. */
34172a1ad637SFrançois Tigeot 		for (i = 0; i < w->nconns; i++) {
34182a1ad637SFrançois Tigeot 			if (w->connsenable[i] == 0)
34192a1ad637SFrançois Tigeot 				continue;
34202a1ad637SFrançois Tigeot 			if (w->selconn != -1 && w->selconn != i)
34212a1ad637SFrançois Tigeot 				continue;
34222a1ad637SFrançois Tigeot 			if ((ret = hdaa_audio_trace_dac(devinfo, as, seq,
34232a1ad637SFrançois Tigeot 			    w->conns[i], dupseq, min, only, depth + 1)) != 0) {
34242a1ad637SFrançois Tigeot 				if (m == 0 || ret < m) {
34252a1ad637SFrançois Tigeot 					m = ret;
34262a1ad637SFrançois Tigeot 					im = i;
34272a1ad637SFrançois Tigeot 				}
34282a1ad637SFrançois Tigeot 				if (only || dupseq >= 0)
34292a1ad637SFrançois Tigeot 					break;
34302a1ad637SFrançois Tigeot 			}
34312a1ad637SFrançois Tigeot 		}
34322a1ad637SFrançois Tigeot 		if (im >= 0 && only && ((w->nconns > 1 &&
34332a1ad637SFrançois Tigeot 		    w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) ||
34342a1ad637SFrançois Tigeot 		    w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR))
34352a1ad637SFrançois Tigeot 			w->selconn = im;
34362a1ad637SFrançois Tigeot 		break;
34372a1ad637SFrançois Tigeot 	}
34382a1ad637SFrançois Tigeot 	if (m && only) {
34392a1ad637SFrançois Tigeot 		w->bindas = as;
34402a1ad637SFrançois Tigeot 		w->bindseqmask |= (1 << seq);
34412a1ad637SFrançois Tigeot 	}
34422a1ad637SFrançois Tigeot 	HDA_BOOTHVERBOSE(
34432a1ad637SFrançois Tigeot 		if (!only) {
34442a1ad637SFrançois Tigeot 			device_printf(devinfo->dev,
34452a1ad637SFrançois Tigeot 			    " %*snid %d returned %d\n",
34462a1ad637SFrançois Tigeot 				depth + 1, "", w->nid, m);
34472a1ad637SFrançois Tigeot 		}
34482a1ad637SFrançois Tigeot 	);
34492a1ad637SFrançois Tigeot 	return (m);
34502a1ad637SFrançois Tigeot }
34512a1ad637SFrançois Tigeot 
34522a1ad637SFrançois Tigeot /*
34532a1ad637SFrançois Tigeot  * Trace path from widget to ADC.
34542a1ad637SFrançois Tigeot  */
34552a1ad637SFrançois Tigeot static nid_t
hdaa_audio_trace_adc(struct hdaa_devinfo * devinfo,int as,int seq,nid_t nid,int mixed,int min,int only,int depth,int * length,int onlylength)34562a1ad637SFrançois Tigeot hdaa_audio_trace_adc(struct hdaa_devinfo *devinfo, int as, int seq, nid_t nid,
34572a1ad637SFrançois Tigeot     int mixed, int min, int only, int depth, int *length, int onlylength)
34582a1ad637SFrançois Tigeot {
34592a1ad637SFrançois Tigeot 	struct hdaa_widget *w, *wc;
34602a1ad637SFrançois Tigeot 	int i, j, im, lm = HDA_PARSE_MAXDEPTH;
34612a1ad637SFrançois Tigeot 	nid_t m = 0, ret;
34622a1ad637SFrançois Tigeot 
34632a1ad637SFrançois Tigeot 	if (depth > HDA_PARSE_MAXDEPTH)
34642a1ad637SFrançois Tigeot 		return (0);
34652a1ad637SFrançois Tigeot 	w = hdaa_widget_get(devinfo, nid);
34662a1ad637SFrançois Tigeot 	if (w == NULL || w->enable == 0)
34672a1ad637SFrançois Tigeot 		return (0);
34682a1ad637SFrançois Tigeot 	HDA_BOOTHVERBOSE(
34692a1ad637SFrançois Tigeot 		device_printf(devinfo->dev,
34702a1ad637SFrançois Tigeot 		    " %*stracing via nid %d\n",
34712a1ad637SFrançois Tigeot 			depth + 1, "", w->nid);
34722a1ad637SFrançois Tigeot 	);
34732a1ad637SFrançois Tigeot 	/* Use only unused widgets */
34742a1ad637SFrançois Tigeot 	if (w->bindas >= 0 && w->bindas != as) {
34752a1ad637SFrançois Tigeot 		HDA_BOOTHVERBOSE(
34762a1ad637SFrançois Tigeot 			device_printf(devinfo->dev,
34772a1ad637SFrançois Tigeot 			    " %*snid %d busy by association %d\n",
34782a1ad637SFrançois Tigeot 				depth + 1, "", w->nid, w->bindas);
34792a1ad637SFrançois Tigeot 		);
34802a1ad637SFrançois Tigeot 		return (0);
34812a1ad637SFrançois Tigeot 	}
34822a1ad637SFrançois Tigeot 	if (!mixed && w->bindseqmask != 0) {
34832a1ad637SFrançois Tigeot 		HDA_BOOTHVERBOSE(
34842a1ad637SFrançois Tigeot 			device_printf(devinfo->dev,
34852a1ad637SFrançois Tigeot 			    " %*snid %d busy by seqmask %x\n",
34862a1ad637SFrançois Tigeot 				depth + 1, "", w->nid, w->bindseqmask);
34872a1ad637SFrançois Tigeot 		);
34882a1ad637SFrançois Tigeot 		return (0);
34892a1ad637SFrançois Tigeot 	}
34902a1ad637SFrançois Tigeot 	switch (w->type) {
34912a1ad637SFrançois Tigeot 	case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT:
34922a1ad637SFrançois Tigeot 		if ((only == 0 || only == w->nid) && (w->nid >= min) &&
34932a1ad637SFrançois Tigeot 		    (onlylength == 0 || onlylength == depth)) {
34942a1ad637SFrançois Tigeot 			m = w->nid;
34952a1ad637SFrançois Tigeot 			*length = depth;
34962a1ad637SFrançois Tigeot 		}
34972a1ad637SFrançois Tigeot 		break;
34982a1ad637SFrançois Tigeot 	case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX:
34992a1ad637SFrançois Tigeot 		if (depth > 0)
35002a1ad637SFrançois Tigeot 			break;
35012a1ad637SFrançois Tigeot 		/* Fall */
35022a1ad637SFrançois Tigeot 	default:
35032a1ad637SFrançois Tigeot 		/* Try to find reachable ADCs with specified nid. */
35042a1ad637SFrançois Tigeot 		for (j = devinfo->startnode; j < devinfo->endnode; j++) {
35052a1ad637SFrançois Tigeot 			wc = hdaa_widget_get(devinfo, j);
35062a1ad637SFrançois Tigeot 			if (wc == NULL || wc->enable == 0)
35072a1ad637SFrançois Tigeot 				continue;
35082a1ad637SFrançois Tigeot 			im = -1;
35092a1ad637SFrançois Tigeot 			for (i = 0; i < wc->nconns; i++) {
35102a1ad637SFrançois Tigeot 				if (wc->connsenable[i] == 0)
35112a1ad637SFrançois Tigeot 					continue;
35122a1ad637SFrançois Tigeot 				if (wc->conns[i] != nid)
35132a1ad637SFrançois Tigeot 					continue;
35142a1ad637SFrançois Tigeot 				if ((ret = hdaa_audio_trace_adc(devinfo, as, seq,
35152a1ad637SFrançois Tigeot 				    j, mixed, min, only, depth + 1,
35162a1ad637SFrançois Tigeot 				    length, onlylength)) != 0) {
35172a1ad637SFrançois Tigeot 					if (m == 0 || ret < m ||
35182a1ad637SFrançois Tigeot 					    (ret == m && *length < lm)) {
35192a1ad637SFrançois Tigeot 						m = ret;
35202a1ad637SFrançois Tigeot 						im = i;
35212a1ad637SFrançois Tigeot 						lm = *length;
35222a1ad637SFrançois Tigeot 					} else
35232a1ad637SFrançois Tigeot 						*length = lm;
35242a1ad637SFrançois Tigeot 					if (only)
35252a1ad637SFrançois Tigeot 						break;
35262a1ad637SFrançois Tigeot 				}
35272a1ad637SFrançois Tigeot 			}
35282a1ad637SFrançois Tigeot 			if (im >= 0 && only && ((wc->nconns > 1 &&
35292a1ad637SFrançois Tigeot 			    wc->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) ||
35302a1ad637SFrançois Tigeot 			    wc->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR))
35312a1ad637SFrançois Tigeot 				wc->selconn = im;
35322a1ad637SFrançois Tigeot 		}
35332a1ad637SFrançois Tigeot 		break;
35342a1ad637SFrançois Tigeot 	}
35352a1ad637SFrançois Tigeot 	if (m && only) {
35362a1ad637SFrançois Tigeot 		w->bindas = as;
35372a1ad637SFrançois Tigeot 		w->bindseqmask |= (1 << seq);
35382a1ad637SFrançois Tigeot 	}
35392a1ad637SFrançois Tigeot 	HDA_BOOTHVERBOSE(
35402a1ad637SFrançois Tigeot 		device_printf(devinfo->dev,
35412a1ad637SFrançois Tigeot 		    " %*snid %d returned %d\n",
35422a1ad637SFrançois Tigeot 			depth + 1, "", w->nid, m);
35432a1ad637SFrançois Tigeot 	);
35442a1ad637SFrançois Tigeot 	return (m);
35452a1ad637SFrançois Tigeot }
35462a1ad637SFrançois Tigeot 
35472a1ad637SFrançois Tigeot /*
35482a1ad637SFrançois Tigeot  * Erase trace path of the specified association.
35492a1ad637SFrançois Tigeot  */
35502a1ad637SFrançois Tigeot static void
hdaa_audio_undo_trace(struct hdaa_devinfo * devinfo,int as,int seq)35512a1ad637SFrançois Tigeot hdaa_audio_undo_trace(struct hdaa_devinfo *devinfo, int as, int seq)
35522a1ad637SFrançois Tigeot {
35532a1ad637SFrançois Tigeot 	struct hdaa_widget *w;
35542a1ad637SFrançois Tigeot 	int i;
35552a1ad637SFrançois Tigeot 
35562a1ad637SFrançois Tigeot 	for (i = devinfo->startnode; i < devinfo->endnode; i++) {
35572a1ad637SFrançois Tigeot 		w = hdaa_widget_get(devinfo, i);
35582a1ad637SFrançois Tigeot 		if (w == NULL || w->enable == 0)
35592a1ad637SFrançois Tigeot 			continue;
35602a1ad637SFrançois Tigeot 		if (w->bindas == as) {
35612a1ad637SFrançois Tigeot 			if (seq >= 0) {
35622a1ad637SFrançois Tigeot 				w->bindseqmask &= ~(1 << seq);
35632a1ad637SFrançois Tigeot 				if (w->bindseqmask == 0) {
35642a1ad637SFrançois Tigeot 					w->bindas = -1;
35652a1ad637SFrançois Tigeot 					w->selconn = -1;
35662a1ad637SFrançois Tigeot 				}
35672a1ad637SFrançois Tigeot 			} else {
35682a1ad637SFrançois Tigeot 				w->bindas = -1;
35692a1ad637SFrançois Tigeot 				w->bindseqmask = 0;
35702a1ad637SFrançois Tigeot 				w->selconn = -1;
35712a1ad637SFrançois Tigeot 			}
35722a1ad637SFrançois Tigeot 		}
35732a1ad637SFrançois Tigeot 	}
35742a1ad637SFrançois Tigeot }
35752a1ad637SFrançois Tigeot 
35762a1ad637SFrançois Tigeot /*
35772a1ad637SFrançois Tigeot  * Trace association path from DAC to output
35782a1ad637SFrançois Tigeot  */
35792a1ad637SFrançois Tigeot static int
hdaa_audio_trace_as_out(struct hdaa_devinfo * devinfo,int as,int seq)35802a1ad637SFrançois Tigeot hdaa_audio_trace_as_out(struct hdaa_devinfo *devinfo, int as, int seq)
35812a1ad637SFrançois Tigeot {
35822a1ad637SFrançois Tigeot 	struct hdaa_audio_as *ases = devinfo->as;
35832a1ad637SFrançois Tigeot 	int i, hpredir;
35842a1ad637SFrançois Tigeot 	nid_t min, res;
35852a1ad637SFrançois Tigeot 
35862a1ad637SFrançois Tigeot 	/* Find next pin */
35872a1ad637SFrançois Tigeot 	for (i = seq; i < 16 && ases[as].pins[i] == 0; i++)
35882a1ad637SFrançois Tigeot 		;
35892a1ad637SFrançois Tigeot 	/* Check if there is no any left. If so - we succeeded. */
35902a1ad637SFrançois Tigeot 	if (i == 16)
35912a1ad637SFrançois Tigeot 		return (1);
35922a1ad637SFrançois Tigeot 
35932a1ad637SFrançois Tigeot 	hpredir = (i == 15 && ases[as].fakeredir == 0)?ases[as].hpredir:-1;
35942a1ad637SFrançois Tigeot 	min = 0;
35952a1ad637SFrançois Tigeot 	do {
35962a1ad637SFrançois Tigeot 		HDA_BOOTHVERBOSE(
35972a1ad637SFrançois Tigeot 			device_printf(devinfo->dev,
35982a1ad637SFrançois Tigeot 			    " Tracing pin %d with min nid %d",
35992a1ad637SFrançois Tigeot 			    ases[as].pins[i], min);
36002a1ad637SFrançois Tigeot 			if (hpredir >= 0)
360167931cc4SFrançois Tigeot 				kprintf(" and hpredir %d", hpredir);
360267931cc4SFrançois Tigeot 			kprintf("\n");
36032a1ad637SFrançois Tigeot 		);
36042a1ad637SFrançois Tigeot 		/* Trace this pin taking min nid into account. */
36052a1ad637SFrançois Tigeot 		res = hdaa_audio_trace_dac(devinfo, as, i,
36062a1ad637SFrançois Tigeot 		    ases[as].pins[i], hpredir, min, 0, 0);
36072a1ad637SFrançois Tigeot 		if (res == 0) {
36082a1ad637SFrançois Tigeot 			/* If we failed - return to previous and redo it. */
36092a1ad637SFrançois Tigeot 			HDA_BOOTVERBOSE(
36102a1ad637SFrançois Tigeot 				device_printf(devinfo->dev,
36112a1ad637SFrançois Tigeot 				    " Unable to trace pin %d seq %d with min "
36122a1ad637SFrançois Tigeot 				    "nid %d",
36132a1ad637SFrançois Tigeot 				    ases[as].pins[i], i, min);
36142a1ad637SFrançois Tigeot 				if (hpredir >= 0)
361567931cc4SFrançois Tigeot 					kprintf(" and hpredir %d", hpredir);
361667931cc4SFrançois Tigeot 				kprintf("\n");
36172a1ad637SFrançois Tigeot 			);
36182a1ad637SFrançois Tigeot 			return (0);
36192a1ad637SFrançois Tigeot 		}
36202a1ad637SFrançois Tigeot 		HDA_BOOTVERBOSE(
36212a1ad637SFrançois Tigeot 			device_printf(devinfo->dev,
36222a1ad637SFrançois Tigeot 			    " Pin %d traced to DAC %d",
36232a1ad637SFrançois Tigeot 			    ases[as].pins[i], res);
36242a1ad637SFrançois Tigeot 			if (hpredir >= 0)
362567931cc4SFrançois Tigeot 				kprintf(" and hpredir %d", hpredir);
36262a1ad637SFrançois Tigeot 			if (ases[as].fakeredir)
362767931cc4SFrançois Tigeot 				kprintf(" with fake redirection");
362867931cc4SFrançois Tigeot 			kprintf("\n");
36292a1ad637SFrançois Tigeot 		);
36302a1ad637SFrançois Tigeot 		/* Trace again to mark the path */
36312a1ad637SFrançois Tigeot 		hdaa_audio_trace_dac(devinfo, as, i,
36322a1ad637SFrançois Tigeot 		    ases[as].pins[i], hpredir, min, res, 0);
36332a1ad637SFrançois Tigeot 		ases[as].dacs[0][i] = res;
36342a1ad637SFrançois Tigeot 		/* We succeeded, so call next. */
36352a1ad637SFrançois Tigeot 		if (hdaa_audio_trace_as_out(devinfo, as, i + 1))
36362a1ad637SFrançois Tigeot 			return (1);
36372a1ad637SFrançois Tigeot 		/* If next failed, we should retry with next min */
36382a1ad637SFrançois Tigeot 		hdaa_audio_undo_trace(devinfo, as, i);
36392a1ad637SFrançois Tigeot 		ases[as].dacs[0][i] = 0;
36402a1ad637SFrançois Tigeot 		min = res + 1;
36412a1ad637SFrançois Tigeot 	} while (1);
36422a1ad637SFrançois Tigeot }
36432a1ad637SFrançois Tigeot 
36442a1ad637SFrançois Tigeot /*
36452a1ad637SFrançois Tigeot  * Check equivalency of two DACs.
36462a1ad637SFrançois Tigeot  */
36472a1ad637SFrançois Tigeot static int
hdaa_audio_dacs_equal(struct hdaa_widget * w1,struct hdaa_widget * w2)36482a1ad637SFrançois Tigeot hdaa_audio_dacs_equal(struct hdaa_widget *w1, struct hdaa_widget *w2)
36492a1ad637SFrançois Tigeot {
36502a1ad637SFrançois Tigeot 	struct hdaa_devinfo *devinfo = w1->devinfo;
36512a1ad637SFrançois Tigeot 	struct hdaa_widget *w3;
36522a1ad637SFrançois Tigeot 	int i, j, c1, c2;
36532a1ad637SFrançois Tigeot 
36542a1ad637SFrançois Tigeot 	if (memcmp(&w1->param, &w2->param, sizeof(w1->param)))
36552a1ad637SFrançois Tigeot 		return (0);
36562a1ad637SFrançois Tigeot 	for (i = devinfo->startnode; i < devinfo->endnode; i++) {
36572a1ad637SFrançois Tigeot 		w3 = hdaa_widget_get(devinfo, i);
36582a1ad637SFrançois Tigeot 		if (w3 == NULL || w3->enable == 0)
36592a1ad637SFrançois Tigeot 			continue;
36602a1ad637SFrançois Tigeot 		if (w3->bindas != w1->bindas)
36612a1ad637SFrançois Tigeot 			continue;
36622a1ad637SFrançois Tigeot 		if (w3->nconns == 0)
36632a1ad637SFrançois Tigeot 			continue;
36642a1ad637SFrançois Tigeot 		c1 = c2 = -1;
36652a1ad637SFrançois Tigeot 		for (j = 0; j < w3->nconns; j++) {
36662a1ad637SFrançois Tigeot 			if (w3->connsenable[j] == 0)
36672a1ad637SFrançois Tigeot 				continue;
36682a1ad637SFrançois Tigeot 			if (w3->conns[j] == w1->nid)
36692a1ad637SFrançois Tigeot 				c1 = j;
36702a1ad637SFrançois Tigeot 			if (w3->conns[j] == w2->nid)
36712a1ad637SFrançois Tigeot 				c2 = j;
36722a1ad637SFrançois Tigeot 		}
36732a1ad637SFrançois Tigeot 		if (c1 < 0)
36742a1ad637SFrançois Tigeot 			continue;
36752a1ad637SFrançois Tigeot 		if (c2 < 0)
36762a1ad637SFrançois Tigeot 			return (0);
36772a1ad637SFrançois Tigeot 		if (w3->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER)
36782a1ad637SFrançois Tigeot 			return (0);
36792a1ad637SFrançois Tigeot 	}
36802a1ad637SFrançois Tigeot 	return (1);
36812a1ad637SFrançois Tigeot }
36822a1ad637SFrançois Tigeot 
36832a1ad637SFrançois Tigeot /*
36842a1ad637SFrançois Tigeot  * Check equivalency of two ADCs.
36852a1ad637SFrançois Tigeot  */
36862a1ad637SFrançois Tigeot static int
hdaa_audio_adcs_equal(struct hdaa_widget * w1,struct hdaa_widget * w2)36872a1ad637SFrançois Tigeot hdaa_audio_adcs_equal(struct hdaa_widget *w1, struct hdaa_widget *w2)
36882a1ad637SFrançois Tigeot {
36892a1ad637SFrançois Tigeot 	struct hdaa_devinfo *devinfo = w1->devinfo;
36902a1ad637SFrançois Tigeot 	struct hdaa_widget *w3, *w4;
36912a1ad637SFrançois Tigeot 	int i;
36922a1ad637SFrançois Tigeot 
36932a1ad637SFrançois Tigeot 	if (memcmp(&w1->param, &w2->param, sizeof(w1->param)))
36942a1ad637SFrançois Tigeot 		return (0);
36952a1ad637SFrançois Tigeot 	if (w1->nconns != 1 || w2->nconns != 1)
36962a1ad637SFrançois Tigeot 		return (0);
36972a1ad637SFrançois Tigeot 	if (w1->conns[0] == w2->conns[0])
36982a1ad637SFrançois Tigeot 		return (1);
36992a1ad637SFrançois Tigeot 	w3 = hdaa_widget_get(devinfo, w1->conns[0]);
37002a1ad637SFrançois Tigeot 	if (w3 == NULL || w3->enable == 0)
37012a1ad637SFrançois Tigeot 		return (0);
37022a1ad637SFrançois Tigeot 	w4 = hdaa_widget_get(devinfo, w2->conns[0]);
37032a1ad637SFrançois Tigeot 	if (w4 == NULL || w4->enable == 0)
37042a1ad637SFrançois Tigeot 		return (0);
37052a1ad637SFrançois Tigeot 	if (w3->bindas == w4->bindas && w3->bindseqmask == w4->bindseqmask)
37062a1ad637SFrançois Tigeot 		return (1);
37072a1ad637SFrançois Tigeot 	if (w4->bindas >= 0)
37082a1ad637SFrançois Tigeot 		return (0);
37092a1ad637SFrançois Tigeot 	if (w3->type != w4->type)
37102a1ad637SFrançois Tigeot 		return (0);
37112a1ad637SFrançois Tigeot 	if (memcmp(&w3->param, &w4->param, sizeof(w3->param)))
37122a1ad637SFrançois Tigeot 		return (0);
37132a1ad637SFrançois Tigeot 	if (w3->nconns != w4->nconns)
37142a1ad637SFrançois Tigeot 		return (0);
37152a1ad637SFrançois Tigeot 	for (i = 0; i < w3->nconns; i++) {
37162a1ad637SFrançois Tigeot 		if (w3->conns[i] != w4->conns[i])
37172a1ad637SFrançois Tigeot 			return (0);
37182a1ad637SFrançois Tigeot 	}
37192a1ad637SFrançois Tigeot 	return (1);
37202a1ad637SFrançois Tigeot }
37212a1ad637SFrançois Tigeot 
37222a1ad637SFrançois Tigeot /*
37232a1ad637SFrançois Tigeot  * Look for equivalent DAC/ADC to implement second channel.
37242a1ad637SFrançois Tigeot  */
37252a1ad637SFrançois Tigeot static void
hdaa_audio_adddac(struct hdaa_devinfo * devinfo,int asid)37262a1ad637SFrançois Tigeot hdaa_audio_adddac(struct hdaa_devinfo *devinfo, int asid)
37272a1ad637SFrançois Tigeot {
37282a1ad637SFrançois Tigeot 	struct hdaa_audio_as *as = &devinfo->as[asid];
37292a1ad637SFrançois Tigeot 	struct hdaa_widget *w1, *w2;
37302a1ad637SFrançois Tigeot 	int i, pos;
37312a1ad637SFrançois Tigeot 	nid_t nid1, nid2;
37322a1ad637SFrançois Tigeot 
37332a1ad637SFrançois Tigeot 	HDA_BOOTVERBOSE(
37342a1ad637SFrançois Tigeot 		device_printf(devinfo->dev,
37352a1ad637SFrançois Tigeot 		    "Looking for additional %sC "
37362a1ad637SFrançois Tigeot 		    "for association %d (%d)\n",
37372a1ad637SFrançois Tigeot 		    (as->dir == HDAA_CTL_OUT) ? "DA" : "AD",
37382a1ad637SFrançois Tigeot 		    asid, as->index);
37392a1ad637SFrançois Tigeot 	);
37402a1ad637SFrançois Tigeot 
37412a1ad637SFrançois Tigeot 	/* Find the exisitng DAC position and return if found more the one. */
37422a1ad637SFrançois Tigeot 	pos = -1;
37432a1ad637SFrançois Tigeot 	for (i = 0; i < 16; i++) {
37442a1ad637SFrançois Tigeot 		if (as->dacs[0][i] <= 0)
37452a1ad637SFrançois Tigeot 			continue;
37462a1ad637SFrançois Tigeot 		if (pos >= 0 && as->dacs[0][i] != as->dacs[0][pos])
37472a1ad637SFrançois Tigeot 			return;
37482a1ad637SFrançois Tigeot 		pos = i;
37492a1ad637SFrançois Tigeot 	}
37502a1ad637SFrançois Tigeot 
37512a1ad637SFrançois Tigeot 	nid1 = as->dacs[0][pos];
37522a1ad637SFrançois Tigeot 	w1 = hdaa_widget_get(devinfo, nid1);
37532a1ad637SFrançois Tigeot 	w2 = NULL;
37542a1ad637SFrançois Tigeot 	for (nid2 = devinfo->startnode; nid2 < devinfo->endnode; nid2++) {
37552a1ad637SFrançois Tigeot 		w2 = hdaa_widget_get(devinfo, nid2);
37562a1ad637SFrançois Tigeot 		if (w2 == NULL || w2->enable == 0)
37572a1ad637SFrançois Tigeot 			continue;
37582a1ad637SFrançois Tigeot 		if (w2->bindas >= 0)
37592a1ad637SFrançois Tigeot 			continue;
37602a1ad637SFrançois Tigeot 		if (w1->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT) {
37612a1ad637SFrançois Tigeot 			if (w2->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT)
37622a1ad637SFrançois Tigeot 				continue;
37632a1ad637SFrançois Tigeot 			if (hdaa_audio_dacs_equal(w1, w2))
37642a1ad637SFrançois Tigeot 				break;
37652a1ad637SFrançois Tigeot 		} else {
37662a1ad637SFrançois Tigeot 			if (w2->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT)
37672a1ad637SFrançois Tigeot 				continue;
37682a1ad637SFrançois Tigeot 			if (hdaa_audio_adcs_equal(w1, w2))
37692a1ad637SFrançois Tigeot 				break;
37702a1ad637SFrançois Tigeot 		}
37712a1ad637SFrançois Tigeot 	}
37722a1ad637SFrançois Tigeot 	if (nid2 >= devinfo->endnode)
37732a1ad637SFrançois Tigeot 		return;
37742a1ad637SFrançois Tigeot 	w2->bindas = w1->bindas;
37752a1ad637SFrançois Tigeot 	w2->bindseqmask = w1->bindseqmask;
37762a1ad637SFrançois Tigeot 	if (w1->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT) {
37772a1ad637SFrançois Tigeot 		HDA_BOOTVERBOSE(
37782a1ad637SFrançois Tigeot 			device_printf(devinfo->dev,
37792a1ad637SFrançois Tigeot 			    " ADC %d considered equal to ADC %d\n", nid2, nid1);
37802a1ad637SFrançois Tigeot 		);
37812a1ad637SFrançois Tigeot 		w1 = hdaa_widget_get(devinfo, w1->conns[0]);
37822a1ad637SFrançois Tigeot 		w2 = hdaa_widget_get(devinfo, w2->conns[0]);
37832a1ad637SFrançois Tigeot 		w2->bindas = w1->bindas;
37842a1ad637SFrançois Tigeot 		w2->bindseqmask = w1->bindseqmask;
37852a1ad637SFrançois Tigeot 	} else {
37862a1ad637SFrançois Tigeot 		HDA_BOOTVERBOSE(
37872a1ad637SFrançois Tigeot 			device_printf(devinfo->dev,
37882a1ad637SFrançois Tigeot 			    " DAC %d considered equal to DAC %d\n", nid2, nid1);
37892a1ad637SFrançois Tigeot 		);
37902a1ad637SFrançois Tigeot 	}
37912a1ad637SFrançois Tigeot 	for (i = 0; i < 16; i++) {
37922a1ad637SFrançois Tigeot 		if (as->dacs[0][i] <= 0)
37932a1ad637SFrançois Tigeot 			continue;
37942a1ad637SFrançois Tigeot 		as->dacs[as->num_chans][i] = nid2;
37952a1ad637SFrançois Tigeot 	}
37962a1ad637SFrançois Tigeot 	as->num_chans++;
37972a1ad637SFrançois Tigeot }
37982a1ad637SFrançois Tigeot 
37992a1ad637SFrançois Tigeot /*
38002a1ad637SFrançois Tigeot  * Trace association path from input to ADC
38012a1ad637SFrançois Tigeot  */
38022a1ad637SFrançois Tigeot static int
hdaa_audio_trace_as_in(struct hdaa_devinfo * devinfo,int as)38032a1ad637SFrançois Tigeot hdaa_audio_trace_as_in(struct hdaa_devinfo *devinfo, int as)
38042a1ad637SFrançois Tigeot {
38052a1ad637SFrançois Tigeot 	struct hdaa_audio_as *ases = devinfo->as;
38062a1ad637SFrançois Tigeot 	struct hdaa_widget *w;
38072a1ad637SFrançois Tigeot 	int i, j, k, length;
38082a1ad637SFrançois Tigeot 
38092a1ad637SFrançois Tigeot 	for (j = devinfo->startnode; j < devinfo->endnode; j++) {
38102a1ad637SFrançois Tigeot 		w = hdaa_widget_get(devinfo, j);
38112a1ad637SFrançois Tigeot 		if (w == NULL || w->enable == 0)
38122a1ad637SFrançois Tigeot 			continue;
38132a1ad637SFrançois Tigeot 		if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT)
38142a1ad637SFrançois Tigeot 			continue;
38152a1ad637SFrançois Tigeot 		if (w->bindas >= 0 && w->bindas != as)
38162a1ad637SFrançois Tigeot 			continue;
38172a1ad637SFrançois Tigeot 
38182a1ad637SFrançois Tigeot 		/* Find next pin */
38192a1ad637SFrançois Tigeot 		for (i = 0; i < 16; i++) {
38202a1ad637SFrançois Tigeot 			if (ases[as].pins[i] == 0)
38212a1ad637SFrançois Tigeot 				continue;
38222a1ad637SFrançois Tigeot 
38232a1ad637SFrançois Tigeot 			HDA_BOOTHVERBOSE(
38242a1ad637SFrançois Tigeot 				device_printf(devinfo->dev,
38252a1ad637SFrançois Tigeot 				    " Tracing pin %d to ADC %d\n",
38262a1ad637SFrançois Tigeot 				    ases[as].pins[i], j);
38272a1ad637SFrançois Tigeot 			);
38282a1ad637SFrançois Tigeot 			/* Trace this pin taking goal into account. */
38292a1ad637SFrançois Tigeot 			if (hdaa_audio_trace_adc(devinfo, as, i,
38302a1ad637SFrançois Tigeot 			    ases[as].pins[i], 1, 0, j, 0, &length, 0) == 0) {
38312a1ad637SFrançois Tigeot 				/* If we failed - return to previous and redo it. */
38322a1ad637SFrançois Tigeot 				HDA_BOOTVERBOSE(
38332a1ad637SFrançois Tigeot 					device_printf(devinfo->dev,
38342a1ad637SFrançois Tigeot 					    " Unable to trace pin %d to ADC %d, undo traces\n",
38352a1ad637SFrançois Tigeot 					    ases[as].pins[i], j);
38362a1ad637SFrançois Tigeot 				);
38372a1ad637SFrançois Tigeot 				hdaa_audio_undo_trace(devinfo, as, -1);
38382a1ad637SFrançois Tigeot 				for (k = 0; k < 16; k++)
38392a1ad637SFrançois Tigeot 					ases[as].dacs[0][k] = 0;
38402a1ad637SFrançois Tigeot 				break;
38412a1ad637SFrançois Tigeot 			}
38422a1ad637SFrançois Tigeot 			HDA_BOOTVERBOSE(
38432a1ad637SFrançois Tigeot 				device_printf(devinfo->dev,
38442a1ad637SFrançois Tigeot 				    " Pin %d traced to ADC %d\n",
38452a1ad637SFrançois Tigeot 				    ases[as].pins[i], j);
38462a1ad637SFrançois Tigeot 			);
38472a1ad637SFrançois Tigeot 			ases[as].dacs[0][i] = j;
38482a1ad637SFrançois Tigeot 		}
38492a1ad637SFrançois Tigeot 		if (i == 16)
38502a1ad637SFrançois Tigeot 			return (1);
38512a1ad637SFrançois Tigeot 	}
38522a1ad637SFrançois Tigeot 	return (0);
38532a1ad637SFrançois Tigeot }
38542a1ad637SFrançois Tigeot 
38552a1ad637SFrançois Tigeot /*
38562a1ad637SFrançois Tigeot  * Trace association path from input to multiple ADCs
38572a1ad637SFrançois Tigeot  */
38582a1ad637SFrançois Tigeot static int
hdaa_audio_trace_as_in_mch(struct hdaa_devinfo * devinfo,int as,int seq)38592a1ad637SFrançois Tigeot hdaa_audio_trace_as_in_mch(struct hdaa_devinfo *devinfo, int as, int seq)
38602a1ad637SFrançois Tigeot {
38612a1ad637SFrançois Tigeot 	struct hdaa_audio_as *ases = devinfo->as;
38622a1ad637SFrançois Tigeot 	int i, length;
38632a1ad637SFrançois Tigeot 	nid_t min, res;
38642a1ad637SFrançois Tigeot 
38652a1ad637SFrançois Tigeot 	/* Find next pin */
38662a1ad637SFrançois Tigeot 	for (i = seq; i < 16 && ases[as].pins[i] == 0; i++)
38672a1ad637SFrançois Tigeot 		;
38682a1ad637SFrançois Tigeot 	/* Check if there is no any left. If so - we succeeded. */
38692a1ad637SFrançois Tigeot 	if (i == 16)
38702a1ad637SFrançois Tigeot 		return (1);
38712a1ad637SFrançois Tigeot 
38722a1ad637SFrançois Tigeot 	min = 0;
38732a1ad637SFrançois Tigeot 	do {
38742a1ad637SFrançois Tigeot 		HDA_BOOTHVERBOSE(
38752a1ad637SFrançois Tigeot 			device_printf(devinfo->dev,
38762a1ad637SFrançois Tigeot 			    " Tracing pin %d with min nid %d",
38772a1ad637SFrançois Tigeot 			    ases[as].pins[i], min);
387867931cc4SFrançois Tigeot 			kprintf("\n");
38792a1ad637SFrançois Tigeot 		);
38802a1ad637SFrançois Tigeot 		/* Trace this pin taking min nid into account. */
38812a1ad637SFrançois Tigeot 		res = hdaa_audio_trace_adc(devinfo, as, i,
38822a1ad637SFrançois Tigeot 		    ases[as].pins[i], 0, min, 0, 0, &length, 0);
38832a1ad637SFrançois Tigeot 		if (res == 0) {
38842a1ad637SFrançois Tigeot 			/* If we failed - return to previous and redo it. */
38852a1ad637SFrançois Tigeot 			HDA_BOOTVERBOSE(
38862a1ad637SFrançois Tigeot 				device_printf(devinfo->dev,
38872a1ad637SFrançois Tigeot 				    " Unable to trace pin %d seq %d with min "
38882a1ad637SFrançois Tigeot 				    "nid %d",
38892a1ad637SFrançois Tigeot 				    ases[as].pins[i], i, min);
389067931cc4SFrançois Tigeot 				kprintf("\n");
38912a1ad637SFrançois Tigeot 			);
38922a1ad637SFrançois Tigeot 			return (0);
38932a1ad637SFrançois Tigeot 		}
38942a1ad637SFrançois Tigeot 		HDA_BOOTVERBOSE(
38952a1ad637SFrançois Tigeot 			device_printf(devinfo->dev,
38962a1ad637SFrançois Tigeot 			    " Pin %d traced to ADC %d\n",
38972a1ad637SFrançois Tigeot 			    ases[as].pins[i], res);
38982a1ad637SFrançois Tigeot 		);
38992a1ad637SFrançois Tigeot 		/* Trace again to mark the path */
39002a1ad637SFrançois Tigeot 		hdaa_audio_trace_adc(devinfo, as, i,
39012a1ad637SFrançois Tigeot 		    ases[as].pins[i], 0, min, res, 0, &length, length);
39022a1ad637SFrançois Tigeot 		ases[as].dacs[0][i] = res;
39032a1ad637SFrançois Tigeot 		/* We succeeded, so call next. */
39042a1ad637SFrançois Tigeot 		if (hdaa_audio_trace_as_in_mch(devinfo, as, i + 1))
39052a1ad637SFrançois Tigeot 			return (1);
39062a1ad637SFrançois Tigeot 		/* If next failed, we should retry with next min */
39072a1ad637SFrançois Tigeot 		hdaa_audio_undo_trace(devinfo, as, i);
39082a1ad637SFrançois Tigeot 		ases[as].dacs[0][i] = 0;
39092a1ad637SFrançois Tigeot 		min = res + 1;
39102a1ad637SFrançois Tigeot 	} while (1);
39112a1ad637SFrançois Tigeot }
39122a1ad637SFrançois Tigeot 
39132a1ad637SFrançois Tigeot /*
39142a1ad637SFrançois Tigeot  * Trace input monitor path from mixer to output association.
39152a1ad637SFrançois Tigeot  */
39162a1ad637SFrançois Tigeot static int
hdaa_audio_trace_to_out(struct hdaa_devinfo * devinfo,nid_t nid,int depth)39172a1ad637SFrançois Tigeot hdaa_audio_trace_to_out(struct hdaa_devinfo *devinfo, nid_t nid, int depth)
39182a1ad637SFrançois Tigeot {
39192a1ad637SFrançois Tigeot 	struct hdaa_audio_as *ases = devinfo->as;
39202a1ad637SFrançois Tigeot 	struct hdaa_widget *w, *wc;
39212a1ad637SFrançois Tigeot 	int i, j;
39222a1ad637SFrançois Tigeot 	nid_t res = 0;
39232a1ad637SFrançois Tigeot 
39242a1ad637SFrançois Tigeot 	if (depth > HDA_PARSE_MAXDEPTH)
39252a1ad637SFrançois Tigeot 		return (0);
39262a1ad637SFrançois Tigeot 	w = hdaa_widget_get(devinfo, nid);
39272a1ad637SFrançois Tigeot 	if (w == NULL || w->enable == 0)
39282a1ad637SFrançois Tigeot 		return (0);
39292a1ad637SFrançois Tigeot 	HDA_BOOTHVERBOSE(
39302a1ad637SFrançois Tigeot 		device_printf(devinfo->dev,
39312a1ad637SFrançois Tigeot 		    " %*stracing via nid %d\n",
39322a1ad637SFrançois Tigeot 			depth + 1, "", w->nid);
39332a1ad637SFrançois Tigeot 	);
39342a1ad637SFrançois Tigeot 	/* Use only unused widgets */
39352a1ad637SFrançois Tigeot 	if (depth > 0 && w->bindas != -1) {
39362a1ad637SFrançois Tigeot 		if (w->bindas < 0 || ases[w->bindas].dir == HDAA_CTL_OUT) {
39372a1ad637SFrançois Tigeot 			HDA_BOOTHVERBOSE(
39382a1ad637SFrançois Tigeot 				device_printf(devinfo->dev,
39392a1ad637SFrançois Tigeot 				    " %*snid %d found output association %d\n",
39402a1ad637SFrançois Tigeot 					depth + 1, "", w->nid, w->bindas);
39412a1ad637SFrançois Tigeot 			);
39422a1ad637SFrançois Tigeot 			if (w->bindas >= 0)
39432a1ad637SFrançois Tigeot 				w->pflags |= HDAA_ADC_MONITOR;
39442a1ad637SFrançois Tigeot 			return (1);
39452a1ad637SFrançois Tigeot 		} else {
39462a1ad637SFrançois Tigeot 			HDA_BOOTHVERBOSE(
39472a1ad637SFrançois Tigeot 				device_printf(devinfo->dev,
39482a1ad637SFrançois Tigeot 				    " %*snid %d busy by input association %d\n",
39492a1ad637SFrançois Tigeot 					depth + 1, "", w->nid, w->bindas);
39502a1ad637SFrançois Tigeot 			);
39512a1ad637SFrançois Tigeot 			return (0);
39522a1ad637SFrançois Tigeot 		}
39532a1ad637SFrançois Tigeot 	}
39542a1ad637SFrançois Tigeot 
39552a1ad637SFrançois Tigeot 	switch (w->type) {
39562a1ad637SFrançois Tigeot 	case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT:
39572a1ad637SFrançois Tigeot 		/* Do not traverse input. AD1988 has digital monitor
39582a1ad637SFrançois Tigeot 		for which we are not ready. */
39592a1ad637SFrançois Tigeot 		break;
39602a1ad637SFrançois Tigeot 	case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX:
39612a1ad637SFrançois Tigeot 		if (depth > 0)
39622a1ad637SFrançois Tigeot 			break;
39632a1ad637SFrançois Tigeot 		/* Fall */
39642a1ad637SFrançois Tigeot 	default:
39652a1ad637SFrançois Tigeot 		/* Try to find reachable ADCs with specified nid. */
39662a1ad637SFrançois Tigeot 		for (j = devinfo->startnode; j < devinfo->endnode; j++) {
39672a1ad637SFrançois Tigeot 			wc = hdaa_widget_get(devinfo, j);
39682a1ad637SFrançois Tigeot 			if (wc == NULL || wc->enable == 0)
39692a1ad637SFrançois Tigeot 				continue;
39702a1ad637SFrançois Tigeot 			for (i = 0; i < wc->nconns; i++) {
39712a1ad637SFrançois Tigeot 				if (wc->connsenable[i] == 0)
39722a1ad637SFrançois Tigeot 					continue;
39732a1ad637SFrançois Tigeot 				if (wc->conns[i] != nid)
39742a1ad637SFrançois Tigeot 					continue;
39752a1ad637SFrançois Tigeot 				if (hdaa_audio_trace_to_out(devinfo,
39762a1ad637SFrançois Tigeot 				    j, depth + 1) != 0) {
39772a1ad637SFrançois Tigeot 					res = 1;
39782a1ad637SFrançois Tigeot 					if (wc->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR &&
39792a1ad637SFrançois Tigeot 					    wc->selconn == -1)
39802a1ad637SFrançois Tigeot 						wc->selconn = i;
39812a1ad637SFrançois Tigeot 				}
39822a1ad637SFrançois Tigeot 			}
39832a1ad637SFrançois Tigeot 		}
39842a1ad637SFrançois Tigeot 		break;
39852a1ad637SFrançois Tigeot 	}
39862a1ad637SFrançois Tigeot 	if (res && w->bindas == -1)
39872a1ad637SFrançois Tigeot 		w->bindas = -2;
39882a1ad637SFrançois Tigeot 
39892a1ad637SFrançois Tigeot 	HDA_BOOTHVERBOSE(
39902a1ad637SFrançois Tigeot 		device_printf(devinfo->dev,
39912a1ad637SFrançois Tigeot 		    " %*snid %d returned %d\n",
39922a1ad637SFrançois Tigeot 			depth + 1, "", w->nid, res);
39932a1ad637SFrançois Tigeot 	);
39942a1ad637SFrançois Tigeot 	return (res);
39952a1ad637SFrançois Tigeot }
39962a1ad637SFrançois Tigeot 
39972a1ad637SFrançois Tigeot /*
39982a1ad637SFrançois Tigeot  * Trace extra associations (beeper, monitor)
39992a1ad637SFrançois Tigeot  */
40002a1ad637SFrançois Tigeot static void
hdaa_audio_trace_as_extra(struct hdaa_devinfo * devinfo)40012a1ad637SFrançois Tigeot hdaa_audio_trace_as_extra(struct hdaa_devinfo *devinfo)
40022a1ad637SFrançois Tigeot {
40032a1ad637SFrançois Tigeot 	struct hdaa_audio_as *as = devinfo->as;
40042a1ad637SFrançois Tigeot 	struct hdaa_widget *w;
40052a1ad637SFrançois Tigeot 	int j;
40062a1ad637SFrançois Tigeot 
40072a1ad637SFrançois Tigeot 	/* Input monitor */
40082a1ad637SFrançois Tigeot 	/* Find mixer associated with input, but supplying signal
40092a1ad637SFrançois Tigeot 	   for output associations. Hope it will be input monitor. */
40102a1ad637SFrançois Tigeot 	HDA_BOOTVERBOSE(
40112a1ad637SFrançois Tigeot 		device_printf(devinfo->dev,
40122a1ad637SFrançois Tigeot 		    "Tracing input monitor\n");
40132a1ad637SFrançois Tigeot 	);
40142a1ad637SFrançois Tigeot 	for (j = devinfo->startnode; j < devinfo->endnode; j++) {
40152a1ad637SFrançois Tigeot 		w = hdaa_widget_get(devinfo, j);
40162a1ad637SFrançois Tigeot 		if (w == NULL || w->enable == 0)
40172a1ad637SFrançois Tigeot 			continue;
40182a1ad637SFrançois Tigeot 		if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER)
40192a1ad637SFrançois Tigeot 			continue;
40202a1ad637SFrançois Tigeot 		if (w->bindas < 0 || as[w->bindas].dir != HDAA_CTL_IN)
40212a1ad637SFrançois Tigeot 			continue;
40222a1ad637SFrançois Tigeot 		HDA_BOOTVERBOSE(
40232a1ad637SFrançois Tigeot 			device_printf(devinfo->dev,
40242a1ad637SFrançois Tigeot 			    " Tracing nid %d to out\n",
40252a1ad637SFrançois Tigeot 			    j);
40262a1ad637SFrançois Tigeot 		);
40272a1ad637SFrançois Tigeot 		if (hdaa_audio_trace_to_out(devinfo, w->nid, 0)) {
40282a1ad637SFrançois Tigeot 			HDA_BOOTVERBOSE(
40292a1ad637SFrançois Tigeot 				device_printf(devinfo->dev,
40302a1ad637SFrançois Tigeot 				    " nid %d is input monitor\n",
40312a1ad637SFrançois Tigeot 					w->nid);
40322a1ad637SFrançois Tigeot 			);
40332a1ad637SFrançois Tigeot 			w->ossdev = SOUND_MIXER_IMIX;
40342a1ad637SFrançois Tigeot 		}
40352a1ad637SFrançois Tigeot 	}
40362a1ad637SFrançois Tigeot 
40372a1ad637SFrançois Tigeot 	/* Other inputs monitor */
40382a1ad637SFrançois Tigeot 	/* Find input pins supplying signal for output associations.
40392a1ad637SFrançois Tigeot 	   Hope it will be input monitoring. */
40402a1ad637SFrançois Tigeot 	HDA_BOOTVERBOSE(
40412a1ad637SFrançois Tigeot 		device_printf(devinfo->dev,
40422a1ad637SFrançois Tigeot 		    "Tracing other input monitors\n");
40432a1ad637SFrançois Tigeot 	);
40442a1ad637SFrançois Tigeot 	for (j = devinfo->startnode; j < devinfo->endnode; j++) {
40452a1ad637SFrançois Tigeot 		w = hdaa_widget_get(devinfo, j);
40462a1ad637SFrançois Tigeot 		if (w == NULL || w->enable == 0)
40472a1ad637SFrançois Tigeot 			continue;
40482a1ad637SFrançois Tigeot 		if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX)
40492a1ad637SFrançois Tigeot 			continue;
40502a1ad637SFrançois Tigeot 		if (w->bindas < 0 || as[w->bindas].dir != HDAA_CTL_IN)
40512a1ad637SFrançois Tigeot 			continue;
40522a1ad637SFrançois Tigeot 		HDA_BOOTVERBOSE(
40532a1ad637SFrançois Tigeot 			device_printf(devinfo->dev,
40542a1ad637SFrançois Tigeot 			    " Tracing nid %d to out\n",
40552a1ad637SFrançois Tigeot 			    j);
40562a1ad637SFrançois Tigeot 		);
40572a1ad637SFrançois Tigeot 		if (hdaa_audio_trace_to_out(devinfo, w->nid, 0)) {
40582a1ad637SFrançois Tigeot 			HDA_BOOTVERBOSE(
40592a1ad637SFrançois Tigeot 				device_printf(devinfo->dev,
40602a1ad637SFrançois Tigeot 				    " nid %d is input monitor\n",
40612a1ad637SFrançois Tigeot 					w->nid);
40622a1ad637SFrançois Tigeot 			);
40632a1ad637SFrançois Tigeot 		}
40642a1ad637SFrançois Tigeot 	}
40652a1ad637SFrançois Tigeot 
40662a1ad637SFrançois Tigeot 	/* Beeper */
40672a1ad637SFrançois Tigeot 	HDA_BOOTVERBOSE(
40682a1ad637SFrançois Tigeot 		device_printf(devinfo->dev,
40692a1ad637SFrançois Tigeot 		    "Tracing beeper\n");
40702a1ad637SFrançois Tigeot 	);
40712a1ad637SFrançois Tigeot 	for (j = devinfo->startnode; j < devinfo->endnode; j++) {
40722a1ad637SFrançois Tigeot 		w = hdaa_widget_get(devinfo, j);
40732a1ad637SFrançois Tigeot 		if (w == NULL || w->enable == 0)
40742a1ad637SFrançois Tigeot 			continue;
40752a1ad637SFrançois Tigeot 		if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET)
40762a1ad637SFrançois Tigeot 			continue;
40772a1ad637SFrançois Tigeot 		HDA_BOOTHVERBOSE(
40782a1ad637SFrançois Tigeot 			device_printf(devinfo->dev,
40792a1ad637SFrançois Tigeot 			    " Tracing nid %d to out\n",
40802a1ad637SFrançois Tigeot 			    j);
40812a1ad637SFrançois Tigeot 		);
40822a1ad637SFrançois Tigeot 		if (hdaa_audio_trace_to_out(devinfo, w->nid, 0)) {
40832a1ad637SFrançois Tigeot 			HDA_BOOTVERBOSE(
40842a1ad637SFrançois Tigeot 				device_printf(devinfo->dev,
40852a1ad637SFrançois Tigeot 				    " nid %d traced to out\n",
40862a1ad637SFrançois Tigeot 				    j);
40872a1ad637SFrançois Tigeot 			);
40882a1ad637SFrançois Tigeot 		}
40892a1ad637SFrançois Tigeot 		w->bindas = -2;
40902a1ad637SFrançois Tigeot 	}
40912a1ad637SFrançois Tigeot }
40922a1ad637SFrançois Tigeot 
40932a1ad637SFrançois Tigeot /*
40942a1ad637SFrançois Tigeot  * Bind assotiations to PCM channels
40952a1ad637SFrançois Tigeot  */
40962a1ad637SFrançois Tigeot static void
hdaa_audio_bind_as(struct hdaa_devinfo * devinfo)40972a1ad637SFrançois Tigeot hdaa_audio_bind_as(struct hdaa_devinfo *devinfo)
40982a1ad637SFrançois Tigeot {
40992a1ad637SFrançois Tigeot 	struct hdaa_audio_as *as = devinfo->as;
41002a1ad637SFrançois Tigeot 	int i, j, cnt = 0, free;
41012a1ad637SFrançois Tigeot 
41022a1ad637SFrançois Tigeot 	for (j = 0; j < devinfo->ascnt; j++) {
41032a1ad637SFrançois Tigeot 		if (as[j].enable)
41042a1ad637SFrançois Tigeot 			cnt += as[j].num_chans;
41052a1ad637SFrançois Tigeot 	}
41062a1ad637SFrançois Tigeot 	if (devinfo->num_chans == 0) {
410767931cc4SFrançois Tigeot 		devinfo->chans = (struct hdaa_chan *)kmalloc(
41082a1ad637SFrançois Tigeot 		    sizeof(struct hdaa_chan) * cnt,
41094e8e900cSMatthew Dillon 		    M_HDAA, M_ZERO | M_WAITOK);
41102a1ad637SFrançois Tigeot 		if (devinfo->chans == NULL) {
41112a1ad637SFrançois Tigeot 			device_printf(devinfo->dev,
41122a1ad637SFrançois Tigeot 			    "Channels memory allocation failed!\n");
41132a1ad637SFrançois Tigeot 			return;
41142a1ad637SFrançois Tigeot 		}
41152a1ad637SFrançois Tigeot 	} else {
411667931cc4SFrançois Tigeot 		devinfo->chans = (struct hdaa_chan *)krealloc(devinfo->chans,
41172a1ad637SFrançois Tigeot 		    sizeof(struct hdaa_chan) * (devinfo->num_chans + cnt),
41184e8e900cSMatthew Dillon 		    M_HDAA, M_ZERO | M_WAITOK);
41192a1ad637SFrançois Tigeot 		if (devinfo->chans == NULL) {
41202a1ad637SFrançois Tigeot 			devinfo->num_chans = 0;
41212a1ad637SFrançois Tigeot 			device_printf(devinfo->dev,
41222a1ad637SFrançois Tigeot 			    "Channels memory allocation failed!\n");
41232a1ad637SFrançois Tigeot 			return;
41242a1ad637SFrançois Tigeot 		}
41252a1ad637SFrançois Tigeot 		/* Fixup relative pointers after realloc */
41262a1ad637SFrançois Tigeot 		for (j = 0; j < devinfo->num_chans; j++)
41272a1ad637SFrançois Tigeot 			devinfo->chans[j].caps.fmtlist = devinfo->chans[j].fmtlist;
41282a1ad637SFrançois Tigeot 	}
41292a1ad637SFrançois Tigeot 	free = devinfo->num_chans;
41302a1ad637SFrançois Tigeot 	devinfo->num_chans += cnt;
41312a1ad637SFrançois Tigeot 
41322a1ad637SFrançois Tigeot 	for (j = free; j < free + cnt; j++) {
41332a1ad637SFrançois Tigeot 		devinfo->chans[j].devinfo = devinfo;
41342a1ad637SFrançois Tigeot 		devinfo->chans[j].as = -1;
41352a1ad637SFrançois Tigeot 	}
41362a1ad637SFrançois Tigeot 
41372a1ad637SFrançois Tigeot 	/* Assign associations in order of their numbers, */
41382a1ad637SFrançois Tigeot 	for (j = 0; j < devinfo->ascnt; j++) {
41392a1ad637SFrançois Tigeot 		if (as[j].enable == 0)
41402a1ad637SFrançois Tigeot 			continue;
41412a1ad637SFrançois Tigeot 		for (i = 0; i < as[j].num_chans; i++) {
41422a1ad637SFrançois Tigeot 			devinfo->chans[free].as = j;
41432a1ad637SFrançois Tigeot 			devinfo->chans[free].asindex = i;
41442a1ad637SFrançois Tigeot 			devinfo->chans[free].dir =
41452a1ad637SFrançois Tigeot 			    (as[j].dir == HDAA_CTL_IN) ? PCMDIR_REC : PCMDIR_PLAY;
41462a1ad637SFrançois Tigeot 			hdaa_pcmchannel_setup(&devinfo->chans[free]);
41472a1ad637SFrançois Tigeot 			as[j].chans[i] = free;
41482a1ad637SFrançois Tigeot 			free++;
41492a1ad637SFrançois Tigeot 		}
41502a1ad637SFrançois Tigeot 	}
41512a1ad637SFrançois Tigeot }
41522a1ad637SFrançois Tigeot 
41532a1ad637SFrançois Tigeot static void
hdaa_audio_disable_nonaudio(struct hdaa_devinfo * devinfo)41542a1ad637SFrançois Tigeot hdaa_audio_disable_nonaudio(struct hdaa_devinfo *devinfo)
41552a1ad637SFrançois Tigeot {
41562a1ad637SFrançois Tigeot 	struct hdaa_widget *w;
41572a1ad637SFrançois Tigeot 	int i;
41582a1ad637SFrançois Tigeot 
41592a1ad637SFrançois Tigeot 	/* Disable power and volume widgets. */
41602a1ad637SFrançois Tigeot 	for (i = devinfo->startnode; i < devinfo->endnode; i++) {
41612a1ad637SFrançois Tigeot 		w = hdaa_widget_get(devinfo, i);
41622a1ad637SFrançois Tigeot 		if (w == NULL || w->enable == 0)
41632a1ad637SFrançois Tigeot 			continue;
41642a1ad637SFrançois Tigeot 		if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_POWER_WIDGET ||
41652a1ad637SFrançois Tigeot 		    w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_VOLUME_WIDGET) {
41662a1ad637SFrançois Tigeot 			w->enable = 0;
41672a1ad637SFrançois Tigeot 			HDA_BOOTHVERBOSE(
41682a1ad637SFrançois Tigeot 				device_printf(devinfo->dev,
4169*e9d1c8d1SSascha Wildner 				    " Disabling nid %d due to its"
41702a1ad637SFrançois Tigeot 				    " non-audio type.\n",
41712a1ad637SFrançois Tigeot 				    w->nid);
41722a1ad637SFrançois Tigeot 			);
41732a1ad637SFrançois Tigeot 		}
41742a1ad637SFrançois Tigeot 	}
41752a1ad637SFrançois Tigeot }
41762a1ad637SFrançois Tigeot 
41772a1ad637SFrançois Tigeot static void
hdaa_audio_disable_useless(struct hdaa_devinfo * devinfo)41782a1ad637SFrançois Tigeot hdaa_audio_disable_useless(struct hdaa_devinfo *devinfo)
41792a1ad637SFrançois Tigeot {
41802a1ad637SFrançois Tigeot 	struct hdaa_widget *w, *cw;
41812a1ad637SFrançois Tigeot 	struct hdaa_audio_ctl *ctl;
41822a1ad637SFrançois Tigeot 	int done, found, i, j, k;
41832a1ad637SFrançois Tigeot 
41842a1ad637SFrançois Tigeot 	/* Disable useless pins. */
41852a1ad637SFrançois Tigeot 	for (i = devinfo->startnode; i < devinfo->endnode; i++) {
41862a1ad637SFrançois Tigeot 		w = hdaa_widget_get(devinfo, i);
41872a1ad637SFrançois Tigeot 		if (w == NULL || w->enable == 0)
41882a1ad637SFrançois Tigeot 			continue;
41892a1ad637SFrançois Tigeot 		if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) {
41902a1ad637SFrançois Tigeot 			if ((w->wclass.pin.config &
41912a1ad637SFrançois Tigeot 			    HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK) ==
41922a1ad637SFrançois Tigeot 			    HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_NONE) {
41932a1ad637SFrançois Tigeot 				w->enable = 0;
41942a1ad637SFrançois Tigeot 				HDA_BOOTHVERBOSE(
41952a1ad637SFrançois Tigeot 					device_printf(devinfo->dev,
41962a1ad637SFrançois Tigeot 					    " Disabling pin nid %d due"
41972a1ad637SFrançois Tigeot 					    " to None connectivity.\n",
41982a1ad637SFrançois Tigeot 					    w->nid);
41992a1ad637SFrançois Tigeot 				);
42002a1ad637SFrançois Tigeot 			} else if ((w->wclass.pin.config &
42012a1ad637SFrançois Tigeot 			    HDA_CONFIG_DEFAULTCONF_ASSOCIATION_MASK) == 0) {
42022a1ad637SFrançois Tigeot 				w->enable = 0;
42032a1ad637SFrançois Tigeot 				HDA_BOOTHVERBOSE(
42042a1ad637SFrançois Tigeot 					device_printf(devinfo->dev,
42052a1ad637SFrançois Tigeot 					    " Disabling unassociated"
42062a1ad637SFrançois Tigeot 					    " pin nid %d.\n",
42072a1ad637SFrançois Tigeot 					    w->nid);
42082a1ad637SFrançois Tigeot 				);
42092a1ad637SFrançois Tigeot 			}
42102a1ad637SFrançois Tigeot 		}
42112a1ad637SFrançois Tigeot 	}
42122a1ad637SFrançois Tigeot 	do {
42132a1ad637SFrançois Tigeot 		done = 1;
42142a1ad637SFrançois Tigeot 		/* Disable and mute controls for disabled widgets. */
42152a1ad637SFrançois Tigeot 		i = 0;
42162a1ad637SFrançois Tigeot 		while ((ctl = hdaa_audio_ctl_each(devinfo, &i)) != NULL) {
42172a1ad637SFrançois Tigeot 			if (ctl->enable == 0)
42182a1ad637SFrançois Tigeot 				continue;
42192a1ad637SFrançois Tigeot 			if (ctl->widget->enable == 0 ||
42202a1ad637SFrançois Tigeot 			    (ctl->childwidget != NULL &&
42212a1ad637SFrançois Tigeot 			    ctl->childwidget->enable == 0)) {
42222a1ad637SFrançois Tigeot 				ctl->forcemute = 1;
42232a1ad637SFrançois Tigeot 				ctl->muted = HDAA_AMP_MUTE_ALL;
42242a1ad637SFrançois Tigeot 				ctl->left = 0;
42252a1ad637SFrançois Tigeot 				ctl->right = 0;
42262a1ad637SFrançois Tigeot 				ctl->enable = 0;
42272a1ad637SFrançois Tigeot 				if (ctl->ndir == HDAA_CTL_IN)
42282a1ad637SFrançois Tigeot 					ctl->widget->connsenable[ctl->index] = 0;
42292a1ad637SFrançois Tigeot 				done = 0;
42302a1ad637SFrançois Tigeot 				HDA_BOOTHVERBOSE(
42312a1ad637SFrançois Tigeot 					device_printf(devinfo->dev,
42322a1ad637SFrançois Tigeot 					    " Disabling ctl %d nid %d cnid %d due"
42332a1ad637SFrançois Tigeot 					    " to disabled widget.\n", i,
42342a1ad637SFrançois Tigeot 					    ctl->widget->nid,
42352a1ad637SFrançois Tigeot 					    (ctl->childwidget != NULL)?
42362a1ad637SFrançois Tigeot 					    ctl->childwidget->nid:-1);
42372a1ad637SFrançois Tigeot 				);
42382a1ad637SFrançois Tigeot 			}
42392a1ad637SFrançois Tigeot 		}
42402a1ad637SFrançois Tigeot 		/* Disable useless widgets. */
42412a1ad637SFrançois Tigeot 		for (i = devinfo->startnode; i < devinfo->endnode; i++) {
42422a1ad637SFrançois Tigeot 			w = hdaa_widget_get(devinfo, i);
42432a1ad637SFrançois Tigeot 			if (w == NULL || w->enable == 0)
42442a1ad637SFrançois Tigeot 				continue;
42452a1ad637SFrançois Tigeot 			/* Disable inputs with disabled child widgets. */
42462a1ad637SFrançois Tigeot 			for (j = 0; j < w->nconns; j++) {
42472a1ad637SFrançois Tigeot 				if (w->connsenable[j]) {
42482a1ad637SFrançois Tigeot 					cw = hdaa_widget_get(devinfo, w->conns[j]);
42492a1ad637SFrançois Tigeot 					if (cw == NULL || cw->enable == 0) {
42502a1ad637SFrançois Tigeot 						w->connsenable[j] = 0;
42512a1ad637SFrançois Tigeot 						HDA_BOOTHVERBOSE(
42522a1ad637SFrançois Tigeot 							device_printf(devinfo->dev,
42532a1ad637SFrançois Tigeot 							    " Disabling nid %d connection %d due"
42542a1ad637SFrançois Tigeot 							    " to disabled child widget.\n",
42552a1ad637SFrançois Tigeot 							    i, j);
42562a1ad637SFrançois Tigeot 						);
42572a1ad637SFrançois Tigeot 					}
42582a1ad637SFrançois Tigeot 				}
42592a1ad637SFrançois Tigeot 			}
42602a1ad637SFrançois Tigeot 			if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR &&
42612a1ad637SFrançois Tigeot 			    w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER)
42622a1ad637SFrançois Tigeot 				continue;
42632a1ad637SFrançois Tigeot 			/* Disable mixers and selectors without inputs. */
42642a1ad637SFrançois Tigeot 			found = 0;
42652a1ad637SFrançois Tigeot 			for (j = 0; j < w->nconns; j++) {
42662a1ad637SFrançois Tigeot 				if (w->connsenable[j]) {
42672a1ad637SFrançois Tigeot 					found = 1;
42682a1ad637SFrançois Tigeot 					break;
42692a1ad637SFrançois Tigeot 				}
42702a1ad637SFrançois Tigeot 			}
42712a1ad637SFrançois Tigeot 			if (found == 0) {
42722a1ad637SFrançois Tigeot 				w->enable = 0;
42732a1ad637SFrançois Tigeot 				done = 0;
42742a1ad637SFrançois Tigeot 				HDA_BOOTHVERBOSE(
42752a1ad637SFrançois Tigeot 					device_printf(devinfo->dev,
4276*e9d1c8d1SSascha Wildner 					    " Disabling nid %d due to all its"
42772a1ad637SFrançois Tigeot 					    " inputs disabled.\n", w->nid);
42782a1ad637SFrançois Tigeot 				);
42792a1ad637SFrançois Tigeot 			}
42802a1ad637SFrançois Tigeot 			/* Disable nodes without consumers. */
42812a1ad637SFrançois Tigeot 			if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR &&
42822a1ad637SFrançois Tigeot 			    w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER)
42832a1ad637SFrançois Tigeot 				continue;
42842a1ad637SFrançois Tigeot 			found = 0;
42852a1ad637SFrançois Tigeot 			for (k = devinfo->startnode; k < devinfo->endnode; k++) {
42862a1ad637SFrançois Tigeot 				cw = hdaa_widget_get(devinfo, k);
42872a1ad637SFrançois Tigeot 				if (cw == NULL || cw->enable == 0)
42882a1ad637SFrançois Tigeot 					continue;
42892a1ad637SFrançois Tigeot 				for (j = 0; j < cw->nconns; j++) {
42902a1ad637SFrançois Tigeot 					if (cw->connsenable[j] && cw->conns[j] == i) {
42912a1ad637SFrançois Tigeot 						found = 1;
42922a1ad637SFrançois Tigeot 						break;
42932a1ad637SFrançois Tigeot 					}
42942a1ad637SFrançois Tigeot 				}
42952a1ad637SFrançois Tigeot 			}
42962a1ad637SFrançois Tigeot 			if (found == 0) {
42972a1ad637SFrançois Tigeot 				w->enable = 0;
42982a1ad637SFrançois Tigeot 				done = 0;
42992a1ad637SFrançois Tigeot 				HDA_BOOTHVERBOSE(
43002a1ad637SFrançois Tigeot 					device_printf(devinfo->dev,
4301*e9d1c8d1SSascha Wildner 					    " Disabling nid %d due to all its"
43022a1ad637SFrançois Tigeot 					    " consumers disabled.\n", w->nid);
43032a1ad637SFrançois Tigeot 				);
43042a1ad637SFrançois Tigeot 			}
43052a1ad637SFrançois Tigeot 		}
43062a1ad637SFrançois Tigeot 	} while (done == 0);
43072a1ad637SFrançois Tigeot 
43082a1ad637SFrançois Tigeot }
43092a1ad637SFrançois Tigeot 
43102a1ad637SFrançois Tigeot static void
hdaa_audio_disable_unas(struct hdaa_devinfo * devinfo)43112a1ad637SFrançois Tigeot hdaa_audio_disable_unas(struct hdaa_devinfo *devinfo)
43122a1ad637SFrançois Tigeot {
43132a1ad637SFrançois Tigeot 	struct hdaa_audio_as *as = devinfo->as;
43142a1ad637SFrançois Tigeot 	struct hdaa_widget *w, *cw;
43152a1ad637SFrançois Tigeot 	struct hdaa_audio_ctl *ctl;
43162a1ad637SFrançois Tigeot 	int i, j, k;
43172a1ad637SFrançois Tigeot 
43182a1ad637SFrançois Tigeot 	/* Disable unassosiated widgets. */
43192a1ad637SFrançois Tigeot 	for (i = devinfo->startnode; i < devinfo->endnode; i++) {
43202a1ad637SFrançois Tigeot 		w = hdaa_widget_get(devinfo, i);
43212a1ad637SFrançois Tigeot 		if (w == NULL || w->enable == 0)
43222a1ad637SFrançois Tigeot 			continue;
43232a1ad637SFrançois Tigeot 		if (w->bindas == -1) {
43242a1ad637SFrançois Tigeot 			w->enable = 0;
43252a1ad637SFrançois Tigeot 			HDA_BOOTHVERBOSE(
43262a1ad637SFrançois Tigeot 				device_printf(devinfo->dev,
43272a1ad637SFrançois Tigeot 				    " Disabling unassociated nid %d.\n",
43282a1ad637SFrançois Tigeot 				    w->nid);
43292a1ad637SFrançois Tigeot 			);
43302a1ad637SFrançois Tigeot 		}
43312a1ad637SFrançois Tigeot 	}
43322a1ad637SFrançois Tigeot 	/* Disable input connections on input pin and
43332a1ad637SFrançois Tigeot 	 * output on output. */
43342a1ad637SFrançois Tigeot 	for (i = devinfo->startnode; i < devinfo->endnode; i++) {
43352a1ad637SFrançois Tigeot 		w = hdaa_widget_get(devinfo, i);
43362a1ad637SFrançois Tigeot 		if (w == NULL || w->enable == 0)
43372a1ad637SFrançois Tigeot 			continue;
43382a1ad637SFrançois Tigeot 		if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX)
43392a1ad637SFrançois Tigeot 			continue;
43402a1ad637SFrançois Tigeot 		if (w->bindas < 0)
43412a1ad637SFrançois Tigeot 			continue;
43422a1ad637SFrançois Tigeot 		if (as[w->bindas].dir == HDAA_CTL_IN) {
43432a1ad637SFrançois Tigeot 			for (j = 0; j < w->nconns; j++) {
43442a1ad637SFrançois Tigeot 				if (w->connsenable[j] == 0)
43452a1ad637SFrançois Tigeot 					continue;
43462a1ad637SFrançois Tigeot 				w->connsenable[j] = 0;
43472a1ad637SFrançois Tigeot 				HDA_BOOTHVERBOSE(
43482a1ad637SFrançois Tigeot 					device_printf(devinfo->dev,
43492a1ad637SFrançois Tigeot 					    " Disabling connection to input pin "
43502a1ad637SFrançois Tigeot 					    "nid %d conn %d.\n",
43512a1ad637SFrançois Tigeot 					    i, j);
43522a1ad637SFrançois Tigeot 				);
43532a1ad637SFrançois Tigeot 			}
43542a1ad637SFrançois Tigeot 			ctl = hdaa_audio_ctl_amp_get(devinfo, w->nid,
43552a1ad637SFrançois Tigeot 			    HDAA_CTL_IN, -1, 1);
43562a1ad637SFrançois Tigeot 			if (ctl && ctl->enable) {
43572a1ad637SFrançois Tigeot 				ctl->forcemute = 1;
43582a1ad637SFrançois Tigeot 				ctl->muted = HDAA_AMP_MUTE_ALL;
43592a1ad637SFrançois Tigeot 				ctl->left = 0;
43602a1ad637SFrançois Tigeot 				ctl->right = 0;
43612a1ad637SFrançois Tigeot 				ctl->enable = 0;
43622a1ad637SFrançois Tigeot 			}
43632a1ad637SFrançois Tigeot 		} else {
43642a1ad637SFrançois Tigeot 			ctl = hdaa_audio_ctl_amp_get(devinfo, w->nid,
43652a1ad637SFrançois Tigeot 			    HDAA_CTL_OUT, -1, 1);
43662a1ad637SFrançois Tigeot 			if (ctl && ctl->enable) {
43672a1ad637SFrançois Tigeot 				ctl->forcemute = 1;
43682a1ad637SFrançois Tigeot 				ctl->muted = HDAA_AMP_MUTE_ALL;
43692a1ad637SFrançois Tigeot 				ctl->left = 0;
43702a1ad637SFrançois Tigeot 				ctl->right = 0;
43712a1ad637SFrançois Tigeot 				ctl->enable = 0;
43722a1ad637SFrançois Tigeot 			}
43732a1ad637SFrançois Tigeot 			for (k = devinfo->startnode; k < devinfo->endnode; k++) {
43742a1ad637SFrançois Tigeot 				cw = hdaa_widget_get(devinfo, k);
43752a1ad637SFrançois Tigeot 				if (cw == NULL || cw->enable == 0)
43762a1ad637SFrançois Tigeot 					continue;
43772a1ad637SFrançois Tigeot 				for (j = 0; j < cw->nconns; j++) {
43782a1ad637SFrançois Tigeot 					if (cw->connsenable[j] && cw->conns[j] == i) {
43792a1ad637SFrançois Tigeot 						cw->connsenable[j] = 0;
43802a1ad637SFrançois Tigeot 						HDA_BOOTHVERBOSE(
43812a1ad637SFrançois Tigeot 							device_printf(devinfo->dev,
43822a1ad637SFrançois Tigeot 							    " Disabling connection from output pin "
43832a1ad637SFrançois Tigeot 							    "nid %d conn %d cnid %d.\n",
43842a1ad637SFrançois Tigeot 							    k, j, i);
43852a1ad637SFrançois Tigeot 						);
43862a1ad637SFrançois Tigeot 						if (cw->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX &&
43872a1ad637SFrançois Tigeot 						    cw->nconns > 1)
43882a1ad637SFrançois Tigeot 							continue;
43892a1ad637SFrançois Tigeot 						ctl = hdaa_audio_ctl_amp_get(devinfo, k,
43902a1ad637SFrançois Tigeot 					    HDAA_CTL_IN, j, 1);
43912a1ad637SFrançois Tigeot 						if (ctl && ctl->enable) {
43922a1ad637SFrançois Tigeot 							ctl->forcemute = 1;
43932a1ad637SFrançois Tigeot 							ctl->muted = HDAA_AMP_MUTE_ALL;
43942a1ad637SFrançois Tigeot 							ctl->left = 0;
43952a1ad637SFrançois Tigeot 							ctl->right = 0;
43962a1ad637SFrançois Tigeot 							ctl->enable = 0;
43972a1ad637SFrançois Tigeot 						}
43982a1ad637SFrançois Tigeot 					}
43992a1ad637SFrançois Tigeot 				}
44002a1ad637SFrançois Tigeot 			}
44012a1ad637SFrançois Tigeot 		}
44022a1ad637SFrançois Tigeot 	}
44032a1ad637SFrançois Tigeot }
44042a1ad637SFrançois Tigeot 
44052a1ad637SFrançois Tigeot static void
hdaa_audio_disable_notselected(struct hdaa_devinfo * devinfo)44062a1ad637SFrançois Tigeot hdaa_audio_disable_notselected(struct hdaa_devinfo *devinfo)
44072a1ad637SFrançois Tigeot {
44082a1ad637SFrançois Tigeot 	struct hdaa_audio_as *as = devinfo->as;
44092a1ad637SFrançois Tigeot 	struct hdaa_widget *w;
44102a1ad637SFrançois Tigeot 	int i, j;
44112a1ad637SFrançois Tigeot 
44122a1ad637SFrançois Tigeot 	/* On playback path we can safely disable all unseleted inputs. */
44132a1ad637SFrançois Tigeot 	for (i = devinfo->startnode; i < devinfo->endnode; i++) {
44142a1ad637SFrançois Tigeot 		w = hdaa_widget_get(devinfo, i);
44152a1ad637SFrançois Tigeot 		if (w == NULL || w->enable == 0)
44162a1ad637SFrançois Tigeot 			continue;
44172a1ad637SFrançois Tigeot 		if (w->nconns <= 1)
44182a1ad637SFrançois Tigeot 			continue;
44192a1ad637SFrançois Tigeot 		if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER)
44202a1ad637SFrançois Tigeot 			continue;
44212a1ad637SFrançois Tigeot 		if (w->bindas < 0 || as[w->bindas].dir == HDAA_CTL_IN)
44222a1ad637SFrançois Tigeot 			continue;
44232a1ad637SFrançois Tigeot 		for (j = 0; j < w->nconns; j++) {
44242a1ad637SFrançois Tigeot 			if (w->connsenable[j] == 0)
44252a1ad637SFrançois Tigeot 				continue;
44262a1ad637SFrançois Tigeot 			if (w->selconn < 0 || w->selconn == j)
44272a1ad637SFrançois Tigeot 				continue;
44282a1ad637SFrançois Tigeot 			w->connsenable[j] = 0;
44292a1ad637SFrançois Tigeot 			HDA_BOOTHVERBOSE(
44302a1ad637SFrançois Tigeot 				device_printf(devinfo->dev,
44312a1ad637SFrançois Tigeot 				    " Disabling unselected connection "
44322a1ad637SFrançois Tigeot 				    "nid %d conn %d.\n",
44332a1ad637SFrançois Tigeot 				    i, j);
44342a1ad637SFrançois Tigeot 			);
44352a1ad637SFrançois Tigeot 		}
44362a1ad637SFrançois Tigeot 	}
44372a1ad637SFrançois Tigeot }
44382a1ad637SFrançois Tigeot 
44392a1ad637SFrançois Tigeot static void
hdaa_audio_disable_crossas(struct hdaa_devinfo * devinfo)44402a1ad637SFrançois Tigeot hdaa_audio_disable_crossas(struct hdaa_devinfo *devinfo)
44412a1ad637SFrançois Tigeot {
44422a1ad637SFrançois Tigeot 	struct hdaa_audio_as *ases = devinfo->as;
44432a1ad637SFrançois Tigeot 	struct hdaa_widget *w, *cw;
44442a1ad637SFrançois Tigeot 	struct hdaa_audio_ctl *ctl;
44452a1ad637SFrançois Tigeot 	int i, j;
44462a1ad637SFrançois Tigeot 
44472a1ad637SFrançois Tigeot 	/* Disable crossassociatement and unwanted crosschannel connections. */
44482a1ad637SFrançois Tigeot 	/* ... using selectors */
44492a1ad637SFrançois Tigeot 	for (i = devinfo->startnode; i < devinfo->endnode; i++) {
44502a1ad637SFrançois Tigeot 		w = hdaa_widget_get(devinfo, i);
44512a1ad637SFrançois Tigeot 		if (w == NULL || w->enable == 0)
44522a1ad637SFrançois Tigeot 			continue;
44532a1ad637SFrançois Tigeot 		if (w->nconns <= 1)
44542a1ad637SFrançois Tigeot 			continue;
44552a1ad637SFrançois Tigeot 		if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER)
44562a1ad637SFrançois Tigeot 			continue;
44572a1ad637SFrançois Tigeot 		/* Allow any -> mix */
44582a1ad637SFrançois Tigeot 		if (w->bindas == -2)
44592a1ad637SFrançois Tigeot 			continue;
44602a1ad637SFrançois Tigeot 		for (j = 0; j < w->nconns; j++) {
44612a1ad637SFrançois Tigeot 			if (w->connsenable[j] == 0)
44622a1ad637SFrançois Tigeot 				continue;
44632a1ad637SFrançois Tigeot 			cw = hdaa_widget_get(devinfo, w->conns[j]);
44642a1ad637SFrançois Tigeot 			if (cw == NULL || w->enable == 0)
44652a1ad637SFrançois Tigeot 				continue;
44662a1ad637SFrançois Tigeot 			/* Allow mix -> out. */
44672a1ad637SFrançois Tigeot 			if (cw->bindas == -2 && w->bindas >= 0 &&
44682a1ad637SFrançois Tigeot 			    ases[w->bindas].dir == HDAA_CTL_OUT)
44692a1ad637SFrançois Tigeot 				continue;
44702a1ad637SFrançois Tigeot 			/* Allow mix -> mixed-in. */
44712a1ad637SFrançois Tigeot 			if (cw->bindas == -2 && w->bindas >= 0 &&
44722a1ad637SFrançois Tigeot 			    ases[w->bindas].mixed)
44732a1ad637SFrançois Tigeot 				continue;
44742a1ad637SFrançois Tigeot 			/* Allow in -> mix. */
44752a1ad637SFrançois Tigeot 			if ((w->pflags & HDAA_ADC_MONITOR) &&
44762a1ad637SFrançois Tigeot 			     cw->bindas >= 0 &&
44772a1ad637SFrançois Tigeot 			     ases[cw->bindas].dir == HDAA_CTL_IN)
44782a1ad637SFrançois Tigeot 				continue;
44792a1ad637SFrançois Tigeot 			/* Allow if have common as/seqs. */
44802a1ad637SFrançois Tigeot 			if (w->bindas == cw->bindas &&
44812a1ad637SFrançois Tigeot 			    (w->bindseqmask & cw->bindseqmask) != 0)
44822a1ad637SFrançois Tigeot 				continue;
44832a1ad637SFrançois Tigeot 			w->connsenable[j] = 0;
44842a1ad637SFrançois Tigeot 			HDA_BOOTHVERBOSE(
44852a1ad637SFrançois Tigeot 				device_printf(devinfo->dev,
44862a1ad637SFrançois Tigeot 				    " Disabling crossassociatement connection "
44872a1ad637SFrançois Tigeot 				    "nid %d conn %d cnid %d.\n",
44882a1ad637SFrançois Tigeot 				    i, j, cw->nid);
44892a1ad637SFrançois Tigeot 			);
44902a1ad637SFrançois Tigeot 		}
44912a1ad637SFrançois Tigeot 	}
44922a1ad637SFrançois Tigeot 	/* ... using controls */
44932a1ad637SFrançois Tigeot 	i = 0;
44942a1ad637SFrançois Tigeot 	while ((ctl = hdaa_audio_ctl_each(devinfo, &i)) != NULL) {
44952a1ad637SFrançois Tigeot 		if (ctl->enable == 0 || ctl->childwidget == NULL)
44962a1ad637SFrançois Tigeot 			continue;
44972a1ad637SFrançois Tigeot 		/* Allow any -> mix */
44982a1ad637SFrançois Tigeot 		if (ctl->widget->bindas == -2)
44992a1ad637SFrançois Tigeot 			continue;
45002a1ad637SFrançois Tigeot 		/* Allow mix -> out. */
45012a1ad637SFrançois Tigeot 		if (ctl->childwidget->bindas == -2 &&
45022a1ad637SFrançois Tigeot 		    ctl->widget->bindas >= 0 &&
45032a1ad637SFrançois Tigeot 		    ases[ctl->widget->bindas].dir == HDAA_CTL_OUT)
45042a1ad637SFrançois Tigeot 			continue;
45052a1ad637SFrançois Tigeot 		/* Allow mix -> mixed-in. */
45062a1ad637SFrançois Tigeot 		if (ctl->childwidget->bindas == -2 &&
45072a1ad637SFrançois Tigeot 		    ctl->widget->bindas >= 0 &&
45082a1ad637SFrançois Tigeot 		    ases[ctl->widget->bindas].mixed)
45092a1ad637SFrançois Tigeot 			continue;
45102a1ad637SFrançois Tigeot 		/* Allow in -> mix. */
45112a1ad637SFrançois Tigeot 		if ((ctl->widget->pflags & HDAA_ADC_MONITOR) &&
45122a1ad637SFrançois Tigeot 		    ctl->childwidget->bindas >= 0 &&
45132a1ad637SFrançois Tigeot 		    ases[ctl->childwidget->bindas].dir == HDAA_CTL_IN)
45142a1ad637SFrançois Tigeot 			continue;
45152a1ad637SFrançois Tigeot 		/* Allow if have common as/seqs. */
45162a1ad637SFrançois Tigeot 		if (ctl->widget->bindas == ctl->childwidget->bindas &&
45172a1ad637SFrançois Tigeot 		    (ctl->widget->bindseqmask & ctl->childwidget->bindseqmask) != 0)
45182a1ad637SFrançois Tigeot 			continue;
45192a1ad637SFrançois Tigeot 		ctl->forcemute = 1;
45202a1ad637SFrançois Tigeot 		ctl->muted = HDAA_AMP_MUTE_ALL;
45212a1ad637SFrançois Tigeot 		ctl->left = 0;
45222a1ad637SFrançois Tigeot 		ctl->right = 0;
45232a1ad637SFrançois Tigeot 		ctl->enable = 0;
45242a1ad637SFrançois Tigeot 		if (ctl->ndir == HDAA_CTL_IN)
45252a1ad637SFrançois Tigeot 			ctl->widget->connsenable[ctl->index] = 0;
45262a1ad637SFrançois Tigeot 		HDA_BOOTHVERBOSE(
45272a1ad637SFrançois Tigeot 			device_printf(devinfo->dev,
45282a1ad637SFrançois Tigeot 			    " Disabling crossassociatement connection "
45292a1ad637SFrançois Tigeot 			    "ctl %d nid %d cnid %d.\n", i,
45302a1ad637SFrançois Tigeot 			    ctl->widget->nid,
45312a1ad637SFrançois Tigeot 			    ctl->childwidget->nid);
45322a1ad637SFrançois Tigeot 		);
45332a1ad637SFrançois Tigeot 	}
45342a1ad637SFrançois Tigeot 
45352a1ad637SFrançois Tigeot }
45362a1ad637SFrançois Tigeot 
45372a1ad637SFrançois Tigeot /*
45382a1ad637SFrançois Tigeot  * Find controls to control amplification for source and calculate possible
45392a1ad637SFrançois Tigeot  * amplification range.
45402a1ad637SFrançois Tigeot  */
45412a1ad637SFrançois Tigeot static int
hdaa_audio_ctl_source_amp(struct hdaa_devinfo * devinfo,nid_t nid,int index,int ossdev,int ctlable,int depth,int * minamp,int * maxamp)45422a1ad637SFrançois Tigeot hdaa_audio_ctl_source_amp(struct hdaa_devinfo *devinfo, nid_t nid, int index,
45432a1ad637SFrançois Tigeot     int ossdev, int ctlable, int depth, int *minamp, int *maxamp)
45442a1ad637SFrançois Tigeot {
45452a1ad637SFrançois Tigeot 	struct hdaa_widget *w, *wc;
45462a1ad637SFrançois Tigeot 	struct hdaa_audio_ctl *ctl;
45472a1ad637SFrançois Tigeot 	int i, j, conns = 0, tminamp, tmaxamp, cminamp, cmaxamp, found = 0;
45482a1ad637SFrançois Tigeot 
45492a1ad637SFrançois Tigeot 	if (depth > HDA_PARSE_MAXDEPTH)
45502a1ad637SFrançois Tigeot 		return (found);
45512a1ad637SFrançois Tigeot 
45522a1ad637SFrançois Tigeot 	w = hdaa_widget_get(devinfo, nid);
45532a1ad637SFrançois Tigeot 	if (w == NULL || w->enable == 0)
45542a1ad637SFrançois Tigeot 		return (found);
45552a1ad637SFrançois Tigeot 
45562a1ad637SFrançois Tigeot 	/* Count number of active inputs. */
45572a1ad637SFrançois Tigeot 	if (depth > 0) {
45582a1ad637SFrançois Tigeot 		for (j = 0; j < w->nconns; j++) {
45592a1ad637SFrançois Tigeot 			if (!w->connsenable[j])
45602a1ad637SFrançois Tigeot 				continue;
45612a1ad637SFrançois Tigeot 			conns++;
45622a1ad637SFrançois Tigeot 		}
45632a1ad637SFrançois Tigeot 	}
45642a1ad637SFrançois Tigeot 
45652a1ad637SFrançois Tigeot 	/* If this is not a first step - use input mixer.
45662a1ad637SFrançois Tigeot 	   Pins have common input ctl so care must be taken. */
45672a1ad637SFrançois Tigeot 	if (depth > 0 && ctlable && (conns == 1 ||
45682a1ad637SFrançois Tigeot 	    w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX)) {
45692a1ad637SFrançois Tigeot 		ctl = hdaa_audio_ctl_amp_get(devinfo, w->nid, HDAA_CTL_IN,
45702a1ad637SFrançois Tigeot 		    index, 1);
45712a1ad637SFrançois Tigeot 		if (ctl) {
45722a1ad637SFrançois Tigeot 			ctl->ossmask |= (1 << ossdev);
45732a1ad637SFrançois Tigeot 			found++;
45742a1ad637SFrançois Tigeot 			if (*minamp == *maxamp) {
45752a1ad637SFrançois Tigeot 				*minamp += MINQDB(ctl);
45762a1ad637SFrançois Tigeot 				*maxamp += MAXQDB(ctl);
45772a1ad637SFrançois Tigeot 			}
45782a1ad637SFrançois Tigeot 		}
45792a1ad637SFrançois Tigeot 	}
45802a1ad637SFrançois Tigeot 
45812a1ad637SFrançois Tigeot 	/* If widget has own ossdev - not traverse it.
45822a1ad637SFrançois Tigeot 	   It will be traversed on it's own. */
45832a1ad637SFrançois Tigeot 	if (w->ossdev >= 0 && depth > 0)
45842a1ad637SFrançois Tigeot 		return (found);
45852a1ad637SFrançois Tigeot 
45862a1ad637SFrançois Tigeot 	/* We must not traverse pin */
45872a1ad637SFrançois Tigeot 	if ((w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT ||
45882a1ad637SFrançois Tigeot 	    w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) &&
45892a1ad637SFrançois Tigeot 	    depth > 0)
45902a1ad637SFrançois Tigeot 		return (found);
45912a1ad637SFrançois Tigeot 
45922a1ad637SFrançois Tigeot 	/* record that this widget exports such signal, */
45932a1ad637SFrançois Tigeot 	w->ossmask |= (1 << ossdev);
45942a1ad637SFrançois Tigeot 
45952a1ad637SFrançois Tigeot 	/*
45962a1ad637SFrançois Tigeot 	 * If signals mixed, we can't assign controls farther.
45972a1ad637SFrançois Tigeot 	 * Ignore this on depth zero. Caller must knows why.
45982a1ad637SFrançois Tigeot 	 */
45992a1ad637SFrançois Tigeot 	if (conns > 1 &&
46002a1ad637SFrançois Tigeot 	    w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER)
46012a1ad637SFrançois Tigeot 		ctlable = 0;
46022a1ad637SFrançois Tigeot 
46032a1ad637SFrançois Tigeot 	if (ctlable) {
46042a1ad637SFrançois Tigeot 		ctl = hdaa_audio_ctl_amp_get(devinfo, w->nid, HDAA_CTL_OUT, -1, 1);
46052a1ad637SFrançois Tigeot 		if (ctl) {
46062a1ad637SFrançois Tigeot 			ctl->ossmask |= (1 << ossdev);
46072a1ad637SFrançois Tigeot 			found++;
46082a1ad637SFrançois Tigeot 			if (*minamp == *maxamp) {
46092a1ad637SFrançois Tigeot 				*minamp += MINQDB(ctl);
46102a1ad637SFrançois Tigeot 				*maxamp += MAXQDB(ctl);
46112a1ad637SFrançois Tigeot 			}
46122a1ad637SFrançois Tigeot 		}
46132a1ad637SFrançois Tigeot 	}
46142a1ad637SFrançois Tigeot 
46152a1ad637SFrançois Tigeot 	cminamp = cmaxamp = 0;
46162a1ad637SFrançois Tigeot 	for (i = devinfo->startnode; i < devinfo->endnode; i++) {
46172a1ad637SFrançois Tigeot 		wc = hdaa_widget_get(devinfo, i);
46182a1ad637SFrançois Tigeot 		if (wc == NULL || wc->enable == 0)
46192a1ad637SFrançois Tigeot 			continue;
46202a1ad637SFrançois Tigeot 		for (j = 0; j < wc->nconns; j++) {
46212a1ad637SFrançois Tigeot 			if (wc->connsenable[j] && wc->conns[j] == nid) {
46222a1ad637SFrançois Tigeot 				tminamp = tmaxamp = 0;
46232a1ad637SFrançois Tigeot 				found += hdaa_audio_ctl_source_amp(devinfo,
46242a1ad637SFrançois Tigeot 				    wc->nid, j, ossdev, ctlable, depth + 1,
46252a1ad637SFrançois Tigeot 				    &tminamp, &tmaxamp);
46262a1ad637SFrançois Tigeot 				if (cminamp == 0 && cmaxamp == 0) {
46272a1ad637SFrançois Tigeot 					cminamp = tminamp;
46282a1ad637SFrançois Tigeot 					cmaxamp = tmaxamp;
46292a1ad637SFrançois Tigeot 				} else if (tminamp != tmaxamp) {
46302a1ad637SFrançois Tigeot 					cminamp = imax(cminamp, tminamp);
46312a1ad637SFrançois Tigeot 					cmaxamp = imin(cmaxamp, tmaxamp);
46322a1ad637SFrançois Tigeot 				}
46332a1ad637SFrançois Tigeot 			}
46342a1ad637SFrançois Tigeot 		}
46352a1ad637SFrançois Tigeot 	}
46362a1ad637SFrançois Tigeot 	if (*minamp == *maxamp && cminamp < cmaxamp) {
46372a1ad637SFrançois Tigeot 		*minamp += cminamp;
46382a1ad637SFrançois Tigeot 		*maxamp += cmaxamp;
46392a1ad637SFrançois Tigeot 	}
46402a1ad637SFrançois Tigeot 	return (found);
46412a1ad637SFrançois Tigeot }
46422a1ad637SFrançois Tigeot 
46432a1ad637SFrançois Tigeot /*
46442a1ad637SFrançois Tigeot  * Find controls to control amplification for destination and calculate
46452a1ad637SFrançois Tigeot  * possible amplification range.
46462a1ad637SFrançois Tigeot  */
46472a1ad637SFrançois Tigeot static int
hdaa_audio_ctl_dest_amp(struct hdaa_devinfo * devinfo,nid_t nid,int index,int ossdev,int depth,int * minamp,int * maxamp)46482a1ad637SFrançois Tigeot hdaa_audio_ctl_dest_amp(struct hdaa_devinfo *devinfo, nid_t nid, int index,
46492a1ad637SFrançois Tigeot     int ossdev, int depth, int *minamp, int *maxamp)
46502a1ad637SFrançois Tigeot {
46512a1ad637SFrançois Tigeot 	struct hdaa_audio_as *as = devinfo->as;
46522a1ad637SFrançois Tigeot 	struct hdaa_widget *w, *wc;
46532a1ad637SFrançois Tigeot 	struct hdaa_audio_ctl *ctl;
46542a1ad637SFrançois Tigeot 	int i, j, consumers, tminamp, tmaxamp, cminamp, cmaxamp, found = 0;
46552a1ad637SFrançois Tigeot 
46562a1ad637SFrançois Tigeot 	if (depth > HDA_PARSE_MAXDEPTH)
46572a1ad637SFrançois Tigeot 		return (found);
46582a1ad637SFrançois Tigeot 
46592a1ad637SFrançois Tigeot 	w = hdaa_widget_get(devinfo, nid);
46602a1ad637SFrançois Tigeot 	if (w == NULL || w->enable == 0)
46612a1ad637SFrançois Tigeot 		return (found);
46622a1ad637SFrançois Tigeot 
46632a1ad637SFrançois Tigeot 	if (depth > 0) {
46642a1ad637SFrançois Tigeot 		/* If this node produce output for several consumers,
46652a1ad637SFrançois Tigeot 		   we can't touch it. */
46662a1ad637SFrançois Tigeot 		consumers = 0;
46672a1ad637SFrançois Tigeot 		for (i = devinfo->startnode; i < devinfo->endnode; i++) {
46682a1ad637SFrançois Tigeot 			wc = hdaa_widget_get(devinfo, i);
46692a1ad637SFrançois Tigeot 			if (wc == NULL || wc->enable == 0)
46702a1ad637SFrançois Tigeot 				continue;
46712a1ad637SFrançois Tigeot 			for (j = 0; j < wc->nconns; j++) {
46722a1ad637SFrançois Tigeot 				if (wc->connsenable[j] && wc->conns[j] == nid)
46732a1ad637SFrançois Tigeot 					consumers++;
46742a1ad637SFrançois Tigeot 			}
46752a1ad637SFrançois Tigeot 		}
46762a1ad637SFrançois Tigeot 		/* The only exception is if real HP redirection is configured
46772a1ad637SFrançois Tigeot 		   and this is a duplication point.
46782a1ad637SFrançois Tigeot 		   XXX: Actually exception is not completely correct.
46792a1ad637SFrançois Tigeot 		   XXX: Duplication point check is not perfect. */
46802a1ad637SFrançois Tigeot 		if ((consumers == 2 && (w->bindas < 0 ||
46812a1ad637SFrançois Tigeot 		    as[w->bindas].hpredir < 0 || as[w->bindas].fakeredir ||
46822a1ad637SFrançois Tigeot 		    (w->bindseqmask & (1 << 15)) == 0)) ||
46832a1ad637SFrançois Tigeot 		    consumers > 2)
46842a1ad637SFrançois Tigeot 			return (found);
46852a1ad637SFrançois Tigeot 
46862a1ad637SFrançois Tigeot 		/* Else use it's output mixer. */
46872a1ad637SFrançois Tigeot 		ctl = hdaa_audio_ctl_amp_get(devinfo, w->nid,
46882a1ad637SFrançois Tigeot 		    HDAA_CTL_OUT, -1, 1);
46892a1ad637SFrançois Tigeot 		if (ctl) {
46902a1ad637SFrançois Tigeot 			ctl->ossmask |= (1 << ossdev);
46912a1ad637SFrançois Tigeot 			found++;
46922a1ad637SFrançois Tigeot 			if (*minamp == *maxamp) {
46932a1ad637SFrançois Tigeot 				*minamp += MINQDB(ctl);
46942a1ad637SFrançois Tigeot 				*maxamp += MAXQDB(ctl);
46952a1ad637SFrançois Tigeot 			}
46962a1ad637SFrançois Tigeot 		}
46972a1ad637SFrançois Tigeot 	}
46982a1ad637SFrançois Tigeot 
46992a1ad637SFrançois Tigeot 	/* We must not traverse pin */
47002a1ad637SFrançois Tigeot 	if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX &&
47012a1ad637SFrançois Tigeot 	    depth > 0)
47022a1ad637SFrançois Tigeot 		return (found);
47032a1ad637SFrançois Tigeot 
47042a1ad637SFrançois Tigeot 	cminamp = cmaxamp = 0;
47052a1ad637SFrançois Tigeot 	for (i = 0; i < w->nconns; i++) {
47062a1ad637SFrançois Tigeot 		if (w->connsenable[i] == 0)
47072a1ad637SFrançois Tigeot 			continue;
47082a1ad637SFrançois Tigeot 		if (index >= 0 && i != index)
47092a1ad637SFrançois Tigeot 			continue;
47102a1ad637SFrançois Tigeot 		tminamp = tmaxamp = 0;
47112a1ad637SFrançois Tigeot 		ctl = hdaa_audio_ctl_amp_get(devinfo, w->nid,
47122a1ad637SFrançois Tigeot 		    HDAA_CTL_IN, i, 1);
47132a1ad637SFrançois Tigeot 		if (ctl) {
47142a1ad637SFrançois Tigeot 			ctl->ossmask |= (1 << ossdev);
47152a1ad637SFrançois Tigeot 			found++;
47162a1ad637SFrançois Tigeot 			if (*minamp == *maxamp) {
47172a1ad637SFrançois Tigeot 				tminamp += MINQDB(ctl);
47182a1ad637SFrançois Tigeot 				tmaxamp += MAXQDB(ctl);
47192a1ad637SFrançois Tigeot 			}
47202a1ad637SFrançois Tigeot 		}
47212a1ad637SFrançois Tigeot 		found += hdaa_audio_ctl_dest_amp(devinfo, w->conns[i], -1, ossdev,
47222a1ad637SFrançois Tigeot 		    depth + 1, &tminamp, &tmaxamp);
47232a1ad637SFrançois Tigeot 		if (cminamp == 0 && cmaxamp == 0) {
47242a1ad637SFrançois Tigeot 			cminamp = tminamp;
47252a1ad637SFrançois Tigeot 			cmaxamp = tmaxamp;
47262a1ad637SFrançois Tigeot 		} else if (tminamp != tmaxamp) {
47272a1ad637SFrançois Tigeot 			cminamp = imax(cminamp, tminamp);
47282a1ad637SFrançois Tigeot 			cmaxamp = imin(cmaxamp, tmaxamp);
47292a1ad637SFrançois Tigeot 		}
47302a1ad637SFrançois Tigeot 	}
47312a1ad637SFrançois Tigeot 	if (*minamp == *maxamp && cminamp < cmaxamp) {
47322a1ad637SFrançois Tigeot 		*minamp += cminamp;
47332a1ad637SFrançois Tigeot 		*maxamp += cmaxamp;
47342a1ad637SFrançois Tigeot 	}
47352a1ad637SFrançois Tigeot 	return (found);
47362a1ad637SFrançois Tigeot }
47372a1ad637SFrançois Tigeot 
47382a1ad637SFrançois Tigeot /*
47392a1ad637SFrançois Tigeot  * Assign OSS names to sound sources
47402a1ad637SFrançois Tigeot  */
47412a1ad637SFrançois Tigeot static void
hdaa_audio_assign_names(struct hdaa_devinfo * devinfo)47422a1ad637SFrançois Tigeot hdaa_audio_assign_names(struct hdaa_devinfo *devinfo)
47432a1ad637SFrançois Tigeot {
47442a1ad637SFrançois Tigeot 	struct hdaa_audio_as *as = devinfo->as;
47452a1ad637SFrançois Tigeot 	struct hdaa_widget *w;
47462a1ad637SFrançois Tigeot 	int i, j;
47472a1ad637SFrançois Tigeot 	int type = -1, use, used = 0;
47482a1ad637SFrançois Tigeot 	static const int types[7][13] = {
47492a1ad637SFrançois Tigeot 	    { SOUND_MIXER_LINE, SOUND_MIXER_LINE1, SOUND_MIXER_LINE2,
47502a1ad637SFrançois Tigeot 	      SOUND_MIXER_LINE3, -1 },	/* line */
47512a1ad637SFrançois Tigeot 	    { SOUND_MIXER_MONITOR, SOUND_MIXER_MIC, -1 }, /* int mic */
47522a1ad637SFrançois Tigeot 	    { SOUND_MIXER_MIC, SOUND_MIXER_MONITOR, -1 }, /* ext mic */
47532a1ad637SFrançois Tigeot 	    { SOUND_MIXER_CD, -1 },	/* cd */
47542a1ad637SFrançois Tigeot 	    { SOUND_MIXER_SPEAKER, -1 },	/* speaker */
47552a1ad637SFrançois Tigeot 	    { SOUND_MIXER_DIGITAL1, SOUND_MIXER_DIGITAL2, SOUND_MIXER_DIGITAL3,
47562a1ad637SFrançois Tigeot 	      -1 },	/* digital */
47572a1ad637SFrançois Tigeot 	    { SOUND_MIXER_LINE, SOUND_MIXER_LINE1, SOUND_MIXER_LINE2,
47582a1ad637SFrançois Tigeot 	      SOUND_MIXER_LINE3, SOUND_MIXER_PHONEIN, SOUND_MIXER_PHONEOUT,
47592a1ad637SFrançois Tigeot 	      SOUND_MIXER_VIDEO, SOUND_MIXER_RADIO, SOUND_MIXER_DIGITAL1,
47602a1ad637SFrançois Tigeot 	      SOUND_MIXER_DIGITAL2, SOUND_MIXER_DIGITAL3, SOUND_MIXER_MONITOR,
47612a1ad637SFrançois Tigeot 	      -1 }	/* others */
47622a1ad637SFrançois Tigeot 	};
47632a1ad637SFrançois Tigeot 
47642a1ad637SFrançois Tigeot 	/* Surely known names */
47652a1ad637SFrançois Tigeot 	for (i = devinfo->startnode; i < devinfo->endnode; i++) {
47662a1ad637SFrançois Tigeot 		w = hdaa_widget_get(devinfo, i);
47672a1ad637SFrançois Tigeot 		if (w == NULL || w->enable == 0)
47682a1ad637SFrançois Tigeot 			continue;
47692a1ad637SFrançois Tigeot 		if (w->bindas == -1)
47702a1ad637SFrançois Tigeot 			continue;
47712a1ad637SFrançois Tigeot 		use = -1;
47722a1ad637SFrançois Tigeot 		switch (w->type) {
47732a1ad637SFrançois Tigeot 		case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX:
47742a1ad637SFrançois Tigeot 			if (as[w->bindas].dir == HDAA_CTL_OUT)
47752a1ad637SFrançois Tigeot 				break;
47762a1ad637SFrançois Tigeot 			type = -1;
47772a1ad637SFrançois Tigeot 			switch (w->wclass.pin.config & HDA_CONFIG_DEFAULTCONF_DEVICE_MASK) {
47782a1ad637SFrançois Tigeot 			case HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_IN:
47792a1ad637SFrançois Tigeot 				type = 0;
47802a1ad637SFrançois Tigeot 				break;
47812a1ad637SFrançois Tigeot 			case HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN:
47822a1ad637SFrançois Tigeot 				if ((w->wclass.pin.config & HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK)
47832a1ad637SFrançois Tigeot 				    == HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_JACK)
47842a1ad637SFrançois Tigeot 					break;
47852a1ad637SFrançois Tigeot 				type = 1;
47862a1ad637SFrançois Tigeot 				break;
47872a1ad637SFrançois Tigeot 			case HDA_CONFIG_DEFAULTCONF_DEVICE_CD:
47882a1ad637SFrançois Tigeot 				type = 3;
47892a1ad637SFrançois Tigeot 				break;
47902a1ad637SFrançois Tigeot 			case HDA_CONFIG_DEFAULTCONF_DEVICE_SPEAKER:
47912a1ad637SFrançois Tigeot 				type = 4;
47922a1ad637SFrançois Tigeot 				break;
47932a1ad637SFrançois Tigeot 			case HDA_CONFIG_DEFAULTCONF_DEVICE_SPDIF_IN:
47942a1ad637SFrançois Tigeot 			case HDA_CONFIG_DEFAULTCONF_DEVICE_DIGITAL_OTHER_IN:
47952a1ad637SFrançois Tigeot 				type = 5;
47962a1ad637SFrançois Tigeot 				break;
47972a1ad637SFrançois Tigeot 			}
47982a1ad637SFrançois Tigeot 			if (type == -1)
47992a1ad637SFrançois Tigeot 				break;
48002a1ad637SFrançois Tigeot 			j = 0;
48012a1ad637SFrançois Tigeot 			while (types[type][j] >= 0 &&
48022a1ad637SFrançois Tigeot 			    (used & (1 << types[type][j])) != 0) {
48032a1ad637SFrançois Tigeot 				j++;
48042a1ad637SFrançois Tigeot 			}
48052a1ad637SFrançois Tigeot 			if (types[type][j] >= 0)
48062a1ad637SFrançois Tigeot 				use = types[type][j];
48072a1ad637SFrançois Tigeot 			break;
48082a1ad637SFrançois Tigeot 		case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT:
48092a1ad637SFrançois Tigeot 			use = SOUND_MIXER_PCM;
48102a1ad637SFrançois Tigeot 			break;
48112a1ad637SFrançois Tigeot 		case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET:
48122a1ad637SFrançois Tigeot 			use = SOUND_MIXER_SPEAKER;
48132a1ad637SFrançois Tigeot 			break;
48142a1ad637SFrançois Tigeot 		default:
48152a1ad637SFrançois Tigeot 			break;
48162a1ad637SFrançois Tigeot 		}
48172a1ad637SFrançois Tigeot 		if (use >= 0) {
48182a1ad637SFrançois Tigeot 			w->ossdev = use;
48192a1ad637SFrançois Tigeot 			used |= (1 << use);
48202a1ad637SFrançois Tigeot 		}
48212a1ad637SFrançois Tigeot 	}
48222a1ad637SFrançois Tigeot 	/* Semi-known names */
48232a1ad637SFrançois Tigeot 	for (i = devinfo->startnode; i < devinfo->endnode; i++) {
48242a1ad637SFrançois Tigeot 		w = hdaa_widget_get(devinfo, i);
48252a1ad637SFrançois Tigeot 		if (w == NULL || w->enable == 0)
48262a1ad637SFrançois Tigeot 			continue;
48272a1ad637SFrançois Tigeot 		if (w->ossdev >= 0)
48282a1ad637SFrançois Tigeot 			continue;
48292a1ad637SFrançois Tigeot 		if (w->bindas == -1)
48302a1ad637SFrançois Tigeot 			continue;
48312a1ad637SFrançois Tigeot 		if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX)
48322a1ad637SFrançois Tigeot 			continue;
48332a1ad637SFrançois Tigeot 		if (as[w->bindas].dir == HDAA_CTL_OUT)
48342a1ad637SFrançois Tigeot 			continue;
48352a1ad637SFrançois Tigeot 		type = -1;
48362a1ad637SFrançois Tigeot 		switch (w->wclass.pin.config & HDA_CONFIG_DEFAULTCONF_DEVICE_MASK) {
48372a1ad637SFrançois Tigeot 		case HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_OUT:
48382a1ad637SFrançois Tigeot 		case HDA_CONFIG_DEFAULTCONF_DEVICE_SPEAKER:
48392a1ad637SFrançois Tigeot 		case HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT:
48402a1ad637SFrançois Tigeot 		case HDA_CONFIG_DEFAULTCONF_DEVICE_AUX:
48412a1ad637SFrançois Tigeot 			type = 0;
48422a1ad637SFrançois Tigeot 			break;
48432a1ad637SFrançois Tigeot 		case HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN:
48442a1ad637SFrançois Tigeot 			type = 2;
48452a1ad637SFrançois Tigeot 			break;
48462a1ad637SFrançois Tigeot 		case HDA_CONFIG_DEFAULTCONF_DEVICE_SPDIF_OUT:
48472a1ad637SFrançois Tigeot 		case HDA_CONFIG_DEFAULTCONF_DEVICE_DIGITAL_OTHER_OUT:
48482a1ad637SFrançois Tigeot 			type = 5;
48492a1ad637SFrançois Tigeot 			break;
48502a1ad637SFrançois Tigeot 		}
48512a1ad637SFrançois Tigeot 		if (type == -1)
48522a1ad637SFrançois Tigeot 			break;
48532a1ad637SFrançois Tigeot 		j = 0;
48542a1ad637SFrançois Tigeot 		while (types[type][j] >= 0 &&
48552a1ad637SFrançois Tigeot 		    (used & (1 << types[type][j])) != 0) {
48562a1ad637SFrançois Tigeot 			j++;
48572a1ad637SFrançois Tigeot 		}
48582a1ad637SFrançois Tigeot 		if (types[type][j] >= 0) {
48592a1ad637SFrançois Tigeot 			w->ossdev = types[type][j];
48602a1ad637SFrançois Tigeot 			used |= (1 << types[type][j]);
48612a1ad637SFrançois Tigeot 		}
48622a1ad637SFrançois Tigeot 	}
48632a1ad637SFrançois Tigeot 	/* Others */
48642a1ad637SFrançois Tigeot 	for (i = devinfo->startnode; i < devinfo->endnode; i++) {
48652a1ad637SFrançois Tigeot 		w = hdaa_widget_get(devinfo, i);
48662a1ad637SFrançois Tigeot 		if (w == NULL || w->enable == 0)
48672a1ad637SFrançois Tigeot 			continue;
48682a1ad637SFrançois Tigeot 		if (w->ossdev >= 0)
48692a1ad637SFrançois Tigeot 			continue;
48702a1ad637SFrançois Tigeot 		if (w->bindas == -1)
48712a1ad637SFrançois Tigeot 			continue;
48722a1ad637SFrançois Tigeot 		if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX)
48732a1ad637SFrançois Tigeot 			continue;
48742a1ad637SFrançois Tigeot 		if (as[w->bindas].dir == HDAA_CTL_OUT)
48752a1ad637SFrançois Tigeot 			continue;
48762a1ad637SFrançois Tigeot 		j = 0;
48772a1ad637SFrançois Tigeot 		while (types[6][j] >= 0 &&
48782a1ad637SFrançois Tigeot 		    (used & (1 << types[6][j])) != 0) {
48792a1ad637SFrançois Tigeot 			j++;
48802a1ad637SFrançois Tigeot 		}
48812a1ad637SFrançois Tigeot 		if (types[6][j] >= 0) {
48822a1ad637SFrançois Tigeot 			w->ossdev = types[6][j];
48832a1ad637SFrançois Tigeot 			used |= (1 << types[6][j]);
48842a1ad637SFrançois Tigeot 		}
48852a1ad637SFrançois Tigeot 	}
48862a1ad637SFrançois Tigeot }
48872a1ad637SFrançois Tigeot 
48882a1ad637SFrançois Tigeot static void
hdaa_audio_build_tree(struct hdaa_devinfo * devinfo)48892a1ad637SFrançois Tigeot hdaa_audio_build_tree(struct hdaa_devinfo *devinfo)
48902a1ad637SFrançois Tigeot {
48912a1ad637SFrançois Tigeot 	struct hdaa_audio_as *as = devinfo->as;
48922a1ad637SFrançois Tigeot 	int j, res;
48932a1ad637SFrançois Tigeot 
48942a1ad637SFrançois Tigeot 	/* Trace all associations in order of their numbers. */
48952a1ad637SFrançois Tigeot 	for (j = 0; j < devinfo->ascnt; j++) {
48962a1ad637SFrançois Tigeot 		if (as[j].enable == 0)
48972a1ad637SFrançois Tigeot 			continue;
48982a1ad637SFrançois Tigeot 		HDA_BOOTVERBOSE(
48992a1ad637SFrançois Tigeot 			device_printf(devinfo->dev,
49002a1ad637SFrançois Tigeot 			    "Tracing association %d (%d)\n", j, as[j].index);
49012a1ad637SFrançois Tigeot 		);
49022a1ad637SFrançois Tigeot 		if (as[j].dir == HDAA_CTL_OUT) {
49032a1ad637SFrançois Tigeot retry:
49042a1ad637SFrançois Tigeot 			res = hdaa_audio_trace_as_out(devinfo, j, 0);
49052a1ad637SFrançois Tigeot 			if (res == 0 && as[j].hpredir >= 0 &&
49062a1ad637SFrançois Tigeot 			    as[j].fakeredir == 0) {
49072a1ad637SFrançois Tigeot 				/* If CODEC can't do analog HP redirection
49082a1ad637SFrançois Tigeot 				   try to make it using one more DAC. */
49092a1ad637SFrançois Tigeot 				as[j].fakeredir = 1;
49102a1ad637SFrançois Tigeot 				goto retry;
49112a1ad637SFrançois Tigeot 			}
49122a1ad637SFrançois Tigeot 		} else if (as[j].mixed)
49132a1ad637SFrançois Tigeot 			res = hdaa_audio_trace_as_in(devinfo, j);
49142a1ad637SFrançois Tigeot 		else
49152a1ad637SFrançois Tigeot 			res = hdaa_audio_trace_as_in_mch(devinfo, j, 0);
49162a1ad637SFrançois Tigeot 		if (res) {
49172a1ad637SFrançois Tigeot 			HDA_BOOTVERBOSE(
49182a1ad637SFrançois Tigeot 				device_printf(devinfo->dev,
49192a1ad637SFrançois Tigeot 				    "Association %d (%d) trace succeeded\n",
49202a1ad637SFrançois Tigeot 				    j, as[j].index);
49212a1ad637SFrançois Tigeot 			);
49222a1ad637SFrançois Tigeot 		} else {
49232a1ad637SFrançois Tigeot 			HDA_BOOTVERBOSE(
49242a1ad637SFrançois Tigeot 				device_printf(devinfo->dev,
49252a1ad637SFrançois Tigeot 				    "Association %d (%d) trace failed\n",
49262a1ad637SFrançois Tigeot 				    j, as[j].index);
49272a1ad637SFrançois Tigeot 			);
49282a1ad637SFrançois Tigeot 			as[j].enable = 0;
49292a1ad637SFrançois Tigeot 		}
49302a1ad637SFrançois Tigeot 	}
49312a1ad637SFrançois Tigeot 
49322a1ad637SFrançois Tigeot 	/* Look for additional DACs/ADCs. */
49332a1ad637SFrançois Tigeot 	for (j = 0; j < devinfo->ascnt; j++) {
49342a1ad637SFrançois Tigeot 		if (as[j].enable == 0)
49352a1ad637SFrançois Tigeot 			continue;
49362a1ad637SFrançois Tigeot 		hdaa_audio_adddac(devinfo, j);
49372a1ad637SFrançois Tigeot 	}
49382a1ad637SFrançois Tigeot 
49392a1ad637SFrançois Tigeot 	/* Trace mixer and beeper pseudo associations. */
49402a1ad637SFrançois Tigeot 	hdaa_audio_trace_as_extra(devinfo);
49412a1ad637SFrançois Tigeot }
49422a1ad637SFrançois Tigeot 
49432a1ad637SFrançois Tigeot /*
49442a1ad637SFrançois Tigeot  * Store in pdevinfo new data about whether and how we can control signal
49452a1ad637SFrançois Tigeot  * for OSS device to/from specified widget.
49462a1ad637SFrançois Tigeot  */
49472a1ad637SFrançois Tigeot static void
hdaa_adjust_amp(struct hdaa_widget * w,int ossdev,int found,int minamp,int maxamp)49482a1ad637SFrançois Tigeot hdaa_adjust_amp(struct hdaa_widget *w, int ossdev,
49492a1ad637SFrançois Tigeot     int found, int minamp, int maxamp)
49502a1ad637SFrançois Tigeot {
49512a1ad637SFrançois Tigeot 	struct hdaa_devinfo *devinfo = w->devinfo;
49522a1ad637SFrançois Tigeot 	struct hdaa_pcm_devinfo *pdevinfo;
49532a1ad637SFrançois Tigeot 
49542a1ad637SFrançois Tigeot 	if (w->bindas >= 0)
49552a1ad637SFrançois Tigeot 		pdevinfo = devinfo->as[w->bindas].pdevinfo;
49562a1ad637SFrançois Tigeot 	else
49572a1ad637SFrançois Tigeot 		pdevinfo = &devinfo->devs[0];
49582a1ad637SFrançois Tigeot 	if (found)
49592a1ad637SFrançois Tigeot 		pdevinfo->ossmask |= (1 << ossdev);
49602a1ad637SFrançois Tigeot 	if (minamp == 0 && maxamp == 0)
49612a1ad637SFrançois Tigeot 		return;
49622a1ad637SFrançois Tigeot 	if (pdevinfo->minamp[ossdev] == 0 && pdevinfo->maxamp[ossdev] == 0) {
49632a1ad637SFrançois Tigeot 		pdevinfo->minamp[ossdev] = minamp;
49642a1ad637SFrançois Tigeot 		pdevinfo->maxamp[ossdev] = maxamp;
49652a1ad637SFrançois Tigeot 	} else {
49662a1ad637SFrançois Tigeot 		pdevinfo->minamp[ossdev] = imax(pdevinfo->minamp[ossdev], minamp);
49672a1ad637SFrançois Tigeot 		pdevinfo->maxamp[ossdev] = imin(pdevinfo->maxamp[ossdev], maxamp);
49682a1ad637SFrançois Tigeot 	}
49692a1ad637SFrançois Tigeot }
49702a1ad637SFrançois Tigeot 
49712a1ad637SFrançois Tigeot /*
49722a1ad637SFrançois Tigeot  * Trace signals from/to all possible sources/destionstions to find possible
49732a1ad637SFrançois Tigeot  * recording sources, OSS device control ranges and to assign controls.
49742a1ad637SFrançois Tigeot  */
49752a1ad637SFrançois Tigeot static void
hdaa_audio_assign_mixers(struct hdaa_devinfo * devinfo)49762a1ad637SFrançois Tigeot hdaa_audio_assign_mixers(struct hdaa_devinfo *devinfo)
49772a1ad637SFrançois Tigeot {
49782a1ad637SFrançois Tigeot 	struct hdaa_audio_as *as = devinfo->as;
49792a1ad637SFrançois Tigeot 	struct hdaa_widget *w, *cw;
49802a1ad637SFrançois Tigeot 	int i, j, minamp, maxamp, found;
49812a1ad637SFrançois Tigeot 
49822a1ad637SFrançois Tigeot 	/* Assign mixers to the tree. */
49832a1ad637SFrançois Tigeot 	for (i = devinfo->startnode; i < devinfo->endnode; i++) {
49842a1ad637SFrançois Tigeot 		w = hdaa_widget_get(devinfo, i);
49852a1ad637SFrançois Tigeot 		if (w == NULL || w->enable == 0)
49862a1ad637SFrançois Tigeot 			continue;
49872a1ad637SFrançois Tigeot 		minamp = maxamp = 0;
49882a1ad637SFrançois Tigeot 		if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT ||
49892a1ad637SFrançois Tigeot 		    w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET ||
49902a1ad637SFrançois Tigeot 		    (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX &&
49912a1ad637SFrançois Tigeot 		    as[w->bindas].dir == HDAA_CTL_IN)) {
49922a1ad637SFrançois Tigeot 			if (w->ossdev < 0)
49932a1ad637SFrançois Tigeot 				continue;
49942a1ad637SFrançois Tigeot 			found = hdaa_audio_ctl_source_amp(devinfo, w->nid, -1,
49952a1ad637SFrançois Tigeot 			    w->ossdev, 1, 0, &minamp, &maxamp);
49962a1ad637SFrançois Tigeot 			hdaa_adjust_amp(w, w->ossdev, found, minamp, maxamp);
49972a1ad637SFrançois Tigeot 		} else if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT) {
49982a1ad637SFrançois Tigeot 			found = hdaa_audio_ctl_dest_amp(devinfo, w->nid, -1,
49992a1ad637SFrançois Tigeot 			    SOUND_MIXER_RECLEV, 0, &minamp, &maxamp);
50002a1ad637SFrançois Tigeot 			hdaa_adjust_amp(w, SOUND_MIXER_RECLEV, found, minamp, maxamp);
50012a1ad637SFrançois Tigeot 		} else if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX &&
50022a1ad637SFrançois Tigeot 		    as[w->bindas].dir == HDAA_CTL_OUT) {
50032a1ad637SFrançois Tigeot 			found = hdaa_audio_ctl_dest_amp(devinfo, w->nid, -1,
50042a1ad637SFrançois Tigeot 			    SOUND_MIXER_VOLUME, 0, &minamp, &maxamp);
50052a1ad637SFrançois Tigeot 			hdaa_adjust_amp(w, SOUND_MIXER_VOLUME, found, minamp, maxamp);
50062a1ad637SFrançois Tigeot 		}
50072a1ad637SFrançois Tigeot 		if (w->ossdev == SOUND_MIXER_IMIX) {
50082a1ad637SFrançois Tigeot 			minamp = maxamp = 0;
50092a1ad637SFrançois Tigeot 			found = hdaa_audio_ctl_source_amp(devinfo, w->nid, -1,
50102a1ad637SFrançois Tigeot 			    w->ossdev, 1, 0, &minamp, &maxamp);
50112a1ad637SFrançois Tigeot 			if (minamp == maxamp) {
50122a1ad637SFrançois Tigeot 				/* If we are unable to control input monitor
50132a1ad637SFrançois Tigeot 				   as source - try to control it as destination. */
50142a1ad637SFrançois Tigeot 				found += hdaa_audio_ctl_dest_amp(devinfo, w->nid, -1,
50152a1ad637SFrançois Tigeot 				    w->ossdev, 0, &minamp, &maxamp);
50162a1ad637SFrançois Tigeot 				w->pflags |= HDAA_IMIX_AS_DST;
50172a1ad637SFrançois Tigeot 			}
50182a1ad637SFrançois Tigeot 			hdaa_adjust_amp(w, w->ossdev, found, minamp, maxamp);
50192a1ad637SFrançois Tigeot 		}
50202a1ad637SFrançois Tigeot 		if (w->pflags & HDAA_ADC_MONITOR) {
50212a1ad637SFrançois Tigeot 			for (j = 0; j < w->nconns; j++) {
50222a1ad637SFrançois Tigeot 				if (!w->connsenable[j])
50232a1ad637SFrançois Tigeot 				    continue;
50242a1ad637SFrançois Tigeot 				cw = hdaa_widget_get(devinfo, w->conns[j]);
50252a1ad637SFrançois Tigeot 				if (cw == NULL || cw->enable == 0)
50262a1ad637SFrançois Tigeot 				    continue;
50272a1ad637SFrançois Tigeot 				if (cw->bindas == -1)
50282a1ad637SFrançois Tigeot 				    continue;
50292a1ad637SFrançois Tigeot 				if (cw->bindas >= 0 &&
50302a1ad637SFrançois Tigeot 				    as[cw->bindas].dir != HDAA_CTL_IN)
50312a1ad637SFrançois Tigeot 					continue;
50322a1ad637SFrançois Tigeot 				minamp = maxamp = 0;
50332a1ad637SFrançois Tigeot 				found = hdaa_audio_ctl_dest_amp(devinfo,
50342a1ad637SFrançois Tigeot 				    w->nid, j, SOUND_MIXER_IGAIN, 0,
50352a1ad637SFrançois Tigeot 				    &minamp, &maxamp);
50362a1ad637SFrançois Tigeot 				hdaa_adjust_amp(w, SOUND_MIXER_IGAIN,
50372a1ad637SFrançois Tigeot 				    found, minamp, maxamp);
50382a1ad637SFrançois Tigeot 			}
50392a1ad637SFrançois Tigeot 		}
50402a1ad637SFrançois Tigeot 	}
50412a1ad637SFrançois Tigeot }
50422a1ad637SFrançois Tigeot 
50432a1ad637SFrançois Tigeot static void
hdaa_audio_prepare_pin_ctrl(struct hdaa_devinfo * devinfo)50442a1ad637SFrançois Tigeot hdaa_audio_prepare_pin_ctrl(struct hdaa_devinfo *devinfo)
50452a1ad637SFrançois Tigeot {
50462a1ad637SFrançois Tigeot 	struct hdaa_audio_as *as = devinfo->as;
50472a1ad637SFrançois Tigeot 	struct hdaa_widget *w;
50482a1ad637SFrançois Tigeot 	uint32_t pincap;
50492a1ad637SFrançois Tigeot 	int i;
50502a1ad637SFrançois Tigeot 
50512a1ad637SFrançois Tigeot 	for (i = 0; i < devinfo->nodecnt; i++) {
50522a1ad637SFrançois Tigeot 		w = &devinfo->widget[i];
50532a1ad637SFrançois Tigeot 		if (w == NULL)
50542a1ad637SFrançois Tigeot 			continue;
50552a1ad637SFrançois Tigeot 		if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX &&
50562a1ad637SFrançois Tigeot 		    w->waspin == 0)
50572a1ad637SFrançois Tigeot 			continue;
50582a1ad637SFrançois Tigeot 
50592a1ad637SFrançois Tigeot 		pincap = w->wclass.pin.cap;
50602a1ad637SFrançois Tigeot 
50612a1ad637SFrançois Tigeot 		/* Disable everything. */
50622a1ad637SFrançois Tigeot 		w->wclass.pin.ctrl &= ~(
50632a1ad637SFrançois Tigeot 		    HDA_CMD_SET_PIN_WIDGET_CTRL_HPHN_ENABLE |
50642a1ad637SFrançois Tigeot 		    HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE |
50652a1ad637SFrançois Tigeot 		    HDA_CMD_SET_PIN_WIDGET_CTRL_IN_ENABLE |
50662a1ad637SFrançois Tigeot 		    HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE_MASK);
50672a1ad637SFrançois Tigeot 
50682a1ad637SFrançois Tigeot 		if (w->enable == 0) {
50692a1ad637SFrançois Tigeot 			/* Pin is unused so left it disabled. */
50702a1ad637SFrançois Tigeot 			continue;
50712a1ad637SFrançois Tigeot 		} else if (w->waspin) {
50722a1ad637SFrançois Tigeot 			/* Enable input for beeper input. */
50732a1ad637SFrançois Tigeot 			w->wclass.pin.ctrl |=
50742a1ad637SFrançois Tigeot 			    HDA_CMD_SET_PIN_WIDGET_CTRL_IN_ENABLE;
50752a1ad637SFrançois Tigeot 		} else if (w->bindas < 0 || as[w->bindas].enable == 0) {
50762a1ad637SFrançois Tigeot 			/* Pin is unused so left it disabled. */
50772a1ad637SFrançois Tigeot 			continue;
50782a1ad637SFrançois Tigeot 		} else if (as[w->bindas].dir == HDAA_CTL_IN) {
50792a1ad637SFrançois Tigeot 			/* Input pin, configure for input. */
50802a1ad637SFrançois Tigeot 			if (HDA_PARAM_PIN_CAP_INPUT_CAP(pincap))
50812a1ad637SFrançois Tigeot 				w->wclass.pin.ctrl |=
50822a1ad637SFrançois Tigeot 				    HDA_CMD_SET_PIN_WIDGET_CTRL_IN_ENABLE;
50832a1ad637SFrançois Tigeot 
50842a1ad637SFrançois Tigeot 			if ((devinfo->quirks & HDAA_QUIRK_IVREF100) &&
50852a1ad637SFrançois Tigeot 			    HDA_PARAM_PIN_CAP_VREF_CTRL_100(pincap))
50862a1ad637SFrançois Tigeot 				w->wclass.pin.ctrl |=
50872a1ad637SFrançois Tigeot 				    HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE(
50882a1ad637SFrançois Tigeot 				    HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_100);
50892a1ad637SFrançois Tigeot 			else if ((devinfo->quirks & HDAA_QUIRK_IVREF80) &&
50902a1ad637SFrançois Tigeot 			    HDA_PARAM_PIN_CAP_VREF_CTRL_80(pincap))
50912a1ad637SFrançois Tigeot 				w->wclass.pin.ctrl |=
50922a1ad637SFrançois Tigeot 				    HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE(
50932a1ad637SFrançois Tigeot 				    HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_80);
50942a1ad637SFrançois Tigeot 			else if ((devinfo->quirks & HDAA_QUIRK_IVREF50) &&
50952a1ad637SFrançois Tigeot 			    HDA_PARAM_PIN_CAP_VREF_CTRL_50(pincap))
50962a1ad637SFrançois Tigeot 				w->wclass.pin.ctrl |=
50972a1ad637SFrançois Tigeot 				    HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE(
50982a1ad637SFrançois Tigeot 				    HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_50);
50992a1ad637SFrançois Tigeot 		} else {
51002a1ad637SFrançois Tigeot 			/* Output pin, configure for output. */
51012a1ad637SFrançois Tigeot 			if (HDA_PARAM_PIN_CAP_OUTPUT_CAP(pincap))
51022a1ad637SFrançois Tigeot 				w->wclass.pin.ctrl |=
51032a1ad637SFrançois Tigeot 				    HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE;
51042a1ad637SFrançois Tigeot 
51052a1ad637SFrançois Tigeot 			if (HDA_PARAM_PIN_CAP_HEADPHONE_CAP(pincap) &&
51062a1ad637SFrançois Tigeot 			    (w->wclass.pin.config &
51072a1ad637SFrançois Tigeot 			    HDA_CONFIG_DEFAULTCONF_DEVICE_MASK) ==
51082a1ad637SFrançois Tigeot 			    HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT)
51092a1ad637SFrançois Tigeot 				w->wclass.pin.ctrl |=
51102a1ad637SFrançois Tigeot 				    HDA_CMD_SET_PIN_WIDGET_CTRL_HPHN_ENABLE;
51112a1ad637SFrançois Tigeot 
51122a1ad637SFrançois Tigeot 			if ((devinfo->quirks & HDAA_QUIRK_OVREF100) &&
51132a1ad637SFrançois Tigeot 			    HDA_PARAM_PIN_CAP_VREF_CTRL_100(pincap))
51142a1ad637SFrançois Tigeot 				w->wclass.pin.ctrl |=
51152a1ad637SFrançois Tigeot 				    HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE(
51162a1ad637SFrançois Tigeot 				    HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_100);
51172a1ad637SFrançois Tigeot 			else if ((devinfo->quirks & HDAA_QUIRK_OVREF80) &&
51182a1ad637SFrançois Tigeot 			    HDA_PARAM_PIN_CAP_VREF_CTRL_80(pincap))
51192a1ad637SFrançois Tigeot 				w->wclass.pin.ctrl |=
51202a1ad637SFrançois Tigeot 				    HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE(
51212a1ad637SFrançois Tigeot 				    HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_80);
51222a1ad637SFrançois Tigeot 			else if ((devinfo->quirks & HDAA_QUIRK_OVREF50) &&
51232a1ad637SFrançois Tigeot 			    HDA_PARAM_PIN_CAP_VREF_CTRL_50(pincap))
51242a1ad637SFrançois Tigeot 				w->wclass.pin.ctrl |=
51252a1ad637SFrançois Tigeot 				    HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE(
51262a1ad637SFrançois Tigeot 				    HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_50);
51272a1ad637SFrançois Tigeot 		}
51282a1ad637SFrançois Tigeot 	}
51292a1ad637SFrançois Tigeot }
51302a1ad637SFrançois Tigeot 
51312a1ad637SFrançois Tigeot static void
hdaa_audio_ctl_commit(struct hdaa_devinfo * devinfo)51322a1ad637SFrançois Tigeot hdaa_audio_ctl_commit(struct hdaa_devinfo *devinfo)
51332a1ad637SFrançois Tigeot {
51342a1ad637SFrançois Tigeot 	struct hdaa_audio_ctl *ctl;
51352a1ad637SFrançois Tigeot 	int i, z;
51362a1ad637SFrançois Tigeot 
51372a1ad637SFrançois Tigeot 	i = 0;
51382a1ad637SFrançois Tigeot 	while ((ctl = hdaa_audio_ctl_each(devinfo, &i)) != NULL) {
51392a1ad637SFrançois Tigeot 		if (ctl->enable == 0 || ctl->ossmask != 0) {
51402a1ad637SFrançois Tigeot 			/* Mute disabled and mixer controllable controls.
51412a1ad637SFrançois Tigeot 			 * Last will be initialized by mixer_init().
51422a1ad637SFrançois Tigeot 			 * This expected to reduce click on startup. */
51432a1ad637SFrançois Tigeot 			hdaa_audio_ctl_amp_set(ctl, HDAA_AMP_MUTE_ALL, 0, 0);
51442a1ad637SFrançois Tigeot 			continue;
51452a1ad637SFrançois Tigeot 		}
51462a1ad637SFrançois Tigeot 		/* Init fixed controls to 0dB amplification. */
51472a1ad637SFrançois Tigeot 		z = ctl->offset;
51482a1ad637SFrançois Tigeot 		if (z > ctl->step)
51492a1ad637SFrançois Tigeot 			z = ctl->step;
51502a1ad637SFrançois Tigeot 		hdaa_audio_ctl_amp_set(ctl, HDAA_AMP_MUTE_NONE, z, z);
51512a1ad637SFrançois Tigeot 	}
51522a1ad637SFrançois Tigeot }
51532a1ad637SFrançois Tigeot 
51542a1ad637SFrançois Tigeot static void
hdaa_gpio_commit(struct hdaa_devinfo * devinfo)51552a1ad637SFrançois Tigeot hdaa_gpio_commit(struct hdaa_devinfo *devinfo)
51562a1ad637SFrançois Tigeot {
51572a1ad637SFrançois Tigeot 	uint32_t gdata, gmask, gdir;
51582a1ad637SFrançois Tigeot 	int i, numgpio;
51592a1ad637SFrançois Tigeot 
51602a1ad637SFrançois Tigeot 	numgpio = HDA_PARAM_GPIO_COUNT_NUM_GPIO(devinfo->gpio_cap);
51612a1ad637SFrançois Tigeot 	if (devinfo->gpio != 0 && numgpio != 0) {
51622a1ad637SFrançois Tigeot 		gdata = hda_command(devinfo->dev,
51632a1ad637SFrançois Tigeot 		    HDA_CMD_GET_GPIO_DATA(0, devinfo->nid));
51642a1ad637SFrançois Tigeot 		gmask = hda_command(devinfo->dev,
51652a1ad637SFrançois Tigeot 		    HDA_CMD_GET_GPIO_ENABLE_MASK(0, devinfo->nid));
51662a1ad637SFrançois Tigeot 		gdir = hda_command(devinfo->dev,
51672a1ad637SFrançois Tigeot 		    HDA_CMD_GET_GPIO_DIRECTION(0, devinfo->nid));
51682a1ad637SFrançois Tigeot 		for (i = 0; i < numgpio; i++) {
51692a1ad637SFrançois Tigeot 			if ((devinfo->gpio & HDAA_GPIO_MASK(i)) ==
51702a1ad637SFrançois Tigeot 			    HDAA_GPIO_SET(i)) {
51712a1ad637SFrançois Tigeot 				gdata |= (1 << i);
51722a1ad637SFrançois Tigeot 				gmask |= (1 << i);
51732a1ad637SFrançois Tigeot 				gdir |= (1 << i);
51742a1ad637SFrançois Tigeot 			} else if ((devinfo->gpio & HDAA_GPIO_MASK(i)) ==
51752a1ad637SFrançois Tigeot 			    HDAA_GPIO_CLEAR(i)) {
51762a1ad637SFrançois Tigeot 				gdata &= ~(1 << i);
51772a1ad637SFrançois Tigeot 				gmask |= (1 << i);
51782a1ad637SFrançois Tigeot 				gdir |= (1 << i);
51792a1ad637SFrançois Tigeot 			} else if ((devinfo->gpio & HDAA_GPIO_MASK(i)) ==
51802a1ad637SFrançois Tigeot 			    HDAA_GPIO_DISABLE(i)) {
51812a1ad637SFrançois Tigeot 				gmask &= ~(1 << i);
51822a1ad637SFrançois Tigeot 			} else if ((devinfo->gpio & HDAA_GPIO_MASK(i)) ==
51832a1ad637SFrançois Tigeot 			    HDAA_GPIO_INPUT(i)) {
51842a1ad637SFrançois Tigeot 				gmask |= (1 << i);
51852a1ad637SFrançois Tigeot 				gdir &= ~(1 << i);
51862a1ad637SFrançois Tigeot 			}
51872a1ad637SFrançois Tigeot 		}
51882a1ad637SFrançois Tigeot 		HDA_BOOTVERBOSE(
51892a1ad637SFrançois Tigeot 			device_printf(devinfo->dev, "GPIO commit\n");
51902a1ad637SFrançois Tigeot 		);
51912a1ad637SFrançois Tigeot 		hda_command(devinfo->dev,
51922a1ad637SFrançois Tigeot 		    HDA_CMD_SET_GPIO_ENABLE_MASK(0, devinfo->nid, gmask));
51932a1ad637SFrançois Tigeot 		hda_command(devinfo->dev,
51942a1ad637SFrançois Tigeot 		    HDA_CMD_SET_GPIO_DIRECTION(0, devinfo->nid, gdir));
51952a1ad637SFrançois Tigeot 		hda_command(devinfo->dev,
51962a1ad637SFrançois Tigeot 		    HDA_CMD_SET_GPIO_DATA(0, devinfo->nid, gdata));
51972a1ad637SFrançois Tigeot 		HDA_BOOTVERBOSE(
51982a1ad637SFrançois Tigeot 			hdaa_dump_gpio(devinfo);
51992a1ad637SFrançois Tigeot 		);
52002a1ad637SFrançois Tigeot 	}
52012a1ad637SFrançois Tigeot }
52022a1ad637SFrançois Tigeot 
52032a1ad637SFrançois Tigeot static void
hdaa_gpo_commit(struct hdaa_devinfo * devinfo)52042a1ad637SFrançois Tigeot hdaa_gpo_commit(struct hdaa_devinfo *devinfo)
52052a1ad637SFrançois Tigeot {
52062a1ad637SFrançois Tigeot 	uint32_t gdata;
52072a1ad637SFrançois Tigeot 	int i, numgpo;
52082a1ad637SFrançois Tigeot 
52092a1ad637SFrançois Tigeot 	numgpo = HDA_PARAM_GPIO_COUNT_NUM_GPO(devinfo->gpio_cap);
52102a1ad637SFrançois Tigeot 	if (devinfo->gpo != 0 && numgpo != 0) {
52112a1ad637SFrançois Tigeot 		gdata = hda_command(devinfo->dev,
52122a1ad637SFrançois Tigeot 		    HDA_CMD_GET_GPO_DATA(0, devinfo->nid));
52132a1ad637SFrançois Tigeot 		for (i = 0; i < numgpo; i++) {
52142a1ad637SFrançois Tigeot 			if ((devinfo->gpio & HDAA_GPIO_MASK(i)) ==
52152a1ad637SFrançois Tigeot 			    HDAA_GPIO_SET(i)) {
52162a1ad637SFrançois Tigeot 				gdata |= (1 << i);
52172a1ad637SFrançois Tigeot 			} else if ((devinfo->gpio & HDAA_GPIO_MASK(i)) ==
52182a1ad637SFrançois Tigeot 			    HDAA_GPIO_CLEAR(i)) {
52192a1ad637SFrançois Tigeot 				gdata &= ~(1 << i);
52202a1ad637SFrançois Tigeot 			}
52212a1ad637SFrançois Tigeot 		}
52222a1ad637SFrançois Tigeot 		HDA_BOOTVERBOSE(
52232a1ad637SFrançois Tigeot 			device_printf(devinfo->dev, "GPO commit\n");
52242a1ad637SFrançois Tigeot 		);
52252a1ad637SFrançois Tigeot 		hda_command(devinfo->dev,
52262a1ad637SFrançois Tigeot 		    HDA_CMD_SET_GPO_DATA(0, devinfo->nid, gdata));
52272a1ad637SFrançois Tigeot 		HDA_BOOTVERBOSE(
52282a1ad637SFrançois Tigeot 			hdaa_dump_gpo(devinfo);
52292a1ad637SFrançois Tigeot 		);
52302a1ad637SFrançois Tigeot 	}
52312a1ad637SFrançois Tigeot }
52322a1ad637SFrançois Tigeot 
52332a1ad637SFrançois Tigeot static void
hdaa_audio_commit(struct hdaa_devinfo * devinfo)52342a1ad637SFrançois Tigeot hdaa_audio_commit(struct hdaa_devinfo *devinfo)
52352a1ad637SFrançois Tigeot {
52362a1ad637SFrançois Tigeot 	struct hdaa_widget *w;
52372a1ad637SFrançois Tigeot 	int i;
52382a1ad637SFrançois Tigeot 
52392a1ad637SFrançois Tigeot 	/* Commit controls. */
52402a1ad637SFrançois Tigeot 	hdaa_audio_ctl_commit(devinfo);
52412a1ad637SFrançois Tigeot 
52422a1ad637SFrançois Tigeot 	/* Commit selectors, pins and EAPD. */
52432a1ad637SFrançois Tigeot 	for (i = 0; i < devinfo->nodecnt; i++) {
52442a1ad637SFrançois Tigeot 		w = &devinfo->widget[i];
52452a1ad637SFrançois Tigeot 		if (w == NULL)
52462a1ad637SFrançois Tigeot 			continue;
52472a1ad637SFrançois Tigeot 		if (w->selconn == -1)
52482a1ad637SFrançois Tigeot 			w->selconn = 0;
52492a1ad637SFrançois Tigeot 		if (w->nconns > 0)
52502a1ad637SFrançois Tigeot 			hdaa_widget_connection_select(w, w->selconn);
52512a1ad637SFrançois Tigeot 		if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX ||
52522a1ad637SFrançois Tigeot 		    w->waspin) {
52532a1ad637SFrançois Tigeot 			hda_command(devinfo->dev,
52542a1ad637SFrançois Tigeot 			    HDA_CMD_SET_PIN_WIDGET_CTRL(0, w->nid,
52552a1ad637SFrançois Tigeot 			    w->wclass.pin.ctrl));
52562a1ad637SFrançois Tigeot 		}
52572a1ad637SFrançois Tigeot 		if (w->param.eapdbtl != HDA_INVALID) {
52582a1ad637SFrançois Tigeot 			uint32_t val;
52592a1ad637SFrançois Tigeot 
52602a1ad637SFrançois Tigeot 			val = w->param.eapdbtl;
52612a1ad637SFrançois Tigeot 			if (devinfo->quirks &
52622a1ad637SFrançois Tigeot 			    HDAA_QUIRK_EAPDINV)
52632a1ad637SFrançois Tigeot 				val ^= HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD;
52642a1ad637SFrançois Tigeot 			hda_command(devinfo->dev,
52652a1ad637SFrançois Tigeot 			    HDA_CMD_SET_EAPD_BTL_ENABLE(0, w->nid,
52662a1ad637SFrançois Tigeot 			    val));
52672a1ad637SFrançois Tigeot 		}
52682a1ad637SFrançois Tigeot 	}
52692a1ad637SFrançois Tigeot 
52702a1ad637SFrançois Tigeot 	hdaa_gpio_commit(devinfo);
52712a1ad637SFrançois Tigeot 	hdaa_gpo_commit(devinfo);
52722a1ad637SFrançois Tigeot }
52732a1ad637SFrançois Tigeot 
52742a1ad637SFrançois Tigeot static void
hdaa_powerup(struct hdaa_devinfo * devinfo)52752a1ad637SFrançois Tigeot hdaa_powerup(struct hdaa_devinfo *devinfo)
52762a1ad637SFrançois Tigeot {
52772a1ad637SFrançois Tigeot 	int i;
52782a1ad637SFrançois Tigeot 
52792a1ad637SFrançois Tigeot 	hda_command(devinfo->dev,
52802a1ad637SFrançois Tigeot 	    HDA_CMD_SET_POWER_STATE(0,
52812a1ad637SFrançois Tigeot 	    devinfo->nid, HDA_CMD_POWER_STATE_D0));
52822a1ad637SFrançois Tigeot 	DELAY(100);
52832a1ad637SFrançois Tigeot 
52842a1ad637SFrançois Tigeot 	for (i = devinfo->startnode; i < devinfo->endnode; i++) {
52852a1ad637SFrançois Tigeot 		hda_command(devinfo->dev,
52862a1ad637SFrançois Tigeot 		    HDA_CMD_SET_POWER_STATE(0,
52872a1ad637SFrançois Tigeot 		    i, HDA_CMD_POWER_STATE_D0));
52882a1ad637SFrançois Tigeot 	}
52892a1ad637SFrançois Tigeot 	DELAY(1000);
52902a1ad637SFrançois Tigeot }
52912a1ad637SFrançois Tigeot 
52922a1ad637SFrançois Tigeot static int
hdaa_pcmchannel_setup(struct hdaa_chan * ch)52932a1ad637SFrançois Tigeot hdaa_pcmchannel_setup(struct hdaa_chan *ch)
52942a1ad637SFrançois Tigeot {
52952a1ad637SFrançois Tigeot 	struct hdaa_devinfo *devinfo = ch->devinfo;
52962a1ad637SFrançois Tigeot 	struct hdaa_audio_as *as = devinfo->as;
52972a1ad637SFrançois Tigeot 	struct hdaa_widget *w;
52982a1ad637SFrançois Tigeot 	uint32_t cap, fmtcap, pcmcap;
52992a1ad637SFrançois Tigeot 	int i, j, ret, channels, onlystereo;
53002a1ad637SFrançois Tigeot 	uint16_t pinset;
53012a1ad637SFrançois Tigeot 
53022a1ad637SFrançois Tigeot 	ch->caps = hdaa_caps;
53032a1ad637SFrançois Tigeot 	ch->caps.fmtlist = ch->fmtlist;
53042a1ad637SFrançois Tigeot 	ch->bit16 = 1;
53052a1ad637SFrançois Tigeot 	ch->bit32 = 0;
53062a1ad637SFrançois Tigeot 	ch->pcmrates[0] = 48000;
53072a1ad637SFrançois Tigeot 	ch->pcmrates[1] = 0;
53082a1ad637SFrançois Tigeot 	ch->stripecap = 0xff;
53092a1ad637SFrançois Tigeot 
53102a1ad637SFrançois Tigeot 	ret = 0;
53112a1ad637SFrançois Tigeot 	channels = 0;
53122a1ad637SFrançois Tigeot 	onlystereo = 1;
53132a1ad637SFrançois Tigeot 	pinset = 0;
53142a1ad637SFrançois Tigeot 	fmtcap = devinfo->supp_stream_formats;
53152a1ad637SFrançois Tigeot 	pcmcap = devinfo->supp_pcm_size_rate;
53162a1ad637SFrançois Tigeot 
53172a1ad637SFrançois Tigeot 	for (i = 0; i < 16; i++) {
53182a1ad637SFrançois Tigeot 		/* Check as is correct */
53192a1ad637SFrançois Tigeot 		if (ch->as < 0)
53202a1ad637SFrançois Tigeot 			break;
53212a1ad637SFrançois Tigeot 		/* Cound only present DACs */
53222a1ad637SFrançois Tigeot 		if (as[ch->as].dacs[ch->asindex][i] <= 0)
53232a1ad637SFrançois Tigeot 			continue;
53242a1ad637SFrançois Tigeot 		/* Ignore duplicates */
53252a1ad637SFrançois Tigeot 		for (j = 0; j < ret; j++) {
53262a1ad637SFrançois Tigeot 			if (ch->io[j] == as[ch->as].dacs[ch->asindex][i])
53272a1ad637SFrançois Tigeot 				break;
53282a1ad637SFrançois Tigeot 		}
53292a1ad637SFrançois Tigeot 		if (j < ret)
53302a1ad637SFrançois Tigeot 			continue;
53312a1ad637SFrançois Tigeot 
53322a1ad637SFrançois Tigeot 		w = hdaa_widget_get(devinfo, as[ch->as].dacs[ch->asindex][i]);
53332a1ad637SFrançois Tigeot 		if (w == NULL || w->enable == 0)
53342a1ad637SFrançois Tigeot 			continue;
53352a1ad637SFrançois Tigeot 		cap = w->param.supp_stream_formats;
53362a1ad637SFrançois Tigeot 		if (!HDA_PARAM_SUPP_STREAM_FORMATS_PCM(cap) &&
53372a1ad637SFrançois Tigeot 		    !HDA_PARAM_SUPP_STREAM_FORMATS_AC3(cap))
53382a1ad637SFrançois Tigeot 			continue;
53392a1ad637SFrançois Tigeot 		/* Many CODECs does not declare AC3 support on SPDIF.
53402a1ad637SFrançois Tigeot 		   I don't beleave that they doesn't support it! */
53412a1ad637SFrançois Tigeot 		if (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap))
53422a1ad637SFrançois Tigeot 			cap |= HDA_PARAM_SUPP_STREAM_FORMATS_AC3_MASK;
53432a1ad637SFrançois Tigeot 		if (ret == 0) {
53442a1ad637SFrançois Tigeot 			fmtcap = cap;
53452a1ad637SFrançois Tigeot 			pcmcap = w->param.supp_pcm_size_rate;
53462a1ad637SFrançois Tigeot 		} else {
53472a1ad637SFrançois Tigeot 			fmtcap &= cap;
53482a1ad637SFrançois Tigeot 			pcmcap &= w->param.supp_pcm_size_rate;
53492a1ad637SFrançois Tigeot 		}
53502a1ad637SFrançois Tigeot 		ch->io[ret++] = as[ch->as].dacs[ch->asindex][i];
53512a1ad637SFrançois Tigeot 		ch->stripecap &= w->wclass.conv.stripecap;
53522a1ad637SFrançois Tigeot 		/* Do not count redirection pin/dac channels. */
53532a1ad637SFrançois Tigeot 		if (i == 15 && as[ch->as].hpredir >= 0)
53542a1ad637SFrançois Tigeot 			continue;
53552a1ad637SFrançois Tigeot 		channels += HDA_PARAM_AUDIO_WIDGET_CAP_CC(w->param.widget_cap) + 1;
53562a1ad637SFrançois Tigeot 		if (HDA_PARAM_AUDIO_WIDGET_CAP_CC(w->param.widget_cap) != 1)
53572a1ad637SFrançois Tigeot 			onlystereo = 0;
53582a1ad637SFrançois Tigeot 		pinset |= (1 << i);
53592a1ad637SFrançois Tigeot 	}
53602a1ad637SFrançois Tigeot 	ch->io[ret] = -1;
53612a1ad637SFrançois Tigeot 	ch->channels = channels;
53622a1ad637SFrançois Tigeot 
53632a1ad637SFrançois Tigeot 	if (as[ch->as].fakeredir)
53642a1ad637SFrançois Tigeot 		ret--;
53652a1ad637SFrançois Tigeot 	/* Standard speaks only about stereo pins and playback, ... */
53662a1ad637SFrançois Tigeot 	if ((!onlystereo) || as[ch->as].mixed)
53672a1ad637SFrançois Tigeot 		pinset = 0;
53682a1ad637SFrançois Tigeot 	/* ..., but there it gives us info about speakers layout. */
53692a1ad637SFrançois Tigeot 	as[ch->as].pinset = pinset;
53702a1ad637SFrançois Tigeot 
53712a1ad637SFrançois Tigeot 	ch->supp_stream_formats = fmtcap;
53722a1ad637SFrançois Tigeot 	ch->supp_pcm_size_rate = pcmcap;
53732a1ad637SFrançois Tigeot 
53742a1ad637SFrançois Tigeot 	/*
53752a1ad637SFrançois Tigeot 	 *  8bit = 0
53762a1ad637SFrançois Tigeot 	 * 16bit = 1
53772a1ad637SFrançois Tigeot 	 * 20bit = 2
53782a1ad637SFrançois Tigeot 	 * 24bit = 3
53792a1ad637SFrançois Tigeot 	 * 32bit = 4
53802a1ad637SFrançois Tigeot 	 */
53812a1ad637SFrançois Tigeot 	if (ret > 0) {
53822a1ad637SFrançois Tigeot 		i = 0;
53832a1ad637SFrançois Tigeot 		if (HDA_PARAM_SUPP_STREAM_FORMATS_PCM(fmtcap)) {
53842a1ad637SFrançois Tigeot 			if (HDA_PARAM_SUPP_PCM_SIZE_RATE_16BIT(pcmcap))
53852a1ad637SFrançois Tigeot 				ch->bit16 = 1;
53862a1ad637SFrançois Tigeot 			else if (HDA_PARAM_SUPP_PCM_SIZE_RATE_8BIT(pcmcap))
53872a1ad637SFrançois Tigeot 				ch->bit16 = 0;
53882a1ad637SFrançois Tigeot 			if (HDA_PARAM_SUPP_PCM_SIZE_RATE_24BIT(pcmcap))
53892a1ad637SFrançois Tigeot 				ch->bit32 = 3;
53902a1ad637SFrançois Tigeot 			else if (HDA_PARAM_SUPP_PCM_SIZE_RATE_20BIT(pcmcap))
53912a1ad637SFrançois Tigeot 				ch->bit32 = 2;
53922a1ad637SFrançois Tigeot 			else if (HDA_PARAM_SUPP_PCM_SIZE_RATE_32BIT(pcmcap))
53932a1ad637SFrançois Tigeot 				ch->bit32 = 4;
53942a1ad637SFrançois Tigeot 			if (!(devinfo->quirks & HDAA_QUIRK_FORCESTEREO)) {
53952a1ad637SFrançois Tigeot 				ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 1, 0);
53962a1ad637SFrançois Tigeot 				if (ch->bit32)
53972a1ad637SFrançois Tigeot 					ch->fmtlist[i++] = SND_FORMAT(AFMT_S32_LE, 1, 0);
53982a1ad637SFrançois Tigeot 			}
53992a1ad637SFrançois Tigeot 			if (channels >= 2) {
54002a1ad637SFrançois Tigeot 				ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 2, 0);
54012a1ad637SFrançois Tigeot 				if (ch->bit32)
54022a1ad637SFrançois Tigeot 					ch->fmtlist[i++] = SND_FORMAT(AFMT_S32_LE, 2, 0);
54032a1ad637SFrançois Tigeot 			}
54042a1ad637SFrançois Tigeot 			if (channels >= 3 && !onlystereo) {
54052a1ad637SFrançois Tigeot 				ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 3, 0);
54062a1ad637SFrançois Tigeot 				if (ch->bit32)
54072a1ad637SFrançois Tigeot 					ch->fmtlist[i++] = SND_FORMAT(AFMT_S32_LE, 3, 0);
54082a1ad637SFrançois Tigeot 				ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 3, 1);
54092a1ad637SFrançois Tigeot 				if (ch->bit32)
54102a1ad637SFrançois Tigeot 					ch->fmtlist[i++] = SND_FORMAT(AFMT_S32_LE, 3, 1);
54112a1ad637SFrançois Tigeot 			}
54122a1ad637SFrançois Tigeot 			if (channels >= 4) {
54132a1ad637SFrançois Tigeot 				ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 4, 0);
54142a1ad637SFrançois Tigeot 				if (ch->bit32)
54152a1ad637SFrançois Tigeot 					ch->fmtlist[i++] = SND_FORMAT(AFMT_S32_LE, 4, 0);
54162a1ad637SFrançois Tigeot 				if (!onlystereo) {
54172a1ad637SFrançois Tigeot 					ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 4, 1);
54182a1ad637SFrançois Tigeot 					if (ch->bit32)
54192a1ad637SFrançois Tigeot 						ch->fmtlist[i++] = SND_FORMAT(AFMT_S32_LE, 4, 1);
54202a1ad637SFrançois Tigeot 				}
54212a1ad637SFrançois Tigeot 			}
54222a1ad637SFrançois Tigeot 			if (channels >= 5 && !onlystereo) {
54232a1ad637SFrançois Tigeot 				ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 5, 0);
54242a1ad637SFrançois Tigeot 				if (ch->bit32)
54252a1ad637SFrançois Tigeot 					ch->fmtlist[i++] = SND_FORMAT(AFMT_S32_LE, 5, 0);
54262a1ad637SFrançois Tigeot 				ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 5, 1);
54272a1ad637SFrançois Tigeot 				if (ch->bit32)
54282a1ad637SFrançois Tigeot 					ch->fmtlist[i++] = SND_FORMAT(AFMT_S32_LE, 5, 1);
54292a1ad637SFrançois Tigeot 			}
54302a1ad637SFrançois Tigeot 			if (channels >= 6) {
54312a1ad637SFrançois Tigeot 				ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 6, 1);
54322a1ad637SFrançois Tigeot 				if (ch->bit32)
54332a1ad637SFrançois Tigeot 					ch->fmtlist[i++] = SND_FORMAT(AFMT_S32_LE, 6, 1);
54342a1ad637SFrançois Tigeot 				if (!onlystereo) {
54352a1ad637SFrançois Tigeot 					ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 6, 0);
54362a1ad637SFrançois Tigeot 					if (ch->bit32)
54372a1ad637SFrançois Tigeot 						ch->fmtlist[i++] = SND_FORMAT(AFMT_S32_LE, 6, 0);
54382a1ad637SFrançois Tigeot 				}
54392a1ad637SFrançois Tigeot 			}
54402a1ad637SFrançois Tigeot 			if (channels >= 7 && !onlystereo) {
54412a1ad637SFrançois Tigeot 				ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 7, 0);
54422a1ad637SFrançois Tigeot 				if (ch->bit32)
54432a1ad637SFrançois Tigeot 					ch->fmtlist[i++] = SND_FORMAT(AFMT_S32_LE, 7, 0);
54442a1ad637SFrançois Tigeot 				ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 7, 1);
54452a1ad637SFrançois Tigeot 				if (ch->bit32)
54462a1ad637SFrançois Tigeot 					ch->fmtlist[i++] = SND_FORMAT(AFMT_S32_LE, 7, 1);
54472a1ad637SFrançois Tigeot 			}
54482a1ad637SFrançois Tigeot 			if (channels >= 8) {
54492a1ad637SFrançois Tigeot 				ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 8, 1);
54502a1ad637SFrançois Tigeot 				if (ch->bit32)
54512a1ad637SFrançois Tigeot 					ch->fmtlist[i++] = SND_FORMAT(AFMT_S32_LE, 8, 1);
54522a1ad637SFrançois Tigeot 			}
54532a1ad637SFrançois Tigeot 		}
54542a1ad637SFrançois Tigeot 		if (HDA_PARAM_SUPP_STREAM_FORMATS_AC3(fmtcap)) {
54552a1ad637SFrançois Tigeot 			ch->fmtlist[i++] = SND_FORMAT(AFMT_AC3, 2, 0);
54562a1ad637SFrançois Tigeot 			if (channels >= 8) {
54572a1ad637SFrançois Tigeot 				ch->fmtlist[i++] = SND_FORMAT(AFMT_AC3, 8, 0);
54582a1ad637SFrançois Tigeot 				ch->fmtlist[i++] = SND_FORMAT(AFMT_AC3, 8, 1);
54592a1ad637SFrançois Tigeot 			}
54602a1ad637SFrançois Tigeot 		}
54612a1ad637SFrançois Tigeot 		ch->fmtlist[i] = 0;
54622a1ad637SFrançois Tigeot 		i = 0;
54632a1ad637SFrançois Tigeot 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_8KHZ(pcmcap))
54642a1ad637SFrançois Tigeot 			ch->pcmrates[i++] = 8000;
54652a1ad637SFrançois Tigeot 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_11KHZ(pcmcap))
54662a1ad637SFrançois Tigeot 			ch->pcmrates[i++] = 11025;
54672a1ad637SFrançois Tigeot 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_16KHZ(pcmcap))
54682a1ad637SFrançois Tigeot 			ch->pcmrates[i++] = 16000;
54692a1ad637SFrançois Tigeot 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_22KHZ(pcmcap))
54702a1ad637SFrançois Tigeot 			ch->pcmrates[i++] = 22050;
54712a1ad637SFrançois Tigeot 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_32KHZ(pcmcap))
54722a1ad637SFrançois Tigeot 			ch->pcmrates[i++] = 32000;
54732a1ad637SFrançois Tigeot 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_44KHZ(pcmcap))
54742a1ad637SFrançois Tigeot 			ch->pcmrates[i++] = 44100;
54752a1ad637SFrançois Tigeot 		/* if (HDA_PARAM_SUPP_PCM_SIZE_RATE_48KHZ(pcmcap)) */
54762a1ad637SFrançois Tigeot 		ch->pcmrates[i++] = 48000;
54772a1ad637SFrançois Tigeot 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_88KHZ(pcmcap))
54782a1ad637SFrançois Tigeot 			ch->pcmrates[i++] = 88200;
54792a1ad637SFrançois Tigeot 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_96KHZ(pcmcap))
54802a1ad637SFrançois Tigeot 			ch->pcmrates[i++] = 96000;
54812a1ad637SFrançois Tigeot 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_176KHZ(pcmcap))
54822a1ad637SFrançois Tigeot 			ch->pcmrates[i++] = 176400;
54832a1ad637SFrançois Tigeot 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_192KHZ(pcmcap))
54842a1ad637SFrançois Tigeot 			ch->pcmrates[i++] = 192000;
54852a1ad637SFrançois Tigeot 		/* if (HDA_PARAM_SUPP_PCM_SIZE_RATE_384KHZ(pcmcap)) */
54862a1ad637SFrançois Tigeot 		ch->pcmrates[i] = 0;
54872a1ad637SFrançois Tigeot 		if (i > 0) {
54882a1ad637SFrançois Tigeot 			ch->caps.minspeed = ch->pcmrates[0];
54892a1ad637SFrançois Tigeot 			ch->caps.maxspeed = ch->pcmrates[i - 1];
54902a1ad637SFrançois Tigeot 		}
54912a1ad637SFrançois Tigeot 	}
54922a1ad637SFrançois Tigeot 
54932a1ad637SFrançois Tigeot 	return (ret);
54942a1ad637SFrançois Tigeot }
54952a1ad637SFrançois Tigeot 
54962a1ad637SFrançois Tigeot static void
hdaa_prepare_pcms(struct hdaa_devinfo * devinfo)54972a1ad637SFrançois Tigeot hdaa_prepare_pcms(struct hdaa_devinfo *devinfo)
54982a1ad637SFrançois Tigeot {
54992a1ad637SFrançois Tigeot 	struct hdaa_audio_as *as = devinfo->as;
55002a1ad637SFrançois Tigeot 	int i, j, k, apdev = 0, ardev = 0, dpdev = 0, drdev = 0;
55012a1ad637SFrançois Tigeot 
55022a1ad637SFrançois Tigeot 	for (i = 0; i < devinfo->ascnt; i++) {
55032a1ad637SFrançois Tigeot 		if (as[i].enable == 0)
55042a1ad637SFrançois Tigeot 			continue;
55052a1ad637SFrançois Tigeot 		if (as[i].dir == HDAA_CTL_IN) {
55062a1ad637SFrançois Tigeot 			if (as[i].digital)
55072a1ad637SFrançois Tigeot 				drdev++;
55082a1ad637SFrançois Tigeot 			else
55092a1ad637SFrançois Tigeot 				ardev++;
55102a1ad637SFrançois Tigeot 		} else {
55112a1ad637SFrançois Tigeot 			if (as[i].digital)
55122a1ad637SFrançois Tigeot 				dpdev++;
55132a1ad637SFrançois Tigeot 			else
55142a1ad637SFrançois Tigeot 				apdev++;
55152a1ad637SFrançois Tigeot 		}
55162a1ad637SFrançois Tigeot 	}
55172a1ad637SFrançois Tigeot 	devinfo->num_devs =
55182a1ad637SFrançois Tigeot 	    max(ardev, apdev) + max(drdev, dpdev);
55192a1ad637SFrançois Tigeot 	devinfo->devs =
552067931cc4SFrançois Tigeot 	    (struct hdaa_pcm_devinfo *)kmalloc(
55212a1ad637SFrançois Tigeot 	    devinfo->num_devs * sizeof(struct hdaa_pcm_devinfo),
55224e8e900cSMatthew Dillon 	    M_HDAA, M_ZERO | M_WAITOK);
55232a1ad637SFrançois Tigeot 	if (devinfo->devs == NULL) {
55242a1ad637SFrançois Tigeot 		device_printf(devinfo->dev,
55252a1ad637SFrançois Tigeot 		    "Unable to allocate memory for devices\n");
55262a1ad637SFrançois Tigeot 		return;
55272a1ad637SFrançois Tigeot 	}
55282a1ad637SFrançois Tigeot 	for (i = 0; i < devinfo->num_devs; i++) {
55292a1ad637SFrançois Tigeot 		devinfo->devs[i].index = i;
55302a1ad637SFrançois Tigeot 		devinfo->devs[i].devinfo = devinfo;
55312a1ad637SFrançois Tigeot 		devinfo->devs[i].playas = -1;
55322a1ad637SFrançois Tigeot 		devinfo->devs[i].recas = -1;
55332a1ad637SFrançois Tigeot 		devinfo->devs[i].digital = 255;
55342a1ad637SFrançois Tigeot 	}
55352a1ad637SFrançois Tigeot 	for (i = 0; i < devinfo->ascnt; i++) {
55362a1ad637SFrançois Tigeot 		if (as[i].enable == 0)
55372a1ad637SFrançois Tigeot 			continue;
55382a1ad637SFrançois Tigeot 		for (j = 0; j < devinfo->num_devs; j++) {
55392a1ad637SFrançois Tigeot 			if (devinfo->devs[j].digital != 255 &&
55402a1ad637SFrançois Tigeot 			    (!devinfo->devs[j].digital) !=
55412a1ad637SFrançois Tigeot 			    (!as[i].digital))
55422a1ad637SFrançois Tigeot 				continue;
55432a1ad637SFrançois Tigeot 			if (as[i].dir == HDAA_CTL_IN) {
55442a1ad637SFrançois Tigeot 				if (devinfo->devs[j].recas >= 0)
55452a1ad637SFrançois Tigeot 					continue;
55462a1ad637SFrançois Tigeot 				devinfo->devs[j].recas = i;
55472a1ad637SFrançois Tigeot 			} else {
55482a1ad637SFrançois Tigeot 				if (devinfo->devs[j].playas >= 0)
55492a1ad637SFrançois Tigeot 					continue;
55502a1ad637SFrançois Tigeot 				devinfo->devs[j].playas = i;
55512a1ad637SFrançois Tigeot 			}
55522a1ad637SFrançois Tigeot 			as[i].pdevinfo = &devinfo->devs[j];
55532a1ad637SFrançois Tigeot 			for (k = 0; k < as[i].num_chans; k++) {
55542a1ad637SFrançois Tigeot 				devinfo->chans[as[i].chans[k]].pdevinfo =
55552a1ad637SFrançois Tigeot 				    &devinfo->devs[j];
55562a1ad637SFrançois Tigeot 			}
55572a1ad637SFrançois Tigeot 			devinfo->devs[j].digital = as[i].digital;
55582a1ad637SFrançois Tigeot 			break;
55592a1ad637SFrançois Tigeot 		}
55602a1ad637SFrançois Tigeot 	}
55612a1ad637SFrançois Tigeot }
55622a1ad637SFrançois Tigeot 
55632a1ad637SFrançois Tigeot static void
hdaa_create_pcms(struct hdaa_devinfo * devinfo)55642a1ad637SFrançois Tigeot hdaa_create_pcms(struct hdaa_devinfo *devinfo)
55652a1ad637SFrançois Tigeot {
55662a1ad637SFrançois Tigeot 	int i;
55672a1ad637SFrançois Tigeot 
55682a1ad637SFrançois Tigeot 	for (i = 0; i < devinfo->num_devs; i++) {
55692a1ad637SFrançois Tigeot 		struct hdaa_pcm_devinfo *pdevinfo = &devinfo->devs[i];
55702a1ad637SFrançois Tigeot 
55712a1ad637SFrançois Tigeot 		pdevinfo->dev = device_add_child(devinfo->dev, "pcm", -1);
55722a1ad637SFrançois Tigeot 		device_set_ivars(pdevinfo->dev, (void *)pdevinfo);
55732a1ad637SFrançois Tigeot 	}
55742a1ad637SFrançois Tigeot }
55752a1ad637SFrançois Tigeot 
55762a1ad637SFrançois Tigeot static void
hdaa_dump_ctls(struct hdaa_pcm_devinfo * pdevinfo,const char * banner,uint32_t flag)55772a1ad637SFrançois Tigeot hdaa_dump_ctls(struct hdaa_pcm_devinfo *pdevinfo, const char *banner, uint32_t flag)
55782a1ad637SFrançois Tigeot {
55792a1ad637SFrançois Tigeot 	struct hdaa_devinfo *devinfo = pdevinfo->devinfo;
55802a1ad637SFrançois Tigeot 	struct hdaa_audio_ctl *ctl;
55812a1ad637SFrançois Tigeot 	char buf[64];
55822a1ad637SFrançois Tigeot 	int i, j, printed = 0;
55832a1ad637SFrançois Tigeot 
55842a1ad637SFrançois Tigeot 	if (flag == 0) {
55852a1ad637SFrançois Tigeot 		flag = ~(SOUND_MASK_VOLUME | SOUND_MASK_PCM |
55862a1ad637SFrançois Tigeot 		    SOUND_MASK_CD | SOUND_MASK_LINE | SOUND_MASK_RECLEV |
55872a1ad637SFrançois Tigeot 		    SOUND_MASK_MIC | SOUND_MASK_SPEAKER | SOUND_MASK_IGAIN |
55882a1ad637SFrançois Tigeot 		    SOUND_MASK_OGAIN | SOUND_MASK_IMIX | SOUND_MASK_MONITOR);
55892a1ad637SFrançois Tigeot 	}
55902a1ad637SFrançois Tigeot 
55912a1ad637SFrançois Tigeot 	for (j = 0; j < SOUND_MIXER_NRDEVICES; j++) {
55922a1ad637SFrançois Tigeot 		if ((flag & (1 << j)) == 0)
55932a1ad637SFrançois Tigeot 			continue;
55942a1ad637SFrançois Tigeot 		i = 0;
55952a1ad637SFrançois Tigeot 		printed = 0;
55962a1ad637SFrançois Tigeot 		while ((ctl = hdaa_audio_ctl_each(devinfo, &i)) != NULL) {
55972a1ad637SFrançois Tigeot 			if (ctl->enable == 0 ||
55982a1ad637SFrançois Tigeot 			    ctl->widget->enable == 0)
55992a1ad637SFrançois Tigeot 				continue;
56002a1ad637SFrançois Tigeot 			if (!((pdevinfo->playas >= 0 &&
56012a1ad637SFrançois Tigeot 			    ctl->widget->bindas == pdevinfo->playas) ||
56022a1ad637SFrançois Tigeot 			    (pdevinfo->recas >= 0 &&
56032a1ad637SFrançois Tigeot 			    ctl->widget->bindas == pdevinfo->recas) ||
56042a1ad637SFrançois Tigeot 			    (ctl->widget->bindas == -2 && pdevinfo->index == 0)))
56052a1ad637SFrançois Tigeot 				continue;
56062a1ad637SFrançois Tigeot 			if ((ctl->ossmask & (1 << j)) == 0)
56072a1ad637SFrançois Tigeot 				continue;
56082a1ad637SFrançois Tigeot 
56092a1ad637SFrançois Tigeot 			if (printed == 0) {
56102a1ad637SFrançois Tigeot 				if (banner != NULL) {
56112a1ad637SFrançois Tigeot 					device_printf(pdevinfo->dev, "%s", banner);
56122a1ad637SFrançois Tigeot 				} else {
56132a1ad637SFrançois Tigeot 					device_printf(pdevinfo->dev, "Unknown Ctl");
56142a1ad637SFrançois Tigeot 				}
561567931cc4SFrançois Tigeot 				kprintf(" (OSS: %s)",
56162a1ad637SFrançois Tigeot 				    hdaa_audio_ctl_ossmixer_mask2allname(1 << j,
56172a1ad637SFrançois Tigeot 				    buf, sizeof(buf)));
56182a1ad637SFrançois Tigeot 				if (pdevinfo->ossmask & (1 << j)) {
561967931cc4SFrançois Tigeot 					kprintf(": %+d/%+ddB\n",
56202a1ad637SFrançois Tigeot 					    pdevinfo->minamp[j] / 4,
56212a1ad637SFrançois Tigeot 					    pdevinfo->maxamp[j] / 4);
56222a1ad637SFrançois Tigeot 				} else
562367931cc4SFrançois Tigeot 					kprintf("\n");
56242a1ad637SFrançois Tigeot 				printed = 1;
56252a1ad637SFrançois Tigeot 			}
56262a1ad637SFrançois Tigeot 			device_printf(pdevinfo->dev, "   +- ctl %2d (nid %3d %s", i,
56272a1ad637SFrançois Tigeot 				ctl->widget->nid,
56282a1ad637SFrançois Tigeot 				(ctl->ndir == HDAA_CTL_IN)?"in ":"out");
56292a1ad637SFrançois Tigeot 			if (ctl->ndir == HDAA_CTL_IN && ctl->ndir == ctl->dir)
563067931cc4SFrançois Tigeot 				kprintf(" %2d): ", ctl->index);
56312a1ad637SFrançois Tigeot 			else
563267931cc4SFrançois Tigeot 				kprintf("):    ");
56332a1ad637SFrançois Tigeot 			if (ctl->step > 0) {
563467931cc4SFrançois Tigeot 				kprintf("%+d/%+ddB (%d steps)%s\n",
56352a1ad637SFrançois Tigeot 				    MINQDB(ctl) / 4,
56362a1ad637SFrançois Tigeot 				    MAXQDB(ctl) / 4,
56372a1ad637SFrançois Tigeot 				    ctl->step + 1,
56382a1ad637SFrançois Tigeot 				    ctl->mute?" + mute":"");
56392a1ad637SFrançois Tigeot 			} else
564067931cc4SFrançois Tigeot 				kprintf("%s\n", ctl->mute?"mute":"");
56412a1ad637SFrançois Tigeot 		}
56422a1ad637SFrançois Tigeot 	}
56432a1ad637SFrançois Tigeot 	if (printed)
56442a1ad637SFrançois Tigeot 		device_printf(pdevinfo->dev, "\n");
56452a1ad637SFrançois Tigeot }
56462a1ad637SFrançois Tigeot 
56472a1ad637SFrançois Tigeot static void
hdaa_dump_audio_formats(device_t dev,uint32_t fcap,uint32_t pcmcap)56482a1ad637SFrançois Tigeot hdaa_dump_audio_formats(device_t dev, uint32_t fcap, uint32_t pcmcap)
56492a1ad637SFrançois Tigeot {
56502a1ad637SFrançois Tigeot 	uint32_t cap;
56512a1ad637SFrançois Tigeot 
56522a1ad637SFrançois Tigeot 	cap = fcap;
56532a1ad637SFrançois Tigeot 	if (cap != 0) {
56542a1ad637SFrançois Tigeot 		device_printf(dev, "     Stream cap: 0x%08x", cap);
56552a1ad637SFrançois Tigeot 		if (HDA_PARAM_SUPP_STREAM_FORMATS_AC3(cap))
565667931cc4SFrançois Tigeot 			kprintf(" AC3");
56572a1ad637SFrançois Tigeot 		if (HDA_PARAM_SUPP_STREAM_FORMATS_FLOAT32(cap))
565867931cc4SFrançois Tigeot 			kprintf(" FLOAT32");
56592a1ad637SFrançois Tigeot 		if (HDA_PARAM_SUPP_STREAM_FORMATS_PCM(cap))
566067931cc4SFrançois Tigeot 			kprintf(" PCM");
566167931cc4SFrançois Tigeot 		kprintf("\n");
56622a1ad637SFrançois Tigeot 	}
56632a1ad637SFrançois Tigeot 	cap = pcmcap;
56642a1ad637SFrançois Tigeot 	if (cap != 0) {
56652a1ad637SFrançois Tigeot 		device_printf(dev, "        PCM cap: 0x%08x", cap);
56662a1ad637SFrançois Tigeot 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_8BIT(cap))
566767931cc4SFrançois Tigeot 			kprintf(" 8");
56682a1ad637SFrançois Tigeot 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_16BIT(cap))
566967931cc4SFrançois Tigeot 			kprintf(" 16");
56702a1ad637SFrançois Tigeot 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_20BIT(cap))
567167931cc4SFrançois Tigeot 			kprintf(" 20");
56722a1ad637SFrançois Tigeot 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_24BIT(cap))
567367931cc4SFrançois Tigeot 			kprintf(" 24");
56742a1ad637SFrançois Tigeot 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_32BIT(cap))
567567931cc4SFrançois Tigeot 			kprintf(" 32");
567667931cc4SFrançois Tigeot 		kprintf(" bits,");
56772a1ad637SFrançois Tigeot 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_8KHZ(cap))
567867931cc4SFrançois Tigeot 			kprintf(" 8");
56792a1ad637SFrançois Tigeot 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_11KHZ(cap))
568067931cc4SFrançois Tigeot 			kprintf(" 11");
56812a1ad637SFrançois Tigeot 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_16KHZ(cap))
568267931cc4SFrançois Tigeot 			kprintf(" 16");
56832a1ad637SFrançois Tigeot 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_22KHZ(cap))
568467931cc4SFrançois Tigeot 			kprintf(" 22");
56852a1ad637SFrançois Tigeot 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_32KHZ(cap))
568667931cc4SFrançois Tigeot 			kprintf(" 32");
56872a1ad637SFrançois Tigeot 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_44KHZ(cap))
568867931cc4SFrançois Tigeot 			kprintf(" 44");
568967931cc4SFrançois Tigeot 		kprintf(" 48");
56902a1ad637SFrançois Tigeot 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_88KHZ(cap))
569167931cc4SFrançois Tigeot 			kprintf(" 88");
56922a1ad637SFrançois Tigeot 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_96KHZ(cap))
569367931cc4SFrançois Tigeot 			kprintf(" 96");
56942a1ad637SFrançois Tigeot 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_176KHZ(cap))
569567931cc4SFrançois Tigeot 			kprintf(" 176");
56962a1ad637SFrançois Tigeot 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_192KHZ(cap))
569767931cc4SFrançois Tigeot 			kprintf(" 192");
569867931cc4SFrançois Tigeot 		kprintf(" KHz\n");
56992a1ad637SFrançois Tigeot 	}
57002a1ad637SFrançois Tigeot }
57012a1ad637SFrançois Tigeot 
57022a1ad637SFrançois Tigeot static void
hdaa_dump_pin(struct hdaa_widget * w)57032a1ad637SFrançois Tigeot hdaa_dump_pin(struct hdaa_widget *w)
57042a1ad637SFrançois Tigeot {
57052a1ad637SFrançois Tigeot 	uint32_t pincap;
57062a1ad637SFrançois Tigeot 
57072a1ad637SFrançois Tigeot 	pincap = w->wclass.pin.cap;
57082a1ad637SFrançois Tigeot 
57092a1ad637SFrançois Tigeot 	device_printf(w->devinfo->dev, "        Pin cap: 0x%08x", pincap);
57102a1ad637SFrançois Tigeot 	if (HDA_PARAM_PIN_CAP_IMP_SENSE_CAP(pincap))
571167931cc4SFrançois Tigeot 		kprintf(" ISC");
57122a1ad637SFrançois Tigeot 	if (HDA_PARAM_PIN_CAP_TRIGGER_REQD(pincap))
571367931cc4SFrançois Tigeot 		kprintf(" TRQD");
57142a1ad637SFrançois Tigeot 	if (HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP(pincap))
571567931cc4SFrançois Tigeot 		kprintf(" PDC");
57162a1ad637SFrançois Tigeot 	if (HDA_PARAM_PIN_CAP_HEADPHONE_CAP(pincap))
571767931cc4SFrançois Tigeot 		kprintf(" HP");
57182a1ad637SFrançois Tigeot 	if (HDA_PARAM_PIN_CAP_OUTPUT_CAP(pincap))
571967931cc4SFrançois Tigeot 		kprintf(" OUT");
57202a1ad637SFrançois Tigeot 	if (HDA_PARAM_PIN_CAP_INPUT_CAP(pincap))
572167931cc4SFrançois Tigeot 		kprintf(" IN");
57222a1ad637SFrançois Tigeot 	if (HDA_PARAM_PIN_CAP_BALANCED_IO_PINS(pincap))
572367931cc4SFrançois Tigeot 		kprintf(" BAL");
57242a1ad637SFrançois Tigeot 	if (HDA_PARAM_PIN_CAP_HDMI(pincap))
572567931cc4SFrançois Tigeot 		kprintf(" HDMI");
57262a1ad637SFrançois Tigeot 	if (HDA_PARAM_PIN_CAP_VREF_CTRL(pincap)) {
572767931cc4SFrançois Tigeot 		kprintf(" VREF[");
57282a1ad637SFrançois Tigeot 		if (HDA_PARAM_PIN_CAP_VREF_CTRL_50(pincap))
572967931cc4SFrançois Tigeot 			kprintf(" 50");
57302a1ad637SFrançois Tigeot 		if (HDA_PARAM_PIN_CAP_VREF_CTRL_80(pincap))
573167931cc4SFrançois Tigeot 			kprintf(" 80");
57322a1ad637SFrançois Tigeot 		if (HDA_PARAM_PIN_CAP_VREF_CTRL_100(pincap))
573367931cc4SFrançois Tigeot 			kprintf(" 100");
57342a1ad637SFrançois Tigeot 		if (HDA_PARAM_PIN_CAP_VREF_CTRL_GROUND(pincap))
573567931cc4SFrançois Tigeot 			kprintf(" GROUND");
57362a1ad637SFrançois Tigeot 		if (HDA_PARAM_PIN_CAP_VREF_CTRL_HIZ(pincap))
573767931cc4SFrançois Tigeot 			kprintf(" HIZ");
573867931cc4SFrançois Tigeot 		kprintf(" ]");
57392a1ad637SFrançois Tigeot 	}
57402a1ad637SFrançois Tigeot 	if (HDA_PARAM_PIN_CAP_EAPD_CAP(pincap))
574167931cc4SFrançois Tigeot 		kprintf(" EAPD");
57422a1ad637SFrançois Tigeot 	if (HDA_PARAM_PIN_CAP_DP(pincap))
574367931cc4SFrançois Tigeot 		kprintf(" DP");
57442a1ad637SFrançois Tigeot 	if (HDA_PARAM_PIN_CAP_HBR(pincap))
574567931cc4SFrançois Tigeot 		kprintf(" HBR");
574667931cc4SFrançois Tigeot 	kprintf("\n");
57472a1ad637SFrançois Tigeot 	device_printf(w->devinfo->dev, "     Pin config: 0x%08x\n",
57482a1ad637SFrançois Tigeot 	    w->wclass.pin.config);
57492a1ad637SFrançois Tigeot 	device_printf(w->devinfo->dev, "    Pin control: 0x%08x", w->wclass.pin.ctrl);
57502a1ad637SFrançois Tigeot 	if (w->wclass.pin.ctrl & HDA_CMD_SET_PIN_WIDGET_CTRL_HPHN_ENABLE)
575167931cc4SFrançois Tigeot 		kprintf(" HP");
57522a1ad637SFrançois Tigeot 	if (w->wclass.pin.ctrl & HDA_CMD_SET_PIN_WIDGET_CTRL_IN_ENABLE)
575367931cc4SFrançois Tigeot 		kprintf(" IN");
57542a1ad637SFrançois Tigeot 	if (w->wclass.pin.ctrl & HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE)
575567931cc4SFrançois Tigeot 		kprintf(" OUT");
57562a1ad637SFrançois Tigeot 	if (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap)) {
57572a1ad637SFrançois Tigeot 		if ((w->wclass.pin.ctrl &
57582a1ad637SFrançois Tigeot 		    HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE_MASK) == 0x03)
575967931cc4SFrançois Tigeot 			kprintf(" HBR");
57602a1ad637SFrançois Tigeot 		else if ((w->wclass.pin.ctrl &
57612a1ad637SFrançois Tigeot 		    HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE_MASK) != 0)
576267931cc4SFrançois Tigeot 			kprintf(" EPTs");
57632a1ad637SFrançois Tigeot 	} else {
57642a1ad637SFrançois Tigeot 		if ((w->wclass.pin.ctrl &
57652a1ad637SFrançois Tigeot 		    HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE_MASK) != 0)
576667931cc4SFrançois Tigeot 			kprintf(" VREFs");
57672a1ad637SFrançois Tigeot 	}
576867931cc4SFrançois Tigeot 	kprintf("\n");
57692a1ad637SFrançois Tigeot }
57702a1ad637SFrançois Tigeot 
57712a1ad637SFrançois Tigeot static void
hdaa_dump_pin_config(struct hdaa_widget * w,uint32_t conf)57722a1ad637SFrançois Tigeot hdaa_dump_pin_config(struct hdaa_widget *w, uint32_t conf)
57732a1ad637SFrançois Tigeot {
57742a1ad637SFrançois Tigeot 
57752a1ad637SFrançois Tigeot 	device_printf(w->devinfo->dev, "%2d %08x %-2d %-2d "
57762a1ad637SFrançois Tigeot 	    "%-13s %-5s %-7s %-10s %-7s %d%s\n",
57772a1ad637SFrançois Tigeot 	    w->nid, conf,
57782a1ad637SFrançois Tigeot 	    HDA_CONFIG_DEFAULTCONF_ASSOCIATION(conf),
57792a1ad637SFrançois Tigeot 	    HDA_CONFIG_DEFAULTCONF_SEQUENCE(conf),
57802a1ad637SFrançois Tigeot 	    HDA_DEVS[HDA_CONFIG_DEFAULTCONF_DEVICE(conf)],
57812a1ad637SFrançois Tigeot 	    HDA_CONNS[HDA_CONFIG_DEFAULTCONF_CONNECTIVITY(conf)],
57822a1ad637SFrançois Tigeot 	    HDA_CONNECTORS[HDA_CONFIG_DEFAULTCONF_CONNECTION_TYPE(conf)],
57832a1ad637SFrançois Tigeot 	    HDA_LOCS[HDA_CONFIG_DEFAULTCONF_LOCATION(conf)],
57842a1ad637SFrançois Tigeot 	    HDA_COLORS[HDA_CONFIG_DEFAULTCONF_COLOR(conf)],
57852a1ad637SFrançois Tigeot 	    HDA_CONFIG_DEFAULTCONF_MISC(conf),
57862a1ad637SFrançois Tigeot 	    (w->enable == 0)?" DISA":"");
57872a1ad637SFrançois Tigeot }
57882a1ad637SFrançois Tigeot 
57892a1ad637SFrançois Tigeot static void
hdaa_dump_pin_configs(struct hdaa_devinfo * devinfo)57902a1ad637SFrançois Tigeot hdaa_dump_pin_configs(struct hdaa_devinfo *devinfo)
57912a1ad637SFrançois Tigeot {
57922a1ad637SFrançois Tigeot 	struct hdaa_widget *w;
57932a1ad637SFrançois Tigeot 	int i;
57942a1ad637SFrançois Tigeot 
57952a1ad637SFrançois Tigeot 	device_printf(devinfo->dev, "nid   0x    as seq "
57962a1ad637SFrançois Tigeot 	    "device       conn  jack    loc        color   misc\n");
57972a1ad637SFrançois Tigeot 	for (i = devinfo->startnode; i < devinfo->endnode; i++) {
57982a1ad637SFrançois Tigeot 		w = hdaa_widget_get(devinfo, i);
57992a1ad637SFrançois Tigeot 		if (w == NULL)
58002a1ad637SFrançois Tigeot 			continue;
58012a1ad637SFrançois Tigeot 		if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX)
58022a1ad637SFrançois Tigeot 			continue;
58032a1ad637SFrançois Tigeot 		hdaa_dump_pin_config(w, w->wclass.pin.config);
58042a1ad637SFrançois Tigeot 	}
58052a1ad637SFrançois Tigeot }
58062a1ad637SFrançois Tigeot 
58072a1ad637SFrançois Tigeot static void
hdaa_dump_amp(device_t dev,uint32_t cap,const char * banner)58082a1ad637SFrançois Tigeot hdaa_dump_amp(device_t dev, uint32_t cap, const char *banner)
58092a1ad637SFrançois Tigeot {
58102a1ad637SFrançois Tigeot 	int offset, size, step;
58112a1ad637SFrançois Tigeot 
58122a1ad637SFrançois Tigeot 	offset = HDA_PARAM_OUTPUT_AMP_CAP_OFFSET(cap);
58132a1ad637SFrançois Tigeot 	size = HDA_PARAM_OUTPUT_AMP_CAP_STEPSIZE(cap);
58142a1ad637SFrançois Tigeot 	step = HDA_PARAM_OUTPUT_AMP_CAP_NUMSTEPS(cap);
58152a1ad637SFrançois Tigeot 	device_printf(dev, "     %s amp: 0x%08x "
58162a1ad637SFrançois Tigeot 	    "mute=%d step=%d size=%d offset=%d (%+d/%+ddB)\n",
58172a1ad637SFrançois Tigeot 	    banner, cap,
58182a1ad637SFrançois Tigeot 	    HDA_PARAM_OUTPUT_AMP_CAP_MUTE_CAP(cap),
58192a1ad637SFrançois Tigeot 	    step, size, offset,
58202a1ad637SFrançois Tigeot 	    ((0 - offset) * (size + 1)) / 4,
58212a1ad637SFrançois Tigeot 	    ((step - offset) * (size + 1)) / 4);
58222a1ad637SFrançois Tigeot }
58232a1ad637SFrançois Tigeot 
58242a1ad637SFrançois Tigeot static void
hdaa_dump_nodes(struct hdaa_devinfo * devinfo)58252a1ad637SFrançois Tigeot hdaa_dump_nodes(struct hdaa_devinfo *devinfo)
58262a1ad637SFrançois Tigeot {
58272a1ad637SFrançois Tigeot 	struct hdaa_widget *w, *cw;
58282a1ad637SFrançois Tigeot 	char buf[64];
58292a1ad637SFrançois Tigeot 	int i, j;
58302a1ad637SFrançois Tigeot 
58312a1ad637SFrançois Tigeot 	device_printf(devinfo->dev, "\n");
58322a1ad637SFrançois Tigeot 	device_printf(devinfo->dev, "Default parameters:\n");
58332a1ad637SFrançois Tigeot 	hdaa_dump_audio_formats(devinfo->dev,
58342a1ad637SFrançois Tigeot 	    devinfo->supp_stream_formats,
58352a1ad637SFrançois Tigeot 	    devinfo->supp_pcm_size_rate);
58362a1ad637SFrançois Tigeot 	hdaa_dump_amp(devinfo->dev, devinfo->inamp_cap, " Input");
58372a1ad637SFrançois Tigeot 	hdaa_dump_amp(devinfo->dev, devinfo->outamp_cap, "Output");
58382a1ad637SFrançois Tigeot 	for (i = devinfo->startnode; i < devinfo->endnode; i++) {
58392a1ad637SFrançois Tigeot 		w = hdaa_widget_get(devinfo, i);
58402a1ad637SFrançois Tigeot 		if (w == NULL) {
58412a1ad637SFrançois Tigeot 			device_printf(devinfo->dev, "Ghost widget nid=%d\n", i);
58422a1ad637SFrançois Tigeot 			continue;
58432a1ad637SFrançois Tigeot 		}
58442a1ad637SFrançois Tigeot 		device_printf(devinfo->dev, "\n");
58452a1ad637SFrançois Tigeot 		device_printf(devinfo->dev, "            nid: %d%s\n", w->nid,
58462a1ad637SFrançois Tigeot 		    (w->enable == 0) ? " [DISABLED]" : "");
58472a1ad637SFrançois Tigeot 		device_printf(devinfo->dev, "           Name: %s\n", w->name);
58482a1ad637SFrançois Tigeot 		device_printf(devinfo->dev, "     Widget cap: 0x%08x",
58492a1ad637SFrançois Tigeot 		    w->param.widget_cap);
58502a1ad637SFrançois Tigeot 		if (w->param.widget_cap & 0x0ee1) {
58512a1ad637SFrançois Tigeot 			if (HDA_PARAM_AUDIO_WIDGET_CAP_LR_SWAP(w->param.widget_cap))
585267931cc4SFrançois Tigeot 			    kprintf(" LRSWAP");
58532a1ad637SFrançois Tigeot 			if (HDA_PARAM_AUDIO_WIDGET_CAP_POWER_CTRL(w->param.widget_cap))
585467931cc4SFrançois Tigeot 			    kprintf(" PWR");
58552a1ad637SFrançois Tigeot 			if (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap))
585667931cc4SFrançois Tigeot 			    kprintf(" DIGITAL");
58572a1ad637SFrançois Tigeot 			if (HDA_PARAM_AUDIO_WIDGET_CAP_UNSOL_CAP(w->param.widget_cap))
585867931cc4SFrançois Tigeot 			    kprintf(" UNSOL");
58592a1ad637SFrançois Tigeot 			if (HDA_PARAM_AUDIO_WIDGET_CAP_PROC_WIDGET(w->param.widget_cap))
586067931cc4SFrançois Tigeot 			    kprintf(" PROC");
58612a1ad637SFrançois Tigeot 			if (HDA_PARAM_AUDIO_WIDGET_CAP_STRIPE(w->param.widget_cap))
586267931cc4SFrançois Tigeot 			    kprintf(" STRIPE(x%d)",
58632a1ad637SFrançois Tigeot 				1 << (fls(w->wclass.conv.stripecap) - 1));
58642a1ad637SFrançois Tigeot 			j = HDA_PARAM_AUDIO_WIDGET_CAP_CC(w->param.widget_cap);
58652a1ad637SFrançois Tigeot 			if (j == 1)
586667931cc4SFrançois Tigeot 			    kprintf(" STEREO");
58672a1ad637SFrançois Tigeot 			else if (j > 1)
586867931cc4SFrançois Tigeot 			    kprintf(" %dCH", j + 1);
58692a1ad637SFrançois Tigeot 		}
587067931cc4SFrançois Tigeot 		kprintf("\n");
58712a1ad637SFrançois Tigeot 		if (w->bindas != -1) {
58722a1ad637SFrançois Tigeot 			device_printf(devinfo->dev, "    Association: %d (0x%04x)\n",
58732a1ad637SFrançois Tigeot 			    w->bindas, w->bindseqmask);
58742a1ad637SFrançois Tigeot 		}
58752a1ad637SFrançois Tigeot 		if (w->ossmask != 0 || w->ossdev >= 0) {
58762a1ad637SFrançois Tigeot 			device_printf(devinfo->dev, "            OSS: %s",
58772a1ad637SFrançois Tigeot 			    hdaa_audio_ctl_ossmixer_mask2allname(w->ossmask, buf, sizeof(buf)));
58782a1ad637SFrançois Tigeot 			if (w->ossdev >= 0)
587967931cc4SFrançois Tigeot 			    kprintf(" (%s)", ossnames[w->ossdev]);
588067931cc4SFrançois Tigeot 			kprintf("\n");
58812a1ad637SFrançois Tigeot 		}
58822a1ad637SFrançois Tigeot 		if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT ||
58832a1ad637SFrançois Tigeot 		    w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT) {
58842a1ad637SFrançois Tigeot 			hdaa_dump_audio_formats(devinfo->dev,
58852a1ad637SFrançois Tigeot 			    w->param.supp_stream_formats,
58862a1ad637SFrançois Tigeot 			    w->param.supp_pcm_size_rate);
58872a1ad637SFrançois Tigeot 		} else if (w->type ==
58882a1ad637SFrançois Tigeot 		    HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX || w->waspin)
58892a1ad637SFrançois Tigeot 			hdaa_dump_pin(w);
58902a1ad637SFrançois Tigeot 		if (w->param.eapdbtl != HDA_INVALID)
58912a1ad637SFrançois Tigeot 			device_printf(devinfo->dev, "           EAPD: 0x%08x\n",
58922a1ad637SFrançois Tigeot 			    w->param.eapdbtl);
58932a1ad637SFrançois Tigeot 		if (HDA_PARAM_AUDIO_WIDGET_CAP_OUT_AMP(w->param.widget_cap) &&
58942a1ad637SFrançois Tigeot 		    w->param.outamp_cap != 0)
58952a1ad637SFrançois Tigeot 			hdaa_dump_amp(devinfo->dev, w->param.outamp_cap, "Output");
58962a1ad637SFrançois Tigeot 		if (HDA_PARAM_AUDIO_WIDGET_CAP_IN_AMP(w->param.widget_cap) &&
58972a1ad637SFrançois Tigeot 		    w->param.inamp_cap != 0)
58982a1ad637SFrançois Tigeot 			hdaa_dump_amp(devinfo->dev, w->param.inamp_cap, " Input");
58992a1ad637SFrançois Tigeot 		if (w->nconns > 0)
59002a1ad637SFrançois Tigeot 			device_printf(devinfo->dev, "    Connections: %d\n", w->nconns);
59012a1ad637SFrançois Tigeot 		for (j = 0; j < w->nconns; j++) {
59022a1ad637SFrançois Tigeot 			cw = hdaa_widget_get(devinfo, w->conns[j]);
59032a1ad637SFrançois Tigeot 			device_printf(devinfo->dev, "          + %s<- nid=%d [%s]",
59042a1ad637SFrançois Tigeot 			    (w->connsenable[j] == 0)?"[DISABLED] ":"",
59052a1ad637SFrançois Tigeot 			    w->conns[j], (cw == NULL) ? "GHOST!" : cw->name);
59062a1ad637SFrançois Tigeot 			if (cw == NULL)
590767931cc4SFrançois Tigeot 				kprintf(" [UNKNOWN]");
59082a1ad637SFrançois Tigeot 			else if (cw->enable == 0)
590967931cc4SFrançois Tigeot 				kprintf(" [DISABLED]");
59102a1ad637SFrançois Tigeot 			if (w->nconns > 1 && w->selconn == j && w->type !=
59112a1ad637SFrançois Tigeot 			    HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER)
591267931cc4SFrançois Tigeot 				kprintf(" (selected)");
591367931cc4SFrançois Tigeot 			kprintf("\n");
59142a1ad637SFrançois Tigeot 		}
59152a1ad637SFrançois Tigeot 	}
59162a1ad637SFrançois Tigeot 
59172a1ad637SFrançois Tigeot }
59182a1ad637SFrançois Tigeot 
59192a1ad637SFrançois Tigeot static void
hdaa_dump_dst_nid(struct hdaa_pcm_devinfo * pdevinfo,nid_t nid,int depth)59202a1ad637SFrançois Tigeot hdaa_dump_dst_nid(struct hdaa_pcm_devinfo *pdevinfo, nid_t nid, int depth)
59212a1ad637SFrançois Tigeot {
59222a1ad637SFrançois Tigeot 	struct hdaa_devinfo *devinfo = pdevinfo->devinfo;
59232a1ad637SFrançois Tigeot 	struct hdaa_widget *w, *cw;
59242a1ad637SFrançois Tigeot 	char buf[64];
59252a1ad637SFrançois Tigeot 	int i;
59262a1ad637SFrançois Tigeot 
59272a1ad637SFrançois Tigeot 	if (depth > HDA_PARSE_MAXDEPTH)
59282a1ad637SFrançois Tigeot 		return;
59292a1ad637SFrançois Tigeot 
59302a1ad637SFrançois Tigeot 	w = hdaa_widget_get(devinfo, nid);
59312a1ad637SFrançois Tigeot 	if (w == NULL || w->enable == 0)
59322a1ad637SFrançois Tigeot 		return;
59332a1ad637SFrançois Tigeot 
59342a1ad637SFrançois Tigeot 	if (depth == 0)
59352a1ad637SFrançois Tigeot 		device_printf(pdevinfo->dev, "%*s", 4, "");
59362a1ad637SFrançois Tigeot 	else
59372a1ad637SFrançois Tigeot 		device_printf(pdevinfo->dev, "%*s  + <- ", 4 + (depth - 1) * 7, "");
593867931cc4SFrançois Tigeot 	kprintf("nid=%d [%s]", w->nid, w->name);
59392a1ad637SFrançois Tigeot 
59402a1ad637SFrançois Tigeot 	if (depth > 0) {
59412a1ad637SFrançois Tigeot 		if (w->ossmask == 0) {
594267931cc4SFrançois Tigeot 			kprintf("\n");
59432a1ad637SFrançois Tigeot 			return;
59442a1ad637SFrançois Tigeot 		}
594567931cc4SFrançois Tigeot 		kprintf(" [src: %s]",
59462a1ad637SFrançois Tigeot 		    hdaa_audio_ctl_ossmixer_mask2allname(
59472a1ad637SFrançois Tigeot 			w->ossmask, buf, sizeof(buf)));
59482a1ad637SFrançois Tigeot 		if (w->ossdev >= 0) {
594967931cc4SFrançois Tigeot 			kprintf("\n");
59502a1ad637SFrançois Tigeot 			return;
59512a1ad637SFrançois Tigeot 		}
59522a1ad637SFrançois Tigeot 	}
595367931cc4SFrançois Tigeot 	kprintf("\n");
59542a1ad637SFrançois Tigeot 
59552a1ad637SFrançois Tigeot 	for (i = 0; i < w->nconns; i++) {
59562a1ad637SFrançois Tigeot 		if (w->connsenable[i] == 0)
59572a1ad637SFrançois Tigeot 			continue;
59582a1ad637SFrançois Tigeot 		cw = hdaa_widget_get(devinfo, w->conns[i]);
59592a1ad637SFrançois Tigeot 		if (cw == NULL || cw->enable == 0 || cw->bindas == -1)
59602a1ad637SFrançois Tigeot 			continue;
59612a1ad637SFrançois Tigeot 		hdaa_dump_dst_nid(pdevinfo, w->conns[i], depth + 1);
59622a1ad637SFrançois Tigeot 	}
59632a1ad637SFrançois Tigeot 
59642a1ad637SFrançois Tigeot }
59652a1ad637SFrançois Tigeot 
59662a1ad637SFrançois Tigeot static void
hdaa_dump_dac(struct hdaa_pcm_devinfo * pdevinfo)59672a1ad637SFrançois Tigeot hdaa_dump_dac(struct hdaa_pcm_devinfo *pdevinfo)
59682a1ad637SFrançois Tigeot {
59692a1ad637SFrançois Tigeot 	struct hdaa_devinfo *devinfo = pdevinfo->devinfo;
59702a1ad637SFrançois Tigeot 	struct hdaa_audio_as *as;
59712a1ad637SFrançois Tigeot 	struct hdaa_widget *w;
59722a1ad637SFrançois Tigeot 	nid_t *nids;
59732a1ad637SFrançois Tigeot 	int chid, i;
59742a1ad637SFrançois Tigeot 
59752a1ad637SFrançois Tigeot 	if (pdevinfo->playas < 0)
59762a1ad637SFrançois Tigeot 		return;
59772a1ad637SFrançois Tigeot 
59782a1ad637SFrançois Tigeot 	device_printf(pdevinfo->dev, "Playback:\n");
59792a1ad637SFrançois Tigeot 
59802a1ad637SFrançois Tigeot 	chid = devinfo->as[pdevinfo->playas].chans[0];
59812a1ad637SFrançois Tigeot 	hdaa_dump_audio_formats(pdevinfo->dev,
59822a1ad637SFrançois Tigeot 	    devinfo->chans[chid].supp_stream_formats,
59832a1ad637SFrançois Tigeot 	    devinfo->chans[chid].supp_pcm_size_rate);
59842a1ad637SFrançois Tigeot 	for (i = 0; i < devinfo->as[pdevinfo->playas].num_chans; i++) {
59852a1ad637SFrançois Tigeot 		chid = devinfo->as[pdevinfo->playas].chans[i];
59862a1ad637SFrançois Tigeot 		device_printf(pdevinfo->dev, "            DAC:");
59872a1ad637SFrançois Tigeot 		for (nids = devinfo->chans[chid].io; *nids != -1; nids++)
598867931cc4SFrançois Tigeot 			kprintf(" %d", *nids);
598967931cc4SFrançois Tigeot 		kprintf("\n");
59902a1ad637SFrançois Tigeot 	}
59912a1ad637SFrançois Tigeot 
59922a1ad637SFrançois Tigeot 	as = &devinfo->as[pdevinfo->playas];
59932a1ad637SFrançois Tigeot 	for (i = 0; i < 16; i++) {
59942a1ad637SFrançois Tigeot 		if (as->pins[i] <= 0)
59952a1ad637SFrançois Tigeot 			continue;
59962a1ad637SFrançois Tigeot 		w = hdaa_widget_get(devinfo, as->pins[i]);
59972a1ad637SFrançois Tigeot 		if (w == NULL || w->enable == 0)
59982a1ad637SFrançois Tigeot 			continue;
59992a1ad637SFrançois Tigeot 		device_printf(pdevinfo->dev, "\n");
60002a1ad637SFrançois Tigeot 		hdaa_dump_dst_nid(pdevinfo, as->pins[i], 0);
60012a1ad637SFrançois Tigeot 	}
60022a1ad637SFrançois Tigeot 	device_printf(pdevinfo->dev, "\n");
60032a1ad637SFrançois Tigeot }
60042a1ad637SFrançois Tigeot 
60052a1ad637SFrançois Tigeot static void
hdaa_dump_adc(struct hdaa_pcm_devinfo * pdevinfo)60062a1ad637SFrançois Tigeot hdaa_dump_adc(struct hdaa_pcm_devinfo *pdevinfo)
60072a1ad637SFrançois Tigeot {
60082a1ad637SFrançois Tigeot 	struct hdaa_devinfo *devinfo = pdevinfo->devinfo;
60092a1ad637SFrançois Tigeot 	struct hdaa_widget *w;
60102a1ad637SFrançois Tigeot 	nid_t *nids;
60112a1ad637SFrançois Tigeot 	int chid, i;
60122a1ad637SFrançois Tigeot 
60132a1ad637SFrançois Tigeot 	if (pdevinfo->recas < 0)
60142a1ad637SFrançois Tigeot 		return;
60152a1ad637SFrançois Tigeot 
60162a1ad637SFrançois Tigeot 	device_printf(pdevinfo->dev, "Record:\n");
60172a1ad637SFrançois Tigeot 
60182a1ad637SFrançois Tigeot 	chid = devinfo->as[pdevinfo->recas].chans[0];
60192a1ad637SFrançois Tigeot 	hdaa_dump_audio_formats(pdevinfo->dev,
60202a1ad637SFrançois Tigeot 	    devinfo->chans[chid].supp_stream_formats,
60212a1ad637SFrançois Tigeot 	    devinfo->chans[chid].supp_pcm_size_rate);
60222a1ad637SFrançois Tigeot 	for (i = 0; i < devinfo->as[pdevinfo->recas].num_chans; i++) {
60232a1ad637SFrançois Tigeot 		chid = devinfo->as[pdevinfo->recas].chans[i];
60242a1ad637SFrançois Tigeot 		device_printf(pdevinfo->dev, "            ADC:");
60252a1ad637SFrançois Tigeot 		for (nids = devinfo->chans[chid].io; *nids != -1; nids++)
602667931cc4SFrançois Tigeot 			kprintf(" %d", *nids);
602767931cc4SFrançois Tigeot 		kprintf("\n");
60282a1ad637SFrançois Tigeot 	}
60292a1ad637SFrançois Tigeot 
60302a1ad637SFrançois Tigeot 	for (i = devinfo->startnode; i < devinfo->endnode; i++) {
60312a1ad637SFrançois Tigeot 		w = hdaa_widget_get(devinfo, i);
60322a1ad637SFrançois Tigeot 		if (w == NULL || w->enable == 0)
60332a1ad637SFrançois Tigeot 			continue;
60342a1ad637SFrançois Tigeot 		if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT)
60352a1ad637SFrançois Tigeot 			continue;
60362a1ad637SFrançois Tigeot 		if (w->bindas != pdevinfo->recas)
60372a1ad637SFrançois Tigeot 			continue;
60382a1ad637SFrançois Tigeot 		device_printf(pdevinfo->dev, "\n");
60392a1ad637SFrançois Tigeot 		hdaa_dump_dst_nid(pdevinfo, i, 0);
60402a1ad637SFrançois Tigeot 	}
60412a1ad637SFrançois Tigeot 	device_printf(pdevinfo->dev, "\n");
60422a1ad637SFrançois Tigeot }
60432a1ad637SFrançois Tigeot 
60442a1ad637SFrançois Tigeot static void
hdaa_dump_mix(struct hdaa_pcm_devinfo * pdevinfo)60452a1ad637SFrançois Tigeot hdaa_dump_mix(struct hdaa_pcm_devinfo *pdevinfo)
60462a1ad637SFrançois Tigeot {
60472a1ad637SFrançois Tigeot 	struct hdaa_devinfo *devinfo = pdevinfo->devinfo;
60482a1ad637SFrançois Tigeot 	struct hdaa_widget *w;
60492a1ad637SFrançois Tigeot 	int i;
60502a1ad637SFrançois Tigeot 	int printed = 0;
60512a1ad637SFrançois Tigeot 
60522a1ad637SFrançois Tigeot 	for (i = devinfo->startnode; i < devinfo->endnode; i++) {
60532a1ad637SFrançois Tigeot 		w = hdaa_widget_get(devinfo, i);
60542a1ad637SFrançois Tigeot 		if (w == NULL || w->enable == 0)
60552a1ad637SFrançois Tigeot 			continue;
60562a1ad637SFrançois Tigeot 		if (w->ossdev != SOUND_MIXER_IMIX)
60572a1ad637SFrançois Tigeot 			continue;
60582a1ad637SFrançois Tigeot 		if (w->bindas != pdevinfo->recas)
60592a1ad637SFrançois Tigeot 			continue;
60602a1ad637SFrançois Tigeot 		if (printed == 0) {
60612a1ad637SFrançois Tigeot 			printed = 1;
60622a1ad637SFrançois Tigeot 			device_printf(pdevinfo->dev, "Input Mix:\n");
60632a1ad637SFrançois Tigeot 		}
60642a1ad637SFrançois Tigeot 		device_printf(pdevinfo->dev, "\n");
60652a1ad637SFrançois Tigeot 		hdaa_dump_dst_nid(pdevinfo, i, 0);
60662a1ad637SFrançois Tigeot 	}
60672a1ad637SFrançois Tigeot 	if (printed)
60682a1ad637SFrançois Tigeot 		device_printf(pdevinfo->dev, "\n");
60692a1ad637SFrançois Tigeot }
60702a1ad637SFrançois Tigeot 
60712a1ad637SFrançois Tigeot static void
hdaa_pindump(device_t dev)60722a1ad637SFrançois Tigeot hdaa_pindump(device_t dev)
60732a1ad637SFrançois Tigeot {
60742a1ad637SFrançois Tigeot 	struct hdaa_devinfo *devinfo = device_get_softc(dev);
60752a1ad637SFrançois Tigeot 	struct hdaa_widget *w;
60762a1ad637SFrançois Tigeot 	uint32_t res, pincap, delay;
60772a1ad637SFrançois Tigeot 	int i;
60782a1ad637SFrançois Tigeot 
60792a1ad637SFrançois Tigeot 	device_printf(dev, "Dumping AFG pins:\n");
60802a1ad637SFrançois Tigeot 	device_printf(dev, "nid   0x    as seq "
60812a1ad637SFrançois Tigeot 	    "device       conn  jack    loc        color   misc\n");
60822a1ad637SFrançois Tigeot 	for (i = devinfo->startnode; i < devinfo->endnode; i++) {
60832a1ad637SFrançois Tigeot 		w = hdaa_widget_get(devinfo, i);
60842a1ad637SFrançois Tigeot 		if (w == NULL || w->type !=
60852a1ad637SFrançois Tigeot 		    HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX)
60862a1ad637SFrançois Tigeot 			continue;
60872a1ad637SFrançois Tigeot 		hdaa_dump_pin_config(w, w->wclass.pin.config);
60882a1ad637SFrançois Tigeot 		pincap = w->wclass.pin.cap;
60892a1ad637SFrançois Tigeot 		device_printf(dev, "    Caps: %2s %3s %2s %4s %4s",
60902a1ad637SFrançois Tigeot 		    HDA_PARAM_PIN_CAP_INPUT_CAP(pincap)?"IN":"",
60912a1ad637SFrançois Tigeot 		    HDA_PARAM_PIN_CAP_OUTPUT_CAP(pincap)?"OUT":"",
60922a1ad637SFrançois Tigeot 		    HDA_PARAM_PIN_CAP_HEADPHONE_CAP(pincap)?"HP":"",
60932a1ad637SFrançois Tigeot 		    HDA_PARAM_PIN_CAP_EAPD_CAP(pincap)?"EAPD":"",
60942a1ad637SFrançois Tigeot 		    HDA_PARAM_PIN_CAP_VREF_CTRL(pincap)?"VREF":"");
60952a1ad637SFrançois Tigeot 		if (HDA_PARAM_PIN_CAP_IMP_SENSE_CAP(pincap) ||
60962a1ad637SFrançois Tigeot 		    HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP(pincap)) {
60972a1ad637SFrançois Tigeot 			if (HDA_PARAM_PIN_CAP_TRIGGER_REQD(pincap)) {
60982a1ad637SFrançois Tigeot 				delay = 0;
60992a1ad637SFrançois Tigeot 				hda_command(dev,
61002a1ad637SFrançois Tigeot 				    HDA_CMD_SET_PIN_SENSE(0, w->nid, 0));
61012a1ad637SFrançois Tigeot 				do {
61022a1ad637SFrançois Tigeot 					res = hda_command(dev,
61032a1ad637SFrançois Tigeot 					    HDA_CMD_GET_PIN_SENSE(0, w->nid));
61042a1ad637SFrançois Tigeot 					if (res != 0x7fffffff && res != 0xffffffff)
61052a1ad637SFrançois Tigeot 						break;
61062a1ad637SFrançois Tigeot 					DELAY(10);
61072a1ad637SFrançois Tigeot 				} while (++delay < 10000);
61082a1ad637SFrançois Tigeot 			} else {
61092a1ad637SFrançois Tigeot 				delay = 0;
61102a1ad637SFrançois Tigeot 				res = hda_command(dev, HDA_CMD_GET_PIN_SENSE(0,
61112a1ad637SFrançois Tigeot 				    w->nid));
61122a1ad637SFrançois Tigeot 			}
611367931cc4SFrançois Tigeot 			kprintf(" Sense: 0x%08x (%sconnected%s)", res,
61142a1ad637SFrançois Tigeot 			    (res & HDA_CMD_GET_PIN_SENSE_PRESENCE_DETECT) ?
61152a1ad637SFrançois Tigeot 			     "" : "dis",
61162a1ad637SFrançois Tigeot 			    (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap) &&
61172a1ad637SFrançois Tigeot 			     (res & HDA_CMD_GET_PIN_SENSE_ELD_VALID)) ?
61182a1ad637SFrançois Tigeot 			      ", ELD valid" : "");
61192a1ad637SFrançois Tigeot 			if (delay > 0)
612067931cc4SFrançois Tigeot 				kprintf(" delay %dus", delay * 10);
61212a1ad637SFrançois Tigeot 		}
612267931cc4SFrançois Tigeot 		kprintf("\n");
61232a1ad637SFrançois Tigeot 	}
61242a1ad637SFrançois Tigeot 	device_printf(dev,
61252a1ad637SFrançois Tigeot 	    "NumGPIO=%d NumGPO=%d NumGPI=%d GPIWake=%d GPIUnsol=%d\n",
61262a1ad637SFrançois Tigeot 	    HDA_PARAM_GPIO_COUNT_NUM_GPIO(devinfo->gpio_cap),
61272a1ad637SFrançois Tigeot 	    HDA_PARAM_GPIO_COUNT_NUM_GPO(devinfo->gpio_cap),
61282a1ad637SFrançois Tigeot 	    HDA_PARAM_GPIO_COUNT_NUM_GPI(devinfo->gpio_cap),
61292a1ad637SFrançois Tigeot 	    HDA_PARAM_GPIO_COUNT_GPI_WAKE(devinfo->gpio_cap),
61302a1ad637SFrançois Tigeot 	    HDA_PARAM_GPIO_COUNT_GPI_UNSOL(devinfo->gpio_cap));
61312a1ad637SFrançois Tigeot 	hdaa_dump_gpi(devinfo);
61322a1ad637SFrançois Tigeot 	hdaa_dump_gpio(devinfo);
61332a1ad637SFrançois Tigeot 	hdaa_dump_gpo(devinfo);
61342a1ad637SFrançois Tigeot }
61352a1ad637SFrançois Tigeot 
61362a1ad637SFrançois Tigeot static void
hdaa_configure(device_t dev)61372a1ad637SFrançois Tigeot hdaa_configure(device_t dev)
61382a1ad637SFrançois Tigeot {
61392a1ad637SFrançois Tigeot 	struct hdaa_devinfo *devinfo = device_get_softc(dev);
61402a1ad637SFrançois Tigeot 	struct hdaa_audio_ctl *ctl;
61412a1ad637SFrançois Tigeot 	int i;
61422a1ad637SFrançois Tigeot 
61432a1ad637SFrançois Tigeot 	HDA_BOOTHVERBOSE(
61442a1ad637SFrançois Tigeot 		device_printf(dev, "Applying built-in patches...\n");
61452a1ad637SFrançois Tigeot 	);
61462a1ad637SFrançois Tigeot 	hdaa_patch(devinfo);
61472a1ad637SFrançois Tigeot 	HDA_BOOTHVERBOSE(
61482a1ad637SFrançois Tigeot 		device_printf(dev, "Applying local patches...\n");
61492a1ad637SFrançois Tigeot 	);
61502a1ad637SFrançois Tigeot 	hdaa_local_patch(devinfo);
61512a1ad637SFrançois Tigeot 	hdaa_audio_postprocess(devinfo);
61522a1ad637SFrançois Tigeot 	HDA_BOOTHVERBOSE(
61532a1ad637SFrançois Tigeot 		device_printf(dev, "Parsing Ctls...\n");
61542a1ad637SFrançois Tigeot 	);
61552a1ad637SFrançois Tigeot 	hdaa_audio_ctl_parse(devinfo);
61562a1ad637SFrançois Tigeot 	HDA_BOOTHVERBOSE(
61572a1ad637SFrançois Tigeot 		device_printf(dev, "Disabling nonaudio...\n");
61582a1ad637SFrançois Tigeot 	);
61592a1ad637SFrançois Tigeot 	hdaa_audio_disable_nonaudio(devinfo);
61602a1ad637SFrançois Tigeot 	HDA_BOOTHVERBOSE(
61612a1ad637SFrançois Tigeot 		device_printf(dev, "Disabling useless...\n");
61622a1ad637SFrançois Tigeot 	);
61632a1ad637SFrançois Tigeot 	hdaa_audio_disable_useless(devinfo);
61642a1ad637SFrançois Tigeot 	HDA_BOOTVERBOSE(
61652a1ad637SFrançois Tigeot 		device_printf(dev, "Patched pins configuration:\n");
61662a1ad637SFrançois Tigeot 		hdaa_dump_pin_configs(devinfo);
61672a1ad637SFrançois Tigeot 	);
61682a1ad637SFrançois Tigeot 	HDA_BOOTHVERBOSE(
61692a1ad637SFrançois Tigeot 		device_printf(dev, "Parsing pin associations...\n");
61702a1ad637SFrançois Tigeot 	);
61712a1ad637SFrançois Tigeot 	hdaa_audio_as_parse(devinfo);
61722a1ad637SFrançois Tigeot 	HDA_BOOTHVERBOSE(
61732a1ad637SFrançois Tigeot 		device_printf(dev, "Building AFG tree...\n");
61742a1ad637SFrançois Tigeot 	);
61752a1ad637SFrançois Tigeot 	hdaa_audio_build_tree(devinfo);
61762a1ad637SFrançois Tigeot 	HDA_BOOTHVERBOSE(
61772a1ad637SFrançois Tigeot 		device_printf(dev, "Disabling unassociated "
61782a1ad637SFrançois Tigeot 		    "widgets...\n");
61792a1ad637SFrançois Tigeot 	);
61802a1ad637SFrançois Tigeot 	hdaa_audio_disable_unas(devinfo);
61812a1ad637SFrançois Tigeot 	HDA_BOOTHVERBOSE(
61822a1ad637SFrançois Tigeot 		device_printf(dev, "Disabling nonselected "
61832a1ad637SFrançois Tigeot 		    "inputs...\n");
61842a1ad637SFrançois Tigeot 	);
61852a1ad637SFrançois Tigeot 	hdaa_audio_disable_notselected(devinfo);
61862a1ad637SFrançois Tigeot 	HDA_BOOTHVERBOSE(
61872a1ad637SFrançois Tigeot 		device_printf(dev, "Disabling useless...\n");
61882a1ad637SFrançois Tigeot 	);
61892a1ad637SFrançois Tigeot 	hdaa_audio_disable_useless(devinfo);
61902a1ad637SFrançois Tigeot 	HDA_BOOTHVERBOSE(
61912a1ad637SFrançois Tigeot 		device_printf(dev, "Disabling "
61922a1ad637SFrançois Tigeot 		    "crossassociatement connections...\n");
61932a1ad637SFrançois Tigeot 	);
61942a1ad637SFrançois Tigeot 	hdaa_audio_disable_crossas(devinfo);
61952a1ad637SFrançois Tigeot 	HDA_BOOTHVERBOSE(
61962a1ad637SFrançois Tigeot 		device_printf(dev, "Disabling useless...\n");
61972a1ad637SFrançois Tigeot 	);
61982a1ad637SFrançois Tigeot 	hdaa_audio_disable_useless(devinfo);
61992a1ad637SFrançois Tigeot 	HDA_BOOTHVERBOSE(
62002a1ad637SFrançois Tigeot 		device_printf(dev, "Binding associations to channels...\n");
62012a1ad637SFrançois Tigeot 	);
62022a1ad637SFrançois Tigeot 	hdaa_audio_bind_as(devinfo);
62032a1ad637SFrançois Tigeot 	HDA_BOOTHVERBOSE(
62042a1ad637SFrançois Tigeot 		device_printf(dev, "Assigning names to signal sources...\n");
62052a1ad637SFrançois Tigeot 	);
62062a1ad637SFrançois Tigeot 	hdaa_audio_assign_names(devinfo);
62072a1ad637SFrançois Tigeot 	HDA_BOOTHVERBOSE(
62082a1ad637SFrançois Tigeot 		device_printf(dev, "Preparing PCM devices...\n");
62092a1ad637SFrançois Tigeot 	);
62102a1ad637SFrançois Tigeot 	hdaa_prepare_pcms(devinfo);
62112a1ad637SFrançois Tigeot 	HDA_BOOTHVERBOSE(
62122a1ad637SFrançois Tigeot 		device_printf(dev, "Assigning mixers to the tree...\n");
62132a1ad637SFrançois Tigeot 	);
62142a1ad637SFrançois Tigeot 	hdaa_audio_assign_mixers(devinfo);
62152a1ad637SFrançois Tigeot 	HDA_BOOTHVERBOSE(
62162a1ad637SFrançois Tigeot 		device_printf(dev, "Preparing pin controls...\n");
62172a1ad637SFrançois Tigeot 	);
62182a1ad637SFrançois Tigeot 	hdaa_audio_prepare_pin_ctrl(devinfo);
62192a1ad637SFrançois Tigeot 	HDA_BOOTHVERBOSE(
62202a1ad637SFrançois Tigeot 		device_printf(dev, "AFG commit...\n");
62212a1ad637SFrançois Tigeot 	);
62222a1ad637SFrançois Tigeot 	hdaa_audio_commit(devinfo);
62232a1ad637SFrançois Tigeot 	HDA_BOOTHVERBOSE(
62242a1ad637SFrançois Tigeot 		device_printf(dev, "Applying direct built-in patches...\n");
62252a1ad637SFrançois Tigeot 	);
62262a1ad637SFrançois Tigeot 	hdaa_patch_direct(devinfo);
62272a1ad637SFrançois Tigeot 	HDA_BOOTHVERBOSE(
62282a1ad637SFrançois Tigeot 		device_printf(dev, "Pin sense init...\n");
62292a1ad637SFrançois Tigeot 	);
62302a1ad637SFrançois Tigeot 	hdaa_sense_init(devinfo);
62312a1ad637SFrançois Tigeot 	HDA_BOOTHVERBOSE(
62322a1ad637SFrançois Tigeot 		device_printf(dev, "Creating PCM devices...\n");
62332a1ad637SFrançois Tigeot 	);
62342a1ad637SFrançois Tigeot 	hdaa_create_pcms(devinfo);
62352a1ad637SFrançois Tigeot 
62362a1ad637SFrançois Tigeot 	HDA_BOOTVERBOSE(
62372a1ad637SFrançois Tigeot 		if (devinfo->quirks != 0) {
62382a1ad637SFrançois Tigeot 			device_printf(dev, "FG config/quirks:");
62392a1ad637SFrançois Tigeot 			for (i = 0; i < nitems(hdaa_quirks_tab); i++) {
62402a1ad637SFrançois Tigeot 				if ((devinfo->quirks &
62412a1ad637SFrançois Tigeot 				    hdaa_quirks_tab[i].value) ==
62422a1ad637SFrançois Tigeot 				    hdaa_quirks_tab[i].value)
624367931cc4SFrançois Tigeot 					kprintf(" %s", hdaa_quirks_tab[i].key);
62442a1ad637SFrançois Tigeot 			}
624567931cc4SFrançois Tigeot 			kprintf("\n");
62462a1ad637SFrançois Tigeot 		}
62472a1ad637SFrançois Tigeot 	);
62482a1ad637SFrançois Tigeot 
62492a1ad637SFrançois Tigeot 	HDA_BOOTHVERBOSE(
62502a1ad637SFrançois Tigeot 		device_printf(dev, "\n");
62512a1ad637SFrançois Tigeot 		device_printf(dev, "+-----------+\n");
62522a1ad637SFrançois Tigeot 		device_printf(dev, "| HDA NODES |\n");
62532a1ad637SFrançois Tigeot 		device_printf(dev, "+-----------+\n");
62542a1ad637SFrançois Tigeot 		hdaa_dump_nodes(devinfo);
62552a1ad637SFrançois Tigeot 
62562a1ad637SFrançois Tigeot 		device_printf(dev, "\n");
62572a1ad637SFrançois Tigeot 		device_printf(dev, "+----------------+\n");
62582a1ad637SFrançois Tigeot 		device_printf(dev, "| HDA AMPLIFIERS |\n");
62592a1ad637SFrançois Tigeot 		device_printf(dev, "+----------------+\n");
62602a1ad637SFrançois Tigeot 		device_printf(dev, "\n");
62612a1ad637SFrançois Tigeot 		i = 0;
62622a1ad637SFrançois Tigeot 		while ((ctl = hdaa_audio_ctl_each(devinfo, &i)) != NULL) {
62632a1ad637SFrançois Tigeot 			device_printf(dev, "%3d: nid %3d %s (%s) index %d", i,
62642a1ad637SFrançois Tigeot 			    (ctl->widget != NULL) ? ctl->widget->nid : -1,
62652a1ad637SFrançois Tigeot 			    (ctl->ndir == HDAA_CTL_IN)?"in ":"out",
62662a1ad637SFrançois Tigeot 			    (ctl->dir == HDAA_CTL_IN)?"in ":"out",
62672a1ad637SFrançois Tigeot 			    ctl->index);
62682a1ad637SFrançois Tigeot 			if (ctl->childwidget != NULL)
626967931cc4SFrançois Tigeot 				kprintf(" cnid %3d", ctl->childwidget->nid);
62702a1ad637SFrançois Tigeot 			else
627167931cc4SFrançois Tigeot 				kprintf("         ");
627267931cc4SFrançois Tigeot 			kprintf(" ossmask=0x%08x\n",
62732a1ad637SFrançois Tigeot 			    ctl->ossmask);
62742a1ad637SFrançois Tigeot 			device_printf(dev,
62752a1ad637SFrançois Tigeot 			    "       mute: %d step: %3d size: %3d off: %3d%s\n",
62762a1ad637SFrançois Tigeot 			    ctl->mute, ctl->step, ctl->size, ctl->offset,
62772a1ad637SFrançois Tigeot 			    (ctl->enable == 0) ? " [DISABLED]" :
62782a1ad637SFrançois Tigeot 			    ((ctl->ossmask == 0) ? " [UNUSED]" : ""));
62792a1ad637SFrançois Tigeot 		}
62802a1ad637SFrançois Tigeot 		device_printf(dev, "\n");
62812a1ad637SFrançois Tigeot 	);
62822a1ad637SFrançois Tigeot }
62832a1ad637SFrançois Tigeot 
62842a1ad637SFrançois Tigeot static void
hdaa_unconfigure(device_t dev)62852a1ad637SFrançois Tigeot hdaa_unconfigure(device_t dev)
62862a1ad637SFrançois Tigeot {
62872a1ad637SFrançois Tigeot 	struct hdaa_devinfo *devinfo = device_get_softc(dev);
62882a1ad637SFrançois Tigeot 	struct hdaa_widget *w;
62892a1ad637SFrançois Tigeot 	int i, j;
62902a1ad637SFrançois Tigeot 
62912a1ad637SFrançois Tigeot 	HDA_BOOTHVERBOSE(
62922a1ad637SFrançois Tigeot 		device_printf(dev, "Pin sense deinit...\n");
62932a1ad637SFrançois Tigeot 	);
62942a1ad637SFrançois Tigeot 	hdaa_sense_deinit(devinfo);
62955a2a1f05SSascha Wildner 	if (devinfo->ctlcnt > 0)
629667931cc4SFrançois Tigeot 		kfree(devinfo->ctl, M_HDAA);
62972a1ad637SFrançois Tigeot 	devinfo->ctl = NULL;
62982a1ad637SFrançois Tigeot 	devinfo->ctlcnt = 0;
629967931cc4SFrançois Tigeot 	kfree(devinfo->as, M_HDAA);
63002a1ad637SFrançois Tigeot 	devinfo->as = NULL;
63012a1ad637SFrançois Tigeot 	devinfo->ascnt = 0;
630267931cc4SFrançois Tigeot 	kfree(devinfo->devs, M_HDAA);
63032a1ad637SFrançois Tigeot 	devinfo->devs = NULL;
63042a1ad637SFrançois Tigeot 	devinfo->num_devs = 0;
630567931cc4SFrançois Tigeot 	kfree(devinfo->chans, M_HDAA);
63062a1ad637SFrançois Tigeot 	devinfo->chans = NULL;
63072a1ad637SFrançois Tigeot 	devinfo->num_chans = 0;
63082a1ad637SFrançois Tigeot 	for (i = devinfo->startnode; i < devinfo->endnode; i++) {
63092a1ad637SFrançois Tigeot 		w = hdaa_widget_get(devinfo, i);
63102a1ad637SFrançois Tigeot 		if (w == NULL)
63112a1ad637SFrançois Tigeot 			continue;
63122a1ad637SFrançois Tigeot 		w->enable = 1;
63132a1ad637SFrançois Tigeot 		w->selconn = -1;
63142a1ad637SFrançois Tigeot 		w->pflags = 0;
63152a1ad637SFrançois Tigeot 		w->bindas = -1;
63162a1ad637SFrançois Tigeot 		w->bindseqmask = 0;
63172a1ad637SFrançois Tigeot 		w->ossdev = -1;
63182a1ad637SFrançois Tigeot 		w->ossmask = 0;
63192a1ad637SFrançois Tigeot 		for (j = 0; j < w->nconns; j++)
63202a1ad637SFrançois Tigeot 			w->connsenable[j] = 1;
63212a1ad637SFrançois Tigeot 		if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX)
63222a1ad637SFrançois Tigeot 			w->wclass.pin.config = w->wclass.pin.newconf;
63232a1ad637SFrançois Tigeot 		if (w->eld != NULL) {
63242a1ad637SFrançois Tigeot 			w->eld_len = 0;
632567931cc4SFrançois Tigeot 			kfree(w->eld, M_HDAA);
63262a1ad637SFrançois Tigeot 			w->eld = NULL;
63272a1ad637SFrançois Tigeot 		}
63282a1ad637SFrançois Tigeot 	}
63292a1ad637SFrançois Tigeot }
63302a1ad637SFrançois Tigeot 
63312a1ad637SFrançois Tigeot static int
hdaa_sysctl_gpi_state(SYSCTL_HANDLER_ARGS)63322a1ad637SFrançois Tigeot hdaa_sysctl_gpi_state(SYSCTL_HANDLER_ARGS)
63332a1ad637SFrançois Tigeot {
63342a1ad637SFrançois Tigeot 	struct hdaa_devinfo *devinfo = oidp->oid_arg1;
63352a1ad637SFrançois Tigeot 	device_t dev = devinfo->dev;
63362a1ad637SFrançois Tigeot 	char buf[256];
63372a1ad637SFrançois Tigeot 	int n = 0, i, numgpi;
63382a1ad637SFrançois Tigeot 	uint32_t data = 0;
63392a1ad637SFrançois Tigeot 
63402a1ad637SFrançois Tigeot 	buf[0] = 0;
63412a1ad637SFrançois Tigeot 	hdaa_lock(devinfo);
63422a1ad637SFrançois Tigeot 	numgpi = HDA_PARAM_GPIO_COUNT_NUM_GPI(devinfo->gpio_cap);
63432a1ad637SFrançois Tigeot 	if (numgpi > 0) {
63442a1ad637SFrançois Tigeot 		data = hda_command(dev,
63452a1ad637SFrançois Tigeot 		    HDA_CMD_GET_GPI_DATA(0, devinfo->nid));
63462a1ad637SFrançois Tigeot 	}
63472a1ad637SFrançois Tigeot 	hdaa_unlock(devinfo);
63482a1ad637SFrançois Tigeot 	for (i = 0; i < numgpi; i++) {
634967931cc4SFrançois Tigeot 		n += ksnprintf(buf + n, sizeof(buf) - n, "%s%d=%d",
63502a1ad637SFrançois Tigeot 		    n != 0 ? " " : "", i, ((data >> i) & 1));
63512a1ad637SFrançois Tigeot 	}
63522a1ad637SFrançois Tigeot 	return (sysctl_handle_string(oidp, buf, sizeof(buf), req));
63532a1ad637SFrançois Tigeot }
63542a1ad637SFrançois Tigeot 
63552a1ad637SFrançois Tigeot static int
hdaa_sysctl_gpio_state(SYSCTL_HANDLER_ARGS)63562a1ad637SFrançois Tigeot hdaa_sysctl_gpio_state(SYSCTL_HANDLER_ARGS)
63572a1ad637SFrançois Tigeot {
63582a1ad637SFrançois Tigeot 	struct hdaa_devinfo *devinfo = oidp->oid_arg1;
63592a1ad637SFrançois Tigeot 	device_t dev = devinfo->dev;
63602a1ad637SFrançois Tigeot 	char buf[256];
63612a1ad637SFrançois Tigeot 	int n = 0, i, numgpio;
63622a1ad637SFrançois Tigeot 	uint32_t data = 0, enable = 0, dir = 0;
63632a1ad637SFrançois Tigeot 
63642a1ad637SFrançois Tigeot 	buf[0] = 0;
63652a1ad637SFrançois Tigeot 	hdaa_lock(devinfo);
63662a1ad637SFrançois Tigeot 	numgpio = HDA_PARAM_GPIO_COUNT_NUM_GPIO(devinfo->gpio_cap);
63672a1ad637SFrançois Tigeot 	if (numgpio > 0) {
63682a1ad637SFrançois Tigeot 		data = hda_command(dev,
63692a1ad637SFrançois Tigeot 		    HDA_CMD_GET_GPIO_DATA(0, devinfo->nid));
63702a1ad637SFrançois Tigeot 		enable = hda_command(dev,
63712a1ad637SFrançois Tigeot 		    HDA_CMD_GET_GPIO_ENABLE_MASK(0, devinfo->nid));
63722a1ad637SFrançois Tigeot 		dir = hda_command(dev,
63732a1ad637SFrançois Tigeot 		    HDA_CMD_GET_GPIO_DIRECTION(0, devinfo->nid));
63742a1ad637SFrançois Tigeot 	}
63752a1ad637SFrançois Tigeot 	hdaa_unlock(devinfo);
63762a1ad637SFrançois Tigeot 	for (i = 0; i < numgpio; i++) {
637767931cc4SFrançois Tigeot 		n += ksnprintf(buf + n, sizeof(buf) - n, "%s%d=",
63782a1ad637SFrançois Tigeot 		    n != 0 ? " " : "", i);
63792a1ad637SFrançois Tigeot 		if ((enable & (1 << i)) == 0) {
638067931cc4SFrançois Tigeot 			n += ksnprintf(buf + n, sizeof(buf) - n, "disabled");
63812a1ad637SFrançois Tigeot 			continue;
63822a1ad637SFrançois Tigeot 		}
638367931cc4SFrançois Tigeot 		n += ksnprintf(buf + n, sizeof(buf) - n, "%sput(%d)",
63842a1ad637SFrançois Tigeot 		    ((dir >> i) & 1) ? "out" : "in", ((data >> i) & 1));
63852a1ad637SFrançois Tigeot 	}
63862a1ad637SFrançois Tigeot 	return (sysctl_handle_string(oidp, buf, sizeof(buf), req));
63872a1ad637SFrançois Tigeot }
63882a1ad637SFrançois Tigeot 
63892a1ad637SFrançois Tigeot static int
hdaa_sysctl_gpio_config(SYSCTL_HANDLER_ARGS)63902a1ad637SFrançois Tigeot hdaa_sysctl_gpio_config(SYSCTL_HANDLER_ARGS)
63912a1ad637SFrançois Tigeot {
63922a1ad637SFrançois Tigeot 	struct hdaa_devinfo *devinfo = oidp->oid_arg1;
63932a1ad637SFrançois Tigeot 	char buf[256];
63942a1ad637SFrançois Tigeot 	int error, n = 0, i, numgpio;
63952a1ad637SFrançois Tigeot 	uint32_t gpio, x;
63962a1ad637SFrançois Tigeot 
63972a1ad637SFrançois Tigeot 	gpio = devinfo->newgpio;
63982a1ad637SFrançois Tigeot 	numgpio = HDA_PARAM_GPIO_COUNT_NUM_GPIO(devinfo->gpio_cap);
63992a1ad637SFrançois Tigeot 	buf[0] = 0;
64002a1ad637SFrançois Tigeot 	for (i = 0; i < numgpio; i++) {
64012a1ad637SFrançois Tigeot 		x = (gpio & HDAA_GPIO_MASK(i)) >> HDAA_GPIO_SHIFT(i);
640267931cc4SFrançois Tigeot 		n += ksnprintf(buf + n, sizeof(buf) - n, "%s%d=%s",
64032a1ad637SFrançois Tigeot 		    n != 0 ? " " : "", i, HDA_GPIO_ACTIONS[x]);
64042a1ad637SFrançois Tigeot 	}
64052a1ad637SFrançois Tigeot 	error = sysctl_handle_string(oidp, buf, sizeof(buf), req);
64062a1ad637SFrançois Tigeot 	if (error != 0 || req->newptr == NULL)
64072a1ad637SFrançois Tigeot 		return (error);
64082a1ad637SFrançois Tigeot 	if (strncmp(buf, "0x", 2) == 0)
64092a1ad637SFrançois Tigeot 		gpio = strtol(buf + 2, NULL, 16);
64102a1ad637SFrançois Tigeot 	else
64112a1ad637SFrançois Tigeot 		gpio = hdaa_gpio_patch(gpio, buf);
64122a1ad637SFrançois Tigeot 	hdaa_lock(devinfo);
64132a1ad637SFrançois Tigeot 	devinfo->newgpio = devinfo->gpio = gpio;
64142a1ad637SFrançois Tigeot 	hdaa_gpio_commit(devinfo);
64152a1ad637SFrançois Tigeot 	hdaa_unlock(devinfo);
64162a1ad637SFrançois Tigeot 	return (0);
64172a1ad637SFrançois Tigeot }
64182a1ad637SFrançois Tigeot 
64192a1ad637SFrançois Tigeot static int
hdaa_sysctl_gpo_state(SYSCTL_HANDLER_ARGS)64202a1ad637SFrançois Tigeot hdaa_sysctl_gpo_state(SYSCTL_HANDLER_ARGS)
64212a1ad637SFrançois Tigeot {
64222a1ad637SFrançois Tigeot 	struct hdaa_devinfo *devinfo = oidp->oid_arg1;
64232a1ad637SFrançois Tigeot 	device_t dev = devinfo->dev;
64242a1ad637SFrançois Tigeot 	char buf[256];
64252a1ad637SFrançois Tigeot 	int n = 0, i, numgpo;
64262a1ad637SFrançois Tigeot 	uint32_t data = 0;
64272a1ad637SFrançois Tigeot 
64282a1ad637SFrançois Tigeot 	buf[0] = 0;
64292a1ad637SFrançois Tigeot 	hdaa_lock(devinfo);
64302a1ad637SFrançois Tigeot 	numgpo = HDA_PARAM_GPIO_COUNT_NUM_GPO(devinfo->gpio_cap);
64312a1ad637SFrançois Tigeot 	if (numgpo > 0) {
64322a1ad637SFrançois Tigeot 		data = hda_command(dev,
64332a1ad637SFrançois Tigeot 		    HDA_CMD_GET_GPO_DATA(0, devinfo->nid));
64342a1ad637SFrançois Tigeot 	}
64352a1ad637SFrançois Tigeot 	hdaa_unlock(devinfo);
64362a1ad637SFrançois Tigeot 	for (i = 0; i < numgpo; i++) {
643767931cc4SFrançois Tigeot 		n += ksnprintf(buf + n, sizeof(buf) - n, "%s%d=%d",
64382a1ad637SFrançois Tigeot 		    n != 0 ? " " : "", i, ((data >> i) & 1));
64392a1ad637SFrançois Tigeot 	}
64402a1ad637SFrançois Tigeot 	return (sysctl_handle_string(oidp, buf, sizeof(buf), req));
64412a1ad637SFrançois Tigeot }
64422a1ad637SFrançois Tigeot 
64432a1ad637SFrançois Tigeot static int
hdaa_sysctl_gpo_config(SYSCTL_HANDLER_ARGS)64442a1ad637SFrançois Tigeot hdaa_sysctl_gpo_config(SYSCTL_HANDLER_ARGS)
64452a1ad637SFrançois Tigeot {
64462a1ad637SFrançois Tigeot 	struct hdaa_devinfo *devinfo = oidp->oid_arg1;
64472a1ad637SFrançois Tigeot 	char buf[256];
64482a1ad637SFrançois Tigeot 	int error, n = 0, i, numgpo;
64492a1ad637SFrançois Tigeot 	uint32_t gpo, x;
64502a1ad637SFrançois Tigeot 
64512a1ad637SFrançois Tigeot 	gpo = devinfo->newgpo;
64522a1ad637SFrançois Tigeot 	numgpo = HDA_PARAM_GPIO_COUNT_NUM_GPO(devinfo->gpio_cap);
64532a1ad637SFrançois Tigeot 	buf[0] = 0;
64542a1ad637SFrançois Tigeot 	for (i = 0; i < numgpo; i++) {
64552a1ad637SFrançois Tigeot 		x = (gpo & HDAA_GPIO_MASK(i)) >> HDAA_GPIO_SHIFT(i);
645667931cc4SFrançois Tigeot 		n += ksnprintf(buf + n, sizeof(buf) - n, "%s%d=%s",
64572a1ad637SFrançois Tigeot 		    n != 0 ? " " : "", i, HDA_GPIO_ACTIONS[x]);
64582a1ad637SFrançois Tigeot 	}
64592a1ad637SFrançois Tigeot 	error = sysctl_handle_string(oidp, buf, sizeof(buf), req);
64602a1ad637SFrançois Tigeot 	if (error != 0 || req->newptr == NULL)
64612a1ad637SFrançois Tigeot 		return (error);
64622a1ad637SFrançois Tigeot 	if (strncmp(buf, "0x", 2) == 0)
64632a1ad637SFrançois Tigeot 		gpo = strtol(buf + 2, NULL, 16);
64642a1ad637SFrançois Tigeot 	else
64652a1ad637SFrançois Tigeot 		gpo = hdaa_gpio_patch(gpo, buf);
64662a1ad637SFrançois Tigeot 	hdaa_lock(devinfo);
64672a1ad637SFrançois Tigeot 	devinfo->newgpo = devinfo->gpo = gpo;
64682a1ad637SFrançois Tigeot 	hdaa_gpo_commit(devinfo);
64692a1ad637SFrançois Tigeot 	hdaa_unlock(devinfo);
64702a1ad637SFrançois Tigeot 	return (0);
64712a1ad637SFrançois Tigeot }
64722a1ad637SFrançois Tigeot 
64732a1ad637SFrançois Tigeot static int
hdaa_sysctl_reconfig(SYSCTL_HANDLER_ARGS)64742a1ad637SFrançois Tigeot hdaa_sysctl_reconfig(SYSCTL_HANDLER_ARGS)
64752a1ad637SFrançois Tigeot {
64762a1ad637SFrançois Tigeot 	device_t dev;
64772a1ad637SFrançois Tigeot 	struct hdaa_devinfo *devinfo;
64782a1ad637SFrançois Tigeot 	int error, val;
64792a1ad637SFrançois Tigeot 
64802a1ad637SFrançois Tigeot 	dev = oidp->oid_arg1;
64812a1ad637SFrançois Tigeot 	devinfo = device_get_softc(dev);
64822a1ad637SFrançois Tigeot 	if (devinfo == NULL)
64832a1ad637SFrançois Tigeot 		return (EINVAL);
64842a1ad637SFrançois Tigeot 	val = 0;
64852a1ad637SFrançois Tigeot 	error = sysctl_handle_int(oidp, &val, 0, req);
64862a1ad637SFrançois Tigeot 	if (error != 0 || req->newptr == NULL || val == 0)
64872a1ad637SFrançois Tigeot 		return (error);
64882a1ad637SFrançois Tigeot 
64892a1ad637SFrançois Tigeot 	HDA_BOOTHVERBOSE(
64902a1ad637SFrançois Tigeot 		device_printf(dev, "Reconfiguration...\n");
64912a1ad637SFrançois Tigeot 	);
64922a1ad637SFrançois Tigeot 	if ((error = device_delete_children(dev)) != 0)
64932a1ad637SFrançois Tigeot 		return (error);
64942a1ad637SFrançois Tigeot 	hdaa_lock(devinfo);
64952a1ad637SFrançois Tigeot 	hdaa_unconfigure(dev);
64962a1ad637SFrançois Tigeot 	hdaa_configure(dev);
64972a1ad637SFrançois Tigeot 	hdaa_unlock(devinfo);
64982a1ad637SFrançois Tigeot 	bus_generic_attach(dev);
64992a1ad637SFrançois Tigeot 	HDA_BOOTHVERBOSE(
65002a1ad637SFrançois Tigeot 		device_printf(dev, "Reconfiguration done\n");
65012a1ad637SFrançois Tigeot 	);
65022a1ad637SFrançois Tigeot 	return (0);
65032a1ad637SFrançois Tigeot }
65042a1ad637SFrançois Tigeot 
65052a1ad637SFrançois Tigeot static int
hdaa_suspend(device_t dev)65062a1ad637SFrançois Tigeot hdaa_suspend(device_t dev)
65072a1ad637SFrançois Tigeot {
65082a1ad637SFrançois Tigeot 	struct hdaa_devinfo *devinfo = device_get_softc(dev);
65092a1ad637SFrançois Tigeot 	int i;
65102a1ad637SFrançois Tigeot 
65112a1ad637SFrançois Tigeot 	HDA_BOOTHVERBOSE(
65122a1ad637SFrançois Tigeot 		device_printf(dev, "Suspend...\n");
65132a1ad637SFrançois Tigeot 	);
65142a1ad637SFrançois Tigeot 	hdaa_lock(devinfo);
65152a1ad637SFrançois Tigeot 	HDA_BOOTHVERBOSE(
65162a1ad637SFrançois Tigeot 		device_printf(dev, "Stop streams...\n");
65172a1ad637SFrançois Tigeot 	);
65182a1ad637SFrançois Tigeot 	for (i = 0; i < devinfo->num_chans; i++) {
65192a1ad637SFrançois Tigeot 		if (devinfo->chans[i].flags & HDAA_CHN_RUNNING) {
65202a1ad637SFrançois Tigeot 			devinfo->chans[i].flags |= HDAA_CHN_SUSPEND;
65212a1ad637SFrançois Tigeot 			hdaa_channel_stop(&devinfo->chans[i]);
65222a1ad637SFrançois Tigeot 		}
65232a1ad637SFrançois Tigeot 	}
65242a1ad637SFrançois Tigeot 	HDA_BOOTHVERBOSE(
65252a1ad637SFrançois Tigeot 		device_printf(dev, "Power down FG"
65262a1ad637SFrançois Tigeot 		    " nid=%d to the D3 state...\n",
65272a1ad637SFrançois Tigeot 		    devinfo->nid);
65282a1ad637SFrançois Tigeot 	);
65292a1ad637SFrançois Tigeot 	hda_command(devinfo->dev,
65302a1ad637SFrançois Tigeot 	    HDA_CMD_SET_POWER_STATE(0,
65312a1ad637SFrançois Tigeot 	    devinfo->nid, HDA_CMD_POWER_STATE_D3));
65322a1ad637SFrançois Tigeot 	callout_stop(&devinfo->poll_jack);
65332a1ad637SFrançois Tigeot 	hdaa_unlock(devinfo);
65342a1ad637SFrançois Tigeot 	callout_drain(&devinfo->poll_jack);
65352a1ad637SFrançois Tigeot 	HDA_BOOTHVERBOSE(
65362a1ad637SFrançois Tigeot 		device_printf(dev, "Suspend done\n");
65372a1ad637SFrançois Tigeot 	);
65382a1ad637SFrançois Tigeot 	return (0);
65392a1ad637SFrançois Tigeot }
65402a1ad637SFrançois Tigeot 
65412a1ad637SFrançois Tigeot static int
hdaa_resume(device_t dev)65422a1ad637SFrançois Tigeot hdaa_resume(device_t dev)
65432a1ad637SFrançois Tigeot {
65442a1ad637SFrançois Tigeot 	struct hdaa_devinfo *devinfo = device_get_softc(dev);
65452a1ad637SFrançois Tigeot 	int i;
65462a1ad637SFrançois Tigeot 
65472a1ad637SFrançois Tigeot 	HDA_BOOTHVERBOSE(
65482a1ad637SFrançois Tigeot 		device_printf(dev, "Resume...\n");
65492a1ad637SFrançois Tigeot 	);
65502a1ad637SFrançois Tigeot 	hdaa_lock(devinfo);
65512a1ad637SFrançois Tigeot 	HDA_BOOTHVERBOSE(
65522a1ad637SFrançois Tigeot 		device_printf(dev, "Power up audio FG nid=%d...\n",
65532a1ad637SFrançois Tigeot 		    devinfo->nid);
65542a1ad637SFrançois Tigeot 	);
65552a1ad637SFrançois Tigeot 	hdaa_powerup(devinfo);
65562a1ad637SFrançois Tigeot 	HDA_BOOTHVERBOSE(
65572a1ad637SFrançois Tigeot 		device_printf(dev, "AFG commit...\n");
65582a1ad637SFrançois Tigeot 	);
65592a1ad637SFrançois Tigeot 	hdaa_audio_commit(devinfo);
65602a1ad637SFrançois Tigeot 	HDA_BOOTHVERBOSE(
65612a1ad637SFrançois Tigeot 		device_printf(dev, "Applying direct built-in patches...\n");
65622a1ad637SFrançois Tigeot 	);
65632a1ad637SFrançois Tigeot 	hdaa_patch_direct(devinfo);
65642a1ad637SFrançois Tigeot 	HDA_BOOTHVERBOSE(
65652a1ad637SFrançois Tigeot 		device_printf(dev, "Pin sense init...\n");
65662a1ad637SFrançois Tigeot 	);
65672a1ad637SFrançois Tigeot 	hdaa_sense_init(devinfo);
65682a1ad637SFrançois Tigeot 
65692a1ad637SFrançois Tigeot 	hdaa_unlock(devinfo);
65702a1ad637SFrançois Tigeot 	for (i = 0; i < devinfo->num_devs; i++) {
65712a1ad637SFrançois Tigeot 		struct hdaa_pcm_devinfo *pdevinfo = &devinfo->devs[i];
65722a1ad637SFrançois Tigeot 		HDA_BOOTHVERBOSE(
65732a1ad637SFrançois Tigeot 			device_printf(pdevinfo->dev,
65742a1ad637SFrançois Tigeot 			    "OSS mixer reinitialization...\n");
65752a1ad637SFrançois Tigeot 		);
65762a1ad637SFrançois Tigeot 		if (mixer_reinit(pdevinfo->dev) == -1)
65772a1ad637SFrançois Tigeot 			device_printf(pdevinfo->dev,
65782a1ad637SFrançois Tigeot 			    "unable to reinitialize the mixer\n");
65792a1ad637SFrançois Tigeot 	}
65802a1ad637SFrançois Tigeot 	hdaa_lock(devinfo);
65812a1ad637SFrançois Tigeot 	HDA_BOOTHVERBOSE(
65822a1ad637SFrançois Tigeot 		device_printf(dev, "Start streams...\n");
65832a1ad637SFrançois Tigeot 	);
65842a1ad637SFrançois Tigeot 	for (i = 0; i < devinfo->num_chans; i++) {
65852a1ad637SFrançois Tigeot 		if (devinfo->chans[i].flags & HDAA_CHN_SUSPEND) {
65862a1ad637SFrançois Tigeot 			devinfo->chans[i].flags &= ~HDAA_CHN_SUSPEND;
65872a1ad637SFrançois Tigeot 			hdaa_channel_start(&devinfo->chans[i]);
65882a1ad637SFrançois Tigeot 		}
65892a1ad637SFrançois Tigeot 	}
65902a1ad637SFrançois Tigeot 	hdaa_unlock(devinfo);
65912a1ad637SFrançois Tigeot 	HDA_BOOTHVERBOSE(
65922a1ad637SFrançois Tigeot 		device_printf(dev, "Resume done\n");
65932a1ad637SFrançois Tigeot 	);
65942a1ad637SFrançois Tigeot 	return (0);
65952a1ad637SFrançois Tigeot }
65962a1ad637SFrançois Tigeot 
65972a1ad637SFrançois Tigeot static int
hdaa_probe(device_t dev)65982a1ad637SFrançois Tigeot hdaa_probe(device_t dev)
65992a1ad637SFrançois Tigeot {
66002a1ad637SFrançois Tigeot 	const char *pdesc;
66012a1ad637SFrançois Tigeot 	char buf[128];
66022a1ad637SFrançois Tigeot 
66032a1ad637SFrançois Tigeot 	if (hda_get_node_type(dev) != HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO)
66042a1ad637SFrançois Tigeot 		return (ENXIO);
66052a1ad637SFrançois Tigeot 	pdesc = device_get_desc(device_get_parent(dev));
660667931cc4SFrançois Tigeot 	ksnprintf(buf, sizeof(buf), "%.*s Audio Function Group",
66072a1ad637SFrançois Tigeot 	    (int)(strlen(pdesc) - 10), pdesc);
66082a1ad637SFrançois Tigeot 	device_set_desc_copy(dev, buf);
66092a1ad637SFrançois Tigeot 	return (BUS_PROBE_DEFAULT);
66102a1ad637SFrançois Tigeot }
66112a1ad637SFrançois Tigeot 
66122a1ad637SFrançois Tigeot static int
hdaa_attach(device_t dev)66132a1ad637SFrançois Tigeot hdaa_attach(device_t dev)
66142a1ad637SFrançois Tigeot {
66152a1ad637SFrançois Tigeot 	struct hdaa_devinfo *devinfo = device_get_softc(dev);
66162a1ad637SFrançois Tigeot 	uint32_t res;
66172a1ad637SFrançois Tigeot 	nid_t nid = hda_get_node_id(dev);
66182a1ad637SFrançois Tigeot 
66192a1ad637SFrançois Tigeot 	devinfo->dev = dev;
66202a1ad637SFrançois Tigeot 	devinfo->lock = HDAC_GET_MTX(device_get_parent(dev), dev);
66212a1ad637SFrançois Tigeot 	devinfo->nid = nid;
66222a1ad637SFrançois Tigeot 	devinfo->newquirks = -1;
66232a1ad637SFrançois Tigeot 	devinfo->newgpio = -1;
66242a1ad637SFrançois Tigeot 	devinfo->newgpo = -1;
662567931cc4SFrançois Tigeot 	callout_init_mp(&devinfo->poll_jack);
66262a1ad637SFrançois Tigeot 	devinfo->poll_ival = hz;
66272a1ad637SFrançois Tigeot 
66282a1ad637SFrançois Tigeot 	hdaa_lock(devinfo);
66292a1ad637SFrançois Tigeot 	res = hda_command(dev,
66302a1ad637SFrançois Tigeot 	    HDA_CMD_GET_PARAMETER(0 , nid, HDA_PARAM_SUB_NODE_COUNT));
66312a1ad637SFrançois Tigeot 	hdaa_unlock(devinfo);
66322a1ad637SFrançois Tigeot 
66332a1ad637SFrançois Tigeot 	devinfo->nodecnt = HDA_PARAM_SUB_NODE_COUNT_TOTAL(res);
66342a1ad637SFrançois Tigeot 	devinfo->startnode = HDA_PARAM_SUB_NODE_COUNT_START(res);
66352a1ad637SFrançois Tigeot 	devinfo->endnode = devinfo->startnode + devinfo->nodecnt;
66362a1ad637SFrançois Tigeot 
66372a1ad637SFrançois Tigeot 	HDA_BOOTVERBOSE(
66382a1ad637SFrançois Tigeot 		device_printf(dev, "Subsystem ID: 0x%08x\n",
66392a1ad637SFrançois Tigeot 		    hda_get_subsystem_id(dev));
66402a1ad637SFrançois Tigeot 	);
66412a1ad637SFrançois Tigeot 	HDA_BOOTHVERBOSE(
66422a1ad637SFrançois Tigeot 		device_printf(dev,
66432a1ad637SFrançois Tigeot 		    "Audio Function Group at nid=%d: %d subnodes %d-%d\n",
66442a1ad637SFrançois Tigeot 		    nid, devinfo->nodecnt,
66452a1ad637SFrançois Tigeot 		    devinfo->startnode, devinfo->endnode - 1);
66462a1ad637SFrançois Tigeot 	);
66472a1ad637SFrançois Tigeot 
66482a1ad637SFrançois Tigeot 	if (devinfo->nodecnt > 0)
664967931cc4SFrançois Tigeot 		devinfo->widget = (struct hdaa_widget *)kmalloc(
66502a1ad637SFrançois Tigeot 		    sizeof(*(devinfo->widget)) * devinfo->nodecnt, M_HDAA,
66512a1ad637SFrançois Tigeot 		    M_WAITOK | M_ZERO);
66522a1ad637SFrançois Tigeot 	else
66532a1ad637SFrançois Tigeot 		devinfo->widget = NULL;
66542a1ad637SFrançois Tigeot 
66552a1ad637SFrançois Tigeot 	hdaa_lock(devinfo);
66562a1ad637SFrançois Tigeot 	HDA_BOOTHVERBOSE(
66572a1ad637SFrançois Tigeot 		device_printf(dev, "Powering up...\n");
66582a1ad637SFrançois Tigeot 	);
66592a1ad637SFrançois Tigeot 	hdaa_powerup(devinfo);
66602a1ad637SFrançois Tigeot 	HDA_BOOTHVERBOSE(
66612a1ad637SFrançois Tigeot 		device_printf(dev, "Parsing audio FG...\n");
66622a1ad637SFrançois Tigeot 	);
66632a1ad637SFrançois Tigeot 	hdaa_audio_parse(devinfo);
66642a1ad637SFrançois Tigeot 	HDA_BOOTVERBOSE(
66652a1ad637SFrançois Tigeot 		device_printf(dev, "Original pins configuration:\n");
66662a1ad637SFrançois Tigeot 		hdaa_dump_pin_configs(devinfo);
66672a1ad637SFrançois Tigeot 	);
66682a1ad637SFrançois Tigeot 	hdaa_configure(dev);
66692a1ad637SFrançois Tigeot 	hdaa_unlock(devinfo);
66702a1ad637SFrançois Tigeot 
66712a1ad637SFrançois Tigeot 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
66722a1ad637SFrançois Tigeot 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
667367931cc4SFrançois Tigeot 	    "config", CTLTYPE_STRING | CTLFLAG_RW,
66742a1ad637SFrançois Tigeot 	    &devinfo->newquirks, sizeof(&devinfo->newquirks),
66752a1ad637SFrançois Tigeot 	    hdaa_sysctl_quirks, "A", "Configuration options");
66762a1ad637SFrançois Tigeot 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
66772a1ad637SFrançois Tigeot 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
667867931cc4SFrançois Tigeot 	    "gpi_state", CTLTYPE_STRING | CTLFLAG_RD,
66792a1ad637SFrançois Tigeot 	    devinfo, sizeof(devinfo),
66802a1ad637SFrançois Tigeot 	    hdaa_sysctl_gpi_state, "A", "GPI state");
66812a1ad637SFrançois Tigeot 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
66822a1ad637SFrançois Tigeot 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
668367931cc4SFrançois Tigeot 	    "gpio_state", CTLTYPE_STRING | CTLFLAG_RD,
66842a1ad637SFrançois Tigeot 	    devinfo, sizeof(devinfo),
66852a1ad637SFrançois Tigeot 	    hdaa_sysctl_gpio_state, "A", "GPIO state");
66862a1ad637SFrançois Tigeot 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
66872a1ad637SFrançois Tigeot 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
668867931cc4SFrançois Tigeot 	    "gpio_config", CTLTYPE_STRING | CTLFLAG_RW,
66892a1ad637SFrançois Tigeot 	    devinfo, sizeof(devinfo),
66902a1ad637SFrançois Tigeot 	    hdaa_sysctl_gpio_config, "A", "GPIO configuration");
66912a1ad637SFrançois Tigeot 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
66922a1ad637SFrançois Tigeot 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
669367931cc4SFrançois Tigeot 	    "gpo_state", CTLTYPE_STRING | CTLFLAG_RD,
66942a1ad637SFrançois Tigeot 	    devinfo, sizeof(devinfo),
66952a1ad637SFrançois Tigeot 	    hdaa_sysctl_gpo_state, "A", "GPO state");
66962a1ad637SFrançois Tigeot 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
66972a1ad637SFrançois Tigeot 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
669867931cc4SFrançois Tigeot 	    "gpo_config", CTLTYPE_STRING | CTLFLAG_RW,
66992a1ad637SFrançois Tigeot 	    devinfo, sizeof(devinfo),
67002a1ad637SFrançois Tigeot 	    hdaa_sysctl_gpo_config, "A", "GPO configuration");
67012a1ad637SFrançois Tigeot 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
67022a1ad637SFrançois Tigeot 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
67032a1ad637SFrançois Tigeot 	    "reconfig", CTLTYPE_INT | CTLFLAG_RW,
67042a1ad637SFrançois Tigeot 	    dev, sizeof(dev),
67052a1ad637SFrançois Tigeot 	    hdaa_sysctl_reconfig, "I", "Reprocess configuration");
67062a1ad637SFrançois Tigeot 	bus_generic_attach(dev);
67072a1ad637SFrançois Tigeot 	return (0);
67082a1ad637SFrançois Tigeot }
67092a1ad637SFrançois Tigeot 
67102a1ad637SFrançois Tigeot static int
hdaa_detach(device_t dev)67112a1ad637SFrançois Tigeot hdaa_detach(device_t dev)
67122a1ad637SFrançois Tigeot {
67132a1ad637SFrançois Tigeot 	struct hdaa_devinfo *devinfo = device_get_softc(dev);
67142a1ad637SFrançois Tigeot 	int error;
67152a1ad637SFrançois Tigeot 
67162a1ad637SFrançois Tigeot 	if ((error = device_delete_children(dev)) != 0)
67172a1ad637SFrançois Tigeot 		return (error);
67182a1ad637SFrançois Tigeot 
67192a1ad637SFrançois Tigeot 	hdaa_lock(devinfo);
67202a1ad637SFrançois Tigeot 	hdaa_unconfigure(dev);
67212a1ad637SFrançois Tigeot 	devinfo->poll_ival = 0;
67222a1ad637SFrançois Tigeot 	callout_stop(&devinfo->poll_jack);
67232a1ad637SFrançois Tigeot 	hdaa_unlock(devinfo);
67242a1ad637SFrançois Tigeot 	callout_drain(&devinfo->poll_jack);
67252a1ad637SFrançois Tigeot 
672667931cc4SFrançois Tigeot 	kfree(devinfo->widget, M_HDAA);
67272a1ad637SFrançois Tigeot 	return (0);
67282a1ad637SFrançois Tigeot }
67292a1ad637SFrançois Tigeot 
67302a1ad637SFrançois Tigeot static int
hdaa_print_child(device_t dev,device_t child)67312a1ad637SFrançois Tigeot hdaa_print_child(device_t dev, device_t child)
67322a1ad637SFrançois Tigeot {
67332a1ad637SFrançois Tigeot 	struct hdaa_devinfo *devinfo = device_get_softc(dev);
67342a1ad637SFrançois Tigeot 	struct hdaa_pcm_devinfo *pdevinfo =
67352a1ad637SFrançois Tigeot 	    (struct hdaa_pcm_devinfo *)device_get_ivars(child);
67362a1ad637SFrançois Tigeot 	struct hdaa_audio_as *as;
67372a1ad637SFrançois Tigeot 	int retval, first = 1, i;
67382a1ad637SFrançois Tigeot 
67392a1ad637SFrançois Tigeot 	retval = bus_print_child_header(dev, child);
674067931cc4SFrançois Tigeot 	retval += kprintf(" at nid ");
67412a1ad637SFrançois Tigeot 	if (pdevinfo->playas >= 0) {
67422a1ad637SFrançois Tigeot 		as = &devinfo->as[pdevinfo->playas];
67432a1ad637SFrançois Tigeot 		for (i = 0; i < 16; i++) {
67442a1ad637SFrançois Tigeot 			if (as->pins[i] <= 0)
67452a1ad637SFrançois Tigeot 				continue;
674667931cc4SFrançois Tigeot 			retval += kprintf("%s%d", first ? "" : ",", as->pins[i]);
67472a1ad637SFrançois Tigeot 			first = 0;
67482a1ad637SFrançois Tigeot 		}
67492a1ad637SFrançois Tigeot 	}
67502a1ad637SFrançois Tigeot 	if (pdevinfo->recas >= 0) {
67512a1ad637SFrançois Tigeot 		if (pdevinfo->playas >= 0) {
675267931cc4SFrançois Tigeot 			retval += kprintf(" and ");
67532a1ad637SFrançois Tigeot 			first = 1;
67542a1ad637SFrançois Tigeot 		}
67552a1ad637SFrançois Tigeot 		as = &devinfo->as[pdevinfo->recas];
67562a1ad637SFrançois Tigeot 		for (i = 0; i < 16; i++) {
67572a1ad637SFrançois Tigeot 			if (as->pins[i] <= 0)
67582a1ad637SFrançois Tigeot 				continue;
675967931cc4SFrançois Tigeot 			retval += kprintf("%s%d", first ? "" : ",", as->pins[i]);
67602a1ad637SFrançois Tigeot 			first = 0;
67612a1ad637SFrançois Tigeot 		}
67622a1ad637SFrançois Tigeot 	}
67632a1ad637SFrançois Tigeot 	retval += bus_print_child_footer(dev, child);
67642a1ad637SFrançois Tigeot 
67652a1ad637SFrançois Tigeot 	return (retval);
67662a1ad637SFrançois Tigeot }
67672a1ad637SFrançois Tigeot 
67682a1ad637SFrançois Tigeot static int
hdaa_child_location_str(device_t dev,device_t child,char * buf,size_t buflen)67692a1ad637SFrançois Tigeot hdaa_child_location_str(device_t dev, device_t child, char *buf,
67702a1ad637SFrançois Tigeot     size_t buflen)
67712a1ad637SFrançois Tigeot {
67722a1ad637SFrançois Tigeot 	struct hdaa_devinfo *devinfo = device_get_softc(dev);
67732a1ad637SFrançois Tigeot 	struct hdaa_pcm_devinfo *pdevinfo =
67742a1ad637SFrançois Tigeot 	    (struct hdaa_pcm_devinfo *)device_get_ivars(child);
67752a1ad637SFrançois Tigeot 	struct hdaa_audio_as *as;
67762a1ad637SFrançois Tigeot 	int first = 1, i, len = 0;
67772a1ad637SFrançois Tigeot 
677867931cc4SFrançois Tigeot 	len += ksnprintf(buf + len, buflen - len, "nid=");
67792a1ad637SFrançois Tigeot 	if (pdevinfo->playas >= 0) {
67802a1ad637SFrançois Tigeot 		as = &devinfo->as[pdevinfo->playas];
67812a1ad637SFrançois Tigeot 		for (i = 0; i < 16; i++) {
67822a1ad637SFrançois Tigeot 			if (as->pins[i] <= 0)
67832a1ad637SFrançois Tigeot 				continue;
678467931cc4SFrançois Tigeot 			len += ksnprintf(buf + len, buflen - len,
67852a1ad637SFrançois Tigeot 			    "%s%d", first ? "" : ",", as->pins[i]);
67862a1ad637SFrançois Tigeot 			first = 0;
67872a1ad637SFrançois Tigeot 		}
67882a1ad637SFrançois Tigeot 	}
67892a1ad637SFrançois Tigeot 	if (pdevinfo->recas >= 0) {
67902a1ad637SFrançois Tigeot 		as = &devinfo->as[pdevinfo->recas];
67912a1ad637SFrançois Tigeot 		for (i = 0; i < 16; i++) {
67922a1ad637SFrançois Tigeot 			if (as->pins[i] <= 0)
67932a1ad637SFrançois Tigeot 				continue;
679467931cc4SFrançois Tigeot 			len += ksnprintf(buf + len, buflen - len,
67952a1ad637SFrançois Tigeot 			    "%s%d", first ? "" : ",", as->pins[i]);
67962a1ad637SFrançois Tigeot 			first = 0;
67972a1ad637SFrançois Tigeot 		}
67982a1ad637SFrançois Tigeot 	}
67992a1ad637SFrançois Tigeot 	return (0);
68002a1ad637SFrançois Tigeot }
68012a1ad637SFrançois Tigeot 
68022a1ad637SFrançois Tigeot static void
hdaa_stream_intr(device_t dev,int dir,int stream)68032a1ad637SFrançois Tigeot hdaa_stream_intr(device_t dev, int dir, int stream)
68042a1ad637SFrançois Tigeot {
68052a1ad637SFrançois Tigeot 	struct hdaa_devinfo *devinfo = device_get_softc(dev);
68062a1ad637SFrançois Tigeot 	struct hdaa_chan *ch;
68072a1ad637SFrançois Tigeot 	int i;
68082a1ad637SFrançois Tigeot 
68092a1ad637SFrançois Tigeot 	for (i = 0; i < devinfo->num_chans; i++) {
68102a1ad637SFrançois Tigeot 		ch = &devinfo->chans[i];
68112a1ad637SFrançois Tigeot 		if (!(ch->flags & HDAA_CHN_RUNNING))
68122a1ad637SFrançois Tigeot 			continue;
68132a1ad637SFrançois Tigeot 		if (ch->dir == ((dir == 1) ? PCMDIR_PLAY : PCMDIR_REC) &&
68142a1ad637SFrançois Tigeot 		    ch->sid == stream) {
68152a1ad637SFrançois Tigeot 			hdaa_unlock(devinfo);
68162a1ad637SFrançois Tigeot 			chn_intr(ch->c);
68172a1ad637SFrançois Tigeot 			hdaa_lock(devinfo);
68182a1ad637SFrançois Tigeot 		}
68192a1ad637SFrançois Tigeot 	}
68202a1ad637SFrançois Tigeot }
68212a1ad637SFrançois Tigeot 
68222a1ad637SFrançois Tigeot static void
hdaa_unsol_intr(device_t dev,uint32_t resp)68232a1ad637SFrançois Tigeot hdaa_unsol_intr(device_t dev, uint32_t resp)
68242a1ad637SFrançois Tigeot {
68252a1ad637SFrançois Tigeot 	struct hdaa_devinfo *devinfo = device_get_softc(dev);
68262a1ad637SFrançois Tigeot 	struct hdaa_widget *w;
68272a1ad637SFrançois Tigeot 	int i, tag, flags;
68282a1ad637SFrançois Tigeot 
68292a1ad637SFrançois Tigeot 	HDA_BOOTHVERBOSE(
68302a1ad637SFrançois Tigeot 		device_printf(dev, "Unsolicited response %08x\n", resp);
68312a1ad637SFrançois Tigeot 	);
68322a1ad637SFrançois Tigeot 	tag = resp >> 26;
68332a1ad637SFrançois Tigeot 	for (i = devinfo->startnode; i < devinfo->endnode; i++) {
68342a1ad637SFrançois Tigeot 		w = hdaa_widget_get(devinfo, i);
68352a1ad637SFrançois Tigeot 		if (w == NULL || w->enable == 0 || w->type !=
68362a1ad637SFrançois Tigeot 		    HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX)
68372a1ad637SFrançois Tigeot 			continue;
68382a1ad637SFrançois Tigeot 		if (w->unsol != tag)
68392a1ad637SFrançois Tigeot 			continue;
68402a1ad637SFrançois Tigeot 		if (HDA_PARAM_PIN_CAP_DP(w->wclass.pin.cap) ||
68412a1ad637SFrançois Tigeot 		    HDA_PARAM_PIN_CAP_HDMI(w->wclass.pin.cap))
68422a1ad637SFrançois Tigeot 			flags = resp & 0x03;
68432a1ad637SFrançois Tigeot 		else
68442a1ad637SFrançois Tigeot 			flags = 0x01;
68452a1ad637SFrançois Tigeot 		if (flags & 0x01)
68462a1ad637SFrançois Tigeot 			hdaa_presence_handler(w);
68472a1ad637SFrançois Tigeot 		if (flags & 0x02)
68482a1ad637SFrançois Tigeot 			hdaa_eld_handler(w);
68492a1ad637SFrançois Tigeot 	}
68502a1ad637SFrançois Tigeot }
68512a1ad637SFrançois Tigeot 
68522a1ad637SFrançois Tigeot static device_method_t hdaa_methods[] = {
68532a1ad637SFrançois Tigeot 	/* device interface */
68542a1ad637SFrançois Tigeot 	DEVMETHOD(device_probe,		hdaa_probe),
68552a1ad637SFrançois Tigeot 	DEVMETHOD(device_attach,	hdaa_attach),
68562a1ad637SFrançois Tigeot 	DEVMETHOD(device_detach,	hdaa_detach),
68572a1ad637SFrançois Tigeot 	DEVMETHOD(device_suspend,	hdaa_suspend),
68582a1ad637SFrançois Tigeot 	DEVMETHOD(device_resume,	hdaa_resume),
68592a1ad637SFrançois Tigeot 	/* Bus interface */
68602a1ad637SFrançois Tigeot 	DEVMETHOD(bus_print_child,	hdaa_print_child),
68612a1ad637SFrançois Tigeot 	DEVMETHOD(bus_child_location_str, hdaa_child_location_str),
68622a1ad637SFrançois Tigeot 	DEVMETHOD(hdac_stream_intr,	hdaa_stream_intr),
68632a1ad637SFrançois Tigeot 	DEVMETHOD(hdac_unsol_intr,	hdaa_unsol_intr),
68642a1ad637SFrançois Tigeot 	DEVMETHOD(hdac_pindump,		hdaa_pindump),
68652a1ad637SFrançois Tigeot 	DEVMETHOD_END
68662a1ad637SFrançois Tigeot };
68672a1ad637SFrançois Tigeot 
68682a1ad637SFrançois Tigeot static driver_t hdaa_driver = {
68692a1ad637SFrançois Tigeot 	"hdaa",
68702a1ad637SFrançois Tigeot 	hdaa_methods,
68712a1ad637SFrançois Tigeot 	sizeof(struct hdaa_devinfo),
68722a1ad637SFrançois Tigeot };
68732a1ad637SFrançois Tigeot 
68742a1ad637SFrançois Tigeot static devclass_t hdaa_devclass;
68752a1ad637SFrançois Tigeot 
68762a1ad637SFrançois Tigeot DRIVER_MODULE(snd_hda, hdacc, hdaa_driver, hdaa_devclass, NULL, NULL);
68772a1ad637SFrançois Tigeot 
68782a1ad637SFrançois Tigeot static void
hdaa_chan_formula(struct hdaa_devinfo * devinfo,int asid,char * buf,int buflen)68792a1ad637SFrançois Tigeot hdaa_chan_formula(struct hdaa_devinfo *devinfo, int asid,
68802a1ad637SFrançois Tigeot     char *buf, int buflen)
68812a1ad637SFrançois Tigeot {
68822a1ad637SFrançois Tigeot 	struct hdaa_audio_as *as;
68832a1ad637SFrançois Tigeot 	int c;
68842a1ad637SFrançois Tigeot 
68852a1ad637SFrançois Tigeot 	as = &devinfo->as[asid];
68862a1ad637SFrançois Tigeot 	c = devinfo->chans[as->chans[0]].channels;
68872a1ad637SFrançois Tigeot 	if (c == 1)
688867931cc4SFrançois Tigeot 		ksnprintf(buf, buflen, "mono");
68892a1ad637SFrançois Tigeot 	else if (c == 2) {
68902a1ad637SFrançois Tigeot 		if (as->hpredir < 0)
68912a1ad637SFrançois Tigeot 			buf[0] = 0;
68922a1ad637SFrançois Tigeot 		else
689367931cc4SFrançois Tigeot 			ksnprintf(buf, buflen, "2.0");
68942a1ad637SFrançois Tigeot 	} else if (as->pinset == 0x0003)
689567931cc4SFrançois Tigeot 		ksnprintf(buf, buflen, "3.1");
68962a1ad637SFrançois Tigeot 	else if (as->pinset == 0x0005 || as->pinset == 0x0011)
689767931cc4SFrançois Tigeot 		ksnprintf(buf, buflen, "4.0");
68982a1ad637SFrançois Tigeot 	else if (as->pinset == 0x0007 || as->pinset == 0x0013)
689967931cc4SFrançois Tigeot 		ksnprintf(buf, buflen, "5.1");
69002a1ad637SFrançois Tigeot 	else if (as->pinset == 0x0017)
690167931cc4SFrançois Tigeot 		ksnprintf(buf, buflen, "7.1");
69022a1ad637SFrançois Tigeot 	else
690367931cc4SFrançois Tigeot 		ksnprintf(buf, buflen, "%dch", c);
69042a1ad637SFrançois Tigeot 	if (as->hpredir >= 0)
69052a1ad637SFrançois Tigeot 		strlcat(buf, "+HP", buflen);
69062a1ad637SFrançois Tigeot }
69072a1ad637SFrançois Tigeot 
69082a1ad637SFrançois Tigeot static int
hdaa_chan_type(struct hdaa_devinfo * devinfo,int asid)69092a1ad637SFrançois Tigeot hdaa_chan_type(struct hdaa_devinfo *devinfo, int asid)
69102a1ad637SFrançois Tigeot {
69112a1ad637SFrançois Tigeot 	struct hdaa_audio_as *as;
69122a1ad637SFrançois Tigeot 	struct hdaa_widget *w;
69132a1ad637SFrançois Tigeot 	int i, t = -1, t1;
69142a1ad637SFrançois Tigeot 
69152a1ad637SFrançois Tigeot 	as = &devinfo->as[asid];
69162a1ad637SFrançois Tigeot 	for (i = 0; i < 16; i++) {
69172a1ad637SFrançois Tigeot 		w = hdaa_widget_get(devinfo, as->pins[i]);
69182a1ad637SFrançois Tigeot 		if (w == NULL || w->enable == 0 || w->type !=
69192a1ad637SFrançois Tigeot 		    HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX)
69202a1ad637SFrançois Tigeot 			continue;
69212a1ad637SFrançois Tigeot 		t1 = HDA_CONFIG_DEFAULTCONF_DEVICE(w->wclass.pin.config);
69222a1ad637SFrançois Tigeot 		if (t == -1)
69232a1ad637SFrançois Tigeot 			t = t1;
69242a1ad637SFrançois Tigeot 		else if (t != t1) {
69252a1ad637SFrançois Tigeot 			t = -2;
69262a1ad637SFrançois Tigeot 			break;
69272a1ad637SFrançois Tigeot 		}
69282a1ad637SFrançois Tigeot 	}
69292a1ad637SFrançois Tigeot 	return (t);
69302a1ad637SFrançois Tigeot }
69312a1ad637SFrançois Tigeot 
69322a1ad637SFrançois Tigeot static int
hdaa_sysctl_32bit(SYSCTL_HANDLER_ARGS)69332a1ad637SFrançois Tigeot hdaa_sysctl_32bit(SYSCTL_HANDLER_ARGS)
69342a1ad637SFrançois Tigeot {
69352a1ad637SFrançois Tigeot 	struct hdaa_audio_as *as = (struct hdaa_audio_as *)oidp->oid_arg1;
69362a1ad637SFrançois Tigeot 	struct hdaa_pcm_devinfo *pdevinfo = as->pdevinfo;
69372a1ad637SFrançois Tigeot 	struct hdaa_devinfo *devinfo = pdevinfo->devinfo;
69382a1ad637SFrançois Tigeot 	struct hdaa_chan *ch;
69392a1ad637SFrançois Tigeot 	int error, val, i;
69402a1ad637SFrançois Tigeot 	uint32_t pcmcap;
69412a1ad637SFrançois Tigeot 
69422a1ad637SFrançois Tigeot 	ch = &devinfo->chans[as->chans[0]];
69432a1ad637SFrançois Tigeot 	val = (ch->bit32 == 4) ? 32 : ((ch->bit32 == 3) ? 24 :
69442a1ad637SFrançois Tigeot 	    ((ch->bit32 == 2) ? 20 : 0));
69452a1ad637SFrançois Tigeot 	error = sysctl_handle_int(oidp, &val, 0, req);
69462a1ad637SFrançois Tigeot 	if (error != 0 || req->newptr == NULL)
69472a1ad637SFrançois Tigeot 		return (error);
69482a1ad637SFrançois Tigeot 	pcmcap = ch->supp_pcm_size_rate;
69492a1ad637SFrançois Tigeot 	if (val == 32 && HDA_PARAM_SUPP_PCM_SIZE_RATE_32BIT(pcmcap))
69502a1ad637SFrançois Tigeot 		ch->bit32 = 4;
69512a1ad637SFrançois Tigeot 	else if (val == 24 && HDA_PARAM_SUPP_PCM_SIZE_RATE_24BIT(pcmcap))
69522a1ad637SFrançois Tigeot 		ch->bit32 = 3;
69532a1ad637SFrançois Tigeot 	else if (val == 20 && HDA_PARAM_SUPP_PCM_SIZE_RATE_20BIT(pcmcap))
69542a1ad637SFrançois Tigeot 		ch->bit32 = 2;
69552a1ad637SFrançois Tigeot 	else
69562a1ad637SFrançois Tigeot 		return (EINVAL);
69572a1ad637SFrançois Tigeot 	for (i = 1; i < as->num_chans; i++)
69582a1ad637SFrançois Tigeot 		devinfo->chans[as->chans[i]].bit32 = ch->bit32;
69592a1ad637SFrançois Tigeot 	return (0);
69602a1ad637SFrançois Tigeot }
69612a1ad637SFrançois Tigeot 
69622a1ad637SFrançois Tigeot static int
hdaa_pcm_probe(device_t dev)69632a1ad637SFrançois Tigeot hdaa_pcm_probe(device_t dev)
69642a1ad637SFrançois Tigeot {
69652a1ad637SFrançois Tigeot 	struct hdaa_pcm_devinfo *pdevinfo =
69662a1ad637SFrançois Tigeot 	    (struct hdaa_pcm_devinfo *)device_get_ivars(dev);
69672a1ad637SFrançois Tigeot 	struct hdaa_devinfo *devinfo = pdevinfo->devinfo;
69682a1ad637SFrançois Tigeot 	const char *pdesc;
69692a1ad637SFrançois Tigeot 	char chans1[8], chans2[8];
69702a1ad637SFrançois Tigeot 	char buf[128];
69712a1ad637SFrançois Tigeot 	int loc1, loc2, t1, t2;
69722a1ad637SFrançois Tigeot 
69732a1ad637SFrançois Tigeot 	if (pdevinfo->playas >= 0)
69742a1ad637SFrançois Tigeot 		loc1 = devinfo->as[pdevinfo->playas].location;
69752a1ad637SFrançois Tigeot 	else
69762a1ad637SFrançois Tigeot 		loc1 = devinfo->as[pdevinfo->recas].location;
69772a1ad637SFrançois Tigeot 	if (pdevinfo->recas >= 0)
69782a1ad637SFrançois Tigeot 		loc2 = devinfo->as[pdevinfo->recas].location;
69792a1ad637SFrançois Tigeot 	else
69802a1ad637SFrançois Tigeot 		loc2 = loc1;
69812a1ad637SFrançois Tigeot 	if (loc1 != loc2)
69822a1ad637SFrançois Tigeot 		loc1 = -2;
69832a1ad637SFrançois Tigeot 	if (loc1 >= 0 && HDA_LOCS[loc1][0] == '0')
69842a1ad637SFrançois Tigeot 		loc1 = -2;
69852a1ad637SFrançois Tigeot 	chans1[0] = 0;
69862a1ad637SFrançois Tigeot 	chans2[0] = 0;
69872a1ad637SFrançois Tigeot 	t1 = t2 = -1;
69882a1ad637SFrançois Tigeot 	if (pdevinfo->playas >= 0) {
69892a1ad637SFrançois Tigeot 		hdaa_chan_formula(devinfo, pdevinfo->playas,
69902a1ad637SFrançois Tigeot 		    chans1, sizeof(chans1));
69912a1ad637SFrançois Tigeot 		t1 = hdaa_chan_type(devinfo, pdevinfo->playas);
69922a1ad637SFrançois Tigeot 	}
69932a1ad637SFrançois Tigeot 	if (pdevinfo->recas >= 0) {
69942a1ad637SFrançois Tigeot 		hdaa_chan_formula(devinfo, pdevinfo->recas,
69952a1ad637SFrançois Tigeot 		    chans2, sizeof(chans2));
69962a1ad637SFrançois Tigeot 		t2 = hdaa_chan_type(devinfo, pdevinfo->recas);
69972a1ad637SFrançois Tigeot 	}
69982a1ad637SFrançois Tigeot 	if (chans1[0] != 0 || chans2[0] != 0) {
69992a1ad637SFrançois Tigeot 		if (chans1[0] == 0 && pdevinfo->playas >= 0)
700067931cc4SFrançois Tigeot 			ksnprintf(chans1, sizeof(chans1), "2.0");
70012a1ad637SFrançois Tigeot 		else if (chans2[0] == 0 && pdevinfo->recas >= 0)
700267931cc4SFrançois Tigeot 			ksnprintf(chans2, sizeof(chans2), "2.0");
70032a1ad637SFrançois Tigeot 		if (strcmp(chans1, chans2) == 0)
70042a1ad637SFrançois Tigeot 			chans2[0] = 0;
70052a1ad637SFrançois Tigeot 	}
70062a1ad637SFrançois Tigeot 	if (t1 == -1)
70072a1ad637SFrançois Tigeot 		t1 = t2;
70082a1ad637SFrançois Tigeot 	else if (t2 == -1)
70092a1ad637SFrançois Tigeot 		t2 = t1;
70102a1ad637SFrançois Tigeot 	if (t1 != t2)
70112a1ad637SFrançois Tigeot 		t1 = -2;
70122a1ad637SFrançois Tigeot 	if (pdevinfo->digital)
70132a1ad637SFrançois Tigeot 		t1 = -2;
70142a1ad637SFrançois Tigeot 	pdesc = device_get_desc(device_get_parent(dev));
701567931cc4SFrançois Tigeot 	ksnprintf(buf, sizeof(buf), "%.*s (%s%s%s%s%s%s%s%s%s)",
70162a1ad637SFrançois Tigeot 	    (int)(strlen(pdesc) - 21), pdesc,
70172a1ad637SFrançois Tigeot 	    loc1 >= 0 ? HDA_LOCS[loc1] : "", loc1 >= 0 ? " " : "",
70182a1ad637SFrançois Tigeot 	    (pdevinfo->digital == 0x7)?"HDMI/DP":
70192a1ad637SFrançois Tigeot 	    ((pdevinfo->digital == 0x5)?"DisplayPort":
70202a1ad637SFrançois Tigeot 	    ((pdevinfo->digital == 0x3)?"HDMI":
70212a1ad637SFrançois Tigeot 	    ((pdevinfo->digital)?"Digital":"Analog"))),
70222a1ad637SFrançois Tigeot 	    chans1[0] ? " " : "", chans1,
70232a1ad637SFrançois Tigeot 	    chans2[0] ? "/" : "", chans2,
70242a1ad637SFrançois Tigeot 	    t1 >= 0 ? " " : "", t1 >= 0 ? HDA_DEVS[t1] : "");
70252a1ad637SFrançois Tigeot 	device_set_desc_copy(dev, buf);
70262a1ad637SFrançois Tigeot 	return (BUS_PROBE_SPECIFIC);
70272a1ad637SFrançois Tigeot }
70282a1ad637SFrançois Tigeot 
70292a1ad637SFrançois Tigeot static int
hdaa_pcm_attach(device_t dev)70302a1ad637SFrançois Tigeot hdaa_pcm_attach(device_t dev)
70312a1ad637SFrançois Tigeot {
70322a1ad637SFrançois Tigeot 	struct hdaa_pcm_devinfo *pdevinfo =
70332a1ad637SFrançois Tigeot 	    (struct hdaa_pcm_devinfo *)device_get_ivars(dev);
70342a1ad637SFrançois Tigeot 	struct hdaa_devinfo *devinfo = pdevinfo->devinfo;
70352a1ad637SFrançois Tigeot 	struct hdaa_audio_as *as;
70362a1ad637SFrançois Tigeot 	struct snddev_info *d;
70372a1ad637SFrançois Tigeot 	char status[SND_STATUSLEN];
70382a1ad637SFrançois Tigeot 	int i;
70392a1ad637SFrançois Tigeot 
70402a1ad637SFrançois Tigeot 	pdevinfo->chan_size = pcm_getbuffersize(dev,
70412a1ad637SFrançois Tigeot 	    HDA_BUFSZ_MIN, HDA_BUFSZ_DEFAULT, HDA_BUFSZ_MAX);
70422a1ad637SFrançois Tigeot 
70432a1ad637SFrançois Tigeot 	HDA_BOOTVERBOSE(
70442a1ad637SFrançois Tigeot 		hdaa_dump_dac(pdevinfo);
70452a1ad637SFrançois Tigeot 		hdaa_dump_adc(pdevinfo);
70462a1ad637SFrançois Tigeot 		hdaa_dump_mix(pdevinfo);
70472a1ad637SFrançois Tigeot 		hdaa_dump_ctls(pdevinfo, "Master Volume", SOUND_MASK_VOLUME);
70482a1ad637SFrançois Tigeot 		hdaa_dump_ctls(pdevinfo, "PCM Volume", SOUND_MASK_PCM);
70492a1ad637SFrançois Tigeot 		hdaa_dump_ctls(pdevinfo, "CD Volume", SOUND_MASK_CD);
70502a1ad637SFrançois Tigeot 		hdaa_dump_ctls(pdevinfo, "Microphone Volume", SOUND_MASK_MIC);
70512a1ad637SFrançois Tigeot 		hdaa_dump_ctls(pdevinfo, "Microphone2 Volume", SOUND_MASK_MONITOR);
70522a1ad637SFrançois Tigeot 		hdaa_dump_ctls(pdevinfo, "Line-in Volume", SOUND_MASK_LINE);
70532a1ad637SFrançois Tigeot 		hdaa_dump_ctls(pdevinfo, "Speaker/Beep Volume", SOUND_MASK_SPEAKER);
70542a1ad637SFrançois Tigeot 		hdaa_dump_ctls(pdevinfo, "Recording Level", SOUND_MASK_RECLEV);
70552a1ad637SFrançois Tigeot 		hdaa_dump_ctls(pdevinfo, "Input Mix Level", SOUND_MASK_IMIX);
70562a1ad637SFrançois Tigeot 		hdaa_dump_ctls(pdevinfo, "Input Monitoring Level", SOUND_MASK_IGAIN);
70572a1ad637SFrançois Tigeot 		hdaa_dump_ctls(pdevinfo, NULL, 0);
70582a1ad637SFrançois Tigeot 	);
70592a1ad637SFrançois Tigeot 
70602a1ad637SFrançois Tigeot 	if (resource_int_value(device_get_name(dev),
70612a1ad637SFrançois Tigeot 	    device_get_unit(dev), "blocksize", &i) == 0 && i > 0) {
70622a1ad637SFrançois Tigeot 		i &= HDA_BLK_ALIGN;
70632a1ad637SFrançois Tigeot 		if (i < HDA_BLK_MIN)
70642a1ad637SFrançois Tigeot 			i = HDA_BLK_MIN;
70652a1ad637SFrançois Tigeot 		pdevinfo->chan_blkcnt = pdevinfo->chan_size / i;
70662a1ad637SFrançois Tigeot 		i = 0;
70672a1ad637SFrançois Tigeot 		while (pdevinfo->chan_blkcnt >> i)
70682a1ad637SFrançois Tigeot 			i++;
70692a1ad637SFrançois Tigeot 		pdevinfo->chan_blkcnt = 1 << (i - 1);
70702a1ad637SFrançois Tigeot 		if (pdevinfo->chan_blkcnt < HDA_BDL_MIN)
70712a1ad637SFrançois Tigeot 			pdevinfo->chan_blkcnt = HDA_BDL_MIN;
70722a1ad637SFrançois Tigeot 		else if (pdevinfo->chan_blkcnt > HDA_BDL_MAX)
70732a1ad637SFrançois Tigeot 			pdevinfo->chan_blkcnt = HDA_BDL_MAX;
70742a1ad637SFrançois Tigeot 	} else
70752a1ad637SFrançois Tigeot 		pdevinfo->chan_blkcnt = HDA_BDL_DEFAULT;
70762a1ad637SFrançois Tigeot 
70772a1ad637SFrançois Tigeot 	/*
70782a1ad637SFrançois Tigeot 	 * We don't register interrupt handler with snd_setup_intr
70792a1ad637SFrançois Tigeot 	 * in pcm device. Mark pcm device as MPSAFE manually.
70802a1ad637SFrançois Tigeot 	 */
70812a1ad637SFrançois Tigeot 	pcm_setflags(dev, pcm_getflags(dev) | SD_F_MPSAFE);
70822a1ad637SFrançois Tigeot 
70832a1ad637SFrançois Tigeot 	HDA_BOOTHVERBOSE(
70842a1ad637SFrançois Tigeot 		device_printf(dev, "OSS mixer initialization...\n");
70852a1ad637SFrançois Tigeot 	);
70862a1ad637SFrançois Tigeot 	if (mixer_init(dev, &hdaa_audio_ctl_ossmixer_class, pdevinfo) != 0)
70872a1ad637SFrançois Tigeot 		device_printf(dev, "Can't register mixer\n");
70882a1ad637SFrançois Tigeot 
70892a1ad637SFrançois Tigeot 	HDA_BOOTHVERBOSE(
70902a1ad637SFrançois Tigeot 		device_printf(dev, "Registering PCM channels...\n");
70912a1ad637SFrançois Tigeot 	);
70922a1ad637SFrançois Tigeot 	if (pcm_register(dev, pdevinfo, (pdevinfo->playas >= 0)?1:0,
70932a1ad637SFrançois Tigeot 	    (pdevinfo->recas >= 0)?1:0) != 0)
70942a1ad637SFrançois Tigeot 		device_printf(dev, "Can't register PCM\n");
70952a1ad637SFrançois Tigeot 
70962a1ad637SFrançois Tigeot 	pdevinfo->registered++;
70972a1ad637SFrançois Tigeot 
70982a1ad637SFrançois Tigeot 	d = device_get_softc(dev);
70992a1ad637SFrançois Tigeot 	if (pdevinfo->playas >= 0) {
71002a1ad637SFrançois Tigeot 		as = &devinfo->as[pdevinfo->playas];
71012a1ad637SFrançois Tigeot 		for (i = 0; i < as->num_chans; i++)
71022a1ad637SFrançois Tigeot 			pcm_addchan(dev, PCMDIR_PLAY, &hdaa_channel_class,
71032a1ad637SFrançois Tigeot 			    &devinfo->chans[as->chans[i]]);
71042a1ad637SFrançois Tigeot 		SYSCTL_ADD_PROC(&d->play_sysctl_ctx,
71052a1ad637SFrançois Tigeot 		    SYSCTL_CHILDREN(d->play_sysctl_tree), OID_AUTO,
710667931cc4SFrançois Tigeot 		    "32bit", CTLTYPE_INT | CTLFLAG_RW,
71072a1ad637SFrançois Tigeot 		    as, sizeof(as), hdaa_sysctl_32bit, "I",
71082a1ad637SFrançois Tigeot 		    "Resolution of 32bit samples (20/24/32bit)");
71092a1ad637SFrançois Tigeot 	}
71102a1ad637SFrançois Tigeot 	if (pdevinfo->recas >= 0) {
71112a1ad637SFrançois Tigeot 		as = &devinfo->as[pdevinfo->recas];
71122a1ad637SFrançois Tigeot 		for (i = 0; i < as->num_chans; i++)
71132a1ad637SFrançois Tigeot 			pcm_addchan(dev, PCMDIR_REC, &hdaa_channel_class,
71142a1ad637SFrançois Tigeot 			    &devinfo->chans[as->chans[i]]);
71152a1ad637SFrançois Tigeot 		SYSCTL_ADD_PROC(&d->rec_sysctl_ctx,
71162a1ad637SFrançois Tigeot 		    SYSCTL_CHILDREN(d->rec_sysctl_tree), OID_AUTO,
711767931cc4SFrançois Tigeot 		    "32bit", CTLTYPE_INT | CTLFLAG_RW,
71182a1ad637SFrançois Tigeot 		    as, sizeof(as), hdaa_sysctl_32bit, "I",
71192a1ad637SFrançois Tigeot 		    "Resolution of 32bit samples (20/24/32bit)");
71202a1ad637SFrançois Tigeot 		pdevinfo->autorecsrc = 2;
71212a1ad637SFrançois Tigeot 		resource_int_value(device_get_name(dev), device_get_unit(dev),
71222a1ad637SFrançois Tigeot 		    "rec.autosrc", &pdevinfo->autorecsrc);
71232a1ad637SFrançois Tigeot 		SYSCTL_ADD_INT(&d->rec_sysctl_ctx,
71242a1ad637SFrançois Tigeot 		    SYSCTL_CHILDREN(d->rec_sysctl_tree), OID_AUTO,
71252a1ad637SFrançois Tigeot 		    "autosrc", CTLFLAG_RW,
71262a1ad637SFrançois Tigeot 		    &pdevinfo->autorecsrc, 0,
71272a1ad637SFrançois Tigeot 		    "Automatic recording source selection");
71282a1ad637SFrançois Tigeot 	}
71292a1ad637SFrançois Tigeot 
71302a1ad637SFrançois Tigeot 	if (pdevinfo->mixer != NULL) {
71312a1ad637SFrançois Tigeot 		hdaa_audio_ctl_set_defaults(pdevinfo);
71322a1ad637SFrançois Tigeot 		hdaa_lock(devinfo);
71332a1ad637SFrançois Tigeot 		if (pdevinfo->playas >= 0) {
71342a1ad637SFrançois Tigeot 			as = &devinfo->as[pdevinfo->playas];
71352a1ad637SFrançois Tigeot 			hdaa_channels_handler(as);
71362a1ad637SFrançois Tigeot 		}
71372a1ad637SFrançois Tigeot 		if (pdevinfo->recas >= 0) {
71382a1ad637SFrançois Tigeot 			as = &devinfo->as[pdevinfo->recas];
71392a1ad637SFrançois Tigeot 			hdaa_autorecsrc_handler(as, NULL);
71402a1ad637SFrançois Tigeot 			hdaa_channels_handler(as);
71412a1ad637SFrançois Tigeot 		}
71422a1ad637SFrançois Tigeot 		hdaa_unlock(devinfo);
71432a1ad637SFrançois Tigeot 	}
71442a1ad637SFrançois Tigeot 
714567931cc4SFrançois Tigeot 	ksnprintf(status, SND_STATUSLEN, "on %s %s",
71462a1ad637SFrançois Tigeot 	    device_get_nameunit(device_get_parent(dev)),
71472a1ad637SFrançois Tigeot 	    PCM_KLDSTRING(snd_hda));
71482a1ad637SFrançois Tigeot 	pcm_setstatus(dev, status);
71492a1ad637SFrançois Tigeot 
71502a1ad637SFrançois Tigeot 	return (0);
71512a1ad637SFrançois Tigeot }
71522a1ad637SFrançois Tigeot 
71532a1ad637SFrançois Tigeot static int
hdaa_pcm_detach(device_t dev)71542a1ad637SFrançois Tigeot hdaa_pcm_detach(device_t dev)
71552a1ad637SFrançois Tigeot {
71562a1ad637SFrançois Tigeot 	struct hdaa_pcm_devinfo *pdevinfo =
71572a1ad637SFrançois Tigeot 	    (struct hdaa_pcm_devinfo *)device_get_ivars(dev);
71582a1ad637SFrançois Tigeot 	int err;
71592a1ad637SFrançois Tigeot 
71602a1ad637SFrançois Tigeot 	if (pdevinfo->registered > 0) {
71612a1ad637SFrançois Tigeot 		err = pcm_unregister(dev);
71622a1ad637SFrançois Tigeot 		if (err != 0)
71632a1ad637SFrançois Tigeot 			return (err);
71642a1ad637SFrançois Tigeot 	}
71652a1ad637SFrançois Tigeot 
71662a1ad637SFrançois Tigeot 	return (0);
71672a1ad637SFrançois Tigeot }
71682a1ad637SFrançois Tigeot 
71692a1ad637SFrançois Tigeot static device_method_t hdaa_pcm_methods[] = {
71702a1ad637SFrançois Tigeot 	/* device interface */
71712a1ad637SFrançois Tigeot 	DEVMETHOD(device_probe,		hdaa_pcm_probe),
71722a1ad637SFrançois Tigeot 	DEVMETHOD(device_attach,	hdaa_pcm_attach),
71732a1ad637SFrançois Tigeot 	DEVMETHOD(device_detach,	hdaa_pcm_detach),
71742a1ad637SFrançois Tigeot 	DEVMETHOD_END
71752a1ad637SFrançois Tigeot };
71762a1ad637SFrançois Tigeot 
71772a1ad637SFrançois Tigeot static driver_t hdaa_pcm_driver = {
71782a1ad637SFrançois Tigeot 	"pcm",
71792a1ad637SFrançois Tigeot 	hdaa_pcm_methods,
71802a1ad637SFrançois Tigeot 	PCM_SOFTC_SIZE,
71812a1ad637SFrançois Tigeot };
71822a1ad637SFrançois Tigeot 
71832a1ad637SFrançois Tigeot DRIVER_MODULE(snd_hda_pcm, hdaa, hdaa_pcm_driver, pcm_devclass, NULL, NULL);
71842a1ad637SFrançois Tigeot MODULE_DEPEND(snd_hda, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
71852a1ad637SFrançois Tigeot MODULE_VERSION(snd_hda, 1);
7186